1/* valacompiler.vala
2 *
3 * Copyright (C) 2006-2012  Jürg Billeter
4 * Copyright (C) 1996-2002, 2004, 2005, 2006 Free Software Foundation, Inc.
5 *
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.
10
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.
15
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
19 *
20 * Author:
21 * 	Jürg Billeter <j@bitron.ch>
22 */
23
24using GLib;
25
26class Vala.Compiler {
27	private const string DEFAULT_COLORS = "error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01";
28
29	static string basedir;
30	static string directory;
31	static bool version;
32	static bool api_version;
33	[CCode (array_length = false, array_null_terminated = true)]
34	static string[] sources;
35	[CCode (array_length = false, array_null_terminated = true)]
36	static string[] vapi_directories;
37	[CCode (array_length = false, array_null_terminated = true)]
38	static string[] gir_directories;
39	[CCode (array_length = false, array_null_terminated = true)]
40	static string[] metadata_directories;
41	static string vapi_filename;
42	static string library;
43	static string shared_library;
44	static string gir;
45	[CCode (array_length = false, array_null_terminated = true)]
46	static string[] packages;
47	[CCode (array_length = false, array_null_terminated = true)]
48	static string[] fast_vapis;
49	static string target_glib;
50	[CCode (array_length = false, array_null_terminated = true)]
51	static string[] gresources;
52	[CCode (array_length = false, array_null_terminated = true)]
53	static string[] gresources_directories;
54
55	static bool ccode_only;
56	static bool abi_stability;
57	static string header_filename;
58	static bool use_header;
59	static string internal_header_filename;
60	static string internal_vapi_filename;
61	static string fast_vapi_filename;
62	static bool vapi_comments;
63	static string symbols_filename;
64	static string includedir;
65	static bool compile_only;
66	static string output;
67	static bool debug;
68	static bool mem_profiler;
69	static bool disable_assert;
70	static bool enable_checking;
71	static bool deprecated;
72	static bool hide_internal;
73	static bool experimental;
74	static bool experimental_non_null;
75	static bool gobject_tracing;
76	static bool disable_since_check;
77	static bool disable_warnings;
78	static bool keep_going;
79	static bool list_sources;
80	static string cc_command;
81	[CCode (array_length = false, array_null_terminated = true)]
82	static string[] cc_options;
83	static string pkg_config_command;
84	static string dump_tree;
85	static bool save_temps;
86	[CCode (array_length = false, array_null_terminated = true)]
87	static string[] defines;
88	static bool quiet_mode;
89	static bool verbose_mode;
90	static string profile;
91	static bool nostdpkg;
92	static bool enable_version_header;
93	static bool disable_version_header;
94	static bool fatal_warnings;
95	static bool disable_colored_output;
96	static Report.Colored colored_output = Report.Colored.AUTO;
97	static string dependencies;
98	static string depfile;
99
100	static string entry_point;
101
102	static bool run_output;
103	static string run_args;
104
105	private CodeContext context;
106
107	const OptionEntry[] options = {
108		{ "vapidir", 0, 0, OptionArg.FILENAME_ARRAY, ref vapi_directories, "Look for package bindings in DIRECTORY", "DIRECTORY..." },
109		{ "girdir", 0, 0, OptionArg.FILENAME_ARRAY, ref gir_directories, "Look for .gir files in DIRECTORY", "DIRECTORY..." },
110		{ "metadatadir", 0, 0, OptionArg.FILENAME_ARRAY, ref metadata_directories, "Look for GIR .metadata files in DIRECTORY", "DIRECTORY..." },
111		{ "pkg", 0, 0, OptionArg.STRING_ARRAY, ref packages, "Include binding for PACKAGE", "PACKAGE..." },
112		{ "vapi", 0, 0, OptionArg.FILENAME, ref vapi_filename, "Output VAPI file name", "FILE" },
113		{ "library", 0, 0, OptionArg.STRING, ref library, "Library name", "NAME" },
114		{ "shared-library", 0, 0, OptionArg.STRING, ref shared_library, "Shared library name used in generated gir", "NAME" },
115		{ "gir", 0, 0, OptionArg.STRING, ref gir, "GObject-Introspection repository file name", "NAME-VERSION.gir" },
116		{ "basedir", 'b', 0, OptionArg.FILENAME, ref basedir, "Base source directory", "DIRECTORY" },
117		{ "directory", 'd', 0, OptionArg.FILENAME, ref directory, "Change output directory from current working directory", "DIRECTORY" },
118		{ "version", 0, 0, OptionArg.NONE, ref version, "Display version number", null },
119		{ "api-version", 0, 0, OptionArg.NONE, ref api_version, "Display API version number", null },
120		{ "ccode", 'C', 0, OptionArg.NONE, ref ccode_only, "Output C code", null },
121		{ "header", 'H', 0, OptionArg.FILENAME, ref header_filename, "Output C header file", "FILE" },
122		{ "use-header", 0, 0, OptionArg.NONE, ref use_header, "Use C header file", null },
123		{ "includedir", 0, 0, OptionArg.FILENAME, ref includedir, "Directory used to include the C header file", "DIRECTORY" },
124		{ "internal-header", 'h', 0, OptionArg.FILENAME, ref internal_header_filename, "Output internal C header file", "FILE" },
125		{ "internal-vapi", 0, 0, OptionArg.FILENAME, ref internal_vapi_filename, "Output vapi with internal api", "FILE" },
126		{ "fast-vapi", 0, 0, OptionArg.STRING, ref fast_vapi_filename, "Output vapi without performing symbol resolution", null },
127		{ "use-fast-vapi", 0, 0, OptionArg.STRING_ARRAY, ref fast_vapis, "Use --fast-vapi output during this compile", null },
128		{ "vapi-comments", 0, 0, OptionArg.NONE, ref vapi_comments, "Include comments in generated vapi", null },
129		{ "deps", 0, 0, OptionArg.STRING, ref dependencies, "Write make-style dependency information to this file", null },
130		{ "depfile", 0, 0, OptionArg.STRING, ref depfile, "Write make-style external dependency information for build systems to this file", null },
131		{ "list-sources", 0, 0, OptionArg.NONE, ref list_sources, "Output a list of all source and binding files which are used", null },
132		{ "symbols", 0, 0, OptionArg.FILENAME, ref symbols_filename, "Output symbols file", "FILE" },
133		{ "compile", 'c', 0, OptionArg.NONE, ref compile_only, "Compile but do not link", null },
134		{ "output", 'o', 0, OptionArg.FILENAME, ref output, "Place output in file FILE", "FILE" },
135		{ "debug", 'g', 0, OptionArg.NONE, ref debug, "Produce debug information", null },
136		{ "thread", 0, OptionFlags.OPTIONAL_ARG | OptionFlags.NO_ARG, OptionArg.CALLBACK, (void*) option_deprecated, "Enable multithreading support (DEPRECATED AND IGNORED)", null },
137		{ "enable-mem-profiler", 0, 0, OptionArg.NONE, ref mem_profiler, "Enable GLib memory profiler", null },
138		{ "define", 'D', 0, OptionArg.STRING_ARRAY, ref defines, "Define SYMBOL", "SYMBOL..." },
139		{ "main", 0, 0, OptionArg.STRING, ref entry_point, "Use SYMBOL as entry point", "SYMBOL..." },
140		{ "nostdpkg", 0, 0, OptionArg.NONE, ref nostdpkg, "Do not include standard packages", null },
141		{ "disable-assert", 0, 0, OptionArg.NONE, ref disable_assert, "Disable assertions", null },
142		{ "enable-checking", 0, 0, OptionArg.NONE, ref enable_checking, "Enable additional run-time checks", null },
143		{ "enable-deprecated", 0, 0, OptionArg.NONE, ref deprecated, "Enable deprecated features", null },
144		{ "hide-internal", 0, 0, OptionArg.NONE, ref hide_internal, "Hide symbols marked as internal", null },
145		{ "enable-experimental", 0, 0, OptionArg.NONE, ref experimental, "Enable experimental features", null },
146		{ "disable-warnings", 0, 0, OptionArg.NONE, ref disable_warnings, "Disable warnings", null },
147		{ "fatal-warnings", 0, 0, OptionArg.NONE, ref fatal_warnings, "Treat warnings as fatal", null },
148		{ "disable-since-check", 0, 0, OptionArg.NONE, ref disable_since_check, "Do not check whether used symbols exist in local packages", null },
149		{ "keep-going", 'k', 0, OptionArg.NONE, ref keep_going, "Continue as much as possible after an error", null },
150		{ "enable-experimental-non-null", 0, 0, OptionArg.NONE, ref experimental_non_null, "Enable experimental enhancements for non-null types", null },
151		{ "enable-gobject-tracing", 0, 0, OptionArg.NONE, ref gobject_tracing, "Enable GObject creation tracing", null },
152		{ "cc", 0, 0, OptionArg.STRING, ref cc_command, "Use COMMAND as C compiler command", "COMMAND" },
153		{ "Xcc", 'X', 0, OptionArg.STRING_ARRAY, ref cc_options, "Pass OPTION to the C compiler", "OPTION..." },
154		{ "pkg-config", 0, 0, OptionArg.STRING, ref pkg_config_command, "Use COMMAND as pkg-config command", "COMMAND" },
155		{ "dump-tree", 0, 0, OptionArg.FILENAME, ref dump_tree, "Write code tree to FILE", "FILE" },
156		{ "save-temps", 0, 0, OptionArg.NONE, ref save_temps, "Keep temporary files", null },
157		{ "profile", 0, 0, OptionArg.STRING, ref profile, "Use the given profile instead of the default", "PROFILE" },
158		{ "quiet", 'q', 0, OptionArg.NONE, ref quiet_mode, "Do not print messages to the console", null },
159		{ "verbose", 'v', 0, OptionArg.NONE, ref verbose_mode, "Print additional messages to the console", null },
160		{ "no-color", 0, 0, OptionArg.NONE, ref disable_colored_output, "Disable colored output, alias for --color=never", null },
161		{ "color", 0, OptionFlags.OPTIONAL_ARG, OptionArg.CALLBACK, (void*) option_parse_color, "Enable color output, options are 'always', 'never', or 'auto'", "WHEN" },
162		{ "target-glib", 0, 0, OptionArg.STRING, ref target_glib, "Target version of glib for code generation", "'MAJOR.MINOR', or 'auto'" },
163		{ "gresources", 0, 0, OptionArg.FILENAME_ARRAY, ref gresources, "XML of gresources", "FILE..." },
164		{ "gresourcesdir", 0, 0, OptionArg.FILENAME_ARRAY, ref gresources_directories, "Look for resources in DIRECTORY", "DIRECTORY..." },
165		{ "enable-version-header", 0, 0, OptionArg.NONE, ref enable_version_header, "Write vala build version in generated files", null },
166		{ "disable-version-header", 0, 0, OptionArg.NONE, ref disable_version_header, "Do not write vala build version in generated files", null },
167		{ "run-args", 0, 0, OptionArg.STRING, ref run_args, "Arguments passed to directly compiled executable", null },
168		{ "abi-stability", 0, 0, OptionArg.NONE, ref abi_stability, "Enable support for ABI stability", null },
169		{ OPTION_REMAINING, 0, 0, OptionArg.FILENAME_ARRAY, ref sources, null, "FILE..." },
170		{ null }
171	};
172
173	static bool option_parse_color (string option_name, string? val, void* data) throws OptionError {
174		switch (val) {
175			case "auto": colored_output = Report.Colored.AUTO; break;
176			case "never": colored_output = Report.Colored.NEVER; break;
177			case null:
178			case "always": colored_output = Report.Colored.ALWAYS; break;
179			default: throw new OptionError.FAILED ("Invalid --color argument '%s'", val);
180		}
181		return true;
182	}
183
184	static bool option_deprecated (string option_name, string? val, void* data) throws OptionError {
185		stdout.printf ("Command-line option `%s` is deprecated and will be ignored\n", option_name);
186		return true;
187	}
188
189	private int quit () {
190		if (context.report.get_errors () == 0 && context.report.get_warnings () == 0) {
191			CodeContext.pop ();
192			return 0;
193		}
194		if (context.report.get_errors () == 0 && (!fatal_warnings || context.report.get_warnings () == 0)) {
195			if (!quiet_mode) {
196				stdout.printf ("Compilation succeeded - %d warning(s)\n", context.report.get_warnings ());
197			}
198			CodeContext.pop ();
199			return 0;
200		} else {
201			if (!quiet_mode) {
202				stdout.printf ("Compilation failed: %d error(s), %d warning(s)\n", context.report.get_errors (), context.report.get_warnings ());
203			}
204			CodeContext.pop ();
205			return 1;
206		}
207	}
208
209	private int run () {
210		context = new CodeContext ();
211		CodeContext.push (context);
212
213		if (disable_colored_output) {
214			colored_output = Report.Colored.NEVER;
215		}
216
217		if (colored_output != Report.Colored.NEVER) {
218			unowned string env_colors = Environment.get_variable ("VALA_COLORS");
219			if (env_colors != null) {
220				context.report.set_colors (env_colors, colored_output);
221			} else {
222				context.report.set_colors (DEFAULT_COLORS, colored_output);
223			}
224		}
225
226
227		// default to build executable
228		if (!ccode_only && !compile_only && output == null) {
229			// strip extension if there is one
230			// else we use the default output file of the C compiler
231			if (sources[0].last_index_of_char ('.') != -1) {
232				int dot = sources[0].last_index_of_char ('.');
233				output = Path.get_basename (sources[0].substring (0, dot));
234			}
235		}
236
237		context.assert = !disable_assert;
238		context.checking = enable_checking;
239		context.deprecated = deprecated;
240		context.since_check = !disable_since_check;
241		context.hide_internal = hide_internal;
242		context.experimental = experimental;
243		context.experimental_non_null = experimental_non_null;
244		context.gobject_tracing = gobject_tracing;
245		context.keep_going = keep_going;
246		context.report.enable_warnings = !disable_warnings;
247		context.report.set_verbose_errors (!quiet_mode);
248		context.verbose_mode = verbose_mode;
249		context.version_header = !disable_version_header;
250
251		context.ccode_only = ccode_only;
252		if (ccode_only && cc_options != null) {
253			Report.warning (null, "-X has no effect when -C or --ccode is set");
254		}
255		context.abi_stability = abi_stability;
256		context.compile_only = compile_only;
257		context.header_filename = header_filename;
258		if (header_filename == null && use_header) {
259			Report.error (null, "--use-header may only be used in combination with --header");
260		}
261		context.use_header = use_header;
262		context.internal_header_filename = internal_header_filename;
263		context.symbols_filename = symbols_filename;
264		context.includedir = includedir;
265		context.output = output;
266		if (output != null && ccode_only) {
267			Report.warning (null, "--output and -o have no effect when -C or --ccode is set");
268		}
269		if (basedir == null) {
270			context.basedir = CodeContext.realpath (".");
271		} else {
272			context.basedir = CodeContext.realpath (basedir);
273		}
274		if (directory != null) {
275			context.directory = CodeContext.realpath (directory);
276		} else {
277			context.directory = context.basedir;
278		}
279		context.vapi_directories = vapi_directories;
280		context.vapi_comments = vapi_comments;
281		context.gir_directories = gir_directories;
282		context.metadata_directories = metadata_directories;
283		context.debug = debug;
284		context.mem_profiler = mem_profiler;
285		context.save_temps = save_temps;
286		if (ccode_only && save_temps) {
287			Report.warning (null, "--save-temps has no effect when -C or --ccode is set");
288		}
289		if (profile == "posix") {
290			context.profile = Profile.POSIX;
291			context.add_define ("POSIX");
292		} else if (profile == "gobject-2.0" || profile == "gobject" || profile == null) {
293			// default profile
294			context.profile = Profile.GOBJECT;
295			context.add_define ("GOBJECT");
296		} else {
297			Report.error (null, "Unknown profile %s".printf (profile));
298		}
299		nostdpkg |= fast_vapi_filename != null;
300		context.nostdpkg = nostdpkg;
301
302		context.entry_point_name = entry_point;
303
304		context.run_output = run_output;
305
306		if (pkg_config_command == null) {
307			pkg_config_command = Environment.get_variable ("PKG_CONFIG") ?? "pkg-config";
308		}
309		context.pkg_config_command = pkg_config_command;
310
311		if (defines != null) {
312			foreach (string define in defines) {
313				context.add_define (define);
314			}
315		}
316
317		if (context.profile == Profile.POSIX) {
318			if (!nostdpkg) {
319				/* default package */
320				context.add_external_package ("posix");
321			}
322		} else if (context.profile == Profile.GOBJECT) {
323			if (target_glib != null) {
324				context.set_target_glib_version (target_glib);
325			}
326
327			if (!nostdpkg) {
328				/* default packages */
329				context.add_external_package ("glib-2.0");
330				context.add_external_package ("gobject-2.0");
331			}
332		}
333
334		if (packages != null) {
335			foreach (string package in packages) {
336				context.add_external_package (package);
337			}
338			packages = null;
339		}
340
341		if (fast_vapis != null) {
342			foreach (string vapi in fast_vapis) {
343				var rpath = CodeContext.realpath (vapi);
344				var source_file = new SourceFile (context, SourceFileType.FAST, rpath);
345				context.add_source_file (source_file);
346			}
347			context.use_fast_vapi = true;
348		}
349
350		context.gresources = gresources;
351		context.gresources_directories = gresources_directories;
352
353		if (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () > 0)) {
354			return quit ();
355		}
356
357		if (context.profile == Profile.GOBJECT) {
358			context.codegen = new GDBusServerModule ();
359		} else {
360			context.codegen = new CCodeDelegateModule ();
361		}
362
363		bool has_c_files = false;
364		bool has_h_files = false;
365
366		foreach (string source in sources) {
367			if (context.add_source_filename (source, run_output, true)) {
368				if (source.has_suffix (".c")) {
369					has_c_files = true;
370				} else if (source.has_suffix (".h")) {
371					has_h_files = true;
372				}
373			}
374		}
375		sources = null;
376		if (ccode_only && (has_c_files || has_h_files)) {
377			Report.warning (null, "C header and source files are ignored when -C or --ccode is set");
378		}
379
380		if (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () > 0)) {
381			return quit ();
382		}
383
384		if (list_sources) {
385			foreach (SourceFile file in context.get_source_files ()) {
386				print ("%s\n", file.filename);
387			}
388			if (!ccode_only) {
389				foreach (string filename in context.get_c_source_files ()) {
390					print ("%s\n", filename);
391				}
392			}
393			return 0;
394		}
395
396		var parser = new Parser ();
397		parser.parse (context);
398
399		var genie_parser = new Genie.Parser ();
400		genie_parser.parse (context);
401
402		var gir_parser = new GirParser ();
403		gir_parser.parse (context);
404
405		if (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () > 0)) {
406			return quit ();
407		}
408
409		if (fast_vapi_filename != null) {
410			var interface_writer = new CodeWriter (CodeWriterType.FAST);
411			interface_writer.write_file (context, fast_vapi_filename);
412			return quit ();
413		}
414
415		context.check ();
416
417		if (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () > 0)) {
418			return quit ();
419		}
420
421		if (!ccode_only && !compile_only && library == null) {
422			// building program, require entry point
423			if (!has_c_files && context.entry_point == null) {
424				Report.error (null, "program does not contain a static `main' method");
425			}
426		}
427
428		if (dump_tree != null) {
429			var code_writer = new CodeWriter (CodeWriterType.DUMP);
430			code_writer.write_file (context, dump_tree);
431		}
432
433		if (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () > 0)) {
434			return quit ();
435		}
436
437		context.codegen.emit (context);
438
439		if (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () > 0)) {
440			return quit ();
441		}
442
443		if (vapi_filename == null && library != null) {
444			// keep backward compatibility with --library option
445			vapi_filename = "%s.vapi".printf (library);
446		}
447
448		if (library != null) {
449			if (gir != null) {
450				if (context.profile == Profile.GOBJECT) {
451					string gir_base = Path.get_basename (gir);
452					long gir_len = gir_base.length;
453					int last_hyphen = gir_base.last_index_of_char ('-');
454
455					if (last_hyphen == -1 || !gir_base.has_suffix (".gir")) {
456						Report.error (null, "GIR file name `%s' is not well-formed, expected NAME-VERSION.gir".printf (gir));
457					} else {
458						string gir_namespace = gir_base.substring (0, last_hyphen);
459						string gir_version = gir_base.substring (last_hyphen + 1, gir_len - last_hyphen - 5);
460						gir_version.canon ("0123456789.", '?');
461						if (gir_namespace == "" || gir_version == "" || !gir_version[0].isdigit () || gir_version.contains ("?")) {
462							Report.error (null, "GIR file name `%s' is not well-formed, expected NAME-VERSION.gir".printf (gir));
463						} else {
464							var gir_writer = new GIRWriter ();
465
466							// put .gir file in current directory unless -d has been explicitly specified
467							string gir_directory = ".";
468							if (directory != null) {
469								gir_directory = context.directory;
470							}
471
472							gir_writer.write_file (context, gir_directory, gir, gir_namespace, gir_version, library, shared_library);
473						}
474					}
475				}
476
477				gir = null;
478			}
479
480			library = null;
481		} else {
482			if (gir != null) {
483				Report.warning (null, "--gir has no effect without --library");
484				gir = null;
485			}
486		}
487
488		// The GIRWriter places the gir_namespace and gir_version into the top namespace, so write the vapi after that stage
489		if (vapi_filename != null) {
490			var interface_writer = new CodeWriter ();
491
492			// put .vapi file in current directory unless -d has been explicitly specified
493			if (directory != null && !Path.is_absolute (vapi_filename)) {
494				vapi_filename = "%s%c%s".printf (context.directory, Path.DIR_SEPARATOR, vapi_filename);
495			}
496
497			interface_writer.write_file (context, vapi_filename);
498		}
499
500		if (internal_vapi_filename != null) {
501			if (internal_header_filename == null ||
502			    header_filename == null) {
503				Report.error (null, "--internal-vapi may only be used in combination with --header and --internal-header");
504				return quit();
505			}
506
507			var interface_writer = new CodeWriter (CodeWriterType.INTERNAL);
508
509			if (context.includedir != null) {
510				var prefixed_header_filename = Path.build_path ("/", context.includedir, Path.get_basename (header_filename));
511				var prefixed_internal_header_filename = Path.build_path ("/", context.includedir, Path.get_basename (internal_header_filename));
512				interface_writer.set_cheader_override (prefixed_header_filename, prefixed_internal_header_filename);
513			} else {
514				interface_writer.set_cheader_override (header_filename, internal_header_filename);
515			}
516
517			string vapi_filename = internal_vapi_filename;
518
519			// put .vapi file in current directory unless -d has been explicitly specified
520			if (directory != null && !Path.is_absolute (vapi_filename)) {
521				vapi_filename = "%s%c%s".printf (context.directory, Path.DIR_SEPARATOR, vapi_filename);
522			}
523
524			interface_writer.write_file (context, vapi_filename);
525
526			internal_vapi_filename = null;
527		}
528
529		if (dependencies != null) {
530			context.write_dependencies (dependencies);
531		}
532
533		if (depfile != null) {
534			context.write_external_dependencies (depfile);
535		}
536
537		if (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () > 0)) {
538			return quit ();
539		}
540
541		if (!ccode_only) {
542			var ccompiler = new CCodeCompiler ();
543			if (cc_command == null && Environment.get_variable ("CC") != null) {
544				cc_command = Environment.get_variable ("CC");
545			}
546			if (cc_options == null) {
547				ccompiler.compile (context, cc_command, new string[] { });
548			} else {
549				ccompiler.compile (context, cc_command, cc_options);
550			}
551		}
552
553		return quit ();
554	}
555
556	static int run_source (string[] args) {
557		try {
558			var opt_context = new OptionContext ("- Vala Interpreter");
559			opt_context.set_help_enabled (true);
560			opt_context.add_main_entries (options, null);
561			opt_context.parse (ref args);
562		} catch (OptionError e) {
563			stdout.printf ("%s\n", e.message);
564			stdout.printf ("Run '%s --help' to see a full list of available command line options.\n", args[0]);
565			return 1;
566		}
567
568		if (version) {
569			stdout.printf ("Vala %s\n", Vala.BUILD_VERSION);
570			return 0;
571		} else if (api_version) {
572			stdout.printf ("%s\n", Vala.API_VERSION);
573			return 0;
574		}
575
576		if (sources == null) {
577			stderr.printf ("No source file specified.\n");
578			return 1;
579		}
580
581		output = "%s/%s.XXXXXX".printf (Environment.get_tmp_dir (), Path.get_basename (sources[0]));
582		int outputfd = FileUtils.mkstemp (output);
583		if (outputfd < 0) {
584			return 1;
585		}
586
587		ccode_only = false;
588		compile_only = false;
589		run_output = true;
590		disable_warnings = true;
591		quiet_mode = true;
592		library = null;
593		shared_library = null;
594
595		var compiler = new Compiler ();
596		int ret = compiler.run ();
597		if (ret != 0) {
598			return ret;
599		}
600
601		FileUtils.close (outputfd);
602		if (FileUtils.chmod (output, 0700) != 0) {
603			FileUtils.unlink (output);
604			return 1;
605		}
606
607		string[] target_args = { output };
608		if (run_args != null) {
609			string[] target_run_args = run_args.split (" ");
610			foreach (string arg in target_run_args) {
611				target_args += arg;
612			}
613		}
614
615		try {
616			Pid pid;
617			var loop = new MainLoop ();
618			int child_status = 0;
619
620			Process.spawn_async (null, target_args, null, SpawnFlags.CHILD_INHERITS_STDIN | SpawnFlags.DO_NOT_REAP_CHILD, null, out pid);
621
622			FileUtils.unlink (output);
623			ChildWatch.add (pid, (pid, status) => {
624				child_status = (status & 0xff00) >> 8;
625				loop.quit ();
626			});
627
628			loop.run ();
629
630			return child_status;
631		} catch (SpawnError e) {
632			stdout.printf ("%s\n", e.message);
633			return 1;
634		}
635	}
636
637	static int main (string[] args) {
638		// initialize locale
639		Intl.setlocale (LocaleCategory.ALL, "");
640
641		if (Path.get_basename (args[0]) == "vala" || Path.get_basename (args[0]) == "vala" + Config.PACKAGE_SUFFIX) {
642			return run_source (args);
643		}
644
645		try {
646			var opt_context = new OptionContext ("- Vala Compiler");
647			opt_context.set_help_enabled (true);
648			opt_context.add_main_entries (options, null);
649			opt_context.parse (ref args);
650		} catch (OptionError e) {
651			stdout.printf ("%s\n", e.message);
652			stdout.printf ("Run '%s --help' to see a full list of available command line options.\n", args[0]);
653			return 1;
654		}
655
656		if (version) {
657			stdout.printf ("Vala %s\n", Vala.BUILD_VERSION);
658			return 0;
659		} else if (api_version) {
660			stdout.printf ("%s\n", Vala.API_VERSION);
661			return 0;
662		}
663
664		if (sources == null && fast_vapis == null) {
665			stderr.printf ("No source file specified.\n");
666			return 1;
667		}
668
669		var compiler = new Compiler ();
670		return compiler.run ();
671	}
672}
673