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), 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 "glib:boxed": 143 reader.skipTag(); 144 break; 145 case "bitfield": 146 case "enumeration": 147 GtkEnum gtkEnum = new GtkEnum(wrapper, this); 148 gtkEnum.parse(reader); 149 collectedEnums[gtkEnum.name] = gtkEnum; 150 break; 151 case "class": 152 case "interface": 153 case "record": 154 case "union": 155 GtkStruct gtkStruct = new GtkStruct(wrapper, this); 156 gtkStruct.parse(reader); 157 158 //Workaround: Dont overwrite the regular pango classes. 159 if ( gtkStruct.cType.among("PangoCairoFont", "PangoCairoFontMap") ) 160 { 161 collectedStructs["FcFontMap"].merge(gtkStruct); 162 break; 163 } 164 165 collectedStructs[gtkStruct.name] = gtkStruct; 166 167 if ( name == "pango" ) 168 gtkStruct.name = "Pg"~gtkStruct.name; 169 break; 170 case "callback": 171 GtkFunction callback = new GtkFunction(wrapper, null); 172 callback.parse(reader); 173 collectedCallbacks[callback.name] = callback; 174 break; 175 case "constant": 176 parseConstant(reader); 177 break; 178 case "function": 179 parseFunction(reader); 180 break; 181 default: 182 throw new XMLException(reader, "Unexpected tag: "~ reader.front.value ~" in GtkPackage: "~ name); 183 } 184 reader.popFront(); 185 } 186 } 187 188 void parseConstant(T)(XMLReader!T reader) 189 { 190 if ( reader.front.attributes["name"].startsWith("STOCK_") ) 191 { 192 GtkEnumMember member = GtkEnumMember(wrapper); 193 member.parse(reader); 194 member.name = member.name[6..$]; 195 196 stockIDs.members ~= member; 197 return; 198 } 199 else if ( reader.front.attributes["c:type"].startsWith("GDK_KEY_") ) 200 { 201 GtkEnumMember member = GtkEnumMember(wrapper); 202 member.parse(reader); 203 member.name = "GDK_"~ member.name[4..$]; 204 205 GdkKeys.members ~= member; 206 return; 207 } 208 209 //TODO: other constants. 210 reader.skipTag(); 211 } 212 213 void parseFunction(T)(XMLReader!T reader) 214 { 215 GtkFunction funct = new GtkFunction(wrapper, null); 216 funct.parse(reader); 217 collectedFunctions[funct.name] = funct; 218 } 219 220 GtkStruct getStruct(string name) 221 { 222 GtkPackage pack = this; 223 224 if ( name.canFind(".") ) 225 { 226 string[] vals = name.split("."); 227 228 if ( vals[0] !in namespaces ) 229 return null; 230 231 pack = namespaces[vals[0]]; 232 name = vals[1]; 233 } 234 return pack.collectedStructs.get(name, pack.collectedStructs.get("lookup"~name, null)); 235 } 236 237 GtkEnum getEnum(string name) 238 { 239 GtkPackage pack = this; 240 241 if ( name.canFind(".") ) 242 { 243 string[] vals = name.split("."); 244 245 if ( vals[0] !in namespaces ) 246 return null; 247 248 pack = namespaces[vals[0]]; 249 name = vals[1]; 250 } 251 return pack.collectedEnums.get(name, null); 252 } 253 254 void writeClasses() 255 { 256 foreach ( strct; collectedStructs ) 257 strct.writeClass(); 258 } 259 260 void writeTypes() 261 { 262 string buff = wrapper.licence; 263 auto indenter = new IndentedStringBuilder(); 264 265 buff ~= "module "~ bindDir ~"."~ name ~"types;\n\n"; 266 267 buff ~= indenter.format(lookupAliases); 268 foreach ( a; collectedAliases ) 269 { 270 buff ~= "\n"; 271 buff ~= indenter.format(a.getAliasDeclaration()); 272 } 273 274 buff ~= indenter.format(lookupEnums); 275 foreach ( e; collectedEnums ) 276 { 277 buff ~= "\n"; 278 buff ~= indenter.format(e.getEnumDeclaration()); 279 } 280 281 buff ~= indenter.format(lookupStructs); 282 foreach ( s; collectedStructs ) 283 { 284 if ( s.noExternal || s.noDecleration ) 285 continue; 286 287 buff ~= "\n"; 288 buff ~= indenter.format(s.getStructDeclaration()); 289 } 290 291 buff ~= indenter.format(lookupFuncts); 292 foreach ( f; collectedCallbacks ) 293 { 294 buff ~= "\n"; 295 buff ~= indenter.format(f.getCallbackDeclaration()); 296 } 297 298 buff ~= indenter.format(lookupConstants); 299 if ( stockIDs.members !is null ) 300 { 301 stockIDs.cName = "StockID"; 302 stockIDs.doc = "StockIds"; 303 buff ~= "\n"; 304 buff ~= indenter.format(stockIDs.getEnumDeclaration()); 305 } 306 307 if ( GdkKeys.members !is null ) 308 writeGdkKeys(); 309 310 std.file.write(buildPath(wrapper.outputRoot, srcDir, bindDir, name ~"types.d"), buff); 311 } 312 313 void writeGdkKeys() 314 { 315 string buff = wrapper.licence; 316 317 buff ~= "module "~ name ~".Keysyms;\n\n"; 318 319 buff ~= "/**\n"; 320 buff ~= " * GdkKeysyms.\n"; 321 buff ~= " */\n"; 322 buff ~= "public enum GdkKeysyms\n"; 323 buff ~= "{\n"; 324 325 foreach ( member; GdkKeys.members ) 326 { 327 buff ~= "\t"~ tokenToGtkD(member.name, wrapper.aliasses, false) ~" = "~ member.value ~",\n"; 328 } 329 330 buff ~= "}\n"; 331 332 std.file.write(buildPath(wrapper.outputRoot, srcDir, name, "Keysyms.d"), buff); 333 } 334 335 void writeLoaderTable() 336 { 337 string buff = wrapper.licence; 338 339 buff ~= "module "~ bindDir ~"."~ name ~";\n\n"; 340 buff ~= "import std.stdio;\n"; 341 buff ~= "import "~ bindDir ~"."~ name ~"types;\n"; 342 343 if ( name == "glib" ) 344 buff ~= "import gtkc.gobjecttypes;\n"; 345 if ( name == "gdk" || name == "pango" ) 346 buff ~= "import gtkc.cairotypes;\n"; 347 348 buff ~= "import gtkc.Loader;\n" 349 ~ "import gtkc.paths;\n\n" 350 ~ "shared static this()\n" 351 ~ "{"; 352 353 foreach ( strct; collectedStructs ) 354 { 355 if ( strct.functions.empty || strct.noExternal ) 356 continue; 357 358 buff ~= "\n\t// "~ name ~"."~ strct.name ~"\n\n"; 359 360 foreach ( funct; strct.functions ) 361 { 362 if ( funct.type == GtkFunctionType.Callback || funct.type == GtkFunctionType.Signal || funct.name.empty ) 363 continue; 364 365 buff ~= "\tLinker.link("~ funct.cType ~", \""~ funct.cType ~"\", "~ getLibrary(funct.cType) ~");\n"; 366 } 367 } 368 369 buff ~= "}\n\n" 370 ~ "__gshared extern(C)\n" 371 ~ "{\n"; 372 373 foreach ( strct; collectedStructs ) 374 { 375 if ( strct.functions.empty || strct.noExternal ) 376 continue; 377 378 buff ~= "\n\t// "~ name ~"."~ strct.name ~"\n\n"; 379 380 foreach ( funct; strct.functions ) 381 { 382 if ( funct.type == GtkFunctionType.Callback || funct.type == GtkFunctionType.Signal || funct.name.empty ) 383 continue; 384 385 buff ~= "\t"~ funct.getExternal() ~"\n"; 386 } 387 } 388 389 buff ~= "}\n\n"; 390 391 foreach ( strct; collectedStructs ) 392 { 393 if ( strct.functions.empty || strct.noExternal ) 394 continue; 395 396 buff ~= "\n// "~ name ~"."~ strct.name ~"\n\n"; 397 398 foreach ( funct; strct.functions ) 399 { 400 if ( funct.type == GtkFunctionType.Callback || funct.type == GtkFunctionType.Signal || funct.name.empty ) 401 continue; 402 403 if (name == "glgdk") 404 buff ~= "alias glc_"~ funct.cType ~" "~ funct.cType ~";\n"; 405 else 406 buff ~= "alias c_"~ funct.cType ~" "~ funct.cType ~";\n"; 407 } 408 } 409 410 std.file.write(buildPath(wrapper.outputRoot, srcDir, bindDir, name ~".d"), buff); 411 } 412 413 private string getLibrary(string funct) 414 { 415 string library = "LIBRARY."~ name.toUpper(); 416 417 if ( startsWith(funct, "gdk") && !startsWith(funct, "gdk_gl") ) 418 return library ~ ", LIBRARY.GDKPIXBUF"; 419 else if ( startsWith(funct, "pango_cairo") ) 420 return library ~ ", LIBRARY.PANGOCAIRO"; 421 else if ( startsWith(funct, "g_module") ) 422 return library ~ ", LIBRARY.GMODULE"; 423 else 424 return library; 425 } 426 }