blob: 2ceca81677dbc68d0e041271ea9702ec1c3302c0 [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
[email protected]ea3690c2013-09-23 17:59:227#include <algorithm>
8
thestig1ecdcf42014-09-12 05:09:149#include "base/files/file_util.h"
[email protected]96ea63d2013-07-30 10:17:0710#include "base/logging.h"
[email protected]f6b314d12013-08-23 21:07:4711#include "base/strings/string_util.h"
[email protected]96ea63d2013-07-30 10:17:0712#include "base/strings/utf_string_conversions.h"
13#include "build/build_config.h"
14#include "tools/gn/location.h"
[email protected]05b94b432013-11-22 22:09:5515#include "tools/gn/settings.h"
[email protected]96ea63d2013-07-30 10:17:0716#include "tools/gn/source_dir.h"
17
18namespace {
19
20enum DotDisposition {
21 // The given dot is just part of a filename and is not special.
22 NOT_A_DIRECTORY,
23
24 // The given dot is the current directory.
25 DIRECTORY_CUR,
26
27 // The given dot is the first of a double dot that should take us up one.
28 DIRECTORY_UP
29};
30
31// When we find a dot, this function is called with the character following
32// that dot to see what it is. The return value indicates what type this dot is
33// (see above). This code handles the case where the dot is at the end of the
34// input.
35//
36// |*consumed_len| will contain the number of characters in the input that
37// express what we found.
38DotDisposition ClassifyAfterDot(const std::string& path,
39 size_t after_dot,
40 size_t* consumed_len) {
41 if (after_dot == path.size()) {
42 // Single dot at the end.
43 *consumed_len = 1;
44 return DIRECTORY_CUR;
45 }
[email protected]858ceda2014-02-27 23:26:4046 if (IsSlash(path[after_dot])) {
[email protected]96ea63d2013-07-30 10:17:0747 // Single dot followed by a slash.
48 *consumed_len = 2; // Consume the slash
49 return DIRECTORY_CUR;
50 }
51
52 if (path[after_dot] == '.') {
53 // Two dots.
54 if (after_dot + 1 == path.size()) {
55 // Double dot at the end.
56 *consumed_len = 2;
57 return DIRECTORY_UP;
58 }
[email protected]858ceda2014-02-27 23:26:4059 if (IsSlash(path[after_dot + 1])) {
[email protected]96ea63d2013-07-30 10:17:0760 // Double dot folowed by a slash.
61 *consumed_len = 3;
62 return DIRECTORY_UP;
63 }
64 }
65
66 // The dots are followed by something else, not a directory.
67 *consumed_len = 1;
68 return NOT_A_DIRECTORY;
69}
70
[email protected]f6b314d12013-08-23 21:07:4771#if defined(OS_WIN)
72inline char NormalizeWindowsPathChar(char c) {
73 if (c == '/')
74 return '\\';
75 return base::ToLowerASCII(c);
76}
77
78// Attempts to do a case and slash-insensitive comparison of two 8-bit Windows
79// paths.
80bool AreAbsoluteWindowsPathsEqual(const base::StringPiece& a,
81 const base::StringPiece& b) {
82 if (a.size() != b.size())
83 return false;
84
85 // For now, just do a case-insensitive ASCII comparison. We could convert to
brettw8a800902015-07-10 18:28:3386 // UTF-16 and use ICU if necessary.
[email protected]f6b314d12013-08-23 21:07:4787 for (size_t i = 0; i < a.size(); i++) {
88 if (NormalizeWindowsPathChar(a[i]) != NormalizeWindowsPathChar(b[i]))
89 return false;
90 }
91 return true;
92}
93
94bool DoesBeginWindowsDriveLetter(const base::StringPiece& path) {
95 if (path.size() < 3)
96 return false;
97
98 // Check colon first, this will generally fail fastest.
99 if (path[1] != ':')
100 return false;
101
[email protected]92df9852014-05-15 21:52:35102 // Check drive letter.
brettwb3413062015-06-24 00:39:02103 if (!base::IsAsciiAlpha(path[0]))
[email protected]f6b314d12013-08-23 21:07:47104 return false;
105
[email protected]858ceda2014-02-27 23:26:40106 if (!IsSlash(path[2]))
[email protected]f6b314d12013-08-23 21:07:47107 return false;
108 return true;
109}
110#endif
111
[email protected]a623db1872014-02-19 19:11:17112// A wrapper around FilePath.GetComponents that works the way we need. This is
113// not super efficient since it does some O(n) transformations on the path. If
114// this is called a lot, we might want to optimize.
115std::vector<base::FilePath::StringType> GetPathComponents(
116 const base::FilePath& path) {
117 std::vector<base::FilePath::StringType> result;
118 path.GetComponents(&result);
119
120 if (result.empty())
121 return result;
122
123 // GetComponents will preserve the "/" at the beginning, which confuses us.
124 // We don't expect to have relative paths in this function.
125 // Don't use IsSeparator since we always want to allow backslashes.
126 if (result[0] == FILE_PATH_LITERAL("/") ||
127 result[0] == FILE_PATH_LITERAL("\\"))
128 result.erase(result.begin());
129
130#if defined(OS_WIN)
131 // On Windows, GetComponents will give us [ "C:", "/", "foo" ], and we
132 // don't want the slash in there. This doesn't support input like "C:foo"
133 // which means foo relative to the current directory of the C drive but
134 // that's basically legacy DOS behavior we don't need to support.
pkasting5c4699c22014-10-17 18:17:15135 if (result.size() >= 2 && result[1].size() == 1 &&
136 IsSlash(static_cast<char>(result[1][0])))
[email protected]a623db1872014-02-19 19:11:17137 result.erase(result.begin() + 1);
138#endif
139
140 return result;
141}
142
143// Provides the equivalent of == for filesystem strings, trying to do
144// approximately the right thing with case.
145bool FilesystemStringsEqual(const base::FilePath::StringType& a,
146 const base::FilePath::StringType& b) {
147#if defined(OS_WIN)
148 // Assume case-insensitive filesystems on Windows. We use the CompareString
149 // function to do a case-insensitive comparison based on the current locale
150 // (we don't want GN to depend on ICU which is large and requires data
151 // files). This isn't perfect, but getting this perfectly right is very
152 // difficult and requires I/O, and this comparison should cover 99.9999% of
153 // all cases.
154 //
155 // Note: The documentation for CompareString says it runs fastest on
156 // null-terminated strings with -1 passed for the length, so we do that here.
157 // There should not be embedded nulls in filesystem strings.
158 return ::CompareString(LOCALE_USER_DEFAULT, LINGUISTIC_IGNORECASE,
159 a.c_str(), -1, b.c_str(), -1) == CSTR_EQUAL;
160#else
161 // Assume case-sensitive filesystems on non-Windows.
162 return a == b;
163#endif
164}
165
[email protected]f6b314d12013-08-23 21:07:47166} // namespace
[email protected]96ea63d2013-07-30 10:17:07167
[email protected]1bcf0012013-09-19 21:13:32168std::string FilePathToUTF8(const base::FilePath::StringType& str) {
[email protected]96ea63d2013-07-30 10:17:07169#if defined(OS_WIN)
[email protected]6c3bf032013-12-25 19:37:03170 return base::WideToUTF8(str);
[email protected]96ea63d2013-07-30 10:17:07171#else
[email protected]1bcf0012013-09-19 21:13:32172 return str;
[email protected]96ea63d2013-07-30 10:17:07173#endif
174}
175
176base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
177#if defined(OS_WIN)
[email protected]6c3bf032013-12-25 19:37:03178 return base::FilePath(base::UTF8ToWide(sp));
[email protected]96ea63d2013-07-30 10:17:07179#else
180 return base::FilePath(sp.as_string());
181#endif
182}
183
184size_t FindExtensionOffset(const std::string& path) {
185 for (int i = static_cast<int>(path.size()); i >= 0; i--) {
[email protected]858ceda2014-02-27 23:26:40186 if (IsSlash(path[i]))
[email protected]96ea63d2013-07-30 10:17:07187 break;
188 if (path[i] == '.')
189 return i + 1;
190 }
191 return std::string::npos;
192}
193
194base::StringPiece FindExtension(const std::string* path) {
195 size_t extension_offset = FindExtensionOffset(*path);
196 if (extension_offset == std::string::npos)
197 return base::StringPiece();
198 return base::StringPiece(&path->data()[extension_offset],
199 path->size() - extension_offset);
200}
201
202size_t FindFilenameOffset(const std::string& path) {
203 for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) {
[email protected]858ceda2014-02-27 23:26:40204 if (IsSlash(path[i]))
[email protected]96ea63d2013-07-30 10:17:07205 return i + 1;
206 }
207 return 0; // No filename found means everything was the filename.
208}
209
210base::StringPiece FindFilename(const std::string* path) {
211 size_t filename_offset = FindFilenameOffset(*path);
212 if (filename_offset == 0)
213 return base::StringPiece(*path); // Everything is the file name.
214 return base::StringPiece(&(*path).data()[filename_offset],
215 path->size() - filename_offset);
216}
217
218base::StringPiece FindFilenameNoExtension(const std::string* path) {
219 if (path->empty())
220 return base::StringPiece();
221 size_t filename_offset = FindFilenameOffset(*path);
222 size_t extension_offset = FindExtensionOffset(*path);
223
224 size_t name_len;
225 if (extension_offset == std::string::npos)
226 name_len = path->size() - filename_offset;
227 else
228 name_len = extension_offset - filename_offset - 1;
229
230 return base::StringPiece(&(*path).data()[filename_offset], name_len);
231}
232
233void RemoveFilename(std::string* path) {
234 path->resize(FindFilenameOffset(*path));
235}
236
237bool EndsWithSlash(const std::string& s) {
[email protected]858ceda2014-02-27 23:26:40238 return !s.empty() && IsSlash(s[s.size() - 1]);
[email protected]96ea63d2013-07-30 10:17:07239}
240
241base::StringPiece FindDir(const std::string* path) {
242 size_t filename_offset = FindFilenameOffset(*path);
243 if (filename_offset == 0u)
244 return base::StringPiece();
245 return base::StringPiece(path->data(), filename_offset);
246}
247
[email protected]a3372c72014-04-28 22:39:26248base::StringPiece FindLastDirComponent(const SourceDir& dir) {
249 const std::string& dir_string = dir.value();
250
251 if (dir_string.empty())
252 return base::StringPiece();
253 int cur = static_cast<int>(dir_string.size()) - 1;
254 DCHECK(dir_string[cur] == '/');
255 int end = cur;
256 cur--; // Skip before the last slash.
257
258 for (; cur >= 0; cur--) {
259 if (dir_string[cur] == '/')
260 return base::StringPiece(&dir_string[cur + 1], end - cur - 1);
261 }
262 return base::StringPiece(&dir_string[0], end);
263}
264
brettw56affab2015-06-04 22:01:03265bool IsStringInOutputDir(const SourceDir& output_dir, const std::string& str) {
266 // This check will be wrong for all proper prefixes "e.g. "/output" will
267 // match "/out" but we don't really care since this is just a sanity check.
268 const std::string& dir_str = output_dir.value();
269 return str.compare(0, dir_str.length(), dir_str) == 0;
270}
271
272bool EnsureStringIsInOutputDir(const SourceDir& output_dir,
[email protected]96ea63d2013-07-30 10:17:07273 const std::string& str,
[email protected]0dfcae72014-08-19 22:52:16274 const ParseNode* origin,
[email protected]96ea63d2013-07-30 10:17:07275 Err* err) {
brettw56affab2015-06-04 22:01:03276 if (IsStringInOutputDir(output_dir, str))
[email protected]f0bbcdc22014-07-11 17:13:35277 return true; // Output directory is hardcoded.
278
[email protected]0dfcae72014-08-19 22:52:16279 *err = Err(origin, "File is not inside output directory.",
[email protected]f0bbcdc22014-07-11 17:13:35280 "The given file should be in the output directory. Normally you would "
281 "specify\n\"$target_out_dir/foo\" or "
282 "\"$target_gen_dir/foo\". I interpreted this as\n\""
283 + str + "\".");
284 return false;
[email protected]96ea63d2013-07-30 10:17:07285}
286
[email protected]ac1128e2013-08-23 00:26:56287bool IsPathAbsolute(const base::StringPiece& path) {
288 if (path.empty())
289 return false;
290
[email protected]858ceda2014-02-27 23:26:40291 if (!IsSlash(path[0])) {
[email protected]ac1128e2013-08-23 00:26:56292#if defined(OS_WIN)
293 // Check for Windows system paths like "C:\foo".
[email protected]858ceda2014-02-27 23:26:40294 if (path.size() > 2 && path[1] == ':' && IsSlash(path[2]))
[email protected]ac1128e2013-08-23 00:26:56295 return true;
296#endif
297 return false; // Doesn't begin with a slash, is relative.
298 }
299
[email protected]858ceda2014-02-27 23:26:40300 // Double forward slash at the beginning means source-relative (we don't
301 // allow backslashes for denoting this).
[email protected]ac1128e2013-08-23 00:26:56302 if (path.size() > 1 && path[1] == '/')
[email protected]858ceda2014-02-27 23:26:40303 return false;
[email protected]ac1128e2013-08-23 00:26:56304
305 return true;
306}
307
308bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
309 const base::StringPiece& path,
310 std::string* dest) {
311 DCHECK(IsPathAbsolute(source_root));
312 DCHECK(IsPathAbsolute(path));
313
314 dest->clear();
315
316 if (source_root.size() > path.size())
317 return false; // The source root is longer: the path can never be inside.
318
319#if defined(OS_WIN)
[email protected]858ceda2014-02-27 23:26:40320 // Source root should be canonical on Windows. Note that the initial slash
321 // must be forward slash, but that the other ones can be either forward or
322 // backward.
[email protected]ac1128e2013-08-23 00:26:56323 DCHECK(source_root.size() > 2 && source_root[0] != '/' &&
[email protected]858ceda2014-02-27 23:26:40324 source_root[1] == ':' && IsSlash(source_root[2]));
[email protected]f6b314d12013-08-23 21:07:47325
326 size_t after_common_index = std::string::npos;
327 if (DoesBeginWindowsDriveLetter(path)) {
328 // Handle "C:\foo"
329 if (AreAbsoluteWindowsPathsEqual(source_root,
330 path.substr(0, source_root.size())))
331 after_common_index = source_root.size();
332 else
333 return false;
334 } else if (path[0] == '/' && source_root.size() <= path.size() - 1 &&
335 DoesBeginWindowsDriveLetter(path.substr(1))) {
336 // Handle "/C:/foo"
337 if (AreAbsoluteWindowsPathsEqual(source_root,
338 path.substr(1, source_root.size())))
339 after_common_index = source_root.size() + 1;
340 else
341 return false;
342 } else {
343 return false;
344 }
345
346 // If we get here, there's a match and after_common_index identifies the
347 // part after it.
348
349 // The base may or may not have a trailing slash, so skip all slashes from
350 // the path after our prefix match.
351 size_t first_after_slash = after_common_index;
[email protected]858ceda2014-02-27 23:26:40352 while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
[email protected]f6b314d12013-08-23 21:07:47353 first_after_slash++;
354
355 dest->assign("//"); // Result is source root relative.
356 dest->append(&path.data()[first_after_slash],
357 path.size() - first_after_slash);
358 return true;
359
[email protected]ac1128e2013-08-23 00:26:56360#else
[email protected]f6b314d12013-08-23 21:07:47361
[email protected]ac1128e2013-08-23 00:26:56362 // On non-Windows this is easy. Since we know both are absolute, just do a
363 // prefix check.
364 if (path.substr(0, source_root.size()) == source_root) {
[email protected]ac1128e2013-08-23 00:26:56365 // The base may or may not have a trailing slash, so skip all slashes from
366 // the path after our prefix match.
367 size_t first_after_slash = source_root.size();
[email protected]858ceda2014-02-27 23:26:40368 while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
[email protected]ac1128e2013-08-23 00:26:56369 first_after_slash++;
370
[email protected]f6b314d12013-08-23 21:07:47371 dest->assign("//"); // Result is source root relative.
[email protected]ac1128e2013-08-23 00:26:56372 dest->append(&path.data()[first_after_slash],
373 path.size() - first_after_slash);
374 return true;
375 }
[email protected]ac1128e2013-08-23 00:26:56376 return false;
[email protected]f6b314d12013-08-23 21:07:47377#endif
[email protected]ac1128e2013-08-23 00:26:56378}
379
slan910ac442015-12-04 01:40:29380void NormalizePath(std::string* path, const base::StringPiece& source_root) {
tfarina9b636af2014-12-23 00:52:07381 char* pathbuf = path->empty() ? nullptr : &(*path)[0];
[email protected]96ea63d2013-07-30 10:17:07382
383 // top_index is the first character we can modify in the path. Anything
384 // before this indicates where the path is relative to.
385 size_t top_index = 0;
386 bool is_relative = true;
387 if (!path->empty() && pathbuf[0] == '/') {
388 is_relative = false;
389
390 if (path->size() > 1 && pathbuf[1] == '/') {
391 // Two leading slashes, this is a path into the source dir.
392 top_index = 2;
393 } else {
394 // One leading slash, this is a system-absolute path.
395 top_index = 1;
396 }
397 }
398
399 size_t dest_i = top_index;
400 for (size_t src_i = top_index; src_i < path->size(); /* nothing */) {
401 if (pathbuf[src_i] == '.') {
[email protected]858ceda2014-02-27 23:26:40402 if (src_i == 0 || IsSlash(pathbuf[src_i - 1])) {
[email protected]96ea63d2013-07-30 10:17:07403 // Slash followed by a dot, see if it's something special.
404 size_t consumed_len;
405 switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
406 case NOT_A_DIRECTORY:
407 // Copy the dot to the output, it means nothing special.
408 pathbuf[dest_i++] = pathbuf[src_i++];
409 break;
410 case DIRECTORY_CUR:
411 // Current directory, just skip the input.
412 src_i += consumed_len;
413 break;
414 case DIRECTORY_UP:
415 // Back up over previous directory component. If we're already
416 // at the top, preserve the "..".
417 if (dest_i > top_index) {
418 // The previous char was a slash, remove it.
419 dest_i--;
420 }
421
422 if (dest_i == top_index) {
423 if (is_relative) {
424 // We're already at the beginning of a relative input, copy the
425 // ".." and continue. We need the trailing slash if there was
426 // one before (otherwise we're at the end of the input).
427 pathbuf[dest_i++] = '.';
428 pathbuf[dest_i++] = '.';
429 if (consumed_len == 3)
430 pathbuf[dest_i++] = '/';
431
432 // This also makes a new "root" that we can't delete by going
433 // up more levels. Otherwise "../.." would collapse to
434 // nothing.
435 top_index = dest_i;
slan910ac442015-12-04 01:40:29436 } else if (top_index == 2 && !source_root.empty()) {
437 // |path| was passed in as a source-absolute path. Prepend
438 // |source_root| to make |path| absolute. |source_root| must not
439 // end with a slash unless we are at root.
440 DCHECK(source_root.size() == 1u ||
441 !IsSlash(source_root[source_root.size() - 1u]));
442 size_t source_root_len = source_root.size();
443
444#if defined(OS_WIN)
445 // On Windows, if the source_root does not start with a slash,
446 // append one here for consistency.
447 if (!IsSlash(source_root[0])) {
448 path->insert(0, "/" + source_root.as_string());
449 source_root_len++;
450 } else {
451 path->insert(0, source_root.data(), source_root_len);
452 }
453
454 // Normalize slashes in source root portion.
455 for (size_t i = 0; i < source_root_len; ++i) {
456 if ((*path)[i] == '\\')
457 (*path)[i] = '/';
458 }
459#else
460 path->insert(0, source_root.data(), source_root_len);
461#endif
462
463 // |path| is now absolute, so |top_index| is 1. |dest_i| and
464 // |src_i| should be incremented to keep the same relative
465 // position. Comsume the leading "//" by decrementing |dest_i|.
466 top_index = 1;
467 pathbuf = &(*path)[0];
468 dest_i += source_root_len - 2;
469 src_i += source_root_len;
470
471 // Just find the previous slash or the beginning of input.
472 while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1]))
473 dest_i--;
[email protected]96ea63d2013-07-30 10:17:07474 }
slan910ac442015-12-04 01:40:29475 // Otherwise we're at the beginning of a system-absolute path, or
476 // a source-absolute path for which we don't know the absolute
477 // path. Don't allow ".." to go up another level, and just eat it.
[email protected]96ea63d2013-07-30 10:17:07478 } else {
479 // Just find the previous slash or the beginning of input.
[email protected]858ceda2014-02-27 23:26:40480 while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1]))
[email protected]96ea63d2013-07-30 10:17:07481 dest_i--;
482 }
483 src_i += consumed_len;
484 }
485 } else {
486 // Dot not preceeded by a slash, copy it literally.
487 pathbuf[dest_i++] = pathbuf[src_i++];
488 }
[email protected]858ceda2014-02-27 23:26:40489 } else if (IsSlash(pathbuf[src_i])) {
490 if (src_i > 0 && IsSlash(pathbuf[src_i - 1])) {
[email protected]96ea63d2013-07-30 10:17:07491 // Two slashes in a row, skip over it.
492 src_i++;
493 } else {
[email protected]858ceda2014-02-27 23:26:40494 // Just one slash, copy it, normalizing to foward slash.
495 pathbuf[dest_i] = '/';
496 dest_i++;
497 src_i++;
[email protected]96ea63d2013-07-30 10:17:07498 }
499 } else {
500 // Input nothing special, just copy it.
501 pathbuf[dest_i++] = pathbuf[src_i++];
502 }
503 }
504 path->resize(dest_i);
505}
506
507void ConvertPathToSystem(std::string* path) {
508#if defined(OS_WIN)
509 for (size_t i = 0; i < path->size(); i++) {
510 if ((*path)[i] == '/')
511 (*path)[i] = '\\';
512 }
513#endif
514}
515
zeuthen6f9c64562014-11-11 21:01:13516std::string MakeRelativePath(const std::string& input,
517 const std::string& dest) {
ohrn8808c5542015-02-10 10:18:38518#if defined(OS_WIN)
519 // Make sure that absolute |input| path starts with a slash if |dest| path
520 // does. Otherwise skipping common prefixes won't work properly. Ensure the
521 // same for |dest| path too.
522 if (IsPathAbsolute(input) && !IsSlash(input[0]) && IsSlash(dest[0])) {
523 std::string corrected_input(1, dest[0]);
524 corrected_input.append(input);
525 return MakeRelativePath(corrected_input, dest);
526 }
527 if (IsPathAbsolute(dest) && !IsSlash(dest[0]) && IsSlash(input[0])) {
528 std::string corrected_dest(1, input[0]);
529 corrected_dest.append(dest);
530 return MakeRelativePath(input, corrected_dest);
531 }
532#endif
533
zeuthen6f9c64562014-11-11 21:01:13534 std::string ret;
[email protected]ea3690c2013-09-23 17:59:22535
536 // Skip the common prefixes of the source and dest as long as they end in
537 // a [back]slash.
zeuthen6f9c64562014-11-11 21:01:13538 size_t common_prefix_len = 0;
[email protected]ea3690c2013-09-23 17:59:22539 size_t max_common_length = std::min(input.size(), dest.size());
540 for (size_t i = common_prefix_len; i < max_common_length; i++) {
[email protected]858ceda2014-02-27 23:26:40541 if (IsSlash(input[i]) && IsSlash(dest[i]))
[email protected]ea3690c2013-09-23 17:59:22542 common_prefix_len = i + 1;
543 else if (input[i] != dest[i])
544 break;
545 }
546
547 // Invert the dest dir starting from the end of the common prefix.
[email protected]ea3690c2013-09-23 17:59:22548 for (size_t i = common_prefix_len; i < dest.size(); i++) {
[email protected]858ceda2014-02-27 23:26:40549 if (IsSlash(dest[i]))
[email protected]ea3690c2013-09-23 17:59:22550 ret.append("../");
551 }
552
553 // Append any remaining unique input.
554 ret.append(&input[common_prefix_len], input.size() - common_prefix_len);
555
556 // If the result is still empty, the paths are the same.
557 if (ret.empty())
558 ret.push_back('.');
559
560 return ret;
561}
[email protected]05b94b432013-11-22 22:09:55562
zeuthen6f9c64562014-11-11 21:01:13563std::string RebasePath(const std::string& input,
564 const SourceDir& dest_dir,
565 const base::StringPiece& source_root) {
566 std::string ret;
567 DCHECK(source_root.empty() || !source_root.ends_with("/"));
568
569 bool input_is_source_path = (input.size() >= 2 &&
570 input[0] == '/' && input[1] == '/');
571
572 if (!source_root.empty() &&
573 (!input_is_source_path || !dest_dir.is_source_absolute())) {
574 std::string input_full;
575 std::string dest_full;
576 if (input_is_source_path) {
577 source_root.AppendToString(&input_full);
578 input_full.push_back('/');
579 input_full.append(input, 2, std::string::npos);
580 } else {
581 input_full.append(input);
582 }
583 if (dest_dir.is_source_absolute()) {
584 source_root.AppendToString(&dest_full);
585 dest_full.push_back('/');
586 dest_full.append(dest_dir.value(), 2, std::string::npos);
587 } else {
588#if defined(OS_WIN)
589 // On Windows, SourceDir system-absolute paths start
590 // with /, e.g. "/C:/foo/bar".
591 const std::string& value = dest_dir.value();
592 if (value.size() > 2 && value[2] == ':')
593 dest_full.append(dest_dir.value().substr(1));
594 else
595 dest_full.append(dest_dir.value());
596#else
597 dest_full.append(dest_dir.value());
598#endif
599 }
600 bool remove_slash = false;
601 if (!EndsWithSlash(input_full)) {
602 input_full.push_back('/');
603 remove_slash = true;
604 }
605 ret = MakeRelativePath(input_full, dest_full);
606 if (remove_slash && ret.size() > 1)
607 ret.resize(ret.size() - 1);
608 return ret;
609 }
610
611 ret = MakeRelativePath(input, dest_dir.value());
612 return ret;
613}
614
[email protected]05b94b432013-11-22 22:09:55615std::string DirectoryWithNoLastSlash(const SourceDir& dir) {
616 std::string ret;
617
618 if (dir.value().empty()) {
619 // Just keep input the same.
620 } else if (dir.value() == "/") {
621 ret.assign("/.");
622 } else if (dir.value() == "//") {
623 ret.assign("//.");
624 } else {
625 ret.assign(dir.value());
626 ret.resize(ret.size() - 1);
627 }
628 return ret;
629}
630
[email protected]a623db1872014-02-19 19:11:17631SourceDir SourceDirForPath(const base::FilePath& source_root,
632 const base::FilePath& path) {
633 std::vector<base::FilePath::StringType> source_comp =
634 GetPathComponents(source_root);
635 std::vector<base::FilePath::StringType> path_comp =
636 GetPathComponents(path);
637
638 // See if path is inside the source root by looking for each of source root's
639 // components at the beginning of path.
640 bool is_inside_source;
scottmg865b9bb2014-12-02 10:48:29641 if (path_comp.size() < source_comp.size() || source_root.empty()) {
[email protected]a623db1872014-02-19 19:11:17642 // Too small to fit.
643 is_inside_source = false;
644 } else {
645 is_inside_source = true;
646 for (size_t i = 0; i < source_comp.size(); i++) {
647 if (!FilesystemStringsEqual(source_comp[i], path_comp[i])) {
648 is_inside_source = false;
649 break;
650 }
651 }
652 }
653
654 std::string result_str;
655 size_t initial_path_comp_to_use;
656 if (is_inside_source) {
657 // Construct a source-relative path beginning in // and skip all of the
658 // shared directories.
659 result_str = "//";
660 initial_path_comp_to_use = source_comp.size();
661 } else {
662 // Not inside source code, construct a system-absolute path.
663 result_str = "/";
664 initial_path_comp_to_use = 0;
665 }
666
667 for (size_t i = initial_path_comp_to_use; i < path_comp.size(); i++) {
668 result_str.append(FilePathToUTF8(path_comp[i]));
669 result_str.push_back('/');
670 }
671 return SourceDir(result_str);
672}
673
674SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root) {
675 base::FilePath cd;
[email protected]37b3c1992014-03-11 20:59:02676 base::GetCurrentDirectory(&cd);
[email protected]a623db1872014-02-19 19:11:17677 return SourceDirForPath(source_root, cd);
678}
679
[email protected]7380ca72014-05-13 16:56:20680std::string GetOutputSubdirName(const Label& toolchain_label, bool is_default) {
681 // The default toolchain has no subdir.
682 if (is_default)
683 return std::string();
684
685 // For now just assume the toolchain name is always a valid dir name. We may
686 // want to clean up the in the future.
687 return toolchain_label.name() + "/";
688}
689
tmoniuszko36deb392016-02-19 11:16:42690bool ContentsEqual(const base::FilePath& file_path, const std::string& data) {
691 // Compare file and stream sizes first. Quick and will save us some time if
692 // they are different sizes.
693 int64_t file_size;
694 if (!base::GetFileSize(file_path, &file_size) ||
695 static_cast<size_t>(file_size) != data.size()) {
696 return false;
697 }
698
699 std::string file_data;
700 file_data.resize(file_size);
701 if (!base::ReadFileToString(file_path, &file_data))
702 return false;
703
704 return file_data == data;
705}
706
707bool WriteFileIfChanged(const base::FilePath& file_path,
708 const std::string& data,
709 Err* err) {
710 if (ContentsEqual(file_path, data))
711 return true;
712
713 // Create the directory if necessary.
714 if (!base::CreateDirectory(file_path.DirName())) {
715 if (err) {
716 *err =
717 Err(Location(), "Unable to create directory.",
718 "I was using \"" + FilePathToUTF8(file_path.DirName()) + "\".");
719 }
720 return false;
721 }
722
723 int size = static_cast<int>(data.size());
724 bool write_success = false;
725
726#if defined(OS_WIN)
727 // On Windows, provide a custom implementation of base::WriteFile. Sometimes
728 // the base version fails, especially on the bots. The guess is that Windows
729 // Defender or other antivirus programs still have the file open (after
730 // checking for the read) when the write happens immediately after. This
731 // version opens with FILE_SHARE_READ (normally not what you want when
732 // replacing the entire contents of the file) which lets us continue even if
733 // another program has the file open for reading. See http://crbug.com/468437
734 base::win::ScopedHandle file(::CreateFile(file_path.value().c_str(),
735 GENERIC_WRITE, FILE_SHARE_READ,
736 NULL, CREATE_ALWAYS, 0, NULL));
737 if (file.IsValid()) {
738 DWORD written;
739 BOOL result = ::WriteFile(file.Get(), data.c_str(), size, &written, NULL);
740 if (result) {
741 if (static_cast<int>(written) == size) {
742 write_success = true;
743 } else {
744 // Didn't write all the bytes.
745 LOG(ERROR) << "wrote" << written << " bytes to "
746 << base::UTF16ToUTF8(file_path.value()) << " expected "
747 << size;
748 }
749 } else {
750 // WriteFile failed.
751 PLOG(ERROR) << "writing file " << base::UTF16ToUTF8(file_path.value())
752 << " failed";
753 }
754 } else {
755 PLOG(ERROR) << "CreateFile failed for path "
756 << base::UTF16ToUTF8(file_path.value());
757 }
758#else
759 write_success = base::WriteFile(file_path, data.c_str(), size) == size;
760#endif
761
762 if (!write_success && err) {
763 *err = Err(Location(), "Unable to write file.",
764 "I was writing \"" + FilePathToUTF8(file_path) + "\".");
765 }
766
767 return write_success;
768}
769
[email protected]05b94b432013-11-22 22:09:55770SourceDir GetToolchainOutputDir(const Settings* settings) {
[email protected]0dfcae72014-08-19 22:52:16771 return settings->toolchain_output_subdir().AsSourceDir(
772 settings->build_settings());
[email protected]05b94b432013-11-22 22:09:55773}
774
[email protected]7380ca72014-05-13 16:56:20775SourceDir GetToolchainOutputDir(const BuildSettings* build_settings,
776 const Label& toolchain_label, bool is_default) {
777 std::string result = build_settings->build_dir().value();
778 result.append(GetOutputSubdirName(toolchain_label, is_default));
779 return SourceDir(SourceDir::SWAP_IN, &result);
780}
781
[email protected]05b94b432013-11-22 22:09:55782SourceDir GetToolchainGenDir(const Settings* settings) {
[email protected]0dfcae72014-08-19 22:52:16783 return GetToolchainGenDirAsOutputFile(settings).AsSourceDir(
784 settings->build_settings());
785}
[email protected]05b94b432013-11-22 22:09:55786
[email protected]0dfcae72014-08-19 22:52:16787OutputFile GetToolchainGenDirAsOutputFile(const Settings* settings) {
788 OutputFile result(settings->toolchain_output_subdir());
789 result.value().append("gen/");
790 return result;
[email protected]05b94b432013-11-22 22:09:55791}
792
[email protected]7380ca72014-05-13 16:56:20793SourceDir GetToolchainGenDir(const BuildSettings* build_settings,
794 const Label& toolchain_label, bool is_default) {
795 std::string result = GetToolchainOutputDir(
796 build_settings, toolchain_label, is_default).value();
797 result.append("gen/");
798 return SourceDir(SourceDir::SWAP_IN, &result);
799}
800
[email protected]05b94b432013-11-22 22:09:55801SourceDir GetOutputDirForSourceDir(const Settings* settings,
802 const SourceDir& source_dir) {
brettw6aeaa45a2015-08-25 23:43:27803 return GetOutputDirForSourceDir(
804 settings->build_settings(), source_dir,
805 settings->toolchain_label(), settings->is_default());
[email protected]0dfcae72014-08-19 22:52:16806}
[email protected]05b94b432013-11-22 22:09:55807
tsniatowski5b6813c02015-10-26 06:32:18808void AppendFixedAbsolutePathSuffix(const BuildSettings* build_settings,
809 const SourceDir& source_dir,
810 OutputFile* result) {
811 const std::string& build_dir = build_settings->build_dir().value();
812
813 if (base::StartsWith(source_dir.value(), build_dir,
814 base::CompareCase::SENSITIVE)) {
815 size_t build_dir_size = build_dir.size();
816 result->value().append(&source_dir.value()[build_dir_size],
817 source_dir.value().size() - build_dir_size);
818 } else {
819 result->value().append("ABS_PATH");
820#if defined(OS_WIN)
821 // Windows absolute path contains ':' after drive letter. Remove it to
822 // avoid inserting ':' in the middle of path (eg. "ABS_PATH/C:/").
823 std::string src_dir_value = source_dir.value();
824 const auto colon_pos = src_dir_value.find(':');
825 if (colon_pos != std::string::npos)
826 src_dir_value.erase(src_dir_value.begin() + colon_pos);
827#else
828 const std::string& src_dir_value = source_dir.value();
829#endif
830 result->value().append(src_dir_value);
831 }
832}
833
brettw6aeaa45a2015-08-25 23:43:27834SourceDir GetOutputDirForSourceDir(
835 const BuildSettings* build_settings,
836 const SourceDir& source_dir,
837 const Label& toolchain_label,
838 bool is_default_toolchain) {
839 return GetOutputDirForSourceDirAsOutputFile(
840 build_settings, source_dir, toolchain_label, is_default_toolchain)
841 .AsSourceDir(build_settings);
842}
843
844OutputFile GetOutputDirForSourceDirAsOutputFile(
845 const BuildSettings* build_settings,
846 const SourceDir& source_dir,
847 const Label& toolchain_label,
848 bool is_default_toolchain) {
849 OutputFile result(GetOutputSubdirName(toolchain_label, is_default_toolchain));
[email protected]0dfcae72014-08-19 22:52:16850 result.value().append("obj/");
[email protected]05b94b432013-11-22 22:09:55851
[email protected]e9bf4fc2014-06-19 16:50:52852 if (source_dir.is_source_absolute()) {
853 // The source dir is source-absolute, so we trim off the two leading
854 // slashes to append to the toolchain object directory.
[email protected]0dfcae72014-08-19 22:52:16855 result.value().append(&source_dir.value()[2],
856 source_dir.value().size() - 2);
zeuthen6f9c64562014-11-11 21:01:13857 } else {
brettw3dab5fe2015-06-29 23:00:15858 // System-absolute.
tsniatowski5b6813c02015-10-26 06:32:18859 AppendFixedAbsolutePathSuffix(build_settings, source_dir, &result);
[email protected]e9bf4fc2014-06-19 16:50:52860 }
[email protected]0dfcae72014-08-19 22:52:16861 return result;
[email protected]05b94b432013-11-22 22:09:55862}
863
brettw6aeaa45a2015-08-25 23:43:27864OutputFile GetOutputDirForSourceDirAsOutputFile(const Settings* settings,
865 const SourceDir& source_dir) {
866 return GetOutputDirForSourceDirAsOutputFile(
867 settings->build_settings(), source_dir,
868 settings->toolchain_label(), settings->is_default());
869}
870
[email protected]05b94b432013-11-22 22:09:55871SourceDir GetGenDirForSourceDir(const Settings* settings,
872 const SourceDir& source_dir) {
[email protected]0dfcae72014-08-19 22:52:16873 return GetGenDirForSourceDirAsOutputFile(settings, source_dir).AsSourceDir(
874 settings->build_settings());
875}
[email protected]05b94b432013-11-22 22:09:55876
[email protected]0dfcae72014-08-19 22:52:16877OutputFile GetGenDirForSourceDirAsOutputFile(const Settings* settings,
878 const SourceDir& source_dir) {
879 OutputFile result = GetToolchainGenDirAsOutputFile(settings);
[email protected]05b94b432013-11-22 22:09:55880
[email protected]e9bf4fc2014-06-19 16:50:52881 if (source_dir.is_source_absolute()) {
882 // The source dir should be source-absolute, so we trim off the two leading
883 // slashes to append to the toolchain object directory.
884 DCHECK(source_dir.is_source_absolute());
[email protected]0dfcae72014-08-19 22:52:16885 result.value().append(&source_dir.value()[2],
886 source_dir.value().size() - 2);
tsniatowski5b6813c02015-10-26 06:32:18887 } else {
888 // System-absolute.
889 AppendFixedAbsolutePathSuffix(settings->build_settings(), source_dir,
890 &result);
[email protected]e9bf4fc2014-06-19 16:50:52891 }
[email protected]0dfcae72014-08-19 22:52:16892 return result;
[email protected]05b94b432013-11-22 22:09:55893}
894
895SourceDir GetTargetOutputDir(const Target* target) {
[email protected]0dfcae72014-08-19 22:52:16896 return GetOutputDirForSourceDirAsOutputFile(
897 target->settings(), target->label().dir()).AsSourceDir(
898 target->settings()->build_settings());
899}
900
901OutputFile GetTargetOutputDirAsOutputFile(const Target* target) {
902 return GetOutputDirForSourceDirAsOutputFile(
903 target->settings(), target->label().dir());
[email protected]05b94b432013-11-22 22:09:55904}
905
906SourceDir GetTargetGenDir(const Target* target) {
[email protected]0dfcae72014-08-19 22:52:16907 return GetTargetGenDirAsOutputFile(target).AsSourceDir(
908 target->settings()->build_settings());
909}
910
911OutputFile GetTargetGenDirAsOutputFile(const Target* target) {
912 return GetGenDirForSourceDirAsOutputFile(
913 target->settings(), target->label().dir());
[email protected]05b94b432013-11-22 22:09:55914}
915
916SourceDir GetCurrentOutputDir(const Scope* scope) {
[email protected]0dfcae72014-08-19 22:52:16917 return GetOutputDirForSourceDirAsOutputFile(
918 scope->settings(), scope->GetSourceDir()).AsSourceDir(
919 scope->settings()->build_settings());
[email protected]05b94b432013-11-22 22:09:55920}
921
922SourceDir GetCurrentGenDir(const Scope* scope) {
923 return GetGenDirForSourceDir(scope->settings(), scope->GetSourceDir());
924}