summaryrefslogtreecommitdiff
path: root/tools/generate_wrap_init.pl.in
blob: 6e4dcf3cf4cec91b746ddd104dc465bed2fbc16d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
#! @PERL@
#
# @configure_input@
#

use strict;

my @namespace_whole = (); # list of strings.
my $function_prefix = "";
my $parent_dir = ""; # e.g. gtkmm
my $debug = 0;
my %objects = ();    # Hashmap of arrays of objects (classes) in each file
my %exceptions = (); # Hashmap of arrays of exception classes in each file
my %conditional_compilation = ();
my %deprecated = ();
my %extra_includes = ();

# Hashmap telling if the registration of the classes in a file should not be
# included in the wrap_init() function.  (This is useful for modules such as
# gstreamermm that want to exclude plug-in types from being registered in their
# wrap_init() function.)
my %exclude_from_wrap_init = ();

# The keys in all hashes except %extra_includes are names of header files (xyz.h),
# corresponding to the read .hg files.
#
# The keys in %extra_includes are names of header files to include without reading.
# %extra_includes is a hash rather than a list because we don't want duplicates.
# Its values are not used. In C++ it would have been a std::set.

# $objects{$filename_header} is a reference to an array. That array contains
# references to arrays with two or more elements, ($cppname, $basename, @extra_namespace),
# one reference per object defined in file $filename_header.
# %exceptions has the same structure as %objects.

sub print_with_guards
{
  my $file = $_[0];
  my $message = $_[1];
  if ($deprecated{$file})
  {
    # The uc(parent_dir) is a bit of a hack. One day it will get it wrong.
    print "#ifndef " . uc($parent_dir) ."_DISABLE_DEPRECATED\n"
  }

  if ($conditional_compilation{$file})
  {
    print "#$conditional_compilation{$file}\n"
  }

  print "$message";

  if ($conditional_compilation{$file})
  {
    print "#endif // $conditional_compilation{$file}\n"
  }

  if ($deprecated{$file})
  {
    print "#endif // *_DISABLE_DEPRECATED\n"
  }
}

# Loop through command line flags, setting variables:
while ($ARGV[0] =~ /^-/)
{
  if ($ARGV[0] =~ /--namespace=(\S+)/)
  {
    push(@namespace_whole, split('::', $1));

    if($parent_dir eq "")
      { $parent_dir = lc($1) . "mm"; }
  }
  elsif ($ARGV[0] =~ /--function_prefix=(\S+)/)
  {
    $function_prefix = "$1";
  }
  elsif ($ARGV[0] =~ /--parent_dir=(\S+)/)
  {
    $parent_dir = "$1";
  }
  elsif ($ARGV[0] =~ /--debug/)
  {
    $debug = 1;
  }
  elsif ($ARGV[0] =~ /--path=(\S+)/)
  {
    # Do nothing. Just for backwards compatibility.
  }
  else
  {
    print "Error: unknown option $ARGV[0]\n";
    exit;
  }

  shift @ARGV;
}

