• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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