1 // Copyright (c) 2013 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 <algorithm>
6
7 #include "gn/builder.h"
8 #include "gn/config.h"
9 #include "gn/loader.h"
10 #include "gn/target.h"
11 #include "gn/test_with_scheduler.h"
12 #include "gn/test_with_scope.h"
13 #include "gn/toolchain.h"
14 #include "util/test/test.h"
15
16 namespace gn_builder_unittest {
17
18 class MockLoader : public Loader {
19 public:
20 MockLoader() = default;
21
22 // Loader implementation:
Load(const SourceFile & file,const LocationRange & origin,const Label & toolchain_name)23 void Load(const SourceFile& file,
24 const LocationRange& origin,
25 const Label& toolchain_name) override {
26 files_.push_back(file);
27 }
ToolchainLoaded(const Toolchain * toolchain)28 void ToolchainLoaded(const Toolchain* toolchain) override {}
GetDefaultToolchain() const29 Label GetDefaultToolchain() const override { return Label(); }
GetToolchainSettings(const Label & label) const30 const Settings* GetToolchainSettings(const Label& label) const override {
31 return nullptr;
32 }
BuildFileForLabel(const Label & label) const33 SourceFile BuildFileForLabel(const Label& label) const override {
34 return SourceFile(label.dir().value() + "BUILD.gn");
35 }
36
HasLoadedNone() const37 bool HasLoadedNone() const { return files_.empty(); }
38
39 // Returns true if one/two loads have been requested and they match the given
40 // file(s). This will clear the records so it will be empty for the next call.
HasLoadedOne(const SourceFile & file)41 bool HasLoadedOne(const SourceFile& file) {
42 if (files_.size() != 1u) {
43 files_.clear();
44 return false;
45 }
46 bool match = (files_[0] == file);
47 files_.clear();
48 return match;
49 }
HasLoadedTwo(const SourceFile & a,const SourceFile & b)50 bool HasLoadedTwo(const SourceFile& a, const SourceFile& b) {
51 if (files_.size() != 2u) {
52 files_.clear();
53 return false;
54 }
55
56 bool match = ((files_[0] == a && files_[1] == b) ||
57 (files_[0] == b && files_[1] == a));
58 files_.clear();
59 return match;
60 }
HasLoadedOnce(const SourceFile & f)61 bool HasLoadedOnce(const SourceFile& f) {
62 return count(files_.begin(), files_.end(), f) == 1;
63 }
64
65 private:
66 ~MockLoader() override = default;
67
68 std::vector<SourceFile> files_;
69 };
70
71 class BuilderTest : public TestWithScheduler {
72 public:
BuilderTest()73 BuilderTest()
74 : loader_(new MockLoader),
75 builder_(loader_.get()),
76 settings_(&build_settings_, std::string()),
77 scope_(&settings_) {
78 build_settings_.SetBuildDir(SourceDir("//out/"));
79 settings_.set_toolchain_label(Label(SourceDir("//tc/"), "default"));
80 settings_.set_default_toolchain_label(settings_.toolchain_label());
81 }
82
DefineToolchain()83 Toolchain* DefineToolchain() {
84 Toolchain* tc = new Toolchain(&settings_, settings_.toolchain_label());
85 TestWithScope::SetupToolchain(tc);
86 builder_.ItemDefined(std::unique_ptr<Item>(tc));
87 return tc;
88 }
89
90 protected:
91 scoped_refptr<MockLoader> loader_;
92 Builder builder_;
93 BuildSettings build_settings_;
94 Settings settings_;
95 Scope scope_;
96 };
97
TEST_F(BuilderTest,BasicDeps)98 TEST_F(BuilderTest, BasicDeps) {
99 SourceDir toolchain_dir = settings_.toolchain_label().dir();
100 std::string toolchain_name = settings_.toolchain_label().name();
101
102 // Construct a dependency chain: A -> B -> C. Define A first with a
103 // forward-reference to B, then C, then B to test the different orders that
104 // the dependencies are hooked up.
105 Label a_label(SourceDir("//a/"), "a", toolchain_dir, toolchain_name);
106 Label b_label(SourceDir("//b/"), "b", toolchain_dir, toolchain_name);
107 Label c_label(SourceDir("//c/"), "c", toolchain_dir, toolchain_name);
108
109 // The builder will take ownership of the pointers.
110 Target* a = new Target(&settings_, a_label);
111 a->public_deps().push_back(LabelTargetPair(b_label));
112 a->set_output_type(Target::EXECUTABLE);
113 builder_.ItemDefined(std::unique_ptr<Item>(a));
114
115 // Should have requested that B and the toolchain is loaded.
116 EXPECT_TRUE(loader_->HasLoadedTwo(SourceFile("//tc/BUILD.gn"),
117 SourceFile("//b/BUILD.gn")));
118
119 // Define the toolchain.
120 DefineToolchain();
121 BuilderRecord* toolchain_record =
122 builder_.GetRecord(settings_.toolchain_label());
123 ASSERT_TRUE(toolchain_record);
124 EXPECT_EQ(BuilderRecord::ITEM_TOOLCHAIN, toolchain_record->type());
125
126 // A should be unresolved with an item
127 BuilderRecord* a_record = builder_.GetRecord(a_label);
128 EXPECT_TRUE(a_record->item());
129 EXPECT_FALSE(a_record->resolved());
130 EXPECT_FALSE(a_record->can_resolve());
131
132 // B should be unresolved, have no item, and no deps.
133 BuilderRecord* b_record = builder_.GetRecord(b_label);
134 EXPECT_FALSE(b_record->item());
135 EXPECT_FALSE(b_record->resolved());
136 EXPECT_FALSE(b_record->can_resolve());
137 EXPECT_TRUE(b_record->all_deps().empty());
138
139 // A should have two deps: B and the toolchain. Only B should be unresolved.
140 EXPECT_EQ(2u, a_record->all_deps().size());
141 EXPECT_TRUE(a_record->all_deps().contains(toolchain_record));
142 EXPECT_TRUE(a_record->all_deps().contains(b_record));
143
144 std::vector<const BuilderRecord*> a_unresolved =
145 a_record->GetSortedUnresolvedDeps();
146 EXPECT_EQ(1u, a_unresolved.size());
147 EXPECT_EQ(a_unresolved[0], b_record);
148
149 // B should be marked as having A waiting on it.
150 EXPECT_EQ(1u, b_record->waiting_on_resolution().size());
151 EXPECT_TRUE(b_record->waiting_on_resolution().contains(a_record));
152
153 // Add the C target.
154 Target* c = new Target(&settings_, c_label);
155 c->set_output_type(Target::STATIC_LIBRARY);
156 c->visibility().SetPublic();
157 builder_.ItemDefined(std::unique_ptr<Item>(c));
158
159 // C only depends on the already-loaded toolchain so we shouldn't have
160 // requested anything else.
161 EXPECT_TRUE(loader_->HasLoadedNone());
162
163 // Add the B target.
164 Target* b = new Target(&settings_, b_label);
165 a->public_deps().push_back(LabelTargetPair(c_label));
166 b->set_output_type(Target::SHARED_LIBRARY);
167 b->visibility().SetPublic();
168 builder_.ItemDefined(std::unique_ptr<Item>(b));
169
170 // B depends only on the already-loaded C and toolchain so we shouldn't have
171 // requested anything else.
172 EXPECT_TRUE(loader_->HasLoadedNone());
173
174 // All targets should now be resolved.
175 BuilderRecord* c_record = builder_.GetRecord(c_label);
176 EXPECT_TRUE(a_record->resolved());
177 EXPECT_TRUE(b_record->resolved());
178 EXPECT_TRUE(c_record->resolved());
179
180 EXPECT_TRUE(a_record->GetSortedUnresolvedDeps().empty());
181 EXPECT_TRUE(b_record->GetSortedUnresolvedDeps().empty());
182 EXPECT_TRUE(c_record->GetSortedUnresolvedDeps().empty());
183
184 EXPECT_TRUE(a_record->waiting_on_resolution().empty());
185 EXPECT_TRUE(b_record->waiting_on_resolution().empty());
186 EXPECT_TRUE(c_record->waiting_on_resolution().empty());
187 }
188
TEST_F(BuilderTest,SortedUnresolvedDeps)189 TEST_F(BuilderTest, SortedUnresolvedDeps) {
190 SourceDir toolchain_dir = settings_.toolchain_label().dir();
191 std::string toolchain_name = settings_.toolchain_label().name();
192
193 // Construct a dependency graph with:
194 // A -> B
195 // A -> D
196 // A -> C
197 //
198 // Ensure that the unresolved list of A is always [B, C, D]
199 //
200 Label a_label(SourceDir("//a/"), "a", toolchain_dir, toolchain_name);
201 Label b_label(SourceDir("//b/"), "b", toolchain_dir, toolchain_name);
202 Label c_label(SourceDir("//c/"), "c", toolchain_dir, toolchain_name);
203 Label d_label(SourceDir("//d/"), "d", toolchain_dir, toolchain_name);
204
205 BuilderRecord* a_record = builder_.GetOrCreateRecordForTesting(a_label);
206 BuilderRecord* b_record = builder_.GetOrCreateRecordForTesting(b_label);
207 BuilderRecord* c_record = builder_.GetOrCreateRecordForTesting(c_label);
208 BuilderRecord* d_record = builder_.GetOrCreateRecordForTesting(d_label);
209
210 a_record->AddDep(b_record);
211 a_record->AddDep(d_record);
212 a_record->AddDep(c_record);
213
214 std::vector<const BuilderRecord*> a_unresolved =
215 a_record->GetSortedUnresolvedDeps();
216 EXPECT_EQ(3u, a_unresolved.size()) << a_unresolved.size();
217 EXPECT_EQ(b_record, a_unresolved[0]);
218 EXPECT_EQ(c_record, a_unresolved[1]);
219 EXPECT_EQ(d_record, a_unresolved[2]);
220 }
221
222 // Tests that the "should generate" flag is set and propagated properly.
TEST_F(BuilderTest,ShouldGenerate)223 TEST_F(BuilderTest, ShouldGenerate) {
224 DefineToolchain();
225
226 // Define a secondary toolchain.
227 Settings settings2(&build_settings_, "secondary/");
228 Label toolchain_label2(SourceDir("//tc/"), "secondary");
229 settings2.set_toolchain_label(toolchain_label2);
230 Toolchain* tc2 = new Toolchain(&settings2, toolchain_label2);
231 TestWithScope::SetupToolchain(tc2);
232 builder_.ItemDefined(std::unique_ptr<Item>(tc2));
233
234 // Construct a dependency chain: A -> B. A is in the default toolchain, B
235 // is not.
236 Label a_label(SourceDir("//foo/"), "a", settings_.toolchain_label().dir(),
237 "a");
238 Label b_label(SourceDir("//foo/"), "b", toolchain_label2.dir(),
239 toolchain_label2.name());
240
241 // First define B.
242 Target* b = new Target(&settings2, b_label);
243 b->visibility().SetPublic();
244 b->set_output_type(Target::EXECUTABLE);
245 builder_.ItemDefined(std::unique_ptr<Item>(b));
246
247 // B should not be marked generated by default.
248 BuilderRecord* b_record = builder_.GetRecord(b_label);
249 EXPECT_FALSE(b_record->should_generate());
250
251 // Define A with a dependency on B.
252 Target* a = new Target(&settings_, a_label);
253 a->public_deps().push_back(LabelTargetPair(b_label));
254 a->set_output_type(Target::EXECUTABLE);
255 builder_.ItemDefined(std::unique_ptr<Item>(a));
256
257 // A should have the generate bit set since it's in the default toolchain.
258 BuilderRecord* a_record = builder_.GetRecord(a_label);
259 EXPECT_TRUE(a_record->should_generate());
260
261 // It should have gotten pushed to B.
262 EXPECT_TRUE(b_record->should_generate());
263 }
264
265 // Test that "gen_deps" forces targets to be generated.
TEST_F(BuilderTest,GenDeps)266 TEST_F(BuilderTest, GenDeps) {
267 DefineToolchain();
268
269 // Define another toolchain
270 Settings settings2(&build_settings_, "alternate/");
271 Label alt_tc(SourceDir("//tc/"), "alternate");
272 settings2.set_toolchain_label(alt_tc);
273 Toolchain* tc2 = new Toolchain(&settings2, alt_tc);
274 TestWithScope::SetupToolchain(tc2);
275 builder_.ItemDefined(std::unique_ptr<Item>(tc2));
276
277 // Construct the dependency chain A -> B -gen-> C -gen-> D where A is the only
278 // target in the default toolchain. This should cause all 4 targets to be
279 // generated.
280 Label a_label(SourceDir("//a/"), "a", settings_.toolchain_label().dir(),
281 settings_.toolchain_label().name());
282 Label b_label(SourceDir("//b/"), "b", alt_tc.dir(), alt_tc.name());
283 Label c_label(SourceDir("//c/"), "c", alt_tc.dir(), alt_tc.name());
284 Label d_label(SourceDir("//d/"), "d", alt_tc.dir(), alt_tc.name());
285
286 Target* c = new Target(&settings2, c_label);
287 c->set_output_type(Target::EXECUTABLE);
288 c->gen_deps().push_back(LabelTargetPair(d_label));
289 builder_.ItemDefined(std::unique_ptr<Item>(c));
290
291 Target* b = new Target(&settings2, b_label);
292 b->set_output_type(Target::EXECUTABLE);
293 b->gen_deps().push_back(LabelTargetPair(c_label));
294 builder_.ItemDefined(std::unique_ptr<Item>(b));
295
296 Target* a = new Target(&settings_, a_label);
297 a->set_output_type(Target::EXECUTABLE);
298 a->private_deps().push_back(LabelTargetPair(b_label));
299 builder_.ItemDefined(std::unique_ptr<Item>(a));
300
301 // At this point, "should generate" should have propogated to C which should
302 // request for D to be loaded
303 EXPECT_TRUE(loader_->HasLoadedOnce(SourceFile("//d/BUILD.gn")));
304
305 Target* d = new Target(&settings2, d_label);
306 d->set_output_type(Target::EXECUTABLE);
307 builder_.ItemDefined(std::unique_ptr<Item>(d));
308
309 BuilderRecord* a_record = builder_.GetRecord(a_label);
310 BuilderRecord* b_record = builder_.GetRecord(b_label);
311 BuilderRecord* c_record = builder_.GetRecord(c_label);
312 BuilderRecord* d_record = builder_.GetRecord(d_label);
313 EXPECT_TRUE(a_record->should_generate());
314 EXPECT_TRUE(b_record->should_generate());
315 EXPECT_TRUE(c_record->should_generate());
316 EXPECT_TRUE(d_record->should_generate());
317 }
318
319 // Test that circular dependencies between gen_deps and deps are allowed
TEST_F(BuilderTest,GenDepsCircle)320 TEST_F(BuilderTest, GenDepsCircle) {
321 DefineToolchain();
322 Settings settings2(&build_settings_, "alternate/");
323 Label alt_tc(SourceDir("//tc/"), "alternate");
324 settings2.set_toolchain_label(alt_tc);
325 Toolchain* tc2 = new Toolchain(&settings2, alt_tc);
326 TestWithScope::SetupToolchain(tc2);
327 builder_.ItemDefined(std::unique_ptr<Item>(tc2));
328
329 // A is in the default toolchain and lists B as a gen_dep
330 // B is in an alternate toolchain and lists A as a normal dep
331 Label a_label(SourceDir("//a/"), "a", settings_.toolchain_label().dir(),
332 settings_.toolchain_label().name());
333 Label b_label(SourceDir("//b/"), "b", alt_tc.dir(), alt_tc.name());
334
335 Target* a = new Target(&settings_, a_label);
336 a->gen_deps().push_back(LabelTargetPair(b_label));
337 a->set_output_type(Target::EXECUTABLE);
338 builder_.ItemDefined(std::unique_ptr<Item>(a));
339
340 Target* b = new Target(&settings2, b_label);
341 b->private_deps().push_back(LabelTargetPair(a_label));
342 b->set_output_type(Target::EXECUTABLE);
343 builder_.ItemDefined(std::unique_ptr<Item>(b));
344
345 Err err;
346 EXPECT_TRUE(builder_.CheckForBadItems(&err));
347 BuilderRecord* b_record = builder_.GetRecord(b_label);
348 EXPECT_TRUE(b_record->should_generate());
349 }
350
351 // Tests that configs applied to a config get loaded (bug 536844).
TEST_F(BuilderTest,ConfigLoad)352 TEST_F(BuilderTest, ConfigLoad) {
353 SourceDir toolchain_dir = settings_.toolchain_label().dir();
354 std::string toolchain_name = settings_.toolchain_label().name();
355
356 // Construct a dependency chain: A -> B -> C. Define A first with a
357 // forward-reference to B, then C, then B to test the different orders that
358 // the dependencies are hooked up.
359 Label a_label(SourceDir("//a/"), "a", toolchain_dir, toolchain_name);
360 Label b_label(SourceDir("//b/"), "b", toolchain_dir, toolchain_name);
361 Label c_label(SourceDir("//c/"), "c", toolchain_dir, toolchain_name);
362
363 // The builder will take ownership of the pointers.
364 Config* a = new Config(&settings_, a_label);
365 a->configs().push_back(LabelConfigPair(b_label));
366 builder_.ItemDefined(std::unique_ptr<Item>(a));
367
368 // Should have requested that B is loaded.
369 EXPECT_TRUE(loader_->HasLoadedOne(SourceFile("//b/BUILD.gn")));
370 }
371
372 } // namespace gn_builder_unittest
373