3 * Copyright (C) 2006-2008 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>
27 * Represents a Vala source or VAPI package file.
29 public class Vala
.SourceFile
{
31 * The name of this source file.
33 public string filename
{ get; set; }
36 * The header comment of this source file.
38 public string comment
{ get; set; }
41 * Specifies whether this file is a VAPI package file.
43 public bool external_package
{ get; set; }
46 * Specifies the dependency cycle this source file is member of. If this
47 * source file is in a cycle, all type definitions of that cycle will
48 * only be written to the C header file of the cycle head.
50 public SourceFileCycle cycle
{ get; set; }
53 * Specifies whether this source file is the head of the cycle, if it is
56 public bool is_cycle_head
{ get; set; }
59 * Mark used for cycle detection.
62 * 1: currently visiting
65 public int mark
{ get; set; }
68 * The context this source file belongs to.
70 public weak CodeContext context
{ get; set; }
72 public string? content
{
73 get { return this
._content
; }
75 this
._content
= value
;
76 this
.source_array
= null;
80 private Gee
.List
<UsingDirective
> using_directives
= new ArrayList
<UsingDirective
> ();
82 private Gee
.List
<CodeNode
> nodes
= new ArrayList
<CodeNode
> ();
84 private string cheader_filename
= null;
85 private string csource_filename
= null;
86 private string cinclude_filename
= null;
88 private Gee
.List
<string> header_external_includes
= new ArrayList
<string> ();
89 private Gee
.List
<string> header_internal_includes
= new ArrayList
<string> ();
90 private Gee
.List
<string> source_external_includes
= new ArrayList
<string> ();
91 private Gee
.List
<string> source_internal_includes
= new ArrayList
<string> ();
93 private Gee
.List
<weak SourceFile
> header_internal_full_dependencies
= new ArrayList
<weak SourceFile
> ();
94 private Gee
.List
<weak SourceFile
> header_internal_dependencies
= new ArrayList
<weak SourceFile
> ();
96 private Gee
.Set
<Symbol
> source_symbol_dependencies
= new HashSet
<Symbol
> ();
98 private Gee
.ArrayList
<string> source_array
= null;
100 private MappedFile mapped_file
= null;
102 private string? _content
= null;
105 * Creates a new source file.
107 * @param filename source file name
108 * @param pkg true if this is a VAPI package file
109 * @return newly created source file
111 public SourceFile (CodeContext context
, string filename
, bool pkg
= false, string? content
= null) {
112 this
.filename
= filename
;
113 this
.external_package
= pkg
;
114 this
.context
= context
;
115 this
.content
= content
;
119 * Adds a new using directive with the specified namespace.
121 * @param ns reference to namespace
123 public void add_using_directive (UsingDirective ns
) {
124 foreach (UsingDirective using_directive
in using_directives
) {
125 if (same_symbol (using_directive
.namespace_symbol
, ns
.namespace_symbol
)) {
130 using_directives
.add (ns
);
133 public void clear_using_directives () {
134 using_directives
.clear ();
137 bool same_symbol (Symbol? sym1
, Symbol? sym2
) {
142 var unresolved_symbol1
= sym1 as UnresolvedSymbol
;
143 var unresolved_symbol2
= sym2 as UnresolvedSymbol
;
144 if (unresolved_symbol1
!= null && unresolved_symbol2
!= null) {
145 if (same_symbol (unresolved_symbol1
.inner
, unresolved_symbol2
.inner
)) {
146 return (unresolved_symbol1
.name
== unresolved_symbol2
.name
);
154 * Returns a copy of the list of using directives.
156 * @return using directive list
158 public Gee
.List
<UsingDirective
> get_using_directives () {
159 return new ReadOnlyList
<UsingDirective
> (using_directives
);
163 * Adds the specified code node to this source file.
165 * @param node a code node
167 public void add_node (CodeNode node
) {
171 public void remove_node (CodeNode node
) {
176 * Returns a copy of the list of code nodes.
178 * @return code node list
180 public Gee
.List
<CodeNode
> get_nodes () {
181 return new ReadOnlyList
<CodeNode
> (nodes
);
184 public void accept (CodeVisitor visitor
) {
185 visitor
.visit_source_file (this
);
188 public void accept_children (CodeVisitor visitor
) {
189 foreach (UsingDirective ns_ref
in using_directives
) {
190 ns_ref
.accept (visitor
);
193 foreach (CodeNode node
in nodes
) {
194 node
.accept (visitor
);
198 private string get_subdir () {
199 if (context
.basedir
== null) {
203 // filename and basedir are already canonicalized
204 if (filename
.has_prefix (context
.basedir
)) {
205 var basename
= Path
.get_basename (filename
);
206 var subdir
= filename
.substring (context
.basedir
.len (), filename
.len () - context
.basedir
.len () - basename
.len ());
207 while (subdir
[0] == '/') {
208 subdir
= subdir
.offset (1);
215 private string get_destination_directory () {
216 if (context
.directory
== null) {
217 return get_subdir ();
219 return "%s/%s".printf (context
.directory
, get_subdir ());
222 private string get_basename () {
223 long dot
= filename
.pointer_to_offset (filename
.rchr (-1, '.'));
224 return Path
.get_basename (filename
.substring (0, dot
));
227 public string get_relative_filename () {
228 return get_subdir () + Path
.get_basename (filename
);
232 * Returns the filename to use when generating C header files.
234 * @return generated C header filename
236 public string get_cheader_filename () {
237 if (cheader_filename
== null) {
238 cheader_filename
= "%s%s.h".printf (get_destination_directory (), get_basename ());
240 return cheader_filename
;
244 * Returns the filename to use when generating C source files.
246 * @return generated C source filename
248 public string get_csource_filename () {
249 if (csource_filename
== null) {
250 csource_filename
= "%s%s.c".printf (get_destination_directory (), get_basename ());
252 return csource_filename
;
256 * Returns the filename to use when including the generated C header
259 * @return C header filename to include
261 public string get_cinclude_filename () {
262 if (cinclude_filename
== null) {
263 cinclude_filename
= "%s%s.h".printf (get_subdir (), get_basename ());
265 return cinclude_filename
;
269 * Adds the specified symbol to the list of symbols code in this source
272 * TODO Move source and header file dependency analysis to
275 * @param sym a symbol
276 * @param dep_type type of dependency
278 public void add_symbol_dependency (Symbol? sym
, SourceFileDependencyType dep_type
) {
279 if (external_package
) {
285 if (sym is ErrorCode
) {
286 s
= sym
.parent_symbol
;
287 } else if (sym is TypeSymbol
||
293 } else if (sym is FormalParameter
) {
294 var fp
= (FormalParameter
) sym
;
295 s
= fp
.parameter_type
.data_type
;
297 /* generic type parameter */
304 if (dep_type
== SourceFileDependencyType
.SOURCE
) {
305 source_symbol_dependencies
.add (s
);
306 if (s
.external_package
) {
307 foreach (string fn
in s
.get_cheader_filenames ()) {
308 source_external_includes
.add (fn
);
311 foreach (string fn
in s
.get_cheader_filenames ()) {
312 source_internal_includes
.add (fn
);
318 if (s
.external_package
) {
319 /* external package */
320 foreach (string fn
in s
.get_cheader_filenames ()) {
321 header_external_includes
.add (fn
);
326 if (dep_type
== SourceFileDependencyType
.HEADER_FULL
) {
327 foreach (string fn
in s
.get_cheader_filenames ()) {
328 header_internal_includes
.add (fn
);
330 header_internal_full_dependencies
.add (s
.source_reference
.file
);
333 header_internal_dependencies
.add (s
.source_reference
.file
);
337 * Adds the symbols that define the specified type to the list of
338 * symbols code in this source file depends on.
340 * TODO Move source and header file dependency analysis to
343 * @param type a data type
344 * @param dep_type type of dependency
346 public void add_type_dependency (DataType type
, SourceFileDependencyType dep_type
) {
347 foreach (Symbol type_symbol
in type
.get_symbols ()) {
348 add_symbol_dependency (type_symbol
, dep_type
);
353 * Returns the list of external includes the generated C header file
356 * @return external include list for C header file
358 public Gee
.List
<string> get_header_external_includes () {
359 return new ReadOnlyList
<string> (header_external_includes
);
363 * Adds the specified filename to the list of package-internal includes
364 * the generated C header file requires.
366 * @param include internal include for C header file
368 public void add_header_internal_include (string include
) {
369 /* skip includes to self */
370 if (include
!= get_cinclude_filename ()) {
371 header_internal_includes
.add (include
);
376 * Returns the list of package-internal includes the generated C header
379 * @return internal include list for C header file
381 public Gee
.List
<string> get_header_internal_includes () {
382 return new ReadOnlyList
<string> (header_internal_includes
);
386 * Returns the list of external includes the generated C source file
389 * @return include list for C source file
391 public Gee
.List
<string> get_source_external_includes () {
392 return new ReadOnlyList
<string> (source_external_includes
);
396 * Returns the list of package-internal includes the generated C source
399 * @return include list for C source file
401 public Gee
.List
<string> get_source_internal_includes () {
402 return new ReadOnlyList
<string> (source_internal_includes
);
406 * Returns the list of source files the generated C header file requires
409 * @return definite source file dependencies
411 public Gee
.List
<weak SourceFile
> get_header_internal_full_dependencies () {
412 return new ReadOnlyList
<weak SourceFile
> (header_internal_full_dependencies
);
416 * Returns the list of source files the generated C header file loosely
419 * @return loose source file dependencies
421 public Gee
.List
<weak SourceFile
> get_header_internal_dependencies () {
422 return new ReadOnlyList
<weak SourceFile
> (header_internal_dependencies
);
425 public Gee
.Set
<Symbol
> get_source_symbol_dependencies () {
426 return new ReadOnlySet
<Symbol
> (source_symbol_dependencies
);
430 * Returns the requested line from this file, loading it if needed.
432 * @param lineno 1-based line number
433 * @return the specified source line
435 public string?
get_source_line (int lineno
) {
436 if (source_array
== null) {
437 if (content
!= null) {
438 read_source_lines (content
);
443 if (lineno
< 1 || lineno
> source_array
.size
) {
446 return source_array
.get (lineno
- 1);
450 * Parses the input file into ::source_array.
452 private void read_source_file () {
455 FileUtils
.get_contents (filename
, out cont
);
456 } catch (FileError fe
) {
459 read_source_lines (cont
);
462 private void read_source_lines (string cont
)
464 source_array
= new Gee
.ArrayList
<string> ();
465 string[] lines
= cont
.split ("\n", 0);
467 for (idx
= 0; lines
[idx
] != null; ++idx
) {
468 source_array
.add (lines
[idx
]);
472 public char* get_mapped_contents () {
473 if (content
!= null) {
477 if (mapped_file
== null) {
479 mapped_file
= new
MappedFile (filename
, false);
480 } catch (FileError e
) {
481 Report
.error (null, "Unable to map file `%s': %s".printf (filename
, e
.message
));
486 return mapped_file
.get_contents ();
489 public size_t
get_mapped_length () {
490 if (content
!= null) {
491 return content
.length
;
494 return mapped_file
.get_length ();
497 public bool check (SemanticAnalyzer analyzer
) {
498 foreach (CodeNode node
in nodes
) {
499 node
.check (analyzer
);
505 public enum Vala
.SourceFileDependencyType
{