Move most license checking logic into a module to make it easier to eventually remove.

There's basically four groups of license-related logic in Bazel:

1) Syntactic support in BUILD files
2) Semantics that checks third_party rules have licenses() declared
3) LicenseProvider, which collects rules' transitive license declarations
4) Semantics that checks if a build's licenses are valid

This change only covers 4).

This also simplifies AnalysisPhaseRunner and License.

Part of #7444.

PiperOrigin-RevId: 235585865
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index a05ecf9..6a92c8b 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -808,6 +808,22 @@
 )
 
 java_library(
+    name = "bazel/LicenseCheckingModule",
+    srcs = ["bazel/LicenseCheckingModule.java"],
+    deps = [
+        ":build-base",
+        ":events",
+        ":packages-internal",
+        ":runtime",
+        "//src/main/java/com/google/devtools/build/lib/cmdline",
+        "//src/main/java/com/google/devtools/build/lib/collect",
+        "//src/main/java/com/google/devtools/build/lib/collect/nestedset",
+        "//src/main/java/com/google/devtools/build/lib/profiler",
+        "//third_party:guava",
+    ],
+)
+
+java_library(
     name = "bazel-main",
     srcs = ["bazel/Bazel.java"],
     resources = [
@@ -845,8 +861,10 @@
         exclude = [
             "bazel/Bazel.java",
             "bazel/BazelRepositoryModule.java",
+            "bazel/LicenseCheckingModule.java",
         ],
     ),
+    exports = [":bazel/LicenseCheckingModule"],
     deps = [
         ":build-base",
         ":build-info",
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java b/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java
index f2829af..4ece65e 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java
@@ -43,6 +43,7 @@
           com.google.devtools.build.lib.bazel.BazelWorkspaceStatusModule.class,
           com.google.devtools.build.lib.bazel.BazelDiffAwarenessModule.class,
           com.google.devtools.build.lib.bazel.BazelRepositoryModule.class,
+          com.google.devtools.build.lib.bazel.LicenseCheckingModule.class,
           com.google.devtools.build.lib.bazel.debug.WorkspaceRuleModule.class,
           com.google.devtools.build.lib.bazel.coverage.BazelCoverageReportModule.class,
           com.google.devtools.build.lib.skylarkdebug.module.SkylarkDebuggerModule.class,
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/LicenseCheckingModule.java b/src/main/java/com/google/devtools/build/lib/bazel/LicenseCheckingModule.java
new file mode 100644
index 0000000..869b9a0
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/LicenseCheckingModule.java
@@ -0,0 +1,256 @@
+// Copyright 2019 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.bazel;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Table;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.LicensesProvider;
+import com.google.devtools.build.lib.analysis.LicensesProvider.TargetLicense;
+import com.google.devtools.build.lib.analysis.StaticallyLinkedMarkerProvider;
+import com.google.devtools.build.lib.analysis.ViewCreationFailedException;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.config.BuildOptions;
+import com.google.devtools.build.lib.buildtool.BuildRequest;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.events.EventHandler;
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.InputFile;
+import com.google.devtools.build.lib.packages.License;
+import com.google.devtools.build.lib.packages.License.DistributionType;
+import com.google.devtools.build.lib.packages.License.LicenseType;
+import com.google.devtools.build.lib.packages.NoSuchPackageException;
+import com.google.devtools.build.lib.packages.NoSuchTargetException;
+import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.Target;
+import com.google.devtools.build.lib.packages.TargetUtils;
+import com.google.devtools.build.lib.profiler.ProfilePhase;
+import com.google.devtools.build.lib.profiler.Profiler;
+import com.google.devtools.build.lib.profiler.SilentCloseable;
+import com.google.devtools.build.lib.runtime.BlazeModule;
+import com.google.devtools.build.lib.runtime.CommandEnvironment;
+import java.util.EnumSet;
+import java.util.Set;
+
+/**
+ * Module responsible for checking third party license compliance.
+ *
+ * <p><b>This is outdated logic marked for removal.</b> See <a
+ * href="https://github.com/bazelbuild/bazel/issues/7444">#7444</a> for details.
+ */
+public class LicenseCheckingModule extends BlazeModule {
+
+  private boolean shouldCheckLicenses(BuildOptions buildOptions) {
+    return buildOptions.get(BuildConfiguration.Options.class).checkLicenses;
+  }
+
+  @Override
+  public void afterAnalysis(
+      CommandEnvironment env,
+      BuildRequest request,
+      BuildOptions buildOptions,
+      Iterable<ConfiguredTarget> configuredTargets)
+      throws InterruptedException, ViewCreationFailedException {
+    // Check licenses.
+    // We check licenses if the first target configuration has license checking enabled. Right
+    // now, it is not possible to have multiple target configurations with different settings
+    // for this flag, which allows us to take this short cut.
+    if (shouldCheckLicenses(buildOptions)) {
+      Profiler.instance().markPhase(ProfilePhase.LICENSE);
+      try (SilentCloseable c = Profiler.instance().profile("validateLicensingForTargets")) {
+        validateLicensingForTargets(env, configuredTargets, request.getKeepGoing());
+      }
+    }
+  }
+
+  /**
+   * Takes a set of configured targets, and checks if the distribution methods declared for the
+   * targets are compatible with the constraints imposed by their prerequisites' licenses.
+   *
+   * @param configuredTargets the targets to check
+   * @param keepGoing if false, and a licensing error is encountered, both generates an error
+   *     message on the reporter, <em>and</em> throws an exception. If true, then just generates a
+   *     message on the reporter.
+   * @throws ViewCreationFailedException if the license checking failed (and not --keep_going)
+   */
+  private static void validateLicensingForTargets(
+      CommandEnvironment env, Iterable<ConfiguredTarget> configuredTargets, boolean keepGoing)
+      throws ViewCreationFailedException {
+    for (ConfiguredTarget configuredTarget : configuredTargets) {
+      Target target = null;
+      try {
+        target = env.getPackageManager().getTarget(env.getReporter(), configuredTarget.getLabel());
+      } catch (NoSuchPackageException | NoSuchTargetException | InterruptedException e) {
+        env.getReporter().handle(Event.error("Failed to get target to validate license"));
+        throw new ViewCreationFailedException(
+            "Build aborted due to issue getting targets to validate licenses", e);
+      }
+
+      if (TargetUtils.isTestRule(target)) {
+        continue; // Tests are exempt from license checking
+      }
+
+      final Set<DistributionType> distribs = target.getDistributions();
+      StaticallyLinkedMarkerProvider markerProvider =
+          configuredTarget.getProvider(StaticallyLinkedMarkerProvider.class);
+      boolean staticallyLinked = markerProvider != null && markerProvider.isLinkedStatically();
+
+      LicensesProvider provider = configuredTarget.getProvider(LicensesProvider.class);
+      if (provider != null) {
+        NestedSet<TargetLicense> licenses = provider.getTransitiveLicenses();
+        for (TargetLicense targetLicense : licenses) {
+          if (!checkCompatibility(
+              targetLicense.getLicense(),
+              distribs,
+              target,
+              targetLicense.getLabel(),
+              env.getReporter(),
+              staticallyLinked)) {
+            if (!keepGoing) {
+              throw new ViewCreationFailedException("Build aborted due to licensing error");
+            }
+          }
+        }
+      } else if (target instanceof InputFile) {
+        // Input file targets do not provide licenses because they do not
+        // depend on the rule where their license is taken from. This is usually
+        // not a problem, because the transitive collection of licenses always
+        // hits the rule they come from, except when the input file is a
+        // top-level target. Thus, we need to handle that case specially here.
+        //
+        // See FileTarget#getLicense for more information about the handling of
+        // license issues with File targets.
+        License license = target.getLicense();
+        if (!checkCompatibility(
+            license,
+            distribs,
+            target,
+            configuredTarget.getLabel(),
+            env.getReporter(),
+            staticallyLinked)) {
+          if (!keepGoing) {
+            throw new ViewCreationFailedException("Build aborted due to licensing error");
+          }
+        }
+      }
+    }
+  }
+
+  private static final Object MARKER = new Object();
+
+  /**
+   * The license incompatibility set. This contains the set of (Distribution,License) pairs that
+   * should generate errors.
+   */
+  private static final Table<DistributionType, LicenseType, Object> licenseIncompatibilies =
+      createLicenseIncompatibilitySet();
+
+  private static Table<DistributionType, LicenseType, Object> createLicenseIncompatibilitySet() {
+    Table<DistributionType, LicenseType, Object> result = HashBasedTable.create();
+    result.put(DistributionType.CLIENT, LicenseType.RESTRICTED, MARKER);
+    result.put(DistributionType.EMBEDDED, LicenseType.RESTRICTED, MARKER);
+    result.put(DistributionType.INTERNAL, LicenseType.BY_EXCEPTION_ONLY, MARKER);
+    result.put(DistributionType.CLIENT, LicenseType.BY_EXCEPTION_ONLY, MARKER);
+    result.put(DistributionType.WEB, LicenseType.BY_EXCEPTION_ONLY, MARKER);
+    result.put(DistributionType.EMBEDDED, LicenseType.BY_EXCEPTION_ONLY, MARKER);
+    return ImmutableTable.copyOf(result);
+  }
+
+  /**
+   * The license warning set. This contains the set of (Distribution,License) pairs that should
+   * generate warnings when the user requests verbose license checking.
+   */
+  private static final Table<DistributionType, LicenseType, Object> licenseWarnings =
+      createLicenseWarningsSet();
+
+  private static Table<DistributionType, LicenseType, Object> createLicenseWarningsSet() {
+    Table<DistributionType, LicenseType, Object> result = HashBasedTable.create();
+    result.put(DistributionType.CLIENT, LicenseType.RECIPROCAL, MARKER);
+    result.put(DistributionType.EMBEDDED, LicenseType.RECIPROCAL, MARKER);
+    result.put(DistributionType.CLIENT, LicenseType.NOTICE, MARKER);
+    result.put(DistributionType.EMBEDDED, LicenseType.NOTICE, MARKER);
+    return ImmutableTable.copyOf(result);
+  }
+
+  /**
+   * Checks if the given license is compatible with distributing a particular target in some set of
+   * distribution modes.
+   *
+   * @param license the license to check
+   * @param dists the modes of distribution
+   * @param target the target which is being checked, and which will be used for checking exceptions
+   * @param licensedTarget the target which declared the license being checked.
+   * @param eventHandler a reporter where any licensing issues discovered should be reported
+   * @param staticallyLinked whether the target is statically linked under this command
+   * @return true if the license is compatible with the distributions
+   */
+  @VisibleForTesting
+  public static boolean checkCompatibility(
+      License license,
+      Set<DistributionType> dists,
+      Target target,
+      Label licensedTarget,
+      EventHandler eventHandler,
+      boolean staticallyLinked) {
+    Location location = (target instanceof Rule) ? ((Rule) target).getLocation() : null;
+
+    LicenseType leastRestrictiveLicense;
+    if (license.getLicenseTypes().contains(LicenseType.RESTRICTED_IF_STATICALLY_LINKED)) {
+      Set<LicenseType> tempLicenses = EnumSet.copyOf(license.getLicenseTypes());
+      tempLicenses.remove(LicenseType.RESTRICTED_IF_STATICALLY_LINKED);
+      if (staticallyLinked) {
+        tempLicenses.add(LicenseType.RESTRICTED);
+      } else {
+        tempLicenses.add(LicenseType.UNENCUMBERED);
+      }
+      leastRestrictiveLicense = License.leastRestrictive(tempLicenses);
+    } else {
+      leastRestrictiveLicense = License.leastRestrictive(license.getLicenseTypes());
+    }
+    for (DistributionType dt : dists) {
+      if (licenseIncompatibilies.contains(dt, leastRestrictiveLicense)) {
+        if (!license.getExceptions().contains(target.getLabel())) {
+          eventHandler.handle(
+              Event.error(
+                  location,
+                  "Build target '"
+                      + target.getLabel()
+                      + "' is not compatible with license '"
+                      + license
+                      + "' from target '"
+                      + licensedTarget
+                      + "'"));
+          return false;
+        }
+      } else if (licenseWarnings.contains(dt, leastRestrictiveLicense)) {
+        eventHandler.handle(
+            Event.warn(
+                location,
+                "Build target '"
+                    + target
+                    + "' has a potential licensing issue with a '"
+                    + license
+                    + "' license from target '"
+                    + licensedTarget
+                    + "'"));
+      }
+    }
+    return true;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/AnalysisPhaseRunner.java b/src/main/java/com/google/devtools/build/lib/buildtool/AnalysisPhaseRunner.java
index 8cc38c0..6af3eb8 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/AnalysisPhaseRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/AnalysisPhaseRunner.java
@@ -21,9 +21,6 @@
 import com.google.devtools.build.lib.analysis.AnalysisResult;
 import com.google.devtools.build.lib.analysis.BuildView;
 import com.google.devtools.build.lib.analysis.ConfiguredTarget;
-import com.google.devtools.build.lib.analysis.LicensesProvider;
-import com.google.devtools.build.lib.analysis.LicensesProvider.TargetLicense;
-import com.google.devtools.build.lib.analysis.StaticallyLinkedMarkerProvider;
 import com.google.devtools.build.lib.analysis.ViewCreationFailedException;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
 import com.google.devtools.build.lib.analysis.config.BuildOptions;
@@ -35,19 +32,13 @@
 import com.google.devtools.build.lib.buildtool.buildevent.TestFilteringCompleteEvent;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.TargetParsingException;
-import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.events.Event;
-import com.google.devtools.build.lib.packages.InputFile;
-import com.google.devtools.build.lib.packages.License;
-import com.google.devtools.build.lib.packages.License.DistributionType;
-import com.google.devtools.build.lib.packages.NoSuchPackageException;
-import com.google.devtools.build.lib.packages.NoSuchTargetException;
 import com.google.devtools.build.lib.packages.Target;
-import com.google.devtools.build.lib.packages.TargetUtils;
 import com.google.devtools.build.lib.pkgcache.LoadingFailedException;
 import com.google.devtools.build.lib.profiler.ProfilePhase;
 import com.google.devtools.build.lib.profiler.Profiler;
 import com.google.devtools.build.lib.profiler.SilentCloseable;
+import com.google.devtools.build.lib.runtime.BlazeModule;
 import com.google.devtools.build.lib.runtime.CommandEnvironment;
 import com.google.devtools.build.lib.skyframe.BuildConfigurationValue;
 import com.google.devtools.build.lib.skyframe.TargetPatternPhaseValue;
@@ -118,16 +109,8 @@
             runAnalysisPhase(request, loadingResult, buildOptions, request.getMultiCpus());
       }
 
-      // Check licenses.
-      // We check licenses if the first target configuration has license checking enabled. Right
-      // now, it is not possible to have multiple target configurations with different settings
-      // for this flag, which allows us to take this short cut.
-      boolean checkLicenses = buildOptions.get(BuildConfiguration.Options.class).checkLicenses;
-      if (checkLicenses) {
-        Profiler.instance().markPhase(ProfilePhase.LICENSE);
-        try (SilentCloseable c = Profiler.instance().profile("validateLicensingForTargets")) {
-          validateLicensingForTargets(analysisResult.getTargetsToBuild(), request.getKeepGoing());
-        }
+      for (BlazeModule module : env.getRuntime().getBlazeModules()) {
+        module.afterAnalysis(env, request, buildOptions, analysisResult.getTargetsToBuild());
       }
 
       reportTargets(analysisResult);
@@ -288,73 +271,4 @@
                   "Found " + targetCount + (targetCount == 1 ? " target..." : " targets...")));
     }
   }
