valasourcefile.vala 8.86 KB
Newer Older
1 2
/* valasourcefile.vala
 *
3
 * Copyright (C) 2006-2009  Jürg Billeter
4 5 6 7
 *
 * 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
8
 * version 2.1 of the License, or (at your option) any later version.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

 * 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;

25 26 27
/**
 * Represents a Vala source or VAPI package file.
 */
28
public class Vala.SourceFile {
29 30 31
	/**
	 * The name of this source file.
	 */
32
	public string filename { get; set; }
33

34 35 36 37 38 39
	public string? relative_filename {
		set {
			this._relative_filename = value;
		}
	}

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
	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;

73 74
			if (_package_name != null) {
				_installed_version = context.pkg_config_modversion (package_name);
75 76 77 78 79 80 81 82 83 84 85
			}

			return _installed_version;
		}
		set {
			_version_requested = value != null;
			_installed_version = value;
		}
	}


86 87 88
	/**
	 * Specifies whether this file is a VAPI package file.
	 */
89
	public SourceFileType file_type { get; set; }
90

91 92 93 94 95
	/**
	 * Specifies whether this file came from the command line directly.
	 */
	public bool from_commandline { get; set; }

Rob Taylor's avatar
Rob Taylor committed
96 97 98 99 100 101 102 103 104 105 106 107
	/**
	 *  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; }

108 109 110 111
	/**
	 * The context this source file belongs to.
	 */
	public weak CodeContext context { get; set; }
112

113 114 115 116 117 118 119 120
	public string? content {
		get { return this._content; }
		set {
			this._content = value;
			this.source_array = null;
		}
	}

121 122
	/**
	 * If the file has been used (ie: if anything in the file has
123
	 * been emitted into C code as a definition or declaration).
124 125 126
	 */
	public bool used { get; set; }

127 128 129 130 131
	/**
	 * Whether this source-file was explicitly passed on the commandline.
	 */
	public bool explicit { get; set; }

132 133
	private ArrayList<Comment> comments = new ArrayList<Comment> ();

134
	public List<UsingDirective> current_using_directives { get; set; default = new ArrayList<UsingDirective> (); }
135

136
	private List<CodeNode> nodes = new ArrayList<CodeNode> ();
137 138 139

	string? _relative_filename;

140
	private string csource_filename = null;
141
	private string cinclude_filename = null;
142

143
	private ArrayList<string> source_array = null;
144

145 146
	private MappedFile mapped_file = null;

147 148
	private string? _content = null;

149 150 151 152 153 154
	/**
	 * Creates a new source file.
	 *
	 * @param filename source file name
	 * @return         newly created source file
	 */
155
	public SourceFile (CodeContext context, SourceFileType type, string filename, string? content = null, bool cmdline = false) {
156
		this.context = context;
157 158
		this.file_type = type;
		this.filename = filename;
159
		this.content = content;
160
		this.from_commandline = cmdline;
161
	}
162 163 164 165 166 167 168 169 170 171 172 173 174

	/**
	 * Adds a header comment to this source file.
	 */
	public void add_comment (Comment comment) {
		comments.add (comment);
	}

	/**
	 * Returns a copy of the list of header comments.
	 *
	 * @return list of comments
	 */
175
	public List<Comment> get_comments () {
176
		return comments;
177 178
	}

179 180 181 182 183
	/**
	 * Adds a new using directive with the specified namespace.
	 *
	 * @param ns reference to namespace
	 */
184
	public void add_using_directive (UsingDirective ns) {
185 186 187 188 189 190
		// 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<UsingDirective> ();
		foreach (var using_directive in old_using_directives) {
			current_using_directives.add (using_directive);
191
		}
192
		current_using_directives.add (ns);
193 194
	}

195
	/**
196
	 * Adds the specified code node to this source file.
197
	 *
198
	 * @param node a code node
199
	 */
200
	public void add_node (CodeNode node) {
201
		nodes.add (node);
202
	}
203

204
	public void remove_node (CodeNode node) {
205 206 207
		nodes.remove (node);
	}

208
	/**
209
	 * Returns a copy of the list of code nodes.
210
	 *
211
	 * @return code node list
212
	 */
213
	public List<CodeNode> get_nodes () {
214
		return nodes;
215
	}
216

217
	public void accept (CodeVisitor visitor) {
218 219
		visitor.visit_source_file (this);
	}
