valacompiler.vala 11.4 KB
Newer Older
1 2
/* valacompiler.vala
 *
3
 * Copyright (C) 2006-2007  Jürg Billeter
4
 * Copyright (C) 1996-2002, 2004, 2005, 2006 Free Software Foundation, Inc.
5 6 7 8
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * 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 12 13 14 15 16 17 18 19 20 21 22 23 24 25

 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 *
 * Author:
 * 	Jürg Billeter <j@bitron.ch>
 */

using GLib;

26
class Vala.Compiler : Object {
27
	static string basedir;
28 29
	static string directory;
	static bool version;
30
	[NoArrayLength ()]
31
	static string[] sources;
32
	[NoArrayLength ()]
33 34
	static string[] vapi_directories;
	static string library;
35
	[NoArrayLength ()]
36 37
	static string[] packages;
	static bool disable_memory_management;
38

39 40 41 42
	static bool ccode_only;
	static bool compile_only;
	static string output;
	static bool debug;
43
	static bool thread;
44 45
	static int optlevel;
	static bool disable_assert;
46
	static bool disable_checking;
47 48 49
	static string cc_command;
	[NoArrayLength]
	static string[] cc_options;
50
	static bool save_temps;
51

52
	private CodeContext context;
53 54 55 56 57

	const OptionEntry[] options = {
		{ "vapidir", 0, 0, OptionArg.FILENAME_ARRAY, out vapi_directories, "Look for package bindings in DIRECTORY", "DIRECTORY..." },
		{ "pkg", 0, 0, OptionArg.STRING_ARRAY, out packages, "Include binding for PACKAGE", "PACKAGE..." },
		{ "library", 0, 0, OptionArg.STRING, out library, "Library name", "NAME" },
58
		{ "basedir", 'b', 0, OptionArg.FILENAME, out basedir, "Base source directory", "DIRECTORY" },
59 60 61
		{ "directory", 'd', 0, OptionArg.FILENAME, out directory, "Output directory", "DIRECTORY" },
		{ "version", 0, 0, OptionArg.NONE, ref version, "Display version number", null },
		{ "disable-memory-management", 0, 0, OptionArg.NONE, ref disable_memory_management, "Disable memory management", null },
62 63 64 65
		{ "ccode", 'C', 0, OptionArg.NONE, ref ccode_only, "Output C code", null },
		{ "compile", 'c', 0, OptionArg.NONE, ref compile_only, "Compile but do not link", null },
		{ "output", 'o', 0, OptionArg.FILENAME, out output, "Place output in file FILE", "FILE" },
		{ "debug", 'g', 0, OptionArg.NONE, ref debug, "Produce debug information", null },
66
		{ "thread", 0, 0, OptionArg.NONE, ref thread, "Enable multithreading support", null },
67 68
		{ "optimize", 'O', 0, OptionArg.INT, ref optlevel, "Optimization level", "OPTLEVEL" },
		{ "disable-assert", 0, 0, OptionArg.NONE, ref disable_assert, "Disable assertions", null },
69
		{ "disable-checking", 0, 0, OptionArg.NONE, ref disable_checking, "Disable run-time checks", null },
70 71
		{ "cc", 0, 0, OptionArg.STRING, out cc_command, "Use COMMAND as C compiler command", "COMMAND" },
		{ "Xcc", 'X', 0, OptionArg.STRING_ARRAY, out cc_options, "Pass OPTION to the C compiler", "OPTION..." },
72
		{ "save-temps", 0, 0, OptionArg.NONE, out save_temps, "Keep temporary files", null },
73 74 75
		{ "", 0, 0, OptionArg.FILENAME_ARRAY, out sources, null, "FILE..." },
		{ null }
	};
76
	
77 78 79 80 81 82 83 84 85 86
	private int quit () {
		if (Report.get_errors () == 0) {
			stdout.printf ("Compilation succeeded - %d warning(s)\n", Report.get_warnings ());
			return 0;
		} else {
			stdout.printf ("Compilation failed: %d error(s), %d warning(s)\n", Report.get_errors (), Report.get_warnings ());
			return 1;
		}
	}
	
87
	private string get_package_path (string! pkg) {
88 89
		string basename = "%s.vapi".printf (pkg);
		string basename_old = "%s.vala".printf (pkg);
90

91 92
		if (vapi_directories != null) {
			foreach (string vapidir in vapi_directories) {
93
				var filename = Path.build_filename (vapidir, basename);
94
				if (FileUtils.test (filename, FileTest.EXISTS)) {
95 96
					return filename;
				}
97 98 99 100
				var filename_old = Path.build_filename (vapidir, basename_old);
				if (FileUtils.test (filename_old, FileTest.EXISTS)) {
					return filename_old;
				}
101 102
			}
		}
103 104

		string filename = Path.build_filename (Config.PACKAGE_DATADIR, "vapi", basename);
105
		if (FileUtils.test (filename, FileTest.EXISTS)) {
106 107
			return filename;
		}
108

109 110 111 112 113
		string filename_old = Path.build_filename (Config.PACKAGE_DATADIR, "vapi", basename_old);
		if (FileUtils.test (filename_old, FileTest.EXISTS)) {
			return filename_old;
		}

114
		filename = Path.build_filename ("/usr/local/share/vala/vapi", basename);
115
		if (FileUtils.test (filename, FileTest.EXISTS)) {
116 117 118
			return filename;
		}

119 120 121 122 123
		filename_old = Path.build_filename ("/usr/local/share/vala/vapi", basename_old);
		if (FileUtils.test (filename_old, FileTest.EXISTS)) {
			return filename_old;
		}

124
		filename = Path.build_filename ("/usr/share/vala/vapi", basename);
125
		if (FileUtils.test (filename, FileTest.EXISTS)) {
126 127
			return filename;
		}
128

129 130 131 132 133
		filename_old = Path.build_filename ("/usr/share/vala/vapi", basename_old);
		if (FileUtils.test (filename_old, FileTest.EXISTS)) {
			return filename_old;
		}

134 135 136
		return null;
	}
	
137 138
	private bool add_package (CodeContext! context, string! pkg) {
		if (context.has_package (pkg)) {
139 140 141 142
			// ignore multiple occurences of the same package
			return true;
		}
	
143 144 145 146
		var package_path = get_package_path (pkg);
		
		if (package_path == null) {
			return false;
147 148
		}
		
149
		context.add_package (pkg);
150
		
151
		context.add_source_file (new SourceFile (context, package_path, true));
152
		
153
		var deps_filename = Path.build_filename (Path.get_dirname (package_path), "%s.deps".printf (pkg));
154
		if (FileUtils.test (deps_filename, FileTest.EXISTS)) {
155 156 157 158 159 160 161 162 163
			try {
				string deps_content;
				long deps_len;
				FileUtils.get_contents (deps_filename, out deps_content, out deps_len);
				foreach (string dep in deps_content.split ("\n")) {
					if (dep != "") {
						if (!add_package (context, dep)) {
							Report.error (null, "%s, dependency of %s, not found in specified Vala API directories".printf (dep, pkg));
						}
164 165
					}
				}
166 167
			} catch (FileError e) {
				Report.error (null, "Unable to read dependency file: %s".printf (e.message));
168 169 170
			}
		}
		
171 172 173 174 175
		return true;
	}
	
