|  | // Copyright 2014 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include "base/command_line.h" | 
|  | #include "base/strings/stringprintf.h" | 
|  | #include "tools/gn/commands.h" | 
|  | #include "tools/gn/header_checker.h" | 
|  | #include "tools/gn/setup.h" | 
|  | #include "tools/gn/standard_out.h" | 
|  | #include "tools/gn/switches.h" | 
|  | #include "tools/gn/target.h" | 
|  | #include "tools/gn/trace.h" | 
|  |  | 
|  | namespace commands { | 
|  |  | 
|  | const char kNoGnCheck_Help[] = | 
|  | R"(nogncheck: Skip an include line from checking. | 
|  |  | 
|  | GN's header checker helps validate that the includes match the build | 
|  | dependency graph. Sometimes an include might be conditional or otherwise | 
|  | problematic, but you want to specifically allow it. In this case, it can be | 
|  | whitelisted. | 
|  |  | 
|  | Include lines containing the substring "nogncheck" will be excluded from | 
|  | header checking. The most common case is a conditional include: | 
|  |  | 
|  | #if defined(ENABLE_DOOM_MELON) | 
|  | #include "tools/doom_melon/doom_melon.h"  // nogncheck | 
|  | #endif | 
|  |  | 
|  | If the build file has a conditional dependency on the corresponding target | 
|  | that matches the conditional include, everything will always link correctly: | 
|  |  | 
|  | source_set("mytarget") { | 
|  | ... | 
|  | if (enable_doom_melon) { | 
|  | defines = [ "ENABLE_DOOM_MELON" ] | 
|  | deps += [ "//tools/doom_melon" ] | 
|  | } | 
|  |  | 
|  | But GN's header checker does not understand preprocessor directives, won't | 
|  | know it matches the build dependencies, and will flag this include as | 
|  | incorrect when the condition is false. | 
|  |  | 
|  | More information | 
|  |  | 
|  | The topic "gn help check" has general information on how checking works and | 
|  | advice on fixing problems. Targets can also opt-out of checking, see | 
|  | "gn help check_includes". | 
|  | )"; | 
|  |  | 
|  | const char kCheck[] = "check"; | 
|  | const char kCheck_HelpShort[] = "check: Check header dependencies."; | 
|  | const char kCheck_Help[] = | 
|  | R"(gn check <out_dir> [<label_pattern>] [--force] | 
|  |  | 
|  | GN's include header checker validates that the includes for C-like source | 
|  | files match the build dependency graph. | 
|  |  | 
|  | "gn check" is the same thing as "gn gen" with the "--check" flag except that | 
|  | this command does not write out any build files. It's intended to be an easy | 
|  | way to manually trigger include file checking. | 
|  |  | 
|  | The <label_pattern> can take exact labels or patterns that match more than | 
|  | one (although not general regular expressions). If specified, only those | 
|  | matching targets will be checked. See "gn help label_pattern" for details. | 
|  |  | 
|  | Command-specific switches | 
|  |  | 
|  | --force | 
|  | Ignores specifications of "check_includes = false" and checks all | 
|  | target's files that match the target label. | 
|  |  | 
|  | What gets checked | 
|  |  | 
|  | The .gn file may specify a list of targets to be checked. Only these targets | 
|  | will be checked if no label_pattern is specified on the command line. | 
|  | Otherwise, the command-line list is used instead. See "gn help dotfile". | 
|  |  | 
|  | Targets can opt-out from checking with "check_includes = false" (see | 
|  | "gn help check_includes"). | 
|  |  | 
|  | For targets being checked: | 
|  |  | 
|  | - GN opens all C-like source files in the targets to be checked and scans | 
|  | the top for includes. | 
|  |  | 
|  | - Includes with a "nogncheck" annotation are skipped (see | 
|  | "gn help nogncheck"). | 
|  |  | 
|  | - Only includes using "quotes" are checked. <brackets> are assumed to be | 
|  | system includes. | 
|  |  | 
|  | - Include paths are assumed to be relative to any of the "include_dirs" for | 
|  | the target (including the implicit current dir). | 
|  |  | 
|  | - GN does not run the preprocessor so will not understand conditional | 
|  | includes. | 
|  |  | 
|  | - Only includes matching known files in the build are checked: includes | 
|  | matching unknown paths are ignored. | 
|  |  | 
|  | For an include to be valid: | 
|  |  | 
|  | - The included file must be in the current target, or there must be a path | 
|  | following only public dependencies to a target with the file in it | 
|  | ("gn path" is a good way to diagnose problems). | 
|  |  | 
|  | - There can be multiple targets with an included file: only one needs to be | 
|  | valid for the include to be allowed. | 
|  |  | 
|  | - If there are only "sources" in a target, all are considered to be public | 
|  | and can be included by other targets with a valid public dependency path. | 
|  |  | 
|  | - If a target lists files as "public", only those files are able to be | 
|  | included by other targets. Anything in the sources will be considered | 
|  | private and will not be includable regardless of dependency paths. | 
|  |  | 
|  | - Outputs from actions are treated like public sources on that target. | 
|  |  | 
|  | - A target can include headers from a target that depends on it if the | 
|  | other target is annotated accordingly. See "gn help | 
|  | allow_circular_includes_from". | 
|  |  | 
|  | Advice on fixing problems | 
|  |  | 
|  | If you have a third party project that is difficult to fix or doesn't care | 
|  | about include checks it's generally best to exclude that target from checking | 
|  | altogether via "check_includes = false". | 
|  |  | 
|  | If you have conditional includes, make sure the build conditions and the | 
|  | preprocessor conditions match, and annotate the line with "nogncheck" (see | 
|  | "gn help nogncheck" for an example). | 
|  |  | 
|  | If two targets are hopelessly intertwined, use the | 
|  | "allow_circular_includes_from" annotation. Ideally each should have identical | 
|  | dependencies so configs inherited from those dependencies are consistent (see | 
|  | "gn help allow_circular_includes_from"). | 
|  |  | 
|  | If you have a standalone header file or files that need to be shared between | 
|  | a few targets, you can consider making a source_set listing only those | 
|  | headers as public sources. With only header files, the source set will be a | 
|  | no-op from a build perspective, but will give a central place to refer to | 
|  | those headers. That source set's files will still need to pass "gn check" in | 
|  | isolation. | 
|  |  | 
|  | In rare cases it makes sense to list a header in more than one target if it | 
|  | could be considered conceptually a member of both. | 
|  |  | 
|  | Examples | 
|  |  | 
|  | gn check out/Debug | 
|  | Check everything. | 
|  |  | 
|  | gn check out/Default //foo:bar | 
|  | Check only the files in the //foo:bar target. | 
|  |  | 
|  | gn check out/Default "//foo/* | 
|  | Check only the files in targets in the //foo directory tree. | 
|  | )"; | 
|  |  | 
|  | int RunCheck(const std::vector<std::string>& args) { | 
|  | if (args.size() != 1 && args.size() != 2) { | 
|  | Err(Location(), "You're holding it wrong.", | 
|  | "Usage: \"gn check <out_dir> [<target_label>]\"") | 
|  | .PrintToStdout(); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | // Deliberately leaked to avoid expensive process teardown. | 
|  | Setup* setup = new Setup(); | 
|  | if (!setup->DoSetup(args[0], false)) | 
|  | return 1; | 
|  | if (!setup->Run()) | 
|  | return 1; | 
|  |  | 
|  | std::vector<const Target*> all_targets = | 
|  | setup->builder().GetAllResolvedTargets(); | 
|  |  | 
|  | bool filtered_by_build_config = false; | 
|  | std::vector<const Target*> targets_to_check; | 
|  | if (args.size() > 1) { | 
|  | // Compute the targets to check. | 
|  | std::vector<std::string> inputs(args.begin() + 1, args.end()); | 
|  | UniqueVector<const Target*> target_matches; | 
|  | UniqueVector<const Config*> config_matches; | 
|  | UniqueVector<const Toolchain*> toolchain_matches; | 
|  | UniqueVector<SourceFile> file_matches; | 
|  | if (!ResolveFromCommandLineInput(setup, inputs, false, &target_matches, | 
|  | &config_matches, &toolchain_matches, | 
|  | &file_matches)) | 
|  | return 1; | 
|  |  | 
|  | if (target_matches.size() == 0) { | 
|  | OutputString("No matching targets.\n"); | 
|  | return 1; | 
|  | } | 
|  | targets_to_check.insert(targets_to_check.begin(), target_matches.begin(), | 
|  | target_matches.end()); | 
|  | } else { | 
|  | // No argument means to check everything allowed by the filter in | 
|  | // the build config file. | 
|  | if (setup->check_patterns()) { | 
|  | FilterTargetsByPatterns(all_targets, *setup->check_patterns(), | 
|  | &targets_to_check); | 
|  | filtered_by_build_config = targets_to_check.size() != all_targets.size(); | 
|  | } else { | 
|  | // No global filter, check everything. | 
|  | targets_to_check = all_targets; | 
|  | } | 
|  | } | 
|  |  | 
|  | const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); | 
|  | bool force = cmdline->HasSwitch("force"); | 
|  |  | 
|  | if (!CheckPublicHeaders(&setup->build_settings(), all_targets, | 
|  | targets_to_check, force)) | 
|  | return 1; | 
|  |  | 
|  | if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kQuiet)) { | 
|  | if (filtered_by_build_config) { | 
|  | // Tell the user about the implicit filtering since this is obscure. | 
|  | OutputString(base::StringPrintf( | 
|  | "%d targets out of %d checked based on the check_targets defined in" | 
|  | " \".gn\".\n", | 
|  | static_cast<int>(targets_to_check.size()), | 
|  | static_cast<int>(all_targets.size()))); | 
|  | } | 
|  | OutputString("Header dependency check OK\n", DECORATION_GREEN); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | bool CheckPublicHeaders(const BuildSettings* build_settings, | 
|  | const std::vector<const Target*>& all_targets, | 
|  | const std::vector<const Target*>& to_check, | 
|  | bool force_check) { | 
|  | ScopedTrace trace(TraceItem::TRACE_CHECK_HEADERS, "Check headers"); | 
|  |  | 
|  | scoped_refptr<HeaderChecker> header_checker( | 
|  | new HeaderChecker(build_settings, all_targets)); | 
|  |  | 
|  | std::vector<Err> header_errors; | 
|  | header_checker->Run(to_check, force_check, &header_errors); | 
|  | for (size_t i = 0; i < header_errors.size(); i++) { | 
|  | if (i > 0) | 
|  | OutputString("___________________\n", DECORATION_YELLOW); | 
|  | header_errors[i].PrintToStdout(); | 
|  | } | 
|  | return header_errors.empty(); | 
|  | } | 
|  |  | 
|  | }  // namespace commands |