Delete app_host from apps/
[chromium-blink-merge.git] / tools / gn / filesystem_utils.cc
blob75b72b26a343d62ad8fedc6cbafa65dabaa70f06
1 // 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.
5 #include "tools/gn/filesystem_utils.h"
7 #include "base/logging.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "build/build_config.h"
11 #include "tools/gn/location.h"
12 #include "tools/gn/source_dir.h"
14 namespace {
16 enum DotDisposition {
17 // The given dot is just part of a filename and is not special.
18 NOT_A_DIRECTORY,
20 // The given dot is the current directory.
21 DIRECTORY_CUR,
23 // The given dot is the first of a double dot that should take us up one.
24 DIRECTORY_UP
27 // When we find a dot, this function is called with the character following
28 // that dot to see what it is. The return value indicates what type this dot is
29 // (see above). This code handles the case where the dot is at the end of the
30 // input.
32 // |*consumed_len| will contain the number of characters in the input that
33 // express what we found.
34 DotDisposition ClassifyAfterDot(const std::string& path,
35 size_t after_dot,
36 size_t* consumed_len) {
37 if (after_dot == path.size()) {
38 // Single dot at the end.
39 *consumed_len = 1;
40 return DIRECTORY_CUR;
42 if (path[after_dot] == '/') {
43 // Single dot followed by a slash.
44 *consumed_len = 2; // Consume the slash
45 return DIRECTORY_CUR;
48 if (path[after_dot] == '.') {
49 // Two dots.
50 if (after_dot + 1 == path.size()) {
51 // Double dot at the end.
52 *consumed_len = 2;
53 return DIRECTORY_UP;
55 if (path[after_dot + 1] == '/') {
56 // Double dot folowed by a slash.
57 *consumed_len = 3;
58 return DIRECTORY_UP;
62 // The dots are followed by something else, not a directory.
63 *consumed_len = 1;
64 return NOT_A_DIRECTORY;
67 #if defined(OS_WIN)
68 inline char NormalizeWindowsPathChar(char c) {
69 if (c == '/')
70 return '\\';
71 return base::ToLowerASCII(c);
74 // Attempts to do a case and slash-insensitive comparison of two 8-bit Windows
75 // paths.
76 bool AreAbsoluteWindowsPathsEqual(const base::StringPiece& a,
77 const base::StringPiece& b) {
78 if (a.size() != b.size())
79 return false;
81 // For now, just do a case-insensitive ASCII comparison. We could convert to
82 // UTF-16 and use ICU if necessary. Or maybe base::strcasecmp is good enough?
83 for (size_t i = 0; i < a.size(); i++) {
84 if (NormalizeWindowsPathChar(a[i]) != NormalizeWindowsPathChar(b[i]))
85 return false;
87 return true;
90 bool DoesBeginWindowsDriveLetter(const base::StringPiece& path) {
91 if (path.size() < 3)
92 return false;
94 // Check colon first, this will generally fail fastest.
95 if (path[1] != ':')
96 return false;
98 // Check drive letter
99 if (!((path[0] >= 'A' && path[0] <= 'Z') ||
100 path[0] >= 'a' && path[0] <= 'z'))
101 return false;
103 if (path[2] != '/' && path[2] != '\\')
104 return false;
105 return true;
107 #endif
109 } // namespace
111 SourceFileType GetSourceFileType(const SourceFile& file,
112 Settings::TargetOS os) {
113 base::StringPiece extension = FindExtension(&file.value());
114 if (extension == "cc" || extension == "cpp" || extension == "cxx")
115 return SOURCE_CC;
116 if (extension == "h")
117 return SOURCE_H;
118 if (extension == "c")
119 return SOURCE_C;
121 switch (os) {
122 case Settings::MAC:
123 if (extension == "m")
124 return SOURCE_M;
125 if (extension == "mm")
126 return SOURCE_MM;
127 break;
129 case Settings::WIN:
130 if (extension == "rc")
131 return SOURCE_RC;
132 // TODO(brettw) asm files.
133 break;
135 default:
136 break;
139 if (os != Settings::WIN) {
140 if (extension == "S")
141 return SOURCE_S;
144 return SOURCE_UNKNOWN;
147 const char* GetExtensionForOutputType(Target::OutputType type,
148 Settings::TargetOS os) {
149 switch (os) {
150 case Settings::MAC:
151 switch (type) {
152 case Target::EXECUTABLE:
153 return "";
154 case Target::SHARED_LIBRARY:
155 return "dylib";
156 case Target::STATIC_LIBRARY:
157 return "a";
158 default:
159 NOTREACHED();
161 break;
163 case Settings::WIN:
164 switch (type) {
165 case Target::EXECUTABLE:
166 return "exe";
167 case Target::SHARED_LIBRARY:
168 return "dll.lib"; // Extension of import library.
169 case Target::STATIC_LIBRARY:
170 return "lib";
171 default:
172 NOTREACHED();
174 break;
176 case Settings::LINUX:
177 switch (type) {
178 case Target::EXECUTABLE:
179 return "";
180 case Target::SHARED_LIBRARY:
181 return "so";
182 case Target::STATIC_LIBRARY:
183 return "a";
184 default:
185 NOTREACHED();
187 break;
189 default:
190 NOTREACHED();
192 return "";
195 std::string FilePathToUTF8(const base::FilePath& path) {
196 #if defined(OS_WIN)
197 return WideToUTF8(path.value());
198 #else
199 return path.value();
200 #endif
203 base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
204 #if defined(OS_WIN)
205 return base::FilePath(UTF8ToWide(sp));
206 #else
207 return base::FilePath(sp.as_string());
208 #endif
211 size_t FindExtensionOffset(const std::string& path) {
212 for (int i = static_cast<int>(path.size()); i >= 0; i--) {
213 if (path[i] == '/')
214 break;
215 if (path[i] == '.')
216 return i + 1;
218 return std::string::npos;
221 base::StringPiece FindExtension(const std::string* path) {
222 size_t extension_offset = FindExtensionOffset(*path);
223 if (extension_offset == std::string::npos)
224 return base::StringPiece();
225 return base::StringPiece(&path->data()[extension_offset],
226 path->size() - extension_offset);
229 size_t FindFilenameOffset(const std::string& path) {
230 for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) {
231 if (path[i] == '/')
232 return i + 1;
234 return 0; // No filename found means everything was the filename.
237 base::StringPiece FindFilename(const std::string* path) {
238 size_t filename_offset = FindFilenameOffset(*path);
239 if (filename_offset == 0)
240 return base::StringPiece(*path); // Everything is the file name.
241 return base::StringPiece(&(*path).data()[filename_offset],
242 path->size() - filename_offset);
245 base::StringPiece FindFilenameNoExtension(const std::string* path) {
246 if (path->empty())
247 return base::StringPiece();
248 size_t filename_offset = FindFilenameOffset(*path);
249 size_t extension_offset = FindExtensionOffset(*path);
251 size_t name_len;
252 if (extension_offset == std::string::npos)
253 name_len = path->size() - filename_offset;
254 else
255 name_len = extension_offset - filename_offset - 1;
257 return base::StringPiece(&(*path).data()[filename_offset], name_len);
260 void RemoveFilename(std::string* path) {
261 path->resize(FindFilenameOffset(*path));
264 bool EndsWithSlash(const std::string& s) {
265 return !s.empty() && s[s.size() - 1] == '/';
268 base::StringPiece FindDir(const std::string* path) {
269 size_t filename_offset = FindFilenameOffset(*path);
270 if (filename_offset == 0u)
271 return base::StringPiece();
272 return base::StringPiece(path->data(), filename_offset);
275 bool EnsureStringIsInOutputDir(const SourceDir& dir,
276 const std::string& str,
277 const Value& originating,
278 Err* err) {
279 // The last char of the dir will be a slash. We don't care if the input ends
280 // in a slash or not, so just compare up until there.
282 // This check will be wrong for all proper prefixes "e.g. "/output" will
283 // match "/out" but we don't really care since this is just a sanity check.
284 const std::string& dir_str = dir.value();
285 if (str.compare(0, dir_str.length() - 1, dir_str, 0, dir_str.length() - 1)
286 != 0) {
287 *err = Err(originating, "File not inside output directory.",
288 "The given file should be in the output directory. Normally you would "
289 "specify\n\"$target_output_dir/foo\" or "
290 "\"$target_gen_dir/foo\". I interpreted this as\n\""
291 + str + "\".");
292 return false;
294 return true;
297 bool IsPathAbsolute(const base::StringPiece& path) {
298 if (path.empty())
299 return false;
301 if (path[0] != '/') {
302 #if defined(OS_WIN)
303 // Check for Windows system paths like "C:\foo".
304 if (path.size() > 2 &&
305 path[1] == ':' && (path[2] == '/' || path[2] == '\\'))
306 return true;
307 #endif
308 return false; // Doesn't begin with a slash, is relative.
311 if (path.size() > 1 && path[1] == '/')
312 return false; // Double slash at the beginning means source-relative.
314 return true;
317 bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
318 const base::StringPiece& path,
319 std::string* dest) {
320 DCHECK(IsPathAbsolute(source_root));
321 DCHECK(IsPathAbsolute(path));
323 dest->clear();
325 if (source_root.size() > path.size())
326 return false; // The source root is longer: the path can never be inside.
328 #if defined(OS_WIN)
329 // Source root should be canonical on Windows.
330 DCHECK(source_root.size() > 2 && source_root[0] != '/' &&
331 source_root[1] == ':' && source_root[2] =='\\');
333 size_t after_common_index = std::string::npos;
334 if (DoesBeginWindowsDriveLetter(path)) {
335 // Handle "C:\foo"
336 if (AreAbsoluteWindowsPathsEqual(source_root,
337 path.substr(0, source_root.size())))
338 after_common_index = source_root.size();
339 else
340 return false;
341 } else if (path[0] == '/' && source_root.size() <= path.size() - 1 &&
342 DoesBeginWindowsDriveLetter(path.substr(1))) {
343 // Handle "/C:/foo"
344 if (AreAbsoluteWindowsPathsEqual(source_root,
345 path.substr(1, source_root.size())))
346 after_common_index = source_root.size() + 1;
347 else
348 return false;
349 } else {
350 return false;
353 // If we get here, there's a match and after_common_index identifies the
354 // part after it.
356 // The base may or may not have a trailing slash, so skip all slashes from
357 // the path after our prefix match.
358 size_t first_after_slash = after_common_index;
359 while (first_after_slash < path.size() &&
360 (path[first_after_slash] == '/' || path[first_after_slash] == '\\'))
361 first_after_slash++;
363 dest->assign("//"); // Result is source root relative.
364 dest->append(&path.data()[first_after_slash],
365 path.size() - first_after_slash);
366 return true;
368 #else
370 // On non-Windows this is easy. Since we know both are absolute, just do a
371 // prefix check.
372 if (path.substr(0, source_root.size()) == source_root) {
373 // The base may or may not have a trailing slash, so skip all slashes from
374 // the path after our prefix match.
375 size_t first_after_slash = source_root.size();
376 while (first_after_slash < path.size() && path[first_after_slash] == '/')
377 first_after_slash++;
379 dest->assign("//"); // Result is source root relative.
380 dest->append(&path.data()[first_after_slash],
381 path.size() - first_after_slash);
382 return true;
384 return false;
385 #endif
388 std::string InvertDir(const SourceDir& path) {
389 const std::string value = path.value();
390 if (value.empty())
391 return std::string();
393 DCHECK(value[0] == '/');
394 size_t begin_index = 1;
396 // If the input begins with two slashes, skip over both (this is a
397 // source-relative dir).
398 if (value.size() > 1 && value[1] == '/')
399 begin_index = 2;
401 std::string ret;
402 for (size_t i = begin_index; i < value.size(); i++) {
403 if (value[i] == '/')
404 ret.append("../");
406 return ret;
409 void NormalizePath(std::string* path) {
410 char* pathbuf = path->empty() ? NULL : &(*path)[0];
412 // top_index is the first character we can modify in the path. Anything
413 // before this indicates where the path is relative to.
414 size_t top_index = 0;
415 bool is_relative = true;
416 if (!path->empty() && pathbuf[0] == '/') {
417 is_relative = false;
419 if (path->size() > 1 && pathbuf[1] == '/') {
420 // Two leading slashes, this is a path into the source dir.
421 top_index = 2;
422 } else {
423 // One leading slash, this is a system-absolute path.
424 top_index = 1;
428 size_t dest_i = top_index;
429 for (size_t src_i = top_index; src_i < path->size(); /* nothing */) {
430 if (pathbuf[src_i] == '.') {
431 if (src_i == 0 || pathbuf[src_i - 1] == '/') {
432 // Slash followed by a dot, see if it's something special.
433 size_t consumed_len;
434 switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
435 case NOT_A_DIRECTORY:
436 // Copy the dot to the output, it means nothing special.
437 pathbuf[dest_i++] = pathbuf[src_i++];
438 break;
439 case DIRECTORY_CUR:
440 // Current directory, just skip the input.
441 src_i += consumed_len;
442 break;
443 case DIRECTORY_UP:
444 // Back up over previous directory component. If we're already
445 // at the top, preserve the "..".
446 if (dest_i > top_index) {
447 // The previous char was a slash, remove it.
448 dest_i--;
451 if (dest_i == top_index) {
452 if (is_relative) {
453 // We're already at the beginning of a relative input, copy the
454 // ".." and continue. We need the trailing slash if there was
455 // one before (otherwise we're at the end of the input).
456 pathbuf[dest_i++] = '.';
457 pathbuf[dest_i++] = '.';
458 if (consumed_len == 3)
459 pathbuf[dest_i++] = '/';
461 // This also makes a new "root" that we can't delete by going
462 // up more levels. Otherwise "../.." would collapse to
463 // nothing.
464 top_index = dest_i;
466 // Otherwise we're at the beginning of an absolute path. Don't
467 // allow ".." to go up another level and just eat it.
468 } else {
469 // Just find the previous slash or the beginning of input.
470 while (dest_i > 0 && pathbuf[dest_i - 1] != '/')
471 dest_i--;
473 src_i += consumed_len;
475 } else {
476 // Dot not preceeded by a slash, copy it literally.
477 pathbuf[dest_i++] = pathbuf[src_i++];
479 } else if (pathbuf[src_i] == '/') {
480 if (src_i > 0 && pathbuf[src_i - 1] == '/') {
481 // Two slashes in a row, skip over it.
482 src_i++;
483 } else {
484 // Just one slash, copy it.
485 pathbuf[dest_i++] = pathbuf[src_i++];
487 } else {
488 // Input nothing special, just copy it.
489 pathbuf[dest_i++] = pathbuf[src_i++];
492 path->resize(dest_i);
495 void ConvertPathToSystem(std::string* path) {
496 #if defined(OS_WIN)
497 for (size_t i = 0; i < path->size(); i++) {
498 if ((*path)[i] == '/')
499 (*path)[i] = '\\';
501 #endif
504 std::string PathToSystem(const std::string& path) {
505 std::string ret(path);
506 ConvertPathToSystem(&ret);
507 return ret;