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 "tools/gn/header_checker.h"
6
7#include <algorithm>
8
9#include "base/bind.h"
10#include "base/files/file_util.h"
11#include "base/message_loop/message_loop.h"
12#include "base/strings/string_util.h"
13#include "base/threading/sequenced_worker_pool.h"
14#include "tools/gn/build_settings.h"
15#include "tools/gn/builder.h"
16#include "tools/gn/c_include_iterator.h"
17#include "tools/gn/config.h"
18#include "tools/gn/err.h"
19#include "tools/gn/filesystem_utils.h"
20#include "tools/gn/scheduler.h"
21#include "tools/gn/source_file_type.h"
22#include "tools/gn/target.h"
23#include "tools/gn/trace.h"
24
25namespace {
26
27struct PublicGeneratedPair {
28  PublicGeneratedPair() : is_public(false), is_generated(false) {}
29  bool is_public;
30  bool is_generated;
31};
32
33// If the given file is in the "gen" folder, trims this so it treats the gen
34// directory as the source root:
35//   //out/Debug/gen/foo/bar.h -> //foo/bar.h
36// If the file isn't in the generated root, returns the input unchanged.
37SourceFile RemoveRootGenDirFromFile(const Target* target,
38                                    const SourceFile& file) {
39  const SourceDir& gen = target->settings()->toolchain_gen_dir();
40  if (!gen.is_null() && StartsWithASCII(file.value(), gen.value(), true))
41    return SourceFile("//" + file.value().substr(gen.value().size()));
42  return file;
43}
44
45// This class makes InputFiles on the stack as it reads files to check. When
46// we throw an error, the Err indicates a locatin which has a pointer to
47// an InputFile that must persist as long as the Err does.
48//
49// To make this work, this function creates a clone of the InputFile managed
50// by the InputFileManager so the error can refer to something that
51// persists. This means that the current file contents will live as long as
52// the program, but this is OK since we're erroring out anyway.
53LocationRange CreatePersistentRange(const InputFile& input_file,
54                                    const LocationRange& range) {
55  InputFile* clone_input_file;
56  std::vector<Token>* tokens;  // Don't care about this.
57  scoped_ptr<ParseNode>* parse_root;  // Don't care about this.
58
59  g_scheduler->input_file_manager()->AddDynamicInput(
60      input_file.name(), &clone_input_file, &tokens, &parse_root);
61  clone_input_file->SetContents(input_file.contents());
62
63  return LocationRange(Location(clone_input_file,
64                                range.begin().line_number(),
65                                range.begin().char_offset(),
66                                -1 /* TODO(scottmg) */),
67                       Location(clone_input_file,
68                                range.end().line_number(),
69                                range.end().char_offset(),
70                                -1 /* TODO(scottmg) */));
71}
72
73// Given a reverse dependency chain where the target chain[0]'s includes are
74// being used by chain[end] and not all deps are public, returns the string
75// describing the error.
76std::string GetDependencyChainPublicError(
77    const HeaderChecker::Chain& chain) {
78  std::string ret = "The target:\n  " +
79      chain[chain.size() - 1].target->label().GetUserVisibleName(false) +
80      "\nis including a file from the target:\n  " +
81      chain[0].target->label().GetUserVisibleName(false) +
82      "\n";
83
84  // Invalid chains should always be 0 (no chain) or more than two
85  // (intermediate private dependencies). 1 and 2 are impossible because a
86  // target can always include headers from itself and its direct dependents.
87  DCHECK(chain.size() != 1 && chain.size() != 2);
88  if (chain.empty()) {
89    ret += "There is no dependency chain between these targets.";
90  } else {
91    // Indirect dependency chain, print the chain.
92    ret += "\nIt's usually best to depend directly on the destination target.\n"
93        "In some cases, the destination target is considered a subcomponent\n"
94        "of an intermediate target. In this case, the intermediate target\n"
95        "should depend publicly on the destination to forward the ability\n"
96        "to include headers.\n"
97        "\n"
98        "Dependency chain (there may also be others):\n";
99
100    for (int i = static_cast<int>(chain.size()) - 1; i >= 0; i--) {
101      ret.append("  " + chain[i].target->label().GetUserVisibleName(false));
102      if (i != 0) {
103        // Identify private dependencies so the user can see where in the
104        // dependency chain things went bad. Don't list this for the first link
105        // in the chain since direct dependencies are OK, and listing that as
106        // "private" may make people feel like they need to fix it.
107        if (i == static_cast<int>(chain.size()) - 1 || chain[i - 1].is_public)
108          ret.append(" -->");
109        else
110          ret.append(" --[private]-->");
111      }
112      ret.append("\n");
113    }
114  }
115  return ret;
116}
117
118}  // namespace
119
120HeaderChecker::HeaderChecker(const BuildSettings* build_settings,
121                             const std::vector<const Target*>& targets)
122    : main_loop_(base::MessageLoop::current()),
123      build_settings_(build_settings) {
124  for (size_t i = 0; i < targets.size(); i++)
125    AddTargetToFileMap(targets[i], &file_map_);
126}
127
128HeaderChecker::~HeaderChecker() {
129}
130
131bool HeaderChecker::Run(const std::vector<const Target*>& to_check,
132                        bool force_check,
133                        std::vector<Err>* errors) {
134  if (to_check.empty()) {
135    // Check all files.
136    RunCheckOverFiles(file_map_, force_check);
137  } else {
138    // Run only over the files in the given targets.
139    FileMap files_to_check;
140    for (size_t i = 0; i < to_check.size(); i++)
141      AddTargetToFileMap(to_check[i], &files_to_check);
142    RunCheckOverFiles(files_to_check, force_check);
143  }
144
145  if (errors_.empty())
146    return true;
147  *errors = errors_;
148  return false;
149}
150
151void HeaderChecker::RunCheckOverFiles(const FileMap& files, bool force_check) {
152  if (files.empty())
153    return;
154
155  scoped_refptr<base::SequencedWorkerPool> pool(
156      new base::SequencedWorkerPool(16, "HeaderChecker"));
157  for (FileMap::const_iterator file_i = files.begin();
158       file_i != files.end(); ++file_i) {
159    const TargetVector& vect = file_i->second;
160
161    // Only check C-like source files (RC files also have includes).
162    SourceFileType type = GetSourceFileType(file_i->first);
163    if (type != SOURCE_CC && type != SOURCE_H && type != SOURCE_C &&
164        type != SOURCE_M && type != SOURCE_MM && type != SOURCE_RC)
165      continue;
166
167    // Do a first pass to find if this should be skipped. All targets including
168    // this source file must exclude it from checking, or any target
169    // must mark it as generated (for cases where one target generates a file,
170    // and another lists it as a source to compile it).
171    if (!force_check) {
172      bool check_includes = false;
173      bool is_generated = false;
174      for (size_t vect_i = 0; vect_i < vect.size(); ++vect_i) {
175        check_includes |= vect[vect_i].target->check_includes();
176        is_generated |= vect[vect_i].is_generated;
177      }
178      if (!check_includes || is_generated)
179        continue;
180    }
181
182    for (size_t vect_i = 0; vect_i < vect.size(); ++vect_i) {
183      pool->PostWorkerTaskWithShutdownBehavior(
184          FROM_HERE,
185          base::Bind(&HeaderChecker::DoWork, this,
186                     vect[vect_i].target, file_i->first),
187          base::SequencedWorkerPool::BLOCK_SHUTDOWN);
188    }
189  }
190
191  // After this call we're single-threaded again.
192  pool->Shutdown();
193}
194
195void HeaderChecker::DoWork(const Target* target, const SourceFile& file) {
196  Err err;
197  if (!CheckFile(target, file, &err)) {
198    base::AutoLock lock(lock_);
199    errors_.push_back(err);
200  }
201}
202
203// static
204void HeaderChecker::AddTargetToFileMap(const Target* target, FileMap* dest) {
205  // Files in the sources have this public bit by default.
206  bool default_public = target->all_headers_public();
207
208  std::map<SourceFile, PublicGeneratedPair> files_to_public;
209
210  // First collect the normal files, they get the default visibility. Always
211  // trim the root gen dir if it exists. This will only exist on outputs of an
212  // action, but those are often then wired into the sources of a compiled
213  // target to actually compile generated code. If you depend on the compiled
214  // target, it should be enough to be able to include the header.
215  const Target::FileList& sources = target->sources();
216  for (size_t i = 0; i < sources.size(); i++) {
217    SourceFile file = RemoveRootGenDirFromFile(target, sources[i]);
218    files_to_public[file].is_public = default_public;
219  }
220
221  // Add in the public files, forcing them to public. This may overwrite some
222  // entries, and it may add new ones.
223  const Target::FileList& public_list = target->public_headers();
224  if (default_public)
225    DCHECK(public_list.empty());  // List only used when default is not public.
226  for (size_t i = 0; i < public_list.size(); i++) {
227    SourceFile file = RemoveRootGenDirFromFile(target, public_list[i]);
228    files_to_public[file].is_public = true;
229  }
230
231  // Add in outputs from actions. These are treated as public (since if other
232  // targets can't use them, then there wouldn't be any point in outputting).
233  std::vector<SourceFile> outputs;
234  target->action_values().GetOutputsAsSourceFiles(target, &outputs);
235  for (size_t i = 0; i < outputs.size(); i++) {
236    // For generated files in the "gen" directory, add the filename to the
237    // map assuming "gen" is the source root. This means that when files include
238    // the generated header relative to there (the recommended practice), we'll
239    // find the file.
240    SourceFile output_file = RemoveRootGenDirFromFile(target, outputs[i]);
241    PublicGeneratedPair* pair = &files_to_public[output_file];
242    pair->is_public = true;
243    pair->is_generated = true;
244  }
245
246  // Add the merged list to the master list of all files.
247  for (std::map<SourceFile, PublicGeneratedPair>::const_iterator i =
248           files_to_public.begin();
249       i != files_to_public.end(); ++i) {
250    (*dest)[i->first].push_back(TargetInfo(
251        target, i->second.is_public, i->second.is_generated));
252  }
253}
254
255bool HeaderChecker::IsFileInOuputDir(const SourceFile& file) const {
256  const std::string& build_dir = build_settings_->build_dir().value();
257  return file.value().compare(0, build_dir.size(), build_dir) == 0;
258}
259
260// This current assumes all include paths are relative to the source root
261// which is generally the case for Chromium.
262//
263// A future enhancement would be to search the include path for the target
264// containing the source file containing this include and find the file to
265// handle the cases where people do weird things with the paths.
266SourceFile HeaderChecker::SourceFileForInclude(
267    const base::StringPiece& input) const {
268  std::string str("//");
269  input.AppendToString(&str);
270  return SourceFile(str);
271}
272
273bool HeaderChecker::CheckFile(const Target* from_target,
274                              const SourceFile& file,
275                              Err* err) const {
276  ScopedTrace trace(TraceItem::TRACE_CHECK_HEADER, file.value());
277
278  // Sometimes you have generated source files included as sources in another
279  // target. These won't exist at checking time. Since we require all generated
280  // files to be somewhere in the output tree, we can just check the name to
281  // see if they should be skipped.
282  if (IsFileInOuputDir(file))
283    return true;
284
285  base::FilePath path = build_settings_->GetFullPath(file);
286  std::string contents;
287  if (!base::ReadFileToString(path, &contents)) {
288    *err = Err(from_target->defined_from(), "Source file not found.",
289        "The target:\n  " + from_target->label().GetUserVisibleName(false) +
290        "\nhas a source file:\n  " + file.value() +
291        "\nwhich was not found.");
292    return false;
293  }
294
295  InputFile input_file(file);
296  input_file.SetContents(contents);
297
298  CIncludeIterator iter(&input_file);
299  base::StringPiece current_include;
300  LocationRange range;
301  while (iter.GetNextIncludeString(&current_include, &range)) {
302    SourceFile include = SourceFileForInclude(current_include);
303    if (!CheckInclude(from_target, input_file, include, range, err))
304      return false;
305  }
306
307  return true;
308}
309
310// If the file exists:
311//  - It must be in one or more dependencies of the given target.
312//  - Those dependencies must have visibility from the source file.
313//  - The header must be in the public section of those dependeices.
314//  - Those dependencies must either have no direct dependent configs with
315//    flags that affect the compiler, or those direct dependent configs apply
316//    to the "from_target" (it's one "hop" away). This ensures that if the
317//    include file needs needs compiler settings to compile it, that those
318//    settings are applied to the file including it.
319bool HeaderChecker::CheckInclude(const Target* from_target,
320                                 const InputFile& source_file,
321                                 const SourceFile& include_file,
322                                 const LocationRange& range,
323                                 Err* err) const {
324  // Assume if the file isn't declared in our sources that we don't need to
325  // check it. It would be nice if we could give an error if this happens, but
326  // our include finder is too primitive and returns all includes, even if
327  // they're in a #if not executed in the current build. In that case, it's
328  // not unusual for the buildfiles to not specify that header at all.
329  FileMap::const_iterator found = file_map_.find(include_file);
330  if (found == file_map_.end())
331    return true;
332
333  const TargetVector& targets = found->second;
334  Chain chain;  // Prevent reallocating in the loop.
335
336  // For all targets containing this file, we require that at least one be
337  // a direct or public dependency of the current target, and that the header
338  // is public within the target.
339  //
340  // If there is more than one target containing this header, we may encounter
341  // some error cases before finding a good one. This error stores the previous
342  // one encountered, which we may or may not throw away.
343  Err last_error;
344
345  bool found_dependency = false;
346  for (size_t i = 0; i < targets.size(); i++) {
347    // We always allow source files in a target to include headers also in that
348    // target.
349    const Target* to_target = targets[i].target;
350    if (to_target == from_target)
351      return true;
352
353    bool is_permitted_chain = false;
354    if (IsDependencyOf(to_target, from_target, &chain, &is_permitted_chain)) {
355      DCHECK(chain.size() >= 2);
356      DCHECK(chain[0].target == to_target);
357      DCHECK(chain[chain.size() - 1].target == from_target);
358
359      found_dependency = true;
360
361      if (targets[i].is_public && is_permitted_chain) {
362        // This one is OK, we're done.
363        last_error = Err();
364        break;
365      }
366
367      // Diagnose the error.
368      if (!targets[i].is_public) {
369        // Danger: must call CreatePersistentRange to put in Err.
370        last_error = Err(
371            CreatePersistentRange(source_file, range),
372            "Including a private header.",
373            "This file is private to the target " +
374                targets[i].target->label().GetUserVisibleName(false));
375      } else if (!is_permitted_chain) {
376        last_error = Err(
377            CreatePersistentRange(source_file, range),
378            "Can't include this header from here.",
379                GetDependencyChainPublicError(chain));
380      } else {
381        NOTREACHED();
382      }
383    } else if (
384        to_target->allow_circular_includes_from().find(from_target->label()) !=
385        to_target->allow_circular_includes_from().end()) {
386      // Not a dependency, but this include is whitelisted from the destination.
387      found_dependency = true;
388      last_error = Err();
389      break;
390    }
391  }
392
393  if (!found_dependency) {
394    DCHECK(!last_error.has_error());
395
396    std::string msg = "It is not in any dependency of " +
397        from_target->label().GetUserVisibleName(false);
398    msg += "\nThe include file is in the target(s):\n";
399    for (size_t i = 0; i < targets.size(); i++)
400      msg += "  " + targets[i].target->label().GetUserVisibleName(false) + "\n";
401    if (targets.size() > 1)
402      msg += "at least one of ";
403    msg += "which should somehow be reachable from " +
404        from_target->label().GetUserVisibleName(false);
405
406    // Danger: must call CreatePersistentRange to put in Err.
407    *err = Err(CreatePersistentRange(source_file, range),
408               "Include not allowed.", msg);
409    return false;
410  }
411  if (last_error.has_error()) {
412    // Found at least one dependency chain above, but it had an error.
413    *err = last_error;
414    return false;
415  }
416
417  // One thing we didn't check for is targets that expose their dependents
418  // headers in their own public headers.
419  //
420  // Say we have A -> B -> C. If C has public_configs, everybody getting headers
421  // from C should get the configs also or things could be out-of-sync. Above,
422  // we check for A including C's headers directly, but A could also include a
423  // header from B that in turn includes a header from C.
424  //
425  // There are two ways to solve this:
426  //  - If a public header in B includes C, force B to publicly depend on C.
427  //    This is possible to check, but might be super annoying because most
428  //    targets (especially large leaf-node targets) don't declare
429  //    public/private headers and you'll get lots of false positives.
430  //
431  //  - Save the includes found in each file and actually compute the graph of
432  //    includes to detect when A implicitly includes C's header. This will not
433  //    have the annoying false positive problem, but is complex to write.
434
435  return true;
436}
437
438bool HeaderChecker::IsDependencyOf(const Target* search_for,
439                                   const Target* search_from,
440                                   Chain* chain,
441                                   bool* is_permitted) const {
442  if (search_for == search_from) {
443    // A target is always visible from itself.
444    *is_permitted = true;
445    return false;
446  }
447
448  // Find the shortest public dependency chain.
449  if (IsDependencyOf(search_for, search_from, true, chain)) {
450    *is_permitted = true;
451    return true;
452  }
453
454  // If not, try to find any dependency chain at all.
455  if (IsDependencyOf(search_for, search_from, false, chain)) {
456    *is_permitted = false;
457    return true;
458  }
459
460  *is_permitted = false;
461  return false;
462}
463
464bool HeaderChecker::IsDependencyOf(const Target* search_for,
465                                   const Target* search_from,
466                                   bool require_permitted,
467                                   Chain* chain) const {
468  // This method conducts a breadth-first search through the dependency graph
469  // to find a shortest chain from search_from to search_for.
470  //
471  // work_queue maintains a queue of targets which need to be considered as
472  // part of this chain, in the order they were first traversed.
473  //
474  // Each time a new transitive dependency of search_from is discovered for
475  // the first time, it is added to work_queue and a "breadcrumb" is added,
476  // indicating which target it was reached from when first discovered.
477  //
478  // Once this search finds search_for, the breadcrumbs are used to reconstruct
479  // a shortest dependency chain (in reverse order) from search_from to
480  // search_for.
481
482  std::map<const Target*, ChainLink> breadcrumbs;
483  std::queue<ChainLink> work_queue;
484  work_queue.push(ChainLink(search_from, true));
485
486  bool first_time = true;
487  while (!work_queue.empty()) {
488    ChainLink cur_link = work_queue.front();
489    const Target* target = cur_link.target;
490    work_queue.pop();
491
492    if (target == search_for) {
493      // Found it! Reconstruct the chain.
494      chain->clear();
495      while (target != search_from) {
496        chain->push_back(cur_link);
497        cur_link = breadcrumbs[target];
498        target = cur_link.target;
499      }
500      chain->push_back(ChainLink(search_from, true));
501      return true;
502    }
503
504    // Always consider public dependencies as possibilities.
505    const LabelTargetVector& public_deps = target->public_deps();
506    for (size_t i = 0; i < public_deps.size(); i++) {
507      if (breadcrumbs.insert(
508              std::make_pair(public_deps[i].ptr, cur_link)).second)
509        work_queue.push(ChainLink(public_deps[i].ptr, true));
510    }
511
512    if (first_time || !require_permitted) {
513      // Consider all dependencies since all target paths are allowed, so add
514      // in private ones. Also do this the first time through the loop, since
515      // a target can include headers from its direct deps regardless of
516      // public/private-ness.
517      first_time = false;
518      const LabelTargetVector& private_deps = target->private_deps();
519      for (size_t i = 0; i < private_deps.size(); i++) {
520        if (breadcrumbs.insert(
521                std::make_pair(private_deps[i].ptr, cur_link)).second)
522          work_queue.push(ChainLink(private_deps[i].ptr, false));
523      }
524    }
525  }
526
527  return false;
528}
529