Add no_check_targets config option.

Bug: 179

Change-Id: I6c58caa95353554a99036d0a2111f42b5467501b
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/9120
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: Brett Wilson <brettw@chromium.org>
diff --git a/docs/reference.md b/docs/reference.md
index 8d8d30e..ad1ef27 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -383,8 +383,10 @@
 
 ```
   The .gn file may specify a list of targets to be checked in the list
-  check_targets (see "gn help dotfile"). If a label pattern is specified
-  on the command line, check_targets is not used.
+  check_targets (see "gn help dotfile"). Alternatively, the .gn file may
+  specify a list of targets not to be checked in no_check_targets. If a label
+  pattern is specified on the command line, neither check_targets or
+  no_check_targets is used.
 
   Targets can opt-out from checking with "check_includes = false" (see
   "gn help check_includes").
@@ -6593,9 +6595,21 @@
 
   check_targets [optional]
       A list of labels and label patterns that should be checked when running
-      "gn check" or "gn gen --check". If unspecified, all targets will be
-      checked. If it is the empty list, no targets will be checked. To
-      bypass this list, request an explicit check of targets, like "//*".
+      "gn check" or "gn gen --check". If neither check_targets or
+      no_check_targets (see below) is specified, all targets will be checked.
+      It is an error to specify both check_targets and no_check_targets. If it
+      is the empty list, no targets will be checked. To bypass this list,
+      request an explicit check of targets, like "//*".
+
+      The format of this list is identical to that of "visibility" so see "gn
+      help visibility" for examples.
+
+  no_check_targets [optional]
+      A list of labels and label patterns that should *not* be checked when
+      running "gn check" or "gn gen --check". All other targets will be checked.
+      If neither check_targets (see above) or no_check_targets is specified, all
+      targets will be checked. It is an error to specify both check_targets and
+      no_check_targets.
 
       The format of this list is identical to that of "visibility" so see "gn
       help visibility" for examples.
diff --git a/src/gn/command_check.cc b/src/gn/command_check.cc
index 98a5e1c..38a474c 100644
--- a/src/gn/command_check.cc
+++ b/src/gn/command_check.cc
@@ -88,8 +88,10 @@
 What gets checked
 
   The .gn file may specify a list of targets to be checked in the list
-  check_targets (see "gn help dotfile"). If a label pattern is specified
-  on the command line, check_targets is not used.
+  check_targets (see "gn help dotfile"). Alternatively, the .gn file may
+  specify a list of targets not to be checked in no_check_targets. If a label
+  pattern is specified on the command line, neither check_targets or
+  no_check_targets is used.
 
   Targets can opt-out from checking with "check_includes = false" (see
   "gn help check_includes").
@@ -225,6 +227,10 @@
       FilterTargetsByPatterns(all_targets, *setup->check_patterns(),
                               &targets_to_check);
       filtered_by_build_config = targets_to_check.size() != all_targets.size();
+    } else if (setup->no_check_patterns()) {
+      FilterOutTargetsByPatterns(all_targets, *setup->no_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;
@@ -245,8 +251,8 @@
     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",
+          "%d targets out of %d checked based on the check_targets or "
+          "no_check_targets defined in \".gn\".\n",
           static_cast<int>(targets_to_check.size()),
           static_cast<int>(all_targets.size())));
     }
diff --git a/src/gn/commands.cc b/src/gn/commands.cc
index 431f8d2..8f1a1a7 100644
--- a/src/gn/commands.cc
+++ b/src/gn/commands.cc
@@ -534,6 +534,19 @@
   }
 }
 
+void FilterOutTargetsByPatterns(const std::vector<const Target*>& input,
+                                const std::vector<LabelPattern>& filter,
+                                std::vector<const Target*>* output) {
+  for (auto* target : input) {
+    for (const auto& pattern : filter) {
+      if (!pattern.Matches(target->label())) {
+        output->push_back(target);
+        break;
+      }
+    }
+  }
+}
+
 bool FilterPatternsFromString(const BuildSettings* build_settings,
                               const std::string& label_list_string,
                               std::vector<LabelPattern>* filters,
diff --git a/src/gn/commands.h b/src/gn/commands.h
index 41b99bc..28d713a 100644
--- a/src/gn/commands.h
+++ b/src/gn/commands.h
@@ -165,6 +165,11 @@
                              const std::vector<LabelPattern>& filter,
                              UniqueVector<const Target*>* output);
 
+// Removes targets from the input that match the given pattern list.
+void FilterOutTargetsByPatterns(const std::vector<const Target*>& input,
+                                const std::vector<LabelPattern>& filter,
+                                std::vector<const Target*>* output);
+
 // Builds a list of pattern from a semicolon-separated list of labels.
 bool FilterPatternsFromString(const BuildSettings* build_settings,
                               const std::string& label_list_string,
diff --git a/src/gn/setup.cc b/src/gn/setup.cc
index c1d8688..bf48e38 100644
--- a/src/gn/setup.cc
+++ b/src/gn/setup.cc
@@ -70,9 +70,21 @@
 
   check_targets [optional]
       A list of labels and label patterns that should be checked when running
-      "gn check" or "gn gen --check". If unspecified, all targets will be
-      checked. If it is the empty list, no targets will be checked. To
-      bypass this list, request an explicit check of targets, like "//*".
+      "gn check" or "gn gen --check". If neither check_targets or
+      no_check_targets (see below) is specified, all targets will be checked.
+      It is an error to specify both check_targets and no_check_targets. If it
+      is the empty list, no targets will be checked. To bypass this list,
+      request an explicit check of targets, like "//*".
+
+      The format of this list is identical to that of "visibility" so see "gn
+      help visibility" for examples.
+
+  no_check_targets [optional]
+      A list of labels and label patterns that should *not* be checked when
+      running "gn check" or "gn gen --check". All other targets will be checked.
+      If neither check_targets (see above) or no_check_targets is specified, all
+      targets will be checked. It is an error to specify both check_targets and
+      no_check_targets.
 
       The format of this list is identical to that of "visibility" so see "gn
       help visibility" for examples.
@@ -862,7 +874,7 @@
   const Value* check_targets_value =
       dotfile_scope_.GetValue("check_targets", true);
   if (check_targets_value) {
-    check_patterns_.reset(new std::vector<LabelPattern>);
+    check_patterns_ = std::make_unique<std::vector<LabelPattern>>();
     ExtractListOfLabelPatterns(&build_settings_, *check_targets_value,
                                current_dir, check_patterns_.get(), err);
     if (err->has_error()) {
@@ -870,6 +882,27 @@
     }
   }
 
+  // Targets not to check.
+  const Value* no_check_targets_value =
+      dotfile_scope_.GetValue("no_check_targets", true);
+  if (no_check_targets_value) {
+    if (check_targets_value) {
+      Err(Location(), "Conflicting check settings.",
+          "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) +
+              "\")\n"
+              "specified both check_targets and no_check_targets and at most "
+              "one is allowed.")
+          .PrintToStdout();
+      return false;
+    }
+    no_check_patterns_ = std::make_unique<std::vector<LabelPattern>>();
+    ExtractListOfLabelPatterns(&build_settings_, *no_check_targets_value,
+                               current_dir, no_check_patterns_.get(), err);
+    if (err->has_error()) {
+      return false;
+    }
+  }
+
   const Value* check_system_includes_value =
       dotfile_scope_.GetValue("check_system_includes", true);
   if (check_system_includes_value) {
diff --git a/src/gn/setup.h b/src/gn/setup.h
index bf1e1ca..1c5d280 100644
--- a/src/gn/setup.h
+++ b/src/gn/setup.h
@@ -106,6 +106,14 @@
     return check_patterns_.get();
   }
 
+  // Read from the .gn file, these are the targets *not* to check. If the .gn
+  // file does not specify anything, this will be null. If the .gn file
+  // specifies the empty list, this will be non-null but empty. At least one of
+  // check_patterns() and no_check_patterns() will be null.
+  const std::vector<LabelPattern>* no_check_patterns() const {
+    return no_check_patterns_.get();
+  }
+
   BuildSettings& build_settings() { return build_settings_; }
   Builder& builder() { return builder_; }
   LoaderImpl* loader() { return loader_.get(); }
@@ -167,6 +175,7 @@
 
   // See getter for info.
   std::unique_ptr<std::vector<LabelPattern>> check_patterns_;
+  std::unique_ptr<std::vector<LabelPattern>> no_check_patterns_;
 
   Scheduler scheduler_;