Internal change

PiperOrigin-RevId: 162194755
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/CustomCommandLine.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/CustomCommandLine.java
index 2ed135b..b05b039 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/actions/CustomCommandLine.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/CustomCommandLine.java
@@ -411,22 +411,6 @@
     }
 
     /**
-     * For every element in `artifacts`, adds the `arg` followed by the exec path of the artifact.
-     *
-     * <p>Results in adding: <code>[arg, execpath1, arg, execpath2, ...]</code>.
-     *
-     * <p>Has no effect if `artifacts` is null or empty.
-     */
-    public Builder addExecPaths(String arg, @Nullable Iterable<Artifact> artifacts) {
-      Preconditions.checkNotNull(arg);
-      if (artifacts != null && !Iterables.isEmpty(artifacts)) {
-        arguments.add(
-            InterspersingArgs.fromExecPaths(artifacts, /*beforeEach=*/ arg, /*formatEach=*/ null));
-      }
-      return this;
-    }
-
-    /**
      * Adds a placeholder TreeArtifact exec path. When the command line is used in an action
      * template, the placeholder will be replaced by the exec path of a {@link TreeFileArtifact}
      * inside the TreeArtifact at execution time for each expanded action.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/LibraryRGeneratorActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/LibraryRGeneratorActionBuilder.java
index f715bed..52cab02 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/LibraryRGeneratorActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/LibraryRGeneratorActionBuilder.java
@@ -80,9 +80,11 @@
             .toList();
 
     if (!symbolProviders.isEmpty()) {
-      builder.addExecPaths("--symbol", symbolProviders);
-      inputs.addTransitive(NestedSetBuilder.wrap(Order.NAIVE_LINK_ORDER, symbolProviders));
+      builder
+          .add("--symbols")
+          .addJoinExecPaths(ruleContext.getConfiguration().getHostPathSeparator(), symbolProviders);
     }
+    inputs.addTransitive(NestedSetBuilder.wrap(Order.NAIVE_LINK_ORDER, symbolProviders));
 
     builder
         .addExecPath("--classJarOutput", rJavaClassJar)
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ResourceShrinkerActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceShrinkerActionBuilder.java
index 42f65dd..ef077eb 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/ResourceShrinkerActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceShrinkerActionBuilder.java
@@ -202,9 +202,12 @@
 
     List<Artifact> dependencyManifests = getManifests(dependencyResources);
     if (!dependencyManifests.isEmpty()) {
-      commandLine.addExecPaths("--dependencyManifest", dependencyManifests);
-      inputs.addAll(dependencyManifests);
+      commandLine
+          .add("--dependencyManifests")
+          .addJoinExecPaths(
+              ruleContext.getConfiguration().getHostPathSeparator(), dependencyManifests);
     }
+    inputs.addAll(dependencyManifests);
 
     List<String> resourcePackages = getResourcePackages(primaryResources, dependencyResources);
     if (!resourcePackages.isEmpty()) {
diff --git a/src/test/java/com/google/devtools/build/android/ConvertersTest.java b/src/test/java/com/google/devtools/build/android/ConvertersTest.java
index 0bb8366..92c9cf2 100644
--- a/src/test/java/com/google/devtools/build/android/ConvertersTest.java
+++ b/src/test/java/com/google/devtools/build/android/ConvertersTest.java
@@ -15,13 +15,18 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import com.google.common.base.Joiner;
 import com.google.devtools.build.android.Converters.ExistingPathConverter;
+import com.google.devtools.build.android.Converters.ExistingPathListConverter;
 import com.google.devtools.build.android.Converters.ExistingPathStringDictionaryConverter;
 import com.google.devtools.build.android.Converters.PathConverter;
+import com.google.devtools.build.android.Converters.PathListConverter;
 import com.google.devtools.build.android.Converters.StringDictionaryConverter;
 import com.google.devtools.common.options.OptionsParsingException;
+import java.io.File;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.List;
 import java.util.Map;
 import org.junit.Rule;
 import org.junit.Test;
@@ -36,6 +41,8 @@
 @RunWith(JUnit4.class)
 public final class ConvertersTest {
 
+  private static final String SEPARATOR = File.pathSeparator;
+
   @Rule public final TemporaryFolder tmp = new TemporaryFolder();
 
   @Rule
@@ -51,9 +58,9 @@
   @Test
   public void testPathConverter_invalid() throws Exception {
     String arg = "\u0000";
-    PathConverter converter = new PathConverter();
     expected.expect(OptionsParsingException.class);
     expected.expectMessage(String.format("%s is not a valid path:", arg));
+    PathConverter converter = new PathConverter();
     converter.convert(arg);
   }
 
@@ -67,9 +74,9 @@
   @Test
   public void testExistingPathConverter_nonExisting() throws Exception {
     String arg = "test_file";
-    ExistingPathConverter converter = new ExistingPathConverter();
     expected.expect(OptionsParsingException.class);
     expected.expectMessage(String.format("%s is not a valid path: it does not exist.", arg));
+    ExistingPathConverter converter = new ExistingPathConverter();
     converter.convert(arg);
   }
 
@@ -82,30 +89,50 @@
   }
 
   @Test
+  public void testPathListConverter() throws Exception {
+    PathListConverter converter = new PathListConverter();
+    List<Path> result =
+        converter.convert("foo" + SEPARATOR + "bar" + SEPARATOR + SEPARATOR + "baz" + SEPARATOR);
+    assertThat(result)
+        .containsAllOf(Paths.get("foo"), Paths.get("bar"), Paths.get("baz"))
+        .inOrder();
+  }
+
+  @Test
+  public void testExisingPathListConverter() throws Exception {
+    String arg = "non-existing";
+    Path existingFile = tmp.newFile("existing").toPath();
+    expected.expect(OptionsParsingException.class);
+    expected.expectMessage(String.format("%s is not a valid path: it does not exist.", arg));
+    ExistingPathListConverter converter = new ExistingPathListConverter();
+    converter.convert(Joiner.on(SEPARATOR).join(existingFile.toString(), arg));
+  }
+
+  @Test
   public void testStringDictionaryConverter_emptyEntry() throws Exception {
-    StringDictionaryConverter converter = new StringDictionaryConverter();
     expected.expect(OptionsParsingException.class);
     expected.expectMessage("Dictionary entry [] does not contain both a key and a value.");
+    StringDictionaryConverter converter = new StringDictionaryConverter();
     converter.convert("foo:bar,,baz:bar");
   }
 
   @Test
   public void testStringDictionaryConverter_missingKeyOrValue() throws Exception {
     String badEntry = "foo";
-    StringDictionaryConverter converter = new StringDictionaryConverter();
     expected.expect(OptionsParsingException.class);
     expected.expectMessage(String.format(
         "Dictionary entry [%s] does not contain both a key and a value.", badEntry));
+    StringDictionaryConverter converter = new StringDictionaryConverter();
     converter.convert(badEntry);
   }
 
   @Test
   public void testStringDictionaryConverter_extraFields() throws Exception {
     String badEntry = "foo:bar:baz";
-    StringDictionaryConverter converter = new StringDictionaryConverter();
     expected.expect(OptionsParsingException.class);
     expected.expectMessage(String.format(
         "Dictionary entry [%s] contains too many fields.", badEntry));
+    StringDictionaryConverter converter = new StringDictionaryConverter();
     converter.convert(badEntry);
   }
 
@@ -113,10 +140,10 @@
   public void testStringDictionaryConverter_duplicateKey() throws Exception {
     String key = "foo";
     String arg = String.format("%s:%s,%s:%s", key, "bar", key, "baz");
-    StringDictionaryConverter converter = new StringDictionaryConverter();
     expected.expect(OptionsParsingException.class);
     expected.expectMessage(String.format(
         "Dictionary already contains the key [%s].", key));
+    StringDictionaryConverter converter = new StringDictionaryConverter();
     converter.convert(arg);
   }
 
diff --git a/src/test/java/com/google/devtools/build/android/ideinfo/JarFilterTest.java b/src/test/java/com/google/devtools/build/android/ideinfo/JarFilterTest.java
index 2bb779f..a17048d 100644
--- a/src/test/java/com/google/devtools/build/android/ideinfo/JarFilterTest.java
+++ b/src/test/java/com/google/devtools/build/android/ideinfo/JarFilterTest.java
@@ -17,6 +17,7 @@
 import static com.google.common.truth.Truth.assertThat;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
+import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.io.Files;
@@ -105,7 +106,7 @@
 
     String[] args =
         new String[] {
-          "--jar",
+          "--jars",
           filterJar.getPath(),
           "--output",
           outputJar.getPath(),
@@ -197,17 +198,13 @@
 
     String[] args =
         new String[] {
-          "--keep_java_file",
-          fooJava.getPath(),
-          "--keep_java_file",
-          barJava.getPath(),
-          "--keep_source_jar",
-          srcJar.getPath(),
-          "--keep_source_jar",
-          src3Jar.getPath(),
-          "--filter_jar",
+          "--keep_java_files",
+          fooJava.getPath() + File.pathSeparator + barJava.getPath(),
+          "--keep_source_jars",
+          Joiner.on(File.pathSeparator).join(srcJar.getPath(), src3Jar.getPath()),
+          "--filter_jars",
           filterJar.getPath(),
-          "--filter_source_jar",
+          "--filter_source_jars",
           filterSrcJar.getPath(),
           "--filtered_jar",
           filteredJar.getPath(),
diff --git a/src/test/java/com/google/devtools/build/lib/actions/CustomCommandLineTest.java b/src/test/java/com/google/devtools/build/lib/actions/CustomCommandLineTest.java
index ea24af9..f7995b4 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/CustomCommandLineTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/CustomCommandLineTest.java
@@ -103,22 +103,14 @@
   }
 
   @Test
-  public void testArtifactExecPaths() {
-    CustomCommandLine cl =
-        CustomCommandLine.builder()
-            .addExecPaths(ImmutableList.of(artifact1, artifact2))
-            .build();
-    assertThat(cl.arguments()).isEqualTo(ImmutableList.of("dir/file1.txt", "dir/file2.txt"));
-  }
-
-  @Test
   public void testArtifactExecPathsArgs() {
     CustomCommandLine cl =
         CustomCommandLine.builder()
-            .addExecPaths("--path", ImmutableList.of(artifact1, artifact2))
+            .add("--path")
+            .addExecPaths(ImmutableList.of(artifact1, artifact2))
             .build();
     assertThat(cl.arguments())
-        .isEqualTo(ImmutableList.of("--path", "dir/file1.txt", "--path", "dir/file2.txt"));
+        .isEqualTo(ImmutableList.of("--path", "dir/file1.txt", "dir/file2.txt"));
   }
 
   @Test
@@ -203,8 +195,6 @@
             .add(ImmutableList.<String>of())
             .addExecPaths(null)
             .addExecPaths(ImmutableList.of())
-            .addExecPaths("foo", null)
-            .addExecPaths("foo", ImmutableList.of())
             .addExecPath("foo", null)
             .addPlaceholderTreeArtifactExecPath(null)
             .addJoinStrings("foo", null)
@@ -250,7 +240,6 @@
     npt.testMethod(obj, clazz.getMethod("addFormatEach", String.class, Iterable.class));
 
     npt.setDefault(Iterable.class, ImmutableList.of(artifact1));
-    npt.testMethod(obj, clazz.getMethod("addExecPaths", String.class, Iterable.class));
     npt.testMethod(obj, clazz.getMethod("addJoinExecPaths", String.class, Iterable.class));
     npt.testMethod(obj, clazz.getMethod("addBeforeEachExecPath", String.class, Iterable.class));
 
diff --git a/src/tools/android/java/com/google/devtools/build/android/Converters.java b/src/tools/android/java/com/google/devtools/build/android/Converters.java
index 9475449..63f5980 100644
--- a/src/tools/android/java/com/google/devtools/build/android/Converters.java
+++ b/src/tools/android/java/com/google/devtools/build/android/Converters.java
@@ -22,11 +22,14 @@
 import com.google.devtools.common.options.Converter;
 import com.google.devtools.common.options.EnumConverter;
 import com.google.devtools.common.options.OptionsParsingException;
+import java.io.File;
 import java.lang.reflect.ParameterizedType;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.InvalidPathException;
 import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -283,6 +286,49 @@
     }
   }
 
+  /**
+   * Validating converter for a list of Paths.
+   * A Path is considered valid if it resolves to a file.
+   */
+  public static class PathListConverter implements Converter<List<Path>> {
+
+    private final PathConverter baseConverter;
+
+    public PathListConverter() {
+      this(false);
+    }
+
+    protected PathListConverter(boolean mustExist) {
+      baseConverter = new PathConverter(mustExist);
+    }
+
+    @Override
+    public List<Path> convert(String input) throws OptionsParsingException {
+      List<Path> list = new ArrayList<>();
+      for (String piece : input.split(File.pathSeparator)) {
+        if (!piece.isEmpty()) {
+          list.add(baseConverter.convert(piece));
+        }
+      }
+      return Collections.unmodifiableList(list);
+    }
+
+    @Override
+    public String getTypeDescription() {
+      return "a colon-separated list of paths";
+    }
+  }
+
+  /**
+   * Validating converter for a list of Paths. The list is considered valid if all Paths resolve to
+   * a file that exists.
+   */
+  public static class ExistingPathListConverter extends PathListConverter {
+    public ExistingPathListConverter() {
+      super(true);
+    }
+  }
+
   // Commas that are not escaped by a backslash.
   private static final String UNESCAPED_COMMA_REGEX = "(?<!\\\\)\\,";
   // Colons that are not escaped by a backslash.