220

221
	public void accept_children (CodeVisitor visitor) {
222 223
		foreach (CodeNode node in nodes) {
			node.accept (visitor);
224
		}
225
	}
226

227
	private string get_subdir () {
228 229 230 231 232
		if (context.basedir == null) {
			return "";
		}

		// filename and basedir are already canonicalized
233
		if (filename.has_prefix (context.basedir + "/")) {
234
			var basename = Path.get_basename (filename);
235
			var subdir = filename.substring (context.basedir.length, filename.length - context.basedir.length - basename.length);
236
			while (subdir[0] == '/') {
237
				subdir = subdir.substring (1);
238 239 240 241 242 243
			}
			return subdir;
		}
		return "";
	}

244
	private string get_destination_directory () {
245 246 247
		if (context.directory == null) {
			return get_subdir ();
		}
248
		return Path.build_path ("/", context.directory, get_subdir ());
249 250
	}

251
	private string get_basename () {
252
		int dot = filename.last_index_of_char ('.');
253 254 255
		return Path.get_basename (filename.substring (0, dot));
	}

256
	public string get_relative_filename () {
257 258 259 260 261
		if (_relative_filename != null) {
			return _relative_filename;
		} else {
			return Path.get_basename (filename);
		}
262 263
	}

264 265 266 267 268
	/**
	 * Returns the filename to use when generating C source files.
	 *
	 * @return generated C source filename
	 */
269
	public string get_csource_filename () {
270
		if (csource_filename == null) {
271 272 273
			if (context.run_output) {
				csource_filename = context.output + ".c";
			} else if (context.ccode_only || context.save_csources) {
274
				csource_filename = Path.build_path ("/", get_destination_directory (), get_basename () + ".c");
275 276
			} else {
				// temporary file
277
				csource_filename = Path.build_path ("/", get_destination_directory (), get_basename () + ".vala.c");
278
			}
279 280 281
		}
		return csource_filename;
	}
282

283 284 285 286 287 288
	/**
	 * Returns the filename to use when including the generated C header
	 * file.
	 *
	 * @return C header filename to include
	 */
289
	public string get_cinclude_filename () {
290
		if (cinclude_filename == null) {
291 292
			if (context.header_filename != null) {
				cinclude_filename = Path.get_basename (context.header_filename);
293
				if (context.includedir != null) {
294
					cinclude_filename = Path.build_path ("/", context.includedir, cinclude_filename);
295
				}
296
			} else {
297
				cinclude_filename = Path.build_path ("/", get_subdir (), get_basename () + ".h");
298
			}
299
		}
300
		return cinclude_filename;
301 302
	}

303 304 305 306 307 308
	/**
	 * Returns the requested line from this file, loading it if needed.
	 *
	 * @param lineno 1-based line number
	 * @return       the specified source line
	 */
309
	public string? get_source_line (int lineno) {
310
		if (source_array == null) {
311 312 313 314 315
			if (content != null) {
				read_source_lines (content);
			} else {
				read_source_file ();
			}
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
		}
		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;
		}
333 334 335 336 337
		read_source_lines (cont);
	}

	private void read_source_lines (string cont)
	{
338
		source_array = new ArrayList<string> ();
339
		string[] lines = cont.split ("\n", 0);
340
		int idx;
341 342 343 344 345
		for (idx = 0; lines[idx] != null; ++idx) {
			source_array.add (lines[idx]);
		}
	}

346
	public char* get_mapped_contents () {
347
		if (content != null) {
348
			return (char*) content;
349 350
		}

351
		if (mapped_file == null) {
352 353 354 355 356 357
			try {
				mapped_file = new MappedFile (filename, false);
			} catch (FileError e) {
				Report.error (null, "Unable to map file `%s': %s".printf (filename, e.message));
				return null;
			}
358 359 360 361
		}

		return mapped_file.get_contents ();
	}
362

363
	public size_t get_mapped_length () {
364 365 366 367
		if (content != null) {
			return content.length;
		}

368 369
		return mapped_file.get_length ();
	}
370

371
	public bool check (CodeContext context) {
372
		foreach (CodeNode node in nodes) {
373
			node.check (context);
374 375 376
		}
		return true;
	}
377 378
}

379
public enum Vala.SourceFileType {
380
	NONE,
381
	SOURCE,
382 383
	PACKAGE,
	FAST
384
}