diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-23 17:59:22 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-23 17:59:22 +0000 |
commit | ea3690cf973006fd3829c7032f71d4e8e4c47402 (patch) | |
tree | daf2613a3b856cbc8e38480e4654ffa14181582e | |
parent | 7191e2211c96f02000bed9c449af72332e412ac0 (diff) | |
download | chromium_src-ea3690cf973006fd3829c7032f71d4e8e4c47402.zip chromium_src-ea3690cf973006fd3829c7032f71d4e8e4c47402.tar.gz chromium_src-ea3690cf973006fd3829c7032f71d4e8e4c47402.tar.bz2 |
Replace to_build_path() with the more general rebase_path(). Use rebase_path to (finally) make grit paths work properly.
Add more colors to help, yay! I also removed a bunch of colons from headings since they look stupid with the colors.
Add a new variable root_build_dir.
Fix some bugs in the scope_per_file_provider that didn't include the toolchain subdirectory. Add some more tests for this.
Add a --no-exec command-line option to skip executing scripts so one can see how fast things run without forking out to grit and friends.
Add non-Linux build for expat. Other Windows build stuff for SSL, base
BUG=
R=scottmg@chromium.org
Review URL: https://codereview.chromium.org/23618064
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@224747 0039d316-1c4b-4281-b951-d872f2087c98
35 files changed, 840 insertions, 355 deletions
diff --git a/tools/gn/BUILD.gn b/tools/gn/BUILD.gn index 0b46d5a..2c38661 100644 --- a/tools/gn/BUILD.gn +++ b/tools/gn/BUILD.gn @@ -38,10 +38,10 @@ static_library("gn_lib") { "function_exec_script.cc", "function_process_file_template.cc", "function_read_file.cc", + "function_rebase_path.cc", "function_set_default_toolchain.cc", "function_set_defaults.cc", "function_template.cc", - "function_to_build_path.cc", "function_toolchain.cc", "function_write_file.cc", "group_target_generator.cc", @@ -156,7 +156,7 @@ test("gn_unittests") { "escape_unittest.cc", "file_template_unittest.cc", "filesystem_utils_unittest.cc", - "function_to_build_path_unittest.cc", + "function_rebase_path_unittest.cc", "input_conversion_unittest.cc", "label_unittest.cc", "ninja_copy_target_writer_unittest.cc", diff --git a/tools/gn/args.cc b/tools/gn/args.cc index d11892bc..64069f4 100644 --- a/tools/gn/args.cc +++ b/tools/gn/args.cc @@ -7,12 +7,12 @@ #include "tools/gn/variables.h" const char kBuildArgs_Help[] = - "Build Arguments Overview.\n" + "Build Arguments Overview\n" "\n" " Build arguments are variables passed in from outside of the build\n" " that build files can query to determine how the build works.\n" "\n" - "How build arguments are set:\n" + "How build arguments are set\n" "\n" " First, system default arguments are set based on the current system.\n" " The built-in arguments are:\n" @@ -36,7 +36,7 @@ const char kBuildArgs_Help[] = " It is an error to specify an override for a build argument that never\n" " appears in a \"declare_args\" call.\n" "\n" - "How build arguments are used:\n" + "How build arguments are used\n" "\n" " If you want to use an argument, you use declare_args() and specify\n" " default values. These default values will apply if none of the steps\n" diff --git a/tools/gn/command_help.cc b/tools/gn/command_help.cc index 5232a9b..f378e80 100644 --- a/tools/gn/command_help.cc +++ b/tools/gn/command_help.cc @@ -5,6 +5,7 @@ #include <algorithm> #include <iostream> +#include "base/strings/string_split.h" #include "tools/gn/args.h" #include "tools/gn/commands.h" #include "tools/gn/err.h" @@ -49,6 +50,44 @@ void PrintShortHelp(const std::string& line) { OutputString(line.substr(first_normal) + "\n"); } +// Rules: +// - Lines beginning with non-whitespace are highlighted up to the first +// colon (or the whole line if not). +// - Lines whose first non-whitespace character is a # are dimmed. +void PrintLongHelp(const std::string& text) { + std::vector<std::string> lines; + base::SplitStringDontTrim(text, '\n', &lines); + + for (size_t i = 0; i < lines.size(); i++) { + const std::string& line = lines[i]; + + // Check for a heading line. + if (!line.empty() && line[0] != ' ') { + // Highlight up to the colon (if any). + size_t chars_to_highlight = line.find(':'); + if (chars_to_highlight == std::string::npos) + chars_to_highlight = line.size(); + OutputString(line.substr(0, chars_to_highlight), DECORATION_YELLOW); + OutputString(line.substr(chars_to_highlight) + "\n"); + continue; + } + + // Check for a comment. + TextDecoration dec = DECORATION_NONE; + for (size_t char_i = 0; char_i < line.size(); char_i++) { + if (line[char_i] == '#') { + // Got a comment, draw dimmed. + dec = DECORATION_DIM; + break; + } else if (line[char_i] != ' ') { + break; + } + } + + OutputString(line + "\n", dec); + } +} + void PrintToplevelHelp() { OutputString("Commands (type \"gn help <command>\" for more details):\n"); @@ -65,6 +104,8 @@ void PrintToplevelHelp() { PrintShortHelp( "--args: Specifies build args overrides. See \"gn help buildargs\"."); PrintShortHelp( + "--no-exec: Skips exec_script calls (for performance testing)."); + PrintShortHelp( "-q: Quiet mode, don't print anything on success."); PrintShortHelp( "--output: Directory for build output (relative to source root)."); @@ -135,7 +176,7 @@ int RunHelp(const std::vector<std::string>& args) { commands::CommandInfoMap::const_iterator found_command = command_map.find(args[0]); if (found_command != command_map.end()) { - OutputString(found_command->second.help); + PrintLongHelp(found_command->second.help); return 0; } @@ -144,7 +185,7 @@ int RunHelp(const std::vector<std::string>& args) { functions::FunctionInfoMap::const_iterator found_function = function_map.find(args[0]); if (found_function != function_map.end()) { - OutputString(found_function->second.help); + PrintLongHelp(found_function->second.help); return 0; } @@ -154,7 +195,7 @@ int RunHelp(const std::vector<std::string>& args) { variables::VariableInfoMap::const_iterator found_builtin_var = builtin_vars.find(args[0]); if (found_builtin_var != builtin_vars.end()) { - OutputString(found_builtin_var->second.help); + PrintLongHelp(found_builtin_var->second.help); return 0; } @@ -164,29 +205,29 @@ int RunHelp(const std::vector<std::string>& args) { variables::VariableInfoMap::const_iterator found_target_var = target_vars.find(args[0]); if (found_target_var != target_vars.end()) { - OutputString(found_target_var->second.help); + PrintLongHelp(found_target_var->second.help); return 0; } // Random other topics. if (args[0] == "buildargs") { - OutputString(kBuildArgs_Help); + PrintLongHelp(kBuildArgs_Help); return 0; } if (args[0] == "dotfile") { - OutputString(kDotfile_Help); + PrintLongHelp(kDotfile_Help); return 0; } if (args[0] == "input_conversion") { - OutputString(kInputConversion_Help); + PrintLongHelp(kInputConversion_Help); return 0; } if (args[0] == "patterns") { - OutputString(kPattern_Help); + PrintLongHelp(kPattern_Help); return 0; } if (args[0] == "source_expansion") { - OutputString(kSourceExpansion_Help); + PrintLongHelp(kSourceExpansion_Help); return 0; } diff --git a/tools/gn/file_template.cc b/tools/gn/file_template.cc index 62235c4..1616c52 100644 --- a/tools/gn/file_template.cc +++ b/tools/gn/file_template.cc @@ -35,7 +35,7 @@ const char kSourceExpansion_Help[] = " See \"gn help copy\" and \"gn help custom\" for more on how this is\n" " applied.\n" "\n" - "Placeholders:\n" + "Placeholders\n" "\n" " {{source}}\n" " The name of the source file relative to the root build output\n" @@ -54,7 +54,7 @@ const char kSourceExpansion_Help[] = " same name but different extension. For the source \"foo/bar.txt\"\n" " the source name part will be \"bar\".\n" "\n" - "Examples:\n" + "Examples\n" "\n" " Non-varying outputs:\n" " script(\"hardcoded_outputs\") {\n" diff --git a/tools/gn/filesystem_utils.cc b/tools/gn/filesystem_utils.cc index 3ecdc97..ee7c5d5 100644 --- a/tools/gn/filesystem_utils.cc +++ b/tools/gn/filesystem_utils.cc @@ -4,6 +4,8 @@ #include "tools/gn/filesystem_utils.h" +#include <algorithm> + #include "base/logging.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -507,3 +509,40 @@ std::string PathToSystem(const std::string& path) { return ret; } +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(); + + // Skip the common prefixes of the source and dest as long as they end in + // a [back]slash. + size_t common_prefix_len = 2; // The beginning two "//" are always the same. + size_t max_common_length = std::min(input.size(), dest.size()); + for (size_t i = common_prefix_len; i < max_common_length; i++) { + if ((input[i] == '/' || input[i] == '\\') && + (dest[i] == '/' || dest[i] == '\\')) + common_prefix_len = i + 1; + else if (input[i] != dest[i]) + break; + } + + // Invert the dest dir starting from the end of the common prefix. + std::string ret; + for (size_t i = common_prefix_len; i < dest.size(); i++) { + if (dest[i] == '/' || dest[i] == '\\') + ret.append("../"); + } + + // Append any remaining unique input. + ret.append(&input[common_prefix_len], input.size() - common_prefix_len); + + // If the result is still empty, the paths are the same. + if (ret.empty()) + ret.push_back('.'); + + return ret; +} diff --git a/tools/gn/filesystem_utils.h b/tools/gn/filesystem_utils.h index bf214dc..9899cae 100644 --- a/tools/gn/filesystem_utils.h +++ b/tools/gn/filesystem_utils.h @@ -133,4 +133,9 @@ void NormalizePath(std::string* path); void ConvertPathToSystem(std::string* path); std::string PathToSystem(const std::string& path); +// Takes a source-absolute path (must begin with "//") and makes it relative +// to the given directory, which also must be source-absolute. +std::string RebaseSourceAbsolutePath(const std::string& input, + const SourceDir& dest_dir); + #endif // TOOLS_GN_FILESYSTEM_UTILS_H_ diff --git a/tools/gn/filesystem_utils_unittest.cc b/tools/gn/filesystem_utils_unittest.cc index 6c80cdb..aeb09a4 100644 --- a/tools/gn/filesystem_utils_unittest.cc +++ b/tools/gn/filesystem_utils_unittest.cc @@ -195,3 +195,53 @@ TEST(FilesystemUtils, NormalizePath) { NormalizePath(&input); EXPECT_EQ("../", input); } + +TEST(FilesystemUtils, RebaseSourceAbsolutePath) { + // Degenerate case. + EXPECT_EQ(".", RebaseSourceAbsolutePath("//", SourceDir("//"))); + EXPECT_EQ(".", + RebaseSourceAbsolutePath("//foo/bar/", SourceDir("//foo/bar/"))); + + // Going up the tree. + EXPECT_EQ("../foo", + RebaseSourceAbsolutePath("//foo", SourceDir("//bar/"))); + EXPECT_EQ("../foo/", + RebaseSourceAbsolutePath("//foo/", SourceDir("//bar/"))); + EXPECT_EQ("../../foo", + RebaseSourceAbsolutePath("//foo", SourceDir("//bar/moo"))); + EXPECT_EQ("../../foo/", + RebaseSourceAbsolutePath("//foo/", SourceDir("//bar/moo"))); + + // Going down the tree. + EXPECT_EQ("foo/bar", + RebaseSourceAbsolutePath("//foo/bar", SourceDir("//"))); + EXPECT_EQ("foo/bar/", + RebaseSourceAbsolutePath("//foo/bar/", SourceDir("//"))); + + // Going up and down the tree. + EXPECT_EQ("../../foo/bar", + RebaseSourceAbsolutePath("//foo/bar", SourceDir("//a/b/"))); + EXPECT_EQ("../../foo/bar/", + RebaseSourceAbsolutePath("//foo/bar/", SourceDir("//a/b/"))); + + // Sharing prefix. + EXPECT_EQ("foo", + RebaseSourceAbsolutePath("//a/foo", SourceDir("//a/"))); + EXPECT_EQ("foo/", + RebaseSourceAbsolutePath("//a/foo/", SourceDir("//a/"))); + EXPECT_EQ("foo", + RebaseSourceAbsolutePath("//a/b/foo", SourceDir("//a/b/"))); + EXPECT_EQ("foo/", + RebaseSourceAbsolutePath("//a/b/foo/", SourceDir("//a/b/"))); + EXPECT_EQ("foo/bar", + RebaseSourceAbsolutePath("//a/b/foo/bar", SourceDir("//a/b/"))); + EXPECT_EQ("foo/bar/", + RebaseSourceAbsolutePath("//a/b/foo/bar/", SourceDir("//a/b/"))); + + // One could argue about this case. Since the input doesn't have a slash it + // would normally not be treated like a directory and we'd go up, which is + // simpler. However, since it matches the output directory's name, we could + // potentially infer that it's the same and return "." for this. + EXPECT_EQ("../bar", + RebaseSourceAbsolutePath("//foo/bar", SourceDir("//foo/bar/"))); +} diff --git a/tools/gn/function_exec_script.cc b/tools/gn/function_exec_script.cc index 0ffe90b..c00579a 100644 --- a/tools/gn/function_exec_script.cc +++ b/tools/gn/function_exec_script.cc @@ -38,6 +38,8 @@ namespace functions { namespace { +const char kNoExecSwitch[] = "no-exec"; + #if defined(OS_WIN) bool ExecProcess(const CommandLine& cmdline, const base::FilePath& startup_dir, @@ -350,11 +352,13 @@ Value RunExecScript(Scope* scope, std::string output; std::string stderr_output; // TODO(brettw) not hooked up, see above. int exit_code = 0; - if (!ExecProcess(cmdline, startup_dir, - &output, &stderr_output, &exit_code)) { - *err = Err(function->function(), "Could not execute python.", - "I was trying to execute \"" + FilePathToUTF8(python_path) + "\"."); - return Value(); + if (!CommandLine::ForCurrentProcess()->HasSwitch(kNoExecSwitch)) { + if (!ExecProcess(cmdline, startup_dir, + &output, &stderr_output, &exit_code)) { + *err = Err(function->function(), "Could not execute python.", + "I was trying to execute \"" + FilePathToUTF8(python_path) + "\"."); + return Value(); + } } if (g_scheduler->verbose_logging()) { g_scheduler->Log("Pythoning", script_source.value() + " took " + diff --git a/tools/gn/function_read_file.cc b/tools/gn/function_read_file.cc index 1067d9b..e510267 100644 --- a/tools/gn/function_read_file.cc +++ b/tools/gn/function_read_file.cc @@ -25,7 +25,7 @@ const char kReadFile_Help[] = " Whitespace will be trimmed from the end of the file. Throws an error\n" " if the file can not be opened.\n" "\n" - "Arguments:\n" + "Arguments\n" "\n" " filename\n" " Filename to read, relative to the build file.\n" @@ -34,7 +34,7 @@ const char kReadFile_Help[] = " Controls how the file is read and parsed.\n" " See \"gn help input_conversion\".\n" "\n" - "Example:\n" + "Example\n" " lines = read_file(\"foo.txt\", \"list lines\")\n"; Value RunReadFile(Scope* scope, diff --git a/tools/gn/function_rebase_path.cc b/tools/gn/function_rebase_path.cc new file mode 100644 index 0000000..68c6f1f --- /dev/null +++ b/tools/gn/function_rebase_path.cc @@ -0,0 +1,332 @@ +// 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/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 { + +enum SeparatorConversion { + SEP_NO_CHANGE, // Don't change. + SEP_TO_SYSTEM, // Slashes to system ones. + SEP_FROM_SYSTEM // System ones to slashes. +}; + +// Does the specified path separator conversion in-place. +void ConvertSlashes(std::string* str, SeparatorConversion mode) { +#if defined(OS_WIN) + switch (mode) { + case SEP_NO_CHANGE: + break; + case SEP_TO_SYSTEM: + for (size_t i = 0; i < str->size(); i++) { + if ((*str)[i] == '/') + (*str)[i] = '\\'; + } + break; + case SEP_FROM_SYSTEM: + for (size_t i = 0; i < str->size(); i++) { + if ((*str)[i] == '\\') + (*str)[i] = '/'; + } + break; + } +#else + DCHECK(str->find('\\') == std::string::npos) + << "Filename contains a backslash on a non-Windows platform."; +#endif +} + +bool EndsInSlash(const std::string& s) { + return !s.empty() && (s[s.size() - 1] == '/' || s[s.size() - 1] == '\\'); +} + +// 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 (EndsInSlash(input)) { + if (!EndsInSlash(*output)) // Preserve same slash type as input. + output->push_back(input[input.size() - 1]); + } else { + if (EndsInSlash(*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 (value[value_size - num_dots - 1] == '/' || + 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, + SeparatorConversion separator_conversion, + 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(string_value)); + } else { + system_path = scope->settings()->build_settings()->GetFullPath( + from_dir.ResolveRelativeFile(string_value)); + } + result = Value(function, FilePathToUTF8(system_path)); + if (looks_like_dir) + MakeSlashEndingMatchInput(string_value, &result.string_value()); + ConvertPathToSystem(&result.string_value()); + return result; + } + + if (from_dir.is_system_absolute() || to_dir.is_system_absolute()) { + *err = Err(function, "System-absolute directories are not supported for " + "the source or dest dir for rebase_path. It would be nice to add this " + "if you're so inclined!"); + return result; + } + + result = Value(function, Value::STRING); + if (looks_like_dir) { + result.string_value() = RebaseSourceAbsolutePath( + from_dir.ResolveRelativeDir(string_value).value(), + to_dir); + MakeSlashEndingMatchInput(string_value, &result.string_value()); + } else { + result.string_value() = RebaseSourceAbsolutePath( + from_dir.ResolveRelativeFile(string_value).value(), + to_dir); + } + + ConvertSlashes(&result.string_value(), separator_conversion); + return result; +} + +} // namespace + +const char kRebasePath[] = "rebase_path"; +const char kRebasePath_Help[] = + "rebase_path: Rebase a file or directory to another location.\n" + "\n" + " converted = rebase_path(input, current_base, new_base,\n" + " [path_separators])\n" + "\n" + " Takes a string argument representing a file name, or a list of such\n" + " strings and converts it/them to be relative to a different base\n" + " directory.\n" + "\n" + " When invoking the compiler or scripts, GN will automatically convert\n" + " sources and include directories to be relative to the build directory.\n" + " However, if you're passing files directly in the \"args\" array or\n" + " doing other manual manipulations where GN doesn't know something is\n" + " a file name, you will need to convert paths to be relative to what\n" + " your tool is expecting.\n" + "\n" + " The common case is to use this to convert paths relative to the\n" + " current directory to be relative to the build directory (which will\n" + " be the current directory when executing scripts).\n" + "\n" + "Arguments\n" + "\n" + " input\n" + " A string or list of strings representing file or directory names\n" + " These can be relative paths (\"foo/bar.txt\", system absolte paths\n" + " (\"/foo/bar.txt\"), or source absolute paths (\"//foo/bar.txt\").\n" + "\n" + " current_base\n" + " Directory representing the base for relative paths in the input.\n" + " If this is not an absolute path, it will be treated as being\n" + " relative to the current build file. Use \".\" to convert paths\n" + " from the current BUILD-file's directory.\n" + "\n" + " new_base\n" + " The directory to convert the paths to be relative to. As with the\n" + " current_base, this can be a relative path, which will be treated\n" + " as being relative to the current BUILD-file's directory.\n" + "\n" + " As a special case, if new_base is the empty string, all paths\n" + " will be converted to system-absolute native style paths with\n" + " system path separators. This is useful for invoking external\n" + " programs.\n" + "\n" + " path_separators\n" + " On Windows systems, indicates whether and how path separators\n" + " should be converted as part of the transformation. It can be one\n" + " of the following strings:\n" + " - \"none\" Perform no changes on path separators. This is the\n" + " default if this argument is unspecified.\n" + " - \"to_system\" Convert to the system path separators\n" + " (backslashes on Windows).\n" + " - \"from_system\" Convert system path separators to forward\n" + " slashes.\n" + "\n" + " On Posix systems there are no path separator transformations\n" + " applied. If the new_base is empty (specifying absolute output)\n" + " this parameter should not be supplied since paths will always be\n" + " converted,\n" + "\n" + "Return value\n" + "\n" + " The return value will be the same type as the input value (either a\n" + " string or a list of strings). All relative and source-absolute file\n" + " names will be converted to be relative to the requested output\n" + " System-absolute paths will be unchanged.\n" + "\n" + "Example\n" + "\n" + " # Convert a file in the current directory to be relative to the build\n" + " # directory (the current dir when executing compilers and scripts).\n" + " foo = rebase_path(\"myfile.txt\", \".\", root_build_dir)\n" + " # might produce \"../../project/myfile.txt\".\n" + "\n" + " # Convert a file to be system absolute:\n" + " foo = rebase_path(\"myfile.txt\", \".\", \"\")\n" + " # Might produce \"D:\\source\\project\\myfile.txt\" on Windows or\n" + " # \"/home/you/source/project/myfile.txt\" on Linux.\n" + "\n" + " # Convert a file's path separators from forward slashes to system\n" + " # slashes.\n" + " foo = rebase_path(\"source/myfile.txt\", \".\", \".\", \"to_system\")\n" + "\n" + " # Typical usage for converting to the build directory for a script.\n" + " custom(\"myscript\") {\n" + " # Don't convert sources, GN will automatically convert these to be\n" + " # relative to the build directory when it contructs the command\n" + " # line for your script.\n" + " sources = [ \"foo.txt\", \"bar.txt\" ]\n" + "\n" + " # Extra file args passed manually need to be explicitly converted\n" + " # to be relative to the build directory:\n" + " args = [\n" + " \"--data\",\n" + " rebase_path(\"//mything/data/input.dat\", \".\", root_build_dir),\n" + " \"--rel\",\n" + " rebase_path(\"relative_path.txt\", \".\", root_build_dir)\n" + " ]\n" + " }\n"; + +Value RunRebasePath(Scope* scope, + const FunctionCallNode* function, + const std::vector<Value>& args, + Err* err) { + Value result; + + // Inputs. + if (args.size() != 3 && args.size() != 4) { + *err = Err(function->function(), "rebase_path takes 3 or 4 args."); + return result; + } + const Value& inputs = args[0]; + + // From path. + if (!args[1].VerifyTypeIs(Value::STRING, err)) + return result; + const SourceDir& current_dir = scope->GetSourceDir(); + SourceDir from_dir = current_dir.ResolveRelativeDir(args[1].string_value()); + + // To path. + if (!args[2].VerifyTypeIs(Value::STRING, err)) + return result; + bool convert_to_system_absolute = false; + SourceDir to_dir; + if (args[2].string_value().empty()) { + convert_to_system_absolute = true; + } else { + to_dir = current_dir.ResolveRelativeDir(args[2].string_value()); + } + + // Path conversion. + SeparatorConversion sep_conversion = SEP_NO_CHANGE; + if (args.size() == 4) { + if (convert_to_system_absolute) { + *err = Err(function, "Can't specify slash conversion.", + "You specified absolute system path output by using an empty string " + "for the desination directory on rebase_path(). In this case, you " + "can't specify slash conversion."); + return result; + } + + if (!args[3].VerifyTypeIs(Value::STRING, err)) + return result; + const std::string& sep_string = args[3].string_value(); + if (sep_string == "to_system") { + sep_conversion = SEP_TO_SYSTEM; + } else if (sep_string == "from_system") { + sep_conversion = SEP_FROM_SYSTEM; + } else if (sep_string != "none") { + *err = Err(args[3], "Invalid path separator conversion mode.", + "I was expecting \"none\", \"to_system\", or \"from_system\" and\n" + "you gave me \"" + args[3].string_value() + "\"."); + return result; + } + } + + if (inputs.type() == Value::STRING) { + return ConvertOnePath(scope, function, inputs, + from_dir, to_dir, convert_to_system_absolute, + sep_conversion, err); + + } else if (inputs.type() == Value::LIST) { + result = Value(function, Value::LIST); + result.list_value().reserve(inputs.list_value().size()); + + for (size_t i = 0; i < inputs.list_value().size(); i++) { + result.list_value().push_back( + ConvertOnePath(scope, function, inputs.list_value()[i], + from_dir, to_dir, convert_to_system_absolute, + sep_conversion, 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 diff --git a/tools/gn/function_rebase_path_unittest.cc b/tools/gn/function_rebase_path_unittest.cc new file mode 100644 index 0000000..19e0692 --- /dev/null +++ b/tools/gn/function_rebase_path_unittest.cc @@ -0,0 +1,148 @@ +// 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 "build/build_config.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "tools/gn/functions.h" +#include "tools/gn/parse_tree.h" +#include "tools/gn/test_with_scope.h" + +namespace { + +std::string RebaseOne(Scope scope, + const char* input, + const char* from_dir, + const char* to_dir, + const char* sep = NULL) { + std::vector<Value> args; + args.push_back(Value(NULL, input)); + args.push_back(Value(NULL, from_dir)); + args.push_back(Value(NULL, to_dir)); + if (sep) + args.push_back(Value(NULL, sep)); + + Err err; + FunctionCallNode function; + Value result = functions::RunRebasePath(&scope, &function, args, &err); + bool is_string = result.type() == Value::STRING; + EXPECT_TRUE(is_string); + + return result.string_value(); +} + +} // namespace + +TEST(RebasePath, Strings) { + TestWithScope setup; + setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); + Scope* scope = setup.scope(); + scope->set_source_dir(SourceDir("//tools/gn/")); + + // Build-file relative paths. + EXPECT_EQ("../../tools/gn", RebaseOne(scope, ".", ".", "//out/Debug")); + EXPECT_EQ("../../tools/gn/", RebaseOne(scope, "./", ".", "//out/Debug")); + EXPECT_EQ("../../tools/gn/foo", RebaseOne(scope, "foo", ".", "//out/Debug")); + EXPECT_EQ("../..", RebaseOne(scope, "../..", ".", "//out/Debug")); + EXPECT_EQ("../../", RebaseOne(scope, "../../", ".", "//out/Debug")); + + // We don't allow going above the root source dir. + EXPECT_EQ("../..", RebaseOne(scope, "../../..", ".", "//out/Debug")); + + // Source-absolute input paths. + EXPECT_EQ("./", RebaseOne(scope, "//", "//", "//")); + EXPECT_EQ("foo", RebaseOne(scope, "//foo", "//", "//")); + EXPECT_EQ("foo/", RebaseOne(scope, "//foo/", "//", "//")); + EXPECT_EQ("../../foo/bar", RebaseOne(scope, "//foo/bar", ".", "//out/Debug")); + EXPECT_EQ("./", RebaseOne(scope, "//foo/", "//", "//foo/")); + // Thie one is technically correct but could be simplified to "." if + // necessary. + EXPECT_EQ("../foo", RebaseOne(scope, "//foo", "//", "//foo")); + + // Test slash conversion. +#if defined(OS_WIN) + EXPECT_EQ("foo/bar", RebaseOne(scope, "foo/bar", ".", ".", "none")); + EXPECT_EQ("foo\\bar", RebaseOne(scope, "foo/bar", ".", ".", "to_system")); + EXPECT_EQ("foo/bar", RebaseOne(scope, "foo/bar", ".", ".", "from_system")); + + EXPECT_EQ("foo\\bar", RebaseOne(scope, "foo\\bar", ".", ".", "none")); + EXPECT_EQ("foo\\bar", RebaseOne(scope, "foo\\bar", ".", ".", "to_system")); + EXPECT_EQ("foo/bar", RebaseOne(scope, "foo\\bar", ".", ".", "from_system")); +#else // No transformations on Posix. + EXPECT_EQ("foo/bar", RebaseOne(scope, "foo/bar", ".", ".", "none")); + EXPECT_EQ("foo/bar", RebaseOne(scope, "foo/bar", ".", ".", "to_system")); + EXPECT_EQ("foo/bar", RebaseOne(scope, "foo/bar", ".", ".", "from_system")); + + EXPECT_EQ("foo\\bar", RebaseOne(scope, "foo\\bar", ".", ".", "none")); + EXPECT_EQ("foo\\bar", RebaseOne(scope, "foo\\bar", ".", ".", "to_system")); + EXPECT_EQ("foo\\bar", RebaseOne(scope, "foo\\bar", ".", ".", "from_system")); +#endif + + // Test system path output. +#if defined(OS_WIN) + setup.build_settings()->SetRootPath(base::FilePath(L"C:\\source")); + EXPECT_EQ("C:\\source", RebaseOne(scope, ".", "//", "")); + EXPECT_EQ("C:\\source\\", RebaseOne(scope, "//", "//", "")); + EXPECT_EQ("C:\\source\\foo", RebaseOne(scope, "foo", "//", "")); + EXPECT_EQ("C:\\source\\foo\\", RebaseOne(scope, "foo/", "//", "")); + EXPECT_EQ("C:\\source\\tools\\gn\\foo", RebaseOne(scope, "foo", ".", "")); +#else + setup.build_settings()->SetRootPath(base::FilePath("/source")); + EXPECT_EQ("/source", RebaseOne(scope, ".", "//", "")); + EXPECT_EQ("/source/", RebaseOne(scope, "//", "//", "")); + EXPECT_EQ("/source/foo", RebaseOne(scope, "foo", "//", "")); + EXPECT_EQ("/source/foo/", RebaseOne(scope, "foo/", "//", "")); + EXPECT_EQ("/source/tools/gn/foo", RebaseOne(scope, "foo", ".", "")); +#endif +} + +// Test list input. +TEST(RebasePath, List) { + TestWithScope setup; + setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); + setup.scope()->set_source_dir(SourceDir("//tools/gn/")); + + std::vector<Value> args; + args.push_back(Value(NULL, Value::LIST)); + args[0].list_value().push_back(Value(NULL, "foo.txt")); + args[0].list_value().push_back(Value(NULL, "bar.txt")); + args.push_back(Value(NULL, ".")); + args.push_back(Value(NULL, "//out/Debug/")); + + Err err; + FunctionCallNode function; + Value ret = functions::RunRebasePath(setup.scope(), &function, args, &err); + EXPECT_FALSE(err.has_error()); + + ASSERT_EQ(Value::LIST, ret.type()); + ASSERT_EQ(2u, ret.list_value().size()); + + EXPECT_EQ("../../tools/gn/foo.txt", ret.list_value()[0].string_value()); + EXPECT_EQ("../../tools/gn/bar.txt", ret.list_value()[1].string_value()); +} + +TEST(RebasePath, Errors) { + TestWithScope setup; + setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); + + // No arg input should issue an error. + Err err; + std::vector<Value> args; + FunctionCallNode function; + Value ret = functions::RunRebasePath(setup.scope(), &function, args, &err); + EXPECT_TRUE(err.has_error()); + + // One arg int input. + args.push_back(Value(NULL, static_cast<int64>(5))); + err = Err(); + ret = functions::RunRebasePath(setup.scope(), &function, args, &err); + EXPECT_TRUE(err.has_error()); + + // Two arg string input. + args.clear(); + args.push_back(Value(NULL, "hello")); + args.push_back(Value(NULL, "world")); + err = Err(); + ret = functions::RunRebasePath(setup.scope(), &function, args, &err); + EXPECT_TRUE(err.has_error()); +} diff --git a/tools/gn/function_to_build_path.cc b/tools/gn/function_to_build_path.cc deleted file mode 100644 index 362f8c9..0000000 --- a/tools/gn/function_to_build_path.cc +++ /dev/null @@ -1,140 +0,0 @@ -// 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/build_settings.h" -#include "tools/gn/functions.h" -#include "tools/gn/parse_tree.h" -#include "tools/gn/path_output.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 { - -// 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 (value[value_size - num_dots - 1] == '/' || - value[value_size - num_dots - 1] == '\\') - return true; // String is a [back]slash followed by 0 or more dots. - - // Anything else. - return false; -} - -Value ConvertOneBuildPath(const Scope* scope, - const FunctionCallNode* function, - const Value& value, - const PathOutput& path_output, - Err* err) { - if (!value.VerifyTypeIs(Value::STRING, err)) - return Value(); - - const std::string& string_value = value.string_value(); - - std::ostringstream buffer; - if (ValueLooksLikeDir(string_value)) { - SourceDir absolute = - scope->GetSourceDir().ResolveRelativeDir(string_value); - path_output.WriteDir(buffer, absolute, PathOutput::DIR_NO_LAST_SLASH); - } else { - SourceFile absolute = - scope->GetSourceDir().ResolveRelativeFile(string_value); - path_output.WriteFile(buffer, absolute); - } - return Value(function, buffer.str()); -} - -} // namespace - -const char kToBuildPath[] = "to_build_path"; -const char kToBuildPath_Help[] = - "to_build_path: Rebase a file or directory to the build output dir.\n" - "\n" - " <converted> = to_build_path(<file_or_path_string_or_list>)\n" - "\n" - " Takes a string argument representing a file name, or a list of such\n" - " strings and converts it/them to be relative to the root build output\n" - " directory (which is the current directory when running scripts).\n" - "\n" - " The input can be:\n" - " - Paths relative to the BUILD file like \"foo.txt\".\n" - " - Source-root absolute paths like \"//foo/bar/foo.txt\".\n" - " - System absolute paths like \"/usr/include/foo.h\" or\n" - " \"/C:/foo/bar.h\" (these will be passed unchanged).\n" - " - A list of such values (the result will be a list of each item\n" - " converted as per the above description).\n" - "\n" - " Normally for sources and in cases where GN is providing file names\n" - " to a tool, the paths will automatically be converted to be relative\n" - " to the build directory. However, if you pass additional arguments,\n" - " GN won't know that the string is actually a file path. These will\n" - " need to be manually converted to be relative to the build dir using\n" - " to_build_path().\n" - "\n" - " Trailing slashes will not be reflected in the output.\n" - "\n" - " Additionally, on Windows, slashes will be converted to backslashes.\n" - "\n" - "Example:\n" - " custom(\"myscript\") {\n" - " # Don't use for sources, GN will automatically convert these since\n" - " # it knows they're files.\n" - " sources = [ \"foo.txt\", \"bar.txt\" ]\n" - "\n" - " # Extra file args passed manually need to be explicitly converted:\n" - " args = [ \"--data\", to_build_path(\"//mything/data/input.dat\"),\n" - " \"--rel\", to_build_path(\"relative_path.txt\") ]\n" - " }\n"; - -Value RunToBuildPath(Scope* scope, - const FunctionCallNode* function, - const std::vector<Value>& args, - Err* err) { - if (args.size() != 1) { - *err = Err(function->function(), "to_build_path takes one argument."); - return Value(); - } - - const Value& value = args[0]; - PathOutput path_output(scope->settings()->build_settings()->build_dir(), - ESCAPE_NONE, true); - - if (value.type() == Value::STRING) { - return ConvertOneBuildPath(scope, function, value, path_output, err); - - } else if (value.type() == Value::LIST) { - Value ret(function, Value::LIST); - ret.list_value().reserve(value.list_value().size()); - - for (size_t i = 0; i < value.list_value().size(); i++) { - ret.list_value().push_back( - ConvertOneBuildPath(scope, function, value.list_value()[i], - path_output, err)); - } - return ret; - } - - *err = Err(function->function(), - "to_build_path requires a list or a string."); - return Value(); -} - -} // namespace functions diff --git a/tools/gn/function_to_build_path_unittest.cc b/tools/gn/function_to_build_path_unittest.cc deleted file mode 100644 index 7e01912..0000000 --- a/tools/gn/function_to_build_path_unittest.cc +++ /dev/null @@ -1,110 +0,0 @@ -// 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 "testing/gtest/include/gtest/gtest.h" -#include "tools/gn/functions.h" -#include "tools/gn/parse_tree.h" -#include "tools/gn/test_with_scope.h" - -namespace { - -void ExpectCallEqualsString(Scope* scope, - const std::string& input, - const std::string& expected) { - std::vector<Value> args; - args.push_back(Value(NULL, input)); - - Err err; - FunctionCallNode function; - Value result = functions::RunToBuildPath(scope, &function, args, &err); - - EXPECT_FALSE(err.has_error()); - EXPECT_EQ(Value::STRING, result.type()); - EXPECT_EQ(expected, result.string_value()); -} - -} // namespace - -TEST(ToBuildPath, Strings) { - TestWithScope setup; - setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); - - Scope* scope = setup.scope(); - scope->set_source_dir(SourceDir("//tools/gn/")); - - // Absolute system paths are unchanged. - ExpectCallEqualsString(scope, "/", "/"); - ExpectCallEqualsString(scope, "/foo", "/foo"); - ExpectCallEqualsString(scope, "/foo/", "/foo"); - ExpectCallEqualsString(scope, "/foo/bar.txt", "/foo/bar.txt"); - - // Build-file relative paths. - ExpectCallEqualsString(scope, ".", "../../tools/gn"); - ExpectCallEqualsString(scope, "foo.txt", "../../tools/gn/foo.txt"); - ExpectCallEqualsString(scope, "..", "../../tools"); - ExpectCallEqualsString(scope, "../", "../../tools"); - ExpectCallEqualsString(scope, "../foo.txt", "../../tools/foo.txt"); - - // Source-root paths. - ExpectCallEqualsString(scope, "//", "../.."); - ExpectCallEqualsString(scope, "//foo/", "../../foo"); - ExpectCallEqualsString(scope, "//foo.txt", "../../foo.txt"); - ExpectCallEqualsString(scope, "//foo/bar.txt", "../../foo/bar.txt"); - - // Source-root paths. It might be nice if we detected the strings start - // with the build dir and collapse, but this is the current behavior. - ExpectCallEqualsString(scope, "//out/Debug/", - "../../out/Debug"); // Could be ".". - ExpectCallEqualsString(scope, "//out/Debug/foo/", - "../../out/Debug/foo"); // Could be "foo". -} - -// Test list input. -TEST(ToBuildPath, List) { - TestWithScope setup; - setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); - setup.scope()->set_source_dir(SourceDir("//tools/gn/")); - - std::vector<Value> args; - args.push_back(Value(NULL, Value::LIST)); - args[0].list_value().push_back(Value(NULL, "foo.txt")); - args[0].list_value().push_back(Value(NULL, "bar.txt")); - - Err err; - FunctionCallNode function; - Value ret = functions::RunToBuildPath(setup.scope(), &function, args, &err); - EXPECT_FALSE(err.has_error()); - - ASSERT_EQ(Value::LIST, ret.type()); - ASSERT_EQ(2u, ret.list_value().size()); - - EXPECT_EQ("../../tools/gn/foo.txt", ret.list_value()[0].string_value()); - EXPECT_EQ("../../tools/gn/bar.txt", ret.list_value()[1].string_value()); -} - -TEST(ToBuildPath, Errors) { - TestWithScope setup; - setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); - - // No arg input should issue an error. - Err err; - std::vector<Value> args; - FunctionCallNode function; - Value ret = functions::RunToBuildPath(setup.scope(), &function, args, &err); - EXPECT_TRUE(err.has_error()); - - // One arg int input. - args.push_back(Value(NULL, static_cast<int64>(5))); - err = Err(); - ret = functions::RunToBuildPath(setup.scope(), &function, args, &err); - EXPECT_TRUE(err.has_error()); - - // Two arg string input. - args.clear(); - args.push_back(Value(NULL, "hello")); - args.push_back(Value(NULL, "world")); - err = Err(); - ret = functions::RunToBuildPath(setup.scope(), &function, args, &err); - EXPECT_TRUE(err.has_error()); -} diff --git a/tools/gn/function_write_file.cc b/tools/gn/function_write_file.cc index ea42100..f794168 100644 --- a/tools/gn/function_write_file.cc +++ b/tools/gn/function_write_file.cc @@ -29,7 +29,7 @@ const char kWriteFile_Help[] = " TODO(brettw) we probably need an optional third argument to control\n" " list formatting.\n" "\n" - "Arguments:\n" + "Arguments\n" "\n" " filename\n" " Filename to write. This must be within the output directory.\n" diff --git a/tools/gn/functions.cc b/tools/gn/functions.cc index ade3643..bac4b8e 100644 --- a/tools/gn/functions.cc +++ b/tools/gn/functions.cc @@ -562,6 +562,7 @@ struct FunctionInfoInitializer { INSERT_FUNCTION(Print) INSERT_FUNCTION(ProcessFileTemplate) INSERT_FUNCTION(ReadFile) + INSERT_FUNCTION(RebasePath) INSERT_FUNCTION(SetDefaults) INSERT_FUNCTION(SetDefaultToolchain) INSERT_FUNCTION(SetSourcesAssignmentFilter) @@ -569,7 +570,6 @@ struct FunctionInfoInitializer { INSERT_FUNCTION(StaticLibrary) INSERT_FUNCTION(Template) INSERT_FUNCTION(Test) - INSERT_FUNCTION(ToBuildPath) INSERT_FUNCTION(Tool) INSERT_FUNCTION(Toolchain) INSERT_FUNCTION(ToolchainArgs) diff --git a/tools/gn/functions.h b/tools/gn/functions.h index 0ced17c..9c419cc 100644 --- a/tools/gn/functions.h +++ b/tools/gn/functions.h @@ -157,6 +157,13 @@ Value RunReadFile(Scope* scope, const std::vector<Value>& args, Err* err); +extern const char kRebasePath[]; +extern const char kRebasePath_Help[]; +Value RunRebasePath(Scope* scope, + const FunctionCallNode* function, + const std::vector<Value>& args, + Err* err); + extern const char kSetDefaults[]; extern const char kSetDefaults_Help[]; Value RunSetDefaults(Scope* scope, @@ -211,13 +218,6 @@ Value RunTest(Scope* scope, BlockNode* block, Err* err); -extern const char kToBuildPath[]; -extern const char kToBuildPath_Help[]; -Value RunToBuildPath(Scope* scope, - const FunctionCallNode* function, - const std::vector<Value>& args, - Err* err); - extern const char kTool[]; extern const char kTool_Help[]; Value RunTool(Scope* scope, diff --git a/tools/gn/functions_target.cc b/tools/gn/functions_target.cc index eba5073..62b7645 100644 --- a/tools/gn/functions_target.cc +++ b/tools/gn/functions_target.cc @@ -133,7 +133,8 @@ const char kCopy_Help[] = " expansion (see \"gn help source_expansion\"). The placeholders will\n" " will look like \"{{source_name_part}}\", for example.\n" "\n" - "Examples:\n" + "Examples\n" + "\n" " # Write a rule that copies a checked-in DLL to the output directory.\n" " copy(\"mydll\") {\n" " sources = [ \"mydll.dll\" ]\n" @@ -207,12 +208,12 @@ const char kCustom_Help[] = " will look like \"{{source}}\", for example, and can appear in\n" " either the outputs or the args lists.\n" "\n" - "Variables:\n" + "Variables\n" "\n" " args, deps, outputs, script*, source_prereqs, sources\n" " * = required\n" "\n" - "Examples:\n" + "Examples\n" "\n" " # Runs the script over each IDL file. The IDL script will generate\n" " # both a .cc and a .h file for each input.\n" @@ -254,7 +255,8 @@ const char kExecutable[] = "executable"; const char kExecutable_Help[] = "executable: Declare an executable target.\n" "\n" - "Variables:\n" + "Variables\n" + "\n" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS @@ -284,12 +286,14 @@ const char kGroup_Help[] = " through the group so you shouldn't need to use\n" " \"forward_dependent_configs_from.\n" "\n" - "Variables:\n" + "Variables\n" + "\n" DEPS_VARS DEPENDENT_CONFIG_VARS " Other variables: external\n" "\n" - "Example:\n" + "Example\n" + "\n" " group(\"all\") {\n" " deps = [\n" " \"//project:runner\",\n" @@ -317,7 +321,8 @@ const char kSharedLibrary_Help[] = " (say you dynamically load the library at runtime), then you should\n" " depend on the shared library via \"datadeps\" instead.\n" "\n" - "Variables:\n" + "Variables\n" + "\n" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS @@ -338,7 +343,8 @@ const char kStaticLibrary[] = "static_library"; const char kStaticLibrary_Help[] = "static_library: Declare a static library target.\n" "\n" - "Variables:\n" + "Variables\n" + "\n" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS diff --git a/tools/gn/gn.gyp b/tools/gn/gn.gyp index 4411c52..52c928e 100644 --- a/tools/gn/gn.gyp +++ b/tools/gn/gn.gyp @@ -48,10 +48,10 @@ 'function_exec_script.cc', 'function_process_file_template.cc', 'function_read_file.cc', + 'function_rebase_path.cc', 'function_set_default_toolchain.cc', 'function_set_defaults.cc', 'function_template.cc', - 'function_to_build_path.cc', 'function_toolchain.cc', 'function_write_file.cc', 'group_target_generator.cc', @@ -166,7 +166,7 @@ 'escape_unittest.cc', 'file_template_unittest.cc', 'filesystem_utils_unittest.cc', - 'function_to_build_path_unittest.cc', + 'function_rebase_path_unittest.cc', 'input_conversion_unittest.cc', 'label_unittest.cc', 'ninja_helper_unittest.cc', diff --git a/tools/gn/pattern.cc b/tools/gn/pattern.cc index b7f4229..7a198e9 100644 --- a/tools/gn/pattern.cc +++ b/tools/gn/pattern.cc @@ -26,7 +26,8 @@ const char kPattern_Help[] = " \\b Matches a path boundary. This will match the beginning or end of\n" " a string, or a slash.\n" "\n" - "Examples:\n" + "Examples\n" + "\n" " \"*asdf*\"\n" " Matches a string containing \"asdf\" anywhere.\n" "\n" diff --git a/tools/gn/scope_per_file_provider.cc b/tools/gn/scope_per_file_provider.cc index 1990193..525aab6 100644 --- a/tools/gn/scope_per_file_provider.cc +++ b/tools/gn/scope_per_file_provider.cc @@ -27,6 +27,8 @@ const Value* ScopePerFileProvider::GetProgrammaticValue( if (ident == variables::kPythonPath) return GetPythonPath(); + if (ident == variables::kRootBuildDir) + return GetRootBuildDir(); if (ident == variables::kRootGenDir) return GetRootGenDir(); if (ident == variables::kRootOutDir) @@ -65,10 +67,18 @@ const Value* ScopePerFileProvider::GetPythonPath() { return python_path_.get(); } +const Value* ScopePerFileProvider::GetRootBuildDir() { + if (!root_build_dir_) { + root_build_dir_.reset(new Value(NULL, + "/" + GetRootOutputDirWithNoLastSlash(scope_->settings()))); + } + return root_build_dir_.get(); +} + const Value* ScopePerFileProvider::GetRootGenDir() { if (!root_gen_dir_) { root_gen_dir_.reset(new Value(NULL, - "/" + GetRootGenDirWithNoLastSlash(scope_->settings()))); + "/" + GetToolchainGenDirWithNoLastSlash(scope_->settings()))); } return root_gen_dir_.get(); } @@ -76,7 +86,7 @@ const Value* ScopePerFileProvider::GetRootGenDir() { const Value* ScopePerFileProvider::GetRootOutDir() { if (!root_out_dir_) { root_out_dir_.reset(new Value(NULL, - "/" + GetRootOutputDirWithNoLastSlash(scope_->settings()))); + "/" + GetToolchainOutputDirWithNoLastSlash(scope_->settings()))); } return root_out_dir_.get(); } @@ -85,7 +95,7 @@ const Value* ScopePerFileProvider::GetTargetGenDir() { if (!target_gen_dir_) { target_gen_dir_.reset(new Value(NULL, "/" + - GetRootGenDirWithNoLastSlash(scope_->settings()) + + GetToolchainGenDirWithNoLastSlash(scope_->settings()) + GetFileDirWithNoLastSlash())); } return target_gen_dir_.get(); @@ -95,7 +105,7 @@ const Value* ScopePerFileProvider::GetTargetOutDir() { if (!target_out_dir_) { target_out_dir_.reset(new Value(NULL, "/" + - GetRootOutputDirWithNoLastSlash(scope_->settings()) + "/obj" + + GetToolchainOutputDirWithNoLastSlash(scope_->settings()) + "/obj" + GetFileDirWithNoLastSlash())); } return target_out_dir_.get(); @@ -117,9 +127,28 @@ std::string ScopePerFileProvider::GetRootOutputDirWithNoLastSlash( } // static -std::string ScopePerFileProvider::GetRootGenDirWithNoLastSlash( +std::string ScopePerFileProvider::GetToolchainOutputDirWithNoLastSlash( + const Settings* settings) { + const OutputFile& toolchain_subdir = settings->toolchain_output_subdir(); + + std::string result; + if (toolchain_subdir.value().empty()) { + result = GetRootOutputDirWithNoLastSlash(settings); + } else { + // The toolchain subdir ends in a slash, trim it. + result = GetRootOutputDirWithNoLastSlash(settings) + "/" + + toolchain_subdir.value(); + DCHECK(toolchain_subdir.value()[toolchain_subdir.value().size() - 1] == + '/'); + result.resize(result.size() - 1); + } + return result; +} + +// static +std::string ScopePerFileProvider::GetToolchainGenDirWithNoLastSlash( const Settings* settings) { - return GetRootOutputDirWithNoLastSlash(settings) + "/gen"; + return GetToolchainOutputDirWithNoLastSlash(settings) + "/gen"; } std::string ScopePerFileProvider::GetFileDirWithNoLastSlash() const { diff --git a/tools/gn/scope_per_file_provider.h b/tools/gn/scope_per_file_provider.h index d406c2f..362ac6a 100644 --- a/tools/gn/scope_per_file_provider.h +++ b/tools/gn/scope_per_file_provider.h @@ -25,13 +25,17 @@ class ScopePerFileProvider : public Scope::ProgrammaticProvider { const Value* GetCurrentToolchain(); const Value* GetDefaultToolchain(); const Value* GetPythonPath(); + const Value* GetRootBuildDir(); const Value* GetRootGenDir(); const Value* GetRootOutDir(); const Value* GetTargetGenDir(); const Value* GetTargetOutDir(); static std::string GetRootOutputDirWithNoLastSlash(const Settings* settings); - static std::string GetRootGenDirWithNoLastSlash(const Settings* settings); + static std::string GetToolchainOutputDirWithNoLastSlash( + const Settings* settings); + static std::string GetToolchainGenDirWithNoLastSlash( + const Settings* settings); std::string GetFileDirWithNoLastSlash() const; @@ -39,6 +43,7 @@ class ScopePerFileProvider : public Scope::ProgrammaticProvider { scoped_ptr<Value> current_toolchain_; scoped_ptr<Value> default_toolchain_; scoped_ptr<Value> python_path_; + scoped_ptr<Value> root_build_dir_; scoped_ptr<Value> root_gen_dir_; scoped_ptr<Value> root_out_dir_; scoped_ptr<Value> target_gen_dir_; diff --git a/tools/gn/scope_per_file_provider_unittest.cc b/tools/gn/scope_per_file_provider_unittest.cc index 4abada8..1137d3b 100644 --- a/tools/gn/scope_per_file_provider_unittest.cc +++ b/tools/gn/scope_per_file_provider_unittest.cc @@ -20,23 +20,40 @@ TEST(ScopePerFileProvider, Expected) { build_settings.SetBuildDir(SourceDir("//out/Debug/")); - Toolchain toolchain(Label(SourceDir("//toolchain/"), "tc", SourceDir(), "")); - Settings settings(&build_settings, &toolchain, std::string()); - Scope scope(&settings); - scope.set_source_dir(SourceDir("//source/")); - - ScopePerFileProvider provider(&scope); - - EXPECT_EQ("//toolchain:tc", provider.GetProgrammaticValue( - variables::kCurrentToolchain)->string_value()); - EXPECT_EQ("//toolchain:default", provider.GetProgrammaticValue( - variables::kDefaultToolchain)->string_value()); - EXPECT_EQ("//out/Debug/gen",provider.GetProgrammaticValue( - variables::kRootGenDir)->string_value()); - EXPECT_EQ("//out/Debug",provider.GetProgrammaticValue( - variables::kRootOutDir)->string_value()); - EXPECT_EQ("//out/Debug/gen/source",provider.GetProgrammaticValue( - variables::kTargetGenDir)->string_value()); - EXPECT_EQ("//out/Debug/obj/source",provider.GetProgrammaticValue( - variables::kTargetOutDir)->string_value()); +// Prevent horrible wrapping of calls below. +#define GPV(val) provider.GetProgrammaticValue(val)->string_value() + + // Test the default toolchain. + { + Toolchain toolchain(Label(SourceDir("//toolchain/"), "tc")); + Settings settings(&build_settings, &toolchain, std::string()); + + Scope scope(&settings); + scope.set_source_dir(SourceDir("//source/")); + ScopePerFileProvider provider(&scope); + + EXPECT_EQ("//toolchain:tc", GPV(variables::kCurrentToolchain)); + EXPECT_EQ("//toolchain:default", GPV(variables::kDefaultToolchain)); + EXPECT_EQ("//out/Debug", GPV(variables::kRootBuildDir)); + EXPECT_EQ("//out/Debug/gen", GPV(variables::kRootGenDir)); + EXPECT_EQ("//out/Debug", GPV(variables::kRootOutDir)); + EXPECT_EQ("//out/Debug/gen/source", GPV(variables::kTargetGenDir)); + EXPECT_EQ("//out/Debug/obj/source", GPV(variables::kTargetOutDir)); + } + + // Test some with an alternate toolchain. + { + Toolchain toolchain(Label(SourceDir("//toolchain/"), "tc")); + Settings settings(&build_settings, &toolchain, "tc"); + + Scope scope(&settings); + scope.set_source_dir(SourceDir("//source/")); + ScopePerFileProvider provider(&scope); + + EXPECT_EQ("//out/Debug", GPV(variables::kRootBuildDir)); + EXPECT_EQ("//out/Debug/tc/gen", GPV(variables::kRootGenDir)); + EXPECT_EQ("//out/Debug/tc", GPV(variables::kRootOutDir)); + EXPECT_EQ("//out/Debug/tc/gen/source", GPV(variables::kTargetGenDir)); + EXPECT_EQ("//out/Debug/tc/obj/source", GPV(variables::kTargetOutDir)); + } } diff --git a/tools/gn/secondary/base/BUILD.gn b/tools/gn/secondary/base/BUILD.gn index 6455ff2..ca26584 100644 --- a/tools/gn/secondary/base/BUILD.gn +++ b/tools/gn/secondary/base/BUILD.gn @@ -969,8 +969,8 @@ static_library("test_support_base") { if (!is_posix) { sources -= [ - "scoped_locale.cc", - "scoped_locale.h", + "test/scoped_locale.cc", + "test/scoped_locale.h", ] } if (is_ios) { diff --git a/tools/gn/secondary/chrome/BUILD.gn b/tools/gn/secondary/chrome/BUILD.gn index 53d772c..4ba0c43 100644 --- a/tools/gn/secondary/chrome/BUILD.gn +++ b/tools/gn/secondary/chrome/BUILD.gn @@ -183,7 +183,8 @@ static_library("utility") { # Credits ---------------------------------------------------------------------- about_credits_file = "$root_gen_dir/about_credits.html" -build_relative_about_credits_file = to_build_path(about_credits_file) +build_relative_about_credits_file = + rebase_path(about_credits_file, ".", root_build_dir) custom("about_credits") { script = "//tools/licenses.py" @@ -194,7 +195,7 @@ custom("about_credits") { # is added or removed, it will change the result, but there is no way to # express this as a build dependency. We approximate this by depending on # the last change file to force an update whenever the code is updated. - source_deps = [ "//build/utils/LASTCHANGE" ] + source_prereqs = [ "//build/utils/LASTCHANGE" ] hard_dep = true diff --git a/tools/gn/secondary/crypto/ssl/BUILD.gn b/tools/gn/secondary/crypto/ssl/BUILD.gn index 87f80f4..b48f111 100644 --- a/tools/gn/secondary/crypto/ssl/BUILD.gn +++ b/tools/gn/secondary/crypto/ssl/BUILD.gn @@ -9,8 +9,16 @@ config("ssl_preprocessor_flags") { defines = [ "USE_OPENSSL" ] } else if (use_nss) { defines = [ "USE_NSS" ] - } else { - assert(false) # Either OpenSSL or NSS should be defined. + } +} + +# Config for system SSL on Linux. +if (is_linux && use_system_ssl) { + pkg_script = "//build/config/linux/pkg-config.py" + config("system_ssl_config") { + defines = [ "USE_SYSTEM_SSL" ] + cflags = exec_script(pkg_script, [ "--cflags", "nss" ], "list lines") + ldflags = exec_script(pkg_script, [ "--libs", "nss" ], "list lines") } } @@ -18,10 +26,16 @@ config("ssl_preprocessor_flags") { group("metassl") { direct_dependent_configs = [ ":ssl_preprocessor_flags" ] - deps = [] - if (is_linux) { - # TODO(brettw) figure this out. - deps += [ "//net/third_party/nss/ssl" ] + if (use_openssl) { + assert(is_linux) + deps = "//third_party/openssl" + use_openssl = false + } else if (use_system_ssl) { + assert(is_linux) + direct_dependent_configs = ":system_ssl_config" + } else { + deps = [ "//net/third_party/nss/ssl" ] } + forward_dependent_configs_from = deps } diff --git a/tools/gn/secondary/crypto/ssl/flags.gni b/tools/gn/secondary/crypto/ssl/flags.gni index 598ad71..aac6e96 100644 --- a/tools/gn/secondary/crypto/ssl/flags.gni +++ b/tools/gn/secondary/crypto/ssl/flags.gni @@ -2,9 +2,11 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# This file declares build flags for +# This file declares build flags for the SSL library configuration. declare_args() { + # Use OpenSSL instead of NSS. This is used for Android and is experimental + # in other cases (see http://crbug.com/62803). use_openssl = false # Use the built-in system SSL library rather than our tree's libnss/OpenSSL. diff --git a/tools/gn/secondary/skia/BUILD.gn b/tools/gn/secondary/skia/BUILD.gn index a3fd838..7f16db3 100644 --- a/tools/gn/secondary/skia/BUILD.gn +++ b/tools/gn/secondary/skia/BUILD.gn @@ -635,6 +635,7 @@ static_library("skia_library") { # ["exclude", "opts_check_SSE2\\.cpp$"], # ], + includes = [] if (is_win) { includes += [ "//third_party/skia/include/utils/win", diff --git a/tools/gn/secondary/third_party/expat/BUILD.gn b/tools/gn/secondary/third_party/expat/BUILD.gn index 4fcb84e..1ff7b14 100644 --- a/tools/gn/secondary/third_party/expat/BUILD.gn +++ b/tools/gn/secondary/third_party/expat/BUILD.gn @@ -10,4 +10,28 @@ if (is_linux) { group("expat") { direct_dependent_configs = [ ":expat_config" ] } + + # TODO(brettw) Android needs direct dependent includes of + # <android_src>/external/expat/lib +} else { + config("expat_config") { + includes = [ "files/lib" ] + defines = [ "XML_STATIC" ] + } + + static_library("expat") { + sources = [ + "files/lib/expat.h", + "files/lib/xmlparse.c", + "files/lib/xmlrole.c", + "files/lib/xmltok.c", + ] + + defines = [ "_LIB" ] + if (is_win) { + defines += "COMPILED_FROM_DSP" + } else { + defines += "HAVE_EXPAT_CONFIG_H" + } + } } diff --git a/tools/gn/secondary/third_party/icu/BUILD.gn b/tools/gn/secondary/third_party/icu/BUILD.gn index 4c76ffe..1a55536 100644 --- a/tools/gn/secondary/third_party/icu/BUILD.gn +++ b/tools/gn/secondary/third_party/icu/BUILD.gn @@ -404,7 +404,7 @@ if (is_win) { copy("icudata") { external = true sources = [ "windows/icudt.dll" ] - outputs = [ "$root_output_dir/icudt.dll" ] + outputs = [ "$root_out_dir/icudt.dll" ] } } else { static_library("icudata") { diff --git a/tools/gn/secondary/third_party/libusb/BUILD.gn b/tools/gn/secondary/third_party/libusb/BUILD.gn index 9bdd38e..2517df8 100644 --- a/tools/gn/secondary/third_party/libusb/BUILD.gn +++ b/tools/gn/secondary/third_party/libusb/BUILD.gn @@ -50,7 +50,6 @@ static_library("libusb") { configs -= "//build/config/compiler:chromium_code" configs += "//build/config/compiler:no_chromium_code" - configs += "//build/config/linux:udev" direct_dependent_configs = [ ":libusb_config" ] @@ -64,13 +63,6 @@ static_library("libusb") { "POLL_NFDS_TYPE=nfds_t", "THREADS_POSIX=1", ] - } else { - sources -= [ - "src/libusb/os/poll_posix.c", - "src/libusb/os/poll_posix.h", - "src/libusb/os/threads_posix.c", - "src/libusb/os/threads_posix.h", - ] } if (is_mac) { @@ -89,6 +81,7 @@ static_library("libusb") { "USE_UDEV=1", "_GNU_SOURCE=1", ] + configs += "//build/config/linux:udev" } else { sources -= [ "src/libusb/os/linux_udev.c", diff --git a/tools/gn/secondary/tools/grit/grit_rule.gni b/tools/gn/secondary/tools/grit/grit_rule.gni index 3db2c33..32e5409 100644 --- a/tools/gn/secondary/tools/grit/grit_rule.gni +++ b/tools/gn/secondary/tools/grit/grit_rule.gni @@ -19,25 +19,32 @@ template("grit") { "Neither \"sources\" nor \"outputs\" can be defined for the grit " + "template $target_name") - resource_ids = to_build_path("//tools/gritsettings/resource_ids") - output_dir = to_build_path(target_gen_dir) - grit_info_script = to_build_path("//tools/grit/grit_info.py") - source_path = to_build_path(source) + grit_info_script = "//tools/grit/grit_info.py" + + # These are all passed as arguments to the script so have to be relative to + # the build directory. + resource_ids = + rebase_path("//tools/gritsettings/resource_ids", ".", root_build_dir) + output_dir = rebase_path(target_gen_dir, ".", root_build_dir) + source_path = rebase_path(source, ".", root_build_dir) if (!defined(grit_flags)) { grit_flags = [] # These are optional so default to empty list. } - # TODO(brettw) these are wrong because grit outputs the file names relative - # to its current directory (the build output) and we want them relative - # to the current input directory. We need a way to rebase the paths we get - # back. - grit_inputs = exec_script(grit_info_script, + grit_inputs_build_rel = exec_script(grit_info_script, [ "--inputs", source_path, "-f", resource_ids] + grit_flags, "list lines") - grit_outputs = exec_script(grit_info_script, + # The inputs are relative to the current (build) directory, rebase to + # the current one. + grit_inputs = rebase_path(grit_inputs_build_rel, root_build_dir, ".") + + grit_outputs_build_rel = exec_script(grit_info_script, [ "--outputs", "$output_dir", source_path, "-f", resource_ids ] + grit_flags, "list lines") + # The inputs are relative to the current (build) directory, rebase to + # the current one. + grit_outputs = rebase_path(grit_outputs_build_rel, root_build_dir, ".") # The current grit setup makes an file in $target_gen_dir/grit/foo.h that # the source code expects to include via "grit/foo.h". It would be nice to @@ -70,7 +77,6 @@ template("grit") { # Since we generate a file, we need to be run before the targets that # depend on us. hard_dep = true - sources = grit_outputs # Deps set on the template invocation will go on the grit script running diff --git a/tools/gn/settings.cc b/tools/gn/settings.cc index 63b8afb..bfab85d 100644 --- a/tools/gn/settings.cc +++ b/tools/gn/settings.cc @@ -32,7 +32,6 @@ Settings::Settings(const BuildSettings* build_settings, if (!toolchain_output_dir_.is_null()) toolchain_gen_dir_ = SourceDir(toolchain_output_dir_.value() + "gen/"); - #if defined(OS_WIN) target_os_ = WIN; #elif defined(OS_MACOSX) diff --git a/tools/gn/setup.cc b/tools/gn/setup.cc index 3ab34f2..28f8ec6 100644 --- a/tools/gn/setup.cc +++ b/tools/gn/setup.cc @@ -28,7 +28,8 @@ extern const char kDotfile_Help[] = " same as a buildfile, but with very limited build setup-specific\n" " meaning.\n" "\n" - "Variables:\n" + "Variables\n" + "\n" " buildconfig [required]\n" " Label of the build config file. This file will be used to setup\n" " the build file execution environment for each toolchain.\n" @@ -45,7 +46,7 @@ extern const char kDotfile_Help[] = "\n" " The secondary source root must be inside the main source tree.\n" "\n" - "Example .gn file contents:\n" + "Example .gn file contents\n" "\n" " buildconfig = \"//build/config/BUILDCONFIG.gn\"\n" "\n" @@ -110,8 +111,8 @@ bool Setup::DoSetup() { // FIXME(brettw) get python path! #if defined(OS_WIN) - build_settings_.set_python_path( - base::FilePath(FILE_PATH_LITERAL("cmd.exe /c python"))); + build_settings_.set_python_path(base::FilePath( + FILE_PATH_LITERAL("python.exe"))); #else build_settings_.set_python_path(base::FilePath(FILE_PATH_LITERAL("python"))); #endif diff --git a/tools/gn/variables.cc b/tools/gn/variables.cc index 499ba6b..baacb3c 100644 --- a/tools/gn/variables.cc +++ b/tools/gn/variables.cc @@ -97,6 +97,18 @@ const char kPythonPath_Help[] = " requires Python. You will normally not need this when invoking scripts\n" " since GN automatically finds it for you.\n"; +const char kRootBuildDir[] = "root_build_dir"; +const char kRootBuildDir_HelpShort[] = + "root_build_dir: [string] Directory where build commands are run."; +const char kRootBuildDir_Help[] = + "root_build_dir: [string] Directory where build commands are run.\n" + "\n" + " This is the root build output directory which will be the current\n" + " directory when executing all compilers and scripts.\n" + "\n" + " Most often this is used with rebase_path (see \"gn help rebase_path\")\n" + " to convert arguments to be relative to a script's current directory.\n"; + const char kRootGenDir[] = "root_gen_dir"; const char kRootGenDir_HelpShort[] = "root_gen_dir: [string] Directory for the toolchain's generated files."; @@ -647,6 +659,7 @@ const VariableInfoMap& GetBuiltinVariables() { INSERT_VARIABLE(IsPosix) INSERT_VARIABLE(IsWin) INSERT_VARIABLE(PythonPath) + INSERT_VARIABLE(RootBuildDir) INSERT_VARIABLE(RootGenDir) INSERT_VARIABLE(RootOutDir) INSERT_VARIABLE(TargetGenDir) diff --git a/tools/gn/variables.h b/tools/gn/variables.h index ab27eafa..703ebde 100644 --- a/tools/gn/variables.h +++ b/tools/gn/variables.h @@ -45,6 +45,10 @@ extern const char kPythonPath[]; extern const char kPythonPath_HelpShort[]; extern const char kPythonPath_Help[]; +extern const char kRootBuildDir[]; +extern const char kRootBuildDir_HelpShort[]; +extern const char kRootBuildDir_Help[]; + extern const char kRootGenDir[]; extern const char kRootGenDir_HelpShort[]; extern const char kRootGenDir_Help[]; |