mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-28 08:16:09 +03:00
296 lines
10 KiB
C++
296 lines
10 KiB
C++
|
// 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 <stddef.h>
|
||
|
|
||
|
#include "tools/gn/build_settings.h"
|
||
|
#include "tools/gn/filesystem_utils.h"
|
||
|
#include "tools/gn/functions.h"
|
||
|
#include "tools/gn/parse_tree.h"
|
||
|
#include "tools/gn/scope.h"
|
||
|
#include "tools/gn/settings.h"
|
||
|
#include "tools/gn/source_dir.h"
|
||
|
#include "tools/gn/source_file.h"
|
||
|
#include "tools/gn/value.h"
|
||
|
|
||
|
namespace functions {
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
// We want the output to match the input in terms of ending in a slash or not.
|
||
|
// Through all the transformations, these can get added or removed in various
|
||
|
// cases.
|
||
|
void MakeSlashEndingMatchInput(const std::string& input, std::string* output) {
|
||
|
if (EndsWithSlash(input)) {
|
||
|
if (!EndsWithSlash(*output)) // Preserve same slash type as input.
|
||
|
output->push_back(input[input.size() - 1]);
|
||
|
} else {
|
||
|
if (EndsWithSlash(*output))
|
||
|
output->resize(output->size() - 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Returns true if the given value looks like a directory, otherwise we'll
|
||
|
// assume it's a file.
|
||
|
bool ValueLooksLikeDir(const std::string& value) {
|
||
|
if (value.empty())
|
||
|
return true;
|
||
|
size_t value_size = value.size();
|
||
|
|
||
|
// Count the number of dots at the end of the string.
|
||
|
size_t num_dots = 0;
|
||
|
while (num_dots < value_size && value[value_size - num_dots - 1] == '.')
|
||
|
num_dots++;
|
||
|
|
||
|
if (num_dots == value.size())
|
||
|
return true; // String is all dots.
|
||
|
|
||
|
if (IsSlash(value[value_size - num_dots - 1]))
|
||
|
return true; // String is a [back]slash followed by 0 or more dots.
|
||
|
|
||
|
// Anything else.
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
Value ConvertOnePath(const Scope* scope,
|
||
|
const FunctionCallNode* function,
|
||
|
const Value& value,
|
||
|
const SourceDir& from_dir,
|
||
|
const SourceDir& to_dir,
|
||
|
bool convert_to_system_absolute,
|
||
|
Err* err) {
|
||
|
Value result; // Ensure return value optimization.
|
||
|
|
||
|
if (!value.VerifyTypeIs(Value::STRING, err))
|
||
|
return result;
|
||
|
const std::string& string_value = value.string_value();
|
||
|
|
||
|
bool looks_like_dir = ValueLooksLikeDir(string_value);
|
||
|
|
||
|
// System-absolute output special case.
|
||
|
if (convert_to_system_absolute) {
|
||
|
base::FilePath system_path;
|
||
|
if (looks_like_dir) {
|
||
|
system_path = scope->settings()->build_settings()->GetFullPath(
|
||
|
from_dir.ResolveRelativeDir(value, err,
|
||
|
scope->settings()->build_settings()->root_path_utf8()));
|
||
|
} else {
|
||
|
system_path = scope->settings()->build_settings()->GetFullPath(
|
||
|
from_dir.ResolveRelativeFile(value, err,
|
||
|
scope->settings()->build_settings()->root_path_utf8()));
|
||
|
}
|
||
|
if (err->has_error())
|
||
|
return Value();
|
||
|
|
||
|
result = Value(function, FilePathToUTF8(system_path));
|
||
|
if (looks_like_dir)
|
||
|
MakeSlashEndingMatchInput(string_value, &result.string_value());
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
result = Value(function, Value::STRING);
|
||
|
if (looks_like_dir) {
|
||
|
result.string_value() = RebasePath(
|
||
|
from_dir.ResolveRelativeDir(value, err,
|
||
|
scope->settings()->build_settings()->root_path_utf8()).value(),
|
||
|
to_dir,
|
||
|
scope->settings()->build_settings()->root_path_utf8());
|
||
|
MakeSlashEndingMatchInput(string_value, &result.string_value());
|
||
|
} else {
|
||
|
SourceFile resolved_file =
|
||
|
from_dir.ResolveRelativeFile(value, err,
|
||
|
scope->settings()->build_settings()->root_path_utf8());
|
||
|
if (err->has_error())
|
||
|
return Value();
|
||
|
// Special case:
|
||
|
// rebase_path("//foo", "//bar") ==> "../foo"
|
||
|
// rebase_path("//foo", "//foo") ==> "." and not "../foo"
|
||
|
if (resolved_file.value() ==
|
||
|
to_dir.value().substr(0, to_dir.value().size() - 1)) {
|
||
|
result.string_value() = ".";
|
||
|
} else {
|
||
|
result.string_value() = RebasePath(
|
||
|
resolved_file.value(),
|
||
|
to_dir,
|
||
|
scope->settings()->build_settings()->root_path_utf8());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
const char kRebasePath[] = "rebase_path";
|
||
|
const char kRebasePath_HelpShort[] =
|
||
|
"rebase_path: Rebase a file or directory to another location.";
|
||
|
const char kRebasePath_Help[] =
|
||
|
R"(rebase_path: Rebase a file or directory to another location.
|
||
|
|
||
|
converted = rebase_path(input,
|
||
|
new_base = "",
|
||
|
current_base = ".")
|
||
|
|
||
|
Takes a string argument representing a file name, or a list of such strings
|
||
|
and converts it/them to be relative to a different base directory.
|
||
|
|
||
|
When invoking the compiler or scripts, GN will automatically convert sources
|
||
|
and include directories to be relative to the build directory. However, if
|
||
|
you're passing files directly in the "args" array or doing other manual
|
||
|
manipulations where GN doesn't know something is a file name, you will need
|
||
|
to convert paths to be relative to what your tool is expecting.
|
||
|
|
||
|
The common case is to use this to convert paths relative to the current
|
||
|
directory to be relative to the build directory (which will be the current
|
||
|
directory when executing scripts).
|
||
|
|
||
|
If you want to convert a file path to be source-absolute (that is, beginning
|
||
|
with a double slash like "//foo/bar"), you should use the get_path_info()
|
||
|
function. This function won't work because it will always make relative
|
||
|
paths, and it needs to support making paths relative to the source root, so
|
||
|
can't also generate source-absolute paths without more special-cases.
|
||
|
|
||
|
Arguments
|
||
|
|
||
|
input
|
||
|
A string or list of strings representing file or directory names These
|
||
|
can be relative paths ("foo/bar.txt"), system absolute paths
|
||
|
("/foo/bar.txt"), or source absolute paths ("//foo/bar.txt").
|
||
|
|
||
|
new_base
|
||
|
The directory to convert the paths to be relative to. This can be an
|
||
|
absolute path or a relative path (which will be treated as being relative
|
||
|
to the current BUILD-file's directory).
|
||
|
|
||
|
As a special case, if new_base is the empty string (the default), all
|
||
|
paths will be converted to system-absolute native style paths with system
|
||
|
path separators. This is useful for invoking external programs.
|
||
|
|
||
|
current_base
|
||
|
Directory representing the base for relative paths in the input. If this
|
||
|
is not an absolute path, it will be treated as being relative to the
|
||
|
current build file. Use "." (the default) to convert paths from the
|
||
|
current BUILD-file's directory.
|
||
|
|
||
|
Return value
|
||
|
|
||
|
The return value will be the same type as the input value (either a string or
|
||
|
a list of strings). All relative and source-absolute file names will be
|
||
|
converted to be relative to the requested output System-absolute paths will
|
||
|
be unchanged.
|
||
|
|
||
|
Whether an output path will end in a slash will match whether the
|
||
|
corresponding input path ends in a slash. It will return "." or "./"
|
||
|
(depending on whether the input ends in a slash) to avoid returning empty
|
||
|
strings. This means if you want a root path ("//" or "/") not ending in a
|
||
|
slash, you can add a dot ("//.").
|
||
|
|
||
|
Example
|
||
|
|
||
|
# Convert a file in the current directory to be relative to the build
|
||
|
# directory (the current dir when executing compilers and scripts).
|
||
|
foo = rebase_path("myfile.txt", root_build_dir)
|
||
|
# might produce "../../project/myfile.txt".
|
||
|
|
||
|
# Convert a file to be system absolute:
|
||
|
foo = rebase_path("myfile.txt")
|
||
|
# Might produce "D:\\source\\project\\myfile.txt" on Windows or
|
||
|
# "/home/you/source/project/myfile.txt" on Linux.
|
||
|
|
||
|
# Typical usage for converting to the build directory for a script.
|
||
|
action("myscript") {
|
||
|
# Don't convert sources, GN will automatically convert these to be relative
|
||
|
# to the build directory when it constructs the command line for your
|
||
|
# script.
|
||
|
sources = [ "foo.txt", "bar.txt" ]
|
||
|
|
||
|
# Extra file args passed manually need to be explicitly converted
|
||
|
# to be relative to the build directory:
|
||
|
args = [
|
||
|
"--data",
|
||
|
rebase_path("//mything/data/input.dat", root_build_dir),
|
||
|
"--rel",
|
||
|
rebase_path("relative_path.txt", root_build_dir)
|
||
|
] + rebase_path(sources, root_build_dir)
|
||
|
}
|
||
|
)";
|
||
|
|
||
|
Value RunRebasePath(Scope* scope,
|
||
|
const FunctionCallNode* function,
|
||
|
const std::vector<Value>& args,
|
||
|
Err* err) {
|
||
|
Value result;
|
||
|
|
||
|
// Argument indices.
|
||
|
static const size_t kArgIndexInputs = 0;
|
||
|
static const size_t kArgIndexDest = 1;
|
||
|
static const size_t kArgIndexFrom = 2;
|
||
|
|
||
|
// Inputs.
|
||
|
if (args.size() < 1 || args.size() > 3) {
|
||
|
*err = Err(function->function(), "Wrong # of arguments for rebase_path.");
|
||
|
return result;
|
||
|
}
|
||
|
const Value& inputs = args[kArgIndexInputs];
|
||
|
|
||
|
// To path.
|
||
|
bool convert_to_system_absolute = true;
|
||
|
SourceDir to_dir;
|
||
|
const SourceDir& current_dir = scope->GetSourceDir();
|
||
|
if (args.size() > kArgIndexDest) {
|
||
|
if (!args[kArgIndexDest].VerifyTypeIs(Value::STRING, err))
|
||
|
return result;
|
||
|
if (!args[kArgIndexDest].string_value().empty()) {
|
||
|
to_dir = current_dir.ResolveRelativeDir(
|
||
|
args[kArgIndexDest], err,
|
||
|
scope->settings()->build_settings()->root_path_utf8());
|
||
|
if (err->has_error())
|
||
|
return Value();
|
||
|
convert_to_system_absolute = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// From path.
|
||
|
SourceDir from_dir;
|
||
|
if (args.size() > kArgIndexFrom) {
|
||
|
if (!args[kArgIndexFrom].VerifyTypeIs(Value::STRING, err))
|
||
|
return result;
|
||
|
from_dir = current_dir.ResolveRelativeDir(
|
||
|
args[kArgIndexFrom], err,
|
||
|
scope->settings()->build_settings()->root_path_utf8());
|
||
|
if (err->has_error())
|
||
|
return Value();
|
||
|
} else {
|
||
|
// Default to current directory if unspecified.
|
||
|
from_dir = current_dir;
|
||
|
}
|
||
|
|
||
|
// Path conversion.
|
||
|
if (inputs.type() == Value::STRING) {
|
||
|
return ConvertOnePath(scope, function, inputs,
|
||
|
from_dir, to_dir, convert_to_system_absolute, err);
|
||
|
|
||
|
} else if (inputs.type() == Value::LIST) {
|
||
|
result = Value(function, Value::LIST);
|
||
|
result.list_value().reserve(inputs.list_value().size());
|
||
|
|
||
|
for (const auto& input : inputs.list_value()) {
|
||
|
result.list_value().push_back(
|
||
|
ConvertOnePath(scope, function, input,
|
||
|
from_dir, to_dir, convert_to_system_absolute, err));
|
||
|
if (err->has_error()) {
|
||
|
result = Value();
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
*err = Err(function->function(),
|
||
|
"rebase_path requires a list or a string.");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
} // namespace functions
|