-
-  /**
-   * Takes a set of configured targets, and checks if the distribution methods declared for the
-   * targets are compatible with the constraints imposed by their prerequisites' licenses.
-   *
-   * @param configuredTargets the targets to check
-   * @param keepGoing if false, and a licensing error is encountered, both generates an error
-   *     message on the reporter, <em>and</em> throws an exception. If true, then just generates a
-   *     message on the reporter.
-   * @throws ViewCreationFailedException if the license checking failed (and not --keep_going)
-   */
-  private void validateLicensingForTargets(
-      Iterable<ConfiguredTarget> configuredTargets, boolean keepGoing)
-      throws ViewCreationFailedException {
-    for (ConfiguredTarget configuredTarget : configuredTargets) {
-      Target target = null;
-      try {
-        target = env.getPackageManager().getTarget(env.getReporter(), configuredTarget.getLabel());
-      } catch (NoSuchPackageException | NoSuchTargetException | InterruptedException e) {
-        env.getReporter().handle(Event.error("Failed to get target to validate license"));
-        throw new ViewCreationFailedException(
-            "Build aborted due to issue getting targets to validate licenses", e);
-      }
-
-      if (TargetUtils.isTestRule(target)) {
-        continue; // Tests are exempt from license checking
-      }
-
-      final Set<DistributionType> distribs = target.getDistributions();
-      StaticallyLinkedMarkerProvider markerProvider =
-          configuredTarget.getProvider(StaticallyLinkedMarkerProvider.class);
-      boolean staticallyLinked = markerProvider != null && markerProvider.isLinkedStatically();
-
-      LicensesProvider provider = configuredTarget.getProvider(LicensesProvider.class);
-      if (provider != null) {
-        NestedSet<TargetLicense> licenses = provider.getTransitiveLicenses();
-        for (TargetLicense targetLicense : licenses) {
-          if (!targetLicense
-              .getLicense()
-              .checkCompatibility(
-                  distribs,
-                  target,
-                  targetLicense.getLabel(),
-                  env.getReporter(),
-                  staticallyLinked)) {
-            if (!keepGoing) {
-              throw new ViewCreationFailedException("Build aborted due to licensing error");
-            }
-          }
-        }
-      } else if (target instanceof InputFile) {
-        // Input file targets do not provide licenses because they do not
-        // depend on the rule where their license is taken from. This is usually
-        // not a problem, because the transitive collection of licenses always
-        // hits the rule they come from, except when the input file is a
-        // top-level target. Thus, we need to handle that case specially here.
-        //
-        // See FileTarget#getLicense for more information about the handling of
-        // license issues with File targets.
-        License license = target.getLicense();
-        if (!license.checkCompatibility(
-            distribs, target, configuredTarget.getLabel(), env.getReporter(), staticallyLinked)) {
-          if (!keepGoing) {
-            throw new ViewCreationFailedException("Build aborted due to licensing error");
-          }
-        }
-      }
-    }
-  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequest.java b/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequest.java
index 87cf21a..e6213e4 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequest.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequest.java
@@ -223,7 +223,7 @@
   }
 
   /** Returns the value of the --keep_going option. */
