summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-29 23:30:07 +0000
committerbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-29 23:30:07 +0000
commitc88bd8f2c08838c6730b946dc4a50d4386ef43f9 (patch)
treee9264e0a99f419ab9d52e864acd38bb06a579c3a
parent6e778e0ad540ff7abf5252c8af25346aec6b3371 (diff)
downloadchromium_src-c88bd8f2c08838c6730b946dc4a50d4386ef43f9.zip
chromium_src-c88bd8f2c08838c6730b946dc4a50d4386ef43f9.tar.gz
chromium_src-c88bd8f2c08838c6730b946dc4a50d4386ef43f9.tar.bz2
Add initial prototype for the GN meta-buildsystem.
This is currently not hooked into the build. To build, add a reference to the gn.gyp file to build/all.gyp R=darin@chromium.org, scottmg@chromium.org Review URL: https://codereview.chromium.org/21114002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@214254 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--.gn8
-rw-r--r--tools/gn/BUILD.gn153
-rw-r--r--tools/gn/OWNERS1
-rw-r--r--tools/gn/README.txt7
-rw-r--r--tools/gn/build_settings.cc44
-rw-r--r--tools/gn/build_settings.h110
-rw-r--r--tools/gn/command_desc.cc201
-rw-r--r--tools/gn/command_gen.cc66
-rw-r--r--tools/gn/commands.h18
-rw-r--r--tools/gn/config.cc78
-rw-r--r--tools/gn/config.h45
-rw-r--r--tools/gn/config_values.cc11
-rw-r--r--tools/gn/config_values.h50
-rw-r--r--tools/gn/config_values_extractors.cc22
-rw-r--r--tools/gn/config_values_extractors.h50
-rw-r--r--tools/gn/config_values_generator.cc89
-rw-r--r--tools/gn/config_values_generator.h48
-rw-r--r--tools/gn/err.cc196
-rw-r--r--tools/gn/err.h85
-rw-r--r--tools/gn/escape.cc77
-rw-r--r--tools/gn/escape.h53
-rw-r--r--tools/gn/escape_unittest.cc4
-rw-r--r--tools/gn/file_template.cc125
-rw-r--r--tools/gn/file_template.h74
-rw-r--r--tools/gn/file_template_unittest.cc45
-rw-r--r--tools/gn/filesystem_utils.cc350
-rw-r--r--tools/gn/filesystem_utils.h115
-rw-r--r--tools/gn/filesystem_utils_unittest.cc146
-rw-r--r--tools/gn/function_define_rule.cc37
-rw-r--r--tools/gn/function_exec_script.cc254
-rw-r--r--tools/gn/function_process_file_template.cc65
-rw-r--r--tools/gn/function_read_file.cc67
-rw-r--r--tools/gn/function_set_default_toolchain.cc70
-rw-r--r--tools/gn/function_template.cc38
-rw-r--r--tools/gn/function_toolchain.cc126
-rw-r--r--tools/gn/function_write_file.cc84
-rw-r--r--tools/gn/functions.cc443
-rw-r--r--tools/gn/functions.h182
-rw-r--r--tools/gn/functions_target.cc221
-rw-r--r--tools/gn/generate_test_gn_data.cc129
-rw-r--r--tools/gn/gn.gyp171
-rw-r--r--tools/gn/gn_main.cc59
-rw-r--r--tools/gn/import_manager.cc83
-rw-r--r--tools/gn/import_manager.h41
-rw-r--r--tools/gn/input_conversion.cc205
-rw-r--r--tools/gn/input_conversion.h26
-rw-r--r--tools/gn/input_conversion_unittest.cc81
-rw-r--r--tools/gn/input_file.cc30
-rw-r--r--tools/gn/input_file.h52
-rw-r--r--tools/gn/input_file_manager.cc254
-rw-r--r--tools/gn/input_file_manager.h123
-rw-r--r--tools/gn/item.cc31
-rw-r--r--tools/gn/item.h45
-rw-r--r--tools/gn/item_node.cc51
-rw-r--r--tools/gn/item_node.h119
-rw-r--r--tools/gn/item_tree.cc193
-rw-r--r--tools/gn/item_tree.h69
-rw-r--r--tools/gn/label.cc263
-rw-r--r--tools/gn/label.h116
-rw-r--r--tools/gn/label_unittest.cc88
-rw-r--r--tools/gn/location.h77
-rw-r--r--tools/gn/ninja_build_writer.cc165
-rw-r--r--tools/gn/ninja_build_writer.h53
-rw-r--r--tools/gn/ninja_helper.cc165
-rw-r--r--tools/gn/ninja_helper.h71
-rw-r--r--tools/gn/ninja_helper_unittest.cc73
-rw-r--r--tools/gn/ninja_target_writer.cc550
-rw-r--r--tools/gn/ninja_target_writer.h67
-rw-r--r--tools/gn/ninja_toolchain_writer.cc94
-rw-r--r--tools/gn/ninja_toolchain_writer.h46
-rw-r--r--tools/gn/ninja_writer.cc64
-rw-r--r--tools/gn/ninja_writer.h27
-rw-r--r--tools/gn/operators.cc573
-rw-r--r--tools/gn/operators.h35
-rw-r--r--tools/gn/output_file.h41
-rw-r--r--tools/gn/output_stream.h42
-rw-r--r--tools/gn/parse_tree.cc472
-rw-r--r--tools/gn/parse_tree.h366
-rw-r--r--tools/gn/parser.cc470
-rw-r--r--tools/gn/parser.h81
-rw-r--r--tools/gn/parser_unittest.cc329
-rw-r--r--tools/gn/path_output.cc116
-rw-r--r--tools/gn/path_output.h80
-rw-r--r--tools/gn/path_output_unittest.cc193
-rw-r--r--tools/gn/pattern.cc185
-rw-r--r--tools/gn/pattern.h86
-rw-r--r--tools/gn/pattern_unittest.cc61
-rw-r--r--tools/gn/scheduler.cc130
-rw-r--r--tools/gn/scheduler.h90
-rw-r--r--tools/gn/scope.cc372
-rw-r--r--tools/gn/scope.h260
-rw-r--r--tools/gn/scope_per_file_provider.cc186
-rw-r--r--tools/gn/scope_per_file_provider.h79
-rw-r--r--tools/gn/secondary/base/BUILD.gn919
-rw-r--r--tools/gn/secondary/base/allocator/BUILD.gn6
-rw-r--r--tools/gn/secondary/base/third_party/dynamic_annotations/BUILD.gn7
-rw-r--r--tools/gn/secondary/build/config/BUILD.gn52
-rw-r--r--tools/gn/secondary/build/config/BUILDCONFIG.gn187
-rw-r--r--tools/gn/secondary/build/config/compiler/BUILD.gn134
-rw-r--r--tools/gn/secondary/build/config/win/BUILD.gn184
-rw-r--r--tools/gn/secondary/build/config/win/get_msvc_config.py77
-rw-r--r--tools/gn/secondary/build/config/win/get_msvc_config_real.py575
-rw-r--r--tools/gn/secondary/ipc/BUILD.gn168
-rw-r--r--tools/gn/secondary/testing/BUILD.gn44
-rw-r--r--tools/gn/secondary/third_party/modp_b64/BUILD.gn11
-rw-r--r--tools/gn/settings.cc38
-rw-r--r--tools/gn/settings.h107
-rw-r--r--tools/gn/setup.cc195
-rw-r--r--tools/gn/setup.h69
-rw-r--r--tools/gn/source_dir.cc98
-rw-r--r--tools/gn/source_dir.h104
-rw-r--r--tools/gn/source_dir_unittest.cc45
-rw-r--r--tools/gn/source_file.cc67
-rw-r--r--tools/gn/source_file.h97
-rw-r--r--tools/gn/standard_out.cc84
-rw-r--r--tools/gn/standard_out.h22
-rw-r--r--tools/gn/string_utils.cc168
-rw-r--r--tools/gn/string_utils.h51
-rw-r--r--tools/gn/string_utils_unittest.cc71
-rw-r--r--tools/gn/target.cc94
-rw-r--r--tools/gn/target.h145
-rw-r--r--tools/gn/target_generator.cc334
-rw-r--r--tools/gn/target_generator.h83
-rw-r--r--tools/gn/target_generator_unittest.cc8
-rw-r--r--tools/gn/target_manager.cc134
-rw-r--r--tools/gn/target_manager.h73
-rw-r--r--tools/gn/target_manager_unittest.cc95
-rw-r--r--tools/gn/token.cc60
-rw-r--r--tools/gn/token.h52
-rw-r--r--tools/gn/tokenizer.cc309
-rw-r--r--tools/gn/tokenizer.h86
-rw-r--r--tools/gn/tokenizer_unittest.cc162
-rw-r--r--tools/gn/toolchain.cc79
-rw-r--r--tools/gn/toolchain.h94
-rw-r--r--tools/gn/toolchain_manager.cc480
-rw-r--r--tools/gn/toolchain_manager.h167
-rw-r--r--tools/gn/value.cc126
-rw-r--r--tools/gn/value.h91
-rw-r--r--tools/gn/value_extractors.cc102
-rw-r--r--tools/gn/value_extractors.h57
140 files changed, 18197 insertions, 0 deletions
diff --git a/.gn b/.gn
new file mode 100644
index 0000000..aaaf5e0
--- /dev/null
+++ b/.gn
@@ -0,0 +1,8 @@
+# This file is used by the experimental meta-buildsystem in src/tools/gn to
+# find the root of the source tree and to set startup options.
+
+# The location of the build configuration file.
+buildconfig = "//build/config/BUILDCONFIG.gn"
+
+# The secondary source root is a parallel directory tree where
+secondary_source = "//tools/gn/secondary/"
diff --git a/tools/gn/BUILD.gn b/tools/gn/BUILD.gn
new file mode 100644
index 0000000..ccb5411
--- /dev/null
+++ b/tools/gn/BUILD.gn
@@ -0,0 +1,153 @@
+static_library("gn_lib") {
+ sources = [
+ "build_settings.cc",
+ "build_settings.h",
+ "command_desc.cc",
+ "command_desc.h",
+ "command_gen.cc",
+ "command_gen.h",
+ "command.cc",
+ "config.cc",
+ "config.h",
+ "config_values.h",
+ "config_values_extractors.cc",
+ "config_values_extractors.h",
+ "config_values_generator.cc",
+ "config_values_generator.h",
+ "err.cc",
+ "err.h",
+ "escape.cc",
+ "escape.h",
+ "file_template.cc",
+ "file_template.h",
+ "filesystem_utils.cc",
+ "filesystem_utils.h",
+ "functions.cc",
+ "functions.h",
+ "functions_target.cc",
+ "function_exec_script.cc",
+ "function_process_file_template.cc",
+ "function_read_file.cc",
+ "function_set_default_toolchain.cc",
+ "function_template.cc",
+ "function_toolchain.cc",
+ "function_write_file.cc",
+ "import_manager.cc",
+ "import_manager.h",
+ "input_conversion.cc",
+ "input_conversion.h",
+ "input_file.cc",
+ "input_file.h",
+ "input_file_manager.cc",
+ "input_file_manager.h",
+ "item.cc",
+ "item.h",
+ "item_node.cc",
+ "item_node.h",
+ "item_tree.cc",
+ "item_tree.h",
+ "label.cc",
+ "label.h",
+ "location.h",
+ "ninja_build_writer.cc",
+ "ninja_build_writer.h",
+ "ninja_helper.cc",
+ "ninja_helper.h",
+ "ninja_target_writer.cc",
+ "ninja_target_writer.h",
+ "ninja_toolchain_writer.cc",
+ "ninja_toolchain_writer.h",
+ "ninja_writer.cc",
+ "ninja_writer.h",
+ "operators.cc",
+ "operators.h",
+ "output_file.h",
+ "parse_tree.cc",
+ "parse_tree.h",
+ "parser.cc",
+ "parser.h",
+ "path_output.cc",
+ "path_output.h",
+ "pattern.cc",
+ "pattern.h",
+ "scheduler.cc",
+ "scheduler.h",
+ "scope.cc",
+ "scope.h",
+ "scope_per_file_provider.cc",
+ "scope_per_file_provider.h",
+ "settings.cc",
+ "settings.h",
+ "setup.cc",
+ "setup.h",
+ "source_dir.cc",
+ "source_dir.h",
+ "source_file.cc",
+ "source_file.h",
+ "standard_out.cc",
+ "standard_out.h",
+ "string_utils.cc",
+ "string_utils.h",
+ "target.cc",
+ "target.h",
+ "target_generator.cc",
+ "target_generator.h",
+ "target_manager.cc",
+ "target_manager.h",
+ "token.cc",
+ "token.h",
+ "tokenizer.cc",
+ "tokenizer.h",
+ "toolchain.cc",
+ "toolchain.h",
+ "toolchain_manager.cc",
+ "toolchain_manager.h",
+ "value.cc",
+ "value.h",
+ "value_extractors.cc",
+ "value_extractors.h",
+ ]
+ deps = [
+ "//base",
+ "//base/third_party/dynamic_annotations",
+ ]
+}
+
+executable("gn") {
+ sources = [
+ "gn_main.cc",
+ ]
+ deps = [
+ ":gn_lib",
+ ]
+}
+
+test("gn_unittests") {
+ sources = [
+ "escape_unittest.cc",
+ "file_template_unittest.cc",
+ "filesystem_utils_unittest.cc",
+ "input_conversion_unittest.cc",
+ "label_unittest.cc",
+ "ninja_helper_unittest.cc",
+ "parser_unittest.cc",
+ "path_output_unittest.cc",
+ "pattern_unittest.cc",
+ "source_dir_unittest.cc",
+ "string_utils_unittest.cc",
+ "target_generator_unittest.cc",
+ "target_manager_unittest.cc",
+ "tokenizer_unittest.cc",
+ ]
+ deps = [
+ ":gn_lib",
+ "//base:run_all_unittests",
+ "//base:test_support_base",
+ "//testing:gtest",
+ ]
+}
+
+executable("generate_test_gn_data") {
+ sources = [ "generate_test_gn_data.cc" ]
+ deps = [ "//base" ]
+}
diff --git a/tools/gn/OWNERS b/tools/gn/OWNERS
new file mode 100644
index 0000000..06fefbf
--- /dev/null
+++ b/tools/gn/OWNERS
@@ -0,0 +1 @@
+brettw@chromium.org
diff --git a/tools/gn/README.txt b/tools/gn/README.txt
new file mode 100644
index 0000000..0a637bf
--- /dev/null
+++ b/tools/gn/README.txt
@@ -0,0 +1,7 @@
+GN "Generate Ninja"
+
+This tool is an experimental metabuildsystem. It is not currently in a state
+where it is ready for public consumption.
+
+It is not currently used in the build and there are currently no plans to
+replace GYP.
diff --git a/tools/gn/build_settings.cc b/tools/gn/build_settings.cc
new file mode 100644
index 0000000..09b4c99
--- /dev/null
+++ b/tools/gn/build_settings.cc
@@ -0,0 +1,44 @@
+// 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"
+
+BuildSettings::BuildSettings()
+ : item_tree_(),
+ target_manager_(this),
+ toolchain_manager_(this) {
+}
+
+BuildSettings::~BuildSettings() {
+}
+
+void BuildSettings::SetSecondarySourcePath(const SourceDir& d) {
+ secondary_source_path_ = GetFullPath(d);
+}
+
+void BuildSettings::SetBuildDir(const SourceDir& d) {
+ build_dir_ = d;
+ build_to_source_dir_string_ = InvertDir(d);
+}
+
+base::FilePath BuildSettings::GetFullPath(const SourceFile& file) const {
+ return file.Resolve(root_path_);
+}
+
+base::FilePath BuildSettings::GetFullPath(const SourceDir& dir) const {
+ return dir.Resolve(root_path_);
+}
+
+base::FilePath BuildSettings::GetFullPathSecondary(
+ const SourceFile& file) const {
+ return file.Resolve(secondary_source_path_);
+}
+
+base::FilePath BuildSettings::GetFullPathSecondary(
+ const SourceDir& dir) const {
+ return dir.Resolve(secondary_source_path_);
+}
+
diff --git a/tools/gn/build_settings.h b/tools/gn/build_settings.h
new file mode 100644
index 0000000..fe04266
--- /dev/null
+++ b/tools/gn/build_settings.h
@@ -0,0 +1,110 @@
+// 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.
+
+#ifndef TOOLS_GN_BUILD_SETTINGS_H_
+#define TOOLS_GN_BUILD_SETTINGS_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "tools/gn/item_tree.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/source_dir.h"
+#include "tools/gn/source_file.h"
+#include "tools/gn/target_manager.h"
+#include "tools/gn/toolchain_manager.h"
+
+// Settings for one build, which is one toplevel output directory. There
+// may be multiple Settings objects that refer to this, one for each toolchain.
+class BuildSettings {
+ public:
+ typedef base::Callback<void(const Target*)> TargetResolvedCallback;
+
+ BuildSettings();
+ ~BuildSettings();
+
+ // Absolute path of the source root on the local system. Everything is
+ // relative to this.
+ const base::FilePath& root_path() const { return root_path_; }
+ void set_root_path(const base::FilePath& r) { root_path_ = r; }
+
+ // When nonempty, specifies a parallel directory higherarchy in which to
+ // search for buildfiles if they're not found in the root higherarchy. This
+ // allows us to keep buildfiles in a separate tree during development.
+ const base::FilePath& secondary_source_path() const {
+ return secondary_source_path_;
+ }
+ void SetSecondarySourcePath(const SourceDir& d);
+
+ // Path of the python executable to run scripts with.
+ base::FilePath python_path() const { return python_path_; }
+ void set_python_path(const base::FilePath& p) { python_path_ = p; }
+
+ const SourceFile& build_config_file() const { return build_config_file_; }
+ void set_build_config_file(const SourceFile& f) { build_config_file_ = f; }
+
+ // The build directory is the root of all output files. The default toolchain
+ // files go into here, and non-default toolchains will have separate
+ // toolchain-specific root directories inside this.
+ const SourceDir& build_dir() const { return build_dir_; }
+ void SetBuildDir(const SourceDir& dir);
+
+ // The inverse of relative_build_dir, ending with a separator.
+ // Example: relative_build_dir_ = "out/Debug/" this will be "../../"
+ const std::string& build_to_source_dir_string() const {
+ return build_to_source_dir_string_;
+ }
+
+ // These accessors do not return const objects since the resulting objects
+ // are threadsafe. In this setting, we use constness primarily to ensure
+ // that the Settings object is used in a threadsafe manner. Although this
+ // violates the concept of logical constness, that's less important in our
+ // application, and actually implementing this in a way that preserves
+ // logical constness is cumbersome.
+ ItemTree& item_tree() const { return item_tree_; }
+ TargetManager& target_manager() const { return target_manager_; }
+ ToolchainManager& toolchain_manager() const { return toolchain_manager_; }
+
+ // Returns the full absolute OS path cooresponding to the given file in the
+ // root source tree.
+ base::FilePath GetFullPath(const SourceFile& file) const;
+ base::FilePath GetFullPath(const SourceDir& dir) const;
+
+ // Returns the absolute OS path inside the secondary source path. Will return
+ // an empty FilePath if the secondary source path is empty. When loading a
+ // buildfile, the GetFullPath should always be consulted first.
+ base::FilePath GetFullPathSecondary(const SourceFile& file) const;
+ base::FilePath GetFullPathSecondary(const SourceDir& dir) const;
+
+ // This is the callback to execute when a target is marked resolved. If we
+ // don't need to do anything, this will be null. When a target is resolved,
+ // this callback should be posted to the scheduler pool so the work is
+ // distributed properly.
+ const TargetResolvedCallback& target_resolved_callback() const {
+ return target_resolved_callback_;
+ }
+ void set_target_resolved_callback(const TargetResolvedCallback& cb) {
+ target_resolved_callback_ = cb;
+ }
+
+ private:
+ base::FilePath root_path_;
+ base::FilePath secondary_source_path_;
+ base::FilePath python_path_;
+
+ SourceFile build_config_file_;
+ SourceDir build_dir_;
+ std::string build_to_source_dir_string_;
+
+ TargetResolvedCallback target_resolved_callback_;
+
+ // See getters above.
+ mutable ItemTree item_tree_;
+ mutable TargetManager target_manager_;
+ mutable ToolchainManager toolchain_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(BuildSettings);
+};
+
+#endif // TOOLS_GN_BUILD_SETTINGS_H_
diff --git a/tools/gn/command_desc.cc b/tools/gn/command_desc.cc
new file mode 100644
index 0000000..bf91776
--- /dev/null
+++ b/tools/gn/command_desc.cc
@@ -0,0 +1,201 @@
+// 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 <algorithm>
+#include <set>
+
+#include "tools/gn/commands.h"
+#include "tools/gn/config.h"
+#include "tools/gn/item.h"
+#include "tools/gn/item_node.h"
+#include "tools/gn/label.h"
+#include "tools/gn/setup.h"
+#include "tools/gn/standard_out.h"
+#include "tools/gn/target.h"
+
+namespace {
+
+struct CompareTargetLabel {
+ bool operator()(const Target* a, const Target* b) const {
+ return a->label() < b->label();
+ }
+};
+
+const Target* GetTargetForDesc(const std::vector<std::string>& args) {
+ // Deliberately leaked to avoid expensive process teardown.
+ Setup* setup = new Setup;
+ if (!setup->DoSetup())
+ return NULL;
+
+ // FIXME(brettw): set the output dir to be a sandbox one to avoid polluting
+ // the real output dir with files written by the build scripts.
+
+ // Do the actual load. This will also write out the target ninja files.
+ if (!setup->Run())
+ return NULL;
+
+ // Need to resolve the label after we know the default toolchain.
+ // TODO(brettw) find the current directory and resolve the input label
+ // relative to that.
+ Label default_toolchain = setup->build_settings().toolchain_manager()
+ .GetDefaultToolchainUnlocked();
+ Value arg_value(NULL, args[0]);
+ Err err;
+ Label label = Label::Resolve(SourceDir(), default_toolchain, arg_value, &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return NULL;
+ }
+
+ ItemNode* node;
+ {
+ base::AutoLock lock(setup->build_settings().item_tree().lock());
+ node = setup->build_settings().item_tree().GetExistingNodeLocked(label);
+ }
+ if (!node) {
+ Err(Location(), "",
+ "I don't know about this \"" + label.GetUserVisibleName(false) +
+ "\"").PrintToStdout();
+ return NULL;
+ }
+
+ const Target* target = node->item()->AsTarget();
+ if (!target) {
+ Err(Location(), "Not a target.",
+ "The \"" + label.GetUserVisibleName(false) + "\" thing\n"
+ "is not a target. Somebody should probably implement this command for "
+ "other\nitem types.");
+ return NULL;
+ }
+
+ return target;
+}
+
+void RecursiveCollectDeps(const Target* target, std::set<Label>* result) {
+ if (result->find(target->label()) != result->end())
+ return; // Already did this target.
+ result->insert(target->label());
+
+ const std::vector<const Target*>& deps = target->deps();
+ for (size_t i = 0; i < deps.size(); i++)
+ RecursiveCollectDeps(deps[i], result);
+}
+
+// Prints dependencies of the given target (not the target itself).
+void RecursivePrintDeps(const Target* target,
+ const Label& default_toolchain,
+ int indent_level) {
+ std::vector<const Target*> sorted_deps = target->deps();
+ std::sort(sorted_deps.begin(), sorted_deps.end(), CompareTargetLabel());
+
+ std::string indent(indent_level * 2, ' ');
+ for (size_t i = 0; i < sorted_deps.size(); i++) {
+ OutputString(indent +
+ sorted_deps[i]->label().GetUserVisibleName(default_toolchain) + "\n");
+ RecursivePrintDeps(sorted_deps[i], default_toolchain, indent_level + 1);
+ }
+}
+
+} // namespace
+
+int RunDescCommand(const std::vector<std::string>& args) {
+ if (args.size() != 1) {
+ Err(Location(), "You're holding it wrong.",
+ "Usage: \"gn desc <target_name>\"").PrintToStdout();
+ return NULL;
+ }
+
+ const Target* target = GetTargetForDesc(args);
+ if (!target)
+ return 1;
+
+ // Generally we only want to display toolchains on labels when the toolchain
+ // is different than the default one for this target (which we always print
+ // in the header).
+ Label target_toolchain = target->label().GetToolchainLabel();
+
+ // Header.
+ std::string title_target =
+ "Target: " + target->label().GetUserVisibleName(false);
+ std::string title_toolchain =
+ "Toolchain: " + target_toolchain.GetUserVisibleName(false);
+ OutputString(title_target + "\n", DECORATION_YELLOW);
+ OutputString(title_toolchain + "\n", DECORATION_YELLOW);
+ OutputString(std::string(
+ std::max(title_target.size(), title_toolchain.size()), '=') + "\n");
+
+ OutputString("Sources:\n");
+ const Target::FileList& sources = target->sources();
+ for (size_t i = 0; i < sources.size(); i++)
+ OutputString(" " + sources[i].value() + "\n");
+
+ // Configs (don't sort since the order determines how things are processed).
+ OutputString("\nConfigs:\n");
+ const std::vector<const Config*>& configs = target->configs();
+ for (size_t i = 0; i < configs.size(); i++) {
+ OutputString(" " +
+ configs[i]->label().GetUserVisibleName(target_toolchain) + "\n");
+ }
+
+ // Deps. Sorted for convenience. Sort the labels rather than the strings so
+ // that "//foo:bar" comes before "//foo/third_party:bar".
+ OutputString("\nDirect dependencies:\n"
+ "(Use \"gn deps\" or \"gn tree\" to display recursive deps.)\n");
+ const std::vector<const Target*>& deps = target->deps();
+ std::vector<Label> sorted_deps;
+ for (size_t i = 0; i < deps.size(); i++)
+ sorted_deps.push_back(deps[i]->label());
+ std::sort(sorted_deps.begin(), sorted_deps.end());
+ for (size_t i = 0; i < sorted_deps.size(); i++) {
+ OutputString(" " + sorted_deps[i].GetUserVisibleName(target_toolchain) +
+ "\n");
+ }
+ return 0;
+}
+
+int RunDepsCommand(const std::vector<std::string>& args) {
+ if (args.size() != 1) {
+ Err(Location(), "You're holding it wrong.",
+ "Usage: \"gn deps <target_name>\"").PrintToStdout();
+ return NULL;
+ }
+
+ const Target* target = GetTargetForDesc(args);
+ if (!target)
+ return 1;
+
+ // Generally we only want to display toolchains on labels when the toolchain
+ // is different than the default one for this target (which we always print
+ // in the header).
+ Label target_toolchain = target->label().GetToolchainLabel();
+
+ std::set<Label> all_deps;
+ RecursiveCollectDeps(target, &all_deps);
+
+ OutputString("Recursive dependencies of " +
+ target->label().GetUserVisibleName(true) + "\n",
+ DECORATION_YELLOW);
+
+ for (std::set<Label>::iterator i = all_deps.begin();
+ i != all_deps.end(); ++i)
+ OutputString(" " + i->GetUserVisibleName(target_toolchain) + "\n");
+ return 0;
+}
+
+int RunTreeCommand(const std::vector<std::string>& args) {
+ if (args.size() != 1) {
+ Err(Location(), "You're holding it wrong.",
+ "Usage: \"gn tree <target_name>\"").PrintToStdout();
+ return NULL;
+ }
+
+ const Target* target = GetTargetForDesc(args);
+ if (!target)
+ return 1;
+
+ OutputString(target->label().GetUserVisibleName(false) + "\n");
+ RecursivePrintDeps(target, target->label().GetToolchainLabel(), 1);
+
+ return 0;
+}
diff --git a/tools/gn/command_gen.cc b/tools/gn/command_gen.cc
new file mode 100644
index 0000000..ab64a8d
--- /dev/null
+++ b/tools/gn/command_gen.cc
@@ -0,0 +1,66 @@
+// 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 "base/bind.h"
+#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/commands.h"
+#include "tools/gn/ninja_target_writer.h"
+#include "tools/gn/ninja_writer.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/setup.h"
+#include "tools/gn/standard_out.h"
+
+namespace {
+
+// Suppress output on success.
+const char kSwitchQuiet[] = "q";
+
+} // namespace
+
+int RunGenCommand(const std::vector<std::string>& args) {
+ base::TimeTicks begin_time = base::TimeTicks::Now();
+
+ // Deliberately leaked to avoid expensive process teardown.
+ Setup* setup = new Setup;
+ if (!setup->DoSetup())
+ return 1;
+
+ // Cause the load to also generate the ninja files for each target.
+ setup->build_settings().set_target_resolved_callback(
+ base::Bind(&NinjaTargetWriter::RunAndWriteFile));
+
+ // Do the actual load. This will also write out the target ninja files.
+ if (!setup->Run())
+ return 1;
+
+ // Write the root ninja files.
+ if (!NinjaWriter::RunAndWriteFiles(&setup->build_settings())) {
+ Err(Location(),
+ "Couldn't open root buildfile(s) for writing").PrintToStdout();
+ return 1;
+ }
+
+ base::TimeTicks end_time = base::TimeTicks::Now();
+
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(kSwitchQuiet)) {
+ OutputString("Done. ", DECORATION_GREEN);
+
+ // TODO(brettw) get the number of targets without getting the entire list.
+ std::vector<const Target*> all_targets;
+ setup->build_settings().target_manager().GetAllTargets(&all_targets);
+ std::string stats = "Generated " +
+ base::IntToString(static_cast<int>(all_targets.size())) +
+ " targets from " +
+ base::IntToString(
+ setup->scheduler().input_file_manager()->GetInputFileCount()) +
+ " files in " +
+ base::IntToString((end_time - begin_time).InMilliseconds()) + "ms\n";
+ OutputString(stats);
+ }
+
+ return 0;
+}
diff --git a/tools/gn/commands.h b/tools/gn/commands.h
new file mode 100644
index 0000000..1d4449c
--- /dev/null
+++ b/tools/gn/commands.h
@@ -0,0 +1,18 @@
+// 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.
+
+#ifndef TOOLS_GN_COMMANDS_H_
+#define TOOLS_GN_COMMANDS_H_
+
+#include <string>
+#include <vector>
+
+// The different commands we have, returns the value we should return from
+// main().
+int RunDepsCommand(const std::vector<std::string>& args);
+int RunDescCommand(const std::vector<std::string>& args);
+int RunGenCommand(const std::vector<std::string>& args);
+int RunTreeCommand(const std::vector<std::string>& args);
+
+#endif // TOOLS_GN_COMMANDS_H_
diff --git a/tools/gn/config.cc b/tools/gn/config.cc
new file mode 100644
index 0000000..1175d7f
--- /dev/null
+++ b/tools/gn/config.cc
@@ -0,0 +1,78 @@
+// 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/config.h"
+
+#include "tools/gn/err.h"
+#include "tools/gn/input_file_manager.h"
+#include "tools/gn/item_node.h"
+#include "tools/gn/item_tree.h"
+#include "tools/gn/scheduler.h"
+
+Config::Config(const Label& label) : Item(label) {
+}
+
+Config::~Config() {
+}
+
+Config* Config::AsConfig() {
+ return this;
+}
+
+const Config* Config::AsConfig() const {
+ return this;
+}
+
+// static
+Config* Config::GetConfig(const Settings* settings,
+ const LocationRange& specified_from_here,
+ const Label& label,
+ Item* dep_from,
+ Err* err) {
+ DCHECK(!label.is_null());
+
+ ItemTree* tree = &settings->build_settings()->item_tree();
+ base::AutoLock lock(tree->lock());
+
+ ItemNode* node = tree->GetExistingNodeLocked(label);
+ Config* config = NULL;
+ if (!node) {
+ config = new Config(label);
+ node = new ItemNode(config);
+ tree->AddNodeLocked(node);
+
+ // Only schedule loading the given target if somebody is depending on it
+ // (and we optimize by not re-asking it to run the current file).
+ // Otherwise, we're probably generating it right now.
+ if (dep_from && dep_from->label().dir() != label.dir()) {
+ settings->build_settings()->toolchain_manager().ScheduleInvocationLocked(
+ specified_from_here, label.GetToolchainLabel(), label.dir(),
+ err);
+ }
+ } else if ((config = node->item()->AsConfig())) {
+ // Previously saw this item as a config.
+
+ // If we have no dep_from, we're generating it. In this case, it had better
+ // not already be generated.
+ if (!dep_from && node->state() != ItemNode::REFERENCED) {
+ *err = Err(specified_from_here, "Duplicate config definition.",
+ "You already told me about a config with this name.");
+ return NULL;
+ }
+ } else {
+ // Previously saw this thing as a non-config.
+ *err = Err(specified_from_here,
+ "Config name already used.",
+ "Previously you specified a " +
+ node->item()->GetItemTypeName() + " with this name instead.");
+ return NULL;
+ }
+
+ // Keep a record of the guy asking us for this dependency. We know if
+ // somebody is adding a dependency, that guy it himself not resolved.
+ if (dep_from && node->state() != ItemNode::RESOLVED)
+ tree->GetExistingNodeLocked(dep_from->label())->AddDependency(node);
+
+ return config;
+}
diff --git a/tools/gn/config.h b/tools/gn/config.h
new file mode 100644
index 0000000..0ee9b7b
--- /dev/null
+++ b/tools/gn/config.h
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef TOOLS_GN_CONFIG_H_
+#define TOOLS_GN_CONFIG_H_
+
+#include "base/compiler_specific.h"
+#include "tools/gn/config_values.h"
+#include "tools/gn/item.h"
+
+class Err;
+class ItemTree;
+class LocationRange;
+class Settings;
+
+// Represents a named config in the dependency graph.
+class Config : public Item {
+ public:
+ Config(const Label& label);
+ virtual ~Config();
+
+ virtual Config* AsConfig() OVERRIDE;
+ virtual const Config* AsConfig() const OVERRIDE;
+
+ ConfigValues& config_values() { return config_values_; }
+ const ConfigValues& config_values() const { return config_values_; }
+
+ // Gets or creates a config.
+ //
+ // This is like the TargetManager is for Targets, but Configs are so much
+ // simpler that this one function is all we need.
+ static Config* GetConfig(const Settings* settings,
+ const LocationRange& specified_from_here,
+ const Label& label,
+ Item* dep_from,
+ Err* err);
+
+ private:
+ ConfigValues config_values_;
+
+ DISALLOW_COPY_AND_ASSIGN(Config);
+};
+
+#endif // TOOLS_GN_CONFIG_H_
diff --git a/tools/gn/config_values.cc b/tools/gn/config_values.cc
new file mode 100644
index 0000000..53466df
--- /dev/null
+++ b/tools/gn/config_values.cc
@@ -0,0 +1,11 @@
+// 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/config_values.h"
+
+ConfigValues::ConfigValues() {
+}
+
+ConfigValues::~ConfigValues() {
+}
diff --git a/tools/gn/config_values.h b/tools/gn/config_values.h
new file mode 100644
index 0000000..e7c1712
--- /dev/null
+++ b/tools/gn/config_values.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef TOOLS_GN_CONFIG_VALUES_H_
+#define TOOLS_GN_CONFIG_VALUES_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "tools/gn/source_dir.h"
+
+// Holds the values (includes, defines, compiler flags, etc.) for a given
+// config or target.
+class ConfigValues {
+ public:
+ ConfigValues();
+ ~ConfigValues();
+
+ const std::vector<SourceDir>& includes() const { return includes_; }
+ void swap_in_includes(std::vector<SourceDir>* lo) { includes_.swap(*lo); }
+
+ const std::vector<std::string>& defines() const { return defines_; }
+ void swap_in_defines(std::vector<std::string>* d) { defines_.swap(*d); }
+
+ const std::vector<std::string>& cflags() const { return cflags_; }
+ void swap_in_cflags(std::vector<std::string>* lo) { cflags_.swap(*lo); }
+
+ const std::vector<std::string>& cflags_c() const { return cflags_c_; }
+ void swap_in_cflags_c(std::vector<std::string>* lo) { cflags_c_.swap(*lo); }
+
+ const std::vector<std::string>& cflags_cc() const { return cflags_cc_; }
+ void swap_in_cflags_cc(std::vector<std::string>* lo) { cflags_cc_.swap(*lo); }
+
+ const std::vector<std::string>& ldflags() const { return ldflags_; }
+ void swap_in_ldflags(std::vector<std::string>* lo) { ldflags_.swap(*lo); }
+
+ private:
+ std::vector<SourceDir> includes_;
+ std::vector<std::string> defines_;
+ std::vector<std::string> cflags_;
+ std::vector<std::string> cflags_c_;
+ std::vector<std::string> cflags_cc_;
+ std::vector<std::string> ldflags_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConfigValues);
+};
+
+#endif // TOOLS_GN_CONFIG_VALUES_H_
diff --git a/tools/gn/config_values_extractors.cc b/tools/gn/config_values_extractors.cc
new file mode 100644
index 0000000..96b514a
--- /dev/null
+++ b/tools/gn/config_values_extractors.cc
@@ -0,0 +1,22 @@
+// 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/config_values_extractors.h"
+
+namespace {
+
+struct StringWriter {
+ void operator()(const std::string& s, std::ostream& out) const {
+ out << " " << s;
+ }
+};
+
+} // namespace
+
+void RecursiveTargetConfigStringsToStream(
+ const Target* target,
+ const std::vector<std::string>& (ConfigValues::* getter)() const,
+ std::ostream& out) {
+ RecursiveTargetConfigToStream(target, getter, StringWriter(), out);
+}
diff --git a/tools/gn/config_values_extractors.h b/tools/gn/config_values_extractors.h
new file mode 100644
index 0000000..0eaa2c9
--- /dev/null
+++ b/tools/gn/config_values_extractors.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef TOOLS_GN_CONFIG_VALUES_EXTRACTORS_H_
+#define TOOLS_GN_CONFIG_VALUES_EXTRACTORS_H_
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "tools/gn/config.h"
+#include "tools/gn/config_values.h"
+#include "tools/gn/target.h"
+
+template<typename T, class Writer>
+inline void ConfigValuesToStream(
+ const ConfigValues& values,
+ const std::vector<T>& (ConfigValues::* getter)() const,
+ const Writer& writer,
+ std::ostream& out) {
+ const std::vector<T>& v = (values.*getter)();
+ for (size_t i = 0; i < v.size(); i++)
+ writer(v[i], out);
+};
+
+template<typename T, class Writer>
+inline void RecursiveTargetConfigToStream(
+ const Target* target,
+ const std::vector<T>& (ConfigValues::* getter)() const,
+ const Writer& writer,
+ std::ostream& out) {
+ // Write all configs in reverse order (to get oldest first, which will look
+ // more normal in the output).
+ for (int i = static_cast<int>(target->configs().size() - 1); i >= 0; i--) {
+ ConfigValuesToStream(target->configs()[i]->config_values(), getter,
+ writer, out);
+ }
+
+ // Last write from the config from the Target itself, if any.
+ ConfigValuesToStream(target->config_values(), getter, writer, out);
+}
+
+// Writes the values out as strings with no transformation.
+void RecursiveTargetConfigStringsToStream(
+ const Target* target,
+ const std::vector<std::string>& (ConfigValues::* getter)() const,
+ std::ostream& out);
+
+#endif // TOOLS_GN_CONFIG_VALUES_EXTRACTORS_H_
diff --git a/tools/gn/config_values_generator.cc b/tools/gn/config_values_generator.cc
new file mode 100644
index 0000000..34c3f67
--- /dev/null
+++ b/tools/gn/config_values_generator.cc
@@ -0,0 +1,89 @@
+// 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/config_values_generator.h"
+
+#include "tools/gn/config_values.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/value.h"
+#include "tools/gn/value_extractors.h"
+
+namespace {
+
+void GetStringList(
+ const Scope* scope,
+ const char* var_name,
+ ConfigValues* config_values,
+ void (ConfigValues::* swapper_inner)(std::vector<std::string>*),
+ Err* err) {
+ const Value* value = scope->GetValue(var_name);
+ if (!value)
+ return; // No value, empty input and succeed.
+
+ std::vector<std::string> result;
+ ExtractListOfStringValues(*value, &result, err);
+ (config_values->*swapper_inner)(&result);
+}
+
+} // namespace
+
+ConfigValuesGenerator::ConfigValuesGenerator(ConfigValues* dest_values,
+ const Scope* scope,
+ const Token& function_token,
+ const SourceDir& input_dir,
+ Err* err)
+ : config_values_(dest_values),
+ scope_(scope),
+ function_token_(function_token),
+ input_dir_(input_dir),
+ err_(err) {
+}
+
+ConfigValuesGenerator::~ConfigValuesGenerator() {
+}
+
+void ConfigValuesGenerator::Run() {
+ FillDefines();
+ FillIncludes();
+ FillCflags();
+ FillCflags_C();
+ FillCflags_CC();
+ FillLdflags();
+}
+
+void ConfigValuesGenerator::FillDefines() {
+ GetStringList(scope_, "defines", config_values_,
+ &ConfigValues::swap_in_defines, err_);
+}
+
+void ConfigValuesGenerator::FillIncludes() {
+ const Value* value = scope_->GetValue("includes");
+ if (!value)
+ return; // No value, empty input and succeed.
+
+ std::vector<SourceDir> includes;
+ if (!ExtractListOfRelativeDirs(*value, input_dir_, &includes, err_))
+ return;
+ config_values_->swap_in_includes(&includes);
+}
+
+void ConfigValuesGenerator::FillCflags() {
+ GetStringList(scope_, "cflags", config_values_,
+ &ConfigValues::swap_in_cflags, err_);
+}
+
+void ConfigValuesGenerator::FillCflags_C() {
+ GetStringList(scope_, "cflags_c", config_values_,
+ &ConfigValues::swap_in_cflags_c, err_);
+}
+
+void ConfigValuesGenerator::FillCflags_CC() {
+ GetStringList(scope_, "cflags_cc", config_values_,
+ &ConfigValues::swap_in_cflags_cc, err_);
+}
+
+void ConfigValuesGenerator::FillLdflags() {
+ GetStringList(scope_, "ldflags", config_values_,
+ &ConfigValues::swap_in_ldflags, err_);
+}
diff --git a/tools/gn/config_values_generator.h b/tools/gn/config_values_generator.h
new file mode 100644
index 0000000..79d9067
--- /dev/null
+++ b/tools/gn/config_values_generator.h
@@ -0,0 +1,48 @@
+// 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.
+
+#ifndef TOOLS_GN_CONFIG_VALUES_GENERATOR_H_
+#define TOOLS_GN_CONFIG_VALUES_GENERATOR_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "tools/gn/source_dir.h"
+
+class ConfigValues;
+class Err;
+class Scope;
+class Token;
+
+class ConfigValuesGenerator {
+ public:
+ ConfigValuesGenerator(ConfigValues* dest_values,
+ const Scope* scope,
+ const Token& function_token,
+ const SourceDir& input_dir,
+ Err* err);
+ ~ConfigValuesGenerator();
+
+ // Sets the error passed to the constructor on failure.
+ void Run();
+
+ private:
+ void FillDefines();
+ void FillIncludes();
+ void FillCflags();
+ void FillCflags_C();
+ void FillCflags_CC();
+ void FillLdflags();
+
+ ConfigValues* config_values_;
+ const Scope* scope_;
+ const Token& function_token_;
+ const SourceDir input_dir_;
+ Err* err_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConfigValuesGenerator);
+};
+
+#endif // TOOLS_GN_CONFIG_VALUES_GENERATOR_H_
diff --git a/tools/gn/err.cc b/tools/gn/err.cc
new file mode 100644
index 0000000..068ea6d
--- /dev/null
+++ b/tools/gn/err.cc
@@ -0,0 +1,196 @@
+// 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/err.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/standard_out.h"
+#include "tools/gn/tokenizer.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+std::string GetNthLine(const base::StringPiece& data, int n) {
+ size_t line_off = Tokenizer::ByteOffsetOfNthLine(data, n);
+ size_t end = line_off + 1;
+ while (end < data.size() && !Tokenizer::IsNewline(data, end))
+ end++;
+ return data.substr(line_off, end - line_off).as_string();
+}
+
+void FillRangeOnLine(const LocationRange& range, int line_number,
+ std::string* line) {
+ // Only bother if the range's begin or end overlaps the line. If the entire
+ // line is highlighted as a result of this range, it's not very helpful.
+ if (range.begin().line_number() != line_number &&
+ range.end().line_number() != line_number)
+ return;
+
+ // Watch out, the char offsets in the location are 1-based, so we have to
+ // subtract 1.
+ int begin_char;
+ if (range.begin().line_number() < line_number)
+ begin_char = 0;
+ else
+ begin_char = range.begin().char_offset() - 1;
+
+ int end_char;
+ if (range.end().line_number() > line_number)
+ end_char = line->size(); // Ending is non-inclusive.
+ else
+ end_char = range.end().char_offset() - 1;
+
+ CHECK(end_char >= begin_char);
+ CHECK(begin_char >= 0 && begin_char <= static_cast<int>(line->size()));
+ CHECK(end_char >= 0 && end_char <= static_cast<int>(line->size()));
+ for (int i = begin_char; i < end_char; i++)
+ line->at(i) = '-';
+}
+
+// The line length is used to clip the maximum length of the markers we'll
+// make if the error spans more than one line (like unterminated literals).
+void OutputHighlighedPosition(const Location& location,
+ const Err::RangeList& ranges,
+ size_t line_length) {
+ // Make a buffer of the line in spaces.
+ std::string highlight;
+ highlight.resize(line_length);
+ for (size_t i = 0; i < line_length; i++)
+ highlight[i] = ' ';
+
+ // Highlight all the ranges on the line.
+ for (size_t i = 0; i < ranges.size(); i++)
+ FillRangeOnLine(ranges[i], location.line_number(), &highlight);
+
+ // Allow the marker to be one past the end of the line for marking the end.
+ highlight.push_back(' ');
+ CHECK(location.char_offset() - 1 >= 0 &&
+ location.char_offset() - 1 < static_cast<int>(highlight.size()));
+ highlight[location.char_offset() - 1] = '^';
+
+ // Trim unused spaces from end of line.
+ while (!highlight.empty() && highlight[highlight.size() - 1] == ' ')
+ highlight.resize(highlight.size() - 1);
+
+ highlight += "\n";
+ OutputString(highlight, DECORATION_BLUE);
+}
+
+} // namespace
+
+Err::Err() : has_error_(false) {
+}
+
+Err::Err(const Location& location,
+ const std::string& msg,
+ const std::string& help)
+ : has_error_(true),
+ location_(location),
+ message_(msg),
+ help_text_(help) {
+}
+
+Err::Err(const LocationRange& range,
+ const std::string& msg,
+ const std::string& help)
+ : has_error_(true),
+ location_(range.begin()),
+ message_(msg),
+ help_text_(help) {
+ ranges_.push_back(range);
+}
+
+Err::Err(const Token& token,
+ const std::string& msg,
+ const std::string& help)
+ : has_error_(true),
+ location_(token.location()),
+ message_(msg),
+ help_text_(help) {
+ ranges_.push_back(token.range());
+}
+
+Err::Err(const ParseNode* node,
+ const std::string& msg,
+ const std::string& help_text)
+ : has_error_(true),
+ message_(msg),
+ help_text_(help_text) {
+ // Node will be null in certain tests.
+ if (node) {
+ LocationRange range = node->GetRange();
+ location_ = range.begin();
+ ranges_.push_back(range);
+ }
+}
+
+Err::Err(const Value& value,
+ const std::string msg,
+ const std::string& help_text)
+ : has_error_(true),
+ message_(msg),
+ help_text_(help_text) {
+ if (value.origin()) {
+ LocationRange range = value.origin()->GetRange();
+ location_ = range.begin();
+ ranges_.push_back(range);
+ }
+}
+
+Err::~Err() {
+}
+
+void Err::PrintToStdout() const {
+ InternalPrintToStdout(false);
+}
+
+void Err::AppendSubErr(const Err& err) {
+ sub_errs_.push_back(err);
+}
+
+void Err::InternalPrintToStdout(bool is_sub_err) const {
+ DCHECK(has_error_);
+
+ if (!is_sub_err)
+ OutputString("ERROR ", DECORATION_RED);
+
+ // File name and location.
+ const InputFile* input_file = location_.file();
+ std::string loc_str;
+ if (input_file) {
+ std::string path8;
+ path8.assign(input_file->name().value());
+
+ if (is_sub_err)
+ loc_str = "See ";
+ else
+ loc_str = "at ";
+ loc_str += path8 + ": " +
+ base::IntToString(location_.line_number()) + ":" +
+ base::IntToString(location_.char_offset()) + ": ";
+ }
+ OutputString(loc_str + message_ + "\n");
+
+ // Quoted line.
+ if (input_file) {
+ std::string line = GetNthLine(input_file->contents(),
+ location_.line_number());
+ if (!ContainsOnlyWhitespaceASCII(line)) {
+ OutputString(line + "\n", DECORATION_BOLD);
+ OutputHighlighedPosition(location_, ranges_, line.size());
+ }
+ }
+
+ // Optional help text.
+ if (!help_text_.empty())
+ OutputString(help_text_ + "\n");
+
+ // Sub errors.
+ for (size_t i = 0; i < sub_errs_.size(); i++)
+ sub_errs_[i].InternalPrintToStdout(true);
+}
diff --git a/tools/gn/err.h b/tools/gn/err.h
new file mode 100644
index 0000000..3e077e9
--- /dev/null
+++ b/tools/gn/err.h
@@ -0,0 +1,85 @@
+// 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.
+
+#ifndef TOOLS_GN_ERR_H_
+#define TOOLS_GN_ERR_H_
+
+#include <string>
+#include <vector>
+
+#include "tools/gn/location.h"
+#include "tools/gn/token.h"
+
+class ParseNode;
+class Value;
+
+// Result of doing some operation. Check has_error() to see if an error
+// occurred.
+//
+// An error has a location and a message. Below that, is some optional help
+// text to go with the annotation of the location.
+//
+// An error can also have sub-errors which are additionally printed out
+// below. They can provide additional context.
+class Err {
+ public:
+ typedef std::vector<LocationRange> RangeList;
+
+ // Indicates no error.
+ Err();
+
+ // Error at a single point.
+ Err(const Location& location,
+ const std::string& msg,
+ const std::string& help = std::string());
+
+ // Error at a given range.
+ Err(const LocationRange& range,
+ const std::string& msg,
+ const std::string& help = std::string());
+
+ // Error at a given token.
+ Err(const Token& token,
+ const std::string& msg,
+ const std::string& help_text = std::string());
+
+ // Error at a given node.
+ Err(const ParseNode* node,
+ const std::string& msg,
+ const std::string& help_text = std::string());
+
+ // Error at a given value.
+ Err(const Value& value,
+ const std::string msg,
+ const std::string& help_text = std::string());
+
+ ~Err();
+
+ bool has_error() const { return has_error_; }
+ const Location& location() const { return location_; }
+ const std::string& message() const { return message_; }
+ const std::string& help_text() const { return help_text_; }
+
+ void AppendRange(const LocationRange& range) { ranges_.push_back(range); }
+ const RangeList& ranges() const { return ranges_; }
+
+ void AppendSubErr(const Err& err);
+
+ void PrintToStdout() const;
+
+ private:
+ void InternalPrintToStdout(bool is_sub_err) const;
+
+ bool has_error_;
+ Location location_;
+
+ std::vector<LocationRange> ranges_;
+
+ std::string message_;
+ std::string help_text_;
+
+ std::vector<Err> sub_errs_;
+};
+
+#endif // TOOLS_GN_ERR_H_
diff --git a/tools/gn/escape.cc b/tools/gn/escape.cc
new file mode 100644
index 0000000..5fc7b6f
--- /dev/null
+++ b/tools/gn/escape.cc
@@ -0,0 +1,77 @@
+// 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/escape.h"
+
+#include "base/containers/stack_container.h"
+
+namespace {
+
+template<typename DestString>
+void EscapeStringToString(const base::StringPiece& str,
+ const EscapeOptions& options,
+ DestString* dest) {
+ bool used_quotes = false;
+
+ for (size_t i = 0; i < str.size(); i++) {
+ if (str[i] == '$' && options.mode == ESCAPE_NINJA) {
+ // Escape dollars signs since ninja treats these specially.
+ dest->push_back('$');
+ dest->push_back('$');
+ } else if (str[i] == '"' && options.mode == ESCAPE_SHELL) {
+ // Escape quotes with backslashes for the command-line (Ninja doesn't
+ // care).
+ dest->push_back('\\');
+ dest->push_back('"');
+ } else if (str[i] == ' ') {
+ if (options.mode == ESCAPE_NINJA) {
+ // For ninja just escape spaces with $.
+ dest->push_back('$');
+ } else if (options.mode == ESCAPE_SHELL && !options.inhibit_quoting) {
+ // For the shell, quote the whole string.
+ if (!used_quotes) {
+ used_quotes = true;
+ dest->insert(dest->begin(), '"');
+ }
+ }
+ dest->push_back(' ');
+#if defined(OS_WIN)
+ } else if (str[i] == '/' && options.convert_slashes) {
+ // Convert slashes on Windows if requested.
+ dest->push_back('\\');
+#else
+ } else if (str[i] == '\\' && options.mode == ESCAPE_SHELL) {
+ // For non-Windows shell, escape backslashes.
+ dest->push_back('\\');
+ dest->push_back('\\');
+#endif
+ } else {
+ dest->push_back(str[i]);
+ }
+ }
+
+ if (used_quotes)
+ dest->push_back('"');
+}
+
+} // namespace
+
+std::string EscapeString(const base::StringPiece& str,
+ const EscapeOptions& options) {
+ std::string result;
+ result.reserve(str.size() + 4); // Guess we'll add a couple of extra chars.
+ EscapeStringToString(str, options, &result);
+ return result;
+}
+
+void EscapeStringToStream(std::ostream& out,
+ const base::StringPiece& str,
+ const EscapeOptions& options) {
+ // Escape to a stack buffer and then write out to the stream.
+ base::StackVector<char, 256> result;
+ result->reserve(str.size() + 4); // Guess we'll add a couple of extra chars.
+ EscapeStringToString(str, options, &result.container());
+ if (!result->empty())
+ out.write(result->data(), result->size());
+}
diff --git a/tools/gn/escape.h b/tools/gn/escape.h
new file mode 100644
index 0000000..1791161
--- /dev/null
+++ b/tools/gn/escape.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef TOOLS_GN_ESCAPE_H_
+#define TOOLS_GN_ESCAPE_H_
+
+#include <iosfwd>
+
+#include "base/strings/string_piece.h"
+
+// TODO(brettw) we may need to make this a bitfield. If we want to write a
+// shell command in a ninja file, we need the shell characters to be escaped,
+// and THEN the ninja characters. Or maybe we require the caller to do two
+// passes.
+enum EscapingMode {
+ ESCAPE_NONE, // No escaping.
+ ESCAPE_NINJA, // Ninja string escaping.
+ ESCAPE_SHELL, // Shell string escaping.
+};
+
+struct EscapeOptions {
+ EscapeOptions()
+ : mode(ESCAPE_NONE),
+ convert_slashes(false),
+ inhibit_quoting(false) {
+ }
+
+ EscapingMode mode;
+
+ // When set, converts forward-slashes to system-specific path separators.
+ bool convert_slashes;
+
+ // When the escaping mode is ESCAPE_SHELL, the escaper will normally put
+ // quotes around things with spaces. If this value is set to true, we'll
+ // disable the quoting feature and just add the spaces.
+ //
+ // This mode is for when quoting is done at some higher-level. Defaults to
+ // false.
+ bool inhibit_quoting;
+};
+
+// Escapes the given input, returnining the result.
+std::string EscapeString(const base::StringPiece& str,
+ const EscapeOptions& options);
+
+// Same as EscapeString but writes the results to the given stream, saving a
+// copy.
+void EscapeStringToStream(std::ostream& out,
+ const base::StringPiece& str,
+ const EscapeOptions& options);
+
+#endif // TOOLS_GN_ESCAPE_H_
diff --git a/tools/gn/escape_unittest.cc b/tools/gn/escape_unittest.cc
new file mode 100644
index 0000000..a7c19b3
--- /dev/null
+++ b/tools/gn/escape_unittest.cc
@@ -0,0 +1,4 @@
+// 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.
+
diff --git a/tools/gn/file_template.cc b/tools/gn/file_template.cc
new file mode 100644
index 0000000..8b5d09f
--- /dev/null
+++ b/tools/gn/file_template.cc
@@ -0,0 +1,125 @@
+// 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/file_template.h"
+
+#include "tools/gn/filesystem_utils.h"
+
+const char FileTemplate::kSource[] = "{{source}}";
+const char FileTemplate::kSourceNamePart[] = "{{source_name_part}}";
+
+FileTemplate::FileTemplate(const Value& t, Err* err) {
+ ParseInput(t, err);
+}
+
+FileTemplate::FileTemplate(const std::vector<std::string>& t) {
+ for (size_t i = 0; i < t.size(); i++)
+ ParseOneTemplateString(t[i]);
+}
+
+FileTemplate::~FileTemplate() {
+}
+
+void FileTemplate::Apply(const Value& sources,
+ const ParseNode* origin,
+ std::vector<Value>* dest,
+ Err* err) const {
+ if (!sources.VerifyTypeIs(Value::LIST, err))
+ return;
+ dest->reserve(sources.list_value().size() * templates_.container().size());
+
+ // Temporary holding place, allocate outside to re-use- buffer.
+ std::vector<std::string> string_output;
+
+ const std::vector<Value>& sources_list = sources.list_value();
+ for (size_t i = 0; i < sources_list.size(); i++) {
+ if (!sources_list[i].VerifyTypeIs(Value::STRING, err))
+ return;
+
+ ApplyString(sources_list[i].string_value(), &string_output);
+ for (size_t out_i = 0; out_i < string_output.size(); out_i++)
+ dest->push_back(Value(origin, string_output[i]));
+ }
+}
+
+void FileTemplate::ApplyString(const std::string& str,
+ std::vector<std::string>* output) const {
+ // Compute all substitutions needed so we can just do substitutions below.
+ // We skip the LITERAL one since that varies each time.
+ std::string subst[Subrange::NUM_TYPES];
+ if (types_required_[Subrange::SOURCE])
+ subst[Subrange::SOURCE] = str;
+ if (types_required_[Subrange::NAME_PART])
+ subst[Subrange::NAME_PART] = FindFilenameNoExtension(&str).as_string();
+
+ output->resize(templates_.container().size());
+ for (size_t template_i = 0;
+ template_i < templates_.container().size(); template_i++) {
+ const Template& t = templates_[template_i];
+ (*output)[template_i].clear();
+ for (size_t subrange_i = 0; subrange_i < t.container().size();
+ subrange_i++) {
+ if (t[subrange_i].type == Subrange::LITERAL)
+ (*output)[template_i].append(t[subrange_i].literal);
+ else
+ (*output)[template_i].append(subst[t[subrange_i].type]);
+ }
+ }
+}
+
+void FileTemplate::ParseInput(const Value& value, Err* err) {
+ switch (value.type()) {
+ case Value::STRING:
+ ParseOneTemplateString(value.string_value());
+ break;
+ case Value::LIST:
+ for (size_t i = 0; i < value.list_value().size(); i++) {
+ if (!value.list_value()[i].VerifyTypeIs(Value::STRING, err))
+ return;
+ ParseOneTemplateString(value.list_value()[i].string_value());
+ }
+ break;
+ default:
+ *err = Err(value, "File template must be a string or list.",
+ "A sarcastic comment about your skills goes here.");
+ }
+}
+
+void FileTemplate::ParseOneTemplateString(const std::string& str) {
+ templates_.container().resize(templates_.container().size() + 1);
+ Template& t = templates_[templates_.container().size() - 1];
+
+ size_t cur = 0;
+ while (true) {
+ size_t next = str.find("{{", cur);
+
+ // Pick up everything from the previous spot to here as a literal.
+ if (next == std::string::npos) {
+ if (cur != str.size())
+ t.container().push_back(Subrange(Subrange::LITERAL, str.substr(cur)));
+ break;
+ } else if (next > cur) {
+ t.container().push_back(
+ Subrange(Subrange::LITERAL, str.substr(cur, next - cur)));
+ }
+
+ // Decode the template param.
+ if (str.compare(next, arraysize(kSource) - 1, kSource) == 0) {
+ t.container().push_back(Subrange(Subrange::SOURCE));
+ types_required_[Subrange::SOURCE] = true;
+ cur = next + arraysize(kSource) - 1;
+ } else if (str.compare(next, arraysize(kSourceNamePart) - 1,
+ kSourceNamePart) == 0) {
+ t.container().push_back(Subrange(Subrange::NAME_PART));
+ types_required_[Subrange::NAME_PART] = true;
+ cur = next + arraysize(kSourceNamePart) - 1;
+ } else {
+ // If it's not a match, treat it like a one-char literal (this will be
+ // rare, so it's not worth the bother to add to the previous literal) so
+ // we can keep going.
+ t.container().push_back(Subrange(Subrange::LITERAL, "{"));
+ cur = next + 1;
+ }
+ }
+}
diff --git a/tools/gn/file_template.h b/tools/gn/file_template.h
new file mode 100644
index 0000000..25d2251
--- /dev/null
+++ b/tools/gn/file_template.h
@@ -0,0 +1,74 @@
+// 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.
+
+#ifndef TOOLS_GN_FILE_TEMPLATE_H_
+#define TOOLS_GN_FILE_TEMPLATE_H_
+
+#include "base/basictypes.h"
+#include "base/containers/stack_container.h"
+#include "tools/gn/err.h"
+#include "tools/gn/value.h"
+
+class ParseNode;
+
+class FileTemplate {
+ public:
+ struct Subrange {
+ enum Type {
+ LITERAL = 0,
+ SOURCE,
+ NAME_PART,
+ NUM_TYPES // Must be last
+ };
+ Subrange(Type t, const std::string& l = std::string())
+ : type(t),
+ literal(l) {
+ }
+
+ Type type;
+
+ // When type_ == LITERAL, this specifies the literal.
+ std::string literal;
+ };
+
+ // Constructs a template from the given value. On error, the err will be
+ // set. In this case you should not use this object.
+ FileTemplate(const Value& t, Err* err);
+ FileTemplate(const std::vector<std::string>& t);
+ ~FileTemplate();
+
+ // Applies this template to the given list of sources, appending all
+ // results to the given dest list. The sources must be a list for the
+ // one that takes a value as an input, otherwise the given error will be set.
+ void Apply(const Value& sources,
+ const ParseNode* origin,
+ std::vector<Value>* dest,
+ Err* err) const;
+ void ApplyString(const std::string& input,
+ std::vector<std::string>* output) const;
+
+ // Known template types.
+ static const char kSource[];
+ static const char kSourceNamePart[];
+
+ private:
+ typedef base::StackVector<Subrange, 8> Template;
+ typedef base::StackVector<Template, 8> TemplateVector;
+
+ void ParseInput(const Value& value, Err* err);
+
+ // Parses a template string and adds it to the templates_ list.
+ void ParseOneTemplateString(const std::string& str);
+
+ TemplateVector templates_;
+
+ // The corresponding value is set to true if the given subrange type is
+ // required. This allows us to precompute these types whem applying them
+ // to a given source file.
+ bool types_required_[Subrange::NUM_TYPES];
+
+ DISALLOW_COPY_AND_ASSIGN(FileTemplate);
+};
+
+#endif // TOOLS_GN_FILE_TEMPLATE_H_
diff --git a/tools/gn/file_template_unittest.cc b/tools/gn/file_template_unittest.cc
new file mode 100644
index 0000000..47c84f0
--- /dev/null
+++ b/tools/gn/file_template_unittest.cc
@@ -0,0 +1,45 @@
+// 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/file_template.h"
+
+TEST(FileTemplate, Static) {
+ std::vector<std::string> templates;
+ templates.push_back("something_static");
+ FileTemplate t(templates);
+
+ std::vector<std::string> result;
+ t.ApplyString("", &result);
+ ASSERT_EQ(1u, result.size());
+ EXPECT_EQ("something_static", result[0]);
+
+ t.ApplyString("lalala", &result);
+ ASSERT_EQ(1u, result.size());
+ EXPECT_EQ("something_static", result[0]);
+}
+
+TEST(FileTemplate, Typical) {
+ std::vector<std::string> templates;
+ templates.push_back("foo/{{source_name_part}}.cc");
+ templates.push_back("foo/{{source_name_part}}.h");
+ FileTemplate t(templates);
+
+ std::vector<std::string> result;
+ t.ApplyString("sources/ha.idl", &result);
+ ASSERT_EQ(2u, result.size());
+ EXPECT_EQ("foo/ha.cc", result[0]);
+ EXPECT_EQ("foo/ha.h", result[1]);
+}
+
+TEST(FileTemplate, Weird) {
+ std::vector<std::string> templates;
+ templates.push_back("{{{source}}{{source}}{{");
+ FileTemplate t(templates);
+
+ std::vector<std::string> result;
+ t.ApplyString("foo/lalala.c", &result);
+ ASSERT_EQ(1u, result.size());
+ EXPECT_EQ("{foo/lalala.cfoo/lalala.c{{", result[0]);
+}
diff --git a/tools/gn/filesystem_utils.cc b/tools/gn/filesystem_utils.cc
new file mode 100644
index 0000000..625a358
--- /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;
+}
+
diff --git a/tools/gn/filesystem_utils.h b/tools/gn/filesystem_utils.h
new file mode 100644
index 0000000..19139f8
--- /dev/null
+++ b/tools/gn/filesystem_utils.h
@@ -0,0 +1,115 @@
+// 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.
+
+#ifndef TOOLS_GN_FILESYSTEM_UTILS_H_
+#define TOOLS_GN_FILESYSTEM_UTILS_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/strings/string_piece.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/target.h"
+
+class Err;
+class Location;
+class Value;
+
+enum SourceFileType {
+ SOURCE_UNKNOWN,
+ SOURCE_ASM,
+ SOURCE_C,
+ SOURCE_CC,
+ SOURCE_H,
+ SOURCE_M,
+ SOURCE_MM,
+ //SOURCE_S, // TODO(brettw) what is this?
+ SOURCE_RC,
+};
+
+SourceFileType GetSourceFileType(const SourceFile& file,
+ Settings::TargetOS os);
+
+// Returns the extension, not including the dot, for the given file type on the
+// given system.
+//
+// Some targets make multiple files (like a .dll and an import library). This
+// function returns the name of the file other targets should depend on and
+// link to (so in this example, the import library).
+const char* GetExtensionForOutputType(Target::OutputType type,
+ Settings::TargetOS os);
+
+std::string FilePathToUTF8(const base::FilePath& path);
+base::FilePath UTF8ToFilePath(const base::StringPiece& sp);
+
+// Extensions -----------------------------------------------------------------
+
+// Returns the index of the extension (character after the last dot not after a
+// slash). Returns std::string::npos if not found. Returns path.size() if the
+// file ends with a dot.
+size_t FindExtensionOffset(const std::string& path);
+
+// Returns a string piece pointing into the input string identifying the
+// extension. Note that the input pointer must outlive the output.
+base::StringPiece FindExtension(const std::string* path);
+
+// Filename parts -------------------------------------------------------------
+
+// Returns the offset of the character following the last slash, or
+// 0 if no slash was found. Returns path.size() if the path ends with a slash.
+// Note that the input pointer must outlive the output.
+size_t FindFilenameOffset(const std::string& path);
+
+// Returns a string piece pointing into the input string identifying the
+// file name (following the last slash, including the extension). Note that the
+// input pointer must outlive the output.
+base::StringPiece FindFilename(const std::string* path);
+
+// Like FindFilename but does not include the extension.
+base::StringPiece FindFilenameNoExtension(const std::string* path);
+
+// Removes everything after the last slash. The last slash, if any, will be
+// preserved.
+void RemoveFilename(std::string* path);
+
+// Returns true if the given path ends with a slash.
+bool EndsWithSlash(const std::string& s);
+
+// Path parts -----------------------------------------------------------------
+
+// Returns a string piece pointing into the input string identifying the
+// directory name of the given path, including the last slash. Note that the
+// input pointer must outlive the output.
+base::StringPiece FindDir(const std::string* path);
+
+// Verifies that the given string references a file inside of the given
+// directory. This is pretty stupid and doesn't handle "." and "..", etc.,
+// it is designed for a sanity check to keep people from writing output files
+// to the source directory accidentally.
+//
+// The originating value will be blamed in the error.
+//
+// If the file isn't in the dir, returns false and sets the error. Otherwise
+// returns true and leaves the error untouched.
+bool EnsureStringIsInOutputDir(const SourceDir& dir,
+ const std::string& str,
+ const Value& originating,
+ Err* err);
+
+// ----------------------------------------------------------------------------
+
+// Converts a directory to its inverse (e.g. "/foo/bar/" -> "../../").
+// This will be the empty string for the root directories ("/" and "//"), and
+// in all other cases, this is guaranteed to end in a slash.
+std::string InvertDir(const SourceDir& dir);
+
+// Collapses "." and sequential "/"s and evaluates "..".
+void NormalizePath(std::string* path);
+
+// Converts slashes to backslashes for Windows. Keeps the string unchanged
+// for other systems.
+void ConvertPathToSystem(std::string* path);
+std::string PathToSystem(const std::string& path);
+
+#endif // TOOLS_GN_FILESYSTEM_UTILS_H_
diff --git a/tools/gn/filesystem_utils_unittest.cc b/tools/gn/filesystem_utils_unittest.cc
new file mode 100644
index 0000000..75bf7cd
--- /dev/null
+++ b/tools/gn/filesystem_utils_unittest.cc
@@ -0,0 +1,146 @@
+// 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 "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/filesystem_utils.h"
+
+TEST(FilesystemUtils, FileExtensionOffset) {
+ EXPECT_EQ(std::string::npos, FindExtensionOffset(""));
+ EXPECT_EQ(std::string::npos, FindExtensionOffset("foo/bar/baz"));
+ EXPECT_EQ(4u, FindExtensionOffset("foo."));
+ EXPECT_EQ(4u, FindExtensionOffset("f.o.bar"));
+ EXPECT_EQ(std::string::npos, FindExtensionOffset("foo.bar/"));
+ EXPECT_EQ(std::string::npos, FindExtensionOffset("foo.bar/baz"));
+}
+
+TEST(FilesystemUtils, FindExtension) {
+ std::string input;
+ EXPECT_EQ("", FindExtension(&input).as_string());
+ input = "foo/bar/baz";
+ EXPECT_EQ("", FindExtension(&input).as_string());
+ input = "foo.";
+ EXPECT_EQ("", FindExtension(&input).as_string());
+ input = "f.o.bar";
+ EXPECT_EQ("bar", FindExtension(&input).as_string());
+ input = "foo.bar/";
+ EXPECT_EQ("", FindExtension(&input).as_string());
+ input = "foo.bar/baz";
+ EXPECT_EQ("", FindExtension(&input).as_string());
+}
+
+TEST(FilesystemUtils, FindFilenameOffset) {
+ EXPECT_EQ(0u, FindFilenameOffset(""));
+ EXPECT_EQ(0u, FindFilenameOffset("foo"));
+ EXPECT_EQ(4u, FindFilenameOffset("foo/"));
+ EXPECT_EQ(4u, FindFilenameOffset("foo/bar"));
+}
+
+TEST(FilesystemUtils, RemoveFilename) {
+ std::string s;
+
+ RemoveFilename(&s);
+ EXPECT_STREQ("", s.c_str());
+
+ s = "foo";
+ RemoveFilename(&s);
+ EXPECT_STREQ("", s.c_str());
+
+ s = "/";
+ RemoveFilename(&s);
+ EXPECT_STREQ("/", s.c_str());
+
+ s = "foo/bar";
+ RemoveFilename(&s);
+ EXPECT_STREQ("foo/", s.c_str());
+
+ s = "foo/bar/baz.cc";
+ RemoveFilename(&s);
+ EXPECT_STREQ("foo/bar/", s.c_str());
+}
+
+TEST(FilesystemUtils, FindDir) {
+ std::string input;
+ EXPECT_EQ("", FindDir(&input));
+ input = "/";
+ EXPECT_EQ("/", FindDir(&input));
+ input = "foo/";
+ EXPECT_EQ("foo/", FindDir(&input));
+ input = "foo/bar/baz";
+ EXPECT_EQ("foo/bar/", FindDir(&input));
+}
+
+TEST(FilesystemUtils, InvertDir) {
+ EXPECT_TRUE(InvertDir(SourceDir()) == "");
+ EXPECT_TRUE(InvertDir(SourceDir("/")) == "");
+ EXPECT_TRUE(InvertDir(SourceDir("//")) == "");
+
+ EXPECT_TRUE(InvertDir(SourceDir("//foo/bar")) == "../../");
+ EXPECT_TRUE(InvertDir(SourceDir("/foo/bar/")) == "../../");
+}
+
+TEST(FilesystemUtils, NormalizePath) {
+ std::string input;
+
+ NormalizePath(&input);
+ EXPECT_EQ("", input);
+
+ input = "foo/bar.txt";
+ NormalizePath(&input);
+ EXPECT_EQ("foo/bar.txt", input);
+
+ input = ".";
+ NormalizePath(&input);
+ EXPECT_EQ("", input);
+
+ input = "..";
+ NormalizePath(&input);
+ EXPECT_EQ("..", input);
+
+ input = "foo//bar";
+ NormalizePath(&input);
+ EXPECT_EQ("foo/bar", input);
+
+ input = "//foo";
+ NormalizePath(&input);
+ EXPECT_EQ("//foo", input);
+
+ input = "foo/..//bar";
+ NormalizePath(&input);
+ EXPECT_EQ("bar", input);
+
+ input = "foo/../../bar";
+ NormalizePath(&input);
+ EXPECT_EQ("../bar", input);
+
+ input = "/../foo"; // Don't go aboe the root dir.
+ NormalizePath(&input);
+ EXPECT_EQ("/foo", input);
+
+ input = "//../foo"; // Don't go aboe the root dir.
+ NormalizePath(&input);
+ EXPECT_EQ("//foo", input);
+
+ input = "../foo";
+ NormalizePath(&input);
+ EXPECT_EQ("../foo", input);
+
+ input = "..";
+ NormalizePath(&input);
+ EXPECT_EQ("..", input);
+
+ input = "./././.";
+ NormalizePath(&input);
+ EXPECT_EQ("", input);
+
+ input = "../../..";
+ NormalizePath(&input);
+ EXPECT_EQ("../../..", input);
+
+ input = "../";
+ NormalizePath(&input);
+ EXPECT_EQ("../", input);
+}
diff --git a/tools/gn/function_define_rule.cc b/tools/gn/function_define_rule.cc
new file mode 100644
index 0000000..afbed9e
--- /dev/null
+++ b/tools/gn/function_define_rule.cc
@@ -0,0 +1,37 @@
+// 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/functions.h"
+
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/value.h"
+
+Value ExecuteDefineRule(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ // TODO(brettw) determine if the function is built-in and throw an error if
+ // it is.
+ if (args.size() != 1) {
+ *err = Err(function->function(),
+ "Need exactly one string arg to define_rule.");
+ return Value();
+ }
+ if (!args[0].VerifyTypeIs(Value::STRING, err))
+ return Value();
+ std::string rule_name = args[0].string_value();
+
+ const FunctionCallNode* existing_rule = scope->GetRule(rule_name);
+ if (existing_rule) {
+ *err = Err(function, "Duplicate rule definition.",
+ "A rule with this name was already defined.");
+ err->AppendSubErr(Err(existing_rule->function(), "Previous definition."));
+ return Value();
+ }
+
+ scope->AddRule(rule_name, function);
+ return Value();
+}
diff --git a/tools/gn/function_exec_script.cc b/tools/gn/function_exec_script.cc
new file mode 100644
index 0000000..a950909
--- /dev/null
+++ b/tools/gn/function_exec_script.cc
@@ -0,0 +1,254 @@
+// 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 "base/command_line.h"
+#include "base/file_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
+#include "tools/gn/err.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/input_conversion.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/value.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#endif
+
+/*
+exec_script: Synchronously run a script and return the output.
+
+ exec_script(filename, arguments, input_conversion, [file_dependencies])
+
+ Runs the given script, returning the stdout of the script. The build
+ generation will fail if the script does not exist or returns a nonzero
+ exit code.
+
+Arguments:
+
+ filename:
+ File name of python script to execute, relative to the build file.
+
+ arguments:
+ A list of strings to be passed to the scripe as arguments.
+
+ input_conversion:
+ Controls how the file is read and parsed. See "help input_conversion".
+
+ dependencies:
+ (Optional) A list of files that this script reads or otherwise depends
+ on. These dependencies will be added to the build result such that if
+ any of them change, the build will be regenerated and the script will
+ be re-run.
+
+ The script itself will be an implicit dependency so you do not need to
+ list it.
+
+Example:
+
+ all_lines = exec_script("myscript.py", [some_input], "list lines",
+ ["data_file.txt"])
+*/
+
+namespace {
+
+#if defined(OS_WIN)
+bool ExecProcess(const CommandLine& cmdline,
+ const base::FilePath& startup_dir,
+ std::string* std_out,
+ std::string* std_err,
+ int* exit_code) {
+ SECURITY_ATTRIBUTES sa_attr;
+ // Set the bInheritHandle flag so pipe handles are inherited.
+ sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa_attr.bInheritHandle = TRUE;
+ sa_attr.lpSecurityDescriptor = NULL;
+
+ // Create the pipe for the child process's STDOUT.
+ HANDLE out_read = NULL;
+ HANDLE out_write = NULL;
+ if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) {
+ NOTREACHED() << "Failed to create pipe";
+ return false;
+ }
+ base::win::ScopedHandle scoped_out_read(out_read);
+ base::win::ScopedHandle scoped_out_write(out_write);
+
+ // Create the pipe for the child process's STDERR.
+ HANDLE err_read = NULL;
+ HANDLE err_write = NULL;
+ if (!CreatePipe(&err_read, &err_write, &sa_attr, 0)) {
+ NOTREACHED() << "Failed to create pipe";
+ return false;
+ }
+ base::win::ScopedHandle scoped_err_read(err_read);
+ base::win::ScopedHandle scoped_err_write(err_write);
+
+ // Ensure the read handle to the pipe for STDOUT/STDERR is not inherited.
+ if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
+ NOTREACHED() << "Failed to disabled pipe inheritance";
+ return false;
+ }
+ if (!SetHandleInformation(err_read, HANDLE_FLAG_INHERIT, 0)) {
+ NOTREACHED() << "Failed to disabled pipe inheritance";
+ return false;
+ }
+
+ base::FilePath::StringType cmdline_str(cmdline.GetCommandLineString());
+
+ base::win::ScopedProcessInformation proc_info;
+ STARTUPINFO start_info = { 0 };
+
+ start_info.cb = sizeof(STARTUPINFO);
+ start_info.hStdOutput = out_write;
+ // Keep the normal stdin.
+ start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ // FIXME(brettw) set stderr here when we actually read it below.
+ //start_info.hStdError = err_write;
+ start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ start_info.dwFlags |= STARTF_USESTDHANDLES;
+
+ // Create the child process.
+ if (!CreateProcess(NULL,
+ &cmdline_str[0],
+ NULL, NULL,
+ TRUE, // Handles are inherited.
+ 0, NULL,
+ startup_dir.value().c_str(),
+ &start_info, proc_info.Receive())) {
+ return false;
+ }
+
+ // Close our writing end of pipes now. Otherwise later read would not be able
+ // to detect end of child's output.
+ scoped_out_write.Close();
+ scoped_err_write.Close();
+
+ // Read output from the child process's pipe for STDOUT
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
+
+ // FIXME(brettw) read from stderr here! This is complicated because we want
+ // to read both of them at the same time, probably need overlapped I/O.
+ // Also uncomment start_info code above.
+ for (;;) {
+ DWORD bytes_read = 0;
+ BOOL success = ReadFile(out_read, buffer, kBufferSize, &bytes_read, NULL);
+ if (!success || bytes_read == 0)
+ break;
+ std_out->append(buffer, bytes_read);
+ }
+
+ // Let's wait for the process to finish.
+ WaitForSingleObject(proc_info.process_handle(), INFINITE);
+
+ DWORD dw_exit_code;
+ GetExitCodeProcess(proc_info.process_handle(), &dw_exit_code);
+ *exit_code = static_cast<int>(dw_exit_code);
+
+ return true;
+}
+#else
+bool ExecProcess(const CommandLine& cmdline,
+ const base::FilePath& startup_dir,
+ std::string* std_out,
+ std::string* std_err,
+ int* exit_code) {
+ //NOTREACHED() << "Implement me.";
+ return false;
+}
+#endif
+
+} // namespace
+
+Value ExecuteExecScript(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 3 && args.size() != 4) {
+ *err = Err(function->function(), "Wrong number of args to write_file",
+ "I expected three or four arguments.");
+ return Value();
+ }
+
+ const Settings* settings = scope->settings();
+ const BuildSettings* build_settings = settings->build_settings();
+ const SourceDir& cur_dir = SourceDirForFunctionCall(function);
+
+ // Find the python script to run.
+ if (!args[0].VerifyTypeIs(Value::STRING, err))
+ return Value();
+ SourceFile script_source =
+ cur_dir.ResolveRelativeFile(args[0].string_value());
+ base::FilePath script_path = build_settings->GetFullPath(script_source);
+ if (!build_settings->secondary_source_path().empty() &&
+ !base::PathExists(script_path)) {
+ // Fall back to secondary source root when the file doesn't exist.
+ script_path = build_settings->GetFullPathSecondary(script_source);
+ }
+
+ // Add all dependencies of this script, including the script itself, to the
+ // build deps.
+ g_scheduler->AddGenDependency(script_source);
+ if (args.size() == 4) {
+ const Value& deps_value = args[3];
+ if (!deps_value.VerifyTypeIs(Value::LIST, err))
+ return Value();
+
+ for (size_t i = 0; i < deps_value.list_value().size(); i++) {
+ if (!deps_value.list_value()[0].VerifyTypeIs(Value::STRING, err))
+ return Value();
+ g_scheduler->AddGenDependency(cur_dir.ResolveRelativeFile(
+ deps_value.list_value()[0].string_value()));
+ }
+ }
+
+ // Make the command line.
+ const base::FilePath& python_path = build_settings->python_path();
+ CommandLine cmdline(python_path);
+ cmdline.AppendArgPath(script_path);
+
+ const Value& script_args = args[1];
+ if (!script_args.VerifyTypeIs(Value::LIST, err))
+ return Value();
+ for (size_t i = 0; i < script_args.list_value().size(); i++) {
+ if (!script_args.list_value()[i].VerifyTypeIs(Value::STRING, err))
+ return Value();
+ cmdline.AppendArg(script_args.list_value()[i].string_value());
+ }
+
+ // Execute the process.
+ // TODO(brettw) set the environment block.
+ std::string output;
+ std::string stderr_output; // TODO(brettw) not hooked up, see above.
+ int exit_code = 0;
+ if (!ExecProcess(cmdline, build_settings->GetFullPath(cur_dir),
+ &output, &stderr_output, &exit_code)) {
+ *err = Err(function->function(), "Could not execute python.",
+ "I was trying to execute \"" + FilePathToUTF8(python_path) + "\".");
+ return Value();
+ }
+
+ // TODO(brettw) maybe we need stderr also for reasonable stack dumps.
+ if (exit_code != 0) {
+ std::string msg =
+ std::string("I was running \"") + FilePathToUTF8(script_path) + "\"\n"
+ "and it returned " + base::IntToString(exit_code);
+ if (!output.empty())
+ msg += " and printed out:\n\n" + output;
+ else
+ msg += ".";
+ *err = Err(function->function(), "Script returned non-zero exit code.",
+ msg);
+ return Value();
+ }
+
+ return ConvertInputToValue(output, function, args[2], err);
+}
diff --git a/tools/gn/function_process_file_template.cc b/tools/gn/function_process_file_template.cc
new file mode 100644
index 0000000..18e1425
--- /dev/null
+++ b/tools/gn/function_process_file_template.cc
@@ -0,0 +1,65 @@
+// 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/file_template.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/parse_tree.h"
+
+/*
+process_file_template: Do template expansion over a list of files.
+
+ process_file_template(source_list, template)
+
+ process_file_template applies a template list to a source file list,
+ returning the result of applying each template to each source. This is
+ typically used for computing output file names from input files.
+
+Arguments:
+
+ The source_list is a list of file names.
+
+ The template can be a string or a list. If it is a list, multiple output
+ strings are generated for each input.
+
+ The following template substrings are used in the template arguments
+ and are replaced with the corresponding part of the input file name:
+
+ "{{source}}": The entire source name.
+
+ "{{source_name_part}}": The source name with no path or extension.
+
+Example:
+
+ sources = [
+ "foo.idl",
+ "bar.idl",
+ ]
+ myoutputs = process_file_template(
+ sources,
+ [ "$target_gen_dir/{{source_name_part}}.cc",
+ "$target_gen_dir/{{source_name_part}}.h" ])
+
+ The result in this case will be:
+ [ "/out/Debug/foo.cc"
+ "/out/Debug/foo.h"
+ "/out/Debug/bar.cc"
+ "/out/Debug/bar.h" ]
+*/
+Value ExecuteProcessFileTemplate(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 2) {
+ *err = Err(function->function(), "Expected two arguments");
+ return Value();
+ }
+
+ FileTemplate file_template(args[1], err);
+ if (err->has_error())
+ return Value();
+
+ Value ret(function, Value::LIST);
+ file_template.Apply(args[0], function, &ret.list_value(), err);
+ return ret;
+}
diff --git a/tools/gn/function_read_file.cc b/tools/gn/function_read_file.cc
new file mode 100644
index 0000000..db981ad
--- /dev/null
+++ b/tools/gn/function_read_file.cc
@@ -0,0 +1,67 @@
+// 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 "base/file_util.h"
+#include "tools/gn/err.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/input_conversion.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/scheduler.h"
+
+// TODO(brettw) consider removing this. I originally wrote it for making the
+// WebKit bindings but misundersood what was required, and didn't need to
+// use this. This seems to have a high potential for misuse.
+
+/*
+read_file: Read a file into a variable.
+
+ read_file(filename, how_to_read)
+
+ Whitespace will be trimmed from the end of the file. Throws an error if the
+ file can not be opened.
+
+Arguments:
+
+ filename:
+ Filename to read, relative to the build file.
+
+ input_conversion:
+ Controls how the file is read and parsed. See "help input_conversion".
+
+Example:
+
+ lines = read_file("foo.txt", "list lines")
+*/
+Value ExecuteReadFile(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 2) {
+ *err = Err(function->function(), "Wrong number of args to read_file",
+ "I expected two arguments.");
+ return Value();
+ }
+ if (!args[0].VerifyTypeIs(Value::STRING, err))
+ return Value();
+
+ // Compute the file name.
+ const SourceDir& cur_dir = SourceDirForFunctionCall(function);
+ SourceFile source_file = cur_dir.ResolveRelativeFile(args[0].string_value());
+ base::FilePath file_path =
+ scope->settings()->build_settings()->GetFullPath(source_file);
+
+ // Ensure that everything is recomputed if the read file changes.
+ g_scheduler->AddGenDependency(source_file);
+
+ // Read contents.
+ std::string file_contents;
+ if (!file_util::ReadFileToString(file_path, &file_contents)) {
+ *err = Err(args[0], "Could not read file.",
+ "I resolved this to \"" + FilePathToUTF8(file_path) + "\".");
+ return Value();
+ }
+
+ return ConvertInputToValue(file_contents, function, args[1], err);
+}
diff --git a/tools/gn/function_set_default_toolchain.cc b/tools/gn/function_set_default_toolchain.cc
new file mode 100644
index 0000000..4939ac8
--- /dev/null
+++ b/tools/gn/function_set_default_toolchain.cc
@@ -0,0 +1,70 @@
+// 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/scope.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/toolchain_manager.h"
+
+/*
+set_default_toolchain: Sets the default toolchain name.
+
+ set_default_toolchain(toolchain_label)
+
+ The given label should identify a toolchain definition (see "toolchain").
+ This toolchain will be used for all targets unless otherwise specified.
+
+ This function is only valid to call during the processing of the build
+ configuration file. Since the build configuration file is processed
+ separately for each toolchain, this function will be a no-op when called
+ under any non-default toolchains.
+
+ For example, the default toolchain should be appropriate for the current
+ environment. If the current environment is 32-bit and somebody references
+ a target with a 64-bit toolchain, we wouldn't want processing of the build
+ config file for the 64-bit toolchain to reset the default toolchain to
+ 64-bit, we want to keep it 32-bits.
+
+Argument:
+
+ toolchain_label:
+ Toolchain name.
+
+Example:
+
+ set_default_toolchain("//build/config/win:vs32")
+*/
+
+Value ExecuteSetDefaultToolchain(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (!scope->IsProcessingBuildConfig()) {
+ *err = Err(function->function(), "Must be called from build config.",
+ "set_default_toolchain can only be called from the build configuration "
+ "file.");
+ return Value();
+ }
+
+ // Ignore non-default-build-config invocations.
+ if (!scope->IsProcessingDefaultBuildConfig())
+ return Value();
+
+ const SourceDir& current_dir = SourceDirForFunctionCall(function);
+ const Label& default_toolchain = ToolchainLabelForScope(scope);
+
+ if (!EnsureSingleStringArg(function, args, err))
+ return Value();
+ Label toolchain_label(
+ Label::Resolve(current_dir, default_toolchain, args[0], err));
+ if (toolchain_label.is_null())
+ return Value();
+
+ ToolchainManager& mgr =
+ scope->settings()->build_settings()->toolchain_manager();
+ mgr.SetDefaultToolchainUnlocked(toolchain_label, function->GetRange(), err);
+ return Value();
+}
diff --git a/tools/gn/function_template.cc b/tools/gn/function_template.cc
new file mode 100644
index 0000000..a85a40f
--- /dev/null
+++ b/tools/gn/function_template.cc
@@ -0,0 +1,38 @@
+// 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/functions.h"
+
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/value.h"
+
+Value ExecuteTemplate(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ // TODO(brettw) determine if the function is built-in and throw an error if
+ // it is.
+ if (args.size() != 1) {
+ *err = Err(function->function(),
+ "Need exactly one string arg to template.");
+ return Value();
+ }
+ if (!args[0].VerifyTypeIs(Value::STRING, err))
+ return Value();
+ std::string template_name = args[0].string_value();
+
+ const FunctionCallNode* existing_template = scope->GetTemplate(template_name);
+ if (existing_template) {
+ *err = Err(function, "Duplicate template definition.",
+ "A template with this name was already defined.");
+ err->AppendSubErr(Err(existing_template->function(),
+ "Previous definition."));
+ return Value();
+ }
+
+ scope->AddTemplate(template_name, function);
+ return Value();
+}
diff --git a/tools/gn/function_toolchain.cc b/tools/gn/function_toolchain.cc
new file mode 100644
index 0000000..b78c639
--- /dev/null
+++ b/tools/gn/function_toolchain.cc
@@ -0,0 +1,126 @@
+// 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/err.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/toolchain.h"
+
+namespace {
+
+// This is jsut a unique value to take the address of to use as the key for
+// the toolchain property on a scope.
+const int kToolchainPropertyKey = 0;
+
+// Reads the given string from the scope (if present) and puts the result into
+// dest. If the value is not a string, sets the error and returns false.
+bool ReadString(Scope& scope, const char* var, std::string* dest, Err* err) {
+ const Value* v = scope.GetValue(var, true);
+ if (!v)
+ return true; // Not present is fine.
+
+ if (!v->VerifyTypeIs(Value::STRING, err))
+ return false;
+ *dest = v->string_value();
+ return true;
+}
+
+} // namespace
+
+Value ExecuteToolchain(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ if (!EnsureNotProcessingImport(function, scope, err) ||
+ !EnsureNotProcessingBuildConfig(function, scope, err))
+ return Value();
+
+ // Note that we don't want to use MakeLabelForScope since that will include
+ // the toolchain name in the label, and toolchain labels don't themselves
+ // have toolchain names.
+ const SourceDir& input_dir = SourceDirForFunctionCall(function);
+ Label label(input_dir, args[0].string_value(), SourceDir(), std::string());
+ if (g_scheduler->verbose_logging())
+ g_scheduler->Log("Generating toolchain", label.GetUserVisibleName(true));
+
+ // This object will actually be copied into the one owned by the toolchain
+ // manager, but that has to be done in the lock.
+ Toolchain toolchain(label);
+
+ Scope block_scope(scope);
+ block_scope.SetProperty(&kToolchainPropertyKey, &toolchain);
+ block->ExecuteBlockInScope(&block_scope, err);
+ block_scope.SetProperty(&kToolchainPropertyKey, NULL);
+ if (err->has_error())
+ return Value();
+ if (!block_scope.CheckForUnusedVars(err))
+ return Value();
+
+ const BuildSettings* build_settings = scope->settings()->build_settings();
+ {
+ // Save the toolchain definition in the toolchain manager and mark the
+ // corresponding item in the dependency tree resolved so that targets
+ // that depend on this toolchain know it's ready.
+ base::AutoLock lock(build_settings->item_tree().lock());
+ build_settings->toolchain_manager().SetToolchainDefinitionLocked(
+ toolchain, function->GetRange(), err);
+ build_settings->item_tree().MarkItemGeneratedLocked(label);
+ }
+ return Value();
+}
+
+Value ExecuteTool(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ // Find the toolchain definition we're executing inside of. The toolchain
+ // function will set a property pointing to it that we'll pick up.
+ Toolchain* toolchain = reinterpret_cast<Toolchain*>(
+ scope->GetProperty(&kToolchainPropertyKey, NULL));
+ if (!toolchain) {
+ *err = Err(function->function(), "tool() called outside of toolchain().",
+ "The tool() function can only be used inside a toolchain() "
+ "definition.");
+ return Value();
+ }
+
+ if (!EnsureSingleStringArg(function, args, err))
+ return Value();
+ const std::string& tool_name = args[0].string_value();
+ Toolchain::ToolType tool_type = Toolchain::ToolNameToType(tool_name);
+ if (tool_type == Toolchain::TYPE_NONE) {
+ *err = Err(args[0], "Unknown tool type");
+ return Value();
+ }
+
+ // Run the tool block.
+ Scope block_scope(scope);
+ block->ExecuteBlockInScope(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ // Extract the stuff we need.
+ Toolchain::Tool t;
+ if (!ReadString(block_scope, "command", &t.command, err) ||
+ !ReadString(block_scope, "depfile", &t.depfile, err) ||
+ !ReadString(block_scope, "deps", &t.deps, err) ||
+ !ReadString(block_scope, "description", &t.description, err) ||
+ !ReadString(block_scope, "pool", &t.pool, err) ||
+ !ReadString(block_scope, "restat", &t.restat, err) ||
+ !ReadString(block_scope, "rspfile", &t.rspfile, err) ||
+ !ReadString(block_scope, "rspfile_content", &t.rspfile_content, err))
+ return Value();
+
+ // Make sure there weren't any vars set in this tool that were unused.
+ if (!block_scope.CheckForUnusedVars(err))
+ return Value();
+
+ toolchain->SetTool(tool_type, t);
+ return Value();
+}
diff --git a/tools/gn/function_write_file.cc b/tools/gn/function_write_file.cc
new file mode 100644
index 0000000..9356feb
--- /dev/null
+++ b/tools/gn/function_write_file.cc
@@ -0,0 +1,84 @@
+// 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 <iostream>
+#include <sstream>
+
+#include "base/file_util.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "tools/gn/err.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scheduler.h"
+
+/*
+write_file: Read a file into a variable.
+
+ write_file(filename, data)
+
+ If data is a list, the list will be written one-item-per-line with no
+ quoting or brackets.
+
+ TODO(brettw) we probably need an optional third argument to control list
+ formatting.
+
+Arguments:
+
+ filename:
+ Filename to write. This must be within the output directory.
+
+ data:
+ The list or string to write.
+*/
+Value ExecuteWriteFile(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 2) {
+ *err = Err(function->function(), "Wrong number of args to write_file",
+ "I expected two arguments.");
+ return Value();
+ }
+
+ // Compute the file name and make sure it's in the output dir.
+ if (!args[0].VerifyTypeIs(Value::STRING, err))
+ return Value();
+ const SourceDir& cur_dir = SourceDirForFunctionCall(function);
+ SourceFile source_file = cur_dir.ResolveRelativeFile(args[0].string_value());
+ if (!EnsureStringIsInOutputDir(
+ scope->settings()->build_settings()->build_dir(),
+ source_file.value(), args[0], err))
+ return Value();
+
+ // Compute output.
+ std::ostringstream contents;
+ if (args[1].type() == Value::LIST) {
+ const std::vector<Value>& list = args[1].list_value();
+ for (size_t i = 0; i < list.size(); i++)
+ contents << list[i].ToString() << std::endl;
+ } else {
+ contents << args[1].ToString();
+ }
+
+ // Write file, creating the directory if necessary.
+ base::FilePath file_path =
+ scope->settings()->build_settings()->GetFullPath(source_file);
+ const std::string& contents_string = contents.str();
+ if (!file_util::CreateDirectory(file_path.DirName())) {
+ *err = Err(function->function(), "Unable to create directory.",
+ "I was using \"" + FilePathToUTF8(file_path.DirName()) + "\".");
+ return Value();
+ }
+ if (file_util::WriteFile(file_path,
+ contents_string.c_str(), contents_string.size())
+ != static_cast<int>(contents_string.size())) {
+ *err = Err(function->function(), "Unable to write file.",
+ "I was writing \"" + FilePathToUTF8(file_path) + "\".");
+ return Value();
+ }
+ return Value();
+}
diff --git a/tools/gn/functions.cc b/tools/gn/functions.cc
new file mode 100644
index 0000000..7f32db2
--- /dev/null
+++ b/tools/gn/functions.cc
@@ -0,0 +1,443 @@
+// 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/functions.h"
+
+#include <iostream>
+
+#include "base/strings/string_util.h"
+#include "tools/gn/config.h"
+#include "tools/gn/config_values_generator.h"
+#include "tools/gn/err.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/item_tree.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/target_manager.h"
+#include "tools/gn/token.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+void FillNeedsBlockError(const FunctionCallNode* function, Err* err) {
+ *err = Err(function->function(), "This function call requires a block.",
+ "The block's \"{\" must be on the same line as the function "
+ "call's \")\".");
+}
+
+Value ExecuteAssert(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 1) {
+ *err = Err(function->function(), "Wrong number of arguments.",
+ "assert() takes one argument, "
+ "were you expecting somethig else?");
+ } else if (args[0].InterpretAsInt() == 0) {
+ *err = Err(function->function(), "Assertion failed.");
+ if (args[0].origin()) {
+ // If you do "assert(foo)" we'd ideally like to show you where foo was
+ // set, and in this case the origin of the args will tell us that.
+ // However, if you do "assert(foo && bar)" the source of the value will
+ // be the assert like, which isn't so helpful.
+ //
+ // So we try to see if the args are from the same line or not. This will
+ // break if you do "assert(\nfoo && bar)" and we may show the second line
+ // as the source, oh well. The way around this is to check to see if the
+ // origin node is inside our function call block.
+ Location origin_location = args[0].origin()->GetRange().begin();
+ if (origin_location.file() != function->function().location().file() ||
+ origin_location.line_number() !=
+ function->function().location().line_number()) {
+ err->AppendSubErr(Err(args[0].origin()->GetRange(), "",
+ "This is where it was set."));
+ }
+ }
+ }
+ return Value();
+}
+
+Value ExecuteConfig(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (!EnsureSingleStringArg(function, args, err) ||
+ !EnsureNotProcessingImport(function, scope, err))
+ return Value();
+
+ Label label(MakeLabelForScope(scope, function, args[0].string_value()));
+
+ if (g_scheduler->verbose_logging())
+ g_scheduler->Log("Generating config", label.GetUserVisibleName(true));
+
+ // Create the empty config object.
+ ItemTree* tree = &scope->settings()->build_settings()->item_tree();
+ Config* config = Config::GetConfig(scope->settings(), function->GetRange(),
+ label, NULL, err);
+ if (err->has_error())
+ return Value();
+
+ // Fill it.
+ const SourceDir input_dir = SourceDirForFunctionCall(function);
+ ConfigValuesGenerator gen(&config->config_values(), scope,
+ function->function(), input_dir, err);
+ gen.Run();
+ if (err->has_error())
+ return Value();
+
+ // Mark as complete.
+ {
+ base::AutoLock lock(tree->lock());
+ tree->MarkItemGeneratedLocked(label);
+ }
+ return Value();
+}
+
+Value ExecuteDeclareArgs(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ // Only allow this to be called once. We use a variable in the current scope
+ // with a name the parser will reject if the user tried to type it.
+ const char did_declare_args_var[] = "@@declared_args";
+ if (scope->GetValue(did_declare_args_var)) {
+ *err = Err(function->function(), "Duplicate call to declared_args.");
+ err->AppendSubErr(
+ Err(scope->GetValue(did_declare_args_var)->origin()->GetRange(),
+ "See the original call."));
+ return Value();
+ }
+
+ // Find the root scope where the values will be set.
+ Scope* root = scope->mutable_containing();
+ if (!root || root->containing() || !scope->IsProcessingBuildConfig()) {
+ *err = Err(function->function(), "declare_args called incorrectly."
+ "It must be called only from the build config script and in the "
+ "root scope.");
+ return Value();
+ }
+
+ // Take all variables set in the current scope as default values and put
+ // them in the parent scope. The values in the current scope are the defaults,
+ // then we apply the external args to this list.
+ Scope::KeyValueVector values;
+ scope->GetCurrentScopeValues(&values);
+ for (size_t i = 0; i < values.size(); i++) {
+ // TODO(brettw) actually import the arguments from the command line rather
+ // than only using the defaults.
+ root->SetValue(values[i].first, values[i].second,
+ values[i].second.origin());
+ }
+
+ scope->SetValue(did_declare_args_var, Value(function, 1), NULL);
+ return Value();
+}
+
+Value ExecuteImport(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (!EnsureSingleStringArg(function, args, err) ||
+ !EnsureNotProcessingImport(function, scope, err))
+ return Value();
+
+ const SourceDir input_dir = SourceDirForFunctionCall(function);
+ SourceFile import_file =
+ input_dir.ResolveRelativeFile(args[0].string_value());
+ scope->settings()->import_manager().DoImport(import_file, function,
+ scope, err);
+ return Value();
+}
+
+Value ExecuteTemplate(Scope* scope,
+ const FunctionCallNode* invocation,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ const FunctionCallNode* rule,
+ Err* err) {
+ if (!EnsureNotProcessingImport(invocation, scope, err))
+ return Value();
+ Scope block_scope(scope);
+ if (!FillTargetBlockScope(scope, invocation,
+ invocation->function().value().data(),
+ block, args, &block_scope, err))
+ return Value();
+
+ // Run the block for the rule invocation.
+ block->ExecuteBlockInScope(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ // Now run the rule itself with that block as the current scope.
+ rule->block()->ExecuteBlockInScope(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ return Value();
+}
+
+Value ExecuteSetDefaults(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ if (!EnsureSingleStringArg(function, args, err))
+ return Value();
+ const std::string& target_type(args[0].string_value());
+
+ // Ensure there aren't defaults already set.
+ if (scope->GetTargetDefaults(target_type)) {
+ *err = Err(function->function(),
+ "This target type defaults were already set.");
+ return Value();
+ }
+
+ // Execute the block in a new scope that has a parent of the containing
+ // scope.
+ Scope block_scope(scope);
+ if (!FillTargetBlockScope(scope, function,
+ function->function().value().data(),
+ block, args, &block_scope, err))
+ return Value();
+
+ // Run the block for the rule invocation.
+ block->ExecuteBlockInScope(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ // Now copy the values set on the scope we made into the free-floating one
+ // (with no containing scope) used to hold the target defaults.
+ Scope* dest = scope->MakeTargetDefaults(target_type);
+ block_scope.NonRecursiveMergeTo(dest, function, "<SHOULD NOT FAIL>", err);
+ return Value();
+}
+
+Value ExecuteSetSourcesAssignmentFilter(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 1) {
+ *err = Err(function, "set_sources_assignment_filter takes one argument.");
+ } else {
+ scoped_ptr<PatternList> f(new PatternList);
+ f->SetFromValue(args[0], err);
+ if (!err->has_error())
+ scope->set_sources_assignment_filter(f.Pass());
+ }
+ return Value();
+}
+
+// void print(...)
+// prints all arguments to the console separated by spaces.
+Value ExecutePrint(const std::vector<Value>& args, Err* err) {
+ for (size_t i = 0; i < args.size(); i++) {
+ if (i != 0)
+ std::cout << " ";
+ std::cout << args[i].ToString();
+ }
+ std::cout << std::endl;
+ return Value();
+}
+
+} // namespace
+
+// ----------------------------------------------------------------------------
+
+namespace functions {
+
+const char kAssert[] = "assert";
+const char kComponent[] = "component";
+const char kConfig[] = "config";
+const char kCopy[] = "copy";
+const char kCustom[] = "custom";
+const char kDeclareArgs[] = "declare_args";
+const char kExecScript[] = "exec_script";
+const char kExecutable[] = "executable";
+const char kGroup[] = "group";
+const char kImport[] = "import";
+const char kPrint[] = "print";
+const char kProcessFileTemplate[] = "process_file_template";
+const char kReadFile[] = "read_file";
+const char kSetDefaults[] = "set_defaults";
+const char kSetDefaultToolchain[] = "set_default_toolchain";
+const char kSetSourcesAssignmentFilter[] = "set_sources_assignment_filter";
+const char kSharedLibrary[] = "shared_library";
+const char kStaticLibrary[] = "static_library";
+const char kTemplate[] = "template";
+const char kTool[] = "tool";
+const char kToolchain[] = "toolchain";
+const char kTest[] = "test";
+const char kWriteFile[] = "write_file";
+
+} // namespace functions
+
+// ----------------------------------------------------------------------------
+
+bool EnsureNotProcessingImport(const ParseNode* node,
+ const Scope* scope,
+ Err* err) {
+ if (scope->IsProcessingImport()) {
+ *err = Err(node, "Not valid from an import.",
+ "We need to talk about this thing you are doing here. Doing this\n"
+ "kind of thing from an imported file makes me feel like you are\n"
+ "abusing me. Imports are for defining defaults, variables, and rules.\n"
+ "The appropriate place for this kind of thing is really in a normal\n"
+ "BUILD file.");
+ return false;
+ }
+ return true;
+}
+
+bool EnsureNotProcessingBuildConfig(const ParseNode* node,
+ const Scope* scope,
+ Err* err) {
+ if (scope->IsProcessingBuildConfig()) {
+ *err = Err(node, "Not valid from the build config.",
+ "You can't do this kind of thing from the build config script, "
+ "silly!\nPut it in a regular BUILD file.");
+ return false;
+ }
+ return true;
+}
+
+bool FillTargetBlockScope(const Scope* scope,
+ const FunctionCallNode* function,
+ const char* target_type,
+ const BlockNode* block,
+ const std::vector<Value>& args,
+ Scope* block_scope,
+ Err* err) {
+ if (!block) {
+ FillNeedsBlockError(function, err);
+ return false;
+ }
+
+ // Copy the target defaults, if any, into the scope we're going to execute
+ // the block in.
+ const Scope* default_scope = scope->GetTargetDefaults(target_type);
+ if (default_scope) {
+ if (!default_scope->NonRecursiveMergeTo(block_scope, function,
+ "target defaults", err))
+ return false;
+ }
+
+ // The name is the single argument to the target function.
+ if (!EnsureSingleStringArg(function, args, err))
+ return false;
+
+ // Set the target name variable to the current target, and mark it used
+ // because we don't want to issue an error if the script ignores it.
+ const base::StringPiece target_name("target_name");
+ block_scope->SetValue(target_name, Value(function, args[0].string_value()),
+ function);
+ block_scope->MarkUsed(target_name);
+ return true;
+}
+
+bool EnsureSingleStringArg(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 1) {
+ *err = Err(function->function(), "Incorrect arguments.",
+ "This function requires a single string argument.");
+ return false;
+ }
+ return args[0].VerifyTypeIs(Value::STRING, err);
+}
+
+const SourceDir& SourceDirForFunctionCall(const FunctionCallNode* function) {
+ return function->function().location().file()->dir();
+}
+
+const Label& ToolchainLabelForScope(const Scope* scope) {
+ return scope->settings()->toolchain()->label();
+}
+
+Label MakeLabelForScope(const Scope* scope,
+ const FunctionCallNode* function,
+ const std::string& name) {
+ const SourceDir& input_dir = SourceDirForFunctionCall(function);
+ const Label& toolchain_label = ToolchainLabelForScope(scope);
+ return Label(input_dir, name, toolchain_label.dir(), toolchain_label.name());
+}
+
+Value ExecuteFunction(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ const Token& name = function->function();
+ if (block) {
+ // These target generators need to execute the block themselves.
+ if (name.IsIdentifierEqualTo(functions::kComponent))
+ return ExecuteComponent(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kCustom))
+ return ExecuteCustom(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kExecutable))
+ return ExecuteExecutable(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kSetDefaults))
+ return ExecuteSetDefaults(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kSharedLibrary))
+ return ExecuteSharedLibrary(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kStaticLibrary))
+ return ExecuteStaticLibrary(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kGroup))
+ return ExecuteGroup(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kTest))
+ return ExecuteExecutable(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kTemplate))
+ return ExecuteTemplate(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kTool))
+ return ExecuteTool(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kToolchain))
+ return ExecuteToolchain(scope, function, args, block, err);
+
+ const FunctionCallNode* rule =
+ scope->GetTemplate(function->function().value().as_string());
+ if (rule)
+ return ExecuteTemplate(scope, function, args, block, rule, err);
+
+ // FIXME(brettw) This is not right, what if you specify a function that
+ // doesn't take a block but specify one?!?!?
+
+ // The rest of the functions can take a pre-executed block for simplicity.
+ Scope block_scope(scope);
+ block->ExecuteBlockInScope(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ if (name.IsIdentifierEqualTo(functions::kConfig))
+ return ExecuteConfig(&block_scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kCopy))
+ return ExecuteCopy(&block_scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kDeclareArgs))
+ return ExecuteDeclareArgs(&block_scope, function, args, err);
+
+ *err = Err(name, "Unknown function.");
+ return Value();
+ }
+
+ if (name.IsIdentifierEqualTo(functions::kAssert))
+ return ExecuteAssert(function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kExecScript))
+ return ExecuteExecScript(scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kImport))
+ return ExecuteImport(scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kPrint))
+ return ExecutePrint(args, err);
+ if (name.IsIdentifierEqualTo(functions::kProcessFileTemplate))
+ return ExecuteProcessFileTemplate(scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kReadFile))
+ return ExecuteReadFile(scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kSetDefaultToolchain))
+ return ExecuteSetDefaultToolchain(scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kSetSourcesAssignmentFilter))
+ return ExecuteSetSourcesAssignmentFilter(scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kWriteFile))
+ return ExecuteWriteFile(scope, function, args, err);
+
+ *err = Err(function, "Unknown function.");
+ return Value();
+}
diff --git a/tools/gn/functions.h b/tools/gn/functions.h
new file mode 100644
index 0000000..806de1d
--- /dev/null
+++ b/tools/gn/functions.h
@@ -0,0 +1,182 @@
+// 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.
+
+#ifndef TOOLS_GN_FUNCTIONS_H_
+#define TOOLS_GN_FUNCTIONS_H_
+
+#include <string>
+#include <vector>
+
+class Err;
+class BlockNode;
+class FunctionCallNode;
+class Label;
+class ListNode;
+class ParseNode;
+class Scope;
+class SourceDir;
+class Token;
+class Value;
+
+Value ExecuteFunction(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block, // Optional.
+ Err* err);
+
+// Function executing functions -----------------------------------------------
+
+Value ExecuteTemplate(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+Value ExecuteExecScript(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+Value ExecuteProcessFileTemplate(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+Value ExecuteReadFile(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+Value ExecuteSetDefaultToolchain(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+Value ExecuteTool(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+Value ExecuteToolchain(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+Value ExecuteWriteFile(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+// Target-generating functions.
+Value ExecuteComponent(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+Value ExecuteCopy(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+Value ExecuteCustom(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+Value ExecuteExecutable(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+Value ExecuteSharedLibrary(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+Value ExecuteStaticLibrary(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+Value ExecuteGroup(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+// Helper functions -----------------------------------------------------------
+
+// Verifies that the current scope is not processing an import. If it is, it
+// will set the error, blame the given parse node for it, and return false.
+bool EnsureNotProcessingImport(const ParseNode* node,
+ const Scope* scope,
+ Err* err);
+
+// Like EnsureNotProcessingImport but checks for running the build config.
+bool EnsureNotProcessingBuildConfig(const ParseNode* node,
+ const Scope* scope,
+ Err* err);
+
+// Sets up the |block_scope| for executing a target (or something like it).
+// The |scope| is the containing scope. It should have been already set as the
+// parent for the |block_scope| when the |block_scope| was created.
+//
+// This will set up the target defaults and set the |target_name| variable in
+// the block scope to the current target name, which is assumed to be the first
+// argument to the function.
+//
+// On success, returns true. On failure, sets the error and returns false.
+bool FillTargetBlockScope(const Scope* scope,
+ const FunctionCallNode* function,
+ const char* target_type,
+ const BlockNode* block,
+ const std::vector<Value>& args,
+ Scope* block_scope,
+ Err* err);
+
+// Validates that the given function call has one string argument. This is
+// the most common function signature, so it saves space to have this helper.
+// Returns false and sets the error on failure.
+bool EnsureSingleStringArg(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+// Returns the source directory for the file comtaining the given function
+// invocation.
+const SourceDir& SourceDirForFunctionCall(const FunctionCallNode* function);
+
+// Returns the name of the toolchain for the given scope.
+const Label& ToolchainLabelForScope(const Scope* scope);
+
+// Generates a label for the given scope, using the current directory and
+// toolchain, and the given name.
+Label MakeLabelForScope(const Scope* scope,
+ const FunctionCallNode* function,
+ const std::string& name);
+
+// Function name constants ----------------------------------------------------
+
+namespace functions {
+
+extern const char kAssert[];
+extern const char kComponent[];
+extern const char kConfig[];
+extern const char kCopy[];
+extern const char kCustom[];
+extern const char kDeclareArgs[];
+extern const char kExecScript[];
+extern const char kExecutable[];
+extern const char kGroup[];
+extern const char kImport[];
+extern const char kPrint[];
+extern const char kProcessFileTemplate[];
+extern const char kReadFile[];
+extern const char kSetDefaults[];
+extern const char kSetDefaultToolchain[];
+extern const char kSetSourcesAssignmentFilter[];
+extern const char kSharedLibrary[];
+extern const char kStaticLibrary[];
+extern const char kTemplate[];
+extern const char kTest[];
+extern const char kTool[];
+extern const char kToolchain[];
+extern const char kWriteFile[];
+
+} // namespace functions
+
+#endif // TOOLS_GN_FUNCTIONS_H_
diff --git a/tools/gn/functions_target.cc b/tools/gn/functions_target.cc
new file mode 100644
index 0000000..a5991e77
--- /dev/null
+++ b/tools/gn/functions_target.cc
@@ -0,0 +1,221 @@
+// 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/functions.h"
+
+#include "tools/gn/err.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/target_generator.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+Value ExecuteGenericTarget(const char* target_type,
+ Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ if (!EnsureNotProcessingImport(function, scope, err) ||
+ !EnsureNotProcessingBuildConfig(function, scope, err))
+ return Value();
+ Scope block_scope(scope);
+ if (!FillTargetBlockScope(scope, function, target_type, block,
+ args, &block_scope, err))
+ return Value();
+
+ block->ExecuteBlockInScope(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ TargetGenerator::GenerateTarget(&block_scope, function->function(), args,
+ target_type, err);
+
+ block_scope.CheckForUnusedVars(err);
+ return Value();
+}
+
+} // namespace
+
+Value ExecuteComponent(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ // A component is either a shared or static library, depending on the value
+ // of |component_mode|.
+ const Value* component_mode_value = scope->GetValue("component_mode");
+
+ static const char helptext[] =
+ "You're declaring a component here but have not defined "
+ "\"component_mode\" to\neither \"shared_library\" or \"static_library\".";
+ if (!component_mode_value) {
+ *err = Err(function->function(), "No component mode set.", helptext);
+ return Value();
+ }
+ if (component_mode_value->type() != Value::STRING ||
+ (component_mode_value->string_value() != functions::kSharedLibrary &&
+ component_mode_value->string_value() != functions::kStaticLibrary)) {
+ *err = Err(function->function(), "Invalid component mode set.", helptext);
+ return Value();
+ }
+ const std::string& component_mode = component_mode_value->string_value();
+
+ if (!EnsureNotProcessingImport(function, scope, err))
+ return Value();
+ Scope block_scope(scope);
+ if (!FillTargetBlockScope(scope, function, component_mode.c_str(), block,
+ args, &block_scope, err))
+ return Value();
+
+ block->ExecuteBlockInScope(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ TargetGenerator::GenerateTarget(&block_scope, function->function(), args,
+ component_mode, err);
+ return Value();
+}
+
+Value ExecuteCopy(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (!EnsureNotProcessingImport(function, scope, err) ||
+ !EnsureNotProcessingBuildConfig(function, scope, err))
+ return Value();
+ TargetGenerator::GenerateTarget(scope, function->function(), args,
+ functions::kCopy, err);
+ return Value();
+}
+
+/*
+custom: Declare a script-generated target.
+
+ This target type allows you to run a script over a set of sources files and
+ generate a set of output files.
+
+ The script will be executed with the given arguments with the current
+ directory being that of the current BUILD file.
+
+ There are two modes. The first mode is the "per-file" mode where you
+ specify a list of sources and the script is run once for each one as a build
+ rule. In this case, each file specified in the |outputs| variable must be
+ unique when applied to each source file (normally you would reference
+ "{{source_name_part}}" from within each one) or the build system will get
+ confused about how to build those files. You should use the |data| variable
+ to list all additional dependencies of your script: these will be added
+ as dependencies for each build step.
+
+ The second mode is when you just want to run a script once rather than as a
+ general rule over a set of files. In this case you don't list any sources.
+ Dependencies of your script are specified only in the |data| variable and
+ your |outputs| variable should just list all outputs.
+
+Variables:
+
+ args, data, deps, outputs, script*, sources
+ * = required
+
+ There are some special substrings that will be searched for when processing
+ some variables:
+
+ "{{source}}"
+ Expanded in |args|, this is the name of the source file relative to the
+ current directory when running the script. This is how you specify
+ the current input file to your script.
+
+ "{{source_name_part}}"
+ Expanded in |args| and |outputs|, this is just the filename part of the
+ current source file with no directory or extension. This is how you
+ specify a name transoformation to the output. Normally you would
+ write an output as "$target_output_dir/{{source_name_part}}.o".
+
+ All |outputs| files must be inside the output directory of the build. You
+ would generally use "$target_output_dir" or "$target_gen_dir" to reference
+ the output or generated intermediate file directories, respectively.
+
+Examples:
+
+ custom("general_rule") {
+ script = "do_processing.py"
+ sources = [ "foo.idl" ]
+ data = [ "my_configuration.txt" ]
+ outputs = [ "$target_gen_dir/{{source_name_part}}.h" ]
+ args = [ "{{source}}",
+ "-o", "$relative_target_gen_dir/{{source_name_part}}.h" ]
+ }
+
+ custom("just_run_this_guy_once") {
+ script = "doprocessing.py"
+ data = [ "my_configuration.txt" ]
+ outputs = [ "$target_gen_dir/insightful_output.txt" ]
+ args = [ "--output_dir", $target_gen_dir ]
+ }
+*/
+Value ExecuteCustom(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kCustom, scope, function, args,
+ block, err);
+}
+
+Value ExecuteExecutable(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kExecutable, scope, function, args,
+ block, err);
+}
+
+Value ExecuteSharedLibrary(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kSharedLibrary, scope, function, args,
+ block, err);
+}
+
+Value ExecuteStaticLibrary(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kStaticLibrary, scope, function, args,
+ block, err);
+}
+
+/*
+group: Declare a group of targets.
+
+ This target type allows you to create meta-targets that just collect a set
+ of dependencies into one named target.
+
+Variables:
+
+ deps
+
+Example:
+
+ group("all") {
+ deps = [
+ "//project:runner",
+ "//project:unit_tests",
+ ]
+ }
+*/
+Value ExecuteGroup(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kGroup, scope, function, args,
+ block, err);
+}
+
diff --git a/tools/gn/generate_test_gn_data.cc b/tools/gn/generate_test_gn_data.cc
new file mode 100644
index 0000000..017c4c4
--- /dev/null
+++ b/tools/gn/generate_test_gn_data.cc
@@ -0,0 +1,129 @@
+// 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 <fstream>
+#include <iostream>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+
+// Usage: just run in the directory where you want your test source root to be.
+
+int files_written = 0;
+int targets_written = 0;
+
+base::FilePath UTF8ToFilePath(const std::string& s) {
+#if defined(OS_WIN)
+ return base::FilePath(UTF8ToWide(s));
+#else
+ return base::FilePath(s);
+#endif
+}
+
+std::string FilePathToUTF8(const base::FilePath& path) {
+#if defined(OS_WIN)
+ return WideToUTF8(path.value());
+#else
+ return path.value();
+#endif
+}
+
+base::FilePath RepoPathToPathName(const std::vector<int>& repo_path) {
+ base::FilePath ret;
+ for (size_t i = 0; i < repo_path.size(); i++) {
+ ret = ret.Append(UTF8ToFilePath(base::IntToString(repo_path[i])));
+ }
+ return ret;
+}
+
+std::string TargetIndexToLetter(int target_index) {
+ char ret[2];
+ ret[0] = 'a' + target_index;
+ ret[1] = 0;
+ return ret;
+}
+
+std::string RepoPathToTargetName(const std::vector<int>& repo_path,
+ int target_index) {
+ std::string ret;
+ for (size_t i = 0; i < repo_path.size(); i++) {
+ if (i != 0)
+ ret.push_back('_');
+ ret.append(base::IntToString(repo_path[i]));
+ }
+ ret += TargetIndexToLetter(target_index);
+ return ret;
+}
+
+std::string RepoPathToFullTargetName(const std::vector<int>& repo_path,
+ int target_index) {
+ std::string ret;
+ for (size_t i = 0; i < repo_path.size(); i++) {
+ ret.push_back('/');
+ ret.append(base::IntToString(repo_path[i]));
+ }
+
+ ret += ":" + RepoPathToTargetName(repo_path, target_index);
+ return ret;
+}
+
+void WriteLevel(const std::vector<int>& repo_path,
+ int spread,
+ int max_depth,
+ int targets_per_level,
+ int files_per_target) {
+ base::FilePath dirname = RepoPathToPathName(repo_path);
+ base::FilePath filename = dirname.AppendASCII("BUILD.gn");
+ std::cout << "Writing " << FilePathToUTF8(filename) << "\n";
+
+ // Don't keep the file open while recursing.
+ {
+ file_util::CreateDirectory(dirname);
+
+ std::ofstream file;
+ file.open(FilePathToUTF8(filename).c_str(),
+ std::ios_base::out | std::ios_base::binary);
+ files_written++;
+
+ for (int i = 0; i < targets_per_level; i++) {
+ targets_written++;
+ file << "executable(\"" << RepoPathToTargetName(repo_path, i)
+ << "\") {\n";
+ file << " sources = [\n";
+ for (int f = 0; f < files_per_target; f++)
+ file << " \"" << base::IntToString(f) << ".cc\",\n";
+
+ if (repo_path.size() < (size_t)max_depth) {
+ file << " ]\n";
+ file << " deps = [\n";
+ for (int d = 0; d < spread; d++) {
+ std::vector<int> cur = repo_path;
+ cur.push_back(d);
+ for (int t = 0; t < targets_per_level; t++)
+ file << " \"" << RepoPathToFullTargetName(cur, t) << "\",\n";
+ }
+ }
+ file << " ]\n}\n\n";
+ }
+ }
+ if (repo_path.size() < (size_t)max_depth) {
+ // Recursively generate subdirs.
+ for (int i = 0; i < spread; i++) {
+ std::vector<int> cur = repo_path;
+ cur.push_back(i);
+ WriteLevel(cur, spread, max_depth, targets_per_level, files_per_target);
+ }
+ }
+}
+
+int main() {
+ WriteLevel(std::vector<int>(), 5, 4, 3, 50); // 781 files, 2343 targets
+ //WriteLevel(std::vector<int>(), 6, 4, 2, 50);
+ std::cout << "Wrote " << files_written << " files and "
+ << targets_written << " targets.\n";
+ return 0;
+}
diff --git a/tools/gn/gn.gyp b/tools/gn/gn.gyp
new file mode 100644
index 0000000..4904e3d
--- /dev/null
+++ b/tools/gn/gn.gyp
@@ -0,0 +1,171 @@
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'gn_lib',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ ],
+ 'sources': [
+ 'build_settings.cc',
+ 'build_settings.h',
+ 'command_desc.cc',
+ 'command_desc.h',
+ 'command_gen.cc',
+ 'command_gen.h',
+ 'commands.h',
+ 'config.cc',
+ 'config.h',
+ 'config_values.cc',
+ 'config_values.h',
+ 'config_values_extractors.cc',
+ 'config_values_extractors.h',
+ 'config_values_generator.cc',
+ 'config_values_generator.h',
+ 'err.cc',
+ 'err.h',
+ 'escape.cc',
+ 'escape.h',
+ 'file_template.cc',
+ 'file_template.h',
+ 'filesystem_utils.cc',
+ 'filesystem_utils.h',
+ 'functions_target.cc',
+ 'functions.cc',
+ 'functions.h',
+ 'function_exec_script.cc',
+ 'function_process_file_template.cc',
+ 'function_read_file.cc',
+ 'function_set_default_toolchain.cc',
+ 'function_template.cc',
+ 'function_toolchain.cc',
+ 'function_write_file.cc',
+ 'import_manager.cc',
+ 'import_manager.h',
+ 'input_conversion.cc',
+ 'input_conversion.h',
+ 'input_file.cc',
+ 'input_file.h',
+ 'input_file_manager.cc',
+ 'input_file_manager.h',
+ 'item.cc',
+ 'item.h',
+ 'item_node.cc',
+ 'item_node.h',
+ 'item_tree.cc',
+ 'item_tree.h',
+ 'label.cc',
+ 'label.h',
+ 'location.h',
+ 'ninja_build_writer.cc',
+ 'ninja_build_writer.h',
+ 'ninja_helper.cc',
+ 'ninja_helper.h',
+ 'ninja_target_writer.cc',
+ 'ninja_target_writer.h',
+ 'ninja_toolchain_writer.cc',
+ 'ninja_toolchain_writer.h',
+ 'ninja_writer.cc',
+ 'ninja_writer.h',
+ 'operators.cc',
+ 'operators.h',
+ 'output_file.h',
+ 'parse_tree.cc',
+ 'parse_tree.h',
+ 'parser.cc',
+ 'parser.h',
+ 'path_output.cc',
+ 'path_output.h',
+ 'pattern.cc',
+ 'pattern.h',
+ 'scheduler.cc',
+ 'scheduler.h',
+ 'scope.cc',
+ 'scope.h',
+ 'scope_per_file_provider.cc',
+ 'scope_per_file_provider.h',
+ 'settings.cc',
+ 'settings.h',
+ 'setup.cc',
+ 'setup.h',
+ 'source_dir.cc',
+ 'source_dir.h',
+ 'source_file.cc',
+ 'source_file.h',
+ 'standard_out.cc',
+ 'standard_out.h',
+ 'string_utils.cc',
+ 'string_utils.h',
+ 'target.cc',
+ 'target.h',
+ 'target_generator.cc',
+ 'target_generator.h',
+ 'target_manager.cc',
+ 'target_manager.h',
+ 'token.cc',
+ 'token.h',
+ 'tokenizer.cc',
+ 'tokenizer.h',
+ 'toolchain.cc',
+ 'toolchain.h',
+ 'toolchain_manager.cc',
+ 'toolchain_manager.h',
+ 'value.cc',
+ 'value.h',
+ 'value_extractors.cc',
+ 'value_extractors.h',
+ ],
+ },
+ {
+ 'target_name': 'gn',
+ 'type': 'executable',
+ 'sources': [
+ 'gn_main.cc',
+ ],
+ 'dependencies': [
+ 'gn_lib',
+ '../../base/base.gyp:base',
+ '../../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ ],
+ },
+ {
+ 'target_name': 'gn_unittests',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ 'escape_unittest.cc',
+ 'file_template_unittest.cc',
+ 'filesystem_utils_unittest.cc',
+ 'input_conversion_unittest.cc',
+ 'label_unittest.cc',
+ 'ninja_helper_unittest.cc',
+ 'parser_unittest.cc',
+ 'path_output_unittest.cc',
+ 'pattern_unittest.cc',
+ 'source_dir_unittest.cc',
+ 'string_utils_unittest.cc',
+ 'target_generator_unittest.cc',
+ 'target_manager_unittest.cc',
+ 'tokenizer_unittest.cc',
+ ],
+ 'dependencies': [
+ 'gn_lib',
+ '../../base/base.gyp:run_all_unittests',
+ '../../base/base.gyp:test_support_base',
+ '../../testing/gtest.gyp:gtest',
+ ],
+ },
+ {
+ 'target_name': 'generate_test_gn_data',
+ 'type': 'executable',
+ 'sources': [
+ 'generate_test_gn_data.cc',
+ ],
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ ],
+ }
+ ],
+}
diff --git a/tools/gn/gn_main.cc b/tools/gn/gn_main.cc
new file mode 100644
index 0000000..6f5b31e
--- /dev/null
+++ b/tools/gn/gn_main.cc
@@ -0,0 +1,59 @@
+// 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 "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/strings/utf_string_conversions.h"
+#include "tools/gn/commands.h"
+#include "tools/gn/err.h"
+#include "tools/gn/location.h"
+
+namespace {
+
+std::vector<std::string> GetArgs(const CommandLine& cmdline) {
+ CommandLine::StringVector in_args = cmdline.GetArgs();
+#if defined(OS_WIN)
+ std::vector<std::string> out_args;
+ for (size_t i = 0; i < in_args.size(); i++)
+ out_args.push_back(base::WideToUTF8(in_args[i]));
+ return out_args;
+#else
+ return in_args;
+#endif
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ base::AtExitManager at_exit;
+ CommandLine::Init(argc, argv);
+
+ std::vector<std::string> args = GetArgs(*CommandLine::ForCurrentProcess());
+
+ std::string command;
+ if (args.empty()) {
+ command = "gen";
+ } else {
+ command = args[0];
+ args.erase(args.begin());
+ }
+
+ int retval = 0;
+ if (command == "gen") {
+ retval = RunGenCommand(args);
+ } else if (command == "desc" || command == "wtf") {
+ retval = RunDescCommand(args);
+ } else if (command == "deps") {
+ retval = RunDepsCommand(args);
+ } else if (command == "tree") {
+ retval = RunTreeCommand(args);
+ } else {
+ Err(Location(),
+ "Command \"" + command + "\" unknown.").PrintToStdout();
+ retval = 1;
+ }
+
+ exit(retval); // Don't free memory, it can be really slow!
+ return retval;
+}
diff --git a/tools/gn/import_manager.cc b/tools/gn/import_manager.cc
new file mode 100644
index 0000000..774f866
--- /dev/null
+++ b/tools/gn/import_manager.cc
@@ -0,0 +1,83 @@
+// 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/import_manager.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scheduler.h"
+
+namespace {
+
+// Returns a newly-allocated scope on success, null on failure.
+Scope* UncachedImport(const Settings* settings,
+ const SourceFile& file,
+ const ParseNode* node_for_err,
+ Err* err) {
+ const ParseNode* node = g_scheduler->input_file_manager()->SyncLoadFile(
+ node_for_err->GetRange(), settings->build_settings(), file, err);
+ if (!node)
+ return NULL;
+ const BlockNode* block = node->AsBlock();
+ CHECK(block);
+
+ scoped_ptr<Scope> scope(new Scope(settings->base_config()));
+ scope->SetProcessingImport();
+ block->ExecuteBlockInScope(scope.get(), err);
+ if (err->has_error())
+ return NULL;
+ scope->ClearProcessingImport();
+
+ return scope.release();
+}
+
+} // namesapce
+
+ImportManager::ImportManager() {
+}
+
+ImportManager::~ImportManager() {
+ STLDeleteContainerPairSecondPointers(imports_.begin(), imports_.end());
+}
+
+bool ImportManager::DoImport(const SourceFile& file,
+ const ParseNode* node_for_err,
+ Scope* scope,
+ Err* err) {
+ // See if we have a cached import, but be careful to actually do the scope
+ // copying outside of the lock.
+ const Scope* imported_scope = NULL;
+ {
+ base::AutoLock lock(lock_);
+ ImportMap::const_iterator found = imports_.find(file);
+ if (found != imports_.end())
+ imported_scope = found->second;
+ }
+
+ if (!imported_scope) {
+ // Do a new import of the file.
+ imported_scope = UncachedImport(scope->settings(), file,
+ node_for_err, err);
+ if (!imported_scope)
+ return false;
+
+ // We loaded the file outside the lock. This means that there could be a
+ // race and the file was already loaded on a background thread. Recover
+ // from this and use the existing one if that happens.
+ {
+ base::AutoLock lock(lock_);
+ ImportMap::const_iterator found = imports_.find(file);
+ if (found != imports_.end()) {
+ delete imported_scope;
+ imported_scope = found->second;
+ } else {
+ imports_[file] = imported_scope;
+ }
+ }
+ }
+
+ return imported_scope->NonRecursiveMergeTo(scope, node_for_err,
+ "import", err);
+}
diff --git a/tools/gn/import_manager.h b/tools/gn/import_manager.h
new file mode 100644
index 0000000..725a5b2
--- /dev/null
+++ b/tools/gn/import_manager.h
@@ -0,0 +1,41 @@
+// 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.
+
+#ifndef TOOLS_GN_IMPORT_MANAGER_H_
+#define TOOLS_GN_IMPORT_MANAGER_H_
+
+#include <map>
+
+#include "base/synchronization/lock.h"
+
+class Err;
+class ParseNode;
+class Scope;
+class SourceFile;
+
+// Provides a cache of the results of importing scopes so the results can
+// be re-used rather than running the imported files multiple times.
+class ImportManager {
+ public:
+ ImportManager();
+ ~ImportManager();
+
+ // Does an import of the given file into the given scope. On error, sets the
+ // error and returns false.
+ bool DoImport(const SourceFile& file,
+ const ParseNode* node_for_err,
+ Scope* scope,
+ Err* err);
+
+ private:
+ base::Lock lock_;
+
+ // Owning pointers to the scopes.
+ typedef std::map<SourceFile, const Scope*> ImportMap;
+ ImportMap imports_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImportManager);
+};
+
+#endif // TOOLS_GN_IMPORT_MANAGER_H_
diff --git a/tools/gn/input_conversion.cc b/tools/gn/input_conversion.cc
new file mode 100644
index 0000000..c0a0685
--- /dev/null
+++ b/tools/gn/input_conversion.cc
@@ -0,0 +1,205 @@
+// 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 "input_conversion.h"
+
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/err.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/label.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/parser.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/tokenizer.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+// Returns the "first bit" of some script output for writing to error messages.
+std::string GetExampleOfBadInput(const std::string& input) {
+ std::string result(input);
+
+ // Maybe the result starts with a blank line or something, which we don't
+ // want.
+ TrimWhitespaceASCII(result, TRIM_ALL, &result);
+
+ // Now take the first line, or the first set of chars, whichever is shorter.
+ bool trimmed = false;
+ size_t newline_offset = result.find('\n');
+ if (newline_offset != std::string::npos) {
+ trimmed = true;
+ result.resize(newline_offset);
+ }
+ TrimWhitespaceASCII(result, TRIM_ALL, &result);
+
+ const int kMaxSize = 50;
+ if (result.size() > kMaxSize) {
+ trimmed = true;
+ result.resize(kMaxSize);
+ }
+
+ if (trimmed)
+ result.append("...");
+ return result;
+}
+
+// When parsing the result as a value, we may get various types of errors.
+// This creates an error message for this case with an optional nested error
+// message to reference. If there is no nested err, pass Err().
+//
+// This code also takes care to rewrite the original error which will reference
+// the temporary InputFile which won't exist when the error is propogated
+// out to a higher level.
+Err MakeParseErr(const std::string& input,
+ const ParseNode* origin,
+ const Err& nested) {
+ std::string help_text =
+ "When parsing a result as a \"value\" it should look like a list:\n"
+ " [ \"a\", \"b\", 5 ]\n"
+ "or a single literal:\n"
+ " \"my result\"\n"
+ "but instead I got this, which I find very confusing:\n";
+ help_text.append(input);
+ if (nested.has_error())
+ help_text.append("\nThe exact error was:");
+
+ Err result(origin, "Script result wasn't a valid value.", help_text);
+ if (nested.has_error()) {
+ result.AppendSubErr(Err(LocationRange(), nested.message(),
+ nested.help_text()));
+ }
+ return result;
+}
+
+// Sets the origin of the value and any nested values with the given node.
+void RecursivelySetOrigin(Value* value, const ParseNode* origin) {
+ value->set_origin(origin);
+ if (value->type() == Value::LIST) {
+ std::vector<Value>& list_value = value->list_value();
+ for (size_t i = 0; i < list_value.size(); i++)
+ RecursivelySetOrigin(&list_value[i], origin);
+ }
+}
+
+Value ParseString(const std::string& input,
+ const ParseNode* origin,
+ Err* err) {
+ SourceFile empty_source_for_most_vexing_parse;
+ InputFile input_file(empty_source_for_most_vexing_parse);
+ input_file.SetContents(input);
+
+ std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, err);
+ if (err->has_error()) {
+ *err = MakeParseErr(input, origin, *err);
+ return Value();
+ }
+
+ scoped_ptr<ParseNode> expression = Parser::ParseExpression(tokens, err);
+ if (err->has_error()) {
+ *err = MakeParseErr(input, origin, *err);
+ return Value();
+ }
+
+ // It's valid for the result to be a null pointer, this just means that the
+ // script returned nothing.
+ if (!expression)
+ return Value();
+
+ // The result should either be a list or a literal, anything else is
+ // invalid.
+ if (!expression->AsList() && !expression->AsLiteral()) {
+ *err = MakeParseErr(input, origin, Err());
+ return Value();
+ }
+
+ BuildSettings build_settings;
+ Label empty_label;
+ Toolchain toolchain(empty_label);
+ Settings settings(&build_settings, &toolchain, std::string());
+ Scope scope(&settings);
+
+ Err nested_err;
+ Value result = expression->Execute(&scope, &nested_err);
+ if (nested_err.has_error()) {
+ *err = MakeParseErr(input, origin, nested_err);
+ return Value();
+ }
+
+ // The returned value will have references to the temporary parse nodes we
+ // made on the stack. If the values are used in an error message in the
+ // future, this will crash. Reset the origin of all values to be our
+ // containing origin.
+ RecursivelySetOrigin(&result, origin);
+ return result;
+}
+
+Value ParseList(const std::string& input,
+ const ParseNode* origin,
+ Err* err) {
+ Value ret(origin, Value::LIST);
+ std::vector<std::string> as_lines;
+ base::SplitString(input, '\n', &as_lines);
+
+ // Trim empty lines from the end.
+ // Do we want to make this configurable?
+ while (!as_lines.empty() && as_lines[as_lines.size() - 1].empty())
+ as_lines.resize(as_lines.size() - 1);
+
+ ret.list_value().reserve(as_lines.size());
+ for (size_t i = 0; i < as_lines.size(); i++)
+ ret.list_value().push_back(Value(origin, as_lines[i]));
+ return ret;
+}
+
+} // namespace
+
+/*
+input_conversion: Specifies how to transform input to a variable.
+
+ input_conversion is an argument to read_file and exec_script that specifies
+ how the result of the read operation should be converted into a variable.
+
+ "list lines":
+ Return the file contents as a list, with a string for each line. The
+ newlines will not be present in the result. Empty newlines will be
+ trimmed from the trailing end of the returned list.
+
+ "value":
+ Parse the input as if it was a literal rvalue in a buildfile.
+ Examples of typical program output using this mode:
+ [ "foo", "bar" ] (result will be a list)
+ or
+ "foo bar" (result will be a string)
+ or
+ 5 (result will be an integer)
+
+ Note that if the input is empty, the result will be a null value which
+ will produce an error if assigned to a variable.
+
+ "string":
+ Return the file contents into a single string.
+*/
+
+Value ConvertInputToValue(const std::string& input,
+ const ParseNode* origin,
+ const Value& input_conversion_value,
+ Err* err) {
+ if (!input_conversion_value.VerifyTypeIs(Value::STRING, err))
+ return Value();
+ const std::string& input_conversion = input_conversion_value.string_value();
+
+ if (input_conversion == "value")
+ return ParseString(input, origin, err);
+ if (input_conversion == "string")
+ return Value(origin, input);
+ if (input_conversion == "list lines")
+ return ParseList(input, origin, err);
+
+ *err = Err(input_conversion_value, "Not a valid read file mode.",
+ "Have you considered a career in retail?");
+ return Value();
+}
diff --git a/tools/gn/input_conversion.h b/tools/gn/input_conversion.h
new file mode 100644
index 0000000..d15e513
--- /dev/null
+++ b/tools/gn/input_conversion.h
@@ -0,0 +1,26 @@
+// 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.
+
+#ifndef TOOLS_GN_INPUT_CONVERSION_H_
+#define TOOLS_GN_INPUT_CONVERSION_H_
+
+#include <string>
+
+class Err;
+class ParseNode;
+class Value;
+
+// Converts the given input string (is read from a file or output from a
+// script) to a Value. Conversions as specified in the input_conversion string
+// will be performed. The given origin will be used for constructing the
+// resulting Value.
+//
+// If the conversion string is invalid, the error will be set and an empty
+// value will be returned.
+Value ConvertInputToValue(const std::string& input,
+ const ParseNode* origin,
+ const Value& input_conversion_value,
+ Err* err);
+
+#endif // TOOLS_GN_INPUT_CONVERSION_H_
diff --git a/tools/gn/input_conversion_unittest.cc b/tools/gn/input_conversion_unittest.cc
new file mode 100644
index 0000000..59c0123
--- /dev/null
+++ b/tools/gn/input_conversion_unittest.cc
@@ -0,0 +1,81 @@
+// 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/err.h"
+#include "tools/gn/input_conversion.h"
+#include "tools/gn/value.h"
+
+TEST(InputConversion, String) {
+ Err err;
+ std::string input("\nfoo bar \n");
+ Value result = ConvertInputToValue(input, NULL, Value(NULL, "string"), &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(Value::STRING, result.type());
+ EXPECT_EQ(input, result.string_value());
+}
+
+TEST(InputConversion, ListLines) {
+ Err err;
+ std::string input("\nfoo\nbar \n");
+ Value result = ConvertInputToValue(input, NULL, Value(NULL, "list lines"),
+ &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(Value::LIST, result.type());
+ ASSERT_EQ(3u, result.list_value().size());
+ EXPECT_EQ("", result.list_value()[0].string_value());
+ EXPECT_EQ("foo", result.list_value()[1].string_value());
+ EXPECT_EQ("bar", result.list_value()[2].string_value());
+}
+
+TEST(InputConversion, ValueString) {
+ Err err;
+ std::string input("\"str\"");
+ Value result = ConvertInputToValue(input, NULL, Value(NULL, "value"), &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(Value::STRING, result.type());
+ EXPECT_EQ("str", result.string_value());
+}
+
+TEST(InputConversion, ValueInt) {
+ Err err;
+ std::string input("\n\n 6 \n ");
+ Value result = ConvertInputToValue(input, NULL, Value(NULL, "value"), &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(Value::INTEGER, result.type());
+ EXPECT_EQ(6, result.int_value());
+}
+
+TEST(InputConversion, ValueList) {
+ Err err;
+ std::string input("\n [ \"a\", 5]");
+ Value result = ConvertInputToValue(input, NULL, Value(NULL, "value"), &err);
+ EXPECT_FALSE(err.has_error());
+ ASSERT_EQ(Value::LIST, result.type());
+ ASSERT_EQ(2u, result.list_value().size());
+ EXPECT_EQ("a", result.list_value()[0].string_value());
+ EXPECT_EQ(5, result.list_value()[1].int_value());
+}
+
+TEST(InputConversion, ValueEmpty) {
+ Err err;
+ ConvertInputToValue("", NULL, Value(NULL, "value"), &err);
+}
+
+TEST(InputConversion, ValueError) {
+ Err err;
+ std::string input("\n [ \"a\", 5\nfoo bar");
+ Value result = ConvertInputToValue(input, NULL, Value(NULL, "value"), &err);
+ EXPECT_TRUE(err.has_error());
+
+ // Blocks not allowed.
+ input = "{ foo = 5 }";
+ result = ConvertInputToValue(input, NULL, Value(NULL, "value"), &err);
+ EXPECT_TRUE(err.has_error());
+
+ // Function calls not allowed.
+ input = "print(5)";
+ result = ConvertInputToValue(input, NULL, Value(NULL, "value"), &err);
+ EXPECT_TRUE(err.has_error());
+}
diff --git a/tools/gn/input_file.cc b/tools/gn/input_file.cc
new file mode 100644
index 0000000..b2c3c0b
--- /dev/null
+++ b/tools/gn/input_file.cc
@@ -0,0 +1,30 @@
+// 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/input_file.h"
+
+#include "base/file_util.h"
+
+InputFile::InputFile(const SourceFile& name)
+ : name_(name),
+ dir_(name_.GetDir()),
+ contents_loaded_(false) {
+}
+
+InputFile::~InputFile() {
+}
+
+void InputFile::SetContents(const std::string& c) {
+ contents_loaded_ = true;
+ contents_ = c;
+}
+
+bool InputFile::Load(const base::FilePath& system_path) {
+ if (file_util::ReadFileToString(system_path, &contents_)) {
+ contents_loaded_ = true;
+ return true;
+ }
+ return false;
+}
+
diff --git a/tools/gn/input_file.h b/tools/gn/input_file.h
new file mode 100644
index 0000000..66cf55c
--- /dev/null
+++ b/tools/gn/input_file.h
@@ -0,0 +1,52 @@
+// 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.
+
+#ifndef TOOLS_GN_INPUT_FILE_H_
+#define TOOLS_GN_INPUT_FILE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "tools/gn/source_dir.h"
+#include "tools/gn/source_file.h"
+
+class InputFile {
+ public:
+ InputFile(const SourceFile& name);
+
+ // Constructor for testing. Uses an empty file path and a given contents.
+ //InputFile(const char* contents);
+ ~InputFile();
+
+ const SourceFile& name() const { return name_; }
+
+ // The directory is just a cached version of name_->GetDir() but we get this
+ // a lot so computing it once up front saves a bunch of work.
+ const SourceDir& dir() const { return dir_; }
+
+ const std::string& contents() const {
+ DCHECK(contents_loaded_);
+ return contents_;
+ }
+
+ // For testing and in cases where this input doesn't actually refer to
+ // "a file".
+ void SetContents(const std::string& c);
+
+ // Loads the given file synchronously, returning true on success. This
+ bool Load(const base::FilePath& system_path);
+
+ private:
+ SourceFile name_;
+ SourceDir dir_;
+
+ bool contents_loaded_;
+ std::string contents_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputFile);
+};
+
+#endif // TOOLS_GN_INPUT_FILE_H_
diff --git a/tools/gn/input_file_manager.cc b/tools/gn/input_file_manager.cc
new file mode 100644
index 0000000..e840ba0
--- /dev/null
+++ b/tools/gn/input_file_manager.cc
@@ -0,0 +1,254 @@
+// 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/input_file_manager.h"
+
+#include "base/bind.h"
+#include "base/stl_util.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/parser.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/scope_per_file_provider.h"
+#include "tools/gn/tokenizer.h"
+
+namespace {
+
+void InvokeFileLoadCallback(const InputFileManager::FileLoadCallback& cb,
+ const ParseNode* node) {
+ cb.Run(node);
+}
+
+} // namespace
+
+InputFileManager::InputFileData::InputFileData(const SourceFile& file_name)
+ : file(file_name),
+ loaded(false),
+ sync_invocation(false) {
+}
+
+InputFileManager::InputFileData::~InputFileData() {
+}
+
+InputFileManager::InputFileManager() {
+}
+
+InputFileManager::~InputFileManager() {
+ // Should be single-threaded by now.
+ STLDeleteContainerPairSecondPointers(input_files_.begin(),
+ input_files_.end());
+}
+
+bool InputFileManager::AsyncLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& file_name,
+ const FileLoadCallback& callback,
+ Err* err) {
+ // Try not to schedule callbacks while holding the lock. All cases that don't
+ // want to schedule should return early. Otherwise, this will be scheduled
+ // after we leave the lock.
+ base::Closure schedule_this;
+ {
+ base::AutoLock lock(lock_);
+
+ InputFileMap::const_iterator found = input_files_.find(file_name);
+ if (found == input_files_.end()) {
+ // New file, schedule load.
+ InputFileData* data = new InputFileData(file_name);
+ data->scheduled_callbacks.push_back(callback);
+ input_files_[file_name] = data;
+
+ schedule_this = base::Bind(&InputFileManager::BackgroundLoadFile,
+ this,
+ origin,
+ build_settings,
+ file_name,
+ &data->file);
+ } else {
+ InputFileData* data = found->second;
+
+ // Prevent mixing async and sync loads. See SyncLoadFile for discussion.
+ if (data->sync_invocation) {
+ g_scheduler->FailWithError(Err(
+ origin, "Load type mismatch.",
+ "The file \"" + file_name.value() + "\" was previously loaded\n"
+ "synchronously (via an import) and now you're trying to load it "
+ "asynchronously\n(via a deps rule). This is a class 2 misdemeanor: "
+ "a single input file must\nbe loaded the same way each time to "
+ "avoid blowing my tiny, tiny mind."));
+ return false;
+ }
+
+ if (data->loaded) {
+ // Can just directly issue the callback on the background thread.
+ schedule_this = base::Bind(&InvokeFileLoadCallback, callback,
+ data->parsed_root.get());
+ } else {
+ // Load is pending on this file, schedule the invoke.
+ data->scheduled_callbacks.push_back(callback);
+ return true;
+ }
+ }
+ }
+ g_scheduler->pool()->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE, schedule_this,
+ base::SequencedWorkerPool::BLOCK_SHUTDOWN);
+ return true;
+}
+
+const ParseNode* InputFileManager::SyncLoadFile(
+ const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& file_name,
+ Err* err) {
+ base::AutoLock lock(lock_);
+
+ InputFileData* data = NULL;
+ InputFileMap::iterator found = input_files_.find(file_name);
+ if (found == input_files_.end()) {
+ base::AutoUnlock unlock(lock_);
+
+ // Haven't seen this file yet, start loading right now.
+ data = new InputFileData(file_name);
+ data->sync_invocation = true;
+ input_files_[file_name] = data;
+
+ if (!LoadFile(origin, build_settings, file_name, &data->file, err))
+ return NULL;
+ } else {
+ // This file has either been loaded or is pending loading.
+ data = found->second;
+
+ if (!data->sync_invocation) {
+ // Don't allow mixing of sync and async loads. If an async load is
+ // scheduled and then a bunch of threads need to load it synchronously
+ // and block on it loading, it could deadlock or at least cause a lot
+ // of wasted CPU while those threads wait for the load to complete (which
+ // may be far back in the input queue).
+ //
+ // We could work around this by promoting the load to a sync load. This
+ // requires a bunch of extra code to either check flags and likely do
+ // extra locking (bad) or to just do both types of load on the file and
+ // deal with the race condition.
+ //
+ // I have no practical way to test this, and generally we should have
+ // all include files processed synchronously and all build files
+ // processed asynchronously, so it doesn't happen in practice.
+ *err = Err(
+ origin, "Load type mismatch.",
+ "The file \"" + file_name.value() + "\" was previously loaded\n"
+ "asynchronously (via a deps rule) and now you're trying to load it "
+ "synchronously.\nThis is a class 2 misdemeanor: a single input file "
+ "must be loaded the same way\neach time to avoid blowing my tiny, "
+ "tiny mind.");
+ return NULL;
+ }
+
+ if (!data->loaded) {
+ // Wait for the already-pending sync load to complete.
+ if (!data->completion_event)
+ data->completion_event.reset(new base::WaitableEvent(false, false));
+ {
+ base::AutoUnlock unlock(lock_);
+ data->completion_event->Wait();
+ }
+ }
+ }
+
+ // The other load could have failed. In this case that error will be printed
+ // to the console, but we need to return something here, so make up a
+ // dummy error.
+ if (!data->parsed_root)
+ *err = Err(origin, "File parse failed");
+ return data->parsed_root.get();
+}
+
+int InputFileManager::GetInputFileCount() const {
+ base::AutoLock lock(lock_);
+ return input_files_.size();
+}
+
+void InputFileManager::GetAllInputFileNames(
+ std::vector<SourceFile>* result) const {
+ base::AutoLock lock(lock_);
+ result->reserve(input_files_.size());
+ for (InputFileMap::const_iterator i = input_files_.begin();
+ i != input_files_.end(); ++i) {
+ result->push_back(i->second->file.name());
+ }
+}
+
+void InputFileManager::BackgroundLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& name,
+ InputFile* file) {
+ Err err;
+ if (!LoadFile(origin, build_settings, name, file, &err))
+ g_scheduler->FailWithError(err);
+}
+
+bool InputFileManager::LoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& name,
+ InputFile* file,
+ Err* err) {
+ // Do all of this stuff outside the lock. We should not give out file
+ // pointers until the read is complete.
+ if (g_scheduler->verbose_logging())
+ g_scheduler->Log("Loading", name.value());
+
+ // Read.
+ base::FilePath primary_path = build_settings->GetFullPath(name);
+ if (!file->Load(primary_path)) {
+ if (!build_settings->secondary_source_path().empty()) {
+ // Fall back to secondary source tree.
+ base::FilePath secondary_path =
+ build_settings->GetFullPathSecondary(name);
+ if (!file->Load(secondary_path)) {
+ *err = Err(origin, "Can't load input file.",
+ "Unable to load either \n" +
+ FilePathToUTF8(primary_path) + " or \n" +
+ FilePathToUTF8(secondary_path));
+ return false;
+ }
+ } else {
+ *err = Err(origin,
+ "Unable to load \"" + FilePathToUTF8(primary_path) + "\".");
+ return false;
+ }
+ }
+
+ if (g_scheduler->verbose_logging())
+ g_scheduler->Log("Parsing", name.value());
+
+ // Tokenize.
+ std::vector<Token> tokens = Tokenizer::Tokenize(file, err);
+ if (err->has_error())
+ return false;
+
+ // Parse.
+ scoped_ptr<ParseNode> root = Parser::Parse(tokens, err);
+ if (err->has_error())
+ return false;
+ ParseNode* unowned_root = root.get();
+
+ std::vector<FileLoadCallback> callbacks;
+ {
+ base::AutoLock lock(lock_);
+ DCHECK(input_files_.find(name) != input_files_.end());
+
+ InputFileData* data = input_files_[name];
+ data->loaded = true;
+ data->tokens.swap(tokens);
+ data->parsed_root = root.Pass();
+
+ callbacks.swap(data->scheduled_callbacks);
+ }
+
+ // Run pending invocations. Theoretically we could schedule each of these
+ // separately to get some parallelism. But normally there will only be one
+ // item in the list, so that's extra overhead and complexity for no gain.
+ for (size_t i = 0; i < callbacks.size(); i++)
+ callbacks[i].Run(unowned_root);
+ return true;
+}
diff --git a/tools/gn/input_file_manager.h b/tools/gn/input_file_manager.h
new file mode 100644
index 0000000..0f708d5
--- /dev/null
+++ b/tools/gn/input_file_manager.h
@@ -0,0 +1,123 @@
+// 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.
+
+#ifndef TOOLS_GN_INPUT_FILE_MANAGER_H_
+#define TOOLS_GN_INPUT_FILE_MANAGER_H_
+
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/containers/hash_tables.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/settings.h"
+
+class Err;
+class LocationRange;
+class ParseNode;
+class Token;
+
+// Manages loading and parsing files from disk. This doesn't actually have
+// any context for executing the results, so potentially multiple configs
+// could use the same input file (saving parsing).
+//
+// This class is threadsafe.
+//
+// InputFile objects must never be deleted while the program is running since
+// various state points into them.
+class InputFileManager : public base::RefCountedThreadSafe<InputFileManager> {
+ public:
+ // Callback issued when a file is laoded. On auccess, the parse node will
+ // refer to the root block of the file. On failure, this will be NULL.
+ typedef base::Callback<void(const ParseNode*)> FileLoadCallback;
+
+ InputFileManager();
+
+ // Loads the given file and executes the callback on the worker pool.
+ //
+ // There are two types of errors. For errors known synchronously, the error
+ // will be set, it will return false, and no work will be scheduled.
+ //
+ // For parse errors and such that happen in the future, the error will be
+ // logged to the scheduler and the callback will be invoked with a null
+ // ParseNode pointer. The given |origin| will be blamed for the invocation.
+ bool AsyncLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& file_name,
+ const FileLoadCallback& callback,
+ Err* err);
+
+ // Loads and parses the given file synchronously, returning the root block
+ // corresponding to the parsed result. On error, return NULL and the given
+ // Err is set.
+ const ParseNode* SyncLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& file_name,
+ Err* err);
+
+ int GetInputFileCount() const;
+
+ void GetAllInputFileNames(std::vector<SourceFile>* result) const;
+
+ private:
+ friend class base::RefCountedThreadSafe<InputFileManager>;
+
+ struct InputFileData {
+ InputFileData(const SourceFile& file_name);
+ ~InputFileData();
+
+ // Don't touch this outside the lock until it's marked loaded.
+ InputFile file;
+
+ bool loaded;
+
+ bool sync_invocation;
+
+ // Lists all invocations that need to be executed when the file completes
+ // loading.
+ std::vector<FileLoadCallback> scheduled_callbacks;
+
+ // Event to signal when the load is complete (or fails). This is lazily
+ // created only when a thread is synchronously waiting for this load (which
+ // only happens for imports).
+ scoped_ptr<base::WaitableEvent> completion_event;
+
+ std::vector<Token> tokens;
+
+ // Null before the file is loaded or if loading failed.
+ scoped_ptr<ParseNode> parsed_root;
+ };
+
+ virtual ~InputFileManager();
+
+ void BackgroundLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& name,
+ InputFile* file);
+
+ // Loads the given file. On error, sets the Err and return false.
+ bool LoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& name,
+ InputFile* file,
+ Err* err);
+
+ mutable base::Lock lock_;
+
+ // Maps repo-relative filenames to the corresponding owned pointer.
+ typedef base::hash_map<SourceFile, InputFileData*> InputFileMap;
+ InputFileMap input_files_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputFileManager);
+};
+
+#endif // TOOLS_GN_INPUT_FILE_MANAGER_H_
diff --git a/tools/gn/item.cc b/tools/gn/item.cc
new file mode 100644
index 0000000..747183cb
--- /dev/null
+++ b/tools/gn/item.cc
@@ -0,0 +1,31 @@
+// 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/item.h"
+
+#include "base/logging.h"
+
+Item::Item(const Label& label) : label_(label) {
+}
+
+Item::~Item() {
+}
+
+Config* Item::AsConfig() { return NULL; }
+const Config* Item::AsConfig() const { return NULL; }
+Target* Item::AsTarget() { return NULL; }
+const Target* Item::AsTarget() const { return NULL; }
+Toolchain* Item::AsToolchain() { return NULL; }
+const Toolchain* Item::AsToolchain() const { return NULL; }
+
+std::string Item::GetItemTypeName() const {
+ if (AsConfig())
+ return "config";
+ if (AsTarget())
+ return "target";
+ if (AsToolchain())
+ return "toolchain";
+ NOTREACHED();
+ return "this thing that I have no idea what it is";
+}
diff --git a/tools/gn/item.h b/tools/gn/item.h
new file mode 100644
index 0000000..aa71544
--- /dev/null
+++ b/tools/gn/item.h
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef TOOLS_GN_ITEM_H_
+#define TOOLS_GN_ITEM_H_
+
+#include <string>
+
+#include "tools/gn/label.h"
+
+class Config;
+class Target;
+class Toolchain;
+
+// A named item (target, config, etc.) that participates in the dependency
+// graph.
+class Item {
+ public:
+ Item(const Label& label);
+ virtual ~Item();
+
+ const Label& label() const { return label_; }
+
+ // Manual RTTI.
+ virtual Config* AsConfig();
+ virtual const Config* AsConfig() const;
+ virtual Target* AsTarget();
+ virtual const Target* AsTarget() const;
+ virtual Toolchain* AsToolchain();
+ virtual const Toolchain* AsToolchain() const;
+
+ // Returns a name like "target" or "config" for the type of item this is, to
+ // be used in logging and error messages.
+ std::string GetItemTypeName() const;
+
+ // Called when this item is resolved, meaning it and all of its dependents
+ // have no unresolved deps.
+ virtual void OnResolved() {}
+
+ private:
+ Label label_;
+};
+
+#endif // TOOLS_GN_ITEM_H_
diff --git a/tools/gn/item_node.cc b/tools/gn/item_node.cc
new file mode 100644
index 0000000..776a126
--- /dev/null
+++ b/tools/gn/item_node.cc
@@ -0,0 +1,51 @@
+// 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/item_node.h"
+
+#include <algorithm>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "tools/gn/item.h"
+
+ItemNode::ItemNode(Item* i)
+ : state_(REFERENCED),
+ item_(i) {
+}
+
+ItemNode::~ItemNode() {
+}
+
+void ItemNode::AddDependency(ItemNode* node) {
+ if (direct_dependencies_.find(node) != direct_dependencies_.end())
+ return; // Already have this dep.
+ direct_dependencies_.insert(node);
+
+ if (node->state() != RESOLVED) {
+ // Wire up the pending resolution info.
+ unresolved_dependencies_.insert(node);
+ node->waiting_on_resolution_.insert(this);
+ }
+}
+
+void ItemNode::MarkDirectDependencyResolved(ItemNode* node) {
+ DCHECK(unresolved_dependencies_.find(node) != unresolved_dependencies_.end());
+ unresolved_dependencies_.erase(node);
+}
+
+void ItemNode::SwapOutWaitingDependencySet(ItemNodeSet* out_set) {
+ waiting_on_resolution_.swap(*out_set);
+}
+
+void ItemNode::SetGenerated() {
+ state_ = GENERATED;
+}
+
+void ItemNode::SetResolved() {
+ state_ = RESOLVED;
+
+ if (!resolved_closure_.is_null())
+ resolved_closure_.Run();
+}
diff --git a/tools/gn/item_node.h b/tools/gn/item_node.h
new file mode 100644
index 0000000..297010f
--- /dev/null
+++ b/tools/gn/item_node.h
@@ -0,0 +1,119 @@
+// 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.
+
+#ifndef TOOLS_GN_ITEM_NODE_H_
+#define TOOLS_GN_ITEM_NODE_H_
+
+#include <set>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "tools/gn/location.h"
+
+class Item;
+
+// Represents a node in the depdency tree. It references an Item which is
+// the actual thing.
+//
+// The items and nodes are split apart so that the ItemTree can manipulate
+// the dependencies one one thread while the Item itself is been modified on
+// another.
+class ItemNode {
+ public:
+ enum State {
+ // Another item has referenced this one by name, but we have not yet
+ // encountered this item to know what it is.
+ REFERENCED,
+
+ // This item has been defined but some of the dependencies it references
+ // have not been.
+ GENERATED,
+
+ // All of this item's transitive dependencies have been found and
+ // resolved.
+ RESOLVED,
+ };
+
+ typedef std::set<ItemNode*> ItemNodeSet;
+
+ // Takes ownership of the pointer.
+ // Initial state will be REFERENCED.
+ ItemNode(Item* i);
+ ~ItemNode();
+
+ State state() const { return state_; }
+
+ // This closure will be executed when the item is resolved.
+ void set_resolved_closure(const base::Closure& closure) {
+ resolved_closure_ = closure;
+ }
+
+ const Item* item() const { return item_.get(); }
+ Item* item() { return item_.get(); }
+
+ // Where this was created from, which might be when it was generated or
+ // when it was first referenced from another target.
+ const LocationRange& originally_referenced_from_here() const {
+ return originally_referenced_from_here_;
+ }
+ void set_originally_referenced_from_here(const LocationRange& r) {
+ originally_referenced_from_here_ = r;
+ }
+
+ // Where this was generated from. This will be empty for items that have
+ // been referenced but not generated. Note that this has to be one the
+ // ItemNode because it can be changing from multiple threads and we need
+ // to be sure that access is serialized.
+ const LocationRange& generated_from_here() const {
+ return generated_from_here_;
+ }
+ void set_generated_from_here(const LocationRange& r) {
+ generated_from_here_ = r;
+ }
+
+ const ItemNodeSet& direct_dependencies() const {
+ return direct_dependencies_;
+ }
+ const ItemNodeSet& unresolved_dependencies() const {
+ return unresolved_dependencies_;
+ }
+
+ void AddDependency(ItemNode* node);
+
+ // Removes the given dependency from the unresolved list. Does not do
+ // anything else to update waiters.
+ void MarkDirectDependencyResolved(ItemNode* node);
+
+ // Destructively retrieve the set of waiting nodes.
+ void SwapOutWaitingDependencySet(ItemNodeSet* out_set);
+
+ void SetGenerated();
+ void SetResolved();
+
+ private:
+ State state_;
+ scoped_ptr<Item> item_;
+
+ LocationRange originally_referenced_from_here_;
+ LocationRange generated_from_here_;
+
+ // What to run when this item is resolved.
+ base::Closure resolved_closure_;
+
+ // Everything this item directly depends on.
+ ItemNodeSet direct_dependencies_;
+
+ // Unresolved things this item directly depends on.
+ ItemNodeSet unresolved_dependencies_;
+
+ // These items are waiting on us to be resolved before they can be
+ // resolved. This is the backpointer for unresolved_dependencies_.
+ ItemNodeSet waiting_on_resolution_;
+
+ DISALLOW_COPY_AND_ASSIGN(ItemNode);
+};
+
+#endif // TOOLS_GN_ITEM_NODE_H_
diff --git a/tools/gn/item_tree.cc b/tools/gn/item_tree.cc
new file mode 100644
index 0000000..a4d1181
--- /dev/null
+++ b/tools/gn/item_tree.cc
@@ -0,0 +1,193 @@
+// 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/item_tree.h"
+
+#include <algorithm>
+
+#include "base/stl_util.h"
+#include "tools/gn/err.h"
+#include "tools/gn/item.h"
+#include "tools/gn/item_node.h"
+
+namespace {
+
+// Recursively looks in the tree for a given node, returning true if it
+// was found in the dependecy graph. This is used to see if a given node
+// participates in a cycle.
+//
+// Note that "look_for" and "search_in" will be the same node when starting the
+// search, so we don't want to return true in that case.
+//
+// If a cycle is found, the return value will be true and the cycle vector will
+// be filled with the path (in reverse order).
+bool RecursiveFindCycle(const ItemNode* look_for,
+ const ItemNode* search_in,
+ std::vector<const ItemNode*>* cycle) {
+ const ItemNode::ItemNodeSet& unresolved =
+ search_in->unresolved_dependencies();
+ for (ItemNode::ItemNodeSet::const_iterator i = unresolved.begin();
+ i != unresolved.end(); ++i) {
+ if (*i == look_for) {
+ cycle->push_back(*i);
+ return true;
+ }
+
+ if (RecursiveFindCycle(look_for, *i, cycle)) {
+ // Found a cycle inside this one, record our path and return.
+ cycle->push_back(*i);
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+ItemTree::ItemTree() {
+}
+
+ItemTree::~ItemTree() {
+ STLDeleteContainerPairSecondPointers(items_.begin(), items_.end());
+}
+
+ItemNode* ItemTree::GetExistingNodeLocked(const Label& label) {
+ lock_.AssertAcquired();
+ StringToNodeHash::iterator found = items_.find(label);
+ if (found == items_.end())
+ return NULL;
+ return found->second;
+}
+
+void ItemTree::AddNodeLocked(ItemNode* node) {
+ lock_.AssertAcquired();
+ DCHECK(items_.find(node->item()->label()) == items_.end());
+ items_[node->item()->label()] = node;
+}
+
+Err ItemTree::MarkItemGeneratedLocked(const Label& label) {
+ lock_.AssertAcquired();
+ DCHECK(items_.find(label) != items_.end());
+
+ ItemNode* node = items_[label];
+
+ if (!node->unresolved_dependencies().empty()) {
+ // Still some pending dependencies, wait for those to be resolved.
+ node->SetGenerated();
+ return Err();
+ }
+ return MarkItemResolvedLocked(node);
+}
+
+void ItemTree::GetAllItemsLocked(std::vector<const Item*>* dest) const {
+ lock_.AssertAcquired();
+ dest->reserve(items_.size());
+ for (StringToNodeHash::const_iterator i = items_.begin();
+ i != items_.end(); ++i) {
+ dest->push_back(i->second->item());
+ }
+}
+
+Err ItemTree::CheckForBadItems() const {
+ base::AutoLock lock(lock_);
+
+ // Look for errors where we find a GENERATED node that refers to a REFERENCED
+ // one. There may be other nodes depending on the GENERATED one, but listing
+ // all of those isn't helpful, we want to find the broken link.
+ //
+ // This finds normal "missing dependency" errors but does not find circular
+ // dependencies because in this case all items in the cycle will be GENERATED
+ // but none will be resolved. If this happens, we'll check explicitly for
+ // that below.
+ std::vector<const ItemNode*> bad_nodes;
+ std::string depstring;
+ for (StringToNodeHash::const_iterator i = items_.begin();
+ i != items_.end(); ++i) {
+ const ItemNode* src = i->second;
+
+ if (src->state() == ItemNode::GENERATED) {
+ bad_nodes.push_back(src);
+
+ // Check dependencies.
+ for (ItemNode::ItemNodeSet::const_iterator dest =
+ src->unresolved_dependencies().begin();
+ dest != src->unresolved_dependencies().end();
+ ++dest) {
+ if ((*dest)->state() == ItemNode::REFERENCED) {
+ depstring += "\"" + src->item()->label().GetUserVisibleName(false) +
+ "\" needs " + (*dest)->item()->GetItemTypeName() +
+ " \"" + (*dest)->item()->label().GetUserVisibleName(false) +
+ "\"\n";
+ }
+ }
+ }
+ }
+
+ if (!bad_nodes.empty() && depstring.empty()) {
+ // Our logic above found a bad node but didn't identify the problem. This
+ // normally means a circular dependency.
+ depstring = CheckForCircularDependenciesLocked(bad_nodes);
+ if (depstring.empty()) {
+ // Something's very wrong, just dump out the bad nodes.
+ depstring = "I have no idea what went wrong, but these are unresolved, "
+ "possible due to an\ninternal error:";
+ for (size_t i = 0; i < bad_nodes.size(); i++) {
+ depstring += "\n\"" +
+ bad_nodes[i]->item()->label().GetUserVisibleName(false) + "\"";
+ }
+ }
+ }
+
+ if (depstring.empty())
+ return Err();
+ return Err(Location(), "Unresolved dependencies.", depstring);
+}
+
+Err ItemTree::MarkItemResolvedLocked(ItemNode* node) {
+ node->SetResolved();
+ node->item()->OnResolved();
+
+ // Now update our waiters, pushing the "resolved" bit.
+ ItemNode::ItemNodeSet waiting;
+ node->SwapOutWaitingDependencySet(&waiting);
+ for (ItemNode::ItemNodeSet::iterator i = waiting.begin();
+ i != waiting.end(); ++i) {
+ ItemNode* waiter = *i;
+
+ // Our node should be unresolved in the waiter.
+ DCHECK(waiter->unresolved_dependencies().find(node) !=
+ waiter->unresolved_dependencies().end());
+ waiter->MarkDirectDependencyResolved(node);
+
+ // Recursively mark nodes as resolved.
+ if (waiter->state() == ItemNode::GENERATED &&
+ waiter->unresolved_dependencies().empty()) {
+ Err err = MarkItemResolvedLocked(waiter);
+ if (err.has_error())
+ return err;
+ }
+ }
+
+ return Err();
+}
+
+std::string ItemTree::CheckForCircularDependenciesLocked(
+ const std::vector<const ItemNode*>& bad_nodes) const {
+ std::vector<const ItemNode*> cycle;
+ if (!RecursiveFindCycle(bad_nodes[0], bad_nodes[0], &cycle))
+ return std::string(); // Didn't find a cycle, something else is wrong.
+
+ cycle.push_back(bad_nodes[0]);
+ std::string ret = "There is a dependency cycle:";
+
+ // Walk backwards since the dependency arrows point in the reverse direction.
+ for (int i = static_cast<int>(cycle.size()) - 1; i >= 0; i--) {
+ ret += "\n \"" + cycle[i]->item()->label().GetUserVisibleName(false) +
+ "\"";
+ if (i != 0)
+ ret += " ->";
+ }
+
+ return ret;
+}
diff --git a/tools/gn/item_tree.h b/tools/gn/item_tree.h
new file mode 100644
index 0000000..f2ac4ae
--- /dev/null
+++ b/tools/gn/item_tree.h
@@ -0,0 +1,69 @@
+// 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.
+
+#ifndef TOOLS_GN_ITEM_TREE_H_
+#define TOOLS_GN_ITEM_TREE_H_
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "tools/gn/label.h"
+
+class Err;
+class Item;
+class ItemNode;
+
+// Represents the full dependency tree if labeled items in the system.
+// Generally you will interact with this through the target manager, etc.
+class ItemTree {
+ public:
+ ItemTree();
+ ~ItemTree();
+
+ // This lock must be held when calling the "Locked" functions below.
+ base::Lock& lock() { return lock_; }
+
+ // Returns NULL if the item is not found.
+ //
+ // The lock must be held.
+ ItemNode* GetExistingNodeLocked(const Label& label);
+
+ // There must not be an item with this label in the tree already. Takes
+ // ownership of the pointer.
+ //
+ // The lock must be held.
+ void AddNodeLocked(ItemNode* node);
+
+ // Mark the given item as being generated. If it has no unresolved
+ // dependencies, it will be marked resolved, and the resolved state will be
+ // recursively pushed into the dependency tree. Returns an error if there was
+ // an error.
+ Err MarkItemGeneratedLocked(const Label& label);
+
+ // Fills the given vector with all known items.
+ void GetAllItemsLocked(std::vector<const Item*>* dest) const;
+
+ // Returns an error if there are unresolved dependencies, or no error if
+ // there aren't.
+ //
+ // The lock should not be held.
+ Err CheckForBadItems() const;
+
+ private:
+ Err MarkItemResolvedLocked(ItemNode* node);
+
+ // Given a set of unresolved nodes, looks for cycles and returns the error
+ // message describing any cycles it found.
+ std::string CheckForCircularDependenciesLocked(
+ const std::vector<const ItemNode*>& bad_nodes) const;
+
+ mutable base::Lock lock_;
+
+ typedef base::hash_map<Label, ItemNode*> StringToNodeHash;
+ StringToNodeHash items_; // Owning pointer.
+
+ DISALLOW_COPY_AND_ASSIGN(ItemTree);
+};
+
+#endif // TOOLS_GN_ITEM_TREE_H_
diff --git a/tools/gn/label.cc b/tools/gn/label.cc
new file mode 100644
index 0000000..f9b48da
--- /dev/null
+++ b/tools/gn/label.cc
@@ -0,0 +1,263 @@
+// 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/label.h"
+
+#include "base/logging.h"
+#include "tools/gn/err.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+// We print user visible label names with no trailing slash after the
+// directory name.
+std::string DirWithNoTrailingSlash(const SourceDir& dir) {
+ // Be careful not to trim if the input is just "/" or "//".
+ if (dir.value().size() > 2)
+ return dir.value().substr(0, dir.value().size() - 1);
+ return dir.value();
+}
+
+// Given the separate-out input (everything before the colon) in the dep rule,
+// computes the final build rule. Sets err on failure. On success,
+// |*used_implicit| will be set to whether the implicit current directory was
+// used. The value is used only for generating error messages.
+bool ComputeBuildLocationFromDep(const Value& input_value,
+ const SourceDir& current_dir,
+ const base::StringPiece& input,
+ SourceDir* result,
+ Err* err) {
+ // No rule, use the current locaton.
+ if (input.empty()) {
+ *result = current_dir;
+ return true;
+ }
+
+ // Don't allow directories to start with a single slash. All labels must be
+ // in the source root.
+ if (input[0] == '/' && (input.size() == 1 || input[1] != '/')) {
+ *err = Err(input_value, "Label can't start with a single slash",
+ "Labels must be either relative (no slash at the beginning) or be "
+ "absolute\ninside the source root (two slashes at the beginning).");
+ return false;
+ }
+
+ *result = current_dir.ResolveRelativeDir(input);
+ return true;
+}
+
+// Given the separated-out target name (after the colon) computes the final
+// name, using the implicit name from the previously-generated
+// computed_location if necessary. The input_value is used only for generating
+// error messages.
+bool ComputeTargetNameFromDep(const Value& input_value,
+ const SourceDir& computed_location,
+ const base::StringPiece& input,
+ std::string* result,
+ Err* err) {
+ if (!input.empty()) {
+ // Easy case: input is specified, just use it.
+ result->assign(input.data(), input.size());
+ return true;
+ }
+
+ const std::string& loc = computed_location.value();
+
+ // Use implicit name. The path will be "//", "//base/", "//base/i18n/", etc.
+ if (loc.size() <= 1) {
+ *err = Err(input_value, "This dependency name is empty");
+ return false;
+ }
+
+ size_t next_to_last_slash = loc.rfind('/', loc.size() - 2);
+ DCHECK(next_to_last_slash != std::string::npos);
+ result->assign(&loc[next_to_last_slash + 1],
+ loc.size() - next_to_last_slash - 2);
+ return true;
+}
+
+// The original value is used only for error reporting, use the |input| as the
+// input to this function (which may be a substring of the original value when
+// we're parsing toolchains.
+//
+// If the output toolchain vars are NULL, then we'll report an error if we
+// find a toolchain specified (this is used when recursively parsing toolchain
+// labels which themselves can't have toolchain specs).
+//
+// We assume that the output variables are initialized to empty so we don't
+// write them unless we need them to contain something.
+//
+// Returns true on success. On failure, the out* variables might be written to
+// but shouldn't be used.
+bool Resolve(const SourceDir& current_dir,
+ const Label& current_toolchain,
+ const Value& original_value,
+ const base::StringPiece& input,
+ SourceDir* out_dir,
+ std::string* out_name,
+ SourceDir* out_toolchain_dir,
+ std::string* out_toolchain_name,
+ Err* err) {
+ // To workaround the problem that StringPiece operator[] doesn't return a ref.
+ const char* input_str = input.data();
+
+ size_t path_separator = input.find_first_of(":(");
+ base::StringPiece location_piece;
+ base::StringPiece name_piece;
+ base::StringPiece toolchain_piece;
+ if (path_separator == std::string::npos) {
+ location_piece = input;
+ // Leave name & toolchain piece null.
+ } else {
+ location_piece = base::StringPiece(&input_str[0], path_separator);
+
+ size_t toolchain_separator = input.find('(', path_separator);
+ if (toolchain_separator == std::string::npos) {
+ name_piece = base::StringPiece(&input_str[path_separator + 1],
+ input.size() - path_separator - 1);
+ // Leave location piece null.
+ } else if (!out_toolchain_dir) {
+ // Toolchain specified but not allows in this context.
+ *err = Err(original_value, "Toolchain has a toolchain.",
+ "Your toolchain definition (inside the parens) seems to itself "
+ "have a\ntoolchain. Don't do this.");
+ return false;
+ } else {
+ // Name piece is everything between the two separators. Note that the
+ // separators may be the same (e.g. "//foo(bar)" which means empty name.
+ if (toolchain_separator > path_separator) {
+ name_piece = base::StringPiece(
+ &input_str[path_separator + 1],
+ toolchain_separator - path_separator - 1);
+ }
+
+ // Toolchain name should end in a ) and this should be the end of the
+ // string.
+ if (input[input.size() - 1] != ')') {
+ *err = Err(original_value, "Bad toolchain name.",
+ "Toolchain name must end in a \")\" at the end of the label.");
+ return false;
+ }
+
+ // Subtract off the two parens to just get the toolchain name.
+ toolchain_piece = base::StringPiece(
+ &input_str[toolchain_separator + 1],
+ input.size() - toolchain_separator - 2);
+ }
+ }
+
+ // Everything before the separator is the filename.
+ // We allow three cases:
+ // Absolute: "//foo:bar" -> /foo:bar
+ // Target in current file: ":foo" -> <currentdir>:foo
+ // Path with implicit name: "/foo" -> /foo:foo
+ if (location_piece.empty() && name_piece.empty()) {
+ // Can't use both implicit filename and name (":").
+ *err = Err(original_value, "This doesn't specify a dependency.");
+ return false;
+ }
+
+ if (!ComputeBuildLocationFromDep(original_value, current_dir, location_piece,
+ out_dir, err))
+ return false;
+
+ if (!ComputeTargetNameFromDep(original_value, *out_dir, name_piece,
+ out_name, err))
+ return false;
+
+ // Last, do the toolchains.
+ if (out_toolchain_dir) {
+ // Handle empty toolchain strings. We don't allow normal labels to be
+ // empty so we can't allow the recursive call of this function to do this
+ // check.
+ if (toolchain_piece.empty()) {
+ *out_toolchain_dir = current_toolchain.dir();
+ *out_toolchain_name = current_toolchain.name();
+ return true;
+ } else {
+ return Resolve(current_dir, current_toolchain,
+ original_value, toolchain_piece,
+ out_toolchain_dir, out_toolchain_name, NULL, NULL, err);
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+Label::Label() {
+}
+
+Label::Label(const SourceDir& dir,
+ const base::StringPiece& name,
+ const SourceDir& toolchain_dir,
+ const base::StringPiece& toolchain_name)
+ : dir_(dir),
+ toolchain_dir_(toolchain_dir) {
+ name_.assign(name.data(), name.size());
+ toolchain_name_.assign(toolchain_name.data(), toolchain_name.size());
+}
+
+Label::~Label() {
+}
+
+// static
+Label Label::Resolve(const SourceDir& current_dir,
+ const Label& current_toolchain,
+ const Value& input,
+ Err* err) {
+ Label ret;
+ if (input.type() != Value::STRING) {
+ *err = Err(input, "Dependency is not a string.");
+ return ret;
+ }
+ const std::string& input_string = input.string_value();
+ if (input_string.empty()) {
+ *err = Err(input, "Dependency string is empty.");
+ return ret;
+ }
+
+ if (!::Resolve(current_dir, current_toolchain, input, input_string,
+ &ret.dir_, &ret.name_,
+ &ret.toolchain_dir_, &ret.toolchain_name_,
+ err))
+ return Label();
+ return ret;
+}
+
+Label Label::GetToolchainLabel() const {
+ return Label(toolchain_dir_, toolchain_name_,
+ SourceDir(), base::StringPiece());
+}
+
+std::string Label::GetUserVisibleName(bool include_toolchain) const {
+ std::string ret;
+ ret.reserve(dir_.value().size() + name_.size() + 1);
+
+ if (dir_.is_null())
+ return ret;
+
+ ret = DirWithNoTrailingSlash(dir_);
+ ret.push_back(':');
+ ret.append(name_);
+
+ if (include_toolchain) {
+ ret.push_back('(');
+ if (!toolchain_dir_.is_null() && !toolchain_name_.empty()) {
+ ret.append(DirWithNoTrailingSlash(toolchain_dir_));
+ ret.push_back(':');
+ ret.append(toolchain_name_);
+ }
+ ret.push_back(')');
+ }
+ return ret;
+}
+
+std::string Label::GetUserVisibleName(const Label& default_toolchain) const {
+ bool include_toolchain =
+ default_toolchain.dir() != toolchain_dir_ ||
+ default_toolchain.name() != toolchain_name_;
+ return GetUserVisibleName(include_toolchain);
+}
diff --git a/tools/gn/label.h b/tools/gn/label.h
new file mode 100644
index 0000000..b31117a
--- /dev/null
+++ b/tools/gn/label.h
@@ -0,0 +1,116 @@
+// 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.
+
+#ifndef TOOLS_GN_LABEL_H_
+#define TOOLS_GN_LABEL_H_
+
+#include "base/containers/hash_tables.h"
+#include "build/build_config.h"
+#include "tools/gn/source_dir.h"
+
+class Err;
+class Value;
+
+// A label represents the name of a target or some other named thing in
+// the source path. The label is always absolute and always includes a name
+// part, so it starts with a slash, and has one colon.
+class Label {
+ public:
+ Label();
+
+ // Makes a label given an already-separate out path and name.
+ // See also Resolve().
+ Label(const SourceDir& dir,
+ const base::StringPiece& name,
+ const SourceDir& toolchain_dir,
+ const base::StringPiece& toolchain_name);
+ ~Label();
+
+ // Resolives a string from a build file that may be relative to the
+ // current directory into a fully qualified label. On failure returns an
+ // is_null() label and sets the error.
+ static Label Resolve(const SourceDir& current_dir,
+ const Label& current_toolchain,
+ const Value& input,
+ Err* err);
+
+ bool is_null() const { return dir_.is_null(); }
+
+ const SourceDir& dir() const { return dir_; }
+ const std::string& name() const { return name_; }
+
+ const SourceDir& toolchain_dir() const { return toolchain_dir_; }
+ const std::string& toolchain_name() const { return toolchain_name_; }
+ Label GetToolchainLabel() const;
+
+ // Formats this label in a way that we can present to the user or expose to
+ // other parts of the system. SourceDirs end in slashes, but the user
+ // expects names like "//chrome/renderer:renderer_config" when printed. The
+ // toolchain is optionally included.
+ std::string GetUserVisibleName(bool include_toolchain) const;
+
+ // Like the above version, but automatically includes the toolchain if it's
+ // not the default one. Normally the user only cares about the toolchain for
+ // non-default ones, so this can make certain output more clear.
+ std::string GetUserVisibleName(const Label& default_toolchain) const;
+
+ bool operator==(const Label& other) const {
+ return name_ == other.name_ && dir_ == other.dir_ &&
+ toolchain_dir_ == other.toolchain_dir_ &&
+ toolchain_name_ == other.toolchain_name_;
+ }
+ bool operator!=(const Label& other) const {
+ return !operator==(other);
+ }
+ bool operator<(const Label& other) const {
+ // TODO(brettw) could be optimized to avoid an extra full string check
+ // (one for operator==, one for <).
+ if (dir_ != other.dir_)
+ return dir_ < other.dir_;
+ if (name_ != other.name_)
+ return name_ < other.name_;
+ if (toolchain_dir_ != other.toolchain_dir_)
+ return toolchain_dir_ < other.toolchain_dir_;
+ return toolchain_name_ < other.toolchain_name_;
+ }
+
+ // Returns true if the toolchain dir/name of this object matches some
+ // other object.
+ bool ToolchainsEqual(const Label& other) const {
+ return toolchain_dir_ == other.toolchain_dir_ &&
+ toolchain_name_ == other.toolchain_name_;
+ }
+
+ private:
+ SourceDir dir_;
+ std::string name_;
+
+ SourceDir toolchain_dir_;
+ std::string toolchain_name_;
+};
+
+namespace BASE_HASH_NAMESPACE {
+
+#if defined(COMPILER_GCC)
+template<> struct hash<Label> {
+ std::size_t operator()(const Label& v) const {
+ hash<std::string> stringhash;
+ return ((stringhash(v.dir().value()) * 131 +
+ stringhash(v.name())) * 131 +
+ stringhash(v.toolchain_dir().value())) * 131 +
+ stringhash(v.toolchain_name());
+ }
+};
+#elif defined(COMPILER_MSVC)
+inline size_t hash_value(const Label& v) {
+ return ((hash_value(v.dir().value()) * 131 +
+ hash_value(v.name())) * 131 +
+ hash_value(v.toolchain_dir().value())) * 131 +
+ hash_value(v.toolchain_name());
+}
+#endif // COMPILER...
+
+} // namespace BASE_HASH_NAMESPACE
+
+#endif // TOOLS_GN_LABEL_H_
diff --git a/tools/gn/label_unittest.cc b/tools/gn/label_unittest.cc
new file mode 100644
index 0000000..74eb3da
--- /dev/null
+++ b/tools/gn/label_unittest.cc
@@ -0,0 +1,88 @@
+// 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/err.h"
+#include "tools/gn/label.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+struct ParseDepStringCase {
+ const char* cur_dir;
+ const char* str;
+ bool success;
+ const char* expected_dir;
+ const char* expected_name;
+ const char* expected_toolchain_dir;
+ const char* expected_toolchain_name;
+};
+
+} // namespace
+
+TEST(Label, Resolve) {
+ ParseDepStringCase cases[] = {
+ // cur input succ expected dir name tc dir tc name
+ { "//chrome/", "", false, "", "", "", "" },
+ { "//chrome/", "/", false, "", "", "", "" },
+ { "//chrome/", ":", false, "", "", "", "" },
+ { "//chrome/", "/:", false, "", "", "", "" },
+ { "//chrome/", "blah", true, "//chrome/blah/", "blah", "//t/", "d" },
+ { "//chrome/", "blah:bar", true, "//chrome/blah/", "bar", "//t/", "d" },
+ // No single-leading slash.
+ { "//chrome/", "/chrome:bar", false, "", "", "", "" },
+ // No trailing slash.
+ { "//chrome/", "/chrome/:bar", false, "", "", "", "" },
+ // Refers to root dir.
+ { "//chrome/", "//:bar", true, "//", "bar", "//t/", "d" },
+ // Implicit directory
+ { "//chrome/", ":bar", true, "//chrome/", "bar", "//t/", "d" },
+ { "//chrome/renderer/", ":bar", true, "//chrome/renderer/", "bar", "//t/", "d" },
+ // Implicit names.
+ { "//chrome/", "//base", true, "//base/", "base", "//t/", "d" },
+ { "//chrome/", "//base/i18n", true, "//base/i18n/", "i18n", "//t/", "d" },
+ { "//chrome/", "//base/i18n:foo", true, "//base/i18n/", "foo", "//t/", "d" },
+ // Toolchain parsing.
+ { "//chrome/", "//chrome:bar(//t:n)", true, "//chrome/", "bar", "//t/", "n" },
+ { "//chrome/", "//chrome:bar(//t)", true, "//chrome/", "bar", "//t/", "t" },
+ { "//chrome/", "//chrome:bar(//t:)", true, "//chrome/", "bar", "//t/", "t" },
+ { "//chrome/", "//chrome:bar()", true, "//chrome/", "bar", "//t/", "d" },
+ { "//chrome/", "//chrome:bar(foo)", true, "//chrome/", "bar", "//chrome/foo/", "foo" },
+ { "//chrome/", "//chrome:bar(:foo)", true, "//chrome/", "bar", "//chrome/", "foo" },
+ // TODO(brettw) it might be nice to make this an error:
+ //{ "//chrome/", "//chrome:bar())", false, "", "", "", "" },
+ { "//chrome/", "//chrome:bar(//t:bar(tc))", false, "", "", "", "" },
+ { "//chrome/", "//chrome:bar(()", false, "", "", "", "" },
+ { "//chrome/", "(t:b)", false, "", "", "", "" },
+ { "//chrome/", ":bar(//t/b)", true, "//chrome/", "bar", "//t/b/", "b" },
+ { "//chrome/", ":bar(/t/b)", false, "", "", "", "" },
+ { "//chrome/", ":bar(t/b)", true, "//chrome/", "bar", "//chrome/t/b/", "b" },
+ };
+
+ Label default_toolchain(SourceDir("//t/"), "d",
+ SourceDir(), std::string());
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
+ const ParseDepStringCase& cur = cases[i];
+
+ std::string location, name;
+ Err err;
+ Value v(NULL, Value::STRING);
+ v.string_value() = cur.str;
+ Label result =
+ Label::Resolve(SourceDir(cur.cur_dir), default_toolchain, v, &err);
+ EXPECT_EQ(cur.success, !err.has_error()) << i << " " << cur.str;
+ if (!err.has_error() && cur.success) {
+ EXPECT_EQ(cur.expected_dir, result.dir().value())
+ << i << " " << cur.str;
+ EXPECT_EQ(cur.expected_name, result.name())
+ << i << " " << cur.str;
+ EXPECT_EQ(cur.expected_toolchain_dir,
+ result.toolchain_dir().value())
+ << i << " " << cur.str;
+ EXPECT_EQ(cur.expected_toolchain_name, result.toolchain_name())
+ << i << " " << cur.str;
+ }
+ }
+}
diff --git a/tools/gn/location.h b/tools/gn/location.h
new file mode 100644
index 0000000..2055125
--- /dev/null
+++ b/tools/gn/location.h
@@ -0,0 +1,77 @@
+// 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.
+
+#ifndef TOOLS_GN_LOCATION_H_
+#define TOOLS_GN_LOCATION_H_
+
+#include <algorithm>
+
+#include "base/logging.h"
+
+class InputFile;
+
+// Represents a place in a source file. Used for error reporting.
+class Location {
+ public:
+ Location()
+ : file_(NULL),
+ line_number_(-1),
+ char_offset_(-1) {
+ }
+ Location(const InputFile* file, int line_number, int char_offset)
+ : file_(file),
+ line_number_(line_number),
+ char_offset_(char_offset) {
+ }
+
+ const InputFile* file() const { return file_; }
+ int line_number() const { return line_number_; }
+ int char_offset() const { return char_offset_; }
+
+ bool operator==(const Location& other) const {
+ return other.file_ == file_ &&
+ other.line_number_ == line_number_ &&
+ other.char_offset_ == char_offset_;
+ }
+
+ bool operator<(const Location& other) const {
+ DCHECK(file_ == other.file_);
+ if (line_number_ != other.line_number_)
+ return line_number_ < other.line_number_;
+ return char_offset_ < other.char_offset_;
+ }
+
+ private:
+ const InputFile* file_; // Null when unset.
+ int line_number_; // -1 when unset.
+ int char_offset_; // -1 when unset.
+};
+
+// Represents a range in a source file. Used for error reporting.
+// The end is exclusive i.e. [begin, end)
+class LocationRange {
+ public:
+ LocationRange() {}
+ LocationRange(const Location& begin, const Location& end)
+ : begin_(begin),
+ end_(end) {
+ DCHECK(begin_.file() == end_.file());
+ }
+
+ const Location& begin() const { return begin_; }
+ const Location& end() const { return end_; }
+
+ LocationRange Union(const LocationRange& other) const {
+ DCHECK(begin_.file() == other.begin_.file());
+ return LocationRange(
+ begin_ < other.begin_ ? begin_ : other.begin_,
+ end_ < other.end_ ? other.end_ : end_);
+ }
+
+ private:
+ Location begin_;
+ Location end_;
+};
+
+#endif // TOOLS_GN_LOCATION_H_
diff --git a/tools/gn/ninja_build_writer.cc b/tools/gn/ninja_build_writer.cc
new file mode 100644
index 0000000..a63765c
--- /dev/null
+++ b/tools/gn/ninja_build_writer.cc
@@ -0,0 +1,165 @@
+// 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/ninja_build_writer.h"
+
+#include <fstream>
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/process/process_handle.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/input_file_manager.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/target.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace {
+
+std::string GetSelfInvocationCommand(const BuildSettings* build_settings) {
+#if defined(OS_WIN)
+ wchar_t module[MAX_PATH];
+ GetModuleFileName(NULL, module, MAX_PATH);
+ //result = "\"" + WideToUTF8(module) + "\"";
+ base::FilePath executable(module);
+#elif defined(OS_MACOSX)
+ // FIXME(brettw) write this on Mac!
+ base::FilePath executable("gn");
+#else
+ base::FilePath executable =
+ base::GetProcessExecutablePath(base::GetCurrentProcessHandle());
+#endif
+
+/*
+ // Append the root path.
+ CommandLine* cmdline = CommandLine::ForCurrentProcess();
+ result += " --root=\"" + FilePathToUTF8(settings->root_path()) + "\"";
+*/
+
+ CommandLine cmdline(executable);
+ cmdline.AppendSwitchPath("--root", build_settings->root_path());
+
+ // TODO(brettw) append other parameters.
+
+#if defined(OS_WIN)
+ return WideToUTF8(cmdline.GetCommandLineString());
+#else
+ return cmdline.GetCommandLineString();
+#endif
+}
+
+} // namespace
+
+NinjaBuildWriter::NinjaBuildWriter(
+ const BuildSettings* build_settings,
+ const std::vector<const Settings*>& all_settings,
+ const std::vector<const Target*>& default_toolchain_targets,
+ std::ostream& out)
+ : build_settings_(build_settings),
+ all_settings_(all_settings),
+ default_toolchain_targets_(default_toolchain_targets),
+ out_(out),
+ path_output_(build_settings->build_dir(), ESCAPE_NINJA, true),
+ helper_(build_settings) {
+}
+
+NinjaBuildWriter::~NinjaBuildWriter() {
+}
+
+void NinjaBuildWriter::Run() {
+ WriteNinjaRules();
+ WriteSubninjas();
+ WritePhonyAndAllRules();
+}
+
+// static
+bool NinjaBuildWriter::RunAndWriteFile(
+ const BuildSettings* build_settings,
+ const std::vector<const Settings*>& all_settings,
+ const std::vector<const Target*>& default_toolchain_targets) {
+ base::FilePath ninja_file(build_settings->GetFullPath(
+ SourceFile(build_settings->build_dir().value() + "build.ninja")));
+ file_util::CreateDirectory(ninja_file.DirName());
+
+ std::ofstream file;
+ file.open(FilePathToUTF8(ninja_file).c_str(),
+ std::ios_base::out | std::ios_base::binary);
+ if (file.fail())
+ return false;
+
+ NinjaBuildWriter gen(build_settings, all_settings,
+ default_toolchain_targets, file);
+ gen.Run();
+ return true;
+}
+
+void NinjaBuildWriter::WriteNinjaRules() {
+ out_ << "rule gn\n";
+ out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n";
+ out_ << " description = GN the world\n\n";
+
+ out_ << "build build.ninja: gn";
+
+ // Input build files.
+ std::vector<SourceFile> input_files;
+ g_scheduler->input_file_manager()->GetAllInputFileNames(&input_files);
+ for (size_t i = 0; i < input_files.size(); i++) {
+ out_ << " ";
+ path_output_.WriteFile(out_, input_files[i]);
+ }
+
+ // Other files read by the build.
+ std::vector<SourceFile> other_files = g_scheduler->GetGenDependencies();
+ for (size_t i = 0; i < other_files.size(); i++) {
+ out_ << " ";
+ path_output_.WriteFile(out_, other_files[i]);
+ }
+
+ out_ << std::endl << std::endl;
+}
+
+void NinjaBuildWriter::WriteSubninjas() {
+ for (size_t i = 0; i < all_settings_.size(); i++) {
+ out_ << "subninja ";
+ path_output_.WriteFile(out_,
+ helper_.GetNinjaFileForToolchain(all_settings_[i]));
+ out_ << std::endl;
+ }
+ out_ << std::endl;
+}
+
+void NinjaBuildWriter::WritePhonyAndAllRules() {
+ std::string all_rules;
+
+ // Write phony rules for the default toolchain (don't do other toolchains or
+ // we'll get naming conflicts).
+ for (size_t i = 0; i < default_toolchain_targets_.size(); i++) {
+ const Target* target = default_toolchain_targets_[i];
+ if (target->output_type() == Target::NONE)
+ continue; // Nothing to generate.
+
+ OutputFile target_file = helper_.GetTargetOutputFile(target);
+ if (target_file.value() != target->label().name()) {
+ out_ << "build " << target->label().name() << ": phony ";
+ path_output_.WriteFile(out_, target_file);
+ out_ << std::endl;
+ }
+
+ if (!all_rules.empty())
+ all_rules.append(" $\n ");
+ all_rules.append(target_file.value());
+ }
+
+ if (!all_rules.empty()) {
+ out_ << "\nbuild all: phony " << all_rules << std::endl;
+ out_ << "default all" << std::endl;
+ }
+}
+
diff --git a/tools/gn/ninja_build_writer.h b/tools/gn/ninja_build_writer.h
new file mode 100644
index 0000000..85548df
--- /dev/null
+++ b/tools/gn/ninja_build_writer.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef TOOLS_GN_NINJA_BUILD_WRITER_H_
+#define TOOLS_GN_NINJA_BUILD_WRITER_H_
+
+#include <iosfwd>
+#include <vector>
+
+#include "tools/gn/ninja_helper.h"
+#include "tools/gn/path_output.h"
+
+class BuildSettings;
+class Settings;
+class Target;
+
+// Generates the toplevel "build.ninja" file. This references the individual
+// toolchain files and lists all input .gn files as dependencies of the
+// build itself.
+class NinjaBuildWriter {
+ public:
+ static bool RunAndWriteFile(
+ const BuildSettings* settings,
+ const std::vector<const Settings*>& all_settings,
+ const std::vector<const Target*>& default_toolchain_targets);
+
+ private:
+ NinjaBuildWriter(const BuildSettings* settings,
+ const std::vector<const Settings*>& all_settings,
+ const std::vector<const Target*>& default_toolchain_targets,
+ std::ostream& out);
+ ~NinjaBuildWriter();
+
+ void Run();
+
+ void WriteNinjaRules();
+ void WriteSubninjas();
+ void WritePhonyAndAllRules();
+
+ const BuildSettings* build_settings_;
+ std::vector<const Settings*> all_settings_;
+ std::vector<const Target*> default_toolchain_targets_;
+ std::ostream& out_;
+ PathOutput path_output_;
+
+ NinjaHelper helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaBuildWriter);
+};
+
+#endif // TOOLS_GN_NINJA_BUILD_GENERATOR_H_
+
diff --git a/tools/gn/ninja_helper.cc b/tools/gn/ninja_helper.cc
new file mode 100644
index 0000000..c27c0e4
--- /dev/null
+++ b/tools/gn/ninja_helper.cc
@@ -0,0 +1,165 @@
+// 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/ninja_helper.h"
+
+#include "base/logging.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/string_utils.h"
+#include "tools/gn/target.h"
+
+namespace {
+
+const char kLibDirWithSlash[] = "lib";
+const char kObjectDirNoSlash[] = "obj";
+
+} // namespace
+
+NinjaHelper::NinjaHelper(const BuildSettings* build_settings)
+ : build_settings_(build_settings) {
+ build_to_src_no_last_slash_ = build_settings->build_to_source_dir_string();
+ if (!build_to_src_no_last_slash_.empty() &&
+ build_to_src_no_last_slash_[build_to_src_no_last_slash_.size() - 1] ==
+ '/')
+ build_to_src_no_last_slash_.resize(build_to_src_no_last_slash_.size() - 1);
+
+ build_to_src_system_no_last_slash_ = build_to_src_no_last_slash_;
+ ConvertPathToSystem(&build_to_src_system_no_last_slash_);
+}
+
+NinjaHelper::~NinjaHelper() {
+}
+
+std::string NinjaHelper::GetTopleveOutputDir() const {
+ return kObjectDirNoSlash;
+}
+
+std::string NinjaHelper::GetTargetOutputDir(const Target* target) const {
+ return kObjectDirNoSlash + target->label().dir().SourceAbsoluteWithOneSlash();
+}
+
+OutputFile NinjaHelper::GetNinjaFileForTarget(const Target* target) const {
+ OutputFile ret(target->settings()->toolchain_output_subdir());
+ ret.value().append(kObjectDirNoSlash);
+ AppendStringPiece(&ret.value(),
+ target->label().dir().SourceAbsoluteWithOneSlash());
+ ret.value().append(target->label().name());
+ ret.value().append(".ninja");
+ return ret;
+}
+
+OutputFile NinjaHelper::GetNinjaFileForToolchain(
+ const Settings* settings) const {
+ OutputFile ret;
+ ret.value().append(settings->toolchain_output_subdir().value());
+ ret.value().append("toolchain.ninja");
+ return ret;
+}
+
+// In Python, GypPathToUniqueOutput does the qualification. The only case where
+// the Python version doesn't qualify the name is for target outputs, which we
+// handle in a separate function.
+OutputFile NinjaHelper::GetOutputFileForSource(
+ const Target* target,
+ const SourceFile& source,
+ SourceFileType type) const {
+ // Extract the filename and remove the extension (keep the dot).
+ base::StringPiece filename = FindFilename(&source.value());
+ std::string name(filename.data(), filename.size());
+ size_t extension_offset = FindExtensionOffset(name);
+ CHECK(extension_offset != std::string::npos);
+ name.resize(extension_offset);
+
+ // Append the new extension.
+ switch (type) {
+ case SOURCE_ASM:
+ case SOURCE_C:
+ case SOURCE_CC:
+ case SOURCE_M:
+ case SOURCE_MM:
+ name.append(target->settings()->IsWin() ? "obj" : "o");
+ break;
+
+ case SOURCE_RC:
+ name.append("res");
+ break;
+
+ case SOURCE_H:
+ case SOURCE_UNKNOWN:
+ NOTREACHED();
+ return OutputFile();
+ }
+
+ // Use the scheme <path>/<target>.<name>.<extension> so that all output
+ // names are unique to different targets.
+ OutputFile ret(kObjectDirNoSlash);
+
+ // Find the directory, assume it starts with two slashes, and trim to one.
+ base::StringPiece dir = FindDir(&source.value());
+ CHECK(dir.size() >= 2 && dir[0] == '/' && dir[1] == '/')
+ << "Source file isn't in the source repo: " << dir;
+ AppendStringPiece(&ret.value(), dir.substr(1));
+
+ ret.value().append(target->label().name());
+ ret.value().append(".");
+ ret.value().append(name);
+ return ret;
+}
+
+OutputFile NinjaHelper::GetTargetOutputFile(const Target* target) const {
+ OutputFile ret;
+ if (target->output_type() == Target::NONE) {
+ NOTREACHED();
+ return ret;
+ }
+
+ const char* extension;
+ if (target->output_type() == Target::NONE ||
+ target->output_type() == Target::COPY_FILES ||
+ target->output_type() == Target::CUSTOM) {
+ extension = "stamp";
+ } else {
+ extension = GetExtensionForOutputType(target->output_type(),
+ target->settings()->target_os());
+ }
+
+ // Everything goes into the toolchain directory (which will be empty for the
+ // default toolchain, and will end in a slash otherwise).
+ ret.value().append(target->settings()->toolchain_output_subdir().value());
+
+ // Binaries and loadable libraries go into the toolchain root.
+ if (target->output_type() == Target::EXECUTABLE ||
+ target->output_type() == Target::LOADABLE_MODULE ||
+ (target->settings()->IsMac() &&
+ (target->output_type() == Target::SHARED_LIBRARY ||
+ target->output_type() == Target::STATIC_LIBRARY)) ||
+ (target->settings()->IsWin() &&
+ target->output_type() == Target::SHARED_LIBRARY)) {
+ // Generate a name like "<toolchain>/<name>.<extension>".
+ ret.value().append(target->label().name());
+ ret.value().push_back('.');
+ ret.value().append(extension);
+ return ret;
+ }
+
+ // Libraries go into the library subdirectory like
+ // "<toolchain>/lib/<name>.<extension>".
+ if (target->output_type() == Target::SHARED_LIBRARY) {
+ ret.value().append(kLibDirWithSlash);
+ ret.value().append(target->label().name());
+ ret.value().push_back('.');
+ ret.value().append(extension);
+ return ret;
+ }
+
+ // Everything else goes next to the target's .ninja file like
+ // "<toolchain>/obj/<path>/<name>.<extension>".
+ ret.value().append(kObjectDirNoSlash);
+ AppendStringPiece(&ret.value(),
+ target->label().dir().SourceAbsoluteWithOneSlash());
+ ret.value().append(target->label().name());
+ ret.value().push_back('.');
+ ret.value().append(extension);
+ return ret;
+}
diff --git a/tools/gn/ninja_helper.h b/tools/gn/ninja_helper.h
new file mode 100644
index 0000000..5bea29e
--- /dev/null
+++ b/tools/gn/ninja_helper.h
@@ -0,0 +1,71 @@
+// 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.
+
+#ifndef TOOLS_GN_NINJA_HELPER_H_
+#define TOOLS_GN_NINJA_HELPER_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/output_file.h"
+
+class BuildSettings;
+class SourceDir;
+class SourceFile;
+class Target;
+
+// NinjaHelper -----------------------------------------------------------------
+
+class NinjaHelper {
+ public:
+ NinjaHelper(const BuildSettings* build_settings);
+ ~NinjaHelper();
+
+ // Ends in a slash.
+ std::string GetTopleveOutputDir() const;
+
+ // Ends in a slash.
+ std::string GetTargetOutputDir(const Target* target) const;
+
+ // Example: "base/base.ninja". The string version will not be escaped, and
+ // will always have slashes for path separators.
+ OutputFile GetNinjaFileForTarget(const Target* target) const;
+
+ // Returns the name of the root .ninja file for the given toolchain.
+ OutputFile GetNinjaFileForToolchain(const Settings* settings) const;
+
+ // Given a source file relative to the source root, returns the output
+ // filename.
+ OutputFile GetOutputFileForSource(const Target* target,
+ const SourceFile& source,
+ SourceFileType type) const;
+
+ // Returns the filename produced by the given output.
+ //
+ // Some targets make multiple files (like a .dll and an import library). This
+ // function returns the name of the file other targets should depend on and
+ // link to (so in this example, the import library).
+ OutputFile GetTargetOutputFile(const Target* target) const;
+
+ // Returns the relative directory in either slashes or the system separator
+ // from the ninja directory (e.g. "out/Debug") to the source root (e.g.
+ // "../.."). It has no terminating slash.
+ const std::string& build_to_src_no_last_slash() const {
+ return build_to_src_no_last_slash_;
+ }
+ const std::string& build_to_src_system_no_last_slash() const {
+ return build_to_src_system_no_last_slash_;
+ }
+
+ private:
+ const BuildSettings* build_settings_;
+
+ std::string build_to_src_no_last_slash_;
+ std::string build_to_src_system_no_last_slash_;
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaHelper);
+};
+
+#endif // TOOLS_GN_NINJA_HELPER_H_
diff --git a/tools/gn/ninja_helper_unittest.cc b/tools/gn/ninja_helper_unittest.cc
new file mode 100644
index 0000000..4aec032
--- /dev/null
+++ b/tools/gn/ninja_helper_unittest.cc
@@ -0,0 +1,73 @@
+// 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/filesystem_utils.h"
+#include "tools/gn/ninja_helper.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/target.h"
+#include "tools/gn/toolchain.h"
+
+namespace {
+
+class HelperSetterUpper {
+ public:
+ HelperSetterUpper()
+ : build_settings(),
+ toolchain(Label(SourceDir("//"), "tc", SourceDir(), std::string())),
+ settings(&build_settings, &toolchain, std::string()),
+ target(&settings,
+ Label(SourceDir("//tools/gn/"), "name",
+ SourceDir(), std::string())) {
+ settings.set_target_os(Settings::WIN);
+
+ // Output going to "out/Debug".
+ build_settings.SetBuildDir(SourceDir("/out/Debug/"));
+
+ // Our source target is in "tools/gn".
+ target.set_output_type(Target::EXECUTABLE);
+ }
+
+ BuildSettings build_settings;
+ Toolchain toolchain;
+ Settings settings;
+ Target target;
+};
+
+} // namespace
+
+TEST(NinjaHelper, GetNinjaFileForTarget) {
+ HelperSetterUpper setup;
+ NinjaHelper helper(&setup.build_settings);
+
+ // Default toolchain.
+ EXPECT_EQ(OutputFile("obj/tools/gn/name.ninja").value(),
+ helper.GetNinjaFileForTarget(&setup.target).value());
+}
+
+TEST(NinjaHelper, GetOutputFileForSource) {
+ HelperSetterUpper setup;
+ NinjaHelper helper(&setup.build_settings);
+
+ // On Windows, expect ".obj"
+ EXPECT_EQ(OutputFile("obj/tools/gn/name.foo.obj").value(),
+ helper.GetOutputFileForSource(&setup.target,
+ SourceFile("//tools/gn/foo.cc"),
+ SOURCE_CC).value());
+}
+
+TEST(NinjaHelper, GetTargetOutputFile) {
+ HelperSetterUpper setup;
+ NinjaHelper helper(&setup.build_settings);
+ EXPECT_EQ(OutputFile("name.exe"),
+ helper.GetTargetOutputFile(&setup.target));
+
+ // Static library on Windows goes alongside the object files.
+ setup.target.set_output_type(Target::STATIC_LIBRARY);
+ EXPECT_EQ(OutputFile("obj/tools/gn/name.lib"),
+ helper.GetTargetOutputFile(&setup.target));
+
+ // TODO(brettw) test output to library and other OS types.
+}
diff --git a/tools/gn/ninja_target_writer.cc b/tools/gn/ninja_target_writer.cc
new file mode 100644
index 0000000..ed2d09d
--- /dev/null
+++ b/tools/gn/ninja_target_writer.cc
@@ -0,0 +1,550 @@
+// 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/ninja_target_writer.h"
+
+#include <fstream>
+#include <sstream>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "tools/gn/config_values_extractors.h"
+#include "tools/gn/err.h"
+#include "tools/gn/escape.h"
+#include "tools/gn/file_template.h"
+#include "tools/gn/location.h"
+#include "tools/gn/path_output.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/string_utils.h"
+#include "tools/gn/target.h"
+
+namespace {
+
+static const char kCustomTargetSourceKey[] = "{{source}}";
+static const char kCustomTargetSourceNamePartKey[] = "{{source_name_part}}";
+
+struct DefineWriter {
+ void operator()(const std::string& s, std::ostream& out) const {
+ out << " -D" << s;
+ }
+};
+
+struct IncludeWriter {
+ IncludeWriter(PathOutput& path_output,
+ const NinjaHelper& h)
+ : helper(h),
+ path_output_(path_output),
+ old_inhibit_quoting_(path_output.inhibit_quoting()) {
+ // Inhibit quoting since we'll put quotes around the whole thing ourselves.
+ // Since we're writing in NINJA escaping mode, this won't actually do
+ // anything, but I think we may need to change to shell-and-then-ninja
+ // escaping for this in the future.
+ path_output_.set_inhibit_quoting(true);
+ }
+ ~IncludeWriter() {
+ path_output_.set_inhibit_quoting(old_inhibit_quoting_);
+ }
+
+ void operator()(const SourceDir& d, std::ostream& out) const {
+ out << " \"-I";
+ // It's important not to include the trailing slash on directories or on
+ // Windows it will be a backslash and the compiler might think we're
+ // escaping the quote!
+ path_output_.WriteDir(out, d, PathOutput::DIR_NO_LAST_SLASH);
+ out << "\"";
+ }
+
+ const NinjaHelper& helper;
+ PathOutput& path_output_;
+ bool old_inhibit_quoting_; // So we can put the PathOutput back.
+};
+
+} // namespace
+
+NinjaTargetWriter::NinjaTargetWriter(const Target* target, std::ostream& out)
+ : settings_(target->settings()),
+ target_(target),
+ out_(out),
+ path_output_(settings_->build_settings()->build_dir(),
+ ESCAPE_NINJA, true),
+ helper_(settings_->build_settings()) {
+}
+
+NinjaTargetWriter::~NinjaTargetWriter() {
+}
+
+void NinjaTargetWriter::Run() {
+ out_ << "arch = environment.x86\n";
+
+ if (target_->output_type() == Target::COPY_FILES) {
+ WriteCopyRules();
+ } else if (target_->output_type() == Target::CUSTOM) {
+ WriteCustomRules();
+ } else {
+ WriteCompilerVars();
+
+ std::vector<OutputFile> obj_files;
+ WriteSources(&obj_files);
+
+ WriteLinkerStuff(obj_files);
+ }
+}
+
+// static
+void NinjaTargetWriter::RunAndWriteFile(const Target* target) {
+ if (target->output_type() == Target::NONE)
+ return;
+
+ const Settings* settings = target->settings();
+ NinjaHelper helper(settings->build_settings());
+
+ base::FilePath ninja_file(settings->build_settings()->GetFullPath(
+ helper.GetNinjaFileForTarget(target).GetSourceFile(
+ settings->build_settings())));
+
+ file_util::CreateDirectory(ninja_file.DirName());
+
+ // It's rediculously faster to write to a string and then write that to
+ // disk in one operation than to use an fstream here.
+ std::stringstream file;
+ if (file.fail()) {
+ g_scheduler->FailWithError(
+ Err(Location(), "Error writing ninja file.",
+ "Unable to open \"" + FilePathToUTF8(ninja_file) + "\"\n"
+ "for writing."));
+ return;
+ }
+
+ NinjaTargetWriter gen(target, file);
+ gen.Run();
+
+ std::string contents = file.str();
+ file_util::WriteFile(ninja_file, contents.c_str(), contents.size());
+}
+
+void NinjaTargetWriter::WriteCopyRules() {
+ // The dest dir should be inside the output dir so we can just remove the
+ // prefix and get ninja-relative paths.
+ const std::string& output_dir =
+ settings_->build_settings()->build_dir().value();
+ const std::string& dest_dir = target_->destdir().value();
+ DCHECK(StartsWithASCII(dest_dir, output_dir, true));
+ std::string relative_dest_dir(&dest_dir[output_dir.size()],
+ dest_dir.size() - output_dir.size());
+
+ const Target::FileList& sources = target_->sources();
+ std::vector<OutputFile> dest_files;
+ dest_files.reserve(sources.size());
+
+ // Write out rules for each file copied.
+ for (size_t i = 0; i < sources.size(); i++) {
+ const SourceFile& input_file = sources[i];
+
+ // The files should have the same name but in the dest dir.
+ base::StringPiece name_part = FindFilename(&input_file.value());
+ OutputFile dest_file(relative_dest_dir);
+ AppendStringPiece(&dest_file.value(), name_part);
+
+ dest_files.push_back(dest_file);
+
+ out_ << "build ";
+ path_output_.WriteFile(out_, dest_file);
+ out_ << ": copy ";
+ path_output_.WriteFile(out_, input_file);
+ out_ << std::endl;
+ }
+
+ // Write out the rule for the target to copy all of them.
+ out_ << std::endl << "build ";
+ path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
+ out_ << ": stamp";
+ for (size_t i = 0; i < dest_files.size(); i++) {
+ out_ << " ";
+ path_output_.WriteFile(out_, dest_files[i]);
+ }
+ out_ << std::endl;
+
+ // TODO(brettw) need some kind of stamp file for depending on this, as well
+ // as order_only=prebuild.
+}
+
+void NinjaTargetWriter::WriteCustomRules() {
+ // Make a unique name for this rule.
+ std::string target_label = target_->label().GetUserVisibleName(true);
+ std::string custom_rule_name(target_label);
+ ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name);
+ custom_rule_name.append("_rule");
+
+ // Run the script from the dir of the BUILD file. This has no trailing
+ // slash.
+ const SourceDir& script_cd = target_->label().dir();
+ std::string script_cd_to_root = InvertDir(script_cd);
+ if (script_cd_to_root.empty()) {
+ script_cd_to_root = ".";
+ } else {
+ // Remove trailing slash
+ DCHECK(script_cd_to_root[script_cd_to_root.size() - 1] == '/');
+ script_cd_to_root.resize(script_cd_to_root.size() - 1);
+ }
+
+ std::string script_relative_to_cd =
+ script_cd_to_root + target_->script().value();
+
+ bool no_sources = target_->sources().empty();
+
+ // Use a unique name for the response file when there are multiple build
+ // steps so that they don't stomp on each other.
+ std::string rspfile = custom_rule_name;
+ if (!no_sources)
+ rspfile += ".$unique_name";
+ rspfile += ".rsp";
+
+ // First write the custom rule.
+ out_ << "rule " << custom_rule_name << std::endl;
+ out_ << " command = $pythonpath gyp-win-tool action-wrapper $arch "
+ << rspfile << " ";
+ path_output_.WriteDir(out_, script_cd, PathOutput::DIR_NO_LAST_SLASH);
+ out_ << std::endl;
+ out_ << " description = CUSTOM " << target_label << std::endl;
+ out_ << " restat = 1" << std::endl;
+ out_ << " rspfile = " << rspfile << std::endl;
+
+ // The build command goes in the rsp file.
+ out_ << " rspfile_content = $pythonpath " << script_relative_to_cd;
+ for (size_t i = 0; i < target_->script_args().size(); i++) {
+ const std::string& arg = target_->script_args()[i];
+ out_ << " ";
+ WriteCustomArg(arg);
+ }
+ out_ << std::endl << std::endl;
+
+ // Precompute the common dependencies for each step. This includes the
+ // script itself (changing the script should force a rebuild) and any data
+ // files.
+ std::ostringstream common_deps_stream;
+ path_output_.WriteFile(common_deps_stream, target_->script());
+ const Target::FileList& datas = target_->data();
+ for (size_t i = 0; i < datas.size(); i++) {
+ common_deps_stream << " ";
+ path_output_.WriteFile(common_deps_stream, datas[i]);
+ }
+ const std::string& common_deps = common_deps_stream.str();
+
+ // Collects all output files for writing below.
+ std::vector<OutputFile> output_files;
+
+ if (no_sources) {
+ // No sources, write a rule that invokes the script once with the
+ // outputs as outputs, and the data as inputs.
+ out_ << "build";
+ const Target::FileList& outputs = target_->outputs();
+ for (size_t i = 0; i < outputs.size(); i++) {
+ OutputFile output_path(
+ RemovePrefix(outputs[i].value(),
+ settings_->build_settings()->build_dir().value()));
+ output_files.push_back(output_path);
+ out_ << " ";
+ path_output_.WriteFile(out_, output_path);
+ }
+ out_ << ": " << custom_rule_name << " " << common_deps << std::endl;
+ } else {
+ // Write separate rules for each input source file.
+ WriteCustomSourceRules(custom_rule_name, common_deps, script_cd,
+ script_cd_to_root, &output_files);
+ }
+ out_ << std::endl;
+
+ // Last write a stamp rule to collect all outputs.
+ out_ << "build ";
+ path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
+ out_ << ": stamp";
+ for (size_t i = 0; i < output_files.size(); i++) {
+ out_ << " ";
+ path_output_.WriteFile(out_, output_files[i]);
+ }
+ out_ << std::endl;
+}
+
+void NinjaTargetWriter::WriteCustomArg(const std::string& arg) {
+ // This can be optimized if it's called a lot.
+ EscapeOptions options;
+ options.mode = ESCAPE_NINJA;
+ std::string output_str = EscapeString(arg, options);
+
+ // Do this substitution after escaping our our $ will be escaped (which we
+ // don't want).
+ ReplaceSubstringsAfterOffset(&output_str, 0, FileTemplate::kSource,
+ "${source}");
+ ReplaceSubstringsAfterOffset(&output_str, 0, FileTemplate::kSourceNamePart,
+ "${source_name_part}");
+ out_ << output_str;
+}
+
+void NinjaTargetWriter::WriteCustomSourceRules(
+ const std::string& custom_rule_name,
+ const std::string& common_deps,
+ const SourceDir& script_cd,
+ const std::string& script_cd_to_root,
+ std::vector<OutputFile>* output_files) {
+ // Construct the template for generating the output files from each source.
+ const Target::FileList& outputs = target_->outputs();
+ std::vector<std::string> output_template_args;
+ for (size_t i = 0; i < outputs.size(); i++) {
+ // All outputs should be in the output dir.
+ output_template_args.push_back(
+ RemovePrefix(outputs[i].value(),
+ settings_->build_settings()->build_dir().value()));
+ }
+ FileTemplate output_template(output_template_args);
+
+ // Prevent re-allocating each time by initializing outside the loop.
+ std::vector<std::string> output_template_result;
+
+ // Path output formatter for wrigin source paths passed to the script.
+ PathOutput script_source_path_output(script_cd, ESCAPE_SHELL, true);
+
+ const Target::FileList& sources = target_->sources();
+ for (size_t i = 0; i < sources.size(); i++) {
+ // Write outputs for this source file computed by the template.
+ out_ << "build";
+ output_template.ApplyString(sources[i].value(), &output_template_result);
+ for (size_t out_i = 0; out_i < output_template_result.size(); out_i++) {
+ OutputFile output_path(output_template_result[out_i]);
+ output_files->push_back(output_path);
+ out_ << " ";
+ path_output_.WriteFile(out_, output_path);
+ }
+
+ out_ << ": " << custom_rule_name
+ << " " << common_deps
+ << " ";
+ path_output_.WriteFile(out_, sources[i]);
+ out_ << std::endl;
+
+ out_ << " unique_name = " << i << std::endl;
+
+ // The source file here should be relative to the script directory since
+ // this is the variable passed to the script. Here we slightly abuse the
+ // OutputFile object by putting a non-output-relative path in it to signal
+ // that the PathWriter should not prepend directories.
+ out_ << " source = ";
+ script_source_path_output.WriteFile(out_, sources[i]);
+ out_ << std::endl;
+
+ out_ << " source_name_part = "
+ << FindFilenameNoExtension(&sources[i].value()).as_string()
+ << std::endl;
+ }
+}
+
+void NinjaTargetWriter::WriteCompilerVars() {
+ // Defines.
+ out_ << "defines =";
+ RecursiveTargetConfigToStream(target_, &ConfigValues::defines,
+ DefineWriter(), out_);
+ out_ << std::endl;
+
+ // Includes.
+ out_ << "includes =";
+ RecursiveTargetConfigToStream(target_, &ConfigValues::includes,
+ IncludeWriter(path_output_, helper_), out_);
+
+ out_ << std::endl;
+
+ // C flags and friends.
+ out_ << "cflags =";
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags, out_);
+ out_ << std::endl;
+ out_ << "cflags_c =";
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_c, out_);
+ out_ << std::endl;
+ out_ << "cflags_cc =";
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_cc, out_);
+ out_ << std::endl;
+
+ out_ << std::endl;
+}
+
+void NinjaTargetWriter::WriteSources(
+ std::vector<OutputFile>* object_files) {
+ const Target::FileList& sources = target_->sources();
+ object_files->reserve(sources.size());
+
+ for (size_t i = 0; i < sources.size(); i++) {
+ const SourceFile& input_file = sources[i];
+
+ SourceFileType input_file_type = GetSourceFileType(input_file,
+ settings_->target_os());
+ if (input_file_type == SOURCE_UNKNOWN)
+ continue; // Skip unknown file types.
+ const char* command = GetCommandForSourceType(input_file_type);
+ if (!command)
+ continue; // Skip files not needing compilation.
+
+ OutputFile output_file = helper_.GetOutputFileForSource(
+ target_, input_file, input_file_type);
+ object_files->push_back(output_file);
+
+ out_ << "build ";
+ path_output_.WriteFile(out_, output_file);
+ out_ << ": " << command << " ";
+ path_output_.WriteFile(out_, input_file);
+ out_ << std::endl;
+ }
+ out_ << std::endl;
+}
+
+void NinjaTargetWriter::WriteLinkerStuff(
+ const std::vector<OutputFile>& object_files) {
+ // Manifest file on Windows.
+ // TODO(brettw) this seems not to be necessary for static libs, skip in
+ // that case?
+ OutputFile windows_manifest;
+ if (settings_->IsWin()) {
+ windows_manifest.value().assign(helper_.GetTargetOutputDir(target_));
+ windows_manifest.value().append(target_->label().name());
+ windows_manifest.value().append(".intermediate.manifest");
+ out_ << "manifests = ";
+ path_output_.WriteFile(out_, windows_manifest);
+ out_ << std::endl;
+ }
+
+ // Linker flags, append manifest flag on Windows to reference our file.
+ out_ << "ldflags =";
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags, out_);
+ if (settings_->IsWin())
+ out_ << " /MANIFEST /ManifestFile:";
+ path_output_.WriteFile(out_, windows_manifest);
+ { // HACK ERASEME BRETTW FIXME
+ out_ << " /DEBUG /MACHINE:X86 /LIBPATH:\"C:\\Program Files (x86)\\Windows Kits\\8.0\\Lib\\win8\\um\\x86\" /DELAYLOAD:dbghelp.dll /DELAYLOAD:dwmapi.dll /DELAYLOAD:shell32.dll /DELAYLOAD:uxtheme.dll /safeseh /dynamicbase /ignore:4199 /ignore:4221 /nxcompat /SUBSYSTEM:CONSOLE /INCREMENTAL /FIXED:NO /DYNAMICBASE:NO wininet.lib dnsapi.lib version.lib msimg32.lib ws2_32.lib usp10.lib psapi.lib dbghelp.lib winmm.lib shlwapi.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib user32.lib uuid.lib odbc32.lib odbccp32.lib delayimp.lib /NXCOMPAT";
+ }
+ out_ << std::endl;
+
+ // Libraries to link.
+ out_ << "libs =" << std::endl;
+
+ // The external output file is the one that other libs depend on.
+ OutputFile external_output_file = helper_.GetTargetOutputFile(target_);
+
+ // The internal output file is the "main thing" we think we're making. In
+ // the case of shared libraries, this is the shared library and the external
+ // output file is the import library. In other cases, the internal one and
+ // the external one are the same.
+ OutputFile internal_output_file;
+ if (target_->output_type() == Target::SHARED_LIBRARY) {
+ if (settings_->IsWin()) {
+ internal_output_file = OutputFile(target_->label().name() + ".dll");
+ } else {
+ NOTREACHED(); // TODO(brettw) write this.
+ }
+ } else {
+ internal_output_file = external_output_file;
+ }
+
+ // TODO(brettw) should we append data files to this?
+
+ // In Python see "self.ninja.build(output, command, input,"
+ out_ << "build ";
+ path_output_.WriteFile(out_, internal_output_file);
+ if (external_output_file != internal_output_file) {
+ out_ << " ";
+ path_output_.WriteFile(out_, external_output_file);
+ }
+ out_ << ": " << GetCommandForTargetType();
+ for (size_t i = 0; i < object_files.size(); i++) {
+ out_ << " ";
+ path_output_.WriteFile(out_, object_files[i]);
+ }
+
+ if (target_->output_type() == Target::EXECUTABLE ||
+ target_->output_type() == Target::SHARED_LIBRARY ||
+ target_->output_type() == Target::LOADABLE_MODULE) {
+ const std::vector<const Target*>& deps = target_->deps();
+ const std::set<const Target*>& inherited = target_->inherited_libraries();
+
+ // Now append linkable libraries to the linker command.
+ for (size_t i = 0; i < deps.size(); i++) {
+ if (deps[i]->IsLinkable() &&
+ inherited.find(deps[i]) == inherited.end()) {
+ out_ << " ";
+ path_output_.WriteFile(out_,
+ helper_.GetTargetOutputFile(target_->deps()[i]));
+ }
+ }
+ for (std::set<const Target*>::const_iterator i = inherited.begin();
+ i != inherited.end(); ++i) {
+ out_ << " ";
+ path_output_.WriteFile(out_, helper_.GetTargetOutputFile(*i));
+ }
+ }
+ out_ << std::endl;
+
+ if (target_->output_type() == Target::SHARED_LIBRARY) {
+ out_ << " soname = ";
+ path_output_.WriteFile(out_, internal_output_file);
+ out_ << std::endl;
+
+ out_ << " lib = ";
+ path_output_.WriteFile(out_, internal_output_file);
+ out_ << std::endl;
+
+ out_ << " dll = ";
+ path_output_.WriteFile(out_, internal_output_file);
+ out_ << std::endl;
+
+ if (settings_->IsWin()) {
+ out_ << " implibflag = /IMPLIB:";
+ path_output_.WriteFile(out_, external_output_file);
+ out_ << std::endl;
+ }
+ }
+
+ // TODO(brettw) postbuild steps here.
+
+ out_ << std::endl;
+}
+
+const char* NinjaTargetWriter::GetCommandForSourceType(
+ SourceFileType type) const {
+ if (type == SOURCE_C)
+ return "cc";
+ if (type == SOURCE_CC)
+ return "cxx";
+
+ // TODO(brettw) asm files.
+
+ if (settings_->IsMac()) {
+ if (type == SOURCE_M)
+ return "objc";
+ if (type == SOURCE_MM)
+ return "objcxx";
+ }
+
+ if (settings_->IsWin()) {
+ if (type == SOURCE_RC)
+ return "rc";
+ }
+
+ // TODO(brettw) stuff about "S" files on non-Windows.
+ return NULL;
+}
+
+const char* NinjaTargetWriter::GetCommandForTargetType() const {
+ if (target_->output_type() == Target::NONE) {
+ NOTREACHED();
+ return "";
+ }
+
+ if (target_->output_type() == Target::STATIC_LIBRARY) {
+ // TODO(brettw) stuff about standalong static libraryes on Unix in
+ // WriteTarget in the Python one, and lots of postbuild steps.
+ return "alink";
+ }
+
+ if (target_->output_type() == Target::SHARED_LIBRARY)
+ return "solink";
+
+ return "link";
+}
diff --git a/tools/gn/ninja_target_writer.h b/tools/gn/ninja_target_writer.h
new file mode 100644
index 0000000..5e6827d
--- /dev/null
+++ b/tools/gn/ninja_target_writer.h
@@ -0,0 +1,67 @@
+// 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.
+
+#ifndef TOOLS_GN_NINJA_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_TARGET_WRITER_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/ninja_helper.h"
+#include "tools/gn/path_output.h"
+#include "tools/gn/settings.h"
+
+class Target;
+
+// Generates one target's ".ninja" file. The toplevel "build.ninja" file is
+// generated by the NinjaBuildGenerator.
+class NinjaTargetWriter {
+ public:
+ NinjaTargetWriter(const Target* target, std::ostream& out);
+ ~NinjaTargetWriter();
+
+ void Run();
+
+ static void RunAndWriteFile(const Target* target);
+
+ private:
+ void WriteCopyRules();
+
+ void WriteCustomRules();
+ void WriteCustomArg(const std::string& arg);
+
+ // Writs the rules for compiling each source, writing all output files
+ // to the given vector.
+ //
+ // common_deps is a precomputed string of all ninja files that are common
+ // to each build step. This is added to each one.
+ void WriteCustomSourceRules(const std::string& custom_rule_name,
+ const std::string& common_deps,
+ const SourceDir& script_cd,
+ const std::string& script_cd_to_root,
+ std::vector<OutputFile>* output_files);
+
+ void WriteCompilerVars();
+ void WriteSources(std::vector<OutputFile>* object_files);
+ void WriteLinkerStuff(const std::vector<OutputFile>& object_files);
+
+ // Returns NULL if the source type should not be compiled on this target.
+ const char* GetCommandForSourceType(SourceFileType type) const;
+
+ const char* GetCommandForTargetType() const;
+
+ const Settings* settings_; // Non-owning.
+ const Target* target_; // Non-owning.
+ std::ostream& out_;
+ PathOutput path_output_;
+
+ NinjaHelper helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaTargetWriter);
+};
+
+#endif // TOOLS_GN_NINJA_TARGET_WRITER_H_
diff --git a/tools/gn/ninja_toolchain_writer.cc b/tools/gn/ninja_toolchain_writer.cc
new file mode 100644
index 0000000..0e38b8c
--- /dev/null
+++ b/tools/gn/ninja_toolchain_writer.cc
@@ -0,0 +1,94 @@
+// 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/ninja_toolchain_writer.h"
+
+#include <fstream>
+
+#include "base/file_util.h"
+#include "base/strings/stringize_macros.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/target.h"
+#include "tools/gn/toolchain.h"
+
+NinjaToolchainWriter::NinjaToolchainWriter(
+ const Settings* settings,
+ const std::vector<const Target*>& targets,
+ std::ostream& out)
+ : settings_(settings),
+ targets_(targets),
+ out_(out),
+ path_output_(settings_->build_settings()->build_dir(),
+ ESCAPE_NINJA, true),
+ helper_(settings->build_settings()) {
+}
+
+NinjaToolchainWriter::~NinjaToolchainWriter() {
+}
+
+void NinjaToolchainWriter::Run() {
+ WriteRules();
+ WriteSubninjas();
+}
+
+// static
+bool NinjaToolchainWriter::RunAndWriteFile(
+ const Settings* settings,
+ const std::vector<const Target*>& targets) {
+ NinjaHelper helper(settings->build_settings());
+ base::FilePath ninja_file(settings->build_settings()->GetFullPath(
+ helper.GetNinjaFileForToolchain(settings).GetSourceFile(
+ settings->build_settings())));
+ file_util::CreateDirectory(ninja_file.DirName());
+
+ std::ofstream file;
+ file.open(FilePathToUTF8(ninja_file).c_str(),
+ std::ios_base::out | std::ios_base::binary);
+ if (file.fail())
+ return false;
+
+ NinjaToolchainWriter gen(settings, targets, file);
+ gen.Run();
+ return true;
+}
+
+void NinjaToolchainWriter::WriteRules() {
+ const Toolchain* tc = settings_->toolchain();
+ std::string indent(" ");
+
+ for (int i = Toolchain::TYPE_NONE + 1; i < Toolchain::TYPE_NUMTYPES; i++) {
+ Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(i);
+ const Toolchain::Tool& tool = tc->GetTool(tool_type);
+ if (tool.empty())
+ continue;
+
+ out_ << "rule " << Toolchain::ToolTypeToName(tool_type) << std::endl;
+
+ #define WRITE_ARG(name) \
+ if (!tool.name.empty()) \
+ out_ << indent << " " STRINGIZE(name) " = " << tool.name << std::endl;
+ WRITE_ARG(command);
+ WRITE_ARG(depfile);
+ WRITE_ARG(deps);
+ WRITE_ARG(description);
+ WRITE_ARG(pool);
+ WRITE_ARG(restat);
+ WRITE_ARG(rspfile);
+ WRITE_ARG(rspfile_content);
+ #undef WRITE_ARG
+ }
+ out_ << std::endl;
+}
+
+void NinjaToolchainWriter::WriteSubninjas() {
+ for (size_t i = 0; i < targets_.size(); i++) {
+ if (targets_[i]->output_type() != Target::NONE) {
+ out_ << "subninja ";
+ path_output_.WriteFile(out_, helper_.GetNinjaFileForTarget(targets_[i]));
+ out_ << std::endl;
+ }
+ }
+ out_ << std::endl;
+}
diff --git a/tools/gn/ninja_toolchain_writer.h b/tools/gn/ninja_toolchain_writer.h
new file mode 100644
index 0000000..71759ef
--- /dev/null
+++ b/tools/gn/ninja_toolchain_writer.h
@@ -0,0 +1,46 @@
+// 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.
+
+#ifndef TOOLS_GN_NINJA_TOOLCHAIN_WRITER_H_
+#define TOOLS_GN_NINJA_TOOLCHAIN_WRITER_H_
+
+#include <iosfwd>
+#include <vector>
+
+#include "tools/gn/ninja_helper.h"
+#include "tools/gn/path_output.h"
+
+class BuildSettings;
+class Settings;
+class Target;
+
+class NinjaToolchainWriter {
+ public:
+ // Takes the settings for the toolchain, as well as the list of all targets
+ // assicoated with the toolchain.
+ static bool RunAndWriteFile(const Settings* settings,
+ const std::vector<const Target*>& targets);
+
+ private:
+ NinjaToolchainWriter(const Settings* settings,
+ const std::vector<const Target*>& targets,
+ std::ostream& out);
+ ~NinjaToolchainWriter();
+
+ void Run();
+
+ void WriteRules();
+ void WriteSubninjas();
+
+ const Settings* settings_;
+ std::vector<const Target*> targets_;
+ std::ostream& out_;
+ PathOutput path_output_;
+
+ NinjaHelper helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaToolchainWriter);
+};
+
+#endif // TOOLS_GN_NINJA_TOOLCHAIN_WRITER_H_
diff --git a/tools/gn/ninja_writer.cc b/tools/gn/ninja_writer.cc
new file mode 100644
index 0000000..8b69a3f
--- /dev/null
+++ b/tools/gn/ninja_writer.cc
@@ -0,0 +1,64 @@
+// 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/ninja_writer.h"
+
+#include "tools/gn/location.h"
+#include "tools/gn/ninja_build_writer.h"
+#include "tools/gn/ninja_toolchain_writer.h"
+
+
+NinjaWriter::NinjaWriter(const BuildSettings* build_settings)
+ : build_settings_(build_settings) {
+}
+
+NinjaWriter::~NinjaWriter() {
+}
+
+// static
+bool NinjaWriter::RunAndWriteFiles(const BuildSettings* build_settings) {
+ NinjaWriter writer(build_settings);
+ return writer.WriteRootBuildfiles();
+}
+
+bool NinjaWriter::WriteRootBuildfiles() {
+ // Categorize all targets by toolchain.
+ typedef std::map<Label, std::vector<const Target*> > CategorizedMap;
+ CategorizedMap categorized;
+
+ std::vector<const Target*> all_targets;
+ build_settings_->target_manager().GetAllTargets(&all_targets);
+ for (size_t i = 0; i < all_targets.size(); i++) {
+ categorized[all_targets[i]->label().GetToolchainLabel()].push_back(
+ all_targets[i]);
+ }
+
+ Label default_label =
+ build_settings_->toolchain_manager().GetDefaultToolchainUnlocked();
+
+ // Write out the toolchain buildfiles, and also accumulate the set of
+ // all settings and find the list of targets in the default toolchain.
+ std::vector<const Settings*> all_settings;
+ const std::vector<const Target*>* default_targets = NULL;
+ for (CategorizedMap::const_iterator i = categorized.begin();
+ i != categorized.end(); ++i) {
+ const Settings* settings;
+ {
+ base::AutoLock lock(build_settings_->item_tree().lock());
+ Err ignored;
+ settings =
+ build_settings_->toolchain_manager().GetSettingsForToolchainLocked(
+ LocationRange(), i->first, &ignored);
+ }
+ if (i->first == default_label)
+ default_targets = &i->second;
+ all_settings.push_back(settings);
+ if (!NinjaToolchainWriter::RunAndWriteFile(settings, i->second))
+ return false;
+ }
+
+ // Write the root buildfile.
+ return NinjaBuildWriter::RunAndWriteFile(build_settings_, all_settings,
+ *default_targets);
+}
diff --git a/tools/gn/ninja_writer.h b/tools/gn/ninja_writer.h
new file mode 100644
index 0000000..9b6d2c1
--- /dev/null
+++ b/tools/gn/ninja_writer.h
@@ -0,0 +1,27 @@
+// 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.
+
+#ifndef TOOLS_GN_NINJA_WRITER_H_
+#define TOOLS_GN_NINJA_WRITER_H_
+
+#include "base/basictypes.h"
+
+class BuildSettings;
+
+class NinjaWriter {
+ public:
+ static bool RunAndWriteFiles(const BuildSettings* build_settings);
+
+ private:
+ NinjaWriter(const BuildSettings* build_settings);
+ ~NinjaWriter();
+
+ bool WriteRootBuildfiles();
+
+ const BuildSettings* build_settings_;
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaWriter);
+};
+
+#endif // TOOLS_GN_NINJA_WRITER_H_
diff --git a/tools/gn/operators.cc b/tools/gn/operators.cc
new file mode 100644
index 0000000..afded9f
--- /dev/null
+++ b/tools/gn/operators.cc
@@ -0,0 +1,573 @@
+// 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/operators.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "tools/gn/err.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/token.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+const char kSourcesName[] = "sources";
+
+// Applies the sources assignment filter from the given scope to each element
+// of source (can be a list or a string), appending it to dest if it doesn't
+// match.
+void AppendFilteredSourcesToValue(const Scope* scope,
+ const Value& source,
+ Value* dest) {
+ const PatternList* filter = scope->GetSourcesAssignmentFilter();
+
+ const std::vector<Value>& source_list = source.list_value();
+
+ if (source.type() == Value::STRING) {
+ if (!filter || filter->is_empty() ||
+ !filter->MatchesValue(source))
+ dest->list_value().push_back(source);
+ return;
+ }
+
+ // Otherwise source is a list.
+ DCHECK(source.type() == Value::LIST);
+ if (!filter || filter->is_empty()) {
+ // No filter, append everything.
+ for (size_t i = 0; i < source_list.size(); i++)
+ dest->list_value().push_back(source_list[i]);
+ return;
+ }
+
+ // Note: don't reserve() the dest vector here since that actually hurts
+ // the allocation pattern when the build script is doing multiple small
+ // additions.
+ for (size_t i = 0; i < source_list.size(); i++) {
+ if (!filter->MatchesValue(source_list[i]))
+ dest->list_value().push_back(source_list[i]);
+ }
+}
+
+void RemoveMatchesFromList(const BinaryOpNode* op_node,
+ Value* list,
+ const Value& to_remove,
+ Err* err) {
+ std::vector<Value>& v = list->list_value();
+ switch (to_remove.type()) {
+ case Value::INTEGER: // Filter out the individual int/string.
+ case Value::STRING: {
+ bool found_match = false;
+ for (size_t i = 0; i < v.size(); /* nothing */) {
+ if (v[i] == to_remove) {
+ found_match = true;
+ v.erase(v.begin() + i);
+ } else {
+ i++;
+ }
+ }
+ if (!found_match) {
+ *err = Err(to_remove.origin()->GetRange(), "Item not found",
+ "You were trying to remove \"" + to_remove.ToString() +
+ "\"\nfrom the list but it wasn't there.");
+ }
+ break;
+ }
+
+ case Value::LIST: // Filter out each individual thing.
+ for (size_t i = 0; i < to_remove.list_value().size(); i++) {
+ // TODO(brettw) if the nested item is a list, we may want to search
+ // for the literal list rather than remote the items in it.
+ RemoveMatchesFromList(op_node, list, to_remove.list_value()[i], err);
+ if (err->has_error())
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+// Assignment -----------------------------------------------------------------
+
+Value ExecuteEquals(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Token& left,
+ const Value& right,
+ Err* err) {
+ const Value* old_value = scope->GetValue(left.value(), false);
+ if (old_value) {
+ if (scope->IsSetButUnused(left.value())) {
+ // Throw an error for re-assigning without using the value first. The
+ // exception is that you can overwrite an empty list with another list
+ // since this is the way to get around the "can't overwrite a nonempty
+ // list with another nonempty list" restriction.
+ if (old_value->type() != Value::LIST ||
+ !old_value->list_value().empty()) {
+ *err = Err(op_node->left()->GetRange(), "Overwriting unused variable.",
+ "This overwrites a previous assignment to \"" +
+ left.value().as_string() + "\" that had no effect.");
+ err->AppendSubErr(Err(*scope->GetValue(left.value()),
+ "Previously set here.",
+ "Maybe you wanted \"+=\" to append instead?"));
+ return Value();
+ }
+ } else {
+ // Throw an error when overwriting a nonempty list with another nonempty
+ // list item. This is to detect the case where you write
+ // defines = ["FOO"]
+ // and you overwrote inherited ones, when instead you mean to append:
+ // defines += ["FOO"]
+ if (old_value->type() == Value::LIST &&
+ !old_value->list_value().empty() &&
+ right.type() == Value::LIST &&
+ !right.list_value().empty()) {
+ *err = Err(op_node->left()->GetRange(), "Replacing nonempty list.",
+ std::string("This overwrites a previously-defined nonempty list ") +
+ "(length " + base::IntToString(old_value->list_value().size()) +
+ ").");
+ err->AppendSubErr(Err(*old_value, "for previous definition",
+ "with another one (length " +
+ base::IntToString(right.list_value().size()) + "). Did you mean " +
+ "\"+=\" to append instead? If you\nreally want to do this, do\n " +
+ left.value().as_string() + " = []\nbefore reassigning."));
+ return Value();
+ }
+ }
+ }
+ if (err->has_error())
+ return Value();
+
+ if (right.type() == Value::LIST && left.value() == kSourcesName) {
+ // Assigning to sources, filter the list. Here we do the filtering and
+ // copying in one step to save an extra list copy (the lists may be
+ // long).
+ Value* set_value = scope->SetValue(left.value(),
+ Value(op_node, Value::LIST), op_node);
+ set_value->list_value().reserve(right.list_value().size());
+ AppendFilteredSourcesToValue(scope, right, set_value);
+ } else {
+ // Normal value set, just copy it.
+ scope->SetValue(left.value(), right, op_node->right());
+ }
+ return Value();
+}
+
+// allow_type_conversion indicates if we're allowed to change the type of the
+// left value. This is set to true when doing +, and false when doing +=.
+void ValuePlusEquals(const Scope* scope,
+ const BinaryOpNode* op_node,
+ const Token& left_token,
+ Value* left,
+ const Value& right,
+ bool allow_type_conversion,
+ Err* err) {
+ switch (left->type()) {
+ // Left-hand-side int.
+ case Value::INTEGER:
+ switch (right.type()) {
+ case Value::INTEGER: // int + int -> addition.
+ left->int_value() += right.int_value();
+ return;
+
+ case Value::STRING: // int + string -> string concat.
+ if (allow_type_conversion) {
+ *left = Value(op_node,
+ base::Int64ToString(left->int_value()) + right.string_value());
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ // Left-hand-side string.
+ case Value::STRING:
+ switch (right.type()) {
+ case Value::INTEGER: // string + int -> string concat.
+ left->string_value().append(base::Int64ToString(right.int_value()));
+ return;
+
+ case Value::STRING: // string + string -> string contat.
+ left->string_value().append(right.string_value());
+ return;
+
+ default:
+ break;
+ }
+ break;
+
+ // Left-hand-side list.
+ case Value::LIST:
+ switch (right.type()) {
+ case Value::INTEGER: // list + integer -> list append.
+ case Value::STRING: // list + string -> list append.
+ if (left_token.value() == kSourcesName)
+ AppendFilteredSourcesToValue(scope, right, left);
+ else
+ left->list_value().push_back(right);
+ return;
+
+ case Value::LIST: // list + list -> list concat.
+ if (left_token.value() == kSourcesName) {
+ // Filter additions through the assignment filter.
+ AppendFilteredSourcesToValue(scope, right, left);
+ } else {
+ // Normal list concat.
+ for (size_t i = 0; i < right.list_value().size(); i++)
+ left->list_value().push_back(right.list_value()[i]);
+ }
+ return;
+
+ default:
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ *err = Err(op_node->op(), "Incompatible types to add.",
+ std::string("I see a ") + Value::DescribeType(left->type()) + " and a " +
+ Value::DescribeType(right.type()) + ".");
+}
+
+Value ExecutePlusEquals(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Token& left,
+ const Value& right,
+ Err* err) {
+ // We modify in-place rather than doing read-modify-write to avoid
+ // copying large lists.
+ Value* left_value =
+ scope->GetValueForcedToCurrentScope(left.value(), op_node);
+ if (!left_value) {
+ *err = Err(left, "Undefined variable for +=.",
+ "I don't have something with this name in scope now.");
+ return Value();
+ }
+ ValuePlusEquals(scope, op_node, left, left_value, right, false, err);
+ left_value->set_origin(op_node);
+ scope->MarkUnused(left.value());
+ return Value();
+}
+
+void ValueMinusEquals(const BinaryOpNode* op_node,
+ Value* left,
+ const Value& right,
+ bool allow_type_conversion,
+ Err* err) {
+ switch (left->type()) {
+ // Left-hand-side int.
+ case Value::INTEGER:
+ switch (right.type()) {
+ case Value::INTEGER: // int - int -> subtraction.
+ left->int_value() -= right.int_value();
+ return;
+
+ default:
+ break;
+ }
+ break;
+
+ // Left-hand-side string.
+ case Value::STRING:
+ break; // All are errors.
+
+ // Left-hand-side list.
+ case Value::LIST:
+ RemoveMatchesFromList(op_node, left, right, err);
+ return;
+
+ default:
+ break;
+ }
+
+ *err = Err(op_node->op(), "Incompatible types to add.",
+ std::string("I see a ") + Value::DescribeType(left->type()) + " and a " +
+ Value::DescribeType(right.type()) + ".");
+}
+
+Value ExecuteMinusEquals(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Token& left,
+ const Value& right,
+ Err* err) {
+ Value* left_value =
+ scope->GetValueForcedToCurrentScope(left.value(), op_node);
+ if (!left_value) {
+ *err = Err(left, "Undefined variable for -=.",
+ "I don't have something with this name in scope now.");
+ return Value();
+ }
+ ValueMinusEquals(op_node, left_value, right, false, err);
+ left_value->set_origin(op_node);
+ scope->MarkUnused(left.value());
+ return Value();
+}
+
+// Plus/Minus -----------------------------------------------------------------
+
+Value ExecutePlus(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ Value ret = left;
+ ValuePlusEquals(scope, op_node, Token(), &ret, right, true, err);
+ ret.set_origin(op_node);
+ return ret;
+}
+
+Value ExecuteMinus(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ Value ret = left;
+ ValueMinusEquals(op_node, &ret, right, true, err);
+ ret.set_origin(op_node);
+ return ret;
+}
+
+// Comparison -----------------------------------------------------------------
+
+Value ExecuteEqualsEquals(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ if (left == right)
+ return Value(op_node, 1);
+ return Value(op_node, 0);
+}
+
+Value ExecuteNotEquals(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ // Evaluate in terms of ==.
+ Value result = ExecuteEqualsEquals(scope, op_node, left, right, err);
+ result.int_value() = static_cast<int64>(!result.int_value());
+ return result;
+}
+
+Value FillNeedsToIntegersError(const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ *err = Err(op_node, "Comparison requires two integers.",
+ "This operator can only compare two integers.");
+ err->AppendRange(left.origin()->GetRange());
+ err->AppendRange(right.origin()->GetRange());
+ return Value();
+}
+
+Value ExecuteLessEquals(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
+ return FillNeedsToIntegersError(op_node, left, right, err);
+ return Value(op_node, left.int_value() <= right.int_value());
+}
+
+Value ExecuteGreaterEquals(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
+ return FillNeedsToIntegersError(op_node, left, right, err);
+ return Value(op_node, left.int_value() >= right.int_value());
+}
+
+Value ExecuteGreater(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
+ return FillNeedsToIntegersError(op_node, left, right, err);
+ return Value(op_node, left.int_value() > right.int_value());
+}
+
+Value ExecuteLess(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
+ return FillNeedsToIntegersError(op_node, left, right, err);
+ return Value(op_node, left.int_value() < right.int_value());
+}
+
+// Binary ----------------------------------------------------------------------
+
+Value ExecuteOr(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ return Value(op_node,
+ static_cast<int64>(left.InterpretAsInt() || right.InterpretAsInt()));
+}
+
+Value ExecuteAnd(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ return Value(op_node,
+ static_cast<int64>(left.InterpretAsInt() && right.InterpretAsInt()));
+}
+
+} // namespace
+
+// ----------------------------------------------------------------------------
+
+bool IsUnaryOperator(const Token& token) {
+ if (token.type() != Token::OPERATOR)
+ return false;
+ return token.value() == "!";
+}
+
+bool IsBinaryOperator(const Token& token) {
+ if (token.type() != Token::OPERATOR)
+ return false;
+ return token.value() == "=" ||
+ token.value() == "+=" ||
+ token.value() == "-=" ||
+ token.value() == "+" ||
+ token.value() == "-" ||
+ token.value() == "==" ||
+ token.value() == "!=" ||
+ token.value() == "<=" ||
+ token.value() == ">=" ||
+ token.value() == "<" ||
+ token.value() == ">" ||
+ token.value() == "&&" ||
+ token.value() == "||";
+}
+
+bool IsFunctionCallArgBeginScoper(const Token& token) {
+ return token.IsScoperEqualTo("(");
+}
+
+bool IsFunctionCallArgEndScoper(const Token& token) {
+ return token.IsScoperEqualTo(")");
+}
+
+bool IsScopeBeginScoper(const Token& token) {
+ return token.IsScoperEqualTo("{");
+}
+
+bool IsScopeEndScoper(const Token& token) {
+ return token.IsScoperEqualTo("}");
+}
+
+Value ExecuteUnaryOperator(Scope* scope,
+ const UnaryOpNode* op_node,
+ const Value& expr,
+ Err* err) {
+ DCHECK(op_node->op().IsOperatorEqualTo("!"));
+ return Value(op_node, !expr.InterpretAsInt());
+}
+
+Value ExecuteBinaryOperator(Scope* scope,
+ const BinaryOpNode* op_node,
+ const ParseNode* left,
+ const ParseNode* right,
+ Err* err) {
+ const Token& op = op_node->op();
+
+ // First handle the ones that take an lvalue.
+ if (op.IsOperatorEqualTo("=") ||
+ op.IsOperatorEqualTo("+=") ||
+ op.IsOperatorEqualTo("-=")) {
+ const IdentifierNode* left_id = left->AsIdentifier();
+ if (!left_id) {
+ *err = Err(op, "Operator requires an lvalue.",
+ "This thing on the left is not an idenfitier.");
+ err->AppendRange(left->GetRange());
+ return Value();
+ }
+ const Token& dest = left_id->value();
+
+ Value right_value = right->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ if (right_value.type() == Value::NONE) {
+ *err = Err(op, "Operator requires an rvalue.",
+ "This thing on the right does not evaluate to a value.");
+ err->AppendRange(right->GetRange());
+ return Value();
+ }
+
+ if (op.IsOperatorEqualTo("="))
+ return ExecuteEquals(scope, op_node, dest, right_value, err);
+ if (op.IsOperatorEqualTo("+="))
+ return ExecutePlusEquals(scope, op_node, dest, right_value, err);
+ if (op.IsOperatorEqualTo("-="))
+ return ExecuteMinusEquals(scope, op_node, dest, right_value, err);
+ NOTREACHED();
+ return Value();
+ }
+
+ // Left value.
+ Value left_value = left->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ if (left_value.type() == Value::NONE) {
+ *err = Err(op, "Operator requires an value.",
+ "This thing on the left does not evaluate to a value.");
+ err->AppendRange(left->GetRange());
+ return Value();
+ }
+
+ // Right value. Note: don't move this above to share code with the lvalue
+ // version since in this case we want to execute the left side first.
+ Value right_value = right->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ if (right_value.type() == Value::NONE) {
+ *err = Err(op, "Operator requires an value.",
+ "This thing on the right does not evaluate to a value.");
+ err->AppendRange(right->GetRange());
+ return Value();
+ }
+
+ // +, -.
+ if (op.IsOperatorEqualTo("-"))
+ return ExecuteMinus(scope, op_node, left_value, right_value, err);
+ if (op.IsOperatorEqualTo("+"))
+ return ExecutePlus(scope, op_node, left_value, right_value, err);
+
+ // Comparisons.
+ if (op.IsOperatorEqualTo("=="))
+ return ExecuteEqualsEquals(scope, op_node, left_value, right_value, err);
+ if (op.IsOperatorEqualTo("!="))
+ return ExecuteNotEquals(scope, op_node, left_value, right_value, err);
+ if (op.IsOperatorEqualTo(">="))
+ return ExecuteGreaterEquals(scope, op_node, left_value, right_value, err);
+ if (op.IsOperatorEqualTo("<="))
+ return ExecuteLessEquals(scope, op_node, left_value, right_value, err);
+ if (op.IsOperatorEqualTo(">"))
+ return ExecuteGreater(scope, op_node, left_value, right_value, err);
+ if (op.IsOperatorEqualTo("<"))
+ return ExecuteLess(scope, op_node, left_value, right_value, err);
+
+ // ||, &&.
+ if (op.IsOperatorEqualTo("||"))
+ return ExecuteOr(scope, op_node, left_value, right_value, err);
+ if (op.IsOperatorEqualTo("&&"))
+ return ExecuteAnd(scope, op_node, left_value, right_value, err);
+
+ return Value();
+}
diff --git a/tools/gn/operators.h b/tools/gn/operators.h
new file mode 100644
index 0000000..3401c22
--- /dev/null
+++ b/tools/gn/operators.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef TOOLS_GN_OPERATORS_H_
+#define TOOLS_GN_OPERATORS_H_
+
+class BinaryOpNode;
+class Err;
+class ParseNode;
+class Scope;
+class Token;
+class UnaryOpNode;
+class Value;
+
+bool IsUnaryOperator(const Token& token);
+bool IsBinaryOperator(const Token& token);
+
+bool IsFunctionCallArgBeginScoper(const Token& token); // "("
+bool IsFunctionCallArgEndScoper(const Token& token); // ")"
+
+bool IsScopeBeginScoper(const Token& token); // "{"
+bool IsScopeEndScoper(const Token& token); // "}"
+
+Value ExecuteUnaryOperator(Scope* scope,
+ const UnaryOpNode* op_node,
+ const Value& value,
+ Err* err);
+Value ExecuteBinaryOperator(Scope* scope,
+ const BinaryOpNode* op_node,
+ const ParseNode* left,
+ const ParseNode* right,
+ Err* err);
+
+#endif // TOOLS_GN_OPERATORS_H_
diff --git a/tools/gn/output_file.h b/tools/gn/output_file.h
new file mode 100644
index 0000000..9c5e4b2
--- /dev/null
+++ b/tools/gn/output_file.h
@@ -0,0 +1,41 @@
+// 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.
+
+#ifndef TOOLS_GN_OUTPUT_FILE_H_
+#define TOOLS_GN_OUTPUT_FILE_H_
+
+#include <string>
+
+#include "tools/gn/build_settings.h"
+#include "tools/gn/source_file.h"
+
+// A simple wrapper around a string that indicates the string is a path
+// relative to the output directory.
+class OutputFile {
+ public:
+ OutputFile() : value_() {}
+ explicit OutputFile(const base::StringPiece& str)
+ : value_(str.data(), str.size()) {
+ }
+
+ std::string& value() { return value_; }
+ const std::string& value() const { return value_; }
+
+ // Converts to a SourceFile by prepending the build directory to the file.
+ SourceFile GetSourceFile(const BuildSettings* build_settings) const {
+ return SourceFile(build_settings->build_dir().value() + value_);
+ }
+
+ bool operator==(const OutputFile& other) const {
+ return value_ == other.value_;
+ }
+ bool operator!=(const OutputFile& other) const {
+ return value_ != other.value_;
+ }
+
+ private:
+ std::string value_;
+};
+
+#endif
diff --git a/tools/gn/output_stream.h b/tools/gn/output_stream.h
new file mode 100644
index 0000000..6f39c86
--- /dev/null
+++ b/tools/gn/output_stream.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef TOOLS_GN_OUTPUT_STREAM_H_
+#define TOOLS_GN_OUTPUT_STREAM_H_
+
+class OutputStream {
+ public:
+
+
+
+ OutputStream& WriteBuffer(const char* buf, size_t len);
+ OutputStream& WriteInt(int i);
+
+ // Write a literal.
+ // This template expansion prevents having to look for nulls.
+ template<size_t size> OutputStream& Write(const char (&buf)[size]) {
+ return WriteBuffer(buf, size);
+ }
+
+ // Write a literal string.
+ OutputStream& Write(const std::string& str) {
+ return WriteBuffer(str.c_str(), str.size());
+ }
+
+ // Quotes if necessary, and does necessary escaping. If more than one
+ // input is provided, the results will be concatenated together (useful
+ // for constructing paths without a temporary buffer).
+ OutputStream& WritePath(const std::string& s);
+ OutputStream& WritePath(const std::string& s0,
+ const std::string& s1);
+ OutputStream& WritePath(const std::string& s0,
+ const std::string& s1,
+ const std::string& s2);
+
+ OutputStream& EndLine() {
+ return WriteBuffer("\n", 1);
+ }
+};
+
+#endif // TOOLS_GN_OUTPUT_STREAM_H_
diff --git a/tools/gn/parse_tree.cc b/tools/gn/parse_tree.cc
new file mode 100644
index 0000000..0f84970
--- /dev/null
+++ b/tools/gn/parse_tree.cc
@@ -0,0 +1,472 @@
+// 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/parse_tree.h"
+
+#include <string>
+
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/operators.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/string_utils.h"
+
+namespace {
+
+std::string IndentFor(int value) {
+ std::string ret;
+ for (int i = 0; i < value; i++)
+ ret.append(" ");
+ return ret;
+}
+
+} // namespace
+
+ParseNode::ParseNode() {
+}
+
+ParseNode::~ParseNode() {
+}
+
+const AccessorNode* ParseNode::AsAccessor() const { return NULL; }
+const BinaryOpNode* ParseNode::AsBinaryOp() const { return NULL; }
+const BlockNode* ParseNode::AsBlock() const { return NULL; }
+const ConditionNode* ParseNode::AsConditionNode() const { return NULL; }
+const FunctionCallNode* ParseNode::AsFunctionCall() const { return NULL; }
+const IdentifierNode* ParseNode::AsIdentifier() const { return NULL; }
+const ListNode* ParseNode::AsList() const { return NULL; }
+const LiteralNode* ParseNode::AsLiteral() const { return NULL; }
+const UnaryOpNode* ParseNode::AsUnaryOp() const { return NULL; }
+
+// AccessorNode ---------------------------------------------------------------
+
+AccessorNode::AccessorNode() {
+}
+
+AccessorNode::~AccessorNode() {
+}
+
+const AccessorNode* AccessorNode::AsAccessor() const {
+ return this;
+}
+
+Value AccessorNode::Execute(Scope* scope, Err* err) const {
+ Value index_value = index_->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ if (!index_value.VerifyTypeIs(Value::INTEGER, err))
+ return Value();
+
+ const Value* base_value = scope->GetValue(base_.value(), true);
+ if (!base_value) {
+ *err = MakeErrorDescribing("Undefined identifier.");
+ return Value();
+ }
+ if (!base_value->VerifyTypeIs(Value::LIST, err))
+ return Value();
+
+ int64 index_int = index_value.int_value();
+ if (index_int < 0) {
+ *err = Err(index_->GetRange(), "Negative array subscript.",
+ "You gave me " + base::Int64ToString(index_int) + ".");
+ return Value();
+ }
+ size_t index_sizet = static_cast<size_t>(index_int);
+ if (index_sizet >= base_value->list_value().size()) {
+ *err = Err(index_->GetRange(), "Array subscript out of range.",
+ "You gave me " + base::Int64ToString(index_int) +
+ " but I was expecting something from 0 to " +
+ base::Int64ToString(
+ static_cast<int64>(base_value->list_value().size()) - 1) +
+ ", inclusive.");
+ return Value();
+ }
+
+ // Doing this assumes that there's no way in the language to do anything
+ // between the time the reference is created and the time that the reference
+ // is used. If there is, this will crash! Currently, this is just used for
+ // array accesses where this "shouldn't" happen.
+ return base_value->list_value()[index_sizet];
+}
+
+LocationRange AccessorNode::GetRange() const {
+ return LocationRange(base_.location(), index_->GetRange().end());
+}
+
+Err AccessorNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(GetRange(), msg, help);
+}
+
+void AccessorNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "ACCESSOR\n";
+ out << IndentFor(indent + 1) << base_.value() << "\n";
+ index_->Print(out, indent + 1);
+}
+
+// BinaryOpNode ---------------------------------------------------------------
+
+BinaryOpNode::BinaryOpNode() {
+}
+
+BinaryOpNode::~BinaryOpNode() {
+}
+
+const BinaryOpNode* BinaryOpNode::AsBinaryOp() const {
+ return this;
+}
+
+Value BinaryOpNode::Execute(Scope* scope, Err* err) const {
+ return ExecuteBinaryOperator(scope, this, left_.get(), right_.get(), err);
+}
+
+LocationRange BinaryOpNode::GetRange() const {
+ return left_->GetRange().Union(right_->GetRange());
+}
+
+Err BinaryOpNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(op_, msg, help);
+}
+
+void BinaryOpNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "BINARY(" << op_.value() << ")\n";
+ left_->Print(out, indent + 1);
+ right_->Print(out, indent + 1);
+}
+
+// BlockNode ------------------------------------------------------------------
+
+BlockNode::BlockNode(bool has_scope)
+ : has_scope_(has_scope),
+ begin_token_(NULL),
+ end_token_(NULL) {
+}
+
+BlockNode::~BlockNode() {
+ STLDeleteContainerPointers(statements_.begin(), statements_.end());
+}
+
+const BlockNode* BlockNode::AsBlock() const {
+ return this;
+}
+
+Value BlockNode::Execute(Scope* containing_scope, Err* err) const {
+ if (has_scope_) {
+ Scope our_scope(containing_scope);
+ Value ret = ExecuteBlockInScope(&our_scope, err);
+ if (err->has_error())
+ return Value();
+
+ // Check for unused vars in the scope.
+ //our_scope.CheckForUnusedVars(err);
+ return ret;
+ }
+ return ExecuteBlockInScope(containing_scope, err);
+}
+
+LocationRange BlockNode::GetRange() const {
+ if (begin_token_ && end_token_) {
+ return begin_token_->range().Union(end_token_->range());
+ }
+ return LocationRange(); // TODO(brettw) indicate the entire file somehow.
+}
+
+Err BlockNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ if (begin_token_)
+ return Err(*begin_token_, msg, help);
+ // TODO(brettw) this should have the beginning of the file in it or something.
+ return Err(Location(NULL, 1, 1), msg, help);
+}
+
+void BlockNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "BLOCK\n";
+ for (size_t i = 0; i < statements_.size(); i++)
+ statements_[i]->Print(out, indent + 1);
+}
+
+Value BlockNode::ExecuteBlockInScope(Scope* our_scope, Err* err) const {
+ for (size_t i = 0; i < statements_.size() && !err->has_error(); i++) {
+ // Check for trying to execute things with no side effects in a block.
+ const ParseNode* cur = statements_[i];
+ if (cur->AsList() || cur->AsLiteral() || cur->AsUnaryOp() ||
+ cur->AsIdentifier()) {
+ *err = cur->MakeErrorDescribing(
+ "This statment has no effect.",
+ "Either delete it or do something with the result.");
+ return Value();
+ }
+ cur->Execute(our_scope, err);
+ }
+ return Value();
+}
+
+// ConditionNode --------------------------------------------------------------
+
+ConditionNode::ConditionNode() {
+}
+
+ConditionNode::~ConditionNode() {
+}
+
+const ConditionNode* ConditionNode::AsConditionNode() const {
+ return this;
+}
+
+Value ConditionNode::Execute(Scope* scope, Err* err) const {
+ Value condition_result = condition_->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ if (condition_result.type() == Value::NONE) {
+ *err = condition_->MakeErrorDescribing(
+ "This does not evaluate to a value.",
+ "Please give me something to work with for the if statement.");
+ err->AppendRange(if_token_.range());
+ return Value();
+ }
+
+ if (condition_result.InterpretAsInt()) {
+ if_true_->ExecuteBlockInScope(scope, err);
+ } else if (if_false_) {
+ // The else block is optional. It's either another condition (for an
+ // "else if" and we can just Execute it and the condition will handle
+ // the scoping) or it's a block indicating an "else" in which ase we
+ // need to be sure it inherits our scope.
+ const BlockNode* if_false_block = if_false_->AsBlock();
+ if (if_false_block)
+ if_false_block->ExecuteBlockInScope(scope, err);
+ else
+ if_false_->Execute(scope, err);
+ }
+
+ return Value();
+}
+
+LocationRange ConditionNode::GetRange() const {
+ if (if_false_)
+ return if_token_.range().Union(if_false_->GetRange());
+ return if_token_.range().Union(if_true_->GetRange());
+}
+
+Err ConditionNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(if_token_, msg, help);
+}
+
+void ConditionNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "CONDITION\n";
+ condition_->Print(out, indent + 1);
+ if_true_->Print(out, indent + 1);
+ if (if_false_)
+ if_false_->Print(out, indent + 1);
+}
+
+// FunctionCallNode -----------------------------------------------------------
+
+FunctionCallNode::FunctionCallNode() {
+}
+
+FunctionCallNode::~FunctionCallNode() {
+}
+
+const FunctionCallNode* FunctionCallNode::AsFunctionCall() const {
+ return this;
+}
+
+Value FunctionCallNode::Execute(Scope* scope, Err* err) const {
+ Value args = args_->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ return ExecuteFunction(scope, this, args.list_value(), block_.get(), err);
+}
+
+LocationRange FunctionCallNode::GetRange() const {
+ if (block_)
+ return function_.range().Union(block_->GetRange());
+ return function_.range().Union(args_->GetRange());
+}
+
+Err FunctionCallNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(function_, msg, help);
+}
+
+void FunctionCallNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "FUNCTION(" << function_.value() << ")\n";
+ args_->Print(out, indent + 1);
+ if (block_)
+ block_->Print(out, indent + 1);
+}
+
+// IdentifierNode --------------------------------------------------------------
+
+IdentifierNode::IdentifierNode() {
+}
+
+IdentifierNode::IdentifierNode(const Token& token) : value_(token) {
+}
+
+IdentifierNode::~IdentifierNode() {
+}
+
+const IdentifierNode* IdentifierNode::AsIdentifier() const {
+ return this;
+}
+
+Value IdentifierNode::Execute(Scope* scope, Err* err) const {
+ const Value* result = scope->GetValue(value_.value(), true);
+ if (!result) {
+ *err = MakeErrorDescribing("Undefined identifier");
+ return Value();
+ }
+ return *result;
+}
+
+LocationRange IdentifierNode::GetRange() const {
+ return value_.range();
+}
+
+Err IdentifierNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(value_, msg, help);
+}
+
+void IdentifierNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "IDENTIFIER(" << value_.value() << ")\n";
+}
+
+// ListNode -------------------------------------------------------------------
+
+ListNode::ListNode() {
+}
+
+ListNode::~ListNode() {
+ STLDeleteContainerPointers(contents_.begin(), contents_.end());
+}
+
+const ListNode* ListNode::AsList() const {
+ return this;
+}
+
+Value ListNode::Execute(Scope* scope, Err* err) const {
+ Value result_value(this, Value::LIST);
+ std::vector<Value>& results = result_value.list_value();
+ results.resize(contents_.size());
+
+ for (size_t i = 0; i < contents_.size(); i++) {
+ const ParseNode* cur = contents_[i];
+ results[i] = cur->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ if (results[i].type() == Value::NONE) {
+ *err = cur->MakeErrorDescribing(
+ "This does not evaluate to a value.",
+ "I can't do something with nothing.");
+ return Value();
+ }
+ }
+ return result_value;
+}
+
+LocationRange ListNode::GetRange() const {
+ return LocationRange(begin_token_.location(), end_token_.location());
+}
+
+Err ListNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(begin_token_, msg, help);
+}
+
+void ListNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "LIST\n";
+ for (size_t i = 0; i < contents_.size(); i++)
+ contents_[i]->Print(out, indent + 1);
+}
+
+// LiteralNode -----------------------------------------------------------------
+
+LiteralNode::LiteralNode() {
+}
+
+LiteralNode::LiteralNode(const Token& token) : value_(token) {
+}
+
+LiteralNode::~LiteralNode() {
+}
+
+const LiteralNode* LiteralNode::AsLiteral() const {
+ return this;
+}
+
+Value LiteralNode::Execute(Scope* scope, Err* err) const {
+ switch (value_.type()) {
+ case Token::INTEGER: {
+ int64 result_int;
+ if (!base::StringToInt64(value_.value(), &result_int)) {
+ *err = MakeErrorDescribing("This does not look like an integer");
+ return Value();
+ }
+ return Value(this, result_int);
+ }
+ case Token::STRING: {
+ // TODO(brettw) Unescaping probably needs to be moved & improved.
+ // The input value includes the quotes around the string, strip those
+ // off and unescape.
+ Value v(this, Value::STRING);
+ ExpandStringLiteral(scope, value_, &v, err);
+ return v;
+ }
+ default:
+ NOTREACHED();
+ return Value();
+ }
+}
+
+LocationRange LiteralNode::GetRange() const {
+ return value_.range();
+}
+
+Err LiteralNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(value_, msg, help);
+}
+
+void LiteralNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "LITERAL(" << value_.value() << ")\n";
+}
+
+// UnaryOpNode ----------------------------------------------------------------
+
+UnaryOpNode::UnaryOpNode() {
+}
+
+UnaryOpNode::~UnaryOpNode() {
+}
+
+const UnaryOpNode* UnaryOpNode::AsUnaryOp() const {
+ return this;
+}
+
+Value UnaryOpNode::Execute(Scope* scope, Err* err) const {
+ Value operand_value = operand_->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ return ExecuteUnaryOperator(scope, this, operand_value, err);
+}
+
+LocationRange UnaryOpNode::GetRange() const {
+ return op_.range().Union(operand_->GetRange());
+}
+
+Err UnaryOpNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(op_, msg, help);
+}
+
+void UnaryOpNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "UNARY(" << op_.value() << ")\n";
+ operand_->Print(out, indent + 1);
+}
diff --git a/tools/gn/parse_tree.h b/tools/gn/parse_tree.h
new file mode 100644
index 0000000..09646e5
--- /dev/null
+++ b/tools/gn/parse_tree.h
@@ -0,0 +1,366 @@
+// 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.
+
+#ifndef TOOLS_GN_PARSE_TREE_H_
+#define TOOLS_GN_PARSE_TREE_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "tools/gn/err.h"
+#include "tools/gn/token.h"
+#include "tools/gn/value.h"
+
+class AccessorNode;
+class BinaryOpNode;
+class BlockNode;
+class ConditionNode;
+class FunctionCallNode;
+class IdentifierNode;
+class ListNode;
+class LiteralNode;
+class Scope;
+class UnaryOpNode;
+
+// ParseNode -------------------------------------------------------------------
+
+// A node in the AST.
+class ParseNode {
+ public:
+ ParseNode();
+ virtual ~ParseNode();
+
+ virtual const AccessorNode* AsAccessor() const;
+ virtual const BinaryOpNode* AsBinaryOp() const;
+ virtual const BlockNode* AsBlock() const;
+ virtual const ConditionNode* AsConditionNode() const;
+ virtual const FunctionCallNode* AsFunctionCall() const;
+ virtual const IdentifierNode* AsIdentifier() const;
+ virtual const ListNode* AsList() const;
+ virtual const LiteralNode* AsLiteral() const;
+ virtual const UnaryOpNode* AsUnaryOp() const;
+
+ virtual Value Execute(Scope* scope, Err* err) const = 0;
+
+ virtual LocationRange GetRange() const = 0;
+
+ // Returns an error with the given messages and the range set to something
+ // that indicates this node.
+ virtual Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const = 0;
+
+ // Prints a representation of this node to the given string, indenting
+ // by the given number of spaces.
+ virtual void Print(std::ostream& out, int indent) const = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ParseNode);
+};
+
+// AccessorNode ----------------------------------------------------------------
+
+// Access an array element.
+//
+// If we need to add support for member variables like "variable.len" I was
+// thinking this would also handle that case.
+class AccessorNode : public ParseNode {
+ public:
+ AccessorNode();
+ virtual ~AccessorNode();
+
+ virtual const AccessorNode* AsAccessor() const OVERRIDE;
+ virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+ virtual LocationRange GetRange() const OVERRIDE;
+ virtual Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const OVERRIDE;
+ virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+ // Base is the thing on the left of the [], currently always required to be
+ // an identifier token.
+ const Token& base() const { return base_; }
+ void set_base(const Token& b) { base_ = b; }
+
+ // Index is the expression inside the [].
+ const ParseNode* index() const { return index_.get(); }
+ void set_index(scoped_ptr<ParseNode> i) { index_ = i.Pass(); }
+
+ private:
+ Token base_;
+ scoped_ptr<ParseNode> index_;
+
+ DISALLOW_COPY_AND_ASSIGN(AccessorNode);
+};
+
+// BinaryOpNode ----------------------------------------------------------------
+
+class BinaryOpNode : public ParseNode {
+ public:
+ BinaryOpNode();
+ virtual ~BinaryOpNode();
+
+ virtual const BinaryOpNode* AsBinaryOp() const OVERRIDE;
+ virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+ virtual LocationRange GetRange() const OVERRIDE;
+ virtual Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const OVERRIDE;
+ virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+ const Token& op() const { return op_; }
+ void set_op(const Token& t) { op_ = t; }
+
+ const ParseNode* left() const { return left_.get(); }
+ void set_left(scoped_ptr<ParseNode> left) {
+ left_ = left.Pass();
+ }
+
+ const ParseNode* right() const { return right_.get(); }
+ void set_right(scoped_ptr<ParseNode> right) {
+ right_ = right.Pass();
+ }
+
+ private:
+ scoped_ptr<ParseNode> left_;
+ Token op_;
+ scoped_ptr<ParseNode> right_;
+
+ DISALLOW_COPY_AND_ASSIGN(BinaryOpNode);
+};
+
+// BlockNode -------------------------------------------------------------------
+
+class BlockNode : public ParseNode {
+ public:
+ // Set has_scope if this block introduces a nested scope.
+ BlockNode(bool has_scope);
+ virtual ~BlockNode();
+
+ virtual const BlockNode* AsBlock() const OVERRIDE;
+ virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+ virtual LocationRange GetRange() const OVERRIDE;
+ virtual Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const OVERRIDE;
+ virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+ void set_begin_token(const Token* t) { begin_token_ = t; }
+ void set_end_token(const Token* t) { end_token_ = t; }
+
+ const std::vector<ParseNode*>& statements() const { return statements_; }
+ void append_statement(scoped_ptr<ParseNode> s) {
+ statements_.push_back(s.release());
+ }
+
+ // Doesn't create a nested scope.
+ Value ExecuteBlockInScope(Scope* our_scope, Err* err) const;
+
+ private:
+ bool has_scope_;
+
+ // Tokens corresponding to { and }, if any (may be NULL).
+ const Token* begin_token_;
+ const Token* end_token_;
+
+ // Owning pointers, use unique_ptr when we can use C++11.
+ std::vector<ParseNode*> statements_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlockNode);
+};
+
+// ConditionNode ---------------------------------------------------------------
+
+class ConditionNode : public ParseNode {
+ public:
+ ConditionNode();
+ virtual ~ConditionNode();
+
+ virtual const ConditionNode* AsConditionNode() const OVERRIDE;
+ virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+ virtual LocationRange GetRange() const OVERRIDE;
+ virtual Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const OVERRIDE;
+ virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+ void set_if_token(const Token& token) { if_token_ = token; }
+
+ const ParseNode* condition() const { return condition_.get(); }
+ void set_condition(scoped_ptr<ParseNode> c) {
+ condition_ = c.Pass();
+ }
+
+ const BlockNode* if_true() const { return if_true_.get(); }
+ void set_if_true(scoped_ptr<BlockNode> t) {
+ if_true_ = t.Pass();
+ }
+
+ // This is either empty, a block (for the else clause), or another
+ // condition.
+ const ParseNode* if_false() const { return if_false_.get(); }
+ void set_if_false(scoped_ptr<ParseNode> f) {
+ if_false_ = f.Pass();
+ }
+
+ private:
+ // Token corresponding to the "if" string.
+ Token if_token_;
+
+ scoped_ptr<ParseNode> condition_; // Always non-null.
+ scoped_ptr<BlockNode> if_true_; // Always non-null.
+ scoped_ptr<ParseNode> if_false_; // May be null.
+
+ DISALLOW_COPY_AND_ASSIGN(ConditionNode);
+};
+
+// FunctionCallNode ------------------------------------------------------------
+
+class FunctionCallNode : public ParseNode {
+ public:
+ FunctionCallNode();
+ virtual ~FunctionCallNode();
+
+ virtual const FunctionCallNode* AsFunctionCall() const OVERRIDE;
+ virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+ virtual LocationRange GetRange() const OVERRIDE;
+ virtual Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const OVERRIDE;
+ virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+ const Token& function() const { return function_; }
+ void set_function(Token t) { function_ = t; }
+
+ const ListNode* args() const { return args_.get(); }
+ void set_args(scoped_ptr<ListNode> a) { args_ = a.Pass(); }
+
+ const BlockNode* block() const { return block_.get(); }
+ void set_block(scoped_ptr<BlockNode> b) { block_ = b.Pass(); }
+
+ private:
+ Token function_;
+ scoped_ptr<ListNode> args_;
+ scoped_ptr<BlockNode> block_; // May be null.
+
+ DISALLOW_COPY_AND_ASSIGN(FunctionCallNode);
+};
+
+// IdentifierNode --------------------------------------------------------------
+
+class IdentifierNode : public ParseNode {
+ public:
+ IdentifierNode();
+ IdentifierNode(const Token& token);
+ virtual ~IdentifierNode();
+
+ virtual const IdentifierNode* AsIdentifier() const OVERRIDE;
+ virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+ virtual LocationRange GetRange() const OVERRIDE;
+ virtual Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const OVERRIDE;
+ virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+ const Token& value() const { return value_; }
+ void set_value(const Token& t) { value_ = t; }
+
+ private:
+ Token value_;
+
+ DISALLOW_COPY_AND_ASSIGN(IdentifierNode);
+};
+
+// ListNode --------------------------------------------------------------------
+
+class ListNode : public ParseNode {
+ public:
+ ListNode();
+ virtual ~ListNode();
+
+ virtual const ListNode* AsList() const OVERRIDE;
+ virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+ virtual LocationRange GetRange() const OVERRIDE;
+ virtual Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const OVERRIDE;
+ virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+ void set_begin_token(const Token& t) { begin_token_ = t; }
+ void set_end_token(const Token& t) { end_token_ = t; }
+
+ void append_item(scoped_ptr<ParseNode> s) {
+ contents_.push_back(s.release());
+ }
+ const std::vector<ParseNode*>& contents() const { return contents_; }
+
+ private:
+ // Tokens corresponding to the [ and ].
+ Token begin_token_;
+ Token end_token_;
+
+ // Owning pointers, use unique_ptr when we can use C++11.
+ std::vector<ParseNode*> contents_;
+
+ DISALLOW_COPY_AND_ASSIGN(ListNode);
+};
+
+// LiteralNode -----------------------------------------------------------------
+
+class LiteralNode : public ParseNode {
+ public:
+ LiteralNode();
+ LiteralNode(const Token& token);
+ virtual ~LiteralNode();
+
+ virtual const LiteralNode* AsLiteral() const OVERRIDE;
+ virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+ virtual LocationRange GetRange() const OVERRIDE;
+ virtual Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const OVERRIDE;
+ virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+ const Token& value() const { return value_; }
+ void set_value(const Token& t) { value_ = t; }
+
+ private:
+ Token value_;
+
+ DISALLOW_COPY_AND_ASSIGN(LiteralNode);
+};
+
+// UnaryOpNode -----------------------------------------------------------------
+
+class UnaryOpNode : public ParseNode {
+ public:
+ UnaryOpNode();
+ virtual ~UnaryOpNode();
+
+ virtual const UnaryOpNode* AsUnaryOp() const OVERRIDE;
+ virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+ virtual LocationRange GetRange() const OVERRIDE;
+ virtual Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const OVERRIDE;
+ virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+ const Token& op() const { return op_; }
+ void set_op(const Token& t) { op_ = t; }
+
+ const ParseNode* operand() const { return operand_.get(); }
+ void set_operand(scoped_ptr<ParseNode> operand) {
+ operand_ = operand.Pass();
+ }
+
+ private:
+ Token op_;
+ scoped_ptr<ParseNode> operand_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnaryOpNode);
+};
+
+#endif // TOOLS_GN_PARSE_TREE_H_
diff --git a/tools/gn/parser.cc b/tools/gn/parser.cc
new file mode 100644
index 0000000..385aa34
--- /dev/null
+++ b/tools/gn/parser.cc
@@ -0,0 +1,470 @@
+// 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/parser.h"
+
+#include "base/logging.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/operators.h"
+#include "tools/gn/token.h"
+
+namespace {
+
+// Returns true if the two tokens are on the same line. We assume they're in
+// the same file.
+bool IsSameLine(const Token& a, const Token& b) {
+ DCHECK(a.location().file() == b.location().file());
+ return a.location().line_number() == b.location().line_number();
+}
+
+} // namespace
+
+Parser::Parser(const std::vector<Token>& tokens, Err* err)
+ : tokens_(tokens),
+ err_(err),
+ cur_(0) {
+}
+
+Parser::~Parser() {
+}
+
+// static
+scoped_ptr<ParseNode> Parser::Parse(const std::vector<Token>& tokens,
+ Err* err) {
+ Parser p(tokens, err);
+ return p.ParseBlock(false).PassAs<ParseNode>();
+}
+
+// static
+scoped_ptr<ParseNode> Parser::ParseExpression(const std::vector<Token>& tokens,
+ Err* err) {
+ Parser p(tokens, err);
+ return p.ParseExpression().Pass();
+}
+
+bool Parser::IsToken(Token::Type type, char* str) const {
+ if (at_end())
+ return false;
+ return cur_token().type() == type || cur_token().value() == str;
+}
+
+scoped_ptr<AccessorNode> Parser::ParseAccessor() {
+ scoped_ptr<AccessorNode> accessor(new AccessorNode);
+
+ DCHECK(cur_token().type() == Token::IDENTIFIER);
+ accessor->set_base(cur_token());
+ cur_++; // Skip identifier.
+ cur_++; // Skip "[" (we know this exists because the existance of this
+ // token is how the caller knows it's an accessor.
+
+ if (at_end()) {
+ *err_ = MakeEOFError("Got EOF when looking for list index.");
+ return scoped_ptr<AccessorNode>();
+ }
+
+ // Get the expression.
+ scoped_ptr<ParseNode> expr = ParseExpression().Pass();
+ if (has_error())
+ return scoped_ptr<AccessorNode>();
+ if (at_end()) {
+ *err_ = MakeEOFError("Got EOF when looking for list accessor ]");
+ return scoped_ptr<AccessorNode>();
+ }
+ accessor->set_index(expr.Pass());
+
+ // Skip over "]"
+ if (!cur_token().IsScoperEqualTo("]")) {
+ *err_ = Err(cur_token(), "Expecting ]",
+ "You started a list access but didn't terminate it, and instead "
+ "I fould this\nstupid thing.");
+ return scoped_ptr<AccessorNode>();
+ }
+ cur_++;
+
+ return accessor.Pass();
+}
+
+// Blocks at the file scope don't need {} so we have the option to ignore
+// them. When need_braces is set, we'll expect a begin an end brace.
+//
+// block := "{" block_contents "}"
+// block_contents := (expression | conditional | block)*
+scoped_ptr<BlockNode> Parser::ParseBlock(bool need_braces) {
+ scoped_ptr<BlockNode> block(new BlockNode(true));
+
+ // Eat initial { if necessary.
+ const Token* opening_curly_brace;
+ if (need_braces) {
+ if (at_end()) {
+ *err_ = MakeEOFError("Got EOF when looking for { for block.",
+ "It should have been after here.");
+ return scoped_ptr<BlockNode>();
+ } else if(!IsScopeBeginScoper(cur_token())) {
+ *err_ = Err(cur_token(), "Expecting { instead of this thing.",
+ "THOU SHALT USE CURLY BRACES FOR ALL BLOCKS.");
+ return scoped_ptr<BlockNode>();
+ }
+ opening_curly_brace = &cur_token();
+ block->set_begin_token(opening_curly_brace);
+ cur_++;
+ }
+
+ // Loop until EOF or end brace found.
+ while (!at_end() && !IsScopeEndScoper(cur_token())) {
+ if (cur_token().IsIdentifierEqualTo("if")) {
+ // Conditional.
+ block->append_statement(ParseCondition().PassAs<ParseNode>());
+ } else if (IsScopeBeginScoper(cur_token())) {
+ // Nested block.
+ block->append_statement(ParseBlock(true).PassAs<ParseNode>());
+ } else {
+ // Everything else is an expression.
+ block->append_statement(ParseExpression().PassAs<ParseNode>());
+ }
+ if (has_error())
+ return scoped_ptr<BlockNode>();
+ }
+
+ // Eat the ending "}" if necessary.
+ if (need_braces) {
+ if (at_end() || !IsScopeEndScoper(cur_token())) {
+ *err_ = Err(*opening_curly_brace, "Expecting }",
+ "I ran headlong into the end of the file looking for the "
+ "closing brace\ncorresponding to this one.");
+ return scoped_ptr<BlockNode>();
+ }
+ block->set_end_token(&cur_token());
+ cur_++; // Skip past "}".
+ }
+
+ return block.Pass();
+}
+
+// conditional := "if (" expression ")" block [else_conditional]
+// else_conditional := ("else" block) | ("else" conditional)
+scoped_ptr<ConditionNode> Parser::ParseCondition() {
+ scoped_ptr<ConditionNode> cond(new ConditionNode);
+
+ // Skip past "if".
+ const Token& if_token = cur_token();
+ cond->set_if_token(if_token);
+ DCHECK(if_token.IsIdentifierEqualTo("if"));
+ cur_++;
+
+ if (at_end() || !IsFunctionCallArgBeginScoper(cur_token())) {
+ *err_ = Err(if_token, "Expecting \"(\" after \"if\"",
+ "Did you think this was Python or something?");
+ return scoped_ptr<ConditionNode>();
+ }
+
+ // Skip over (.
+ const Token& open_paren_token = cur_token();
+ cur_++;
+ if (at_end()) {
+ *err_ = Err(if_token, "Unexpected EOF inside if condition");
+ return scoped_ptr<ConditionNode>();
+ }
+
+ // Condition inside ().
+ cond->set_condition(ParseExpression().Pass());
+ if (has_error())
+ return scoped_ptr<ConditionNode>();
+
+ if (at_end() || !IsFunctionCallArgEndScoper(cur_token())) {
+ *err_ = Err(open_paren_token, "Expecting \")\" for \"if\" condition",
+ "You didn't finish the thought you started here.");
+ return scoped_ptr<ConditionNode>();
+ }
+ cur_++; // Skip over )
+
+ // Contents of {}.
+ cond->set_if_true(ParseBlock(true).Pass());
+ if (has_error())
+ return scoped_ptr<ConditionNode>();
+
+ // Optional "else" at the end.
+ if (!at_end() && cur_token().IsIdentifierEqualTo("else")) {
+ cur_++;
+
+ // The else may be followed by an if or a block.
+ if (at_end()) {
+ *err_ = MakeEOFError("Ran into end of file after \"else\".",
+ "else, WHAT?!?!?");
+ return scoped_ptr<ConditionNode>();
+ }
+ if (cur_token().IsIdentifierEqualTo("if")) {
+ // "else if() {"
+ cond->set_if_false(ParseCondition().PassAs<ParseNode>());
+ } else if (IsScopeBeginScoper(cur_token())) {
+ // "else {"
+ cond->set_if_false(ParseBlock(true).PassAs<ParseNode>());
+ } else {
+ // else <anything else>
+ *err_ = Err(cur_token(), "Expected \"if\" or \"{\" after \"else\".",
+ "This is neither of those things.");
+ return scoped_ptr<ConditionNode>();
+ }
+ }
+
+ if (has_error())
+ return scoped_ptr<ConditionNode>();
+ return cond.Pass();
+}
+
+// expression := paren_expression | accessor | identifier | literal |
+// funccall | unary_expression | binary_expression
+//
+// accessor := identifier <non-newline-whitespace>* "[" expression "]"
+//
+// The "non-newline-whitespace is used to differentiate between this case:
+// a[1]
+// and this one:
+// a
+// [1]
+// The second one is kind of stupid (since it does nothing with the values)
+// but is still legal.
+scoped_ptr<ParseNode> Parser::ParseExpression() {
+ scoped_ptr<ParseNode> expr = ParseExpressionExceptBinaryOperators();
+ if (has_error())
+ return scoped_ptr<ParseNode>();
+
+ // That may have hit EOF, in which case we can't have any binary operators.
+ if (at_end())
+ return expr.Pass();
+
+ // TODO(brettw) handle operator precidence!
+ // Gobble up all subsequent expressions as long as there are binary
+ // operators.
+
+ if (IsBinaryOperator(cur_token())) {
+ scoped_ptr<BinaryOpNode> binary_op(new BinaryOpNode);
+ binary_op->set_left(expr.Pass());
+ const Token& operator_token = cur_token();
+ binary_op->set_op(operator_token);
+ cur_++;
+ if (at_end()) {
+ *err_ = Err(operator_token, "Unexpected EOF in expression.",
+ "I was looking for the right-hand-side of this operator.");
+ return scoped_ptr<ParseNode>();
+ }
+ binary_op->set_right(ParseExpression().Pass());
+ if (has_error())
+ return scoped_ptr<ParseNode>();
+ return binary_op.PassAs<ParseNode>();
+ }
+
+ return expr.Pass();
+}
+
+
+// This internal one does not handle binary operators, since it requires
+// looking at the "next" thing. The regular ParseExpression above handles it.
+scoped_ptr<ParseNode> Parser::ParseExpressionExceptBinaryOperators() {
+ if (at_end())
+ return scoped_ptr<ParseNode>();
+
+ const Token& token = cur_token();
+
+ // Unary expression.
+ if (IsUnaryOperator(token))
+ return ParseUnaryOp().PassAs<ParseNode>();
+
+ // Parenthesized expressions.
+ if (token.IsScoperEqualTo("("))
+ return ParseParenExpression();
+
+ // Function calls.
+ if (token.type() == Token::IDENTIFIER) {
+ if (has_next_token() && IsFunctionCallArgBeginScoper(next_token()))
+ return ParseFunctionCall().PassAs<ParseNode>();
+ }
+
+ // Lists.
+ if (token.IsScoperEqualTo("[")) {
+ return ParseList(Token(Location(), Token::SCOPER, "["),
+ Token(Location(), Token::SCOPER, "]")).PassAs<ParseNode>();
+ }
+
+ // Literals.
+ if (token.type() == Token::STRING || token.type() == Token::INTEGER) {
+ cur_++;
+ return scoped_ptr<ParseNode>(new LiteralNode(token));
+ }
+
+ // Accessors.
+ if (token.type() == Token::IDENTIFIER &&
+ has_next_token() && next_token().IsScoperEqualTo("[") &&
+ IsSameLine(token, next_token())) {
+ return ParseAccessor().PassAs<ParseNode>();
+ }
+
+ // Identifiers.
+ if (token.type() == Token::IDENTIFIER) {
+ cur_++;
+ return scoped_ptr<ParseNode>(new IdentifierNode(token));
+ }
+
+ // Handle errors.
+ if (token.type() == Token::SEPARATOR) {
+ *err_ = Err(token, "Unexpected comma.",
+ "You can't put a comma here, it must be in list separating "
+ "complete\nthoughts.");
+ } else if (IsScopeBeginScoper(token)) {
+ *err_ = Err(token, "Unexpected token.",
+ "You can't put a \"{\" scope here, it must be in a block.");
+ } else {
+ *err_ = Err(token, "Unexpected token.",
+ "I was really hoping for something else here and you let me down.");
+ }
+ return scoped_ptr<ParseNode>();
+}
+
+// function_call := identifier "(" list_contents ")"
+// [<non-newline-whitespace>* block]
+scoped_ptr<FunctionCallNode> Parser::ParseFunctionCall() {
+ scoped_ptr<FunctionCallNode> func(new FunctionCallNode);
+
+ const Token& function_token = cur_token();
+ func->set_function(function_token);
+
+ // This function should only get called when we know we have a function,
+ // which only happens when there is a paren following the name. Skip past it.
+ DCHECK(has_next_token());
+ cur_++; // Skip past function name to (.
+ const Token& open_paren_token = cur_token();
+ DCHECK(IsFunctionCallArgBeginScoper(open_paren_token));
+
+ if (at_end()) {
+ *err_ = Err(open_paren_token, "Unexpected EOF for function call.",
+ "You didn't finish the thought you started here.");
+ return scoped_ptr<FunctionCallNode>();
+ }
+
+ // Arguments.
+ func->set_args(ParseList(Token(Location(), Token::SCOPER, "("),
+ Token(Location(), Token::SCOPER, ")")));
+ if (has_error())
+ return scoped_ptr<FunctionCallNode>();
+
+ // Optional {} after function call for certain functions. The "{" must be on
+ // the same line as the ")" to disambiguate the case of a function followed
+ // by a random block just used for scoping purposes.
+ if (!at_end() && IsScopeBeginScoper(cur_token())) {
+ const Token& args_end_token = tokens_[cur_ - 1];
+ DCHECK(args_end_token.IsScoperEqualTo(")"));
+ if (IsSameLine(args_end_token, cur_token()))
+ func->set_block(ParseBlock(true).Pass());
+ }
+
+ if (has_error())
+ return scoped_ptr<FunctionCallNode>();
+ return func.Pass();
+}
+
+// list := "[" expression* "]"
+// list_contents := [(expression ",")* expression [","]]
+//
+// The list_contents is also used in function calls surrounded by parens, so
+// this function takes the tokens that are expected to surround the list.
+scoped_ptr<ListNode> Parser::ParseList(const Token& expected_begin,
+ const Token& expected_end) {
+ scoped_ptr<ListNode> list(new ListNode);
+
+ const Token& open_bracket_token = cur_token();
+ list->set_begin_token(open_bracket_token);
+ cur_++; // Skip "[" or "(".
+
+ bool need_separator = false;
+ while(true) {
+ if (at_end()) {
+ *err_ = Err(open_bracket_token, "EOF found when parsing list.",
+ "I expected a \"" + expected_end.value().as_string() +
+ "\" corresponding to this one.");
+ return scoped_ptr<ListNode>();
+ }
+ if (cur_token().type() == expected_end.type() &&
+ cur_token().value() == expected_end.value()) {
+ list->set_end_token(cur_token());
+ cur_++;
+ break;
+ }
+
+ if (need_separator) {
+ DCHECK(!list->contents().empty());
+ LocationRange prev_item_range =
+ list->contents().at(list->contents().size() - 1)->GetRange();
+ *err_ = Err(prev_item_range.end(),
+ "Need comma separating items in list.",
+ "You probably need a comma after this thingy.");
+ err_->AppendRange(prev_item_range);
+ return scoped_ptr<ListNode>();
+ }
+ scoped_ptr<ParseNode> expr = ParseExpression().Pass();
+ if (has_error())
+ return scoped_ptr<ListNode>();
+ list->append_item(expr.Pass());
+
+ need_separator = true;
+ if (!at_end()) {
+ // Skip over the separator, marking that we found it.
+ if (cur_token().type() == Token::SEPARATOR) {
+ cur_++;
+ need_separator = false;
+ }
+ }
+ }
+ return list.Pass();
+}
+
+// paren_expression := "(" expression ")"
+scoped_ptr<ParseNode> Parser::ParseParenExpression() {
+ const Token& open_paren_token = cur_token();
+ cur_++; // Skip over (
+
+ scoped_ptr<ParseNode> ret = ParseExpression();
+ if (has_error())
+ return scoped_ptr<ParseNode>();
+
+ if (at_end()) {
+ *err_ = Err(open_paren_token, "EOF found when parsing expression.",
+ "I was looking for a \")\" corresponding to this one.");
+ return scoped_ptr<ParseNode>();
+ }
+ if (!cur_token().IsScoperEqualTo(")")) {
+ *err_ = Err(open_paren_token, "Expected \")\" for expression",
+ "I was looking for a \")\" corresponding to this one.");
+ return scoped_ptr<ParseNode>();
+ }
+ cur_++; // Skip over )
+ return ret.Pass();
+}
+
+// unary_expression := "!" expression
+scoped_ptr<UnaryOpNode> Parser::ParseUnaryOp() {
+ scoped_ptr<UnaryOpNode> unary(new UnaryOpNode);
+
+ DCHECK(!at_end() && IsUnaryOperator(cur_token()));
+ const Token& op_token = cur_token();
+ unary->set_op(op_token);
+ cur_++;
+
+ if (at_end()) {
+ *err_ = Err(op_token, "Expected expression.",
+ "This operator needs something to operate on.");
+ return scoped_ptr<UnaryOpNode>();
+ }
+ unary->set_operand(ParseExpression().Pass());
+ if (has_error())
+ return scoped_ptr<UnaryOpNode>();
+ return unary.Pass();
+}
+
+Err Parser::MakeEOFError(const std::string& message,
+ const std::string& help) const {
+ if (tokens_.empty())
+ return Err(Location(NULL, 1, 1), message, help);
+
+ const Token& last = tokens_[tokens_.size() - 1];
+ return Err(last, message, help);
+}
diff --git a/tools/gn/parser.h b/tools/gn/parser.h
new file mode 100644
index 0000000..252b801
--- /dev/null
+++ b/tools/gn/parser.h
@@ -0,0 +1,81 @@
+// 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.
+
+#ifndef TOOLS_GN_PARSER_H_
+#define TOOLS_GN_PARSER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "tools/gn/err.h"
+#include "tools/gn/parse_tree.h"
+
+// Parses a series of tokens. The resulting AST will refer to the tokens passed
+// to the input, so the tokens an the file data they refer to must outlive your
+// use of the ParseNode.
+class Parser {
+ public:
+ // Will return a null pointer and set the err on error.
+ static scoped_ptr<ParseNode> Parse(const std::vector<Token>& tokens,
+ Err* err);
+
+ // Alternative to parsing that assumes the input is an expression.
+ static scoped_ptr<ParseNode> ParseExpression(const std::vector<Token>& tokens,
+ Err* err);
+
+ private:
+ // Vector must be valid for lifetime of call.
+ Parser(const std::vector<Token>& tokens, Err* err);
+ ~Parser();
+
+ scoped_ptr<AccessorNode> ParseAccessor();
+ scoped_ptr<BlockNode> ParseBlock(bool need_braces);
+ scoped_ptr<ConditionNode> ParseCondition();
+ scoped_ptr<ParseNode> ParseExpression();
+ scoped_ptr<ParseNode> ParseExpressionExceptBinaryOperators();
+ scoped_ptr<FunctionCallNode> ParseFunctionCall();
+ scoped_ptr<ListNode> ParseList(const Token& expected_begin,
+ const Token& expected_end);
+ scoped_ptr<ParseNode> ParseParenExpression();
+ scoped_ptr<UnaryOpNode> ParseUnaryOp();
+
+ bool IsToken(Token::Type type, char* str) const;
+
+ // Gets an error corresponding to the last token. When we hit an EOF
+ // usually we've already gone beyond the end (or maybe there are no tokens)
+ // so there is some tricky logic to report this.
+ Err MakeEOFError(const std::string& message,
+ const std::string& help = std::string()) const;
+
+ const Token& cur_token() const { return tokens_[cur_]; }
+
+ bool done() const { return at_end() || has_error(); }
+ bool at_end() const { return cur_ >= tokens_.size(); }
+ bool has_error() const { return err_->has_error(); }
+
+ const Token& next_token() const { return tokens_[cur_ + 1]; }
+ bool has_next_token() const { return cur_ + 1 < tokens_.size(); }
+
+ const std::vector<Token>& tokens_;
+
+ Err* err_;
+
+ // Current index into the tokens.
+ size_t cur_;
+
+ FRIEND_TEST_ALL_PREFIXES(Parser, BinaryOp);
+ FRIEND_TEST_ALL_PREFIXES(Parser, Block);
+ FRIEND_TEST_ALL_PREFIXES(Parser, Condition);
+ FRIEND_TEST_ALL_PREFIXES(Parser, Expression);
+ FRIEND_TEST_ALL_PREFIXES(Parser, FunctionCall);
+ FRIEND_TEST_ALL_PREFIXES(Parser, List);
+ FRIEND_TEST_ALL_PREFIXES(Parser, ParenExpression);
+ FRIEND_TEST_ALL_PREFIXES(Parser, UnaryOp);
+
+ DISALLOW_COPY_AND_ASSIGN(Parser);
+};
+
+#endif // TOOLS_GN_PARSER_H_
diff --git a/tools/gn/parser_unittest.cc b/tools/gn/parser_unittest.cc
new file mode 100644
index 0000000..3fd8ebe
--- /dev/null
+++ b/tools/gn/parser_unittest.cc
@@ -0,0 +1,329 @@
+// 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 <iostream>
+#include <sstream>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/parser.h"
+#include "tools/gn/tokenizer.h"
+
+namespace {
+
+bool GetTokens(const InputFile* input, std::vector<Token>* result) {
+ result->clear();
+ Err err;
+ *result = Tokenizer::Tokenize(input, &err);
+ return !err.has_error();
+}
+
+bool IsIdentifierEqual(const ParseNode* node, const char* val) {
+ if (!node)
+ return false;
+ const IdentifierNode* ident = node->AsIdentifier();
+ if (!ident)
+ return false;
+ return ident->value().value() == val;
+}
+
+bool IsLiteralEqual(const ParseNode* node, const char* val) {
+ if (!node)
+ return false;
+ const LiteralNode* lit = node->AsLiteral();
+ if (!lit)
+ return false;
+ return lit->value().value() == val;
+}
+
+// Returns true if the given node as a simple assignment to a given value.
+bool IsAssignment(const ParseNode* node, const char* ident, const char* value) {
+ if (!node)
+ return false;
+ const BinaryOpNode* binary = node->AsBinaryOp();
+ if (!binary)
+ return false;
+ return binary->op().IsOperatorEqualTo("=") &&
+ IsIdentifierEqual(binary->left(), ident) &&
+ IsLiteralEqual(binary->right(), value);
+}
+
+// Returns true if the given node is a block with one assignment statement.
+bool IsBlockWithAssignment(const ParseNode* node,
+ const char* ident, const char* value) {
+ if (!node)
+ return false;
+ const BlockNode* block = node->AsBlock();
+ if (!block)
+ return false;
+ if (block->statements().size() != 1)
+ return false;
+ return IsAssignment(block->statements()[0], ident, value);
+}
+
+void DoParserPrintTest(const char* input, const char* expected) {
+ std::vector<Token> tokens;
+ InputFile input_file(SourceFile("/test"));
+ input_file.SetContents(input);
+ ASSERT_TRUE(GetTokens(&input_file, &tokens));
+
+ Err err;
+ scoped_ptr<ParseNode> result = Parser::Parse(tokens, &err);
+ ASSERT_TRUE(result);
+
+ std::ostringstream collector;
+ result->Print(collector, 0);
+
+ EXPECT_EQ(expected, collector.str());
+}
+
+// Expects the tokenizer or parser to identify an error at the given line and
+// character.
+void DoParserErrorTest(const char* input, int err_line, int err_char) {
+ InputFile input_file(SourceFile("/test"));
+ input_file.SetContents(input);
+
+ Err err;
+ std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
+ if (!err.has_error()) {
+ scoped_ptr<ParseNode> result = Parser::Parse(tokens, &err);
+ ASSERT_FALSE(result);
+ ASSERT_TRUE(err.has_error());
+ }
+
+ EXPECT_EQ(err_line, err.location().line_number());
+ EXPECT_EQ(err_char, err.location().char_offset());
+}
+
+} // namespace
+
+TEST(Parser, BinaryOp) {
+ std::vector<Token> tokens;
+
+ // Simple set expression.
+ InputFile expr_input(SourceFile("/test"));
+ expr_input.SetContents("a=2");
+ ASSERT_TRUE(GetTokens(&expr_input, &tokens));
+ Err err;
+ Parser set(tokens, &err);
+ scoped_ptr<ParseNode> expr = set.ParseExpression();
+ ASSERT_TRUE(expr);
+
+ const BinaryOpNode* binary_op = expr->AsBinaryOp();
+ ASSERT_TRUE(binary_op);
+
+ EXPECT_TRUE(binary_op->left()->AsIdentifier());
+
+ EXPECT_TRUE(binary_op->op().type() == Token::OPERATOR);
+ EXPECT_TRUE(binary_op->op().value() == "=");
+
+ EXPECT_TRUE(binary_op->right()->AsLiteral());
+}
+
+TEST(Parser, Condition) {
+ std::vector<Token> tokens;
+
+ InputFile cond_input(SourceFile("/test"));
+ cond_input.SetContents("if(1) { a = 2 }");
+ ASSERT_TRUE(GetTokens(&cond_input, &tokens));
+ Err err;
+ Parser simple_if(tokens, &err);
+ scoped_ptr<ConditionNode> cond = simple_if.ParseCondition();
+ ASSERT_TRUE(cond);
+
+ EXPECT_TRUE(IsLiteralEqual(cond->condition(), "1"));
+ EXPECT_FALSE(cond->if_false()); // No else block.
+ EXPECT_TRUE(IsBlockWithAssignment(cond->if_true(), "a", "2"));
+
+ // Now try a complicated if/else if/else one.
+ InputFile complex_if_input(SourceFile("/test"));
+ complex_if_input.SetContents(
+ "if(1) { a = 2 } else if (0) { a = 3 } else { a = 4 }");
+ ASSERT_TRUE(GetTokens(&complex_if_input, &tokens));
+ Parser complex_if(tokens, &err);
+ cond = complex_if.ParseCondition();
+ ASSERT_TRUE(cond);
+
+ EXPECT_TRUE(IsLiteralEqual(cond->condition(), "1"));
+ EXPECT_TRUE(IsBlockWithAssignment(cond->if_true(), "a", "2"));
+
+ ASSERT_TRUE(cond->if_false());
+ const ConditionNode* nested_cond = cond->if_false()->AsConditionNode();
+ ASSERT_TRUE(nested_cond);
+ EXPECT_TRUE(IsLiteralEqual(nested_cond->condition(), "0"));
+ EXPECT_TRUE(IsBlockWithAssignment(nested_cond->if_true(), "a", "3"));
+ EXPECT_TRUE(IsBlockWithAssignment(nested_cond->if_false(), "a", "4"));
+}
+
+TEST(Parser, FunctionCall) {
+ const char* input = "foo(a, 1, 2,) bar()";
+ const char* expected =
+ "BLOCK\n"
+ " FUNCTION(foo)\n"
+ " LIST\n"
+ " IDENTIFIER(a)\n"
+ " LITERAL(1)\n"
+ " LITERAL(2)\n"
+ " FUNCTION(bar)\n"
+ " LIST\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, ParenExpression) {
+ const char* input = "(foo(1)) + (a + b)";
+ const char* expected =
+ "BLOCK\n"
+ " BINARY(+)\n"
+ " FUNCTION(foo)\n"
+ " LIST\n"
+ " LITERAL(1)\n"
+ " BINARY(+)\n"
+ " IDENTIFIER(a)\n"
+ " IDENTIFIER(b)\n";
+ DoParserPrintTest(input, expected);
+ DoParserErrorTest("(a +", 1, 4);
+}
+
+TEST(Parser, UnaryOp) {
+ std::vector<Token> tokens;
+
+ InputFile ident_input(SourceFile("/test"));
+ ident_input.SetContents("!foo");
+ ASSERT_TRUE(GetTokens(&ident_input, &tokens));
+ Err err;
+ Parser ident(tokens, &err);
+ scoped_ptr<UnaryOpNode> op = ident.ParseUnaryOp();
+
+ ASSERT_TRUE(op);
+ EXPECT_TRUE(op->op().type() == Token::OPERATOR);
+ EXPECT_TRUE(op->op().value() == "!");
+}
+
+TEST(Parser, CompleteFunction) {
+ const char* input =
+ "cc_test(\"foo\") {\n"
+ " sources = [\n"
+ " \"foo.cc\",\n"
+ " \"foo.h\"\n"
+ " ]\n"
+ " dependencies = [\n"
+ " \"base\"\n"
+ " ]\n"
+ "}\n";
+ const char* expected =
+ "BLOCK\n"
+ " FUNCTION(cc_test)\n"
+ " LIST\n"
+ " LITERAL(\"foo\")\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(sources)\n"
+ " LIST\n"
+ " LITERAL(\"foo.cc\")\n"
+ " LITERAL(\"foo.h\")\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(dependencies)\n"
+ " LIST\n"
+ " LITERAL(\"base\")\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, FunctionWithConditional) {
+ const char* input =
+ "cc_test(\"foo\") {\n"
+ " sources = [\"foo.cc\"]\n"
+ " if (OS == \"mac\") {\n"
+ " sources += \"bar.cc\"\n"
+ " } else if (OS == \"win\") {\n"
+ " sources -= [\"asd.cc\", \"foo.cc\"]\n"
+ " } else {\n"
+ " dependencies += [\"bar.cc\"]\n"
+ " }\n"
+ "}\n";
+ const char* expected =
+ "BLOCK\n"
+ " FUNCTION(cc_test)\n"
+ " LIST\n"
+ " LITERAL(\"foo\")\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(sources)\n"
+ " LIST\n"
+ " LITERAL(\"foo.cc\")\n"
+ " CONDITION\n"
+ " BINARY(==)\n"
+ " IDENTIFIER(OS)\n"
+ " LITERAL(\"mac\")\n"
+ " BLOCK\n"
+ " BINARY(+=)\n"
+ " IDENTIFIER(sources)\n"
+ " LITERAL(\"bar.cc\")\n"
+ " CONDITION\n"
+ " BINARY(==)\n"
+ " IDENTIFIER(OS)\n"
+ " LITERAL(\"win\")\n"
+ " BLOCK\n"
+ " BINARY(-=)\n"
+ " IDENTIFIER(sources)\n"
+ " LIST\n"
+ " LITERAL(\"asd.cc\")\n"
+ " LITERAL(\"foo.cc\")\n"
+ " BLOCK\n"
+ " BINARY(+=)\n"
+ " IDENTIFIER(dependencies)\n"
+ " LIST\n"
+ " LITERAL(\"bar.cc\")\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, NestedBlocks) {
+ const char* input = "{cc_test(\"foo\") {{foo=1}{}}}";
+ const char* expected =
+ "BLOCK\n"
+ " BLOCK\n"
+ " FUNCTION(cc_test)\n"
+ " LIST\n"
+ " LITERAL(\"foo\")\n"
+ " BLOCK\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(foo)\n"
+ " LITERAL(1)\n"
+ " BLOCK\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, List) {
+ const char* input = "[] a = [1,asd,] b = [1, 2+3 - foo]";
+ const char* expected =
+ "BLOCK\n"
+ " LIST\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(a)\n"
+ " LIST\n"
+ " LITERAL(1)\n"
+ " IDENTIFIER(asd)\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(b)\n"
+ " LIST\n"
+ " LITERAL(1)\n"
+ " BINARY(+)\n"
+ " LITERAL(2)\n"
+ " BINARY(-)\n"
+ " LITERAL(3)\n"
+ " IDENTIFIER(foo)\n";
+ DoParserPrintTest(input, expected);
+
+ DoParserErrorTest("[a, 2+,]", 1, 7);
+ DoParserErrorTest("[,]", 1, 2);
+ DoParserErrorTest("[a,,]", 1, 4);
+}
+
+TEST(Parser, UnterminatedBlock) {
+ DoParserErrorTest("hello {", 1, 7);
+}
+
+TEST(Parser, BadlyTerminatedNumber) {
+ DoParserErrorTest("1234z", 1, 5);
+}
diff --git a/tools/gn/path_output.cc b/tools/gn/path_output.cc
new file mode 100644
index 0000000..f67b3d4
--- /dev/null
+++ b/tools/gn/path_output.cc
@@ -0,0 +1,116 @@
+// 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/path_output.h"
+
+#include "build/build_config.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/output_file.h"
+#include "tools/gn/string_utils.h"
+
+PathOutput::PathOutput(const SourceDir& current_dir,
+ EscapingMode escaping,
+ bool convert_slashes)
+ : current_dir_(current_dir) {
+ inverse_current_dir_ = InvertDir(current_dir_);
+
+ options_.mode = escaping;
+ options_.convert_slashes = convert_slashes;
+ options_.inhibit_quoting = false;
+
+ if (convert_slashes)
+ ConvertPathToSystem(&inverse_current_dir_);
+}
+
+PathOutput::~PathOutput() {
+}
+
+void PathOutput::WriteFile(std::ostream& out, const SourceFile& file) const {
+ WritePathStr(out, file.value());
+}
+
+void PathOutput::WriteDir(std::ostream& out,
+ const SourceDir& dir,
+ DirSlashEnding slash_ending) const {
+ if (dir.value() == "/") {
+ // Writing system root is always a slash (this will normally only come up
+ // on Posix systems).
+ out << "/";
+ } else if (dir.value() == "//") {
+ // Writing out the source root.
+ if (slash_ending == DIR_NO_LAST_SLASH) {
+ // The inverse_current_dir_ will contain a [back]slash at the end, so we
+ // can't just write it out.
+ if (inverse_current_dir_.empty()) {
+ out << ".";
+ } else {
+ out.write(inverse_current_dir_.c_str(),
+ inverse_current_dir_.size() - 1);
+ }
+ } else {
+ if (inverse_current_dir_.empty())
+ out << "./";
+ else
+ out << inverse_current_dir_;
+ }
+ } else if (slash_ending == DIR_INCLUDE_LAST_SLASH) {
+ WritePathStr(out, dir.value());
+ } else {
+ // DIR_NO_LAST_SLASH mode, just trim the last char.
+ WritePathStr(out, base::StringPiece(dir.value().data(),
+ dir.value().size() - 1));
+ }
+}
+
+void PathOutput::WriteFile(std::ostream& out, const OutputFile& file) const {
+ // Here we assume that the path is already preprocessed.
+ EscapeStringToStream(out, file.value(), options_);
+}
+
+void PathOutput::WriteSourceRelativeString(
+ std::ostream& out,
+ const base::StringPiece& str) const {
+ // Input begins with two slashes, is relative to source root. Strip off
+ // the two slashes when cat-ing it.
+ if (options_.mode == ESCAPE_SHELL) {
+ // Shell escaping needs an intermediate string since it may end up
+ // quoting the whole thing. On Windows, the slashes may already be
+ // converted to backslashes in inverse_current_dir_, but we assume that on
+ // Windows the escaper won't try to then escape the preconverted
+ // backslashes and will just pass them, so this is fine.
+ std::string intermediate;
+ intermediate.reserve(inverse_current_dir_.size() + str.size());
+ intermediate.assign(inverse_current_dir_.c_str(),
+ inverse_current_dir_.size());
+ intermediate.append(str.data(), str.size());
+
+ EscapeStringToStream(out,
+ base::StringPiece(intermediate.c_str(), intermediate.size()),
+ options_);
+ } else {
+ // Ninja (and none) escaping can avoid the intermediate string and
+ // reprocessing of the inverse_current_dir_.
+ out << inverse_current_dir_;
+ EscapeStringToStream(out, str, options_);
+ }
+}
+
+void PathOutput::WritePathStr(std::ostream& out,
+ const base::StringPiece& str) const {
+ DCHECK(str.size() > 0 && str[0] == '/');
+
+ if (str.size() >= 2 && str[1] == '/') {
+ WriteSourceRelativeString(out, str.substr(2));
+ } else {
+ // Input begins with one slash, don't write the current directory since
+ // it's system-absolute.
+#if defined(OS_WIN)
+ // On Windows, trim the leading slash, since the input for absolute
+ // paths will look like "/C:/foo/bar.txt".
+ EscapeStringToStream(out, str.substr(1), options_);
+#else
+ EscapeStringToStream(out, str, options_);
+#endif
+ }
+}
diff --git a/tools/gn/path_output.h b/tools/gn/path_output.h
new file mode 100644
index 0000000..00bdbee
--- /dev/null
+++ b/tools/gn/path_output.h
@@ -0,0 +1,80 @@
+// 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.
+
+#ifndef TOOLS_GN_PATH_OUTPUT_H_
+#define TOOLS_GN_PATH_OUTPUT_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "tools/gn/escape.h"
+#include "tools/gn/source_dir.h"
+
+class OutputFile;
+class SourceFile;
+
+// Writes file names to streams assuming a certain input directory and
+// escaping rules. This gives us a central place for managing this state.
+class PathOutput {
+ public:
+ // Controls whether writing directory names include the trailing slash.
+ // Often we don't want the trailing slash when writing out to a command line,
+ // especially on Windows where it's a backslash and might be interpreted as
+ // escaping the thing following it.
+ enum DirSlashEnding {
+ DIR_INCLUDE_LAST_SLASH,
+ DIR_NO_LAST_SLASH,
+ };
+
+ PathOutput(const SourceDir& current_dir,
+ EscapingMode escaping,
+ bool convert_slashes);
+ ~PathOutput();
+
+ // Read-only since inverse_current_dir_ is computed depending on this.
+ EscapingMode escaping_mode() const { return options_.mode; }
+
+ // When true, converts slashes to the system-type path separators (on
+ // Windows, this is a backslash, this is a NOP otherwise).
+ //
+ // Read-only since inverse_current_dir_ is computed depending on this.
+ bool convert_slashes_to_system() const { return options_.convert_slashes; }
+
+ // When the output escaping is ESCAPE_SHELL, the escaper will normally put
+ // quotes around suspect things. If this value is set to true, we'll disable
+ // the quoting feature. This means that in ESCAPE_SHELL mode, strings with
+ // spaces in them qon't be quoted. This mode is for when quoting is done at
+ // some higher-level. Defaults to false.
+ bool inhibit_quoting() const { return options_.inhibit_quoting; }
+ void set_inhibit_quoting(bool iq) { options_.inhibit_quoting = iq; }
+
+ void WriteFile(std::ostream& out, const SourceFile& file) const;
+ void WriteFile(std::ostream& out, const OutputFile& file) const;
+ void WriteDir(std::ostream& out,
+ const SourceDir& dir,
+ DirSlashEnding slash_ending) const;
+
+ // Backend for WriteFile and WriteDir. This appends the given file or
+ // directory string to the file.
+ void WritePathStr(std::ostream& out, const base::StringPiece& str) const;
+
+ private:
+ // Takes the given string and writes it out, appending to the inverse
+ // current dir. This assumes leading slashes have been trimmed.
+ void WriteSourceRelativeString(std::ostream& out,
+ const base::StringPiece& str) const;
+
+ SourceDir current_dir_;
+
+ // Uses system slashes if convert_slashes_to_system_.
+ std::string inverse_current_dir_;
+
+ // Since the inverse_current_dir_ depends on some of these, we don't expose
+ // this directly to modification.
+ EscapeOptions options_;
+};
+
+#endif // TOOLS_GN_PATH_OUTPUT_H_
diff --git a/tools/gn/path_output_unittest.cc b/tools/gn/path_output_unittest.cc
new file mode 100644
index 0000000..5133b79
--- /dev/null
+++ b/tools/gn/path_output_unittest.cc
@@ -0,0 +1,193 @@
+// 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 <sstream>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/path_output.h"
+#include "tools/gn/source_dir.h"
+#include "tools/gn/source_file.h"
+
+TEST(PathOutput, Basic) {
+ SourceDir build_dir("//out/Debug/");
+ PathOutput writer(build_dir, ESCAPE_NONE, false);
+ {
+ // Normal source-root path.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/bar.cc"));
+ EXPECT_EQ("../../foo/bar.cc", out.str());
+ }
+ {
+ // File in the root dir.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo.cc"));
+ EXPECT_EQ("../../foo.cc", out.str());
+ }
+#if defined(OS_WIN)
+ {
+ // System-absolute path.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("/C:/foo/bar.cc"));
+ EXPECT_EQ("C:/foo/bar.cc", out.str());
+ }
+#else
+ {
+ // System-absolute path.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("/foo/bar.cc"));
+ EXPECT_EQ("/foo/bar.cc", out.str());
+ }
+#endif
+}
+
+// Same as basic but the output dir is the root.
+TEST(PathOutput, BasicInRoot) {
+ SourceDir build_dir("//");
+ PathOutput writer(build_dir, ESCAPE_NONE, false);
+ {
+ // Normal source-root path.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/bar.cc"));
+ EXPECT_EQ("foo/bar.cc", out.str());
+ }
+ {
+ // File in the root dir.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo.cc"));
+ EXPECT_EQ("foo.cc", out.str());
+ }
+}
+
+TEST(PathOutput, NinjaEscaping) {
+ SourceDir build_dir("//out/Debug/");
+ PathOutput writer(build_dir, ESCAPE_NINJA, false);
+ {
+ // Spaces and $ in filenames.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/foo bar$.cc"));
+ EXPECT_EQ("../../foo/foo$ bar$$.cc", out.str());
+ }
+ {
+ // Not other weird stuff
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/\"foo\\bar\".cc"));
+ EXPECT_EQ("../../foo/\"foo\\bar\".cc", out.str());
+ }
+}
+
+TEST(PathOutput, ShellEscaping) {
+ SourceDir build_dir("//out/Debug/");
+ PathOutput writer(build_dir, ESCAPE_SHELL, false);
+ {
+ // Spaces in filenames should get quoted.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
+ EXPECT_EQ("\"../../foo/foo bar.cc\"", out.str());
+ }
+ {
+ // Quotes should get blackslash-escaped.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/\"foobar\".cc"));
+ EXPECT_EQ("../../foo/\\\"foobar\\\".cc", out.str());
+ }
+ {
+ // Backslashes should get escaped on non-Windows and preserved on Windows.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo\\bar.cc"));
+#if defined(OS_WIN)
+ EXPECT_EQ("../../foo\\bar.cc", out.str());
+#else
+ EXPECT_EQ("../../foo\\\\bar.cc", out.str());
+#endif
+ }
+}
+
+TEST(PathOutput, SlashConversion) {
+ SourceDir build_dir("//out/Debug/");
+ PathOutput writer(build_dir, ESCAPE_NINJA, true);
+ {
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/bar.cc"));
+#if defined(OS_WIN)
+ EXPECT_EQ("..\\..\\foo\\bar.cc", out.str());
+#else
+ EXPECT_EQ("../../foo/bar.cc", out.str());
+#endif
+ }
+}
+
+TEST(PathOutput, InhibitQuoting) {
+ SourceDir build_dir("//out/Debug/");
+ PathOutput writer(build_dir, ESCAPE_SHELL, false);
+ writer.set_inhibit_quoting(true);
+ {
+ // We should get unescaped spaces in the output with no quotes.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
+ EXPECT_EQ("../../foo/foo bar.cc", out.str());
+ }
+}
+
+TEST(PathOutput, WriteDir) {
+ {
+ SourceDir build_dir("//out/Debug/");
+ PathOutput writer(build_dir, ESCAPE_NINJA, false);
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("//foo/bar/"),
+ PathOutput::DIR_INCLUDE_LAST_SLASH);
+ EXPECT_EQ("../../foo/bar/", out.str());
+ }
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("//foo/bar/"),
+ PathOutput::DIR_NO_LAST_SLASH);
+ EXPECT_EQ("../../foo/bar", out.str());
+ }
+
+ // Output source root dir.
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("//"),
+ PathOutput::DIR_INCLUDE_LAST_SLASH);
+ EXPECT_EQ("../../", out.str());
+ }
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("//"),
+ PathOutput::DIR_NO_LAST_SLASH);
+ EXPECT_EQ("../..", out.str());
+ }
+
+ // Output system root dir.
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("/"),
+ PathOutput::DIR_INCLUDE_LAST_SLASH);
+ EXPECT_EQ("/", out.str());
+ }
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("/"),
+ PathOutput::DIR_NO_LAST_SLASH);
+ EXPECT_EQ("/", out.str());
+ }
+ }
+ {
+ // Empty build dir writer.
+ PathOutput root_writer(SourceDir("//"), ESCAPE_NINJA, false);
+ {
+ std::ostringstream out;
+ root_writer.WriteDir(out, SourceDir("//"),
+ PathOutput::DIR_INCLUDE_LAST_SLASH);
+ EXPECT_EQ("./", out.str());
+ }
+ {
+ std::ostringstream out;
+ root_writer.WriteDir(out, SourceDir("//"),
+ PathOutput::DIR_NO_LAST_SLASH);
+ EXPECT_EQ(".", out.str());
+ }
+ }
+}
diff --git a/tools/gn/pattern.cc b/tools/gn/pattern.cc
new file mode 100644
index 0000000..cc08b2c
--- /dev/null
+++ b/tools/gn/pattern.cc
@@ -0,0 +1,185 @@
+// 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/pattern.h"
+
+#include "tools/gn/value.h"
+
+namespace {
+
+void ParsePattern(const std::string& s, std::vector<Pattern::Subrange>* out) {
+ // Set when the last subrange is a literal so we can just append when we
+ // find another literal.
+ Pattern::Subrange* last_literal = NULL;
+
+ for (size_t i = 0; i < s.size(); i++) {
+ if (s[i] == '*') {
+ // Don't allow two **.
+ if (out->size() == 0 ||
+ (*out)[out->size() - 1].type != Pattern::Subrange::ANYTHING)
+ out->push_back(Pattern::Subrange(Pattern::Subrange::ANYTHING));
+ last_literal = NULL;
+ } else if (s[i] == '\\') {
+ if (i < s.size() - 1 && s[i + 1] == 'b') {
+ // "\b" means path boundary.
+ i++;
+ out->push_back(Pattern::Subrange(Pattern::Subrange::PATH_BOUNDARY));
+ last_literal = NULL;
+ } else {
+ // Backslash + anything else means that literal char.
+ if (!last_literal) {
+ out->push_back(Pattern::Subrange(Pattern::Subrange::LITERAL));
+ last_literal = &(*out)[out->size() - 1];
+ }
+ if (i < s.size() - 1) {
+ i++;
+ last_literal->literal.push_back(s[i]);
+ } else {
+ // Single backslash at end, use literal backslash.
+ last_literal->literal.push_back('\\');
+ }
+ }
+ } else {
+ if (!last_literal) {
+ out->push_back(Pattern::Subrange(Pattern::Subrange::LITERAL));
+ last_literal = &(*out)[out->size() - 1];
+ }
+ last_literal->literal.push_back(s[i]);
+ }
+ }
+}
+
+} // namespace
+
+Pattern::Pattern(const std::string& s) {
+ ParsePattern(s, &subranges_);
+ is_suffix_ =
+ (subranges_.size() == 2 &&
+ subranges_[0].type == Subrange::ANYTHING &&
+ subranges_[1].type == Subrange::LITERAL);
+}
+
+Pattern::~Pattern() {
+}
+
+bool Pattern::MatchesString(const std::string& s) const {
+ // Empty pattern matches only empty string.
+ if (subranges_.empty())
+ return s.empty();
+
+ if (is_suffix_) {
+ const std::string& suffix = subranges_[1].literal;
+ if (suffix.size() > s.size())
+ return false; // Too short.
+ return s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
+ }
+
+ return RecursiveMatch(s, 0, 0, true);
+}
+
+// We assume the number of ranges is small so recursive is always reasonable.
+// Could be optimized to only be recursive for *.
+bool Pattern::RecursiveMatch(const std::string& s,
+ size_t begin_char,
+ size_t subrange_index,
+ bool allow_implicit_path_boundary) const {
+ if (subrange_index >= subranges_.size()) {
+ // Hit the end of our subranges, the text should also be at the end for a
+ // match.
+ return begin_char == s.size();
+ }
+
+ const Subrange& sr = subranges_[subrange_index];
+ switch (sr.type) {
+ case Subrange::LITERAL: {
+ if (s.size() - begin_char < sr.literal.size())
+ return false; // Not enough room.
+ if (s.compare(begin_char, sr.literal.size(), sr.literal) != 0)
+ return false; // Literal doesn't match.
+
+ // Recursively check the next one.
+ return RecursiveMatch(s, begin_char + sr.literal.size(),
+ subrange_index + 1, true);
+ }
+
+ case Subrange::PATH_BOUNDARY: {
+ // When we can accept an implicit path boundary, we have to check both
+ // a match of the literal and the implicit one.
+ if (allow_implicit_path_boundary &&
+ (begin_char == 0 || begin_char == s.size())) {
+ // At implicit path boundary, see if the rest of the pattern matches.
+ if (RecursiveMatch(s, begin_char, subrange_index + 1, false))
+ return true;
+ }
+
+ // Check for a literal "/".
+ if (begin_char < s.size() && s[begin_char] == '/') {
+ // At explicit boundary, see if the rest of the pattern matches.
+ if (RecursiveMatch(s, begin_char + 1, subrange_index + 1, true))
+ return true;
+ }
+ return false;
+ }
+
+ case Subrange::ANYTHING: {
+ if (subrange_index == subranges_.size() - 1)
+ return true; // * at the end, consider it matching.
+
+ size_t min_next_size = sr.MinSize();
+
+ // We don't care about exactly what matched as long as there was a match,
+ // so we can do this front-to-back. If we needed the match, we would
+ // normally want "*" to be greedy so would work backwards.
+ for (size_t i = begin_char; i < s.size() - min_next_size; i++) {
+ // Note: this could probably be faster by detecting the type of the
+ // next match in advance and checking for a match in this loop rather
+ // than doing a full recursive call for each character.
+ if (RecursiveMatch(s, i, subrange_index + 1, true))
+ return true;
+ }
+ return false;
+ }
+
+ default:
+ NOTREACHED();
+ }
+
+ return false;
+}
+
+PatternList::PatternList() {
+}
+
+PatternList::~PatternList() {
+}
+
+void PatternList::SetFromValue(const Value& v, Err* err) {
+ patterns_.clear();
+
+ if (v.type() != Value::LIST) {
+ *err = Err(v.origin(), "This value must be a list.");
+ return;
+ }
+
+ const std::vector<Value>& list = v.list_value();
+ for (size_t i = 0; i < list.size(); i++) {
+ if (!list[i].VerifyTypeIs(Value::STRING, err))
+ return;
+ patterns_.push_back(Pattern(list[i].string_value()));
+ }
+}
+
+bool PatternList::MatchesString(const std::string& s) const {
+ for (size_t i = 0; i < patterns_.size(); i++) {
+ if (patterns_[i].MatchesString(s))
+ return true;
+ }
+ return false;
+}
+
+bool PatternList::MatchesValue(const Value& v) const {
+ if (v.type() == Value::STRING)
+ return MatchesString(v.string_value());
+ return false;
+}
diff --git a/tools/gn/pattern.h b/tools/gn/pattern.h
new file mode 100644
index 0000000..582cfea
--- /dev/null
+++ b/tools/gn/pattern.h
@@ -0,0 +1,86 @@
+// 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.
+
+#ifndef TOOLS_GN_PATTERN_H_
+#define TOOLS_GN_PATTERN_H_
+
+#include <string>
+#include <vector>
+
+#include "tools/gn/value.h"
+
+class Pattern {
+ public:
+ struct Subrange {
+ enum Type {
+ LITERAL, // Matches exactly the contents of the string.
+ ANYTHING, // * (zero or more chars).
+ PATH_BOUNDARY // '/' or beginning of string.
+ };
+
+ Subrange(Type t, const std::string& l = std::string())
+ : type(t),
+ literal(l) {
+ }
+
+ // Returns the minimum number of chars that this subrange requires.
+ size_t MinSize() const {
+ switch (type) {
+ case LITERAL:
+ return literal.size();
+ case ANYTHING:
+ return 0;
+ case PATH_BOUNDARY:
+ return 0; // Can match beginning or end of string, which is 0 len.
+ default:
+ return 0;
+ }
+ }
+
+ Type type;
+
+ // When type == LITERAL this is the text to match.
+ std::string literal;
+ };
+
+ Pattern(const std::string& s);
+ ~Pattern();
+
+ // Returns true if the current pattern matches the given string.
+ bool MatchesString(const std::string& s) const;
+
+ private:
+ // allow_implicit_path_boundary determines if a path boundary should accept
+ // matches at the beginning or end of the string.
+ bool RecursiveMatch(const std::string& s,
+ size_t begin_char,
+ size_t subrange_index,
+ bool allow_implicit_path_boundary) const;
+
+ std::vector<Subrange> subranges_;
+
+ // Set to true when the subranges are "*foo" ("ANYTHING" followed by a
+ // literal). This covers most patterns so we optimize for this.
+ bool is_suffix_;
+};
+
+class PatternList {
+ public:
+ PatternList();
+ ~PatternList();
+
+ bool is_empty() const { return patterns_.empty(); }
+
+ // Initializes the pattern list from a give list of pattern strings. Sets
+ // |*err| on failure.
+ void SetFromValue(const Value& v, Err* err);
+
+ bool MatchesString(const std::string& s) const;
+ bool MatchesValue(const Value& v) const;
+
+ private:
+ std::vector<Pattern> patterns_;
+};
+
+#endif // TOOLS_GN_PATTERN_H_
diff --git a/tools/gn/pattern_unittest.cc b/tools/gn/pattern_unittest.cc
new file mode 100644
index 0000000..e9ffea6
--- /dev/null
+++ b/tools/gn/pattern_unittest.cc
@@ -0,0 +1,61 @@
+// 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/pattern.h"
+
+namespace {
+
+struct Case {
+ const char* pattern;
+ const char* candidate;
+ bool expected_match;
+};
+
+} // namespace
+
+TEST(Pattern, Matches) {
+ Case pattern_cases[] = {
+ // Empty pattern matches only empty string.
+ { "", "", true },
+ { "", "foo", false },
+ // Exact matches.
+ { "foo", "foo", true },
+ { "foo", "bar", false },
+ // Path boundaries.
+ { "\\b", "", true },
+ { "\\b", "/", true },
+ { "\\b\\b", "/", true },
+ { "\\b\\b\\b", "", false },
+ { "\\b\\b\\b", "/", true },
+ { "\\b", "//", false },
+ { "\\bfoo\\b", "foo", true },
+ { "\\bfoo\\b", "/foo/", true },
+ { "\\b\\bfoo", "/foo", true },
+ // *
+ { "*", "", true },
+ { "*", "foo", true },
+ { "*foo", "foo", true },
+ { "*foo", "gagafoo", true },
+ { "*foo", "gagafoob", false },
+ { "foo*bar", "foobar", true },
+ { "foo*bar", "foo-bar", true },
+ { "foo*bar", "foolalalalabar", true },
+ { "foo*bar", "foolalalalabaz", false },
+ { "*a*b*c*d*", "abcd", true },
+ { "*a*b*c*d*", "1a2b3c4d5", true },
+ { "*a*b*c*d*", "1a2b3c45", false },
+ { "*\\bfoo\\b*", "foo", true },
+ { "*\\bfoo\\b*", "/foo/", true },
+ { "*\\bfoo\\b*", "foob", false },
+ { "*\\bfoo\\b*", "lala/foo/bar/baz", true },
+ };
+ for (size_t i = 0; i < arraysize(pattern_cases); i++) {
+ const Case& c = pattern_cases[i];
+ Pattern pattern(c.pattern);
+ bool result = pattern.MatchesString(c.candidate);
+ EXPECT_EQ(c.expected_match, result) << i << ": \"" << c.pattern
+ << "\", \"" << c.candidate << "\"";
+ }
+}
diff --git a/tools/gn/scheduler.cc b/tools/gn/scheduler.cc
new file mode 100644
index 0000000..33c8f1a
--- /dev/null
+++ b/tools/gn/scheduler.cc
@@ -0,0 +1,130 @@
+// 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/scheduler.h"
+
+#include "base/bind.h"
+#include "tools/gn/ninja_target_writer.h"
+#include "tools/gn/standard_out.h"
+
+Scheduler* g_scheduler = NULL;
+
+Scheduler::Scheduler()
+ : pool_(new base::SequencedWorkerPool(32, "worker_")),
+ input_file_manager_(new InputFileManager),
+ verbose_logging_(false),
+ work_count_(0),
+ is_failed_(false) {
+ g_scheduler = this;
+}
+
+Scheduler::~Scheduler() {
+ g_scheduler = NULL;
+}
+
+bool Scheduler::Run() {
+ runner_.Run();
+ pool_->Shutdown();
+ return !is_failed();
+}
+
+void Scheduler::Log(const std::string& verb, const std::string& msg) {
+ if (base::MessageLoop::current() == &main_loop_) {
+ LogOnMainThread(verb, msg);
+ } else {
+ // The run loop always joins on the sub threads, so the lifetime of this
+ // object outlives the invocations of this function, hence "unretained".
+ main_loop_.PostTask(FROM_HERE,
+ base::Bind(&Scheduler::LogOnMainThread,
+ base::Unretained(this), verb, msg));
+ }
+}
+
+void Scheduler::FailWithError(const Err& err) {
+ DCHECK(err.has_error());
+ {
+ base::AutoLock lock(lock_);
+
+ if (is_failed_)
+ return; // Ignore errors once we see one.
+ is_failed_ = true;
+ }
+
+ if (base::MessageLoop::current() == &main_loop_) {
+ FailWithErrorOnMainThread(err);
+ } else {
+ // The run loop always joins on the sub threads, so the lifetime of this
+ // object outlives the invocations of this function, hence "unretained".
+ main_loop_.PostTask(FROM_HERE,
+ base::Bind(&Scheduler::FailWithErrorOnMainThread,
+ base::Unretained(this), err));
+ }
+}
+
+void Scheduler::ScheduleWork(const base::Closure& work) {
+ IncrementWorkCount();
+ pool_->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE, base::Bind(&Scheduler::DoWork,
+ base::Unretained(this), work),
+ base::SequencedWorkerPool::BLOCK_SHUTDOWN);
+}
+
+void Scheduler::ScheduleTargetFileWrite(const Target* target) {
+ pool_->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE, base::Bind(&Scheduler::DoTargetFileWrite,
+ base::Unretained(this), target),
+ base::SequencedWorkerPool::BLOCK_SHUTDOWN);
+}
+
+void Scheduler::AddGenDependency(const SourceFile& source_file) {
+ base::AutoLock lock(lock_);
+ gen_dependencies_.push_back(source_file);
+}
+
+std::vector<SourceFile> Scheduler::GetGenDependencies() const {
+ base::AutoLock lock(lock_);
+ return gen_dependencies_;
+}
+
+void Scheduler::IncrementWorkCount() {
+ base::AtomicRefCountInc(&work_count_);
+}
+
+void Scheduler::DecrementWorkCount() {
+ if (!base::AtomicRefCountDec(&work_count_)) {
+ if (base::MessageLoop::current() == &main_loop_) {
+ OnComplete();
+ } else {
+ main_loop_.PostTask(FROM_HERE,
+ base::Bind(&Scheduler::OnComplete,
+ base::Unretained(this)));
+ }
+ }
+}
+
+void Scheduler::LogOnMainThread(const std::string& verb,
+ const std::string& msg) {
+ OutputString(verb, DECORATION_YELLOW);
+ OutputString(" " + msg + "\n");
+}
+
+void Scheduler::FailWithErrorOnMainThread(const Err& err) {
+ err.PrintToStdout();
+ runner_.Quit();
+}
+
+void Scheduler::DoTargetFileWrite(const Target* target) {
+ NinjaTargetWriter::RunAndWriteFile(target);
+}
+
+void Scheduler::DoWork(const base::Closure& closure) {
+ closure.Run();
+ DecrementWorkCount();
+}
+
+void Scheduler::OnComplete() {
+ // Should be called on the main thread.
+ DCHECK(base::MessageLoop::current() == main_loop());
+ runner_.Quit();
+}
diff --git a/tools/gn/scheduler.h b/tools/gn/scheduler.h
new file mode 100644
index 0000000..eab996d
--- /dev/null
+++ b/tools/gn/scheduler.h
@@ -0,0 +1,90 @@
+// 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.
+
+#ifndef TOOLS_GN_SCHEDULER_H_
+#define TOOLS_GN_SCHEDULER_H_
+
+#include "base/atomic_ref_count.h"
+#include "base/basictypes.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "tools/gn/input_file_manager.h"
+
+class Target;
+
+// Maintains the thread pool and error state.
+class Scheduler {
+ public:
+ Scheduler();
+ ~Scheduler();
+
+ bool Run();
+
+ base::MessageLoop* main_loop() { return &main_loop_; }
+ base::SequencedWorkerPool* pool() { return pool_; }
+
+ InputFileManager* input_file_manager() { return input_file_manager_; }
+
+ bool verbose_logging() const { return verbose_logging_; }
+ void set_verbose_logging(bool v) { verbose_logging_ = v; }
+
+ // TODO(brettw) data race on this access (benign?).
+ bool is_failed() const { return is_failed_; }
+
+ void Log(const std::string& verb, const std::string& msg);
+ void FailWithError(const Err& err);
+
+ void ScheduleWork(const base::Closure& work);
+
+ void ScheduleTargetFileWrite(const Target* target);
+
+ // Declares that the given file was read and affected the build output.
+ //
+ // TODO(brettw) this is global rather than per-BuildSettings. If we
+ // start using >1 build settings, then we probably want this to take a
+ // BuildSettings object so we know the depdency on a per-build basis.
+ void AddGenDependency(const SourceFile& source_file);
+ std::vector<SourceFile> GetGenDependencies() const;
+
+ // We maintain a count of the things we need to do that works like a
+ // refcount. When this reaches 0, the program exits.
+ void IncrementWorkCount();
+ void DecrementWorkCount();
+
+ private:
+ void LogOnMainThread(const std::string& verb, const std::string& msg);
+ void FailWithErrorOnMainThread(const Err& err);
+
+ void DoTargetFileWrite(const Target* target);
+
+ void DoWork(const base::Closure& closure);
+
+ void OnComplete();
+
+ base::MessageLoop main_loop_;
+ scoped_refptr<base::SequencedWorkerPool> pool_;
+
+ scoped_refptr<InputFileManager> input_file_manager_;
+
+ base::RunLoop runner_;
+
+ bool verbose_logging_;
+
+ base::AtomicRefCount work_count_;
+
+ mutable base::Lock lock_;
+ bool is_failed_;
+
+ // Additional input dependencies. Protected by the lock.
+ std::vector<SourceFile> gen_dependencies_;
+
+ DISALLOW_COPY_AND_ASSIGN(Scheduler);
+};
+
+extern Scheduler* g_scheduler;
+
+#endif // TOOLS_GN_SCHEDULER_H_
+
diff --git a/tools/gn/scope.cc b/tools/gn/scope.cc
new file mode 100644
index 0000000..72664d7
--- /dev/null
+++ b/tools/gn/scope.cc
@@ -0,0 +1,372 @@
+// 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/scope.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "tools/gn/parse_tree.h"
+
+namespace {
+
+// FLags set in the mode_flags_ of a scope. If a bit is set, it applies
+// recursively to all dependent scopes.
+const unsigned kProcessingBuildConfigFlag = 1;
+const unsigned kProcessingDefaultBuildConfigFlag = 2;
+const unsigned kProcessingImportFlag = 4;
+
+} // namespace
+
+Scope::Scope(const Settings* settings)
+ : const_containing_(NULL),
+ mutable_containing_(NULL),
+ settings_(settings),
+ mode_flags_(0) {
+}
+
+Scope::Scope(Scope* parent)
+ : const_containing_(NULL),
+ mutable_containing_(parent),
+ settings_(parent->settings()),
+ mode_flags_(0) {
+}
+
+Scope::Scope(const Scope* parent)
+ : const_containing_(parent),
+ mutable_containing_(NULL),
+ settings_(parent->settings()),
+ mode_flags_(0) {
+}
+
+Scope::~Scope() {
+ STLDeleteContainerPairSecondPointers(target_defaults_.begin(),
+ target_defaults_.end());
+}
+
+const Value* Scope::GetValue(const base::StringPiece& ident,
+ bool counts_as_used) {
+ // First check for programatically-provided values.
+ for (ProviderSet::const_iterator i = programmatic_providers_.begin();
+ i != programmatic_providers_.end(); ++i) {
+ const Value* v = (*i)->GetProgrammaticValue(ident);
+ if (v)
+ return v;
+ }
+
+ RecordMap::iterator found = values_.find(ident);
+ if (found != values_.end()) {
+ if (counts_as_used)
+ found->second.used = true;
+ return &found->second.value;
+ }
+
+ // Search in the parent scope.
+ if (const_containing_)
+ return const_containing_->GetValue(ident);
+ if (mutable_containing_)
+ return mutable_containing_->GetValue(ident, counts_as_used);
+ return NULL;
+}
+
+Value* Scope::GetValueForcedToCurrentScope(const base::StringPiece& ident,
+ const ParseNode* set_node) {
+ RecordMap::iterator found = values_.find(ident);
+ if (found != values_.end())
+ return &found->second.value; // Already have in the current scope.
+
+ // Search in the parent scope.
+ if (containing()) {
+ const Value* in_containing = containing()->GetValue(ident);
+ if (in_containing) {
+ // Promote to current scope.
+ return SetValue(ident, *in_containing, set_node);
+ }
+ }
+ return NULL;
+}
+
+const Value* Scope::GetValue(const base::StringPiece& ident) const {
+ RecordMap::const_iterator found = values_.find(ident);
+ if (found != values_.end())
+ return &found->second.value;
+ if (containing())
+ return containing()->GetValue(ident);
+ return NULL;
+}
+
+Value* Scope::SetValue(const base::StringPiece& ident,
+ const Value& v,
+ const ParseNode* set_node) {
+ Record& r = values_[ident]; // Clears any existing value.
+ r.value = v;
+ r.value.set_origin(set_node);
+ return &r.value;
+}
+
+bool Scope::AddTemplate(const std::string& name, const FunctionCallNode* decl) {
+ if (GetTemplate(name))
+ return false;
+ templates_[name] = decl;
+ return true;
+}
+
+const FunctionCallNode* Scope::GetTemplate(const std::string& name) const {
+ TemplateMap::const_iterator found = templates_.find(name);
+ if (found != templates_.end())
+ return found->second;
+ if (containing())
+ return containing()->GetTemplate(name);
+ return NULL;
+}
+
+void Scope::MarkUsed(const base::StringPiece& ident) {
+ RecordMap::iterator found = values_.find(ident);
+ if (found == values_.end()) {
+ NOTREACHED();
+ return;
+ }
+ found->second.used = true;
+}
+
+void Scope::MarkUnused(const base::StringPiece& ident) {
+ RecordMap::iterator found = values_.find(ident);
+ if (found == values_.end()) {
+ NOTREACHED();
+ return;
+ }
+ found->second.used = false;
+}
+
+bool Scope::IsSetButUnused(const base::StringPiece& ident) const {
+ RecordMap::const_iterator found = values_.find(ident);
+ if (found != values_.end()) {
+ if (!found->second.used) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Scope::CheckForUnusedVars(Err* err) const {
+ for (RecordMap::const_iterator i = values_.begin();
+ i != values_.end(); ++i) {
+ if (!i->second.used) {
+ std::string help = "You set the variable \"" + i->first.as_string() +
+ "\" here and it was unused before it went\nout of scope.";
+
+ const BinaryOpNode* binary = i->second.value.origin()->AsBinaryOp();
+ if (binary) {
+ // Make a nicer error message for normal var sets.
+ *err = Err(binary->left()->GetRange(), "Assignment had no effect.",
+ help);
+ } else {
+ // This will happen for internally-generated variables.
+ *err = Err(i->second.value.origin(), "Assignment had no effect.", help);
+ }
+ return false;
+ }
+ }
+ return true;
+}
+
+void Scope::GetCurrentScopeValues(KeyValueVector* output) const {
+ output->reserve(values_.size());
+ for (RecordMap::const_iterator i = values_.begin(); i != values_.end(); ++i) {
+ output->push_back(std::make_pair(i->first, i->second.value));
+ }
+}
+
+bool Scope::NonRecursiveMergeTo(Scope* dest,
+ const ParseNode* node_for_err,
+ const char* desc_for_err,
+ Err* err) const {
+ // Values.
+ for (RecordMap::const_iterator i = values_.begin(); i != values_.end(); ++i) {
+ const Value* existing_value = dest->GetValue(i->first);
+ if (existing_value) {
+ // Value present in both the source and the dest.
+ std::string desc_string(desc_for_err);
+ *err = Err(node_for_err, "Value collision.",
+ "This " + desc_string + " contains \"" + i->first.as_string() + "\"");
+ err->AppendSubErr(Err(i->second.value, "defined here.",
+ "Which would clobber the one in your current scope"));
+ err->AppendSubErr(Err(*existing_value, "defined here.",
+ "Executing " + desc_string + " should not conflict with anything "
+ "in the current\nscope."));
+ return false;
+ }
+ dest->values_[i->first] = i->second;
+ }
+
+ // Target defaults are owning pointers.
+ for (NamedScopeMap::const_iterator i = target_defaults_.begin();
+ i != target_defaults_.end(); ++i) {
+ if (dest->GetTargetDefaults(i->first)) {
+ // TODO(brettw) it would be nice to know the origin of a
+ // set_target_defaults so we can give locations for the colliding target
+ // defaults.
+ std::string desc_string(desc_for_err);
+ *err = Err(node_for_err, "Target defaults collision.",
+ "This " + desc_string + " contains target defaults for\n"
+ "\"" + i->first + "\" which would clobber one for the\n"
+ "same target type in your current scope. It's unfortunate that I'm "
+ "too stupid\nto tell you the location of where the target defaults "
+ "were set. Usually\nthis happens in the BUILDCONFIG.gn file.");
+ return false;
+ }
+
+ Scope* s = new Scope(settings_);
+ i->second->NonRecursiveMergeTo(s, node_for_err, "<SHOULDN'T HAPPEN>", err);
+ dest->target_defaults_[i->first] = s;
+ }
+
+ // Sources assignment filter.
+ if (sources_assignment_filter_) {
+ if (dest->GetSourcesAssignmentFilter()) {
+ // Sources assignment filter present in both the source and the dest.
+ std::string desc_string(desc_for_err);
+ *err = Err(node_for_err, "Assignment filter collision.",
+ "The " + desc_string + " contains a sources_assignment_filter which\n"
+ "would clobber the one in your current scope.");
+ return false;
+ }
+ dest->sources_assignment_filter_.reset(
+ new PatternList(*sources_assignment_filter_));
+ }
+
+ // Templates.
+ for (TemplateMap::const_iterator i = templates_.begin();
+ i != templates_.end(); ++i) {
+ const FunctionCallNode* existing_template = dest->GetTemplate(i->first);
+ if (existing_template) {
+ // Rule present in both the source and the dest.
+ std::string desc_string(desc_for_err);
+ *err = Err(node_for_err, "Template collision.",
+ "This " + desc_string + " contains a template \"" + i->first + "\"");
+ err->AppendSubErr(Err(i->second->function(), "defined here.",
+ "Which would clobber the one in your current scope"));
+ err->AppendSubErr(Err(existing_template->function(), "defined here.",
+ "Executing " + desc_string + " should not conflict with anything "
+ "in the current\nscope."));
+ return false;
+ }
+ dest->templates_.insert(*i);
+ }
+
+ return true;
+}
+
+Scope* Scope::MakeTargetDefaults(const std::string& target_type) {
+ if (GetTargetDefaults(target_type))
+ return NULL;
+
+ Scope** dest = &target_defaults_[target_type];
+ if (*dest) {
+ NOTREACHED(); // Already set.
+ return *dest;
+ }
+ *dest = new Scope(settings_);
+ return *dest;
+}
+
+const Scope* Scope::GetTargetDefaults(const std::string& target_type) const {
+ NamedScopeMap::const_iterator found = target_defaults_.find(target_type);
+ if (found != target_defaults_.end())
+ return found->second;
+ if (containing())
+ return containing()->GetTargetDefaults(target_type);
+ return NULL;
+}
+
+const PatternList* Scope::GetSourcesAssignmentFilter() const {
+ if (sources_assignment_filter_)
+ return sources_assignment_filter_.get();
+ if (containing())
+ return containing()->GetSourcesAssignmentFilter();
+ return NULL;
+}
+
+void Scope::SetProcessingBuildConfig() {
+ DCHECK((mode_flags_ & kProcessingBuildConfigFlag) == 0);
+ mode_flags_ |= kProcessingBuildConfigFlag;
+}
+
+void Scope::ClearProcessingBuildConfig() {
+ DCHECK(mode_flags_ & kProcessingBuildConfigFlag);
+ mode_flags_ &= ~(kProcessingBuildConfigFlag);
+}
+
+bool Scope::IsProcessingBuildConfig() const {
+ if (mode_flags_ & kProcessingBuildConfigFlag)
+ return true;
+ if (containing())
+ return containing()->IsProcessingBuildConfig();
+ return false;
+}
+
+void Scope::SetProcessingDefaultBuildConfig() {
+ DCHECK((mode_flags_ & kProcessingDefaultBuildConfigFlag) == 0);
+ mode_flags_ |= kProcessingDefaultBuildConfigFlag;
+}
+
+void Scope::ClearProcessingDefaultBuildConfig() {
+ DCHECK(mode_flags_ & kProcessingDefaultBuildConfigFlag);
+ mode_flags_ &= ~(kProcessingDefaultBuildConfigFlag);
+}
+
+bool Scope::IsProcessingDefaultBuildConfig() const {
+ if (mode_flags_ & kProcessingDefaultBuildConfigFlag)
+ return true;
+ if (containing())
+ return containing()->IsProcessingDefaultBuildConfig();
+ return false;
+}
+
+void Scope::SetProcessingImport() {
+ DCHECK((mode_flags_ & kProcessingImportFlag) == 0);
+ mode_flags_ |= kProcessingImportFlag;
+}
+
+void Scope::ClearProcessingImport() {
+ DCHECK(mode_flags_ & kProcessingImportFlag);
+ mode_flags_ &= ~(kProcessingImportFlag);
+}
+
+bool Scope::IsProcessingImport() const {
+ if (mode_flags_ & kProcessingImportFlag)
+ return true;
+ if (containing())
+ return containing()->IsProcessingImport();
+ return false;
+}
+
+void Scope::SetProperty(const void* key, void* value) {
+ if (!value) {
+ DCHECK(properties_.find(key) != properties_.end());
+ properties_.erase(key);
+ } else {
+ properties_[key] = value;
+ }
+}
+
+void* Scope::GetProperty(const void* key, const Scope** found_on_scope) const {
+ PropertyMap::const_iterator found = properties_.find(key);
+ if (found != properties_.end()) {
+ if (found_on_scope)
+ *found_on_scope = this;
+ return found->second;
+ }
+ if (containing())
+ return containing()->GetProperty(key, found_on_scope);
+ return NULL;
+}
+
+void Scope::AddProvider(ProgrammaticProvider* p) {
+ programmatic_providers_.insert(p);
+}
+
+void Scope::RemoveProvider(ProgrammaticProvider* p) {
+ DCHECK(programmatic_providers_.find(p) != programmatic_providers_.end());
+ programmatic_providers_.erase(p);
+}
diff --git a/tools/gn/scope.h b/tools/gn/scope.h
new file mode 100644
index 0000000..7d0547e
--- /dev/null
+++ b/tools/gn/scope.h
@@ -0,0 +1,260 @@
+// 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.
+
+#ifndef TOOLS_GN_SCOPE_H_
+#define TOOLS_GN_SCOPE_H_
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "tools/gn/err.h"
+#include "tools/gn/pattern.h"
+#include "tools/gn/value.h"
+
+class FunctionCallNode;
+class ImportManager;
+class ParseNode;
+class Settings;
+class TargetManager;
+
+// Scope for the script execution.
+//
+// Scopes are nested. Writing goes into the toplevel scope, reading checks
+// values resursively down the stack until a match is found or there are no
+// more containing scopes.
+//
+// A containing scope can be const or non-const. The const containing scope is
+// used primarily to refer to the master build config which is shared across
+// many invocations. A const containing scope, however, prevents us from
+// marking variables "used" which prevents us from issuing errors on unused
+// variables. So you should use a non-const containing scope whenever possible.
+class Scope {
+ public:
+ typedef std::vector<std::pair<base::StringPiece, Value> > KeyValueVector;
+
+ // Allows code to provide values for built-in variables. This class will
+ // automatically register itself on construction and deregister itself on
+ // destruction.
+ class ProgrammaticProvider {
+ public:
+ ProgrammaticProvider(Scope* scope) : scope_(scope) {
+ scope_->AddProvider(this);
+ }
+ ~ProgrammaticProvider() {
+ scope_->RemoveProvider(this);
+ }
+
+ // Returns a non-null value if the given value can be programmatically
+ // generated, or NULL if there is none.
+ virtual const Value* GetProgrammaticValue(
+ const base::StringPiece& ident) = 0;
+
+ protected:
+ Scope* scope_;
+ };
+
+ // Creates an empty toplevel scope.
+ Scope(const Settings* settings);
+
+ // Creates a dependent scope.
+ Scope(Scope* parent);
+ Scope(const Scope* parent);
+
+ ~Scope();
+
+ const Settings* settings() const { return settings_; }
+
+ // See the const_/mutable_containing_ var declaraions below. Yes, it's a
+ // bit weird that we can have a const pointer to the "mutable" one.
+ Scope* mutable_containing() { return mutable_containing_; }
+ const Scope* mutable_containing() const { return mutable_containing_; }
+ const Scope* const_containing() const { return const_containing_; }
+ const Scope* containing() const {
+ return mutable_containing_ ? mutable_containing_ : const_containing_;
+ }
+
+ // Returns NULL if there's no such value.
+ //
+ // counts_as_used should be set if the variable is being read in a way that
+ // should count for unused variable checking.
+ const Value* GetValue(const base::StringPiece& ident,
+ bool counts_as_used);
+ const Value* GetValue(const base::StringPiece& ident) const;
+
+ // Same as GetValue, but if the value exists in a parent scope, we'll copy
+ // it to the current scope. If the return value is non-null, the value is
+ // guaranteed to be set in the current scope. Generatlly this will be used
+ // if the calling code is planning on modifying the value in-place.
+ //
+ // Since this is used when doing read-modifies, we never count this access
+ // as reading the variable, since we assume it will be written to.
+ Value* GetValueForcedToCurrentScope(const base::StringPiece& ident,
+ const ParseNode* set_node);
+
+ // The set_node indicates the statement that caused the set, for displaying
+ // errors later. Returns a pointer to the value in the current scope (a copy
+ // is made for storage).
+ Value* SetValue(const base::StringPiece& ident,
+ const Value& v,
+ const ParseNode* set_node);
+
+ // Templates associated with this scope. A template can only be set once, so
+ // AddTemplate will fail and return NULL if a rule with that name already
+ // exists. GetTemplate returns NULL if the rule doesn't exist, and it will
+ // check all containing scoped rescursively.
+ bool AddTemplate(const std::string& name, const FunctionCallNode* decl);
+ const FunctionCallNode* GetTemplate(const std::string& name) const;
+
+ // Marks the given identifier as (un)used in the current scope.
+ void MarkUsed(const base::StringPiece& ident);
+ void MarkUnused(const base::StringPiece& ident);
+
+ // Checks to see if the scope has a var set that hasn't been used. This is
+ // called before replacing the var with a different one. It does not check
+ // containing scopes.
+ //
+ // If the identifier is present but hasnn't been used, return true.
+ bool IsSetButUnused(const base::StringPiece& ident) const;
+
+ // Checks the scope to see if any values were set but not used, and fills in
+ // the error and returns false if they were.
+ bool CheckForUnusedVars(Err* err) const;
+
+ // Returns all values set in the current scope, without going to the parent
+ // scopes.
+ void GetCurrentScopeValues(KeyValueVector* output) const;
+
+ // Copies this scope's values into the destination. Values from the
+ // containing scope(s) (normally shadowed into the current one) will not be
+ // copied, neither will the reference to the containing scope (this is why
+ // it's "non-recursive").
+ //
+ // It is an error to merge a variable into a scope that already has something
+ // with that name in scope (meaning in that scope or in any of its containing
+ // scopes). If this happens, the error will be set and the function will
+ // return false.
+ //
+ // This is used in different contexts. When generating the error, the given
+ // parse node will be blamed, and the given desc will be used to describe
+ // the operation that doesn't support doing this. For example, desc_for_err
+ // would be "import" when doing an import, and the error string would say
+ // something like "The import contains...".
+ bool NonRecursiveMergeTo(Scope* dest,
+ const ParseNode* node_for_err,
+ const char* desc_for_err,
+ Err* err) const;
+
+ // Makes an empty scope with the given name. Returns NULL if the name is
+ // already set.
+ Scope* MakeTargetDefaults(const std::string& target_type);
+
+ // Gets the scope associated with the given target name, or null if it hasn't
+ // been set.
+ const Scope* GetTargetDefaults(const std::string& target_type) const;
+
+ // Filter to apply when the sources variable is assigned. May return NULL.
+ const PatternList* GetSourcesAssignmentFilter() const;
+ void set_sources_assignment_filter(
+ scoped_ptr<PatternList> f) {
+ sources_assignment_filter_ = f.Pass();
+ }
+
+ // Indicates if we're currently processing the build configuration file.
+ // This is true when processing the config file for any toolchain. See also
+ // *ProcessingDefaultBuildConfig() below.
+ //
+ // To set or clear the flag, it must currently be in the opposite state in
+ // the current scope. Note that querying the state of the flag recursively
+ // checks all containing scopes until it reaches the top or finds the flag
+ // set.
+ void SetProcessingBuildConfig();
+ void ClearProcessingBuildConfig();
+ bool IsProcessingBuildConfig() const;
+
+ // Indicates we're currently processing the default toolchain's build
+ // configuration file.
+ void SetProcessingDefaultBuildConfig();
+ void ClearProcessingDefaultBuildConfig();
+ bool IsProcessingDefaultBuildConfig() const;
+
+ // Indicates if we're currently processing an import file.
+ //
+ // See SetProcessingBaseConfig for how flags work.
+ void SetProcessingImport();
+ void ClearProcessingImport();
+ bool IsProcessingImport() const;
+
+ // Properties are opaque pointers that code can use to set state on a Scope
+ // that it can retrieve later.
+ //
+ // The key should be a pointer to some use-case-specific object (to avoid
+ // collisions, otherwise it doesn't matter). Memory management is up to the
+ // setter. Setting the value to NULL will delete the property.
+ //
+ // Getting a property recursively searches all scopes, and the optional
+ // |found_on_scope| variable will be filled with the actual scope containing
+ // the key (if the pointer is non-NULL).
+ void SetProperty(const void* key, void* value);
+ void* GetProperty(const void* key, const Scope** found_on_scope) const;
+
+ private:
+ friend class ProgrammaticProvider;
+
+ struct Record {
+ Record() : used(false) {}
+ Record(const Value& v) : used(false), value(v) {}
+
+ bool used; // Set to true when the variable is used.
+ Value value;
+ };
+
+ void AddProvider(ProgrammaticProvider* p);
+ void RemoveProvider(ProgrammaticProvider* p);
+
+ // Scopes can have no containing scope (both null), a mutable containing
+ // scope, or a const containing scope. The reason is that when we're doing
+ // a new target, we want to refer to the base_config scope which will be read
+ // by multiple threads at the same time, so we REALLY want it to be const.
+ // When you jsut do a nested {}, however, we sometimes want to be able to
+ // change things (especially marking unused vars).
+ const Scope* const_containing_;
+ Scope* mutable_containing_;
+
+ const Settings* settings_;
+
+ // Bits set for different modes. See the flag definitions in the .cc file
+ // for more.
+ unsigned mode_flags_;
+
+ typedef base::hash_map<base::StringPiece, Record> RecordMap;
+ RecordMap values_;
+
+ // Owning pointers. Note that this can't use string pieces since the names
+ // are constructed from Values which might be deallocated before this goes
+ // out of scope.
+ typedef base::hash_map<std::string, Scope*> NamedScopeMap;
+ NamedScopeMap target_defaults_;
+
+ // Null indicates not set and that we should fallback to the containing
+ // scope's filter.
+ scoped_ptr<PatternList> sources_assignment_filter_;
+
+ // Non-owning pointers, the function calls are owned by the input file which
+ // should be kept around by the input file manager.
+ typedef std::map<std::string, const FunctionCallNode*> TemplateMap;
+ TemplateMap templates_;
+
+ typedef std::map<const void*, void*> PropertyMap;
+ PropertyMap properties_;
+
+ typedef std::set<ProgrammaticProvider*> ProviderSet;
+ ProviderSet programmatic_providers_;
+
+ DISALLOW_COPY_AND_ASSIGN(Scope);
+};
+
+#endif // TOOLS_GN_SCOPE_H_
diff --git a/tools/gn/scope_per_file_provider.cc b/tools/gn/scope_per_file_provider.cc
new file mode 100644
index 0000000..1799d9e
--- /dev/null
+++ b/tools/gn/scope_per_file_provider.cc
@@ -0,0 +1,186 @@
+// 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/scope_per_file_provider.h"
+
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/source_file.h"
+#include "tools/gn/toolchain_manager.h"
+#include "tools/gn/value.h"
+
+const char* ScopePerFileProvider::kDefaultToolchain =
+ "default_toolchain";
+const char* ScopePerFileProvider::kPythonPath =
+ "python_path";
+const char* ScopePerFileProvider::kToolchain =
+ "toolchain";
+const char* ScopePerFileProvider::kRootOutputDirName =
+ "root_output_dir";
+const char* ScopePerFileProvider::kRootGenDirName =
+ "root_gen_dir";
+const char* ScopePerFileProvider::kTargetOutputDirName =
+ "target_output_dir";
+const char* ScopePerFileProvider::kTargetGenDirName =
+ "target_gen_dir";
+const char* ScopePerFileProvider::kRelativeRootOutputDirName =
+ "relative_root_output_dir";
+const char* ScopePerFileProvider::kRelativeRootGenDirName =
+ "relative_root_gen_dir";
+const char* ScopePerFileProvider::kRelativeTargetOutputDirName =
+ "relative_target_output_dir";
+const char* ScopePerFileProvider::kRelativeTargetGenDirName =
+ "relative_target_gen_dir";
+
+ScopePerFileProvider::ScopePerFileProvider(Scope* scope,
+ const SourceFile& source_file)
+ : ProgrammaticProvider(scope),
+ source_file_(source_file) {
+}
+
+ScopePerFileProvider::~ScopePerFileProvider() {
+}
+
+const Value* ScopePerFileProvider::GetProgrammaticValue(
+ const base::StringPiece& ident) {
+ if (ident == kDefaultToolchain)
+ return GetDefaultToolchain();
+ if (ident == kPythonPath)
+ return GetPythonPath();
+
+ if (ident == kTargetOutputDirName)
+ return GetTargetOutputDir();
+ if (ident == kTargetGenDirName)
+ return GetTargetGenDir();
+
+ if (ident == kRelativeRootOutputDirName)
+ return GetRelativeRootOutputDir();
+ if (ident == kRelativeRootGenDirName)
+ return GetRelativeRootGenDir();
+ if (ident == kRelativeTargetOutputDirName)
+ return GetRelativeTargetOutputDir();
+ if (ident == kRelativeTargetGenDirName)
+ return GetRelativeTargetGenDir();
+ return NULL;
+}
+
+// static
+Value ScopePerFileProvider::GetRootOutputDir(const Settings* settings) {
+ return Value(NULL, GetRootOutputDirWithNoLastSlash(settings));
+}
+
+// static
+Value ScopePerFileProvider::GetRootGenDir(const Settings* settings) {
+ return Value(NULL, GetRootGenDirWithNoLastSlash(settings));
+}
+
+const Value* ScopePerFileProvider::GetDefaultToolchain() {
+ if (!default_toolchain_) {
+ const ToolchainManager& toolchain_manager =
+ scope_->settings()->build_settings()->toolchain_manager();
+ default_toolchain_.reset(new Value(NULL,
+ toolchain_manager.GetDefaultToolchainUnlocked().GetUserVisibleName(
+ false)));
+ }
+ return default_toolchain_.get();
+}
+
+const Value* ScopePerFileProvider::GetPythonPath() {
+ if (!python_path_) {
+ python_path_.reset(new Value(NULL,
+ FilePathToUTF8(scope_->settings()->build_settings()->python_path())));
+ }
+ return python_path_.get();
+}
+
+const Value* ScopePerFileProvider::GetToolchain() {
+ if (!toolchain_) {
+ toolchain_.reset(new Value(NULL,
+ scope_->settings()->toolchain()->label().GetUserVisibleName(false)));
+ }
+ return toolchain_.get();
+}
+
+const Value* ScopePerFileProvider::GetTargetOutputDir() {
+ if (!target_output_dir_) {
+ target_output_dir_.reset(new Value(NULL,
+ GetRootOutputDirWithNoLastSlash(scope_->settings()) +
+ GetFileDirWithNoLastSlash()));
+ }
+ return target_output_dir_.get();
+}
+
+const Value* ScopePerFileProvider::GetTargetGenDir() {
+ if (!target_output_dir_) {
+ target_gen_dir_.reset(new Value(NULL,
+ GetRootGenDirWithNoLastSlash(scope_->settings()) +
+ GetFileDirWithNoLastSlash()));
+ }
+ return target_gen_dir_.get();
+}
+
+const Value* ScopePerFileProvider::GetRelativeRootOutputDir() {
+ if (!relative_root_output_dir_) {
+ relative_root_output_dir_.reset(new Value(NULL,
+ GetRelativeRootWithNoLastSlash() +
+ GetRootOutputDirWithNoLastSlash(scope_->settings())));
+ }
+ return relative_root_output_dir_.get();
+}
+
+const Value* ScopePerFileProvider::GetRelativeRootGenDir() {
+ if (!relative_root_gen_dir_) {
+ relative_root_gen_dir_.reset(new Value(NULL,
+ GetRelativeRootWithNoLastSlash() +
+ GetRootGenDirWithNoLastSlash(scope_->settings())));
+ }
+ return relative_root_gen_dir_.get();
+}
+
+const Value* ScopePerFileProvider::GetRelativeTargetOutputDir() {
+ if (!relative_target_output_dir_) {
+ relative_target_output_dir_.reset(new Value(NULL,
+ GetRelativeRootWithNoLastSlash() +
+ GetRootOutputDirWithNoLastSlash(scope_->settings()) + "obj/" +
+ GetFileDirWithNoLastSlash()));
+ }
+ return relative_target_output_dir_.get();
+}
+
+const Value* ScopePerFileProvider::GetRelativeTargetGenDir() {
+ if (!relative_target_gen_dir_) {
+ relative_target_gen_dir_.reset(new Value(NULL,
+ GetRelativeRootWithNoLastSlash() +
+ GetRootGenDirWithNoLastSlash(scope_->settings()) +
+ GetFileDirWithNoLastSlash()));
+ }
+ return relative_target_gen_dir_.get();
+}
+
+// static
+std::string ScopePerFileProvider::GetRootOutputDirWithNoLastSlash(
+ const Settings* settings) {
+ const std::string& output_dir =
+ settings->build_settings()->build_dir().value();
+ CHECK(!output_dir.empty());
+ return output_dir.substr(0, output_dir.size() - 1);
+}
+
+// static
+std::string ScopePerFileProvider::GetRootGenDirWithNoLastSlash(
+ const Settings* settings) {
+ return GetRootOutputDirWithNoLastSlash(settings) + "/gen";
+}
+
+std::string ScopePerFileProvider::GetFileDirWithNoLastSlash() const {
+ std::string dir_value = source_file_.GetDir().value();
+ return dir_value.substr(0, dir_value.size() - 1);
+}
+
+std::string ScopePerFileProvider::GetRelativeRootWithNoLastSlash() const {
+ std::string inverted = InvertDir(source_file_.GetDir());
+ if (inverted.empty())
+ return ".";
+ return inverted.substr(0, inverted.size() - 1);
+}
diff --git a/tools/gn/scope_per_file_provider.h b/tools/gn/scope_per_file_provider.h
new file mode 100644
index 0000000..1edb769
--- /dev/null
+++ b/tools/gn/scope_per_file_provider.h
@@ -0,0 +1,79 @@
+// 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.
+
+#ifndef TOOLS_GN_SCOPE_PER_FILE_PROVIDER_H_
+#define TOOLS_GN_SCOPE_PER_FILE_PROVIDER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/source_file.h"
+
+// ProgrammaticProvider for a scope to provide it with per-file built-in
+// variable support.
+class ScopePerFileProvider : public Scope::ProgrammaticProvider {
+ public:
+ ScopePerFileProvider(Scope* scope, const SourceFile& source_file);
+ virtual ~ScopePerFileProvider();
+
+ // ProgrammaticProvider implementation.
+ virtual const Value* GetProgrammaticValue(
+ const base::StringPiece& ident) OVERRIDE;
+
+ // Returns the value to expose to script for the given thing. These values
+ // are acually set globally, but are put here so we can keep all logic
+ // for converting paths to built-in values in this one file.
+ static Value GetRootOutputDir(const Settings* settings);
+ static Value GetRootGenDir(const Settings* settings);
+
+ // Variable names. These two should be set globally independent of the file
+ // being processed.
+ static const char* kRootOutputDirName;
+ static const char* kRootGenDirName;
+
+ // Variable names. These are provided by this class as needed.
+ static const char* kDefaultToolchain;
+ static const char* kPythonPath;
+ static const char* kToolchain;
+ static const char* kTargetOutputDirName;
+ static const char* kTargetGenDirName;
+ static const char* kRelativeRootOutputDirName;
+ static const char* kRelativeRootGenDirName;
+ static const char* kRelativeTargetOutputDirName;
+ static const char* kRelativeTargetGenDirName;
+
+ private:
+ const Value* GetDefaultToolchain();
+ const Value* GetPythonPath();
+ const Value* GetToolchain();
+ const Value* GetTargetOutputDir();
+ const Value* GetTargetGenDir();
+ const Value* GetRelativeRootOutputDir();
+ const Value* GetRelativeRootGenDir();
+ const Value* GetRelativeTargetOutputDir();
+ const Value* GetRelativeTargetGenDir();
+
+ static std::string GetRootOutputDirWithNoLastSlash(const Settings* settings);
+ static std::string GetRootGenDirWithNoLastSlash(const Settings* settings);
+
+ std::string GetFileDirWithNoLastSlash() const;
+ std::string GetRelativeRootWithNoLastSlash() const;
+
+ SourceFile source_file_;
+
+ // All values are lazily created.
+ scoped_ptr<Value> default_toolchain_;
+ scoped_ptr<Value> python_path_;
+ scoped_ptr<Value> toolchain_;
+ scoped_ptr<Value> target_output_dir_;
+ scoped_ptr<Value> target_gen_dir_;
+ scoped_ptr<Value> relative_root_output_dir_;
+ scoped_ptr<Value> relative_root_gen_dir_;
+ scoped_ptr<Value> relative_target_output_dir_;
+ scoped_ptr<Value> relative_target_gen_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopePerFileProvider);
+};
+
+#endif // TOOLS_GN_SCOPE_PER_FILE_PROVIDER_H_
diff --git a/tools/gn/secondary/base/BUILD.gn b/tools/gn/secondary/base/BUILD.gn
new file mode 100644
index 0000000..2286b19
--- /dev/null
+++ b/tools/gn/secondary/base/BUILD.gn
@@ -0,0 +1,919 @@
+# found in the LICENSE file.
+
+component("base") {
+ sources = [
+ "../build/build_config.h",
+ "third_party/dmg_fp/dmg_fp.h",
+ "third_party/dmg_fp/g_fmt.cc",
+ "third_party/dmg_fp/dtoa_wrapper.cc",
+ "third_party/icu/icu_utf.cc",
+ "third_party/icu/icu_utf.h",
+ "third_party/nspr/prcpucfg.h",
+ "third_party/nspr/prcpucfg_freebsd.h",
+ "third_party/nspr/prcpucfg_linux.h",
+ "third_party/nspr/prcpucfg_mac.h",
+ "third_party/nspr/prcpucfg_nacl.h",
+ "third_party/nspr/prcpucfg_openbsd.h",
+ "third_party/nspr/prcpucfg_solaris.h",
+ "third_party/nspr/prcpucfg_win.h",
+ "third_party/nspr/prtime.cc",
+ "third_party/nspr/prtime.h",
+ "third_party/nspr/prtypes.h",
+ "third_party/xdg_mime/xdgmime.h",
+ "allocator/allocator_extension.cc",
+ "allocator/allocator_extension.h",
+ "allocator/type_profiler_control.cc",
+ "allocator/type_profiler_control.h",
+ "android/activity_status.cc",
+ "android/activity_status.h",
+ "android/base_jni_registrar.cc",
+ "android/base_jni_registrar.h",
+ "android/build_info.cc",
+ "android/build_info.h",
+ "android/cpu_features.cc",
+ "android/fifo_utils.cc",
+ "android/fifo_utils.h",
+ "android/important_file_writer_android.cc",
+ "android/important_file_writer_android.h",
+ "android/scoped_java_ref.cc",
+ "android/scoped_java_ref.h",
+ "android/jni_android.cc",
+ "android/jni_android.h",
+ "android/jni_array.cc",
+ "android/jni_array.h",
+ "android/jni_helper.cc",
+ "android/jni_helper.h",
+ "android/jni_registrar.cc",
+ "android/jni_registrar.h",
+ "android/jni_string.cc",
+ "android/jni_string.h",
+ "android/memory_pressure_listener_android.cc",
+ "android/memory_pressure_listener_android.h",
+ "android/path_service_android.cc",
+ "android/path_service_android.h",
+ "android/path_utils.cc",
+ "android/path_utils.h",
+ "android/sys_utils.cc",
+ "android/sys_utils.h",
+ "android/thread_utils.h",
+ "at_exit.cc",
+ "at_exit.h",
+ "atomic_ref_count.h",
+ "atomic_sequence_num.h",
+ "atomicops.h",
+ "atomicops_internals_gcc.h",
+ "atomicops_internals_mac.h",
+ "atomicops_internals_tsan.h",
+ "atomicops_internals_x86_gcc.cc",
+ "atomicops_internals_x86_gcc.h",
+ "atomicops_internals_x86_msvc.h",
+ "base_export.h",
+ "base_paths.cc",
+ "base_paths.h",
+ "base_paths_android.cc",
+ "base_paths_android.h",
+ "base_paths_mac.h",
+ "base_paths_mac.mm",
+ "base_paths_posix.cc",
+ "base_paths_posix.h",
+ "base_paths_win.cc",
+ "base_paths_win.h",
+ "base_switches.h",
+ "base64.cc",
+ "base64.h",
+ "basictypes.h",
+ "bind.h",
+ "bind_helpers.cc",
+ "bind_helpers.h",
+ "bind_internal.h",
+ "bind_internal_win.h",
+ "bits.h",
+ "build_time.cc",
+ "build_time.h",
+ "callback.h",
+ "callback_helpers.h",
+ "callback_internal.cc",
+ "callback_internal.h",
+ "cancelable_callback.h",
+ "chromeos/chromeos_version.cc",
+ "chromeos/chromeos_version.h",
+ "command_line.cc",
+ "command_line.h",
+ "compiler_specific.h",
+ "containers/hash_tables.h",
+ "containers/linked_list.h",
+ "containers/mru_cache.h",
+ "containers/small_map.h",
+ "containers/stack_container.h",
+ "cpu.cc",
+ "cpu.h",
+ "critical_closure.h",
+ "critical_closure_ios.mm",
+ "debug/alias.cc",
+ "debug/alias.h",
+ "debug/crash_logging.cc",
+ "debug/crash_logging.h",
+ "debug/debug_on_start_win.cc",
+ "debug/debug_on_start_win.h",
+ "debug/debugger.cc",
+ "debug/debugger.h",
+ "debug/debugger_posix.cc",
+ "debug/debugger_win.cc",
+ # This file depends on files from the "allocator" target,
+ # but this target does not depend on "allocator" (see
+ # allocator.gyp for details).
+ "debug/leak_annotations.h",
+ "debug/leak_tracker.h",
+ "debug/proc_maps_linux.cc",
+ "debug/proc_maps_linux.h",
+ "debug/profiler.cc",
+ "debug/profiler.h",
+ "debug/stack_trace.cc",
+ "debug/stack_trace.h",
+ "debug/stack_trace_android.cc",
+ "debug/stack_trace_ios.mm",
+ "debug/stack_trace_posix.cc",
+ "debug/stack_trace_win.cc",
+ "debug/trace_event.h",
+ "debug/trace_event_android.cc",
+ "debug/trace_event_impl.cc",
+ "debug/trace_event_impl.h",
+ "debug/trace_event_impl_constants.cc",
+ "debug/trace_event_win.cc",
+ "deferred_sequenced_task_runner.cc",
+ "deferred_sequenced_task_runner.h",
+ "environment.cc",
+ "environment.h",
+ "file_descriptor_posix.h",
+ "file_util.cc",
+ "file_util.h",
+ "file_util_android.cc",
+ "file_util_linux.cc",
+ "file_util_mac.mm",
+ "file_util_posix.cc",
+ "file_util_win.cc",
+ "file_version_info.h",
+ "file_version_info_mac.h",
+ "file_version_info_mac.mm",
+ "file_version_info_win.cc",
+ "file_version_info_win.h",
+ "files/dir_reader_fallback.h",
+ "files/dir_reader_linux.h",
+ "files/dir_reader_posix.h",
+ "files/file_enumerator.cc",
+ "files/file_enumerator.h",
+ "files/file_enumerator_posix.cc",
+ "files/file_enumerator_win.cc",
+ "files/file_path.cc",
+ "files/file_path.h",
+ "files/file_path_constants.cc",
+ "files/file_path_watcher.cc",
+ "files/file_path_watcher.h",
+ "files/file_path_watcher_kqueue.cc",
+ "files/file_path_watcher_linux.cc",
+ "files/file_path_watcher_stub.cc",
+ "files/file_path_watcher_win.cc",
+ "files/file_util_proxy.cc",
+ "files/file_util_proxy.h",
+ "files/important_file_writer.h",
+ "files/important_file_writer.cc",
+ "files/memory_mapped_file.cc",
+ "files/memory_mapped_file.h",
+ "files/memory_mapped_file_posix.cc",
+ "files/memory_mapped_file_win.cc",
+ "files/scoped_temp_dir.cc",
+ "files/scoped_temp_dir.h",
+ "float_util.h",
+ "format_macros.h",
+ "gtest_prod_util.h",
+ "guid.cc",
+ "guid.h",
+ "guid_posix.cc",
+ "guid_win.cc",
+ "hash.cc",
+ "hash.h",
+ "id_map.h",
+ "ini_parser.cc",
+ "ini_parser.h",
+ "ios/device_util.h",
+ "ios/device_util.mm",
+ "ios/ios_util.h",
+ "ios/ios_util.mm",
+ "ios/scoped_critical_action.h",
+ "ios/scoped_critical_action.mm",
+ "json/json_file_value_serializer.cc",
+ "json/json_file_value_serializer.h",
+ "json/json_parser.cc",
+ "json/json_parser.h",
+ "json/json_reader.cc",
+ "json/json_reader.h",
+ "json/json_string_value_serializer.cc",
+ "json/json_string_value_serializer.h",
+ "json/json_value_converter.h",
+ "json/json_writer.cc",
+ "json/json_writer.h",
+ "json/string_escape.cc",
+ "json/string_escape.h",
+ "lazy_instance.cc",
+ "lazy_instance.h",
+ "location.cc",
+ "location.h",
+ "logging.cc",
+ "logging.h",
+ "logging_win.cc",
+ "logging_win.h",
+ "mac/authorization_util.h",
+ "mac/authorization_util.mm",
+ "mac/bind_objc_block.h",
+ "mac/bundle_locations.h",
+ "mac/bundle_locations.mm",
+ "mac/cocoa_protocols.h",
+ "mac/foundation_util.h",
+ "mac/foundation_util.mm",
+ "mac/launch_services_util.cc",
+ "mac/launch_services_util.h",
+ "mac/launchd.cc",
+ "mac/launchd.h",
+ "mac/libdispatch_task_runner.cc",
+ "mac/libdispatch_task_runner.h",
+ "mac/mac_logging.h",
+ "mac/mac_logging.cc",
+ "mac/mac_util.h",
+ "mac/mac_util.mm",
+ "mac/objc_property_releaser.h",
+ "mac/objc_property_releaser.mm",
+ "mac/os_crash_dumps.cc",
+ "mac/os_crash_dumps.h",
+ "mac/scoped_aedesc.h",
+ "mac/scoped_authorizationref.h",
+ "mac/scoped_block.h",
+ "mac/scoped_cftyperef.h",
+ "mac/scoped_ioobject.h",
+ "mac/scoped_ioplugininterface.h",
+ "mac/scoped_launch_data.h",
+ "mac/scoped_mach_port.cc",
+ "mac/scoped_mach_port.h",
+ "mac/scoped_nsautorelease_pool.h",
+ "mac/scoped_nsautorelease_pool.mm",
+ "mac/scoped_nsexception_enabler.h",
+ "mac/scoped_nsexception_enabler.mm",
+ "mac/scoped_nsobject.h",
+ "mac/scoped_sending_event.h",
+ "mac/scoped_sending_event.mm",
+ "mac/sdk_forward_declarations.h",
+ "memory/aligned_memory.cc",
+ "memory/aligned_memory.h",
+ "memory/discardable_memory.cc",
+ "memory/discardable_memory.h",
+ "memory/discardable_memory_android.cc",
+ "memory/discardable_memory_mac.cc",
+ "memory/linked_ptr.h",
+ "memory/manual_constructor.h",
+ "memory/memory_pressure_listener.cc",
+ "memory/memory_pressure_listener.h",
+ "memory/raw_scoped_refptr_mismatch_checker.h",
+ "memory/ref_counted.cc",
+ "memory/ref_counted.h",
+ "memory/ref_counted_delete_on_message_loop.h",
+ "memory/ref_counted_memory.cc",
+ "memory/ref_counted_memory.h",
+ "memory/scoped_handle.h",
+ "memory/scoped_open_process.h",
+ "memory/scoped_policy.h",
+ "memory/scoped_ptr.h",
+ "memory/scoped_vector.h",
+ "memory/shared_memory.h",
+ "memory/shared_memory_android.cc",
+ "memory/shared_memory_nacl.cc",
+ "memory/shared_memory_posix.cc",
+ "memory/shared_memory_win.cc",
+ "memory/singleton.cc",
+ "memory/singleton.h",
+ "memory/weak_ptr.cc",
+ "memory/weak_ptr.h",
+ "message_loop/message_loop.cc",
+ "message_loop/message_loop.h",
+ "message_loop/message_loop_proxy.cc",
+ "message_loop/message_loop_proxy.h",
+ "message_loop/message_loop_proxy_impl.cc",
+ "message_loop/message_loop_proxy_impl.h",
+ "message_loop/message_pump.cc",
+ "message_loop/message_pump.h",
+ "message_loop/message_pump_android.cc",
+ "message_loop/message_pump_android.h",
+ "message_loop/message_pump_default.cc",
+ "message_loop/message_pump_default.h",
+ "message_loop/message_pump_ozone.cc",
+ "message_loop/message_pump_ozone.h",
+ "message_loop/message_pump_win.cc",
+ "message_loop/message_pump_win.h",
+ "metrics/sample_map.cc",
+ "metrics/sample_map.h",
+ "metrics/sample_vector.cc",
+ "metrics/sample_vector.h",
+ "metrics/bucket_ranges.cc",
+ "metrics/bucket_ranges.h",
+ "metrics/histogram.cc",
+ "metrics/histogram.h",
+ "metrics/histogram_base.cc",
+ "metrics/histogram_base.h",
+ "metrics/histogram_flattener.h",
+ "metrics/histogram_samples.cc",
+ "metrics/histogram_samples.h",
+ "metrics/histogram_snapshot_manager.cc",
+ "metrics/histogram_snapshot_manager.h",
+ "metrics/sparse_histogram.cc",
+ "metrics/sparse_histogram.h",
+ "metrics/statistics_recorder.cc",
+ "metrics/statistics_recorder.h",
+ "metrics/stats_counters.cc",
+ "metrics/stats_counters.h",
+ "metrics/stats_table.cc",
+ "metrics/stats_table.h",
+ "move.h",
+ "native_library.h",
+ "native_library_mac.mm",
+ "native_library_posix.cc",
+ "native_library_win.cc",
+ "nix/mime_util_xdg.cc",
+ "nix/mime_util_xdg.h",
+ "nix/xdg_util.cc",
+ "nix/xdg_util.h",
+ "observer_list.h",
+ "observer_list_threadsafe.h",
+ "os_compat_android.cc",
+ "os_compat_android.h",
+ "os_compat_nacl.cc",
+ "os_compat_nacl.h",
+ "path_service.cc",
+ "path_service.h",
+ "pending_task.cc",
+ "pending_task.h",
+ "pickle.cc",
+ "pickle.h",
+ "platform_file.cc",
+ "platform_file.h",
+ "platform_file_posix.cc",
+ "platform_file_win.cc",
+ "port.h",
+ "posix/eintr_wrapper.h",
+ "posix/global_descriptors.cc",
+ "posix/global_descriptors.h",
+ "posix/unix_domain_socket_linux.cc",
+ "posix/unix_domain_socket_linux.h",
+ "power_monitor/power_monitor.cc",
+ "power_monitor/power_monitor.h",
+ "power_monitor/power_monitor_android.cc",
+ "power_monitor/power_monitor_android.h",
+ "power_monitor/power_monitor_ios.mm",
+ "power_monitor/power_monitor_mac.mm",
+ "power_monitor/power_monitor_posix.cc",
+ "power_monitor/power_monitor_win.cc",
+ "power_monitor/power_observer.h",
+ "process.h",
+ "process_info.h",
+ "process_info_mac.cc",
+ "process_info_win.cc",
+ "process_linux.cc",
+ "process_posix.cc",
+ "process_util.h",
+ "process_win.cc",
+ "process/internal_linux.cc",
+ "process/internal_linux.h",
+ "process/kill.cc",
+ "process/kill.h",
+ "process/kill_mac.cc",
+ "process/kill_posix.cc",
+ "process/kill_win.cc",
+ "process/launch.h",
+ "process/launch_ios.cc",
+ "process/launch_mac.cc",
+ "process/launch_posix.cc",
+ "process/launch_win.cc",
+ "process/memory.h",
+ "process/memory_linux.cc",
+ "process/memory_mac.mm",
+ "process/memory_win.cc",
+ "process/process_handle_freebsd.cc",
+ "process/process_handle_linux.cc",
+ "process/process_handle_mac.cc",
+ "process/process_handle_openbsd.cc",
+ "process/process_handle_posix.cc",
+ "process/process_handle_win.cc",
+ "process/process_iterator.cc",
+ "process/process_iterator.h",
+ "process/process_iterator_freebsd.cc",
+ "process/process_iterator_linux.cc",
+ "process/process_iterator_mac.cc",
+ "process/process_iterator_openbsd.cc",
+ "process/process_iterator_win.cc",
+ "process/process_metrics.h",
+ "process/process_metrics_freebsd.cc",
+ "process/process_metrics_ios.cc",
+ "process/process_metrics_linux.cc",
+ "process/process_metrics_mac.cc",
+ "process/process_metrics_openbsd.cc",
+ "process/process_metrics_posix.cc",
+ "process/process_metrics_win.cc",
+ "profiler/scoped_profile.cc",
+ "profiler/scoped_profile.h",
+ "profiler/alternate_timer.cc",
+ "profiler/alternate_timer.h",
+ "profiler/tracked_time.cc",
+ "profiler/tracked_time.h",
+ "rand_util.cc",
+ "rand_util.h",
+ "rand_util_nacl.cc",
+ "rand_util_posix.cc",
+ "rand_util_win.cc",
+ "run_loop.cc",
+ "run_loop.h",
+ "safe_numerics.h",
+ "safe_strerror_posix.cc",
+ "safe_strerror_posix.h",
+ "scoped_native_library.cc",
+ "scoped_native_library.h",
+ "sequence_checker.h",
+ "sequence_checker_impl.cc",
+ "sequence_checker_impl.h",
+ "sequenced_task_runner.cc",
+ "sequenced_task_runner.h",
+ "sequenced_task_runner_helpers.h",
+ "sha1.h",
+ "sha1_portable.cc",
+ "sha1_win.cc",
+ "single_thread_task_runner.h",
+ "stl_util.h",
+ "strings/latin1_string_conversions.cc",
+ "strings/latin1_string_conversions.h",
+ "strings/nullable_string16.cc",
+ "strings/nullable_string16.h",
+ "strings/string16.cc",
+ "strings/string16.h",
+ "strings/string_number_conversions.cc",
+ "strings/string_split.cc",
+ "strings/string_split.h",
+ "strings/string_number_conversions.h",
+ "strings/string_piece.cc",
+ "strings/string_piece.h",
+ "strings/string_tokenizer.h",
+ "strings/string_util.cc",
+ "strings/string_util.h",
+ "strings/string_util_constants.cc",
+ "strings/string_util_posix.h",
+ "strings/string_util_win.h",
+ "strings/stringize_macros.h",
+ "strings/stringprintf.cc",
+ "strings/stringprintf.h",
+ "strings/sys_string_conversions.h",
+ "strings/sys_string_conversions_mac.mm",
+ "strings/sys_string_conversions_posix.cc",
+ "strings/sys_string_conversions_win.cc",
+ "strings/utf_offset_string_conversions.cc",
+ "strings/utf_offset_string_conversions.h",
+ "strings/utf_string_conversion_utils.cc",
+ "strings/utf_string_conversion_utils.h",
+ "strings/utf_string_conversions.cc",
+ "strings/utf_string_conversions.h",
+ "supports_user_data.cc",
+ "supports_user_data.h",
+ "synchronization/cancellation_flag.cc",
+ "synchronization/cancellation_flag.h",
+ "synchronization/condition_variable.h",
+ "synchronization/condition_variable_posix.cc",
+ "synchronization/condition_variable_win.cc",
+ "synchronization/lock.cc",
+ "synchronization/lock.h",
+ "synchronization/lock_impl.h",
+ "synchronization/lock_impl_posix.cc",
+ "synchronization/lock_impl_win.cc",
+ "synchronization/spin_wait.h",
+ "synchronization/waitable_event.h",
+ "synchronization/waitable_event_posix.cc",
+ "synchronization/waitable_event_watcher.h",
+ "synchronization/waitable_event_watcher_posix.cc",
+ "synchronization/waitable_event_watcher_win.cc",
+ "synchronization/waitable_event_win.cc",
+ "system_monitor/system_monitor.cc",
+ "system_monitor/system_monitor.h",
+ "sys_byteorder.h",
+ "sys_info.cc",
+ "sys_info.h",
+ "sys_info_android.cc",
+ "sys_info_chromeos.cc",
+ "sys_info_freebsd.cc",
+ "sys_info_ios.mm",
+ "sys_info_linux.cc",
+ "sys_info_mac.cc",
+ "sys_info_openbsd.cc",
+ "sys_info_posix.cc",
+ "sys_info_win.cc",
+ "task_runner.cc",
+ "task_runner.h",
+ "task_runner_util.h",
+ "template_util.h",
+ "thread_task_runner_handle.cc",
+ "thread_task_runner_handle.h",
+ "threading/non_thread_safe.h",
+ "threading/non_thread_safe_impl.cc",
+ "threading/non_thread_safe_impl.h",
+ "threading/platform_thread.h",
+ "threading/platform_thread_android.cc",
+ "threading/platform_thread_linux.cc",
+ "threading/platform_thread_mac.mm",
+ "threading/platform_thread_posix.cc",
+ "threading/platform_thread_win.cc",
+ "threading/post_task_and_reply_impl.cc",
+ "threading/post_task_and_reply_impl.h",
+ "threading/sequenced_worker_pool.cc",
+ "threading/sequenced_worker_pool.h",
+ "threading/simple_thread.cc",
+ "threading/simple_thread.h",
+ "threading/thread.cc",
+ "threading/thread.h",
+ "threading/thread_checker.h",
+ "threading/thread_checker_impl.cc",
+ "threading/thread_checker_impl.h",
+ "threading/thread_collision_warner.cc",
+ "threading/thread_collision_warner.h",
+ "threading/thread_id_name_manager.cc",
+ "threading/thread_id_name_manager.h",
+ "threading/thread_local.h",
+ "threading/thread_local_posix.cc",
+ "threading/thread_local_storage.h",
+ "threading/thread_local_storage_posix.cc",
+ "threading/thread_local_storage_win.cc",
+ "threading/thread_local_win.cc",
+ "threading/thread_restrictions.h",
+ "threading/thread_restrictions.cc",
+ "threading/watchdog.cc",
+ "threading/watchdog.h",
+ "threading/worker_pool.h",
+ "threading/worker_pool.cc",
+ "threading/worker_pool_posix.cc",
+ "threading/worker_pool_posix.h",
+ "threading/worker_pool_win.cc",
+ "time/clock.cc",
+ "time/clock.h",
+ "time/default_clock.cc",
+ "time/default_clock.h",
+ "time/default_tick_clock.cc",
+ "time/default_tick_clock.h",
+ "time/tick_clock.cc",
+ "time/tick_clock.h",
+ "time/time.cc",
+ "time/time.h",
+ "time/time_mac.cc",
+ "time/time_posix.cc",
+ "time/time_win.cc",
+ "timer/hi_res_timer_manager_posix.cc",
+ "timer/hi_res_timer_manager_win.cc",
+ "timer/hi_res_timer_manager.h",
+ "timer/timer.cc",
+ "timer/timer.h",
+ "tracked_objects.cc",
+ "tracked_objects.h",
+ "tracking_info.cc",
+ "tracking_info.h",
+ "tuple.h",
+ "values.cc",
+ "values.h",
+ "value_conversions.cc",
+ "value_conversions.h",
+ "version.cc",
+ "version.h",
+ "vlog.cc",
+ "vlog.h",
+ "win/enum_variant.cc",
+ "win/enum_variant.h",
+ "win/event_trace_consumer.h",
+ "win/event_trace_controller.cc",
+ "win/event_trace_controller.h",
+ "win/event_trace_provider.cc",
+ "win/event_trace_provider.h",
+ "win/i18n.cc",
+ "win/i18n.h",
+ "win/iat_patch_function.cc",
+ "win/iat_patch_function.h",
+ "win/iunknown_impl.cc",
+ "win/iunknown_impl.h",
+ "win/message_window.cc",
+ "win/message_window.h",
+ "win/metro.cc",
+ "win/metro.h",
+ "win/object_watcher.cc",
+ "win/object_watcher.h",
+ "win/registry.cc",
+ "win/registry.h",
+ "win/resource_util.cc",
+ "win/resource_util.h",
+ "win/sampling_profiler.cc",
+ "win/sampling_profiler.h",
+ "win/scoped_bstr.cc",
+ "win/scoped_bstr.h",
+ "win/scoped_co_mem.h",
+ "win/scoped_com_initializer.h",
+ "win/scoped_comptr.h",
+ "win/scoped_gdi_object.h",
+ "win/scoped_handle.cc",
+ "win/scoped_handle.h",
+ "win/scoped_hdc.h",
+ "win/scoped_hglobal.h",
+ "win/scoped_process_information.cc",
+ "win/scoped_process_information.h",
+ "win/scoped_propvariant.h",
+ "win/scoped_select_object.h",
+ "win/scoped_variant.cc",
+ "win/scoped_variant.h",
+ "win/shortcut.cc",
+ "win/shortcut.h",
+ "win/startup_information.cc",
+ "win/startup_information.h",
+ "win/text_services_message_filter.cc",
+ "win/text_services_message_filter.h",
+ "win/win_util.cc",
+ "win/win_util.h",
+ "win/windows_version.cc",
+ "win/windows_version.h",
+ "win/wrapped_window_proc.cc",
+ "win/wrapped_window_proc.h",
+ ]
+
+ # TODO(brettw) I don't understand the conditions this file is used.
+ sources -= "files/file_path_watcher_stub.cc"
+
+ sources -= [
+ # TODO(brettw) ozone
+ "message_loop/message_pump_ozone.cc",
+ "message_loop/message_pump_ozone.h",
+
+ "process/process_handle_freebsd.cc",
+ "process/process_handle_openbsd.cc",
+ "process/process_iterator_freebsd.cc",
+ "process/process_iterator_openbsd.cc",
+ "process/process_metrics_freebsd.cc",
+ "process/process_metrics_openbsd.cc",
+ "sys_info_freebsd.cc",
+ "sys_info_openbsd.cc",
+ ]
+
+ if (!is_chromeos) {
+ sources -= [
+ "sys_info_chromeos.cc",
+ ]
+ }
+ if (!is_mac) {
+ sources -= "files/file_path_watcher_kqueue.cc"
+ }
+
+ # Remove nacl stuff.
+ if (!is_nacl) {
+ sources -= [
+ "os_compat_nacl.cc",
+ "os_compat_nacl.h",
+ "rand_util_nacl.cc",
+ "third_party/nspr/prcpucfg_nacl.h",
+ "memory/shared_memory_nacl.cc",
+ ]
+ }
+
+ # Windows stuff.
+ if (is_win && !is_nacl) {
+ sources -= [
+ "strings/string16.cc",
+ # Not using sha1_win.cc because it may have caused a
+ # regression to page cycler moz.
+ "sha1_win.cc",
+ ]
+
+ if (is_component_build) {
+ sources -= "debug/debug_on_start_win.cc"
+ }
+ }
+
+ # Remove non-Mac Unix stuff.
+ if (!is_posix || is_mac) {
+ sources -= [
+ "nix/mime_util_xdg.cc",
+ "nix/mime_util_xdg.h",
+ "nix/xdg_util.cc",
+ "nix/xdg_util.h",
+ ]
+ }
+
+ defines = [
+ "BASE_IMPLEMENTATION",
+ ]
+
+ deps = [
+ ":base_static",
+ "//base/allocator:allocator_extension_thunks",
+ "//third_party/modp_b64",
+ "//base/third_party/dynamic_annotations",
+ ]
+}
+
+# This is the subset of files from base that should not be used with a dynamic
+# library. Note that this library cannot depend on base because base depends on
+# base_static.
+static_library("base_static") {
+ sources = [
+ "base_switches.cc",
+ "base_switches.h",
+ "win/pe_image.cc",
+ "win/pe_image.h",
+ ]
+}
+
+component("base_i18n") {
+ sources = [
+ "i18n/base_i18n_export.h",
+ "i18n/bidi_line_iterator.cc",
+ "i18n/bidi_line_iterator.h",
+ "i18n/break_iterator.cc",
+ "i18n/break_iterator.h",
+ "i18n/char_iterator.cc",
+ "i18n/char_iterator.h",
+ "i18n/case_conversion.cc",
+ "i18n/case_conversion.h",
+ "i18n/file_util_icu.cc",
+ "i18n/file_util_icu.h",
+ "i18n/icu_encoding_detection.cc",
+ "i18n/icu_encoding_detection.h",
+ "i18n/icu_string_conversions.cc",
+ "i18n/icu_string_conversions.h",
+ "i18n/icu_util.cc",
+ "i18n/icu_util.h",
+ "i18n/number_formatting.cc",
+ "i18n/number_formatting.h",
+ "i18n/rtl.cc",
+ "i18n/rtl.h",
+ "i18n/string_compare.cc",
+ "i18n/string_compare.h",
+ "i18n/string_search.cc",
+ "i18n/string_search.h",
+ "i18n/time_formatting.cc",
+ "i18n/time_formatting.h",
+ ]
+ deps = [
+ ":base",
+ "//base/third_party/dynamic_annotations",
+ "//third_party/icu:icui18n",
+ "//third_party/icu:icuuc",
+ ]
+ defines = [
+ "BASE_I18N_IMPLEMENTATION",
+ ]
+ #'conditions': [
+ # ['toolkit_uses_gtk==1', {
+ # 'deps': [
+ # # i18n/rtl.cc uses gtk
+ # '../build/linux/system.gyp:gtk',
+ # ],
+ # }],
+ # ['OS == "win"', {
+ # # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ # 'msvs_disabled_warnings': [
+ # 4267,
+ # ],
+ # }],
+ #],
+ #'export_dependent_settings': [
+ # 'base',
+ #],
+ #'variables': {
+ # 'enable_wexit_time_destructors': 1,
+ # 'optimize': 'max',
+ #},
+}
+
+static_library("test_support_base") {
+ sources = [
+ "perftimer.cc",
+ "test/expectations/expectation.cc",
+ "test/expectations/expectation.h",
+ "test/expectations/parser.cc",
+ "test/expectations/parser.h",
+ "test/mock_chrome_application_mac.h",
+ "test/mock_chrome_application_mac.mm",
+ "test/mock_devices_changed_observer.cc",
+ "test/mock_devices_changed_observer.h",
+ "test/mock_time_provider.cc",
+ "test/mock_time_provider.h",
+ "test/multiprocess_test.cc",
+ "test/multiprocess_test.h",
+ "test/multiprocess_test_android.cc",
+ "test/null_task_runner.cc",
+ "test/null_task_runner.h",
+ "test/perf_test_suite.cc",
+ "test/perf_test_suite.h",
+ "test/scoped_locale.cc",
+ "test/scoped_locale.h",
+ "test/scoped_path_override.cc",
+ "test/scoped_path_override.h",
+ "test/sequenced_task_runner_test_template.cc",
+ "test/sequenced_task_runner_test_template.h",
+ "test/sequenced_worker_pool_owner.cc",
+ "test/sequenced_worker_pool_owner.h",
+ "test/simple_test_clock.cc",
+ "test/simple_test_clock.h",
+ "test/simple_test_tick_clock.cc",
+ "test/simple_test_tick_clock.h",
+ "test/task_runner_test_template.cc",
+ "test/task_runner_test_template.h",
+ "test/test_file_util.h",
+ "test/test_file_util_linux.cc",
+ "test/test_file_util_mac.cc",
+ "test/test_file_util_posix.cc",
+ "test/test_file_util_win.cc",
+ "test/test_listener_ios.h",
+ "test/test_listener_ios.mm",
+ "test/test_pending_task.cc",
+ "test/test_pending_task.h",
+ "test/test_process_killer_win.cc",
+ "test/test_process_killer_win.h",
+ "test/test_reg_util_win.cc",
+ "test/test_reg_util_win.h",
+ "test/test_shortcut_win.cc",
+ "test/test_shortcut_win.h",
+ "test/test_simple_task_runner.cc",
+ "test/test_simple_task_runner.h",
+ "test/test_suite.cc",
+ "test/test_suite.h",
+ "test/test_support_android.cc",
+ "test/test_support_android.h",
+ "test/test_support_ios.h",
+ "test/test_support_ios.mm",
+ "test/test_switches.cc",
+ "test/test_switches.h",
+ "test/test_timeouts.cc",
+ "test/test_timeouts.h",
+ "test/thread_test_helper.cc",
+ "test/thread_test_helper.h",
+ "test/trace_event_analyzer.cc",
+ "test/trace_event_analyzer.h",
+ "test/values_test_util.cc",
+ "test/values_test_util.h",
+ ]
+ deps = [
+ ":base",
+ ":base_static",
+ ":base_i18n",
+ "//testing:gmock",
+ "//testing:gtest",
+ ]
+
+ if (!is_posix) {
+ sources -= [
+ "test/scoped_locale.cc",
+ "test/scoped_locale.h",
+ ]
+ }
+ if (is_ios) {
+ # Pull in specific Mac files for iOS (which have been filtered out
+ # by file name rules).
+ { # Temporarily override the assignment filter in a new scope.
+ set_sources_assignment_filter([])
+ sources += "test/test_file_util_mac.cc"
+ }
+ }
+ #if (!is_bsd) {
+ # sources -= "test/test_file_util_linux.cc"
+ #}
+ #if (use_gtk) {
+ # deps += "/build/linux/system:gtk"
+ #}
+ #export_dependent_settings [
+ # 'base',
+ #]
+}
+
+config("perf_test_config") {
+ defines = [ "PERF_TEST" ]
+}
+
+static_library("test_support_perf") {
+ sources = [
+ "perftimer.cc",
+ "test/run_all_perftests.cc",
+ ]
+ deps = [
+ ":base",
+ "//testing:gtest",
+ ]
+
+ direct_dependent_configs = [ ":perf_test_config" ]
+
+ #if (toolkit_uses_gtk) {
+ # deps += "/build/linux/system:gtk",
+ #}
+}
+
+static_library("run_all_unittests") {
+ sources = [
+ "test/run_all_unittests.cc",
+ ]
+ deps = [
+ ":test_support_base",
+ ]
+}
diff --git a/tools/gn/secondary/base/allocator/BUILD.gn b/tools/gn/secondary/base/allocator/BUILD.gn
new file mode 100644
index 0000000..bbcfe65
--- /dev/null
+++ b/tools/gn/secondary/base/allocator/BUILD.gn
@@ -0,0 +1,6 @@
+static_library("allocator_extension_thunks") {
+ sources = [
+ "allocator_extension_thunks.cc",
+ "allocator_extension_thunks.h",
+ ]
+}
diff --git a/tools/gn/secondary/base/third_party/dynamic_annotations/BUILD.gn b/tools/gn/secondary/base/third_party/dynamic_annotations/BUILD.gn
new file mode 100644
index 0000000..e3939c35
--- /dev/null
+++ b/tools/gn/secondary/base/third_party/dynamic_annotations/BUILD.gn
@@ -0,0 +1,7 @@
+static_library("dynamic_annotations") {
+ sources = [
+ "dynamic_annotations.c",
+ "dynamic_annotations.h",
+ "../valgrind/valgrind.h",
+ ]
+}
diff --git a/tools/gn/secondary/build/config/BUILD.gn b/tools/gn/secondary/build/config/BUILD.gn
new file mode 100644
index 0000000..74c57af
--- /dev/null
+++ b/tools/gn/secondary/build/config/BUILD.gn
@@ -0,0 +1,52 @@
+config("my_msvs") {
+ includes = [ "../.." ]
+ cflags = [
+ "/Od", "/WX", "/Zi", "/Gy", "/GS", "/RTC1", "/EHsc",
+ ]
+ defines = [
+ "CHROMIUM_BUILD",
+ "TOOLKIT_VIEWS=1",
+ "USE_LIBJPEG_TURBO=1",
+ "ENABLE_ONE_CLICK_SIGNIN",
+ "ENABLE_REMOTING=1",
+ "ENABLE_WEBRTC=1",
+ "ENABLE_CONFIGURATION_POLICY",
+ "ENABLE_INPUT_SPEECH",
+ "ENABLE_NOTIFICATIONS",
+ "ENABLE_GPU=1",
+ "ENABLE_EGLIMAGE=1",
+ "ENABLE_TASK_MANAGER=1",
+ "ENABLE_EXTENSIONS=1",
+ "ENABLE_PLUGIN_INSTALLATION=1",
+ "ENABLE_PLUGINS=1",
+ "ENABLE_SESSION_SERVICE=1",
+ "ENABLE_THEMES=1",
+ "ENABLE_AUTOFILL_DIALOG=1",
+ "ENABLE_BACKGROUND=1",
+ "ENABLE_AUTOMATION=1",
+ "ENABLE_GOOGLE_NOW=1",
+ "ENABLE_LANGUAGE_DETECTION=1",
+ "ENABLE_PRINTING=1",
+ "ENABLE_CAPTIVE_PORTAL_DETECTION=1",
+ "ENABLE_APP_LIST=1",
+ "ENABLE_MESSAGE_CENTER=1",
+ "ENABLE_SETTINGS_APP=1",
+ "ENABLE_MANAGED_USERS=1",
+ ]
+}
+
+config("feature_flags") {
+ #defines =
+}
+
+config("debug") {
+ defines = [
+ "_DEBUG",
+ "DYNAMIC_ANNOTATIONS_ENABLED=1",
+ "WTF_USE_DYNAMIC_ANNOTATIONS=1",
+ ]
+}
+
+config("release") {
+
+}
diff --git a/tools/gn/secondary/build/config/BUILDCONFIG.gn b/tools/gn/secondary/build/config/BUILDCONFIG.gn
new file mode 100644
index 0000000..0245597
--- /dev/null
+++ b/tools/gn/secondary/build/config/BUILDCONFIG.gn
@@ -0,0 +1,187 @@
+# =============================================================================
+# BUILD FLAGS
+# =============================================================================
+#
+# This block lists input arguments to the build, along with their default
+# values. GN requires listing them explicitly so it can validate input and have
+# a central place to manage the build flags.
+#
+# If a value is specified on the command line, it will overwrite the defaults
+# given here, otherwise the default will be injected into the root scope.
+#
+# KEEP IN ALPHABETICAL ORDER and write a good description for everything.
+# Use "is_*" names for intrinsic platform descriptions and build modes, and
+# "use_*" names for optional features libraries, and configurations.
+declare_args() {
+ is_component_build = 1
+ is_chromeos = 0
+ is_debug = 1
+ use_ash = 0
+ use_aura = 0
+ use_ozone = 0
+}
+
+# =============================================================================
+# SOURCES FILTERS
+# =============================================================================
+#
+# These patterns filter out platform-specific files when assigning to the
+# sources variable. The magic variable |sources_assignment_filter| is applied
+# to each assignment or appending to the sources variable and matches are
+# automatcally removed.
+#
+# We define lists of filters for each platform for all builds so they can
+# be used by individual targets if necessary (a target can always change
+# sources_assignment_filter on itself if it needs something more specific).
+#
+# Note that the patterns are NOT regular expressions. Only "*" and "\b" (path
+# boundary = end of string or slash) are supported, and the entire string
+# muct match the pattern (so you need "*.cc" to match all .cc files, for
+# example).
+
+windows_sources_filters = [
+ "*_win.cc",
+ "*_win.h",
+ "*_win_unittest.cc",
+ "*\bwin/*",
+]
+mac_sources_filters = [
+ "*_mac.h",
+ "*_mac.cc",
+ "*_mac.mm",
+ "*_mac_unittest.h",
+ "*_mac_unittest.cc",
+ "*_mac_unittest.mm",
+ "*\bmac/*",
+ "*_cocoa.h",
+ "*_cocoa.cc",
+ "*_cocoa.mm",
+ "*_cocoa_unittest.h",
+ "*_cocoa_unittest.cc",
+ "*_cocoa_unittest.mm",
+ "*\bcocoa/*",
+]
+ios_sources_filters = [
+ "*_ios.h",
+ "*_ios.cc",
+ "*_ios.mm",
+ "*_ios_unittest.h",
+ "*_ios_unittest.cc",
+ "*_ios_unittest.mm",
+ "*\bios/*",
+]
+objective_c_sources_filters = [
+ "*.mm",
+]
+linux_sources_filters = [
+ "*_linux.h",
+ "*_linux.cc",
+ "*_linux_unittest.h",
+ "*_linux_unittest.cc",
+ "*\blinux/*",
+]
+android_sources_filters = [
+ "*_android.h",
+ "*_android.cc",
+ "*_android_unittest.h",
+ "*_android_unittest.cc",
+ "*\bandroid/*",
+]
+posix_sources_filters = [
+ "*_posix.h",
+ "*_posix.cc",
+ "*_posix_unittest.h",
+ "*_posix_unittest.cc",
+ "*\bposix/*",
+]
+
+# Construct the full list of sources we're using for this platform.
+sources_assignment_filter = []
+if (is_win) {
+ sources_assignment_filter += posix_sources_filters
+} else {
+ sources_assignment_filter += windows_sources_filters
+}
+if (!is_mac) {
+ sources_assignment_filter += mac_sources_filters
+}
+if (!is_ios) {
+ sources_assignment_filter += ios_sources_filters
+}
+if (!is_mac && !is_ios) {
+ sources_assignment_filter += objective_c_sources_filters
+}
+if (!is_linux) {
+ sources_assignment_filter += linux_sources_filters
+}
+if (!is_android) {
+ sources_assignment_filter += android_sources_filters
+}
+
+# This is the actual set.
+set_sources_assignment_filter(sources_assignment_filter)
+
+# =============================================================================
+# SYSTEM CONFIG
+# =============================================================================
+
+is_nacl = 0
+
+# =============================================================================
+# BUILD OPTIONS
+# =============================================================================
+
+if (is_component_build) {
+ component_mode = "shared_library"
+} else {
+ component_mode = "static_library"
+}
+
+# =============================================================================
+# TARGET DEFAULTS
+# =============================================================================
+#
+# Set up the default configuration for every build target of the given type.
+# The values configured here will be automatically set on the scope of the
+# corresponding target. Target definitions can add or remove to the settings
+# here as needed.
+
+# Holds all configs used for making native executables and libraries, to avoid
+# duplication in each target below.
+native_compiler_configs = [
+ "//build/config:my_msvs", # TODO(brettw) eraseme
+
+ "//build/config/compiler:chromium_code",
+ "//build/config/compiler:disable_annoying_warnings",
+ "//build/config/compiler:no_rtti",
+ "//build/config/compiler:runtime_library",
+]
+if (is_win) {
+ native_compiler_configs += "//build/config/win:sdk"
+}
+
+if (is_debug) {
+ native_compiler_configs += "//build/config:debug"
+} else {
+ native_compiler_configs += "//build/config::release"
+}
+
+set_defaults("executable") {
+ configs = native_compiler_configs
+}
+
+set_defaults("static_library") {
+ configs = native_compiler_configs
+}
+
+set_defaults("shared_library") {
+ configs = native_compiler_configs
+}
+
+# ==============================================================================
+# TOOLCHAIN SETUP
+# ==============================================================================
+
+if (is_win) {
+ set_default_toolchain("//build/config/win:32")
+}
diff --git a/tools/gn/secondary/build/config/compiler/BUILD.gn b/tools/gn/secondary/build/config/compiler/BUILD.gn
new file mode 100644
index 0000000..0487828
--- /dev/null
+++ b/tools/gn/secondary/build/config/compiler/BUILD.gn
@@ -0,0 +1,134 @@
+# 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.
+
+# runtime_library -------------------------------------------------------------
+#
+# Sets the runtime library and associated options.
+#
+# We don't bother making multiple versions that are toggle-able since there
+# is more than one axis of control (which makes it complicated) and there's
+# no practical reason for anybody to change this since the CRT must agree.
+
+config("runtime_library") {
+ if (is_component_build) {
+ # Component mode: dynamic CRT.
+ defines = [ "COMPONENT_BUILD" ]
+ if (is_win) {
+ # Since the library is shared, it requires exceptions or will give errors
+ # about things not matching, so keep exceptions on.
+ if (is_debug) {
+ cflags = [ "/MDd" ]
+ } else {
+ cflags = [ "/MD" ]
+ }
+ }
+ } else {
+ # Static CRT.
+ if (is_win) {
+ # We don't use exceptions, and when we link statically we can just get
+ # rid of them entirely.
+ defines = [ "_HAS_EXCEPTIONS=0" ]
+ if (is_debug) {
+ cflags = [ "/MTd" ]
+ } else {
+ cflags = [ "/MT" ]
+ }
+ }
+ }
+
+ if (is_win) {
+ defines += [
+ "__STD_C",
+ "__STDC_CONSTANT_MACROS",
+ "__STDC_FORMAT_MACROS",
+ "_CRT_RAND_S",
+ "_CRT_SECURE_NO_DEPRECATE",
+ "_SCL_SECURE_NO_DEPRECATE",
+ "_UNICODE",
+ "UNICODE",
+ ]
+ }
+}
+
+# chromium_code ---------------------------------------------------------------
+#
+# Toggles between higher and lower warnings for code that is (or isn't)
+# part of Chromium.
+
+config("chromium_code") {
+ if (is_win) {
+ cflags = [
+ "/W4", # Warning level 4.
+ ]
+ }
+}
+config("no_chromium_code") {
+ if (is_win) {
+ cflags = [
+ "/W3", # Warning level 3.
+ "/wd4800", # Disable warning when forcing value to bool.
+ ]
+ defines = [
+ "_CRT_NONSTDC_NO_WARNINGS",
+ "_CRT_NONSTDC_NO_DEPRECATE",
+ ]
+ }
+}
+
+# rtti ------------------------------------------------------------------------
+#
+# Allows turning Run-Time Type Identification on or off.
+
+config("rtti") {
+ if (is_win) {
+ cflags = [ "/GR" ]
+ }
+}
+config("no_rtti") {
+ if (is_win) {
+ cflags = [ "/GR-" ]
+ }
+}
+
+# Warnings ---------------------------------------------------------------------
+
+config("disable_annoying_warnings") {
+ if (is_win) {
+ # Please keep ordered and add names if you add more.
+ cflags = [
+ "/wd4018", # Comparing signed and unsigned values.
+ "/wd4100", # Unreferenced formal function parameter.
+ "/wd4121", # Alignment of a member was sensitive to packing.
+ "/wd4125", # Decimal digit terminates octal escape sequence.
+ "/wd4127", # Conditional expression is constant.
+ "/wd4130", # Logical operation on address of string constant.
+ # TODO(brettw) is this necessary? If so, it should probably be on whoever
+ # is silly enough to be doing this rather than globally.
+ #"/wd4131", # Function uses old-style declarator.
+ "/wd4189", # A variable was declared and initialized but never used.
+ "/wd4201", # Nonstandard extension used: nameless struct/union.
+ "/wd4238", # Nonstandard extension used: class rvalue used as lvalue.
+ "/wd4244", # Conversion: possible loss of data.
+ "/wd4245", # Conversion: signed/unsigned mismatch,
+ "/wd4251", # Class needs to have dll-interface.
+ "/wd4310", # Cast truncates constant value.
+ "/wd4351", # Elements of array will be default initialized.
+ "/wd4355", # 'this' used in base member initializer list.
+ "/wd4396", # Inline friend template thing.
+ "/wd4428", # Universal character name encountered in source.
+ "/wd4481", # Nonstandard extension: override specifier.
+ "/wd4503", # Decorated name length exceeded, name was truncated.
+ "/wd4505", # Unreferenced local function has been removed.
+ "/wd4510", # Default constructor could not be generated.
+ "/wd4512", # Assignment operator could not be generated.
+ "/wd4530", # Exception handler used, but unwind semantics not enabled.
+ "/wd4610", # Class can never be instantiated, constructor required.
+ "/wd4611", # C++ object destruction and 'catch'.
+ "/wd4701", # Potentially uninitialized local variable name used.
+ "/wd4702", # Unreachable code.
+ "/wd4706", # Assignment within conditional expression.
+ "/wd4819", # Character not in the current code page.
+ ]
+ }
+}
diff --git a/tools/gn/secondary/build/config/win/BUILD.gn b/tools/gn/secondary/build/config/win/BUILD.gn
new file mode 100644
index 0000000..955504a
--- /dev/null
+++ b/tools/gn/secondary/build/config/win/BUILD.gn
@@ -0,0 +1,184 @@
+# 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.
+
+# Should only be running on Windows.
+assert(is_win)
+
+# Setup the Visual Studio state.
+#
+# Its argument is the location to write the environment files.
+# It will write "environment.x86" and "environment.x64" to this directory,
+# and return a list to us.
+#
+# The list contains the include path as its only element. (I'm expecting to
+# add more so it's currently a list inside a list.)
+msvc_config = [["foo"]]
+#exec_script("get_msvc_config.py",
+ # [relative_root_output_dir],
+ # "value")
+
+# 32-bit toolchain -------------------------------------------------------------
+
+toolchain("32") {
+ tool("cc") {
+ command = "ninja -t msvc -e \$arch -- cl.exe /nologo /showIncludes /FC @\$out.rsp /c \$in /Fo\$out /Fd\$pdbname"
+ description = "CC \$out"
+ rspfile = "\$out.rsp"
+ rspfile_content = "\$defines \$includes \$cflags \$cflags_c"
+ deps = "msvc"
+ }
+ tool("cxx") {
+ command = "ninja -t msvc -e \$arch -- cl.exe /nologo /showIncludes /FC @\$out.rsp /c \$in /Fo\$out /Fd\$pdbname"
+ description = "CXX \$out"
+ rspfile = "\$out.rsp"
+ rspfile_content = "\$defines \$includes \$cflags \$cflags_cc"
+ deps = "msvc"
+ }
+ #tool("idl") {
+ # command = $python_path gyp-win-tool midl-wrapper \$arch \$outdir \$tlb \$h \$dlldata \$iid \$
+ # \$proxy \$in \$idlflags
+ # description = IDL \$in
+ #}
+ #tool("rc") {
+ # command = $python_path gyp-win-tool rc-wrapper \$arch rc.exe \$defines \$includes \$rcflags \$
+ # /fo\$out \$in
+ # description = RC \$in
+ #}
+ #tool("asm") {
+ # command = $python_path gyp-win-tool asm-wrapper \$arch ml.exe \$defines \$includes /c /Fo \$
+ # \$out \$in
+ # description = ASM \$in
+ #}
+ tool("alink") {
+ command = "$python_path gyp-win-tool link-wrapper \$arch lib.exe /nologo /ignore:4221 /OUT:\$out @\$out.rsp"
+ description = "LIB \$out"
+ rspfile = "\$out.rsp"
+ rspfile_content = "\$in_newline \$libflags"
+ }
+ #tool("solink_embed_inc") {
+ # command = cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$implibflag \$
+ # /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp && $python_path gyp-win-tool \$
+ # manifest-wrapper \$arch cmd /c if exist \$dll.manifest del \$dll.manifest && \$
+ # $python_path gyp-win-tool manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests \$
+ # -out:\$dll.manifest && $python_path gyp-win-tool manifest-to-rc \$arch \$dll.manifest \$
+ # \$dll.manifest.rc 2 && $python_path gyp-win-tool rc-wrapper \$arch rc.exe \$
+ # \$dll.manifest.rc && $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$
+ # \$implibflag /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp \$dll.manifest.res
+ # description = LINK_EMBED_INC(DLL) \$dll
+ # restat = 1
+ # rspfile = \$dll.rsp
+ # rspfile_content = \$libs \$in_newline \$ldflags
+ #}
+ #tool("solink_module_embed_inc") {
+ # command = cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$implibflag \$
+ # /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp && $python_path gyp-win-tool \$
+ # manifest-wrapper \$arch cmd /c if exist \$dll.manifest del \$dll.manifest && \$
+ # $python_path gyp-win-tool manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests \$
+ # -out:\$dll.manifest && $python_path gyp-win-tool manifest-to-rc \$arch \$dll.manifest \$
+ # \$dll.manifest.rc 2 && $python_path gyp-win-tool rc-wrapper \$arch rc.exe \$
+ # \$dll.manifest.rc && $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$
+ # \$implibflag /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp \$dll.manifest.res
+ # description = LINK_EMBED_INC(DLL) \$dll
+ # restat = 1
+ # rspfile = \$dll.rsp
+ # rspfile_content = \$libs \$in_newline \$ldflags
+ #}
+ #rule link_embed_inc
+ # command = cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo /OUT:\$out \$
+ # /PDB:\$out.pdb @\$out.rsp && $python_path gyp-win-tool manifest-wrapper \$arch cmd /c \$
+ # if exist \$out.manifest del \$out.manifest && $python_path gyp-win-tool \$
+ # manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests -out:\$out.manifest && \$
+ # $python_path gyp-win-tool manifest-to-rc \$arch \$out.manifest \$out.manifest.rc 1 && \$
+ # $python_path gyp-win-tool rc-wrapper \$arch rc.exe \$out.manifest.rc && \$
+ # $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo /OUT:\$out /PDB:\$out.pdb \$
+ # @\$out.rsp \$out.manifest.res
+ # description = LINK_EMBED_INC \$out
+ # rspfile = \$out.rsp
+ # rspfile_content = \$in_newline \$libs \$ldflags
+ #rule solink_embed
+ # command = cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$implibflag \$
+ # /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp && $python_path gyp-win-tool \$
+ # manifest-wrapper \$arch cmd /c if exist \$dll.manifest del \$dll.manifest && \$
+ # $python_path gyp-win-tool manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests \$
+ # -outputresource:\$dll;2
+ # description = LINK_EMBED(DLL) \$dll
+ # restat = 1
+ # rspfile = \$dll.rsp
+ # rspfile_content = \$libs \$in_newline \$ldflags
+ #rule solink_module_embed
+ # command = cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$implibflag \$
+ # /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp && $python_path gyp-win-tool \$
+ # manifest-wrapper \$arch cmd /c if exist \$dll.manifest del \$dll.manifest && \$
+ # $python_path gyp-win-tool manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests \$
+ # -outputresource:\$dll;2
+ # description = LINK_EMBED(DLL) \$dll
+ # restat = 1
+ # rspfile = \$dll.rsp
+ # rspfile_content = \$libs \$in_newline \$ldflags
+ #rule link_embed
+ # command = cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo /OUT:\$out \$
+ # /PDB:\$out.pdb @\$out.rsp && $python_path gyp-win-tool manifest-wrapper \$arch cmd /c \$
+ # if exist \$out.manifest del \$out.manifest && $python_path gyp-win-tool \$
+ # manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests -outputresource:\$out;1
+ # description = LINK_EMBED \$out
+ # rspfile = \$out.rsp
+ # rspfile_content = \$in_newline \$libs \$ldflags
+ tool("solink") {
+ command = "cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$implibflag /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp && $python_path gyp-win-tool manifest-wrapper \$arch cmd /c if exist \$dll.manifest del \$dll.manifest && $python_path gyp-win-tool manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests -out:\$dll.manifest"
+ description = "LINK(DLL) \$dll"
+ restat = "1"
+ rspfile = "\$dll.rsp"
+ rspfile_content = "\$libs \$in_newline \$ldflags"
+ }
+ tool("solink_module") {
+ command = "cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$implibflag /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp && $python_path gyp-win-tool manifest-wrapper \$arch cmd /c if exist \$dll.manifest del \$dll.manifest && $python_path gyp-win-tool manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests -out:\$dll.manifet"
+ description = "LINK(DLL) \$dll"
+ restat = "1"
+ rspfile = "\$dll.rsp"
+ rspfile_content = "\$libs \$in_newline \$ldflags"
+ }
+ tool("link") {
+ command = "cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo /OUT:\$out /PDB:\$out.pdb @\$out.rsp && $python_path gyp-win-tool manifest-wrapper \$arch cmd /c if exist \$out.manifest del \$out.manifest && $python_path gyp-win-tool manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests -out:\$out.manifest"
+ description = "LINK \$out"
+ rspfile = "\$out.rsp"
+ rspfile_content = "\$in_newline \$libs \$ldflags"
+ }
+ tool("stamp") {
+ command = "$python_path gyp-win-tool stamp \$out"
+ description = "STAMP \$out"
+ }
+ tool("copy") {
+ command = "$python_path gyp-win-tool recursive-mirror \$in \$out"
+ description = "COPY \$in \$out"
+ }
+}
+
+# 64-bit toolchain -------------------------------------------------------------
+
+toolchain("64") {
+}
+
+# SDK setup --------------------------------------------------------------------
+
+config("sdk") {
+ # The include path is the stuff returned by the script plus out own WTL
+ # checkout.
+ # TODO(brettw) should adding WTL be at this level or should it be more on
+ # a per-project basis?
+ includes = msvc_config[0] + "../../third_party/wtl/include"
+
+ defines = [
+ "_ATL_NO_OPENGL",
+ "_SECURE_ATL",
+ "_WIN32_WINNT=0x0602",
+ "_WINDOWS",
+ "CERT_CHAIN_PARA_HAS_EXTRA_FIELDS",
+ "NOMINMAX",
+ "NTDDI_VERSION=0x06020000",
+ "PSAPI_VERSION=1",
+ "WIN32",
+ "WIN32_LEAN_AND_MEAN",
+ "WINVER=0x0602",
+ ]
+}
diff --git a/tools/gn/secondary/build/config/win/get_msvc_config.py b/tools/gn/secondary/build/config/win/get_msvc_config.py
new file mode 100644
index 0000000..01380cd
--- /dev/null
+++ b/tools/gn/secondary/build/config/win/get_msvc_config.py
@@ -0,0 +1,77 @@
+# 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.
+
+# This file returns the MSVC config used by the Windows build.
+# It's a bit hardcoded right now. I suspect we want to build this functionality
+# into GN itself in the future.
+
+import sys
+
+# This script expects one parameter: the path to the root output directory.
+
+# TODO(brettw): do escaping.
+def FormatStringForGN(x):
+ return '"' + x + '"'
+
+def PrintListOfStrings(x):
+ print '['
+ for i in x:
+ print FormatStringForGN(i) + ', '
+ print ']'
+
+# GN wants system-absolutepaths to begin in slashes.
+sdk_root = '/C:\\Program Files (x86)\\Windows Kits\\8.0\\'
+vs_root = '/C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\'
+
+def GetIncludes():
+ return [
+ sdk_root + 'Include\\shared',
+ sdk_root + 'Include\\um',
+ sdk_root + 'Include\\winrt',
+ vs_root + 'VC\\atlmfc\\include'
+ ]
+
+def _FormatAsEnvironmentBlock(envvar_dict):
+ """Format as an 'environment block' directly suitable for CreateProcess.
+ Briefly this is a list of key=value\0, terminated by an additional \0. See
+ CreateProcess documentation for more details."""
+ block = ''
+ nul = '\0'
+ for key, value in envvar_dict.iteritems():
+ block += key + '=' + value + nul
+ block += nul
+ return block
+
+def WriteEnvFile(file_path, values):
+ f = open(file_path, "w")
+ f.write(_FormatAsEnvironmentBlock(values))
+
+includes = GetIncludes()
+
+# Write the environment files.
+WriteEnvFile(sys.argv[1] + '\\environment.x86',
+ { 'TMP': 'C:\\Users\\brettw\\AppData\\Local\\Temp',
+ 'SYSTEMROOT': 'C:\\Windows',
+ 'TEMP': 'C:\\Users\\brettw\\AppData\\Local\\Temp',
+ 'LIB': 'c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\lib;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\lib;',
+ 'LIBPATH': 'C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB;C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB;',
+ 'PATH': 'C:\\apps\\depot_tools\\python_bin;c:\\Program Files (x86)\\Microsoft F#\\v4.0\\;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VSTSDB\\Deploy;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\IDE\\;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\BIN;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\Tools;C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\VCPackages;C:\\Program Files (x86)\\HTML Help Workshop;C:\\Program Files (x86)\\HTML Help Workshop;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin\\NETFX 4.0 Tools;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin;C:\\apps\\depot_tools\\python_bin;C:\\apps\\depot_tools\\;C:\\apps\\depot_tools\\;C:\\apps\\depot_tools\\;c:\\Program Files (x86)\\Microsoft F#\\v4.0\\;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VSTSDB\\Deploy;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\IDE\\;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\BIN;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\Tools;C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\VCPackages;C:\\Program Files (x86)\\HTML Help Workshop;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin\\NETFX 4.0 Tools;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\windows\\corpam;C:\\python_26_amd64\\files;C:\\Windows\\ccmsetup;c:\\Program Files (x86)\\Microsoft SQL Server\\100\\Tools\\Binn\\;c:\\Program Files\\Microsoft SQL Server\\100\\Tools\\Binn\\;c:\\Program Files\\Microsoft SQL Server\\100\\DTS\\Binn\\;c:\\cygwin\\bin;C:\\apps\\;C:\\apps\\depot_tools;C:\\Program Files (x86)\\Windows Kits\\8.0\\Windows Performance Toolkit\\;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\Google\\Cert Installer;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\Google\\google_appengine\\',
+ 'PATHEXT': '=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC',
+ 'INCLUDE': 'c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\INCLUDE;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\include;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\INCLUDE;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\include;'})
+
+WriteEnvFile(sys.argv[1] + '\\environment.x64',
+ { 'TMP': 'C:\\Users\\brettw\\AppData\\Local\\Temp',
+ 'SYSTEMROOT': 'C:\\Windows',
+ 'TEMP': 'C:\\Users\\brettw\\AppData\\Local\\Temp',
+ 'LIB': 'c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB\\amd64;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB\\amd64;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\lib\\x64;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\lib;',
+ 'LIBPATH': 'C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework64\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB\\amd64;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB\\amd64;C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB;',
+ 'PATH': 'C:\\apps\\depot_tools\\python_bin;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\BIN\\amd64;C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework64\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\VCPackages;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\IDE;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\Tools;C:\\Program Files (x86)\\HTML Help Workshop;C:\\Program Files (x86)\\HTML Help Workshop;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin\\NETFX 4.0 Tools\\x64;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin\\x64;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin;C:\\apps\\depot_tools\\python_bin;C:\\apps\\depot_tools\\;C:\\apps\\depot_tools\\;C:\\apps\\depot_tools\\;c:\\Program Files (x86)\\Microsoft F#\\v4.0\\;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VSTSDB\\Deploy;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\IDE\\;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\BIN;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\Tools;C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\VCPackages;C:\\Program Files (x86)\\HTML Help Workshop;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin\\NETFX 4.0 Tools;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\windows\\corpam;C:\\python_26_amd64\\files;C:\\Windows\\ccmsetup;c:\\Program Files (x86)\\Microsoft SQL Server\\100\\Tools\\Binn\\;c:\\Program Files\\Microsoft SQL Server\\100\\Tools\\Binn\\;c:\\Program Files\\Microsoft SQL Server\\100\\DTS\\Binn\\;c:\\cygwin\\bin;C:\\apps\\;C:\\apps\\depot_tools;C:\\Program Files (x86)\\Windows Kits\\8.0\\Windows Performance Toolkit\\;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\Google\\Cert Installer;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\Google\\google_appengine\\',
+ 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC',
+ 'INCLUDE': 'c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\INCLUDE;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\include;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\INCLUDE;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\include;'})
+
+# Return the includes and such.
+print '['
+PrintListOfStrings(includes)
+print ']'
+
diff --git a/tools/gn/secondary/build/config/win/get_msvc_config_real.py b/tools/gn/secondary/build/config/win/get_msvc_config_real.py
new file mode 100644
index 0000000..a209d7f
--- /dev/null
+++ b/tools/gn/secondary/build/config/win/get_msvc_config_real.py
@@ -0,0 +1,575 @@
+# 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.
+
+# This file copies the logic from GYP to find the MSVC configuration. It's not
+# currently used because it is too slow. We will probably build this
+# functionality into the C++ code in the future.
+
+"""Handle version information related to Visual Stuio."""
+
+import errno
+import os
+import re
+import subprocess
+import sys
+
+class VisualStudioVersion(object):
+ """Information regarding a version of Visual Studio."""
+
+ def __init__(self, short_name, description,
+ solution_version, project_version, flat_sln, uses_vcxproj,
+ path, sdk_based, default_toolset=None):
+ self.short_name = short_name
+ self.description = description
+ self.solution_version = solution_version
+ self.project_version = project_version
+ self.flat_sln = flat_sln
+ self.uses_vcxproj = uses_vcxproj
+ self.path = path
+ self.sdk_based = sdk_based
+ self.default_toolset = default_toolset
+
+ def ShortName(self):
+ return self.short_name
+
+ def Description(self):
+ """Get the full description of the version."""
+ return self.description
+
+ def SolutionVersion(self):
+ """Get the version number of the sln files."""
+ return self.solution_version
+
+ def ProjectVersion(self):
+ """Get the version number of the vcproj or vcxproj files."""
+ return self.project_version
+
+ def FlatSolution(self):
+ return self.flat_sln
+
+ def UsesVcxproj(self):
+ """Returns true if this version uses a vcxproj file."""
+ return self.uses_vcxproj
+
+ def ProjectExtension(self):
+ """Returns the file extension for the project."""
+ return self.uses_vcxproj and '.vcxproj' or '.vcproj'
+
+ def Path(self):
+ """Returns the path to Visual Studio installation."""
+ return self.path
+
+ def ToolPath(self, tool):
+ """Returns the path to a given compiler tool. """
+ return os.path.normpath(os.path.join(self.path, "VC/bin", tool))
+
+ def DefaultToolset(self):
+ """Returns the msbuild toolset version that will be used in the absence
+ of a user override."""
+ return self.default_toolset
+
+ def SetupScript(self, target_arch):
+ """Returns a command (with arguments) to be used to set up the
+ environment."""
+ # Check if we are running in the SDK command line environment and use
+ # the setup script from the SDK if so. |target_arch| should be either
+ # 'x86' or 'x64'.
+ assert target_arch in ('x86', 'x64')
+ sdk_dir = os.environ.get('WindowsSDKDir')
+ if self.sdk_based and sdk_dir:
+ return [os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.Cmd')),
+ '/' + target_arch]
+ else:
+ # We don't use VC/vcvarsall.bat for x86 because vcvarsall calls
+ # vcvars32, which it can only find if VS??COMNTOOLS is set, which it
+ # isn't always.
+ if target_arch == 'x86':
+ return [os.path.normpath(
+ os.path.join(self.path, 'Common7/Tools/vsvars32.bat'))]
+ else:
+ assert target_arch == 'x64'
+ arg = 'x86_amd64'
+ if (os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or
+ os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64'):
+ # Use the 64-on-64 compiler if we can.
+ arg = 'amd64'
+ return [os.path.normpath(
+ os.path.join(self.path, 'VC/vcvarsall.bat')), arg]
+
+
+def _RegistryQueryBase(sysdir, key, value):
+ """Use reg.exe to read a particular key.
+
+ While ideally we might use the win32 module, we would like gyp to be
+ python neutral, so for instance cygwin python lacks this module.
+
+ Arguments:
+ sysdir: The system subdirectory to attempt to launch reg.exe from.
+ key: The registry key to read from.
+ value: The particular value to read.
+ Return:
+ stdout from reg.exe, or None for failure.
+ """
+ # Skip if not on Windows or Python Win32 setup issue
+ if sys.platform not in ('win32', 'cygwin'):
+ return None
+ # Setup params to pass to and attempt to launch reg.exe
+ cmd = [os.path.join(os.environ.get('WINDIR', ''), sysdir, 'reg.exe'),
+ 'query', key]
+ if value:
+ cmd.extend(['/v', value])
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ # Obtain the stdout from reg.exe, reading to the end so p.returncode is valid
+ # Note that the error text may be in [1] in some cases
+ text = p.communicate()[0]
+ # Check return code from reg.exe; officially 0==success and 1==error
+ if p.returncode:
+ return None
+ return text
+
+
+def _RegistryQuery(key, value=None):
+ """Use reg.exe to read a particular key through _RegistryQueryBase.
+
+ First tries to launch from %WinDir%\Sysnative to avoid WoW64 redirection. If
+ that fails, it falls back to System32. Sysnative is available on Vista and
+ up and available on Windows Server 2003 and XP through KB patch 942589. Note
+ that Sysnative will always fail if using 64-bit python due to it being a
+ virtual directory and System32 will work correctly in the first place.
+
+ KB 942589 - http://support.microsoft.com/kb/942589/en-us.
+
+ Arguments:
+ key: The registry key.
+ value: The particular registry value to read (optional).
+ Return:
+ stdout from reg.exe, or None for failure.
+ """
+ text = None
+ try:
+ text = _RegistryQueryBase('Sysnative', key, value)
+ except OSError, e:
+ if e.errno == errno.ENOENT:
+ text = _RegistryQueryBase('System32', key, value)
+ else:
+ raise
+ return text
+
+
+def _RegistryGetValue(key, value):
+ """Use reg.exe to obtain the value of a registry key.
+
+ Args:
+ key: The registry key.
+ value: The particular registry value to read.
+ Return:
+ contents of the registry key's value, or None on failure.
+ """
+ text = _RegistryQuery(key, value)
+ if not text:
+ return None
+ # Extract value.
+ match = re.search(r'REG_\w+\s+([^\r]+)\r\n', text)
+ if not match:
+ return None
+ return match.group(1)
+
+
+def _RegistryKeyExists(key):
+ """Use reg.exe to see if a key exists.
+
+ Args:
+ key: The registry key to check.
+ Return:
+ True if the key exists
+ """
+ if not _RegistryQuery(key):
+ return False
+ return True
+
+
+def _CreateVersion(name, path, sdk_based=False):
+ """Sets up MSVS project generation.
+
+ Setup is based off the GYP_MSVS_VERSION environment variable or whatever is
+ autodetected if GYP_MSVS_VERSION is not explicitly specified. If a version is
+ passed in that doesn't match a value in versions python will throw a error.
+ """
+ if path:
+ path = os.path.normpath(path)
+ versions = {
+ '2013': VisualStudioVersion('2013',
+ 'Visual Studio 2013',
+ solution_version='13.00',
+ project_version='4.0',
+ flat_sln=False,
+ uses_vcxproj=True,
+ path=path,
+ sdk_based=sdk_based,
+ default_toolset='v110'),
+ '2013e': VisualStudioVersion('2013e',
+ 'Visual Studio 2013',
+ solution_version='13.00',
+ project_version='4.0',
+ flat_sln=True,
+ uses_vcxproj=True,
+ path=path,
+ sdk_based=sdk_based,
+ default_toolset='v110'),
+ '2012': VisualStudioVersion('2012',
+ 'Visual Studio 2012',
+ solution_version='12.00',
+ project_version='4.0',
+ flat_sln=False,
+ uses_vcxproj=True,
+ path=path,
+ sdk_based=sdk_based,
+ default_toolset='v110'),
+ '2012e': VisualStudioVersion('2012e',
+ 'Visual Studio 2012',
+ solution_version='12.00',
+ project_version='4.0',
+ flat_sln=True,
+ uses_vcxproj=True,
+ path=path,
+ sdk_based=sdk_based,
+ default_toolset='v110'),
+ '2010': VisualStudioVersion('2010',
+ 'Visual Studio 2010',
+ solution_version='11.00',
+ project_version='4.0',
+ flat_sln=False,
+ uses_vcxproj=True,
+ path=path,
+ sdk_based=sdk_based),
+ '2010e': VisualStudioVersion('2010e',
+ 'Visual Studio 2010',
+ solution_version='11.00',
+ project_version='4.0',
+ flat_sln=True,
+ uses_vcxproj=True,
+ path=path,
+ sdk_based=sdk_based),
+ '2008': VisualStudioVersion('2008',
+ 'Visual Studio 2008',
+ solution_version='10.00',
+ project_version='9.00',
+ flat_sln=False,
+ uses_vcxproj=False,
+ path=path,
+ sdk_based=sdk_based),
+ '2008e': VisualStudioVersion('2008e',
+ 'Visual Studio 2008',
+ solution_version='10.00',
+ project_version='9.00',
+ flat_sln=True,
+ uses_vcxproj=False,
+ path=path,
+ sdk_based=sdk_based),
+ '2005': VisualStudioVersion('2005',
+ 'Visual Studio 2005',
+ solution_version='9.00',
+ project_version='8.00',
+ flat_sln=False,
+ uses_vcxproj=False,
+ path=path,
+ sdk_based=sdk_based),
+ '2005e': VisualStudioVersion('2005e',
+ 'Visual Studio 2005',
+ solution_version='9.00',
+ project_version='8.00',
+ flat_sln=True,
+ uses_vcxproj=False,
+ path=path,
+ sdk_based=sdk_based),
+ }
+ return versions[str(name)]
+
+
+def _ConvertToCygpath(path):
+ """Convert to cygwin path if we are using cygwin."""
+ if sys.platform == 'cygwin':
+ p = subprocess.Popen(['cygpath', path], stdout=subprocess.PIPE)
+ path = p.communicate()[0].strip()
+ return path
+
+
+def _DetectVisualStudioVersions(versions_to_check, force_express):
+ """Collect the list of installed visual studio versions.
+
+ Returns:
+ A list of visual studio versions installed in descending order of
+ usage preference.
+ Base this on the registry and a quick check if devenv.exe exists.
+ Only versions 8-10 are considered.
+ Possibilities are:
+ 2005(e) - Visual Studio 2005 (8)
+ 2008(e) - Visual Studio 2008 (9)
+ 2010(e) - Visual Studio 2010 (10)
+ 2012(e) - Visual Studio 2012 (11)
+ 2013(e) - Visual Studio 2013 (11)
+ Where (e) is e for express editions of MSVS and blank otherwise.
+ """
+ version_to_year = {
+ '8.0': '2005',
+ '9.0': '2008',
+ '10.0': '2010',
+ '11.0': '2012',
+ '12.0': '2013',
+ }
+ versions = []
+ for version in versions_to_check:
+ # Old method of searching for which VS version is installed
+ # We don't use the 2010-encouraged-way because we also want to get the
+ # path to the binaries, which it doesn't offer.
+ keys = [r'HKLM\Software\Microsoft\VisualStudio\%s' % version,
+ r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\%s' % version,
+ r'HKLM\Software\Microsoft\VCExpress\%s' % version,
+ r'HKLM\Software\Wow6432Node\Microsoft\VCExpress\%s' % version]
+ for index in range(len(keys)):
+ path = _RegistryGetValue(keys[index], 'InstallDir')
+ if not path:
+ continue
+ path = _ConvertToCygpath(path)
+ # Check for full.
+ full_path = os.path.join(path, 'devenv.exe')
+ express_path = os.path.join(path, 'vcexpress.exe')
+ if not force_express and os.path.exists(full_path):
+ # Add this one.
+ versions.append(_CreateVersion(version_to_year[version],
+ os.path.join(path, '..', '..')))
+ # Check for express.
+ elif os.path.exists(express_path):
+ # Add this one.
+ versions.append(_CreateVersion(version_to_year[version] + 'e',
+ os.path.join(path, '..', '..')))
+
+ # The old method above does not work when only SDK is installed.
+ keys = [r'HKLM\Software\Microsoft\VisualStudio\SxS\VC7',
+ r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\SxS\VC7']
+ for index in range(len(keys)):
+ path = _RegistryGetValue(keys[index], version)
+ if not path:
+ continue
+ path = _ConvertToCygpath(path)
+ versions.append(_CreateVersion(version_to_year[version] + 'e',
+ os.path.join(path, '..'), sdk_based=True))
+
+ return versions
+
+
+def SelectVisualStudioVersion(version='auto'):
+ """Select which version of Visual Studio projects to generate.
+
+ Arguments:
+ version: Hook to allow caller to force a particular version (vs auto).
+ Returns:
+ An object representing a visual studio project format version.
+ """
+ # In auto mode, check environment variable for override.
+ if version == 'auto':
+ version = os.environ.get('GYP_MSVS_VERSION', 'auto')
+ version_map = {
+ 'auto': ('10.0', '9.0', '8.0', '11.0'),
+ '2005': ('8.0',),
+ '2005e': ('8.0',),
+ '2008': ('9.0',),
+ '2008e': ('9.0',),
+ '2010': ('10.0',),
+ '2010e': ('10.0',),
+ '2012': ('11.0',),
+ '2012e': ('11.0',),
+ '2013': ('12.0',),
+ '2013e': ('12.0',),
+ }
+ override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH')
+ if override_path:
+ msvs_version = os.environ.get('GYP_MSVS_VERSION')
+ if not msvs_version or 'e' not in msvs_version:
+ raise ValueError('GYP_MSVS_OVERRIDE_PATH requires GYP_MSVS_VERSION to be '
+ 'set to an "e" version (e.g. 2010e)')
+ return _CreateVersion(msvs_version, override_path, sdk_based=True)
+ version = str(version)
+ versions = _DetectVisualStudioVersions(version_map[version], 'e' in version)
+ if not versions:
+ if version == 'auto':
+ # Default to 2005 if we couldn't find anything
+ return _CreateVersion('2005', None)
+ else:
+ return _CreateVersion(version, None)
+ return versions[0]
+
+def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags, open_out):
+ """It's not sufficient to have the absolute path to the compiler, linker,
+ etc. on Windows, as those tools rely on .dlls being in the PATH. We also
+ need to support both x86 and x64 compilers within the same build (to support
+ msvs_target_platform hackery). Different architectures require a different
+ compiler binary, and different supporting environment variables (INCLUDE,
+ LIB, LIBPATH). So, we extract the environment here, wrap all invocations
+ of compiler tools (cl, link, lib, rc, midl, etc.) via win_tool.py which
+ sets up the environment, and then we do not prefix the compiler with
+ an absolute path, instead preferring something like "cl.exe" in the rule
+ which will then run whichever the environment setup has put in the path.
+ When the following procedure to generate environment files does not
+ meet your requirement (e.g. for custom toolchains), you can pass
+ "-G ninja_use_custom_environment_files" to the gyp to suppress file
+ generation and use custom environment files prepared by yourself."""
+ archs = ('x86', 'x64')
+ if generator_flags.get('ninja_use_custom_environment_files', 0):
+ cl_paths = {}
+ for arch in archs:
+ cl_paths[arch] = 'cl.exe'
+ return cl_paths
+ vs = GetVSVersion(generator_flags)
+ cl_paths = {}
+ for arch in archs:
+ # Extract environment variables for subprocesses.
+ args = vs.SetupScript(arch)
+ args.extend(('&&', 'set'))
+ popen = subprocess.Popen(
+ args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ variables, _ = popen.communicate()
+ env = _ExtractImportantEnvironment(variables)
+ env_block = _FormatAsEnvironmentBlock(env)
+ f = open_out(os.path.join(toplevel_build_dir, 'environment.' + arch), 'wb')
+ f.write(env_block)
+ f.close()
+
+ # Find cl.exe location for this architecture.
+ args = vs.SetupScript(arch)
+ args.extend(('&&',
+ 'for', '%i', 'in', '(cl.exe)', 'do', '@echo', 'LOC:%~$PATH:i'))
+ popen = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE)
+ output, _ = popen.communicate()
+ cl_paths[arch] = _ExtractCLPath(output)
+ return cl_paths
+
+def OpenOutput(path, mode='w'):
+ """Open |path| for writing, creating directories if necessary."""
+ try:
+ os.makedirs(os.path.dirname(path))
+ except OSError:
+ pass
+ return open(path, mode)
+
+vs_version = None
+def GetVSVersion(generator_flags):
+ global vs_version
+ if not vs_version:
+ vs_version = SelectVisualStudioVersion(
+ generator_flags.get('msvs_version', 'auto'))
+ return vs_version
+
+def _ExtractImportantEnvironment(output_of_set):
+ """Extracts environment variables required for the toolchain to run from
+ a textual dump output by the cmd.exe 'set' command."""
+ envvars_to_save = (
+ 'goma_.*', # TODO(scottmg): This is ugly, but needed for goma.
+ 'include',
+ 'lib',
+ 'libpath',
+ 'path',
+ 'pathext',
+ 'systemroot',
+ 'temp',
+ 'tmp',
+ )
+ env = {}
+ for line in output_of_set.splitlines():
+ for envvar in envvars_to_save:
+ if re.match(envvar + '=', line.lower()):
+ var, setting = line.split('=', 1)
+ if envvar == 'path':
+ # Our own rules (for running gyp-win-tool) and other actions in
+ # Chromium rely on python being in the path. Add the path to this
+ # python here so that if it's not in the path when ninja is run
+ # later, python will still be found.
+ setting = os.path.dirname(sys.executable) + os.pathsep + setting
+ env[var.upper()] = setting
+ break
+ for required in ('SYSTEMROOT', 'TEMP', 'TMP'):
+ if required not in env:
+ raise Exception('Environment variable "%s" '
+ 'required to be set to valid path' % required)
+ return env
+
+def _FormatAsEnvironmentBlock(envvar_dict):
+ """Format as an 'environment block' directly suitable for CreateProcess.
+ Briefly this is a list of key=value\0, terminated by an additional \0. See
+ CreateProcess documentation for more details."""
+ block = ''
+ nul = '\0'
+ for key, value in envvar_dict.iteritems():
+ block += key + '=' + value + nul
+ block += nul
+ return block
+
+
+def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags):
+ """It's not sufficient to have the absolute path to the compiler, linker,
+ etc. on Windows, as those tools rely on .dlls being in the PATH. We also
+ need to support both x86 and x64 compilers within the same build (to support
+ msvs_target_platform hackery). Different architectures require a different
+ compiler binary, and different supporting environment variables (INCLUDE,
+ LIB, LIBPATH). So, we extract the environment here, wrap all invocations
+ of compiler tools (cl, link, lib, rc, midl, etc.) via win_tool.py which
+ sets up the environment, and then we do not prefix the compiler with
+ an absolute path, instead preferring something like "cl.exe" in the rule
+ which will then run whichever the environment setup has put in the path.
+ When the following procedure to generate environment files does not
+ meet your requirement (e.g. for custom toolchains), you can pass
+ "-G ninja_use_custom_environment_files" to the gyp to suppress file
+ generation and use custom environment files prepared by yourself."""
+ archs = ('x86', 'x64')
+ if generator_flags.get('ninja_use_custom_environment_files', 0):
+ cl_paths = {}
+ for arch in archs:
+ cl_paths[arch] = 'cl.exe'
+ return cl_paths
+ vs = GetVSVersion(generator_flags)
+ cl_paths = {}
+ for arch in archs:
+ # Extract environment variables for subprocesses.
+ args = vs.SetupScript(arch)
+ args.extend(('&&', 'set'))
+ popen = subprocess.Popen(
+ args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ variables, _ = popen.communicate()
+ env = _ExtractImportantEnvironment(variables)
+ env_block = _FormatAsEnvironmentBlock(env)
+ f = OpenOutput(os.path.join(toplevel_build_dir, 'environment.' + arch), 'wb')
+ f.write(env_block)
+ f.close()
+
+ # Find cl.exe location for this architecture.
+ args = vs.SetupScript(arch)
+ args.extend(('&&',
+ 'for', '%i', 'in', '(cl.exe)', 'do', '@echo', 'LOC:%~$PATH:i'))
+ popen = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE)
+ output, _ = popen.communicate()
+ cl_paths[arch] = _ExtractCLPath(output)
+ return cl_paths
+
+def _ExtractCLPath(output_of_where):
+ """Gets the path to cl.exe based on the output of calling the environment
+ setup batch file, followed by the equivalent of `where`."""
+ # Take the first line, as that's the first found in the PATH.
+ for line in output_of_where.strip().splitlines():
+ if line.startswith('LOC:'):
+ return line[len('LOC:'):].strip()
+
+#print SelectVisualStudioVersion().DefaultToolset()
+#GenerateEnvironmentFiles("D:\\src\\src1\\src\\out\\gn\\eraseme", {})
+#print '"', GetVSVersion({}).Path(), '"'
+print '"', GetVSVersion({}).sdk_based, '"'
+
+#-------------------------------------------------------------------------------
+
+version_info = {
+ '2010': {
+ 'includes': [
+ 'VC\\atlmfc\\include',
+ ],
+ },
+}
diff --git a/tools/gn/secondary/ipc/BUILD.gn b/tools/gn/secondary/ipc/BUILD.gn
new file mode 100644
index 0000000..5e55ee9
--- /dev/null
+++ b/tools/gn/secondary/ipc/BUILD.gn
@@ -0,0 +1,168 @@
+# Copyright (c) 2012 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.
+
+component("ipc") {
+ sources = [
+ "file_descriptor_set_posix.cc",
+ "file_descriptor_set_posix.h",
+ "ipc_channel.cc",
+ "ipc_channel.h",
+ "ipc_channel_factory.cc",
+ "ipc_channel_factory.h",
+ "ipc_channel_handle.h",
+ "ipc_channel_nacl.cc",
+ "ipc_channel_nacl.h",
+ "ipc_channel_posix.cc",
+ "ipc_channel_posix.h",
+ "ipc_channel_proxy.cc",
+ "ipc_channel_proxy.h",
+ "ipc_channel_reader.cc",
+ "ipc_channel_reader.h",
+ "ipc_channel_win.cc",
+ "ipc_channel_win.h",
+ "ipc_descriptors.h",
+ "ipc_export.h",
+ "ipc_forwarding_message_filter.cc",
+ "ipc_forwarding_message_filter.h",
+ "ipc_listener.h",
+ "ipc_logging.cc",
+ "ipc_logging.h",
+ "ipc_message.cc",
+ "ipc_message.h",
+ "ipc_message_macros.h",
+ "ipc_message_start.h",
+ "ipc_message_utils.cc",
+ "ipc_message_utils.h",
+ "ipc_param_traits.h",
+ "ipc_platform_file.cc",
+ "ipc_platform_file.h",
+ "ipc_sender.h",
+ "ipc_switches.cc",
+ "ipc_switches.h",
+ "ipc_sync_channel.cc",
+ "ipc_sync_channel.h",
+ "ipc_sync_message.cc",
+ "ipc_sync_message.h",
+ "ipc_sync_message_filter.cc",
+ "ipc_sync_message_filter.h",
+ "param_traits_log_macros.h",
+ "param_traits_macros.h",
+ "param_traits_read_macros.h",
+ "param_traits_write_macros.h",
+ "struct_constructor_macros.h",
+ "struct_destructor_macros.h",
+ "unix_domain_socket_util.cc",
+ "unix_domain_socket_util.h",
+ ]
+
+ #if (!is_untrusted_nacl) {
+ sources -= [
+ "ipc_channel_nacl.cc",
+ "ipc_channel_nacl.h",
+ ]
+
+ if (is_win || is_ios) {
+ sources -= [
+ "ipc_channel_factory.cc",
+ "unix_domain_socket_util.cc",
+ ]
+ }
+
+ defines = [ "IPC_IMPLEMENTATION" ]
+
+ deps = [
+ "//base",
+ # TODO(viettrungluu): Needed for base/lazy_instance.h, which is suspect.
+ "//base/third_party/dynamic_annotations",
+ ]
+}
+
+test("ipc_tests") {
+ sources = [
+ "file_descriptor_set_posix_unittest.cc",
+ "ipc_channel_posix_unittest.cc",
+ "ipc_channel_unittest.cc",
+ "ipc_fuzzing_tests.cc",
+ "ipc_message_unittest.cc",
+ "ipc_message_utils_unittest.cc",
+ "ipc_send_fds_test.cc",
+ "ipc_sync_channel_unittest.cc",
+ "ipc_sync_message_unittest.cc",
+ "ipc_sync_message_unittest.h",
+ "ipc_test_base.cc",
+ "ipc_test_base.h",
+ "sync_socket_unittest.cc",
+ "unix_domain_socket_util_unittest.cc",
+ ]
+
+ #if (toolkit_uses_gtk) {
+ # deps += "/build/linux/system:gtk"
+ #}
+ if (is_win || is_ios) {
+ sources -= "unix_domain_socket_util_unittest.cc"
+ }
+ #if (is_android && gtest_target_type == "shared_library") {
+ # deps += "/testing/android/native_test.gyp:native_testNative_code"
+ #}
+ #if (is_posix && !is_mac && !is_android) {
+ # if (linux_use_tcmalloc) {
+ # deps += "/base/allocator"
+ # }
+ #}
+
+ deps = [
+ ":ipc",
+ ":test_support_ipc",
+ "//base",
+ "//base:base_i18n",
+ "//base:run_all_unittests",
+ "//base:test_support_base",
+ "//testing:gtest",
+ ]
+}
+
+test("ipc_perftests") {
+ sources = [
+ "ipc_perftests.cc",
+ "ipc_test_base.cc",
+ "ipc_test_base.h",
+ ]
+
+ #if (toolkit_uses_gtk) {
+ # deps += "/build/linux/system:gtk"
+ #}
+ #if (is_android && gtest_target_type == "shared_library") {
+ # deps += "/testing/android/native_test.gyp:native_testNative_code"
+ #}
+ #if (is_posix && !is_mac && !is_android) {
+ # if (linux_use_tcmalloc) {
+ # deps += "/base/allocator"
+ # }
+ #}
+
+ deps = [
+ ":ipc",
+ ":test_support_ipc",
+ "//base",
+ "//base:base_i18n",
+ "//base:test_support_base",
+ "//base:test_support_perf",
+ "//testing:gtest",
+ ]
+}
+
+static_library("test_support_ipc") {
+ sources = [
+ "ipc_multiprocess_test.cc",
+ "ipc_multiprocess_test.h",
+ "ipc_test_sink.cc",
+ "ipc_test_sink.h",
+ ]
+ deps = [
+ ":ipc",
+ "//base",
+ "//testing:gtest",
+ ]
+}
+
diff --git a/tools/gn/secondary/testing/BUILD.gn b/tools/gn/secondary/testing/BUILD.gn
new file mode 100644
index 0000000..b54ceba
--- /dev/null
+++ b/tools/gn/secondary/testing/BUILD.gn
@@ -0,0 +1,44 @@
+# 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.
+
+static_library("gtest") {
+ sources = [
+ "gtest/include/gtest/gtest-death-test.h",
+ "gtest/include/gtest/gtest-message.h",
+ "gtest/include/gtest/gtest-param-test.h",
+ "gtest/include/gtest/gtest-printers.h",
+ "gtest/include/gtest/gtest-spi.h",
+ "gtest/include/gtest/gtest-test-part.h",
+ "gtest/include/gtest/gtest-typed-test.h",
+ "gtest/include/gtest/gtest.h",
+ "gtest/include/gtest/gtest_pred_impl.h",
+ "gtest/include/gtest/internal/gtest-death-test-internal.h",
+ "gtest/include/gtest/internal/gtest-filepath.h",
+ "gtest/include/gtest/internal/gtest-internal.h",
+ "gtest/include/gtest/internal/gtest-linked_ptr.h",
+ "gtest/include/gtest/internal/gtest-param-util-generated.h",
+ "gtest/include/gtest/internal/gtest-param-util.h",
+ "gtest/include/gtest/internal/gtest-port.h",
+ "gtest/include/gtest/internal/gtest-string.h",
+ "gtest/include/gtest/internal/gtest-tuple.h",
+ "gtest/include/gtest/internal/gtest-type-util.h",
+ #"gtest/src/gtest-all.cc", # Not needed by our build.
+ "gtest/src/gtest-death-test.cc",
+ "gtest/src/gtest-filepath.cc",
+ "gtest/src/gtest-internal-inl.h",
+ "gtest/src/gtest-port.cc",
+ "gtest/src/gtest-printers.cc",
+ "gtest/src/gtest-test-part.cc",
+ "gtest/src/gtest-typed-test.cc",
+ "gtest/src/gtest.cc",
+ "multiprocess_func_list.cc",
+ "multiprocess_func_list.h",
+ "platform_test.h",
+ ]
+
+}
+
+static_library("gmock") {
+
+}
diff --git a/tools/gn/secondary/third_party/modp_b64/BUILD.gn b/tools/gn/secondary/third_party/modp_b64/BUILD.gn
new file mode 100644
index 0000000..5598f12
--- /dev/null
+++ b/tools/gn/secondary/third_party/modp_b64/BUILD.gn
@@ -0,0 +1,11 @@
+# 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.
+
+static_library("modp_b64") {
+ sources = [
+ "modp_b64.cc",
+ "modp_b64.h",
+ "modp_b64_data.h",
+ ]
+}
diff --git a/tools/gn/settings.cc b/tools/gn/settings.cc
new file mode 100644
index 0000000..82014a7
--- /dev/null
+++ b/tools/gn/settings.cc
@@ -0,0 +1,38 @@
+// 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/settings.h"
+
+#include "base/logging.h"
+#include "tools/gn/filesystem_utils.h"
+
+Settings::Settings(const BuildSettings* build_settings,
+ const Toolchain* toolchain,
+ const std::string& output_subdir_name)
+ : build_settings_(build_settings),
+ toolchain_(toolchain),
+ target_os_(WIN), // FIXME(brettw) set this properly.
+ import_manager_(),
+ base_config_(this) {
+ DCHECK(output_subdir_name.find('/') == std::string::npos);
+ if (output_subdir_name.empty()) {
+ toolchain_output_dir_ = build_settings->build_dir();
+ } else {
+ // We guarantee this ends in a slash.
+ toolchain_output_subdir_.value().append(output_subdir_name);
+ toolchain_output_subdir_.value().push_back('/');
+
+ toolchain_output_dir_ = SourceDir(build_settings->build_dir().value() +
+ toolchain_output_subdir_.value());
+ }
+ // The output dir will be null in some tests and when invoked to parsed
+ // one-off data without doing generation.
+ if (!toolchain_output_dir_.is_null())
+ toolchain_gen_dir_ = SourceDir(toolchain_output_dir_.value() + "gen/");
+}
+
+Settings::~Settings() {
+}
+
+
diff --git a/tools/gn/settings.h b/tools/gn/settings.h
new file mode 100644
index 0000000..93a1ae6
--- /dev/null
+++ b/tools/gn/settings.h
@@ -0,0 +1,107 @@
+// 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.
+
+#ifndef TOOLS_GN_SETTINGS_H_
+#define TOOLS_GN_SETTINGS_H_
+
+#include "base/files/file_path.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/import_manager.h"
+#include "tools/gn/output_file.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/source_dir.h"
+#include "tools/gn/toolchain.h"
+
+// Holds the settings for one toolchain invocation. There will be one
+// Settings object for each toolchain type, each referring to the same
+// BuildSettings object for shared stuff.
+//
+// The Settings object is const once it is constructed, which allows us to
+// use it from multiple threads during target generation without locking (which
+// is important, because it gets used a lot).
+//
+// The Toolchain object holds the set of stuff that is set by the toolchain
+// declaration, which obviously needs to be set later when we actually parse
+// the file with the toolchain declaration in it.
+class Settings {
+ public:
+ enum TargetOS {
+ UNKNOWN,
+ LINUX,
+ MAC,
+ WIN
+ };
+
+ // Constructs a toolchain settings. The output_subdir_name is the name we
+ // should use for the subdirectory in the build output directory for this
+ // toolchain's outputs. It should have no slashes in it. The default
+ // toolchain should use an empty string.
+ Settings(const BuildSettings* build_settings,
+ const Toolchain* toolchain,
+ const std::string& output_subdir_name);
+ ~Settings();
+
+ const BuildSettings* build_settings() const { return build_settings_; }
+
+ // Danger: this must only be used for getting the toolchain label until the
+ // toolchain has been resolved. Otherwise, it will be modified on an
+ // arbitrary thread when the toolchain invocation is found. Generally, you
+ // will only read this from the target generation where we know everything
+ // has been resolved and won't change.
+ const Toolchain* toolchain() const { return toolchain_; }
+
+ bool IsMac() const { return target_os_ == MAC; }
+ bool IsWin() const { return target_os_ == WIN; }
+
+ TargetOS target_os() const { return target_os_; }
+ void set_target_os(TargetOS t) { target_os_ = t; }
+
+ const OutputFile& toolchain_output_subdir() const {
+ return toolchain_output_subdir_;
+ }
+ const SourceDir& toolchain_output_dir() const {
+ return toolchain_output_dir_;
+ }
+
+ // Directory for generated files.
+ const SourceDir& toolchain_gen_dir() const {
+ return toolchain_gen_dir_;
+ }
+
+ // The import manager caches the result of executing imported files in the
+ // context of a given settings object.
+ //
+ // See the ItemTree getter in GlobalSettings for why this doesn't return a
+ // const pointer.
+ ImportManager& import_manager() const { return import_manager_; }
+
+ const Scope* base_config() const { return &base_config_; }
+ Scope* base_config() { return &base_config_; }
+
+ private:
+ const BuildSettings* build_settings_;
+
+ const Toolchain* toolchain_;
+
+ TargetOS target_os_;
+
+ mutable ImportManager import_manager_;
+
+ // The subdirectory inside the build output for this toolchain. For the
+ // default toolchain, this will be empty (since the deafult toolchain's
+ // output directory is the same as the build directory). When nonempty, this
+ // is guaranteed to end in a slash.
+ OutputFile toolchain_output_subdir_;
+
+ // Full source file path to the toolchain output directory.
+ SourceDir toolchain_output_dir_;
+
+ SourceDir toolchain_gen_dir_;
+
+ Scope base_config_;
+
+ DISALLOW_COPY_AND_ASSIGN(Settings);
+};
+
+#endif // TOOLS_GN_SETTINGS_H_
diff --git a/tools/gn/setup.cc b/tools/gn/setup.cc
new file mode 100644
index 0000000..f93cd4d
--- /dev/null
+++ b/tools/gn/setup.cc
@@ -0,0 +1,195 @@
+// 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/setup.h"
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/parser.h"
+#include "tools/gn/source_dir.h"
+#include "tools/gn/source_file.h"
+#include "tools/gn/tokenizer.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+// More logging.
+const char kSwitchVerbose[] = "v";
+
+const char kSwitchRoot[] = "root";
+const char kSecondarySource[] = "secondary";
+
+const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
+
+base::FilePath FindDotFile(const base::FilePath& current_dir) {
+ base::FilePath try_this_file = current_dir.Append(kGnFile);
+ if (base::PathExists(try_this_file))
+ return try_this_file;
+
+ base::FilePath with_no_slash = current_dir.StripTrailingSeparators();
+ base::FilePath up_one_dir = with_no_slash.DirName();
+ if (up_one_dir == current_dir)
+ return base::FilePath(); // Got to the top.
+
+ return FindDotFile(up_one_dir);
+}
+
+} // namespace
+
+Setup::Setup()
+ : dotfile_toolchain_(Label()),
+ dotfile_settings_(&dotfile_build_settings_, &dotfile_toolchain_,
+ std::string()),
+ dotfile_scope_(&dotfile_settings_) {
+}
+
+Setup::~Setup() {
+}
+
+bool Setup::DoSetup() {
+ CommandLine* cmdline = CommandLine::ForCurrentProcess();
+
+ scheduler_.set_verbose_logging(cmdline->HasSwitch(kSwitchVerbose));
+
+ if (!FillSourceDir(*cmdline))
+ return false;
+ if (!RunConfigFile())
+ return false;
+ if (!FillOtherConfig(*cmdline))
+ return false;
+
+ // FIXME(brettw) get python path!
+/*#if defined(OS_WIN)
+ build_settings_.set_python_path(base::FilePath(
+ //L"P:\\depot_tools\\python_bin\\python.exe"));
+ L"C:\\apps\\depot_tools\\python_bin\\python.exe"));
+#else*/
+ build_settings_.set_python_path(base::FilePath("python"));
+//#endif
+
+ build_settings_.SetBuildDir(SourceDir("//out/gn/"));
+
+ return true;
+}
+
+bool Setup::Run() {
+ // Load the root build file and start runnung.
+ build_settings_.toolchain_manager().StartLoadingUnlocked(
+ SourceFile("//BUILD.gn"));
+ if (!scheduler_.Run())
+ return false;
+
+ Err err = build_settings_.item_tree().CheckForBadItems();
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return false;
+ }
+ return true;
+}
+
+bool Setup::FillSourceDir(const CommandLine& cmdline) {
+ // Find the .gn file.
+ base::FilePath root_path;
+
+ // Prefer the command line args to the config file.
+ base::FilePath relative_root_path = cmdline.GetSwitchValuePath(kSwitchRoot);
+ if (!relative_root_path.empty()) {
+ root_path = base::MakeAbsoluteFilePath(relative_root_path);
+ dotfile_name_ = root_path.Append(kGnFile);
+ } else {
+ base::FilePath cur_dir;
+ file_util::GetCurrentDirectory(&cur_dir);
+ dotfile_name_ = FindDotFile(cur_dir);
+ if (dotfile_name_.empty()) {
+ Err(Location(), "Can't find source root.",
+ "I could not find a \".gn\" file in the current directory or any "
+ "parent,\nand the --root command-line argument was not specified.")
+ .PrintToStdout();
+ return false;
+ }
+ root_path = dotfile_name_.DirName();
+ }
+
+ if (scheduler_.verbose_logging())
+ scheduler_.Log("Using source root", FilePathToUTF8(root_path));
+ build_settings_.set_root_path(root_path);
+
+ return true;
+}
+
+bool Setup::RunConfigFile() {
+ if (scheduler_.verbose_logging())
+ scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_));
+
+ dotfile_input_file_.reset(new InputFile(SourceFile("//.gn")));
+ if (!dotfile_input_file_->Load(dotfile_name_)) {
+ Err(Location(), "Could not load dotfile.",
+ "The file \"" + FilePathToUTF8(dotfile_name_) + "\" cound't be loaded")
+ .PrintToStdout();
+ return false;
+ }
+
+ Err err;
+ dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return false;
+ }
+
+ dotfile_root_ = Parser::Parse(dotfile_tokens_, &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return false;
+ }
+
+ dotfile_root_->AsBlock()->ExecuteBlockInScope(&dotfile_scope_, &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return false;
+ }
+
+ return true;
+}
+
+bool Setup::FillOtherConfig(const CommandLine& cmdline) {
+ Err err;
+
+ // Secondary source path.
+ SourceDir secondary_source;
+ if (cmdline.HasSwitch(kSecondarySource)) {
+ // Prefer the command line over the config file.
+ secondary_source =
+ SourceDir(cmdline.GetSwitchValueASCII(kSecondarySource));
+ } else {
+ // Read from the config file if present.
+ const Value* secondary_value =
+ dotfile_scope_.GetValue("secondary_source", true);
+ if (secondary_value) {
+ if (!secondary_value->VerifyTypeIs(Value::STRING, &err)) {
+ err.PrintToStdout();
+ return false;
+ }
+ build_settings_.SetSecondarySourcePath(
+ SourceDir(secondary_value->string_value()));
+ }
+ }
+
+ // Build config dir.
+ const Value* build_config_value =
+ dotfile_scope_.GetValue("buildconfig", true);
+ if (!build_config_value) {
+ Err(Location(), "No build config file.",
+ "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) + "\")\n"
+ "didn't specify a \"buildconfig\" value.").PrintToStdout();
+ return false;
+ }
+ build_settings_.set_build_config_file(
+ SourceFile("//build/config/BUILDCONFIG.gn"));
+
+ return true;
+}
diff --git a/tools/gn/setup.h b/tools/gn/setup.h
new file mode 100644
index 0000000..e698ac5
--- /dev/null
+++ b/tools/gn/setup.h
@@ -0,0 +1,69 @@
+// 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.
+
+#ifndef TOOLS_GN_SETUP_H_
+#define TOOLS_GN_SETUP_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/token.h"
+#include "tools/gn/toolchain.h"
+
+class CommandLine;
+class InputFile;
+class ParseNode;
+
+// Helper class to setup the build settings and environment for the various
+// commands to run.
+class Setup {
+ public:
+ Setup();
+ ~Setup();
+
+ // Configures the build for the current command line. On success returns
+ // true. On failure, prints the error and returns false.
+ bool DoSetup();
+
+ // Runs the load, returning true on success. On failure, prints the error
+ // and returns false.
+ bool Run();
+
+ BuildSettings& build_settings() { return build_settings_; }
+ Scheduler& scheduler() { return scheduler_; }
+
+ private:
+ // Fills the root directory into the settings. Returns true on success.
+ bool FillSourceDir(const CommandLine& cmdline);
+
+ // Run config file.
+ bool RunConfigFile();
+
+ bool FillOtherConfig(const CommandLine& cmdline);
+
+ BuildSettings build_settings_;
+ Scheduler scheduler_;
+
+ // State for invoking the dotfile.
+ // TODO(brettw) this seems a bit excessive, maybe we can get this down
+ // somehow?
+ base::FilePath dotfile_name_;
+ scoped_ptr<InputFile> dotfile_input_file_;
+ std::vector<Token> dotfile_tokens_;
+ scoped_ptr<ParseNode> dotfile_root_;
+ BuildSettings dotfile_build_settings_;
+ Toolchain dotfile_toolchain_;
+ Settings dotfile_settings_;
+ Scope dotfile_scope_;
+
+ DISALLOW_COPY_AND_ASSIGN(Setup);
+};
+
+#endif // TOOLS_GN_SETUP_H_
diff --git a/tools/gn/source_dir.cc b/tools/gn/source_dir.cc
new file mode 100644
index 0000000..5739b52
--- /dev/null
+++ b/tools/gn/source_dir.cc
@@ -0,0 +1,98 @@
+// 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/source_dir.h"
+
+#include "base/logging.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/source_file.h"
+
+namespace {
+
+void AssertValueSourceDirString(const std::string& s) {
+ DCHECK(!s.empty());
+ DCHECK(s[0] == '/');
+ DCHECK(EndsWithSlash(s));
+}
+
+} // namespace
+
+SourceDir::SourceDir() {
+}
+
+SourceDir::SourceDir(const base::StringPiece& p)
+ : value_(p.data(), p.size()) {
+ if (!EndsWithSlash(value_))
+ value_.push_back('/');
+ AssertValueSourceDirString(value_);
+}
+
+SourceDir::~SourceDir() {
+}
+
+SourceFile SourceDir::ResolveRelativeFile(const base::StringPiece& p) const {
+ SourceFile ret;
+
+ // It's an error to resolve an empty string or one that is a directory
+ // (indicated by a trailing slash) because this is the function that expects
+ // to return a file.
+ if (p.empty() || (p.size() > 0 && p[p.size() - 1] == '/'))
+ return SourceFile();
+ if (p[0] == '/') {
+ // Absolute path.
+ ret.value_.assign(p.data(), p.size());
+ return ret;
+ }
+
+ ret.value_.reserve(value_.size() + p.size());
+ ret.value_.assign(value_);
+ ret.value_.append(p.data(), p.size());
+
+ NormalizePath(&ret.value_);
+ return ret;
+}
+
+SourceDir SourceDir::ResolveRelativeDir(const base::StringPiece& p) const {
+ SourceDir ret;
+
+ if (p.empty())
+ return ret;
+ if (p[0] == '/') {
+ // Absolute path.
+ return SourceDir(p);
+ }
+
+ ret.value_.reserve(value_.size() + p.size());
+ ret.value_.assign(value_);
+ ret.value_.append(p.data(), p.size());
+
+ NormalizePath(&ret.value_);
+ if (!EndsWithSlash(ret.value_))
+ ret.value_.push_back('/');
+ AssertValueSourceDirString(ret.value_);
+
+ return ret;
+}
+
+base::FilePath SourceDir::Resolve(const base::FilePath& source_root) const {
+ if (is_null())
+ return base::FilePath();
+
+ std::string converted;
+ if (is_system_absolute()) {
+ converted = value_;
+ ConvertPathToSystem(&converted);
+ return base::FilePath(UTF8ToFilePath(converted));
+ }
+
+ // String the double-leading slash for source-relative paths.
+ converted.assign(&value_[2], value_.size() - 2);
+ ConvertPathToSystem(&converted);
+ return source_root.Append(UTF8ToFilePath(converted));
+}
+
+void SourceDir::SwapInValue(std::string* v) {
+ value_.swap(*v);
+ AssertValueSourceDirString(value_);
+}
diff --git a/tools/gn/source_dir.h b/tools/gn/source_dir.h
new file mode 100644
index 0000000..3b6caee
--- /dev/null
+++ b/tools/gn/source_dir.h
@@ -0,0 +1,104 @@
+// 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.
+
+#ifndef TOOLS_GN_SOURCE_DIR_H_
+#define TOOLS_GN_SOURCE_DIR_H_
+
+#include <string>
+
+#include "base/containers/hash_tables.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+
+class SourceFile;
+
+// Represents a directory within the source tree. Source dirs begin and end in
+// slashes.
+//
+// If there is one slash at the beginning, it will mean a system-absolute file
+// path. On Windows, absolute system paths will be of the form "/C:/foo/bar".
+//
+// Two slashes at the beginning indicate a path relative to the source root.
+class SourceDir {
+ public:
+ SourceDir();
+ explicit SourceDir(const base::StringPiece& p);
+ ~SourceDir();
+
+ // Resolves a file or dir name relative to this source directory. Will return
+ // an empty SourceDir/File on error. Empty input is always an error (it's
+ // possible we should say ResolveRelativeDir vs. an empty string should be
+ // the source dir, but we require "." instead).
+ SourceFile ResolveRelativeFile(const base::StringPiece& p) const;
+ SourceDir ResolveRelativeDir(const base::StringPiece& p) const;
+
+ // Resolves this source file relative to some given source root. Returns
+ // an empty file path on error.
+ base::FilePath Resolve(const base::FilePath& source_root) const;
+
+ bool is_null() const { return value_.empty(); }
+ const std::string& value() const { return value_; }
+
+ // Returns true if this path starts with a "//" which indicates a path
+ // from the source root.
+ bool is_source_absolute() const {
+ return value_.size() >= 2 && value_[0] == '/' && value_[1] == '/';
+ }
+
+ // Returns true if this path starts with a single slash which indicates a
+ // system-absolute path.
+ bool is_system_absolute() const {
+ return !is_source_absolute();
+ }
+
+ // Returns a source-absolute path starting with only one slash at the
+ // beginning (normally source-absolute paths start with two slashes to mark
+ // them as such). This is normally used when concatenating directories
+ // together.
+ //
+ // This function asserts that the directory is actually source-absolute. The
+ // return value points into our buffer.
+ base::StringPiece SourceAbsoluteWithOneSlash() const {
+ CHECK(is_source_absolute());
+ return base::StringPiece(&value_[1], value_.size() - 1);
+ }
+
+ void SwapInValue(std::string* v);
+
+ bool operator==(const SourceDir& other) const {
+ return value_ == other.value_;
+ }
+ bool operator!=(const SourceDir& other) const {
+ return !operator==(other);
+ }
+ bool operator<(const SourceDir& other) const {
+ return value_ < other.value_;
+ }
+
+ private:
+ friend class SourceFile;
+ std::string value_;
+
+ // Copy & assign supported.
+};
+
+namespace BASE_HASH_NAMESPACE {
+
+#if defined(COMPILER_GCC)
+template<> struct hash<SourceDir> {
+ std::size_t operator()(const SourceDir& v) const {
+ hash<std::string> h;
+ return h(v.value());
+ }
+};
+#elif defined(COMPILER_MSVC)
+inline size_t hash_value(const SourceDir& v) {
+ return hash_value(v.value());
+}
+#endif // COMPILER...
+
+} // namespace BASE_HASH_NAMESPACE
+
+#endif // TOOLS_GN_SOURCE_DIR_H_
diff --git a/tools/gn/source_dir_unittest.cc b/tools/gn/source_dir_unittest.cc
new file mode 100644
index 0000000..745513d
--- /dev/null
+++ b/tools/gn/source_dir_unittest.cc
@@ -0,0 +1,45 @@
+// 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/source_dir.h"
+#include "tools/gn/source_file.h"
+
+TEST(SourceDir, ResolveRelativeFile) {
+ SourceDir base("//base/");
+
+ // Empty input is an error.
+ EXPECT_TRUE(base.ResolveRelativeFile("") == SourceFile());
+
+ // These things are directories, so should be an error.
+ EXPECT_TRUE(base.ResolveRelativeFile("//foo/bar/") == SourceFile());
+ EXPECT_TRUE(base.ResolveRelativeFile("bar/") == SourceFile());
+
+ // Absolute paths should be passed unchanged.
+ EXPECT_TRUE(base.ResolveRelativeFile("//foo") == SourceFile("//foo"));
+ EXPECT_TRUE(base.ResolveRelativeFile("/foo") == SourceFile("/foo"));
+
+ // Basic relative stuff.
+ EXPECT_TRUE(base.ResolveRelativeFile("foo") == SourceFile("//base/foo"));
+ EXPECT_TRUE(base.ResolveRelativeFile("./foo") == SourceFile("//base/foo"));
+ EXPECT_TRUE(base.ResolveRelativeFile("../foo") == SourceFile("//foo"));
+ EXPECT_TRUE(base.ResolveRelativeFile("../../foo") == SourceFile("//foo"));
+}
+
+TEST(SourceDir, ResolveRelativeDir) {
+ SourceDir base("//base/");
+
+ // Empty input is an error.
+ EXPECT_TRUE(base.ResolveRelativeDir("") == SourceDir());
+
+ // Absolute paths should be passed unchanged.
+ EXPECT_TRUE(base.ResolveRelativeDir("//foo") == SourceDir("//foo/"));
+ EXPECT_TRUE(base.ResolveRelativeDir("/foo") == SourceDir("/foo/"));
+
+ // Basic relative stuff.
+ EXPECT_TRUE(base.ResolveRelativeDir("foo") == SourceDir("//base/foo/"));
+ EXPECT_TRUE(base.ResolveRelativeDir("./foo") == SourceDir("//base/foo/"));
+ EXPECT_TRUE(base.ResolveRelativeDir("../foo") == SourceDir("//foo/"));
+ EXPECT_TRUE(base.ResolveRelativeDir("../../foo/") == SourceDir("//foo/"));
+}
diff --git a/tools/gn/source_file.cc b/tools/gn/source_file.cc
new file mode 100644
index 0000000..de07de1
--- /dev/null
+++ b/tools/gn/source_file.cc
@@ -0,0 +1,67 @@
+// 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/source_file.h"
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/source_dir.h"
+
+SourceFile::SourceFile() {
+}
+
+SourceFile::SourceFile(const base::StringPiece& p)
+ : value_(p.data(), p.size()) {
+ DCHECK(!value_.empty());
+ DCHECK(value_[0] == '/');
+ DCHECK(!EndsWithSlash(value_));
+}
+
+SourceFile::~SourceFile() {
+}
+
+std::string SourceFile::GetName() const {
+ if (is_null())
+ return std::string();
+
+ DCHECK(value_.find('/') != std::string::npos);
+ size_t last_slash = value_.rfind('/');
+ return std::string(&value_[last_slash + 1],
+ value_.size() - last_slash - 1);
+}
+
+SourceDir SourceFile::GetDir() const {
+ if (is_null())
+ return SourceDir();
+
+ DCHECK(value_.find('/') != std::string::npos);
+ size_t last_slash = value_.rfind('/');
+ return SourceDir(base::StringPiece(&value_[0], last_slash + 1));
+}
+
+base::FilePath SourceFile::Resolve(const base::FilePath& source_root) const {
+ if (is_null())
+ return base::FilePath();
+
+ std::string converted;
+#if defined(OS_WIN)
+ if (is_system_absolute()) {
+ converted.assign(&value_[1], value_.size() - 1);
+ DCHECK(converted.size() > 2 && converted[1] == ':')
+ << "Expecting Windows absolute file path with a drive letter: "
+ << value_;
+ return base::FilePath(UTF8ToFilePath(converted));
+ }
+
+ converted.assign(&value_[2], value_.size() - 2);
+ ConvertPathToSystem(&converted);
+ return root_path_.Append(UTF8ToFilePath(converted));
+#else
+ if (is_system_absolute())
+ return base::FilePath(value_);
+ converted.assign(&value_[2], value_.size() - 2);
+ return source_root.Append(converted);
+#endif
+}
diff --git a/tools/gn/source_file.h b/tools/gn/source_file.h
new file mode 100644
index 0000000..d883bc0
--- /dev/null
+++ b/tools/gn/source_file.h
@@ -0,0 +1,97 @@
+// 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.
+
+#ifndef TOOLS_GN_SOURCE_FILE_H_
+#define TOOLS_GN_SOURCE_FILE_H_
+
+#include <string>
+
+#include "base/containers/hash_tables.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+
+class SourceDir;
+
+// Represents a file within the source tree. Always begins in a slash, never
+// ends in one.
+class SourceFile {
+ public:
+ SourceFile();
+
+ // Takes a known absolute source file. Always begins in a slash.
+ explicit SourceFile(const base::StringPiece& p);
+
+ ~SourceFile();
+
+ bool is_null() const { return value_.empty(); }
+ const std::string& value() const { return value_; }
+
+ // Returns everythign after the last slash.
+ std::string GetName() const;
+ SourceDir GetDir() const;
+
+ // Resolves this source file relative to some given source root. Returns
+ // an empty file path on error.
+ base::FilePath Resolve(const base::FilePath& source_root) const;
+
+ // Returns true if this file starts with a "//" which indicates a path
+ // from the source root.
+ bool is_source_absolute() const {
+ return value_.size() >= 2 && value_[0] == '/' && value_[1] == '/';
+ }
+
+ // Returns true if this file starts with a single slash which indicates a
+ // system-absolute path.
+ bool is_system_absolute() const {
+ return !is_source_absolute();
+ }
+
+ // Returns a source-absolute path starting with only one slash at the
+ // beginning (normally source-absolute paths start with two slashes to mark
+ // them as such). This is normally used when concatenating names together.
+ //
+ // This function asserts that the file is actually source-absolute. The
+ // return value points into our buffer.
+ base::StringPiece SourceAbsoluteWithOneSlash() const {
+ CHECK(is_source_absolute());
+ return base::StringPiece(&value_[1], value_.size() - 1);
+ }
+
+ bool operator==(const SourceFile& other) const {
+ return value_ == other.value_;
+ }
+ bool operator!=(const SourceFile& other) const {
+ return !operator==(other);
+ }
+ bool operator<(const SourceFile& other) const {
+ return value_ < other.value_;
+ }
+
+ private:
+ friend class SourceDir;
+
+ std::string value_;
+
+ // Copy & assign supported.
+};
+
+namespace BASE_HASH_NAMESPACE {
+
+#if defined(COMPILER_GCC)
+template<> struct hash<SourceFile> {
+ std::size_t operator()(const SourceFile& v) const {
+ hash<std::string> h;
+ return h(v.value());
+ }
+};
+#elif defined(COMPILER_MSVC)
+inline size_t hash_value(const SourceFile& v) {
+ return hash_value(v.value());
+}
+#endif // COMPILER...
+
+} // namespace BASE_HASH_NAMESPACE
+
+#endif // TOOLS_GN_SOURCE_FILE_H_
diff --git a/tools/gn/standard_out.cc b/tools/gn/standard_out.cc
new file mode 100644
index 0000000..f6a2031
--- /dev/null
+++ b/tools/gn/standard_out.cc
@@ -0,0 +1,84 @@
+// 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/standard_out.h"
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#else
+#include <stdio.h>
+#endif
+
+namespace {
+
+bool initialized = false;
+
+#if defined(OS_WIN)
+HANDLE hstdout;
+WORD default_attributes;
+
+bool is_console = false;
+#endif
+
+void EnsureInitialized() {
+ if (initialized)
+ return;
+ initialized = true;
+
+#if defined(OS_WIN)
+ hstdout = ::GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ is_console = !!::GetConsoleScreenBufferInfo(hstdout, &info);
+ default_attributes = info.wAttributes;
+#endif
+}
+
+} // namespace
+
+#if defined(OS_WIN)
+
+void OutputString(const std::string& output, TextDecoration dec) {
+ EnsureInitialized();
+ if (is_console) {
+ switch (dec) {
+ case DECORATION_NONE:
+ break;
+ case DECORATION_BOLD:
+ ::SetConsoleTextAttribute(hstdout, FOREGROUND_INTENSITY);
+ break;
+ case DECORATION_RED:
+ ::SetConsoleTextAttribute(hstdout,
+ FOREGROUND_RED | FOREGROUND_INTENSITY);
+ break;
+ case DECORATION_GREEN:
+ // Keep green non-bold.
+ ::SetConsoleTextAttribute(hstdout, FOREGROUND_GREEN);
+ break;
+ case DECORATION_BLUE:
+ ::SetConsoleTextAttribute(hstdout,
+ FOREGROUND_BLUE | FOREGROUND_INTENSITY);
+ break;
+ case DECORATION_YELLOW:
+ ::SetConsoleTextAttribute(hstdout,
+ FOREGROUND_RED | FOREGROUND_GREEN);
+ break;
+ }
+ }
+
+ DWORD written = 0;
+ ::WriteFile(hstdout, output.c_str(), output.size(), &written, NULL);
+
+ if (is_console)
+ ::SetConsoleTextAttribute(hstdout, default_attributes);
+}
+
+#else
+
+void OutputString(const std::string& output, TextDecoration dec) {
+ printf("%s", output.c_str());
+}
+
+#endif
diff --git a/tools/gn/standard_out.h b/tools/gn/standard_out.h
new file mode 100644
index 0000000..2eb525b
--- /dev/null
+++ b/tools/gn/standard_out.h
@@ -0,0 +1,22 @@
+// 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.
+
+#ifndef TOOLS_GN_STANDARD_OUT_H_
+#define TOOLS_GN_STANDARD_OUT_H_
+
+#include <string>
+
+enum TextDecoration {
+ DECORATION_NONE = 0,
+ DECORATION_BOLD,
+ DECORATION_RED,
+ DECORATION_GREEN,
+ DECORATION_BLUE,
+ DECORATION_YELLOW
+};
+
+void OutputString(const std::string& output,
+ TextDecoration dec = DECORATION_NONE);
+
+#endif // TOOLS_GN_STANDARD_OUT_H_
diff --git a/tools/gn/string_utils.cc b/tools/gn/string_utils.cc
new file mode 100644
index 0000000..14d296a
--- /dev/null
+++ b/tools/gn/string_utils.cc
@@ -0,0 +1,168 @@
+// 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/string_utils.h"
+
+#include "tools/gn/err.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/token.h"
+#include "tools/gn/tokenizer.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+// Constructs an Err indicating a range inside a string. We assume that the
+// token has quotes around it that are not counted by the offset.
+Err ErrInsideStringToken(const Token& token, size_t offset, size_t size,
+ const std::string& msg,
+ const std::string& help = std::string()) {
+ // The "+1" is skipping over the " at the beginning of the token.
+ Location begin_loc(token.location().file(),
+ token.location().line_number(),
+ token.location().char_offset() + offset + 1);
+ Location end_loc(token.location().file(),
+ token.location().line_number(),
+ token.location().char_offset() + offset + 1 + size);
+ return Err(LocationRange(begin_loc, end_loc), msg, help);
+}
+
+// Given the character input[i] indicating the $ in a string, locates the
+// identifier and places its range in |*identifier|, and updates |*i| to
+// point to the last character consumed.
+//
+// On error returns false and sets the error.
+bool LocateInlineIdenfitier(const Token& token,
+ const char* input, size_t size,
+ size_t* i,
+ base::StringPiece* identifier,
+ Err* err) {
+ size_t dollars_index = *i;
+ (*i)++;
+ if (*i == size) {
+ *err = ErrInsideStringToken(token, dollars_index, 1, "$ at end of string.",
+ "I was expecting an identifier after the $.");
+ return false;
+ }
+
+ bool has_brackets;
+ if (input[*i] == '{') {
+ (*i)++;
+ if (*i == size) {
+ *err = ErrInsideStringToken(token, dollars_index, 2,
+ "${ at end of string.",
+ "I was expecting an identifier inside the ${...}.");
+ return false;
+ }
+ has_brackets = true;
+ } else {
+ has_brackets = false;
+ }
+
+ // First char is special.
+ if (!Tokenizer::IsIdentifierFirstChar(input[*i])) {
+ *err = ErrInsideStringToken(
+ token, dollars_index, *i - dollars_index + 1,
+ "$ not followed by an identifier char.",
+ "It you want a literal $ use \"\\$\".");
+ return false;
+ }
+ size_t begin_offset = *i;
+ (*i)++;
+
+ // Find the first non-identifier char following the string.
+ while (*i < size && Tokenizer::IsIdentifierContinuingChar(input[*i]))
+ (*i)++;
+ size_t end_offset = *i;
+
+ // If we started with a bracket, validate that there's an ending one. Leave
+ // *i pointing to the last char we consumed (backing up one).
+ if (has_brackets) {
+ if (*i == size) {
+ *err = ErrInsideStringToken(token, dollars_index, *i - dollars_index,
+ "Unterminated ${...");
+ return false;
+ } else if (input[*i] != '}') {
+ *err = ErrInsideStringToken(token, *i, 1, "Not an identifier in string expansion.",
+ "The contents of ${...} should be an identifier. "
+ "This character is out of sorts.");
+ return false;
+ }
+ // We want to consume the bracket but also back up one, so *i is unchanged.
+ } else {
+ (*i)--;
+ }
+
+ *identifier = base::StringPiece(&input[begin_offset],
+ end_offset - begin_offset);
+ return true;
+}
+
+bool AppendIdentifierValue(Scope* scope,
+ const Token& token,
+ const base::StringPiece& identifier,
+ std::string* output,
+ Err* err) {
+ const Value* value = scope->GetValue(identifier, true);
+ if (!value) {
+ // We assume the identifier points inside the token.
+ *err = ErrInsideStringToken(
+ token, identifier.data() - token.value().data() - 1, identifier.size(),
+ "Undefined identifier in string expansion.",
+ std::string("\"") + identifier + "\" is not currently in scope.");
+ return false;
+ }
+
+ output->append(value->ToString());
+ return true;
+}
+
+} // namespace
+
+bool ExpandStringLiteral(Scope* scope,
+ const Token& literal,
+ Value* result,
+ Err* err) {
+ DCHECK(literal.type() == Token::STRING);
+ DCHECK(literal.value().size() > 1); // Should include quotes.
+ DCHECK(result->type() == Value::STRING); // Should be already set.
+
+ // The token includes the surrounding quotes, so strip those off.
+ const char* input = &literal.value().data()[1];
+ size_t size = literal.value().size() - 2;
+
+ std::string& output = result->string_value();
+ output.reserve(size);
+ for (size_t i = 0; i < size; i++) {
+ if (input[i] == '\\') {
+ if (i < size - 1) {
+ switch (input[i + 1]) {
+ case '\\':
+ case '"':
+ case '$':
+ output.push_back(input[i + 1]);
+ i++;
+ continue;
+ default: // Everything else has no meaning: pass the literal.
+ break;
+ }
+ }
+ output.push_back(input[i]);
+ } else if (input[i] == '$') {
+ base::StringPiece identifier;
+ if (!LocateInlineIdenfitier(literal, input, size, &i, &identifier, err))
+ return false;
+ if (!AppendIdentifierValue(scope, literal, identifier, &output, err))
+ return false;
+ } else {
+ output.push_back(input[i]);
+ }
+ }
+ return true;
+}
+
+std::string RemovePrefix(const std::string& str, const std::string& prefix) {
+ CHECK(str.size() >= prefix.size() &&
+ str.compare(0, prefix.size(), prefix) == 0);
+ return str.substr(prefix.size());
+}
diff --git a/tools/gn/string_utils.h b/tools/gn/string_utils.h
new file mode 100644
index 0000000..7fff1d8
--- /dev/null
+++ b/tools/gn/string_utils.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef TOOLS_GN_STRING_UTILS_H_
+#define TOOLS_GN_STRING_UTILS_H_
+
+#include "base/strings/string_piece.h"
+
+class Err;
+class Scope;
+class Token;
+class Value;
+
+inline std::string operator+(const std::string& a, const base::StringPiece& b) {
+ std::string ret;
+ ret.reserve(a.size() + b.size());
+ ret.assign(a);
+ ret.append(b.data(), b.size());
+ return ret;
+}
+
+inline std::string operator+(const base::StringPiece& a, const std::string& b) {
+ std::string ret;
+ ret.reserve(a.size() + b.size());
+ ret.assign(a.data(), a.size());
+ ret.append(b);
+ return ret;
+}
+
+// Unescapes and expands variables in the given literal, writing the result
+// to the given value. On error, sets |err| and returns false.
+bool ExpandStringLiteral(Scope* scope,
+ const Token& literal,
+ Value* result,
+ Err* err);
+
+// Removes the given prefix from the string. Asserts if the string does
+// not have the given prefix.
+//
+// Note: could potentially return a StringPiece into the str.
+std::string RemovePrefix(const std::string& str, const std::string& prefix);
+
+// Appends the given string piece to the given string. This avoids an
+// intermediate copy.
+inline void AppendStringPiece(std::string* dest,
+ const base::StringPiece& piece) {
+ dest->append(piece.data(), piece.size());
+}
+
+#endif // TOOLS_GN_STRING_UTILS_H_
diff --git a/tools/gn/string_utils_unittest.cc b/tools/gn/string_utils_unittest.cc
new file mode 100644
index 0000000..81181d2
--- /dev/null
+++ b/tools/gn/string_utils_unittest.cc
@@ -0,0 +1,71 @@
+// 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/err.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/string_utils.h"
+#include "tools/gn/token.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+bool CheckExpansionCase(const char* input, const char* expected, bool success) {
+ Scope scope(static_cast<const Settings*>(NULL));
+ scope.SetValue("one", Value(NULL, 1), NULL);
+ scope.SetValue("onestring", Value(NULL, "one"), NULL);
+
+ // Construct the string token, which includes the quotes.
+ std::string literal_string;
+ literal_string.push_back('"');
+ literal_string.append(input);
+ literal_string.push_back('"');
+ Token literal(Location(), Token::STRING, literal_string);
+
+ Value result(NULL, Value::STRING);
+ Err err;
+ bool ret = ExpandStringLiteral(&scope, literal, &result, &err);
+
+ // Err and return value should agree.
+ EXPECT_NE(ret, err.has_error());
+
+ if (ret != success)
+ return false;
+
+ if (!success)
+ return true; // Don't check result on failure.
+ return result.string_value() == expected;
+}
+
+} // namespace
+
+TEST(StringUtils, ExpandStringLiteral) {
+ EXPECT_TRUE(CheckExpansionCase("", "", true));
+ EXPECT_TRUE(CheckExpansionCase("hello", "hello", true));
+ EXPECT_TRUE(CheckExpansionCase("hello #$one", "hello #1", true));
+ EXPECT_TRUE(CheckExpansionCase("hello #$one/two", "hello #1/two", true));
+ EXPECT_TRUE(CheckExpansionCase("hello #${one}", "hello #1", true));
+ EXPECT_TRUE(CheckExpansionCase("hello #${one}one", "hello #1one", true));
+ EXPECT_TRUE(CheckExpansionCase("hello #${one}$one", "hello #11", true));
+ EXPECT_TRUE(CheckExpansionCase("$onestring${one}$one", "one11", true));
+
+ // Errors
+ EXPECT_TRUE(CheckExpansionCase("hello #$", NULL, false));
+ EXPECT_TRUE(CheckExpansionCase("hello #$%", NULL, false));
+ EXPECT_TRUE(CheckExpansionCase("hello #${", NULL, false));
+ EXPECT_TRUE(CheckExpansionCase("hello #${}", NULL, false));
+ EXPECT_TRUE(CheckExpansionCase("hello #$nonexistant", NULL, false));
+ EXPECT_TRUE(CheckExpansionCase("hello #${unterminated", NULL, false));
+
+ // Unknown backslash values aren't special.
+ EXPECT_TRUE(CheckExpansionCase("\\", "\\", true));
+ EXPECT_TRUE(CheckExpansionCase("\\b", "\\b", true));
+
+ // Backslashes escape some special things. \"\$\\ -> "$\ Note that gtest
+ // doesn't like this escape sequence so we have to put it out-of-line.
+ const char* in = "\\\"\\$\\\\";
+ const char* out = "\"$\\";
+ EXPECT_TRUE(CheckExpansionCase(in, out, true));
+}
diff --git a/tools/gn/target.cc b/tools/gn/target.cc
new file mode 100644
index 0000000..0936f22
--- /dev/null
+++ b/tools/gn/target.cc
@@ -0,0 +1,94 @@
+// 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/target.h"
+
+#include "base/bind.h"
+#include "tools/gn/scheduler.h"
+
+namespace {
+
+void TargetResolvedThunk(const base::Callback<void(const Target*)>& cb,
+ const Target* t) {
+ cb.Run(t);
+}
+
+} // namespace
+
+Target::Target(const Settings* settings, const Label& label)
+ : Item(label),
+ settings_(settings),
+ output_type_(NONE),
+ generated_(false),
+ generator_function_(NULL) {
+}
+
+Target::~Target() {
+}
+
+Target* Target::AsTarget() {
+ return this;
+}
+
+const Target* Target::AsTarget() const {
+ return this;
+}
+
+void Target::OnResolved() {
+ // Gather info from our dependents we need.
+ for (size_t dep = 0; dep < deps_.size(); dep++) {
+ // All dependent configs get pulled to us, and to our dependents.
+ const std::vector<const Config*>& all =
+ deps_[dep]->all_dependent_configs();
+ for (size_t i = 0; i < all.size(); i++) {
+ configs_.push_back(all[i]);
+ all_dependent_configs_.push_back(all[i]);
+ }
+
+ // Direct dependent configs get pulled only to us.
+ const std::vector<const Config*>& direct =
+ deps_[dep]->direct_dependent_configs();
+ for (size_t i = 0; i < direct.size(); i++)
+ configs_.push_back(direct[i]);
+
+ // Direct dependent libraries.
+ if (deps_[dep]->output_type() == STATIC_LIBRARY ||
+ deps_[dep]->output_type() == SHARED_LIBRARY ||
+ deps_[dep]->output_type() == LOADABLE_MODULE)
+ inherited_libraries_.insert(deps_[dep]);
+
+ // Inherited libraries. DOn't pull transitive libraries from shared
+ // libraries, since obviously those shouldn't be linked directly into
+ // later deps unless explicitly specified.
+ if (deps_[dep]->output_type() != SHARED_LIBRARY &&
+ deps_[dep]->output_type() != LOADABLE_MODULE &&
+ deps_[dep]->output_type() != EXECUTABLE) {
+ const std::set<const Target*> inherited =
+ deps_[dep]->inherited_libraries();
+ for (std::set<const Target*>::const_iterator i = inherited.begin();
+ i != inherited.end(); ++i)
+ inherited_libraries_.insert(*i);
+ }
+ }
+
+ if (!settings_->build_settings()->target_resolved_callback().is_null()) {
+ g_scheduler->ScheduleWork(base::Bind(&TargetResolvedThunk,
+ settings_->build_settings()->target_resolved_callback(),
+ this));
+ }
+}
+
+bool Target::HasBeenGenerated() const {
+ return generated_;
+}
+
+void Target::SetGenerated(const Token* token) {
+ DCHECK(!generated_);
+ generated_ = true;
+ generator_function_ = token;
+}
+
+bool Target::IsLinkable() const {
+ return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY;
+}
diff --git a/tools/gn/target.h b/tools/gn/target.h
new file mode 100644
index 0000000..63d62ae
--- /dev/null
+++ b/tools/gn/target.h
@@ -0,0 +1,145 @@
+// 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.
+
+#ifndef TOOLS_GN_TARGET_H_
+#define TOOLS_GN_TARGET_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "base/synchronization/lock.h"
+#include "tools/gn/config_values.h"
+#include "tools/gn/item.h"
+#include "tools/gn/source_file.h"
+
+class InputFile;
+class Settings;
+class Token;
+
+class Target : public Item {
+ public:
+ enum OutputType {
+ NONE,
+ EXECUTABLE,
+ SHARED_LIBRARY,
+ STATIC_LIBRARY,
+ LOADABLE_MODULE,
+ COPY_FILES,
+ CUSTOM,
+ };
+ typedef std::vector<SourceFile> FileList;
+ typedef std::vector<std::string> StringVector;
+
+ Target(const Settings* settings, const Label& label);
+ virtual ~Target();
+
+ // Item overrides.
+ virtual Target* AsTarget() OVERRIDE;
+ virtual const Target* AsTarget() const OVERRIDE;
+ virtual void OnResolved() OVERRIDE;
+
+ // This flag indicates if we've run the TargetGenerator for this target to
+ // fill out the rest of the values. Once we've done this, we save the
+ // location of the function that started the generating so that we can detect
+ // duplicate declarations.
+ bool HasBeenGenerated() const;
+ void SetGenerated(const Token* token);
+
+ const Settings* settings() const { return settings_; }
+
+ OutputType output_type() const { return output_type_; }
+ void set_output_type(OutputType t) { output_type_ = t; }
+
+ bool IsLinkable() const;
+
+ const FileList& sources() const { return sources_; }
+ void swap_in_sources(FileList* s) { sources_.swap(*s); }
+
+ const FileList& data() const { return data_; }
+ void swap_in_data(FileList* d) { data_.swap(*d); }
+
+ const std::vector<const Target*>& deps() const { return deps_; }
+ void swap_in_deps(std::vector<const Target*>* d) { deps_.swap(*d); }
+
+ // List of configs that this class inherits settings from.
+ const std::vector<const Config*>& configs() const { return configs_; }
+ void swap_in_configs(std::vector<const Config*>* c) { configs_.swap(*c); }
+
+ // List of configs that all dependencies (direct and indirect) of this
+ // target get. These configs are not added to this target.
+ const std::vector<const Config*>& all_dependent_configs() const {
+ return all_dependent_configs_;
+ }
+ void swap_in_all_dependent_configs(std::vector<const Config*>* c) {
+ all_dependent_configs_.swap(*c);
+ }
+
+ // List of configs that targets depending directly on this one get. These
+ // configs are not added to this target.
+ const std::vector<const Config*>& direct_dependent_configs() const {
+ return direct_dependent_configs_;
+ }
+ void swap_in_direct_dependent_configs(std::vector<const Config*>* c) {
+ direct_dependent_configs_.swap(*c);
+ }
+
+ const std::set<const Target*>& inherited_libraries() const {
+ return inherited_libraries_;
+ }
+
+ // This config represents the configuration set directly on this target.
+ ConfigValues& config_values() { return config_values_; }
+ const ConfigValues& config_values() const { return config_values_; }
+
+ const SourceDir& destdir() const { return destdir_; }
+ void set_destdir(const SourceDir& d) { destdir_ = d; }
+
+ const SourceFile& script() const { return script_; }
+ void set_script(const SourceFile& s) { script_ = s; }
+
+ const std::vector<std::string>& script_args() const { return script_args_; }
+ void swap_in_script_args(std::vector<std::string>* sa) {
+ script_args_.swap(*sa);
+ }
+
+ const FileList& outputs() const { return outputs_; }
+ void swap_in_outputs(FileList* s) { outputs_.swap(*s); }
+
+ private:
+ const Settings* settings_;
+ OutputType output_type_;
+
+ FileList sources_;
+ FileList data_;
+ std::vector<const Target*> deps_;
+ std::vector<const Config*> configs_;
+ std::vector<const Config*> all_dependent_configs_;
+ std::vector<const Config*> direct_dependent_configs_;
+
+ // Libraries from transitive deps. Libraries need to be linked only
+ // with the end target (executable, shared library). These do not get
+ // pushed beyond shared library boundaries.
+ std::set<const Target*> inherited_libraries_;
+
+ ConfigValues config_values_;
+
+ SourceDir destdir_;
+
+ // Script target stuff.
+ SourceFile script_;
+ std::vector<std::string> script_args_;
+ FileList outputs_;
+
+ bool generated_;
+ const Token* generator_function_; // Who generated this: for error messages.
+
+ DISALLOW_COPY_AND_ASSIGN(Target);
+};
+
+#endif // TOOLS_GN_TARGET_H_
diff --git a/tools/gn/target_generator.cc b/tools/gn/target_generator.cc
new file mode 100644
index 0000000..90c8fe6
--- /dev/null
+++ b/tools/gn/target_generator.cc
@@ -0,0 +1,334 @@
+// 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/target_generator.h"
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/config.h"
+#include "tools/gn/config_values_generator.h"
+#include "tools/gn/err.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/item_node.h"
+#include "tools/gn/ninja_target_writer.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/target_manager.h"
+#include "tools/gn/token.h"
+#include "tools/gn/value.h"
+#include "tools/gn/value_extractors.h"
+
+namespace {
+
+bool TypeHasConfigs(Target::OutputType type) {
+ return type == Target::EXECUTABLE ||
+ type == Target::SHARED_LIBRARY ||
+ type == Target::STATIC_LIBRARY ||
+ type == Target::LOADABLE_MODULE;
+}
+
+bool TypeHasConfigValues(Target::OutputType type) {
+ return type == Target::EXECUTABLE ||
+ type == Target::SHARED_LIBRARY ||
+ type == Target::STATIC_LIBRARY ||
+ type == Target::LOADABLE_MODULE;
+}
+
+bool TypeHasSources(Target::OutputType type) {
+ return type != Target::NONE;
+}
+
+bool TypeHasData(Target::OutputType type) {
+ return type != Target::NONE;
+}
+
+bool TypeHasDestDir(Target::OutputType type) {
+ return type == Target::COPY_FILES;
+}
+
+bool TypeHasOutputs(Target::OutputType type) {
+ return type == Target::CUSTOM;
+}
+
+} // namespace
+
+TargetGenerator::TargetGenerator(Target* target,
+ Scope* scope,
+ const Token& function_token,
+ const std::vector<Value>& args,
+ const std::string& output_type,
+ Err* err)
+ : target_(target),
+ scope_(scope),
+ function_token_(function_token),
+ args_(args),
+ output_type_(output_type),
+ err_(err),
+ input_directory_(function_token.location().file()->dir()) {
+}
+
+TargetGenerator::~TargetGenerator() {
+}
+
+void TargetGenerator::Run() {
+ // Output type.
+ Target::OutputType output_type = GetOutputType();
+ target_->set_output_type(output_type);
+ if (err_->has_error())
+ return;
+
+ if (TypeHasConfigs(output_type)) {
+ FillConfigs();
+ FillAllDependentConfigs();
+ FillDirectDependentConfigs();
+ }
+ if (TypeHasSources(output_type))
+ FillSources();
+ if (TypeHasData(output_type))
+ FillData();
+ if (output_type == Target::CUSTOM) {
+ FillScript();
+ FillScriptArgs();
+ }
+ if (TypeHasOutputs(output_type))
+ FillOutputs();
+ FillDependencies(); // All types have dependencies.
+
+ if (TypeHasConfigValues(output_type)) {
+ ConfigValuesGenerator gen(&target_->config_values(), scope_,
+ function_token_, input_directory_, err_);
+ gen.Run();
+ if (err_->has_error())
+ return;
+ }
+
+ if (TypeHasDestDir(output_type))
+ FillDestDir();
+
+ // Set the toolchain as a dependency of the target.
+ // TODO(brettw) currently we lock separately for each config, dep, and
+ // toolchain we add which is bad! Do this in one lock.
+ {
+ ItemTree* tree = &GetBuildSettings()->item_tree();
+ base::AutoLock lock(tree->lock());
+ ItemNode* tc_node =
+ tree->GetExistingNodeLocked(ToolchainLabelForScope(scope_));
+ tree->GetExistingNodeLocked(target_->label())->AddDependency(tc_node);
+ }
+
+ target_->SetGenerated(&function_token_);
+ GetBuildSettings()->target_manager().TargetGenerationComplete(
+ target_->label());
+}
+
+// static
+void TargetGenerator::GenerateTarget(Scope* scope,
+ const Token& function_token,
+ const std::vector<Value>& args,
+ const std::string& output_type,
+ Err* err) {
+ // Name is the argument to the function.
+ if (args.size() != 1u || args[0].type() != Value::STRING) {
+ *err = Err(function_token,
+ "Target generator requires one string argument.",
+ "Otherwise I'm not sure what to call this target.");
+ return;
+ }
+
+ // The location of the target is the directory name with no slash at the end.
+ // FIXME(brettw) validate name.
+ const Label& toolchain_label = ToolchainLabelForScope(scope);
+ Label label(function_token.location().file()->dir(),
+ args[0].string_value(),
+ toolchain_label.dir(), toolchain_label.name());
+
+ if (g_scheduler->verbose_logging())
+ g_scheduler->Log("Generating target", label.GetUserVisibleName(true));
+
+ Target* t = scope->settings()->build_settings()->target_manager().GetTarget(
+ label, function_token.range(), NULL, err);
+ if (err->has_error())
+ return;
+
+ TargetGenerator gen(t, scope, function_token, args, output_type, err);
+ gen.Run();
+}
+
+Target::OutputType TargetGenerator::GetOutputType() const {
+ if (output_type_ == functions::kGroup)
+ return Target::NONE;
+ if (output_type_ == functions::kExecutable)
+ return Target::EXECUTABLE;
+ if (output_type_ == functions::kSharedLibrary)
+ return Target::SHARED_LIBRARY;
+ if (output_type_ == functions::kStaticLibrary)
+ return Target::STATIC_LIBRARY;
+ // TODO(brettw) what does loadable module mean?
+ //if (output_type_ == ???)
+ // return Target::LOADABLE_MODULE;
+ if (output_type_ == functions::kCopy)
+ return Target::COPY_FILES;
+ if (output_type_ == functions::kCustom)
+ return Target::CUSTOM;
+
+ *err_ = Err(function_token_, "Not a known output type",
+ "I am very confused.");
+ return Target::NONE;
+}
+
+void TargetGenerator::FillGenericConfigs(
+ const char* var_name,
+ void (Target::*setter)(std::vector<const Config*>*)) {
+ const Value* value = scope_->GetValue(var_name, true);
+ if (!value)
+ return;
+
+ std::vector<Label> labels;
+ if (!ExtractListOfLabels(*value, input_directory_,
+ ToolchainLabelForScope(scope_), &labels, err_))
+ return;
+
+ std::vector<const Config*> dest_configs;
+ dest_configs.resize(labels.size());
+ for (size_t i = 0; i < labels.size(); i++) {
+ dest_configs[i] = Config::GetConfig(
+ scope_->settings(),
+ value->list_value()[i].origin()->GetRange(),
+ labels[i], target_, err_);
+ if (err_->has_error())
+ return;
+ }
+ (target_->*setter)(&dest_configs);
+}
+
+void TargetGenerator::FillConfigs() {
+ FillGenericConfigs("configs", &Target::swap_in_configs);
+}
+
+void TargetGenerator::FillAllDependentConfigs() {
+ FillGenericConfigs("all_dependent_configs",
+ &Target::swap_in_all_dependent_configs);
+}
+
+void TargetGenerator::FillDirectDependentConfigs() {
+ FillGenericConfigs("direct_dependent_configs",
+ &Target::swap_in_direct_dependent_configs);
+}
+
+void TargetGenerator::FillSources() {
+ const Value* value = scope_->GetValue("sources", true);
+ if (!value)
+ return;
+
+ Target::FileList dest_sources;
+ if (!ExtractListOfRelativeFiles(*value, input_directory_, &dest_sources,
+ err_))
+ return;
+ target_->swap_in_sources(&dest_sources);
+}
+
+void TargetGenerator::FillData() {
+ const Value* value = scope_->GetValue("data", true);
+ if (!value)
+ return;
+
+ Target::FileList dest_data;
+ if (!ExtractListOfRelativeFiles(*value, input_directory_, &dest_data,
+ err_))
+ return;
+ target_->swap_in_data(&dest_data);
+}
+
+void TargetGenerator::FillDependencies() {
+ const Value* value = scope_->GetValue("deps", true);
+ if (!value)
+ return;
+
+ std::vector<Label> labels;
+ if (!ExtractListOfLabels(*value, input_directory_,
+ ToolchainLabelForScope(scope_), &labels, err_))
+ return;
+
+ std::vector<const Target*> dest_deps;
+ dest_deps.resize(labels.size());
+ for (size_t i = 0; i < labels.size(); i++) {
+ dest_deps[i] = GetBuildSettings()->target_manager().GetTarget(
+ labels[i], value->list_value()[i].origin()->GetRange(), target_, err_);
+ if (err_->has_error())
+ return;
+ }
+
+ target_->swap_in_deps(&dest_deps);
+}
+
+void TargetGenerator::FillDestDir() {
+ // Destdir is required for all targets that use it.
+ const Value* value = scope_->GetValue("destdir", true);
+ if (!value) {
+ *err_ = Err(function_token_, "This target type requires a \"destdir\".");
+ return;
+ }
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return;
+
+ if (!EnsureStringIsInOutputDir(
+ GetBuildSettings()->build_dir(),
+ value->string_value(), *value, err_))
+ return;
+ target_->set_destdir(SourceDir(value->string_value()));
+}
+
+void TargetGenerator::FillScript() {
+ // If this gets called, the target type requires a script, so error out
+ // if it doesn't have one.
+ const Value* value = scope_->GetValue("script", true);
+ if (!value) {
+ *err_ = Err(function_token_, "This target type requires a \"script\".");
+ return;
+ }
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return;
+
+ target_->set_script(
+ input_directory_.ResolveRelativeFile(value->string_value()));
+}
+
+void TargetGenerator::FillScriptArgs() {
+ const Value* value = scope_->GetValue("args", true);
+ if (!value)
+ return;
+
+ std::vector<std::string> args;
+ if (!ExtractListOfStringValues(*value, &args, err_))
+ return;
+ target_->swap_in_script_args(&args);
+}
+
+void TargetGenerator::FillOutputs() {
+ const Value* value = scope_->GetValue("outputs", true);
+ if (!value)
+ return;
+
+ Target::FileList outputs;
+ if (!ExtractListOfRelativeFiles(*value, input_directory_, &outputs, err_))
+ return;
+
+ // Validate that outputs are in the output dir.
+ CHECK(outputs.size() == value->list_value().size());
+ for (size_t i = 0; i < outputs.size(); i++) {
+ if (!EnsureStringIsInOutputDir(
+ GetBuildSettings()->build_dir(),
+ outputs[i].value(), value->list_value()[i], err_))
+ return;
+ }
+ target_->swap_in_outputs(&outputs);
+}
+
+const BuildSettings* TargetGenerator::GetBuildSettings() const {
+ return scope_->settings()->build_settings();
+}
diff --git a/tools/gn/target_generator.h b/tools/gn/target_generator.h
new file mode 100644
index 0000000..11e920c
--- /dev/null
+++ b/tools/gn/target_generator.h
@@ -0,0 +1,83 @@
+// 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.
+
+#ifndef TOOLS_GN_TARGET_GENERATOR_H_
+#define TOOLS_GN_TARGET_GENERATOR_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "tools/gn/source_dir.h"
+#include "tools/gn/target.h"
+
+class BuildSettings;
+class Err;
+class Location;
+class Scope;
+class Token;
+class Value;
+
+// Creates Target objects from a Scope (the result of a script execution).
+class TargetGenerator {
+ public:
+ TargetGenerator(Target* target,
+ Scope* scope,
+ const Token& function_token,
+ const std::vector<Value>& args,
+ const std::string& output_type,
+ Err* err);
+ ~TargetGenerator();
+
+ void Run();
+
+ // The function token is the token of the function name of the generator for
+ // this target. err() will be set on failure.
+ static void GenerateTarget(Scope* scope,
+ const Token& function_token,
+ const std::vector<Value>& args,
+ const std::string& output_type,
+ Err* err);
+
+ private:
+ // Sets err_ on failure.
+ Target::OutputType GetOutputType() const;
+
+ // Reads configs from the given var name, and uses the given setting on the
+ // target to save them
+ void FillGenericConfigs(const char* var_name,
+ void (Target::*setter)(std::vector<const Config*>*));
+
+ void FillConfigs();
+ void FillAllDependentConfigs();
+ void FillDirectDependentConfigs();
+ void FillSources();
+ void FillData();
+ void FillDependencies();
+ void FillDestDir();
+ void FillScript();
+ void FillScriptArgs();
+ void FillOutputs();
+
+ const BuildSettings* GetBuildSettings() const;
+
+ Target* target_;
+ Scope* scope_;
+ const Token& function_token_;
+ std::vector<Value> args_;
+ std::string output_type_;
+ Err* err_;
+
+ // Sources are relative to this. This comes from the input file which doesn't
+ // get freed so we don't acautlly have to make a copy.
+ const SourceDir& input_directory_;
+
+ FRIEND_TEST_ALL_PREFIXES(TargetGenerator, ResolveInputPath);
+
+ DISALLOW_COPY_AND_ASSIGN(TargetGenerator);
+};
+
+#endif // TOOLS_GN_TARGET_GENERATOR_H_
diff --git a/tools/gn/target_generator_unittest.cc b/tools/gn/target_generator_unittest.cc
new file mode 100644
index 0000000..feef6a9
--- /dev/null
+++ b/tools/gn/target_generator_unittest.cc
@@ -0,0 +1,8 @@
+// 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/filesystem_utils.h"
+#include "tools/gn/target_generator.h"
+
diff --git a/tools/gn/target_manager.cc b/tools/gn/target_manager.cc
new file mode 100644
index 0000000..ece9f08
--- /dev/null
+++ b/tools/gn/target_manager.cc
@@ -0,0 +1,134 @@
+// 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/target_manager.h"
+
+#include <deque>
+
+#include "base/bind.h"
+#include "base/strings/string_piece.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/err.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/item_node.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/toolchain_manager.h"
+#include "tools/gn/value.h"
+
+TargetManager::TargetManager(const BuildSettings* build_settings)
+ : build_settings_(build_settings) {
+}
+
+TargetManager::~TargetManager() {
+}
+
+Target* TargetManager::GetTarget(const Label& label,
+ const LocationRange& specified_from_here,
+ Target* dep_from,
+ Err* err) {
+ DCHECK(!label.is_null());
+ DCHECK(!label.toolchain_dir().value().empty());
+ DCHECK(!label.toolchain_name().empty());
+
+ base::AutoLock lock(build_settings_->item_tree().lock());
+
+ ItemNode* target_node =
+ build_settings_->item_tree().GetExistingNodeLocked(label);
+ Target* target = NULL;
+ if (!target_node) {
+ // First time we've seen this, may need to load the file.
+
+ // Compute the settings. The common case is that we have a dep_from and
+ // the toolchains match, so we can use the settings from there rather than
+ // querying the toolchain manager (which requires locking, etc.).
+ const Settings* settings;
+ if (dep_from && dep_from->label().ToolchainsEqual(label)) {
+ settings = dep_from->settings();
+ } else {
+ settings =
+ build_settings_->toolchain_manager().GetSettingsForToolchainLocked(
+ specified_from_here, label.GetToolchainLabel(), err);
+ if (!settings)
+ return NULL;
+ }
+
+ target = new Target(settings, label);
+ target_node = new ItemNode(target);
+ target_node->set_originally_referenced_from_here(specified_from_here);
+ build_settings_->item_tree().AddNodeLocked(target_node);
+
+ // We're generating a node when there is no referencing one.
+ if (!dep_from)
+ target_node->set_generated_from_here(specified_from_here);
+
+ // Only schedule loading the given target if somebody is depending on it
+ // (and we optimize by not re-asking it to run the current file).
+ // Otherwise, we're probably generating it right now.
+ if (dep_from && dep_from->label().dir() != label.dir()) {
+ if (!ScheduleInvocationLocked(specified_from_here, label, err))
+ return NULL;
+ }
+ } else if ((target = target_node->item()->AsTarget())) {
+ // Previously saw this item as a target.
+
+ // If we have no dep_from, we're generating it.
+ if (!dep_from) {
+ // In this case, it had better not already be generated.
+ if (target_node->state() != ItemNode::REFERENCED) {
+ *err = Err(specified_from_here,
+ "Duplicate target.",
+ "\"" + label.GetUserVisibleName(true) +
+ "\" being defined here.");
+ err->AppendSubErr(Err(target_node->generated_from_here(),
+ "Originally defined here."));
+ return NULL;
+ } else {
+ target_node->set_generated_from_here(specified_from_here);
+ }
+ }
+ } else {
+ // Error, we previously saw this thing as a non-target.
+ *err = Err(specified_from_here, "Not previously a target.",
+ "The target being declared here was previously seen referenced as a\n"
+ "non-target (like a config)");
+ err->AppendSubErr(Err(target_node->originally_referenced_from_here(),
+ "Originally referenced from here."));
+ return NULL;
+ }
+
+ // Keep a record of the guy asking us for this dependency. We know if
+ // somebody is adding a dependency, that guy it himself not resolved.
+ if (dep_from && target_node->state() != ItemNode::RESOLVED) {
+ build_settings_->item_tree().GetExistingNodeLocked(
+ dep_from->label())->AddDependency(target_node);
+ }
+
+ return target;
+}
+
+void TargetManager::TargetGenerationComplete(const Label& label) {
+ base::AutoLock lock(build_settings_->item_tree().lock());
+ build_settings_->item_tree().MarkItemGeneratedLocked(label);
+}
+
+void TargetManager::GetAllTargets(
+ std::vector<const Target*>* all_targets) const {
+ base::AutoLock lock(build_settings_->item_tree().lock());
+
+ std::vector<const Item*> all_items;
+ build_settings_->item_tree().GetAllItemsLocked(&all_items);
+ for (size_t i = 0; i < all_items.size(); i++) {
+ const Target* t = all_items[i]->AsTarget();
+ if (t)
+ all_targets->push_back(t);
+ }
+}
+
+bool TargetManager::ScheduleInvocationLocked(
+ const LocationRange& specified_from_here,
+ const Label& label,
+ Err* err) {
+ return build_settings_->toolchain_manager().ScheduleInvocationLocked(
+ specified_from_here, label.GetToolchainLabel(), label.dir(), err);
+}
diff --git a/tools/gn/target_manager.h b/tools/gn/target_manager.h
new file mode 100644
index 0000000..2dd2aed
--- /dev/null
+++ b/tools/gn/target_manager.h
@@ -0,0 +1,73 @@
+// 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.
+
+#ifndef TOOLS_GN_TARGET_MANAGER_H_
+#define TOOLS_GN_TARGET_MANAGER_H_
+
+#include <set>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/synchronization/lock.h"
+#include "tools/gn/target.h"
+
+class BuildSettings;
+class Err;
+class ItemTree;
+class LocationRange;
+class ToolchainManager;
+class Value;
+
+// Manages all the targets in the system. This integrates with the ItemTree
+// to manage the target-specific rules and creation.
+//
+// This class is threadsafe.
+class TargetManager {
+ public:
+ explicit TargetManager(const BuildSettings* settings);
+ ~TargetManager();
+
+ // Gets a reference to a named target. The given target name is created if
+ // it doesn't exist.
+ //
+ // The label should be fully specified in that it should include an
+ // explicit toolchain.
+ //
+ // |specified_from_here| should indicate the dependency or the target
+ // generator causing this access for error message generation.
+ //
+ // |dep_from| should be set when a target is getting a dep that it depends
+ // on. |dep_from| indicates the target that specified the dependency. It
+ // will be used to track outstanding dependencies so we can know when the
+ // target and all of its dependencies are complete. It should be null when
+ // getting a target for other reasons.
+ //
+ // On failure, |err| will be set.
+ //
+ // The returned pointer must not be dereferenced until it's generated, since
+ // it could be being generated on another thread.
+ Target* GetTarget(const Label& label,
+ const LocationRange& specified_from_here,
+ Target* dep_from,
+ Err* err);
+
+ // Called by a target when it has been loaded from the .gin file. Its
+ // dependencies may or may not be resolved yet.
+ void TargetGenerationComplete(const Label& label);
+
+ // Returns a list of all targets.
+ void GetAllTargets(std::vector<const Target*>* all_targets) const;
+
+ private:
+ bool ScheduleInvocationLocked(const LocationRange& specified_from_here,
+ const Label& label,
+ Err* err);
+
+ const BuildSettings* build_settings_;
+
+ DISALLOW_COPY_AND_ASSIGN(TargetManager);
+};
+
+#endif // TOOLS_GN_TARGET_MANAGER_H
diff --git a/tools/gn/target_manager_unittest.cc b/tools/gn/target_manager_unittest.cc
new file mode 100644
index 0000000..a1a3ad2
--- /dev/null
+++ b/tools/gn/target_manager_unittest.cc
@@ -0,0 +1,95 @@
+// 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/err.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/target_manager.h"
+#include "tools/gn/value.h"
+
+/* TODO(brettw) make this compile again
+namespace {
+
+class TestTargetManagerDelegate : public TargetManager::Delegate {
+ public:
+ TestTargetManagerDelegate() {}
+
+ virtual bool ScheduleInvocation(ToolchainManager* toolchain_manager,
+ const LocationRange& origin,
+ const Label& toolchain_name,
+ const SourceDir& dir,
+ Err* err) OVERRIDE {
+ invokes.push_back(dir.value());
+ return true;
+ }
+ virtual void ScheduleTargetFileWrite(const Target* target) {
+ writes.push_back(target);
+ }
+
+ std::vector<std::string> invokes;
+ std::vector<const Target*> writes;
+};
+
+} // namespace
+
+TEST(TargetManager, ResolveDeps) {
+ TestTargetManagerDelegate ttmd;
+ BuildSettings build_settings(&ttmd);
+
+ TargetManager& target_manager = build_settings.target_manager();
+
+ SourceDir tc_dir("/chrome/");
+ std::string tc_name("toolchain");
+
+ // Get a root target. This should not invoke anything.
+ Err err;
+ Label chromelabel(SourceDir("/chrome/"), "chrome", tc_dir, tc_name);
+ Target* chrome = target_manager.GetTarget(
+ chromelabel, LocationRange(), NULL, &err);
+ EXPECT_EQ(0u, ttmd.invokes.size());
+
+ // Declare it has a dependency on content1 and 2. We should get one
+ // invocation of the content build file.
+ Label content1label(SourceDir("/content/"), "content1", tc_dir, tc_name);
+ Target* content1 = target_manager.GetTarget(
+ content1label, LocationRange(), chrome, &err);
+ EXPECT_EQ(1u, ttmd.invokes.size());
+
+ Label content2label(SourceDir("/content/"), "content2", tc_dir, tc_name);
+ Target* content2 = target_manager.GetTarget(
+ content2label, LocationRange(), chrome, &err);
+ EXPECT_EQ(2u, ttmd.invokes.size());
+
+ // Declare chrome has a depdency on base, this should load it.
+ Label baselabel(SourceDir("/base/"), "base", tc_dir, tc_name);
+ Target* base1 = target_manager.GetTarget(
+ baselabel, LocationRange(), chrome, &err);
+ EXPECT_EQ(3u, ttmd.invokes.size());
+
+ // Declare content1 has a dependency on base.
+ Target* base2 = target_manager.GetTarget(
+ baselabel, LocationRange(), content1, &err);
+ EXPECT_EQ(3u, ttmd.invokes.size());
+ EXPECT_EQ(base1, base2);
+
+ // Mark content1 and chrome as done. They have unresolved depdendencies so
+ // shouldn't be written out yet.
+ target_manager.TargetGenerationComplete(content1label);
+ target_manager.TargetGenerationComplete(chromelabel);
+ EXPECT_EQ(0u, ttmd.writes.size());
+
+ // Mark content2 as done. It has no dependencies so should be written.
+ target_manager.TargetGenerationComplete(content2label);
+ ASSERT_EQ(1u, ttmd.writes.size());
+ EXPECT_EQ(content2label, ttmd.writes[0]->label());
+
+ // Mark base as complete. It should have caused itself, content1 and then
+ // chrome to be written.
+ target_manager.TargetGenerationComplete(baselabel);
+ ASSERT_EQ(4u, ttmd.writes.size());
+ EXPECT_EQ(baselabel, ttmd.writes[1]->label());
+ EXPECT_EQ(content1label, ttmd.writes[2]->label());
+ EXPECT_EQ(chromelabel, ttmd.writes[3]->label());
+}
+*/
diff --git a/tools/gn/token.cc b/tools/gn/token.cc
new file mode 100644
index 0000000..cbce5e7
--- /dev/null
+++ b/tools/gn/token.cc
@@ -0,0 +1,60 @@
+// 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/token.h"
+
+#include "base/logging.h"
+
+namespace {
+
+std::string UnescapeString(const base::StringPiece& input) {
+ std::string result;
+ result.reserve(input.size());
+
+ for (size_t i = 0; i < input.size(); i++) {
+ if (input[i] == '\\') {
+ DCHECK(i < input.size() - 1); // Last char shouldn't be a backslash or
+ // it would have escaped the terminator.
+ i++; // Skip backslash, next char is a literal.
+ }
+ result.push_back(input[i]);
+ }
+ return result;
+}
+
+} // namespace
+
+Token::Token() : type_(INVALID), value_() {
+}
+
+Token::Token(const Location& location,
+ Type t,
+ const base::StringPiece& v)
+ : type_(t),
+ value_(v),
+ location_(location) {
+}
+
+bool Token::IsIdentifierEqualTo(const char* v) const {
+ return type_ == IDENTIFIER && value_ == v;
+}
+
+bool Token::IsOperatorEqualTo(const char* v) const {
+ return type_ == OPERATOR && value_ == v;
+}
+
+bool Token::IsScoperEqualTo(const char* v) const {
+ return type_ == SCOPER && value_ == v;
+}
+
+bool Token::IsStringEqualTo(const char* v) const {
+ return type_ == STRING && value_ == v;
+}
+
+std::string Token::StringValue() const {
+ DCHECK(type() == STRING);
+
+ // Trim off the string terminators at the end.
+ return UnescapeString(value_.substr(1, value_.size() - 2));
+}
diff --git a/tools/gn/token.h b/tools/gn/token.h
new file mode 100644
index 0000000..64b06ea
--- /dev/null
+++ b/tools/gn/token.h
@@ -0,0 +1,52 @@
+// 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.
+
+#ifndef TOOLS_GN_TOKEN_H_
+#define TOOLS_GN_TOKEN_H_
+
+#include "base/strings/string_piece.h"
+#include "tools/gn/location.h"
+
+class Token {
+ public:
+ enum Type {
+ INVALID,
+ INTEGER, // 123
+ STRING, // "blah"
+ OPERATOR, // =, +=, -=, +, -, ==, !=, <=, >=, <, >
+ IDENTIFIER, // foo
+ SCOPER, // (, ), [, ], {, }
+ SEPARATOR, // ,
+ COMMENT // #...\n
+ };
+
+ Token();
+ Token(const Location& location, Type t, const base::StringPiece& v);
+
+ Type type() const { return type_; }
+ const base::StringPiece& value() const { return value_; }
+ const Location& location() const { return location_; }
+ LocationRange range() const {
+ return LocationRange(location_,
+ Location(location_.file(), location_.line_number(),
+ location_.char_offset() + value_.size()));
+ }
+
+ // Helper functions for comparing this token to something.
+ bool IsIdentifierEqualTo(const char* v) const;
+ bool IsOperatorEqualTo(const char* v) const;
+ bool IsScoperEqualTo(const char* v) const;
+ bool IsStringEqualTo(const char* v) const;
+
+ // For STRING tokens, returns the string value (no quotes at end, does
+ // unescaping).
+ std::string StringValue() const;
+
+ private:
+ Type type_;
+ base::StringPiece value_;
+ Location location_;
+};
+
+#endif // TOOLS_GN_TOKEN_H_
diff --git a/tools/gn/tokenizer.cc b/tools/gn/tokenizer.cc
new file mode 100644
index 0000000..971f56b
--- /dev/null
+++ b/tools/gn/tokenizer.cc
@@ -0,0 +1,309 @@
+// 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/tokenizer.h"
+
+#include "base/logging.h"
+#include "tools/gn/input_file.h"
+
+namespace {
+
+bool IsNumberChar(char c) {
+ return c == '-' || (c >= '0' && c <= '9');
+}
+
+bool CouldBeTwoCharOperatorBegin(char c) {
+ return c == '<' || c == '>' || c == '!' || c == '=' || c == '-' ||
+ c == '+' || c == '|' || c == '&';
+}
+
+bool CouldBeTwoCharOperatorEnd(char c) {
+ return c == '=' || c == '|' || c == '&';
+}
+
+bool CouldBeOneCharOperator(char c) {
+ return c == '=' || c == '<' || c == '>' || c == '+' || c == '!' ||
+ c == ':' || c == '|' || c == '&' || c == '-';
+}
+
+bool CouldBeOperator(char c) {
+ return CouldBeOneCharOperator(c) || CouldBeTwoCharOperatorBegin(c);
+}
+
+bool IsSeparatorChar(char c) {
+ return c == ',';
+}
+
+bool IsScoperChar(char c) {
+ return c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}';
+}
+
+} // namespace
+
+Tokenizer::Tokenizer(const InputFile* input_file, Err* err)
+ : input_file_(input_file),
+ input_(input_file->contents()),
+ err_(err),
+ cur_(0),
+ line_number_(1),
+ char_in_line_(1) {
+}
+
+Tokenizer::~Tokenizer() {
+}
+
+// static
+std::vector<Token> Tokenizer::Tokenize(const InputFile* input_file, Err* err) {
+ Tokenizer t(input_file, err);
+ return t.Run();
+}
+
+std::vector<Token> Tokenizer::Run() {
+ std::vector<Token> tokens;
+ while (!done()) {
+ AdvanceToNextToken();
+ if (done())
+ break;
+ Location location = GetCurrentLocation();
+
+ Token::Type type = ClassifyCurrent();
+ if (type == Token::INVALID) {
+ *err_ = GetErrorForInvalidToken(location);
+ break;
+ }
+ size_t token_begin = cur_;
+ AdvanceToEndOfToken(location, type);
+ if (has_error())
+ break;
+ size_t token_end = cur_;
+
+ // TODO(brettw) This just strips comments from the token stream. This
+ // is probably wrong, they should be removed at a later stage so we can
+ // do things like rewrite the file. But this makes the parser simpler and
+ // is OK for now.
+ if (type != Token::COMMENT) {
+ tokens.push_back(Token(
+ location,
+ type,
+ base::StringPiece(&input_.data()[token_begin],
+ token_end - token_begin)));
+ }
+ }
+ if (err_->has_error())
+ tokens.clear();
+ return tokens;
+}
+
+// static
+size_t Tokenizer::ByteOffsetOfNthLine(const base::StringPiece& buf, int n) {
+ int cur_line = 1;
+ size_t cur_byte = 0;
+
+ DCHECK(n > 0);
+
+ if (n == 1)
+ return 0;
+
+ while (cur_byte < buf.size()) {
+ if (IsNewline(buf, cur_byte)) {
+ cur_line++;
+ if (cur_line == n)
+ return cur_byte + 1;
+ }
+ cur_byte++;
+ }
+ return -1;
+}
+
+// static
+bool Tokenizer::IsNewline(const base::StringPiece& buffer, size_t offset) {
+ DCHECK(offset < buffer.size());
+ // We may need more logic here to handle different line ending styles.
+ return buffer[offset] == '\n';
+}
+
+
+void Tokenizer::AdvanceToNextToken() {
+ while (!at_end() && IsCurrentWhitespace())
+ Advance();
+}
+
+Token::Type Tokenizer::ClassifyCurrent() const {
+ DCHECK(!at_end());
+ char next_char = cur_char();
+ if (next_char >= '0' && next_char <= '9')
+ return Token::INTEGER;
+ if (next_char == '"')
+ return Token::STRING;
+
+ // Note: '-' handled specially below.
+ if (next_char != '-' && CouldBeOperator(next_char))
+ return Token::OPERATOR;
+
+ if (IsIdentifierFirstChar(next_char))
+ return Token::IDENTIFIER;
+
+ if (IsScoperChar(next_char))
+ return Token::SCOPER;
+
+ if (IsSeparatorChar(next_char))
+ return Token::SEPARATOR;
+
+ if (next_char == '#')
+ return Token::COMMENT;
+
+ // For the case of '-' differentiate between a negative number and anything
+ // else.
+ if (next_char == '-') {
+ if (!CanIncrement())
+ return Token::OPERATOR; // Just the minus before end of file.
+ char following_char = input_[cur_ + 1];
+ if (following_char >= '0' && following_char <= '9')
+ return Token::INTEGER;
+ return Token::OPERATOR;
+ }
+
+ return Token::INVALID;
+}
+
+void Tokenizer::AdvanceToEndOfToken(const Location& location,
+ Token::Type type) {
+ switch (type) {
+ case Token::INTEGER:
+ do {
+ Advance();
+ } while (!at_end() && IsNumberChar(cur_char()));
+ if (!at_end()) {
+ // Require the char after a number to be some kind of space, scope,
+ // or operator.
+ char c = cur_char();
+ if (!IsCurrentWhitespace() && !CouldBeOperator(c) &&
+ !IsScoperChar(c) && !IsSeparatorChar(c)) {
+ *err_ = Err(GetCurrentLocation(),
+ "This is not a valid number.",
+ "Learn to count.");
+ // Highlight the number.
+ err_->AppendRange(LocationRange(location, GetCurrentLocation()));
+ }
+ }
+ break;
+
+ case Token::STRING: {
+ char initial = cur_char();
+ Advance(); // Advance past initial "
+ for (;;) {
+ if (at_end()) {
+ *err_ = Err(LocationRange(location,
+ Location(input_file_, line_number_, char_in_line_)),
+ "Unterminated string literal.",
+ "Don't leave me hanging like this!");
+ break;
+ }
+ if (IsCurrentStringTerminator(initial)) {
+ Advance(); // Skip past last "
+ break;
+ } else if (cur_char() == '\n') {
+ *err_ = Err(LocationRange(location,
+ GetCurrentLocation()),
+ "Newline in string constant.");
+ }
+ Advance();
+ }
+ break;
+ }
+
+ case Token::OPERATOR:
+ // Some operators are two characters, some are one.
+ if (CouldBeTwoCharOperatorBegin(cur_char())) {
+ if (CanIncrement() && CouldBeTwoCharOperatorEnd(input_[cur_ + 1]))
+ Advance();
+ }
+ Advance();
+ break;
+
+ case Token::IDENTIFIER:
+ while (!at_end() && IsIdentifierContinuingChar(cur_char()))
+ Advance();
+ break;
+
+ case Token::SCOPER:
+ case Token::SEPARATOR:
+ Advance(); // All are one char.
+ break;
+
+ case Token::COMMENT:
+ // Eat to EOL.
+ while (!at_end() && !IsCurrentNewline())
+ Advance();
+ break;
+
+ case Token::INVALID:
+ *err_ = Err(location, "Everything is all messed up",
+ "Please insert system disk in drive A: and press any key.");
+ NOTREACHED();
+ return;
+ }
+}
+
+bool Tokenizer::IsCurrentWhitespace() const {
+ DCHECK(!at_end());
+ char c = input_[cur_];
+ // Note that tab (0x09) is illegal.
+ return c == 0x0A || c == 0x0B || c == 0x0C || c == 0x0D || c == 0x20;
+}
+
+bool Tokenizer::IsCurrentStringTerminator(char quote_char) const {
+ DCHECK(!at_end());
+ if (cur_char() != quote_char)
+ return false;
+
+ // Check for escaping. \" is not a string terminator, but \\" is. Count
+ // the number of preceeding backslashes.
+ int num_backslashes = 0;
+ for (int i = static_cast<int>(cur_) - 1; i >= 0 && input_[i] == '\\'; i--)
+ num_backslashes++;
+
+ // Even backslashes mean that they were escaping each other and don't count
+ // as escaping this quote.
+ return (num_backslashes % 2) == 0;
+}
+
+bool Tokenizer::IsCurrentNewline() const {
+ return IsNewline(input_, cur_);
+}
+
+void Tokenizer::Advance() {
+ DCHECK(cur_ < input_.size());
+ if (IsCurrentNewline()) {
+ line_number_++;
+ char_in_line_ = 1;
+ } else {
+ char_in_line_++;
+ }
+ cur_++;
+}
+
+Location Tokenizer::GetCurrentLocation() const {
+ return Location(input_file_, line_number_, char_in_line_);
+}
+
+Err Tokenizer::GetErrorForInvalidToken(const Location& location) const {
+ std::string help;
+ if (cur_char() == ';') {
+ // Semicolon.
+ help = "Semicolons are not needed, delete this one.";
+ } else if (cur_char() == '\t') {
+ // Tab.
+ help = "You got a tab character in here. Tabs are evil. "
+ "Convert to spaces.";
+ } else if (cur_char() == '/' && cur_ + 1 < input_.size() &&
+ (input_[cur_ + 1] == '/' || input_[cur_ + 1] == '*')) {
+ // Different types of comments.
+ help = "Comments should start with # instead";
+ } else {
+ help = "I have no idea what this is.";
+ }
+
+ return Err(location, "Invalid token.", help);
+}
diff --git a/tools/gn/tokenizer.h b/tools/gn/tokenizer.h
new file mode 100644
index 0000000..5e00169
--- /dev/null
+++ b/tools/gn/tokenizer.h
@@ -0,0 +1,86 @@
+// 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.
+
+#ifndef TOOLS_GN_TOKENIZER_H_
+#define TOOLS_GN_TOKENIZER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "tools/gn/err.h"
+#include "tools/gn/token.h"
+
+class InputFile;
+
+class Tokenizer {
+ public:
+ static std::vector<Token> Tokenize(const InputFile* input_file, Err* err);
+
+ // Counts lines in the given buffer (the first line is "1") and returns
+ // the byte offset of the beginning of that line, or (size_t)-1 if there
+ // aren't that many lines in the file. Note that this will return the byte
+ // one past the end of the input if the last character is a newline.
+ //
+ // This is a helper function for error output so that the tokenizer's
+ // notion of lines can be used elsewhere.
+ static size_t ByteOffsetOfNthLine(const base::StringPiece& buf, int n);
+
+ // Returns true if the given offset of the string piece counts as a newline.
+ // The offset must be in the buffer.
+ static bool IsNewline(const base::StringPiece& buffer, size_t offset);
+
+ static bool IsIdentifierFirstChar(char c) {
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_';
+ }
+
+ static bool IsIdentifierContinuingChar(char c) {
+ // Also allow digits after the first char.
+ return IsIdentifierFirstChar(c) || (c >= '0' && c <= '9');
+ }
+
+ private:
+ // InputFile must outlive the tokenizer and all generated tokens.
+ explicit Tokenizer(const InputFile* input_file, Err* err);
+ ~Tokenizer();
+
+ std::vector<Token> Run();
+
+ void AdvanceToNextToken();
+ Token::Type ClassifyCurrent() const;
+ void AdvanceToEndOfToken(const Location& location, Token::Type type);
+
+ bool IsCurrentWhitespace() const;
+ bool IsCurrentNewline() const;
+ bool IsCurrentStringTerminator(char quote_char) const;
+
+ bool CanIncrement() const { return cur_ < input_.size(); }
+
+ // Increments the current location by one.
+ void Advance();
+
+ // Returns the current character in the file as a location.
+ Location GetCurrentLocation() const;
+
+ Err GetErrorForInvalidToken(const Location& location) const;
+
+ bool done() const { return at_end() || has_error(); }
+
+ bool at_end() const { return cur_ == input_.size(); }
+ char cur_char() const { return input_[cur_]; }
+
+ bool has_error() const { return err_->has_error(); }
+
+ const InputFile* input_file_;
+ const base::StringPiece input_;
+ Err* err_;
+ size_t cur_; // Byte offset into input buffer.
+
+ int line_number_;
+ int char_in_line_;
+
+ DISALLOW_COPY_AND_ASSIGN(Tokenizer);
+};
+
+#endif // TOOLS_GN_TOKENIZER_H_
diff --git a/tools/gn/tokenizer_unittest.cc b/tools/gn/tokenizer_unittest.cc
new file mode 100644
index 0000000..d1a6788
--- /dev/null
+++ b/tools/gn/tokenizer_unittest.cc
@@ -0,0 +1,162 @@
+// 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/input_file.h"
+#include "tools/gn/token.h"
+#include "tools/gn/tokenizer.h"
+
+namespace {
+
+struct TokenExpectation {
+ Token::Type type;
+ const char* value;
+};
+
+template<size_t len>
+bool CheckTokenizer(const char* input, const TokenExpectation (&expect)[len]) {
+ InputFile input_file(SourceFile("/test"));
+ input_file.SetContents(input);
+
+ Err err;
+ std::vector<Token> results = Tokenizer::Tokenize(&input_file, &err);
+
+ if (results.size() != len)
+ return false;
+ for (size_t i = 0; i < len; i++) {
+ if (expect[i].type != results[i].type())
+ return false;
+ if (expect[i].value != results[i].value())
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+TEST(Tokenizer, Empty) {
+ InputFile empty_string_input(SourceFile("/test"));
+ empty_string_input.SetContents("");
+
+ Err err;
+ std::vector<Token> results = Tokenizer::Tokenize(&empty_string_input, &err);
+ EXPECT_TRUE(results.empty());
+
+ InputFile whitespace_input(SourceFile("/test"));
+ whitespace_input.SetContents(" \n\r");
+
+ results = Tokenizer::Tokenize(&whitespace_input, &err);
+ EXPECT_TRUE(results.empty());
+}
+
+TEST(Tokenizer, Identifier) {
+ TokenExpectation one_ident[] = {
+ { Token::IDENTIFIER, "foo" }
+ };
+ EXPECT_TRUE(CheckTokenizer(" foo ", one_ident));
+}
+
+TEST(Tokenizer, Integer) {
+ TokenExpectation integers[] = {
+ { Token::INTEGER, "123" },
+ { Token::INTEGER, "-123" }
+ };
+ EXPECT_TRUE(CheckTokenizer(" 123 -123 ", integers));
+}
+
+TEST(Tokenizer, String) {
+ TokenExpectation strings[] = {
+ { Token::STRING, "\"foo\"" },
+ { Token::STRING, "\"bar\\\"baz\"" },
+ { Token::STRING, "\"asdf\\\\\"" }
+ };
+ EXPECT_TRUE(CheckTokenizer(" \"foo\" \"bar\\\"baz\" \"asdf\\\\\" ",
+ strings));
+}
+
+TEST(Tokenizer, Operator) {
+ TokenExpectation operators[] = {
+ { Token::OPERATOR, "-" },
+ { Token::OPERATOR, "+" },
+ { Token::OPERATOR, "=" },
+ { Token::OPERATOR, "+=" },
+ { Token::OPERATOR, "-=" },
+ { Token::OPERATOR, "!=" },
+ { Token::OPERATOR, "==" },
+ { Token::OPERATOR, "<" },
+ { Token::OPERATOR, ">" },
+ { Token::OPERATOR, "<=" },
+ { Token::OPERATOR, ">=" },
+ };
+ EXPECT_TRUE(CheckTokenizer("- + = += -= != == < > <= >=",
+ operators));
+}
+
+TEST(Tokenizer, Scoper) {
+ TokenExpectation scopers[] = {
+ { Token::SCOPER, "{" },
+ { Token::SCOPER, "[" },
+ { Token::SCOPER, "]" },
+ { Token::SCOPER, "}" },
+ { Token::SCOPER, "(" },
+ { Token::SCOPER, ")" },
+ };
+ EXPECT_TRUE(CheckTokenizer("{[ ]} ()", scopers));
+}
+
+TEST(Tokenizer, FunctionCall) {
+ TokenExpectation fn[] = {
+ { Token::IDENTIFIER, "fun" },
+ { Token::SCOPER, "(" },
+ { Token::STRING, "\"foo\"" },
+ { Token::SCOPER, ")" },
+ { Token::SCOPER, "{" },
+ { Token::IDENTIFIER, "foo" },
+ { Token::OPERATOR, "=" },
+ { Token::INTEGER, "12" },
+ { Token::SCOPER, "}" },
+ };
+ EXPECT_TRUE(CheckTokenizer("fun(\"foo\") {\nfoo = 12}", fn));
+}
+
+TEST(Tokenizer, StringUnescaping) {
+ InputFile input(SourceFile("/test"));
+ input.SetContents("\"asd\\\"f\" \"\"");
+ Err err;
+ std::vector<Token> results = Tokenizer::Tokenize(&input, &err);
+
+ ASSERT_EQ(2u, results.size());
+ EXPECT_EQ("asd\"f", results[0].StringValue());
+ EXPECT_EQ("", results[1].StringValue());
+}
+
+TEST(Tokenizer, Locations) {
+ InputFile input(SourceFile("/test"));
+ input.SetContents("1 2 \"three\"\n 4");
+ Err err;
+ std::vector<Token> results = Tokenizer::Tokenize(&input, &err);
+
+ ASSERT_EQ(4u, results.size());
+ ASSERT_TRUE(results[0].location() == Location(&input, 1, 1));
+ ASSERT_TRUE(results[1].location() == Location(&input, 1, 3));
+ ASSERT_TRUE(results[2].location() == Location(&input, 1, 5));
+ ASSERT_TRUE(results[3].location() == Location(&input, 2, 3));
+}
+
+TEST(Tokenizer, ByteOffsetOfNthLine) {
+ EXPECT_EQ(0u, Tokenizer::ByteOffsetOfNthLine("foo", 1));
+
+ // Windows and Posix have different line endings, so check the byte at the
+ // location rather than the offset.
+ char input1[] = "aaa\nxaa\n\nya";
+ EXPECT_EQ('x', input1[Tokenizer::ByteOffsetOfNthLine(input1, 2)]);
+ EXPECT_EQ('y', input1[Tokenizer::ByteOffsetOfNthLine(input1, 4)]);
+
+ char input2[3];
+ input2[0] = 'a';
+ input2[1] = '\n'; // Manually set to avoid Windows double-byte endings.
+ input2[2] = 0;
+ EXPECT_EQ(0u, Tokenizer::ByteOffsetOfNthLine(input2, 1));
+ EXPECT_EQ(2u, Tokenizer::ByteOffsetOfNthLine(input2, 2));
+}
diff --git a/tools/gn/toolchain.cc b/tools/gn/toolchain.cc
new file mode 100644
index 0000000..20b81f5
--- /dev/null
+++ b/tools/gn/toolchain.cc
@@ -0,0 +1,79 @@
+// 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/toolchain.h"
+
+#include "base/logging.h"
+
+const char* Toolchain::kToolCc = "cc";
+const char* Toolchain::kToolCxx = "cxx";
+const char* Toolchain::kToolAsm = "asm";
+const char* Toolchain::kToolAlink = "alink";
+const char* Toolchain::kToolSolink = "solink";
+const char* Toolchain::kToolSolinkModule = "solink_module";
+const char* Toolchain::kToolLink = "link";
+const char* Toolchain::kToolStamp = "stamp";
+const char* Toolchain::kToolCopy = "copy";
+
+Toolchain::Tool::Tool() {
+}
+
+Toolchain::Tool::~Tool() {
+}
+
+Toolchain::Toolchain(const Label& label) : Item(label) {
+}
+
+Toolchain::~Toolchain() {
+}
+
+Toolchain* Toolchain::AsToolchain() {
+ return this;
+}
+
+const Toolchain* Toolchain::AsToolchain() const {
+ return this;
+}
+
+// static
+Toolchain::ToolType Toolchain::ToolNameToType(const base::StringPiece& str) {
+ if (str == kToolCc) return TYPE_CC;
+ if (str == kToolCxx) return TYPE_CXX;
+ if (str == kToolAsm) return TYPE_ASM;
+ if (str == kToolAlink) return TYPE_ALINK;
+ if (str == kToolSolink) return TYPE_SOLINK;
+ if (str == kToolSolinkModule) return TYPE_SOLINK_MODULE;
+ if (str == kToolLink) return TYPE_LINK;
+ if (str == kToolStamp) return TYPE_STAMP;
+ if (str == kToolCopy) return TYPE_COPY;
+ return TYPE_NONE;
+}
+
+// static
+std::string Toolchain::ToolTypeToName(ToolType type) {
+ switch (type) {
+ case TYPE_CC: return kToolCc;
+ case TYPE_CXX: return kToolCxx;
+ case TYPE_ASM: return kToolAsm;
+ case TYPE_ALINK: return kToolAlink;
+ case TYPE_SOLINK: return kToolSolink;
+ case TYPE_SOLINK_MODULE: return kToolSolinkModule;
+ case TYPE_LINK: return kToolLink;
+ case TYPE_STAMP: return kToolStamp;
+ case TYPE_COPY: return kToolCopy;
+ default:
+ NOTREACHED();
+ return std::string();
+ }
+}
+
+const Toolchain::Tool& Toolchain::GetTool(ToolType type) const {
+ DCHECK(type != TYPE_NONE);
+ return tools_[static_cast<size_t>(type)];
+}
+
+void Toolchain::SetTool(ToolType type, const Tool& t) {
+ DCHECK(type != TYPE_NONE);
+ tools_[static_cast<size_t>(type)] = t;
+}
diff --git a/tools/gn/toolchain.h b/tools/gn/toolchain.h
new file mode 100644
index 0000000..22b8151
--- /dev/null
+++ b/tools/gn/toolchain.h
@@ -0,0 +1,94 @@
+// 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.
+
+#ifndef TOOLS_GN_TOOLCHAIN_H_
+#define TOOLS_GN_TOOLCHAIN_H_
+
+#include "base/compiler_specific.h"
+#include "base/strings/string_piece.h"
+#include "tools/gn/item.h"
+
+// Holds information on a specific toolchain. This data is filled in when we
+// encounter a toolchain definition.
+//
+// This class is an Item so it can participate in dependency management. In
+// particular, when a target uses a toolchain, it should have a dependency on
+// that toolchain's object so that we can be sure we loaded the toolchain
+// before generating the build for that target.
+//
+// Note on threadsafety: The label of the toolchain never changes so can
+// safetly be accessed from any thread at any time (we do this when asking for
+// the toolchain name). But the values in the toolchain do, so these can't
+// be accessed until this Item is resolved.
+class Toolchain : public Item {
+ public:
+ enum ToolType {
+ TYPE_NONE = 0,
+ TYPE_CC,
+ TYPE_CXX,
+ TYPE_ASM,
+ TYPE_ALINK,
+ TYPE_SOLINK,
+ TYPE_SOLINK_MODULE,
+ TYPE_LINK,
+ TYPE_STAMP,
+ TYPE_COPY,
+
+ TYPE_NUMTYPES // Must be last.
+ };
+
+ static const char* kToolCc;
+ static const char* kToolCxx;
+ static const char* kToolAsm;
+ static const char* kToolAlink;
+ static const char* kToolSolink;
+ static const char* kToolSolinkModule;
+ static const char* kToolLink;
+ static const char* kToolStamp;
+ static const char* kToolCopy;
+
+ struct Tool {
+ Tool();
+ ~Tool();
+
+ bool empty() const {
+ return command.empty() && depfile.empty() && deps.empty() &&
+ description.empty() && pool.empty() && restat.empty() &&
+ rspfile.empty() && rspfile_content.empty();
+ }
+
+ std::string command;
+ std::string depfile;
+ std::string deps;
+ std::string description;
+ std::string pool;
+ std::string restat;
+ std::string rspfile;
+ std::string rspfile_content;
+ };
+
+ Toolchain(const Label& label);
+ virtual ~Toolchain();
+
+ // Item overrides.
+ virtual Toolchain* AsToolchain() OVERRIDE;
+ virtual const Toolchain* AsToolchain() const OVERRIDE;
+
+ // Returns TYPE_NONE on failure.
+ static ToolType ToolNameToType(const base::StringPiece& str);
+ static std::string ToolTypeToName(ToolType type);
+
+ const Tool& GetTool(ToolType type) const;
+ void SetTool(ToolType type, const Tool& t);
+
+ const std::string& environment() const { return environment_; }
+ void set_environment(const std::string& env) { environment_ = env; }
+
+ private:
+ Tool tools_[TYPE_NUMTYPES];
+
+ std::string environment_;
+};
+
+#endif // TOOLS_GN_TOOLCHAIN_H_
diff --git a/tools/gn/toolchain_manager.cc b/tools/gn/toolchain_manager.cc
new file mode 100644
index 0000000..8a54163
--- /dev/null
+++ b/tools/gn/toolchain_manager.cc
@@ -0,0 +1,480 @@
+// 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/toolchain_manager.h"
+
+#include <set>
+
+#include "base/bind.h"
+#include "tools/gn/err.h"
+#include "tools/gn/item.h"
+#include "tools/gn/item_node.h"
+#include "tools/gn/item_tree.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/scope_per_file_provider.h"
+
+namespace {
+
+SourceFile DirToBuildFile(const SourceDir& dir) {
+ return SourceFile(dir.value() + "BUILD.gn");
+}
+
+void SetSystemVars(const Settings& settings, Scope* scope) {
+ // FIXME(brettw) port.
+ scope->SetValue("is_win", Value(NULL, 1), NULL);
+ scope->SetValue("is_linux", Value(NULL, 0), NULL);
+ scope->SetValue("is_posix", Value(NULL, 0), NULL);
+ scope->SetValue("is_mac", Value(NULL, 0), NULL);
+ scope->SetValue("is_android", Value(NULL, 0), NULL);
+ scope->SetValue("is_ios", Value(NULL, 0), NULL);
+
+ // Set this value without the terminating slash because the code expects
+ // $root_output_dir/foo to work.
+ scope->SetValue(ScopePerFileProvider::kRootOutputDirName,
+ ScopePerFileProvider::GetRootOutputDir(&settings),
+ NULL);
+ scope->SetValue(ScopePerFileProvider::kRootGenDirName,
+ ScopePerFileProvider::GetRootGenDir(&settings),
+ NULL);
+}
+
+} // namespace
+
+struct ToolchainManager::Info {
+ Info(const BuildSettings* build_settings,
+ const Label& toolchain_name,
+ const std::string& output_subdir_name)
+ : state(TOOLCHAIN_SETTINGS_NOT_LOADED),
+ toolchain(toolchain_name),
+ toolchain_set(false),
+ settings(build_settings, &toolchain, output_subdir_name),
+ item_node(NULL) {
+ }
+
+ // MAkes sure that an ItemNode is created for the toolchain, which lets
+ // targets depend on the (potentially future) loading of the toolchain.
+ //
+ // We can't always do this at the beginning since when doing the default
+ // build config, we don't know the toolchain name yet. We also need to go
+ // through some effort to avoid doing this inside the toolchain manager's
+ // lock (to avoid holding two locks at once).
+ void EnsureItemNode() {
+ if (!item_node) {
+ ItemTree& tree = settings.build_settings()->item_tree();
+ item_node = new ItemNode(&toolchain);
+ tree.AddNodeLocked(item_node);
+ }
+ }
+
+ SettingsState state;
+
+ Toolchain toolchain;
+ bool toolchain_set;
+ LocationRange toolchain_definition_location;
+
+ // When the state is TOOLCHAIN_SETTINGS_LOADED, the settings should be
+ // considered read-only and can be read without locking. Otherwise, they
+ // should not be accessed at all except to load them (which can therefore
+ // also be done outside of the lock). This works as long as the state flag
+ // is only ever read or written inside the lock.
+ Settings settings;
+
+ // While state == TOOLCHAIN_SETTINGS_LOADING, this will collect all
+ // scheduled invocations using this toolchain. They'll be issued once the
+ // settings file has been interpreted.
+ //
+ // The map maps the source file to "some" location it was invoked from (so
+ // we can give good error messages. It does NOT map to the root of the
+ // file to be invoked (the file still needs loading). This will be NULL
+ // for internally invoked files.
+ typedef std::map<SourceFile, LocationRange> ScheduledInvocationMap;
+ ScheduledInvocationMap scheduled_invocations;
+
+ // Tracks all scheduled and executed invocations for this toolchain. This
+ // is used to avoid invoking a file more than once for a toolchain.
+ std::set<SourceFile> all_invocations;
+
+ // Filled in by EnsureItemNode, see that for more.
+ ItemNode* item_node;
+};
+
+ToolchainManager::ToolchainManager(const BuildSettings* build_settings)
+ : build_settings_(build_settings) {
+}
+
+ToolchainManager::~ToolchainManager() {
+ for (ToolchainMap::iterator i = toolchains_.begin();
+ i != toolchains_.end(); ++i)
+ delete i->second;
+ toolchains_.clear();
+}
+
+void ToolchainManager::StartLoadingUnlocked(const SourceFile& build_file_name) {
+ // How the default build config works: Initially we don't have a toolchain
+ // name to call the settings for the default build config. So we create one
+ // with an empty toolchain name and execute the default build config file.
+ // When that's done, we'll go and fix up the name to the default build config
+ // that the script set.
+ base::AutoLock lock(GetLock());
+ Err err;
+ Info* info = LoadNewToolchainLocked(LocationRange(), Label(), &err);
+ if (err.has_error())
+ g_scheduler->FailWithError(err);
+ CHECK(info);
+ info->scheduled_invocations[build_file_name] = LocationRange();
+ info->all_invocations.insert(build_file_name);
+
+ g_scheduler->IncrementWorkCount();
+ if (!g_scheduler->input_file_manager()->AsyncLoadFile(
+ LocationRange(), build_settings_,
+ build_settings_->build_config_file(),
+ base::Bind(&ToolchainManager::BackgroundLoadBuildConfig,
+ base::Unretained(this), info, true),
+ &err)) {
+ g_scheduler->FailWithError(err);
+ g_scheduler->DecrementWorkCount();
+ }
+}
+
+const Settings* ToolchainManager::GetSettingsForToolchainLocked(
+ const LocationRange& from_here,
+ const Label& toolchain_name,
+ Err* err) {
+ GetLock().AssertAcquired();
+ ToolchainMap::iterator found = toolchains_.find(toolchain_name);
+ Info* info = NULL;
+ if (found == toolchains_.end()) {
+ info = LoadNewToolchainLocked(from_here, toolchain_name, err);
+ if (!info)
+ return NULL;
+ } else {
+ info = found->second;
+ }
+ info->EnsureItemNode();
+
+ return &info->settings;
+}
+
+const Toolchain* ToolchainManager::GetToolchainDefinitionUnlocked(
+ const Label& toolchain_name) {
+ base::AutoLock lock(GetLock());
+ ToolchainMap::iterator found = toolchains_.find(toolchain_name);
+ if (found == toolchains_.end() || !found->second->toolchain_set)
+ return NULL;
+
+ // Since we don't allow defining a toolchain more than once, we know that
+ // once it's set it won't be mutated, so we can safely return this pointer
+ // for reading outside the lock.
+ return &found->second->toolchain;
+}
+
+bool ToolchainManager::SetDefaultToolchainUnlocked(
+ const Label& default_toolchain,
+ const LocationRange& defined_here,
+ Err* err) {
+ base::AutoLock lock(GetLock());
+ if (!default_toolchain_.is_null()) {
+ *err = Err(defined_here, "Default toolchain already set.");
+ err->AppendSubErr(Err(default_toolchain_defined_here_,
+ "Previously defined here.",
+ "You can only set this once."));
+ return false;
+ }
+
+ if (default_toolchain.is_null()) {
+ *err = Err(defined_here, "Bad default toolchain name.",
+ "You can't set the default toolchain name to nothing.");
+ return false;
+ }
+ if (!default_toolchain.toolchain_dir().is_null() ||
+ !default_toolchain.toolchain_name().empty()) {
+ *err = Err(defined_here, "Toolchain name has toolchain.",
+ "You can't specify a toolchain (inside the parens) for a toolchain "
+ "name. I got:\n" + default_toolchain.GetUserVisibleName(true));
+ return false;
+ }
+
+ default_toolchain_ = default_toolchain;
+ default_toolchain_defined_here_ = defined_here;
+ return true;
+}
+
+Label ToolchainManager::GetDefaultToolchainUnlocked() const {
+ base::AutoLock lock(GetLock());
+ return default_toolchain_;
+}
+
+bool ToolchainManager::SetToolchainDefinitionLocked(
+ const Toolchain& tc,
+ const LocationRange& defined_from,
+ Err* err) {
+ GetLock().AssertAcquired();
+
+ ToolchainMap::iterator found = toolchains_.find(tc.label());
+ Info* info = NULL;
+ if (found == toolchains_.end()) {
+ // New toolchain.
+ info = LoadNewToolchainLocked(defined_from, tc.label(), err);
+ if (!info)
+ return false;
+ } else {
+ // It's important to preserve the exact Toolchain object in our tree since
+ // it will be in the ItemTree and targets may have dependencies on it.
+ info = found->second;
+ }
+
+ // The labels should match or else we're setting the wrong one!
+ CHECK(info->toolchain.label() == tc.label());
+
+ info->toolchain = tc;
+ if (info->toolchain_set) {
+ *err = Err(defined_from, "Duplicate toolchain definition.");
+ err->AppendSubErr(Err(
+ info->toolchain_definition_location,
+ "Previously defined here.",
+ "A toolchain can only be defined once. One tricky way that this could\n"
+ "happen is if your definition is itself in a file that's interpreted\n"
+ "under different toolchains, which would result in multiple\n"
+ "definitions as the file is loaded multiple times. So be sure your\n"
+ "toolchain definitions are in files that either don't define any\n"
+ "targets (probably best) or at least don't contain targets executed\n"
+ "with more than one toolchain."));
+ return false;
+ }
+
+ info->EnsureItemNode();
+
+ info->toolchain_set = true;
+ info->toolchain_definition_location = defined_from;
+ return true;
+}
+
+bool ToolchainManager::ScheduleInvocationLocked(
+ const LocationRange& specified_from,
+ const Label& toolchain_name,
+ const SourceDir& dir,
+ Err* err) {
+ GetLock().AssertAcquired();
+ SourceFile build_file(DirToBuildFile(dir));
+
+ ToolchainMap::iterator found = toolchains_.find(toolchain_name);
+ Info* info = NULL;
+ if (found == toolchains_.end()) {
+ // New toolchain.
+ info = LoadNewToolchainLocked(specified_from, toolchain_name, err);
+ if (!info)
+ return false;
+ } else {
+ // Use existing one.
+ info = found->second;
+ if (info->all_invocations.find(build_file) !=
+ info->all_invocations.end()) {
+ // We've already seen this source file for this toolchain, don't need
+ // to do anything.
+ return true;
+ }
+ }
+
+ info->all_invocations.insert(build_file);
+
+ // True if the settings load needs to be scheduled.
+ bool info_needs_settings_load = false;
+
+ // True if the settings load has completed.
+ bool info_settings_loaded = false;
+
+ switch (info->state) {
+ case TOOLCHAIN_SETTINGS_NOT_LOADED:
+ info_needs_settings_load = true;
+ info->scheduled_invocations[build_file] = specified_from;
+ break;
+
+ case TOOLCHAIN_SETTINGS_LOADING:
+ info->scheduled_invocations[build_file] = specified_from;
+ break;
+
+ case TOOLCHAIN_SETTINGS_LOADED:
+ info_settings_loaded = true;
+ break;
+ }
+
+ if (info_needs_settings_load) {
+ // Load the settings file.
+ g_scheduler->IncrementWorkCount();
+ if (!g_scheduler->input_file_manager()->AsyncLoadFile(
+ specified_from, build_settings_,
+ build_settings_->build_config_file(),
+ base::Bind(&ToolchainManager::BackgroundLoadBuildConfig,
+ base::Unretained(this), info, false),
+ err)) {
+ g_scheduler->DecrementWorkCount();
+ return false;
+ }
+ } else if (info_settings_loaded) {
+ // Settings are ready to go, load the target file.
+ g_scheduler->IncrementWorkCount();
+ if (!g_scheduler->input_file_manager()->AsyncLoadFile(
+ specified_from, build_settings_, build_file,
+ base::Bind(&ToolchainManager::BackgroundInvoke,
+ base::Unretained(this), info, build_file),
+ err)) {
+ g_scheduler->DecrementWorkCount();
+ return false;
+ }
+ }
+ // Else we should have added the infocations to the scheduled_invocations
+ // from within the lock above.
+ return true;
+}
+
+// static
+std::string ToolchainManager::ToolchainToOutputSubdir(
+ const Label& toolchain_name) {
+ // For now just assume the toolchain name is always a valid dir name. We may
+ // want to clean up the in the future.
+ return toolchain_name.name();
+}
+
+ToolchainManager::Info* ToolchainManager::LoadNewToolchainLocked(
+ const LocationRange& specified_from,
+ const Label& toolchain_name,
+ Err* err) {
+ GetLock().AssertAcquired();
+ Info* info = new Info(build_settings_,
+ toolchain_name,
+ ToolchainToOutputSubdir(toolchain_name));
+
+ toolchains_[toolchain_name] = info;
+
+ // Invoke the file containing the toolchain definition so that it gets
+ // defined. The default one (label is empty) will be done spearately.
+ if (!toolchain_name.is_null()) {
+ // The default toolchain should be specified whenever we're requesting
+ // another one. This is how we know under what context we should execute
+ // the invoke for the toolchain file.
+ CHECK(!default_toolchain_.is_null());
+ ScheduleInvocationLocked(specified_from, default_toolchain_,
+ toolchain_name.dir(), err);
+ }
+ return info;
+}
+
+void ToolchainManager::FixupDefaultToolchainLocked() {
+ // Now that we've run the default build config, we should know the
+ // default toolchain name. Fix up our reference.
+ // See Start() for more.
+ GetLock().AssertAcquired();
+ if (default_toolchain_.is_null()) {
+ g_scheduler->FailWithError(Err(Location(),
+ "Default toolchain not set.",
+ "Your build config file \"" +
+ build_settings_->build_config_file().value() +
+ "\"\ndid not call set_default_toolchain(). This is needed so "
+ "I know how to actually\ncompile your code."));
+ return;
+ }
+
+ ToolchainMap::iterator old_default = toolchains_.find(Label());
+ CHECK(old_default != toolchains_.end());
+ Info* info = old_default->second;
+ toolchains_[default_toolchain_] = info;
+ toolchains_.erase(old_default);
+
+ // Toolchain should not have been loaded in the build config file.
+ CHECK(!info->toolchain_set);
+
+ // We need to set the toolchain label now that we know it. There's no way
+ // to set the label, but we can assign the toolchain to a new one. Loading
+ // the build config can not change the toolchain, so we won't be overwriting
+ // anything useful.
+ info->toolchain = Toolchain(default_toolchain_);
+ info->EnsureItemNode();
+
+ // Schedule a load of the toolchain build file.
+ Err err;
+ ScheduleInvocationLocked(LocationRange(), default_toolchain_,
+ default_toolchain_.dir(), &err);
+ if (err.has_error())
+ g_scheduler->FailWithError(err);
+}
+
+void ToolchainManager::BackgroundLoadBuildConfig(Info* info,
+ bool is_default,
+ const ParseNode* root) {
+ // Danger: No early returns without decrementing the work count.
+ if (root && !g_scheduler->is_failed()) {
+ // Nobody should be accessing settings at this point other than us since we
+ // haven't marked it loaded, so we can do it outside the lock.
+ Scope* base_config = info->settings.base_config();
+ SetSystemVars(info->settings, base_config);
+ base_config->SetProcessingBuildConfig();
+ if (is_default)
+ base_config->SetProcessingDefaultBuildConfig();
+
+ const BlockNode* root_block = root->AsBlock();
+ Err err;
+ root_block->ExecuteBlockInScope(base_config, &err);
+
+ base_config->ClearProcessingBuildConfig();
+ if (is_default)
+ base_config->ClearProcessingDefaultBuildConfig();
+
+ if (err.has_error()) {
+ g_scheduler->FailWithError(err);
+ } else {
+ // Base config processing succeeded.
+ Info::ScheduledInvocationMap schedule_these;
+ {
+ base::AutoLock lock(GetLock());
+ schedule_these.swap(info->scheduled_invocations);
+ info->state = TOOLCHAIN_SETTINGS_LOADED;
+ if (is_default)
+ FixupDefaultToolchainLocked();
+ }
+
+ // Schedule build files waiting on this settings. There can be many so we
+ // want to load them in parallel on the pool.
+ for (Info::ScheduledInvocationMap::iterator i = schedule_these.begin();
+ i != schedule_these.end() && !g_scheduler->is_failed(); ++i) {
+ // Note i->second may be NULL, so don't dereference.
+ g_scheduler->IncrementWorkCount();
+ if (!g_scheduler->input_file_manager()->AsyncLoadFile(
+ i->second, build_settings_, i->first,
+ base::Bind(&ToolchainManager::BackgroundInvoke,
+ base::Unretained(this), info, i->first),
+ &err)) {
+ g_scheduler->FailWithError(err);
+ g_scheduler->DecrementWorkCount();
+ break;
+ }
+ }
+ }
+ }
+ g_scheduler->DecrementWorkCount();
+}
+
+void ToolchainManager::BackgroundInvoke(const Info* info,
+ const SourceFile& file_name,
+ const ParseNode* root) {
+ if (root && !g_scheduler->is_failed()) {
+ if (g_scheduler->verbose_logging())
+ g_scheduler->Log("Running", file_name.value());
+
+ Scope our_scope(info->settings.base_config());
+ ScopePerFileProvider per_file_provider(&our_scope, file_name);
+
+ Err err;
+ root->Execute(&our_scope, &err);
+ if (err.has_error())
+ g_scheduler->FailWithError(err);
+ }
+
+ g_scheduler->DecrementWorkCount();
+}
+
+base::Lock& ToolchainManager::GetLock() const {
+ return build_settings_->item_tree().lock();
+}
diff --git a/tools/gn/toolchain_manager.h b/tools/gn/toolchain_manager.h
new file mode 100644
index 0000000..4c0be41
--- /dev/null
+++ b/tools/gn/toolchain_manager.h
@@ -0,0 +1,167 @@
+// 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.
+
+#ifndef TOOLS_GN_TOOLCHAIN_MANAGER_H_
+#define TOOLS_GN_TOOLCHAIN_MANAGER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/synchronization/lock.h"
+#include "tools/gn/label.h"
+#include "tools/gn/location.h"
+#include "tools/gn/source_file.h"
+#include "tools/gn/toolchain.h"
+
+class Err;
+class BuildSettings;
+class ParseNode;
+class Settings;
+
+// The toolchain manager manages the mapping of toolchain names to the
+// settings and toolchain object. It also loads build files in the context of a
+// toolchain context of a toolchain, and manages running the build config
+// script when necessary.
+//
+// This class uses the lock from the item tree to manage threadsafety. The
+// functions requiring this lock to be held are named "Locked" to make this
+// more clear. The "Unlocked" versions will acquire the lock themselves so will
+// break if you call it while locked. (The rationale behind which is which is
+// just based on the needs of the callers, so it can be changed.) There are two
+// reasons for this:
+//
+// The first is that when resolving a target, we do a bunch of script
+// stuff (slow) and then lookup the target, config, and toolchain dependencies
+// based on that. The options are to do a lock around each dependency lookup
+// or do a lock around the entire operation. Given that there's not a huge
+// amount of work, the "big lock" approach is likely a bit better since it
+// avoids lots of locking overhead.
+//
+// The second reason is that if we had a separate lock here, we would need to
+// lock around creating a new toolchain. But creating a new toolchain involves
+// adding it to the item tree, and this needs to be done atomically to prevent
+// other threads from seeing a partially initialized toolchain. This sets up
+// having deadlock do to acquiring multiple locks, or recursive locking
+// problems.
+class ToolchainManager {
+ public:
+ ToolchainManager(const BuildSettings* build_settings);
+ ~ToolchainManager();
+
+ // At the very beginning of processing, this begins loading build files.
+ // This will scheduler loadin the default build config and the given build
+ // file in that context, going out from there.
+ //
+ // This returns immediately, you need to run the Scheduler to actually
+ // process anything. It's assumed this function is called on the main thread
+ // before doing anything, so it does not need locking.
+ void StartLoadingUnlocked(const SourceFile& build_file_name);
+
+ // Returns the settings object for a given toolchain. This does not
+ // schedule loading the given toolchain if it's not loaded yet: you actually
+ // need to invoke a target with that toolchain to get that.
+ //
+ // On error, returns NULL and sets the error.
+ const Settings* GetSettingsForToolchainLocked(const LocationRange& from_here,
+ const Label& toolchain_name,
+ Err* err);
+
+ // Returns the toolchain definition or NULL if the toolchain hasn't been
+ // defined yet.
+ const Toolchain* GetToolchainDefinitionUnlocked(const Label& toolchain_name);
+
+ // Sets the default toolchain. If the default toolchain is already set,
+ // this function will return false and fill in the given err.
+ bool SetDefaultToolchainUnlocked(const Label& dt,
+ const LocationRange& defined_from,
+ Err* err);
+
+ // Returns the default toolchain name. This will be empty if it hasn't been
+ // set.
+ Label GetDefaultToolchainUnlocked() const;
+
+ // Saves the given named toolchain (the name will be taken from the toolchain
+ // parameter). This will fail and return false if the given toolchain was
+ // already defined. In this case, the given error will be set.
+ bool SetToolchainDefinitionLocked(const Toolchain& tc,
+ const LocationRange& defined_from,
+ Err* err);
+
+ // Schedules an invocation of the given file under the given toolchain. The
+ // toolchain file will be loaded if necessary.
+ //
+ // The origin should be the node that will be blamed for this invocation if
+ // an error occurs. If a synchronous error occurs, the given error will be
+ // set and it will return false. If an async error occurs, the error will be
+ // sent to the scheduler.
+ bool ScheduleInvocationLocked(const LocationRange& origin,
+ const Label& toolchain_name,
+ const SourceDir& dir,
+ Err* err);
+
+ private:
+ enum SettingsState {
+ // Toolchain settings have not requested to be loaded. This means we
+ // haven't seen any targets that require this toolchain yet. Not loading
+ // the settings automatically allows you to define a bunch of toolchains
+ // and potentially not use them without much overhead.
+ TOOLCHAIN_SETTINGS_NOT_LOADED,
+
+ // The settings have been scheduled to be loaded but have not completed.
+ TOOLCHAIN_SETTINGS_LOADING,
+
+ // The settings are done being loaded.
+ TOOLCHAIN_SETTINGS_LOADED
+ };
+
+ struct Info;
+
+ static std::string ToolchainToOutputSubdir(const Label& toolchain_name);
+
+ // Creates a new info struct and saves it in the map. A pointer to the
+ // struct is returned. No loads are scheduled.
+ //
+ // If the label is non-empty, the toolchain will be added to the ItemTree
+ // so that other nodes can depend on it. THe empty label case is for the
+ // default build config file (when the toolchain name isn't known yet). It
+ // will be added later.
+ //
+ // On error, will return NULL and the error will be set.
+ Info* LoadNewToolchainLocked(const LocationRange& specified_from,
+ const Label& toolchain_name,
+ Err* err);
+
+ // Fixes up the default toolchain names once they're known when processing
+ // the default build config, or throw an error if the default toolchain
+ // hasn't been set. See the StartLoading() implementation for more.
+ void FixupDefaultToolchainLocked();
+
+ // Loads the base config for the given toolchain. Run on a background thread
+ // asynchronously.
+ void BackgroundLoadBuildConfig(Info* info,
+ bool is_default,
+ const ParseNode* root);
+
+ // Invokes the given file for a toolchain with loaded settings. Run on a
+ // background thread asynchronously.
+ void BackgroundInvoke(const Info* info,
+ const SourceFile& file_name,
+ const ParseNode* root);
+
+ // Returns the lock to use.
+ base::Lock& GetLock() const;
+
+ const BuildSettings* build_settings_;
+
+ // We own the info pointers.
+ typedef std::map<Label, Info*> ToolchainMap;
+ ToolchainMap toolchains_;
+
+ Label default_toolchain_;
+ LocationRange default_toolchain_defined_here_;
+
+ DISALLOW_COPY_AND_ASSIGN(ToolchainManager);
+};
+
+#endif // TOOLS_GN_TOOLCHAIN_MANAGER_H_
diff --git a/tools/gn/value.cc b/tools/gn/value.cc
new file mode 100644
index 0000000..cb78faa
--- /dev/null
+++ b/tools/gn/value.cc
@@ -0,0 +1,126 @@
+// 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/value.h"
+
+#include "base/strings/string_number_conversions.h"
+
+Value::Value()
+ : type_(NONE),
+ int_value_(0),
+ origin_(NULL) {
+}
+
+Value::Value(const ParseNode* origin, Type t)
+ : type_(t),
+ int_value_(0),
+ origin_(origin) {
+}
+
+Value::Value(const ParseNode* origin, int64 int_val)
+ : type_(INTEGER),
+ int_value_(int_val),
+ origin_(origin) {
+}
+
+Value::Value(const ParseNode* origin, const base::StringPiece& str_val)
+ : type_(STRING),
+ string_value_(str_val.as_string()),
+ int_value_(0),
+ origin_(origin) {
+}
+
+Value::~Value() {
+}
+
+// static
+const char* Value::DescribeType(Type t) {
+ switch (t) {
+ case NONE:
+ return "none";
+ case INTEGER:
+ return "integer";
+ case STRING:
+ return "string";
+ case LIST:
+ return "list";
+ default:
+ NOTREACHED();
+ return "UNKNOWN";
+ }
+}
+
+int64 Value::InterpretAsInt() const {
+ switch (type_) {
+ case NONE:
+ return 0;
+ case INTEGER:
+ return int_value_;
+ case STRING:
+ return string_value_.empty() ? 0 : 1;
+ case LIST:
+ return list_value_.empty() ? 0 : 1;
+ }
+ return 0;
+}
+
+std::string Value::ToString() const {
+ switch (type_) {
+ case NONE:
+ return "<void>";
+ case INTEGER:
+ return base::Int64ToString(int_value_);
+ case STRING:
+ return string_value_;
+ case LIST: {
+ std::string result = "[";
+ for (size_t i = 0; i < list_value_.size(); i++) {
+ if (i > 0)
+ result += ", ";
+ // TODO(brettw) maybe also want to escape quotes in the string.
+ if (list_value_[i].type() == STRING)
+ result += std::string("\"") + list_value_[i].ToString() + "\"";
+ else
+ result += list_value_[i].ToString();
+ }
+ result.push_back(']');
+ return result;
+ }
+ }
+ return std::string();
+}
+
+bool Value::VerifyTypeIs(Type t, Err* err) const {
+ if (type_ == t)
+ return true;
+
+ *err = Err(origin(), std::string("This is not a ") + DescribeType(t) + ".");
+ return false;
+}
+
+bool Value::operator==(const Value& other) const {
+ if (type_ != other.type_)
+ return false;
+
+ switch (type_) {
+ case Value::INTEGER:
+ return int_value() == other.int_value();
+ case Value::STRING:
+ return string_value() == other.string_value();
+ case Value::LIST:
+ if (list_value().size() != other.list_value().size())
+ return false;
+ for (size_t i = 0; i < list_value().size(); i++) {
+ if (list_value()[i] != other.list_value()[i])
+ return false;
+ }
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool Value::operator!=(const Value& other) const {
+ return !operator==(other);
+}
diff --git a/tools/gn/value.h b/tools/gn/value.h
new file mode 100644
index 0000000..8802bff
--- /dev/null
+++ b/tools/gn/value.h
@@ -0,0 +1,91 @@
+// 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.
+
+#ifndef TOOLS_GN_VALUE_H_
+#define TOOLS_GN_VALUE_H_
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "tools/gn/err.h"
+
+class ParseNode;
+
+// Represents a variable value in the interpreter.
+class Value {
+ public:
+ enum Type {
+ NONE = 0,
+ INTEGER,
+ STRING,
+ LIST
+ };
+
+ Value();
+ Value(const ParseNode* origin, Type t);
+ Value(const ParseNode* origin, int64 int_val);
+ Value(const ParseNode* origin, const base::StringPiece& str_val);
+ ~Value();
+
+ Type type() const { return type_; }
+
+ // Returns a string describing the given type.
+ static const char* DescribeType(Type t);
+
+ // Returns the node that made this. May be NULL.
+ const ParseNode* origin() const { return origin_; }
+ void set_origin(const ParseNode* o) { origin_ = o; }
+
+ int64& int_value() {
+ DCHECK(type_ == INTEGER);
+ return int_value_;
+ }
+ const int64& int_value() const {
+ DCHECK(type_ == INTEGER);
+ return int_value_;
+ }
+
+ std::string& string_value() {
+ DCHECK(type_ == STRING);
+ return string_value_;
+ }
+ const std::string& string_value() const {
+ DCHECK(type_ == STRING);
+ return string_value_;
+ }
+
+ std::vector<Value>& list_value() {
+ DCHECK(type_ == LIST);
+ return list_value_;
+ }
+ const std::vector<Value>& list_value() const {
+ DCHECK(type_ == LIST);
+ return list_value_;
+ }
+
+ // Returns the current value converted to an int, normally used for
+ // boolean operations. Undefined variables, empty lists, and empty strings
+ // are all interpreted as 0, otherwise 1.
+ int64 InterpretAsInt() const;
+
+ // Converts the given value to a string.
+ std::string ToString() const;
+
+ // Verifies that the value is of the given type. If it isn't, returns
+ // false and sets the error.
+ bool VerifyTypeIs(Type t, Err* err) const;
+
+ // Compares values. Only the "value" is compared, not the origin.
+ bool operator==(const Value& other) const;
+ bool operator!=(const Value& other) const;
+
+ private:
+ Type type_;
+ std::string string_value_;
+ int64 int_value_;
+ std::vector<Value> list_value_;
+ const ParseNode* origin_;
+};
+
+#endif // TOOLS_GN_VALUE_H_
diff --git a/tools/gn/value_extractors.cc b/tools/gn/value_extractors.cc
new file mode 100644
index 0000000..56a2be2
--- /dev/null
+++ b/tools/gn/value_extractors.cc
@@ -0,0 +1,102 @@
+// 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/value_extractors.h"
+
+#include "tools/gn/err.h"
+#include "tools/gn/label.h"
+#include "tools/gn/source_dir.h"
+#include "tools/gn/source_file.h"
+
+namespace {
+
+// This extractor rejects files with system-absolute file paths. If we need
+// that in the future, we'll have to add some flag to control this.
+struct RelativeFileConverter {
+ RelativeFileConverter(const SourceDir& current_dir_in)
+ : current_dir(current_dir_in) {}
+ bool operator()(const Value& v, SourceFile* out, Err* err) const {
+ if (!v.VerifyTypeIs(Value::STRING, err))
+ return false;
+ *out = current_dir.ResolveRelativeFile(v.string_value());
+ if (out->is_system_absolute()) {
+ *err = Err(v, "System-absolute file path.",
+ "You can't list a system-absolute file path here. Please include "
+ "only files in\nthe source tree. Maybe you meant to begin with two "
+ "slashes to indicate an\nabsolute path in the source tree?");
+ return false;
+ }
+ return true;
+ }
+ const SourceDir& current_dir;
+};
+
+struct RelativeDirConverter {
+ RelativeDirConverter(const SourceDir& current_dir_in)
+ : current_dir(current_dir_in) {}
+ bool operator()(const Value& v, SourceDir* out, Err* err) const {
+ if (!v.VerifyTypeIs(Value::STRING, err))
+ return false;
+ *out = current_dir.ResolveRelativeDir(v.string_value());
+ return true;
+ }
+ const SourceDir& current_dir;
+};
+
+struct LabelResolver {
+ LabelResolver(const SourceDir& current_dir_in,
+ const Label& current_toolchain_in)
+ : current_dir(current_dir_in),
+ current_toolchain(current_toolchain_in) {}
+ bool operator()(const Value& v, Label* out, Err* err) const {
+ if (!v.VerifyTypeIs(Value::STRING, err))
+ return false;
+ *out = Label::Resolve(current_dir, current_toolchain, v, err);
+ return !err->has_error();
+ }
+ const SourceDir& current_dir;
+ const Label& current_toolchain;
+};
+
+} // namespace
+
+bool ExtractListOfStringValues(const Value& value,
+ std::vector<std::string>* dest,
+ Err* err) {
+ if (!value.VerifyTypeIs(Value::LIST, err))
+ return false;
+ const std::vector<Value>& input_list = value.list_value();
+ dest->reserve(input_list.size());
+ for (size_t i = 0; i < input_list.size(); i++) {
+ if (!input_list[i].VerifyTypeIs(Value::STRING, err))
+ return false;
+ dest->push_back(input_list[i].string_value());
+ }
+ return true;
+}
+
+bool ExtractListOfRelativeFiles(const Value& value,
+ const SourceDir& current_dir,
+ std::vector<SourceFile>* files,
+ Err* err) {
+ return ListValueExtractor(value, files, err,
+ RelativeFileConverter(current_dir));
+}
+
+bool ExtractListOfRelativeDirs(const Value& value,
+ const SourceDir& current_dir,
+ std::vector<SourceDir>* dest,
+ Err* err) {
+ return ListValueExtractor(value, dest, err,
+ RelativeDirConverter(current_dir));
+}
+
+bool ExtractListOfLabels(const Value& value,
+ const SourceDir& current_dir,
+ const Label& current_toolchain,
+ std::vector<Label>* dest,
+ Err* err) {
+ return ListValueExtractor(value, dest, err,
+ LabelResolver(current_dir, current_toolchain));
+}
diff --git a/tools/gn/value_extractors.h b/tools/gn/value_extractors.h
new file mode 100644
index 0000000..556f44f
--- /dev/null
+++ b/tools/gn/value_extractors.h
@@ -0,0 +1,57 @@
+// 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.
+
+#ifndef TOOLS_GN_VALUE_EXTRACTORS_H_
+#define TOOLS_GN_VALUE_EXTRACTORS_H_
+
+#include <string>
+#include <vector>
+
+#include "tools/gn/value.h"
+
+class Err;
+class Label;
+class SourceDir;
+class SourceFile;
+
+// Sets the error and returns false on failure.
+template<typename T, class Converter>
+bool ListValueExtractor(const Value& value, std::vector<T>* dest,
+ Err* err,
+ const Converter& converter) {
+ if (!value.VerifyTypeIs(Value::LIST, err))
+ return false;
+ const std::vector<Value>& input_list = value.list_value();
+ dest->resize(input_list.size());
+ for (size_t i = 0; i < input_list.size(); i++) {
+ if (!converter(input_list[i], &(*dest)[i], err))
+ return false;
+ }
+ return true;
+}
+
+// On failure, returns false and sets the error.
+bool ExtractListOfStringValues(const Value& value,
+ std::vector<std::string>* dest,
+ Err* err);
+
+// Looks for a list of source files relative to a given current dir.
+bool ExtractListOfRelativeFiles(const Value& value,
+ const SourceDir& current_dir,
+ std::vector<SourceFile>* files,
+ Err* err);
+
+// Looks for a list of source directories relative to a given current dir.
+bool ExtractListOfRelativeDirs(const Value& value,
+ const SourceDir& current_dir,
+ std::vector<SourceDir>* dest,
+ Err* err);
+
+bool ExtractListOfLabels(const Value& value,
+ const SourceDir& current_dir,
+ const Label& current_toolchain,
+ std::vector<Label>* dest,
+ Err* err);
+
+#endif // TOOLS_GN_VALUE_EXTRACTORS_H_