2 // Copyright Artur Skawina 2012.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
18 // Compile in tracing support? (also configurable at runtime)
21 ////////////////////////////////////////////////////////////
23 enum PACKAGE_NAME
= "gtk2";
24 // Module naming notes:
25 // - The filenames of modules needs to be lowercase, to work with
26 // broken filesystems.
27 // - D's module system is primitive, so this was designed to keep things
28 // as simple as possible (eg not using absolute module names can result
29 // in problems, either in the module files or apps that import them in
31 // - The original mixed-caps module names should be used in the modules.
34 string
[] cprotos
; // All seen functions and C "methods".
36 auto ew(S
...)(S args
) { return stderr
.writeln(args
); }
40 void trace(string cl
, string c
, string m
) {
41 // Note: this intentionally writes to stdout - we want
42 // to see the trace in context of the generated output
44 writefln("//TRACE:%s(%s) %s", cl
, c
, m
);
47 static if (!is(typeof(trace
))) {
48 void trace(string cl
, string c
, string m
) { }
51 // ["ab", "cd", "ef", "gh", "ij"] <= "ab,cd, ef, gh , ij"
52 auto csv2arr(scope string s
) { return array( map
!strip( split(s
, ",") ) ); }
57 private static immutable spaces
=
59 @property string
s() { return spaces
[0..level
]; }
61 string
opUnary(string op
:"++")() { level
+= by
; return s
; }
62 string
opUnary(string op
:"--")() { level
-= by
; assert (level
>=0); return s
; }
66 private string
nodkeyword(scope string s
) {
67 // Associative array literals are non-const expressions, so...
69 case "double": return "double_";
70 case "string": return "string_";
71 case "union": return "union_";
72 case "module": return "module_";
73 case "int": return "int_";
74 case "in": return "in_";
75 case "out": return "out_";
76 case "ref": return "ref_";
77 case "function": return "function_";
78 case "foreach": return "foreach_";
79 case "delete": return "delete_";
80 case "continue": return "continue_";
81 case "new": return "new_";
82 case "export": return "export_";
83 case "body": return "body_";
84 case "version": return "version_";
85 case "scope": return "scope_";
86 case "break": return "break_";
87 case "false": return "false_";
88 case "true": return "true_";
89 case "alias": return "alias_";
90 case "main": return "main_";
91 case "default": return "default_";
92 case "interface": return "interface_";
95 // "signals" ie gtk callback functions have '-' both
96 // in function names and args...
97 s
= replace(s
, "-", "_");
99 // Unfortunately shortening the names of some enums leaves
100 // them with a leading digit, so turn "2BIG" into "_2BIG":
102 if (s
&& s
.length
>0 && isDigit(s
[0]))
107 private string
escdquote(scope string s
) {
109 return replace(s
, "\\", "\\\\");
113 // From namespace tags:
114 string
[] ident_prefixes
; // eg. ["G"];
115 string
[] symbol_prefixes
; // eg. ["g", "glib"]
117 // Figure out the native D type from the gtktype and ctype.
118 // "signals" turns on heuristics, which isn't needed elsewhere,
119 // but the signal callback descriptions are lacking vital info...
120 string
Dtype(scope string gt
, scope string ct
, bool signals
=false) {
125 // Some gtk signals have /no/ info; at least don't break the build.
129 // If we're given an array, strip the "[n]" part, do the conversion,
130 // then append the "[n]" back and return the result. Keeps things simple.
131 if (gt
.length
>=1 && gt
[$-1]==']') {
133 auto spos
= std
.string
.indexOf(gt
, '[');
135 slen
= gt
.length
- spos
;
136 if (slen
>0 && gt
[$-slen
..$]==ct
[$-slen
..$]) {
137 string suf
= gt
[$-slen
..$];
138 return Dtype(gt
[0..$-slen
], ct
[0..$-slen
]) ~ suf
;
141 // Ditto for pointers; note - only the ctype has the right type
142 // (number of '*'). Keep in mind arrays of pointers exist.
143 if (ct
.length
>=1 && ct
[$-1]=='*' && gt
[$-1]!='*') {
145 auto apos
= std
.string
.indexOf(ct
, '*');
147 alen
= ct
.length
- apos
;
148 string suf
= ct
[$-alen
..$];
149 return Dtype(gt
, ct
[0..$-alen
]) ~ suf
;
152 // Obvious conversions first:
153 if (gt
=="gint8" && ct
=="gint8") return "byte";
154 if (gt
=="guint8" && ct
=="guint8") return "ubyte";
155 if (gt
=="guint8" && ct
=="gchar") return "ubyte";
156 if (gt
=="guint8" && ct
=="guchar") return "ubyte";
157 if (gt
=="guint8" && ct
=="unsigned char") return "ubyte";
158 if (gt
=="gint16" && ct
=="gint16") return "short";
159 if (gt
=="guint16" && ct
=="guint16") return "ushort";
160 if (gt
=="gint32" && ct
=="gint32") return "int";
161 if (gt
=="guint32" && ct
=="guint32") return "uint";
162 if (gt
=="gint64" && ct
=="gint64") return "long";
163 if (gt
=="guint64" && ct
=="guint64") return "ulong";
166 if (gt
=="gshort" && ct
=="gshort") return "short";
167 if (gt
=="gshort" && ct
=="short") return "short";
168 if (gt
=="gushort" && ct
=="gushort") return "ushort";
171 if (gt
=="gint" && ct
=="signed") return "c_int";
172 if (gt
=="gint" && ct
=="int") return "c_int";
173 if (gt
=="gint" && ct
=="gint") return "c_int";
174 if (gt
=="gint" && !ct
) return "c_int"; // gtk2 needs it.
175 if (gt
=="guint" && ct
=="guint") return "c_uint";
176 if (gt
=="guint" && ct
=="unsigned int") return "c_uint";
177 if (gt
=="guint" && ct
=="unsigned") return "c_uint";
179 // Assumes 32-bit wide target ints, but is far more readable...
180 if (gt
=="gint" && ct
=="signed") return "int";
181 if (gt
=="gint" && ct
=="int") return "int";
182 if (gt
=="gint" && ct
=="gint") return "int";
183 if (gt
=="gint" && !ct
) return "int"; // gtk2 needs it.
184 if (gt
=="guint" && ct
=="guint") return "uint";
185 if (gt
=="guint" && ct
=="unsigned int") return "uint";
186 if (gt
=="guint" && ct
=="unsigned") return "uint";
188 if (gt
=="gfloat" && ct
=="gfloat") return "float";
189 if (gt
=="gfloat" && ct
=="float") return "float";
190 if (gt
=="gdouble" && ct
=="double") return "double";
191 if (gt
=="gdouble" && ct
=="gdouble") return "double";
192 if (gt
=="long double" && ct
=="long double") return "real";
194 if (gt
=="none" && ct
=="void") return "void";
196 if (gt
=="gunichar" && ct
=="gunichar") return "dchar";
197 if (gt
=="guint16" && ct
=="gunichar2") return "wchar";
199 if (gt
=="glong" && ct
=="long") return "c_long";
200 if (gt
=="glong" && ct
=="glong") return "c_long";
201 if (gt
=="gulong" && ct
=="gulong") return "c_ulong";
202 if (gt
=="gulong" && ct
=="unsigned long") return "c_ulong";
204 if (gt
=="gsize" && ct
=="gsize") return "size_t";
205 if (gt
=="gssize" && ct
=="gssize") return "ssize_t";
206 if (gt
=="gint64" && ct
=="goffset") return "long"; // signed 64bit int, C99: off64_t
208 if (gt
=="gchar" && ct
=="gchar") return "char";
209 if (gt
=="gchar" && ct
=="char") return "char";
210 if (gt
=="gchar" && ct
=="guchar") return "ubyte";
212 if (gt
=="utf8" && ct
=="char") return "char";
213 if (gt
=="utf8" && ct
=="gchar") return "char";
215 if (gt
=="guint8" && ct
=="char") return "ubyte";
217 if (gt
=="gpointer" && ct
=="void") return "void";
218 if (gt
=="gpointer" && !ct
) return "void*"; // GTK2
219 if (gt
=="gpointer" && ct
=="gpointer") return "void*";
221 if (gt
=="guint8" && ct
=="void") return "ubyte"; // used for buffer *'s.
222 if (gt
=="guint8" && ct
=="gpointer") return "void*";
223 if (gt
=="gpointer" && ct
=="gconstpointer") return "const(void)*";
225 if (gt
=="guint8" && ct
=="gconstpointer") return "const(ubyte)*";
226 if (gt
=="Variant" && ct
=="gconstpointer") return "const(Variant)*";
227 if (gt
=="VariantType" && ct
=="gconstpointer") return "const(VariantType)*";
229 if (gt
=="Object" && ct
=="gpointer") return "Object*";
230 if (gt
=="TypeClass" && ct
=="gpointer") return "TypeClass*";
231 if (gt
=="TypeInterface" && ct
=="gpointer") return "TypeInterface*";
233 if (gt
=="DBusObjectManagerClient" && ct
=="GDBusObjectManager") return "DBusObjectManagerClient";
236 if (gt
=="gboolean" && ct
=="gboolean") return "int";
237 if (gt
=="glong" && ct
=="time_t") return "time_t"; // well...
239 if (gt
=="guint" && ct
=="uid_t") return "uint"; // Good enough?
242 if (gt
=="gpointer" && ct
=="GArray") return "Array";
243 if (gt
=="gpointer" && ct
=="GByteArray") return "ByteArray";
244 if (gt
=="gpointer" && ct
=="GPtrArray") return "PtrArray";
245 if (gt
=="gpointer" && ct
=="FILE") return "FILE";
247 if (gt
=="guint8" && ct
=="GByteArray") return "ByteArray";
249 // Not even the module names are used consistently...
251 void fixupmodnames(scope string from
, scope string to
) {
253 auto fl
= from
.length
+1; // '+1' cause of the trailing '.'.
254 if (gt
.length
>=fl
&& gt
[0..fl
]==from
~ ".") {
255 gt
= to
~ "." ~ gt
[fl
..$];
257 // Don't change ct for "GObject.ObjectClass" "GObjectClass"
258 if (ct
.length
>1 && orggt
[fl
..$]==ct
[1..$])
262 if (ct
.length
>fl
&& from
== ct
[0..fl
])
267 fixupmodnames("GLib", "GLib2");
268 fixupmodnames("GdkPixbuf", "GdkPixbuf2");
269 fixupmodnames("GObject", "GObject2");
270 fixupmodnames("Gio", "Gio2");
271 fixupmodnames("Gdk", "Gdk2");
273 if (gt
=="va_list" && ct
=="va_list") return "va_list";
276 if (gt
=="GType" && ct
=="GType") return "Type";
277 if (gt
=="GObject2.Object" && ct
=="gpointer") return "GObject2.Object*";
279 if (gt
=="GdkPixbuf2.Pixbuf" && ct
=="GdkPixbuf") return "GdkPixbuf2.Pixbuf"; // in gdk
281 if (gt
=="cairo.Surface" && ct
=="cairo_surface_t") return "cairo.Surface"; // in gdk
282 if (gt
=="cairo.Context" && ct
=="cairo_t") return "cairo.Context"; // in gdk
283 if (gt
=="cairo.FontOptions" && ct
=="cairo_font_options_t") return "cairo.FontOptions"; // in gdk
284 if (gt
=="cairo.Content" && ct
=="cairo_content_t") return "cairo.Content"; // in gdk
285 if (gt
=="cairo.Pattern" && ct
=="cairo_pattern_t") return "cairo.Pattern"; // in gdk
288 // "Pango.Font" "PangoFont*" => "Pango.Font*"; // in gdk
289 // "Pango.GlyphString" "PangoGlyphString*" => "Pango.GlyphString*"; // in gdk
290 // "Pango.Direction" "PangoDirection" => "Pango.Direction"
292 auto mlen
= countUntil(gt
, ".");
293 if (mlen
>0 && ct
.length
>mlen
&& gt
[0..mlen
]==ct
[0..mlen
]) {
294 auto ctapos
= countUntil(ct
, "*");
297 if (ctapos
>0 && gt
[mlen
+1..$]==ct
[mlen
..ctapos
] )
298 return gt
~ ct
[ctapos
..$];
302 // ATK needs this one, as the datatype is missing both name and definition; this lets it build.
303 if (gt
=="AtkPropertyValues*" && ct
=="AtkPropertyValues*") return "_PropertyValues*";
306 if (gt
=="GdkPixbuf2.PixbufAlphaMode" && ct
=="GdkPixbuf2AlphaMode") return "GdkPixbuf2.PixbufAlphaMode";
309 if (gt
=="filename" && ct
=="gchar") return "char"; // Probably const, but...
310 if (gt
=="filename" && !ct
) return "char*"; // Probably const, but...
312 if (gt
=="GdkPixbuf2.PixbufAnimation" && ct
=="GdkPixbuf2Animation") return "GdkPixbuf2.PixbufAnimation";
313 if (gt
=="GdkPixbuf2.PixbufAnimationIter" && ct
=="GdkPixbuf2AnimationIter") return "GdkPixbuf2.PixbufAnimationIter";
316 // In fact if this is for a signal and has no <ct> it probably should be pointer...
317 // We're gonna guess, and we'll be wrong sometimes. But we will be right in many
318 // cases and there isn't really much else that we could do...
319 if (signals
&& !ct
&& gt
[$-1]!='*') {
320 if (gt
=="GObject2.Error") return "GLib2.Error*";
322 if (std
.string
.indexOf(gt
,'.')!=-1) return gt
~ "*";
323 if (gt
[0]>='A' && gt
[0]<='Z') return gt
~ "*";
326 case "gfloat": return "float";
327 case "gdouble": return "double";
328 case "none": return "void";
329 case "utf8": return "char*";
330 case "gint": return "c_int";
331 case "guint": return "c_uint";
332 case "gboolean": return "c_int";
337 // Fix up some obviously broken types.
339 if (gt
=="Object") return "Object*";
342 version(all
) { // Disable this while debugging.
347 if (gt
&& (!ct || ct
==""))
348 return gt
; // what else?...
350 enforce(gt
&& ct
, "Missing type: GTK: \"" ~ gt
~ "\" C: \"" ~ ct
~ "\"");
352 foreach (prefix
; ident_prefixes
) {
353 auto l
= prefix
.length
;
354 if (ct
.length
>l
&& ct
[0..l
]==prefix
) {
355 // Try Quark" "GQuark -> "Quark"
356 string stripped_ct
= ct
[l
..$];
362 // Last try: if ctype starts with 'G' and last dot-delimited component
363 // of gtktype equals the post-G ctype, use that.
364 // Eg. "Gio.AppLaunchContext", "GAppLaunchContext" => "Gio.AppLaunchContext"
366 auto tail
= "." ~ ct
[1..$];
367 if(gt
.length
>=tail
.length
&& gt
[$-tail
.length
..$] == tail
)
371 enforce(0, "Unknown type: GTK: \"" ~ gt
~ "\" C: \"" ~ ct
~ "\"");
372 assert(0); // @noreturn
375 // Set string <dst> to <aa[index]>, but only if <aa> contains such value.
376 // if <req> is given assert if the value is missing.
377 void aassign(ref string dst
, in string
[string
] aa
, in string index
, string req
=null) {
378 if (auto p
= index
in aa
)
381 enforce(0, "Missing " ~ req
~ "; Available: " ~ to
!string(aa
));
383 string
get_name_attr(in Element e
) {
385 if (auto p
= "name" in tag
.attr
) {
386 // Debugging checks, harmless, can be left enabled.
387 if (*p
!="GLib.List" && *p
!="GLib.SList"&& *p
!="GLib.HashTable")
388 enforce(tag
.type
==TagType
.EMPTY
, tag
.toString());
389 // <alias>es trigger this one.
390 //enforce(tag.attr.length==1, to!string(tag.attr.length));
396 string
simplify_version(scope string v
) pure {
399 else if (v
[1..$]==".0")
405 string tagname
, doc
, dochead
, ver
;
407 this(string s
) { tagname
= s
; }
409 final void parse_doc_text(ElementParser ep
) {
410 ep
.onText
= (string s
) {
413 doc
~= "\n" ~ dochead
~ ": ";
416 SkipEndTag(ep
, "doc");
420 final void register_doc_parser(ElementParser ep
, string dochead
=null) {
421 ep
.onStartTag
["doc"] = &parse_doc_text
;
425 if (auto p
= "version" in tag
.attr
)
427 this.dochead
= dochead
;
429 final string
comment_to_D() {
433 while (doc
[$-1]=='\n')
437 r
= "\n// " ~ replace(doc
, "\n", "\n// ");
438 r
= replace(r
, "\n// ", "\n" ~ indent
~ "// ") ~ "\n";
439 // Remove the newline before one-line comments.
440 import std
.algorithm
;
441 if (count(r
, '\n')<=2)
447 final void parsearray(ElementParser p
, ref string _type
, ref string _ctype
,
448 bool makepointer
=0) {
449 p
.onStartTag
["array"] = (ElementParser ep
) {
450 aassign(_ctype
, ep
.tag().attr
, "c:type");
453 ep
.onStartTag
["type"] = (ElementParser ep
) { ep
.parse(); };
454 ep
.onEndTag
["type"] = (in Element e
) {
455 trace("ELEMARR", tagname
, e
.tag
.toString());
456 aassign(_type
, e
.tag
.attr
, "name");
463 final class x_include
: x_elem
{
466 this(string s
) { super(s
); }
468 void parse(in Element e
) {
470 enforce(tag
.attr
.length
==2);
471 name
= tag
.attr
["name"];
472 ver
= tag
.attr
["version"];
475 override string
toString() {
476 string modulebasename
= name
~ simplify_version(ver
);
477 string modulebasenamel
= toLower(modulebasename
);
478 // If we use "import GLib2" the compiler will fail to find the module
479 // while building the other ones.
480 // If we use "import gtk2.GLib2" the compiler will complain about
481 // missing Glib2.* types.
482 // NOTE: Imports are "public" so that apps don't have to import Gdk
483 // just for types only used for callback args etc.
484 return indent
~ "public import " ~ PACKAGE_NAME
~ "." ~ modulebasenamel
~ ";\n"
485 ~ indent
~ "alias " ~ PACKAGE_NAME
~ "." ~ modulebasenamel
~ " "
486 ~ modulebasename
~ ";";
490 string thismodulename
;
492 final class x_namespace
: x_elem
{
495 this(string s
) { super(s
); }
497 void parse(ElementParser ep
) {
498 auto attr
= ep
.tag().attr
;
500 ver
= attr
["version"];
504 if (auto p
= "c:symbol-prefixes" in attr
) {
505 symbol_prefixes
= csv2arr(*p
);
506 writeln("// c:symbol-prefixes: ", symbol_prefixes
);
508 if (auto p
= "c:identifier-prefixes" in attr
) {
509 ident_prefixes
= csv2arr(*p
);
510 write("// c:identifier-prefixes: ", ident_prefixes
, "\n\n");
513 // We do not parse further, ie no ep.parse() here.
516 override string
toString() {
517 thismodulename
= name
~ simplify_version(ver
);
518 string s
= indent
~ "// module " ~ thismodulename
~ ";\n";
519 // Per module fixups:
520 s
~= load_mixin("_MODULE_HEAD");
527 // "ALNUM" <= shortenconst("alnum", "G_ASCII_ALNUM")
528 string
shortenconst(scope string name
, scope string cname
) {
529 if (cname
.length
>=name
.length
) {
530 auto shortname
= cname
[($-name
.length
)..$];
531 if (shortname
==toUpper(name
))
532 return nodkeyword(shortname
);
537 final class x_bitfield
: x_elem
{
541 this(string s
) { super(s
); }
543 void parse(ElementParser ep
) {
544 trace("BITFIELD", tagname
, ep
.tag().toString());
546 name
= tag
.attr
["name"];
548 register_doc_parser(ep
);
549 ep
.onStartTag
["member"] = (ElementParser ep
) {};
550 ep
.onEndTag
["member"] = (in Element e
) {
551 enforce(e
.tag
.type
==TagType
.EMPTY
, e
.tag
.toString());
553 auto m
= new x_field("member");
556 auto mattr
= e
.tag
.attr
;
557 aassign(m
.name
, mattr
, "name", "bitfield name");
558 aassign(m
.value
, mattr
, "value", "bitfield value");
559 aassign(m
.identifier
, mattr
, "c:identifier", "bitfield c:identifier");
562 ep
.onStartTag
["function"] = (ElementParser ep
) {
563 ew("FIXME: Skipping enum func: ", name
, ".", ep
.tag().attr
["name"]);
564 // a gobject-introspection update made functions appear inside
565 // <enumeration> tags. We could handle this by using struct
566 // namespaces, but let's ignore these "functions" for now.
569 ep
.onEndTag
["function"] = (in Element e
) {
575 override string
toString() {
576 // Do we care about the basetype (ie do 31bits+ values happen?)
577 string r
= comment_to_D() ~ indent
~ "enum " ~ name
;
579 r
~= " /* Version" ~ ver
~ " */";
583 foreach (i
, m
; members
) {
584 long v
= to
!long(m
.value
);
586 m
.value
= "cast(uint)" ~ m
.value
;
587 r
~= indent
~ shortenconst(m
.name
, m
.identifier
) ~ " = " ~ m
.value
~
588 (i
!=members
.length
-1 ?
"," : "") ~ "\n";
591 return r
~ --indent
~ "}";
595 x_field gerrfield
; // pseudo field; used by functions that "throw" in gtk-speak.
597 gerrfield
= new x_field("GError");
598 gerrfield
.type
= "GLib2.Error**";
599 gerrfield
.name
= "error=null";
600 gerrfield
.cname
= "error";
603 final class x_field
: x_elem
{
604 string type
, name
, cname
;
605 string value
; // for constants
606 string identifier
; // for bitfields
608 bool varargs
; // true if this parameter is "..."
612 x_struct struct_
; // embedded struct/union;
613 x_function callback
; // a callback, ie embedded function pointer.
614 x_elem docparent
; // parent element, where we place our doc.
615 int bitfield
; // if >0 then we need to handle it specially...
617 this(string s
, x_elem parent
=null) { super(s
); docparent
=parent
;}
619 override string
toString() {
620 string r
= comment_to_D();
622 if (auto p
= name
in delete_symbol
)
623 return "// " ~ tagname
~ " \"" ~ name
~ "\" removed: " ~ *p
;
626 return r
~ struct_
.toString();
632 r
~= indent
~ "enum " ~ name
~ " = \"" ~ escdquote(value
) ~ "\";";
635 string contype
= Dtype(type
, ctype
);
637 // "int MUTEX_DEBUG_MAGIC = cast(int)4175530711;" needs the cast...
639 if (contype
=="int") {
640 long conval
= to
!long(value
);
641 if (conval
>int.max
&& conval
<=uint.max
)
642 concast
= "cast(int)";
644 r
~= indent
~ "enum " ~ contype
~ " " ~ name
~ " = " ~ concast
~ value
~ ";";
648 string dtype
= Dtype(type
, ctype
);
649 // We're renaming types, and that can cause problems, as in Gtk2.Type case.
652 r
~= indent
~ "alias " ~ dtype
~ " " ~ name
~ ";" /*~ "\t// " ~ ctype*/;
656 // case "field": /* Fields in classes/structs are handled in containers. */
658 enforce(0, "Unknown field type: " ~ tagname
);
663 void parse(ElementParser ep
) {
664 trace("FIELD", tagname
, ep
.tag().toString());
665 SkipEndTag(ep
, tagname
);
668 auto field_attr
= tag
.attr
;
669 aassign(name
, field_attr
, "name");
670 aassign(value
, field_attr
, "value"); // Constants.
673 docparent
.register_doc_parser(ep
, name ?
"<" ~ name
~ ">": "PARAM");
675 register_doc_parser(ep
);
677 if (auto p
= "private" in field_attr
)
680 if (auto p
= "bits" in field_attr
) // Bitfields need special treatment.
681 bitfield
= to
!int(*p
);
683 ep
.onStartTag
["type"] = (ElementParser ep
) {};
684 ep
.onEndTag
["type"] = (in Element e
) {
686 aassign(ctype
, a
, "c:type");
687 if (tagname
=="alias" || tagname
=="parameter") {
688 /* GLib.List etc have extra info in child, ignored for now */
689 auto p
= "name" in a
;
690 if ( !p ||
( *p
!="GLib.List" && *p
!="GLib.SList" && *p
!="GLib.HashTable" ) )
691 enforce(e
.tag
.type
==TagType
.EMPTY
, e
.tag
.toString());
695 type
= get_name_attr(e
);
699 onEmptyTag(ep
, "attribute", (in Element e
) {
701 enforce(tag
.attr
.length
==2);
702 enforce(tag
.attr
["name"]=="c:identifier");
703 identifier
= tag
.attr
["value"];
706 if (tagname
=="parameter") {
707 if (auto p
= "allow-none" in field_attr
) {
711 if (auto p
= "direction" in field_attr
)
712 direction
= "/*" ~ *p
~ "*/ ";
714 parsearray(ep
, type
, ctype
);
715 ep
.onStartTag
["varargs"] = (ElementParser ep
) {
718 SkipEndTag(ep
, "varargs");
723 if (tagname
=="field") {
724 ep
.onStartTag
["array"] = (ElementParser ep
) {};
725 ep
.onEndTag
["array"] = (in Element e
) {
726 trace("FIELDARR", tagname
, e
.tag
.toString());
728 aassign(size
, e
.tag
.attr
, "fixed-size");
729 if (auto p
= "readable" in field_attr
) {
730 if (!size
&& *p
=="0")
731 // Generates zero sized array, but as long as all instances
732 // are private and not followed by public fields - harmless.
736 // Ugh, eg GTK.InputDialogClass contains embedded arrays w/o size.
737 // Not usuable anyway, so we only care about it being syntactically correct.
742 size
= "/*GIR: -1*/ 0"; // Hmm, does this really belong in GIR...
744 // Using D syntax for arrays.
745 type
~= "[" ~ size
~ "]";
746 ctype
~= "[" ~ size
~ "]";
750 ep
.onStartTag
["callback"] = (ElementParser ep
) {
751 callback
= new x_function("functionp");
759 final class x_function
: x_elem
{
761 x_struct obj
; // object if this is a method.
762 string rettype
, crettype
, retanno
;
763 string throws
; // actually gerrors, oh well.
767 this(string s
, x_struct pobj
=null) { super(s
); obj
= pobj
; }
769 string
c_func(bool proto
, bool skipthis
, scope const(x_field
)[] args
) {
775 r
~= nodkeyword(cname
) ~ "(";
777 foreach(i
, a
; args
) {
779 r
~= "..."; // Variadic - have to be aliased (avoids having to deal w/ varargs).
782 r
~= a
.direction
~ Dtype(a
.type
, a
.ctype
) ~ " ";
783 r
~= a
.cname ?
nodkeyword(a
.cname
) : nodkeyword(a
.name
);
786 if (i
!=args
.length
-1)
795 override string
toString() {
796 string Drettype
= Dtype(rettype
, crettype
);
797 string r
= comment_to_D() ~ indent
;
801 // Ugh. Turning off -ffunction-sections causes link failures
802 // because if this missing glib function.
803 if (cname
=="glib_dummy_decl")
807 auto pos
= std
.string
.indexOf(cname
, '_');
808 name
= "/*NAME MISSING IN GIR*/ ";
810 name
~= "missing_" ~ cname
;
812 name
~= cname
[pos
+1..$];
815 bool is_funcp
= tagname
=="callback" ||
816 tagname
=="functionp" ||
817 tagname
=="virtual-method";
819 // Fix up the argument list.
824 auto t
= new x_field("this_");
825 t
.type
= obj
.name
~ "*";
828 if (tagname
!="constructor") {
829 // Methods have a this pointer as the first arg.
830 allargs
= t
~ allargs
;
832 if (tagname
!="function")
836 // Constructors return pointers to new objects, not some base type.
837 Drettype
= obj
.name
~ "*";
842 allargs
~= gerrfield
;
844 foreach(i
, a
; allargs
) {
845 // Fix up any missing arg names.
847 a
.name
= "arg_" ~ cast(char)('a'+i
);
848 // Gtk2 has out params w/o a ctype and "gint" type - we'll do our best to fix it...
849 if (a
.direction
=="/*out*/ " && !a
.ctype
/*&& a.type[$-1]!='*'*/) {
850 a
.ctype
= a
.type
~ "*";
851 a
.direction
~= "/*POINTER*/ ";
853 // Fix up default params.
854 if (a
.def_init
=="=null")
855 switch (Dtype(a
.type
, a
.ctype
)) {
856 case "int": a
.def_init
= "=0"; break;
857 default: /**/; break;
861 // Remove default initializers from args that are followed by ones w/o defaults.
863 bool nondefarg_seen
= 0;
864 foreach_reverse(a
; allargs
) {
865 if (!a
.def_init
&& !a
.varargs
)
872 if (tagname
=="glib:signal") {
873 Drettype
= Dtype(rettype
, crettype
, 1);
875 // Signals have an extra 'user_data' pointer as the last arg.
876 auto udata
= new x_field("user_data");
877 udata
.type
= "void*";
878 udata
.name
= "user_data=null"; // "=null" JIC previous args were optional.
879 allargs
= allargs
~ udata
;
884 r
~= Drettype
~ " " ~ retanno
;
887 foreach(i
, a
; allargs
) {
888 r
~= a
.direction
~ Dtype(a
.type
, a
.ctype
, 1) ~ " " ~ nodkeyword(a
.name
) ~ a
.def_init
;
889 if (i
!=allargs
.length
-1)
894 r
~= " signal_" ~ nodkeyword(name
);
897 // Add some convenience templates:
899 if (!obj
.emitted("signal_connect")) {
901 r
~= indent
~ "ulong signal_connect(string name, CB)(CB cb, void* data=null, ConnectFlags cf=cast(ConnectFlags)0) {\n";
902 r
~= ++indent
~ "return super_.signal_connect!name(cb, data, cf);\n";
903 r
~= --indent
~ "}\n\n";
906 r
~= indent
~ `ulong signal_connect(string name:"` ~ name
~ `", `;
907 r
~= `CB:signal_` ~ nodkeyword(name
) ~ ")(CB cb, void* data=null, ConnectFlags cf=cast(ConnectFlags)0) {\n";
908 r
~= ++indent
~ `return signal_connect_data(&this, cast(char*)"` ~ name
~ "\",\n";
909 r
~= indent
~ "cast(GObject2.Callback)cb, data, null, cf);\n";
910 r
~= --indent
~ "}\n";
916 bool unsupported
= tagname
!="function" &&
917 tagname
!="constructor";
919 r
~= "/+ Not available -- variadic " ~
921 "methods unsupported - use the C function directly.\n"
923 "functions with non-D linkage must have >1 parameter.\n" );
927 r
~= "alias " ~ cname
~ " " ~ nodkeyword(name
) ~ "; // Variadic\n";
929 r
~= --indent
~ "+/\n";
934 bool hasUpperCase(string s
) {
942 string refDrettype
= Drettype
;
943 if (reftypes
&& Drettype
[$-1]=='*' && hasUpperCase(Drettype
)) {
944 refDrettype
= Drettype
[0..$-1];
950 if (tagname
=="callback")
952 if (tagname
=="constructor" || tagname
=="function")
956 r
~= "Ref!(" ~ refDrettype
~ ")";
965 r
~= nodkeyword(name
) ~ "(";
972 foreach(i
, a
; args
) {
976 r
~= a
.direction
~ Dtype(a
.type
, a
.ctype
) ~ " " ~ nodkeyword(a
.name
) ~ a
.def_init
;
977 if (i
!=args
.length
-1)
985 r
~= " " ~ nodkeyword(name
);
987 if (tagname
=="functionp")
990 if (tagname
=="callback" || tagname
=="virtual-method") {
995 r
~= " {\n" ~ ++indent
;
997 if (Drettype
!="void")
1001 r
~= "Ref!(" ~ refDrettype
~ ")(";
1005 oname
= allargs
[0].name
;
1006 allargs
[0].name
= "&this";
1008 r
~= c_func(0, 0, allargs
);
1011 r
~= ";\n" ~ --indent
~ "}\n";
1013 allargs
[0].name
= oname
;
1016 if (!is_funcp
/*&& tagname=="function" || obj*/)
1017 cprotos
~= Drettype
~ " " ~ retanno
~ c_func(1, 0, allargs
) ~ ";";
1022 void parse(ElementParser ep
) {
1023 trace("FUNC", tagname
~ (obj ?
": " ~ obj
.name
: ""), ep
.tag().toString());
1024 // "callbacks" in structs are actually pointers.
1025 SkipEndTag(ep
, tagname
!="functionp" ? tagname
: "callback");
1026 register_doc_parser(ep
);
1027 auto tag
= ep
.tag();
1029 name
= tag
.attr
["name"];
1030 aassign(cname
, tag
.attr
, "c:identifier");
1031 aassign(throws
, tag
.attr
, "throws");
1033 auto p
= "introspectable" in tag
.attr
;
1035 doc
~= "Unintrospectable " ~ tagname
~ ": " ~ name
~ "() / " ~ cname
~ "()\n";
1037 ep
.onStartTag
["return-value"] = (ElementParser ep
) {
1038 SkipEndTag(ep
, "return-value");
1039 register_doc_parser(ep
, "RETURNS");
1041 auto tag
= ep
.tag();
1043 if (auto p
= "transfer-ownership" in tag
.attr
) {
1045 case "none": retanno
= ""; break;
1046 case "full": retanno
= "/*new*/ "; break;
1047 case "container": retanno
= "/*new container*/ "; break;
1048 default: enforce(0, "Unknown transfer-ownership: " ~ *p
);
1052 parsearray(ep
, rettype
, crettype
);
1054 ep
.onStartTag
["type"] = (ElementParser ep
) {};
1055 ep
.onEndTag
["type"] = (in Element e
) {
1057 aassign(crettype
, tag
.attr
, "c:type");
1058 aassign(rettype
, tag
.attr
, "name"/*, "func retval typename"*/);
1059 // If we have a ctype, but no type, try using the ctype.
1060 if (!rettype
&& crettype
) {
1061 crettype
= "/*CTYPE*/ " ~ crettype
;
1064 enforce(rettype
, "Missing func retval typename" );
1069 // Note the trick is there can be multiple nested <type>s;
1070 // once we get here we should have filled in the types properly.
1071 // Keep in mind we only care about the outermost type, that's
1072 // why parsing the _end_ </type> tag works for us.
1074 // Unfortunately c:type is often missing, so this check is too strict.
1075 //enforce(crettype, "Missing func retval c:type" ~ to!string(this));
1078 ep
.onStartTag
["parameters"] = (ElementParser pa
) {
1079 trace("FUNCPARM", tagname
, ep
.tag().toString());
1080 SkipEndTag(pa
, "parameters");
1081 pa
.onStartTag
["parameter"] = (ElementParser pa
) {
1082 SkipEndTag(pa
, "parameter");
1083 auto arg
= new x_field("parameter", this); args
~= arg
; arg
.parse(pa
);
1094 final class x_struct
: x_elem
{
1095 string name
, parent
;
1096 x_struct embedded_in
;
1098 x_function
[] methods
;
1099 bool[string
] emitted_
;
1101 this(string tn
, x_struct emb
=null) {
1105 case "record": break;
1106 case "class": break;
1107 case "union": break;
1108 case "interface": break;
1109 default: enforce(0, "Unknown struct type: " ~ tagname
);
1113 private bool emitted(string s
) {
1114 if (auto p
= s
in emitted_
)
1120 private string
basetypename(scope string type
) {
1121 auto i
= lastIndexOf(type
, '.');
1122 return i
==-1 ? type
: type
[i
+1..$];
1125 override string
toString() {
1126 // Everything looks like struct to us. Except unions.
1127 string r
= comment_to_D() ~ indent
~
1128 ((tagname
=="union") ?
"union " : "struct ") ~ name
;
1131 r
~= " /* : " ~ parent
~ " */";
1133 r
~= " /* Version" ~ ver
~ " */";
1138 // "alias super this".
1140 // First, if this struct has a parent, but we don't know any of its
1141 // fields then add the parent as the only (sub)struct -- harmless as
1142 // we couldn't access the opaque empty struct anyway and this lets
1143 // the pseudo-inheritance work.
1144 if (parent
&& fields
.length
==0) {
1145 auto pf
= new x_field("record");
1148 pf
.name
= "method_parent";
1152 if (parent
&& fields
.length
>0 && (
1153 Dtype(fields
[0].type
, fields
[0].ctype
)==Dtype(parent
, null) ||
1154 Dtype(fields
[0].type
, fields
[0].ctype
)==parent
1156 auto fieldname
= nodkeyword(fields
[0].name
);
1158 r
~= indent
~ "alias " ~ fieldname
~ " this;\n";
1159 r
~= indent
~ "alias " ~ fieldname
~ " super_;\n";
1161 // Add an extra alias for easier upcasting.
1162 // NOTE: The name we add could clash with existing symbols;
1163 // this does not happen in practice, will be caught at
1164 // compiletime if it does, and can be addressed then.
1165 // Also "object" is not the safest symbol to add, but
1166 // works so far, let's wait until it causes problems
1167 // before renaming it to something like "gobject".
1168 auto aname
= basetypename(nodkeyword(fields
[0].type
));
1169 aname
= toLower(aname
);
1170 if (aname
!=fieldname
)
1171 r
~= indent
~ "alias " ~ fieldname
~ " " ~ aname
~ ";\n";
1173 // The parent cannot be private, so fix it:
1174 if (fields
[0].private_
)
1175 fields
[0].private_
= 0;
1178 // No bitfields in D.
1181 char bfcount
='A'; // More than one __dummyNN names? Must be different.
1187 // For now we'll assume all bitfields are at least 32bits wide (C int-sized)
1189 if (bfwidth
>0 && bfwidth
<=32)
1191 else if (bfwidth
>32 && bfwidth
<=64)
1193 enforce(rawwidth
, bmix
);
1195 // std.bitmaip reqs total width to be one of 8/16/32/64, so:
1196 if (rawwidth
!=bfwidth
) {
1197 if (rawwidth
<=8 && bfwidth
>0 && bfwidth
<8)
1198 bmix
~= ",\n" ~ indent
~ " uint, \"__dummy8"~bfcount
~"\", " ~ to
!string(8-bfwidth
);
1199 else if (rawwidth
<=16 && bfwidth
<16)
1200 bmix
~= ",\n" ~ indent
~ " uint, \"__dummy16"~bfcount
~"\", " ~ to
!string(16-bfwidth
);
1201 else if (rawwidth
<=32 && bfwidth
<32)
1202 bmix
~= ",\n" ~ indent
~ " uint, \"__dummy32"~bfcount
~"\", " ~ to
!string(32-bfwidth
);
1203 else if (bfwidth
<64)
1204 bmix
~= ",\n" ~ indent
~ " uint, \"__dummy64"~bfcount
~"\", " ~ to
!string(64-bfwidth
);
1205 else if (bfwidth
==64)
1216 // Unfortunately there are methods that end up using the same name
1217 // as struct fields. Build a list of all method names, and in the
1218 // code below check for clashes. Rename the fields as these are
1219 // less likely to be used by apps.
1220 bool[string
] m_names
;
1221 foreach (m
; methods
)
1222 m_names
[m
.name
] = 1;
1224 foreach (i
, f
; fields
) {
1225 string privstr
= f
.private_ ?
"private " : "";
1232 r
~= privstr
~ f
.callback
.toString();
1234 else if (f
.struct_
) {
1241 r
~= "\n" ~ privstr
~ f
.struct_
.toString();
1243 else if (f
.bitfield
) {
1247 bmix
= indent
~ " static import std.bitmanip;"
1248 ~ " mixin(std.bitmanip.bitfields!(\n" ~ indent
;
1251 bmix
~= ",\n" ~ indent
;
1252 string bftype
= Dtype(f
.type
, f
.ctype
);
1254 case "uint", "int", "c_uint", "c_int":
1255 bmix
~= bftype
~ ", \"" ~ f
.name
~"\", "~ to
!string(f
.bitfield
);
1256 bfwidth
+= f
.bitfield
;
1259 enforce(0, "Unknown bitfield type " ~
1260 bftype
~ "[" ~ f
.type
~ "," ~ f
.ctype
~ "] " ~
1261 f
.name
~ ":" ~ to
!string(f
.bitfield
));
1266 string fname
= nodkeyword(f
.name
);
1267 if (fname
in m_names
)
1270 r
~= indent
~ privstr
~ Dtype(f
.type
, f
.ctype
) ~ " " ~ fname
;
1272 string prevtype
, currtype
;
1273 if (fields
[i
-1].type || fields
[i
-1].ctype
)
1274 prevtype
= Dtype(fields
[i
-1].type
, fields
[i
-1].ctype
);
1275 currtype
= Dtype(f
.type
, f
.ctype
);
1276 if ( (prevtype
== currtype
&& fields
[i
-1].type
==f
.type
)
1277 && (fields
[i
-1].private_
== f
.private_
)
1278 && !fields
[i
-1].bitfield
)
1281 r
~= ((!fields
[i
-1].struct_
) ?
";" : "");
1282 r
~= "\n"~ indent
~ privstr
~ currtype
~ " " ~ fname
;
1290 if (fields
.length
>0 && !fields
[$-1].struct_
)
1293 if (fields
.length
>0 && methods
.length
>0)
1296 foreach (f
; methods
)
1299 // Pull in the right mixin, if any:
1301 string mixname
= name
;
1303 for (auto ei
= embedded_in
; ei
; ei
= ei
.embedded_in
)
1304 mixname
= ei
.name
~ "_" ~ mixname
;
1306 r
~= load_mixin(mixname
);
1308 return r
~ --indent
~ "}\n";
1311 void parse(ElementParser ep
) {
1312 trace("STRUCT", tagname
, ep
.tag().toString());
1313 auto tag
= ep
.tag();
1314 name
= tag
.attr
["name"];
1315 aassign(parent
, tag
.attr
, "parent");
1317 register_doc_parser(ep
);
1318 ep
.onStartTag
["field"] = (ElementParser ep
) {
1319 auto nf
= new x_field("field"); fields
~= nf
; nf
.parse(ep
);
1321 ep
.onStartTag
["union"] = (ElementParser ep
) {
1322 auto nf
= new x_field("union"); fields
~= nf
;
1323 auto na
= new x_struct("union", this); nf
.struct_
= na
; na
.parse(ep
);
1325 ep
.onStartTag
["record"] = (ElementParser ep
) {
1326 auto nf
= new x_field("record"); fields
~= nf
;
1327 auto na
= new x_struct("record", this); nf
.struct_
= na
; na
.parse(ep
);
1329 ep
.onStartTag
["property"] = (ElementParser p
) {
1330 p
.parse(); // Drop them.
1332 ep
.onStartTag
["method"] = (ElementParser ep
) {
1333 auto nm
= new x_function("method", this); methods
~= nm
; nm
.parse(ep
);
1335 ep
.onStartTag
["virtual-method"] = (ElementParser p
) {
1336 p
.parse(); // Drop them.
1338 ep
.onStartTag
["function"] = (ElementParser ep
) {
1339 auto nm
= new x_function("function"); methods
~= nm
; nm
.parse(ep
);
1341 ep
.onStartTag
["constructor"] = (ElementParser ep
) {
1342 auto nm
= new x_function("constructor", this); methods
~= nm
; nm
.parse(ep
);
1344 ep
.onStartTag
["glib:signal"] = (ElementParser ep
) {
1345 auto nm
= new x_function("glib:signal", this); methods
~= nm
; nm
.parse(ep
);
1348 ep
.onStartTag
["prerequisite"] = (ElementParser ep
) { ep
.parse(); }; // Drop.
1349 ep
.onEndTag
["prerequisite"] = (in Element e
) {};
1352 ep
.onStartTag
["implements"] = (ElementParser p
) { p
.parse(); };
1353 ep
.onEndTag
["implements"] = (in Element e
) {};
1359 void onUnknownTag(ElementParser ep
, string tagname
) {
1360 ep
.onStartTag
[null] = (ElementParser ep
) {
1361 enforce(0, "Unhandled <"~tagname
~"> start tag: "
1362 ~ ep
.tag
.name
~ "\n# " ~ ep
.tag().toString());
1364 ep
.onEndTag
[null] = (in Element e
) {
1365 enforce(0, "Unhandled <"~tagname
~"> end tag: " ~ "\n# " ~ e
.toString());
1369 void SkipEndTag(ElementParser p
, string tagname
) {
1370 onUnknownTag(p
, tagname
);
1371 p
.onEndTag
[tagname
] = (in Element e
) { };
1374 void onEmptyTag(ElementParser p
, string tagname
, void delegate(in Element e
) deleg
) {
1375 p
.onStartTag
[tagname
] = (ElementParser ep
) {
1376 /* do nothing; only tells the parser that we know about this tag */
1378 p
.onEndTag
[tagname
] = (in Element e
) {
1380 enforce(tag
.type
==TagType
.EMPTY
, tag
.toString());
1386 void onStartTag(ElementParser p
, string tagname
, void delegate(ElementParser ep
) deleg
) {
1387 p
.onStartTag
[tagname
] = (ElementParser ep
) {
1388 onUnknownTag(ep
, tagname
);
1389 ep
.onEndTag
[tagname
] = (in Element e
) {/*Reached our own end tag*/};
1392 p
.onEndTag
[tagname
] = (in Element e
) {
1393 /* Do nothing; only tells the parser that we know about this tag */
1394 /* Ugh. "<record name="AppLaunchContextPrivate" />" happens... */
1398 template OnTag(alias pa
, string Kind
, string xmlname
, string tagname
) {
1400 on"~Kind
~"Tag(" ~ pa
.stringof
~ ", \"" ~ xmlname
~ "\","
1401 " (" ~ ((Kind
=="Start") ?
"ElementParser e" : "in Element e") ~ ") {
1402 auto a = new x_"~tagname
~"(" ~ xmlname
.stringof
~ ");
1404 "`write(a.toString() ~"\n");`
1408 string delete_symbol
[string
]; // [name: reason]
1410 void load_delete() {
1411 string filename
= "mixin/" ~ thismodulename
~ "_" ~ "_DELETE_";
1415 s
= cast(string
)std
.file
.read(filename
);
1416 } catch (std
.file
.FileException
) {
1418 ew("DELETE Failed: " ~ filename
);
1422 foreach (line
; split(s
, "\n")) {
1423 auto arr
= split(line
, ":");
1427 delete_symbol
[line
] = "";
1429 delete_symbol
[arr
[0]] = join(arr
[1..$], ":");
1435 string
load_mixin(string id
) {
1436 string filename
= "mixin/" ~ thismodulename
~ "_" ~ id
~ ".d";
1439 static bool uniq_mix
[string
];
1441 enforce(filename
!in uniq_mix
, "Mixin collission: " ~ filename
);
1442 uniq_mix
[filename
] = 1;
1445 s
= cast(string
)std
.file
.read(filename
);
1446 } catch (std
.file
.FileException
) {
1448 ew("MIXIN Failed: " ~ filename
);
1453 ew("MIXIN Found: " ~ filename
);
1455 string r
= "\n" ~ indent
~ "// --- " ~ filename
~ " --->\n\n";
1457 foreach (sp
; split(s
, "\n"))
1458 r
~= indent
~ sp
~ "\n";
1460 r
~= indent
~ "// <--- " ~ filename
~ " ---\n";
1467 void main(string
[] args
) {
1468 string infilename
, outfilename
, modulename
;
1474 bool delegate(string arg
) eatarg
;
1476 cmdargloop
: foreach(arg
; args
[1..$]) {
1487 die("Unknown extra cmdline file name: " ~ arg
);
1490 die("Missing cmdline option: " ~ arg
);
1493 switch (arg
[2..$]) {
1495 case "trace": showtrace
= 1; break;
1497 case "trace-mixins": tracemixins
= 1; break;
1498 case "check": xmlcheck
= 1; break;
1499 case "reftypes": reftypes
= 1; break;
1500 case "no-reftypes": reftypes
= 0; break;
1501 default: die("Unknown long cmdline option: " ~ arg
);
1506 outfilename
= arg
[2..$];
1508 eatarg
= (string arg
) { outfilename
= arg
; return true; };
1509 continue cmdargloop
;
1512 outfilename
= arg
[2..$];
1514 eatarg
= (string arg
) { modulename
= arg
; return true; };
1515 continue cmdargloop
;
1517 die("Unknown cmdline option: " ~ arg
);
1522 die("Missing cmdline arg");
1524 string s
= cast(string
)std
.file
.read(infilename
);
1527 check(s
); // Running the checks increases the execution time from 0.22s to 1.74s...
1530 stdout
.open(outfilename
, "wt");
1532 writef("// *** DO NOT EDIT ***\n// Automatically generated from \"%s\"\n\n", infilename
);
1535 write("module " ~ modulename
~ ";\n");
1537 auto dp
= new DocumentParser(s
);
1539 void delegate (ElementParser ep
) epskip
= (ElementParser ep
) { ep
.parse(); };
1540 void delegate (ElementParser p
) epfixme
= (ElementParser p
)
1541 { p
.parse(); ew("FIXME: " ~ p
.toString()); };
1542 void delegate (in Element e
) efixme
= (in Element e
) { ew("FIXME: " ~ e
.toString()); };
1544 onUnknownTag(dp
, "Document");
1546 onEmptyTag(dp
, "package", (in Element e
) {
1547 auto name
= get_name_attr(e
);
1548 write("\n// package: \"" ~ name
~ "\";\n");
1551 onEmptyTag(dp
, "c:include", (in Element e
) {
1552 auto name
= get_name_attr(e
);
1553 write("// C header: \"" ~ name
~ "\";\n");
1556 mixin( OnTag
!(dp
, "Empty", "include", "include") );
1557 mixin( OnTag
!(dp
, "Start", "namespace", "namespace") );
1558 dp
.onEndTag
["namespace"] = (in Element e
) { }; // We're assuming one ns per file.
1560 dp
.onEndTag
["repository"] = (in Element e
) { }; // We're assuming one repo per file.
1562 mixin( OnTag
!(dp
, "Start", "class", "struct") );
1563 mixin( OnTag
!(dp
, "Start", "record", "struct") );
1564 mixin( OnTag
!(dp
, "Start", "union", "struct") );
1565 mixin( OnTag
!(dp
, "Start", "interface", "struct") );
1567 mixin( OnTag
!(dp
, "Start", "constant", "field") );
1568 mixin( OnTag
!(dp
, "Start", "alias", "field") );
1570 mixin( OnTag
!(dp
, "Start", "bitfield", "bitfield") );
1571 mixin( OnTag
!(dp
, "Start", "enumeration", "bitfield") );
1573 mixin( OnTag
!(dp
, "Start", "function", "function") );
1574 mixin( OnTag
!(dp
, "Start", "callback", "function") );
1576 dp
.onStartTag
["glib:boxed"] = epskip
;
1578 // Not handled yet. Can't eat them here, we need the types anyway.
1579 //dp.onStartTag["interface"] = epfixme;
1583 write(load_mixin("_MODULE"));
1585 write("\n// C prototypes:\n\nextern (C) {\n");
1586 // Some methods are also documented as functions and the D
1587 // compiler does not like duplicate prototypes; drop them.
1588 bool uniq_proto
[string
];
1589 foreach(s
; cprotos
) {
1590 if (s
!in uniq_proto
)
1597 catch (Exception e
) {
1601 remove(outfilename
);
1603 ew("WARNING: Unable to remove destination file.");
1608 void die(S
...)(S args
) {
1609 import core
.stdc
.stdlib
;