-  boolean getKeepGoing() {
+  public boolean getKeepGoing() {
     return getOptions(KeepGoingOption.class).keepGoing;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/packages/License.java b/src/main/java/com/google/devtools/build/lib/packages/License.java
index 8c68447..c980444 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/License.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/License.java
@@ -15,19 +15,13 @@
 package com.google.devtools.build.lib.packages;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.HashBasedTable;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableTable;
 import com.google.common.collect.Sets;
-import com.google.common.collect.Table;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
-import com.google.devtools.build.lib.events.Event;
-import com.google.devtools.build.lib.events.EventHandler;
-import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
 import com.google.devtools.build.lib.skylarkbuildapi.LicenseApi;
@@ -75,15 +69,16 @@
   }
 
   /**
-   * Gets the least restrictive license type from the list of licenses declared
-   * for a target. For the purposes of license checking, the license type set of
-   * a declared license can be reduced to its least restrictive member.
+   * Gets the least restrictive license type from the list of licenses declared for a target. For
+   * the purposes of license checking, the license type set of a declared license can be reduced to
+   * its least restrictive member.
    *
    * @param types a collection of license types
    * @return the least restrictive license type
    */
   @VisibleForTesting
-  static LicenseType leastRestrictive(Collection<LicenseType> types) {
+  public static LicenseType leastRestrictive(Collection<LicenseType> types) {
+    // TODO(gregce): move this method to LicenseCheckingModule when Bazel's tests no longer use it
     return types.isEmpty() ? LicenseType.BY_EXCEPTION_ONLY : Collections.max(types);
   }
 
@@ -138,43 +133,6 @@
     }
   }
 
