make 'bazel canonicalize-flags' work with starlark options
PiperOrigin-RevId: 300674018
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java
index 15f6905..3afc697 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java
@@ -687,12 +687,7 @@
OptionsParser parser =
OptionsParser.builder()
.optionsData(optionsData)
- // for starlark options
- .skippedPrefix("--//")
- .skippedPrefix("--no//")
- // for starlark options in other repos
- .skippedPrefix("--@")
- .skippedPrefix("--no@")
+ .skipStarlarkOptionPrefixes()
.allowResidue(annotation.allowResidue())
.build();
return parser;
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/StarlarkOptionsParser.java b/src/main/java/com/google/devtools/build/lib/runtime/StarlarkOptionsParser.java
index cbf611a..cbaff9f 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/StarlarkOptionsParser.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/StarlarkOptionsParser.java
@@ -66,7 +66,7 @@
this.nativeOptionsParser = nativeOptionsParser;
}
- static StarlarkOptionsParser newStarlarkOptionsParser(
+ public static StarlarkOptionsParser newStarlarkOptionsParser(
CommandEnvironment env, OptionsParser optionsParser) {
return new StarlarkOptionsParser(
env.getSkyframeExecutor(),
@@ -75,6 +75,7 @@
optionsParser);
}
+ /** Parses all pre "--" residue for Starlark options. */
// TODO(juliexxia): This method somewhat reinvents the wheel of
// OptionsParserImpl.identifyOptionAndPossibleArgument. Consider combining. This would probably
// require multiple rounds of parsing to fit starlark-defined options into native option format.
@@ -98,8 +99,10 @@
parseArg(arg, unparsedArgs, unparsedOptions, eventHandler);
}
- residue.addAll(nativeOptionsParser.getPostDoubleDashResidue());
- nativeOptionsParser.setResidue(residue.build());
+
+ List<String> postDoubleDashResidue = nativeOptionsParser.getPostDoubleDashResidue();
+ residue.addAll(postDoubleDashResidue);
+ nativeOptionsParser.setResidue(residue.build(), postDoubleDashResidue);
if (unparsedOptions.isEmpty()) {
return;
@@ -187,9 +190,7 @@
}
private Target loadBuildSetting(
- String targetToBuild,
- OptionsParser optionsParser,
- ExtendedEventHandler eventHandler)
+ String targetToBuild, OptionsParser optionsParser, ExtendedEventHandler eventHandler)
throws OptionsParsingException {
Target buildSetting;
try {
@@ -227,7 +228,7 @@
@VisibleForTesting
public void setResidueForTesting(List<String> residue) {
- nativeOptionsParser.setResidue(residue);
+ nativeOptionsParser.setResidue(residue, ImmutableList.of());
}
@VisibleForTesting
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/CanonicalizeCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/CanonicalizeCommand.java
index 0e47837..e035988 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/CanonicalizeCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/CanonicalizeCommand.java
@@ -16,13 +16,16 @@
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.pkgcache.PackageCacheOptions;
import com.google.devtools.build.lib.runtime.BlazeCommand;
import com.google.devtools.build.lib.runtime.BlazeCommandResult;
import com.google.devtools.build.lib.runtime.BlazeCommandUtils;
import com.google.devtools.build.lib.runtime.BlazeRuntime;
import com.google.devtools.build.lib.runtime.Command;
import com.google.devtools.build.lib.runtime.CommandEnvironment;
+import com.google.devtools.build.lib.runtime.StarlarkOptionsParser;
import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
+import com.google.devtools.build.lib.util.AbruptExitException;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.common.options.InvocationPolicyEnforcer;
import com.google.devtools.common.options.InvocationPolicyParser;
@@ -35,20 +38,20 @@
import com.google.devtools.common.options.OptionsParsingResult;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
import java.util.logging.Level;
/** The 'blaze canonicalize-flags' command. */
@Command(
- name = "canonicalize-flags",
- options = {CanonicalizeCommand.Options.class},
- allowResidue = true,
- mustRunInWorkspace = false,
- shortDescription = "Canonicalizes a list of %{product} options.",
- help =
- "This command canonicalizes a list of %{product} options. Don't forget to prepend "
- + " '--' to end option parsing before the flags to canonicalize.\n"
- + "%{options}"
-)
+ name = "canonicalize-flags",
+ options = {CanonicalizeCommand.Options.class, PackageCacheOptions.class},
+ allowResidue = true,
+ mustRunInWorkspace = false,
+ shortDescription = "Canonicalizes a list of %{product} options.",
+ help =
+ "This command canonicalizes a list of %{product} options. Don't forget to prepend "
+ + " '--' to end option parsing before the flags to canonicalize.\n"
+ + "%{options}")
public final class CanonicalizeCommand implements BlazeCommand {
public static class Options extends OptionsBase {
@@ -143,10 +146,31 @@
command.getClass(), runtime.getBlazeModules(), runtime.getRuleClassProvider()))
.add(FlagClashCanaryOptions.class)
.build();
+
+ // set up the package cache for starlark options parsing
+ try {
+ env.setupPackageCache(options);
+ } catch (InterruptedException e) {
+ env.getReporter().handle(Event.error("canonicalization interrupted"));
+ return BlazeCommandResult.exitCode(ExitCode.INTERRUPTED);
+ } catch (AbruptExitException e) {
+ env.getReporter().handle(Event.error(null, "Unknown error: " + e.getMessage()));
+ return BlazeCommandResult.exitCode(e.getExitCode());
+ }
+
try {
OptionsParser parser =
- OptionsParser.builder().optionsClasses(optionsClasses).allowResidue(false).build();
+ OptionsParser.builder()
+ .optionsClasses(optionsClasses)
+ .skipStarlarkOptionPrefixes()
+ .allowResidue(true)
+ .build();
parser.parse(options.getResidue());
+ StarlarkOptionsParser.newStarlarkOptionsParser(env, parser).parse(env.getReporter());
+ if (!parser.getResidue().isEmpty()) {
+ String errorMsg = "Unrecognized arguments: " + Joiner.on(' ').join(parser.getResidue());
+ throw new OptionsParsingException(errorMsg);
+ }
InvocationPolicy policy =
InvocationPolicyParser.parsePolicy(canonicalizeOptions.invocationPolicy);
@@ -166,11 +190,14 @@
InvocationPolicyEnforcer.getEffectiveInvocationPolicy(
policy, parser, commandName, Level.INFO);
env.getReporter().getOutErr().printOutLn(effectivePolicy.toString());
-
} else {
// Otherwise, print out the canonical command line
- List<String> result = parser.canonicalize();
- for (String piece : result) {
+ List<String> nativeResult = parser.canonicalize();
+ ImmutableList.Builder<String> result = ImmutableList.<String>builder().addAll(nativeResult);
+ for (Map.Entry<String, Object> starlarkOption : parser.getStarlarkOptions().entrySet()) {
+ result.add("--" + starlarkOption.getKey() + "=" + starlarkOption.getValue());
+ }
+ for (String piece : result.build()) {
env.getReporter().getOutErr().printOutLn(piece);
}
}
diff --git a/src/main/java/com/google/devtools/common/options/OptionsParser.java b/src/main/java/com/google/devtools/common/options/OptionsParser.java
index 98fb74e..f47045b 100644
--- a/src/main/java/com/google/devtools/common/options/OptionsParser.java
+++ b/src/main/java/com/google/devtools/common/options/OptionsParser.java
@@ -22,6 +22,7 @@
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MoreCollectors;
import com.google.common.escape.Escaper;
@@ -188,6 +189,16 @@
return this;
}
+ /** Skip all the prefixes associated with Starlark options */
+ public Builder skipStarlarkOptionPrefixes() {
+ this.implBuilder
+ .skippedPrefix("--//")
+ .skippedPrefix("--no//")
+ .skippedPrefix("--@")
+ .skippedPrefix("--no@");
+ return this;
+ }
+
/**
* Indicates whether or not the parser will allow a non-empty residue; that is, iff this value
* is true then a call to one of the {@code parse} methods will throw {@link
@@ -219,7 +230,7 @@
private final List<String> residue = new ArrayList<>();
private final List<String> postDoubleDashResidue = new ArrayList<>();
private final boolean allowResidue;
- private final Map<String, Object> starlarkOptions = new HashMap<>();
+ private ImmutableSortedMap<String, Object> starlarkOptions = ImmutableSortedMap.of();
private OptionsParser(OptionsParserImpl impl, boolean allowResidue) {
this.impl = impl;
@@ -227,13 +238,12 @@
}
@Override
- public Map<String, Object> getStarlarkOptions() {
- return ImmutableMap.copyOf(starlarkOptions);
+ public ImmutableSortedMap<String, Object> getStarlarkOptions() {
+ return starlarkOptions;
}
public void setStarlarkOptions(Map<String, Object> starlarkOptions) {
- this.starlarkOptions.clear();
- this.starlarkOptions.putAll(starlarkOptions);
+ this.starlarkOptions = ImmutableSortedMap.copyOf(starlarkOptions);
}
public void parseAndExitUponError(String[] args) {
@@ -724,9 +734,15 @@
return ImmutableList.copyOf(postDoubleDashResidue);
}
- public void setResidue(List<String> residue) {
+ /* Sets the residue (all elements parsed as non-options) to {@code residue}, as well as the part
+ * of the residue that follows the double-dash on the command line, {@code postDoubleDashResidue}.
+ * {@code postDoubleDashResidue} must be a subset of {@code residue}. */
+ public void setResidue(List<String> residue, List<String> postDoubleDashResidue) {
+ Preconditions.checkArgument(residue.containsAll(postDoubleDashResidue));
this.residue.clear();
this.residue.addAll(residue);
+ this.postDoubleDashResidue.clear();
+ this.postDoubleDashResidue.addAll(postDoubleDashResidue);
}
/** Returns a list of warnings about problems encountered by previous parse calls. */
diff --git a/src/main/java/com/google/devtools/common/options/OptionsParsingResult.java b/src/main/java/com/google/devtools/common/options/OptionsParsingResult.java
index bdedc7f..b595a4b 100644
--- a/src/main/java/com/google/devtools/common/options/OptionsParsingResult.java
+++ b/src/main/java/com/google/devtools/common/options/OptionsParsingResult.java
@@ -58,6 +58,7 @@
* and the options it expanded to, and so blindly using this list for a new invocation will cause
* double-application of these options.
*/
+ // TODO(b/150222792): make this aware of starlark options.
List<ParsedOptionDescription> asCompleteListOfParsedOptions();
/**
@@ -68,6 +69,8 @@
*
* <p>The list includes undocumented options.
*/
+ // TODO(b/150222792): make this aware of Starlark options. This might be tricky because we don't
+ // store Starlark option values that are explicitly specified to the same value as the default.
List<ParsedOptionDescription> asListOfExplicitOptions();
/**
@@ -78,12 +81,14 @@
*
* <p>The list includes undocumented options.
*/
+ // TODO(b/150222792): make this aware of Starlark options.
List<ParsedOptionDescription> asListOfCanonicalOptions();
/**
* Returns a list of all options, including undocumented ones, and their effective values. There
* is no guaranteed ordering for the result.
*/
+ // TODO(b/150222792): make this aware of Starlark options
List<OptionValueDescription> asListOfOptionValues();
/**
@@ -95,5 +100,7 @@
* is unique, since some flags may have effects unknown to the parser (--config, for Bazel), so we
* do not reorder flags to further simplify the list.
*/
+ // TODO(b/150222792): make this aware of Starlark options. Note - `blaze canonicalize-flags`
+ // already emits Starlark options by doing some extra work on top of calling this method.
List<String> canonicalize();
}
diff --git a/src/test/java/com/google/devtools/build/lib/runtime/commands/TargetPatternsHelperTest.java b/src/test/java/com/google/devtools/build/lib/runtime/commands/TargetPatternsHelperTest.java
index 6a780cb..df7a071 100644
--- a/src/test/java/com/google/devtools/build/lib/runtime/commands/TargetPatternsHelperTest.java
+++ b/src/test/java/com/google/devtools/build/lib/runtime/commands/TargetPatternsHelperTest.java
@@ -82,7 +82,7 @@
@Test
public void testNoTargetPatternFile() throws TargetPatternsHelperException {
ImmutableList<String> patterns = ImmutableList.of("//some/...", "//patterns");
- options.setResidue(patterns);
+ options.setResidue(patterns, ImmutableList.of());
assertThat(TargetPatternsHelper.readFrom(env, options)).isEqualTo(patterns);
}
@@ -90,7 +90,7 @@
@Test
public void testSpecifyPatternAndFileThrows() throws OptionsParsingException {
options.parse("--target_pattern_file=patterns.txt");
- options.setResidue(ImmutableList.of("//some:pattern"));
+ options.setResidue(ImmutableList.of("//some:pattern"), ImmutableList.of());
TargetPatternsHelperException expected =
assertThrows(