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 <http://www.gnu.org/licenses/>.
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 }