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