• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "gn/ninja_outputs_writer.h"
6 
7 #include "base/command_line.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "gn/builder_record.h"
12 #include "gn/filesystem_utils.h"
13 #include "gn/ninja_target_writer.h"
14 #include "gn/setup.h"
15 #include "gn/switches.h"
16 #include "gn/test_with_scheduler.h"
17 #include "util/test/test.h"
18 
19 using NinjaOutputsWriterTest = TestWithScheduler;
20 using NinjaOutputsMap = NinjaOutputsWriter::MapType;
21 
WriteFile(const base::FilePath & file,const std::string & data)22 static void WriteFile(const base::FilePath& file, const std::string& data) {
23   CHECK_EQ(static_cast<int>(data.size()),  // Way smaller than INT_MAX.
24            base::WriteFile(file, data.data(), data.size()));
25 }
26 
27 // Collects Ninja outputs for each target. Used by multiple background threads.
28 struct TargetWriteInfo {
29   std::mutex lock;
30   NinjaOutputsMap ninja_outputs_map;
31 };
32 
33 // Called on worker thread to write the ninja file.
BackgroundDoWrite(TargetWriteInfo * write_info,const Target * target)34 void BackgroundDoWrite(TargetWriteInfo* write_info, const Target* target) {
35   std::vector<OutputFile> target_ninja_outputs;
36   std::string rule = NinjaTargetWriter::RunAndWriteFile(target, nullptr,
37                                                         &target_ninja_outputs);
38 
39   DCHECK(!rule.empty());
40 
41   std::lock_guard<std::mutex> lock(write_info->lock);
42   write_info->ninja_outputs_map.emplace(target,
43                                         std::move(target_ninja_outputs));
44 }
45 
ItemResolvedAndGeneratedCallback(TargetWriteInfo * write_info,const BuilderRecord * record)46 static void ItemResolvedAndGeneratedCallback(TargetWriteInfo* write_info,
47                                              const BuilderRecord* record) {
48   const Item* item = record->item();
49   const Target* target = item->AsTarget();
50   if (target) {
51     g_scheduler->ScheduleWork(
52         [write_info, target]() { BackgroundDoWrite(write_info, target); });
53   }
54 }
55 
TEST_F(NinjaOutputsWriterTest,OutputsFile)56 TEST_F(NinjaOutputsWriterTest, OutputsFile) {
57   base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
58 
59   const char kDotfileContents[] = R"(
60 buildconfig = "//BUILDCONFIG.gn"
61 )";
62 
63   const char kBuildConfigContents[] = R"(
64 set_default_toolchain("//toolchain:default")
65 )";
66 
67   const char kToolchainBuildContents[] = R"##(
68 toolchain("default") {
69   tool("stamp") {
70     command = "stamp"
71   }
72 }
73 
74 toolchain("secondary") {
75   tool("stamp") {
76     command = "stamp2"
77   }
78 }
79 )##";
80 
81   const char kBuildGnContents[] = R"##(
82 group("foo") {
83   deps = [ ":bar", ":zoo(//toolchain:secondary)" ]
84 }
85 
86 action("bar") {
87   script = "//:run_bar_script.py"
88   outputs = [ "$root_build_dir/bar.output" ]
89   args = []
90 }
91 
92 group("zoo") {
93 }
94 )##";
95 
96   // Create a temp directory containing the build.
97   base::ScopedTempDir in_temp_dir;
98   ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
99   base::FilePath in_path = in_temp_dir.GetPath();
100 
101   WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILD.gn")), kBuildGnContents);
102   WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")),
103             kBuildConfigContents);
104   WriteFile(in_path.Append(FILE_PATH_LITERAL(".gn")), kDotfileContents);
105 
106   EXPECT_TRUE(
107       base::CreateDirectory(in_path.Append(FILE_PATH_LITERAL("toolchain"))));
108 
109   WriteFile(in_path.Append(FILE_PATH_LITERAL("toolchain/BUILD.gn")),
110             kToolchainBuildContents);
111 
112   cmdline.AppendSwitch(switches::kRoot, FilePathToUTF8(in_path));
113 
114   base::FilePath outputs_json_path(FILE_PATH_LITERAL("ninja_outputs.json"));
115   cmdline.AppendSwitch("--ninja-outputs-file",
116                        FilePathToUTF8(outputs_json_path));
117 
118   // Create another temp dir for writing the generated files to.
119   base::ScopedTempDir build_temp_dir;
120   ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
121 
122   // Run setup
123   Setup setup;
124   EXPECT_TRUE(
125       setup.DoSetup(FilePathToUTF8(build_temp_dir.GetPath()), true, cmdline));
126 
127   TargetWriteInfo write_info;
128 
129   setup.builder().set_resolved_and_generated_callback(
130       [&write_info](const BuilderRecord* record) {
131         ItemResolvedAndGeneratedCallback(&write_info, record);
132       });
133 
134   // Do the actual load.
135   ASSERT_TRUE(setup.Run());
136 
137   StringOutputBuffer out =
138       NinjaOutputsWriter::GenerateJSON(write_info.ninja_outputs_map);
139 
140   // Verify that the generated file is here.
141   std::string generated = out.str();
142   std::string expected = R"##({
143   "//:bar": [
144     "bar.output",
145     "obj/bar.stamp"
146   ],
147   "//:foo": [
148     "obj/foo.stamp"
149   ],
150   "//:zoo": [
151     "obj/zoo.stamp"
152   ],
153   "//:zoo(//toolchain:secondary)": [
154     "secondary/obj/zoo.stamp"
155   ]
156 })##";
157 
158   EXPECT_EQ(generated, expected);
159 }
160