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