diff options
-rw-r--r-- | tools/gn/BUILD.gn | 3 | ||||
-rw-r--r-- | tools/gn/file_template.cc | 11 | ||||
-rw-r--r-- | tools/gn/file_template.h | 5 | ||||
-rw-r--r-- | tools/gn/filesystem_utils.cc | 85 | ||||
-rw-r--r-- | tools/gn/filesystem_utils.h | 21 | ||||
-rw-r--r-- | tools/gn/filesystem_utils_unittest.cc | 82 | ||||
-rw-r--r-- | tools/gn/functions_target.cc | 2 | ||||
-rw-r--r-- | tools/gn/gn.gyp | 3 | ||||
-rw-r--r-- | tools/gn/gyp_binary_target_writer.cc | 83 | ||||
-rw-r--r-- | tools/gn/gyp_binary_target_writer.h | 14 | ||||
-rw-r--r-- | tools/gn/gyp_script_target_writer.cc | 108 | ||||
-rw-r--r-- | tools/gn/gyp_script_target_writer.h | 29 | ||||
-rw-r--r-- | tools/gn/gyp_script_target_writer_unittest.cc | 57 | ||||
-rw-r--r-- | tools/gn/gyp_target_writer.cc | 32 | ||||
-rw-r--r-- | tools/gn/gyp_target_writer.h | 13 | ||||
-rw-r--r-- | tools/gn/ninja_target_writer.h | 5 | ||||
-rw-r--r-- | tools/gn/scope_per_file_provider.cc | 68 | ||||
-rw-r--r-- | tools/gn/scope_per_file_provider.h | 8 | ||||
-rw-r--r-- | tools/gn/source_dir.cc | 16 | ||||
-rw-r--r-- | tools/gn/source_dir.h | 7 |
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_; |