| Index: tools/gn/filesystem_utils.cc
|
| diff --git a/tools/gn/filesystem_utils.cc b/tools/gn/filesystem_utils.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..625a358a791e6247b7f4f516edce83dddfb08ff5
|
| --- /dev/null
|
| +++ b/tools/gn/filesystem_utils.cc
|
| @@ -0,0 +1,350 @@
|
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "tools/gn/filesystem_utils.h"
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/strings/utf_string_conversions.h"
|
| +#include "build/build_config.h"
|
| +#include "tools/gn/location.h"
|
| +#include "tools/gn/source_dir.h"
|
| +
|
| +namespace {
|
| +
|
| +enum DotDisposition {
|
| + // The given dot is just part of a filename and is not special.
|
| + NOT_A_DIRECTORY,
|
| +
|
| + // The given dot is the current directory.
|
| + DIRECTORY_CUR,
|
| +
|
| + // The given dot is the first of a double dot that should take us up one.
|
| + DIRECTORY_UP
|
| +};
|
| +
|
| +// When we find a dot, this function is called with the character following
|
| +// that dot to see what it is. The return value indicates what type this dot is
|
| +// (see above). This code handles the case where the dot is at the end of the
|
| +// input.
|
| +//
|
| +// |*consumed_len| will contain the number of characters in the input that
|
| +// express what we found.
|
| +DotDisposition ClassifyAfterDot(const std::string& path,
|
| + size_t after_dot,
|
| + size_t* consumed_len) {
|
| + if (after_dot == path.size()) {
|
| + // Single dot at the end.
|
| + *consumed_len = 1;
|
| + return DIRECTORY_CUR;
|
| + }
|
| + if (path[after_dot] == '/') {
|
| + // Single dot followed by a slash.
|
| + *consumed_len = 2; // Consume the slash
|
| + return DIRECTORY_CUR;
|
| + }
|
| +
|
| + if (path[after_dot] == '.') {
|
| + // Two dots.
|
| + if (after_dot + 1 == path.size()) {
|
| + // Double dot at the end.
|
| + *consumed_len = 2;
|
| + return DIRECTORY_UP;
|
| + }
|
| + if (path[after_dot + 1] == '/') {
|
| + // Double dot folowed by a slash.
|
| + *consumed_len = 3;
|
| + return DIRECTORY_UP;
|
| + }
|
| + }
|
| +
|
| + // The dots are followed by something else, not a directory.
|
| + *consumed_len = 1;
|
| + return NOT_A_DIRECTORY;
|
| +}
|
| +
|
| +} // namesapce
|
| +
|
| +SourceFileType GetSourceFileType(const SourceFile& file,
|
| + Settings::TargetOS os) {
|
| + base::StringPiece extension = FindExtension(&file.value());
|
| + if (extension == "cc" || extension == "cpp" || extension == "cxx")
|
| + return SOURCE_CC;
|
| + if (extension == "h")
|
| + return SOURCE_H;
|
| + if (extension == "c")
|
| + return SOURCE_C;
|
| +
|
| + switch (os) {
|
| + case Settings::MAC:
|
| + if (extension == "m")
|
| + return SOURCE_M;
|
| + if (extension == "mm")
|
| + return SOURCE_MM;
|
| + break;
|
| +
|
| + case Settings::WIN:
|
| + if (extension == "rc")
|
| + return SOURCE_RC;
|
| + break;
|
| +
|
| + default:
|
| + break;
|
| + }
|
| +
|
| + // TODO(brettw) asm files.
|
| + // TODO(brettw) weird thing with .S on non-Windows platforms.
|
| + return SOURCE_UNKNOWN;
|
| +}
|
| +
|
| +const char* GetExtensionForOutputType(Target::OutputType type,
|
| + Settings::TargetOS os) {
|
| + switch (os) {
|
| + case Settings::WIN:
|
| + switch (type) {
|
| + case Target::NONE:
|
| + NOTREACHED();
|
| + return "";
|
| + case Target::EXECUTABLE:
|
| + return "exe";
|
| + case Target::SHARED_LIBRARY:
|
| + return "dll.lib"; // Extension of import library.
|
| + case Target::STATIC_LIBRARY:
|
| + return "lib";
|
| + case Target::LOADABLE_MODULE:
|
| + return "dll"; // TODO(brettw) what's this?
|
| + default:
|
| + NOTREACHED();
|
| + }
|
| + break;
|
| +
|
| + default:
|
| + NOTREACHED();
|
| + }
|
| + return "";
|
| +}
|
| +
|
| +std::string FilePathToUTF8(const base::FilePath& path) {
|
| +#if defined(OS_WIN)
|
| + return WideToUTF8(path.value());
|
| +#else
|
| + return path.value();
|
| +#endif
|
| +}
|
| +
|
| +base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
|
| +#if defined(OS_WIN)
|
| + return base::FilePath(UTF8ToWide(sp));
|
| +#else
|
| + return base::FilePath(sp.as_string());
|
| +#endif
|
| +}
|
| +
|
| +size_t FindExtensionOffset(const std::string& path) {
|
| + for (int i = static_cast<int>(path.size()); i >= 0; i--) {
|
| + if (path[i] == '/')
|
| + break;
|
| + if (path[i] == '.')
|
| + return i + 1;
|
| + }
|
| + return std::string::npos;
|
| +}
|
| +
|
| +base::StringPiece FindExtension(const std::string* path) {
|
| + size_t extension_offset = FindExtensionOffset(*path);
|
| + if (extension_offset == std::string::npos)
|
| + return base::StringPiece();
|
| + return base::StringPiece(&path->data()[extension_offset],
|
| + path->size() - extension_offset);
|
| +}
|
| +
|
| +size_t FindFilenameOffset(const std::string& path) {
|
| + for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) {
|
| + if (path[i] == '/')
|
| + return i + 1;
|
| + }
|
| + return 0; // No filename found means everything was the filename.
|
| +}
|
| +
|
| +base::StringPiece FindFilename(const std::string* path) {
|
| + size_t filename_offset = FindFilenameOffset(*path);
|
| + if (filename_offset == 0)
|
| + return base::StringPiece(*path); // Everything is the file name.
|
| + return base::StringPiece(&(*path).data()[filename_offset],
|
| + path->size() - filename_offset);
|
| +}
|
| +
|
| +base::StringPiece FindFilenameNoExtension(const std::string* path) {
|
| + if (path->empty())
|
| + return base::StringPiece();
|
| + size_t filename_offset = FindFilenameOffset(*path);
|
| + size_t extension_offset = FindExtensionOffset(*path);
|
| +
|
| + size_t name_len;
|
| + if (extension_offset == std::string::npos)
|
| + name_len = path->size() - filename_offset;
|
| + else
|
| + name_len = extension_offset - filename_offset - 1;
|
| +
|
| + return base::StringPiece(&(*path).data()[filename_offset], name_len);
|
| +}
|
| +
|
| +void RemoveFilename(std::string* path) {
|
| + path->resize(FindFilenameOffset(*path));
|
| +}
|
| +
|
| +bool EndsWithSlash(const std::string& s) {
|
| + return !s.empty() && s[s.size() - 1] == '/';
|
| +}
|
| +
|
| +base::StringPiece FindDir(const std::string* path) {
|
| + size_t filename_offset = FindFilenameOffset(*path);
|
| + if (filename_offset == 0u)
|
| + return base::StringPiece();
|
| + return base::StringPiece(path->data(), filename_offset);
|
| +}
|
| +
|
| +bool EnsureStringIsInOutputDir(const SourceDir& dir,
|
| + const std::string& str,
|
| + const Value& originating,
|
| + Err* err) {
|
| + // The last char of the dir will be a slash. We don't care if the input ends
|
| + // in a slash or not, so just compare up until there.
|
| + //
|
| + // This check will be wrong for all proper prefixes "e.g. "/output" will
|
| + // match "/out" but we don't really care since this is just a sanity check.
|
| + const std::string& dir_str = dir.value();
|
| + if (str.compare(0, dir_str.length() - 1, dir_str, 0, dir_str.length() - 1)
|
| + != 0) {
|
| + *err = Err(originating, "File not inside output directory.",
|
| + "The given file should be in the output directory. Normally you would "
|
| + "specify\n\"$target_output_dir/foo\" or "
|
| + "\"$target_gen_dir/foo\". I interpreted this as\n\""
|
| + + str + "\".");
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +std::string InvertDir(const SourceDir& path) {
|
| + const std::string value = path.value();
|
| + if (value.empty())
|
| + return std::string();
|
| +
|
| + DCHECK(value[0] == '/');
|
| + size_t begin_index = 1;
|
| +
|
| + // If the input begins with two slashes, skip over both (this is a
|
| + // source-relative dir).
|
| + if (value.size() > 1 && value[1] == '/')
|
| + begin_index = 2;
|
| +
|
| + std::string ret;
|
| + for (size_t i = begin_index; i < value.size(); i++) {
|
| + if (value[i] == '/')
|
| + ret.append("../");
|
| + }
|
| + return ret;
|
| +}
|
| +
|
| +void NormalizePath(std::string* path) {
|
| + char* pathbuf = path->empty() ? NULL : &(*path)[0];
|
| +
|
| + // top_index is the first character we can modify in the path. Anything
|
| + // before this indicates where the path is relative to.
|
| + size_t top_index = 0;
|
| + bool is_relative = true;
|
| + if (!path->empty() && pathbuf[0] == '/') {
|
| + is_relative = false;
|
| +
|
| + if (path->size() > 1 && pathbuf[1] == '/') {
|
| + // Two leading slashes, this is a path into the source dir.
|
| + top_index = 2;
|
| + } else {
|
| + // One leading slash, this is a system-absolute path.
|
| + top_index = 1;
|
| + }
|
| + }
|
| +
|
| + size_t dest_i = top_index;
|
| + for (size_t src_i = top_index; src_i < path->size(); /* nothing */) {
|
| + if (pathbuf[src_i] == '.') {
|
| + if (src_i == 0 || pathbuf[src_i - 1] == '/') {
|
| + // Slash followed by a dot, see if it's something special.
|
| + size_t consumed_len;
|
| + switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
|
| + case NOT_A_DIRECTORY:
|
| + // Copy the dot to the output, it means nothing special.
|
| + pathbuf[dest_i++] = pathbuf[src_i++];
|
| + break;
|
| + case DIRECTORY_CUR:
|
| + // Current directory, just skip the input.
|
| + src_i += consumed_len;
|
| + break;
|
| + case DIRECTORY_UP:
|
| + // Back up over previous directory component. If we're already
|
| + // at the top, preserve the "..".
|
| + if (dest_i > top_index) {
|
| + // The previous char was a slash, remove it.
|
| + dest_i--;
|
| + }
|
| +
|
| + if (dest_i == top_index) {
|
| + if (is_relative) {
|
| + // We're already at the beginning of a relative input, copy the
|
| + // ".." and continue. We need the trailing slash if there was
|
| + // one before (otherwise we're at the end of the input).
|
| + pathbuf[dest_i++] = '.';
|
| + pathbuf[dest_i++] = '.';
|
| + if (consumed_len == 3)
|
| + pathbuf[dest_i++] = '/';
|
| +
|
| + // This also makes a new "root" that we can't delete by going
|
| + // up more levels. Otherwise "../.." would collapse to
|
| + // nothing.
|
| + top_index = dest_i;
|
| + }
|
| + // Otherwise we're at the beginning of an absolute path. Don't
|
| + // allow ".." to go up another level and just eat it.
|
| + } else {
|
| + // Just find the previous slash or the beginning of input.
|
| + while (dest_i > 0 && pathbuf[dest_i - 1] != '/')
|
| + dest_i--;
|
| + }
|
| + src_i += consumed_len;
|
| + }
|
| + } else {
|
| + // Dot not preceeded by a slash, copy it literally.
|
| + pathbuf[dest_i++] = pathbuf[src_i++];
|
| + }
|
| + } else if (pathbuf[src_i] == '/') {
|
| + if (src_i > 0 && pathbuf[src_i - 1] == '/') {
|
| + // Two slashes in a row, skip over it.
|
| + src_i++;
|
| + } else {
|
| + // Just one slash, copy it.
|
| + pathbuf[dest_i++] = pathbuf[src_i++];
|
| + }
|
| + } else {
|
| + // Input nothing special, just copy it.
|
| + pathbuf[dest_i++] = pathbuf[src_i++];
|
| + }
|
| + }
|
| + path->resize(dest_i);
|
| +}
|
| +
|
| +void ConvertPathToSystem(std::string* path) {
|
| +#if defined(OS_WIN)
|
| + for (size_t i = 0; i < path->size(); i++) {
|
| + if ((*path)[i] == '/')
|
| + (*path)[i] = '\\';
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +std::string PathToSystem(const std::string& path) {
|
| + std::string ret(path);
|
| + ConvertPathToSystem(&ret);
|
| + return ret;
|
| +}
|
| +
|
|
|