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 "tools/gn/builder.h"
6 #include "tools/gn/config.h"
7 #include "tools/gn/loader.h"
8 #include "tools/gn/target.h"
9 #include "tools/gn/test_with_scope.h"
10 #include "tools/gn/toolchain.h"
11 #include "util/test/test.h"
12
13 namespace gn_builder_unittest {
14
15 class MockLoader : public Loader {
16 public:
17 MockLoader() = default;
18
19 // Loader implementation:
Load(const SourceFile & file,const LocationRange & origin,const Label & toolchain_name)20 void Load(const SourceFile& file,
21 const LocationRange& origin,
22 const Label& toolchain_name) override {
23 files_.push_back(file);
24 }
ToolchainLoaded(const Toolchain * toolchain)25 void ToolchainLoaded(const Toolchain* toolchain) override {}
GetDefaultToolchain() const26 Label GetDefaultToolchain() const override { return Label(); }
GetToolchainSettings(const Label & label) const27 const Settings* GetToolchainSettings(const Label& label) const override {
28 return nullptr;
29 }
30
HasLoadedNone() const31 bool HasLoadedNone() const { return files_.empty(); }
32
33 // Returns true if one/two loads have been requested and they match the given
34 // file(s). This will clear the records so it will be empty for the next call.
HasLoadedOne(const SourceFile & file)35 bool HasLoadedOne(const SourceFile& file) {
36 if (files_.size() != 1u) {
37 files_.clear();
38 return false;
39 }
40 bool match = (files_[0] == file);
41 files_.clear();
42 return match;
43 }
HasLoadedTwo(const SourceFile & a,const SourceFile & b)44 bool HasLoadedTwo(const SourceFile& a, const SourceFile& b) {
45 if (files_.size() != 2u) {
46 files_.clear();
47 return false;
48 }
49
50 bool match = ((files_[0] == a && files_[1] == b) ||
51 (files_[0] == b && files_[1] == a));
52 files_.clear();
53 return match;
54 }
55
56 private:
57 ~MockLoader() override = default;
58
59 std::vector<SourceFile> files_;
60 };
61
62 class BuilderTest : public testing::Test {
63 public:
BuilderTest()64 BuilderTest()
65 : loader_(new MockLoader),
66 builder_(loader_.get()),
67 settings_(&build_settings_, std::string()),
68 scope_(&settings_) {
69 build_settings_.SetBuildDir(SourceDir("//out/"));
70 settings_.set_toolchain_label(Label(SourceDir("//tc/"), "default"));
71 settings_.set_default_toolchain_label(settings_.toolchain_label());
72 }
73
DefineToolchain()74 Toolchain* DefineToolchain() {
75 Toolchain* tc = new Toolchain(&settings_, settings_.toolchain_label());
76 TestWithScope::SetupToolchain(tc);
77 builder_.ItemDefined(std::unique_ptr<Item>(tc));
78 return tc;
79 }
80
81 protected:
82 scoped_refptr<MockLoader> loader_;
83 Builder builder_;
84 BuildSettings build_settings_;
85 Settings settings_;
86 Scope scope_;
87 };
88
TEST_F(BuilderTest,BasicDeps)89 TEST_F(BuilderTest, BasicDeps) {
90 SourceDir toolchain_dir = settings_.toolchain_label().dir();
91 std::string toolchain_name = settings_.toolchain_label().name();
92
93 // Construct a dependency chain: A -> B -> C. Define A first with a
94 // forward-reference to B, then C, then B to test the different orders that
95 // the dependencies are hooked up.
96 Label a_label(SourceDir("//a/"), "a", toolchain_dir, toolchain_name);
97 Label b_label(SourceDir("//b/"), "b", toolchain_dir, toolchain_name);
98 Label c_label(SourceDir("//c/"), "c", toolchain_dir, toolchain_name);
99
100 // The builder will take ownership of the pointers.
101 Target* a = new Target(&settings_, a_label);
102 a->public_deps().push_back(LabelTargetPair(b_label));
103 a->set_output_type(Target::EXECUTABLE);
104 builder_.ItemDefined(std::unique_ptr<Item>(a));
105
106 // Should have requested that B and the toolchain is loaded.
107 EXPECT_TRUE(loader_->HasLoadedTwo(SourceFile("//tc/BUILD.gn"),
108 SourceFile("//b/BUILD.gn")));
109
110 // Define the toolchain.
111 DefineToolchain();
112 BuilderRecord* toolchain_record =
113 builder_.GetRecord(settings_.toolchain_label());
114 ASSERT_TRUE(toolchain_record);
115 EXPECT_EQ(BuilderRecord::ITEM_TOOLCHAIN, toolchain_record->type());
116
117 // A should be unresolved with an item
118 BuilderRecord* a_record = builder_.GetRecord(a_label);
119 EXPECT_TRUE(a_record->item());
120 EXPECT_FALSE(a_record->resolved());
121 EXPECT_FALSE(a_record->can_resolve());
122
123 // B should be unresolved, have no item, and no deps.
124 BuilderRecord* b_record = builder_.GetRecord(b_label);
125 EXPECT_FALSE(b_record->item());
126 EXPECT_FALSE(b_record->resolved());
127 EXPECT_FALSE(b_record->can_resolve());
128 EXPECT_TRUE(b_record->all_deps().empty());
129
130 // A should have two deps: B and the toolchain. Only B should be unresolved.
131 EXPECT_EQ(2u, a_record->all_deps().size());
132 EXPECT_EQ(1u, a_record->unresolved_deps().size());
133 EXPECT_NE(a_record->all_deps().end(),
134 a_record->all_deps().find(toolchain_record));
135 EXPECT_NE(a_record->all_deps().end(), a_record->all_deps().find(b_record));
136 EXPECT_NE(a_record->unresolved_deps().end(),
137 a_record->unresolved_deps().find(b_record));
138
139 // B should be marked as having A waiting on it.
140 EXPECT_EQ(1u, b_record->waiting_on_resolution().size());
141 EXPECT_NE(b_record->waiting_on_resolution().end(),
142 b_record->waiting_on_resolution().find(a_record));
143
144 // Add the C target.
145 Target* c = new Target(&settings_, c_label);
146 c->set_output_type(Target::STATIC_LIBRARY);
147 c->visibility().SetPublic();
148 builder_.ItemDefined(std::unique_ptr<Item>(c));
149
150 // C only depends on the already-loaded toolchain so we shouldn't have
151 // requested anything else.
152 EXPECT_TRUE(loader_->HasLoadedNone());
153
154 // Add the B target.
155 Target* b = new Target(&settings_, b_label);
156 a->public_deps().push_back(LabelTargetPair(c_label));
157 b->set_output_type(Target::SHARED_LIBRARY);
158 b->visibility().SetPublic();
159 builder_.ItemDefined(std::unique_ptr<Item>(b));
160
161 // B depends only on the already-loaded C and toolchain so we shouldn't have
162 // requested anything else.
163 EXPECT_TRUE(loader_->HasLoadedNone());
164
165 // All targets should now be resolved.
166 BuilderRecord* c_record = builder_.GetRecord(c_label);
167 EXPECT_TRUE(a_record->resolved());
168 EXPECT_TRUE(b_record->resolved());
169 EXPECT_TRUE(c_record->resolved());
170
171 EXPECT_TRUE(a_record->unresolved_deps().empty());
172 EXPECT_TRUE(b_record->unresolved_deps().empty());
173 EXPECT_TRUE(c_record->unresolved_deps().empty());
174
175 EXPECT_TRUE(a_record->waiting_on_resolution().empty());
176 EXPECT_TRUE(b_record->waiting_on_resolution().empty());
177 EXPECT_TRUE(c_record->waiting_on_resolution().empty());
178 }
179
180 // Tests that the "should generate" flag is set and propagated properly.
TEST_F(BuilderTest,ShouldGenerate)181 TEST_F(BuilderTest, ShouldGenerate) {
182 DefineToolchain();
183
184 // Define a secondary toolchain.
185 Settings settings2(&build_settings_, "secondary/");
186 Label toolchain_label2(SourceDir("//tc/"), "secondary");
187 settings2.set_toolchain_label(toolchain_label2);
188 Toolchain* tc2 = new Toolchain(&settings2, toolchain_label2);
189 TestWithScope::SetupToolchain(tc2);
190 builder_.ItemDefined(std::unique_ptr<Item>(tc2));
191
192 // Construct a dependency chain: A -> B. A is in the default toolchain, B
193 // is not.
194 Label a_label(SourceDir("//foo/"), "a", settings_.toolchain_label().dir(),
195 "a");
196 Label b_label(SourceDir("//foo/"), "b", toolchain_label2.dir(),
197 toolchain_label2.name());
198
199 // First define B.
200 Target* b = new Target(&settings2, b_label);
201 b->visibility().SetPublic();
202 b->set_output_type(Target::EXECUTABLE);
203 builder_.ItemDefined(std::unique_ptr<Item>(b));
204
205 // B should not be marked generated by default.
206 BuilderRecord* b_record = builder_.GetRecord(b_label);
207 EXPECT_FALSE(b_record->should_generate());
208
209 // Define A with a dependency on B.
210 Target* a = new Target(&settings_, a_label);
211 a->public_deps().push_back(LabelTargetPair(b_label));
212 a->set_output_type(Target::EXECUTABLE);
213 builder_.ItemDefined(std::unique_ptr<Item>(a));
214
215 // A should have the generate bit set since it's in the default toolchain.
216 BuilderRecord* a_record = builder_.GetRecord(a_label);
217 EXPECT_TRUE(a_record->should_generate());
218
219 // It should have gotten pushed to B.
220 EXPECT_TRUE(b_record->should_generate());
221 }
222
223 // Tests that configs applied to a config get loaded (bug 536844).
TEST_F(BuilderTest,ConfigLoad)224 TEST_F(BuilderTest, ConfigLoad) {
225 SourceDir toolchain_dir = settings_.toolchain_label().dir();
226 std::string toolchain_name = settings_.toolchain_label().name();
227
228 // Construct a dependency chain: A -> B -> C. Define A first with a
229 // forward-reference to B, then C, then B to test the different orders that
230 // the dependencies are hooked up.
231 Label a_label(SourceDir("//a/"), "a", toolchain_dir, toolchain_name);
232 Label b_label(SourceDir("//b/"), "b", toolchain_dir, toolchain_name);
233 Label c_label(SourceDir("//c/"), "c", toolchain_dir, toolchain_name);
234
235 // The builder will take ownership of the pointers.
236 Config* a = new Config(&settings_, a_label);
237 a->configs().push_back(LabelConfigPair(b_label));
238 builder_.ItemDefined(std::unique_ptr<Item>(a));
239
240 // Should have requested that B is loaded.
241 EXPECT_TRUE(loader_->HasLoadedOne(SourceFile("//b/BUILD.gn")));
242 }
243
244 } // namespace gn_builder_unittest
245