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(¤t_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