codegen: Fix freeing of owned parameters
[vala-lang.git] / compiler / valacompiler.vala
blob95e6d198fd9500f44ba2485f72075c3ea9cac95f
1 /* valacompiler.vala
3 * Copyright (C) 2006-2010 Jürg Billeter
4 * Copyright (C) 1996-2002, 2004, 2005, 2006 Free Software Foundation, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 * Author:
21 * Jürg Billeter <j@bitron.ch>
24 using GLib;
26 class Vala.Compiler {
27 static string basedir;
28 static string directory;
29 static bool version;
30 [CCode (array_length = false, array_null_terminated = true)]
31 static string[] sources;
32 [CCode (array_length = false, array_null_terminated = true)]
33 static string[] vapi_directories;
34 [CCode (array_length = false, array_null_terminated = true)]
35 static string[] gir_directories;
36 static string vapi_filename;
37 static string library;
38 static string gir;
39 [CCode (array_length = false, array_null_terminated = true)]
40 static string[] packages;
41 [CCode (array_length = false, array_null_terminated = true)]
42 static string[] fast_vapis;
43 static string target_glib;
45 static bool ccode_only;
46 static string header_filename;
47 static bool use_header;
48 static string internal_header_filename;
49 static string internal_vapi_filename;
50 static string fast_vapi_filename;
51 static string symbols_filename;
52 static string includedir;
53 static bool compile_only;
54 static string output;
55 static bool debug;
56 static bool thread;
57 static bool mem_profiler;
58 static bool disable_assert;
59 static bool enable_checking;
60 static bool deprecated;
61 static bool experimental;
62 static bool experimental_non_null;
63 static bool disable_dbus_transformation;
64 static bool disable_warnings;
65 static string cc_command;
66 [CCode (array_length = false, array_null_terminated = true)]
67 static string[] cc_options;
68 static string dump_tree;
69 static bool save_temps;
70 [CCode (array_length = false, array_null_terminated = true)]
71 static string[] defines;
72 static bool quiet_mode;
73 static bool verbose_mode;
74 static string profile;
75 static bool nostdpkg;
76 static bool enable_version_header;
77 static bool disable_version_header;
78 static bool fatal_warnings;
79 static string dependencies;
81 static string entry_point;
83 static bool run_output;
85 private CodeContext context;
87 const OptionEntry[] options = {
88 { "girdir", 0, 0, OptionArg.FILENAME_ARRAY, ref gir_directories, "Look for .gir files in DIRECTORY", "DIRECTORY..." },
89 { "vapidir", 0, 0, OptionArg.FILENAME_ARRAY, ref vapi_directories, "Look for package bindings in DIRECTORY", "DIRECTORY..." },
90 { "pkg", 0, 0, OptionArg.STRING_ARRAY, ref packages, "Include binding for PACKAGE", "PACKAGE..." },
91 { "vapi", 0, 0, OptionArg.FILENAME, ref vapi_filename, "Output VAPI file name", "FILE" },
92 { "library", 0, 0, OptionArg.STRING, ref library, "Library name", "NAME" },
93 { "gir", 0, 0, OptionArg.STRING, ref gir, "GObject-Introspection repository file name", "NAME-VERSION.gir" },
94 { "basedir", 'b', 0, OptionArg.FILENAME, ref basedir, "Base source directory", "DIRECTORY" },
95 { "directory", 'd', 0, OptionArg.FILENAME, ref directory, "Output directory", "DIRECTORY" },
96 { "version", 0, 0, OptionArg.NONE, ref version, "Display version number", null },
97 { "ccode", 'C', 0, OptionArg.NONE, ref ccode_only, "Output C code", null },
98 { "header", 'H', 0, OptionArg.FILENAME, ref header_filename, "Output C header file", "FILE" },
99 { "use-header", 0, 0, OptionArg.NONE, ref use_header, "Use C header file", null },
100 { "includedir", 0, 0, OptionArg.FILENAME, ref includedir, "Directory used to include the C header file", "DIRECTORY" },
101 { "internal-header", 'h', 0, OptionArg.FILENAME, ref internal_header_filename, "Output internal C header file", "FILE" },
102 { "internal-vapi", 0, 0, OptionArg.FILENAME, ref internal_vapi_filename, "Output vapi with internal api", "FILE" },
103 { "fast-vapi", 0, 0, OptionArg.STRING, ref fast_vapi_filename, "Output vapi without performing symbol resolution", null },
104 { "use-fast-vapi", 0, 0, OptionArg.STRING_ARRAY, ref fast_vapis, "Use --fast-vapi output during this compile", null },
105 { "deps", 0, 0, OptionArg.STRING, ref dependencies, "Write make-style dependency information to this file", null },
106 { "symbols", 0, 0, OptionArg.FILENAME, ref symbols_filename, "Output symbols file", "FILE" },
107 { "compile", 'c', 0, OptionArg.NONE, ref compile_only, "Compile but do not link", null },
108 { "output", 'o', 0, OptionArg.FILENAME, ref output, "Place output in file FILE", "FILE" },
109 { "debug", 'g', 0, OptionArg.NONE, ref debug, "Produce debug information", null },
110 { "thread", 0, 0, OptionArg.NONE, ref thread, "Enable multithreading support", null },
111 { "enable-mem-profiler", 0, 0, OptionArg.NONE, ref mem_profiler, "Enable GLib memory profiler", null },
112 { "define", 'D', 0, OptionArg.STRING_ARRAY, ref defines, "Define SYMBOL", "SYMBOL..." },
113 { "main", 0, 0, OptionArg.STRING, ref entry_point, "Use SYMBOL as entry point", "SYMBOL..." },
114 { "nostdpkg", 0, 0, OptionArg.NONE, ref nostdpkg, "Do not include standard packages", null },
115 { "disable-assert", 0, 0, OptionArg.NONE, ref disable_assert, "Disable assertions", null },
116 { "enable-checking", 0, 0, OptionArg.NONE, ref enable_checking, "Enable additional run-time checks", null },
117 { "enable-deprecated", 0, 0, OptionArg.NONE, ref deprecated, "Enable deprecated features", null },
118 { "enable-experimental", 0, 0, OptionArg.NONE, ref experimental, "Enable experimental features", null },
119 { "disable-warnings", 0, 0, OptionArg.NONE, ref disable_warnings, "Disable warnings", null },
120 { "fatal-warnings", 0, 0, OptionArg.NONE, ref fatal_warnings, "Treat warnings as fatal", null },
121 { "enable-experimental-non-null", 0, 0, OptionArg.NONE, ref experimental_non_null, "Enable experimental enhancements for non-null types", null },
122 { "disable-dbus-transformation", 0, 0, OptionArg.NONE, ref disable_dbus_transformation, "Disable transformation of D-Bus member names", null },
123 { "cc", 0, 0, OptionArg.STRING, ref cc_command, "Use COMMAND as C compiler command", "COMMAND" },
124 { "Xcc", 'X', 0, OptionArg.STRING_ARRAY, ref cc_options, "Pass OPTION to the C compiler", "OPTION..." },
125 { "dump-tree", 0, 0, OptionArg.FILENAME, ref dump_tree, "Write code tree to FILE", "FILE" },
126 { "save-temps", 0, 0, OptionArg.NONE, ref save_temps, "Keep temporary files", null },
127 { "profile", 0, 0, OptionArg.STRING, ref profile, "Use the given profile instead of the default", "PROFILE" },
128 { "quiet", 'q', 0, OptionArg.NONE, ref quiet_mode, "Do not print messages to the console", null },
129 { "verbose", 'v', 0, OptionArg.NONE, ref verbose_mode, "Print additional messages to the console", null },
130 { "target-glib", 0, 0, OptionArg.STRING, ref target_glib, "Target version of glib for code generation", "MAJOR.MINOR" },
131 { "enable-version-header", 0, 0, OptionArg.NONE, ref enable_version_header, "Write vala build version in generated files", null },
132 { "disable-version-header", 0, 0, OptionArg.NONE, ref disable_version_header, "Do not write vala build version in generated files", null },
133 { "", 0, 0, OptionArg.FILENAME_ARRAY, ref sources, null, "FILE..." },
134 { null }
137 private int quit () {
138 if (context.report.get_errors () == 0 && context.report.get_warnings () == 0) {
139 return 0;
141 if (context.report.get_errors () == 0 && (!fatal_warnings || context.report.get_warnings () == 0)) {
142 if (!quiet_mode) {
143 stdout.printf ("Compilation succeeded - %d warning(s)\n", context.report.get_warnings ());
145 return 0;
146 } else {
147 if (!quiet_mode) {
148 stdout.printf ("Compilation failed: %d error(s), %d warning(s)\n", context.report.get_errors (), context.report.get_warnings ());
150 return 1;
154 private int run () {
155 context = new CodeContext ();
156 CodeContext.push (context);
158 // default to build executable
159 if (!ccode_only && !compile_only && output == null) {
160 // strip extension if there is one
161 // else we use the default output file of the C compiler
162 if (sources[0].last_index_of_char ('.') != -1) {
163 int dot = sources[0].last_index_of_char ('.');
164 output = Path.get_basename (sources[0].substring (0, dot));
168 context.assert = !disable_assert;
169 context.checking = enable_checking;
170 context.deprecated = deprecated;
171 context.experimental = experimental;
172 context.experimental_non_null = experimental_non_null;
173 context.dbus_transformation = !disable_dbus_transformation;
174 context.report.enable_warnings = !disable_warnings;
175 context.report.set_verbose_errors (!quiet_mode);
176 context.verbose_mode = verbose_mode;
177 context.version_header = !disable_version_header;
179 context.ccode_only = ccode_only;
180 context.compile_only = compile_only;
181 context.header_filename = header_filename;
182 if (header_filename == null && use_header) {
183 Report.error (null, "--use-header may only be used in combination with --header");
185 context.use_header = use_header;
186 context.internal_header_filename = internal_header_filename;
187 context.symbols_filename = symbols_filename;
188 context.includedir = includedir;
189 context.output = output;
190 if (basedir == null) {
191 context.basedir = CodeContext.realpath (".");
192 } else {
193 context.basedir = CodeContext.realpath (basedir);
195 if (directory != null) {
196 context.directory = CodeContext.realpath (directory);
197 } else {
198 context.directory = context.basedir;
200 context.vapi_directories = vapi_directories;
201 context.gir_directories = gir_directories;
202 context.debug = debug;
203 context.thread = thread;
204 context.mem_profiler = mem_profiler;
205 context.save_temps = save_temps;
206 if (profile == "posix") {
207 context.profile = Profile.POSIX;
208 context.add_define ("POSIX");
209 } else if (profile == "gobject-2.0" || profile == "gobject" || profile == null) {
210 // default profile
211 context.profile = Profile.GOBJECT;
212 context.add_define ("GOBJECT");
213 } else if (profile == "dova") {
214 context.profile = Profile.DOVA;
215 context.add_define ("DOVA");
216 } else {
217 Report.error (null, "Unknown profile %s".printf (profile));
219 nostdpkg |= fast_vapi_filename != null;
220 context.nostdpkg = nostdpkg;
222 context.entry_point_name = entry_point;
224 context.run_output = run_output;
226 if (defines != null) {
227 foreach (string define in defines) {
228 context.add_define (define);
232 for (int i = 2; i <= 12; i += 2) {
233 context.add_define ("VALA_0_%d".printf (i));
236 if (context.profile == Profile.POSIX) {
237 if (!nostdpkg) {
238 /* default package */
239 context.add_external_package ("posix");
241 } else if (context.profile == Profile.GOBJECT) {
242 int glib_major = 2;
243 int glib_minor = 16;
244 if (target_glib != null && target_glib.scanf ("%d.%d", out glib_major, out glib_minor) != 2) {
245 Report.error (null, "Invalid format for --target-glib");
248 context.target_glib_major = glib_major;
249 context.target_glib_minor = glib_minor;
250 if (context.target_glib_major != 2) {
251 Report.error (null, "This version of valac only supports GLib 2");
254 for (int i = 16; i <= glib_minor; i += 2) {
255 context.add_define ("GLIB_2_%d".printf (i));
258 if (!nostdpkg) {
259 /* default packages */
260 context.add_external_package ("glib-2.0");
261 context.add_external_package ("gobject-2.0");
263 } else if (context.profile == Profile.DOVA) {
264 if (!nostdpkg) {
265 /* default package */
266 context.add_external_package ("dova-core-0.1");
270 if (packages != null) {
271 foreach (string package in packages) {
272 context.add_external_package (package);
273 if (context.profile == Profile.GOBJECT && package == "dbus-glib-1") {
274 context.add_define ("DBUS_GLIB");
277 packages = null;
280 if (fast_vapis != null) {
281 foreach (string vapi in fast_vapis) {
282 var rpath = CodeContext.realpath (vapi);
283 var source_file = new SourceFile (context, SourceFileType.FAST, rpath);
284 context.add_source_file (source_file);
288 if (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () > 0)) {
289 return quit ();
292 if (context.profile == Profile.GOBJECT) {
293 if (context.has_package ("dbus-glib-1")) {
294 if (!context.deprecated) {
295 Report.warning (null, "D-Bus GLib is deprecated, use GDBus");
297 context.codegen = new DBusServerModule ();
298 } else {
299 context.codegen = new GDBusServerModule ();
301 } else if (context.profile == Profile.DOVA) {
302 context.codegen = new DovaErrorModule ();
303 } else {
304 context.codegen = new CCodeDelegateModule ();
307 bool has_c_files = false;
309 foreach (string source in sources) {
310 if (context.add_source_filename (source, run_output)) {
311 if (source.has_suffix (".c")) {
312 has_c_files = true;
316 sources = null;
318 if (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () > 0)) {
319 return quit ();
322 var parser = new Parser ();
323 parser.parse (context);
325 var genie_parser = new Genie.Parser ();
326 genie_parser.parse (context);
328 var gir_parser = new GirParser ();
329 gir_parser.parse (context);
331 if (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () > 0)) {
332 return quit ();
335 if (fast_vapi_filename != null) {
336 var interface_writer = new CodeWriter (CodeWriterType.FAST);
337 interface_writer.write_file (context, fast_vapi_filename);
338 return quit ();
341 context.check ();
343 if (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () > 0)) {
344 return quit ();
347 if (!ccode_only && !compile_only && library == null) {
348 // building program, require entry point
349 if (!has_c_files && context.entry_point == null) {
350 Report.error (null, "program does not contain a static `main' method");
354 if (dump_tree != null) {
355 var code_writer = new CodeWriter (CodeWriterType.DUMP);
356 code_writer.write_file (context, dump_tree);
359 if (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () > 0)) {
360 return quit ();
363 context.codegen.emit (context);
365 if (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () > 0)) {
366 return quit ();
369 if (vapi_filename == null && library != null) {
370 // keep backward compatibility with --library option
371 vapi_filename = "%s.vapi".printf (library);
374 if (library != null) {
375 if (gir != null) {
376 if (context.profile == Profile.GOBJECT) {
377 long gir_len = gir.length;
378 int last_hyphen = gir.last_index_of_char ('-');
380 if (last_hyphen == -1 || !gir.has_suffix (".gir")) {
381 Report.error (null, "GIR file name `%s' is not well-formed, expected NAME-VERSION.gir".printf (gir));
382 } else {
383 string gir_namespace = gir.substring (0, last_hyphen);
384 string gir_version = gir.substring (last_hyphen + 1, gir_len - last_hyphen - 5);
385 gir_version.canon ("0123456789.", '?');
386 if (gir_namespace == "" || gir_version == "" || !gir_version[0].isdigit () || gir_version.contains ("?")) {
387 Report.error (null, "GIR file name `%s' is not well-formed, expected NAME-VERSION.gir".printf (gir));
388 } else {
389 var gir_writer = new GIRWriter ();
391 // put .gir file in current directory unless -d has been explicitly specified
392 string gir_directory = ".";
393 if (directory != null) {
394 gir_directory = context.directory;
397 gir_writer.write_file (context, gir_directory, gir_namespace, gir_version, library);
402 gir = null;
405 library = null;
408 // The GIRWriter places the gir_namespace and gir_version into the top namespace, so write the vapi after that stage
409 if (vapi_filename != null) {
410 var interface_writer = new CodeWriter ();
412 // put .vapi file in current directory unless -d has been explicitly specified
413 if (directory != null && !Path.is_absolute (vapi_filename)) {
414 vapi_filename = "%s%c%s".printf (context.directory, Path.DIR_SEPARATOR, vapi_filename);
417 interface_writer.write_file (context, vapi_filename);
420 if (internal_vapi_filename != null) {
421 if (internal_header_filename == null ||
422 header_filename == null) {
423 Report.error (null, "--internal-vapi may only be used in combination with --header and --internal-header");
424 return quit();
427 var interface_writer = new CodeWriter (CodeWriterType.INTERNAL);
428 interface_writer.set_cheader_override(header_filename, internal_header_filename);
429 string vapi_filename = internal_vapi_filename;
431 // put .vapi file in current directory unless -d has been explicitly specified
432 if (directory != null && !Path.is_absolute (vapi_filename)) {
433 vapi_filename = "%s%c%s".printf (context.directory, Path.DIR_SEPARATOR, vapi_filename);
436 interface_writer.write_file (context, vapi_filename);
438 internal_vapi_filename = null;
441 if (dependencies != null) {
442 context.write_dependencies (dependencies);
445 if (!ccode_only) {
446 var ccompiler = new CCodeCompiler ();
447 if (cc_command == null && Environment.get_variable ("CC") != null) {
448 cc_command = Environment.get_variable ("CC");
450 if (cc_options == null) {
451 ccompiler.compile (context, cc_command, new string[] { });
452 } else {
453 ccompiler.compile (context, cc_command, cc_options);
457 return quit ();
460 static int run_source (string[] args) {
461 int i = 1;
462 if (args[i] != null && args[i].has_prefix ("-")) {
463 try {
464 string[] compile_args;
465 Shell.parse_argv ("valac " + args[1], out compile_args);
467 var opt_context = new OptionContext ("- Vala");
468 opt_context.set_help_enabled (true);
469 opt_context.add_main_entries (options, null);
470 unowned string[] temp_args = compile_args;
471 opt_context.parse (ref temp_args);
472 } catch (ShellError e) {
473 stdout.printf ("%s\n", e.message);
474 return 1;
475 } catch (OptionError e) {
476 stdout.printf ("%s\n", e.message);
477 stdout.printf ("Run '%s --help' to see a full list of available command line options.\n", args[0]);
478 return 1;
481 i++;
484 if (args[i] == null) {
485 stderr.printf ("No source file specified.\n");
486 return 1;
489 sources = { args[i] };
490 output = "%s/%s.XXXXXX".printf (Environment.get_tmp_dir (), Path.get_basename (args[i]));
491 int outputfd = FileUtils.mkstemp (output);
492 if (outputfd < 0) {
493 return 1;
496 run_output = true;
497 disable_warnings = true;
498 quiet_mode = true;
500 var compiler = new Compiler ();
501 int ret = compiler.run ();
502 if (ret != 0) {
503 return ret;
506 FileUtils.close (outputfd);
507 if (FileUtils.chmod (output, 0700) != 0) {
508 FileUtils.unlink (output);
509 return 1;
512 string[] target_args = { output };
513 while (i < args.length) {
514 target_args += args[i];
515 i++;
518 try {
519 Pid pid;
520 var loop = new MainLoop ();
521 int child_status = 0;
523 Process.spawn_async (null, target_args, null, SpawnFlags.CHILD_INHERITS_STDIN | SpawnFlags.DO_NOT_REAP_CHILD | SpawnFlags.FILE_AND_ARGV_ZERO, null, out pid);
525 FileUtils.unlink (output);
526 ChildWatch.add (pid, (pid, status) => {
527 child_status = (status & 0xff00) >> 8;
528 loop.quit ();
531 loop.run ();
533 return child_status;
534 } catch (SpawnError e) {
535 stdout.printf ("%s\n", e.message);
536 return 1;
540 static int main (string[] args) {
541 // initialize locale
542 Intl.setlocale (LocaleCategory.ALL, "");
544 if (Path.get_basename (args[0]) == "vala" || Path.get_basename (args[0]) == "vala" + Config.PACKAGE_SUFFIX) {
545 return run_source (args);
548 try {
549 var opt_context = new OptionContext ("- Vala Compiler");
550 opt_context.set_help_enabled (true);
551 opt_context.add_main_entries (options, null);
552 opt_context.parse (ref args);
553 } catch (OptionError e) {
554 stdout.printf ("%s\n", e.message);
555 stdout.printf ("Run '%s --help' to see a full list of available command line options.\n", args[0]);
556 return 1;
559 if (version) {
560 stdout.printf ("Vala %s\n", Config.BUILD_VERSION);
561 return 0;
564 if (sources == null && fast_vapis == null) {
565 stderr.printf ("No source file specified.\n");
566 return 1;
569 var compiler = new Compiler ();
570 return compiler.run ();