diff --git a/src/tools/android/java/com/google/devtools/build/android/LibraryRClassGeneratorAction.java b/src/tools/android/java/com/google/devtools/build/android/LibraryRClassGeneratorAction.java
index a755587..9354547 100644
--- a/src/tools/android/java/com/google/devtools/build/android/LibraryRClassGeneratorAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/LibraryRClassGeneratorAction.java
@@ -15,10 +15,10 @@
 
 import com.google.common.base.Stopwatch;
 import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
 import com.google.devtools.build.android.AndroidResourceMerger.MergingException;
 import com.google.devtools.build.android.AndroidResourceProcessor.AaptConfigOptions;
 import com.google.devtools.build.android.Converters.PathConverter;
+import com.google.devtools.build.android.Converters.PathListConverter;
 import com.google.devtools.common.options.Option;
 import com.google.devtools.common.options.OptionDocumentationCategory;
 import com.google.devtools.common.options.OptionsBase;
@@ -68,10 +68,9 @@
     public String packageForR;
 
     @Option(
-      name = "symbol",
-      allowMultiple = true,
+      name = "symbols",
       defaultValue = "",
-      converter = PathConverter.class,
+      converter = PathListConverter.class,
       documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
       effectTags = {OptionEffectTag.UNKNOWN},
       category = "config",
@@ -88,9 +87,6 @@
     optionsParser.parseAndExitUponError(args);
     AaptConfigOptions aaptConfigOptions = optionsParser.getOptions(AaptConfigOptions.class);
     Options options = optionsParser.getOptions(Options.class);
-    if (options.symbols == null) {
-      options.symbols = ImmutableList.of();
-    }
     logger.fine(
         String.format("Option parsing finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
     try (ScopedTemporaryDirectory scopedTmp =
diff --git a/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java b/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java
index 87854b4..7abbb0c 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java
@@ -26,6 +26,7 @@
 import com.google.devtools.build.android.AndroidResourceProcessor.FlagAaptOptions;
 import com.google.devtools.build.android.Converters.ExistingPathConverter;
 import com.google.devtools.build.android.Converters.PathConverter;
+import com.google.devtools.build.android.Converters.PathListConverter;
 import com.google.devtools.build.android.Converters.VariantTypeConverter;
 import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter;
 import com.google.devtools.common.options.Option;
@@ -62,7 +63,7 @@
  *       --resources path to processed resources zip
  *       --rTxt path to processed resources R.txt
  *       --primaryManifest path to processed resources AndroidManifest.xml
- *       --dependencyManifest path to dependency library manifest (repeated flag)
+ *       --dependencyManifests paths to dependency library manifests
  *       --shrunkResourceApk path to write shrunk ap_
  *       --shrunkResources path to write shrunk resources zip
  * </pre>
@@ -129,14 +130,13 @@
     public Path primaryManifest;
 
     @Option(
-      name = "dependencyManifest",
-      allowMultiple = true,
+      name = "dependencyManifests",
       defaultValue = "",
       category = "input",
-      converter = PathConverter.class,
+      converter = PathListConverter.class,
       documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
       effectTags = {OptionEffectTag.UNKNOWN},
-      help = "Paths to the manifests of the dependencies. Specify one path per flag."
+      help = "A list of paths to the manifests of the dependencies."
     )
     public List<Path> dependencyManifests;
 
@@ -236,9 +236,6 @@
     optionsParser.parseAndExitUponError(args);
     aaptConfigOptions = optionsParser.getOptions(AaptConfigOptions.class);
     options = optionsParser.getOptions(Options.class);
-    if (options.dependencyManifests == null) {
-      options.dependencyManifests = ImmutableList.of();
-    }
 
     AndroidResourceProcessor resourceProcessor = new AndroidResourceProcessor(stdLogger);
     // Setup temporary working directories.
diff --git a/src/tools/android/java/com/google/devtools/build/android/ideinfo/JarFilter.java b/src/tools/android/java/com/google/devtools/build/android/ideinfo/JarFilter.java
index 976649f..354fddf 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ideinfo/JarFilter.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ideinfo/JarFilter.java
@@ -27,6 +27,7 @@
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.devtools.build.android.Converters.PathConverter;
+import com.google.devtools.build.android.Converters.PathListConverter;
 import com.google.devtools.build.lib.ideinfo.androidstudio.PackageManifestOuterClass.ArtifactLocation;
 import com.google.devtools.build.lib.ideinfo.androidstudio.PackageManifestOuterClass.JavaSourcePackage;
 import com.google.devtools.build.lib.ideinfo.androidstudio.PackageManifestOuterClass.PackageManifest;
@@ -64,58 +65,46 @@
   /** The options for a {@JarFilter} action. */
   public static final class JarFilterOptions extends OptionsBase {
     @Option(
-      name = "filter_jar",
-      allowMultiple = true,
+      name = "filter_jars",
       documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
       effectTags = {OptionEffectTag.UNKNOWN},
       defaultValue = "null",
-      converter = PathConverter.class,
+      converter = PathListConverter.class,
       category = "input",
-      help =
-          "Paths to target output jars to filter for generated sources. You may use this flag "
-              + "multiple times, specify each path with a separate instance of the flag."
+      help = "A list of the paths to target output jars to filter for generated sources."
     )
     public List<Path> filterJars;
 
     @Option(
-      name = "filter_source_jar",
-      allowMultiple = true,
+      name = "filter_source_jars",
       documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
       effectTags = {OptionEffectTag.UNKNOWN},
       defaultValue = "null",
-      converter = PathConverter.class,
+      converter = PathListConverter.class,
       category = "input",
-      help =
-          "Paths to target output source jars to filter for generated sources. You may use this "
-              + "flag multiple times, specify each path with a separate instance of the flag."
+      help = "A list of the paths to target output source jars to filter for generated sources."
     )
     public List<Path> filterSourceJars;
 
     @Option(
-      name = "keep_java_file",
-      allowMultiple = true,
+      name = "keep_java_files",
       documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
       effectTags = {OptionEffectTag.UNKNOWN},
       defaultValue = "null",
-      converter = PathConverter.class,
+      converter = PathListConverter.class,
       category = "input",
-      help =
-          "Path of target input java files to keep. You may use this flag multiple times, "
-              + "specify each path with a separate instance of the flag."
+      help = "A list of target input java files to keep."
     )
     public List<Path> keepJavaFiles;
 
     @Option(
-      name = "keep_source_jar",
-      allowMultiple = true,
+      name = "keep_source_jars",
       documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
       effectTags = {OptionEffectTag.UNKNOWN},
       defaultValue = "null",
-      converter = PathConverter.class,
+      converter = PathListConverter.class,
       category = "input",
-      help =
-          "Path of target input .srcjar files to keep. You may use this flag multiple times, "
-              + "specify each path with a separate instance of the flag."
+      help = "A list of target input .srcjar files to keep."
     )
     public List<Path> keepSourceJars;
 
@@ -146,12 +135,11 @@
 
     @Deprecated
     @Option(
-      name = "jar",
-      allowMultiple = true,
+      name = "jars",
       documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
       effectTags = {OptionEffectTag.UNKNOWN},
       defaultValue = "null",
-      converter = PathConverter.class,
+      converter = PathListConverter.class,
       category = "input",
       help = "A list of the paths to jars to filter for generated sources."
     )
@@ -247,25 +235,10 @@
     args = parseParamFileIfUsed(args);
     OptionsParser optionsParser = OptionsParser.newOptionsParser(JarFilterOptions.class);
     optionsParser.parseAndExitUponError(args);
-    JarFilterOptions options = optionsParser.getOptions(JarFilterOptions.class);
 
-    if (options.filterJars == null) {
-      options.filterJars = ImmutableList.of();
-    }
-    if (options.filterSourceJars == null) {
-      options.filterSourceJars = ImmutableList.of();
-    }
-    if (options.keepJavaFiles == null) {
-      options.keepJavaFiles = ImmutableList.of();
-    }
-    if (options.keepSourceJars == null) {
-      options.keepSourceJars = ImmutableList.of();
-    }
-    if (options.jars == null) {
-      options.jars = ImmutableList.of();
-    }
     // Migrate options from v1 jar filter
-    if (options.filterJars.isEmpty() && options.jars != null) {
+    JarFilterOptions options = optionsParser.getOptions(JarFilterOptions.class);
+    if (options.filterJars == null && options.jars != null) {
       options.filterJars = options.jars;
     }
     if (options.filteredJar == null && options.output != null) {
diff --git a/src/tools/android/java/com/google/devtools/build/android/ideinfo/PackageParser.java b/src/tools/android/java/com/google/devtools/build/android/ideinfo/PackageParser.java
index 46b4702..a248d45 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ideinfo/PackageParser.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ideinfo/PackageParser.java
@@ -22,6 +22,7 @@
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.devtools.build.android.Converters.PathConverter;
+import com.google.devtools.build.android.Converters.PathListConverter;
 import com.google.devtools.build.lib.ideinfo.androidstudio.PackageManifestOuterClass.ArtifactLocation;
 import com.google.devtools.build.lib.ideinfo.androidstudio.PackageManifestOuterClass.JavaSourcePackage;
 import com.google.devtools.build.lib.ideinfo.androidstudio.PackageManifestOuterClass.PackageManifest;
@@ -80,16 +81,15 @@
     public Path outputManifest;
 
     @Option(
-      name = "sources_execution_path",
-      allowMultiple = true,
+      name = "sources_execution_paths",
       defaultValue = "null",
-      converter = PathConverter.class,
+      converter = PathListConverter.class,
       category = "input",
       documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
       effectTags = {OptionEffectTag.UNKNOWN},
       help =
-          "The execution paths of the java source files. You may use this flag multiple times, "
-              + "each instance should specify one path."
+          "The execution paths of the java source files. The expected format is a "
+              + "colon-separated list."
     )
     public List<Path> sourcesExecutionPaths;
   }
diff --git a/tools/ide/intellij_info_impl.bzl b/tools/ide/intellij_info_impl.bzl
index 6ec17f7..0ceec5c 100644
--- a/tools/ide/intellij_info_impl.bzl
+++ b/tools/ide/intellij_info_impl.bzl
@@ -451,18 +451,18 @@
   filtered_jar = ctx.new_file(target.label.name + "-filtered-gen.jar")
   filtered_source_jar = ctx.new_file(target.label.name + "-filtered-gen-src.jar")
   args = []
-  for jar in jar_artifacts:
-    args += ["--filter_jar", jar.path]
-  for jar in source_jar_artifacts:
-    args += ["--filter_source_jar", jar.path]
+  args += ["--filter_jars"]
+  args += [":".join([jar.path for jar in jar_artifacts])]
+  args += ["--filter_source_jars"]
+  args += [":".join([jar.path for jar in source_jar_artifacts])]
   args += ["--filtered_jar", filtered_jar.path]
   args += ["--filtered_source_jar", filtered_source_jar.path]
   if gen_java_sources:
-    for java_file in gen_java_sources:
-      args += ["--keep_java_file", java_file.path]
+    args += ["--keep_java_files"]
+    args += [":".join([java_file.path for java_file in gen_java_sources])]
   if srcjars:
-    for source_jar in srcjars:
-      args += ["--keep_source_jar", source_jar.path]
+    args += ["--keep_source_jars"]
+    args += [":".join([source_jar.path for source_jar in srcjars])]
   ctx.action(
       inputs = jar_artifacts + source_jar_artifacts + gen_java_sources + srcjars,
       outputs = [filtered_jar, filtered_source_jar],