├── 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 | --------------------------------------------------------------------------------