Line data Source code
1 : /* valaccodewriter.vala
2 : *
3 : * Copyright (C) 2006-2009 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 : * Represents a writer to write C source files.
27 : */
28 2828 : public class Vala.CCodeWriter {
29 : /**
30 : * Specifies the file to be written.
31 : */
32 2977 : public string filename { get; set; }
33 :
34 : /**
35 : * Specifies the source file used to generate this one.
36 : */
37 991 : private string source_filename;
38 :
39 : /**
40 : * Specifies whether to emit line directives.
41 : */
42 987 : public bool line_directives { get; set; }
43 :
44 : /**
45 : * Specifies whether the output stream is at the beginning of a line.
46 : */
47 : public bool bol {
48 84530 : get { return _bol; }
49 : }
50 :
51 : static GLib.Regex fix_indent_regex;
52 :
53 991 : private string temp_filename;
54 : private bool file_exists;
55 :
56 991 : private FileStream? stream;
57 :
58 : private int indent;
59 991 : private int current_line_number = 1;
60 : private bool using_line_directive;
61 :
62 : /* at begin of line */
63 991 : private bool _bol = true;
64 : /* at begin after empty line */
65 991 : private bool _bael = false;
66 :
67 1982 : public CCodeWriter (string filename, string? source_filename = null) {
68 991 : this.filename = filename;
69 1982 : this.source_filename = source_filename;
70 : }
71 :
72 : /**
73 : * Opens the file.
74 : *
75 : * @return true if the file has been opened successfully,
76 : * false otherwise
77 : */
78 991 : public bool open (bool write_version) {
79 991 : file_exists = FileUtils.test (filename, FileTest.EXISTS);
80 991 : if (file_exists) {
81 151 : temp_filename = "%s.valatmp".printf (filename);
82 151 : stream = FileStream.open (temp_filename, "w");
83 : } else {
84 : /*
85 : * File doesn't exist. In case of a particular destination (-d flag),
86 : * check and create the directory structure.
87 : */
88 840 : var dirname = Path.get_dirname (filename);
89 840 : DirUtils.create_with_parents (dirname, 0755);
90 840 : stream = FileStream.open (filename, "w");
91 : }
92 :
93 991 : if (stream == null) {
94 0 : return false;
95 : }
96 :
97 1982 : var opening = write_version ?
98 1 : "/* %s generated by valac %s, the Vala compiler".printf (Path.get_basename (filename), Vala.BUILD_VERSION) :
99 990 : "/* %s generated by valac, the Vala compiler".printf (Path.get_basename (filename));
100 991 : write_string (opening);
101 :
102 : // Write the file name if known
103 991 : if (source_filename != null) {
104 987 : write_newline ();
105 987 : write_string (" * generated from %s".printf (Path.get_basename (source_filename)));
106 : }
107 :
108 991 : write_string (", do not modify */");
109 991 : write_newline ();
110 991 : write_newline ();
111 :
112 991 : return true;
113 : }
114 :
115 : /**
116 : * Closes the file.
117 : */
118 991 : public void close () {
119 991 : stream = null;
120 :
121 991 : if (file_exists) {
122 151 : var changed = true;
123 :
124 151 : try {
125 151 : var old_file = new MappedFile (filename, false);
126 151 : var new_file = new MappedFile (temp_filename, false);
127 151 : var len = old_file.get_length ();
128 151 : if (len == new_file.get_length ()) {
129 0 : if (Memory.cmp (old_file.get_contents (), new_file.get_contents (), len) == 0) {
130 151 : changed = false;
131 : }
132 : }
133 151 : old_file = null;
134 151 : new_file = null;
135 : } catch (FileError e) {
136 : // assume changed if mmap comparison doesn't work
137 : }
138 :
139 151 : if (changed) {
140 151 : FileUtils.rename (temp_filename, filename);
141 : } else {
142 0 : FileUtils.unlink (temp_filename);
143 0 : if (source_filename != null) {
144 0 : var stats = Stat (source_filename);
145 0 : var target_stats = Stat (filename);
146 0 : if (stats.st_mtime >= target_stats.st_mtime) {
147 0 : UTimBuf timebuf = { stats.st_atime + 1, stats.st_mtime + 1 };
148 0 : FileUtils.utime (filename, timebuf);
149 : }
150 : }
151 : }
152 : }
153 : }
154 :
155 : /**
156 : * Writes tabs according to the current indent level.
157 : */
158 358128 : public void write_indent (CCodeLineDirective? line = null) {
159 358128 : if (line_directives) {
160 162919 : if (line != null) {
161 79820 : line.write (this);
162 79820 : using_line_directive = true;
163 83099 : } else if (using_line_directive) {
164 : // no corresponding Vala line, emit line directive for C line
165 16079 : write_string ("#line %d \"%s\"".printf (current_line_number + 1, Path.get_basename (filename)));
166 16079 : write_newline ();
167 16079 : using_line_directive = false;
168 : }
169 : }
170 :
171 358128 : if (!_bol) {
172 4235 : write_newline ();
173 : }
174 :
175 358128 : stream.puts (string.nfill (indent, '\t'));
176 358128 : _bol = false;
177 : }
178 :
179 : /**
180 : * Writes n spaces.
181 : */
182 27235 : public void write_nspaces (uint n) {
183 27235 : stream.puts (string.nfill (n, ' '));
184 : }
185 :
186 : /**
187 : * Writes the specified string.
188 : *
189 : * @param s a string
190 : */
191 2143658 : public void write_string (string s) {
192 2143658 : stream.puts (s);
193 2143658 : _bol = false;
194 : }
195 :
196 : /**
197 : * Writes a newline.
198 : */
199 568584 : public void write_newline () {
200 568584 : if (!_bol) {
201 515217 : _bael = false;
202 53367 : } else if (!_bael) {
203 28678 : _bael = true;
204 : } else {
205 : return;
206 : }
207 543895 : stream.putc ('\n');
208 543895 : current_line_number++;
209 543895 : _bol = true;
210 : }
211 :
212 : /**
213 : * Opens a new block, increasing the indent level.
214 : */
215 42893 : public void write_begin_block () {
216 42893 : if (!_bol) {
217 21768 : stream.putc (' ');
218 : } else {
219 21125 : write_indent ();
220 : }
221 42893 : stream.putc ('{');
222 42893 : write_newline ();
223 42893 : indent++;
224 : }
225 :
226 : /**
227 : * Closes the current block, decreasing the indent level.
228 : */
229 42893 : public void write_end_block () {
230 42893 : assert (indent > 0);
231 :
232 42893 : indent--;
233 42893 : write_indent ();
234 42893 : stream.putc ('}');
235 : }
236 :
237 : /**
238 : * Writes the specified text as comment.
239 : *
240 : * @param text the comment text
241 : */
242 654 : public void write_comment (string text) {
243 654 : try {
244 654 : write_indent ();
245 654 : stream.puts ("/*");
246 654 : bool first = true;
247 :
248 : // discard tabs at beginning of line
249 654 : if (fix_indent_regex == null)
250 12 : fix_indent_regex = new GLib.Regex ("^\t+");;
251 :
252 11032 : foreach (unowned string line in text.split ("\n")) {
253 4862 : if (!first) {
254 4208 : write_indent ();
255 : } else {
256 : first = false;
257 : }
258 :
259 9724 : var lineparts = fix_indent_regex.replace_literal (line, -1, 0, "").split ("*/");
260 :
261 9716 : for (int i = 0; lineparts[i] != null; i++) {
262 4854 : stream.puts (lineparts[i]);
263 4854 : if (lineparts[i+1] != null) {
264 0 : stream.puts ("* /");
265 : }
266 : }
267 : }
268 654 : stream.puts ("*/");
269 654 : write_newline ();
270 : } catch (RegexError e) {
271 : // ignore
272 : }
273 : }
274 : }
|