# Loop through remaining command line arguments, names of .hg files.
while ($ARGV[0])
{
  if ($debug) {warn "Processing file : $ARGV[0]\n";}

  my $filename = $ARGV[0];
  open FILE, $filename or die "Couldn't open file $filename : $!\n";

  # Store header filename, so we can #include it later:
  my $filename_header = $filename;
  $filename_header =~ s#.*/([^/]+)\.hg#$1.h#;
  $conditional_compilation{$filename_header} = "";
  $deprecated{$filename_header} = 0;

  # Keep track of sub-namespaces, if any.
  # Otherwise we can't tell the difference between Gio::Error and Gio::DBus::Error.
  my @extra_namespace = ();

  while (<FILE>)
  {
    # Skip comments.
    # This is far from foolproof. It only skips one-line comments, and it does
    # not try to skip quoted strings.
    s#//.*##;
    s#/\*.*?\*/##g;

    # This is useful when only some classes use a sub-namespace.
    # Alternatively, we could parse the namespace start and end parentheses,
    # but this hack is easier.
    if (/\b_GMMPROC_EXTRA_NAMESPACE\((\w+)\)/)
    {
      # debug: print "generate_wrap_init: namespace found: $1\n";
      push(@extra_namespace, $1);
    }
    elsif (/\b(_CLASS_GOBJECT|_CLASS_GTKOBJECT|_WRAP_GERROR)\s*\(/)
    {
      my $type_of_class = $1;
      my $line = $_;
      while ($line !~ /\)/ && ($_ = <FILE>))
      {
        $line .= $_;
      }
      if (!$_)
      {
        die "Reached end of file $filename in $type_of_class macro. " .
            "(No right parenthesis.)\n";
      }

      $line =~ s/^.*$type_of_class\s*\(//;
      $line =~ s/\s+//g;
      $line =~ s/\).*//;
      my ($cppname, $cname, $ccast) = split(/,/, $line);
      my $basename = lc($ccast);
      my @names = ($cppname, $basename, @extra_namespace);

      # Don't remove the space between \ and @. When glibmm is built with Meson,
      # the \ would be removed in generate_wrap_int.pl.
      if ($type_of_class eq "_CLASS_GOBJECT" or
          $type_of_class eq "_CLASS_GTKOBJECT")
      {
        push(@{$objects{$filename_header}}, \ @names);
      }
      elsif ($type_of_class eq "_WRAP_GERROR")
      {
        push(@{$exceptions{$filename_header}}, \ @names);
      }
    }
    elsif (/\b_INCLUDE_IN_WRAP_INIT\((.+)\)/)
    {
      $extra_includes{$1} = 1;
    }
    elsif (/\b_GTKMMPROC_WIN32_NO_WRAP\b/)
    {
      # This file will not be compiled on Win32.
      # No extra include is required. If G_OS_WIN32 is defined, it's defined in
      # a file included indirectly by glibmm.h.
      $conditional_compilation{$filename_header} = "ifndef G_OS_WIN32";
    }
    elsif (/\b_GMMPROC_WRAP_CONDITIONALLY\((.+)\)/)
    {
      # The compilation of this file will be controlled by a preprocessor directive.

      # Allow m4 quotes in the parameter. There may be cases where the m4 processor
      # would misunderstand a parameter without quotes.
      my $par = $1;
      $par =~ s/^\s*`*\s*//; # Remove m4 quotes and leading and trailing white space.
      $par =~ s/\s*'*\s*$//;
      $conditional_compilation{$filename_header} = $par;
    }
    elsif (/\b_IS_DEPRECATED\b/)
    {
      $deprecated{$filename_header} = 1; # This file is deprecated
    }
    elsif (/\b_NO_WRAP_INIT_REGISTRATION\b/)
    {
      $exclude_from_wrap_init{$filename_header} = 1;
    }
  }

  shift @ARGV;
  close(FILE);
}


# Print the wrap_init.cc file.

print << "EOF";
// Generated by generate_wrap_init.pl -- DO NOT MODIFY!

#define GLIBMM_INCLUDED_FROM_WRAP_INIT_CC
#include <glibmm.h>

// Disable the 'const' function attribute of the get_type() functions.
// GCC would optimize them out because we don't use the return value.
#undef  G_GNUC_CONST
#define G_GNUC_CONST /* empty */

#include <${parent_dir}/wrap_init.h>
#include <glibmm/error.h>
#include <glibmm/object.h>
EOF

foreach my $filename_header (sort keys %extra_includes)
{
  print "#include <$filename_header>\n";
}

print "\n// #include the widget headers so that we can call the get_type() static methods:\n";

# keys %deprecated contains all filenames, not just the names of deprecated files.
foreach my $filename_header (sort keys %deprecated)
{
  next if($exclude_from_wrap_init{$filename_header});
  print_with_guards($filename_header, "#include \"$filename_header\"\n");
}

# Declarations of glib functions.

print "\nextern \"C\"\n";
print "{\n";
print "//Declarations of the *_get_type() functions:\n\n";

foreach my $filename_header (sort keys %objects)
{
  next if($exclude_from_wrap_init{$filename_header});

  my @objects_in_file = @{$objects{$filename_header}};
  my $message = "";
  foreach my $i (@objects_in_file)
  {
    # $i is a reference to an array with info on one object, declared in file $filename_header.
    my $basename = ${$i}[1];
    $message .= "GType ${basename}_get_type(void);\n";
  }
  print_with_guards($filename_header, $message);
}

print "\n//Declarations of the *_error_quark() functions:\n\n";

foreach my $filename_header (sort keys %exceptions)
{
  my @exceptions_in_file = @{$exceptions{$filename_header}};
  my $message = "";
  foreach my $i (@exceptions_in_file)
  {
    # $i is a reference to an array with info on one exception, declared in file $filename_header.
    my $basename = ${$i}[1];
    $message .= "GQuark ${basename}_quark(void);\n";
  }
  print_with_guards($filename_header, $message);
}

print "} // extern \"C\"\n";
print "\n";

my $namespace_whole_declarations = "";
my $namespace_whole_close = "";
foreach (@namespace_whole)
{
  $namespace_whole_declarations .= "namespace $_ {\n";
  $namespace_whole_close = "} // $_\n$namespace_whole_close";
}

print "$namespace_whole_declarations";

print "\n//Declarations of the *_Class::wrap_new() methods, instead of including all the private headers:\n\n";

foreach my $filename_header (sort keys %objects)
{
  next if($exclude_from_wrap_init{$filename_header});

  my @objects_in_file = @{$objects{$filename_header}};
  my $message = "";
  foreach my $i (@objects_in_file)
  {
    my ($cppname, undef, @extra_namespace) = @{$i};

    my $namespace_declarations = "";
    my $namespace_close = "";
    foreach (@extra_namespace)
    {
      $namespace_declarations .= "namespace $_ { ";
      $namespace_close .= " }";
    }

    $message .= "${namespace_declarations}class ${cppname}_Class " .
                "{ public: static Glib::ObjectBase* wrap_new(GObject*); };$namespace_close\n";
  }
  print_with_guards($filename_header, $message);
}

# wrap_init() calls throw_func() in each exception class. throw_func() is a
# private method. wrap_init() is declared as a friend of the exception class.
# The friends will find each other easily only if the calling wrap_init()
# function is declared in the same namespace as the exception class.
# If there are extra namespaces, we define extra wrap_init() functions, which
# are called from the wrap_init() function in @namespace_whole.

my %extra_namespaces = ();
foreach my $filename_header (keys %exceptions)
{
  my @exceptions_in_file = @{$exceptions{$filename_header}};
  foreach my $i (@exceptions_in_file)
  {
    if (@{$i} > 2)
    {
      my (undef, undef, @extra_namespace) = @{$i};
      $extra_namespaces{join("::", @extra_namespace)} = 1;
    }
  }
}

# Generate the extra wrap_init() functions in sub-namespaces, if any.

# If you suspect that code with three levels of foreach is inefficient, you are
# probably right, but it's not important here. The exception classes are few in
# most modules (hardly more than about 10), and the sub-namespaces are even
# fewer (usually 0 or 1).

print "\n// Register Error domains in sub-namespaces:\n" if keys %extra_namespaces > 0;

foreach my $sub_namespace (sort keys %extra_namespaces)
{
  my @extra_namespace = split("::", $sub_namespace);
  my $namespace_declarations = "";
  my $namespace_close = "";
  foreach (@extra_namespace)
  {
    $namespace_declarations .= "namespace $_ {\n";
    $namespace_close = "} // $_\n$namespace_close";
  }

  print "\n$namespace_declarations";
  print "\nvoid wrap_init()\n{\n";

  foreach my $filename_header (sort keys %exceptions)
  {
    my @exceptions_in_file = @{$exceptions{$filename_header}};
    my $message = "";
    foreach my $i (@exceptions_in_file)
    {
      my ($cppname, $basename, @extra_namespace) = @{$i};
      if (@extra_namespace > 0 && $sub_namespace eq join("::", @extra_namespace))
      {
        $message .= "  Glib::Error::register_domain(${basename}_quark(), &" .
                    "${sub_namespace}::${cppname}::throw_func);\n";
      }
    }
    print_with_guards($filename_header, $message) if $message;
  }

  print "\n} // wrap_init()\n";
  print "\n$namespace_close";
}

# Generate namespace::wrap_init() body

print "\nvoid ${function_prefix}wrap_init()\n{\n";

print "  // Register Error domains in the main namespace:\n";

foreach my $filename_header (sort keys %exceptions)
{
  my @exceptions_in_file = @{$exceptions{$filename_header}};
  my $message = "";
  foreach my $i (@exceptions_in_file)
  {
    my ($cppname, $basename, @extra_namespace) = @{$i};
    if (@extra_namespace == 0)
    {
      $message .= "  Glib::Error::register_domain(${basename}_quark(), &" .
                  "${cppname}::throw_func);\n";
    }
  }
  print_with_guards($filename_header, $message) if $message;
}

# Exception classes in sub-namespaces are registered after the ones in the main
# namespace. If you ever change this order, check that it's ok with Glib::ThreadError
# and Glib::Threads::ThreadError. Both these classes wrap GThreadError, and
# Glib::ThreadError is deprecated (2012-02-27).

print "\n  // Call the wrap_init() functions in sub-namespaces:\n" if keys %extra_namespaces > 0;

foreach my $sub_namespace (sort keys %extra_namespaces)
{
  print "  ${sub_namespace}::wrap_init();\n";
}

print "\n";
print "  // Map gtypes to gtkmm wrapper-creation functions:\n";

foreach my $filename_header (sort keys %objects)
{
  next if($exclude_from_wrap_init{$filename_header});

  my @objects_in_file = @{$objects{$filename_header}};
  my $message = "";
  foreach my $i (@objects_in_file)
  {
    my ($cppname, $basename, @extra_namespace) = @{$i};
    my $qualified_cppname = join("::", (@extra_namespace, $cppname));
    $message .= "  Glib::wrap_register(${basename}_get_type(), &" .
                "${qualified_cppname}_Class::wrap_new);\n";
  }
  print_with_guards($filename_header, $message);
}

print "\n";
print "  // Register the gtkmm gtypes:\n";

foreach my $filename_header (sort keys %objects)
{
  next if($exclude_from_wrap_init{$filename_header});

  my @objects_in_file = @{$objects{$filename_header}};
  my $message = "";
  foreach my $i (@objects_in_file)
  {
    my ($cppname, $basename, @extra_namespace) = @{$i};
    my $qualified_cppname = join("::", (@extra_namespace, $cppname));
    $message .= "  ${qualified_cppname}::get_type();\n"
  }
  print_with_guards($filename_header, $message);
}

print << "EOF";

} // wrap_init()

$namespace_whole_close
EOF

exit 0;