summaryrefslogtreecommitdiffstats
path: root/tools/gn
diff options
context:
space:
mode:
authorbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-22 22:09:55 +0000
committerbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-22 22:09:55 +0000
commit05b94b4340501afbc32619c5b10a1d7c533670cd (patch)
tree1e6d8dc92f528514b445f950f35d3a0d833cfbee /tools/gn
parentbd71e95c4cd64660daf4e00be2a66cd96a022a2c (diff)
downloadchromium_src-05b94b4340501afbc32619c5b10a1d7c533670cd.zip
chromium_src-05b94b4340501afbc32619c5b10a1d7c533670cd.tar.gz
chromium_src-05b94b4340501afbc32619c5b10a1d7c533670cd.tar.bz2
This writes a generator for GYP actions to run Ninja for script targets. This does not yet include the code that actually writes the corresponding ninja files.
The GYP code also needed some output directory generation code, so this was consolidated in filesystem_utils and referenced from the existing place. BUG=322244 R=scottmg@chromium.org Review URL: https://codereview.chromium.org/80463004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@236837 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/gn')
-rw-r--r--tools/gn/BUILD.gn3
-rw-r--r--tools/gn/file_template.cc11
-rw-r--r--tools/gn/file_template.h5
-rw-r--r--tools/gn/filesystem_utils.cc85
-rw-r--r--tools/gn/filesystem_utils.h21
-rw-r--r--tools/gn/filesystem_utils_unittest.cc82
-rw-r--r--tools/gn/functions_target.cc2
-rw-r--r--tools/gn/gn.gyp3
-rw-r--r--tools/gn/gyp_binary_target_writer.cc83
-rw-r--r--tools/gn/gyp_binary_target_writer.h14
-rw-r--r--tools/gn/gyp_script_target_writer.cc108
-rw-r--r--tools/gn/gyp_script_target_writer.h29
-rw-r--r--tools/gn/gyp_script_target_writer_unittest.cc57
-rw-r--r--tools/gn/gyp_target_writer.cc32
-rw-r--r--tools/gn/gyp_target_writer.h13
-rw-r--r--tools/gn/ninja_target_writer.h5
-rw-r--r--tools/gn/scope_per_file_provider.cc68
-rw-r--r--tools/gn/scope_per_file_provider.h8
-rw-r--r--tools/gn/source_dir.cc16
-rw-r--r--tools/gn/source_dir.h7
20 files changed, 514 insertions, 138 deletions
diff --git a/tools/gn/BUILD.gn b/tools/gn/BUILD.gn
index 567f1e9..7e93a84 100644
--- a/tools/gn/BUILD.gn
+++ b/tools/gn/BUILD.gn
@@ -69,6 +69,8 @@ static_library("gn_lib") {
"gyp_binary_target_writer.h",
"gyp_helper.cc",
"gyp_helper.h",
+ "gyp_script_target_writer.cc",
+ "gyp_script_target_writer.h",
"gyp_target_writer.cc",
"gyp_target_writer.h",
"import_manager.cc",
@@ -186,6 +188,7 @@ test("gn_unittests") {
"file_template_unittest.cc",
"filesystem_utils_unittest.cc",
"function_rebase_path_unittest.cc",
+ "gyp_script_target_writer_unittest.cc",
"input_conversion_unittest.cc",
"label_unittest.cc",
"loader_unittest.cc",
diff --git a/tools/gn/file_template.cc b/tools/gn/file_template.cc
index 1616c52..995bb016 100644
--- a/tools/gn/file_template.cc
+++ b/tools/gn/file_template.cc
@@ -9,6 +9,8 @@
#include "tools/gn/escape.h"
#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/string_utils.h"
+#include "tools/gn/target.h"
const char FileTemplate::kSource[] = "{{source}}";
const char FileTemplate::kSourceNamePart[] = "{{source_name_part}}";
@@ -92,6 +94,15 @@ FileTemplate::FileTemplate(const std::vector<std::string>& t)
FileTemplate::~FileTemplate() {
}
+// static
+FileTemplate FileTemplate::GetForTargetOutputs(const Target* target) {
+ const Target::FileList& outputs = target->script_values().outputs();
+ std::vector<std::string> output_template_args;
+ for (size_t i = 0; i < outputs.size(); i++)
+ output_template_args.push_back(outputs[i].value());
+ return FileTemplate(output_template_args);
+}
+
bool FileTemplate::IsTypeUsed(Subrange::Type type) const {
DCHECK(type > Subrange::LITERAL && type < Subrange::NUM_TYPES);
return types_required_[type];
diff --git a/tools/gn/file_template.h b/tools/gn/file_template.h
index 8fddad2..d66b3e3 100644
--- a/tools/gn/file_template.h
+++ b/tools/gn/file_template.h
@@ -14,6 +14,7 @@
struct EscapeOptions;
class ParseNode;
+class Target;
extern const char kSourceExpansion_Help[];
@@ -62,6 +63,10 @@ class FileTemplate {
FileTemplate(const std::vector<std::string>& t);
~FileTemplate();
+ // Returns an output template representing the given target's script
+ // outputs.
+ static FileTemplate GetForTargetOutputs(const Target* target);
+
// Returns true if the given substitution type is used by this template.
bool IsTypeUsed(Subrange::Type type) const;
diff --git a/tools/gn/filesystem_utils.cc b/tools/gn/filesystem_utils.cc
index ee7c5d5..5d9b5f1 100644
--- a/tools/gn/filesystem_utils.cc
+++ b/tools/gn/filesystem_utils.cc
@@ -11,6 +11,7 @@
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "tools/gn/location.h"
+#include "tools/gn/settings.h"
#include "tools/gn/source_dir.h"
namespace {
@@ -546,3 +547,87 @@ std::string RebaseSourceAbsolutePath(const std::string& input,
return ret;
}
+
+std::string DirectoryWithNoLastSlash(const SourceDir& dir) {
+ std::string ret;
+
+ if (dir.value().empty()) {
+ // Just keep input the same.
+ } else if (dir.value() == "/") {
+ ret.assign("/.");
+ } else if (dir.value() == "//") {
+ ret.assign("//.");
+ } else {
+ ret.assign(dir.value());
+ ret.resize(ret.size() - 1);
+ }
+ return ret;
+}
+
+SourceDir GetToolchainOutputDir(const Settings* settings) {
+ const OutputFile& toolchain_subdir = settings->toolchain_output_subdir();
+
+ std::string result = settings->build_settings()->build_dir().value();
+ if (!toolchain_subdir.value().empty())
+ result.append(toolchain_subdir.value());
+
+ return SourceDir(SourceDir::SWAP_IN, &result);
+}
+
+SourceDir GetToolchainGenDir(const Settings* settings) {
+ const OutputFile& toolchain_subdir = settings->toolchain_output_subdir();
+
+ std::string result = settings->build_settings()->build_dir().value();
+ if (!toolchain_subdir.value().empty())
+ result.append(toolchain_subdir.value());
+
+ result.append("gen/");
+ return SourceDir(SourceDir::SWAP_IN, &result);
+}
+
+SourceDir GetOutputDirForSourceDir(const Settings* settings,
+ const SourceDir& source_dir) {
+ SourceDir toolchain = GetToolchainOutputDir(settings);
+
+ std::string ret;
+ toolchain.SwapValue(&ret);
+ ret.append("obj/");
+
+ // The source dir should be source-absolute, so we trim off the two leading
+ // slashes to append to the toolchain object directory.
+ DCHECK(source_dir.is_source_absolute());
+ ret.append(&source_dir.value()[2], source_dir.value().size() - 2);
+
+ return SourceDir(SourceDir::SWAP_IN, &ret);
+}
+
+SourceDir GetGenDirForSourceDir(const Settings* settings,
+ const SourceDir& source_dir) {
+ SourceDir toolchain = GetToolchainGenDir(settings);
+
+ std::string ret;
+ toolchain.SwapValue(&ret);
+
+ // The source dir should be source-absolute, so we trim off the two leading
+ // slashes to append to the toolchain object directory.
+ DCHECK(source_dir.is_source_absolute());
+ ret.append(&source_dir.value()[2], source_dir.value().size() - 2);
+
+ return SourceDir(SourceDir::SWAP_IN, &ret);
+}
+
+SourceDir GetTargetOutputDir(const Target* target) {
+ return GetOutputDirForSourceDir(target->settings(), target->label().dir());
+}
+
+SourceDir GetTargetGenDir(const Target* target) {
+ return GetGenDirForSourceDir(target->settings(), target->label().dir());
+}
+
+SourceDir GetCurrentOutputDir(const Scope* scope) {
+ return GetOutputDirForSourceDir(scope->settings(), scope->GetSourceDir());
+}
+
+SourceDir GetCurrentGenDir(const Scope* scope) {
+ return GetGenDirForSourceDir(scope->settings(), scope->GetSourceDir());
+}
diff --git a/tools/gn/filesystem_utils.h b/tools/gn/filesystem_utils.h
index 9899cae..152851d 100644
--- a/tools/gn/filesystem_utils.h
+++ b/tools/gn/filesystem_utils.h
@@ -138,4 +138,25 @@ std::string PathToSystem(const std::string& path);
std::string RebaseSourceAbsolutePath(const std::string& input,
const SourceDir& dest_dir);
+// Returns the given directory with no terminating slash at the end, such that
+// appending a slash and more stuff will produce a valid path.
+//
+// If the directory refers to either the source or system root, we'll append
+// a "." so this remains valid.
+std::string DirectoryWithNoLastSlash(const SourceDir& dir);
+
+// -----------------------------------------------------------------------------
+
+// These functions return the various flavors of output and gen directories.
+SourceDir GetToolchainOutputDir(const Settings* settings);
+SourceDir GetToolchainGenDir(const Settings* settings);
+SourceDir GetOutputDirForSourceDir(const Settings* settings,
+ const SourceDir& source_dir);
+SourceDir GetGenDirForSourceDir(const Settings* settings,
+ const SourceDir& source_dir);
+SourceDir GetTargetOutputDir(const Target* target);
+SourceDir GetTargetGenDir(const Target* target);
+SourceDir GetCurrentOutputDir(const Scope* scope);
+SourceDir GetCurrentGenDir(const Scope* scope);
+
#endif // TOOLS_GN_FILESYSTEM_UTILS_H_
diff --git a/tools/gn/filesystem_utils_unittest.cc b/tools/gn/filesystem_utils_unittest.cc
index aeb09a4..f62f482 100644
--- a/tools/gn/filesystem_utils_unittest.cc
+++ b/tools/gn/filesystem_utils_unittest.cc
@@ -245,3 +245,85 @@ TEST(FilesystemUtils, RebaseSourceAbsolutePath) {
EXPECT_EQ("../bar",
RebaseSourceAbsolutePath("//foo/bar", SourceDir("//foo/bar/")));
}
+
+TEST(FilesystemUtils, DirectoryWithNoLastSlash) {
+ EXPECT_EQ("", DirectoryWithNoLastSlash(SourceDir()));
+ EXPECT_EQ("/.", DirectoryWithNoLastSlash(SourceDir("/")));
+ EXPECT_EQ("//.", DirectoryWithNoLastSlash(SourceDir("//")));
+ EXPECT_EQ("//foo", DirectoryWithNoLastSlash(SourceDir("//foo/")));
+ EXPECT_EQ("/bar", DirectoryWithNoLastSlash(SourceDir("/bar/")));
+}
+
+TEST(FilesystemUtils, GetToolchainDirs) {
+ BuildSettings build_settings;
+ build_settings.SetBuildDir(SourceDir("//out/Debug/"));
+
+ Settings default_settings(&build_settings, "");
+ EXPECT_EQ("//out/Debug/",
+ GetToolchainOutputDir(&default_settings).value());
+ EXPECT_EQ("//out/Debug/gen/",
+ GetToolchainGenDir(&default_settings).value());
+
+ Settings other_settings(&build_settings, "two");
+ EXPECT_EQ("//out/Debug/two/",
+ GetToolchainOutputDir(&other_settings).value());
+ EXPECT_EQ("//out/Debug/two/gen/",
+ GetToolchainGenDir(&other_settings).value());
+}
+
+TEST(FilesystemUtils, GetOutDirForSourceDir) {
+ BuildSettings build_settings;
+ build_settings.SetBuildDir(SourceDir("//out/Debug/"));
+
+ // Test the default toolchain.
+ Settings default_settings(&build_settings, "");
+ EXPECT_EQ("//out/Debug/obj/",
+ GetOutputDirForSourceDir(&default_settings,
+ SourceDir("//")).value());
+ EXPECT_EQ("//out/Debug/obj/foo/bar/",
+ GetOutputDirForSourceDir(&default_settings,
+ SourceDir("//foo/bar/")).value());
+
+ // Secondary toolchain.
+ Settings other_settings(&build_settings, "two");
+ EXPECT_EQ("//out/Debug/two/obj/",
+ GetOutputDirForSourceDir(&other_settings, SourceDir("//")).value());
+ EXPECT_EQ("//out/Debug/two/obj/foo/bar/",
+ GetOutputDirForSourceDir(&other_settings,
+ SourceDir("//foo/bar/")).value());
+}
+
+TEST(FilesystemUtils, GetGenDirForSourceDir) {
+ BuildSettings build_settings;
+ build_settings.SetBuildDir(SourceDir("//out/Debug/"));
+
+ // Test the default toolchain.
+ Settings default_settings(&build_settings, "");
+ EXPECT_EQ("//out/Debug/gen/",
+ GetGenDirForSourceDir(&default_settings, SourceDir("//")).value());
+ EXPECT_EQ("//out/Debug/gen/foo/bar/",
+ GetGenDirForSourceDir(&default_settings,
+ SourceDir("//foo/bar/")).value());
+
+ // Secondary toolchain.
+ Settings other_settings(&build_settings, "two");
+ EXPECT_EQ("//out/Debug/two/gen/",
+ GetGenDirForSourceDir(&other_settings, SourceDir("//")).value());
+ EXPECT_EQ("//out/Debug/two/gen/foo/bar/",
+ GetGenDirForSourceDir(&other_settings,
+ SourceDir("//foo/bar/")).value());
+}
+
+// Tests handling of output dirs when build dir is the same as the root.
+TEST(FilesystemUtils, GetDirForEmptyBuildDir) {
+ BuildSettings build_settings;
+ build_settings.SetBuildDir(SourceDir("//"));
+ Settings settings(&build_settings, "");
+
+ EXPECT_EQ("//", GetToolchainOutputDir(&settings).value());
+ EXPECT_EQ("//gen/", GetToolchainGenDir(&settings).value());
+ EXPECT_EQ("//obj/",
+ GetOutputDirForSourceDir(&settings, SourceDir("//")).value());
+ EXPECT_EQ("//gen/",
+ GetGenDirForSourceDir(&settings, SourceDir("//")).value());
+}
diff --git a/tools/gn/functions_target.cc b/tools/gn/functions_target.cc
index ceb0fbb..0e6d560 100644
--- a/tools/gn/functions_target.cc
+++ b/tools/gn/functions_target.cc
@@ -177,7 +177,7 @@ const char kCustom_Help[] =
" files and generate a set of output files.\n"
"\n"
" The script will be executed with the given arguments with the current\n"
- " directory being that of the root build directory. If you pass\n"
+ " directory being that of the root build directory. If you pass files\n"
" to your script, see \"gn help to_build_path\" for how to convert\n"
" file names to be relative to the build directory (file names in the\n"
" sources, outputs, and source_prereqs will be all treated as relative\n"
diff --git a/tools/gn/gn.gyp b/tools/gn/gn.gyp
index 3a04387..30d22a9 100644
--- a/tools/gn/gn.gyp
+++ b/tools/gn/gn.gyp
@@ -64,6 +64,8 @@
'gyp_binary_target_writer.h',
'gyp_helper.cc',
'gyp_helper.h',
+ 'gyp_script_target_writer.cc',
+ 'gyp_script_target_writer.h',
'gyp_target_writer.cc',
'gyp_target_writer.h',
'import_manager.cc',
@@ -175,6 +177,7 @@
'file_template_unittest.cc',
'filesystem_utils_unittest.cc',
'function_rebase_path_unittest.cc',
+ 'gyp_script_target_writer_unittest.cc',
'input_conversion_unittest.cc',
'label_unittest.cc',
'loader_unittest.cc',
diff --git a/tools/gn/gyp_binary_target_writer.cc b/tools/gn/gyp_binary_target_writer.cc
index 17d3a5e..ca822a2 100644
--- a/tools/gn/gyp_binary_target_writer.cc
+++ b/tools/gn/gyp_binary_target_writer.cc
@@ -30,17 +30,6 @@ struct Accumulator {
std::vector<T>* result;
};
-// Standalone version of GypBinaryTargetWriter::Indent for use by standalone
-// functions.
-std::ostream& Indent(std::ostream& out, int spaces) {
- const char kSpaces[81] =
- " "
- " ";
- CHECK(static_cast<size_t>(spaces) <= arraysize(kSpaces) - 1);
- out.write(kSpaces, spaces);
- return out;
-}
-
// Writes the given array values. The array should already be declared with the
// opening "[" written to the output. The function will not write the
// terminating "]" either.
@@ -55,24 +44,6 @@ void WriteArrayValues(std::ostream& out,
}
}
-// Writes the given array with the given name. The indent should be the
-// indenting for the name, the values will be indented 2 spaces from there.
-// Writes nothing if there is nothing in the array.
-void WriteNamedArray(std::ostream& out,
- const char* name,
- const std::vector<std::string>& values,
- int indent) {
- if (values.empty())
- return;
-
- EscapeOptions options;
- options.mode = ESCAPE_JSON;
-
- Indent(out, indent) << "'" << name << "': [";
- WriteArrayValues(out, values);
- out << " ],\n";
-}
-
// Returns the value from the already-filled in cflags_* for the optimization
// level to set in the GYP file. Additionally, this removes the flag from the
// given vector so we don't get duplicates.
@@ -110,16 +81,15 @@ void FillConfigListValues(
}
}
-const int kExtraIndent = 2;
-
} // namespace
GypBinaryTargetWriter::Flags::Flags() {}
GypBinaryTargetWriter::Flags::~Flags() {}
GypBinaryTargetWriter::GypBinaryTargetWriter(const TargetGroup& group,
+ const SourceDir& gyp_dir,
std::ostream& out)
- : GypTargetWriter(group.debug->item()->AsTarget(), out),
+ : GypTargetWriter(group.debug->item()->AsTarget(), gyp_dir, out),
group_(group) {
}
@@ -146,10 +116,6 @@ void GypBinaryTargetWriter::Run() {
Indent(indent) << "},\n";
}
-std::ostream& GypBinaryTargetWriter::Indent(int spaces) {
- return ::Indent(out_, spaces);
-}
-
void GypBinaryTargetWriter::WriteName(int indent) {
std::string name = helper_.GetNameForTarget(target_);
Indent(indent) << "'target_name': '" << name << "',\n";
@@ -281,7 +247,7 @@ void GypBinaryTargetWriter::WriteMacConfiguration(int indent) {
void GypBinaryTargetWriter::WriteVCFlags(Flags& flags, int indent) {
// Defines and includes go outside of the msvs settings.
- WriteNamedArray(out_, "defines", flags.defines, indent);
+ WriteNamedArray("defines", flags.defines, indent);
WriteIncludeDirs(flags, indent);
// C flags.
@@ -292,8 +258,7 @@ void GypBinaryTargetWriter::WriteVCFlags(Flags& flags, int indent) {
// This can produce duplicate values. So look up the GYP value corresponding
// to the flags used, and set the same one.
std::string optimization = GetVCOptimization(&flags.cflags);
- WriteNamedArray(out_, "AdditionalOptions", flags.cflags,
- indent + kExtraIndent * 2);
+ WriteNamedArray("AdditionalOptions", flags.cflags, indent + kExtraIndent * 2);
// TODO(brettw) cflags_c and cflags_cc!
Indent(indent + kExtraIndent * 2) << "'Optimization': "
<< optimization << ",\n";
@@ -318,19 +283,19 @@ void GypBinaryTargetWriter::WriteVCFlags(Flags& flags, int indent) {
}
// ...Libraries.
- WriteNamedArray(out_, "AdditionalDependencies", flags.libs,
+ WriteNamedArray("AdditionalDependencies", flags.libs,
indent + kExtraIndent * 2);
// ...LD flags.
// TODO(brettw) EnableUAC defaults to on and needs to be set. Also
// UACExecutionLevel and UACUIAccess depends on that and defaults to 0/false.
- WriteNamedArray(out_, "AdditionalOptions", flags.ldflags, 14);
+ WriteNamedArray("AdditionalOptions", flags.ldflags, 14);
Indent(indent + kExtraIndent) << "},\n";
Indent(indent) << "},\n";
}
void GypBinaryTargetWriter::WriteMacFlags(Flags& flags, int indent) {
- WriteNamedArray(out_, "defines", flags.defines, indent);
+ WriteNamedArray("defines", flags.defines, indent);
WriteIncludeDirs(flags, indent);
// Libraries and library directories.
@@ -382,10 +347,8 @@ void GypBinaryTargetWriter::WriteMacFlags(Flags& flags, int indent) {
// Ld flags. Don't write these for static libraries. Otherwise, they'll be
// passed to the library tool which doesn't expect it (the toolchain does
// not use ldflags so these are ignored in the normal build).
- if (target_->output_type() != Target::STATIC_LIBRARY) {
- WriteNamedArray(out_, "OTHER_LDFLAGS", flags.ldflags,
- indent + kExtraIndent);
- }
+ if (target_->output_type() != Target::STATIC_LIBRARY)
+ WriteNamedArray("OTHER_LDFLAGS", flags.ldflags, indent + kExtraIndent);
base::FilePath clang_path =
target_->settings()->build_settings()->GetFullPath(SourceFile(
@@ -409,12 +372,12 @@ void GypBinaryTargetWriter::WriteLinuxFlagsForTarget(const Target* target,
void GypBinaryTargetWriter::WriteLinuxFlags(const Flags& flags, int indent) {
WriteIncludeDirs(flags, indent);
- WriteNamedArray(out_, "defines", flags.defines, indent);
- WriteNamedArray(out_, "cflags", flags.cflags, indent);
- WriteNamedArray(out_, "cflags_c", flags.cflags_c, indent);
- WriteNamedArray(out_, "cflags_cc", flags.cflags_cc, indent);
- WriteNamedArray(out_, "cflags_objc", flags.cflags_objc, indent);
- WriteNamedArray(out_, "cflags_objcc", flags.cflags_objcc, indent);
+ WriteNamedArray("defines", flags.defines, indent);
+ WriteNamedArray("cflags", flags.cflags, indent);
+ WriteNamedArray("cflags_c", flags.cflags_c, indent);
+ WriteNamedArray("cflags_cc", flags.cflags_cc, indent);
+ WriteNamedArray("cflags_objc", flags.cflags_objc, indent);
+ WriteNamedArray("cflags_objcc", flags.cflags_objcc, indent);
// Put libraries and library directories in with ldflags.
Indent(indent) << "'ldflags': ["; \
@@ -573,3 +536,19 @@ GypBinaryTargetWriter::Flags GypBinaryTargetWriter::FlagsFromConfigList(
return ret;
}
+
+void GypBinaryTargetWriter::WriteNamedArray(
+ const char* name,
+ const std::vector<std::string>& values,
+ int indent) {
+ if (values.empty())
+ return;
+
+ EscapeOptions options;
+ options.mode = ESCAPE_JSON;
+
+ Indent(indent) << "'" << name << "': [";
+ WriteArrayValues(out_, values);
+ out_ << " ],\n";
+}
+
diff --git a/tools/gn/gyp_binary_target_writer.h b/tools/gn/gyp_binary_target_writer.h
index 6fee4ad..8b3660e 100644
--- a/tools/gn/gyp_binary_target_writer.h
+++ b/tools/gn/gyp_binary_target_writer.h
@@ -17,7 +17,9 @@
// shared library, or a static library).
class GypBinaryTargetWriter : public GypTargetWriter {
public:
- GypBinaryTargetWriter(const TargetGroup& group, std::ostream& out);
+ GypBinaryTargetWriter(const TargetGroup& group,
+ const SourceDir& gyp_dir,
+ std::ostream& out);
virtual ~GypBinaryTargetWriter();
virtual void Run() OVERRIDE;
@@ -40,9 +42,6 @@ class GypBinaryTargetWriter : public GypTargetWriter {
std::vector<std::string> libs;
};
- // Writes the given number of spaces to the output stream and returns it.
- std::ostream& Indent(int spaces);
-
void WriteName(int indent);
void WriteType(int indent);
@@ -78,6 +77,13 @@ class GypBinaryTargetWriter : public GypTargetWriter {
Flags FlagsFromTarget(const Target* target) const;
Flags FlagsFromConfigList(const LabelConfigVector& configs) const;
+ // Writes the given array with the given name. The indent should be the
+ // indenting for the name, the values will be indented 2 spaces from there.
+ // Writes nothing if there is nothing in the array.
+ void WriteNamedArray(const char* name,
+ const std::vector<std::string>& values,
+ int indent);
+
// All associated targets.
TargetGroup group_;
diff --git a/tools/gn/gyp_script_target_writer.cc b/tools/gn/gyp_script_target_writer.cc
new file mode 100644
index 0000000..c6f5b1b4
--- /dev/null
+++ b/tools/gn/gyp_script_target_writer.cc
@@ -0,0 +1,108 @@
+// 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/gyp_script_target_writer.h"
+
+#include "tools/gn/builder_record.h"
+#include "tools/gn/err.h"
+#include "tools/gn/file_template.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/target.h"
+
+// Write script targets as GYP actions that just invoke Ninja. This allows us
+// to not have to worry about duplicating the precise GN script execution
+// semantices in GYP for each platform (GYP varies a bit).
+
+GypScriptTargetWriter::GypScriptTargetWriter(const TargetGroup& group,
+ const SourceDir& gyp_dir,
+ std::ostream& out)
+ : GypTargetWriter(group.debug->item()->AsTarget(), gyp_dir, out) {
+}
+
+GypScriptTargetWriter::~GypScriptTargetWriter() {
+}
+
+void GypScriptTargetWriter::Run() {
+ int indent = 4;
+ std::string name = helper_.GetNameForTarget(target_);
+
+ // Put the ninja build for this script target in this directory.
+ SourceDir ninja_dir(GetTargetOutputDir(target_).value() + name + "_ninja/");
+
+ Indent(indent) << "{\n";
+
+ Indent(indent + kExtraIndent) << "'target_name': '" << name << "',\n";
+ Indent(indent + kExtraIndent) << "'type': 'none',\n";
+ Indent(indent + kExtraIndent) << "'actions': [{\n";
+
+ Indent(indent + kExtraIndent * 2) << "'action_name': '" << name
+ << " action',\n";
+
+ Indent(indent + kExtraIndent * 2) << "'action': [\n";
+ Indent(indent + kExtraIndent * 3) << "'ninja',\n";
+ Indent(indent + kExtraIndent * 3) << "'-C', '";
+ path_output_.WriteDir(out_, ninja_dir, PathOutput::DIR_NO_LAST_SLASH);
+ out_ << "',\n";
+ Indent(indent + kExtraIndent * 3) << "'" << name << "',\n";
+ Indent(indent + kExtraIndent * 2) << "],\n";
+
+ WriteActionInputs(indent + kExtraIndent * 2);
+ WriteActionOutputs(indent + kExtraIndent * 2);
+
+ Indent(indent + kExtraIndent) << "}],\n";
+ Indent(indent) << "},\n";
+}
+
+void GypScriptTargetWriter::WriteActionInputs(int indent) {
+ Indent(indent) << "'inputs': [\n";
+
+ // Write everything that should be considered an input for dependency
+ // purposes, which is all sources as well as the prereqs.
+ const Target::FileList& sources = target_->sources();
+ for (size_t i = 0; i < sources.size(); i++) {
+ Indent(indent + kExtraIndent) << "'";
+ path_output_.WriteFile(out_, sources[i]);
+ out_ << "',\n";
+ }
+
+ const Target::FileList& prereqs = target_->source_prereqs();
+ for (size_t i = 0; i < prereqs.size(); i++) {
+ Indent(indent + kExtraIndent) << "'";
+ path_output_.WriteFile(out_, prereqs[i]);
+ out_ << "',\n";
+ }
+
+ Indent(indent) << "],\n";
+}
+
+void GypScriptTargetWriter::WriteActionOutputs(int indent) {
+ Indent(indent) << "'outputs': [\n";
+
+ const Target::FileList& sources = target_->sources();
+ if (sources.empty()) {
+ // Just write outputs directly if there are no sources.
+ const Target::FileList& output = target_->script_values().outputs();
+ for (size_t output_i = 0; output_i < output.size(); output_i++) {
+ Indent(indent + kExtraIndent) << "'";
+ path_output_.WriteFile(out_, output[output_i]);
+ out_ << "',\n";
+ }
+ } else {
+ // There are sources, the outputs should be a template to apply to each.
+ FileTemplate output_template = FileTemplate::GetForTargetOutputs(target_);
+
+ std::vector<std::string> output;
+ for (size_t source_i = 0; source_i < sources.size(); source_i++) {
+ output_template.ApplyString(sources[source_i].value(), &output);
+ for (size_t output_i = 0; output_i < output.size(); output_i++) {
+ Indent(indent + kExtraIndent) << "'";
+ path_output_.WriteFile(out_, SourceFile(output[output_i]));
+ out_ << "',\n";
+ }
+ }
+ }
+
+ Indent(indent) << "],\n";
+}
diff --git a/tools/gn/gyp_script_target_writer.h b/tools/gn/gyp_script_target_writer.h
new file mode 100644
index 0000000..79c53bf
--- /dev/null
+++ b/tools/gn/gyp_script_target_writer.h
@@ -0,0 +1,29 @@
+// 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_GYP_SCRIPT_TARGET_WRITER_H_
+#define TOOLS_GN_GYP_SCRIPT_TARGET_WRITER_H_
+
+#include "base/compiler_specific.h"
+#include "tools/gn/gyp_target_writer.h"
+#include "tools/gn/target.h"
+#include "tools/gn/toolchain.h"
+
+class GypScriptTargetWriter : public GypTargetWriter {
+ public:
+ GypScriptTargetWriter(const TargetGroup& group,
+ const SourceDir& gyp_dir,
+ std::ostream& out);
+ virtual ~GypScriptTargetWriter();
+
+ virtual void Run() OVERRIDE;
+
+ private:
+ void WriteActionInputs(int indent);
+ void WriteActionOutputs(int indent);
+
+ DISALLOW_COPY_AND_ASSIGN(GypScriptTargetWriter);
+};
+
+#endif // TOOLS_GN_GYP_SCRIPT_TARGET_WRITER_H_
diff --git a/tools/gn/gyp_script_target_writer_unittest.cc b/tools/gn/gyp_script_target_writer_unittest.cc
new file mode 100644
index 0000000..af3f3f9
--- /dev/null
+++ b/tools/gn/gyp_script_target_writer_unittest.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/builder_record.h"
+#include "tools/gn/gyp_script_target_writer.h"
+#include "tools/gn/test_with_scope.h"
+
+TEST(GypScriptTargetWriter, Run) {
+ TestWithScope setup;
+ setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
+ scoped_ptr<Target> target(
+ new Target(setup.settings(), Label(SourceDir("//foo/"), "bar")));
+ target->set_output_type(Target::CUSTOM);
+
+ target->sources().push_back(SourceFile("//foo/input1.txt"));
+ target->sources().push_back(SourceFile("//foo/input2.txt"));
+
+ target->script_values().outputs().push_back(
+ SourceFile("//out/Debug/{{source_file_part}}.out"));
+
+ BuilderRecord record(BuilderRecord::ITEM_TARGET, target->label());
+ record.set_item(target.PassAs<Item>());
+ GypTargetWriter::TargetGroup group;
+ group.debug = &record;
+
+ setup.settings()->set_target_os(Settings::WIN);
+
+ std::ostringstream out;
+ GypScriptTargetWriter writer(group, SourceDir("//out/gn_gyp/"), out);
+ writer.Run();
+
+ const char expected[] =
+ " {\n"
+ " 'target_name': 'bar',\n"
+ " 'type': 'none',\n"
+ " 'actions': [{\n"
+ " 'action_name': 'bar action',\n"
+ " 'action': [\n"
+ " 'ninja',\n"
+ " '-C', '../../out/Debug/obj/foo/bar_ninja',\n"
+ " 'bar',\n"
+ " ],\n"
+ " 'inputs': [\n"
+ " '../../foo/input1.txt',\n"
+ " '../../foo/input2.txt',\n"
+ " ],\n"
+ " 'outputs': [\n"
+ " '../../out/Debug/input1.txt.out',\n"
+ " '../../out/Debug/input2.txt.out',\n"
+ " ],\n"
+ " }],\n"
+ " },\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
diff --git a/tools/gn/gyp_target_writer.cc b/tools/gn/gyp_target_writer.cc
index 1c0de5f..725dbdc 100644
--- a/tools/gn/gyp_target_writer.cc
+++ b/tools/gn/gyp_target_writer.cc
@@ -12,14 +12,20 @@
#include "tools/gn/builder_record.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/gyp_binary_target_writer.h"
+#include "tools/gn/gyp_script_target_writer.h"
#include "tools/gn/scheduler.h"
#include "tools/gn/settings.h"
#include "tools/gn/target.h"
-GypTargetWriter::GypTargetWriter(const Target* target, std::ostream& out)
+GypTargetWriter::GypTargetWriter(const Target* target,
+ const SourceDir& gyp_dir,
+ std::ostream& out)
: settings_(target->settings()),
target_(target),
- out_(out) {
+ gyp_dir_(gyp_dir),
+ out_(out),
+ // All GYP paths are relative to GYP file.
+ path_output_(gyp_dir, ESCAPE_JSON, false) {
}
GypTargetWriter::~GypTargetWriter() {
@@ -61,14 +67,19 @@ void GypTargetWriter::WriteFile(const SourceFile& gyp_file,
const Target* cur = targets[i].debug->item()->AsTarget();
switch (cur->output_type()) {
case Target::COPY_FILES:
- case Target::CUSTOM:
+ break; // TODO(brettw)
+ case Target::CUSTOM: {
+ GypScriptTargetWriter writer(targets[i], gyp_file.GetDir(), file);
+ writer.Run();
+ break;
+ }
case Target::GROUP:
break; // TODO(brettw)
case Target::EXECUTABLE:
case Target::STATIC_LIBRARY:
case Target::SHARED_LIBRARY:
case Target::SOURCE_SET: {
- GypBinaryTargetWriter writer(targets[i], file);
+ GypBinaryTargetWriter writer(targets[i], gyp_file.GetDir(), file);
writer.Run();
break;
}
@@ -84,3 +95,16 @@ void GypTargetWriter::WriteFile(const SourceFile& gyp_file,
static_cast<int>(contents.size()));
}
+std::ostream& GypTargetWriter::Indent(int spaces) {
+ return Indent(out_, spaces);
+}
+
+// static
+std::ostream& GypTargetWriter::Indent(std::ostream& out, int spaces) {
+ const char kSpaces[81] =
+ " "
+ " ";
+ CHECK(static_cast<size_t>(spaces) <= arraysize(kSpaces) - 1);
+ out.write(kSpaces, spaces);
+ return out;
+}
diff --git a/tools/gn/gyp_target_writer.h b/tools/gn/gyp_target_writer.h
index 597a609..df44dab 100644
--- a/tools/gn/gyp_target_writer.h
+++ b/tools/gn/gyp_target_writer.h
@@ -10,6 +10,7 @@
#include "base/basictypes.h"
#include "tools/gn/gyp_helper.h"
+#include "tools/gn/path_output.h"
class BuilderRecord;
class Err;
@@ -32,7 +33,9 @@ class GypTargetWriter {
const BuilderRecord* host_release;
};
- GypTargetWriter(const Target* target, std::ostream& out);
+ GypTargetWriter(const Target* target,
+ const SourceDir& gyp_dir,
+ std::ostream& out);
virtual ~GypTargetWriter();
static void WriteFile(const SourceFile& gyp_file,
@@ -42,11 +45,19 @@ class GypTargetWriter {
virtual void Run() = 0;
protected:
+ // Writes the given number of spaces to the output stream and returns it.
+ std::ostream& Indent(int spaces);
+ static std::ostream& Indent(std::ostream& out, int spaces);
+
+ static const int kExtraIndent = 2;
+
const Settings* settings_; // Non-owning.
const Target* target_; // Non-owning.
+ SourceDir gyp_dir_; // Dir of GYP file.
std::ostream& out_;
GypHelper helper_;
+ PathOutput path_output_;
private:
DISALLOW_COPY_AND_ASSIGN(GypTargetWriter);
diff --git a/tools/gn/ninja_target_writer.h b/tools/gn/ninja_target_writer.h
index 937b240..3837868 100644
--- a/tools/gn/ninja_target_writer.h
+++ b/tools/gn/ninja_target_writer.h
@@ -35,7 +35,10 @@ class NinjaTargetWriter {
// implicit dependencies, returns the empty string.
std::string GetSourcesImplicitDeps() const;
- // Returns the FileTemplate constructed from the outputs variable.
+ // Returns the FileTemplate constructed from the outputs variable. This is
+ // like FileTemplate::GetForTargetOutputs except this additionally trims the
+ // build directory from the front so we can just write the names without
+ // further processing.
FileTemplate GetOutputTemplate() const;
const Settings* settings_; // Non-owning.
diff --git a/tools/gn/scope_per_file_provider.cc b/tools/gn/scope_per_file_provider.cc
index c2fdd3e..c6a0cd7 100644
--- a/tools/gn/scope_per_file_provider.cc
+++ b/tools/gn/scope_per_file_provider.cc
@@ -66,8 +66,8 @@ const Value* ScopePerFileProvider::GetPythonPath() {
const Value* ScopePerFileProvider::GetRootBuildDir() {
if (!root_build_dir_) {
- root_build_dir_.reset(new Value(NULL,
- "/" + GetRootOutputDirWithNoLastSlash(scope_->settings())));
+ root_build_dir_.reset(new Value(NULL, DirectoryWithNoLastSlash(
+ scope_->settings()->build_settings()->build_dir())));
}
return root_build_dir_.get();
}
@@ -75,7 +75,7 @@ const Value* ScopePerFileProvider::GetRootBuildDir() {
const Value* ScopePerFileProvider::GetRootGenDir() {
if (!root_gen_dir_) {
root_gen_dir_.reset(new Value(NULL,
- "/" + GetToolchainGenDirWithNoLastSlash(scope_->settings())));
+ DirectoryWithNoLastSlash(GetToolchainGenDir(scope_->settings()))));
}
return root_gen_dir_.get();
}
@@ -83,7 +83,7 @@ const Value* ScopePerFileProvider::GetRootGenDir() {
const Value* ScopePerFileProvider::GetRootOutDir() {
if (!root_out_dir_) {
root_out_dir_.reset(new Value(NULL,
- "/" + GetToolchainOutputDirWithNoLastSlash(scope_->settings())));
+ DirectoryWithNoLastSlash(GetToolchainOutputDir(scope_->settings()))));
}
return root_out_dir_.get();
}
@@ -91,9 +91,7 @@ const Value* ScopePerFileProvider::GetRootOutDir() {
const Value* ScopePerFileProvider::GetTargetGenDir() {
if (!target_gen_dir_) {
target_gen_dir_.reset(new Value(NULL,
- "/" +
- GetToolchainGenDirWithNoLastSlash(scope_->settings()) +
- GetFileDirWithNoLastSlash()));
+ DirectoryWithNoLastSlash(GetCurrentGenDir(scope_))));
}
return target_gen_dir_.get();
}
@@ -101,61 +99,7 @@ const Value* ScopePerFileProvider::GetTargetGenDir() {
const Value* ScopePerFileProvider::GetTargetOutDir() {
if (!target_out_dir_) {
target_out_dir_.reset(new Value(NULL,
- "/" +
- GetToolchainOutputDirWithNoLastSlash(scope_->settings()) + "/obj" +
- GetFileDirWithNoLastSlash()));
+ DirectoryWithNoLastSlash(GetCurrentOutputDir(scope_))));
}
return target_out_dir_.get();
}
-
-// static
-std::string ScopePerFileProvider::GetRootOutputDirWithNoLastSlash(
- const Settings* settings) {
- const std::string& output_dir =
- settings->build_settings()->build_dir().value();
-
- if (output_dir == "//")
- return "//.";
-
- // Trim off a leading and trailing slash. So "//foo/bar/" -> /foo/bar".
- DCHECK(output_dir.size() > 2 && output_dir[0] == '/' &&
- output_dir[output_dir.size() - 1] == '/');
- return output_dir.substr(1, output_dir.size() - 2);
-}
-
-// static
-std::string ScopePerFileProvider::GetToolchainOutputDirWithNoLastSlash(
- const Settings* settings) {
- const OutputFile& toolchain_subdir = settings->toolchain_output_subdir();
-
- std::string result;
- if (toolchain_subdir.value().empty()) {
- result = GetRootOutputDirWithNoLastSlash(settings);
- } else {
- // The toolchain subdir ends in a slash, trim it.
- result = GetRootOutputDirWithNoLastSlash(settings) + "/" +
- toolchain_subdir.value();
- DCHECK(toolchain_subdir.value()[toolchain_subdir.value().size() - 1] ==
- '/');
- result.resize(result.size() - 1);
- }
- return result;
-}
-
-// static
-std::string ScopePerFileProvider::GetToolchainGenDirWithNoLastSlash(
- const Settings* settings) {
- return GetToolchainOutputDirWithNoLastSlash(settings) + "/gen";
-}
-
-std::string ScopePerFileProvider::GetFileDirWithNoLastSlash() const {
- const std::string& dir_value = scope_->GetSourceDir().value();
-
- if (dir_value == "//")
- return "//.";
-
- // Trim off a leading and trailing slash. So "//foo/bar/" -> /foo/bar".
- DCHECK(dir_value.size() > 2 && dir_value[0] == '/' &&
- dir_value[dir_value.size() - 1] == '/');
- return dir_value.substr(1, dir_value.size() - 2);
-}
diff --git a/tools/gn/scope_per_file_provider.h b/tools/gn/scope_per_file_provider.h
index 362ac6a..1021c27 100644
--- a/tools/gn/scope_per_file_provider.h
+++ b/tools/gn/scope_per_file_provider.h
@@ -31,14 +31,6 @@ class ScopePerFileProvider : public Scope::ProgrammaticProvider {
const Value* GetTargetGenDir();
const Value* GetTargetOutDir();
- static std::string GetRootOutputDirWithNoLastSlash(const Settings* settings);
- static std::string GetToolchainOutputDirWithNoLastSlash(
- const Settings* settings);
- static std::string GetToolchainGenDirWithNoLastSlash(
- const Settings* settings);
-
- std::string GetFileDirWithNoLastSlash() const;
-
// All values are lazily created.
scoped_ptr<Value> current_toolchain_;
scoped_ptr<Value> default_toolchain_;
diff --git a/tools/gn/source_dir.cc b/tools/gn/source_dir.cc
index 025f428..81340f9 100644
--- a/tools/gn/source_dir.cc
+++ b/tools/gn/source_dir.cc
@@ -11,9 +11,10 @@
namespace {
void AssertValueSourceDirString(const std::string& s) {
- DCHECK(!s.empty());
- DCHECK(s[0] == '/');
- DCHECK(EndsWithSlash(s));
+ if (!s.empty()) {
+ DCHECK(s[0] == '/');
+ DCHECK(EndsWithSlash(s));
+ }
}
} // namespace
@@ -28,6 +29,13 @@ SourceDir::SourceDir(const base::StringPiece& p)
AssertValueSourceDirString(value_);
}
+SourceDir::SourceDir(SwapIn, std::string* s) {
+ value_.swap(*s);
+ if (!EndsWithSlash(value_))
+ value_.push_back('/');
+ AssertValueSourceDirString(value_);
+}
+
SourceDir::~SourceDir() {
}
@@ -132,7 +140,7 @@ base::FilePath SourceDir::Resolve(const base::FilePath& source_root) const {
return source_root.Append(UTF8ToFilePath(converted));
}
-void SourceDir::SwapInValue(std::string* v) {
+void SourceDir::SwapValue(std::string* v) {
value_.swap(*v);
AssertValueSourceDirString(value_);
}
diff --git a/tools/gn/source_dir.h b/tools/gn/source_dir.h
index 4aa344a..99be5c3 100644
--- a/tools/gn/source_dir.h
+++ b/tools/gn/source_dir.h
@@ -23,8 +23,13 @@ class SourceFile;
// Two slashes at the beginning indicate a path relative to the source root.
class SourceDir {
public:
+ enum SwapIn { SWAP_IN };
+
SourceDir();
explicit SourceDir(const base::StringPiece& p);
+ // Swaps the given string in without copies. The given string will be empty
+ // after this call.
+ SourceDir(SwapIn, std::string* s);
~SourceDir();
// Resolves a file or dir name relative to this source directory. Will return
@@ -73,7 +78,7 @@ class SourceDir {
return base::StringPiece(&value_[1], value_.size() - 1);
}
- void SwapInValue(std::string* v);
+ void SwapValue(std::string* v);
bool operator==(const SourceDir& other) const {
return value_ == other.value_;