This source file includes following definitions.
- ClassifyAfterDot
- NormalizeWindowsPathChar
- AreAbsoluteWindowsPathsEqual
- DoesBeginWindowsDriveLetter
- GetPathComponents
- FilesystemStringsEqual
- GetSourceFileType
- GetExtensionForOutputType
- FilePathToUTF8
- UTF8ToFilePath
- FindExtensionOffset
- FindExtension
- FindFilenameOffset
- FindFilename
- FindFilenameNoExtension
- RemoveFilename
- EndsWithSlash
- FindDir
- EnsureStringIsInOutputDir
- IsPathAbsolute
- MakeAbsolutePathRelativeIfPossible
- InvertDir
- NormalizePath
- ConvertPathToSystem
- RebaseSourceAbsolutePath
- DirectoryWithNoLastSlash
- SourceDirForPath
- SourceDirForCurrentDirectory
- GetToolchainOutputDir
- GetToolchainGenDir
- GetOutputDirForSourceDir
- GetGenDirForSourceDir
- GetTargetOutputDir
- GetTargetGenDir
- GetCurrentOutputDir
- GetCurrentGenDir
#include "tools/gn/filesystem_utils.h"
#include <algorithm>
#include "base/file_util.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "tools/gn/location.h"
#include "tools/gn/settings.h"
#include "tools/gn/source_dir.h"
namespace {
enum DotDisposition {
NOT_A_DIRECTORY,
DIRECTORY_CUR,
DIRECTORY_UP
};
DotDisposition ClassifyAfterDot(const std::string& path,
size_t after_dot,
size_t* consumed_len) {
if (after_dot == path.size()) {
*consumed_len = 1;
return DIRECTORY_CUR;
}
if (IsSlash(path[after_dot])) {
*consumed_len = 2;
return DIRECTORY_CUR;
}
if (path[after_dot] == '.') {
if (after_dot + 1 == path.size()) {
*consumed_len = 2;
return DIRECTORY_UP;
}
if (IsSlash(path[after_dot + 1])) {
*consumed_len = 3;
return DIRECTORY_UP;
}
}
*consumed_len = 1;
return NOT_A_DIRECTORY;
}
#if defined(OS_WIN)
inline char NormalizeWindowsPathChar(char c) {
if (c == '/')
return '\\';
return base::ToLowerASCII(c);
}
bool AreAbsoluteWindowsPathsEqual(const base::StringPiece& a,
const base::StringPiece& b) {
if (a.size() != b.size())
return false;
for (size_t i = 0; i < a.size(); i++) {
if (NormalizeWindowsPathChar(a[i]) != NormalizeWindowsPathChar(b[i]))
return false;
}
return true;
}
bool DoesBeginWindowsDriveLetter(const base::StringPiece& path) {
if (path.size() < 3)
return false;
if (path[1] != ':')
return false;
if (!((path[0] >= 'A' && path[0] <= 'Z') ||
path[0] >= 'a' && path[0] <= 'z'))
return false;
if (!IsSlash(path[2]))
return false;
return true;
}
#endif
std::vector<base::FilePath::StringType> GetPathComponents(
const base::FilePath& path) {
std::vector<base::FilePath::StringType> result;
path.GetComponents(&result);
if (result.empty())
return result;
if (result[0] == FILE_PATH_LITERAL("/") ||
result[0] == FILE_PATH_LITERAL("\\"))
result.erase(result.begin());
#if defined(OS_WIN)
if (result.size() >= 2 && result[1].size() == 1 && IsSlash(result[1][0]))
result.erase(result.begin() + 1);
#endif
return result;
}
bool FilesystemStringsEqual(const base::FilePath::StringType& a,
const base::FilePath::StringType& b) {
#if defined(OS_WIN)
return ::CompareString(LOCALE_USER_DEFAULT, LINGUISTIC_IGNORECASE,
a.c_str(), -1, b.c_str(), -1) == CSTR_EQUAL;
#else
return a == b;
#endif
}
}
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;
}
if (os != Settings::WIN) {
if (extension == "S")
return SOURCE_S;
}
return SOURCE_UNKNOWN;
}
const char* GetExtensionForOutputType(Target::OutputType type,
Settings::TargetOS os) {
switch (os) {
case Settings::MAC:
switch (type) {
case Target::EXECUTABLE:
return "";
case Target::SHARED_LIBRARY:
return "dylib";
case Target::STATIC_LIBRARY:
return "a";
default:
NOTREACHED();
}
break;
case Settings::WIN:
switch (type) {
case Target::EXECUTABLE:
return "exe";
case Target::SHARED_LIBRARY:
return "dll.lib";
case Target::STATIC_LIBRARY:
return "lib";
default:
NOTREACHED();
}
break;
case Settings::LINUX:
switch (type) {
case Target::EXECUTABLE:
return "";
case Target::SHARED_LIBRARY:
return "so";
case Target::STATIC_LIBRARY:
return "a";
default:
NOTREACHED();
}
break;
default:
NOTREACHED();
}
return "";
}
std::string FilePathToUTF8(const base::FilePath::StringType& str) {
#if defined(OS_WIN)
return base::WideToUTF8(str);
#else
return str;
#endif
}
base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
#if defined(OS_WIN)
return base::FilePath(base::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 (IsSlash(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 (IsSlash(path[i]))
return i + 1;
}
return 0;
}
base::StringPiece FindFilename(const std::string* path) {
size_t filename_offset = FindFilenameOffset(*path);
if (filename_offset == 0)
return base::StringPiece(*path);
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() && IsSlash(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) {
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 is not inside output directory.",
"The given file should be in the output directory. Normally you would "
"specify\n\"$target_out_dir/foo\" or "
"\"$target_gen_dir/foo\". I interpreted this as\n\""
+ str + "\".");
return false;
}
return true;
}
bool IsPathAbsolute(const base::StringPiece& path) {
if (path.empty())
return false;
if (!IsSlash(path[0])) {
#if defined(OS_WIN)
if (path.size() > 2 && path[1] == ':' && IsSlash(path[2]))
return true;
#endif
return false;
}
if (path.size() > 1 && path[1] == '/')
return false;
return true;
}
bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
const base::StringPiece& path,
std::string* dest) {
DCHECK(IsPathAbsolute(source_root));
DCHECK(IsPathAbsolute(path));
dest->clear();
if (source_root.size() > path.size())
return false;
#if defined(OS_WIN)
DCHECK(source_root.size() > 2 && source_root[0] != '/' &&
source_root[1] == ':' && IsSlash(source_root[2]));
size_t after_common_index = std::string::npos;
if (DoesBeginWindowsDriveLetter(path)) {
if (AreAbsoluteWindowsPathsEqual(source_root,
path.substr(0, source_root.size())))
after_common_index = source_root.size();
else
return false;
} else if (path[0] == '/' && source_root.size() <= path.size() - 1 &&
DoesBeginWindowsDriveLetter(path.substr(1))) {
if (AreAbsoluteWindowsPathsEqual(source_root,
path.substr(1, source_root.size())))
after_common_index = source_root.size() + 1;
else
return false;
} else {
return false;
}
size_t first_after_slash = after_common_index;
while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
first_after_slash++;
dest->assign("//");
dest->append(&path.data()[first_after_slash],
path.size() - first_after_slash);
return true;
#else
if (path.substr(0, source_root.size()) == source_root) {
size_t first_after_slash = source_root.size();
while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
first_after_slash++;
dest->assign("//");
dest->append(&path.data()[first_after_slash],
path.size() - first_after_slash);
return true;
}
return false;
#endif
}
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 (value.size() > 1 && value[1] == '/')
begin_index = 2;
std::string ret;
for (size_t i = begin_index; i < value.size(); i++) {
if (IsSlash(value[i]))
ret.append("../");
}
return ret;
}
void NormalizePath(std::string* path) {
char* pathbuf = path->empty() ? NULL : &(*path)[0];
size_t top_index = 0;
bool is_relative = true;
if (!path->empty() && pathbuf[0] == '/') {
is_relative = false;
if (path->size() > 1 && pathbuf[1] == '/') {
top_index = 2;
} else {
top_index = 1;
}
}
size_t dest_i = top_index;
for (size_t src_i = top_index; src_i < path->size(); ) {
if (pathbuf[src_i] == '.') {
if (src_i == 0 || IsSlash(pathbuf[src_i - 1])) {
size_t consumed_len;
switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
case NOT_A_DIRECTORY:
pathbuf[dest_i++] = pathbuf[src_i++];
break;
case DIRECTORY_CUR:
src_i += consumed_len;
break;
case DIRECTORY_UP:
if (dest_i > top_index) {
dest_i--;
}
if (dest_i == top_index) {
if (is_relative) {
pathbuf[dest_i++] = '.';
pathbuf[dest_i++] = '.';
if (consumed_len == 3)
pathbuf[dest_i++] = '/';
top_index = dest_i;
}
} else {
while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1]))
dest_i--;
}
src_i += consumed_len;
}
} else {
pathbuf[dest_i++] = pathbuf[src_i++];
}
} else if (IsSlash(pathbuf[src_i])) {
if (src_i > 0 && IsSlash(pathbuf[src_i - 1])) {
src_i++;
} else {
pathbuf[dest_i] = '/';
dest_i++;
src_i++;
}
} else {
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 RebaseSourceAbsolutePath(const std::string& input,
const SourceDir& dest_dir) {
CHECK(input.size() >= 2 && input[0] == '/' && input[1] == '/')
<< "Input to rebase isn't source-absolute: " << input;
CHECK(dest_dir.is_source_absolute())
<< "Dir to rebase to isn't source-absolute: " << dest_dir.value();
const std::string& dest = dest_dir.value();
size_t common_prefix_len = 2;
size_t max_common_length = std::min(input.size(), dest.size());
for (size_t i = common_prefix_len; i < max_common_length; i++) {
if (IsSlash(input[i]) && IsSlash(dest[i]))
common_prefix_len = i + 1;
else if (input[i] != dest[i])
break;
}
std::string ret;
for (size_t i = common_prefix_len; i < dest.size(); i++) {
if (IsSlash(dest[i]))
ret.append("../");
}
ret.append(&input[common_prefix_len], input.size() - common_prefix_len);
if (ret.empty())
ret.push_back('.');
return ret;
}
std::string DirectoryWithNoLastSlash(const SourceDir& dir) {
std::string ret;
if (dir.value().empty()) {
} else if (dir.value() == "/") {
ret.assign("/.");
} else if (dir.value() == "//") {
ret.assign("//.");
} else {
ret.assign(dir.value());
ret.resize(ret.size() - 1);
}
return ret;
}
SourceDir SourceDirForPath(const base::FilePath& source_root,
const base::FilePath& path) {
std::vector<base::FilePath::StringType> source_comp =
GetPathComponents(source_root);
std::vector<base::FilePath::StringType> path_comp =
GetPathComponents(path);
bool is_inside_source;
if (path_comp.size() < source_comp.size()) {
is_inside_source = false;
} else {
is_inside_source = true;
for (size_t i = 0; i < source_comp.size(); i++) {
if (!FilesystemStringsEqual(source_comp[i], path_comp[i])) {
is_inside_source = false;
break;
}
}
}
std::string result_str;
size_t initial_path_comp_to_use;
if (is_inside_source) {
result_str = "//";
initial_path_comp_to_use = source_comp.size();
} else {
result_str = "/";
initial_path_comp_to_use = 0;
}
for (size_t i = initial_path_comp_to_use; i < path_comp.size(); i++) {
result_str.append(FilePathToUTF8(path_comp[i]));
result_str.push_back('/');
}
return SourceDir(result_str);
}
SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root) {
base::FilePath cd;
base::GetCurrentDirectory(&cd);
return SourceDirForPath(source_root, cd);
}
SourceDir GetToolchainOutputDir(const Settings* settings) {
const OutputFile& toolchain_subdir = settings->toolchain_output_subdir();
std::string result = settings->build_settings()->build_dir().value();
if (!toolchain_subdir.value().empty())
result.append(toolchain_subdir.value());
return SourceDir(SourceDir::SWAP_IN, &result);
}
SourceDir GetToolchainGenDir(const Settings* settings) {
const OutputFile& toolchain_subdir = settings->toolchain_output_subdir();
std::string result = settings->build_settings()->build_dir().value();
if (!toolchain_subdir.value().empty())
result.append(toolchain_subdir.value());
result.append("gen/");
return SourceDir(SourceDir::SWAP_IN, &result);
}
SourceDir GetOutputDirForSourceDir(const Settings* settings,
const SourceDir& source_dir) {
SourceDir toolchain = GetToolchainOutputDir(settings);
std::string ret;
toolchain.SwapValue(&ret);
ret.append("obj/");
DCHECK(source_dir.is_source_absolute());
ret.append(&source_dir.value()[2], source_dir.value().size() - 2);
return SourceDir(SourceDir::SWAP_IN, &ret);
}
SourceDir GetGenDirForSourceDir(const Settings* settings,
const SourceDir& source_dir) {
SourceDir toolchain = GetToolchainGenDir(settings);
std::string ret;
toolchain.SwapValue(&ret);
DCHECK(source_dir.is_source_absolute());
ret.append(&source_dir.value()[2], source_dir.value().size() - 2);
return SourceDir(SourceDir::SWAP_IN, &ret);
}
SourceDir GetTargetOutputDir(const Target* target) {
return GetOutputDirForSourceDir(target->settings(), target->label().dir());
}
SourceDir GetTargetGenDir(const Target* target) {
return GetGenDirForSourceDir(target->settings(), target->label().dir());
}
SourceDir GetCurrentOutputDir(const Scope* scope) {
return GetOutputDirForSourceDir(scope->settings(), scope->GetSourceDir());
}
SourceDir GetCurrentGenDir(const Scope* scope) {
return GetGenDirForSourceDir(scope->settings(), scope->GetSourceDir());
}