json_project_writer: Output response_file_contents for action and action_for_each Add response_file_contents as a property of the action and action_for_each targets. Bug: gn:84 Change-Id: I55eba6411013c6edaaac86331a2550078ac04821 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/5100 Reviewed-by: Brett Wilson <brettw@google.com> Commit-Queue: Brett Wilson <brettw@google.com>
diff --git a/AUTHORS b/AUTHORS index fe152fd..417cc5f 100644 --- a/AUTHORS +++ b/AUTHORS
@@ -12,6 +12,7 @@ IBM Inc. <*@*.ibm.com> Loongson Technology Corporation Limited. <*@loongson.cn> MIPS Technologies, Inc. <*@mips.com> +NVIDIA Corporation <*@nvidia.com> Opera Software ASA <*@opera.com> The Chromium Authors <*@chromium.org> Vewd Software AS <*@vewd.com>
diff --git a/build/gen.py b/build/gen.py index 3d356c7..766969f 100755 --- a/build/gen.py +++ b/build/gen.py
@@ -587,6 +587,7 @@ 'tools/gn/header_checker_unittest.cc', 'tools/gn/inherited_libraries_unittest.cc', 'tools/gn/input_conversion_unittest.cc', + 'tools/gn/json_project_writer_unittest.cc', 'tools/gn/label_pattern_unittest.cc', 'tools/gn/label_unittest.cc', 'tools/gn/loader_unittest.cc',
diff --git a/tools/gn/desc_builder.cc b/tools/gn/desc_builder.cc index ab6c686..6ede896 100644 --- a/tools/gn/desc_builder.cc +++ b/tools/gn/desc_builder.cc
@@ -55,6 +55,7 @@ // "walk_keys" : [ list of target walk keys ] // "rebase" : true or false // "output_conversion" : "string for output conversion" +// "response_file_contents": [ list of response file contents entries ] // } // // Optionally, if "what" is specified while generating description, two other @@ -416,6 +417,16 @@ res->SetWithoutPathExpansion(variables::kArgs, std::move(args)); } + if (what(variables::kResponseFileContents) && + !target_->action_values().rsp_file_contents().list().empty()) { + auto rsp_file_contents = std::make_unique<base::ListValue>(); + for (const auto& elem : + target_->action_values().rsp_file_contents().list()) + rsp_file_contents->AppendString(elem.AsString()); + + res->SetWithoutPathExpansion(variables::kResponseFileContents, + std::move(rsp_file_contents)); + } if (what(variables::kDepfile) && !target_->action_values().depfile().empty()) { res->SetKey(variables::kDepfile,
diff --git a/tools/gn/json_project_writer.cc b/tools/gn/json_project_writer.cc index 4f4b0e7..1b00086 100644 --- a/tools/gn/json_project_writer.cc +++ b/tools/gn/json_project_writer.cc
@@ -81,48 +81,6 @@ return true; } -std::string RenderJSON(const BuildSettings* build_settings, - const Builder& builder, - std::vector<const Target*>& all_targets) { - Label default_toolchain_label; - - auto targets = std::make_unique<base::DictionaryValue>(); - for (const auto* target : all_targets) { - if (default_toolchain_label.is_null()) - default_toolchain_label = target->settings()->default_toolchain_label(); - auto description = - DescBuilder::DescriptionForTarget(target, "", false, false, false); - // Outputs need to be asked for separately. - auto outputs = DescBuilder::DescriptionForTarget(target, "source_outputs", - false, false, false); - base::DictionaryValue* outputs_value = nullptr; - if (outputs->GetDictionary("source_outputs", &outputs_value) && - !outputs_value->empty()) { - description->MergeDictionary(outputs.get()); - } - targets->SetWithoutPathExpansion( - target->label().GetUserVisibleName(default_toolchain_label), - std::move(description)); - } - - auto settings = std::make_unique<base::DictionaryValue>(); - settings->SetKey("root_path", base::Value(build_settings->root_path_utf8())); - settings->SetKey("build_dir", - base::Value(build_settings->build_dir().value())); - settings->SetKey( - "default_toolchain", - base::Value(default_toolchain_label.GetUserVisibleName(false))); - - auto output = std::make_unique<base::DictionaryValue>(); - output->SetWithoutPathExpansion("targets", std::move(targets)); - output->SetWithoutPathExpansion("build_settings", std::move(settings)); - - std::string s; - base::JSONWriter::WriteWithOptions( - *output.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &s); - return s; -} - bool InvokePython(const BuildSettings* build_settings, const base::FilePath& python_script_path, const std::string& python_script_extra_args, @@ -192,7 +150,7 @@ return false; } - std::string json = RenderJSON(build_settings, builder, targets); + std::string json = RenderJSON(build_settings, targets); if (!ContentsEqual(output_path, json)) { if (!WriteFileIfChanged(output_path, json, err)) { return false; @@ -218,3 +176,45 @@ return true; } + +std::string JSONProjectWriter::RenderJSON( + const BuildSettings* build_settings, + std::vector<const Target*>& all_targets) { + Label default_toolchain_label; + + auto targets = std::make_unique<base::DictionaryValue>(); + for (const auto* target : all_targets) { + if (default_toolchain_label.is_null()) + default_toolchain_label = target->settings()->default_toolchain_label(); + auto description = + DescBuilder::DescriptionForTarget(target, "", false, false, false); + // Outputs need to be asked for separately. + auto outputs = DescBuilder::DescriptionForTarget(target, "source_outputs", + false, false, false); + base::DictionaryValue* outputs_value = nullptr; + if (outputs->GetDictionary("source_outputs", &outputs_value) && + !outputs_value->empty()) { + description->MergeDictionary(outputs.get()); + } + targets->SetWithoutPathExpansion( + target->label().GetUserVisibleName(default_toolchain_label), + std::move(description)); + } + + auto settings = std::make_unique<base::DictionaryValue>(); + settings->SetKey("root_path", base::Value(build_settings->root_path_utf8())); + settings->SetKey("build_dir", + base::Value(build_settings->build_dir().value())); + settings->SetKey( + "default_toolchain", + base::Value(default_toolchain_label.GetUserVisibleName(false))); + + auto output = std::make_unique<base::DictionaryValue>(); + output->SetWithoutPathExpansion("targets", std::move(targets)); + output->SetWithoutPathExpansion("build_settings", std::move(settings)); + + std::string s; + base::JSONWriter::WriteWithOptions( + *output.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &s); + return s; +}
diff --git a/tools/gn/json_project_writer.h b/tools/gn/json_project_writer.h index 8c293bf..2c8a2b7 100644 --- a/tools/gn/json_project_writer.h +++ b/tools/gn/json_project_writer.h
@@ -21,6 +21,13 @@ const std::string& dir_filter_string, bool quiet, Err* err); + + private: + FRIEND_TEST_ALL_PREFIXES(JSONProjectWriter, ActionWithResponseFile); + FRIEND_TEST_ALL_PREFIXES(JSONProjectWriter, ForEachWithResponseFile); + + static std::string RenderJSON(const BuildSettings* build_settings, + std::vector<const Target*>& all_targets); }; #endif
diff --git a/tools/gn/json_project_writer_unittest.cc b/tools/gn/json_project_writer_unittest.cc new file mode 100644 index 0000000..9c933dd --- /dev/null +++ b/tools/gn/json_project_writer_unittest.cc
@@ -0,0 +1,137 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/strings/string_util.h" +#include "tools/gn/json_project_writer.h" +#include "tools/gn/substitution_list.h" +#include "tools/gn/target.h" +#include "tools/gn/test_with_scope.h" +#include "util/build_config.h" +#include "util/test/test.h" + +TEST(JSONProjectWriter, ActionWithResponseFile) { + Err err; + TestWithScope setup; + + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::ACTION); + + target.sources().push_back(SourceFile("//foo/source1.txt")); + target.config_values().inputs().push_back(SourceFile("//foo/input1.txt")); + target.action_values().set_script(SourceFile("//foo/script.py")); + + target.SetToolchain(setup.toolchain()); + ASSERT_TRUE(target.OnResolved(&err)); + + // Make sure we get interesting substitutions for both the args and the + // response file contents. + target.action_values().args() = + SubstitutionList::MakeForTest("{{response_file_name}}"); + target.action_values().rsp_file_contents() = + SubstitutionList::MakeForTest("-j", "3"); + target.action_values().outputs() = + SubstitutionList::MakeForTest("//out/Debug/output1.out"); + + setup.build_settings()->set_python_path( + base::FilePath(FILE_PATH_LITERAL("/usr/bin/python"))); + std::vector<const Target*> targets; + targets.push_back(&target); + std::string out = + JSONProjectWriter::RenderJSON(setup.build_settings(), targets); +#if defined(OS_WIN) + base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n"); +#endif + const char expected_json[] = + "{\n" + " \"build_settings\": {\n" + " \"build_dir\": \"//out/Debug/\",\n" + " \"default_toolchain\": \"//toolchain:default\",\n" + " \"root_path\": \"\"\n" + " },\n" + " \"targets\": {\n" + " \"//foo:bar()\": {\n" + " \"args\": [ \"{{response_file_name}}\" ],\n" + " \"deps\": [ ],\n" + " \"inputs\": [ \"//foo/input1.txt\" ],\n" + " \"metadata\": {\n" + "\n" + " },\n" + " \"outputs\": [ \"//out/Debug/output1.out\" ],\n" + " \"public\": \"*\",\n" + " \"response_file_contents\": [ \"-j\", \"3\" ],\n" + " \"script\": \"//foo/script.py\",\n" + " \"sources\": [ \"//foo/source1.txt\" ],\n" + " \"testonly\": false,\n" + " \"toolchain\": \"\",\n" + " \"type\": \"action\",\n" + " \"visibility\": [ ]\n" + " }\n" + " }\n" + "}\n"; + EXPECT_EQ(expected_json, out); +} + +TEST(JSONProjectWriter, ForEachWithResponseFile) { + Err err; + TestWithScope setup; + + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::ACTION_FOREACH); + + target.sources().push_back(SourceFile("//foo/input1.txt")); + target.action_values().set_script(SourceFile("//foo/script.py")); + + target.SetToolchain(setup.toolchain()); + ASSERT_TRUE(target.OnResolved(&err)); + + // Make sure we get interesting substitutions for both the args and the + // response file contents. + target.action_values().args() = SubstitutionList::MakeForTest( + "{{source}}", "{{source_file_part}}", "{{response_file_name}}"); + target.action_values().rsp_file_contents() = + SubstitutionList::MakeForTest("-j", "{{source_name_part}}"); + target.action_values().outputs() = + SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out"); + + setup.build_settings()->set_python_path( + base::FilePath(FILE_PATH_LITERAL("/usr/bin/python"))); + std::vector<const Target*> targets; + targets.push_back(&target); + std::string out = + JSONProjectWriter::RenderJSON(setup.build_settings(), targets); +#if defined(OS_WIN) + base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n"); +#endif + const char expected_json[] = + "{\n" + " \"build_settings\": {\n" + " \"build_dir\": \"//out/Debug/\",\n" + " \"default_toolchain\": \"//toolchain:default\",\n" + " \"root_path\": \"\"\n" + " },\n" + " \"targets\": {\n" + " \"//foo:bar()\": {\n" + " \"args\": [ \"{{source}}\", \"{{source_file_part}}\", " + "\"{{response_file_name}}\" ],\n" + " \"deps\": [ ],\n" + " \"metadata\": {\n" + "\n" + " },\n" + " \"output_patterns\": [ " + "\"//out/Debug/{{source_name_part}}.out\" ],\n" + " \"outputs\": [ \"//out/Debug/input1.out\" ],\n" + " \"public\": \"*\",\n" + " \"response_file_contents\": [ \"-j\", \"{{source_name_part}}\" " + "],\n" + " \"script\": \"//foo/script.py\",\n" + " \"sources\": [ \"//foo/input1.txt\" ],\n" + " \"testonly\": false,\n" + " \"toolchain\": \"\",\n" + " \"type\": \"action_foreach\",\n" + " \"visibility\": [ ]\n" + " }\n" + " }\n" + "}\n"; + EXPECT_EQ(expected_json, out); +}