Add --experimental_fix_deps_tool flag to Bazel

RELNOTES: None
PiperOrigin-RevId: 187936071
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java
index a4eb03e..d47fe0d 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java
@@ -68,6 +68,7 @@
   private final JavaSemantics semantics;
   private final ImmutableList<Artifact> additionalJavaBaseInputs;
   private final StrictDepsMode strictJavaDeps;
+  private final String fixDepsTool;
 
   private static final String DEFAULT_ATTRIBUTES_SUFFIX = "";
   private static final PathFragment JAVAC = PathFragment.create("_javac");
@@ -91,6 +92,7 @@
     this.strictJavaDeps = disableStrictDeps
         ? StrictDepsMode.OFF
         : getJavaConfiguration().getFilteredStrictJavaDeps();
+    this.fixDepsTool = getJavaConfiguration().getFixDepsTool();
   }
 
   public JavaCompilationHelper(RuleContext ruleContext, JavaSemantics semantics,
@@ -226,6 +228,7 @@
     builder.setProcessorPaths(attributes.getProcessorPath());
     builder.addProcessorNames(attributes.getProcessorNames());
     builder.setStrictJavaDeps(attributes.getStrictJavaDeps());
+    builder.setFixDepsTool(getJavaConfiguration().getFixDepsTool());
     builder.setDirectJars(attributes.getDirectJars());
     builder.setCompileTimeDependencyArtifacts(attributes.getCompileTimeDependencyArtifacts());
     builder.setTargetLabel(
@@ -755,6 +758,11 @@
     return strictJavaDeps;
   }
 
+  /** Determines which tool to use when fixing dependency errors. */
+  public String getFixDepsTool() {
+    return fixDepsTool;
+  }
+
   /**
    * Gets the value of the "javacopts" attribute combining them with the
    * default options. If the current rule has no javacopts attribute, this
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
index 9846e0f..1c02c96 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
@@ -137,6 +137,9 @@
    */
   private final BuildConfiguration.StrictDepsMode strictJavaDeps;
 
+  /** The tool with which to fix dependency errors. */
+  private final String fixDepsTool;
+
   /** The set of .jdeps artifacts provided by direct dependencies. */
   private final NestedSet<Artifact> compileTimeDependencyArtifacts;
 
@@ -164,6 +167,7 @@
    * @param directJars the subset of classpath jars provided by direct dependencies
    * @param executionInfo the execution info
    * @param strictJavaDeps the Strict Java Deps mode
+   * @param fixDepsTool the tool with which to fix dependency errors
    * @param compileTimeDependencyArtifacts the jdeps files for direct dependencies
    * @param progressMessage the progress message
    */
@@ -190,6 +194,7 @@
       NestedSet<Artifact> directJars,
       Map<String, String> executionInfo,
       StrictDepsMode strictJavaDeps,
+      String fixDepsTool,
       NestedSet<Artifact> compileTimeDependencyArtifacts,
       CharSequence progressMessage,
       RunfilesSupplier runfilesSupplier) {
@@ -225,6 +230,7 @@
     this.javacOpts = ImmutableList.copyOf(javacOpts);
     this.directJars = checkNotNull(directJars, "directJars must not be null");
     this.strictJavaDeps = strictJavaDeps;
+    this.fixDepsTool = checkNotNull(fixDepsTool);
     this.compileTimeDependencyArtifacts = compileTimeDependencyArtifacts;
   }
 
@@ -296,6 +302,10 @@
     return strictJavaDeps;
   }
 
+  public String getFixDepsTool() {
+    return fixDepsTool;
+  }
+
   public PathFragment getClassDirectory() {
     return classDirectory;
   }
@@ -422,6 +432,7 @@
     private final Collection<Artifact> sourceJars = new ArrayList<>();
     private BuildConfiguration.StrictDepsMode strictJavaDeps =
         BuildConfiguration.StrictDepsMode.OFF;
