GType: Fix C warnings for properties in interface_init
[vala-lang.git] / compiler / valacompiler.vala
blobe1cf96b1b3ecfadf463a1fe9bb299df1694a7e14
1 /* valacompiler.vala
3 * Copyright (C) 2006-2009 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 [NoArrayLength]
32 static string[] sources;
33 [CCode (array_length = false, array_null_terminated = true)]
34 [NoArrayLength]
35 static string[] vapi_directories;
36 static string vapi_filename;
37 static string library;
38 static string gir;
39 [CCode (array_length = false, array_null_terminated = true)]
40 [NoArrayLength]
41 static string[] packages;
42 static string target_glib;
44 static bool ccode_only;
45 static string header_filename;
46 static string internal_header_filename;
47 static string internal_vapi_filename;
48 static string includedir;
49 static bool compile_only;
50 static string output;
51 static bool debug;
52 static bool thread;
53 static bool disable_assert;
54 static bool enable_checking;
55 static bool deprecated;
56 static bool experimental;
57 static bool non_null_experimental;
58 static bool disable_dbus_transformation;
59 static string cc_command;
60 [CCode (array_length = false, array_null_terminated = true)]
61 [NoArrayLength]
62 static string[] cc_options;
63 static string dump_tree;
64 static bool save_temps;
65 [CCode (array_length = false, array_null_terminated = true)]
66 [NoArrayLength]
67 static string[] defines;
68 static bool quiet_mode;
69 static bool verbose_mode;
70 static string profile;
72 private CodeContext context;
74 const OptionEntry[] options = {
75 { "vapidir", 0, 0, OptionArg.FILENAME_ARRAY, ref vapi_directories, "Look for package bindings in DIRECTORY", "DIRECTORY..." },
76 { "pkg", 0, 0, OptionArg.STRING_ARRAY, ref packages, "Include binding for PACKAGE", "PACKAGE..." },
77 { "vapi", 0, 0, OptionArg.FILENAME, ref vapi_filename, "Output VAPI file name", "FILE" },
78 { "library", 0, 0, OptionArg.STRING, ref library, "Library name", "NAME" },
79 { "gir", 0, 0, OptionArg.STRING, ref gir, "GObject-Introspection repository file name", "NAME-VERSION.gir" },
80 { "basedir", 'b', 0, OptionArg.FILENAME, ref basedir, "Base source directory", "DIRECTORY" },
81 { "directory", 'd', 0, OptionArg.FILENAME, ref directory, "Output directory", "DIRECTORY" },
82 { "version", 0, 0, OptionArg.NONE, ref version, "Display version number", null },
83 { "ccode", 'C', 0, OptionArg.NONE, ref ccode_only, "Output C code", null },
84 { "header", 'H', 0, OptionArg.FILENAME, ref header_filename, "Output C header file", "FILE" },
85 { "includedir", 0, 0, OptionArg.FILENAME, ref includedir, "Directory used to include the C header file", "DIRECTORY" },
86 { "internal-header", 'h', 0, OptionArg.FILENAME, ref internal_header_filename, "Output internal C header file", "FILE" },
87 { "internal-vapi", 0, 0, OptionArg.FILENAME, ref internal_vapi_filename, "Output vapi with internal api", "FILE" },
88 { "compile", 'c', 0, OptionArg.NONE, ref compile_only, "Compile but do not link", null },
89 { "output", 'o', 0, OptionArg.FILENAME, ref output, "Place output in file FILE", "FILE" },
90 { "debug", 'g', 0, OptionArg.NONE, ref debug, "Produce debug information", null },
91 { "thread", 0, 0, OptionArg.NONE, ref thread, "Enable multithreading support", null },
92 { "define", 'D', 0, OptionArg.STRING_ARRAY, ref defines, "Define SYMBOL", "SYMBOL..." },
93 { "disable-assert", 0, 0, OptionArg.NONE, ref disable_assert, "Disable assertions", null },
94 { "enable-checking", 0, 0, OptionArg.NONE, ref enable_checking, "Enable additional run-time checks", null },
95 { "enable-deprecated", 0, 0, OptionArg.NONE, ref deprecated, "Enable deprecated features", null },
96 { "enable-experimental", 0, 0, OptionArg.NONE, ref experimental, "Enable experimental features", null },
97 { "enable-non-null-experimental", 0, 0, OptionArg.NONE, ref non_null_experimental, "Enable experimental enhancements for non-null types", null },
98 { "disable-dbus-transformation", 0, 0, OptionArg.NONE, ref disable_dbus_transformation, "Disable transformation of D-Bus member names", null },
99 { "cc", 0, 0, OptionArg.STRING, ref cc_command, "Use COMMAND as C compiler command", "COMMAND" },
100 { "Xcc", 'X', 0, OptionArg.STRING_ARRAY, ref cc_options, "Pass OPTION to the C compiler", "OPTION..." },
101 { "dump-tree", 0, 0, OptionArg.FILENAME, ref dump_tree, "Write code tree to FILE", "FILE" },
102 { "save-temps", 0, 0, OptionArg.NONE, ref save_temps, "Keep temporary files", null },
103 { "profile", 0, 0, OptionArg.STRING, ref profile, "Use the given profile instead of the default", "PROFILE" },
104 { "quiet", 'q', 0, OptionArg.NONE, ref quiet_mode, "Do not print messages to the console", null },
105 { "verbose", 'v', 0, OptionArg.NONE, ref verbose_mode, "Print additional messages to the console", null },
106 { "target-glib", 0, 0, OptionArg.STRING, ref target_glib, "Target version of glib for code generation", "MAJOR.MINOR" },
107 { "", 0, 0, OptionArg.FILENAME_ARRAY, ref sources, null, "FILE..." },
108 { null }
111 private int quit () {
112 if (context.report.get_errors () == 0 && context.report.get_warnings () == 0) {
113 return 0;
115 if (context.report.get_errors () == 0) {
116 if (!quiet_mode) {
117 stdout.printf ("Compilation succeeded - %d warning(s)\n", context.report.get_warnings ());
119 return 0;
120 } else {
121 if (!quiet_mode) {
122 stdout.printf ("Compilation failed: %d error(s), %d warning(s)\n", context.report.get_errors (), context.report.get_warnings ());
124 return 1;
128 private bool add_package (CodeContext context, string pkg) {
129 if (context.has_package (pkg)) {
130 // ignore multiple occurences of the same package
131 return true;
134 var package_path = context.get_package_path (pkg, vapi_directories);
136 if (package_path == null) {
137 return false;
140 context.add_package (pkg);
142 context.add_source_file (new SourceFile (context, package_path, true));
144 var deps_filename = Path.build_filename (Path.get_dirname (package_path), "%s.deps".printf (pkg));
145 if (FileUtils.test (deps_filename, FileTest.EXISTS)) {
146 try {
147 string deps_content;
148 ulong deps_len;
149 FileUtils.get_contents (deps_filename, out deps_content, out deps_len);
150 foreach (string dep in deps_content.split ("\n")) {
151 dep = dep.strip ();
152 if (dep != "") {
153 if (!add_package (context, dep)) {
154 Report.error (null, "%s, dependency of %s, not found in specified Vala API directories".printf (dep, pkg));
158 } catch (FileError e) {
159 Report.error (null, "Unable to read dependency file: %s".printf (e.message));
163 return true;
166 private int run () {
167 context = new CodeContext ();
168 CodeContext.push (context);
170 // default to build executable
171 if (!ccode_only && !compile_only && output == null) {
172 // strip extension if there is one
173 // else we use the default output file of the C compiler
174 if (sources[0].rchr (-1, '.') != null) {
175 long dot = sources[0].pointer_to_offset (sources[0].rchr (-1, '.'));
176 output = Path.get_basename (sources[0].substring (0, dot));
180 context.assert = !disable_assert;
181 context.checking = enable_checking;
182 context.deprecated = deprecated;
183 context.experimental = experimental;
184 context.non_null_experimental = non_null_experimental;
185 context.dbus_transformation = !disable_dbus_transformation;
186 context.report.set_verbose_errors (!quiet_mode);
187 context.verbose_mode = verbose_mode;
189 context.ccode_only = ccode_only;
190 context.compile_only = compile_only;
191 context.header_filename = header_filename;
192 context.internal_header_filename = internal_header_filename;
193 context.includedir = includedir;
194 context.output = output;
195 if (basedir == null) {
196 context.basedir = realpath (".");
197 } else {
198 context.basedir = realpath (basedir);
200 if (directory != null) {
201 context.directory = realpath (directory);
202 } else {
203 context.directory = context.basedir;
205 context.debug = debug;
206 context.thread = thread;
207 context.save_temps = save_temps;
208 if (profile == "posix") {
209 context.profile = Profile.POSIX;
210 context.add_define ("POSIX");
211 } else if (profile == "gobject-2.0" || profile == "gobject" || profile == null) {
212 // default profile
213 context.profile = Profile.GOBJECT;
214 context.add_define ("GOBJECT");
215 context.add_define ("VALA_0_7_6_NEW_METHODS");
216 } else {
217 Report.error (null, "Unknown profile %s".printf (profile));
220 if (defines != null) {
221 foreach (string define in defines) {
222 context.add_define (define);
226 if (context.profile == Profile.POSIX) {
227 /* default package */
228 if (!add_package (context, "posix")) {
229 Report.error (null, "posix not found in specified Vala API directories");
231 } else if (context.profile == Profile.GOBJECT) {
232 int glib_major = 2;
233 int glib_minor = 12;
234 if (target_glib != null && target_glib.scanf ("%d.%d", out glib_major, out glib_minor) != 2) {
235 Report.error (null, "Invalid format for --target-glib");
238 context.target_glib_major = glib_major;
239 context.target_glib_minor = glib_minor;
240 if (context.target_glib_major != 2) {
241 Report.error (null, "This version of valac only supports GLib 2");
244 /* default packages */
245 if (!add_package (context, "glib-2.0")) {
246 Report.error (null, "glib-2.0 not found in specified Vala API directories");
248 if (!add_package (context, "gobject-2.0")) {
249 Report.error (null, "gobject-2.0 not found in specified Vala API directories");
253 context.codegen = new CCodeGenerator ();
255 if (packages != null) {
256 foreach (string package in packages) {
257 if (!add_package (context, package)) {
258 Report.error (null, "%s not found in specified Vala API directories".printf (package));
261 packages = null;
264 if (context.report.get_errors () > 0) {
265 return quit ();
268 foreach (string source in sources) {
269 if (FileUtils.test (source, FileTest.EXISTS)) {
270 var rpath = realpath (source);
271 if (source.has_suffix (".vala") || source.has_suffix (".gs")) {
272 var source_file = new SourceFile (context, rpath);
274 if (context.profile == Profile.POSIX) {
275 // import the Posix namespace by default (namespace of backend-specific standard library)
276 source_file.add_using_directive (new UsingDirective (new UnresolvedSymbol (null, "Posix", null)));
277 } else if (context.profile == Profile.GOBJECT) {
278 // import the GLib namespace by default (namespace of backend-specific standard library)
279 source_file.add_using_directive (new UsingDirective (new UnresolvedSymbol (null, "GLib", null)));
282 context.add_source_file (source_file);
283 } else if (source.has_suffix (".vapi")) {
284 context.add_source_file (new SourceFile (context, rpath, true));
285 } else if (source.has_suffix (".c")) {
286 context.add_c_source_file (rpath);
287 } else {
288 Report.error (null, "%s is not a supported source file type. Only .vala, .vapi, .gs, and .c files are supported.".printf (source));
290 } else {
291 Report.error (null, "%s not found".printf (source));
294 sources = null;
296 if (context.report.get_errors () > 0) {
297 return quit ();
300 var parser = new Parser ();
301 parser.parse (context);
303 var genie_parser = new Genie.Parser ();
304 genie_parser.parse (context);
306 if (context.report.get_errors () > 0) {
307 return quit ();
310 var resolver = new SymbolResolver ();
311 resolver.resolve (context);
313 if (context.report.get_errors () > 0) {
314 return quit ();
317 var analyzer = new SemanticAnalyzer ();
318 analyzer.analyze (context);
320 if (!ccode_only && !compile_only && library == null) {
321 // building program, require entry point
322 if (context.entry_point == null) {
323 Report.error (null, "program does not contain a static `main' method");
327 if (dump_tree != null) {
328 var code_writer = new CodeWriter (true);
329 code_writer.write_file (context, dump_tree);
332 if (context.report.get_errors () > 0) {
333 return quit ();
336 var flow_analyzer = new FlowAnalyzer ();
337 flow_analyzer.analyze (context);
339 if (context.report.get_errors () > 0) {
340 return quit ();
343 if (context.non_null_experimental) {
344 var null_checker = new NullChecker ();
345 null_checker.check (context);
347 if (context.report.get_errors () > 0) {
348 return quit ();
352 context.codegen.emit (context);
354 if (context.report.get_errors () > 0) {
355 return quit ();
358 if (vapi_filename == null && library != null) {
359 // keep backward compatibility with --library option
360 vapi_filename = "%s.vapi".printf (library);
363 if (vapi_filename != null) {
364 var interface_writer = new CodeWriter ();
366 // put .vapi file in current directory unless -d has been explicitly specified
367 if (directory != null && !Path.is_absolute (vapi_filename)) {
368 vapi_filename = "%s%c%s".printf (context.directory, Path.DIR_SEPARATOR, vapi_filename);
371 interface_writer.write_file (context, vapi_filename);
374 if (library != null) {
375 if (gir != null) {
376 if (context.profile == Profile.GOBJECT) {
377 long gir_len = gir.len ();
378 unowned string? last_hyphen = gir.rchr (gir_len, '-');
380 if (last_hyphen == null || !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 long offset = gir.pointer_to_offset (last_hyphen);
384 string gir_namespace = gir.substring (0, offset);
385 string gir_version = gir.substring (offset + 1, gir_len - offset - 5);
386 gir_version.canon ("0123456789.", '?');
387 if (gir_namespace == "" || gir_version == "" || !gir_version[0].isdigit () || gir_version.contains ("?")) {
388 Report.error (null, "GIR file name `%s' is not well-formed, expected NAME-VERSION.gir".printf (gir));
389 } else {
390 var gir_writer = new GIRWriter ();
392 // put .gir file in current directory unless -d has been explicitly specified
393 string gir_directory = ".";
394 if (directory != null) {
395 gir_directory = context.directory;
398 gir_writer.write_file (context, gir_directory, gir_namespace, gir_version, library);
403 gir = null;
406 library = null;
408 if (internal_vapi_filename != null) {
409 if (internal_header_filename == null ||
410 header_filename == null) {
411 Report.error (null, "--internal-vapi may only be used in combination with --header and --internal-header");
412 return quit();
415 var interface_writer = new CodeWriter (false, true);
416 interface_writer.set_cheader_override(header_filename, internal_header_filename);
417 string vapi_filename = internal_vapi_filename;
419 // put .vapi file in current directory unless -d has been explicitly specified
420 if (directory != null && !Path.is_absolute (vapi_filename)) {
421 vapi_filename = "%s%c%s".printf (context.directory, Path.DIR_SEPARATOR, vapi_filename);
424 interface_writer.write_file (context, vapi_filename);
426 internal_vapi_filename = null;
429 if (!ccode_only) {
430 var ccompiler = new CCodeCompiler ();
431 if (cc_command == null && Environment.get_variable ("CC") != null) {
432 cc_command = Environment.get_variable ("CC");
434 if (cc_options == null) {
435 ccompiler.compile (context, cc_command, new string[] { });
436 } else {
437 ccompiler.compile (context, cc_command, cc_options);
441 return quit ();
444 private static bool ends_with_dir_separator (string s) {
445 return Path.is_dir_separator (s.offset (s.len () - 1).get_char ());
448 /* ported from glibc */
449 private static string realpath (string name) {
450 string rpath;
452 // start of path component
453 weak string start;
454 // end of path component
455 weak string end;
457 if (!Path.is_absolute (name)) {
458 // relative path
459 rpath = Environment.get_current_dir ();
461 start = end = name;
462 } else {
463 // set start after root
464 start = end = Path.skip_root (name);
466 // extract root
467 rpath = name.substring (0, name.pointer_to_offset (start));
470 long root_len = rpath.pointer_to_offset (Path.skip_root (rpath));
472 for (; start.get_char () != 0; start = end) {
473 // skip sequence of multiple path-separators
474 while (Path.is_dir_separator (start.get_char ())) {
475 start = start.next_char ();
478 // find end of path component
479 long len = 0;
480 for (end = start; end.get_char () != 0 && !Path.is_dir_separator (end.get_char ()); end = end.next_char ()) {
481 len++;
484 if (len == 0) {
485 break;
486 } else if (len == 1 && start.get_char () == '.') {
487 // do nothing
488 } else if (len == 2 && start.has_prefix ("..")) {
489 // back up to previous component, ignore if at root already
490 if (rpath.len () > root_len) {
491 do {
492 rpath = rpath.substring (0, rpath.len () - 1);
493 } while (!ends_with_dir_separator (rpath));
495 } else {
496 if (!ends_with_dir_separator (rpath)) {
497 rpath += Path.DIR_SEPARATOR_S;
500 rpath += start.substring (0, len);
504 if (rpath.len () > root_len && ends_with_dir_separator (rpath)) {
505 rpath = rpath.substring (0, rpath.len () - 1);
508 if (Path.DIR_SEPARATOR != '/') {
509 // don't use backslashes internally,
510 // to avoid problems in #include directives
511 string[] components = rpath.split ("\\");
512 rpath = string.joinv ("/", components);
515 return rpath;
518 static int main (string[] args) {
519 try {
520 var opt_context = new OptionContext ("- Vala Compiler");
521 opt_context.set_help_enabled (true);
522 opt_context.add_main_entries (options, null);
523 opt_context.parse (ref args);
524 } catch (OptionError e) {
525 stdout.printf ("%s\n", e.message);
526 stdout.printf ("Run '%s --help' to see a full list of available command line options.\n", args[0]);
527 return 1;
530 if (version) {
531 stdout.printf ("Vala %s\n", Config.PACKAGE_VERSION);
532 return 0;
535 if (sources == null) {
536 stderr.printf ("No source file specified.\n");
537 return 1;
540 var compiler = new Compiler ();
541 return compiler.run ();