blob: a1089590f4b87c6a2132a9b645b5527cee55882b [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;
89 break;
90
91 default:
92 break;
93 }
94
95 // TODO(brettw) asm files.
96 // TODO(brettw) weird thing with .S on non-Windows platforms.
97 return SOURCE_UNKNOWN;
98}
99
100const char* GetExtensionForOutputType(Target::OutputType type,
101 Settings::TargetOS os) {
102 switch (os) {
[email protected]8338eea2013-08-05 23:08:12103 case Settings::MAC:
104 switch (type) {
105 case Target::NONE:
106 NOTREACHED();
107 return "";
108 case Target::EXECUTABLE:
109 return "";
110 case Target::SHARED_LIBRARY:
111 return "dylib";
112 case Target::STATIC_LIBRARY:
113 return "a";
114 case Target::LOADABLE_MODULE:
115 return "dylib"; // TODO(brettw) what's this?
116 default:
117 NOTREACHED();
118 }
119 break;
120
[email protected]96ea63d2013-07-30 10:17:07121 case Settings::WIN:
122 switch (type) {
123 case Target::NONE:
124 NOTREACHED();
125 return "";
126 case Target::EXECUTABLE:
127 return "exe";
128 case Target::SHARED_LIBRARY:
129 return "dll.lib"; // Extension of import library.
130 case Target::STATIC_LIBRARY:
131 return "lib";
132 case Target::LOADABLE_MODULE:
133 return "dll"; // TODO(brettw) what's this?
134 default:
135 NOTREACHED();
136 }
137 break;
138
139 default:
140 NOTREACHED();
141 }
142 return "";
143}
144
145std::string FilePathToUTF8(const base::FilePath& path) {
146#if defined(OS_WIN)
147 return WideToUTF8(path.value());
148#else
149 return path.value();
150#endif
151}
152
153base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
154#if defined(OS_WIN)
155 return base::FilePath(UTF8ToWide(sp));
156#else
157 return base::FilePath(sp.as_string());
158#endif
159}
160
161size_t FindExtensionOffset(const std::string& path) {
162 for (int i = static_cast<int>(path.size()); i >= 0; i--) {
163 if (path[i] == '/')
164 break;
165 if (path[i] == '.')
166 return i + 1;
167 }
168 return std::string::npos;
169}
170
171base::StringPiece FindExtension(const std::string* path) {
172 size_t extension_offset = FindExtensionOffset(*path);
173 if (extension_offset == std::string::npos)
174 return base::StringPiece();
175 return base::StringPiece(&path->data()[extension_offset],
176 path->size() - extension_offset);
177}
178
179size_t FindFilenameOffset(const std::string& path) {
180 for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) {
181 if (path[i] == '/')
182 return i + 1;
183 }
184 return 0; // No filename found means everything was the filename.
185}
186
187base::StringPiece FindFilename(const std::string* path) {
188 size_t filename_offset = FindFilenameOffset(*path);
189 if (filename_offset == 0)
190 return base::StringPiece(*path); // Everything is the file name.
191 return base::StringPiece(&(*path).data()[filename_offset],
192 path->size() - filename_offset);
193}
194
195base::StringPiece FindFilenameNoExtension(const std::string* path) {
196 if (path->empty())
197 return base::StringPiece();
198 size_t filename_offset = FindFilenameOffset(*path);
199 size_t extension_offset = FindExtensionOffset(*path);
200
201 size_t name_len;
202 if (extension_offset == std::string::npos)
203 name_len = path->size() - filename_offset;
204 else
205 name_len = extension_offset - filename_offset - 1;
206
207 return base::StringPiece(&(*path).data()[filename_offset], name_len);
208}
209
210void RemoveFilename(std::string* path) {
211 path->resize(FindFilenameOffset(*path));
212}
213
214bool EndsWithSlash(const std::string& s) {
215 return !s.empty() && s[s.size() - 1] == '/';
216}
217
218base::StringPiece FindDir(const std::string* path) {
219 size_t filename_offset = FindFilenameOffset(*path);
220 if (filename_offset == 0u)
221 return base::StringPiece();
222 return base::StringPiece(path->data(), filename_offset);
223}
224
225bool EnsureStringIsInOutputDir(const SourceDir& dir,
226 const std::string& str,
227 const Value& originating,
228 Err* err) {
229 // The last char of the dir will be a slash. We don't care if the input ends
230 // in a slash or not, so just compare up until there.
231 //
232 // This check will be wrong for all proper prefixes "e.g. "/output" will
233 // match "/out" but we don't really care since this is just a sanity check.
234 const std::string& dir_str = dir.value();
235 if (str.compare(0, dir_str.length() - 1, dir_str, 0, dir_str.length() - 1)
236 != 0) {
237 *err = Err(originating, "File not inside output directory.",
238 "The given file should be in the output directory. Normally you would "
239 "specify\n\"$target_output_dir/foo\" or "
240 "\"$target_gen_dir/foo\". I interpreted this as\n\""
241 + str + "\".");
242 return false;
243 }
244 return true;
245}
246
247std::string InvertDir(const SourceDir& path) {
248 const std::string value = path.value();
249 if (value.empty())
250 return std::string();
251
252 DCHECK(value[0] == '/');
253 size_t begin_index = 1;
254
255 // If the input begins with two slashes, skip over both (this is a
256 // source-relative dir).
257 if (value.size() > 1 && value[1] == '/')
258 begin_index = 2;
259
260 std::string ret;
261 for (size_t i = begin_index; i < value.size(); i++) {
262 if (value[i] == '/')
263 ret.append("../");
264 }
265 return ret;
266}
267
268void NormalizePath(std::string* path) {
269 char* pathbuf = path->empty() ? NULL : &(*path)[0];
270
271 // top_index is the first character we can modify in the path. Anything
272 // before this indicates where the path is relative to.
273 size_t top_index = 0;
274 bool is_relative = true;
275 if (!path->empty() && pathbuf[0] == '/') {
276 is_relative = false;
277
278 if (path->size() > 1 && pathbuf[1] == '/') {
279 // Two leading slashes, this is a path into the source dir.
280 top_index = 2;
281 } else {
282 // One leading slash, this is a system-absolute path.
283 top_index = 1;
284 }
285 }
286
287 size_t dest_i = top_index;
288 for (size_t src_i = top_index; src_i < path->size(); /* nothing */) {
289 if (pathbuf[src_i] == '.') {
290 if (src_i == 0 || pathbuf[src_i - 1] == '/') {
291 // Slash followed by a dot, see if it's something special.
292 size_t consumed_len;
293 switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
294 case NOT_A_DIRECTORY:
295 // Copy the dot to the output, it means nothing special.
296 pathbuf[dest_i++] = pathbuf[src_i++];
297 break;
298 case DIRECTORY_CUR:
299 // Current directory, just skip the input.
300 src_i += consumed_len;
301 break;
302 case DIRECTORY_UP:
303 // Back up over previous directory component. If we're already
304 // at the top, preserve the "..".
305 if (dest_i > top_index) {
306 // The previous char was a slash, remove it.
307 dest_i--;
308 }
309
310 if (dest_i == top_index) {
311 if (is_relative) {
312 // We're already at the beginning of a relative input, copy the
313 // ".." and continue. We need the trailing slash if there was
314 // one before (otherwise we're at the end of the input).
315 pathbuf[dest_i++] = '.';
316 pathbuf[dest_i++] = '.';
317 if (consumed_len == 3)
318 pathbuf[dest_i++] = '/';
319
320 // This also makes a new "root" that we can't delete by going
321 // up more levels. Otherwise "../.." would collapse to
322 // nothing.
323 top_index = dest_i;
324 }
325 // Otherwise we're at the beginning of an absolute path. Don't
326 // allow ".." to go up another level and just eat it.
327 } else {
328 // Just find the previous slash or the beginning of input.
329 while (dest_i > 0 && pathbuf[dest_i - 1] != '/')
330 dest_i--;
331 }
332 src_i += consumed_len;
333 }
334 } else {
335 // Dot not preceeded by a slash, copy it literally.
336 pathbuf[dest_i++] = pathbuf[src_i++];
337 }
338 } else if (pathbuf[src_i] == '/') {
339 if (src_i > 0 && pathbuf[src_i - 1] == '/') {
340 // Two slashes in a row, skip over it.
341 src_i++;
342 } else {
343 // Just one slash, copy it.
344 pathbuf[dest_i++] = pathbuf[src_i++];
345 }
346 } else {
347 // Input nothing special, just copy it.
348 pathbuf[dest_i++] = pathbuf[src_i++];
349 }
350 }
351 path->resize(dest_i);
352}
353
354void ConvertPathToSystem(std::string* path) {
355#if defined(OS_WIN)
356 for (size_t i = 0; i < path->size(); i++) {
357 if ((*path)[i] == '/')
358 (*path)[i] = '\\';
359 }
360#endif
361}
362
363std::string PathToSystem(const std::string& path) {
364 std::string ret(path);
365 ConvertPathToSystem(&ret);
366 return ret;
367}
368