├── VERSION.in
├── dub.json
├── source
├── gtd
│ ├── WrapException.d
│ ├── GirAlias.d
│ ├── GirConstant.d
│ ├── IndentedStringBuilder.d
│ ├── GirVersion.d
│ ├── Log.d
│ ├── GlibTypes.d
│ ├── DefReader.d
│ ├── GirEnum.d
│ ├── GirType.d
│ ├── LinkedHasMap.d
│ ├── XMLReader.d
│ ├── GirField.d
│ ├── GirPackage.d
│ ├── GirWrapper.d
│ └── GirStruct.d
└── girtod.d
├── meson.build
├── GNUmakefile
├── Readme_APILookup
└── COPYING
/VERSION.in:
--------------------------------------------------------------------------------
1 | @VCS_TAG@
2 |
--------------------------------------------------------------------------------
/dub.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "girtod",
3 | "description": "A binding generator for libraries supporting gobject-introspection.",
4 | "homepage": "https://gtkd.org",
5 | "license": "LGPL-3.0",
6 | "targetType": "executable",
7 | "mainSourceFile": "source/girtod.d",
8 | "stringImportPaths": ["./"],
9 | "preGenerateCommands": [
10 | "echo $$DUB_PACKAGE_VERSION > $PACKAGE_DIR/VERSION"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/source/gtd/WrapException.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.WrapException;
19 |
20 | class WrapException : Exception
21 | {
22 | this(A...)(auto ref A a)
23 | {
24 | import std.functional;
25 | super(forward!a);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/meson.build:
--------------------------------------------------------------------------------
1 | project('GIR-to-D', 'd', version: '0.23.4')
2 |
3 | source = [
4 | 'source/girtod.d',
5 | 'source/gtd/DefReader.d',
6 | 'source/gtd/GlibTypes.d',
7 | 'source/gtd/GirAlias.d',
8 | 'source/gtd/GirConstant.d',
9 | 'source/gtd/GirEnum.d',
10 | 'source/gtd/GirField.d',
11 | 'source/gtd/GirFunction.d',
12 | 'source/gtd/GirPackage.d',
13 | 'source/gtd/GirStruct.d',
14 | 'source/gtd/GirType.d',
15 | 'source/gtd/GirVersion.d',
16 | 'source/gtd/GirWrapper.d',
17 | 'source/gtd/IndentedStringBuilder.d',
18 | 'source/gtd/Log.d',
19 | 'source/gtd/LinkedHasMap.d',
20 | 'source/gtd/WrapException.d',
21 | 'source/gtd/XMLReader.d'
22 | ]
23 |
24 | sources_dir = include_directories('source/')
25 |
26 | version = vcs_tag(command: ['git', 'describe', '--dirty=+', '--tags'], input: 'VERSION.in', output: 'VERSION')
27 | # d_import_dirs was added in meson 0.43 for now add -J manually.
28 | add_project_arguments('-J'+meson.current_build_dir(), language: 'd')
29 |
30 | executable(
31 | 'girtod',
32 | [source, version],
33 | include_directories : [sources_dir],
34 | # d_import_dirs : meson.build_root(),
35 | install : true
36 | )
37 |
--------------------------------------------------------------------------------
/GNUmakefile:
--------------------------------------------------------------------------------
1 | SHELL=/bin/sh
2 | OS=$(shell uname || uname -s)
3 | ARCH=$(shell arch || uname -m)
4 |
5 | GIR_TO_D_VERSION=v0.23.1
6 |
7 | ifndef DC
8 | ifneq ($(strip $(shell which dmd 2>/dev/null)),)
9 | DC=dmd
10 | else ifneq ($(strip $(shell which ldc 2>/dev/null)),)
11 | DC=ldc
12 | else ifneq ($(strip $(shell which ldc2 2>/dev/null)),)
13 | DC=ldc2
14 | else
15 | DC=gdc
16 | endif
17 | endif
18 |
19 | ifeq ("$(DC)","gdc")
20 | DCFLAGS=-O2
21 | INCLUDEFLAG=-J
22 | LINKERFLAG=-Xlinker
23 | DDOCFLAGS=-fsyntax-only -c -fdoc -fdoc-file=$@
24 | DDOCINC=-fdoc-inc=
25 | output=-o $@
26 | else
27 | DCFLAGS=-O
28 | INCLUDEFLAG=-J
29 | LINKERFLAG=-L
30 | DDOCFLAGS=-o- -Df$@
31 | output=-of$@
32 | endif
33 |
34 | #######################################################################
35 |
36 | .DEFAULT_GOAL = $(BINNAME)
37 |
38 | SOURCES = $(wildcard source/*.d) $(wildcard source/gtd/*.d)
39 | BINNAME = girtod
40 |
41 | $(BINNAME): VERSION $(SOURCES)
42 | $(DC) $(filter-out $<,$^) $(output) $(INCLUDEFLAG)./ $(DCFLAGS) $(LDFLAGS)
43 | rm -f *.o
44 |
45 | VERSION: VERSION.in
46 | sed 's/@VCS_TAG@/$(shell git describe --dirty=+ --tags || echo $(GIR_TO_D_VERSION))/g' $< > $@
47 |
48 | clean:
49 | -rm -f $(BINNAME)
50 | -rm -f VERSION
51 |
52 |
--------------------------------------------------------------------------------
/source/gtd/GirAlias.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.GirAlias;
19 |
20 | import std.string : splitLines, strip;
21 |
22 | import gtd.GirType;
23 | import gtd.GirWrapper;
24 | import gtd.Log;
25 | import gtd.XMLReader;
26 |
27 | final class GirAlias
28 | {
29 | string name;
30 | string cType;
31 | string doc;
32 |
33 | GirType baseType;
34 | GirWrapper wrapper;
35 |
36 | this(GirWrapper wrapper)
37 | {
38 | this.wrapper = wrapper;
39 | }
40 |
41 | void parse(T)(XMLReader!T reader)
42 | {
43 | name = reader.front.attributes["name"];
44 | cType = reader.front.attributes["c:type"];
45 |
46 | reader.popFront();
47 |
48 | while( !reader.empty && !reader.endTag("alias") )
49 | {
50 | switch(reader.front.value)
51 | {
52 | case "type":
53 | baseType = new GirType(wrapper);
54 | baseType.parse(reader);
55 | break;
56 | case "doc":
57 | reader.popFront();
58 | doc ~= reader.front.value;
59 | reader.popFront();
60 | break;
61 | case "doc-deprecated":
62 | reader.popFront();
63 | doc ~= "\n\nDeprecated: "~ reader.front.value;
64 | reader.popFront();
65 | break;
66 | case "source-position":
67 | reader.skipTag();
68 | break;
69 | default:
70 | warning("Unexpected tag: ", reader.front.value, " in GirAlias: ", name, reader);
71 | }
72 | reader.popFront();
73 | }
74 | }
75 |
76 | string[] getAliasDeclaration()
77 | {
78 | string[] buff;
79 | if ( doc !is null && wrapper.includeComments )
80 | {
81 | buff ~= "/**";
82 | foreach ( line; doc.splitLines() )
83 | buff ~= " * "~ line.strip();
84 | buff ~= " */";
85 | }
86 |
87 | buff ~= "public alias "~ stringToGtkD(baseType.cType, wrapper.aliasses) ~" "~ tokenToGtkD(cType, wrapper.aliasses) ~";";
88 |
89 | return buff;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/source/gtd/GirConstant.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.GirConstant;
19 |
20 | import std.algorithm : among, canFind;
21 | import std.array : replace;
22 | import std.string : splitLines, strip;
23 |
24 | import gtd.GirPackage;
25 | import gtd.GirType;
26 | import gtd.GirWrapper;
27 | import gtd.Log;
28 | import gtd.XMLReader;
29 |
30 | final class GirConstant
31 | {
32 | string name;
33 | string cType;
34 | string value;
35 | string doc;
36 |
37 | GirType type;
38 | GirPackage pack;
39 | GirWrapper wrapper;
40 |
41 | this(GirWrapper wrapper, GirPackage pack)
42 | {
43 | this.wrapper = wrapper;
44 | this.pack = pack;
45 | }
46 |
47 | void parse(T)(XMLReader!T reader)
48 | {
49 | name = reader.front.attributes["name"];
50 | value = reader.front.attributes["value"];
51 | if ( "c:type" in reader.front.attributes )
52 | cType = reader.front.attributes["c:type"];
53 | else
54 | cType = reader.front.attributes["c:identifier"];
55 |
56 | reader.popFront();
57 |
58 | while( !reader.empty && !reader.endTag("constant") )
59 | {
60 | switch(reader.front.value)
61 | {
62 | case "type":
63 | case "array":
64 | type = new GirType(wrapper);
65 | type.parse(reader);
66 | break;
67 | case "doc":
68 | reader.popFront();
69 | doc ~= reader.front.value;
70 | reader.popFront();
71 | break;
72 | case "doc-deprecated":
73 | reader.popFront();
74 | doc ~= "\n\nDeprecated: "~ reader.front.value;
75 | reader.popFront();
76 | break;
77 | case "source-position":
78 | reader.skipTag();
79 | break;
80 | default:
81 | warning("Unexpected tag: ", reader.front.value, " in GirConstant: ", name, reader);
82 | }
83 | reader.popFront();
84 | }
85 |
86 | if ( value.canFind("\\") )
87 | value = value.replace("\\", "\\\\");
88 |
89 | if ( type.cType.among("gint64", "guint64") )
90 | // See dmd issue 8929 for why we use UL for signed longs. https://issues.dlang.org/show_bug.cgi?id=8929#c7
91 | value ~= "UL";
92 | }
93 |
94 | string[] getConstantDeclaration()
95 | {
96 | string[] buff;
97 | if ( doc !is null && wrapper.includeComments )
98 | {
99 | buff ~= "/**";
100 | foreach ( line; doc.splitLines() )
101 | buff ~= " * "~ line.strip();
102 | buff ~= " */";
103 | }
104 |
105 | if ( type.name in pack.collectedAliases && pack.collectedAliases[type.name].baseType.cType.among("gint64", "guint64") )
106 | value ~= "UL";
107 |
108 | if ( type.isString() )
109 | buff ~= "enum "~ name ~" = \""~ value ~"\";";
110 | else
111 | buff ~= "enum "~ name ~" = "~ value ~";";
112 |
113 | buff ~= "alias "~ cType ~" = "~ name ~";";
114 |
115 | return buff;
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/source/gtd/IndentedStringBuilder.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.IndentedStringBuilder;
19 |
20 | import std.algorithm: canFind, count, startsWith, endsWith;
21 | import std.range: empty;
22 | import std.string: strip;
23 |
24 | /** Keeps track of indentation level while building up a string */
25 | public class IndentedStringBuilder
26 | {
27 | string tabs;
28 | bool statement;
29 | int paramList = 0;
30 |
31 | this()
32 | {
33 | this("");
34 | }
35 |
36 | this(string t)
37 | {
38 | tabs = t;
39 | }
40 |
41 | /**
42 | * Formats the input line while keeping track of indentation.
43 | * Params:
44 | * lines = The lines to format
45 | */
46 | public string format(string line)
47 | {
48 | string text;
49 | line = line.strip();
50 |
51 | if ( (endsWith(line, '{') && !startsWith(line, "}")) || endsWith(line, "(") )
52 | {
53 | statement = false;
54 | }
55 |
56 | //Don't change the indentation when the line is a comment.
57 | if ( startsWith(line, '*') )
58 | {
59 | return tabs ~" "~ line ~ "\n";
60 | }
61 |
62 | if ( endsWith(line, "}", "};") || startsWith(line, "}", "};") || line == "));" || line == "connectFlags);" || (paramList > 0 && endsWith(line, ");", ")") && count(line, '(') != count(line, ')')) )
63 | {
64 | if ( !canFind(line, '{') && tabs.length > 0 )
65 | tabs.length = tabs.length -1;
66 |
67 | if ( line == "connectFlags);" )
68 | statement = true;
69 |
70 | if ( endsWith(line, ");") && !endsWith(line, "));") && line != ");" )
71 | statement = true;
72 |
73 | if ( paramList > 0 )
74 | paramList--;
75 | }
76 |
77 | if ( line.empty )
78 | {
79 | return "\n";
80 | }
81 | else if ( startsWith(line, "&&", "||") )
82 | {
83 | text = tabs ~"\t"~ line ~"\n";
84 | }
85 | else if ( statement )
86 | {
87 | text = tabs ~"\t"~ line ~"\n";
88 | statement = false;
89 | }
90 | else
91 | {
92 | text = tabs ~ line ~"\n";
93 | }
94 |
95 | if ( startsWith(line, "if", "else", "static if","version", "debug", "do", "while") && !endsWith(line, "}", ";") )
96 | {
97 | statement = true;
98 | }
99 | else if ( (endsWith(line, '{') && !startsWith(line, "}")) )
100 | {
101 | tabs ~= '\t';
102 | }
103 | else if ( endsWith(line, "(") )
104 | {
105 | tabs ~= '\t';
106 | paramList++;
107 | }
108 |
109 | return text;
110 | }
111 |
112 | /**
113 | * Formats the input lines while keeping track of indentation
114 | * Params:
115 | * lines = The lines to format
116 | */
117 | public string format(string[] lines)
118 | {
119 | string text = "";
120 | foreach(string line ; lines )
121 | text ~= format(line);
122 | return text;
123 | }
124 |
125 | public void setIndent(string t)
126 | {
127 | tabs = t;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/source/gtd/GirVersion.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.GirVersion;
19 |
20 | import std.array;
21 | import std.conv;
22 | import std.format;
23 |
24 | struct GirVersion
25 | {
26 | uint major;
27 | uint minor;
28 | uint micro;
29 |
30 | this(string _version)
31 | {
32 | parse(_version);
33 | }
34 |
35 | this(uint major, uint minor, uint micro = 0)
36 | {
37 | this.major = major;
38 | this.minor = minor;
39 | this.micro = micro;
40 | }
41 |
42 | void parse(string _version)
43 | {
44 | string[] parts = split(_version, ".");
45 |
46 | if ( parts.length >= 1 && !parts[0].empty )
47 | major = to!uint(parts[0]);
48 | if ( parts.length >= 2 && !parts[1].empty )
49 | minor = to!uint(parts[1]);
50 | if ( parts.length >= 3 && !parts[2].empty )
51 | micro = to!uint(parts[2]);
52 | }
53 |
54 | string toString() const
55 | {
56 | return format("%s.%s.%s", major, minor, micro);
57 | }
58 |
59 | void toString(scope void delegate(const(char)[]) sink) const
60 | {
61 | sink(to!string(major));
62 | sink(".");
63 | sink(to!string(minor));
64 | sink(".");
65 | sink(to!string(micro));
66 | }
67 |
68 | bool opEquals()(auto ref const GirVersion _version) const
69 | {
70 | if ( major != _version.major )
71 | return false;
72 | else if ( minor != _version.minor )
73 | return false;
74 | else if ( micro != _version.micro )
75 | return false;
76 |
77 | return true;
78 | }
79 |
80 | bool opEquals()(auto ref string _version) const
81 | {
82 | string[] parts = split(_version, ".");
83 |
84 | if ( parts.length >= 1 && !parts[0].empty )
85 | {
86 | uint maj = to!uint(parts[0]);
87 |
88 | if ( major != maj )
89 | return false;
90 | }
91 | if ( parts.length >= 2 && !parts[1].empty )
92 | {
93 | uint min = to!uint(parts[1]);
94 |
95 | if ( minor != min )
96 | return false;
97 | }
98 | if ( parts.length >= 3 && !parts[2].empty )
99 | {
100 | uint mic = to!uint(parts[2]);
101 |
102 | if ( micro != mic )
103 | return false;
104 | }
105 |
106 | return true;
107 | }
108 |
109 | int opCmp()(auto ref const GirVersion _version) const
110 | {
111 | if ( major != _version.major )
112 | return major - _version.major;
113 | else if ( minor != _version.minor )
114 | return minor - _version.minor;
115 |
116 | return micro - _version.micro;
117 | }
118 |
119 | int opCmp()(auto ref string _version) const
120 | {
121 | string[] parts = split(_version, ".");
122 |
123 | if ( parts.length >= 1 && !parts[0].empty )
124 | {
125 | uint maj = to!uint(parts[0]);
126 |
127 | if ( major != maj )
128 | return major - maj;
129 | }
130 | if ( parts.length >= 2 && !parts[1].empty )
131 | {
132 | uint min = to!uint(parts[1]);
133 |
134 | if ( minor != min )
135 | return minor - min;
136 | }
137 | if ( parts.length >= 3 && !parts[2].empty )
138 | {
139 | uint mic = to!uint(parts[2]);
140 |
141 | return micro - mic;
142 | }
143 |
144 | return 0;
145 | }
146 | }
147 |
148 | unittest
149 | {
150 | auto v1 = GirVersion("1.2.3");
151 | auto v2 = GirVersion(2, 3);
152 |
153 | assert(v1.minor == 2);
154 | assert(v1 < v2);
155 | assert(v2 == GirVersion("2.3"));
156 | assert(v2 > GirVersion("1.2.3"));
157 |
158 | assert(v2 == "2.3");
159 | assert(v2 > "1.1.2");
160 | assert(v2 < "3.4");
161 | assert(v2 >= "2.3");
162 | }
163 |
--------------------------------------------------------------------------------
/source/gtd/Log.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.Log;
19 |
20 | import core.stdc.stdlib : exit;
21 |
22 | import std.stdio;
23 |
24 | void warning(Args...)(Args args)
25 | {
26 | static if ( is(typeof(args[$-1].fileName)) && is(typeof(args[$-1].lineNumber)) )
27 | {
28 | stderr.writef("%s %s(%s): ", Color.blue("Warning"), args[$-1].fileName, args[$-1].lineNumber);
29 | stderr.writeln(args[0..$-1]);
30 | }
31 | else
32 | {
33 | stderr.write(Color.blue("Warning"), ": ");
34 | stderr.writeln(args);
35 | }
36 | }
37 |
38 | void warningf(Args...)(Args args)
39 | {
40 | static if ( is(typeof(args[$-1].fileName)) && is(typeof(args[$-1].lineNumber)) )
41 | {
42 | stderr.writef("%s %s(%s): ", Color.blue("Warning"), args[$-1].fileName, args[$-1].lineNumber);
43 | stderr.writefln(args[0..$-1]);
44 | }
45 | else
46 | {
47 | stderr.write(Color.blue("Warning"), ": ");
48 | stderr.writefln(args);
49 | }
50 | }
51 |
52 | void error(Args...)(Args args)
53 | {
54 | static if ( is(typeof(args[$-1].fileName)) && is(typeof(args[$-1].lineNumber)) )
55 | {
56 | stderr.writef("%s %s(%s): ", Color.red("Error"), args[$-1].fileName, args[$-1].lineNumber);
57 | stderr.writeln(args[0..$-1]);
58 | }
59 | else
60 | {
61 | stderr.write(Color.red("Error"), ": ");
62 | stderr.writeln(args);
63 | }
64 |
65 | exit(1);
66 | }
67 |
68 | void errorf(Args...)(Args args)
69 | {
70 | static if ( is(typeof(args[$-1].fileName)) && is(typeof(args[$-1].lineNumber)) )
71 | {
72 | stderr.writef("%s %s(%s): ", Color.red("Error"), args[$-1].fileName, args[$-1].lineNumber);
73 | stderr.writefln(args[0..$-1]);
74 | }
75 | else
76 | {
77 | stderr.write(Color.red("Error"), ": ");
78 | stderr.writefln(args);
79 | }
80 |
81 | exit(1);
82 | }
83 |
84 | struct Color
85 | {
86 | string esc;
87 | string text;
88 | string reset;
89 |
90 | private static bool _useColor;
91 | private static bool supportsColor;
92 |
93 | static this()
94 | {
95 | version(Windows)
96 | {
97 | import core.sys.windows.winbase: GetStdHandle, STD_ERROR_HANDLE;
98 | import core.sys.windows.wincon: GetConsoleMode, SetConsoleMode;
99 | import core.sys.windows.windef: DWORD, HANDLE;
100 |
101 | if ( !isatty(stderr.fileno()) )
102 | return;
103 |
104 | DWORD dwMode;
105 | HANDLE err = GetStdHandle(STD_ERROR_HANDLE);
106 |
107 | if ( !GetConsoleMode(err, &dwMode) )
108 | return;
109 |
110 | //ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
111 | dwMode |= 0x0004;
112 |
113 | //Try to set VT100 support on Windows 10.
114 | if ( !SetConsoleMode(err, dwMode) )
115 | return;
116 |
117 | supportsColor = true;
118 | _useColor = true;
119 | }
120 | else version(Posix)
121 | {
122 | if ( !isatty(stderr.fileno()) )
123 | return;
124 |
125 | supportsColor = true;
126 | _useColor = true;
127 | }
128 | }
129 |
130 | void toString(scope void delegate(const(char)[]) sink) const
131 | {
132 | if ( _useColor )
133 | {
134 | sink(esc);
135 | sink(text);
136 | sink(reset);
137 | }
138 | else
139 | {
140 | sink(text);
141 | }
142 | }
143 |
144 | string toString() const
145 | {
146 | if ( _useColor )
147 | return esc ~ text ~ reset;
148 | else
149 | return text;
150 | }
151 |
152 | void useColor(bool val)
153 | {
154 | if ( supportsColor )
155 | _useColor = val;
156 | }
157 |
158 | static Color red(string text)
159 | {
160 | return Color("\033[1;31m", text, "\033[m");
161 | }
162 |
163 | static Color blue(string text)
164 | {
165 | return Color("\033[1;34m", text, "\033[m");
166 | }
167 | }
168 |
169 | extern(C) private int isatty(int);
170 |
--------------------------------------------------------------------------------
/source/gtd/GlibTypes.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.GlibTypes;
19 |
20 | enum string[string] glibTypes = [
21 | "volatile": "",
22 | "G_CONST_RETURN": "",
23 | "gint": "int",
24 | "guint": "uint",
25 | "gboolean": "bool",
26 | "_Bool": "bool",
27 | "gpointer": "void*",
28 | "gconstpointer": "void*",
29 | "gchar": "char",
30 | "guchar": "char",
31 | "gshort": "short",
32 | "gushort": "ushort",
33 | "gint8": "byte",
34 | "guint8": "ubyte",
35 | "gint16": "short",
36 | "guint16": "ushort",
37 | "gint32": "int",
38 | "gint64": "long",
39 | "guint32": "uint",
40 | "guint64": "ulong",
41 | "guintptr": "size_t",
42 | "gfloat": "float",
43 | "gdouble": "double",
44 | "greal": "real",
45 | "goffset": "long",
46 | "gsize": "size_t",
47 | "gssize": "ptrdiff_t",
48 | "gintptr": "ptrdiff_t",
49 | "va_list": "void*",
50 | "unichar": "dchar",
51 | "unichar2": "wchar",
52 | "uchar": "ubyte",
53 | "XID": "uint",
54 |
55 | "gunichar": "dchar",
56 | "gunichar2": "wchar",
57 |
58 | "time_t": "uint",
59 | "uid_t": "uid_t",
60 |
61 | "alias": "alias_",
62 | "align": "align_",
63 | "body": "body_",
64 | "cast": "cast_",
65 | "continue": "continue_",
66 | "debug": "debug_",
67 | "default": "default_",
68 | "delete": "delete_",
69 | "deprecated": "deprecated_",
70 | "export": "export_",
71 | "for": "for_",
72 | "foreach": "foreach_",
73 | "function": "function_",
74 | "Function": "Function_",
75 | "in": "in_",
76 | "instance": "instance_",
77 | "interface": "interface_",
78 | "module": "module_",
79 | "new": "new_",
80 | "out": "out_",
81 | "package": "package_",
82 | "real": "real_",
83 | "ref": "ref_",
84 | "scope": "scope_",
85 | "string": "string_",
86 | "switch": "switch_",
87 | "this": "this_",
88 | "union": "union_",
89 | "version": "version_",
90 | "byte": "byte_",
91 | "shared": "shared_",
92 |
93 | "GLIB_SYSDEF_POLLIN": "=1",
94 | "GLIB_SYSDEF_POLLOUT": "=4",
95 | "GLIB_SYSDEF_POLLPRI": "=2",
96 | "GLIB_SYSDEF_POLLHUP": "=16",
97 | "GLIB_SYSDEF_POLLERR": "=8",
98 | "GLIB_SYSDEF_POLLNVAL": "=32",
99 | ];
100 |
101 | /**
102 | * Set some defaults for the basic libraries.
103 | */
104 | enum string[][string] defaultLookupText = [
105 | "Atk": [
106 | "struct: Implementor",
107 | "interface: Implementor",
108 | "merge: ImplementorIface"
109 | ],
110 | "cairo": [
111 | "struct: Context",
112 | "class: Context",
113 | "struct: Surface",
114 | "class: Surface",
115 | "struct: Matrix",
116 | "class: Matrix",
117 | "struct: Pattern",
118 | "class: Pattern",
119 | "struct: Region",
120 | "class: Region",
121 | "struct: FontOptions",
122 | "class: FontOption",
123 | "struct: FontFace",
124 | "class: FontFace",
125 | "struct: ScaledFont",
126 | "class: ScaledFont"
127 | ],
128 | "Gdk": [
129 | "struct: Atom",
130 | "namespace:",
131 | "struct: Monitor",
132 | "class: MonitorG",
133 | "struct: Rectangle",
134 | "noCode: get_type",
135 | "namespace:"
136 | ],
137 | "GLib": [
138 | "struct: Array",
139 | "class: ArrayG",
140 | "struct: ByteArray",
141 | "class: ByteArray",
142 | "struct: Error",
143 | "class: ErrorG",
144 | "struct: HashTable",
145 | "class: HashTable",
146 | "struct: List",
147 | "class: ListG",
148 | "struct: SList",
149 | "class: ListSG",
150 | "struct: MarkupParseContext",
151 | "class: SimpleXML",
152 | "struct: PtrArray",
153 | "class: PtrArray",
154 | "struct: Scanner",
155 | "class: ScannerG",
156 | "struct: String",
157 | "class: StringG",
158 | "struct: Tree",
159 | "class: BBTree"
160 | ],
161 | "GModule": [
162 | "wrap: glib"
163 | ],
164 | "Pango": [
165 | "struct: AttrList",
166 | "class: PgAttributeList"
167 | ],
168 | "Gst": [
169 | "wrap: gstreamer"
170 | ]
171 | ];
172 |
173 |
--------------------------------------------------------------------------------
/source/gtd/DefReader.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.DefReader;
19 |
20 | import std.algorithm;
21 | import std.array;
22 | import std.conv : hexString;
23 | import std.file;
24 | import std.string : splitLines, strip, indexOf;
25 |
26 | import gtd.WrapException;
27 |
28 | public final class DefReader
29 | {
30 | string fileName;
31 | string key;
32 | string subKey;
33 | string value;
34 |
35 | private size_t _lineNumber;
36 | private size_t lineOffset;
37 | private string[] lines;
38 |
39 | public this(string fileName)
40 | {
41 | this.fileName = fileName;
42 |
43 | lines = readText(fileName).splitLines();
44 | //Skip utf8 BOM.
45 | lines[0].skipOver(hexString!"efbbbf");
46 |
47 | this.popFront();
48 | }
49 |
50 | /**
51 | * Proccess the _lines defined in lines.
52 | * The fileName and lineOffset are only used for error reporting.
53 | */
54 | public this(string[] lines, string fileName = "", size_t lineOffset = 0)
55 | {
56 | this.lines = lines;
57 | this.fileName = fileName;
58 | this.lineOffset = lineOffset;
59 | this.popFront();
60 | }
61 |
62 | public void popFront()
63 | {
64 | string line;
65 |
66 | if ( !lines.empty )
67 | {
68 | line = lines.front.strip();
69 | lines.popFront();
70 | _lineNumber++;
71 |
72 | while ( !lines.empty && ( line.empty || line.startsWith("#") ) )
73 | {
74 | line = lines.front.strip();
75 | lines.popFront();
76 | _lineNumber++;
77 | }
78 | }
79 |
80 | if ( !line.empty && !line.startsWith("#") )
81 | {
82 | ptrdiff_t index = line.indexOf(':');
83 |
84 | key = line[0 .. max(index, 0)].strip();
85 | value = line[index +1 .. $].strip();
86 | subKey = "";
87 |
88 | index = key.indexOf(' ');
89 | if ( index != -1 )
90 | {
91 | subKey = key[index +1 .. $].strip();
92 | key = key[0 .. index].strip();
93 | }
94 | }
95 | else
96 | {
97 | key.length = 0;
98 | value.length = 0;
99 | subKey.length = 0;
100 | }
101 | }
102 |
103 | /**
104 | * Gets the content of a block value
105 | */
106 | public string[] readBlock(string key = "")
107 | {
108 | string[] block;
109 |
110 | if ( key.empty )
111 | key = this.key;
112 |
113 | while ( !lines.empty )
114 | {
115 | if ( startsWith(lines.front.strip(), key) )
116 | {
117 | lines.popFront();
118 | _lineNumber++;
119 | return block;
120 | }
121 |
122 | block ~= lines.front ~ '\n';
123 | lines.popFront();
124 | _lineNumber++;
125 | }
126 |
127 | throw new LookupException(this, "Found EOF while expecting: \""~key~": end\"");
128 | }
129 |
130 | /**
131 | * Skip the content of a block. Supports nested blocks.
132 | */
133 | public void skipBlock(string key = "")
134 | {
135 | if ( key.empty )
136 | key = this.key;
137 |
138 | size_t nestedBlocks = 1;
139 | do
140 | {
141 | do lines.popFront; while ( !lines.front.strip().startsWith(key) );
142 |
143 | if ( lines.front.strip().endsWith("start") )
144 | nestedBlocks++;
145 | else if ( lines.front.strip().endsWith("end") )
146 | nestedBlocks--;
147 | }
148 | while ( nestedBlocks > 0 );
149 | }
150 |
151 | /**
152 | * Gets the current value as a bool
153 | */
154 | public @property bool valueBool() const
155 | {
156 | return !!value.among("1", "ok", "OK", "Ok", "true", "TRUE", "True", "Y", "y", "yes", "YES", "Yes");
157 | }
158 |
159 | public @property bool empty() const
160 | {
161 | return lines.empty && key.empty;
162 | }
163 |
164 | public @property size_t lineNumber() const
165 | {
166 | return _lineNumber + lineOffset;
167 | }
168 | }
169 |
170 | class LookupException : WrapException
171 | {
172 | this(DefReader defReader, string msg)
173 | {
174 | super(msg, defReader.fileName, defReader.lineNumber);
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/source/girtod.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module girtod;
19 |
20 | import std.algorithm: canFind, find, findSkip, startsWith;
21 | import std.array;
22 | import std.file : exists, getcwd, isFile;
23 | import std.getopt;
24 | import std.path;
25 | import std.stdio;
26 | import core.stdc.stdlib;
27 |
28 | import gtd.GirWrapper;
29 | import gtd.Log;
30 | import gtd.WrapException;
31 |
32 | void main(string[] args)
33 | {
34 | bool printFree;
35 | string lookupFile = "APILookup.txt";
36 |
37 | GirWrapper wrapper = new GirWrapper("./", "./out");
38 | Option printFilesOption;
39 |
40 | wrapper.cwdOrBaseDirectory = getcwd();
41 |
42 | printFilesOption.optLong = "--print-files";
43 | printFilesOption.help = "Write a newline separated list of generated files to stdout. Optionally you can pass 'relative[,/base/path] or 'full' to force printing the relative or full paths of the files.";
44 |
45 | auto helpInformation = getopt(
46 | args,
47 | std.getopt.config.passThrough,
48 | "input|i", "Directory containing the API description. Or a lookup file (Default: ./)", &wrapper.inputDir,
49 | "output|o", "Output directory for the generated binding. (Default: ./out)", &wrapper.outputDir,
50 | "use-runtime-linker", "Link the gtk functions with the runtime linker.", &wrapper.useRuntimeLinker,
51 | "gir-directory|g", "Directory to search for gir files before the system directory.", &wrapper.commandlineGirPath,
52 | "print-free", "Print functions that don't have a parent module.", &printFree,
53 | "use-bind-dir", "Include public imports for the old gtkc package.", &wrapper.useBindDir,
54 | "version", "Print the version and exit", (){ writeln("GIR to D ", import("VERSION")); exit(0); }
55 | );
56 |
57 | if (helpInformation.helpWanted)
58 | {
59 | defaultGetoptPrinter("girtod is an utility that generates D bindings using the GObject introspection files.\n\nOptions:",
60 | helpInformation.options ~ printFilesOption);
61 | exit(0);
62 | }
63 |
64 | if ( args.length > 1 )
65 | handlePrintFiles(args, wrapper);
66 |
67 | try
68 | {
69 | //Read in the GIR and API files.
70 | if ( wrapper.inputDir.extension == ".gir" )
71 | {
72 | wrapper.proccessGIR(wrapper.inputDir);
73 | }
74 | else
75 | {
76 | if ( wrapper.inputDir.exists && wrapper.inputDir.isFile() )
77 | {
78 | lookupFile = wrapper.inputDir.baseName();
79 | wrapper.inputDir = wrapper.inputDir.dirName();
80 | }
81 | wrapper.proccess(lookupFile);
82 | }
83 |
84 | if ( printFree )
85 | wrapper.printFreeFunctions();
86 |
87 | //Generate the D binding
88 | foreach(pack; wrapper.packages)
89 | {
90 | if ( pack.name == "cairo" )
91 | continue;
92 |
93 | if ( wrapper.useRuntimeLinker )
94 | pack.writeLoaderTable();
95 | else
96 | pack.writeExternalFunctions();
97 |
98 | pack.writeTypes();
99 | pack.writeClasses();
100 | }
101 | }
102 | catch (WrapException ex)
103 | {
104 | error(ex);
105 | }
106 | }
107 |
108 | void handlePrintFiles(string[] args, GirWrapper wrapper)
109 | {
110 | string value;
111 |
112 | args.popFront();
113 |
114 | if ( args.front.startsWith("--print-files") )
115 | {
116 | if ( args.front.findSkip("=") )
117 | {
118 | value = args.front;
119 | }
120 |
121 | args.popFront();
122 |
123 | if ( value.empty && !args.empty && !args.front.startsWith("--") )
124 | {
125 | value = args.front;
126 | args.popFront();
127 | }
128 | }
129 |
130 | if ( !args.empty )
131 | {
132 | writeln("Unable to parse parameters: Unrecognized option ", args.front);
133 | exit(0);
134 | }
135 |
136 | wrapper.printFiles = true;
137 |
138 | if ( value == "absolute" || value == "full" )
139 | {
140 | wrapper.printFileMethod = PrintFileMethod.Absolute;
141 | }
142 | else if ( value.startsWith("relative") )
143 | {
144 | wrapper.printFileMethod = PrintFileMethod.Relative;
145 |
146 | if ( value.findSkip(",") )
147 | wrapper.cwdOrBaseDirectory = value;
148 |
149 | if ( !isAbsolute(wrapper.cwdOrBaseDirectory) )
150 | error("The base directory passed to relative must be absolute.");
151 | }
152 | else if ( !value.empty )
153 | {
154 | error("Unknown option: '", value, "' for print-files.");
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/source/gtd/GirEnum.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.GirEnum;
19 |
20 | import std.array : split;
21 | import std.algorithm;
22 | import std.range : back, empty;
23 | import std.string : splitLines, strip, toUpper;
24 | import std.uni : isNumber;
25 |
26 | import gtd.GirPackage;
27 | import gtd.GirWrapper;
28 | import gtd.Log;
29 | import gtd.XMLReader;
30 |
31 | final class GirEnum
32 | {
33 | string name;
34 | string cName;
35 | string libVersion;
36 | string doc;
37 |
38 | GirEnumMember[] members;
39 | GirWrapper wrapper;
40 | GirPackage pack;
41 |
42 | this(GirWrapper wrapper, GirPackage pack)
43 | {
44 | this.wrapper = wrapper;
45 | this.pack = pack;
46 | }
47 |
48 | void parse(T)(XMLReader!T reader)
49 | {
50 | name = reader.front.attributes["name"];
51 | cName = reader.front.attributes["c:type"];
52 |
53 | if ( "version" in reader.front.attributes )
54 | libVersion = reader.front.attributes["version"];
55 | reader.popFront();
56 |
57 | while ( !reader.empty && !reader.endTag("bitfield", "enumeration") )
58 | {
59 | switch (reader.front.value)
60 | {
61 | case "doc":
62 | reader.popFront();
63 | doc ~= reader.front.value;
64 | reader.popFront();
65 | break;
66 | case "doc-deprecated":
67 | reader.popFront();
68 | doc ~= "\n\nDeprecated: "~ reader.front.value;
69 | reader.popFront();
70 | break;
71 | case "member":
72 | if ( reader.front.attributes["name"].startsWith("2bu", "2bi", "3bu") )
73 | {
74 | reader.skipTag();
75 | break;
76 | }
77 |
78 | GirEnumMember member = GirEnumMember(wrapper);
79 | member.parse(reader);
80 | members ~= member;
81 | break;
82 | case "function":
83 | //Skip these functions for now
84 | //as they are also availabe as global functions.
85 | //pack.parseFunction(reader);
86 | reader.skipTag();
87 | break;
88 | case "source-position":
89 | reader.skipTag();
90 | break;
91 | default:
92 | warning("Unexpected tag: ", reader.front.value, " in GirEnum: ", name, reader);
93 | }
94 | reader.popFront();
95 | }
96 | }
97 |
98 | string[] getEnumDeclaration()
99 | {
100 | string[] buff;
101 | if ( doc !is null && wrapper.includeComments )
102 | {
103 | buff ~= "/**";
104 | foreach ( line; doc.splitLines() )
105 | buff ~= " * "~ line.strip();
106 |
107 | if ( libVersion )
108 | {
109 | buff ~= " *";
110 | buff ~= " * Since: "~ libVersion;
111 | }
112 |
113 | buff ~= " */";
114 | }
115 |
116 | buff ~= "public enum "~ cName ~(name.among("ParamFlags", "MessageType") ? " : uint" : "");
117 | buff ~= "{";
118 |
119 | foreach ( member; members )
120 | {
121 | buff ~= member.getEnumMemberDeclaration();
122 | }
123 |
124 | buff ~= "}";
125 | if ( name !is null && pack.name.among("glgdk", "glgtk") )
126 | buff ~= "alias "~ cName ~" GL"~ name ~";";
127 | else if ( name !is null && pack.name != "pango" )
128 | buff ~= "alias "~ cName ~" "~ name ~";";
129 |
130 | return buff;
131 | }
132 | }
133 |
134 | struct GirEnumMember
135 | {
136 | string name;
137 | string value;
138 | string doc;
139 |
140 | GirWrapper wrapper;
141 |
142 | @disable this();
143 |
144 | this(GirWrapper wrapper)
145 | {
146 | this.wrapper = wrapper;
147 | }
148 |
149 | void parse(T)(XMLReader!T reader)
150 | {
151 | name = reader.front.attributes["name"];
152 | value = reader.front.attributes["value"];
153 |
154 | if ( name.empty )
155 | name = reader.front.attributes["c:identifier"].split("_").back;
156 |
157 | if ( reader.front.type == XMLNodeType.EmptyTag )
158 | return;
159 |
160 | reader.popFront();
161 |
162 | while ( !reader.empty && !reader.endTag("member", "constant") )
163 | {
164 | switch (reader.front.value)
165 | {
166 | case "doc":
167 | reader.popFront();
168 | doc ~= reader.front.value;
169 | reader.popFront();
170 | break;
171 | case "doc-deprecated":
172 | reader.popFront();
173 | doc ~= "\n\nDeprecated: "~ reader.front.value;
174 | reader.popFront();
175 | break;
176 | case "source-position":
177 | reader.skipTag();
178 | break;
179 | case "type":
180 | if ( reader.front.attributes["name"] == "utf8" )
181 | value = "\""~ value ~"\"";
182 | break;
183 | default:
184 | warning("Unexpected tag: ", reader.front.value, " in GirEnumMember: ", name, reader);
185 | }
186 | reader.popFront();
187 | }
188 | }
189 |
190 | string[] getEnumMemberDeclaration()
191 | {
192 | string[] buff;
193 | if ( doc !is null && wrapper.includeComments )
194 | {
195 | buff ~= "/**";
196 | foreach ( line; doc.splitLines() )
197 | buff ~= " * "~ line.strip();
198 | buff ~= " */";
199 | }
200 |
201 | if ( name[0].isNumber && name !in wrapper.aliasses )
202 | buff ~= "_"~ name.toUpper() ~" = "~ value ~",";
203 | else
204 | buff ~= tokenToGtkD(name.toUpper(), wrapper.aliasses, false) ~" = "~ value ~",";
205 |
206 | return buff;
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/source/gtd/GirType.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.GirType;
19 |
20 | import std.algorithm: among, canFind, startsWith;
21 | import std.array: replace, split;
22 | import std.conv: to;
23 | import std.range: empty;
24 |
25 | import gtd.GirWrapper;
26 | import gtd.XMLReader;
27 |
28 | /**
29 | * Represent sthe type of an field or a parameter.
30 | */
31 | final class GirType
32 | {
33 | string name;
34 | string cType;
35 | string dType;
36 | string doc;
37 | bool constType;
38 |
39 | int size = -1; /// The size of a fixed size array.
40 | int length = -1; /// The index of the param representing the length, not counting the instance param.
41 | bool zeroTerminated; /// Is this array zero-terminated.
42 | bool girArray = false; /// The gir file specifies this as an array. Use isArray to check if this is actually an array.
43 | GirType elementType; /// The type of the array elements, also set for Glib.List, Glib.SList Glib.Array and GLib.HashTable.
44 | GirType keyType; /// The key type of a HashTable;
45 |
46 | GirWrapper wrapper;
47 |
48 | this(GirWrapper wrapper)
49 | {
50 | this.wrapper = wrapper;
51 | }
52 |
53 | void parse(T)(XMLReader!T reader)
54 | {
55 | if ( reader.front.value == "array" )
56 | girArray = true;
57 |
58 | if ( "c:type" in reader.front.attributes )
59 | cType = reader.front.attributes["c:type"];
60 | if ( "length" in reader.front.attributes )
61 | length = to!int(reader.front.attributes["length"]);
62 | if ( "zero-terminated" in reader.front.attributes )
63 | zeroTerminated = to!int(reader.front.attributes["zero-terminated"]) == 1;
64 | if ( "fixed-size" in reader.front.attributes )
65 | size = to!int(reader.front.attributes["fixed-size"]);
66 | if ( "name" in reader.front.attributes )
67 | name = reader.front.attributes["name"];
68 |
69 | if ( cType is null && name is null )
70 | {
71 | name = "none";
72 | }
73 |
74 | // Some GIR files have a none type name.
75 | if (name == "none") {
76 | cType = "void";
77 | }
78 |
79 | if ( cType.canFind(" const") || cType.canFind("const ") )
80 | {
81 | constType = true;
82 | fixType();
83 | }
84 |
85 | if ( cType.canFind("unsigned ") )
86 | {
87 | cType = cType.replace("unsigned ", "u");
88 | }
89 |
90 | if ( name == "long double" )
91 | {
92 | name = "greal";
93 | cType = "greal";
94 | }
95 |
96 | cType = cType.replace("volatile ", "");
97 |
98 | if ( cType == "unsigned" )
99 | cType = name;
100 |
101 | removeInitialyUnowned();
102 |
103 | if ( cType is null && (name == "filename" || name == "utf8") )
104 | cType = "gchar*";
105 |
106 | //Vala libraries can have the package name in there name twice, befoer and after the dot.
107 | string[] arr = split(name, '.');
108 | if ( arr.length > 1 && arr[1].startsWith(arr[0]) )
109 | {
110 | name = arr[1][arr[0].length .. $];
111 | }
112 |
113 | if ( reader.front.type == XMLNodeType.EmptyTag )
114 | return;
115 |
116 | reader.popFront();
117 |
118 | while ( !reader.empty && !reader.endTag("type", "array") )
119 | {
120 | if ( elementType )
121 | keyType = elementType;
122 |
123 | elementType = new GirType(wrapper);
124 | elementType.parse(reader);
125 |
126 | reader.popFront();
127 | }
128 |
129 | if ( cType == elementType.cType && !cType.among("void*", "gpointer", "gconstpointer") && size < 0 )
130 | cType ~= "*";
131 |
132 | if ( isArray() && (cType == "void" || cType.empty) )
133 | {
134 | if ( size > 0 )
135 | cType = elementType.cType;
136 | else
137 | cType = elementType.cType ~"*";
138 | }
139 | }
140 |
141 | bool isString()
142 | {
143 | if ( cType.startsWith("gchar*", "char*", "const(char)*") )
144 | return true;
145 | if ( name.among("utf8", "filename") )
146 | return true;
147 | if ( isArray() && elementType.cType.startsWith("gchar", "char", "const(char)") )
148 | return true;
149 |
150 | return false;
151 | }
152 |
153 | bool isArray()
154 | {
155 | if ( elementType is null )
156 | return false;
157 |
158 | // The GLib Arrays are in the GIR files as arrays but they shouldn't be wrapped as such.
159 | if ( name.among("GLib.Array", "Array", "GLib.ByteArray", "ByteArray", "GLib.PtrArray", "PtrArray") )
160 | return false;
161 |
162 | if ( girArray )
163 | return true;
164 |
165 | return false;
166 | }
167 |
168 | private void fixType()
169 | {
170 | if ( name == "utf8" && !cType.canFind("**") )
171 | {
172 | cType = "const(char)*";
173 | return;
174 | }
175 |
176 | cType = cType.replace("const ", "").replace(" const", "");
177 | }
178 |
179 | private void removeInitialyUnowned()
180 | {
181 | if ( name.among("GObject.InitiallyUnowned", "InitiallyUnowned") )
182 | {
183 | if ( name == "GObject.InitiallyUnowned" )
184 | name = "GObject.Object";
185 | else if ( name == "InitiallyUnowned" )
186 | name = "Object";
187 |
188 | if ( cType == "GInitiallyUnowned" )
189 | cType = "GObject";
190 | else if ( cType == "GInitiallyUnowned*" )
191 | cType = "GObject*";
192 | }
193 | else if ( name.among("GObject.InitiallyUnownedClass", "InitiallyUnownedClass") )
194 | {
195 | if ( name == "GObject.InitiallyUnownedClass" )
196 | name = "GObject.ObjectClass";
197 | else if ( name == "InitiallyUnownedClass" )
198 | name = "ObjectClass";
199 |
200 | if ( cType == "GInitiallyUnownedClass" )
201 | cType = "GObjectClass";
202 | else if ( cType == "GInitiallyUnownedClass*" )
203 | cType = "GObjectClass*";
204 | }
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/Readme_APILookup:
--------------------------------------------------------------------------------
1 | The GTK binding is generated by the girtod utility using APILookup
2 | files and the GIR files from GObject Introspection.
3 |
4 | The girtod utility parses APILookup.txt which tells it what to do/parse
5 | using the commands listed below.
6 | There to two types of commands key value pairs and block commands which are denoted with
7 | "command: start" and "command: end".
8 |
9 | Top level commands:
10 | -------------------
11 | Top level commands can only be set once and apply to everything.
12 |
13 | includeComments: (true/false)
14 | If true the generated code includes comments/documentation
15 | for the classes, functions, etc.
16 |
17 | license: (block)
18 | Sets the license text used at the top of the generated files.
19 |
20 | outputRoot: path
21 | Directory where the generated code is stored.
22 |
23 | Global commands:
24 | ----------------
25 | Global commands can be set more than once, but also affect everything.
26 | srcDir only affects the packages that come after it.
27 |
28 | alias: ctype dtype
29 | Replace ctype with dtype in the generated code.
30 |
31 | copy: filename
32 | Copies filename from inputRoot to outputRoot/srcDir.
33 |
34 | dependency DEP: skip
35 | When generating code don't assume dependency DEB is available.
36 |
37 | dependency DEP: (block)
38 | Apply the lookup definitions in this block to the dependency
39 | before generating any code.
40 |
41 | lookup: filename
42 | Continue parsing in filename.
43 |
44 | srcDir: directory
45 | The source for the following packages is placed in this directory.
46 |
47 | version X.XX: command: value
48 | Only include the command if the version specified in the Gir file is >= X.XX.
49 | An OS identifier can also be used as the version.
50 | Currently supported are: Windows, OSX, Linux and Posix.
51 | The OS versions can de negated.
52 |
53 | version X.XX: (block)
54 | The same as above but applies to all commands in the block.
55 | The version can optionaly be used at the end of the block.
56 |
57 | wrap: packageName
58 | Starts wrapping a package, and uses packageName as it's name.
59 | After this command and until wrap is used again, all commands
60 | only affect the current package.
61 |
62 | When inside a dependency wrap only sets the package name.
63 |
64 | Package commands:
65 | -----------------
66 | These commands apply to the package set with wrap.
67 |
68 | addAliases: (block)
69 | Add the contained text to the alias section of the c definitions.
70 |
71 | addConstants: (block)
72 | Add the contained text to the constant section of the c definitions.
73 |
74 | addEnums: (block)
75 | Add the contained text to the enum section of the c definitions.
76 |
77 | addFuncts: (block)
78 | Add the contained text to the function/callback section of the c definitions.
79 |
80 | addStructs: (block)
81 | Add the contained text to the struct section of the c definitions.
82 |
83 | file: girFilename.
84 | Parse the a GIR file, this command can be used multiple times
85 | to combine different GIR files. girtod will detect the gir directory.
86 |
87 | move: src dest [new_name]
88 | Move function src to struct dest. Optionaly set a new name for the function.
89 | dest will be created if it doesn't exist.
90 | You can list the global functions that haven't been moved by running
91 | girtod with the --print-free argument.
92 |
93 | noAlias: name
94 | Don't generate any code for alias name.
95 | name should be the name specified in the GIR file.
96 |
97 | noConstant: name
98 | Don't generate any code for constant name.
99 | name should be the name specified in the GIR file.
100 |
101 | noEnum: name
102 | Don't generate any code for enum name.
103 | name should be the name specified in the GIR file.
104 |
105 | noCallback: name
106 | Don't generate any code for callback name.
107 | name should be the name specified in the GIR file.
108 |
109 | struct: name
110 | Sets the struct to which the following commands apply.
111 | If the struct doesn't exist it will be created.
112 | The name can be left blank, in which case class and interface can
113 | be used to create a new class or interface.
114 |
115 | Struct commands:
116 | ----------------
117 | These commands apply to the struct set with struct.
118 | class and interface will create a struct if it is not set.
119 |
120 | alias: ctype dtype
121 | Replace ctype with dtype in the generated code for the current struct.
122 |
123 | array: field length_field
124 | Set length_field as the length for the field property.
125 | And handle field as an array and keep the length_field in sync with the actual length.
126 |
127 | class: name
128 | Overwrite the settings in the gir file and generate this struct as a class
129 | using the specified name.
130 | Or create a new class if the struct isn't set.
131 |
132 | code: (block)
133 | Add the contained code/text the generated struct/class.
134 |
135 | cType: type
136 | Override the cType used in the GIR file.
137 |
138 | extend: base_class
139 | Explicitly set the base class for this class. base_class should match the name used
140 | in the GIR files eg: GObject.Object.
141 |
142 | implements: interface
143 | Explicitly set an interface implemented by this class.
144 | This command can be used more than once.
145 |
146 | import: package.module
147 | Explicitly add imports to the generated file.
148 |
149 | interface: name
150 | Overwrite the settings in the gir file and generate this struct as a interface
151 | using the specified name.
152 | Or create a new interface if the struct isn't set.
153 |
154 | interfaceCode: (block)
155 | Add the contained code/text the generated interface.
156 |
157 | merge: name
158 | merge struct 'name' with the struct set by struct:.
159 |
160 | namespace: name
161 | Put the functions in a struct that is used as a namespace.
162 | name can be blank to generate the functions in module scope.
163 |
164 | noCode: name
165 | Don't generate any code for function name.
166 | name should be the name specified in the GIR file.
167 |
168 | noCode: true
169 | Don't generate any code for the struct set by struct:
170 |
171 | noExternal: true
172 | Don't generate the c declaration for the struct set by struct:
173 |
174 | noProperty: name
175 | Don't generate a property for field name.
176 | name should be the name specified in the GIR file.
177 |
178 | noSignal: name
179 | Don't generate any code for signal name.
180 | name should be the name specified in the GIR file.
181 |
182 | noStruct: true
183 | Don't generate the struct declaration for the struct set by struct:
184 |
185 | structWrap: ctype dtype
186 | Wrap ctype as dtype, instead of what's defined by the GIR files.
187 |
188 | Function commands:
189 | ------------------
190 | The Function commands apply to functions of the currently set struct.
191 | They generaly take the function they apply to as there first argument.
192 |
193 | array: func_name param_array [param_length]
194 | Override the GIR file and handle param_array of function func_name
195 | as an array whose length is specified by param_length.
196 | If param_length isn't specified the wrapper assumes the array is zero terminated.
197 | "Return" can be used for either param_array or param_length for when
198 | one of them is returned by the function.
199 |
200 | in: func_name param_name
201 | Override the GIR file and don't generate the specified
202 | parameter of func_name as out or ref.
203 |
204 | out: func_name param_name
205 | Override the GIR file and generate the specified
206 | parameter of func_name as out.
207 |
208 | override: func_name
209 | Explicitly add the override attribute to function func_name.
210 | func_name should be the name specified in the GIR file.
211 |
212 | ref: func_name param_name
213 | Override the GIR file and generate the specified
214 | parameter of func_name as ref.
215 |
216 | inout: func_name param_name
217 | Ditto, Deprecated.
218 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/source/gtd/LinkedHasMap.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.LinkedHasMap;
19 |
20 | import core.memory;
21 |
22 | /**
23 | * An hash map that allows iteration in the insertion-order.
24 | */
25 | struct LinkedHashMap(Key, Value)
26 | {
27 | static struct Node
28 | {
29 | Value val;
30 | Key key;
31 | Node* next;
32 | Node* previous;
33 | }
34 |
35 | private Node*[Key] data;
36 | private Node* front;
37 | private Node* back;
38 |
39 | /**
40 | * Looks up key, if it exsists returns the corresponding value
41 | * else evaluates and returns defaultValue.
42 | */
43 | inout(Value) get(Key key, lazy inout(Value) defaultValue = Value.init) inout pure @safe
44 | {
45 | if ( key !in data )
46 | return defaultValue;
47 |
48 | return data[key].val;
49 | }
50 |
51 | /**
52 | * remove(key) does nothing if the given key does not exist and returns false.
53 | * If the given key does exist, it removes it from the HashMap and returns true.
54 | */
55 | bool remove(Key key) pure nothrow @trusted
56 | {
57 | Node** nodeP = key in data;
58 |
59 | if ( nodeP is null )
60 | return false;
61 |
62 | Node* node = *nodeP;
63 |
64 | if ( node is front )
65 | front = node.next;
66 | if ( node is back )
67 | back = node.previous;
68 |
69 | if ( node.previous )
70 | node.previous.next = node.next;
71 | if ( node.next )
72 | node.next.previous = node.previous;
73 |
74 | data.remove(key);
75 | GC.free(node);
76 |
77 | return true;
78 | }
79 |
80 | /**
81 | * Removes all contents from the LinkedHashMap
82 | */
83 | void clear() pure nothrow @trusted
84 | {
85 | Node* node = front;
86 | Node* previous;
87 |
88 | while ( node !is null )
89 | {
90 | previous = node;
91 | node = node.next;
92 |
93 | GC.free(previous);
94 | }
95 |
96 | data.destroy();
97 | front = null;
98 | back = null;
99 | }
100 |
101 | /**
102 | * Returns: the number of values in the LinkedHasMap.
103 | */
104 | @property size_t length() nothrow pure const @trusted
105 | {
106 | return data.length;
107 | }
108 |
109 | /**
110 | * Returns: true if the LinkedHasmap has no elements.
111 | */
112 | @property bool empty() nothrow pure const @safe
113 | {
114 | return front is null;
115 | }
116 |
117 | /**
118 | * Indexing operators yield or modify the value at a specified index.
119 | */
120 | inout(Value) opIndex(Key key) inout pure @safe
121 | {
122 | return data[key].val;
123 | }
124 |
125 | /// ditto
126 | void opIndexAssign(Value value, Key key) @safe
127 | {
128 | Node* node;
129 |
130 | if ( key !in data )
131 | {
132 | node = new Node;
133 | }
134 | else
135 | {
136 | node = data[key];
137 | node.val = value;
138 | return;
139 | }
140 |
141 | node.val = value;
142 | node.key = key;
143 |
144 | data[key] = node;
145 |
146 | if ( front is null )
147 | {
148 | front = node;
149 | back = node;
150 | return;
151 | }
152 |
153 | node.previous = back;
154 | back.next = node;
155 | back = node;
156 | }
157 |
158 | /// ditto
159 | Value opIndexUnary(string op)(Key key) @safe
160 | {
161 | Node* node = data[key];
162 | return mixin(op ~"node.val;");
163 | }
164 |
165 | /// ditto
166 | Value opIndexOpAssign(string op)(Value v, Key key) @safe
167 | {
168 | Node* node = data[key];
169 | return mixin("node.val" ~ op ~ "=v");
170 | }
171 |
172 | /**
173 | * in operator. Check to see if the given element exists in the container.
174 | */
175 | inout(Value)* opBinaryRight(string op)(Key key) inout nothrow pure @safe
176 | if (op == "in")
177 | {
178 | inout(Node*)* node = key in data;
179 |
180 | if ( node is null )
181 | return null;
182 |
183 | return &((*node).val);
184 | }
185 |
186 | /**
187 | * foreach iteration uses opApply.
188 | */
189 | int opApply(scope int delegate(ref Value) dg)
190 | {
191 | Node* node = front;
192 |
193 | while ( node !is null )
194 | {
195 | int result = dg(node.val);
196 | if ( result )
197 | return result;
198 |
199 | node = node.next;
200 | }
201 |
202 | return 0;
203 | }
204 |
205 | /// ditto
206 | int opApply(scope int delegate(ref Key, ref Value) dg)
207 | {
208 | Node* node = front;
209 |
210 | while ( node !is null )
211 | {
212 | int result = dg(node.key, node.val);
213 | if ( result )
214 | return result;
215 |
216 | node = node.next;
217 | }
218 |
219 | return 0;
220 | }
221 |
222 | /**
223 | * Returns: true if this and that are equal.
224 | */
225 | bool opEquals(inout typeof(this) that) inout nothrow pure @safe
226 | {
227 | return data == that.data;
228 | }
229 |
230 | /**
231 | * Returns: An dynamic array, the elements of which are the keys in the LinkedHashmap.
232 | */
233 | @property inout(Key)[] keys() inout @safe
234 | {
235 | inout(Key)[] k;
236 |
237 | inout(Node)* node = front;
238 |
239 | while ( node !is null )
240 | {
241 | k ~= node.key;
242 | node = node.next;
243 | }
244 |
245 | return k;
246 | }
247 |
248 | /**
249 | * Returns: An dynamic array, the elements of which are the values in the LinkedHashmap.
250 | */
251 | @property inout(Value)[] values() inout @safe
252 | {
253 | inout(Value)[] v;
254 |
255 | inout(Node)* node = front;
256 |
257 | while ( node !is null )
258 | {
259 | v ~= node.val;
260 | node = node.next;
261 | }
262 |
263 | return v;
264 | }
265 |
266 | /**
267 | * Reorganizes the LinkedHashMap in place so that lookups are more efficient,
268 | * rehash is effective when, for example, the program is done loading up
269 | * a symbol table and now needs fast lookups in it.
270 | */
271 | void rehash() @trusted
272 | {
273 | data = data.rehash();
274 | }
275 |
276 | /**
277 | * Create a new LinkedHashMap of the same size and copy the contents of the LinkedHashMap into it.
278 | */
279 | LinkedHashMap!(Key, Value) dub() @safe
280 | {
281 | LinkedHashMap!(Key, Value) copy;
282 | Node* node = front;
283 |
284 | while ( node !is null )
285 | {
286 | copy[node.key] = node.val;
287 | node = node.next;
288 | }
289 |
290 | return copy;
291 | }
292 |
293 | /**
294 | * Returns a delegate suitable for use as a ForeachAggregate to
295 | * a ForeachStatement which will iterate over the keys of the LinkedHashMap.
296 | */
297 | @property auto byKey() pure nothrow @safe
298 | {
299 | static struct KeyRange
300 | {
301 | Node* node;
302 |
303 | this(Node* node) pure nothrow @safe
304 | {
305 | this.node = node;
306 | }
307 |
308 | @property Key front() pure nothrow @safe
309 | {
310 | return node.key;
311 | }
312 |
313 | void popFront() pure nothrow @safe
314 | {
315 | node = node.next;
316 | }
317 |
318 | @property bool empty() pure const nothrow @safe
319 | {
320 | return node is null;
321 | }
322 | }
323 |
324 | return KeyRange(front);
325 | }
326 |
327 | /**
328 | * Returns a delegate suitable for use as a ForeachAggregate to
329 | * a ForeachStatement which will iterate over the values of the LinkedHashMap.
330 | */
331 | @property auto byValue() pure nothrow @safe
332 | {
333 | static struct ValueRange
334 | {
335 | Node* node;
336 |
337 | this(Node* node) pure nothrow @safe
338 | {
339 | this.node = node;
340 | }
341 |
342 | @property Value front() pure nothrow @safe
343 | {
344 | return node.val;
345 | }
346 |
347 | void popFront() pure nothrow @safe
348 | {
349 | node = node.next;
350 | }
351 |
352 | @property bool empty() pure const nothrow @safe
353 | {
354 | return node is null;
355 | }
356 | }
357 |
358 | return ValueRange(front);
359 | }
360 | }
361 |
--------------------------------------------------------------------------------
/source/gtd/XMLReader.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.XMLReader;
19 |
20 | import std.algorithm;
21 | import std.array;
22 | import std.conv : to;
23 | import std.exception;
24 | import std.range;
25 | import std.string;
26 | import std.traits: isSomeChar;
27 | import std.uni;
28 |
29 | import gtd.WrapException;
30 |
31 | struct XMLNode
32 | {
33 | XMLNodeType type;
34 |
35 | string value;
36 | string[string] attributes;
37 | }
38 |
39 | enum XMLNodeType
40 | {
41 | None,
42 | PI,
43 | StartTag,
44 | Text,
45 | CData,
46 | DocType,
47 | Comment,
48 | EmptyTag,
49 | EndTag,
50 | DocumentEnd
51 | }
52 |
53 | class XMLReader(T)
54 | if (isInputRange!T && isSomeChar!(ElementType!T) )
55 | {
56 | XMLNode front;
57 | string fileName;
58 |
59 | static if ( is( T == string ) )
60 | private CountLines!ByChar document;
61 | else
62 | private CountLines!T document;
63 |
64 | /**
65 | * Params:
66 | * document = The XML document to parse.
67 | * fileName = File name to print in diagnostic messages.
68 | */
69 | this(T document, string fileName = null)
70 | {
71 | static if ( is( T == string ) )
72 | this.document = CountLines!ByChar(ByChar(document));
73 | else
74 | this.document = CountLines!T(document);
75 |
76 | this.fileName = fileName;
77 |
78 | popFront();
79 | }
80 |
81 | @property size_t line()
82 | {
83 | return document.line;
84 | }
85 | alias lineNumber = line;
86 |
87 | void popFront()
88 | {
89 | front = XMLNode();
90 |
91 | if ( document.empty )
92 | {
93 | front.type = XMLNodeType.DocumentEnd;
94 | return;
95 | }
96 |
97 | if ( document.front == '<' )
98 | parseTag();
99 | else
100 | parseText();
101 | }
102 |
103 | @property bool empty()
104 | {
105 | return document.empty && front.type == XMLNodeType.DocumentEnd;
106 | }
107 |
108 | private void parseTag()
109 | {
110 | document.popFront();
111 |
112 | switch ( document.front )
113 | {
114 | case '!':
115 | document.popFront();
116 | switch ( document.front )
117 | {
118 | case '[':
119 | enforce(document.skipOver("[CDATA["));
120 | parseCDATA();
121 | break;
122 | case 'D':
123 | enforce(document.skipOver("!DOCTYPE"));
124 | parseDocType();
125 | break;
126 | case '-':
127 | enforce(document.skipOver("--"));
128 | parseComment();
129 | break;
130 | default:
131 | throw new XMLException(this, "Invalid XML tag");
132 | }
133 | break;
134 | case '?':
135 | document.popFront();
136 | parsePI();
137 | break;
138 | case '/':
139 | document.popFront();
140 | parseStartTag();
141 | front.type = XMLNodeType.EndTag;
142 | break;
143 | default:
144 | parseStartTag();
145 | break;
146 | }
147 |
148 | skipWhitespace();
149 | }
150 |
151 | private void parseCDATA()
152 | {
153 | front.type = XMLNodeType.CData;
154 | auto buff = appender!string();
155 |
156 | while ( !document.empty )
157 | {
158 | if ( document.front == ']' )
159 | {
160 | document.popFront();
161 |
162 | if ( document.front != ']' )
163 | {
164 | buff.put(']');
165 | buff.put(document.front);
166 | document.popFront();
167 | continue;
168 | }
169 |
170 | document.popFront();
171 |
172 | if ( document.front == '>' )
173 | {
174 | document.popFront();
175 | return;
176 | }
177 | }
178 |
179 | buff.put(document.front);
180 | document.popFront();
181 | }
182 |
183 | front.value = buff.data;
184 | }
185 |
186 | private void parseDocType()
187 | {
188 | front.type = XMLNodeType.DocType;
189 | auto buff = appender!string();
190 | int bracketCount;
191 |
192 | skipWhitespace();
193 |
194 | while ( !document.empty )
195 | {
196 | switch ( document.front )
197 | {
198 | case '[':
199 | bracketCount++;
200 | break;
201 | case ']':
202 | bracketCount--;
203 | break;
204 | case '>':
205 | if ( bracketCount == 0 )
206 | {
207 | document.popFront();
208 | return;
209 | }
210 | break;
211 | default: break;
212 | }
213 |
214 | buff.put(document.front);
215 | document.popFront();
216 | }
217 |
218 | front.value = buff.data.stripRight();
219 | }
220 |
221 | private void parseComment()
222 | {
223 | front.type = XMLNodeType.Comment;
224 | auto buff = appender!string();
225 |
226 | while ( !document.empty )
227 | {
228 | if ( document.front == '-' )
229 | {
230 | document.popFront();
231 |
232 | if ( document.front != '-' )
233 | {
234 | buff.put('-');
235 | buff.put(document.front);
236 | document.popFront();
237 | continue;
238 | }
239 |
240 | document.popFront();
241 |
242 | if ( document.front == '>' )
243 | {
244 | document.popFront();
245 | return;
246 | }
247 |
248 | throw new XMLException(this, "-- not allowed in comments.");
249 | }
250 |
251 | buff.put(document.front);
252 | document.popFront();
253 | }
254 |
255 | front.value = buff.data.strip();
256 | }
257 |
258 | private void parsePI()
259 | {
260 | front.type = XMLNodeType.PI;
261 | auto buff = appender!string();
262 |
263 | while ( !document.empty )
264 | {
265 | if ( document.front == '?' )
266 | {
267 | document.popFront();
268 |
269 | if ( document.front == '>' )
270 | {
271 | document.popFront();
272 | return;
273 | }
274 |
275 | buff.put('?');
276 | }
277 |
278 | buff.put(document.front);
279 | document.popFront();
280 | }
281 |
282 | front.value = buff.data.stripRight();
283 | }
284 |
285 | private void parseStartTag()
286 | {
287 | front.type = XMLNodeType.StartTag;
288 | auto buff = appender!string();
289 |
290 | while ( !document.empty && !(document.front.isWhite() || document.front == '/' || document.front == '>') )
291 | {
292 | buff.put(document.front);
293 | document.popFront();
294 | }
295 |
296 | front.value = buff.data;
297 |
298 | while ( !document.empty )
299 | {
300 | skipWhitespace();
301 |
302 | if ( document.front == '/' )
303 | {
304 | front.type = XMLNodeType.EmptyTag;
305 | document.popFront();
306 | }
307 |
308 | if ( document.front == '>' )
309 | {
310 | document.popFront();
311 | return;
312 | }
313 |
314 | buff = appender!string();
315 | string attName;
316 |
317 | while ( !document.empty && !(document.front.isWhite() || document.front == '=') )
318 | {
319 | buff.put(document.front);
320 | document.popFront();
321 | }
322 |
323 | document.popFront();
324 | if ( document.front == '=' )
325 | document.popFront();
326 |
327 | attName = buff.data;
328 | buff = appender!string();
329 |
330 | if ( document.front.isWhite() )
331 | skipWhitespace();
332 |
333 | ElementType!(typeof(document)) quote = document.front;
334 | document.popFront();
335 |
336 | AttValue: while ( !document.empty )
337 | {
338 | switch ( document.front )
339 | {
340 | case '\'':
341 | case '"':
342 | if ( document.front != quote )
343 | goto default;
344 |
345 | document.popFront();
346 | break AttValue;
347 | case '&':
348 | parseAmpersand(buff);
349 | break;
350 | default:
351 | buff.put(document.front);
352 | break;
353 | }
354 |
355 | document.popFront();
356 | }
357 |
358 | front.attributes[attName] = buff.data;
359 | }
360 | }
361 |
362 | private void parseText()
363 | {
364 | front.type = XMLNodeType.Text;
365 | auto buff = appender!string();
366 |
367 | Text: while ( !document.empty )
368 | {
369 | switch ( document.front )
370 | {
371 | case '<':
372 | break Text;
373 | case '&':
374 | parseAmpersand(buff);
375 | break;
376 | default:
377 | buff.put(document.front);
378 | break;
379 | }
380 |
381 | document.popFront();
382 | }
383 |
384 | front.value = buff.data.stripRight();
385 | }
386 |
387 | private void skipWhitespace()
388 | {
389 | while ( !document.empty && isWhite(document.front) )
390 | document.popFront();
391 | }
392 |
393 | private void parseAmpersand(Appender!(string) buff)
394 | {
395 | ElementType!(typeof(document))[5] sequence;
396 | int index;
397 |
398 | document.popFront();
399 |
400 | while ( document.front != ';' )
401 | {
402 | sequence[index++] = document.front;
403 | document.popFront();
404 | }
405 |
406 | switch ( sequence[0 .. index] )
407 | {
408 | case "#34":
409 | case "quot":
410 | buff.put('"');
411 | break;
412 | case "#38":
413 | case "amp":
414 | buff.put('&');
415 | break;
416 | case "#39":
417 | case "apos":
418 | buff.put('\'');
419 | break;
420 | case "#60":
421 | case "lt":
422 | buff.put('<');
423 | break;
424 | case "#62":
425 | case "gt":
426 | buff.put('>');
427 | break;
428 | case "#x4":
429 | buff.put('\004');
430 | break;
431 | default:
432 | throw new XMLException(this, "Unregonized escape secuence");
433 | }
434 | }
435 |
436 | unittest
437 | {
438 | auto reader = new XMLReader("<test>");
439 | assert(reader.front.value == "");
440 | }
441 | }
442 |
443 | /**
444 | * Skip the current tag and it's content.
445 | * Leaves the reader pointing to the end tag with the same depth.
446 | */
447 | void skipTag(T)(XMLReader!T reader)
448 | {
449 | if ( reader.front.type == XMLNodeType.EmptyTag )
450 | return;
451 | if ( reader.front.type != XMLNodeType.StartTag )
452 | {
453 | reader.popFront();
454 | return;
455 | }
456 |
457 | string tagName = reader.front.value;
458 | size_t depth;
459 |
460 | while ( !reader.empty )
461 | {
462 | if ( reader.front.type == XMLNodeType.StartTag )
463 | depth++;
464 |
465 | if ( reader.front.type == XMLNodeType.EndTag )
466 | depth--;
467 |
468 | if ( depth == 0 && reader.front.value == tagName )
469 | return;
470 |
471 | reader.popFront();
472 | }
473 | }
474 |
475 | /**
476 | * Is this an end tag with name tagName.
477 | */
478 | bool endTag(T)(XMLReader!T reader, string tagName)
479 | {
480 | return reader.front.type == XMLNodeType.EndTag && reader.front.value == tagName;
481 | }
482 |
483 | /// ditto.
484 | bool endTag(T)(XMLReader!T reader, string[] tagNames ...)
485 | {
486 | return reader.front.type == XMLNodeType.EndTag && tagNames.canFind(reader.front.value);
487 | }
488 |
489 | class XMLException : WrapException
490 | {
491 | this (T)(XMLReader!T reader, string msg)
492 | {
493 | super(msg, reader.fileName, reader.line, null);
494 | }
495 |
496 | override string toString()
497 | {
498 | string s;
499 | toString((buf) { s ~= buf; });
500 | return s;
501 | }
502 |
503 | override void toString(scope void delegate(in char[]) sink) const
504 | {
505 | sink(file);
506 | sink("("); sink(to!string(line)); sink(")");
507 |
508 | if (msg.length)
509 | {
510 | sink(": "); sink(msg);
511 | }
512 | }
513 |
514 | }
515 |
516 | struct ByChar
517 | {
518 | string data;
519 |
520 | @property char front()
521 | {
522 | return data[0];
523 | }
524 |
525 | @property bool empty()
526 | {
527 | return !data.length;
528 | }
529 |
530 | void popFront()
531 | {
532 | assert(data.length, "Attempting to popFront() past the end of an array");
533 | data = data[1 .. $];
534 | }
535 |
536 | @property ByChar save()
537 | {
538 | return this;
539 | }
540 |
541 | alias data this;
542 | }
543 |
544 | struct CountLines(Source) if (isSomeChar!(ElementType!Source))
545 | {
546 | Source src;
547 | size_t line = 1;
548 |
549 | this(Source src)
550 | {
551 | this.src = src;
552 | }
553 |
554 | @property ElementType!Source front()
555 | {
556 | return src.front;
557 | }
558 |
559 | @property bool empty()
560 | {
561 | return src.empty;
562 | }
563 |
564 | void popFront()
565 | {
566 | src.popFront();
567 |
568 | if ( src.front == '\n' )
569 | line++;
570 | }
571 |
572 | @property typeof(this) save()
573 | {
574 | return typeof(this)(src.save);
575 | }
576 | }
577 |
--------------------------------------------------------------------------------
/source/gtd/GirField.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.GirField;
19 |
20 | import std.algorithm: among, endsWith;
21 | import std.conv;
22 | import std.range;
23 | import std.string: splitLines, strip;
24 |
25 | import gtd.Log;
26 | import gtd.GirFunction;
27 | import gtd.GirStruct;
28 | import gtd.GirType;
29 | import gtd.GirWrapper;
30 | import gtd.XMLReader;
31 |
32 | final class GirField
33 | {
34 | string name;
35 | string doc;
36 | GirType type;
37 | int bits = -1;
38 | bool writable = false;
39 | bool isLength = false; ///This field holds the length of an other field.
40 | bool noProperty = false; ///Don't generate a property for this field.
41 |
42 | GirFunction callback;
43 | GirUnion gtkUnion;
44 | GirStruct gtkStruct;
45 |
46 | GirWrapper wrapper;
47 | GirStruct strct;
48 |
49 | this(GirWrapper wrapper, GirStruct strct)
50 | {
51 | this.wrapper = wrapper;
52 | this.strct = strct;
53 | }
54 |
55 | void parse(T)(XMLReader!T reader)
56 | {
57 | name = reader.front.attributes["name"];
58 |
59 | if ( "bits" in reader.front.attributes )
60 | bits = to!int(reader.front.attributes["bits"]);
61 | if ( auto write = "writable" in reader.front.attributes )
62 | writable = *write == "1";
63 |
64 | //TODO: readable private?
65 |
66 | reader.popFront();
67 |
68 | while( !reader.empty && !reader.endTag("field") )
69 | {
70 | if ( reader.front.type == XMLNodeType.EndTag )
71 | {
72 | reader.popFront();
73 | continue;
74 | }
75 |
76 | switch(reader.front.value)
77 | {
78 | case "doc":
79 | reader.popFront();
80 | doc ~= reader.front.value;
81 | reader.popFront();
82 | break;
83 | case "doc-deprecated":
84 | reader.popFront();
85 | doc ~= "\n\nDeprecated: "~ reader.front.value;
86 | reader.popFront();
87 | break;
88 | case "array":
89 | case "type":
90 | type = new GirType(wrapper);
91 | type.parse(reader);
92 | break;
93 | case "callback":
94 | callback = new GirFunction(wrapper, strct);
95 | callback.parse(reader);
96 | break;
97 | case "source-position":
98 | reader.skipTag();
99 | break;
100 | default:
101 | warning("Unexpected tag: ", reader.front.value, " in GirField: ", name, reader);
102 | }
103 | reader.popFront();
104 | }
105 | }
106 |
107 | /**
108 | * A special case for fields, we need to know about all of then
109 | * to properly construct the bitfields.
110 | */
111 | static string[] getFieldDeclarations(GirField[] fields, GirWrapper wrapper)
112 | {
113 | string[] buff;
114 | int bitcount;
115 |
116 | void endBitfield()
117 | {
118 | //AFAIK: C bitfields are padded to a multiple of sizeof uint.
119 | int padding = 32 - (bitcount % 32);
120 |
121 | if ( padding > 0 && padding < 32)
122 | {
123 | buff[buff.length-1] ~= ",";
124 | buff ~= "uint, \"\", "~ to!string(padding);
125 | buff ~= "));";
126 | }
127 | else
128 | {
129 | buff ~= "));";
130 | }
131 |
132 | bitcount = 0;
133 | }
134 |
135 | foreach ( field; fields )
136 | {
137 | if ( field.callback )
138 | {
139 | if ( bitcount > 0 )
140 | endBitfield();
141 | buff ~= field.callback.getFunctionPointerDeclaration();
142 | continue;
143 | }
144 |
145 | if ( field.gtkUnion )
146 | {
147 | if ( bitcount > 0 )
148 | endBitfield();
149 | buff ~= field.gtkUnion.getUnionDeclaration();
150 | continue;
151 | }
152 |
153 | if ( field.gtkStruct )
154 | {
155 | if ( bitcount > 0 )
156 | endBitfield();
157 | buff ~= field.gtkStruct.getStructDeclaration();
158 | buff ~= stringToGtkD(field.gtkStruct.cType ~" "~ field.gtkStruct.name ~";", wrapper.aliasses);
159 | continue;
160 | }
161 |
162 | if ( field.bits > 0 )
163 | {
164 | if ( bitcount == 0 )
165 | {
166 | buff ~= "import std.bitmanip: bitfields;";
167 | buff ~= "mixin(bitfields!(";
168 | }
169 | else
170 | {
171 | buff[buff.length-1] ~= ",";
172 | }
173 |
174 | bitcount += field.bits;
175 | buff ~= stringToGtkD(field.type.cType ~", \""~ field.name ~"\", "~ to!string(field.bits), wrapper.aliasses);
176 | continue;
177 | }
178 | else if ( bitcount > 0)
179 | {
180 | endBitfield();
181 | }
182 |
183 | if ( field.doc !is null && wrapper.includeComments && field.bits < 0 )
184 | {
185 | buff ~= "/**";
186 | foreach ( line; field.doc.splitLines() )
187 | buff ~= " * "~ line.strip();
188 | buff ~= " */";
189 | }
190 |
191 | string dType;
192 |
193 | if ( field.type.size == -1 )
194 | {
195 | if ( field.type.cType.empty )
196 | dType = stringToGtkD(field.type.name, wrapper.aliasses, false);
197 | else
198 | dType = stringToGtkD(field.type.cType, wrapper.aliasses, false);
199 | }
200 | else if ( field.type.elementType.cType.empty )
201 | {
202 | //Special case for GObject.Value.
203 | dType = stringToGtkD(field.type.elementType.name, wrapper.aliasses, false);
204 | dType ~= "["~ to!string(field.type.size) ~"]";
205 | }
206 | else
207 | {
208 | dType = stringToGtkD(field.type.elementType.cType, wrapper.aliasses, false);
209 | dType ~= "["~ to!string(field.type.size) ~"]";
210 | }
211 |
212 | buff ~= dType ~" "~ tokenToGtkD(field.name, wrapper.aliasses) ~";";
213 | }
214 |
215 | if ( bitcount > 0)
216 | {
217 | endBitfield();
218 | }
219 |
220 | return buff;
221 | }
222 |
223 | string[] getProperty()
224 | {
225 | string[] buff;
226 |
227 | if ( !writable || isLength || noProperty )
228 | return null;
229 |
230 | writeDocs(buff);
231 | writeGetter(buff);
232 |
233 | buff ~= "";
234 | if ( wrapper.includeComments )
235 | buff ~= "/** Ditto */";
236 |
237 | writeSetter(buff);
238 |
239 | return buff;
240 | }
241 |
242 | private void writeGetter(ref string[] buff)
243 | {
244 | GirStruct dType;
245 | string dTypeName;
246 |
247 | if ( type.isArray() )
248 | dType = strct.pack.getStruct(type.elementType.name);
249 | else if ( auto dStrct = strct.pack.getStruct(strct.structWrap.get(type.name, "")) )
250 | dType = dStrct;
251 | else
252 | dType = strct.pack.getStruct(type.name);
253 |
254 | if ( dType )
255 | {
256 | if ( dType.name in strct.structWrap )
257 | dTypeName = strct.structWrap[dType.name];
258 | else if ( dType.type == GirStructType.Interface )
259 | dTypeName = dType.name ~"IF";
260 | else
261 | dTypeName = dType.name;
262 | }
263 |
264 | if ( type.isString() )
265 | {
266 | if ( type.isArray() && type.elementType.isString() )
267 | {
268 | buff ~= "public @property string["~ ((type.size > 0)?type.size.to!string:"") ~"] "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"()";
269 | buff ~= "{";
270 |
271 | if ( type.length > -1 )
272 | buff ~= "return Str.toStringArray("~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~", "~ getLengthID(strct) ~");";
273 | else if ( type.size > 0 )
274 | {
275 | buff ~= "string["~ type.size.to!string ~"] arr;";
276 | buff ~= "foreach( i, str; "~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~" )";
277 | buff ~= "{";
278 | buff ~= "arr[i] = Str.toString(str);";
279 | buff ~= "}";
280 | buff ~= "return arr;";
281 | }
282 | else
283 | buff ~= "return Str.toStringArray("~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~");";
284 |
285 | buff ~= "}";
286 | }
287 | else
288 | {
289 | if ( type.size > 0 )
290 | {
291 | buff ~= "public @property char["~ type.size.to!string ~"] "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"()";
292 | buff ~= "{";
293 | buff ~= "return "~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~";";
294 | buff ~= "}";
295 | }
296 | else
297 | {
298 | buff ~= "public @property string "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"()";
299 | buff ~= "{";
300 | buff ~= "return Str.toString("~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~");";
301 | buff ~= "}";
302 | }
303 | }
304 | }
305 | else if ( dType && dType.isDClass() && type.cType.endsWith("*") )
306 | {
307 | if ( type.isArray() )
308 | {
309 | buff ~= "public @property "~ dTypeName ~"["~ ((type.size > 0)?type.size.to!string:"") ~"] "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"()";
310 | buff ~= "{";
311 |
312 | if ( type.length > -1 )
313 | buff ~= dTypeName ~"[] arr = new "~ dTypeName ~"["~ getLengthID(strct) ~"];";
314 | else if ( type.size > 0 )
315 | buff ~= dTypeName ~"["~ type.size.to!string ~"] arr;";
316 | else
317 | buff ~= dTypeName ~"[] arr = new "~ dTypeName ~"[getArrayLength("~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~")];";
318 |
319 | buff ~= "for ( int i = 0; i < arr.length; i++ )";
320 | buff ~= "{";
321 |
322 | if ( dType.pack.name.among("cairo", "glib", "gthread") )
323 | buff ~= "arr[i] = new "~ dTypeName ~"("~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~"[i], false);";
324 | else if( dType.type == GirStructType.Interface )
325 | buff ~= "arr[i] = ObjectG.getDObject!("~ dTypeName ~"IF)("~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~"[i], false);";
326 | else
327 | buff ~= "arr[i] = ObjectG.getDObject!("~ dTypeName ~")("~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~"[i], false);";
328 |
329 | buff ~= "}";
330 | buff ~= "";
331 | buff ~= "return arr;";
332 | buff ~= "}";
333 | }
334 | else
335 | {
336 | buff ~= "public @property "~ dTypeName ~" "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"()";
337 | buff ~= "{";
338 |
339 | if ( dType.pack.name.among("cairo", "glib", "gthread") )
340 | buff ~= "return new "~ dTypeName ~"("~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~", false);";
341 | else if( dType.type == GirStructType.Interface )
342 | buff ~= "return ObjectG.getDObject!("~ dTypeName ~"IF)("~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~", false);";
343 | else
344 | buff ~= "return ObjectG.getDObject!("~ dTypeName ~")("~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~", false);";
345 |
346 | buff ~= "}";
347 | }
348 | }
349 | else if ( type.name.among("bool", "gboolean") || ( type.isArray && type.elementType.name.among("bool", "gboolean") ) )
350 | {
351 | if ( type.isArray() )
352 | {
353 | buff ~= "public @property bool["~ ((type.size > 0)?type.size.to!string:"") ~"] "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"()";
354 | buff ~= "{";
355 |
356 | if ( type.length > -1 )
357 | buff ~= "return "~ strct.getHandleVar ~"."~ tokenToGtkD(name, wrapper.aliasses) ~"[0.."~ getLengthID(strct) ~"];";
358 | else if ( type.size > 0 )
359 | buff ~= "return "~ strct.getHandleVar ~"."~ tokenToGtkD(name, wrapper.aliasses) ~";";
360 | else
361 | error("Is boolean[] field: ", strct.name, ".", name, " really zero terminated?");
362 |
363 | buff ~= "}";
364 | }
365 | else
366 | {
367 | buff ~= "public @property bool "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"()";
368 | buff ~= "{";
369 | buff ~= "return "~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~" != 0;";
370 | buff ~= "}";
371 | }
372 | }
373 | else
374 | {
375 | if ( type.isArray() )
376 | {
377 | if ( type.size > 0 && dType && dType.isDClass() )
378 | {
379 | buff ~= "public @property "~ dTypeName ~"["~ type.size.to!string() ~"] "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"()";
380 | buff ~= "{";
381 | buff ~= dTypeName ~"["~ type.size.to!string() ~"] arr;";
382 |
383 | buff ~= "for ( int i = 0; i < arr.length; i++ )";
384 | buff ~= "{";
385 |
386 | if ( dType.pack.name.among("cairo", "glib", "gthread") )
387 | buff ~= "arr[i] = new "~ dTypeName ~"(&("~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~"[i]), false);";
388 | else if( dType.type == GirStructType.Interface )
389 | buff ~= "arr[i] = ObjectG.getDObject!("~ dTypeName ~"IF)(&("~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~"[i]), false);";
390 | else
391 | buff ~= "arr[i] = ObjectG.getDObject!("~ dTypeName ~")(&("~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~"[i]), false);";
392 |
393 | buff ~= "}";
394 | buff ~= "";
395 | buff ~= "return arr;";
396 | buff ~= "}";
397 | }
398 | else if ( type.size > 0 )
399 | {
400 | buff ~= "public @property "~ stringToGtkD(type.cType, wrapper.aliasses, strct.aliases) ~"["~ ((type.size > 0)?type.size.to!string:"") ~"] "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"()";
401 | buff ~= "{";
402 | buff ~= "return "~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~";";
403 | buff ~= "}";
404 | }
405 | else
406 | {
407 | buff ~= "public @property "~ stringToGtkD(type.cType[0..$-1], wrapper.aliasses, strct.aliases) ~"["~ ((type.size > 0)?type.size.to!string:"") ~"] "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"()";
408 | buff ~= "{";
409 |
410 | if ( type.length > -1 )
411 | buff ~= "return "~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~"[0.."~ getLengthID(strct) ~"];";
412 | else
413 | buff ~= "return "~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~"[0..getArrayLength("~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~")];";
414 |
415 | buff ~= "}";
416 | }
417 | }
418 | else
419 | {
420 | buff ~= "public @property "~ stringToGtkD(type.cType, wrapper.aliasses, strct.aliases) ~" "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"()";
421 | buff ~= "{";
422 | buff ~= "return "~ strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~";";
423 | buff ~= "}";
424 | }
425 | }
426 | }
427 |
428 | private void writeSetter(ref string[] buff)
429 | {
430 | GirStruct dType;
431 | string dTypeName;
432 |
433 | if ( type.isArray() )
434 | dType = strct.pack.getStruct(type.elementType.name);
435 | else if ( auto dStrct = strct.pack.getStruct(strct.structWrap.get(type.name, "")) )
436 | dType = dStrct;
437 | else
438 | dType = strct.pack.getStruct(type.name);
439 |
440 | if ( dType )
441 | {
442 | if ( dType.name in strct.structWrap )
443 | dTypeName = strct.structWrap[dType.name];
444 | else if ( dType.type == GirStructType.Interface )
445 | dTypeName = dType.name ~"IF";
446 | else
447 | dTypeName = dType.name;
448 | }
449 |
450 | if ( type.isString() )
451 | {
452 | if ( type.isArray() && type.elementType.isString() )
453 | {
454 | buff ~= "public @property void "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"(string["~ ((type.size > 0)?type.size.to!string:"") ~"] value)";
455 | buff ~= "{";
456 |
457 | if ( type.size > 0 )
458 | {
459 | buff ~= stringToGtkD(type.elementType.cType, wrapper.aliasses) ~"["~ type.size.to!string ~"] arr;";
460 | buff ~= "foreach( i, str; value )";
461 | buff ~= "{";
462 | buff ~= "arr[i] = Str.toStringz(str);";
463 | buff ~= "}";
464 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~" = arr;";
465 | }
466 | else
467 | {
468 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~" = Str.toStringzArray(value);";
469 | if ( type.length > -1 )
470 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(strct.fields[type.length].name, wrapper.aliasses) ~" = cast("~ stringToGtkD(strct.fields[type.length].type.cType, wrapper.aliasses) ~")value.length;";
471 | }
472 | buff ~= "}";
473 | }
474 | else
475 | {
476 | if ( type.size > 0 )
477 | {
478 | buff ~= "public @property void "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"(char["~ type.size.to!string ~"] value)";
479 | buff ~= "{";
480 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~" = value;";
481 | buff ~= "}";
482 | }
483 | else
484 | {
485 | buff ~= "public @property void "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"(string value)";
486 | buff ~= "{";
487 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~" = Str.toStringz(value);";
488 | buff ~= "}";
489 | }
490 | }
491 | }
492 | else if ( dType && dType.isDClass() && type.cType.endsWith("*") )
493 | {
494 | if ( type.isArray() )
495 | {
496 | buff ~= "public @property void "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"("~ dTypeName ~"["~ ((type.size > 0)?type.size.to!string:"") ~"] value)";
497 | buff ~= "{";
498 | if ( type.size > 0 )
499 | buff ~= dType.cType ~"*["~ type.size.to!string ~"] arr;";
500 | else
501 | buff ~= dType.cType ~"*[] arr = new "~ dType.cType ~"*[value.length+1];";
502 | buff ~= "for ( int i = 0; i < value.length; i++ )";
503 | buff ~= "{";
504 | buff ~= "arr[i] = value[i]."~ dType.getHandleFunc() ~"();";
505 | buff ~= "}";
506 | buff ~= "arr[value.length] = null;";
507 | buff ~= "";
508 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~" = arr.ptr;";
509 |
510 | if ( type.length > -1 )
511 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(strct.fields[type.length].name, wrapper.aliasses) ~" = cast("~ stringToGtkD(strct.fields[type.length].type.cType, wrapper.aliasses) ~")value.length;";
512 |
513 | buff ~= "}";
514 | }
515 | else
516 | {
517 | buff ~= "public @property void "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"("~ dTypeName ~" value)";
518 | buff ~= "{";
519 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~" = value."~ dType.getHandleFunc() ~"();";
520 | buff ~= "}";
521 | }
522 | }
523 | else if ( type.name.among("bool", "gboolean") || ( type.isArray && type.elementType.name.among("bool", "gboolean") ) )
524 | {
525 | if ( type.isArray() )
526 | {
527 | buff ~= "public @property void "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"(bool["~ ((type.size > 0)?type.size.to!string:"") ~"] value)";
528 | buff ~= "{";
529 | if ( type.size > 0 )
530 | {
531 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~" = value;";
532 | }
533 | else
534 | {
535 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~" = value.ptr;";
536 | if ( type.length > -1 )
537 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(strct.fields[type.length].name, wrapper.aliasses) ~" = cast("~ stringToGtkD(strct.fields[type.length].type.cType, wrapper.aliasses) ~")value.length;";
538 | }
539 | buff ~= "}";
540 | }
541 | else
542 | {
543 | buff ~= "public @property void "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"(bool value)";
544 | buff ~= "{";
545 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~" = value;";
546 | buff ~= "}";
547 | }
548 | }
549 | else
550 | {
551 | if ( type.isArray() )
552 | {
553 | if ( type.size > 0 && dType && dType.isDClass() )
554 | {
555 | buff ~= "public @property void "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"("~ dTypeName ~"["~ type.size.to!string() ~"] value)";
556 | buff ~= "{";
557 | buff ~= "for ( int i = 0; i < value.length; i++ )";
558 | buff ~= "{";
559 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~"[i] = *(value[i]."~ dType.getHandleFunc() ~"());";
560 | buff ~= "}";
561 | buff ~= "}";
562 | }
563 | else if ( type.size > 0 )
564 | {
565 | buff ~= "public @property void "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"("~ stringToGtkD(type.cType, wrapper.aliasses, strct.aliases) ~"["~ ((type.size > 0)?type.size.to!string:"") ~"] value)";
566 | buff ~= "{";
567 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~" = value;";
568 | buff ~= "}";
569 | }
570 | else
571 | {
572 | buff ~= "public @property void "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"("~ stringToGtkD(type.cType[0..$-1], wrapper.aliasses, strct.aliases) ~"["~ ((type.size > 0)?type.size.to!string:"") ~"] value)";
573 | buff ~= "{";
574 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~" = value.ptr;";
575 |
576 | if ( type.length > -1 )
577 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(strct.fields[type.length].name, wrapper.aliasses) ~" = cast("~ stringToGtkD(strct.fields[type.length].type.cType, wrapper.aliasses) ~")value.length;";
578 |
579 | buff ~= "}";
580 | }
581 | }
582 | else
583 | {
584 | buff ~= "public @property void "~ tokenToGtkD(name, wrapper.aliasses, strct.aliases) ~"("~ stringToGtkD(type.cType, wrapper.aliasses, strct.aliases) ~" value)";
585 | buff ~= "{";
586 | buff ~= strct.getHandleVar() ~"."~ tokenToGtkD(name, wrapper.aliasses) ~" = value;";
587 | buff ~= "}";
588 | }
589 | }
590 | }
591 |
592 | private void writeDocs(ref string[] buff)
593 | {
594 | if ( doc !is null && wrapper.includeComments )
595 | {
596 | buff ~= "/**";
597 | foreach ( line; doc.splitLines() )
598 | buff ~= " * "~ line.strip();
599 |
600 | buff ~= " */";
601 | }
602 | else if ( wrapper.includeComments )
603 | {
604 | buff ~= "/** */";
605 | }
606 | }
607 |
608 | private string getLengthID(GirStruct strct)
609 | {
610 | if ( type.length > -1 )
611 | return strct.getHandleVar() ~"."~ tokenToGtkD(strct.fields[type.length].name, wrapper.aliasses);
612 | else if ( type.size > 0 )
613 | return to!string(type.size);
614 |
615 | return null;
616 | }
617 | }
618 |
--------------------------------------------------------------------------------
/source/gtd/GirPackage.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.GirPackage;
19 |
20 | import std.algorithm;
21 | import std.array : join, replace;
22 | import std.conv;
23 | import std.file;
24 | import std.path : baseName, buildNormalizedPath;
25 | import std.range : array, back, chain, empty;
26 | import std.regex : ctRegex, matchFirst;
27 | import std.stdio;
28 | import std.string : split, splitLines, strip;
29 | import std.uni;
30 |
31 | import gtd.DefReader;
32 | import gtd.GirAlias;
33 | import gtd.GirConstant;
34 | import gtd.GirEnum;
35 | import gtd.GirFunction;
36 | import gtd.GirStruct;
37 | import gtd.GirVersion;
38 | import gtd.GirWrapper;
39 | import gtd.GlibTypes;
40 | import gtd.IndentedStringBuilder;
41 | import gtd.LinkedHasMap: Map = LinkedHashMap;
42 | import gtd.Log;
43 | import gtd.XMLReader;
44 |
45 | final class GirPackage
46 | {
47 | string name;
48 | string cTypePrefix;
49 | string srcDir;
50 | GirVersion _version;
51 | GirWrapper wrapper;
52 |
53 | string[] lookupAliases; /// Aliases defined in the lookupfile.
54 | string[] lookupConstants; /// Constants defined in the lookupfile.
55 | string[] lookupEnums; /// Enums defined in the lookupfile.
56 | string[] lookupFuncts; /// Functions defined in the lookupfile.
57 | string[] lookupStructs; /// Structs defined in the lookupfile.
58 |
59 | static GirPackage[string] namespaces;
60 | static GirInclude[string] includes;
61 |
62 | string[] libraries;
63 | Map!(string, GirAlias) collectedAliases; /// Aliases defined in the gir file.
64 | Map!(string, GirFunction) collectedCallbacks;
65 | Map!(string, GirConstant) collectedConstants;
66 | Map!(string, GirEnum) collectedEnums; /// Enums defined in the gir file.
67 | Map!(string, GirFunction) collectedFunctions;
68 | Map!(string, GirStruct) collectedStructs;
69 | GirEnum stockIDs; /// The StockID enum (Deprecated).
70 | GirEnum gdkKeys; /// The GdkKey enum.
71 |
72 | public this(string pack, GirWrapper wrapper, string srcDir)
73 | {
74 | this.name = pack;
75 | this.wrapper = wrapper;
76 | this.srcDir = srcDir;
77 | this.stockIDs = new GirEnum(wrapper, this);
78 | this.gdkKeys = new GirEnum(wrapper, this);
79 |
80 | GirInclude gobject;
81 | gobject.name = "GObject";
82 | gobject._version = "2.0";
83 | includes["GObject"] = gobject;
84 | }
85 |
86 | void parseGIR(string girFile)
87 | {
88 | if ( !exists(girFile) )
89 | error("GIR file: '", girFile, "' not found.");
90 |
91 | auto reader = new XMLReader!string(readText(girFile), girFile);
92 |
93 | while ( !reader.empty && reader.front.value != "repository" )
94 | reader.popFront();
95 |
96 | reader.popFront();
97 |
98 | while ( !reader.empty && reader.front.value == "include" )
99 | {
100 | string incName = reader.front.attributes["name"];
101 |
102 | GirInclude inc = includes.get(incName, GirInclude.init);
103 | inc.name = incName;
104 | inc._version = reader.front.attributes["version"];
105 | includes[incName] = inc;
106 |
107 | reader.popFront();
108 | }
109 |
110 | while ( !reader.empty && reader.front.value != "namespace" )
111 | reader.popFront();
112 |
113 | if ( name.empty )
114 | name = reader.front.attributes["name"].toLower();
115 |
116 | namespaces[reader.front.attributes["name"]] = this;
117 | checkVersion(reader.front.attributes["version"]);
118 |
119 | if ( "c:identifier-prefixes" in reader.front.attributes )
120 | {
121 | auto p = reader.front.attributes["c:identifier-prefixes"];
122 | if ( p.length )
123 | cTypePrefix = p.split(',')[0];
124 | }
125 |
126 | if ( "shared-library" in reader.front.attributes )
127 | {
128 | version(Windows)
129 | libraries ~= reader.front.attributes["shared-library"].split(',').map!(a => a.startsWith("lib")?a:"lib"~a~".dll").array;
130 | else version(OSX)
131 | libraries ~= reader.front.attributes["shared-library"].split(',').map!(a => a.baseName.startsWith("lib")?a.baseName:"lib"~a.baseName~".dylib").array;
132 | else
133 | libraries ~= reader.front.attributes["shared-library"].split(',').map!(a => a.startsWith("lib")?a:"lib"~a~".so").array;
134 |
135 | libraries = libraries.sort().uniq.array;
136 | }
137 | reader.popFront();
138 |
139 | while ( !reader.empty && !reader.endTag("namespace") )
140 | {
141 | if ( reader.front.type == XMLNodeType.EndTag )
142 | {
143 | reader.popFront();
144 | continue;
145 | }
146 |
147 | switch (reader.front.value)
148 | {
149 | case "alias":
150 | GirAlias gtkAlias = new GirAlias(wrapper);
151 | gtkAlias.parse(reader);
152 |
153 | if ( gtkAlias.cType == "GType" )
154 | break;
155 |
156 | collectedAliases[gtkAlias.name] = gtkAlias;
157 | break;
158 | case "attribute":
159 | //TODO: Do we need these attibutes?
160 | //dbus.name ccode.ordering deprecated replacement.
161 | reader.skipTag();
162 | break;
163 | case "glib:boxed":
164 | reader.skipTag();
165 | break;
166 | case "bitfield":
167 | case "enumeration":
168 | GirEnum gtkEnum = new GirEnum(wrapper, this);
169 | gtkEnum.parse(reader);
170 | collectedEnums[gtkEnum.name] = gtkEnum;
171 | break;
172 | case "class":
173 | case "interface":
174 | case "record":
175 | case "union":
176 | GirStruct gtkStruct = new GirStruct(wrapper, this);
177 | gtkStruct.parse(reader);
178 |
179 | //Workaround: Dont overwrite the regular pango classes.
180 | if ( gtkStruct.cType.among("PangoCairoFont", "PangoCairoFontMap") )
181 | gtkStruct.name = gtkStruct.cType;
182 |
183 | collectedStructs[gtkStruct.name] = gtkStruct;
184 |
185 | //Don't generate classes named Object.
186 | if ( gtkStruct.name == "Object" )
187 | gtkStruct.name = "Object"~ cTypePrefix;
188 | if ( name == "pango" )
189 | gtkStruct.name = "Pg"~gtkStruct.name;
190 | break;
191 | case "callback":
192 | GirFunction callback = new GirFunction(wrapper, null);
193 | callback.parse(reader);
194 | collectedCallbacks[callback.name] = callback;
195 | break;
196 | case "constant":
197 | parseConstant(reader);
198 | break;
199 | case "function":
200 | parseFunction(reader);
201 | break;
202 | case "function-macro":
203 | // We are not able to wrap these.
204 | reader.skipTag();
205 | break;
206 | case "field":
207 | // We are not able to wrap these.
208 | reader.skipTag();
209 | break;
210 | case "docsection":
211 | // General documentation.
212 | reader.skipTag();
213 | break;
214 | default:
215 | warning("Unexpected tag: ", reader.front.value, " in GirPackage: ", name, reader);
216 | }
217 | reader.popFront();
218 | }
219 | }
220 |
221 | void parseConstant(T)(XMLReader!T reader)
222 | {
223 | if ( reader.front.attributes["name"].startsWith("STOCK_") )
224 | {
225 | GirEnumMember member = GirEnumMember(wrapper);
226 | member.parse(reader);
227 | member.name = member.name[6..$];
228 |
229 | stockIDs.members ~= member;
230 | return;
231 | }
232 | else if ( "c:type" in reader.front.attributes && reader.front.attributes["c:type"].startsWith("GDK_KEY_") )
233 | {
234 | GirEnumMember member = GirEnumMember(wrapper);
235 | member.parse(reader);
236 | member.name = "GDK_"~ member.name[4..$];
237 |
238 | gdkKeys.members ~= member;
239 | return;
240 | }
241 | // The version attribute of the namspace tag is usualy set to MAJOR.0.
242 | else if ( reader.front.attributes["name"].startsWith("MAJOR_VERSION") )
243 | {
244 | _version.major = to!uint(reader.front.attributes["value"]);
245 | }
246 | else if ( reader.front.attributes["name"].startsWith("MINOR_VERSION") )
247 | {
248 | _version.minor = to!uint(reader.front.attributes["value"]);
249 | }
250 | else if ( reader.front.attributes["name"].startsWith("MICRO_VERSION") )
251 | {
252 | _version.micro = to!uint(reader.front.attributes["value"]);
253 | }
254 |
255 | GirConstant constant = new GirConstant(wrapper, this);
256 | constant.parse(reader);
257 |
258 | if (constant.name != "true" && constant.name != "false")
259 | collectedConstants[constant.name] = constant;
260 | }
261 |
262 | void parseFunction(T)(XMLReader!T reader)
263 | {
264 | GirFunction funct = new GirFunction(wrapper, null);
265 | funct.parse(reader);
266 | collectedFunctions[funct.name] = funct;
267 |
268 | checkVersion(funct.libVersion);
269 | }
270 |
271 | GirPackage getNamespace(string name)
272 | {
273 | if ( name.empty )
274 | return null;
275 |
276 | if ( name !in namespaces )
277 | {
278 | if ( auto inc = name in includes )
279 | {
280 | if ( inc.skip ) return null;
281 |
282 | namespaces[name] = inc.parse(wrapper, srcDir);
283 | }
284 | else
285 | {
286 | foreach ( inc; includes.byValue.filter!(inc => inc.name !in namespaces) )
287 | namespaces[inc.name] = inc.parse(wrapper, srcDir);
288 |
289 | if ( !includes.byValue.filter!(inc => inc.name !in namespaces).empty )
290 | return getNamespace(name);
291 | else
292 | return null;
293 | }
294 | }
295 |
296 | return namespaces.get(name, null);
297 | }
298 |
299 | GirStruct getStruct(string name)
300 | {
301 | GirPackage pack = this;
302 |
303 | if ( name.canFind(".") )
304 | {
305 | string[] vals = name.split(".");
306 |
307 | if ( !getNamespace(vals[0]) )
308 | return null;
309 |
310 | pack = namespaces[vals[0]];
311 | name = vals[1];
312 | }
313 | return pack.collectedStructs.get(name, pack.collectedStructs.get("lookup"~name, null));
314 | }
315 |
316 | GirEnum getEnum(string name)
317 | {
318 | GirPackage pack = this;
319 |
320 | if ( name.canFind(".") )
321 | {
322 | string[] vals = name.split(".");
323 |
324 | if ( !getNamespace(vals[0]) )
325 | return null;
326 |
327 | pack = namespaces[vals[0]];
328 | name = vals[1];
329 | }
330 | return pack.collectedEnums.get(name, null);
331 | }
332 |
333 | void checkVersion(string _version)
334 | {
335 | if (this._version < _version)
336 | this._version = GirVersion(_version);
337 | }
338 |
339 | void checkVersion(GirVersion _version)
340 | {
341 | if (this._version < _version)
342 | this._version = _version;
343 | }
344 |
345 | void writeClasses()
346 | {
347 | try
348 | {
349 | if ( !exists(buildNormalizedPath(wrapper.outputDir, srcDir, name.replace(".","/"))) )
350 | mkdirRecurse(buildNormalizedPath(wrapper.outputDir, srcDir, name.replace(".","/")));
351 | }
352 | catch (FileException ex)
353 | {
354 | error("Failed to create directory: ", ex.msg);
355 | }
356 |
357 | foreach ( strct; collectedStructs )
358 | strct.writeClass();
359 | }
360 |
361 | void writeTypes()
362 | {
363 | string buff = wrapper.licence;
364 | auto indenter = new IndentedStringBuilder();
365 |
366 | buff ~= "module "~ name ~".c.types;\n\n";
367 |
368 | buff ~= getTypeImports();
369 |
370 | buff ~= indenter.format(lookupAliases);
371 | foreach ( a; collectedAliases )
372 | {
373 | buff ~= "\n";
374 | buff ~= indenter.format(a.getAliasDeclaration());
375 | }
376 |
377 | buff ~= indenter.format(lookupEnums);
378 | foreach ( e; collectedEnums )
379 | {
380 | buff ~= "\n";
381 | buff ~= indenter.format(e.getEnumDeclaration());
382 | }
383 |
384 | buff ~= indenter.format(lookupStructs);
385 | foreach ( s; collectedStructs )
386 | {
387 | if ( s.noExternal || s.noDeclaration )
388 | continue;
389 |
390 | buff ~= "\n";
391 | buff ~= indenter.format(s.getStructDeclaration());
392 | }
393 |
394 | buff ~= indenter.format(lookupFuncts);
395 | foreach ( f; collectedCallbacks )
396 | {
397 | buff ~= "\n";
398 | buff ~= indenter.format(f.getCallbackDeclaration());
399 | }
400 |
401 | buff ~= indenter.format(lookupConstants);
402 | foreach ( c; collectedConstants )
403 | {
404 | buff ~= "\n";
405 | buff ~= indenter.format(c.getConstantDeclaration());
406 | }
407 |
408 | if ( stockIDs.members !is null )
409 | {
410 | stockIDs.cName = "StockID";
411 | stockIDs.doc = "StockIds";
412 | buff ~= "\n";
413 | buff ~= indenter.format(stockIDs.getEnumDeclaration());
414 | }
415 |
416 | if ( gdkKeys.members !is null )
417 | writeGdkKeys();
418 |
419 | wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, srcDir, name.replace(".","/"), "c/types.d"), buff, true);
420 | }
421 |
422 | void writeGdkKeys()
423 | {
424 | string buff = wrapper.licence;
425 |
426 | buff ~= "module "~ name ~".Keysyms;\n\n";
427 |
428 | buff ~= "/**\n";
429 | buff ~= " * GdkKeysyms.\n";
430 | buff ~= " */\n";
431 | buff ~= "public enum GdkKeysyms\n";
432 | buff ~= "{\n";
433 |
434 | foreach ( member; gdkKeys.members )
435 | {
436 | buff ~= "\t"~ tokenToGtkD(member.name, wrapper.aliasses, false) ~" = "~ member.value ~",\n";
437 | }
438 |
439 | buff ~= "}\n";
440 | buff ~= "alias Keysyms = GdkKeysyms;\n";
441 |
442 | wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, srcDir, name.replace(".","/"), "Keysyms.d"), buff, true);
443 | }
444 |
445 | void writeLoaderTable()
446 | {
447 | string buff = wrapper.licence;
448 |
449 | buff ~= "module "~ name ~".c.functions;\n\n";
450 | buff ~= "import std.stdio;\n";
451 | buff ~= "import "~ name ~".c.types;\n";
452 |
453 | if ( name == "glib" )
454 | buff ~= "import gobject.c.types;\n";
455 | if ( name == "gdk" || name == "pango" )
456 | buff ~= "import cairo.c.types;\n";
457 |
458 | buff ~= "import gtkd.Loader;\n\n";
459 | buff ~= getLibraries();
460 | buff ~= "\n\nshared static this()\n{";
461 |
462 | foreach ( strct; collectedStructs )
463 | {
464 | if ( strct.functions.empty || strct.noExternal )
465 | continue;
466 |
467 | buff ~= "\n\t// "~ name ~"."~ strct.name ~"\n\n";
468 |
469 | foreach ( funct; strct.functions )
470 | {
471 | if ( funct.type == GirFunctionType.Callback || funct.type == GirFunctionType.Signal || funct.name.empty )
472 | continue;
473 |
474 | buff ~= "\tLinker.link("~ funct.cType ~", \""~ funct.cType ~"\", LIBRARY_"~ name.replace(".","").toUpper() ~");\n";
475 | }
476 | }
477 |
478 | //WORKAROUND: Windows has two functions with different names in the 32 and 64 bit versions.
479 | if (name == "glib")
480 | {
481 | buff ~= "\n\tversion(Win64)\n";
482 | buff ~= "\t{\n";
483 | buff ~= "\t\tLinker.link(g_module_name, \"g_module_name_uft8\", LIBRARY_GLIB);\n";
484 | buff ~= "\t\tLinker.link(g_module_open, \"g_module_open_utf8\", LIBRARY_GLIB);\n";
485 | buff ~= "\t}\n";
486 | }
487 |
488 | buff ~= "}\n\n"
489 | ~ "__gshared extern(C)\n"
490 | ~ "{\n";
491 |
492 | foreach ( strct; collectedStructs )
493 | {
494 | if ( strct.functions.empty || strct.noExternal )
495 | continue;
496 |
497 | buff ~= "\n\t// "~ name ~"."~ strct.name ~"\n\n";
498 |
499 | foreach ( funct; strct.functions )
500 | {
501 | if ( funct.type == GirFunctionType.Callback || funct.type == GirFunctionType.Signal || funct.name.empty )
502 | continue;
503 |
504 | buff ~= "\t"~ funct.getLinkerExternal() ~"\n";
505 | }
506 | }
507 |
508 | buff ~= "}\n\n";
509 |
510 | foreach ( strct; collectedStructs )
511 | {
512 | if ( strct.functions.empty || strct.noExternal )
513 | continue;
514 |
515 | buff ~= "\n// "~ name ~"."~ strct.name ~"\n\n";
516 |
517 | foreach ( funct; strct.functions )
518 | {
519 | if ( funct.type == GirFunctionType.Callback || funct.type == GirFunctionType.Signal || funct.name.empty )
520 | continue;
521 |
522 | if (name == "glgdk")
523 | buff ~= "alias glc_"~ funct.cType ~" "~ funct.cType ~";\n";
524 | else
525 | buff ~= "alias c_"~ funct.cType ~" "~ funct.cType ~";\n";
526 | }
527 | }
528 |
529 | wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, srcDir, name.replace(".","/"), "c", "functions.d"), buff, true);
530 | }
531 |
532 | void writeExternalFunctions()
533 | {
534 | string buff = wrapper.licence;
535 |
536 | buff ~= "module "~ name ~".c.functions;\n\n";
537 | buff ~= "import std.stdio;\n";
538 | buff ~= "import "~ name ~".c.types;\n";
539 |
540 | if ( name == "glib" )
541 | buff ~= "import gobject.c.types;\n";
542 | if ( name == "gdk" || name == "pango" )
543 | buff ~= "import cairo.c.types;\n\n";
544 |
545 | buff ~= getLibraries();
546 |
547 | buff ~= "\n\n__gshared extern(C)\n"
548 | ~ "{\n";
549 |
550 | foreach ( strct; collectedStructs )
551 | {
552 | if ( strct.functions.empty || strct.noExternal )
553 | continue;
554 |
555 | buff ~= "\n\t// "~ name ~"."~ strct.name ~"\n\n";
556 |
557 | foreach ( funct; strct.functions )
558 | {
559 | if ( funct.type == GirFunctionType.Callback || funct.type == GirFunctionType.Signal || funct.name.empty )
560 | continue;
561 |
562 | buff ~= "\t"~ funct.getExternal() ~"\n";
563 | }
564 | }
565 |
566 | buff ~= "}";
567 |
568 | wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, srcDir, name.replace(".","/"), "c", "functions.d"), buff, true);
569 | }
570 |
571 | private string getLibraries()
572 | {
573 | string lib = "version (Windows)\n\t";
574 | lib ~= "static immutable LIBRARY_"~ name.replace(".","").toUpper() ~" = ["~ getDllNames() ~"];";
575 | lib ~= "\nelse version (OSX)\n\t";
576 | lib ~= "static immutable LIBRARY_"~ name.replace(".","").toUpper() ~" = ["~ getDylibNames() ~"];";
577 | lib ~= "\nelse\n\t";
578 | lib ~= "static immutable LIBRARY_"~ name.replace(".","").toUpper() ~" = ["~ getSoNames() ~"];";
579 |
580 | return lib;
581 | }
582 |
583 | private auto dllRegex = ctRegex!(`([a-z0-9_]+)(-[0-9\.]+)?(-[0-9]+)?\.dll`);
584 | private auto dylibRegex = ctRegex!(`([a-z0-9_]+)(-[0-9\.]+?)?(\.[0-9]+)?\.dylib`);
585 | private auto soRegex = ctRegex!(`([a-z0-9_]+)(-[0-9\.]+)?\.so(\.[0-9]+)?`);
586 |
587 | private string getDllNames()
588 | {
589 | version (Windows)
590 | {
591 | //TODO: Only the gir files form msys are currently supported on windows.
592 |
593 | string libs;
594 |
595 | foreach ( lib; libraries )
596 | {
597 | auto match = matchFirst(lib, dllRegex);
598 |
599 | //Msys
600 | libs ~= "\""~ lib;
601 |
602 | //wingtk
603 | libs ~= ";";
604 | libs ~= match[1].replace("lib", "");
605 | if ( !match[2].empty )
606 | libs ~= "-"~ match[2][1..$];
607 | if ( !match[3].empty && match[2].canFind('.') )
608 | libs ~= "-"~ match[3][1..$];
609 | else if ( !match[3].empty && !match[2].empty )
610 | libs ~= "-"~ match[2][$-1] ~"."~ match[3][1..$];
611 | libs ~= ".dll";
612 |
613 | //vcpkg
614 | libs ~= ";";
615 | libs ~= match[1].replace("lib", "");
616 | if ( !match[2].empty && match[2].canFind('.') )
617 | libs ~= "-"~ match[2][1..$].split('.')[0];
618 | else if ( !match[2].empty )
619 | libs ~= "-"~ match[2][1..$];
620 | libs ~= ".dll\"";
621 |
622 | if ( lib != libraries.back )
623 | libs ~= ", ";
624 | }
625 |
626 | return libs;
627 | }
628 | else version (OSX)
629 | {
630 | string libs;
631 |
632 | foreach ( lib; libraries )
633 | {
634 | auto match = matchFirst(lib, dylibRegex);
635 |
636 | //Msys
637 | libs ~= "\""~ match[1];
638 | if ( !match[2].empty )
639 | libs ~= "-"~ match[2][1..$];
640 | if ( !match[3].empty )
641 | libs ~= "-"~ match[3][1..$];
642 | libs ~= ".dll";
643 |
644 | //wingtk
645 | libs ~= ";";
646 | libs ~= match[1].replace("lib", "");
647 | if ( !match[2].empty )
648 | libs ~= "-"~ match[2][1..$];
649 | if ( !match[3].empty && match[2].canFind('.') )
650 | libs ~= "-"~ match[3][1..$];
651 | else if ( !match[3].empty && !match[2].empty )
652 | libs ~= "-"~ match[2][$-1] ~"."~ match[3][1..$];
653 | libs ~= ".dll";
654 |
655 | //vcpkg
656 | libs ~= ";";
657 | libs ~= match[1].replace("lib", "");
658 | if ( !match[2].empty && match[2].canFind('.') )
659 | libs ~= "-"~ match[2][1..$].split('.')[0];
660 | else if ( !match[2].empty )
661 | libs ~= "-"~ match[2][1..$];
662 | libs ~= ".dll\"";
663 |
664 | if ( lib != libraries.back )
665 | libs ~= ", ";
666 | }
667 |
668 | return libs;
669 | }
670 | else
671 | {
672 | string libs;
673 |
674 | foreach ( lib; libraries )
675 | {
676 | auto match = matchFirst(lib, soRegex);
677 |
678 | //Msys
679 | libs ~= "\""~ match[1];
680 | if ( !match[2].empty )
681 | libs ~= "-"~ match[2][1..$];
682 | if ( !match[3].empty )
683 | libs ~= "-"~ match[3][1..$];
684 | libs ~= ".dll";
685 |
686 | //wingtk
687 | libs ~= ";";
688 | libs ~= match[1].replace("lib", "");
689 | if ( !match[2].empty )
690 | libs ~= "-"~ match[2][1..$];
691 | if ( !match[3].empty && match[2].canFind('.') )
692 | libs ~= "-"~ match[3][1..$];
693 | else if ( !match[3].empty && !match[2].empty )
694 | libs ~= "-"~ match[2][$-1] ~"."~ match[3][1..$];
695 | libs ~= ".dll";
696 |
697 | //vcpkg
698 | libs ~= ";";
699 | libs ~= match[1].replace("lib", "");
700 | if ( !match[2].empty && match[2].canFind('.') )
701 | libs ~= "-"~ match[2][1..$].split('.')[0];
702 | else if ( !match[2].empty )
703 | libs ~= "-"~ match[2][1..$];
704 | libs ~= ".dll\"";
705 |
706 | if ( lib != libraries.back )
707 | libs ~= ", ";
708 | }
709 |
710 | return libs;
711 | }
712 | }
713 |
714 | private string getDylibNames()
715 | {
716 | version (Windows)
717 | {
718 | string libs;
719 |
720 | foreach ( lib; libraries )
721 | {
722 | auto match = matchFirst(lib, dllRegex);
723 |
724 | libs ~= "\""~ match[1];
725 | if ( !match[2].empty )
726 | libs ~= "-"~ match[2][1..$];
727 | if ( !match[3].empty )
728 | libs ~= "."~ match[3][1..$];
729 | libs ~= ".dylib\"";
730 |
731 | if ( lib != libraries.back )
732 | libs ~= ", ";
733 | }
734 |
735 | return libs;
736 | }
737 | else version (OSX)
738 | {
739 | return "\""~ libraries.join("\", \"") ~"\"";
740 | }
741 | else
742 | {
743 | string libs;
744 |
745 | foreach ( lib; libraries )
746 | {
747 | auto match = matchFirst(lib, soRegex);
748 |
749 | libs ~= "\""~ match[1];
750 | if ( !match[2].empty )
751 | libs ~= "-"~ match[2][1..$];
752 | if ( !match[3].empty )
753 | libs ~= "."~ match[3][1..$];
754 | libs ~= ".dylib\"";
755 |
756 | if ( lib != libraries.back )
757 | libs ~= ", ";
758 | }
759 |
760 | return libs;
761 | }
762 | }
763 |
764 | private string getSoNames()
765 | {
766 | version (Windows)
767 | {
768 | string libs;
769 |
770 | foreach ( lib; libraries )
771 | {
772 | auto match = matchFirst(lib, dllRegex);
773 |
774 | libs ~= "\""~ match[1];
775 | if ( !match[2].empty && !match[3].empty )
776 | libs ~= "-"~ match[2][1..$];
777 | libs ~= ".so";
778 | if ( !match[2].empty && match[3].empty )
779 | libs ~= "."~ match[2][1..$];
780 | if ( !match[3].empty )
781 | libs ~= "."~ match[3][1..$];
782 | libs ~= "\"";
783 |
784 | if ( lib != libraries.back )
785 | libs ~= ", ";
786 | }
787 |
788 | return libs;
789 | }
790 | else version (OSX)
791 | {
792 | string libs;
793 |
794 | foreach ( lib; libraries )
795 | {
796 | auto match = matchFirst(lib, dylibRegex);
797 |
798 | libs ~= "\""~ match[1];
799 | if ( !match[2].empty )
800 | libs ~= "-"~ match[2][1..$];
801 | libs ~= ".so";
802 | if ( !match[3].empty )
803 | libs ~= "."~ match[3][1..$];
804 | libs ~= "\"";
805 |
806 | if ( lib != libraries.back )
807 | libs ~= ", ";
808 | }
809 |
810 | return libs;
811 | }
812 | else
813 | {
814 | return "\""~ libraries.join("\", \"") ~"\"";
815 | }
816 | }
817 |
818 | private string getTypeImports()
819 | {
820 | string imports;
821 | string[] usedNamespaces;
822 | string[] packages;
823 |
824 | foreach ( strct; collectedStructs )
825 | {
826 | if ( strct.noExternal )
827 | continue;
828 |
829 | usedNamespaces = chain(usedNamespaces, strct.usedNamespaces()).sort.uniq.array;
830 | }
831 |
832 | foreach ( ns; usedNamespaces )
833 | {
834 | GirPackage pack = getNamespace(ns);
835 |
836 | if ( pack && pack != this && !packages.canFind(pack.name) )
837 | packages ~= pack.name;
838 | }
839 |
840 | packages = packages.sort().array;
841 |
842 | foreach ( pack; packages )
843 | {
844 | imports ~= "public import "~ pack ~".c.types;\n";
845 | }
846 |
847 | if ( !imports.empty )
848 | imports ~= "\n";
849 |
850 | return imports;
851 | }
852 | }
853 |
854 | struct GirInclude
855 | {
856 | string name;
857 | string _version;
858 |
859 | bool skip;
860 | string[] lookupText;
861 | string lookupFile;
862 | size_t lookupLine;
863 |
864 | string girFileName()
865 | {
866 | return name ~"-"~ _version ~".gir";
867 | }
868 |
869 | GirPackage parse(GirWrapper wrapper, string srcDir)
870 | {
871 | GirPackage inc = new GirPackage(name.toLower, wrapper, srcDir);
872 | inc.parseGIR(wrapper.getAbsoluteGirPath(girFileName()));
873 |
874 | if ( auto text = name in defaultLookupText )
875 | wrapper.proccess(new DefReader(*text, "Default rules"), inc, true);
876 |
877 | wrapper.proccess(new DefReader(lookupText, lookupFile, lookupLine), inc, true);
878 |
879 | return inc;
880 | }
881 | }
882 |
--------------------------------------------------------------------------------
/source/gtd/GirWrapper.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.GirWrapper;
19 |
20 | import std.algorithm;
21 | import std.array;
22 | import std.file;
23 | import std.uni;
24 | import std.path;
25 | import std.stdio;
26 | import std.string;
27 | import std.process : environment;
28 |
29 | import gtd.DefReader;
30 | import gtd.GirField;
31 | import gtd.GirFunction;
32 | import gtd.GirPackage;
33 | import gtd.GirStruct;
34 | import gtd.GirType;
35 | import gtd.GirVersion;
36 | import gtd.GlibTypes;
37 | import gtd.IndentedStringBuilder;
38 | import gtd.Log;
39 |
40 | enum PrintFileMethod
41 | {
42 | Absolute,
43 | Relative,
44 | Default
45 | }
46 |
47 | class GirWrapper
48 | {
49 | bool includeComments = true;
50 | bool useRuntimeLinker;
51 | bool useBindDir;
52 |
53 | bool printFiles;
54 | PrintFileMethod printFileMethod = PrintFileMethod.Default;
55 | string cwdOrBaseDirectory;
56 |
57 | string inputDir;
58 | string outputDir;
59 | string srcDir = "./";
60 | string commandlineGirPath;
61 |
62 | static string licence;
63 | static string[string] aliasses;
64 |
65 | static GirPackage[string] packages;
66 |
67 | public this(string inputDir, string outputDir)
68 | {
69 | this.inputDir = inputDir;
70 | this.outputDir = outputDir;
71 | }
72 |
73 | void proccess(string lookupFileName)
74 | {
75 | if ( !exists(buildPath(inputDir, lookupFileName)) )
76 | error(lookupFileName, " not found, check '--help' for more information.");
77 |
78 | DefReader defReader = new DefReader( buildPath(inputDir, lookupFileName) );
79 |
80 | proccess(defReader);
81 | }
82 |
83 | void proccess(DefReader defReader, GirPackage currentPackage = null, bool isDependency = false, GirStruct currentStruct = null)
84 | {
85 | while ( !defReader.empty )
86 | {
87 | if ( !currentPackage && defReader.key.among(
88 | "addAliases", "addConstants", "addEnums", "addFuncts", "addStructs", "file", "move",
89 | "struct", "class", "interface", "namespace", "noAlias", "noConstant", "noEnum", "noCallback") )
90 | error("Found: '", defReader.key, "' before wrap.", defReader);
91 |
92 | if ( !currentStruct && defReader.key.among(
93 | "code", "cType", "extend", "implements", "import", "interfaceCode", "merge",
94 | "noCode", "noExternal", "noProperty", "noSignal", "noStruct", "override", "structWrap",
95 | "array", "in", "out", "inout", "ref") )
96 | error("Found: '", defReader.key, "' without an active struct.", defReader);
97 |
98 | switch ( defReader.key )
99 | {
100 | //Toplevel keys.
101 | case "bindDir":
102 | warning("Don't use bindDir, it is no longer used since the c definitions have moved.", defReader);
103 | break;
104 | case "includeComments":
105 | includeComments = defReader.valueBool;
106 | break;
107 | case "inputRoot":
108 | warning("Don't use inputRoot, it has been removed as it was never implemented.", defReader);
109 | break;
110 | case "license":
111 | licence = defReader.readBlock().join();
112 | break;
113 | case "outputRoot":
114 | if ( outputDir == "./out" )
115 | outputDir = defReader.value;
116 | break;
117 |
118 | //Global keys.
119 | case "alias":
120 | if ( currentStruct )
121 | loadAA(currentStruct.aliases, defReader);
122 | else
123 | loadAA(aliasses, defReader);
124 | break;
125 | case "copy":
126 | try
127 | copyFiles(inputDir, buildPath(outputDir, srcDir), defReader.value);
128 | catch(FileException ex)
129 | error(ex.msg, defReader);
130 | break;
131 | case "dependency":
132 | loadDependency(defReader);
133 | break;
134 | case "lookup":
135 | DefReader reader = new DefReader( buildPath(inputDir, defReader.value) );
136 |
137 | proccess(reader, currentPackage, isDependency, currentStruct);
138 | break;
139 | case "srcDir":
140 | srcDir = defReader.value;
141 | break;
142 | case "version":
143 | if ( defReader.value == "end" )
144 | break;
145 |
146 | if ( defReader.subKey.empty )
147 | error("No version specified.", defReader);
148 |
149 | bool parseVersion = checkOsVersion(defReader.subKey);
150 | bool smalerThen = false;
151 |
152 | if ( defReader.subKey.length > 1 && defReader.subKey[0] == '<' && defReader.subKey[1].isNumber() )
153 | {
154 | smalerThen = true;
155 | defReader.subKey.popFront();
156 | }
157 |
158 | if ( !parseVersion && defReader.subKey[0].isNumber() )
159 | {
160 | if ( !currentPackage )
161 | error("Only use OS versions before wrap.", defReader);
162 | parseVersion = defReader.subKey <= currentPackage._version;
163 |
164 | if ( smalerThen )
165 | parseVersion = !parseVersion;
166 | }
167 |
168 | if ( defReader.value == "start" )
169 | {
170 | if ( parseVersion )
171 | break;
172 |
173 | defReader.skipBlock();
174 | }
175 |
176 | if ( !parseVersion )
177 | break;
178 |
179 | size_t index = defReader.value.indexOf(':');
180 | defReader.key = defReader.value[0 .. max(index, 0)].strip();
181 | defReader.value = defReader.value[index +1 .. $].strip();
182 |
183 | if ( !defReader.key.empty )
184 | continue;
185 |
186 | break;
187 | case "wrap":
188 | if ( isDependency )
189 | {
190 | currentPackage.name = defReader.value;
191 | break;
192 | }
193 |
194 | if ( outputDir.empty )
195 | error("Found wrap while outputRoot isn't set", defReader);
196 | if (defReader.value in packages)
197 | error("Package '", defReader.value, "' is already defined.", defReader);
198 |
199 | currentStruct = null;
200 | currentPackage = new GirPackage(defReader.value, this, srcDir);
201 | packages[defReader.value] = currentPackage;
202 | break;
203 |
204 | //Package keys
205 | case "addAliases":
206 | currentPackage.lookupAliases ~= defReader.readBlock();
207 | break;
208 | case "addConstants":
209 | currentPackage.lookupConstants ~= defReader.readBlock();
210 | break;
211 | case "addEnums":
212 | currentPackage.lookupEnums ~= defReader.readBlock();
213 | break;
214 | case "addFuncts":
215 | currentPackage.lookupFuncts ~= defReader.readBlock();
216 | break;
217 | case "addStructs":
218 | currentPackage.lookupStructs ~= defReader.readBlock();
219 | break;
220 | case "file":
221 | if ( !isAbsolute(defReader.value) )
222 | {
223 | currentPackage.parseGIR(getAbsoluteGirPath(defReader.value));
224 | }
225 | else
226 | {
227 | warning("Don't use absolute paths for specifying gir files.", defReader);
228 |
229 | currentPackage.parseGIR(defReader.value);
230 | }
231 | break;
232 | case "move":
233 | string[] vals = defReader.value.split();
234 | if ( vals.length <= 1 )
235 | error("No destination for move: ", defReader.value, defReader);
236 | string newFuncName = ( vals.length == 3 ) ? vals[2] : vals[0];
237 | GirStruct dest = currentPackage.getStruct(vals[1]);
238 | if ( dest is null )
239 | dest = createClass(currentPackage, vals[1]);
240 |
241 | if ( currentStruct && vals[0] in currentStruct.functions )
242 | {
243 | currentStruct.functions[vals[0]].strct = dest;
244 | dest.functions[newFuncName] = currentStruct.functions[vals[0]];
245 | dest.functions[newFuncName].name = newFuncName;
246 | if ( newFuncName.startsWith("new") )
247 | dest.functions[newFuncName].type = GirFunctionType.Constructor;
248 | if ( currentStruct.virtualFunctions.canFind(vals[0]) )
249 | dest.virtualFunctions ~= newFuncName;
250 | currentStruct.functions.remove(vals[0]);
251 | }
252 | else if ( vals[0] in currentPackage.collectedFunctions )
253 | {
254 | currentPackage.collectedFunctions[vals[0]].strct = dest;
255 | dest.functions[newFuncName] = currentPackage.collectedFunctions[vals[0]];
256 | dest.functions[newFuncName].name = newFuncName;
257 | currentPackage.collectedFunctions.remove(vals[0]);
258 | }
259 | else
260 | error("Unknown function ", vals[0], defReader);
261 | break;
262 | case "noAlias":
263 | currentPackage.collectedAliases.remove(defReader.value);
264 | break;
265 | case "noConstant":
266 | currentPackage.collectedConstants.remove(defReader.value);
267 | break;
268 | case "noEnum":
269 | currentPackage.collectedEnums.remove(defReader.value);
270 | break;
271 | case "noCallback":
272 | currentPackage.collectedCallbacks.remove(defReader.value);
273 | break;
274 | case "struct":
275 | if ( defReader.value.empty )
276 | {
277 | currentStruct = null;
278 | }
279 | else
280 | {
281 | currentStruct = currentPackage.getStruct(defReader.value);
282 | if ( currentStruct is null )
283 | currentStruct = createClass(currentPackage, defReader.value);
284 | }
285 | break;
286 |
287 | //Struct keys.
288 | case "array":
289 | string[] vals = defReader.value.split();
290 |
291 | if ( vals[0] in currentStruct.functions )
292 | {
293 | GirFunction func = currentStruct.functions[vals[0]];
294 |
295 | if ( vals[1] == "Return" )
296 | {
297 | if ( vals.length < 3 )
298 | {
299 | func.returnType.zeroTerminated = true;
300 | break;
301 | }
302 |
303 | GirType elementType = new GirType(this);
304 |
305 | elementType.name = func.returnType.name;
306 | elementType.cType = func.returnType.cType[0..$-1];
307 | func.returnType.elementType = elementType;
308 | func.returnType.girArray = true;
309 |
310 | foreach( i, p; func.params )
311 | {
312 | if ( p.name == vals[2] )
313 | func.returnType.length = cast(int)i;
314 | }
315 | }
316 | else
317 | {
318 | GirParam param = findParam(currentStruct, vals[0], vals[1]);
319 | GirType elementType = new GirType(this);
320 |
321 | if ( !param )
322 | error("Unknown parameter ", vals[1], " in function ", vals[0]);
323 |
324 | elementType.name = param.type.name;
325 | elementType.cType = param.type.cType[0..$-1];
326 | param.type.elementType = elementType;
327 | param.type.girArray = true;
328 |
329 | if ( vals.length < 3 )
330 | {
331 | param.type.zeroTerminated = true;
332 | break;
333 | }
334 |
335 | if ( vals[2] == "Return" )
336 | {
337 | param.type.length = -2;
338 | break;
339 | }
340 |
341 | foreach( i, p; func.params )
342 | {
343 | if ( p.name == vals[2] )
344 | param.type.length = cast(int)i;
345 | }
346 | }
347 | }
348 | else if ( currentStruct.fields.map!(a => a.name).canFind(vals[0]) )
349 | {
350 | GirField arrayField;
351 | int lengthID = -1;
352 |
353 | foreach ( size_t i, field; currentStruct.fields )
354 | {
355 | if ( field.name == vals[0] )
356 | arrayField = field;
357 | else if ( field.name == vals[1] )
358 | lengthID = cast(int)i;
359 |
360 | if ( arrayField && lengthID > -1 )
361 | break;
362 | }
363 |
364 | arrayField.type.length = lengthID;
365 | currentStruct.fields[lengthID].isLength = true;
366 |
367 | GirType elementType = new GirType(this);
368 | elementType.name = arrayField.type.name;
369 | elementType.cType = arrayField.type.cType[0..$-1];
370 | arrayField.type.elementType = elementType;
371 | arrayField.type.girArray = true;
372 | }
373 | else
374 | {
375 | error("Field or function: `", vals[0], "' is unknown.", defReader);
376 | }
377 | break;
378 | case "class":
379 | if ( currentStruct is null )
380 | currentStruct = createClass(currentPackage, defReader.value);
381 |
382 | currentStruct.lookupClass = true;
383 | currentStruct.name = defReader.value;
384 | break;
385 | case "code":
386 | currentStruct.lookupCode ~= defReader.readBlock;
387 | break;
388 | case "cType":
389 | currentStruct.cType = defReader.value;
390 | break;
391 | case "extend":
392 | currentStruct.lookupParent = true;
393 | currentStruct.parent = defReader.value;
394 | break;
395 | case "implements":
396 | if ( defReader.value.empty )
397 | currentStruct.implements = null;
398 | else
399 | currentStruct.implements ~= defReader.value;
400 | break;
401 | case "import":
402 | currentStruct.imports ~= defReader.value;
403 | break;
404 | case "interface":
405 | if ( currentStruct is null )
406 | currentStruct = createClass(currentPackage, defReader.value);
407 |
408 | currentStruct.lookupInterface = true;
409 | currentStruct.name = defReader.value;
410 | break;
411 | case "interfaceCode":
412 | currentStruct.lookupInterfaceCode ~= defReader.readBlock;
413 | break;
414 | case "merge":
415 | GirStruct mergeStruct = currentPackage.getStruct(defReader.value);
416 | currentStruct.merge(mergeStruct);
417 | GirStruct copy = currentStruct.dup();
418 | copy.noCode = true;
419 | copy.noExternal = true;
420 | mergeStruct.pack.collectedStructs[defReader.value] = copy;
421 | break;
422 | case "namespace":
423 | currentStruct.type = GirStructType.Record;
424 | currentStruct.lookupClass = false;
425 | currentStruct.lookupInterface = false;
426 |
427 | if ( defReader.value.empty )
428 | {
429 | currentStruct.noNamespace = true;
430 | }
431 | else
432 | {
433 | currentStruct.noNamespace = false;
434 | currentStruct.name = defReader.value;
435 | }
436 | break;
437 | case "noCode":
438 | if ( defReader.valueBool )
439 | {
440 | currentStruct.noCode = true;
441 | break;
442 | }
443 | if ( defReader.value !in currentStruct.functions )
444 | error("Unknown function ", defReader.value, ". Possible values: ", currentStruct.functions.keys, defReader);
445 |
446 | currentStruct.functions[defReader.value].noCode = true;
447 | break;
448 | case "noExternal":
449 | currentStruct.noExternal = true;
450 | break;
451 | case "noProperty":
452 | foreach ( field; currentStruct.fields )
453 | {
454 | if ( field.name == defReader.value )
455 | {
456 | field.noProperty = true;
457 | break;
458 | }
459 | else if ( field == currentStruct.fields.back )
460 | error("Unknown field ", defReader.value, defReader);
461 | }
462 | break;
463 | case "noSignal":
464 | currentStruct.functions[defReader.value~"-signal"].noCode = true;
465 | break;
466 | case "noStruct":
467 | currentStruct.noDeclaration = true;
468 | break;
469 | case "structWrap":
470 | loadAA(currentStruct.structWrap, defReader);
471 | break;
472 |
473 | //Function keys
474 | case "in":
475 | string[] vals = defReader.value.split();
476 | if ( vals[0] !in currentStruct.functions )
477 | error("Unknown function ", vals[0], ". Possible values: ", currentStruct.functions, defReader);
478 | GirParam param = findParam(currentStruct, vals[0], vals[1]);
479 | if ( !param )
480 | error("Unknown parameter ", vals[1], " in function ", vals[0]);
481 | param.direction = GirParamDirection.Default;
482 | break;
483 | case "out":
484 | string[] vals = defReader.value.split();
485 | if ( vals[0] !in currentStruct.functions )
486 | error("Unknown function ", vals[0], ". Possible values: ", currentStruct.functions, defReader);
487 | GirParam param = findParam(currentStruct, vals[0], vals[1]);
488 | if ( !param )
489 | error("Unknown parameter ", vals[1], " in function ", vals[0]);
490 | param.direction = GirParamDirection.Out;
491 | break;
492 | case "override":
493 | if ( defReader.value !in currentStruct.functions )
494 | error("Unknown function ", defReader.value, ". Possible values: ", currentStruct.functions.keys, defReader);
495 | currentStruct.functions[defReader.value].lookupOverride = true;
496 | break;
497 | case "inout":
498 | case "ref":
499 | string[] vals = defReader.value.split();
500 | if ( vals[0] !in currentStruct.functions )
501 | error("Unknown function ", vals[0], ". Possible values: ", currentStruct.functions, defReader);
502 | GirParam param = findParam(currentStruct, vals[0], vals[1]);
503 | if ( !param )
504 | error("Unknown parameter ", vals[1], " in function ", vals[0]);
505 | param.direction = GirParamDirection.InOut;
506 | break;
507 |
508 | default:
509 | error("Unknown key: ", defReader.key, defReader);
510 | }
511 |
512 | defReader.popFront();
513 | }
514 | }
515 |
516 | void proccessGIR(string girFile)
517 | {
518 | GirPackage pack = new GirPackage("", this, srcDir);
519 |
520 | if ( !isAbsolute(girFile) )
521 | {
522 | girFile = getAbsoluteGirPath(girFile);
523 | }
524 |
525 | pack.parseGIR(girFile);
526 | packages[pack.name] = pack;
527 | }
528 |
529 | void printFreeFunctions()
530 | {
531 | foreach ( pack; packages )
532 | {
533 | foreach ( func; pack.collectedFunctions )
534 | {
535 | if ( func.movedTo.empty )
536 | writefln("%s: %s", pack.name, func.name);
537 | }
538 | }
539 | }
540 |
541 | void writeFile(string fileName, string contents, bool createDirectory = false)
542 | {
543 | if ( createDirectory )
544 | {
545 | try
546 | {
547 | if ( !exists(fileName.dirName()) )
548 | mkdirRecurse(fileName.dirName());
549 | }
550 | catch (FileException ex)
551 | {
552 | error("Failed to create directory: ", ex.msg);
553 | }
554 | }
555 |
556 | std.file.write(fileName, contents);
557 |
558 | if ( printFiles )
559 | printFilePath(fileName);
560 | }
561 |
562 | string getAbsoluteGirPath(string girFile)
563 | {
564 | string[] girDirectories = getGirDirectories();
565 | foreach(dir; girDirectories)
566 | {
567 | string girFilePath = buildNormalizedPath(dir, girFile);
568 | if (exists(girFilePath) && isFile(girFilePath))
569 | return girFilePath;
570 | }
571 |
572 | error("Couldn't find the gir file: ", girFile, " in any of: ", girDirectories.join(", "));
573 | // Error shouldn't return anyways
574 | assert(0);
575 | }
576 |
577 | private void printFilePath(string fileName)
578 | {
579 | with (PrintFileMethod) switch(printFileMethod)
580 | {
581 | case Absolute:
582 | writeln(asAbsolutePath(fileName));
583 | break;
584 | case Relative:
585 | writeln(asRelativePath(asAbsolutePath(fileName), cwdOrBaseDirectory));
586 | break;
587 | default:
588 | writeln(fileName);
589 | break;
590 | }
591 | }
592 |
593 | private string[] getGirDirectories()
594 | {
595 | static string[] dirs;
596 | if(dirs !is null)
597 | {
598 | return dirs;
599 | }
600 |
601 | if ( commandlineGirPath )
602 | {
603 | dirs ~= commandlineGirPath;
604 | }
605 |
606 | version(Windows)
607 | {
608 | foreach (p; splitter(environment.get("PATH"), ';'))
609 | {
610 | dirs ~= p.buildNormalizedPath("../share/gir-1.0");
611 | }
612 |
613 | }
614 | else version(OSX)
615 | {
616 | string path = environment.get("GTK_BASEPATH");
617 | if(path)
618 | {
619 | dirs ~= path.buildNormalizedPath("../share/gir-1.0");
620 | }
621 |
622 | path = environment.get("HOMEBREW_ROOT");
623 | if(path)
624 | {
625 | dirs ~= path.buildNormalizedPath("share/gir-1.0");
626 | }
627 | }
628 | else
629 | {
630 | string xdgDataDirs = environment.get("XDG_DATA_DIRS", "/usr/local/share:/usr/share");
631 | foreach (p; splitter(xdgDataDirs, ':'))
632 | {
633 | dirs ~= p.buildNormalizedPath("gir-1.0");
634 | }
635 | }
636 |
637 | return dirs.filter!(p=> exists(p) && isDir(p) ).array;
638 | }
639 |
640 | private GirParam findParam(GirStruct strct, string func, string name)
641 | {
642 | foreach( param; strct.functions[func].params )
643 | {
644 | if ( param.name == name )
645 | return param;
646 | }
647 |
648 | return null;
649 | }
650 |
651 | private void loadAA (ref string[string] aa, const DefReader defReader)
652 | {
653 | string[] vals = defReader.value.split();
654 |
655 | if ( vals.length == 1 )
656 | vals ~= "";
657 |
658 | if ( vals.length == 2 )
659 | aa[vals[0]] = vals[1];
660 | else
661 | error("Worng amount of arguments for key: ", defReader.key, defReader);
662 | }
663 |
664 | private void loadDependency(DefReader defReader)
665 | {
666 | if ( defReader.value == "end" )
667 | return;
668 |
669 | if ( defReader.subKey.empty )
670 | error("No dependency specified.", defReader);
671 |
672 | GirInclude inc = GirPackage.includes.get(defReader.subKey, GirInclude.init);
673 |
674 | if ( defReader.value == "skip" )
675 | inc.skip = true;
676 | else if ( defReader.value == "start" )
677 | {
678 | inc.lookupFile = defReader.fileName;
679 | inc.lookupLine = defReader.lineNumber;
680 |
681 | inc.lookupText = defReader.readBlock();
682 | }
683 | else
684 | error("Missing 'skip' or 'start' for dependency: ", defReader.subKey, defReader);
685 |
686 | GirPackage.includes[defReader.subKey] = inc;
687 | }
688 |
689 | private void copyFiles(string srcDir, string destDir, string file)
690 | {
691 | string from = buildNormalizedPath(srcDir, file);
692 | string to = buildNormalizedPath(destDir, file);
693 |
694 | if ( !printFiles )
695 | writefln("copying file [%s] to [%s]", from, to);
696 |
697 | if ( isFile(from) )
698 | {
699 | if ( printFiles )
700 | writeln(to);
701 |
702 | copy(from, to);
703 | return;
704 | }
705 |
706 | void copyDir(string from, string to)
707 | {
708 | if ( !exists(to) )
709 | mkdirRecurse(to);
710 |
711 | foreach ( entry; dirEntries(from, SpanMode.shallow) )
712 | {
713 | string dst = buildPath(to, entry.name.baseName);
714 |
715 | if ( isDir(entry.name) )
716 | {
717 | copyDir(entry.name, dst);
718 | }
719 | else
720 | {
721 | if ( printFiles && !dst.endsWith("functions-runtime.d") && !dst.endsWith("functions-compiletime.d") )
722 | printFilePath(dst);
723 |
724 | copy(entry.name, dst);
725 | }
726 | }
727 | }
728 |
729 | copyDir(from, to);
730 |
731 | if ( file == "cairo" )
732 | {
733 | if ( printFiles )
734 | printFilePath(buildNormalizedPath(to, "c", "functions.d"));
735 |
736 | if ( useRuntimeLinker )
737 | copy(buildNormalizedPath(to, "c", "functions-runtime.d"), buildNormalizedPath(to, "c", "functions.d"));
738 | else
739 | copy(buildNormalizedPath(to, "c", "functions-compiletime.d"), buildNormalizedPath(to, "c", "functions.d"));
740 |
741 | remove(buildNormalizedPath(to, "c", "functions-runtime.d"));
742 | remove(buildNormalizedPath(to, "c", "functions-compiletime.d"));
743 | }
744 | }
745 |
746 | private GirStruct createClass(GirPackage pack, string name)
747 | {
748 | GirStruct strct = new GirStruct(this, pack);
749 | strct.name = name;
750 | strct.cType = pack.cTypePrefix ~ name;
751 | strct.type = GirStructType.Record;
752 | strct.noDeclaration = true;
753 | pack.collectedStructs["lookup"~name] = strct;
754 |
755 | return strct;
756 | }
757 |
758 | private bool checkOsVersion(string _version)
759 | {
760 | if ( _version.empty || !(_version[0].isAlpha() || _version[0] == '!') )
761 | return false;
762 |
763 | version(Windows)
764 | {
765 | return _version.among("Windows", "!OSX", "!linux", "!Linux", "!Posix") != 0;
766 | }
767 | else version(OSX)
768 | {
769 | return _version.among("!Windows", "OSX", "!linux", "!Linux", "Posix") != 0;
770 | }
771 | else version(linux)
772 | {
773 | return _version.among("!Windows", "!OSX", "linux", "Linux", "Posix") != 0;
774 | }
775 | else version(Posix)
776 | {
777 | return _version.among("!Windows", "!OSX", "!linux", "!Linux", "Posix") != 0;
778 | }
779 | else
780 | {
781 | return false;
782 | }
783 | }
784 |
785 | }
786 |
787 | /**
788 | * Apply aliasses to the tokens in the string, and
789 | * camelCase underscore separated tokens.
790 | */
791 | string stringToGtkD(string str, string[string] aliases, bool caseConvert = true)
792 | {
793 | return stringToGtkD(str, aliases, null, caseConvert);
794 | }
795 |
796 | string stringToGtkD(string str, string[string] aliases, string[string] localAliases, bool caseConvert = true)
797 | {
798 | size_t pos, start;
799 | string seps = " \n\r\t\f\v()[]*,;";
800 | auto converted = appender!string();
801 |
802 | while ( pos < str.length )
803 | {
804 | if ( !seps.canFind(str[pos]) )
805 | {
806 | start = pos;
807 |
808 | while ( pos < str.length && !seps.canFind(str[pos]) )
809 | pos++;
810 |
811 | //Workaround for the tm struct, type and variable have the same name.
812 | if ( pos < str.length && str[pos] == '*' && str[start..pos] == "tm" )
813 | converted.put("void");
814 | else
815 | converted.put(tokenToGtkD(str[start..pos], aliases, localAliases, caseConvert));
816 |
817 | if ( pos == str.length )
818 | break;
819 | }
820 |
821 | converted.put(str[pos]);
822 | pos++;
823 | }
824 |
825 | return converted.data;
826 | }
827 |
828 | unittest
829 | {
830 | assert(stringToGtkD("token", ["token":"tok"]) == "tok");
831 | assert(stringToGtkD("string token_to_gtkD(string token, string[string] aliases)", ["token":"tok"])
832 | == "string tokenToGtkD(string tok, string[string] aliases)");
833 | }
834 |
835 | string tokenToGtkD(string token, string[string] aliases, bool caseConvert=true)
836 | {
837 | return tokenToGtkD(token, aliases, null, caseConvert);
838 | }
839 |
840 | string tokenToGtkD(string token, string[string] aliases, string[string] localAliases, bool caseConvert=true)
841 | {
842 | if ( token in glibTypes )
843 | return glibTypes[token];
844 | else if ( token in localAliases )
845 | return localAliases[token];
846 | else if ( token in aliases )
847 | return aliases[token];
848 | else if ( token.endsWith("_t", "_t*", "_t**") )
849 | return token;
850 | else if ( token == "pid_t" || token == "size_t" )
851 | return token;
852 | else if ( caseConvert )
853 | return tokenToGtkD(removeUnderscore(token), aliases, localAliases, false);
854 | else
855 | return token;
856 | }
857 |
858 | string removeUnderscore(string token)
859 | {
860 | char pc;
861 | auto converted = appender!string();
862 |
863 | while ( !token.empty )
864 | {
865 | if ( token[0] == '_' )
866 | {
867 | pc = token[0];
868 | token = token[1..$];
869 |
870 | continue;
871 | }
872 |
873 | if ( pc == '_' )
874 | converted.put(token[0].toUpper());
875 | else
876 | converted.put(token[0]);
877 |
878 | pc = token[0];
879 | token = token[1..$];
880 | }
881 |
882 | return converted.data;
883 | }
884 |
885 | unittest
886 | {
887 | assert(removeUnderscore("this_is_a_test") == "thisIsATest");
888 | }
889 |
--------------------------------------------------------------------------------
/source/gtd/GirStruct.d:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of gir-to-d.
3 | *
4 | * gir-to-d is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License
6 | * as published by the Free Software Foundation, either version 3
7 | * of the License, or (at your option) any later version.
8 | *
9 | * gir-to-d is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with gir-to-d. If not, see .
16 | */
17 |
18 | module gtd.GirStruct;
19 |
20 | import std.algorithm: among, sort, uniq, startsWith, endsWith, canFind;
21 | import std.array : replace;
22 | import std.conv;
23 | import std.file : write;
24 | import std.path: buildNormalizedPath;
25 | import std.uni: toUpper, toLower;
26 | import std.range;
27 | import std.string: capitalize, splitLines, strip, chomp;
28 |
29 | import gtd.GirConstant;
30 | import gtd.GirField;
31 | import gtd.GirFunction;
32 | import gtd.GirPackage;
33 | import gtd.GirType;
34 | import gtd.GirVersion;
35 | import gtd.GirWrapper;
36 | import gtd.IndentedStringBuilder;
37 | import gtd.LinkedHasMap: Map = LinkedHashMap;
38 | import gtd.Log;
39 | import gtd.XMLReader;
40 |
41 | enum GirStructType : string
42 | {
43 | Class = "class",
44 | Interface = "interface",
45 | Record = "record",
46 | Union = "union"
47 | }
48 |
49 | final class GirStruct
50 | {
51 | string name;
52 | GirStructType type;
53 | string doc;
54 | string cType;
55 | string parent;
56 | string libVersion;
57 |
58 | bool lookupClass = false;
59 | bool lookupInterface = false;
60 | bool lookupParent = false; /// is the parent set with the lookup file.
61 | bool noCode = false; /// Only generate the C declarations.
62 | bool noDeclaration = false; /// Don't generate a Declaration of the C struct.
63 | bool noExternal = false; /// Don't generate a Declaration of the C struct. And don't generate the C function declarations.
64 | bool noNamespace = false; /// Generate the functions as global functions.
65 | string[string] structWrap;
66 | string[string] aliases;
67 | string[] lookupCode;
68 | string[] lookupInterfaceCode;
69 |
70 | string[] implements;
71 | string[] imports;
72 | GirField[] fields;
73 | string[] virtualFunctions;
74 | Map!(string, GirFunction) functions;
75 | bool disguised = false;
76 |
77 | GirWrapper wrapper;
78 | GirPackage pack;
79 |
80 | private GirStruct parentStruct;
81 |
82 | this(GirWrapper wrapper, GirPackage pack)
83 | {
84 | this.wrapper = wrapper;
85 | this.pack = pack;
86 | }
87 |
88 | GirStruct dup()
89 | {
90 | GirStruct copy = new GirStruct(wrapper, pack);
91 |
92 | foreach ( i, field; this.tupleof )
93 | copy.tupleof[i] = field;
94 |
95 | return copy;
96 | }
97 |
98 | pure string getFullyQualifiedDName() {
99 | return "D"~ parentStruct.pack.name.capitalize() ~ parentStruct.name;
100 | }
101 |
102 | void parse(T)(XMLReader!T reader)
103 | {
104 | name = reader.front.attributes["name"];
105 | type = cast(GirStructType)reader.front.value;
106 |
107 | if ( "c:type" in reader.front.attributes )
108 | cType = reader.front.attributes["c:type"];
109 | else if ( "glib:type-name" in reader.front.attributes )
110 | cType = reader.front.attributes["glib:type-name"];
111 |
112 | if ( "parent" in reader.front.attributes )
113 | parent = reader.front.attributes["parent"];
114 | if ( "version" in reader.front.attributes )
115 | {
116 | libVersion = reader.front.attributes["version"];
117 | pack.checkVersion(libVersion);
118 | }
119 |
120 | if ( !parent.empty )
121 | {
122 | if ( parent == "GObject.InitiallyUnowned" )
123 | parent = "GObject.Object";
124 | else if ( parent == "InitiallyUnowned" )
125 | parent = "Object";
126 | }
127 |
128 | if ( pack && pack.name != "glib" && "glib:get-type" in reader.front.attributes && reader.front.attributes["glib:get-type"].endsWith("_get_type") )
129 | functions["get_type"] = getTypeFunction(reader.front.attributes["glib:get-type"]);
130 | if ( auto disg = "disguised" in reader.front.attributes )
131 | disguised = *disg == "1";
132 |
133 | if ( reader.front.type == XMLNodeType.EmptyTag )
134 | return;
135 |
136 | reader.popFront();
137 |
138 | while( !reader.empty && !reader.endTag("class", "interface", "record", "union") )
139 | {
140 | switch(reader.front.value)
141 | {
142 | case "attribute":
143 | //TODO: Do we need these attibutes?
144 | //dbus.name ccode.ordering deprecated replacement.
145 | reader.skipTag();
146 | break;
147 | case "constant":
148 | GirConstant constant = new GirConstant(wrapper, pack);
149 | constant.parse(reader);
150 | pack.collectedConstants[constant.name] = constant;
151 | constant.name = name.toUpper() ~"_"~ constant.name;
152 | break;
153 | case "doc":
154 | reader.popFront();
155 | doc ~= reader.front.value;
156 | reader.popFront();
157 | break;
158 | case "doc-deprecated":
159 | reader.popFront();
160 | doc ~= "\n\nDeprecated: "~ reader.front.value;
161 | reader.popFront();
162 | break;
163 | case "field":
164 | GirField field = new GirField(wrapper, this);
165 | field.parse(reader);
166 | fields ~= field;
167 | break;
168 | case "record":
169 | GirField field = new GirField(wrapper, this);
170 | GirStruct strct = new GirStruct(wrapper, null);
171 | strct.parse(reader);
172 | strct.cType = strct.cType.toUpper()[0..1] ~ strct.cType[1 .. $];
173 | field.gtkStruct = strct;
174 | fields ~= field;
175 | break;
176 | case "union":
177 | GirField field = new GirField(wrapper, this);
178 | GirUnion uni = new GirUnion(wrapper);
179 | uni.parse(reader);
180 | field.gtkUnion = uni;
181 | fields ~= field;
182 | break;
183 | case "callback":
184 | GirFunction callback = new GirFunction(wrapper, null);
185 | callback.parse(reader);
186 | pack.collectedCallbacks[callback.name] = callback;
187 | callback.name = name.toUpper() ~ callback.name;
188 | break;
189 | case "constructor":
190 | case "method":
191 | case "glib:signal":
192 | if ( type == GirStructType.Record )
193 | type = GirStructType.Class;
194 | goto case "function";
195 | case "function":
196 | GirFunction func = new GirFunction(wrapper, this);
197 | func.parse(reader);
198 | if ( func.type == GirFunctionType.Signal )
199 | functions[func.name~"-signal"] = func;
200 | else
201 | functions[func.name] = func;
202 | break;
203 | case "virtual-method":
204 | // Virtual methods in the gir file are mirrored
205 | // as regular methods, so we only collect whitch are virtual;
206 | virtualFunctions ~= reader.front.attributes["name"];
207 | reader.skipTag();
208 | break;
209 | case "implements":
210 | implements ~= reader.front.attributes["name"];
211 | break;
212 | case "prerequisite": // Determines whitch base class the implementor of an interface must implement.
213 | case "property":
214 | case "source-position":
215 | reader.skipTag();
216 | break;
217 | default:
218 | warning("Unexpected tag: ", reader.front.value, " in GirStruct: ", name, reader);
219 | }
220 |
221 | reader.popFront();
222 | }
223 |
224 | foreach( func; virtualFunctions )
225 | {
226 | if ( auto vFunc = func in functions )
227 | vFunc.virtual = true;
228 | }
229 |
230 | if ( type == GirStructType.Union )
231 | {
232 | GirField field = new GirField(wrapper, this);
233 | GirUnion uni = new GirUnion(wrapper);
234 | uni.fields = fields;
235 | field.gtkUnion = uni;
236 | fields = [field];
237 |
238 | //special case for "_Value__data__union"
239 | if ( cType.empty )
240 | cType = name;
241 |
242 | type = GirStructType.Record;
243 |
244 | foreach ( funct; functions )
245 | {
246 | if ( funct.type != GirFunctionType.Function )
247 | type = GirStructType.Class;
248 | }
249 | }
250 | }
251 |
252 | GirStruct getParent()
253 | {
254 | if ( !parentStruct )
255 | parentStruct = pack.getStruct(parent);
256 |
257 | return parentStruct;
258 | }
259 |
260 | string[] getStructDeclaration()
261 | {
262 | if ( noExternal || cType.empty )
263 | return null;
264 |
265 | string[] buff;
266 |
267 | if ( doc !is null && wrapper.includeComments && type == GirStructType.Record )
268 | {
269 | buff ~= "/**";
270 | foreach ( line; doc.splitLines() )
271 | buff ~= " * "~ line.strip();
272 |
273 | if ( libVersion )
274 | {
275 | buff ~= " *";
276 | buff ~= " * Since: "~ libVersion;
277 | }
278 |
279 | buff ~= " */";
280 | }
281 |
282 | if ( !fields.empty )
283 | {
284 | buff ~= "struct "~ tokenToGtkD(cType, wrapper.aliasses, false);
285 | buff ~= "{";
286 | buff ~= GirField.getFieldDeclarations(fields, wrapper);
287 | buff ~= "}";
288 | }
289 | else
290 | {
291 | buff ~= "struct "~ tokenToGtkD(cType, wrapper.aliasses, false) ~";";
292 | }
293 |
294 | return buff;
295 | }
296 |
297 | void writeClass()
298 | {
299 | if ( noCode )
300 | return;
301 |
302 | if ( type == GirStructType.Record && !(lookupClass || lookupInterface) && (functions.empty && lookupCode.empty ) )
303 | return;
304 |
305 | parentStruct = pack.getStruct(parent);
306 | resolveImports();
307 |
308 | if ( type == GirStructType.Record && !(lookupClass || lookupInterface) && !("get_type" in functions && isSimpleStruct()) )
309 | {
310 | writeDStruct();
311 | return;
312 | }
313 |
314 | if ( isInterface() )
315 | writeInterface();
316 |
317 | string buff = wrapper.licence;
318 | auto indenter = new IndentedStringBuilder();
319 |
320 | if ( isInterface() )
321 | buff ~= "module "~ pack.name ~"."~ name ~"T;\n\n";
322 | else
323 | buff ~= "module "~ pack.name ~"."~ name ~";\n\n";
324 |
325 | writeImports(buff, isInterface() );
326 | writeDocs(buff);
327 |
328 | if ( isInterface() )
329 | buff ~= "public template "~ name ~"T(TStruct)";
330 | else if ( isSimpleStruct() )
331 | buff ~= "public final class "~ name;
332 | else
333 | buff ~= "public class "~ name;
334 |
335 | if ( lookupParent && !parentStruct )
336 | buff ~= " : "~ parent;
337 | else if ( parentStruct && parentStruct.name != name )
338 | buff ~= " : "~ parentStruct.name;
339 | else if ( parentStruct )
340 | buff ~= " : "~ getFullyQualifiedDName();
341 |
342 | bool first = !parentStruct;
343 |
344 | foreach ( interf; implements )
345 | {
346 | if ( parentStruct && parentStruct.implements.canFind(interf) )
347 | continue;
348 |
349 | // If the parentStruct is in an different package compare without package name.
350 | if ( parentStruct && interf.canFind(".") && parentStruct.implements.canFind(interf.split('.')[1]) )
351 | continue;
352 |
353 | GirStruct strct = pack.getStruct(interf);
354 |
355 | if ( strct && first )
356 | {
357 | buff ~= " :";
358 | first = false;
359 | }
360 | else if ( strct )
361 | buff ~= ",";
362 |
363 | if ( strct )
364 | buff ~= " "~ strct.name ~"IF";
365 | }
366 |
367 | buff ~= "\n";
368 | buff ~= indenter.format("{");
369 |
370 | if ( !cType.empty )
371 | {
372 | if ( !isInterface() )
373 | {
374 | buff ~= indenter.format("/** the main Gtk struct */");
375 | buff ~= indenter.format("protected "~ cType ~"* "~ getHandleVar() ~";");
376 |
377 | if ( !parentStruct )
378 | {
379 | buff ~= indenter.format("protected bool ownedRef;");
380 | }
381 |
382 | buff ~= "\n";
383 | }
384 | buff ~= indenter.format("/** Get the main Gtk struct */");
385 | buff ~= indenter.format("public "~ cType ~"* "~ getHandleFunc() ~"(bool transferOwnership = false)");
386 | buff ~= indenter.format("{");
387 | buff ~= indenter.format("if (transferOwnership)");
388 | buff ~= indenter.format("ownedRef = false;");
389 |
390 | if ( isInterface() )
391 | buff ~= indenter.format("return cast("~ cType ~"*)getStruct();");
392 | else
393 | buff ~= indenter.format("return "~ getHandleVar ~";");
394 |
395 | buff ~= indenter.format("}");
396 | buff ~= "\n";
397 |
398 | if ( !isInterface() )
399 | {
400 | buff ~= indenter.format("/** the main Gtk struct as a void* */");
401 |
402 | if ( parentStruct )
403 | buff ~= indenter.format("protected override void* getStruct()");
404 | else
405 | buff ~= indenter.format("protected void* getStruct()");
406 |
407 | buff ~= indenter.format("{");
408 | buff ~= indenter.format("return cast(void*)"~ getHandleVar ~";");
409 | buff ~= indenter.format("}");
410 | buff ~= "\n";
411 | }
412 |
413 | if ( !isInterface() && !hasDefaultConstructor() )
414 | {
415 | buff ~= indenter.format("/**");
416 | buff ~= indenter.format(" * Sets our main struct and passes it to the parent class.");
417 | buff ~= indenter.format(" */");
418 |
419 | buff ~= indenter.format("public this ("~ cType ~"* "~ getHandleVar() ~", bool ownedRef = false)");
420 | buff ~= indenter.format("{");
421 | buff ~= indenter.format("this."~ getHandleVar() ~" = "~ getHandleVar() ~";");
422 |
423 | if ( parentStruct )
424 | buff ~= indenter.format("super(cast("~ parentStruct.cType ~"*)"~ getHandleVar() ~", ownedRef);");
425 | else
426 | buff ~= indenter.format("this.ownedRef = ownedRef;");
427 |
428 | buff ~= indenter.format("}");
429 | buff ~= "\n";
430 |
431 | if ( shouldFree() )
432 | {
433 | buff ~= indenter.format("~this ()");
434 | buff ~= indenter.format("{");
435 |
436 | if ( wrapper.useRuntimeLinker )
437 | buff ~= indenter.format("if ( Linker.isLoaded(LIBRARY_"~ pack.name.replace(".","").toUpper() ~") && ownedRef )");
438 | else
439 | buff ~= indenter.format("if ( ownedRef )");
440 |
441 | if ( "unref" in functions )
442 | buff ~= indenter.format(functions["unref"].cType ~"("~ getHandleVar ~");");
443 | else
444 | buff ~= indenter.format(functions["free"].cType ~"("~ getHandleVar ~");");
445 |
446 | buff ~= indenter.format("}");
447 | buff ~= "\n";
448 | }
449 | else if ( isSimpleStruct() )
450 | {
451 | buff ~= indenter.format("~this ()");
452 | buff ~= indenter.format("{");
453 |
454 | if ( wrapper.useRuntimeLinker )
455 | buff ~= indenter.format("if ( Linker.isLoaded(LIBRARY_"~ pack.name.replace(".","").toUpper() ~") && ownedRef )");
456 | else
457 | buff ~= indenter.format("if ( ownedRef )");
458 |
459 | buff ~= indenter.format("sliceFree("~ getHandleVar ~");");
460 | buff ~= indenter.format("}");
461 | buff ~= "\n";
462 | }
463 | }
464 |
465 | foreach ( interf; implements )
466 | {
467 | if ( parentStruct && parentStruct.implements.canFind(interf) )
468 | continue;
469 |
470 | if ( parentStruct && interf.canFind(".") && parentStruct.implements.canFind(interf.split('.')[1]) )
471 | continue;
472 |
473 | GirStruct strct = pack.getStruct(interf);
474 |
475 | if ( strct )
476 | {
477 | buff ~= indenter.format("// add the "~ strct.name ~" capabilities");
478 | buff ~= indenter.format("mixin "~ strct.name ~"T!("~ cType.chomp("*") ~");");
479 | buff ~= "\n";
480 | }
481 | }
482 |
483 | }
484 |
485 | if ( !lookupCode.empty )
486 | {
487 | buff ~= indenter.format(lookupCode);
488 | buff ~= "\n";
489 |
490 | buff ~= indenter.format(["/**", "*/"]);
491 | }
492 |
493 | if ( isSimpleStruct() )
494 | {
495 | foreach( field; fields )
496 | {
497 | if ( field.name.startsWith("dummy") )
498 | continue;
499 |
500 | buff ~= "\n";
501 | buff ~= indenter.format(field.getProperty());
502 | }
503 | }
504 |
505 | foreach ( func; functions )
506 | {
507 | if ( func.noCode || func.isVariadic() || func.type == GirFunctionType.Callback )
508 | continue;
509 |
510 | if ( isInterface() && func.type == GirFunctionType.Constructor )
511 | continue;
512 |
513 | if ( isInterface() && func.isStatic() )
514 | continue;
515 |
516 | if ( func.type == GirFunctionType.Signal )
517 | {
518 | buff ~= "\n";
519 | buff ~= indenter.format(func.getAddListenerDeclaration());
520 | buff ~= indenter.format(func.getAddListenerBody());
521 |
522 | foreach ( param; func.params )
523 | {
524 | if ( param.type.name.startsWith("Gdk.Event") && param.type.name != "Gdk.Event" )
525 | {
526 | buff ~= "\n";
527 | buff ~= indenter.format(getGenericEventSignal(func));
528 |
529 | break;
530 | }
531 | }
532 | }
533 | else
534 | {
535 | buff ~= "\n";
536 |
537 | if ( func.name.among("delete", "export", "foreach", "union") )
538 | buff ~= indenter.format("alias "~ func.name[0..$-1] ~" = "~ tokenToGtkD(func.name, wrapper.aliasses) ~";");
539 | else if ( func.name == "ref" )
540 | buff ~= indenter.format("alias doref = "~ tokenToGtkD(func.name, wrapper.aliasses) ~";");
541 |
542 | buff ~= indenter.format(func.getDeclaration());
543 | buff ~= indenter.format("{");
544 | buff ~= indenter.format(func.getBody());
545 | buff ~= indenter.format("}");
546 | }
547 | }
548 |
549 | buff ~= indenter.format("}");
550 |
551 | if ( isInterface() )
552 | wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, pack.srcDir, pack.name.replace(".","/"), name ~"T.d"), buff);
553 | else
554 | wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, pack.srcDir, pack.name.replace(".","/"), name ~".d"), buff);
555 | }
556 |
557 | void writeInterface()
558 | {
559 | string buff = wrapper.licence;
560 | auto indenter = new IndentedStringBuilder();
561 |
562 | buff ~= "module "~ pack.name ~"."~ name ~"IF;\n\n";
563 |
564 | writeImports(buff);
565 | writeDocs(buff);
566 |
567 | buff ~= "public interface "~ name ~"IF";
568 | buff ~= indenter.format("{");
569 |
570 | if ( cType )
571 | {
572 | buff ~= indenter.format("/** Get the main Gtk struct */");
573 | buff ~= indenter.format("public "~ cType ~"* "~ getHandleFunc() ~"(bool transferOwnership = false);");
574 | buff ~= "\n";
575 |
576 | buff ~= indenter.format("/** the main Gtk struct as a void* */");
577 | buff ~= indenter.format("protected void* getStruct();");
578 | buff ~= "\n";
579 |
580 | if ( !lookupInterfaceCode.empty )
581 | {
582 | buff ~= indenter.format(lookupInterfaceCode);
583 | buff ~= "\n";
584 |
585 | buff ~= indenter.format(["/**", "*/"]);
586 | }
587 |
588 | foreach ( func; functions )
589 | {
590 | if ( func.noCode || func.isVariadic() || func.type == GirFunctionType.Callback || func.type == GirFunctionType.Constructor )
591 | continue;
592 |
593 | if ( func.type == GirFunctionType.Signal )
594 | {
595 | string[] dec = func.getAddListenerDeclaration();
596 | dec[$-1] ~= ";";
597 |
598 | buff ~= "\n";
599 | buff ~= indenter.format(dec);
600 | }
601 | else if ( !func.isStatic() )
602 | {
603 | string[] dec = func.getDeclaration();
604 | dec[$-1] = dec[$-1].replace("override ", "");
605 | dec[$-1] ~= ";";
606 |
607 | buff ~= "\n";
608 |
609 | if ( func.name.among("delete", "export", "foreach", "union") )
610 | buff ~= indenter.format("alias "~ func.name[0..$-1] ~" = "~ tokenToGtkD(func.name, wrapper.aliasses) ~";");
611 |
612 | buff ~= indenter.format(dec);
613 | }
614 | else
615 | {
616 | buff ~= "\n";
617 | buff ~= indenter.format(func.getDeclaration());
618 | buff ~= indenter.format("{");
619 | buff ~= indenter.format(func.getBody());
620 | buff ~= indenter.format("}");
621 | }
622 | }
623 |
624 | buff ~= indenter.format("}");
625 | }
626 |
627 | wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, pack.srcDir, pack.name.replace(".","/"), name ~"IF.d"), buff);
628 | }
629 |
630 | void writeDStruct()
631 | {
632 | string buff = wrapper.licence;
633 | auto indenter = new IndentedStringBuilder();
634 |
635 | buff ~= "module "~ pack.name ~"."~ name ~";\n\n";
636 |
637 | writeImports(buff);
638 | writeDocs(buff);
639 |
640 | if ( !noNamespace )
641 | {
642 | buff ~= "public struct "~ name ~"\n";
643 | buff ~= indenter.format("{");
644 | }
645 |
646 | if ( !lookupCode.empty )
647 | {
648 | buff ~= indenter.format(lookupCode);
649 | buff ~= "\n";
650 |
651 | buff ~= indenter.format(["/**", "*/"]);
652 | }
653 |
654 | foreach ( func; functions )
655 | {
656 | if ( func.noCode || func.isVariadic() || !( func.type == GirFunctionType.Function || func.type == GirFunctionType.Method ) )
657 | continue;
658 |
659 | buff ~= "\n";
660 |
661 | if ( func.name.among("delete", "export", "foreach", "union") )
662 | buff ~= indenter.format("alias "~ func.name[0..$-1] ~" = "~ tokenToGtkD(func.name, wrapper.aliasses) ~";");
663 |
664 | buff ~= indenter.format(func.getDeclaration());
665 | buff ~= indenter.format("{");
666 | buff ~= indenter.format(func.getBody());
667 | buff ~= indenter.format("}");
668 | }
669 |
670 | if ( !noNamespace )
671 | buff ~= indenter.format("}");
672 |
673 | wrapper.writeFile(buildNormalizedPath(wrapper.outputDir, pack.srcDir, pack.name.replace(".","/"), name ~".d"), buff);
674 | }
675 |
676 | /**
677 | * Return the variable name the c type is stored in.
678 | */
679 | string getHandleVar()
680 | {
681 | if (cType.length == 0)
682 | return "";
683 |
684 | string p = to!string(toLower(cType[0]));
685 | if ( cType.endsWith("_t") )
686 | {
687 | return p ~ cType[1 .. $ - 2];
688 | } else {
689 | return p ~ cType[1 .. $];
690 | }
691 | }
692 |
693 | /**
694 | * Returns the name of the function that returns the cType.
695 | */
696 | string getHandleFunc()
697 | {
698 | if ( parent && !parentStruct )
699 | parentStruct = getParent();
700 |
701 | if ( parentStruct && parentStruct.name == name )
702 | return "get"~ cast(char)pack.name[0].toUpper ~ pack.name[1..$] ~ name ~"Struct";
703 | else
704 | return "get"~ name ~"Struct";
705 | }
706 |
707 | bool isInterface()
708 | {
709 | if ( lookupInterface )
710 | return true;
711 | if ( lookupClass )
712 | return false;
713 | if ( type == GirStructType.Interface )
714 | return true;
715 |
716 | return false;
717 | }
718 |
719 | bool isNamespace()
720 | {
721 | return type == GirStructType.Record && !(lookupClass || lookupInterface) && !noNamespace;
722 | }
723 |
724 | void merge(GirStruct mergeStruct)
725 | {
726 | foreach ( func; mergeStruct.functions )
727 | {
728 | func.strct = this;
729 | functions[func.name] = func;
730 | }
731 | }
732 |
733 | GirStruct getAncestor()
734 | {
735 | if ( parent.empty )
736 | return this;
737 |
738 | if ( !parentStruct )
739 | parentStruct = pack.getStruct(parent);
740 |
741 | return parentStruct.getAncestor();
742 | }
743 |
744 | bool hasFunction(string funct)
745 | {
746 | if ( funct in functions )
747 | return true;
748 |
749 | if ( parent.empty )
750 | return false;
751 |
752 | if ( !parentStruct )
753 | parentStruct = pack.getStruct(parent);
754 |
755 | if ( !parentStruct )
756 | return false;
757 |
758 | return parentStruct.hasFunction(funct);
759 | }
760 |
761 | private bool hasDefaultConstructor()
762 | {
763 | foreach ( line; lookupCode )
764 | {
765 | //TODO: Whitespace differences?
766 | if ( line.strip == "public this ("~ cType ~"* "~ getHandleVar() ~", bool ownedRef = false)" )
767 | return true;
768 | }
769 |
770 | return false;
771 | }
772 |
773 | bool shouldFree()
774 | {
775 | if ( !parent.empty && parent != "Boxed" )
776 | return false;
777 | if ( name.among("Object", "Boxed") )
778 | return false;
779 |
780 | if ( auto u = "unref" in functions )
781 | {
782 | if ( u.noCode == false && u.params.empty )
783 | return true;
784 | }
785 |
786 | if ( auto f = "free" in functions )
787 | {
788 | if ( f.noCode == false && f.params.empty )
789 | return true;
790 | }
791 | return false;
792 | }
793 |
794 | bool isSimpleStruct()
795 | {
796 | //TODO: don't use this workaround.
797 | //TODO: For TestLogMsg, GArray and GByteArray implement array properties that are not zero terminated.
798 | if ( cType == "PangoAttribute" || cType == "GTestLogMsg" || cType == "GArray" || cType == "GByteArray" || cType == "GtkTreeIter" )
799 | return false;
800 |
801 | if ( pack.name == "cairo" )
802 | return false;
803 |
804 | if ( lookupClass || lookupInterface || noDeclaration || noNamespace )
805 | return false;
806 |
807 | if ( disguised || fields.length == 0 )
808 | return false;
809 |
810 | if ( !fields.empty && fields[0].type )
811 | {
812 | // If the first field is wraped as a D class and isn't declared
813 | // as a pointer we assume its the parent instance.
814 | GirStruct dStruct = pack.getStruct(fields[0].type.name);
815 | if ( dStruct && dStruct.isDClass() && !fields[0].type.cType.endsWith("*") )
816 | return false;
817 | }
818 |
819 | foreach ( field; fields )
820 | {
821 | if ( !field.writable )
822 | return false;
823 | }
824 |
825 | return true;
826 | }
827 |
828 | bool isDClass()
829 | {
830 | if ( type.among(GirStructType.Class, GirStructType.Interface) )
831 | return true;
832 | if ( type == GirStructType.Record && (lookupClass || lookupInterface) )
833 | return true;
834 | if ( "get_type" in functions && isSimpleStruct() )
835 | return true;
836 |
837 | return false;
838 | }
839 |
840 | string[] usedNamespaces()
841 | {
842 | string[] namespaces;
843 |
844 | string getNamespace(GirType type)
845 | {
846 | if ( type.isArray() )
847 | type = type.elementType;
848 |
849 | if ( type.cType in wrapper.aliasses || type.cType in aliases )
850 | return null;
851 |
852 | if ( type.name.canFind(".") )
853 | return type.name.split(".")[0];
854 |
855 | return null;
856 | }
857 |
858 | if ( parent.canFind(".") )
859 | namespaces ~= parent.split(".")[0];
860 |
861 | foreach ( func; functions )
862 | {
863 | namespaces ~= getNamespace(func.returnType);
864 | if ( func.instanceParam )
865 | namespaces ~= getNamespace(func.instanceParam.type);
866 | foreach ( param; func.params )
867 | namespaces ~= getNamespace(param.type);
868 | }
869 |
870 | return namespaces.sort().uniq.array;
871 | }
872 |
873 | private void resolveImports()
874 | {
875 | if ( parentStruct && parentStruct.name != name)
876 | {
877 | imports ~= parentStruct.pack.name ~"."~ parentStruct.name;
878 | }
879 | else if ( parentStruct )
880 | {
881 | string QParent = getFullyQualifiedDName();
882 | imports ~= parentStruct.pack.name ~"."~ parentStruct.name ~" : "~ QParent ~" = "~ parentStruct.name;
883 | structWrap[parent] = QParent;
884 | }
885 |
886 | imports ~= pack.name ~".c.functions";
887 | imports ~= pack.name ~".c.types";
888 |
889 | //Temporarily import the old bindDir.*types modules for backwards compatibility.
890 | const string[string] bindDirs = ["atk": "gtkc", "cairo": "gtkc", "gdk": "gtkc", "gdkpixbuf": "gtkc",
891 | "gio": "gtkc", "glib": "gtkc", "gobject": "gtkc", "gtk": "gtkc", "pango": "gtkc", "gsv": "gsvc",
892 | "vte": "vtec", "gstinterfaces": "gstreamerc", "gstreamer": "gstreamerc"];
893 |
894 | if ( pack.wrapper.useBindDir )
895 | {
896 | if ( auto dir = pack.name in bindDirs )
897 | imports ~= *dir ~"."~ pack.name ~"types";
898 | }
899 |
900 | if ( isSimpleStruct() )
901 | imports ~= "glib.MemorySlice";
902 |
903 | if ( wrapper.useRuntimeLinker && (shouldFree() || isSimpleStruct()) )
904 | imports ~= "gtkd.Loader";
905 |
906 | if ( isSimpleStruct() )
907 | {
908 | foreach ( field; fields )
909 | {
910 | if ( field.type.name in structWrap || field.type.name in aliases )
911 | continue;
912 |
913 | GirStruct dType;
914 |
915 | if ( field.type.isArray() )
916 | dType = pack.getStruct(field.type.elementType.name);
917 | else
918 | dType = pack.getStruct(field.type.name);
919 |
920 | if ( dType is this )
921 | continue;
922 |
923 | if ( dType && dType.isDClass() )
924 | {
925 | if ( !dType.pack.name.among("cairo", "glib", "gthread") )
926 | imports ~= "gobject.ObjectG";
927 |
928 | if ( dType.type == GirStructType.Interface || dType.lookupInterface )
929 | imports ~= dType.pack.name ~"."~ dType.name ~"IF";
930 | else
931 | imports ~= dType.pack.name ~"."~ dType.name;
932 | }
933 | else if ( field.type.isString() || (field.type.isArray() && field.type.elementType.isString()) ) {
934 | imports ~= "glib.Str";
935 | imports ~= "glib.c.functions";
936 | }
937 | }
938 | }
939 |
940 | foreach( func; functions )
941 | {
942 | if ( func.noCode )
943 | continue;
944 |
945 | if ( func.throws )
946 | {
947 | imports ~= "glib.ErrorG";
948 | imports ~= "glib.GException";
949 | }
950 |
951 | void getReturnImport(GirType type)
952 | {
953 | if ( type.name in structWrap || type.name in aliases )
954 | return;
955 |
956 | GirStruct dType = pack.getStruct(type.name);
957 |
958 | if ( dType && dType.isDClass() )
959 | {
960 | if ( !dType.pack.name.among("cairo", "glib", "gthread") )
961 | imports ~= "gobject.ObjectG";
962 |
963 | if ( dType.type == GirStructType.Interface && func.name.startsWith("new") )
964 | return;
965 |
966 | if ( dType is this && dType.type != GirStructType.Interface )
967 | return;
968 |
969 | if ( dType.type == GirStructType.Interface || dType.lookupInterface )
970 | imports ~= dType.pack.name ~"."~ dType.name ~"IF";
971 | else
972 | imports ~= dType.pack.name ~"."~ dType.name;
973 | }
974 | else if ( type.name.among("utf8", "filename") || type.cType.among("guchar**") ) {
975 | imports ~= "glib.Str";
976 | imports ~= "glib.c.functions";
977 | }
978 | }
979 |
980 | if ( func.returnType && func.returnType.cType !in structWrap )
981 | {
982 | getReturnImport(func.returnType);
983 |
984 | if ( func.returnType.isArray() )
985 | getReturnImport(func.returnType.elementType);
986 | }
987 |
988 | void getParamImport(GirType type)
989 | {
990 | if ( type.name in structWrap || type.name in aliases )
991 | return;
992 |
993 | GirStruct dType = pack.getStruct(type.name);
994 |
995 | if ( dType is this )
996 | return;
997 |
998 | if ( func.type == GirFunctionType.Signal && type.name.startsWith("Gdk.Event") )
999 | imports ~= "gdk.Event";
1000 |
1001 | if ( dType && dType.isDClass() )
1002 | {
1003 | if ( dType.type == GirStructType.Interface || dType.lookupInterface )
1004 | imports ~= dType.pack.name ~"."~ dType.name ~"IF";
1005 | else
1006 | imports ~= dType.pack.name ~"."~ dType.name;
1007 | }
1008 | else if ( type.isString() || (type.isArray() && type.elementType.isString()) )
1009 | imports ~= "glib.Str";
1010 | }
1011 |
1012 | foreach ( param; func.params )
1013 | {
1014 | if ( param.type.cType in structWrap )
1015 | continue;
1016 |
1017 | getParamImport(param.type);
1018 |
1019 | if ( param.type.elementType )
1020 | getParamImport(param.type.elementType);
1021 |
1022 | if ( param.direction != GirParamDirection.Default )
1023 | getReturnImport(param.type);
1024 |
1025 | if ( param.direction == GirParamDirection.Out
1026 | && !param.type.cType.endsWith("**")
1027 | && pack.getStruct(param.type.name) !is null
1028 | && pack.getStruct(param.type.name).isDClass() )
1029 | imports ~= "glib.MemorySlice";
1030 |
1031 | if ( param.direction == GirParamDirection.Out
1032 | && param.type.elementType
1033 | && pack.getStruct(param.type.elementType.name) !is null
1034 | && pack.getStruct(param.type.elementType.name).isDClass()
1035 | && param.type.size > 0 )
1036 | imports ~= "glib.MemorySlice";
1037 | }
1038 |
1039 | if ( func.type == GirFunctionType.Signal )
1040 | {
1041 | imports ~= "std.algorithm";
1042 | imports ~= "gobject.Signals";
1043 | }
1044 |
1045 | if ( func.type == GirFunctionType.Constructor )
1046 | imports ~= "glib.ConstructionException";
1047 | }
1048 |
1049 | foreach ( interf; implements )
1050 | {
1051 | if ( parentStruct && parentStruct.implements.canFind(interf) )
1052 | continue;
1053 |
1054 | GirStruct strct = pack.getStruct(interf);
1055 |
1056 | if ( strct )
1057 | {
1058 | imports ~= strct.pack.name ~"."~ strct.name ~"IF";
1059 | imports ~= strct.pack.name ~"."~ strct.name ~"T";
1060 | }
1061 | }
1062 |
1063 | imports = uniq(sort(imports)).array;
1064 | }
1065 |
1066 | private void writeImports(ref string buff, bool _public = false)
1067 | {
1068 | foreach ( imp; imports )
1069 | {
1070 | if ( _public || imp.endsWith("types") )
1071 | buff ~= "public import "~ imp ~";\n";
1072 | else
1073 | buff ~= "private import "~ imp ~";\n";
1074 | }
1075 |
1076 | buff ~= "\n\n";
1077 | }
1078 |
1079 | private void writeDocs(ref string buff)
1080 | {
1081 | if ( doc !is null && wrapper.includeComments )
1082 | {
1083 | buff ~= "/**\n";
1084 | foreach ( line; doc.splitLines() )
1085 | buff ~= " * "~ line.strip() ~"\n";
1086 |
1087 | if ( libVersion )
1088 | {
1089 | buff ~= " *\n * Since: "~ libVersion ~"\n";
1090 | }
1091 |
1092 | buff ~= " */\n";
1093 | }
1094 | else if ( wrapper.includeComments )
1095 | {
1096 | buff ~= "/** */\n";
1097 | }
1098 | }
1099 |
1100 | private GirFunction getTypeFunction(string cIdentifier)
1101 | {
1102 | GirType returnType = new GirType(wrapper);
1103 | returnType.name = "GObject.GType";
1104 | returnType.cType = "GType";
1105 |
1106 | GirFunction func = new GirFunction(wrapper, this);
1107 | func.type = GirFunctionType.Function;
1108 | func.name = "get_type";
1109 | func.cType = cIdentifier;
1110 | func.returnType = returnType;
1111 |
1112 | return func;
1113 | }
1114 |
1115 | /**
1116 | * Get an overload of events that accept an generic Gdk Event
1117 | * instead of the spesific type listed in the gir files.
1118 | *
1119 | * This for backwards compatibility with the documentation based wrapper.
1120 | */
1121 | private string[] getGenericEventSignal(GirFunction func)
1122 | {
1123 | GirFunction signal = func.dup();
1124 | string[] buff;
1125 |
1126 | for ( size_t i; i < signal.params.length; i++ )
1127 | {
1128 | if ( signal.params[i].type.name.startsWith("Gdk.Event") )
1129 | {
1130 | GirType eventType = new GirType(wrapper);
1131 | eventType.name = "Gdk.Event";
1132 |
1133 | GirParam newParam = new GirParam(wrapper);
1134 | newParam.name = signal.params[i].name;
1135 | newParam.doc = signal.params[i].doc;
1136 | newParam.type = eventType;
1137 |
1138 | signal.params[i] = newParam;
1139 |
1140 | break;
1141 | }
1142 | }
1143 |
1144 | buff ~= signal.getAddListenerDeclaration();
1145 | buff ~= signal.getAddListenerBody();
1146 |
1147 | return buff;
1148 | }
1149 | }
1150 |
1151 | final class GirUnion
1152 | {
1153 | string name;
1154 | string doc;
1155 | GirField[] fields;
1156 |
1157 | GirWrapper wrapper;
1158 |
1159 | this(GirWrapper wrapper)
1160 | {
1161 | this.wrapper = wrapper;
1162 | }
1163 |
1164 | void parse(T)(XMLReader!T reader)
1165 | {
1166 | if ( "name" in reader.front.attributes )
1167 | name = reader.front.attributes["name"];
1168 |
1169 | reader.popFront();
1170 |
1171 | while( !reader.empty && !reader.endTag("union") )
1172 | {
1173 | switch(reader.front.value)
1174 | {
1175 | case "doc":
1176 | reader.popFront();
1177 | doc ~= reader.front.value;
1178 | reader.popFront();
1179 | break;
1180 | case "doc-deprecated":
1181 | reader.popFront();
1182 | doc ~= "\n\nDeprecated: "~ reader.front.value;
1183 | reader.popFront();
1184 | break;
1185 | case "field":
1186 | GirField field = new GirField(wrapper, null);
1187 | field.parse(reader);
1188 | fields ~= field;
1189 | break;
1190 | case "record":
1191 | GirField field = new GirField(wrapper, null);
1192 | GirStruct strct = new GirStruct(wrapper, null);
1193 | strct.parse(reader);
1194 | strct.cType = strct.cType.toUpper()[0..1] ~ strct.cType[1 .. $];
1195 | field.gtkStruct = strct;
1196 | fields ~= field;
1197 | break;
1198 | case "source-position":
1199 | reader.skipTag();
1200 | break;
1201 | default:
1202 | warning("Unexpected tag: ", reader.front.value, " in GirUnion: ", name, reader);
1203 | }
1204 | reader.popFront();
1205 | }
1206 | }
1207 |
1208 | string[] getUnionDeclaration()
1209 | {
1210 | string[] buff;
1211 | if ( doc !is null && wrapper.includeComments )
1212 | {
1213 | buff ~= "/**";
1214 | foreach ( line; doc.splitLines() )
1215 | buff ~= " * "~ line.strip();
1216 | buff ~= " */";
1217 | }
1218 |
1219 | if ( name )
1220 | buff ~= "union "~ tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses);
1221 | else
1222 | buff ~= "union";
1223 |
1224 | buff ~= "{";
1225 | buff ~= GirField.getFieldDeclarations(fields, wrapper);
1226 | buff ~= "}";
1227 |
1228 | if ( name )
1229 | buff ~= tokenToGtkD(name.toUpper()[0..1] ~ name[1 .. $], wrapper.aliasses) ~" "~ tokenToGtkD(name.toLower(), wrapper.aliasses) ~";";
1230 |
1231 | return buff;
1232 | }
1233 | }
1234 |
--------------------------------------------------------------------------------