+    private String fixDepsTool = "add_dep";
     private NestedSet<Artifact> directJars = NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER);
     private NestedSet<Artifact> compileTimeDependencyArtifacts =
         NestedSetBuilder.emptySet(Order.STABLE_ORDER);
@@ -557,7 +568,6 @@
             owner, artifactForExperimentalCoverage, sourceFiles, false));
       }
 
-
       NestedSet<Artifact> tools =
           NestedSetBuilder.<Artifact>stableOrder()
               .add(langtoolsJar)
@@ -605,6 +615,7 @@
           directJars,
           executionInfo,
           strictJavaDeps,
+          fixDepsTool,
           compileTimeDependencyArtifacts,
           getProgressMessage(),
           javaBuilder.getRunfilesSupplier());
@@ -699,6 +710,7 @@
           }
         }
       }
+      result.add("--experimental_fix_deps_tool", fixDepsTool);
 
       // Chose what artifact to pass to JavaBuilder, as input to jacoco instrumentation processor.
       // metadata should be null when --experimental_java_coverage is true.
@@ -844,6 +856,12 @@
       return this;
     }
 
+    /** Sets the tool with which to fix dependency errors. */
+    public Builder setFixDepsTool(String depsTool) {
+      fixDepsTool = depsTool;
+      return this;
+    }
+
     /** Accumulates the given jar artifacts as being provided by direct dependencies. */
     public Builder setDirectJars(NestedSet<Artifact> directJars) {
       this.directJars = checkNotNull(directJars, "directJars must not be null");
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaConfiguration.java
index 2d1eb2e..026df11 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaConfiguration.java
@@ -156,6 +156,7 @@
   private final ImmutableList<String> defaultJvmFlags;
   private final ImmutableList<String> checkedConstraints;
   private final StrictDepsMode strictJavaDeps;
+  private final String fixDepsTool;
   private final Label proguardBinary;
   private final ImmutableList<Label> extraProguardSpecs;
   private final TriState bundleTranslations;
@@ -187,6 +188,7 @@
     this.defaultJvmFlags = ImmutableList.copyOf(javaOptions.jvmOpts);
     this.checkedConstraints = ImmutableList.copyOf(javaOptions.checkedConstraints);
     this.strictJavaDeps = javaOptions.strictJavaDeps;
+    this.fixDepsTool = javaOptions.fixDepsTool;
     this.proguardBinary = javaOptions.proguard;
     this.extraProguardSpecs = ImmutableList.copyOf(javaOptions.extraProguardSpecs);
     this.bundleTranslations = javaOptions.bundleTranslations;
@@ -242,6 +244,7 @@
       ImmutableList<String> defaultJvmFlags,
       ImmutableList<String> checkedConstraints,
       StrictDepsMode strictJavaDeps,
+      String fixDepsTool,
       Label proguardBinary,
       ImmutableList<Label> extraProguardSpecs,
       TriState bundleTranslations,
@@ -269,6 +272,7 @@
     this.defaultJvmFlags = defaultJvmFlags;
     this.checkedConstraints = checkedConstraints;
     this.strictJavaDeps = strictJavaDeps;
+    this.fixDepsTool = fixDepsTool;
     this.proguardBinary = proguardBinary;
     this.extraProguardSpecs = extraProguardSpecs;
     this.bundleTranslations = bundleTranslations;
@@ -376,6 +380,11 @@
     }
   }
 
+  /** Which tool to use for fixing dependency errors. */
+  public String getFixDepsTool() {
+    return fixDepsTool;
+  }
+
   /**
    * @return proper label only if --java_launcher= is specified, otherwise null.
    */
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaOptions.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaOptions.java
index 3e9484d..58be271 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaOptions.java
@@ -240,6 +240,15 @@
   )
   public StrictDepsMode strictJavaDeps;
 
+  @Option(
+    name = "experimental_fix_deps_tool",
+    defaultValue = "add_dep",
+    documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+    effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
+    help = "Specifies which tool should be used to resolve missing dependencies."
+  )
+  public String fixDepsTool;
+
   // TODO(bazel-team): This flag should ideally default to true (and eventually removed). We have
   // been accidentally supplying JUnit and Hamcrest deps to java_test targets indirectly via the
   // BazelTestRunner, and setting this flag to true fixes that behaviour.
