Line data Source code
1 : /* valacodecontext.vala
2 : *
3 : * Copyright (C) 2006-2012 Jürg Billeter
4 : *
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.
9 :
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.
14 :
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
18 : *
19 : * Author:
20 : * Jürg Billeter <j@bitron.ch>
21 : */
22 :
23 : using GLib;
24 :
25 : /**
26 : * The root of the code tree.
27 : */
28 10873260 : public class Vala.CodeContext {
29 : /**
30 : * Enable run-time checks for programming errors.
31 : */
32 7843 : public bool assert { get; set; }
33 :
34 : /**
35 : * Enable additional run-time checks such as type checks.
36 : */
37 35323 : public bool checking { get; set; }
38 :
39 : /**
40 : * Do not warn when using deprecated features.
41 : */
42 7384255 : public bool deprecated { get; set; }
43 :
44 : /**
45 : * Hide the symbols marked as internal
46 : */
47 27012 : public bool hide_internal { get; set; }
48 :
49 : /**
50 : * Do not check whether used symbols exist in local packages.
51 : */
52 14952 : public bool since_check { get; set; }
53 :
54 : /**
55 : * Do not warn when using experimental features.
56 : */
57 1600 : public bool experimental { get; set; }
58 :
59 : /**
60 : * Enable experimental enhancements for non-null types.
61 : */
62 3725613 : public bool experimental_non_null { get; set; }
63 :
64 : /**
65 : * Enable GObject creation tracing.
66 : */
67 8377 : public bool gobject_tracing { get; set; }
68 :
69 : /**
70 : * Output C code, don't compile to object code.
71 : */
72 2544 : public bool ccode_only { get; set; }
73 :
74 : /**
75 : * Command to run pkg-config.
76 : */
77 6010 : public string pkg_config_command { get; set; default = "pkg-config"; }
78 :
79 : /**
80 : * Enable support for ABI stability.
81 : */
82 1012807 : public bool abi_stability { get; set; }
83 :
84 : /**
85 : * Output C header file.
86 : */
87 304837 : public string? header_filename { get; set; }
88 :
89 : /**
90 : * Output internal C header file.
91 : */
92 5554 : public string? internal_header_filename { get; set; }
93 :
94 : /**
95 : * Base directory used for header_filename in the VAPIs.
96 : */
97 4683 : public string? includedir { get; set; }
98 :
99 : /**
100 : * Output symbols file.
101 : */
102 5359 : public string? symbols_filename { get; set; }
103 :
104 : /**
105 : * Compile but do not link.
106 : */
107 2299 : public bool compile_only { get; set; }
108 :
109 : /**
110 : * Output filename.
111 : */
112 7800 : public string output { get; set; }
113 :
114 : /**
115 : * Base source directory.
116 : */
117 10303 : public string basedir { get; set; }
118 :
119 : /**
120 : * Code output directory.
121 : */
122 9123 : public string directory { get; set; }
123 :
124 : /**
125 : * List of directories where to find .vapi files.
126 : */
127 14367 : public string[] vapi_directories { get; set; default = {}; }
128 :
129 : /**
130 : * List of directories where to find .gir files.
131 : */
132 6326 : public string[] gir_directories { get; set; default = {}; }
133 :
134 : /**
135 : * List of directories where to find .metadata files for .gir files.
136 : */
137 6246 : public string[] metadata_directories { get; set; default = {}; }
138 :
139 : /**
140 : * Produce debug information.
141 : */
142 3376 : public bool debug { get; set; }
143 :
144 : /**
145 : * Optimization level.
146 : */
147 0 : public int optlevel { get; set; }
148 :
149 : /**
150 : * Enable memory profiler.
151 : */
152 2336 : public bool mem_profiler { get; set; }
153 :
154 : /**
155 : * Specifies the optional module initialization method.
156 : */
157 9722 : public Method module_init_method { get; set; }
158 :
159 : /**
160 : * Keep temporary files produced by the compiler.
161 : */
162 1467 : public bool save_temps { get; set; }
163 :
164 6115006 : public Profile profile { get; private set; }
165 :
166 2299 : public bool verbose_mode { get; set; }
167 :
168 2631 : public bool version_header { get; set; }
169 :
170 196 : public bool use_fast_vapi { get; set; }
171 :
172 : /**
173 : * Continue as much as possible after an error.
174 : */
175 1480 : public bool keep_going { get; set; }
176 :
177 : /**
178 : * Include comments in generated vapi.
179 : */
180 22787 : public bool vapi_comments { get; set; }
181 :
182 : /**
183 : * Returns true if the target version of glib is greater than or
184 : * equal to the specified version.
185 : */
186 11756 : public bool require_glib_version (int major, int minor) {
187 11756 : return (target_glib_major > major) || (target_glib_major == major && target_glib_minor >= minor);
188 : }
189 :
190 : public bool save_csources {
191 1664 : get { return save_temps; }
192 : }
193 :
194 25001 : public Report report { get; set; default = new Report ();}
195 :
196 6395 : public Method? entry_point { get; set; }
197 :
198 10261 : public string entry_point_name { get; set; }
199 :
200 9305 : public bool run_output { get; set; }
201 :
202 6046 : public string[] gresources { get; set; default = {}; }
203 :
204 6010 : public string[] gresources_directories { get; set; default = {}; }
205 :
206 3076 : private List<SourceFile> source_files = new ArrayList<SourceFile> ();
207 3076 : private Map<string,unowned SourceFile> source_files_map = new HashMap<string,unowned SourceFile> (str_hash, str_equal);
208 3076 : private List<string> c_source_files = new ArrayList<string> (str_equal);
209 3076 : private Namespace _root = new Namespace (null);
210 :
211 3076 : private List<string> packages = new ArrayList<string> (str_equal);
212 :
213 3076 : private Set<string> defines = new HashSet<string> (str_hash, str_equal);
214 :
215 1538 : static StaticPrivate context_stack_key = StaticPrivate ();
216 :
217 : int target_glib_major;
218 : int target_glib_minor;
219 :
220 : /**
221 : * The root namespace of the symbol tree.
222 : */
223 : public Namespace root {
224 10254245 : get { return _root; }
225 : }
226 :
227 9157 : public SymbolResolver resolver { get; private set; }
228 :
229 160673952 : public SemanticAnalyzer analyzer { get; private set; }
230 :
231 4614 : public FlowAnalyzer flow_analyzer { get; private set; }
232 :
233 : /**
234 : * The selected code generator.
235 : */
236 6301 : public CodeGenerator codegen { get; set; }
237 :
238 : /**
239 : * Mark attributes used by the compiler and report unused at the end.
240 : */
241 4614 : public UsedAttr used_attr { get; set; }
242 :
243 3076 : public CodeContext () {
244 1538 : add_default_defines ();
245 :
246 1538 : resolver = new SymbolResolver ();
247 1538 : analyzer = new SemanticAnalyzer ();
248 1538 : flow_analyzer = new FlowAnalyzer ();
249 1538 : used_attr = new UsedAttr ();
250 : }
251 :
252 : /**
253 : * Return the topmost context from the context stack.
254 : */
255 : public static CodeContext get () {
256 10856056 : List<CodeContext>* context_stack = context_stack_key.get ();
257 :
258 10856056 : if (context_stack == null || context_stack->size == 0) {
259 0 : error ("internal: No context available to get");
260 : }
261 :
262 10856056 : return context_stack->get (context_stack->size - 1);
263 : }
264 :
265 : /**
266 : * Push the specified context to the context stack.
267 : */
268 1538 : public static void push (CodeContext context) {
269 1538 : ArrayList<CodeContext>* context_stack = context_stack_key.get ();
270 1538 : if (context_stack == null) {
271 1538 : context_stack = new ArrayList<CodeContext> ();
272 1538 : context_stack_key.set (context_stack, null);
273 : }
274 :
275 1538 : context_stack->add (context);
276 : }
277 :
278 : /**
279 : * Remove the topmost context from the context stack.
280 : */
281 : public static void pop () {
282 1538 : List<CodeContext>* context_stack = context_stack_key.get ();
283 :
284 1538 : if (context_stack == null || context_stack->size == 0) {
285 0 : error ("internal: No context available to pop");
286 : }
287 :
288 1538 : context_stack->remove_at (context_stack->size - 1);
289 :
290 1538 : if (context_stack->size == 0) {
291 1538 : delete context_stack;
292 1538 : context_stack_key.set (null, null);
293 : }
294 : }
295 :
296 : /**
297 : * Returns the list of source files.
298 : *
299 : * @return list of source files
300 : */
301 4302 : public unowned List<SourceFile> get_source_files () {
302 4302 : return source_files;
303 : }
304 :
305 : /**
306 : * Returns the list of C source files.
307 : *
308 : * @return list of C source files
309 : */
310 832 : public unowned List<string> get_c_source_files () {
311 832 : return c_source_files;
312 : }
313 :
314 : /**
315 : * Adds the specified file to the list of source files.
316 : *
317 : * @param file a source file
318 : */
319 6886 : public void add_source_file (SourceFile file) {
320 6886 : if (source_files_map.contains (file.filename)) {
321 0 : Report.warning (null, "Ignoring source file `%s', which was already added to this context", file.filename);
322 0 : return;
323 : }
324 :
325 6886 : source_files.add (file);
326 6886 : source_files_map.set (file.filename, file);
327 : }
328 :
329 : /**
330 : * Returns the source file for a given path.
331 : *
332 : * @param filename a path to a source file
333 : * @return the source file if found
334 : */
335 1 : public unowned Vala.SourceFile? get_source_file (string filename) {
336 1 : return source_files_map.get (filename);
337 : }
338 :
339 : /**
340 : * Adds the specified file to the list of C source files.
341 : *
342 : * @param file a C source file
343 : */
344 0 : public void add_c_source_file (string file) {
345 0 : c_source_files.add (file);
346 : }
347 :
348 : /**
349 : * Returns the list of used packages.
350 : *
351 : * @return list of used packages
352 : */
353 832 : public unowned List<string> get_packages () {
354 832 : return packages;
355 : }
356 :
357 : /**
358 : * Returns whether the specified package is being used.
359 : *
360 : * @param pkg a package name
361 : * @return true if the specified package is being used
362 : */
363 10723 : public bool has_package (string pkg) {
364 10723 : return packages.contains (pkg);
365 : }
366 :
367 : /**
368 : * Adds the specified package to the list of used packages.
369 : *
370 : * @param pkg a package name
371 : */
372 5149 : public void add_package (string pkg) {
373 5149 : packages.add (pkg);
374 : }
375 :
376 : /**
377 : * Pull the specified package into the context.
378 : * The method is tolerant if the package has been already loaded.
379 : *
380 : * @param pkg a package name
381 : * @return false if the package could not be loaded
382 : *
383 : */
384 10332 : public bool add_external_package (string pkg) {
385 10332 : if (has_package (pkg)) {
386 : // ignore multiple occurrences of the same package
387 10332 : return true;
388 : }
389 :
390 : // first try .vapi
391 5103 : var path = get_vapi_path (pkg);
392 5103 : if (path == null) {
393 : // try with .gir
394 124 : path = get_gir_path (pkg);
395 : }
396 124 : if (path == null) {
397 0 : Report.error (null, "Package `%s' not found in specified Vala API directories or GObject-Introspection GIR directories", pkg);
398 0 : return false;
399 : }
400 :
401 5103 : add_package (pkg);
402 :
403 5103 : var rpath = realpath (path);
404 5103 : var source_file = new SourceFile (this, SourceFileType.PACKAGE, path);
405 :
406 5103 : add_source_file (source_file);
407 5103 : if (rpath != path) {
408 325 : source_files_map.set (rpath, source_file);
409 : }
410 :
411 5103 : if (verbose_mode) {
412 0 : print ("Loaded package `%s'\n", path);
413 : }
414 :
415 5103 : var deps_filename = Path.build_path ("/", Path.get_dirname (path), pkg + ".deps");
416 5103 : if (!add_packages_from_file (deps_filename)) {
417 0 : return false;
418 : }
419 :
420 5103 : return true;
421 : }
422 :
423 : /**
424 : * Read the given filename and pull in packages.
425 : * The method is tolerant if the file does not exist.
426 : *
427 : * @param filename a filename
428 : * @return false if an error occurs while reading the file or if a package could not be added
429 : */
430 5199 : public bool add_packages_from_file (string filename) {
431 5199 : if (!FileUtils.test (filename, FileTest.EXISTS)) {
432 5199 : return true;
433 : }
434 :
435 3343 : try {
436 : string contents;
437 3343 : FileUtils.get_contents (filename, out contents);
438 27605 : foreach (string package in contents.split ("\n")) {
439 8788 : package = package.strip ();
440 8788 : if (package != "") {
441 5441 : add_external_package (package);
442 : }
443 : }
444 : } catch (FileError e) {
445 0 : Report.error (null, "Unable to read dependency file: %s", e.message);
446 0 : return false;
447 : }
448 :
449 5199 : return true;
450 : }
451 :
452 : /**
453 : * Add the specified source file to the context. Only .vala, .vapi, .gs,
454 : * and .c extensions are supported.
455 : *
456 : * @param filename a filename
457 : * @param is_source true to force adding the file as .vala or .gs
458 : * @param cmdline true if the file came from the command line.
459 : * @return false if the file is not recognized or the file does not exist
460 : */
461 1696 : public bool add_source_filename (string filename, bool is_source = false, bool cmdline = false) {
462 1696 : if (!FileUtils.test (filename, FileTest.EXISTS)) {
463 0 : Report.error (null, "%s not found", filename);
464 0 : return false;
465 : }
466 :
467 1696 : var rpath = realpath (filename);
468 3392 : if (is_source || filename.has_suffix (".vala") || filename.has_suffix (".gs")) {
469 1696 : var source_file = new SourceFile (this, SourceFileType.SOURCE, rpath, null, cmdline);
470 1696 : source_file.relative_filename = filename;
471 :
472 1704 : if (profile == Profile.POSIX) {
473 : // import the Posix namespace by default (namespace of backend-specific standard library)
474 8 : var ns_ref = new UsingDirective (new UnresolvedSymbol (null, "Posix", null));
475 8 : source_file.add_using_directive (ns_ref);
476 8 : root.add_using_directive (ns_ref);
477 3376 : } else if (profile == Profile.GOBJECT) {
478 : // import the GLib namespace by default (namespace of backend-specific standard library)
479 1688 : var ns_ref = new UsingDirective (new UnresolvedSymbol (null, "GLib", null));
480 1688 : source_file.add_using_directive (ns_ref);
481 1688 : root.add_using_directive (ns_ref);
482 : }
483 :
484 1696 : add_source_file (source_file);
485 1696 : if (rpath != filename) {
486 1696 : source_files_map.set (filename, source_file);
487 : }
488 0 : } else if (filename.has_suffix (".vapi") || filename.has_suffix (".gir")) {
489 0 : var source_file = new SourceFile (this, SourceFileType.PACKAGE, rpath, null, cmdline);
490 0 : source_file.relative_filename = filename;
491 :
492 0 : add_source_file (source_file);
493 0 : if (rpath != filename) {
494 0 : source_files_map.set (filename, source_file);
495 : }
496 0 : } else if (filename.has_suffix (".c")) {
497 0 : add_c_source_file (rpath);
498 0 : } else if (filename.has_suffix (".h")) {
499 : /* Ignore */
500 : } else {
501 0 : Report.error (null, "%s is not a supported source file type. Only .vala, .vapi, .gs, and .c files are supported.", filename);
502 0 : return false;
503 : }
504 :
505 1696 : return true;
506 : }
507 :
508 : /**
509 : * Visits the complete code tree file by file.
510 : * It is possible to add new source files while visiting the tree.
511 : *
512 : * @param visitor the visitor to be called when traversing
513 : */
514 6118 : public void accept (CodeVisitor visitor) {
515 6118 : root.accept (visitor);
516 :
517 : // support queueing new source files
518 6118 : int index = 0;
519 61722 : while (index < source_files.size) {
520 27802 : var source_file = source_files[index];
521 27802 : source_file.accept (visitor);
522 27802 : index++;
523 : }
524 : }
525 :
526 : /**
527 : * Resolve and analyze.
528 : */
529 1429 : public void check () {
530 1429 : resolver.resolve (this);
531 :
532 1429 : if (!keep_going && report.get_errors () > 0) {
533 : return;
534 : }
535 :
536 1423 : analyzer.analyze (this);
537 :
538 : // Don't run the FlowAnalyzer if we have semantic errors, since
539 : // the messages from FlowAnalyzer will usually be nonsensical.
540 1423 : if (report.get_errors () > 0) {
541 : return;
542 : }
543 :
544 971 : flow_analyzer.analyze (this);
545 :
546 971 : if (report.get_errors () > 0) {
547 : return;
548 : }
549 :
550 958 : used_attr.check_unused (this);
551 : }
552 :
553 1557 : public void add_define (string define) {
554 1557 : if (is_defined (define)) {
555 0 : Report.warning (null, "`%s' is already defined", define);
556 0 : if (/VALA_0_\d+/.match_all (define)) {
557 0 : Report.warning (null, "`VALA_0_XX' defines are automatically added up to current compiler version in use");
558 0 : } else if (/GLIB_2_\d+/.match_all (define)) {
559 0 : Report.warning (null, "`GLIB_2_XX' defines are automatically added up to targeted glib version");
560 : }
561 : }
562 1557 : defines.add (define);
563 : }
564 :
565 33707 : public bool is_defined (string define) {
566 33707 : return (define in defines);
567 : }
568 :
569 1538 : void add_default_defines () {
570 1538 : int api_major = 0;
571 1538 : int api_minor = 0;
572 :
573 1538 : if (API_VERSION.scanf ("%d.%d", out api_major, out api_minor) != 2
574 1538 : || api_major > 0
575 1538 : || api_minor % 2 != 0) {
576 0 : Report.error (null, "Invalid format for Vala.API_VERSION");
577 0 : return;
578 : }
579 :
580 46140 : for (int i = 2; i <= api_minor; i += 2) {
581 44602 : defines.add ("VALA_0_%d".printf (i));
582 : }
583 :
584 1538 : target_glib_major = 2;
585 1538 : target_glib_minor = 56;
586 :
587 33836 : for (int i = 16; i <= target_glib_minor; i += 2) {
588 32298 : defines.add ("GLIB_2_%d".printf (i));
589 : }
590 : }
591 :
592 : /**
593 : * Set the target profile for code generation.
594 : *
595 : * This must be called once.
596 : *
597 : * @param profile the profile to use
598 : * @param include_stdpkg whether to include profile-specific default packages
599 : */
600 1538 : public void set_target_profile (Profile profile, bool include_stdpkg = true) {
601 1538 : switch (profile) {
602 : default:
603 : case Profile.GOBJECT:
604 : // default profile
605 1530 : this.profile = profile;
606 1530 : add_define ("GOBJECT");
607 :
608 1530 : if (include_stdpkg) {
609 : /* default packages */
610 1529 : add_external_package ("glib-2.0");
611 1529 : add_external_package ("gobject-2.0");
612 : }
613 : break;
614 : case Profile.POSIX:
615 : // case Profile.LIBC:
616 8 : this.profile = profile;
617 8 : add_define ("LIBC");
618 8 : add_define ("POSIX");
619 :
620 8 : if (include_stdpkg) {
621 : /* default package */
622 8 : add_external_package ("posix");
623 : }
624 : break;
625 : }
626 : }
627 :
628 : /**
629 : * Set the target version of glib for code generation.
630 : *
631 : * This may be called once or not at all.
632 : *
633 : * @param target_glib a string of the format "%d.%d"
634 : */
635 2 : public void set_target_glib_version (string target_glib) {
636 2 : int glib_major = 0;
637 2 : int glib_minor = 0;
638 :
639 2 : if (target_glib == "auto") {
640 1 : var available_glib = pkg_config_modversion ("glib-2.0");
641 1 : if (available_glib != null && available_glib.scanf ("%d.%d", out glib_major, out glib_minor) >= 2) {
642 1 : glib_minor++;
643 1 : glib_minor -= glib_minor % 2;
644 1 : set_target_glib_version ("%d.%d".printf (glib_major, glib_minor));
645 : } else {
646 0 : Report.warning (null, "Could not determine the version of `glib-2.0', target version of glib was not set");
647 : }
648 1 : return;
649 : }
650 :
651 1 : glib_major = target_glib_major;
652 1 : glib_minor = target_glib_minor;
653 :
654 1 : if (target_glib != null && target_glib.scanf ("%d.%d", out glib_major, out glib_minor) != 2
655 1 : || glib_minor % 2 != 0) {
656 0 : Report.error (null, "Only a stable version of GLib can be targeted, use MAJOR.MINOR format with MINOR as an even number");
657 : }
658 :
659 1 : if (glib_major != 2) {
660 0 : Report.error (null, "This version of valac only supports GLib 2");
661 : }
662 :
663 1 : if (glib_minor <= target_glib_minor) {
664 : // no additional defines needed
665 : return;
666 : }
667 :
668 39 : for (int i = target_glib_major + 2; i <= glib_minor; i += 2) {
669 38 : defines.add ("GLIB_2_%d".printf (i));
670 : }
671 :
672 1 : target_glib_major = glib_major;
673 1 : target_glib_minor = glib_minor;
674 : }
675 :
676 5103 : public string? get_vapi_path (string pkg) {
677 5103 : var path = get_file_path (pkg + ".vapi", "vala" + Config.PACKAGE_SUFFIX + "/vapi", "vala/vapi", vapi_directories);
678 :
679 5227 : if (path == null) {
680 : /* last chance: try the package compiled-in vapi dir */
681 124 : var filename = Path.build_path ("/", Config.PACKAGE_DATADIR, "vapi", pkg + ".vapi");
682 124 : if (FileUtils.test (filename, FileTest.EXISTS)) {
683 0 : path = filename;
684 : }
685 : }
686 :
687 : return path;
688 : }
689 :
690 124 : public string? get_gir_path (string gir) {
691 124 : const string GIR_SUFFIX = "gir-1.0";
692 124 : string girname = gir + ".gir";
693 124 : string path = null;
694 :
695 124 : foreach (unowned string dir in gir_directories) {
696 0 : path = Path.build_path ("/", dir, girname);
697 0 : if (FileUtils.test (path, FileTest.EXISTS | FileTest.IS_REGULAR)) {
698 0 : return path;
699 : }
700 : }
701 :
702 : // Search $GI_GIR_PATH
703 124 : unowned string? gi_gir_path = Environment.get_variable ("GI_GIR_PATH");
704 124 : if (gi_gir_path != null) {
705 0 : var gir_dirs = gi_gir_path.split (Path.SEARCHPATH_SEPARATOR_S);
706 0 : foreach (unowned string dir in gir_dirs) {
707 0 : path = Path.build_path ("/", dir, girname);
708 0 : if (FileUtils.test (path, FileTest.EXISTS | FileTest.IS_REGULAR)) {
709 0 : return path;
710 : }
711 : }
712 : }
713 :
714 : // Search $XDG_DATA_HOME
715 124 : path = Path.build_path ("/", Environment.get_user_data_dir (), GIR_SUFFIX, girname);
716 124 : if (FileUtils.test (path, FileTest.EXISTS | FileTest.IS_REGULAR)) {
717 0 : return path;
718 : }
719 :
720 : // Search $XDG_DATA_DIRS
721 372 : foreach (unowned string dir in Environment.get_system_data_dirs ()) {
722 248 : path = Path.build_path ("/", dir, GIR_SUFFIX, girname);
723 248 : if (FileUtils.test (path, FileTest.EXISTS | FileTest.IS_REGULAR)) {
724 124 : return path;
725 : }
726 : }
727 :
728 : // Search $GI_GIRDIR set by user or retrieved from gobject-introspection-1.0.pc
729 0 : path = Path.build_path ("/", Config.GI_GIRDIR, girname);
730 0 : if (FileUtils.test (path, FileTest.EXISTS | FileTest.IS_REGULAR)) {
731 0 : return path;
732 : }
733 :
734 : // Search /usr/share
735 0 : path = Path.build_path ("/", "/usr", "share", GIR_SUFFIX, girname);
736 0 : if (FileUtils.test (path, FileTest.EXISTS | FileTest.IS_REGULAR)) {
737 0 : return path;
738 : }
739 :
740 0 : return null;
741 : }
742 :
743 8 : public string? get_gresource_path (string gresource, string resource) {
744 8 : var filename = get_file_path (resource, null, null, { Path.get_dirname (gresource) });
745 8 : if (filename == null) {
746 0 : filename = get_file_path (resource, null, null, gresources_directories);
747 : }
748 : return filename;
749 : }
750 :
751 : /*
752 : * Returns the .metadata file associated with the given .gir file.
753 : */
754 92 : public string? get_metadata_path (string gir_filename) {
755 92 : var basename = Path.get_basename (gir_filename);
756 92 : var metadata_basename = "%s.metadata".printf (basename.substring (0, basename.length - ".gir".length));
757 :
758 : // look into metadata directories
759 92 : var metadata_filename = get_file_path (metadata_basename, null, null, metadata_directories);
760 92 : if (metadata_filename != null) {
761 2 : return metadata_filename;
762 : }
763 :
764 : // look into the same directory of .gir
765 90 : metadata_filename = Path.build_path ("/", Path.get_dirname (gir_filename), metadata_basename);
766 90 : if (FileUtils.test (metadata_filename, FileTest.EXISTS)) {
767 0 : return metadata_filename;
768 : }
769 :
770 90 : return null;
771 : }
772 :
773 5203 : string? get_file_path (string basename, string? versioned_data_dir, string? data_dir, string[] directories) {
774 5203 : string filename = null;
775 :
776 5203 : if (directories != null) {
777 5325 : foreach (unowned string dir in directories) {
778 5201 : filename = Path.build_path ("/", dir, basename);
779 5201 : if (FileUtils.test (filename, FileTest.EXISTS)) {
780 4989 : return filename;
781 : }
782 : }
783 : }
784 :
785 214 : if (data_dir != null) {
786 496 : foreach (unowned string dir in Environment.get_system_data_dirs ()) {
787 248 : filename = Path.build_path ("/", dir, data_dir, basename);
788 248 : if (FileUtils.test (filename, FileTest.EXISTS)) {
789 0 : return filename;
790 : }
791 : }
792 : }
793 :
794 214 : if (versioned_data_dir != null) {
795 496 : foreach (unowned string dir in Environment.get_system_data_dirs ()) {
796 248 : filename = Path.build_path ("/", dir, versioned_data_dir, basename);
797 248 : if (FileUtils.test (filename, FileTest.EXISTS)) {
798 0 : return filename;
799 : }
800 : }
801 : }
802 :
803 214 : return null;
804 : }
805 :
806 : /*
807 : * Create make-style depfile for use by build systems.
808 : */
809 0 : void write_depfile (string filename, List<string> deps) {
810 0 : var stream = FileStream.open (filename, "w");
811 :
812 0 : if (stream == null) {
813 0 : Report.error (null, "unable to open `%s' for writing", filename);
814 0 : return;
815 : }
816 :
817 0 : bool first = true;
818 0 : foreach (var dep in deps) {
819 0 : if (first) {
820 0 : first = false;
821 0 : stream.printf ("%s: ", filename);
822 : } else {
823 0 : stream.puts (" \\\n\t");
824 : }
825 0 : stream.printf ("%s", dep);
826 : }
827 : // Don't write newlines to otherwise empty files to work around
828 : // https://github.com/ninja-build/ninja/issues/2357
829 0 : if (!first) {
830 0 : stream.puts ("\n\n");
831 : }
832 : }
833 :
834 0 : public void write_dependencies (string filename) {
835 0 : List<string> fastvapi_files = new ArrayList<string> (str_equal);
836 0 : foreach (var src in source_files) {
837 0 : if (src.file_type == SourceFileType.FAST && src.used) {
838 0 : fastvapi_files.add (src.filename);
839 : }
840 : }
841 0 : write_depfile (filename, fastvapi_files);
842 : }
843 :
844 0 : public void write_external_dependencies (string filename) {
845 0 : List<string> external_files = new ArrayList<string> (str_equal);
846 0 : foreach (var src in source_files) {
847 0 : if (src.file_type != SourceFileType.SOURCE && src.used) {
848 0 : external_files.add (src.filename);
849 : }
850 : }
851 0 : write_depfile (filename, external_files);
852 : }
853 :
854 35539 : private static bool ends_with_dir_separator (string s) {
855 35539 : return Path.is_dir_separator (s.get_char (s.length - 1));
856 : }
857 :
858 : /**
859 : * Returns canonicalized absolute pathname
860 : * ported from glibc
861 : *
862 : * @param name the path being checked
863 : * @return a canonicalized absolute pathname
864 : */
865 8268 : public static string realpath (string name) {
866 : string rpath;
867 :
868 : // start of path component
869 : weak string start;
870 : // end of path component
871 : weak string end;
872 :
873 8268 : if (!Path.is_absolute (name)) {
874 : // relative path
875 3490 : rpath = Environment.get_current_dir ();
876 :
877 3490 : start = end = name;
878 : } else {
879 : // set start after root
880 4778 : start = end = Path.skip_root (name);
881 :
882 : // extract root
883 4778 : rpath = name.substring (0, (int) ((char*) start - (char*) name));
884 : }
885 :
886 8268 : long root_len = (long) ((char*) Path.skip_root (rpath) - (char*) rpath);
887 :
888 36147 : for (; start.get_char () != 0; start = end) {
889 : // skip sequence of multiple path-separators
890 47490 : while (Path.is_dir_separator (start.get_char ())) {
891 19611 : start = start.next_char ();
892 : }
893 :
894 : // find end of path component
895 : long len = 0;
896 247933 : for (end = start; end.get_char () != 0 && !Path.is_dir_separator (end.get_char ()); end = end.next_char ()) {
897 220054 : len++;
898 : }
899 :
900 27879 : if (len == 0) {
901 : break;
902 27879 : } else if (len == 1 && start.get_char () == '.') {
903 : // do nothing
904 26178 : } else if (len == 2 && start.has_prefix ("..")) {
905 : // back up to previous component, ignore if at root already
906 170 : if (rpath.length > root_len) {
907 1433 : do {
908 1263 : rpath = rpath.substring (0, rpath.length - 1);
909 1263 : } while (!ends_with_dir_separator (rpath));
910 : }
911 : } else {
912 26008 : if (!ends_with_dir_separator (rpath)) {
913 21130 : rpath += Path.DIR_SEPARATOR_S;
914 : }
915 :
916 : // don't use len, substring works on bytes
917 26008 : rpath += start.substring (0, (long)((char*)end - (char*)start));
918 : }
919 : }
920 :
921 8268 : if (rpath.length > root_len && ends_with_dir_separator (rpath)) {
922 0 : rpath = rpath.substring (0, rpath.length - 1);
923 : }
924 :
925 : if (Path.DIR_SEPARATOR != '/') {
926 : // don't use backslashes internally,
927 : // to avoid problems in #include directives
928 : string[] components = rpath.split ("\\");
929 : rpath = string.joinv ("/", components);
930 : }
931 :
932 : return rpath;
933 : }
934 :
935 2502 : public bool pkg_config_exists (string package_name) {
936 2502 : string pc = pkg_config_command + " --exists " + package_name;
937 : int exit_status;
938 :
939 0 : try {
940 5004 : Process.spawn_command_line_sync (pc, null, null, out exit_status);
941 2502 : return (0 == exit_status);
942 : } catch (SpawnError e) {
943 0 : Report.error (null, e.message);
944 0 : return false;
945 : }
946 : }
947 :
948 2895 : public string? pkg_config_modversion (string package_name) {
949 2895 : string pc = pkg_config_command + " --silence-errors --modversion " + package_name;
950 2895 : string? output = null;
951 : int exit_status;
952 :
953 2895 : try {
954 2895 : Process.spawn_command_line_sync (pc, out output, null, out exit_status);
955 2895 : if (exit_status == 0) {
956 2882 : output = output[0:-1];
957 2882 : if (output == "") {
958 0 : output = null;
959 : }
960 : } else {
961 13 : output = null;
962 : }
963 : } catch (SpawnError e) {
964 0 : output = null;
965 : }
966 :
967 2895 : return output;
968 : }
969 :
970 824 : public string? pkg_config_compile_flags (string package_name) {
971 824 : string pc = pkg_config_command + " --cflags";
972 824 : if (!compile_only) {
973 824 : pc += " --libs";
974 : }
975 824 : pc += package_name;
976 :
977 824 : string? output = null;
978 : int exit_status;
979 :
980 824 : try {
981 824 : Process.spawn_command_line_sync (pc, out output, null, out exit_status);
982 824 : if (exit_status != 0) {
983 0 : Report.error (null, "%s exited with status %d", pkg_config_command, exit_status);
984 0 : return null;
985 : }
986 : } catch (SpawnError e) {
987 0 : Report.error (null, e.message);
988 0 : output = null;
989 : }
990 :
991 824 : return output;
992 : }
993 :
994 0 : public string? pkg_config_variable (string package_name, string variable_name) {
995 0 : string pc = pkg_config_command + " --variable="+ variable_name + " " + package_name;
996 0 : string? output = null;
997 : int exit_status;
998 :
999 0 : try {
1000 0 : Process.spawn_command_line_sync (pc, out output, null, out exit_status);
1001 0 : if (exit_status != 0) {
1002 0 : Report.error (null, "%s exited with status %d", pkg_config_command, exit_status);
1003 0 : return null;
1004 : }
1005 : } catch (SpawnError e) {
1006 0 : Report.error (null, e.message);
1007 0 : output = null;
1008 : }
1009 :
1010 0 : return output;
1011 : }
1012 : }
|