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 = a_record->GetSortedUnresolvedDeps();
215 EXPECT_EQ(3u, a_unresolved.size()) << a_unresolved.size();
216 EXPECT_EQ(b_record, a_unresolved[0]);
217 EXPECT_EQ(c_record, a_unresolved[1]);
218 EXPECT_EQ(d_record, a_unresolved[2]);
219 }
220
221 // Tests that the "should generate" flag is set and propagated properly.
TEST_F(BuilderTest,ShouldGenerate)222 TEST_F(BuilderTest, ShouldGenerate) {
223 DefineToolchain();
224
225 // Define a secondary toolchain.
226 Settings settings2(&build_settings_, "secondary/");
227 Label toolchain_label2(SourceDir("//tc/"), "secondary");
228 settings2.set_toolchain_label(toolchain_label2);
229 Toolchain* tc2 = new Toolchain(&settings2, toolchain_label2);
230 TestWithScope::SetupToolchain(tc2);
231 builder_.ItemDefined(std::unique_ptr<Item>(tc2));
232
233 // Construct a dependency chain: A -> B. A is in the default toolchain, B
234 // is not.
235 Label a_label(SourceDir("//foo/"), "a", settings_.toolchain_label().dir(),
236 "a");
237 Label b_label(SourceDir("//foo/"), "b", toolchain_label2.dir(),
238 toolchain_label2.name());
239
240 // First define B.
241 Target* b = new Target(&settings2, b_label);
242 b->visibility().SetPublic();
243 b->set_output_type(Target::EXECUTABLE);
244 builder_.ItemDefined(std::unique_ptr<Item>(b));
245
246 // B should not be marked generated by default.
247 BuilderRecord* b_record = builder_.GetRecord(b_label);
248 EXPECT_FALSE(b_record->should_generate());
249
250 // Define A with a dependency on B.
251 Target* a = new Target(&settings_, a_label);
252 a->public_deps().push_back(LabelTargetPair(b_label));
253 a->set_output_type(Target::EXECUTABLE);
254 builder_.ItemDefined(std::unique_ptr<Item>(a));
255
256 // A should have the generate bit set since it's in the default toolchain.
257 BuilderRecord* a_record = builder_.GetRecord(a_label);
258 EXPECT_TRUE(a_record->should_generate());
259
260 // It should have gotten pushed to B.
261 EXPECT_TRUE(b_record->should_generate());
262 }
263
264 // Test that "gen_deps" forces targets to be generated.
TEST_F(BuilderTest,GenDeps)265 TEST_F(BuilderTest, GenDeps) {
266 DefineToolchain();
267
268 // Define another toolchain
269 Settings settings2(&build_settings_, "alternate/");
270 Label alt_tc(SourceDir("//tc/"), "alternate");
271 settings2.set_toolchain_label(alt_tc);
272 Toolchain* tc2 = new Toolchain(&settings2, alt_tc);
273 TestWithScope::SetupToolchain(tc2);
274 builder_.ItemDefined(std::unique_ptr<Item>(tc2));
275
276 // Construct the dependency chain A -> B -gen-> C -gen-> D where A is the only
277 // target in the default toolchain. This should cause all 4 targets to be
278 // generated.
279 Label a_label(SourceDir("//a/"), "a", settings_.toolchain_label().dir(),
280 settings_.toolchain_label().name());
281 Label b_label(SourceDir("//b/"), "b", alt_tc.dir(), alt_tc.name());
282 Label c_label(SourceDir("//c/"), "c", alt_tc.dir(), alt_tc.name());
283 Label d_label(SourceDir("//d/"), "d", alt_tc.dir(), alt_tc.name());
284
285 Target* c = new Target(&settings2, c_label);
286 c->set_output_type(Target::EXECUTABLE);
287 c->gen_deps().push_back(LabelTargetPair(d_label));
288 builder_.ItemDefined(std::unique_ptr<Item>(c));
289
290 Target* b = new Target(&settings2, b_label);
291 b->set_output_type(Target::EXECUTABLE);
292 b->gen_deps().push_back(LabelTargetPair(c_label));
293 builder_.ItemDefined(std::unique_ptr<Item>(b));
294
295 Target* a = new Target(&settings_, a_label);
296 a->set_output_type(Target::EXECUTABLE);
297 a->private_deps().push_back(LabelTargetPair(b_label));
298 builder_.ItemDefined(std::unique_ptr<Item>(a));
299
300 // At this point, "should generate" should have propogated to C which should
301 // request for D to be loaded
302 EXPECT_TRUE(loader_->HasLoadedOnce(SourceFile("//d/BUILD.gn")));
303
304 Target* d = new Target(&settings2, d_label);
305 d->set_output_type(Target::EXECUTABLE);
306 builder_.ItemDefined(std::unique_ptr<Item>(d));
307
308 BuilderRecord* a_record = builder_.GetRecord(a_label);
309 BuilderRecord* b_record = builder_.GetRecord(b_label);
310 BuilderRecord* c_record = builder_.GetRecord(c_label);
311 BuilderRecord* d_record = builder_.GetRecord(d_label);
312 EXPECT_TRUE(a_record->should_generate());
313 EXPECT_TRUE(b_record->should_generate());
314 EXPECT_TRUE(c_record->should_generate());
315 EXPECT_TRUE(d_record->should_generate());
316 }
317
318 // Test that circular dependencies between gen_deps and deps are allowed
TEST_F(BuilderTest,GenDepsCircle)319 TEST_F(BuilderTest, GenDepsCircle) {
320 DefineToolchain();
321 Settings settings2(&build_settings_, "alternate/");
322 Label alt_tc(SourceDir("//tc/"), "alternate");
323 settings2.set_toolchain_label(alt_tc);
324 Toolchain* tc2 = new Toolchain(&settings2, alt_tc);
325 TestWithScope::SetupToolchain(tc2);
326 builder_.ItemDefined(std::unique_ptr<Item>(tc2));
327
328 // A is in the default toolchain and lists B as a gen_dep
329 // B is in an alternate toolchain and lists A as a normal dep
330 Label a_label(SourceDir("//a/"), "a", settings_.toolchain_label().dir(),
331 settings_.toolchain_label().name());
332 Label b_label(SourceDir("//b/"), "b", alt_tc.dir(), alt_tc.name());
333
334 Target* a = new Target(&settings_, a_label);
335 a->gen_deps().push_back(LabelTargetPair(b_label));
336 a->set_output_type(Target::EXECUTABLE);
337 builder_.ItemDefined(std::unique_ptr<Item>(a));
338
339 Target* b = new Target(&settings2, b_label);
340 b->private_deps().push_back(LabelTargetPair(a_label));
341 b->set_output_type(Target::EXECUTABLE);
342 builder_.ItemDefined(std::unique_ptr<Item>(b));
343
344 Err err;
345 EXPECT_TRUE(builder_.CheckForBadItems(&err));
346 BuilderRecord* b_record = builder_.GetRecord(b_label);
347 EXPECT_TRUE(b_record->should_generate());
348 }
349
350 // Tests that configs applied to a config get loaded (bug 536844).
TEST_F(BuilderTest,ConfigLoad)351 TEST_F(BuilderTest, ConfigLoad) {
352 SourceDir toolchain_dir = settings_.toolchain_label().dir();
353 std::string toolchain_name = settings_.toolchain_label().name();
354
355 // Construct a dependency chain: A -> B -> C. Define A first with a
356 // forward-reference to B, then C, then B to test the different orders that
357 // the dependencies are hooked up.
358 Label a_label(SourceDir("//a/"), "a", toolchain_dir, toolchain_name);
359 Label b_label(SourceDir("//b/"), "b", toolchain_dir, toolchain_name);
360 Label c_label(SourceDir("//c/"), "c", toolchain_dir, toolchain_name);
361
362 // The builder will take ownership of the pointers.
363 Config* a = new Config(&settings_, a_label);
364 a->configs().push_back(LabelConfigPair(b_label));
365 builder_.ItemDefined(std::unique_ptr<Item>(a));
366
367 // Should have requested that B is loaded.
368 EXPECT_TRUE(loader_->HasLoadedOne(SourceFile("//b/BUILD.gn")));
369 }
370
371 } // namespace gn_builder_unittest
372