summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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_;