blob: d3d60c49cbfabef807dde374abb45d9623679aeb [file] [log] [blame]
[email protected]96ea63d2013-07-30 10:17:071// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "tools/gn/filesystem_utils.h"
6
7#include "base/logging.h"
8#include "base/strings/utf_string_conversions.h"
9#include "build/build_config.h"
10#include "tools/gn/location.h"
11#include "tools/gn/source_dir.h"
12
13namespace {
14
15enum DotDisposition {
16 // The given dot is just part of a filename and is not special.
17 NOT_A_DIRECTORY,
18
19 // The given dot is the current directory.
20 DIRECTORY_CUR,
21
22 // The given dot is the first of a double dot that should take us up one.
23 DIRECTORY_UP
24};
25
26// When we find a dot, this function is called with the character following
27// that dot to see what it is. The return value indicates what type this dot is
28// (see above). This code handles the case where the dot is at the end of the
29// input.
30//
31// |*consumed_len| will contain the number of characters in the input that
32// express what we found.
33DotDisposition ClassifyAfterDot(const std::string& path,
34 size_t after_dot,
35 size_t* consumed_len) {
36 if (after_dot == path.size()) {
37 // Single dot at the end.
38 *consumed_len = 1;
39 return DIRECTORY_CUR;
40 }
41 if (path[after_dot] == '/') {
42 // Single dot followed by a slash.
43 *consumed_len = 2; // Consume the slash
44 return DIRECTORY_CUR;
45 }
46
47 if (path[after_dot] == '.') {
48 // Two dots.
49 if (after_dot + 1 == path.size()) {
50 // Double dot at the end.
51 *consumed_len = 2;
52 return DIRECTORY_UP;
53 }
54 if (path[after_dot + 1] == '/') {
55 // Double dot folowed by a slash.
56 *consumed_len = 3;
57 return DIRECTORY_UP;
58 }
59 }
60
61 // The dots are followed by something else, not a directory.
62 *consumed_len = 1;
63 return NOT_A_DIRECTORY;
64}
65
66} // namesapce
67
68SourceFileType GetSourceFileType(const SourceFile& file,
69 Settings::TargetOS os) {
70 base::StringPiece extension = FindExtension(&file.value());
71 if (extension == "cc" || extension == "cpp" || extension == "cxx")
72 return SOURCE_CC;
73 if (extension == "h")
74 return SOURCE_H;
75 if (extension == "c")
76 return SOURCE_C;
77
78 switch (os) {
79 case Settings::MAC:
80 if (extension == "m")
81 return SOURCE_M;
82 if (extension == "mm")
83 return SOURCE_MM;
84 break;
85
86 case Settings::WIN:
87 if (extension == "rc")
88 return SOURCE_RC;
[email protected]c6f27f22013-08-21 21:44:5989 // TODO(brettw) asm files.
[email protected]96ea63d2013-07-30 10:17:0790 break;
91
92 default:
93 break;
94 }
95
[email protected]c6f27f22013-08-21 21:44:5996 if (os != Settings::WIN) {
97 if (extension == "S")
98 return SOURCE_S;
99 }
100
[email protected]96ea63d2013-07-30 10:17:07101 return SOURCE_UNKNOWN;
102}
103
104const char* GetExtensionForOutputType(Target::OutputType type,
105 Settings::TargetOS os) {
106 switch (os) {
[email protected]8338eea2013-08-05 23:08:12107 case Settings::MAC:
108 switch (type) {
[email protected]8338eea2013-08-05 23:08:12109 case Target::EXECUTABLE:
110 return "";
111 case Target::SHARED_LIBRARY:
112 return "dylib";
113 case Target::STATIC_LIBRARY:
114 return "a";
[email protected]8338eea2013-08-05 23:08:12115 default:
116 NOTREACHED();
117 }
118 break;
119
[email protected]96ea63d2013-07-30 10:17:07120 case Settings::WIN:
121 switch (type) {
[email protected]96ea63d2013-07-30 10:17:07122 case Target::EXECUTABLE:
123 return "exe";
124 case Target::SHARED_LIBRARY:
125 return "dll.lib"; // Extension of import library.
126 case Target::STATIC_LIBRARY:
127 return "lib";
[email protected]c0822d7f2013-08-13 17:10:56128 default:
129 NOTREACHED();
130 }
131 break;
132
133 case Settings::LINUX:
134 switch (type) {
135 case Target::EXECUTABLE:
136 return "";
137 case Target::SHARED_LIBRARY:
138 return "so";
139 case Target::STATIC_LIBRARY:
140 return "a";
[email protected]96ea63d2013-07-30 10:17:07141 default:
142 NOTREACHED();
143 }
144 break;
145
146 default:
147 NOTREACHED();
148 }
149 return "";
150}
151
152std::string FilePathToUTF8(const base::FilePath& path) {
153#if defined(OS_WIN)
154 return WideToUTF8(path.value());
155#else
156 return path.value();
157#endif
158}
159
160base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
161#if defined(OS_WIN)
162 return base::FilePath(UTF8ToWide(sp));
163#else
164 return base::FilePath(sp.as_string());
165#endif
166}
167
168size_t FindExtensionOffset(const std::string& path) {
169 for (int i = static_cast<int>(path.size()); i >= 0; i--) {
170 if (path[i] == '/')
171 break;
172 if (path[i] == '.')
173 return i + 1;
174 }
175 return std::string::npos;
176}
177
178base::StringPiece FindExtension(const std::string* path) {
179 size_t extension_offset = FindExtensionOffset(*path);
180 if (extension_offset == std::string::npos)
181 return base::StringPiece();
182 return base::StringPiece(&path->data()[extension_offset],
183 path->size() - extension_offset);
184}
185
186size_t FindFilenameOffset(const std::string& path) {
187 for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) {
188 if (path[i] == '/')
189 return i + 1;
190 }
191 return 0; // No filename found means everything was the filename.
192}
193
194base::StringPiece FindFilename(const std::string* path) {
195 size_t filename_offset = FindFilenameOffset(*path);
196 if (filename_offset == 0)
197 return base::StringPiece(*path); // Everything is the file name.
198 return base::StringPiece(&(*path).data()[filename_offset],
199 path->size() - filename_offset);
200}
201
202base::StringPiece FindFilenameNoExtension(const std::string* path) {
203 if (path->empty())
204 return base::StringPiece();
205 size_t filename_offset = FindFilenameOffset(*path);
206 size_t extension_offset = FindExtensionOffset(*path);
207
208 size_t name_len;
209 if (extension_offset == std::string::npos)
210 name_len = path->size() - filename_offset;
211 else
212 name_len = extension_offset - filename_offset - 1;
213
214 return base::StringPiece(&(*path).data()[filename_offset], name_len);
215}
216
217void RemoveFilename(std::string* path) {
218 path->resize(FindFilenameOffset(*path));
219}
220
221bool EndsWithSlash(const std::string& s) {
222 return !s.empty() && s[s.size() - 1] == '/';
223}
224
225base::StringPiece FindDir(const std::string* path) {
226 size_t filename_offset = FindFilenameOffset(*path);
227 if (filename_offset == 0u)
228 return base::StringPiece();
229 return base::StringPiece(path->data(), filename_offset);
230}
231
232bool EnsureStringIsInOutputDir(const SourceDir& dir,
233 const std::string& str,
234 const Value& originating,
235 Err* err) {
236 // The last char of the dir will be a slash. We don't care if the input ends
237 // in a slash or not, so just compare up until there.
238 //
239 // This check will be wrong for all proper prefixes "e.g. "/output" will
240 // match "/out" but we don't really care since this is just a sanity check.
241 const std::string& dir_str = dir.value();
242 if (str.compare(0, dir_str.length() - 1, dir_str, 0, dir_str.length() - 1)
243 != 0) {
244 *err = Err(originating, "File not inside output directory.",
245 "The given file should be in the output directory. Normally you would "
246 "specify\n\"$target_output_dir/foo\" or "
247 "\"$target_gen_dir/foo\". I interpreted this as\n\""
248 + str + "\".");
249 return false;
250 }
251 return true;
252}
253
254std::string InvertDir(const SourceDir& path) {
255 const std::string value = path.value();
256 if (value.empty())
257 return std::string();
258
259 DCHECK(value[0] == '/');
260 size_t begin_index = 1;
261
262 // If the input begins with two slashes, skip over both (this is a
263 // source-relative dir).
264 if (value.size() > 1 && value[1] == '/')
265 begin_index = 2;
266
267 std::string ret;
268 for (size_t i = begin_index; i < value.size(); i++) {
269 if (value[i] == '/')
270 ret.append("../");
271 }
272 return ret;
273}
274
275void NormalizePath(std::string* path) {
276 char* pathbuf = path->empty() ? NULL : &(*path)[0];
277
278 // top_index is the first character we can modify in the path. Anything
279 // before this indicates where the path is relative to.
280 size_t top_index = 0;
281 bool is_relative = true;
282 if (!path->empty() && pathbuf[0] == '/') {
283 is_relative = false;
284
285 if (path->size() > 1 && pathbuf[1] == '/') {
286 // Two leading slashes, this is a path into the source dir.
287 top_index = 2;
288 } else {
289 // One leading slash, this is a system-absolute path.
290 top_index = 1;
291 }
292 }
293
294 size_t dest_i = top_index;
295 for (size_t src_i = top_index; src_i < path->size(); /* nothing */) {
296 if (pathbuf[src_i] == '.') {
297 if (src_i == 0 || pathbuf[src_i - 1] == '/') {
298 // Slash followed by a dot, see if it's something special.
299 size_t consumed_len;
300 switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
301 case NOT_A_DIRECTORY:
302 // Copy the dot to the output, it means nothing special.
303 pathbuf[dest_i++] = pathbuf[src_i++];
304 break;
305 case DIRECTORY_CUR:
306 // Current directory, just skip the input.
307 src_i += consumed_len;
308 break;
309 case DIRECTORY_UP:
310 // Back up over previous directory component. If we're already
311 // at the top, preserve the "..".
312 if (dest_i > top_index) {
313 // The previous char was a slash, remove it.
314 dest_i--;
315 }
316
317 if (dest_i == top_index) {
318 if (is_relative) {
319 // We're already at the beginning of a relative input, copy the
320 // ".." and continue. We need the trailing slash if there was
321 // one before (otherwise we're at the end of the input).
322 pathbuf[dest_i++] = '.';
323 pathbuf[dest_i++] = '.';
324 if (consumed_len == 3)
325 pathbuf[dest_i++] = '/';
326
327 // This also makes a new "root" that we can't delete by going
328 // up more levels. Otherwise "../.." would collapse to
329 // nothing.
330 top_index = dest_i;
331 }
332 // Otherwise we're at the beginning of an absolute path. Don't
333 // allow ".." to go up another level and just eat it.
334 } else {
335 // Just find the previous slash or the beginning of input.
336 while (dest_i > 0 && pathbuf[dest_i - 1] != '/')
337 dest_i--;
338 }
339 src_i += consumed_len;
340 }
341 } else {
342 // Dot not preceeded by a slash, copy it literally.
343 pathbuf[dest_i++] = pathbuf[src_i++];
344 }
345 } else if (pathbuf[src_i] == '/') {
346 if (src_i > 0 && pathbuf[src_i - 1] == '/') {
347 // Two slashes in a row, skip over it.
348 src_i++;
349 } else {
350 // Just one slash, copy it.
351 pathbuf[dest_i++] = pathbuf[src_i++];
352 }
353 } else {
354 // Input nothing special, just copy it.
355 pathbuf[dest_i++] = pathbuf[src_i++];
356 }
357 }
358 path->resize(dest_i);
359}
360
361void ConvertPathToSystem(std::string* path) {
362#if defined(OS_WIN)
363 for (size_t i = 0; i < path->size(); i++) {
364 if ((*path)[i] == '/')
365 (*path)[i] = '\\';
366 }
367#endif
368}
369
370std::string PathToSystem(const std::string& path) {
371 std::string ret(path);
372 ConvertPathToSystem(&ret);
373 return ret;
374}
375