-  private static final Object MARKER = new Object();
-
-  /**
-   * The license incompatibility set. This contains the set of
-   * (Distribution,License) pairs that should generate errors.
-   */
-  private static Table<DistributionType, LicenseType, Object> LICENSE_INCOMPATIBILIES =
-      createLicenseIncompatibilitySet();
-
-  private static Table<DistributionType, LicenseType, Object> createLicenseIncompatibilitySet() {
-    Table<DistributionType, LicenseType, Object> result = HashBasedTable.create();
-    result.put(DistributionType.CLIENT, LicenseType.RESTRICTED, MARKER);
-    result.put(DistributionType.EMBEDDED, LicenseType.RESTRICTED, MARKER);
-    result.put(DistributionType.INTERNAL, LicenseType.BY_EXCEPTION_ONLY, MARKER);
-    result.put(DistributionType.CLIENT, LicenseType.BY_EXCEPTION_ONLY, MARKER);
-    result.put(DistributionType.WEB, LicenseType.BY_EXCEPTION_ONLY, MARKER);
-    result.put(DistributionType.EMBEDDED, LicenseType.BY_EXCEPTION_ONLY, MARKER);
-    return ImmutableTable.copyOf(result);
-  }
-
-  /**
-   * The license warning set. This contains the set of
-   * (Distribution,License) pairs that should generate warnings when the user
-   * requests verbose license checking.
-   */
-  private static Table<DistributionType, LicenseType, Object> LICENSE_WARNINGS =
-      createLicenseWarningsSet();
-
-  private static Table<DistributionType, LicenseType, Object> createLicenseWarningsSet() {
-    Table<DistributionType, LicenseType, Object> result = HashBasedTable.create();
-    result.put(DistributionType.CLIENT, LicenseType.RECIPROCAL, MARKER);
-    result.put(DistributionType.EMBEDDED, LicenseType.RECIPROCAL, MARKER);
-    result.put(DistributionType.CLIENT, LicenseType.NOTICE, MARKER);
-    result.put(DistributionType.EMBEDDED, LicenseType.NOTICE, MARKER);
-    return ImmutableTable.copyOf(result);
-  }
-
   @AutoCodec.Instantiator
   @VisibleForSerialization
   License(ImmutableSet<LicenseType> licenseTypes, ImmutableSet<Label> exceptions) {
@@ -232,55 +190,6 @@
   }
 
   /**
-   * Checks if this license is compatible with distributing a particular target
-   * in some set of distribution modes.
-   *
-   * @param dists the modes of distribution
-   * @param target the target which is being checked, and which will be used for
-   *        checking exceptions
-   * @param licensedTarget the target which declared the license being checked.
-   * @param eventHandler a reporter where any licensing issues discovered should be
-   *        reported
-   * @param staticallyLinked whether the target is statically linked under this command
-   * @return true if the license is compatible with the distributions
-   */
-  public boolean checkCompatibility(Set<DistributionType> dists,
-      Target target, Label licensedTarget, EventHandler eventHandler,
-      boolean staticallyLinked) {
-    Location location = (target instanceof Rule) ? ((Rule) target).getLocation() : null;
-
-    LicenseType leastRestrictiveLicense;
-    if (licenseTypes.contains(LicenseType.RESTRICTED_IF_STATICALLY_LINKED)) {
-      Set<LicenseType> tempLicenses = EnumSet.copyOf(licenseTypes);
-      tempLicenses.remove(LicenseType.RESTRICTED_IF_STATICALLY_LINKED);
-      if (staticallyLinked) {
-        tempLicenses.add(LicenseType.RESTRICTED);
-      } else {
-        tempLicenses.add(LicenseType.UNENCUMBERED);
-      }
-      leastRestrictiveLicense = leastRestrictive(tempLicenses);
-    } else {
-      leastRestrictiveLicense = leastRestrictive(licenseTypes);
-    }
-    for (DistributionType dt : dists) {
-      if (LICENSE_INCOMPATIBILIES.contains(dt, leastRestrictiveLicense)) {
-        if (!exceptions.contains(target.getLabel())) {
-          eventHandler.handle(Event.error(location, "Build target '" + target.getLabel()
-              + "' is not compatible with license '" + this + "' from target '"
-                  + licensedTarget + "'"));
-          return false;
-        }
-      } else if (LICENSE_WARNINGS.contains(dt, leastRestrictiveLicense)) {
-        eventHandler.handle(
-            Event.warn(location, "Build target '" + target
-                + "' has a potential licensing issue "
-                + "with a '" + this + "' license from target '" + licensedTarget + "'"));
-      }
-    }
-    return true;
-  }
-
-  /**
    * @return an immutable set of {@link LicenseType}s contained in this {@code
    *         License}
    */
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeModule.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeModule.java
index 03bf031..dda1465 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeModule.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeModule.java
@@ -19,7 +19,9 @@
 import com.google.devtools.build.lib.analysis.BlazeDirectories;
 import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
 import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
 import com.google.devtools.build.lib.analysis.ServerDirectories;
