• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 <ostream>
6 #include <vector>
7 
8 #include "gn/config.h"
9 #include "gn/header_checker.h"
10 #include "gn/scheduler.h"
11 #include "gn/target.h"
12 #include "gn/test_with_scheduler.h"
13 #include "gn/test_with_scope.h"
14 #include "util/test/test.h"
15 
16 namespace {
17 
18 class HeaderCheckerTest : public TestWithScheduler {
19  public:
HeaderCheckerTest()20   HeaderCheckerTest()
21       : a_(setup_.settings(), Label(SourceDir("//a/"), "a")),
22         b_(setup_.settings(), Label(SourceDir("//b/"), "b")),
23         c_(setup_.settings(), Label(SourceDir("//c/"), "c")),
24         d_(setup_.settings(), Label(SourceDir("//d/"), "d")) {
25     a_.set_output_type(Target::SOURCE_SET);
26     b_.set_output_type(Target::SOURCE_SET);
27     c_.set_output_type(Target::SOURCE_SET);
28     d_.set_output_type(Target::SOURCE_SET);
29 
30     Err err;
31     a_.SetToolchain(setup_.toolchain(), &err);
32     b_.SetToolchain(setup_.toolchain(), &err);
33     c_.SetToolchain(setup_.toolchain(), &err);
34     d_.SetToolchain(setup_.toolchain(), &err);
35 
36     a_.public_deps().push_back(LabelTargetPair(&b_));
37     b_.public_deps().push_back(LabelTargetPair(&c_));
38 
39     // Start with all public visibility.
40     a_.visibility().SetPublic();
41     b_.visibility().SetPublic();
42     c_.visibility().SetPublic();
43     d_.visibility().SetPublic();
44 
45     d_.OnResolved(&err);
46     c_.OnResolved(&err);
47     b_.OnResolved(&err);
48     a_.OnResolved(&err);
49 
50     targets_.push_back(&a_);
51     targets_.push_back(&b_);
52     targets_.push_back(&c_);
53     targets_.push_back(&d_);
54   }
55 
56  protected:
CreateChecker()57   scoped_refptr<HeaderChecker> CreateChecker() {
58     bool check_generated = false;
59     bool check_system = true;
60     return base::MakeRefCounted<HeaderChecker>(
61         setup_.build_settings(), targets_, check_generated, check_system);
62   }
63 
64   TestWithScope setup_;
65 
66   // Some headers that are automatically set up with a public dependency chain.
67   // a -> b -> c. D is unconnected.
68   Target a_;
69   Target b_;
70   Target c_;
71   Target d_;
72 
73   std::vector<const Target*> targets_;
74 };
75 
76 }  // namespace
77 
PrintTo(const SourceFile & source_file,::std::ostream * os)78 void PrintTo(const SourceFile& source_file, ::std::ostream* os) {
79   *os << source_file.value();
80 }
81 
TEST_F(HeaderCheckerTest,IsDependencyOf)82 TEST_F(HeaderCheckerTest, IsDependencyOf) {
83   auto checker = CreateChecker();
84 
85   // Add a target P ("private") that privately depends on C, and hook up the
86   // chain so that A -> P -> C. A will depend on C via two different paths.
87   Err err;
88   Target p(setup_.settings(), Label(SourceDir("//p/"), "p"));
89   p.set_output_type(Target::SOURCE_SET);
90   p.SetToolchain(setup_.toolchain(), &err);
91   EXPECT_FALSE(err.has_error());
92   p.private_deps().push_back(LabelTargetPair(&c_));
93   p.visibility().SetPublic();
94   p.OnResolved(&err);
95 
96   a_.public_deps().push_back(LabelTargetPair(&p));
97 
98   // A does not depend on itself.
99   bool is_permitted = false;
100   HeaderChecker::Chain chain;
101   EXPECT_FALSE(checker->IsDependencyOf(&a_, &a_, &chain, &is_permitted));
102 
103   // A depends publicly on B.
104   chain.clear();
105   is_permitted = false;
106   EXPECT_TRUE(checker->IsDependencyOf(&b_, &a_, &chain, &is_permitted));
107   ASSERT_EQ(2u, chain.size());
108   EXPECT_EQ(HeaderChecker::ChainLink(&b_, true), chain[0]);
109   EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[1]);
110   EXPECT_TRUE(is_permitted);
111 
112   // A indirectly depends on C. The "public" dependency path through B should
113   // be identified.
114   chain.clear();
115   is_permitted = false;
116   EXPECT_TRUE(checker->IsDependencyOf(&c_, &a_, &chain, &is_permitted));
117   ASSERT_EQ(3u, chain.size());
118   EXPECT_EQ(HeaderChecker::ChainLink(&c_, true), chain[0]);
119   EXPECT_EQ(HeaderChecker::ChainLink(&b_, true), chain[1]);
120   EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[2]);
121   EXPECT_TRUE(is_permitted);
122 
123   // C does not depend on A.
124   chain.clear();
125   is_permitted = false;
126   EXPECT_FALSE(checker->IsDependencyOf(&a_, &c_, &chain, &is_permitted));
127   EXPECT_TRUE(chain.empty());
128   EXPECT_FALSE(is_permitted);
129 
130   // Remove the B -> C public dependency, leaving P's private dep on C the only
131   // path from A to C. This should now be found.
132   chain.clear();
133   EXPECT_EQ(&c_, b_.public_deps()[0].ptr);  // Validate it's the right one.
134   b_.public_deps().erase(b_.public_deps().begin());
135   EXPECT_TRUE(checker->IsDependencyOf(&c_, &a_, &chain, &is_permitted));
136   EXPECT_EQ(3u, chain.size());
137   EXPECT_EQ(HeaderChecker::ChainLink(&c_, false), chain[0]);
138   EXPECT_EQ(HeaderChecker::ChainLink(&p, true), chain[1]);
139   EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[2]);
140   EXPECT_FALSE(is_permitted);
141 
142   // P privately depends on C. That dependency should be OK since it's only
143   // one hop.
144   chain.clear();
145   is_permitted = false;
146   EXPECT_TRUE(checker->IsDependencyOf(&c_, &p, &chain, &is_permitted));
147   ASSERT_EQ(2u, chain.size());
148   EXPECT_EQ(HeaderChecker::ChainLink(&c_, false), chain[0]);
149   EXPECT_EQ(HeaderChecker::ChainLink(&p, true), chain[1]);
150   EXPECT_TRUE(is_permitted);
151 }
152 
TEST_F(HeaderCheckerTest,CheckInclude)153 TEST_F(HeaderCheckerTest, CheckInclude) {
154   InputFile input_file(SourceFile("//some_file.cc"));
155   input_file.SetContents(std::string());
156   LocationRange range;  // Dummy value.
157 
158   // Add a disconnected target d with a header to check that you have to have
159   // to depend on a target listing a header.
160   SourceFile d_header("//d_header.h");
161   d_.sources().push_back(SourceFile(d_header));
162 
163   // Add a header on B and say everything in B is public.
164   SourceFile b_public("//b_public.h");
165   b_.sources().push_back(b_public);
166   c_.set_all_headers_public(true);
167 
168   // Add a public and private header on C.
169   SourceFile c_public("//c_public.h");
170   SourceFile c_private("//c_private.h");
171   c_.sources().push_back(c_private);
172   c_.public_headers().push_back(c_public);
173   c_.set_all_headers_public(false);
174 
175   // Create another toolchain.
176   Settings other_settings(setup_.build_settings(), "other/");
177   Toolchain other_toolchain(&other_settings,
178                             Label(SourceDir("//toolchain/"), "other"));
179   TestWithScope::SetupToolchain(&other_toolchain);
180   other_settings.set_toolchain_label(other_toolchain.label());
181   other_settings.set_default_toolchain_label(setup_.toolchain()->label());
182 
183   // Add a target in the other toolchain with a header in it that is not
184   // connected to any targets in the main toolchain.
185   Target otc(&other_settings,
186              Label(SourceDir("//p/"), "otc", other_toolchain.label().dir(),
187                    other_toolchain.label().name()));
188   otc.set_output_type(Target::SOURCE_SET);
189   Err err;
190   EXPECT_TRUE(otc.SetToolchain(&other_toolchain, &err));
191   otc.visibility().SetPublic();
192   targets_.push_back(&otc);
193 
194   SourceFile otc_header("//otc_header.h");
195   otc.sources().push_back(otc_header);
196   EXPECT_TRUE(otc.OnResolved(&err));
197 
198   auto checker = CreateChecker();
199 
200   std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
201   // A file in target A can't include a header from D because A has no
202   // dependency on D.
203   std::vector<Err> errors;
204   checker->CheckInclude(&a_, input_file, d_header, range, &no_dependency_cache,
205                         &errors);
206   EXPECT_GT(errors.size(), 0);
207 
208   // A can include the public header in B.
209   errors.clear();
210   no_dependency_cache.clear();
211   checker->CheckInclude(&a_, input_file, b_public, range, &no_dependency_cache,
212                         &errors);
213   EXPECT_EQ(errors.size(), 0);
214 
215   // Check A depending on the public and private headers in C.
216   errors.clear();
217   no_dependency_cache.clear();
218   checker->CheckInclude(&a_, input_file, c_public, range, &no_dependency_cache,
219                         &errors);
220   EXPECT_EQ(errors.size(), 0);
221   errors.clear();
222   no_dependency_cache.clear();
223   checker->CheckInclude(&a_, input_file, c_private, range, &no_dependency_cache,
224                         &errors);
225   EXPECT_GT(errors.size(), 0);
226 
227   // A can depend on a random file unknown to the build.
228   errors.clear();
229   no_dependency_cache.clear();
230   checker->CheckInclude(&a_, input_file, SourceFile("//random.h"), range,
231                         &no_dependency_cache, &errors);
232   EXPECT_EQ(errors.size(), 0);
233 
234   // A can depend on a file present only in another toolchain even with no
235   // dependency path.
236   errors.clear();
237   no_dependency_cache.clear();
238   checker->CheckInclude(&a_, input_file, otc_header, range,
239                         &no_dependency_cache, &errors);
240   EXPECT_EQ(errors.size(), 0);
241 }
242 
243 // A public chain of dependencies should always be identified first, even if
244 // it is longer than a private one.
TEST_F(HeaderCheckerTest,PublicFirst)245 TEST_F(HeaderCheckerTest, PublicFirst) {
246   // Now make a A -> Z -> D private dependency chain (one shorter than the
247   // public one to get to D).
248   Target z(setup_.settings(), Label(SourceDir("//a/"), "a"));
249   z.set_output_type(Target::SOURCE_SET);
250   Err err;
251   EXPECT_TRUE(z.SetToolchain(setup_.toolchain(), &err));
252   z.private_deps().push_back(LabelTargetPair(&d_));
253   EXPECT_TRUE(z.OnResolved(&err));
254   targets_.push_back(&z);
255 
256   a_.private_deps().push_back(LabelTargetPair(&z));
257 
258   // Check that D can be found from A, but since it's private, it will be
259   // marked as not permitted.
260   bool is_permitted = false;
261   HeaderChecker::Chain chain;
262   auto checker = CreateChecker();
263   EXPECT_TRUE(checker->IsDependencyOf(&d_, &a_, &chain, &is_permitted));
264 
265   EXPECT_FALSE(is_permitted);
266   ASSERT_EQ(3u, chain.size());
267   EXPECT_EQ(HeaderChecker::ChainLink(&d_, false), chain[0]);
268   EXPECT_EQ(HeaderChecker::ChainLink(&z, false), chain[1]);
269   EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[2]);
270 
271   // Hook up D to the existing public A -> B -> C chain to make a long one, and
272   // search for D again.
273   c_.public_deps().push_back(LabelTargetPair(&d_));
274   checker = CreateChecker();
275   chain.clear();
276   EXPECT_TRUE(checker->IsDependencyOf(&d_, &a_, &chain, &is_permitted));
277 
278   // This should have found the long public one.
279   EXPECT_TRUE(is_permitted);
280   ASSERT_EQ(4u, chain.size());
281   EXPECT_EQ(HeaderChecker::ChainLink(&d_, true), chain[0]);
282   EXPECT_EQ(HeaderChecker::ChainLink(&c_, true), chain[1]);
283   EXPECT_EQ(HeaderChecker::ChainLink(&b_, true), chain[2]);
284   EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[3]);
285 }
286 
287 // Checks that the allow_circular_includes_from list works.
TEST_F(HeaderCheckerTest,CheckIncludeAllowCircular)288 TEST_F(HeaderCheckerTest, CheckIncludeAllowCircular) {
289   InputFile input_file(SourceFile("//some_file.cc"));
290   input_file.SetContents(std::string());
291   LocationRange range;  // Dummy value.
292 
293   // Add an include file to A.
294   SourceFile a_public("//a_public.h");
295   a_.sources().push_back(a_public);
296 
297   auto checker = CreateChecker();
298 
299   // A depends on B. So B normally can't include headers from A.
300   std::vector<Err> errors;
301   std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
302   checker->CheckInclude(&b_, input_file, a_public, range, &no_dependency_cache,
303                         &errors);
304   EXPECT_GT(errors.size(), 0);
305 
306   // Add an allow_circular_includes_from on A that lists B.
307   a_.allow_circular_includes_from().insert(b_.label());
308 
309   // Now the include from B to A should be allowed.
310   errors.clear();
311   no_dependency_cache.clear();
312   checker->CheckInclude(&b_, input_file, a_public, range, &no_dependency_cache,
313                         &errors);
314   EXPECT_EQ(errors.size(), 0);
315 }
316 
317 // Check that CheckInclude() support swift targets.
TEST_F(HeaderCheckerTest,CheckIncludeSwiftModule)318 TEST_F(HeaderCheckerTest, CheckIncludeSwiftModule) {
319   // A target S that build a swift module.
320   Target s(setup_.settings(), Label(SourceDir("//s"), "s"));
321   s.set_output_type(Target::SOURCE_SET);
322 
323   Err err;
324   s.SetToolchain(setup_.toolchain(), &err);
325   ASSERT_FALSE(err.has_error());
326 
327   const SourceFile bridge_header("//bridge.h");
328   const SourceFile generated_header("//out/Debug/gen/s/s.h");
329 
330   // S contains Swift sources and has bridge header set.
331   s.swift_values().module_name() = "s";
332   s.swift_values().bridge_header() = SourceFile("//bridge.h");
333   s.sources().push_back(SourceFile("//some_file.swift"));
334   s.source_types_used().Set(SourceFile::SOURCE_SWIFT);
335   s.visibility().SetPublic();
336 
337   s.OnResolved(&err);
338   ASSERT_FALSE(err.has_error());
339   targets_.push_back(&s);
340 
341   auto checker = CreateChecker();
342 
343   InputFile input_file(SourceFile("//some_file.cc"));
344   input_file.SetContents(std::string());
345   LocationRange range;  // Dummy value.
346 
347   std::vector<Err> errors;
348   std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
349 
350   // Check that unrelated target D cannot include header generated by S.
351   errors.clear();
352   no_dependency_cache.clear();
353   checker->CheckInclude(&d_, input_file, generated_header, range,
354                         &no_dependency_cache, &errors);
355   EXPECT_GT(errors.size(), 0);
356 
357   // Check that unrelated target D cannot include S's bridge header.
358   errors.clear();
359   no_dependency_cache.clear();
360   checker->CheckInclude(&d_, input_file, bridge_header, range,
361                         &no_dependency_cache, &errors);
362   EXPECT_GT(errors.size(), 0);
363 }
364 
TEST_F(HeaderCheckerTest,SourceFileForInclude)365 TEST_F(HeaderCheckerTest, SourceFileForInclude) {
366   using base::FilePath;
367   const std::vector<SourceDir> kIncludeDirs = {
368       SourceDir("/c/custom_include/"), SourceDir("//"), SourceDir("//subdir")};
369   a_.sources().push_back(SourceFile("//lib/header1.h"));
370   b_.sources().push_back(SourceFile("/c/custom_include/header2.h"));
371   d_.sources().push_back(SourceFile("/d/subdir/header3.h"));
372 
373   InputFile dummy_input_file(SourceFile("/d/subdir/some_file.cc"));
374   dummy_input_file.SetContents(std::string());
375 
376   auto checker = CreateChecker();
377   {
378     Err err;
379     IncludeStringWithLocation include;
380     include.contents = "lib/header1.h";
381     SourceFile source_file = checker->SourceFileForInclude(
382         include, kIncludeDirs, dummy_input_file, &err);
383     EXPECT_FALSE(err.has_error());
384     EXPECT_EQ(SourceFile("//lib/header1.h"), source_file);
385   }
386 
387   {
388     Err err;
389     IncludeStringWithLocation include;
390     include.contents = "header2.h";
391     SourceFile source_file = checker->SourceFileForInclude(
392         include, kIncludeDirs, dummy_input_file, &err);
393     EXPECT_FALSE(err.has_error());
394     EXPECT_EQ(SourceFile("/c/custom_include/header2.h"), source_file);
395   }
396 
397   // A non system style include should find a header file in the same directory
398   // as the source file, regardless of include dirs.
399   {
400     Err err;
401     IncludeStringWithLocation include;
402     include.contents = "header3.h";
403     include.system_style_include = false;
404     SourceFile source_file = checker->SourceFileForInclude(
405         include, kIncludeDirs, dummy_input_file, &err);
406     EXPECT_FALSE(err.has_error());
407     EXPECT_EQ(SourceFile("/d/subdir/header3.h"), source_file);
408   }
409 
410   // A system style include should *not* find a header file in the same
411   // directory as the source file if that directory is not in the include dirs.
412   {
413     Err err;
414     IncludeStringWithLocation include;
415     include.contents = "header3.h";
416     include.system_style_include = true;
417     SourceFile source_file = checker->SourceFileForInclude(
418         include, kIncludeDirs, dummy_input_file, &err);
419     EXPECT_TRUE(source_file.is_null());
420     EXPECT_FALSE(err.has_error());
421   }
422 }
423 
TEST_F(HeaderCheckerTest,SourceFileForInclude_FileNotFound)424 TEST_F(HeaderCheckerTest, SourceFileForInclude_FileNotFound) {
425   using base::FilePath;
426   const char kFileContents[] = "Some dummy contents";
427   const std::vector<SourceDir> kIncludeDirs = {SourceDir("//")};
428   auto checker = CreateChecker();
429 
430   Err err;
431   InputFile input_file(SourceFile("//input.cc"));
432   input_file.SetContents(std::string(kFileContents));
433 
434   IncludeStringWithLocation include;
435   include.contents = "header.h";
436   SourceFile source_file =
437       checker->SourceFileForInclude(include, kIncludeDirs, input_file, &err);
438   EXPECT_TRUE(source_file.is_null());
439   EXPECT_FALSE(err.has_error());
440 }
441 
TEST_F(HeaderCheckerTest,Friend)442 TEST_F(HeaderCheckerTest, Friend) {
443   // Note: we have a public dependency chain A -> B -> C set up already.
444   InputFile input_file(SourceFile("//some_file.cc"));
445   input_file.SetContents(std::string());
446   LocationRange range;  // Dummy value.
447 
448   // Add a private header on C.
449   SourceFile c_private("//c_private.h");
450   c_.sources().push_back(c_private);
451   c_.set_all_headers_public(false);
452 
453   // List A as a friend of C.
454   Err err;
455   c_.friends().push_back(LabelPattern::GetPattern(
456       SourceDir("//"), std::string_view(), Value(nullptr, "//a:*"), &err));
457   ASSERT_FALSE(err.has_error());
458 
459   // Must be after setting everything up for it to find the files.
460   auto checker = CreateChecker();
461 
462   // B should not be allowed to include C's private header.
463   std::vector<Err> errors;
464   std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
465   checker->CheckInclude(&b_, input_file, c_private, range, &no_dependency_cache,
466                         &errors);
467   EXPECT_GT(errors.size(), 0);
468 
469   // A should be able to because of the friend declaration.
470   errors.clear();
471   no_dependency_cache.clear();
472   checker->CheckInclude(&a_, input_file, c_private, range, &no_dependency_cache,
473                         &errors);
474   EXPECT_EQ(errors.size(), 0);
475 }
476