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 										status = wrapFile(pack, outPack);
341 									}
342 									if ( prevPack.length>0 && outPack!=prevPack )
343 									{
344 										buildTypedefs(outPack);
345 										buildLoaderTable(outPack, externalDeclarations);
346 										externalDeclarations.length = 0;
347 									}
348 									prevPack = outPack;
349 									break;
350 
351 					case "lookup":
352 									defReaders ~= defReader;
353 									defReader = new DefReader(std.path.buildPath(apiRoot,defReader.getValue()));
354 									defReader.next();
355 									debug(lookup)writefln("lookup on file %s (%s=%s)", defReader.getFileName(), defReader.getKey(), defReader.getValue() );
356 									break;
357 
358 					case "htod":
359 									// not as clean as lookup...
360 									// WARNING!!! writefln's are needed to avoid hang.
361 									writefln("start htod");
362 									new
363 										HTODConvert(defReader.getValue(),outputRoot,apiRoot);
364 									writefln("end htod");
365 									defReader.next();
366 									break;
367 
368 					default:
369 									if ( defReader.getKey().length == 0 )
370 									{
371 										if ( defReaders.length > 0 )
372 										{
373 											defReader = defReaders[defReaders.length-1];
374 											defReaders.length = defReaders.length -1;
375 											debug(lookup)writefln("lookup back to %s (%s=%s)", defReader.getFileName(), defReader.getKey(), defReader.getValue() );
376 											defReader.next();
377 											debug(lookup)writefln("lookup next == %s (%s=%s)", defReader.getFileName(), defReader.getKey(), defReader.getValue() );
378 										}
379 										else
380 										{
381 											defReader = null;
382 										}
383 									}
384 									break;
385 				}
386 				debug(lookup)if(defReader!is null)writefln("loop (%s) %s=%s", defReader.getFileName(), defReader.getKey(), defReader.getValue() );
387 			}
388 		}
389 		else
390 		{
391 			//writefln("status = %s",status);
392 		}
393 
394 		return status;
395 	}
396 
397 	/**
398 	 * Creates an entry on a string[string] associative array
399 	 * Params:
400 	 *      aa =
401 	 *      defReader =
402 	 * Returns:
403 	 */
404 	private static int loadAA(ref string[string] aa, DefReader defReader, WError*[] errors = null)
405 	{
406 		int status = ERR_NONE;
407 		string[] vals = std..string.split(defReader.getValue());
408 		if ( vals.length == 1 )
409 		{
410 			vals ~= "";
411 		}
412 		if ( vals.length == 2 )
413 		{
414 			aa[vals[0]] = vals[1];
415 			debug(aa) writefln("added alias %s = %s", vals[0], vals[1]);
416 		}
417 		else
418 		{
419 			status = ERR_INVALID_ALIAS;
420 			if ( errors !is null )
421 			{
422 				errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Invalid alias");
423 			}
424 		}
425 		return status;
426 	}
427 
428 	/**
429 	 * Creates an entry on a string[][string] associative array
430 	 * Params:
431 	 *      aa =
432 	 *      defReader =
433 	 * Returns:
434 	 */
435 	private static int loadAA(ref string[][string] aa, DefReader defReader, WError*[] errors = null)
436 	{
437 		int status = ERR_NONE;
438 		string[] vals = std..string.split(defReader.getValue());
439 		if ( vals.length == 1 )
440 		{
441 			vals ~= "";
442 		}
443 		if ( vals.length == 2 )
444 		{
445 			aa[vals[0]] ~= vals[1];
446 			debug(aa) writefln("added alias %s = %s", vals[0], vals[1]);
447 		}
448 		else
449 		{
450 			status = ERR_INVALID_ALIAS;
451 			if ( errors !is null )
452 			{
453 				errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Invalid alias");
454 			}
455 		}
456 		return status;
457 	}
458 
459 	/**
460 	 * Creates an entry on a string[][string] associative array
461 	 * Params:
462 	 *      aa =
463 	 *      defReader =
464 	 * Returns:
465 	 */
466 	private static int loadAAA(ref string[string][string] aa, DefReader defReader, WError*[] errors = null)
467 	{
468 		int status = ERR_NONE;
469 		string[] vals = std..string.split(defReader.getValue());
470 		if ( vals.length == 1 )
471 		{
472 			vals ~= "";
473 		}
474 		if ( vals.length == 2 )
475 		{
476 			vals ~= "";
477 		}
478 		if ( vals.length == 3 )
479 		{
480 			aa[vals[0]][vals[1]] ~= vals[2];
481 			debug(aa) writefln("added alias [%s][%s] = %s", vals[0], vals[1], vals[2]);
482 		}
483 		else
484 		{
485 			status = ERR_INVALID_ALIAS;
486 			if ( errors !is null )
487 			{
488 				errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Invalid alias");
489 			}
490 		}
491 		return status;
492 	}
493 
494 	private string loadLicense()
495 	{
496 		license = loadText("license");
497 		return license;
498 	}
499 
500 	private string loadText(string key)
501 	{
502 		string text;
503 
504 		while ( key!=defReader.next(false) && "end"!=defReader.getValue() )
505 		{
506 			if ( text.length > 0 )
507 			{
508 				text ~= "\n" ~ defReader.getFullLine();
509 			}
510 			else
511 			{
512 				text ~= defReader.getFullLine();
513 			}
514 		}
515 
516 		return text;
517 	}
518 
519 	private string[] loadTextMultiLine(string key)
520 	{
521 		string[] text;
522 
523 		while ( key!=defReader.next(false) && "end"!=defReader.getValue() )
524 		{
525 			text ~= defReader.getFullLine();
526 		}
527 
528 		return text;
529 	}
530 
531 	private int copyFile(string fromDir, string toDir, string fileName)
532 	{
533 		debug(writeFile)writefln("GtkWrapper.copyFile %s", fileName);
534 		int status = ERR_NONE;
535 		debug(file)writefln("(1)GtkWrapper.copyFile %s %s", fromDir, fileName);
536 		void[] text = std.file.read(std.path.buildPath(fromDir, fileName));
537 		try
538 		{
539 			//debug(copyFile)
540 			writefln("copying file [%s] to [%s]", std.path.buildPath(fromDir, fileName), std.path.buildPath(toDir, fileName));
541 			if (!std.file.exists(toDir))
542 			{
543 				std.file.mkdir(toDir);
544 			}
545 			std.file.write(std.path.buildPath(toDir, fileName), text);
546 		}
547 		catch ( Exception e)
548 		{
549 			status = ERR_COPY_FILE;
550 			errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Cannot copy  file "~fileName);
551 		}
552 
553 		writefln("Wrappde %s", fileName);
554 
555 		return status;
556 	}
557 
558 	private int wrapFile(string pack, string outPack)
559 	{
560 		debug(file)writefln("GtkWrapper.wrapFile pack=%s outPack=%s", pack, outPack);
561 		int status = ERR_NONE;
562 
563 		GtkDClass gtkDClass;
564 
565 		ConvParms* convParms = new ConvParms;
566 
567 		string text;
568 
569 		string key = defReader.next();
570 
571 		string keys = " file text struct realStruct ctorStruct class template interface extend implements prefix strictPrefix"
572 			" openFile mergeFile closeFile outFile"
573 			" copy import import(tango) structWrap alias moduleAlias override"
574 			" noprefix nostruct nocode nosignal"
575 			" code interfaceCode"
576 			" srcout out inout array"
577 			;
578 		if (outPack == "lib") {string tmp = pack; pack = outPack; outPack = tmp;} //undo Bind hack...oupPack now holds bind dir.
579 		convParms.outPack = outPack;
580 		convParms.bindDir = bindingsDir;
581 
582 		while ( std..string.indexOf(keys, key) > 0 )
583 		{
584 			debug(wrapParameter)writefln("wrapFile [%s] = %s", key, defReader.getValue());
585 			switch ( key )
586 			{
587 				case "copy": status = copyFile(apiRoot,std.path.buildPath(srcOut,outPack),defReader.getValue());
588 							 buildTextLibs ~= "private import " ~outPack~ "." ~defReader.getValue()[0..$-2]~ ";\n";
589 							 break;
590 				case "srcout": srcOut = defReader.getValue(); break;
591 				case "struct": convParms.strct = defReader.getValue(); break;
592 				case "realStruct": convParms.realStrct = defReader.getValue(); break;
593 				case "ctorStruct": convParms.ctorStrct = defReader.getValue(); break;
594 				case "class": convParms.clss = defReader.getValue(); break;
595 				case "extend": convParms.extend = defReader.getValue(); break;
596 				case "implements": convParms.impl ~= defReader.getValue(); break;
597 				case "template": convParms.templ ~= defReader.getValue(); break;
598 				case "prefix": convParms.prefixes ~= defReader.getValue(); break;
599 				case "strictPrefix": convParms.strictPrefix = defReader.getValueBit(); break;
600 				case "noprefix": convParms.noPrefixes ~= defReader.getValue(); break;
601 				case "nocode": convParms.noCode ~= defReader.getValue(); break;
602 				case "nosignal": convParms.noSignals ~= defReader.getValue(); break;
603 				case "nostruct": convParms.noStructs ~= defReader.getValue(); break;
604 				case "import": convParms.imprts ~= defReader.getValue(); break;
605 				case "import(tango)": /*ignore for now*/defReader.getValue(); break;
606 				case "structWrap": loadAA(convParms.structWrap, defReader, errors); break;
607 				case "alias": loadAA(convParms.aliases, defReader, errors); break;
608 				case "moduleAlias": loadAA(convParms.mAliases, defReader, errors); break;
609 				case "override": convParms.overrides ~= defReader.getValue(); break;
610 				case "out": loadAA(convParms.outParms, defReader, errors); break;
611 				case "inout": loadAA(convParms.inoutParms, defReader, errors); break;
612 				case "array": loadAAA(convParms.array, defReader, errors); break;
613 				case "text":
614 							  convParms.text ~= loadTextMultiLine("text");
615 							  break;
616 				case "code":
617 							  convParms.classCode ~= loadText("code");
618 							  break;
619 				case "interfaceCode":
620 							  convParms.interfaceCode ~= loadText("interfaceCode");
621 							  break;
622 
623 				case "openFile":
624 							  gtkDClass = openFile(outPack, text, convParms);
625 							  text.length = 0;
626 							  break;
627 				case "mergeFile":
628 							  gtkDClass.mergeGtkDClass(text, convParms);
629 							  text.length = 0;
630 							  break;
631 				case "closeFile":
632 							  buildText ~= "\nprivate import "
633 								  ~convParms.outPack~"."
634 								  ~defReader.getValue()~";";
635 							  closeFile(text, gtkDClass, convParms);
636 							  text.length = 0;
637 							  break;
638 				case "interface":
639 							  convParms.interf = defReader.getValue();
640 							  string saveClass = convParms.clss;
641 							  string[] saveTempl = convParms.templ;
642 							  convParms.templ.length = 0;
643 							  convParms.outFile = convParms.interf;
644 							  convParms.isInterface = true;
645 							  buildText ~= "\nprivate import "
646 								  ~convParms.outPack~"."
647 								  ~defReader.getValue()~";";
648 							  outFile(outPack, text, convParms);
649 							  convParms.clss = saveClass;
650 							  convParms.templ = saveTempl;
651 							  // mark not interface (anymore)
652 							  convParms.isInterface = false;
653 							  // as outFile is always the last definition
654 							  // there is no need to restore it
655 							  break;
656 				case "outFile":
657 							  buildText ~= "\nprivate import "
658 								  ~convParms.outPack~"."
659 								  ~defReader.getValue()~";";
660 							  outFile(outPack, text, convParms);
661 							  break;
662 				case "file":
663 							  convParms.inFile = std..string.strip(defReader.getValue());
664 							  if ( convParms.inFile.length > 0 )
665 							  {
666 								  if ( startsWith(convParms.inFile, "/") )
667 								  {
668 									  debug(file)writefln("GtkWrapper.wrapFile convParms:\n%s", convParms.toString());
669 									  debug(file)writefln("GtkWrapper.wrapFile convParms:\n%s", defReader.toString());
670 									  debug(file)writefln("(2)GtkWrapper.wrapFile %s", convParms.inFile);
671 									  text = cast(string) std.file.read(convParms.inFile);
672 								  }
673 								  else
674 								  {
675 									  debug(file)writefln("(3)GtkWrapper.wrapFile %s", convParms.inFile);
676 									  text = cast(string) std.file.read(std.path.buildPath(std.path.buildPath(inputRoot,pack),convParms.inFile));
677 								  }
678 							  }
679 							  else
680 							  {
681 								  text.length = 0;
682 							  }
683 							  break;
684 				default:
685 							  status = ERR_FILE_DEFINITION;
686 							  errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Invalid file definition");
687 							  break;
688 			}
689 			key = defReader.next();
690 		}
691 
692 		return status;
693 	}
694 
695 	/**
696 	 * Opens, reads and closes a file
697 	 * Params:
698 	 *      outPack =
699 	 *      text =
700 	 *      convParms =
701 	 */
702 	private void outFile(string outPack, string text, ConvParms* convParms)
703 	{
704 		GtkDClass gtkDClass = openFile(outPack, text, convParms);
705 		closeFile("", gtkDClass, convParms);
706 	}
707 
708 	/**
709 	 * Opens and reads a file
710 	 * Params:
711 	 *      outPack =
712 	 *      text =
713 	 *      convParms =
714 	 * Returns:
715 	 */
716 	private GtkDClass openFile(string outPack, string text, ConvParms* convParms)
717 	{
718 		convParms.outFile = defReader.getValue();
719 		debug(wrapFile)writefln("######### gtkDClass for %s.%s (%s)
720 				bound at %s", outPack, convParms.clss,
721 				convParms.outFile,convParms.bindDir);
722 		GtkDClass gtkDClass = new GtkDClass(this);
723 		convParms.bindDir = bindingsDir;
724 		gtkDClass.openGtkDClass(text, convParms);
725 
726 		return gtkDClass;
727 	}
728 
729 	private void closeFile(string text, GtkDClass gtkDClass, ConvParms* convParms)
730 	{
731 		debug(writeFile)writefln("GtkWrapper.closeFile %s",
732 				gtkDClass.getOutFile(outputRoot, srcDir));
733 		string gtkDText = gtkDClass.closeGtkDClass(text, convParms);
734 		string writeDir = std.path.buildPath(outputRoot, srcDir);
735 		if ( gtkDClass.getError() == 0 )
736 		{
737 			if (!std.file.exists(writeDir))
738 			{
739 				std.file.mkdir(writeDir);
740 			}
741 			std.file.write(gtkDClass.getOutFile(outputRoot,srcDir),gtkDText);
742 		}
743 		writefln("gtk Wrapped %s", gtkDClass.getOutFile(outputRoot, srcDir));
744 		if ( !convParms.isInterface )
745 		{
746 			convParms.clearAll();
747 		}
748 
749 		externalDeclarations ~= gtkDClass.getExternalDeclarations();
750 		collectedAliases ~= gtkDClass.getAliases();
751 		collectedEnums ~=   gtkDClass.getEnums();
752 		collectedFuncts ~=  gtkDClass.getFuncts();
753 		collectedStructs ~= gtkDClass.getStructs();
754 		collectedTypes ~=   gtkDClass.getTypes();
755 		collectedUnions ~=  gtkDClass.getUnions();
756 		collectedConstants ~=   gtkDClass.getConstants();
757 		stockEnums ~= gtkDClass.getStockEnums();
758 		stockChars ~= gtkDClass.getStockChars();
759 		gTypes ~= gtkDClass.getGTypes();
760 	}
761 
762 	/**
763 	 * Assumes input and output packages contains no spaces and are separated by a space
764 	 * Params:
765 	 *      outputRoot =
766 	 *      pack =
767 	 * Returns:
768 	 */
769 	private int createPackage(string outputRoot, string packagevalue)
770 	{
771 		int status = ERR_NONE;
772 
773 		string[] packages = std..string.split(packagevalue, " ");
774 		string packageDir;
775 
776 		if ( packages.length == 1 && "bind" == defReader.getKey() )
777 		{
778 			packages ~="lib";
779 			packageDir = packages[0];
780 		}
781 		else if ( packages.length == 2 && "package" == defReader.getKey() )
782 		{
783 			packageDir = packages[1];
784 			// nothing here
785 		}
786 		else
787 		{
788 			status = ERR_CREATE_PACKAGE;
789 		}
790 
791 		if ( status==ERR_NONE )
792 		{
793 			debug(createPackage)writefln("adding (%s) %s %s", defReader.getKey(), packages[0], packages[1]);
794 			this.packages[packages[0]] = packages[1];
795 			try
796 			{
797 				std.file.mkdir(joinRootDirFile(outputRoot,srcDir,packageDir));
798 			}
799 			catch ( Exception e)
800 			{
801 				//status = 4;
802 				//errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Cannot create src package: "~pack);
803 			}
804 			try
805 			{
806 				std.file.mkdir(joinRootDirFile(outputRoot,"obj",
807 							packageDir));
808 			}
809 			catch ( Exception e)
810 			{
811 				//status = 5;
812 				//errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status, "Cannot create obj package: "~pack);
813 			}
814 		}
815 
816 		if ( status != ERR_NONE )
817 		{
818 			errors ~= WError.create(defReader.fileName, defReader.getLineNumber(), status,
819 					"Invalid package/src definition (need in and out packages or lib name): "~packagevalue);
820 		}
821 
822 		return status;
823 	}
824 
825 	public void printErrors()
826 	{
827 		writefln("printing %s errors", errors.length);
828 		foreach ( WError* error ; errors)
829 		{
830 			error.print();
831 		}
832 	}
833 
834 	public string getLicense()
835 	{
836 		return license;
837 	}
838 
839 	public string[string] getAliases()
840 	{
841 		return aliases;
842 	}
843 
844 	public string[string] getEnumTypes()
845 	{
846 		return enumTypes;
847 	}
848 
849 	public bool includeComments()
850 	{
851 		return currIncludeComments;
852 	}
853 
854 	void buildLoaderTable(string loaderTableName, string[] declarations)
855 	{
856 		string externalText = license;
857 
858 		externalText ~= "\nmodule "~bindingsDir~"."~loaderTableName~";\n"
859 			"\nversion(Tango)"
860 			"\n	private import tango.stdc.stdio;"
861 			"\nelse"
862 			"\n	private import std.stdio;\n"
863 			"\nprivate import "~bindingsDir~"." ~loaderTableName~"types;";
864 
865 		if ( loaderTableName == "glib" )
866 		{
867 			externalText ~= "\nprivate import "~bindingsDir~".gthreadtypes;";
868 		}
869 		if ( loaderTableName == "gdk" || loaderTableName == "pango" )
870 		{
871 			externalText ~= "\nprivate import "~bindingsDir~".cairotypes;";
872 		}
873 
874 		if ( loaderTableName != "gl" &&
875 				loaderTableName != "glu" &&
876 				loaderTableName != "glx"
877 		   )
878 		{
879 			externalText ~= "\nprivate import gtkc.Loader;"
880 				"\nprivate import gtkc.paths;\n"
881 				"\nmixin( _shared ~ \"static this()"
882 				"\n{";
883 
884 			string library = "LIBRARY."~ toUpper(loaderTableName);
885 
886 			//Returns an array of libraries to try and link with.
887 			string getLibrary(string funct)
888 			{
889 				if ( GtkDClass.startsWith(funct, "gdk") )
890 					return library ~ ", LIBRARY.GDKPIXBUF";
891 				else if	( GtkDClass.startsWith(funct, "pango_cairo") )
892 					return library ~ ", LIBRARY.PANGOCAIRO";
893 				else if	( GtkDClass.startsWith(funct, "g_module") )
894 					return library ~ ", LIBRARY.GMODULE";
895 				else
896 					return library;
897 			}
898 
899 			//Generate the static this, where the linking takes place
900 			foreach ( string declaration; declarations )
901 			{
902 				string dec = std..string.strip(declaration);
903 				sizediff_t pos = std..string.lastIndexOf(dec,')');
904 
905 				if (dec.length == 0)
906 					continue;
907 
908 				if ( GtkDClass.startsWith(dec, "//") )
909 					externalText ~= "\n\t"~ dec ~"\n\n";
910 
911 				if ( pos > 0 )
912 				{
913 					externalText ~= '\t';
914 
915 					if ( dec[0]=='#' )
916 					{
917 						externalText ~= "// ";
918 					}
919 					else
920 					{
921 						string functName = std..string.strip(dec[pos+1..$]);
922 						if ( functName.length > 0 )
923 						{
924 							externalText ~= "Linker.link("~ functName ~", \\\""~ functName ~"\\\", "~ getLibrary(functName) ~");";
925 						}
926 					}
927 					externalText ~= '\n';
928 				}
929 			}
930 
931 			externalText ~= "}\");\n\n"
932 				"mixin( gshared ~\"extern(C)\n"
933 				"{";
934 
935 			//Generate the functions.
936 			foreach(string declaration ; declarations)
937 			{
938 				string dec = std..string.strip(declaration);
939 
940 				if (dec.length == 0)
941 					continue;
942 
943 				if ( GtkDClass.startsWith(dec, "//") )
944 				{
945 					externalText ~= "\n\t"~ dec ~"\n\n";
946 					continue;
947 				}
948 
949 				if ( loaderTableName == "glib" || loaderTableName == "pango" ) 
950 					dec = replace(dec, "FILE*", "void*"); //Phobos workaround.
951 
952 				sizediff_t pos = std..string.lastIndexOf(dec,')') + 1;
953 				externalText ~= '\t';
954 
955 				if ( dec[0]=='#' )
956 					externalText ~= "// ";
957 
958 				if ( !GtkDClass.startsWith(dec, "//") && dec[0]!='#' )
959 					externalText ~= dec[0..pos] ~" c_"~ dec[pos..$] ~';';
960 				else
961 					externalText ~= dec;
962 
963 				externalText ~= '\n';
964 			}
965 
966 			externalText ~= "}\");\n";
967 
968 			//Generate the aliases.
969 			foreach ( string declaration; declarations )
970 			{
971 				string dec = std..string.strip(declaration);
972 				sizediff_t pos = std..string.lastIndexOf(dec,')');
973 
974 				if (dec.length == 0)
975 					continue;
976 
977 				if ( GtkDClass.startsWith(dec, "//") )
978 					externalText ~= '\n'~ dec ~"\n\n";
979 
980 				if ( pos > 0 )
981 				{
982 					if ( dec[0]=='#' )
983 					{
984 						externalText ~= "// ";
985 					}
986 					else
987 					{
988 						string functName = std..string.strip(dec[pos+1..$]);
989 						if ( functName.length > 0 )
990 						{
991 							externalText ~= "alias c_"~ functName ~"  "~ functName ~";";
992 						}
993 					}
994 					externalText ~= '\n';
995 				}
996 			}
997 		}
998 
999 		string pathname = joinRootDirFile(std.path.buildPath(outputRoot,srcDir),bindingsDir,loaderTableName~".d");
1000 		std.file.write(pathname,externalText);
1001 	}
1002 
1003 	void buildTypedefs(string outPack)
1004 	{
1005 
1006 		string def = license;
1007 		def ~= "module "~bindingsDir~"."~outPack~"types;\n\n";
1008 
1009 		auto indenter = new IndentedStringBuilder();
1010 
1011 		def ~= indenter.format(lookupTypedefs);
1012 
1013 		if ( gTypes.length > 0 )
1014 		{
1015 			def ~= "\n\n// G_TYPE_*";
1016 			def ~= "\nenum GType : size_t";
1017 			def ~= "\n{\n";
1018 			indenter.setIndent("\t");
1019 			def ~= indenter.format(gTypes);
1020 			def ~= "\n}\n\n";
1021 		}
1022 		indenter.setIndent("");
1023 
1024 		def ~= indenter.format(lookupConstants);
1025 		def ~= indenter.format(lookupAliases);
1026 		def ~= indenter.format(collectedAliases);
1027 
1028 		indenter.setIndent("");
1029 		def ~= indenter.format(lookupEnums);
1030 		def ~= indenter.format(collectedEnums);
1031 
1032 		indenter.setIndent("");
1033 		def ~= indenter.format(lookupStructs);
1034 		def ~= indenter.format(collectedStructs);
1035 
1036 		indenter.setIndent("");
1037 		def ~= indenter.format(lookupTypes);
1038 		def ~= indenter.format(collectedTypes);
1039 
1040 		indenter.setIndent("");
1041 		def ~= indenter.format(lookupFuncts);
1042 		def ~= indenter.format(collectedFuncts);
1043 
1044 		indenter.setIndent("");
1045 		def ~= indenter.format(lookupUnions);
1046 		def ~= indenter.format(collectedUnions);
1047 
1048 		if ( stockEnums.length > 0 )
1049 		{
1050 			def ~= "\n\n// StockIDs";
1051 			def ~= "\nenum StockID";
1052 			def ~= "\n{\n";
1053 			indenter.setIndent("\t");
1054 			def ~= indenter.format(stockEnums);
1055 			def ~= "\n}";
1056 			def ~= "\n\n// Stock strings";
1057 			def ~= "\nstring[] StockDesc = ";
1058 			def ~= "\n[";
1059 			indenter.setIndent("\t");
1060 			def ~= indenter.format(stockChars);
1061 			def ~= "\n];";
1062 		}
1063 		indenter.setIndent("");
1064 		def ~= indenter.format(collectedConstants);
1065 
1066 		string pathname =
1067 			joinRootDirFile(std.path.buildPath(outputRoot,srcDir), bindingsDir, outPack~"types.d");
1068 		std.file.write(pathname,def);
1069 
1070 		lookupTypedefs.length = 0;
1071 		lookupAliases.length = 0;
1072 		lookupEnums.length = 0;
1073 		lookupStructs.length = 0;
1074 		lookupTypes.length = 0;
1075 		lookupFuncts.length = 0;
1076 		lookupUnions.length = 0;
1077 		lookupConstants.length = 0;
1078 
1079 		collectedAliases.length = 0;
1080 		collectedEnums.length = 0;
1081 		collectedStructs.length = 0;
1082 		collectedTypes.length = 0;
1083 		collectedFuncts.length = 0;
1084 		collectedUnions.length = 0;
1085 		collectedConstants.length = 0;
1086 
1087 		stockEnums.length = 0;
1088 		stockChars.length = 0;
1089 		gTypes.length = 0;
1090 	}
1091 }
1092 
1093 int main()
1094 {
1095 	GtkWrapper wrapper = new GtkWrapper("./"); //Run gtkwrapper from the project directory... Make this a config option later.
1096 	int status = wrapper.process("APILookup.txt");
1097 	wrapper.printErrors();
1098 	if ( wrapper.errors.length == 0 )
1099 	{
1100 		wrapper.writeBuildText();
1101 	}
1102 	return status;
1103 }