Adds a mechanism for invocation policy. The policy is taken through the --invocation_policy startup flag and allows an application invoking Bazel to set or override flag values (whether from the command line or a bazelrc).

--
MOS_MIGRATED_REVID=104160290
diff --git a/src/main/java/com/google/devtools/common/options/Option.java b/src/main/java/com/google/devtools/common/options/Option.java
index f41a051..ca3add9 100644
--- a/src/main/java/com/google/devtools/common/options/Option.java
+++ b/src/main/java/com/google/devtools/common/options/Option.java
@@ -62,6 +62,9 @@
    * be a compile-time constant.)  This special interpretation of the string
    * "null" is only applicable when computing the default value; if specified
    * on the command-line, this string will have its usual literal meaning.
+   *
+   * <p>The default value for flags that set allowMultiple to true is always
+   * the empty list and the value in the annotation is ignored. 
    */
   String defaultValue();
 
@@ -90,6 +93,9 @@
    * converter for this option must either match the parameter {@code T} or
    * {@code List<T>}. In the latter case the individual lists are concatenated
    * to form the full options value.
+   *
+   * <p>The {@link #defaultValue()} field of the annotation is ignored for repeatable
+   * flags and the default value will be the empty list.
    */
   boolean allowMultiple() default false;
 
diff --git a/src/main/java/com/google/devtools/common/options/OptionPriority.java b/src/main/java/com/google/devtools/common/options/OptionPriority.java
index b352e88..a28f012 100644
--- a/src/main/java/com/google/devtools/common/options/OptionPriority.java
+++ b/src/main/java/com/google/devtools/common/options/OptionPriority.java
@@ -50,9 +50,13 @@
   COMMAND_LINE,
 
   /**
+   * For options coming from invocation policy.
+   */
+  INVOCATION_POLICY,
+
+  /**
    * This priority can be used to unconditionally override any user-provided options.
    * This should be used rarely and with caution!
    */
   SOFTWARE_REQUIREMENT;
-
 }
diff --git a/src/main/java/com/google/devtools/common/options/OptionsData.java b/src/main/java/com/google/devtools/common/options/OptionsData.java
index d5512a1..61d798a 100644
--- a/src/main/java/com/google/devtools/common/options/OptionsData.java
+++ b/src/main/java/com/google/devtools/common/options/OptionsData.java
@@ -71,12 +71,19 @@
    */
   private final Map<Field, Converter<?>> converters;
 
+  /**
+   * Mapping from each Option-annotated field to a boolean for whether that field allows multiple
+   * values.
+   */
+  private final Map<Field, Boolean> allowMultiple;
+  
   private OptionsData(Map<Class<? extends OptionsBase>, Constructor<?>> optionsClasses,
                       Map<String, Field> nameToField,
                       Map<Character, Field> abbrevToField,
                       Map<Class<? extends OptionsBase>, List<Field>> allOptionsFields,
                       Map<Field, Object> optionDefaults,
-                      Map<Field, Converter<?>> converters) {
+                      Map<Field, Converter<?>> converters,
+                      Map<Field, Boolean> allowMultiple) {
     this.optionsClasses = ImmutableMap.copyOf(optionsClasses);
     this.allOptionsFields = ImmutableMap.copyOf(allOptionsFields);
     this.nameToField = ImmutableMap.copyOf(nameToField);
@@ -84,6 +91,7 @@
     // Can't use an ImmutableMap here because of null values.
     this.optionDefaults = Collections.unmodifiableMap(optionDefaults);
     this.converters = ImmutableMap.copyOf(converters);
+    this.allowMultiple = ImmutableMap.copyOf(allowMultiple);
   }
 
   public Collection<Class<? extends OptionsBase>> getOptionsClasses() {
@@ -119,6 +127,10 @@
     return converters.get(field);
   }
 
+  public boolean getAllowMultiple(Field field) {
+    return allowMultiple.get(field);
+  }
+  
   private static List<Field> getAllAnnotatedFields(Class<? extends OptionsBase> optionsClass) {
     List<Field> allFields = Lists.newArrayList();
     for (Field field : optionsClass.getFields()) {
@@ -157,6 +169,7 @@
     Map<Character, Field> abbrevToFieldBuilder = Maps.newHashMap();
     Map<Field, Object> optionDefaultsBuilder = Maps.newHashMap();
     Map<Field, Converter<?>> convertersBuilder = Maps.newHashMap();
+    Map<Field, Boolean> allowMultipleBuilder = Maps.newHashMap();
 
     // Read all Option annotations:
     for (Class<? extends OptionsBase> parsedOptionsClass : classes) {
@@ -256,9 +269,11 @@
         optionDefaultsBuilder.put(field, retrieveDefaultFromAnnotation(field));
 
         convertersBuilder.put(field, OptionsParserImpl.findConverter(field));
+        
+        allowMultipleBuilder.put(field, annotation.allowMultiple());
       }
     }
     return new OptionsData(constructorBuilder, nameToFieldBuilder, abbrevToFieldBuilder,
-        allOptionsFieldsBuilder, optionDefaultsBuilder, convertersBuilder);
+        allOptionsFieldsBuilder, optionDefaultsBuilder, convertersBuilder, allowMultipleBuilder);
   }
 }
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 80e56cb..400adee 100644
--- a/src/main/java/com/google/devtools/common/options/OptionsParser.java
+++ b/src/main/java/com/google/devtools/common/options/OptionsParser.java
@@ -187,6 +187,41 @@
   }
 
   /**
+   * The metadata about an option.
+   */
+  public static final class OptionDescription {
+
+    private final String name;
+    private final Object defaultValue;
+    private final Converter<?> converter;
+    private final boolean allowMultiple;
+
+    public OptionDescription(String name, Object defaultValue, Converter<?> converter,
+        boolean allowMultiple) {
+      this.name = name;
+      this.defaultValue = defaultValue;
+      this.converter = converter;
+      this.allowMultiple = allowMultiple;
+    }
+
+    public String getName() {
+      return name;
+    }
+
+    public Object getDefaultValue() {
+      return defaultValue;
+    }
+
+    public Converter<?> getConverter() {
+      return converter;
+    }
+
+    public boolean getAllowMultiple() {
+      return allowMultiple;
+    }
+  }
+  
+  /**
    * The name and value of an option with additional metadata describing its
    * priority, source, whether it was set via an implicit dependency, and if so,
    * by which other option.
@@ -217,10 +252,16 @@
       return value;
     }
 
+    /**
+     * @return the priority of the thing that set this value for this flag
+     */
     public OptionPriority getPriority() {
       return priority;
     }
 
