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.GtkWrapper;
21 
22 //debug=copyFile;
23 //debug=wrapFile;
24 //debug=wrapParameter;
25 //debug=createPackage;
26 //debug=aliases;
27 //debug=lookup;
28 //debug=file;
29 //debug=writeFile;
30 
31 struct WError
32 {
33 	private import std.stdio;
34 
35 	int lineNumber;
36 	int code;
37 	string filename;
38 	string message;
39 
40 
41 	static WError* create(string filename, int lineNumber, int code, string message)
42 	{
43 		WError* error = new WError;
44 		error.lineNumber = lineNumber;
45 		error.code = code;
46 		error.filename = filename;
47 		error.message = message;
48 		return error;
49 	}
50 
51 	void print ()
52 	{
53 		writefln("%s(%s), code %s: %s.", filename, lineNumber, code, message);
54 	}
55 }
56 
57 
58 private import utils.WrapperIF;
59 private import utils.HTODConvert;
60 
61 
62 /*
63 Paths:
64   outputRoot: Where all the wrapper files go.
65   srcDir: Wrapper files subdir (src vs srcgl)
66   inputRoot: Where the html files are
67   apiRoot: Where the API files are
68 
69   buildDir: Where the build files go.
70   bindingsDir: Where the C Bindings files go.
71 
72 */
73 
74 string joinRootDirFile(string root, string dir, string file)
75 {
76 	return std.path.buildPath(std.path.buildPath(root,dir),file);
77 }
78 
79 //Moved here because of dsss:
80 private import utils.DefReader;
81 private import utils.GtkDClass;
82 private import utils.convparms;
83 private import utils.IndentedStringBuilder;
84 
85 /**
86  * converts and wrap the GTK libs
87  */
88 public class GtkWrapper : WrapperIF
89 {
90 	private import std.file;
91 	private import std.path;
92 	private import std.stdio;
93 	private import std.string;
94 	private import std.array;
95 
96 	private string buildText;   /// to build the build.d
97 	private string buildTextLibs;   /// to build the build.d libs
98 
99 	string srcOut = "src";      /// the src output directory
100 	string srcDir;
101 	string apiRoot;
102 	string inputRoot;
103 	string outputRoot;
104 	string buildDir = "build";
105 	string buildPath;
106 	string buildFile =  "gtkD.d";
107 	string bindingsDir;
108 
109 	public static string license;
110 
111 	private bool currIncludeComments;
112 
113 	enum {
114 
115 		ERR_NONE = 1000,
116 		ERR_UNKNOWNE,
117 		ERR_NO_LICENSE,
118 		ERR_INVALID_ALIAS,
119 		ERR_NO_IN_ROOT,
120 		ERR_NO_OUT_ROOT,
121 		ERR_COPY_FILE,
122 		ERR_FILE_DEFINITION,
123 		ERR_CREATE_PACKAGE,
124 	}
125 
126 	string[string] aliases;
127 
128 	string[string] enumTypes;
129 
130 	DefReader defReader;
131 
132 
133 	string[string] packages;
134 
135 	WError*[] errors;
136 
137 	private string[] externalDeclarations;  /// the information to build the loader tables
138 
139 	private string[] collectedAliases;  /// public, module level type aliases
140 	private string[] collectedEnums;    /// public, module level definitions of enums
141 	private string[] stockEnums;        /// special enums for StockID
142 	private string[] stockChars;        /// the string values for StockIDs
143 	private string[] gTypes;            /// special enums for G_TYPE_*
144 	private string[] collectedStructs;  /// public, module level definitions of structs
145 	private string[] collectedTypes;    /// public, module level definitions of other types
146 	private string[] collectedFuncts;   /// public, module level definitions of functions
147 	private string[] collectedUnions;   /// public, module level definitions of unions
148 	private string[] collectedConstants;/// public, module level type contants
149 
150 	private string[] lookupTypedefs;    /// lookup file definitions to be included on the typedefs.d
151 	private string[] lookupAliases;     /// lookup file aliases definitions
152 	private string[] lookupEnums;       /// lookup file enum definitions
153 	private string[] lookupStructs;     /// lookup file struct definitions
154 	private string[] lookupTypes;       /// lookup file type definitions
155 	private string[] lookupFuncts;      /// lookup file functs definitions
156 	private string[] lookupUnions;      /// lookup file unions definitions
157 	private string[] lookupConstants;   /// lookup file constants definitions
158 
159 	DefReader[] defReaders;
160 
161 	this(string apiRoot)
162 	{
163 		this.apiRoot = apiRoot;
164 		//srcOut = "src";
165 		//if (!std.file.exists(std.path.buildPath(outputRoot,srcOut)))
166 		//{
167 		//  std.file.mkdir(std.path.buildPath(outputRoot,srcOut));
168 		//}
169 
170 	}
171 
172 
173 	private void startBuildText()
174 	{
175 		buildText =
176 			"/*"
177 			"\n * Automatically generated build imports from"
178 			"\n * the initial version generouselly given by:"
179 			"\n * John Reimer"
180 			"\n */"
181 			"\n"
182 			"\nmodule build;"
183 			"\n"
184 			"\nversion( build )"
185 			"\n{"
186 			"\n pragma (nolink);"
187 			"\n"
188 			"\n version (Windows)     pragma (target, \"GtkD.lib\"  );"
189 			"\n version (linux)   pragma (target, \"libgtkd.a\" );"
190 			"\n}"
191 			"\n"
192 			;
193 
194 		buildTextLibs.length = 0;
195 
196 	}
197 
198 	public void writeBuildText()
199 	{
200 		string outfile;
201 
202 		buildPath = std.path.buildPath(std.path.buildPath(outputRoot,srcDir),buildDir);
203 		if (!std.file.exists(buildPath))
204 		{
205 			std.file.mkdir(buildPath);
206 
207 		}
208 		outfile = std.path.buildPath(buildPath,buildFile);
209 		writefln("writeBuildText: %s",outfile);
210 
211 		std.file.write(outfile, buildText~"\n\n"~buildTextLibs);
212 	}
213 
214 	int process(string apiLookupDefinition)
215 	{
216 
217 		startBuildText();
218 
219 		int status = ERR_NONE;
220 		defReader = new DefReader(std.path.buildPath(apiRoot,apiLookupDefinition));
221 
222 		if ( status==ERR_NONE && "license" == defReader.next() )
223 		{
224 			loadLicense();
225 		}
226 		else
227 		{
228 			status = ERR_NO_LICENSE;
229 			errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Missing license as the first definition");
230 		}
231 
232 		if ( "includeComments" == defReader.next() )
233 		{
234 			currIncludeComments = defReader.getValueBit();
235 			defReader.next();
236 		}
237 
238 		debug(aliases) writefln("key before alias = ",defReader.getKey());
239 		while ( "alias" == defReader.getKey() )
240 		{
241 			status = loadAA(aliases, defReader, errors);
242 			defReader.next();
243 		}
244 
245 		while ( "enumType" == defReader.getKey() )
246 		{
247 			status = loadAA(enumTypes, defReader, errors);
248 			defReader.next();
249 		}
250 
251 		debug(aliases) writefln("key after alias = ",defReader.getKey());
252 
253 		debug(aliases) foreach(string key ; aliases.keys.sort)
254 		{
255 			writefln("alias %s = %s",key, aliases[key]);
256 		}
257 		if ( status==ERR_NONE && "inputRoot" == defReader.getKey() )
258 		{
259 			inputRoot = defReader.getValue();
260 		}
261 		else
262 		{
263 			status = ERR_NO_IN_ROOT;
264 			errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Cannot determine input root");
265 		}
266 
267 		if ( status==ERR_NONE && "outputRoot" == defReader.next() )
268 		{
269 			outputRoot = defReader.getValue();
270 		}
271 		else
272 		{
273 			status = ERR_NO_OUT_ROOT;
274 			errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Cannot determine output root");
275 		}
276 
277 		string key = defReader.next();
278 
279 		while ( status==ERR_NONE && ( "srcdir" ==  key ))
280 		{
281 			srcDir = defReader.getValue();
282 			string chkdir = std.path.buildPath(outputRoot,srcDir);
283 			if (!std.file.exists(chkdir))
284 			{
285 				std.file.mkdir(chkdir);
286 			}
287 			key = defReader.next();
288 			while ( status==ERR_NONE && ( "package" ==  key || "bind" == key ))
289 			{
290 
291 				status = createPackage(outputRoot, defReader.getValue());
292 				key = defReader.next();
293 			}
294 		}
295 
296 
297 
298 		if ( status==ERR_NONE )
299 		{
300 			string pack;
301 			string outPack;
302 			string prevPack;
303 			while ( status==ERR_NONE && defReader !is null
304 					//&& ("wrap" == defReader.getKey() || "lookup" == defReader.getKey())
305 				  )
306 			{
307 				debug(lookup)writefln("(%s) %s=%s", defReader.getFileName(), defReader.getKey(), defReader.getValue() );
308 				switch ( defReader.getKey() )
309 				{
310 					case "alias": loadAA(aliases, defReader, errors); defReader.next(); break;
311 					case "addTypedefs": lookupTypedefs ~= loadTextMultiLine("addTypedefs"); defReader.next();break;
312 					case "addAliases": lookupAliases ~= loadTextMultiLine("addAliases"); defReader.next();break;
313 					case "addEnums": lookupEnums ~= loadTextMultiLine("addEnums"); defReader.next();break;
314 					case "addStructs": lookupStructs ~= loadTextMultiLine("addStructs"); defReader.next();break;
315 					case "addTypes": lookupTypes ~= loadTextMultiLine("addTypes"); defReader.next();break;
316 					case "addFuncts": lookupFuncts ~= loadTextMultiLine("addFuncts"); defReader.next();break;
317 					case "addUnions": lookupUnions ~= loadTextMultiLine("addUnions"); defReader.next();break;
318 					case "addConstants": lookupConstants ~= loadTextMultiLine("addConstants"); defReader.next();break;
319 					case "enumType": loadAA(enumTypes, defReader, errors); defReader.next(); break;
320 					case "srcout": srcOut =
321 								   defReader.getValue();defReader.next(); break;
322 					case "srcdir": srcDir = defReader.getValue();
323 								   defReader.next();
324 					case "bindDir": bindingsDir = defReader.getValue();
325 									defReader.next();break;
326 					case "wrap":
327 									//package = key packagename
328 									//        = pack outpack
329 									pack = defReader.getValue();
330 
331 									outPack = packages[pack];
332 									debug(lookup)writefln("wrap %s", outPack);
333 									if ( outPack !is null )
334 									{
335 										buildText ~= "\n";
336 										if ( outPack != "lib" )
337 										{
338 											buildTextLibs ~= "private import "~bindingsDir~"."~outPack~";\n";
339 										}
340 										if ( outPack == "glgtk" )
341 										{
342 											buildTextLibs ~= "private import "~bindingsDir~".gl;\n";
343 											buildTextLibs ~= "private import "~bindingsDir~".glu;\n";
344 										}
345 										status = wrapFile(pack, outPack);
346 									}
347 									if ( prevPack.length>0 && outPack!=prevPack )
348 									{
349 										buildTypedefs(outPack);
350 										buildLoaderTable(outPack, externalDeclarations);
351 										externalDeclarations.length = 0;
352 									}
353 									prevPack = outPack;
354 									break;
355 
356 					case "lookup":
357 									defReaders ~= defReader;
358 									defReader = new DefReader(std.path.buildPath(apiRoot,defReader.getValue()));
359 									defReader.next();
360 									debug(lookup)writefln("lookup on file %s (%s=%s)", defReader.getFileName(), defReader.getKey(), defReader.getValue() );
361 									break;
362 
363 					case "htod":
364 									// not as clean as lookup...
365 									// WARNING!!! writefln's are needed to avoid hang.
366 									writefln("start htod");
367 									new
368 										HTODConvert(defReader.getValue(),outputRoot,apiRoot);
369 									writefln("end htod");
370 									defReader.next();
371 									break;
372 
373 					default:
374 									if ( defReader.getKey().length == 0 )
375 									{
376 										if ( defReaders.length > 0 )
377 										{
378 											defReader = defReaders[defReaders.length-1];
379 											defReaders.length = defReaders.length -1;
380 											debug(lookup)writefln("lookup back to %s (%s=%s)", defReader.getFileName(), defReader.getKey(), defReader.getValue() );
381 											defReader.next();
382 											debug(lookup)writefln("lookup next == %s (%s=%s)", defReader.getFileName(), defReader.getKey(), defReader.getValue() );
383 										}
384 										else
385 										{
386 											defReader = null;
387 										}
388 									}
389 									break;
390 				}
391 				debug(lookup)if(defReader!is null)writefln("loop (%s) %s=%s", defReader.getFileName(), defReader.getKey(), defReader.getValue() );
392 			}
393 		}
394 		else
395 		{
396 			//writefln("status = %s",status);
397 		}
398 
399 		return status;
400 	}
401 
402 	/**
403 	 * Creates an entry on a string[string] associative array
404 	 * Params:
405 	 *      aa =
406 	 *      defReader =
407 	 * Returns:
408 	 */
409 	private static int loadAA(ref string[string] aa, DefReader defReader, WError*[] errors = null)
410 	{
411 		int status = ERR_NONE;
412 		string[] vals = std..string.split(defReader.getValue());
413 		if ( vals.length == 1 )
414 		{
415 			vals ~= "";
416 		}
417 		if ( vals.length == 2 )
418 		{
419 			aa[vals[0]] = vals[1];
420 			debug(aa) writefln("added alias %s = %s", vals[0], vals[1]);
421 		}
422 		else
423 		{
424 			status = ERR_INVALID_ALIAS;
425 			if ( errors !is null )
426 			{
427 				errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Invalid alias");
428 			}
429 		}
430 		return status;
431 	}
432 
433 	/**
434 	 * Creates an entry on a string[][string] associative array
435 	 * Params:
436 	 *      aa =
437 	 *      defReader =
438 	 * Returns:
439 	 */
440 	private static int loadAA(ref string[][string] aa, DefReader defReader, WError*[] errors = null)
441 	{
442 		int status = ERR_NONE;
443 		string[] vals = std..string.split(defReader.getValue());
444 		if ( vals.length == 1 )
445 		{
446 			vals ~= "";
447 		}
448 		if ( vals.length == 2 )
449 		{
450 			aa[vals[0]] ~= vals[1];
451 			debug(aa) writefln("added alias %s = %s", vals[0], vals[1]);
452 		}
453 		else
454 		{
455 			status = ERR_INVALID_ALIAS;
456 			if ( errors !is null )
457 			{
458 				errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Invalid alias");
459 			}
460 		}
461 		return status;
462 	}
463 
464 	/**
465 	 * Creates an entry on a string[][string] associative array
466 	 * Params:
467 	 *      aa =
468 	 *      defReader =
469 	 * Returns:
470 	 */
471 	private static int loadAAA(ref string[string][string] aa, DefReader defReader, WError*[] errors = null)
472 	{
473 		int status = ERR_NONE;
474 		string[] vals = std..string.split(defReader.getValue());
475 		if ( vals.length == 1 )
476 		{
477 			vals ~= "";
478 		}
479 		if ( vals.length == 2 )
480 		{
481 			vals ~= "";
482 		}
483 		if ( vals.length == 3 )
484 		{
485 			aa[vals[0]][vals[1]] ~= vals[2];
486 			debug(aa) writefln("added alias [%s][%s] = %s", vals[0], vals[1], vals[2]);
487 		}
488 		else
489 		{
490 			status = ERR_INVALID_ALIAS;
491 			if ( errors !is null )
492 			{
493 				errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Invalid alias");
494 			}
495 		}
496 		return status;
497 	}
498 
499 	private string loadLicense()
500 	{
501 		license = loadText("license");
502 		return license;
503 	}
504 
505 	private string loadText(string key)
506 	{
507 		string text;
508 
509 		while ( key!=defReader.next(false) && "end"!=defReader.getValue() )
510 		{
511 			if ( text.length > 0 )
512 			{
513 				text ~= "\n" ~ defReader.getFullLine();
514 			}
515 			else
516 			{
517 				text ~= defReader.getFullLine();
518 			}
519 		}
520 
521 		return text;
522 	}
523 
524 	private string[] loadTextMultiLine(string key)
525 	{
526 		string[] text;
527 
528 		while ( key!=defReader.next(false) && "end"!=defReader.getValue() )
529 		{
530 			text ~= defReader.getFullLine();
531 		}
532 
533 		return text;
534 	}
535 
536 	private int copyFile(string fromDir, string toDir, string fileName)
537 	{
538 		debug(writeFile)writefln("GtkWrapper.copyFile %s", fileName);
539 		int status = ERR_NONE;
540 		debug(file)writefln("(1)GtkWrapper.copyFile %s %s", fromDir, fileName);
541 		void[] text = std.file.read(std.path.buildPath(fromDir, fileName));
542 		try
543 		{
544 			//debug(copyFile)
545 			writefln("copying file [%s] to [%s]", std.path.buildPath(fromDir, fileName), std.path.buildPath(toDir, fileName));
546 			if (!std.file.exists(toDir))
547 			{
548 				std.file.mkdir(toDir);
549 			}
550 			std.file.write(std.path.buildPath(toDir, fileName), text);
551 		}
552 		catch ( Exception e)
553 		{
554 			status = ERR_COPY_FILE;
555 			errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Cannot copy  file "~fileName);
556 		}
557 
558 		writefln("Wrappde %s", fileName);
559 
560 		return status;
561 	}
562 
563 	private int wrapFile(string pack, string outPack)
564 	{
565 		debug(file)writefln("GtkWrapper.wrapFile pack=%s outPack=%s", pack, outPack);
566 		int status = ERR_NONE;
567 
568 		GtkDClass gtkDClass;
569 
570 		ConvParms* convParms = new ConvParms;
571 
572 		string text;
573 
574 		string key = defReader.next();
575 
576 		string keys = " file text struct realStruct ctorStruct class template interface extend implements prefix strictPrefix"
577 			" openFile mergeFile closeFile outFile"
578 			" copy import structWrap alias moduleAlias override"
579 			" noprefix nostruct nocode nosignal"
580 			" code interfaceCode"
581 			" srcout out inout array"
582 			;
583 		if (outPack == "lib") {string tmp = pack; pack = outPack; outPack = tmp;} //undo Bind hack...oupPack now holds bind dir.
584 		convParms.outPack = outPack;
585 		convParms.bindDir = bindingsDir;
586 
587 		while ( std..string.indexOf(keys, key) > 0 )
588 		{
589 			debug(wrapParameter)writefln("wrapFile [%s] = %s", key, defReader.getValue());
590 			switch ( key )
591 			{
592 				case "copy": status = copyFile(apiRoot,std.path.buildPath(srcOut,outPack),defReader.getValue());
593 							 buildTextLibs ~= "private import " ~outPack~ "." ~defReader.getValue()[0..$-2]~ ";\n";
594 							 break;
595 				case "srcout": srcOut = defReader.getValue(); break;
596 				case "struct": convParms.strct = defReader.getValue(); break;
597 				case "realStruct": convParms.realStrct = defReader.getValue(); break;
598 				case "ctorStruct": convParms.ctorStrct = defReader.getValue(); break;
599 				case "class": convParms.clss = defReader.getValue(); break;
600 				case "extend": convParms.extend = defReader.getValue(); break;
601 				case "implements": convParms.impl ~= defReader.getValue(); break;
602 				case "template": convParms.templ ~= defReader.getValue(); break;
603 				case "prefix": convParms.prefixes ~= defReader.getValue(); break;
604 				case "strictPrefix": convParms.strictPrefix = defReader.getValueBit(); break;
605 				case "noprefix": convParms.noPrefixes ~= defReader.getValue(); break;
606 				case "nocode": convParms.noCode ~= defReader.getValue(); break;
607 				case "nosignal": convParms.noSignals ~= defReader.getValue(); break;
608 				case "nostruct": convParms.noStructs ~= defReader.getValue(); break;
609 				case "import": convParms.imprts ~= defReader.getValue(); break;
610 				case "structWrap": loadAA(convParms.structWrap, defReader, errors); break;
611 				case "alias": loadAA(convParms.aliases, defReader, errors); break;
612 				case "moduleAlias": loadAA(convParms.mAliases, defReader, errors); break;
613 				case "override": convParms.overrides ~= defReader.getValue(); break;
614 				case "out": loadAA(convParms.outParms, defReader, errors); break;
615 				case "inout": loadAA(convParms.inoutParms, defReader, errors); break;
616 				case "array": loadAAA(convParms.array, defReader, errors); break;
617 				case "text":
618 							  convParms.text ~= loadTextMultiLine("text");
619 							  break;
620 				case "code":
621 							  convParms.classCode ~= loadText("code");
622 							  break;
623 				case "interfaceCode":
624 							  convParms.interfaceCode ~= loadText("interfaceCode");
625 							  break;
626 
627 				case "openFile":
628 							  gtkDClass = openFile(outPack, text, convParms);
629 							  text.length = 0;
630 							  break;
631 				case "mergeFile":
632 							  gtkDClass.mergeGtkDClass(text, convParms);
633 							  text.length = 0;
634 							  break;
635 				case "closeFile":
636 							  buildText ~= "\nprivate import "
637 								  ~convParms.outPack~"."
638 								  ~defReader.getValue()~";";
639 							  closeFile(text, gtkDClass, convParms);
640 							  text.length = 0;
641 							  break;
642 				case "interface":
643 							  convParms.interf = defReader.getValue();
644 							  string saveClass = convParms.clss;
645 							  string[] saveTempl = convParms.templ;
646 							  convParms.templ.length = 0;
647 							  convParms.outFile = convParms.interf;
648 							  convParms.isInterface = true;
649 							  buildText ~= "\nprivate import "
650 								  ~convParms.outPack~"."
651 								  ~defReader.getValue()~";";
652 							  outFile(outPack, text, convParms);
653 							  convParms.clss = saveClass;
654 							  convParms.templ = saveTempl;
655 							  // mark not interface (anymore)
656 							  convParms.isInterface = false;
657 							  // as outFile is always the last definition
658 							  // there is no need to restore it
659 							  break;
660 				case "outFile":
661 							  buildText ~= "\nprivate import "
662 								  ~convParms.outPack~"."
663 								  ~defReader.getValue()~";";
664 							  outFile(outPack, text, convParms);
665 							  break;
666 				case "file":
667 							  convParms.inFile = std..string.strip(defReader.getValue());
668 							  if ( convParms.inFile.length > 0 )
669 							  {
670 								  if ( startsWith(convParms.inFile, "/") )
671 								  {
672 									  debug(file)writefln("GtkWrapper.wrapFile convParms:\n%s", convParms.toString());
673 									  debug(file)writefln("GtkWrapper.wrapFile convParms:\n%s", defReader.toString());
674 									  debug(file)writefln("(2)GtkWrapper.wrapFile %s", convParms.inFile);
675 									  text = cast(string) std.file.read(convParms.inFile);
676 								  }
677 								  else
678 								  {
679 									  debug(file)writefln("(3)GtkWrapper.wrapFile %s", convParms.inFile);
680 									  text = cast(string) std.file.read(std.path.buildPath(std.path.buildPath(inputRoot,pack),convParms.inFile));
681 								  }
682 							  }
683 							  else
684 							  {
685 								  text.length = 0;
686 							  }
687 							  break;
688 				default:
689 							  status = ERR_FILE_DEFINITION;
690 							  errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Invalid file definition");
691 							  break;
692 			}
693 			key = defReader.next();
694 		}
695 
696 		return status;
697 	}
698 
699 	/**
700 	 * Opens, reads and closes a file
701 	 * Params:
702 	 *      outPack =
703 	 *      text =
704 	 *      convParms =
705 	 */
706 	private void outFile(string outPack, string text, ConvParms* convParms)
707 	{
708 		GtkDClass gtkDClass = openFile(outPack, text, convParms);
709 		closeFile("", gtkDClass, convParms);
710 	}
711 
712 	/**
713 	 * Opens and reads a file
714 	 * Params:
715 	 *      outPack =
716 	 *      text =
717 	 *      convParms =
718 	 * Returns:
719 	 */
720 	private GtkDClass openFile(string outPack, string text, ConvParms* convParms)
721 	{
722 		convParms.outFile = defReader.getValue();
723 		debug(wrapFile)writefln("######### gtkDClass for %s.%s (%s)
724 				bound at %s", outPack, convParms.clss,
725 				convParms.outFile,convParms.bindDir);
726 		GtkDClass gtkDClass = new GtkDClass(this);
727 		convParms.bindDir = bindingsDir;
728 		gtkDClass.openGtkDClass(text, convParms);
729 
730 		return gtkDClass;
731 	}
732 
733 	private void closeFile(string text, GtkDClass gtkDClass, ConvParms* convParms)
734 	{
735 		debug(writeFile)writefln("GtkWrapper.closeFile %s",
736 				gtkDClass.getOutFile(outputRoot, srcDir));
737 		string gtkDText = gtkDClass.closeGtkDClass(text, convParms);
738 		string writeDir = std.path.buildPath(outputRoot, srcDir);
739 		if ( gtkDClass.getError() == 0 )
740 		{
741 			if (!std.file.exists(writeDir))
742 			{
743 				std.file.mkdir(writeDir);
744 			}
745 			std.file.write(gtkDClass.getOutFile(outputRoot,srcDir),gtkDText);
746 		}
747 		writefln("gtk Wrapped %s", gtkDClass.getOutFile(outputRoot, srcDir));
748 		if ( !convParms.isInterface )
749 		{
750 			convParms.clearAll();
751 		}
752 
753 		externalDeclarations ~= gtkDClass.getExternalDeclarations();
754 		collectedAliases ~= gtkDClass.getAliases();
755 		collectedEnums ~=   gtkDClass.getEnums();
756 		collectedFuncts ~=  gtkDClass.getFuncts();
757 		collectedStructs ~= gtkDClass.getStructs();
758 		collectedTypes ~=   gtkDClass.getTypes();
759 		collectedUnions ~=  gtkDClass.getUnions();
760 		collectedConstants ~=   gtkDClass.getConstants();
761 		stockEnums ~= gtkDClass.getStockEnums();
762 		stockChars ~= gtkDClass.getStockChars();
763 		gTypes ~= gtkDClass.getGTypes();
764 	}
765 
766 	/**
767 	 * Assumes input and output packages contains no spaces and are separated by a space
768 	 * Params:
769 	 *      outputRoot =
770 	 *      pack =
771 	 * Returns:
772 	 */
773 	private int createPackage(string outputRoot, string packagevalue)
774 	{
775 		int status = ERR_NONE;
776 
777 		string[] packages = std..string.split(packagevalue, " ");
778 		string packageDir;
779 
780 		if ( packages.length == 1 && "bind" == defReader.getKey() )
781 		{
782 			packages ~="lib";
783 			packageDir = packages[0];
784 		}
785 		else if ( packages.length == 2 && "package" == defReader.getKey() )
786 		{
787 			packageDir = packages[1];
788 			// nothing here
789 		}
790 		else
791 		{
792 			status = ERR_CREATE_PACKAGE;
793 		}
794 
795 		if ( status==ERR_NONE )
796 		{
797 			debug(createPackage)writefln("adding (%s) %s %s", defReader.getKey(), packages[0], packages[1]);
798 			this.packages[packages[0]] = packages[1];
799 			try
800 			{
801 				std.file.mkdir(joinRootDirFile(outputRoot,srcDir,packageDir));
802 			}
803 			catch ( Exception e)
804 			{
805 				//status = 4;
806 				//errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Cannot create src package: "~pack);
807 			}
808 			try
809 			{
810 				std.file.mkdir(joinRootDirFile(outputRoot,"obj",
811 							packageDir));
812 			}
813 			catch ( Exception e)
814 			{
815 				//status = 5;
816 				//errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Cannot create obj package: "~pack);
817 			}
818 		}
819 
820 		if ( status != ERR_NONE )
821 		{
822 			errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status,
823 					"Invalid package/src definition (need in and out packages or lib name): "~packagevalue);
824 		}
825 
826 		return status;
827 	}
828 
829 	public void printErrors()
830 	{
831 		writefln("printing %s errors", errors.length);
832 		foreach ( WError* error ; errors)
833 		{
834 			error.print();
835 		}
836 	}
837 
838 	public string getLicense()
839 	{
840 		return license;
841 	}
842 
843 	public string[string] getAliases()
844 	{
845 		return aliases;
846 	}
847 
848 	public string[string] getEnumTypes()
849 	{
850 		return enumTypes;
851 	}
852 
853 	public bool includeComments()
854 	{
855 		return currIncludeComments;
856 	}
857 
858 	void buildLoaderTable(string loaderTableName, string[] declarations)
859 	{
860 		string externalText = license;
861 
862 		externalText ~= "\nmodule "~bindingsDir~"."~loaderTableName~";\n"
863 			"\nprivate import std.stdio;"
864 			"\nprivate import "~bindingsDir~"." ~loaderTableName~"types;";
865 
866 		if ( loaderTableName == "glib" )
867 		{
868 			externalText ~= "\nprivate import "~bindingsDir~".gthreadtypes;";
869 		}
870 		if ( loaderTableName == "gdk" || loaderTableName == "pango" )
871 		{
872 			externalText ~= "\nprivate import "~bindingsDir~".cairotypes;";
873 		}
874 
875 		if ( loaderTableName != "gl" &&
876 				loaderTableName != "glu" &&
877 				loaderTableName != "glx"
878 		   )
879 		{
880 			externalText ~= "\nprivate import gtkc.Loader;"
881 				"\nprivate import gtkc.paths;\n"
882 				"\nshared static this()"
883 				"\n{";
884 
885 			string library = "LIBRARY."~ toUpper(loaderTableName);
886 
887 			//Returns an array of libraries to try and link with.
888 			string getLibrary(string funct)
889 			{
890 				if ( GtkDClass.startsWith(funct, "gdk") )
891 					return library ~ ", LIBRARY.GDKPIXBUF";
892 				else if	( GtkDClass.startsWith(funct, "pango_cairo") )
893 					return library ~ ", LIBRARY.PANGOCAIRO";
894 				else if	( GtkDClass.startsWith(funct, "g_module") )
895 					return library ~ ", LIBRARY.GMODULE";
896 				else
897 					return library;
898 			}
899 			
900 			string getUtfPostfix(string funct)
901 			{
902 				if ( funct == "g_module_open" ||
903 					funct == "g_module_name" )
904 					return "\"~ _utfPostfix ~\"";
905 					
906 				return "";
907 			}
908 
909 			//Generate the static this, where the linking takes place
910 			foreach ( string declaration; declarations )
911 			{
912 				string dec = std..string.strip(declaration);
913 				sizediff_t pos = std..string.lastIndexOf(dec,')');
914 
915 				if (dec.length == 0)
916 					continue;
917 
918 				if ( GtkDClass.startsWith(dec, "//") )
919 					externalText ~= "\n\t"~ dec ~"\n\n";
920 
921 				if ( pos > 0 )
922 				{
923 					externalText ~= '\t';
924 
925 					if ( dec[0]=='#' )
926 					{
927 						externalText ~= "// ";
928 					}
929 					else
930 					{
931 						string functName = std..string.strip(dec[pos+1..$]);
932 						if ( functName.length > 0 && (functName == "g_module_open" || functName == "g_module_name") )
933 						{
934 							externalText ~= "mixin(\"Linker.link("~ functName ~", \\\""~ functName ~ "\"~ _utfPostfix ~\"\\\", "~ getLibrary(functName) ~");\");";
935 						}
936 						else if ( functName.length > 0 )
937 						{
938 							externalText ~= "Linker.link("~ functName ~", \""~ functName ~"\", "~ getLibrary(functName) ~");";
939 						}
940 					}
941 					externalText ~= '\n';
942 				}
943 			}
944 
945 			externalText ~= "}\n\n"
946 				"__gshared extern(C)\n"
947 				"{";
948 
949 			//Generate the functions.
950 			foreach(string declaration ; declarations)
951 			{
952 				string dec = std..string.strip(declaration);
953 
954 				if (dec.length == 0)
955 					continue;
956 
957 				if ( GtkDClass.startsWith(dec, "//") )
958 				{
959 					externalText ~= "\n\t"~ dec ~"\n\n";
960 					continue;
961 				}
962 
963 				if ( loaderTableName == "glib" || loaderTableName == "pango" ) 
964 					dec = replace(dec, "FILE*", "void*"); //Phobos workaround.
965 
966 				sizediff_t pos = std..string.lastIndexOf(dec,')') + 1;
967 				externalText ~= '\t';
968 
969 				if ( dec[0]=='#' )
970 					externalText ~= "// ";
971 
972 				if ( !GtkDClass.startsWith(dec, "//") && dec[0]!='#' )
973 					externalText ~= dec[0..pos] ~" c_"~ dec[pos..$] ~';';
974 				else
975 					externalText ~= dec;
976 
977 				externalText ~= '\n';
978 			}
979 
980 			externalText ~= "}\n";
981 
982 			//Generate the aliases.
983 			foreach ( string declaration; declarations )
984 			{
985 				string dec = std..string.strip(declaration);
986 				sizediff_t pos = std..string.lastIndexOf(dec,')');
987 
988 				if (dec.length == 0)
989 					continue;
990 
991 				if ( GtkDClass.startsWith(dec, "//") )
992 					externalText ~= '\n'~ dec ~"\n\n";
993 
994 				if ( pos > 0 )
995 				{
996 					if ( dec[0]=='#' )
997 					{
998 						externalText ~= "// ";
999 					}
1000 					else
1001 					{
1002 						string functName = std..string.strip(dec[pos+1..$]);
1003 						if ( functName.length > 0 )
1004 						{
1005 							externalText ~= "alias c_"~ functName ~"  "~ functName ~";";
1006 						}
1007 					}
1008 					externalText ~= '\n';
1009 				}
1010 			}
1011 		}
1012 
1013 		string pathname = joinRootDirFile(std.path.buildPath(outputRoot,srcDir),bindingsDir,loaderTableName~".d");
1014 		std.file.write(pathname,externalText);
1015 	}
1016 
1017 	void buildTypedefs(string outPack)
1018 	{
1019 
1020 		string def = license;
1021 		def ~= "module "~bindingsDir~"."~outPack~"types;\n\n";
1022 
1023 		auto indenter = new IndentedStringBuilder();
1024 
1025 		def ~= indenter.format(lookupTypedefs);
1026 
1027 		if ( gTypes.length > 0 )
1028 		{
1029 			def ~= "\n\n// G_TYPE_*";
1030 			def ~= "\nenum GType : size_t";
1031 			def ~= "\n{\n";
1032 			indenter.setIndent("\t");
1033 			def ~= indenter.format(gTypes);
1034 			def ~= "\n}\n\n";
1035 		}
1036 		indenter.setIndent("");
1037 
1038 		def ~= indenter.format(lookupConstants);
1039 		def ~= indenter.format(lookupAliases);
1040 		def ~= indenter.format(collectedAliases);
1041 
1042 		indenter.setIndent("");
1043 		def ~= indenter.format(lookupEnums);
1044 		def ~= indenter.format(collectedEnums);
1045 
1046 		indenter.setIndent("");
1047 		def ~= indenter.format(lookupStructs);
1048 		def ~= indenter.format(collectedStructs);
1049 
1050 		indenter.setIndent("");
1051 		def ~= indenter.format(lookupTypes);
1052 		def ~= indenter.format(collectedTypes);
1053 
1054 		indenter.setIndent("");
1055 		def ~= indenter.format(lookupFuncts);
1056 		def ~= indenter.format(collectedFuncts);
1057 
1058 		indenter.setIndent("");
1059 		def ~= indenter.format(lookupUnions);
1060 		def ~= indenter.format(collectedUnions);
1061 
1062 		if ( stockEnums.length > 0 )
1063 		{
1064 			def ~= "\n\n// StockIDs";
1065 			def ~= "\nenum StockID";
1066 			def ~= "\n{\n";
1067 			indenter.setIndent("\t");
1068 			def ~= indenter.format(stockEnums);
1069 			def ~= "\n}";
1070 			def ~= "\n\n// Stock strings";
1071 			def ~= "\nstring[] StockDesc = ";
1072 			def ~= "\n[";
1073 			indenter.setIndent("\t");
1074 			def ~= indenter.format(stockChars);
1075 			def ~= "\n];";
1076 		}
1077 		indenter.setIndent("");
1078 		def ~= indenter.format(collectedConstants);
1079 
1080 		string pathname =
1081 			joinRootDirFile(std.path.buildPath(outputRoot,srcDir), bindingsDir, outPack~"types.d");
1082 		std.file.write(pathname,def);
1083 
1084 		lookupTypedefs.length = 0;
1085 		lookupAliases.length = 0;
1086 		lookupEnums.length = 0;
1087 		lookupStructs.length = 0;
1088 		lookupTypes.length = 0;
1089 		lookupFuncts.length = 0;
1090 		lookupUnions.length = 0;
1091 		lookupConstants.length = 0;
1092 
1093 		collectedAliases.length = 0;
1094 		collectedEnums.length = 0;
1095 		collectedStructs.length = 0;
1096 		collectedTypes.length = 0;
1097 		collectedFuncts.length = 0;
1098 		collectedUnions.length = 0;
1099 		collectedConstants.length = 0;
1100 
1101 		stockEnums.length = 0;
1102 		stockChars.length = 0;
1103 		gTypes.length = 0;
1104 	}
1105 }
1106 
1107 int main()
1108 {
1109 	GtkWrapper wrapper = new GtkWrapper("./"); //Run gtkwrapper from the project directory... Make this a config option later.
1110 	int status = wrapper.process("APILookup.txt");
1111 	wrapper.printErrors();
1112 	if ( wrapper.errors.length == 0 )
1113 	{
1114 		wrapper.writeBuildText();
1115 	}
1116 	return status;
1117 }