+import com.google.devtools.build.lib.analysis.ViewCreationFailedException;
 import com.google.devtools.build.lib.analysis.config.BuildOptions;
 import com.google.devtools.build.lib.analysis.test.CoverageReportActionFactory;
 import com.google.devtools.build.lib.buildtool.BuildRequest;
@@ -246,6 +248,22 @@
   }
 
   /**
+   * Called after Bazel analyzes the build's top-level targets. This is called once per build if
+   * --analyze is enabled. Modules can override this to perform extra checks on analysis results.
+   *
+   * @param env the command environment
+   * @param request the build request
+   * @param buildOptions the build's top-level options
+   * @param configuredTargets the build's requested top-level targets as {@link ConfiguredTarget}s
+   */
+  public void afterAnalysis(
+      CommandEnvironment env,
+      BuildRequest request,
+      BuildOptions buildOptions,
+      Iterable<ConfiguredTarget> configuredTargets)
+      throws InterruptedException, ViewCreationFailedException {}
+
+  /**
    * Called when Bazel initializes the action execution subsystem. This is called once per build if
    * action execution is enabled. Modules can override this method to affect how execution is
    * performed.
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 6f17d30..13e090d 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -650,6 +650,7 @@
         "//src/main/java/com/google/devtools/build/lib:bazel-main",
         "//src/main/java/com/google/devtools/build/lib:bazel-repository",
         "//src/main/java/com/google/devtools/build/lib:bazel-rules",
+        "//src/main/java/com/google/devtools/build/lib:bazel/LicenseCheckingModule",
         "//src/main/java/com/google/devtools/build/lib:build-base",
         "//src/main/java/com/google/devtools/build/lib:build-request-options",
         "//src/main/java/com/google/devtools/build/lib:core-rules",
@@ -859,6 +860,7 @@
         ":testutil",
         "//src/main/java/com/google/devtools/build/lib:bazel-main",
         "//src/main/java/com/google/devtools/build/lib:bazel-rules",
+        "//src/main/java/com/google/devtools/build/lib:bazel/LicenseCheckingModule",
         "//src/main/java/com/google/devtools/build/lib:build-base",
         "//src/main/java/com/google/devtools/build/lib:events",
         "//src/main/java/com/google/devtools/build/lib:packages",
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/LicensingTests.java b/src/test/java/com/google/devtools/build/lib/analysis/LicensingTests.java
index 7bcb052..b70021a 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/LicensingTests.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/LicensingTests.java
@@ -26,6 +26,7 @@
 import com.google.devtools.build.lib.analysis.LicensesProvider.TargetLicense;
 import com.google.devtools.build.lib.analysis.util.AnalysisMock;
 import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.bazel.LicenseCheckingModule;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
 import com.google.devtools.build.lib.packages.License;
@@ -552,8 +553,13 @@
         Maps.filterKeys(getTransitiveLicenses(used), AnalysisMock.get().ccSupport().labelFilter());
     Label usedLabel = Label.parseAbsolute("//used", ImmutableMap.of());
     License license = usedActual.get(usedLabel);
-    license.checkCompatibility(EnumSet.of(DistributionType.CLIENT),
-        getTarget("//user"), usedLabel, reporter, false);
+    LicenseCheckingModule.checkCompatibility(
+        license,
+        EnumSet.of(DistributionType.CLIENT),
+        getTarget("//user"),
+        usedLabel,
+        reporter,
+        false);
     assertNoEvents();
   }
 
@@ -576,8 +582,13 @@
         Maps.filterKeys(getTransitiveLicenses(used), AnalysisMock.get().ccSupport().labelFilter());
     Label usedLabel = Label.parseAbsolute("//used", ImmutableMap.of());
     License license = usedActual.get(usedLabel);
-    license.checkCompatibility(EnumSet.of(DistributionType.CLIENT),
-        getTarget("//user"), usedLabel, reporter, false);
+    LicenseCheckingModule.checkCompatibility(
+        license,
+        EnumSet.of(DistributionType.CLIENT),
+        getTarget("//user"),
+        usedLabel,
+        reporter,
+        false);
     assertNoEvents();
   }