+    /**
+     * @return the thing that set this value for this flag
+     */
     public String getSource() {
       return source;
     }
@@ -450,10 +491,24 @@
   }
 
   /**
+   * Returns a description of the option.
+   *
+   * @return The {@link OptionValueDescription} for the option, or null if there is no option by
+   *        the given name.
+   */
+  public OptionDescription getOptionDescription(String name) {
+    return impl.getOptionDescription(name);
+  }
+
+  /**
    * Returns a description of the option value set by the last previous call to
    * {@link #parse(OptionPriority, String, List)} that successfully set the given
    * option. If the option is of type {@link List}, the description will
    * correspond to any one of the calls, but not necessarily the last.
+   *
+   * @return The {@link OptionValueDescription} for the option, or null if the value has not been
+   *        set.
+   * @throws IllegalArgumentException if there is no option by the given name.
    */
   public OptionValueDescription getOptionValueDescription(String name) {
     return impl.getOptionValueDescription(name);
@@ -520,6 +575,23 @@
     }
   }
 
+  /**
+   * Clears the given option. Also clears expansion arguments and implicit requirements for that
+   * option.
+   *
+   * <p>This will not affect options objects that have already been retrieved from this parser
+   * through {@link #getOptions(Class)}.
+   *
+   * @param optionName The full name of the option to clear.
+   * @return A map of an option name to the old value of the options that were cleared.
+   * @throws IllegalArgumentException If the flag does not exist.
+   */
+  public Map<String, OptionValueDescription> clearValue(String optionName) {
+    Map<String, OptionValueDescription> clearedValues = Maps.newHashMap();
+    impl.clearValue(optionName, clearedValues);
+    return clearedValues;
+  }
+
   @Override
   public List<String> getResidue() {
     return ImmutableList.copyOf(residue);
diff --git a/src/main/java/com/google/devtools/common/options/OptionsParserImpl.java b/src/main/java/com/google/devtools/common/options/OptionsParserImpl.java
index bae5293..59d4a0c 100644
--- a/src/main/java/com/google/devtools/common/options/OptionsParserImpl.java
+++ b/src/main/java/com/google/devtools/common/options/OptionsParserImpl.java
@@ -25,6 +25,7 @@
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.google.devtools.common.options.OptionsParser.OptionDescription;
 import com.google.devtools.common.options.OptionsParser.OptionValueDescription;
 import com.google.devtools.common.options.OptionsParser.UnparsedOptionValueDescription;
 
@@ -433,6 +434,28 @@
     entry.addValue(priority, value);
   }
 
+  void clearValue(String optionName, Map<String, OptionValueDescription> clearedValues) {
+    Field field = optionsData.getFieldFromName(optionName);
+    if (field == null) {
+      throw new IllegalArgumentException("No such option '" + optionName + "'");
+    }
+
+    ParsedOptionEntry removed = parsedValues.remove(field);
+    if (removed != null) {
+      clearedValues.put(optionName, removed.asOptionValueDescription(optionName));
+    }
+
+    // Recurse to remove any implicit or expansion flags that this flag may have added when
+    // originally parsed.
+    Option option = field.getAnnotation(Option.class);
+    for (String implicitRequirement : option.implicitRequirements()) {
+      clearValue(implicitRequirement, clearedValues);
+    }
+    for (String expansion : option.expansion()) {
+      clearValue(expansion, clearedValues);
+    }
+  }
+
   private Object getValue(Field field) {
     ParsedOptionEntry entry = parsedValues.get(field);
     return entry == null ? null : entry.getValue();
@@ -450,6 +473,20 @@
     return entry.asOptionValueDescription(name);
   }
 
+  OptionDescription getOptionDescription(String name) {
+    Field field = optionsData.getFieldFromName(name);
+    if (field == null) {
+      return null;
+    }
+
+    Option optionAnnotation = field.getAnnotation(Option.class);
+    return new OptionDescription(
+        name,
+        optionsData.getDefaultValue(field),
+        optionsData.getConverter(field),
+        optionAnnotation.allowMultiple());
+  }
+
   boolean containsExplicitOption(String name) {
     Field field = optionsData.getFieldFromName(name);
     if (field == null) {
@@ -475,7 +512,7 @@
    * of options; in that case, the arg seen last takes precedence.
    *
    * <p>The method uses the invariant that if an option has neither an implicit
-   * dependant nor an expanded from value, then it must have been explicitly
+   * dependent nor an expanded from value, then it must have been explicitly
    * set.
    */
   private List<String> parse(OptionPriority priority,
@@ -720,5 +757,4 @@
       throw new AssertionError(e);
     }
   }
-
 }