1 /* valacodecontext.vala
3 * Copyright (C) 2006-2009 Jürg Billeter
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 * Jürg Billeter <j@bitron.ch>
26 * The root of the code tree.
28 public class Vala
.CodeContext
{
30 * Enable run-time checks for programming errors.
32 public bool assert
{ get; set; }
35 * Enable additional run-time checks such as type checks.
37 public bool checking
{ get; set; }
40 * Do not warn when using deprecated features.
42 public bool deprecated
{ get; set; }
45 * Do not warn when using experimental features.
47 public bool experimental
{ get; set; }
50 * Enable experimental enhancements for non-null types.
52 public bool experimental_non_null
{ get; set; }
55 * Enable transformation of D-Bus member names in dynamic client support.
57 public bool dbus_transformation
{ get; set; }
60 * Output C code, don't compile to object code.
62 public bool ccode_only
{ get; set; }
65 * Output C header file.
67 public string? header_filename
{ get; set; }
70 * Output internal C header file.
72 public string? internal_header_filename
{ get; set; }
74 public bool use_header
{ get; set; }
77 * Base directory used for header_filename in the VAPIs.
79 public string? includedir
{ get; set; }
82 * Output symbols file.
84 public string? symbols_filename
{ get; set; }
87 * Compile but do not link.
89 public bool compile_only
{ get; set; }
94 public string output
{ get; set; }
97 * Base source directory.
99 public string basedir
{ get; set; }
102 * Code output directory.
104 public string directory
{ get; set; }
107 * List of directories where to find .vapi files.
109 public string[] vapi_directories
;
112 * List of directories where to find .gir files.
114 public string[] gir_directories
;
117 * Produce debug information.
119 public bool debug
{ get; set; }
122 * Optimization level.
124 public int optlevel
{ get; set; }
127 * Enable multithreading support.
129 public bool thread
{ get; set; }
132 * Enable memory profiler.
134 public bool mem_profiler
{ get; set; }
137 * Specifies the optional module initialization method.
139 public Method module_init_method
{ get; set; }
142 * Keep temporary files produced by the compiler.
144 public bool save_temps
{ get; set; }
146 public Profile profile
{ get; set; }
149 * Target major version number of glib for code generation.
151 public int target_glib_major
{ get; set; }
154 * Target minor version number of glib for code generation.
156 public int target_glib_minor
{ get; set; }
158 public bool verbose_mode
{ get; set; }
160 public bool version_header
{ get; set; }
162 public bool nostdpkg
{ get; set; }
165 * Returns true if the target version of glib is greater than or
166 * equal to the specified version.
168 public bool require_glib_version (int major
, int minor
) {
169 return (target_glib_major
> major
) || (target_glib_major
== major
&& target_glib_minor
>= minor
);
172 public bool save_csources
{
173 get { return save_temps
; }
176 public Report report
{ get; set; default = new
Report ();}
178 public Method? entry_point
{ get; set; }
180 public string entry_point_name
{ get; set; }
182 public bool run_output
{ get; set; }
184 private List
<SourceFile
> source_files
= new ArrayList
<SourceFile
> ();
185 private List
<string> c_source_files
= new ArrayList
<string> ();
186 private Namespace _root
= new
Namespace (null);
188 private List
<string> packages
= new ArrayList
<string> (str_equal
);
190 private Set
<string> defines
= new HashSet
<string> (str_hash
, str_equal
);
192 static StaticPrivate context_stack_key
= StaticPrivate ();
195 * The root namespace of the symbol tree.
197 * @return root namespace
199 public Namespace root
{
200 get { return _root
; }
203 public SymbolResolver resolver
{ get; private set; }
205 public SemanticAnalyzer analyzer
{ get; private set; }
207 public FlowAnalyzer flow_analyzer
{ get; private set; }
210 * The selected code generator.
212 public CodeGenerator codegen
{ get; set; }
214 public CodeContext () {
215 resolver
= new
SymbolResolver ();
216 analyzer
= new
SemanticAnalyzer ();
217 flow_analyzer
= new
FlowAnalyzer ();
221 * Return the topmost context from the context stack.
223 public static CodeContext
get () {
224 List
<CodeContext
>* context_stack
= context_stack_key
.get ();
226 return context_stack
->get (context_stack
->size
- 1);
230 * Push the specified context to the context stack.
232 public static void push (CodeContext context
) {
233 ArrayList
<CodeContext
>* context_stack
= context_stack_key
.get ();
234 if (context_stack
== null) {
235 context_stack
= new ArrayList
<CodeContext
> ();
236 context_stack_key
.set (context_stack
, null);
239 context_stack
->add (context
);
243 * Remove the topmost context from the context stack.
245 public static void pop () {
246 List
<CodeContext
>* context_stack
= context_stack_key
.get ();
248 context_stack
->remove_at (context_stack
->size
- 1);
252 * Returns a copy of the list of source files.
254 * @return list of source files
256 public List
<SourceFile
> get_source_files () {
261 * Returns a copy of the list of C source files.
263 * @return list of C source files
265 public List
<string> get_c_source_files () {
266 return c_source_files
;
270 * Adds the specified file to the list of source files.
272 * @param file a source file
274 public void add_source_file (SourceFile file
) {
275 source_files
.add (file
);
279 * Adds the specified file to the list of C source files.
281 * @param file a C source file
283 public void add_c_source_file (string file
) {
284 c_source_files
.add (file
);
288 * Returns a copy of the list of used packages.
290 * @return list of used packages
292 public List
<string> get_packages () {
297 * Returns whether the specified package is being used.
299 * @param pkg a package name
300 * @return true if the specified package is being used
302 public bool has_package (string pkg
) {
303 return packages
.contains (pkg
);
307 * Adds the specified package to the list of used packages.
309 * @param pkg a package name
311 public void add_package (string pkg
) {
316 * Pull the specified package into the context.
317 * The method is tolerant if the package has been already loaded.
319 * @param pkg a package name
320 * @return false if the package could not be loaded
323 public bool add_external_package (string pkg
) {
324 if (has_package (pkg
)) {
325 // ignore multiple occurences of the same package
330 var path
= get_vapi_path (pkg
);
333 path
= get_gir_path (pkg
);
336 Report
.error (null, "Package `%s' not found in specified Vala API directories or GObject-Introspection GIR directories".printf (pkg
));
342 add_source_file (new
SourceFile (this
, SourceFileType
.PACKAGE
, path
));
344 var deps_filename
= Path
.build_filename (Path
.get_dirname (path
), "%s.deps".printf (pkg
));
345 if (!add_packages_from_file (deps_filename
)) {
353 * Read the given filename and pull in packages.
354 * The method is tolerant if the file does not exist.
356 * @param filename a filanem
357 * @return false if an error occurs while reading the file or if a package could not be added
359 public bool add_packages_from_file (string filename
) {
360 if (!FileUtils
.test (filename
, FileTest
.EXISTS
)) {
366 FileUtils
.get_contents (filename
, out contents
);
367 foreach (string package
in contents
.split ("\n")) {
368 package
= package
.strip ();
370 add_external_package (package
);
373 } catch (FileError e
) {
374 Report
.error (null, "Unable to read dependency file: %s".printf (e
.message
));
382 * Add the specified source file to the context. Only .vala, .vapi, .gs,
383 * and .c extensions are supported.
385 * @param filename a filename
386 * @param is_source true to force adding the file as .vala or .gs
387 * @return false if the file is not recognized or the file does not exist
389 public bool add_source_filename (string filename
, bool is_source
= false) {
390 if (!FileUtils
.test (filename
, FileTest
.EXISTS
)) {
391 Report
.error (null, "%s not found".printf (filename
));
395 var rpath
= realpath (filename
);
396 if (is_source
|| filename
.has_suffix (".vala") || filename
.has_suffix (".gs")) {
397 var source_file
= new
SourceFile (this
, SourceFileType
.SOURCE
, rpath
);
398 source_file
.relative_filename
= filename
;
400 if (profile
== Profile
.POSIX
) {
401 // import the Posix namespace by default (namespace of backend-specific standard library)
402 var ns_ref
= new
UsingDirective (new
UnresolvedSymbol (null, "Posix", null));
403 source_file
.add_using_directive (ns_ref
);
404 root
.add_using_directive (ns_ref
);
405 } else if (profile
== Profile
.GOBJECT
) {
406 // import the GLib namespace by default (namespace of backend-specific standard library)
407 var ns_ref
= new
UsingDirective (new
UnresolvedSymbol (null, "GLib", null));
408 source_file
.add_using_directive (ns_ref
);
409 root
.add_using_directive (ns_ref
);
410 } else if (profile
== Profile
.DOVA
) {
411 // import the Dova namespace by default (namespace of backend-specific standard library)
412 var ns_ref
= new
UsingDirective (new
UnresolvedSymbol (null, "Dova", null));
413 source_file
.add_using_directive (ns_ref
);
414 root
.add_using_directive (ns_ref
);
417 add_source_file (source_file
);
418 } else if (filename
.has_suffix (".vapi") || filename
.has_suffix (".gir")) {
419 var source_file
= new
SourceFile (this
, SourceFileType
.PACKAGE
, rpath
);
420 source_file
.relative_filename
= filename
;
422 add_source_file (source_file
);
423 // look for a local .deps
424 var deps_filename
= "%s.deps".printf (filename
.ndup (filename
.length
- ".vapi".length
));
425 if (!add_packages_from_file (deps_filename
)) {
428 } else if (filename
.has_suffix (".c")) {
429 add_c_source_file (rpath
);
431 Report
.error (null, "%s is not a supported source file type. Only .vala, .vapi, .gs, and .c files are supported.".printf (filename
));
439 * Visits the complete code tree file by file.
440 * It is possible to add new source files while visiting the tree.
442 * @param visitor the visitor to be called when traversing
444 public void accept (CodeVisitor visitor
) {
445 root
.accept (visitor
);
447 // support queueing new source files
449 while (index
< source_files
.size
) {
450 var source_file
= source_files
[index
];
451 source_file
.accept (visitor
);
457 * Resolve and analyze.
459 public void check () {
460 resolver
.resolve (this
);
462 if (report
.get_errors () > 0) {
466 analyzer
.analyze (this
);
468 if (report
.get_errors () > 0) {
472 flow_analyzer
.analyze (this
);
475 public void add_define (string define
) {
476 defines
.add (define
);
479 public bool is_defined (string define
) {
480 return (define
in defines
);
483 public string?
get_vapi_path (string pkg
) {
484 var path
= get_file_path (pkg
+ ".vapi", "vala" + Config
.PACKAGE_SUFFIX
+ "/vapi", "vala/vapi", vapi_directories
);
487 /* last chance: try the package compiled-in vapi dir */
488 var filename
= Path
.build_filename (Config
.PACKAGE_DATADIR
, "vapi", pkg
+ ".vapi");
489 if (FileUtils
.test (filename
, FileTest
.EXISTS
)) {
497 public string?
get_gir_path (string gir
) {
498 return get_file_path (gir
+ ".gir", "gir-1.0", null, gir_directories
);
501 string?
get_file_path (string basename
, string versioned_data_dir
, string? data_dir
, string[] directories
) {
502 string filename
= null;
504 if (directories
!= null) {
505 foreach (string dir
in directories
) {
506 filename
= Path
.build_filename (dir
, basename
);
507 if (FileUtils
.test (filename
, FileTest
.EXISTS
)) {
513 foreach (string dir
in Environment
.get_system_data_dirs ()) {
514 filename
= Path
.build_filename (dir
, versioned_data_dir
, basename
);
515 if (FileUtils
.test (filename
, FileTest
.EXISTS
)) {
520 if (data_dir
!= null) {
521 foreach (string dir
in Environment
.get_system_data_dirs ()) {
522 filename
= Path
.build_filename (dir
, data_dir
, basename
);
523 if (FileUtils
.test (filename
, FileTest
.EXISTS
)) {
532 public void write_dependencies (string filename
) {
533 var stream
= FileStream
.open (filename
, "w");
535 if (stream
== null) {
536 Report
.error (null, "unable to open `%s' for writing".printf (filename
));
540 stream
.printf ("%s:", filename
);
541 foreach (var src
in source_files
) {
542 if (src
.file_type
== SourceFileType
.FAST
&& src
.used
) {
543 stream
.printf (" %s", src
.filename
);
546 stream
.printf ("\n\n");
549 private static bool ends_with_dir_separator (string s
) {
550 return Path
.is_dir_separator (s
.offset (s
.length
- 1).get_char ());
553 /* ported from glibc */
554 public static string realpath (string name
) {
557 // start of path component
559 // end of path component
562 if (!Path
.is_absolute (name
)) {
564 rpath
= Environment
.get_current_dir ();
568 // set start after root
569 start
= end
= Path
.skip_root (name
);
572 rpath
= name
.substring (0, name
.pointer_to_offset (start
));
575 long root_len
= rpath
.pointer_to_offset (Path
.skip_root (rpath
));
577 for (; start
.get_char () != 0; start
= end
) {
578 // skip sequence of multiple path-separators
579 while (Path
.is_dir_separator (start
.get_char ())) {
580 start
= start
.next_char ();
583 // find end of path component
585 for (end
= start
; end
.get_char () != 0 && !Path
.is_dir_separator (end
.get_char ()); end
= end
.next_char ()) {
591 } else if (len
== 1 && start
.get_char () == '.') {
593 } else if (len
== 2 && start
.has_prefix ("..")) {
594 // back up to previous component, ignore if at root already
595 if (rpath
.length
> root_len
) {
597 rpath
= rpath
.substring (0, rpath
.length
- 1);
598 } while (!ends_with_dir_separator (rpath
));
601 if (!ends_with_dir_separator (rpath
)) {
602 rpath
+= Path
.DIR_SEPARATOR_S
;
605 rpath
+= start
.substring (0, len
);
609 if (rpath
.length
> root_len
&& ends_with_dir_separator (rpath
)) {
610 rpath
= rpath
.substring (0, rpath
.length
- 1);
613 if (Path
.DIR_SEPARATOR
!= '/') {
614 // don't use backslashes internally,
615 // to avoid problems in #include directives
616 string[] components
= rpath
.split ("\\");
617 rpath
= string.joinv ("/", components
);