/* valasourcefile.vala * * Copyright (C) 2006-2009 Jürg Billeter * * 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 * version 2.1 of the License, or (at your option) any later version. * 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 */ using GLib; /** * Represents a Vala source or VAPI package file. */ public class Vala.SourceFile { /** * The name of this source file. */ public string filename { get; private set; } public string? relative_filename { set { if (Path.DIR_SEPARATOR != '/') { // don't use backslashes internally, // to avoid problems in #line / #include directives string[] components = value.split ("\\"); _relative_filename = string.joinv ("/", components); } else { _relative_filename = value; } } } private string _package_name; public string? package_name { get { if (file_type != SourceFileType.PACKAGE) { return null; } if (_package_name == null) { _package_name = Path.get_basename (filename[0:filename.last_index_of_char ('.')]); } return _package_name; } set { _package_name = value; } } private string? _installed_version = null; private bool _version_requested = false; /** * The installed package version or null */ public string? installed_version { get { if (_version_requested) { return _installed_version; } _version_requested = true; if (package_name != null) { _installed_version = context.pkg_config_modversion (package_name); } return _installed_version; } set { _version_requested = value != null; _installed_version = value; } } /** * Specifies whether this file is a VAPI package file. */ public SourceFileType file_type { get; set; } /** * Specifies whether this file came from the command line directly. */ public bool from_commandline { get; set; } /** * GIR Namespace for this source file, if it's a VAPI package */ public string gir_namespace { get; set; } /** * GIR Namespace version for this source file, if it's a VAPI package */ public string gir_version { get; set; } public bool gir_ambiguous { get; set; } /** * The context this source file belongs to. */ public weak CodeContext context { get; set; } public string? content { get { return this._content; } set { this._content = value; this.source_array = null; } } /** * If the file has been used (ie: if anything in the file has * been emitted into C code as a definition or declaration). */ public bool used { get; set; } private ArrayList comments = new ArrayList (); public List current_using_directives { get; set; default = new ArrayList (); } private List nodes = new ArrayList (); string? _relative_filename; private string csource_filename = null; private string cinclude_filename = null; private ArrayList source_array = null; private MappedFile mapped_file = null; private string? _content = null; /** * Creates a new source file. * * @param filename source file name * @return newly created source file */ public SourceFile (CodeContext context, SourceFileType type, string filename, string? content = null, bool cmdline = false) { this.context = context; this.file_type = type; this.filename = filename; this.content = content; this.from_commandline = cmdline; } /** * Adds a header comment to this source file. */ public void add_comment (Comment comment) { comments.add (comment); } /** * Returns the list of header comments. * * @return list of comments */ public unowned List get_comments () { return comments; } /** * Adds a new using directive with the specified namespace. * * @param ns reference to namespace */ public void add_using_directive (UsingDirective ns) { // do not modify current_using_directives, it should be considered immutable // for correct symbol resolving var old_using_directives = current_using_directives; current_using_directives = new ArrayList (); foreach (var using_directive in old_using_directives) { current_using_directives.add (using_directive); } current_using_directives.add (ns); } /** * Adds the specified code node to this source file. * * @param node a code node */ public void add_node (CodeNode node) { nodes.add (node); } public void remove_node (CodeNode node) { nodes.remove (node); } /** * Returns the list of code nodes. * * @return code node list */ public unowned List get_nodes () { return nodes; } public void accept (CodeVisitor visitor) { visitor.visit_source_file (this); } public void accept_children (CodeVisitor visitor) { foreach (CodeNode node in nodes) { node.accept (visitor); } } private string get_subdir () { if (context.basedir == null) { return ""; } // filename and basedir are already canonicalized if (filename.has_prefix (context.basedir + "/")) { var basename = Path.get_basename (filename); var subdir = filename.substring (context.basedir.length, filename.length - context.basedir.length - basename.length); while (subdir[0] == '/') { subdir = subdir.substring (1); } return subdir; } return ""; } private string get_destination_directory () { if (context.directory == null) { return get_subdir (); } return Path.build_path ("/", context.directory, get_subdir ()); } private string get_basename () { int dot = filename.last_index_of_char ('.'); return Path.get_basename (filename.substring (0, dot)); } public string get_relative_filename () { if (_relative_filename != null) { return _relative_filename; } else { return Path.get_basename (filename); } } /** * Returns the filename to use when generating C source files. * * @return generated C source filename */ public string get_csource_filename () { if (csource_filename == null) { if (context.run_output) { csource_filename = context.output + ".c"; } else if (context.ccode_only || context.save_csources) { csource_filename = Path.build_path ("/", get_destination_directory (), get_basename () + ".c"); } else { // temporary file csource_filename = Path.build_path ("/", get_destination_directory (), get_basename () + ".vala.c"); } } return csource_filename; } /** * Returns the filename to use when including the generated C header * file. * * @return C header filename to include */ public string get_cinclude_filename () { if (cinclude_filename == null) { if (context.header_filename != null) { cinclude_filename = Path.get_basename (context.header_filename); if (context.includedir != null) { cinclude_filename = Path.build_path ("/", context.includedir, cinclude_filename); } } else { cinclude_filename = Path.build_path ("/", get_subdir (), get_basename () + ".h"); } } return cinclude_filename; } /** * Returns the requested line from this file, loading it if needed. * * @param lineno 1-based line number * @return the specified source line */ public string? get_source_line (int lineno) { if (source_array == null) { if (content != null) { read_source_lines (content); } else { read_source_file (); } } if (lineno < 1 || lineno > source_array.size) { return null; } return source_array.get (lineno - 1); } /** * Parses the input file into ::source_array. */ private void read_source_file () { string cont; try { FileUtils.get_contents (filename, out cont); } catch (FileError fe) { return; } read_source_lines (cont); } private void read_source_lines (string cont) { source_array = new ArrayList (); string[] lines = cont.split ("\n", 0); int idx; for (idx = 0; lines[idx] != null; ++idx) { source_array.add (lines[idx]); } } public char* get_mapped_contents () { if (content != null) { return (char*) content; } if (mapped_file == null) { try { mapped_file = new MappedFile (filename, false); } catch (FileError e) { Report.error (null, "Unable to map file `%s': %s", filename, e.message); return null; } } return mapped_file.get_contents (); } public size_t get_mapped_length () { if (content != null) { return content.length; } if (mapped_file == null) { return 0; } return mapped_file.get_length (); } public bool check (CodeContext context) { foreach (CodeNode node in nodes) { node.check (context); } return true; } } public enum Vala.SourceFileType { NONE, SOURCE, PACKAGE, FAST }