0

Revert 214325 "Revert 214254 "Add initial prototype for the GN m..."

The issue was already fixed :)

> Revert 214254 "Add initial prototype for the GN meta-buildsystem."
>
> It broke the check_licenses step on Android (see http://build.chromium.org/p/chromium.linux/builders/Android%20Builder%20%28dbg%29/builds/39904/steps/check_licenses/logs/stdio):
>
> @@@BUILD_STEP check_licenses@@@
> > /b/build/slave/Android_Builder__dbg_/build/src/android_webview/tools/webview_licenses.py scan
> Got LicenseError "missing README.chromium or licenses.py SPECIAL_CASES entry" while scanning tools/gn/secondary/base/third_party/dynamic_annotations
> Got LicenseError "missing README.chromium or licenses.py SPECIAL_CASES entry" while scanning tools/gn/secondary/third_party/modp_b64
> < /b/build/slave/Android_Builder__dbg_/build/src/android_webview/tools/webview_licenses.py scan
> ERROR: process exited with code 2
> @@@STEP_FAILURE@@@
>
>
> > 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
>
> TBR=brettw@chromium.org
>
> Review URL: https://codereview.chromium.org/21084010

TBR=bauerb@chromium.org

Review URL: https://codereview.chromium.org/21204003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@214333 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
bauerb@chromium.org
2013-07-30 10:17:07 +00:00
parent fb68e6c9f2
commit 96ea63d0d1
140 changed files with 18197 additions and 0 deletions
.gn
tools/gn
BUILD.gnOWNERSREADME.txtbuild_settings.ccbuild_settings.hcommand_desc.cccommand_gen.cccommands.hconfig.ccconfig.hconfig_values.ccconfig_values.hconfig_values_extractors.ccconfig_values_extractors.hconfig_values_generator.ccconfig_values_generator.herr.ccerr.hescape.ccescape.hescape_unittest.ccfile_template.ccfile_template.hfile_template_unittest.ccfilesystem_utils.ccfilesystem_utils.hfilesystem_utils_unittest.ccfunction_define_rule.ccfunction_exec_script.ccfunction_process_file_template.ccfunction_read_file.ccfunction_set_default_toolchain.ccfunction_template.ccfunction_toolchain.ccfunction_write_file.ccfunctions.ccfunctions.hfunctions_target.ccgenerate_test_gn_data.ccgn.gypgn_main.ccimport_manager.ccimport_manager.hinput_conversion.ccinput_conversion.hinput_conversion_unittest.ccinput_file.ccinput_file.hinput_file_manager.ccinput_file_manager.hitem.ccitem.hitem_node.ccitem_node.hitem_tree.ccitem_tree.hlabel.cclabel.hlabel_unittest.cclocation.hninja_build_writer.ccninja_build_writer.hninja_helper.ccninja_helper.hninja_helper_unittest.ccninja_target_writer.ccninja_target_writer.hninja_toolchain_writer.ccninja_toolchain_writer.hninja_writer.ccninja_writer.hoperators.ccoperators.houtput_file.houtput_stream.hparse_tree.ccparse_tree.hparser.ccparser.hparser_unittest.ccpath_output.ccpath_output.hpath_output_unittest.ccpattern.ccpattern.hpattern_unittest.ccscheduler.ccscheduler.hscope.ccscope.hscope_per_file_provider.ccscope_per_file_provider.h
secondary
base
BUILD.gn
allocator
third_party
dynamic_annotations
build
ipc
testing
third_party
modp_b64
settings.ccsettings.hsetup.ccsetup.hsource_dir.ccsource_dir.hsource_dir_unittest.ccsource_file.ccsource_file.hstandard_out.ccstandard_out.hstring_utils.ccstring_utils.hstring_utils_unittest.cctarget.cctarget.htarget_generator.cctarget_generator.htarget_generator_unittest.cctarget_manager.cctarget_manager.htarget_manager_unittest.cctoken.cctoken.htokenizer.cctokenizer.htokenizer_unittest.cctoolchain.cctoolchain.htoolchain_manager.cctoolchain_manager.hvalue.ccvalue.hvalue_extractors.ccvalue_extractors.h

8
.gn Normal file

@ -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/"

153
tools/gn/BUILD.gn Normal file

@ -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" ]
}

1
tools/gn/OWNERS Normal file

@ -0,0 +1 @@
brettw@chromium.org

7
tools/gn/README.txt Normal file

@ -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.

@ -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_);
}

110
tools/gn/build_settings.h Normal file

@ -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_

201
tools/gn/command_desc.cc Normal file