	private int run () {
		context = new CodeContext ();
176 177 178 179 180 181

		/* support old command line interface */
		if (!ccode_only && !compile_only && output == null) {
			ccode_only = true;
		}

182
		context.library = library;
183 184
		context.memory_management = !disable_memory_management;
		context.assert = !disable_assert;
185
		context.checking = !disable_checking;
186 187 188 189

		context.ccode_only = ccode_only;
		context.compile_only = compile_only;
		context.output = output;
190 191 192 193 194 195 196 197
		if (basedir != null) {
			context.basedir = realpath (basedir);
		}
		if (directory != null) {
			context.directory = realpath (directory);
		} else {
			context.directory = context.basedir;
		}
198
		context.debug = debug;
199
		context.thread = thread;
200
		context.optlevel = optlevel;
201
		context.save_temps = save_temps;
202

203 204
		context.codegen = new CCodeGenerator (!disable_memory_management);

205
		/* default package */
206
		if (!add_package (context, "glib-2.0")) {
207
			Report.error (null, "glib-2.0 not found in specified Vala API directories");
208 209
		}
		
210 211
		if (packages != null) {
			foreach (string package in packages) {
212
				if (!add_package (context, package)) {
213
					Report.error (null, "%s not found in specified Vala API directories".printf (package));
214 215
				}
			}
216 217 218 219 220 221 222 223
			packages = null;
		}
		
		if (Report.get_errors () > 0) {
			return quit ();
		}
		
		foreach (string source in sources) {
224
			if (FileUtils.test (source, FileTest.EXISTS)) {
225
				var rpath = realpath (source);
226
				if (source.has_suffix (".vala")) {
227
					context.add_source_file (new SourceFile (context, rpath));
228
				} else if (source.has_suffix (".vapi")) {
229
					context.add_source_file (new SourceFile (context, rpath, true));
230
				} else if (source.has_suffix (".c")) {
231
					context.add_c_source_file (rpath);
232
				} else {
233
					Report.error (null, "%s is not a supported source file type. Only .vala, .vapi, and .c files are supported.".printf (source));
234
				}
235 236
			} else {
				Report.error (null, "%s not found".printf (source));
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
		}
		sources = null;
		
		if (Report.get_errors () > 0) {
			return quit ();
		}
		
		var parser = new Parser ();
		parser.parse (context);
		
		if (Report.get_errors () > 0) {
			return quit ();
		}
		
		var attributeprocessor = new AttributeProcessor ();
		attributeprocessor.process (context);
		
		if (Report.get_errors () > 0) {
			return quit ();
		}
		
		var resolver = new SymbolResolver ();
		resolver.resolve (context);
		
		if (Report.get_errors () > 0) {
			return quit ();
		}
265 266 267 268

		var dbus_binding_provider = new DBusBindingProvider ();
		dbus_binding_provider.context = context;

269
		var analyzer = new SemanticAnalyzer (!disable_memory_management);
270
		analyzer.add_binding_provider (dbus_binding_provider);
271 272 273 274 275 276 277 278 279
		analyzer.analyze (context);
		
		if (Report.get_errors () > 0) {
			return quit ();
		}
		
		if (!disable_memory_management) {
			var memory_manager = new MemoryManager ();
			memory_manager.analyze (context);
280 281 282 283
			
			if (Report.get_errors () > 0) {
				return quit ();
			}
284 285
		}
		
286
		context.codegen.emit (context);
287
		
288 289 290 291 292 293 294
		if (Report.get_errors () > 0) {
			return quit ();
		}
		
		if (library != null) {
			var interface_writer = new InterfaceWriter ();
			interface_writer.write_file (context, "%s.vala".printf (library));
295
			interface_writer.write_file (context, "%s.vapi".printf (library));
296
			
297 298
			library = null;
		}
299 300 301

		if (!ccode_only) {
			var ccompiler = new CCodeCompiler ();
302
			ccompiler.compile (context, cc_command, cc_options);
303 304
		}

305 306
		return quit ();
	}
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

	/* ported from glibc */
	private string! realpath (string! name) {
		string rpath;

		if (name.get_char () != '/') {
			// relative path
			rpath = Environment.get_current_dir ();
		} else {
			rpath = "/";
		}

		weak string start;
		weak string end;

		for (start = end = name; start.get_char () != 0; start = end) {
			// skip sequence of multiple path-separators
			while (start.get_char () == '/') {
				start = start.next_char ();
			}

			// find end of path component
			long len = 0;
			for (end = start; end.get_char () != 0 && end.get_char () != '/'; end = end.next_char ()) {
				len++;
			}

			if (len == 0) {
				break;
			} else if (len == 1 && start.get_char () == '.') {
				// do nothing
			} else if (len == 2 && start.has_prefix ("..")) {
				// back up to previous component, ignore if at root already
				if (rpath.len () > 1) {
					do {
						rpath = rpath.substring (0, rpath.len () - 1);
					} while (!rpath.has_suffix ("/"));
				}
			} else {
				if (!rpath.has_suffix ("/")) {
					rpath += "/";
				}

				rpath += start.substring (0, len);
			}
		}

		if (rpath.len () > 1 && rpath.has_suffix ("/")) {
			rpath = rpath.substring (0, rpath.len () - 1);
		}

		return rpath;
	}

361
	static int main (string[] args) {
362 363 364 365 366 367 368
		try {
			var opt_context = new OptionContext ("- Vala Compiler");
			opt_context.set_help_enabled (true);
			opt_context.add_main_entries (options, null);
			opt_context.parse (out args);
		} catch (OptionError e) {
			stdout.printf ("%s\n", e.message);
369
			stdout.printf ("Run '%s --help' to see a full list of available command line options.\n", args[0]);
370 371 372
			return 1;
		}
		
373 374 375 376 377
		if (version) {
			stdout.printf ("Vala %s\n", Config.PACKAGE_VERSION);
			return 0;
		}
		
378 379 380 381 382 383 384
		if (sources == null) {
			stderr.printf ("No source file specified.\n");
			return 1;
		}
		
		var compiler = new Compiler ();
		return compiler.run ();
385 386
	}
}