@@ -558,6 +567,7 @@
     host.javaClasspath = javaClasspath;
 
     host.strictJavaDeps = strictJavaDeps;
+    host.fixDepsTool = fixDepsTool;
 
     host.enforceOneVersion = enforceOneVersion;
     // java_test targets can be used as a host tool, Ex: as a validating tool on a genrule.
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
index 814a46f..1b5a01c 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
@@ -2830,6 +2830,56 @@
   }
 
   @Test
+  public void testFixDepsToolFlag() throws Exception {
+    useConfiguration("--experimental_fix_deps_tool=autofixer");
+
+    scratch.file("java/foo/A.java", "foo");
+    scratch.file(
+        "java/foo/BUILD",
+        "android_binary(name = 'a', manifest = 'AndroidManifest.xml', ",
+        "  srcs = ['A.java'])");
+
+    Iterable<String> commandLine =
+        ((JavaCompileAction)
+                actionsTestUtil()
+                    .getActionForArtifactEndingWith(
+                        actionsTestUtil()
+                            .artifactClosureOf(
+                                getGeneratingAction(
+                                        getFileConfiguredTarget("//java/foo:a_deploy.jar")
+                                            .getArtifact())
+                                    .getInputs()),
+                        "liba.jar"))
+            .buildCommandLine();
+
+    assertThat(commandLine).containsAllOf("--experimental_fix_deps_tool", "autofixer").inOrder();
+  }
+
+  @Test
+  public void testFixDepsToolFlagEmpty() throws Exception {
+    scratch.file("java/foo/A.java", "foo");
+    scratch.file(
+        "java/foo/BUILD",
+        "android_binary(name = 'a', manifest = 'AndroidManifest.xml', ",
+        "  srcs = ['A.java'])");
+
+    Iterable<String> commandLine =
+        ((JavaCompileAction)
+                actionsTestUtil()
+                    .getActionForArtifactEndingWith(
+                        actionsTestUtil()
+                            .artifactClosureOf(
+                                getGeneratingAction(
+                                        getFileConfiguredTarget("//java/foo:a_deploy.jar")
+                                            .getArtifact())
+                                    .getInputs()),
+                        "liba.jar"))
+            .buildCommandLine();
+
+    assertThat(commandLine).containsAllOf("--experimental_fix_deps_tool", "add_dep").inOrder();
+  }
+
+  @Test
   public void testAndroidBinaryExportsJavaCompilationArgsProvider() throws Exception {
 
     scratch.file("java/foo/A.java", "foo");
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidLibraryTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidLibraryTest.java
index 18079f7..d99b56d 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidLibraryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidLibraryTest.java
@@ -211,6 +211,25 @@
   }
 
   @Test
+  public void testFixDepsToolEmpty() throws Exception {
+    scratch.file("java/android/BUILD", "android_library(name = 'b', srcs = ['B.java'])");
+    List<String> commandLine =
+        getGeneratingSpawnActionArgs(
+            getFileConfiguredTarget("//java/android:libb.jar").getArtifact());
+    assertThat(commandLine).containsAllOf("--experimental_fix_deps_tool", "add_dep").inOrder();
+  }
+
+  @Test
+  public void testFixDepsTool() throws Exception {
+    useConfiguration("--experimental_fix_deps_tool=auto_fixer");
+    scratch.file("java/android/BUILD", "android_library(name = 'b', srcs = ['B.java'])");
+    List<String> commandLine =
+        getGeneratingSpawnActionArgs(
+            getFileConfiguredTarget("//java/android:libb.jar").getArtifact());
+    assertThat(commandLine).containsAllOf("--experimental_fix_deps_tool", "auto_fixer").inOrder();
+  }
+
+  @Test
   public void testJavaPluginProcessorPath() throws Exception {
     scratch.file("java/test/BUILD",
         "java_library(name = 'plugin_dep',",