@ -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;
}

66
tools/gn/command_gen.cc Normal file

@ -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;
}

18
tools/gn/commands.h Normal file

@ -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_

78
tools/gn/config.cc Normal file

@ -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;
}

45
tools/gn/config.h Normal file

@ -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_

11
tools/gn/config_values.cc Normal file

@ -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() {
}

50
tools/gn/config_values.h Normal file

@ -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_

@ -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);
}

@ -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_

@ -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_);
}

@ -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_

196
tools/gn/err.cc Normal file

@ -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);
}

85
tools/gn/err.h Normal file

@ -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_

77
tools/gn/escape.cc Normal file

@ -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());
}

53
tools/gn/escape.h Normal file

@ -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_

@ -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.

125
tools/gn/file_template.cc Normal file

@ -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;
}
}
}

74
tools/gn/file_template.h Normal file

@ -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_

@ -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]);
}

@ -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;
}

115
tools/gn/filesystem_utils.h Normal file

@ -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_

@ -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);
}

@ -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();
}

@ -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);
}

@ -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;
}

@ -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);
}

@ -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();
}

@ -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();
}

@ -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();
}

@ -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();
}

443
tools/gn/functions.cc Normal file

@ -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();
}

182
tools/gn/functions.h Normal file

@ -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_

@ -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);
}

@ -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;
}

171
tools/gn/gn.gyp Normal file

@ -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',
],
}
],
}

59
tools/gn/gn_main.cc Normal file

@ -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;
}

@ -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);
}

41
tools/gn/import_manager.h Normal file

@ -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_

@ -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();
}

@ -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_

@ -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());
}

30
tools/gn/input_file.cc Normal file

@ -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;
}

52
tools/gn/input_file.h Normal file

@ -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_

@ -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;
}

@ -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_

31
tools/gn/item.cc Normal file

@ -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";
}

45
tools/gn/item.h Normal file

@ -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_

51
tools/gn/item_node.cc Normal file

@ -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();
}

119
tools/gn/item_node.h Normal file

@ -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_

193
tools/gn/item_tree.cc Normal file

@ -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;
}

69
tools/gn/item_tree.h Normal file

@ -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_

263
tools/gn/label.cc Normal file

@ -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);
}

116
tools/gn/label.h Normal file

@ -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_

@ -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;
}
}
}

77
tools/gn/location.h Normal file

@ -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_

@ -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;
}
}

@ -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_

165
tools/gn/ninja_helper.cc Normal file

@ -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;
}

71
tools/gn/ninja_helper.h Normal file

@ -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_

@ -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.
}

@ -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";
}

@ -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_

@ -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;
}

@ -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_

64
tools/gn/ninja_writer.cc Normal file

@ -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);
}

27
tools/gn/ninja_writer.h Normal file

@ -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_

573
tools/gn/operators.cc Normal file

@ -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();
}

35
tools/gn/operators.h Normal file

@ -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_

41
tools/gn/output_file.h Normal file

@ -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

42
tools/gn/output_stream.h Normal file

@ -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_

472
tools/gn/parse_tree.cc Normal file

@ -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);
}

366
tools/gn/parse_tree.h Normal file

@ -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_

470
tools/gn/parser.cc Normal file

@ -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);
}

81
tools/gn/parser.h Normal file

@ -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_

329
tools/gn/parser_unittest.cc Normal file

@ -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);
}

116
tools/gn/path_output.cc Normal file

@ -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
}
}

80
tools/gn/path_output.h Normal file

@ -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_

@ -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());
}
}
}

185
tools/gn/pattern.cc Normal file

@ -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;
}

86
tools/gn/pattern.h Normal file

@ -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_

@ -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 << "\"";
}
}

130
tools/gn/scheduler.cc Normal file

@ -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();
}

90
tools/gn/scheduler.h Normal file

@ -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_

372
tools/gn/scope.cc Normal file

@ -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);
}

260
tools/gn/scope.h Normal file

@ -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_

@ -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);
}

@ -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_

@ -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",
]
}

@ -0,0 +1,6 @@
static_library("allocator_extension_thunks") {
sources = [
"allocator_extension_thunks.cc",
"allocator_extension_thunks.h",
]
}

@ -0,0 +1,7 @@
static_library("dynamic_annotations") {
sources = [
"dynamic_annotations.c",
"dynamic_annotations.h",
"../valgrind/valgrind.h",
]
}

@ -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") {
}

@ -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")
}

@ -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.
]
}
}

@ -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",
]
}

Some files were not shown because too many files have changed in this diff Show More