1 /* 2 * This file is part of gtkD. 3 * 4 * gtkD 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, with 8 * some exceptions, please read the COPYING file. 9 * 10 * gtkD is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public License 16 * along with gtkD; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA 18 */ 19 20 module utils.GtkPackage; 21 22 import std.algorithm; 23 import std.array: empty; 24 import std.file; 25 import std.path; 26 import std.string : splitLines, strip, split; 27 import std.uni; 28 import std.stdio; 29 30 import utils.GtkAlias; 31 import utils.GtkEnum; 32 import utils.GtkFunction; 33 import utils.GtkStruct; 34 import utils.GtkWrapper; 35 import utils.IndentedStringBuilder; 36 import utils.XML; 37 import utils.LinkedHasMap: Map = LinkedHashMap; 38 39 class GtkPackage 40 { 41 string name; 42 string cTypePrefix; 43 string srcDir; 44 string bindDir; 45 GtkWrapper wrapper; 46 47 string[] publicImports; 48 string[] lookupAliases; /// Aliases defined in the lookupfile. 49 string[] lookupEnums; /// Enums defined in the lookupfile. 50 string[] lookupStructs; /// Structs defined in the lookupfile. 51 string[] lookupFuncts; /// Functions defined in the lookupfile. 52 string[] lookupConstants; /// Constants defined in the lookupfile. 53 54 static GtkPackage[string] namespaces; 55 56 Map!(string, GtkAlias) collectedAliases; /// Aliases defined in the gir file. 57 Map!(string, GtkEnum) collectedEnums; /// Enums defined in the gir file. 58 Map!(string, GtkStruct) collectedStructs; 59 Map!(string, GtkFunction) collectedCallbacks; 60 Map!(string, GtkFunction) collectedFunctions; 61 GtkEnum stockIDs; /// The StockID enum (Deprecated). 62 GtkEnum GdkKeys; /// The GdkKey enum. 63 64 public this(string pack, GtkWrapper wrapper, string srcDir, string bindDir) 65 { 66 this.name = pack; 67 this.wrapper = wrapper; 68 this.srcDir = srcDir; 69 this.bindDir = bindDir; 70 this.stockIDs = new GtkEnum(wrapper, this); 71 this.GdkKeys = new GtkEnum(wrapper, this); 72 73 try 74 { 75 if ( !exists(buildPath(wrapper.outputRoot, srcDir, bindDir)) ) 76 mkdirRecurse(buildPath(wrapper.outputRoot, srcDir, bindDir)); 77 } 78 catch (Exception) 79 { 80 throw new Exception("Failed to create directory: "~ buildPath(wrapper.outputRoot, srcDir, bindDir)); 81 } 82 83 try 84 { 85 if ( !exists(buildPath(wrapper.outputRoot, srcDir, pack)) ) 86 mkdirRecurse(buildPath(wrapper.outputRoot, srcDir, pack)); 87 } 88 catch (Exception) 89 { 90 throw new Exception("Failed to create directory: "~ buildPath(wrapper.outputRoot, srcDir, pack)); 91 } 92 93 publicImports ~= bindDir ~"."~ pack; 94 } 95 96 void parseGIR(string girFile) 97 { 98 if ( !isAbsolute(girFile) ) 99 girFile = buildNormalizedPath("/usr/share/gir-1.0", girFile); 100 101 auto reader = new XMLReader!string(readText(girFile)); 102 103 while ( !reader.empty && reader.front.value != "repository" ) 104 reader.popFront(); 105 106 reader.popFront(); 107 108 while ( !reader.empty && reader.front.value == "include" ) 109 { 110 //TODO: parse imports. 111 112 reader.popFront(); 113 } 114 115 while ( !reader.empty && reader.front.value != "namespace" ) 116 reader.popFront(); 117 118 namespaces[reader.front.attributes["name"]] = this; 119 cTypePrefix = reader.front.attributes["c:identifier-prefixes"]; 120 121 reader.popFront(); 122 123 while ( !reader.empty && !reader.endTag("namespace") ) 124 { 125 if ( reader.front.type == XMLNodeType.EndTag ) 126 { 127 reader.popFront(); 128 continue; 129 } 130 131 switch (reader.front.value) 132 { 133 case "alias": 134 GtkAlias gtkAlias = new GtkAlias(wrapper); 135 gtkAlias.parse(reader); 136 137 if ( gtkAlias.cType == "GType" ) 138 break; 139 140 collectedAliases[gtkAlias.name] = gtkAlias; 141 break; 142 case "bitfield": 143 case "enumeration": 144 GtkEnum gtkEnum = new GtkEnum(wrapper, this); 145 gtkEnum.parse(reader); 146 collectedEnums[gtkEnum.name] = gtkEnum; 147 break; 148 case "class": 149 case "interface": 150 case "record": 151 case "union": 152 GtkStruct gtkStruct = new GtkStruct(wrapper, this); 153 gtkStruct.parse(reader); 154 155 //Workaround: Dont overwrite the regular pango classes. 156 if ( gtkStruct.cType.among("PangoCairoFont", "PangoCairoFontMap") ) 157 { 158 collectedStructs["FcFontMap"].merge(gtkStruct); 159 break; 160 } 161 162 collectedStructs[gtkStruct.name] = gtkStruct; 163 164 if ( name == "pango" ) 165 gtkStruct.name = "Pg"~gtkStruct.name; 166 break; 167 case "callback": 168 GtkFunction callback = new GtkFunction(wrapper, null); 169 callback.parse(reader); 170 collectedCallbacks[callback.name] = callback; 171 break; 172 case "constant": 173 parseConstant(reader); 174 break; 175 case "function": 176 parseFunction(reader); 177 break; 178 default: 179 assert(false, "Unexpected tag: "~ reader.front.value); 180 } 181 reader.popFront(); 182 } 183 } 184 185 void parseConstant(T)(XMLReader!T reader) 186 { 187 if ( reader.front.attributes["name"].startsWith("STOCK_") ) 188 { 189 GtkEnumMember member = GtkEnumMember(wrapper); 190 member.parse(reader); 191 member.name = member.name[6..$]; 192 193 stockIDs.members ~= member; 194 return; 195 } 196 else if ( reader.front.attributes["c:type"].startsWith("GDK_KEY_") ) 197 { 198 GtkEnumMember member = GtkEnumMember(wrapper); 199 member.parse(reader); 200 member.name = "GDK_"~ member.name[4..$]; 201 202 GdkKeys.members ~= member; 203 return; 204 } 205 206 //TODO: other constants. 207 reader.skipTag(); 208 } 209 210 void parseFunction(T)(XMLReader!T reader) 211 { 212 GtkFunction funct = new GtkFunction(wrapper, null); 213 funct.parse(reader); 214 collectedFunctions[funct.name] = funct; 215 } 216 217 GtkStruct getStruct(string name) 218 { 219 GtkPackage pack = this; 220 221 if ( name.canFind(".") ) 222 { 223 string[] vals = name.split("."); 224 225 if ( vals[0] !in namespaces ) 226 return null; 227 228 pack = namespaces[vals[0]]; 229 name = vals[1]; 230 } 231 return pack.collectedStructs.get(name, pack.collectedStructs.get("lookup"~name, null)); 232 } 233 234 GtkEnum getEnum(string name) 235 { 236 GtkPackage pack = this; 237 238 if ( name.canFind(".") ) 239 { 240 string[] vals = name.split("."); 241 242 if ( vals[0] !in namespaces ) 243 return null; 244 245 pack = namespaces[vals[0]]; 246 name = vals[1]; 247 } 248 return pack.collectedEnums.get(name, null); 249 } 250 251 void writeClasses() 252 { 253 foreach ( strct; collectedStructs ) 254 strct.writeClass(); 255 } 256 257 void writeTypes() 258 { 259 string buff = wrapper.licence; 260 auto indenter = new IndentedStringBuilder(); 261 262 buff ~= "module "~ bindDir ~"."~ name ~"types;\n\n"; 263 264 buff ~= indenter.format(lookupAliases); 265 foreach ( a; collectedAliases ) 266 { 267 buff ~= "\n"; 268 buff ~= indenter.format(a.getAliasDeclaration()); 269 } 270 271 buff ~= indenter.format(lookupEnums); 272 foreach ( e; collectedEnums ) 273 { 274 buff ~= "\n"; 275 buff ~= indenter.format(e.getEnumDeclaration()); 276 } 277 278 buff ~= indenter.format(lookupStructs); 279 foreach ( s; collectedStructs ) 280 { 281 if ( s.noExternal || s.noDecleration ) 282 continue; 283 284 buff ~= "\n"; 285 buff ~= indenter.format(s.getStructDeclaration()); 286 } 287 288 buff ~= indenter.format(lookupFuncts); 289 foreach ( f; collectedCallbacks ) 290 { 291 buff ~= "\n"; 292 buff ~= indenter.format(f.getCallbackDeclaration()); 293 } 294 295 buff ~= indenter.format(lookupConstants); 296 if ( stockIDs.members !is null ) 297 { 298 stockIDs.cName = "StockID"; 299 stockIDs.doc = "StockIds"; 300 buff ~= "\n"; 301 buff ~= indenter.format(stockIDs.getEnumDeclaration()); 302 } 303 304 if ( GdkKeys.members !is null ) 305 writeGdkKeys(); 306 307 std.file.write(buildPath(wrapper.outputRoot, srcDir, bindDir, name ~"types.d"), buff); 308 } 309 310 void writeGdkKeys() 311 { 312 string buff = wrapper.licence; 313 314 buff ~= "module "~ name ~".Keysyms;\n\n"; 315 316 buff ~= "/**\n"; 317 buff ~= " * GdkKeysyms.\n"; 318 buff ~= " */\n"; 319 buff ~= "public enum GdkKeysyms\n"; 320 buff ~= "{\n"; 321 322 foreach ( member; GdkKeys.members ) 323 { 324 buff ~= "\t"~ tokenToGtkD(member.name, wrapper.aliasses, false) ~" = "~ member.value ~",\n"; 325 } 326 327 buff ~= "}\n"; 328 329 std.file.write(buildPath(wrapper.outputRoot, srcDir, name, "Keysyms.d"), buff); 330 } 331 332 void writeLoaderTable() 333 { 334 string buff = wrapper.licence; 335 336 buff ~= "module "~ bindDir ~"."~ name ~";\n\n"; 337 buff ~= "import std.stdio;\n"; 338 buff ~= "import "~ bindDir ~"."~ name ~"types;\n"; 339 340 if ( name == "glib" ) 341 buff ~= "import gtkc.gobjecttypes;\n"; 342 if ( name == "gdk" || name == "pango" ) 343 buff ~= "import gtkc.cairotypes;\n"; 344 345 buff ~= "import gtkc.Loader;\n" 346 ~ "import gtkc.paths;\n\n" 347 ~ "shared static this()\n" 348 ~ "{"; 349 350 foreach ( strct; collectedStructs ) 351 { 352 if ( strct.functions.empty || strct.noExternal ) 353 continue; 354 355 buff ~= "\n\t// "~ name ~"."~ strct.name ~"\n\n"; 356 357 foreach ( funct; strct.functions ) 358 { 359 if ( funct.type == GtkFunctionType.Callback || funct.type == GtkFunctionType.Signal || funct.name.empty ) 360 continue; 361 362 buff ~= "\tLinker.link("~ funct.cType ~", \""~ funct.cType ~"\", "~ getLibrary(funct.cType) ~");\n"; 363 } 364 } 365 366 buff ~= "}\n\n" 367 ~ "__gshared extern(C)\n" 368 ~ "{\n"; 369 370 foreach ( strct; collectedStructs ) 371 { 372 if ( strct.functions.empty || strct.noExternal ) 373 continue; 374 375 buff ~= "\n\t// "~ name ~"."~ strct.name ~"\n\n"; 376 377 foreach ( funct; strct.functions ) 378 { 379 if ( funct.type == GtkFunctionType.Callback || funct.type == GtkFunctionType.Signal || funct.name.empty ) 380 continue; 381 382 buff ~= "\t"~ funct.getExternal() ~"\n"; 383 } 384 } 385 386 buff ~= "}\n\n"; 387 388 foreach ( strct; collectedStructs ) 389 { 390 if ( strct.functions.empty || strct.noExternal ) 391 continue; 392 393 buff ~= "\n// "~ name ~"."~ strct.name ~"\n\n"; 394 395 foreach ( funct; strct.functions ) 396 { 397 if ( funct.type == GtkFunctionType.Callback || funct.type == GtkFunctionType.Signal || funct.name.empty ) 398 continue; 399 400 buff ~= "alias c_"~ funct.cType ~" "~ funct.cType ~";\n"; 401 } 402 } 403 404 std.file.write(buildPath(wrapper.outputRoot, srcDir, bindDir, name ~".d"), buff); 405 } 406 407 private string getLibrary(string funct) 408 { 409 string library = "LIBRARY."~ name.toUpper(); 410 411 if ( startsWith(funct, "gdk") && !startsWith(funct, "gdk_gl") ) 412 return library ~ ", LIBRARY.GDKPIXBUF"; 413 else if ( startsWith(funct, "pango_cairo") ) 414 return library ~ ", LIBRARY.PANGOCAIRO"; 415 else if ( startsWith(funct, "g_module") ) 416 return library ~ ", LIBRARY.GMODULE"; 417 else 418 return library; 419 } 420 }