Home | History | Annotate | Download | only in gn
      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 
     25 namespace {
     26 
     27 struct 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.
     37 SourceFile 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.
     53 LocationRange 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.
     76 std::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 
    120 HeaderChecker::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 
    128 HeaderChecker::~HeaderChecker() {
    129 }
    130 
    131 bool 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 
    151 void 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 
    195 void 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
    204 void 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 
    255 bool 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.
    266 SourceFile HeaderChecker::SourceFileForInclude(
    267     const base::StringPiece& input) const {
    268   std::string str("//");
    269   input.AppendToString(&str);
    270   return SourceFile(str);
    271 }
    272 
    273 bool 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.
    319 bool 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 
    438 bool 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 
    464 bool 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