diff options
Diffstat (limited to 'java/com/google/devtools/common/options/InvocationPolicyEnforcer.java')
-rw-r--r-- | java/com/google/devtools/common/options/InvocationPolicyEnforcer.java | 536 |
1 files changed, 277 insertions, 259 deletions
diff --git a/java/com/google/devtools/common/options/InvocationPolicyEnforcer.java b/java/com/google/devtools/common/options/InvocationPolicyEnforcer.java index 742acb6..a53ff5b 100644 --- a/java/com/google/devtools/common/options/InvocationPolicyEnforcer.java +++ b/java/com/google/devtools/common/options/InvocationPolicyEnforcer.java @@ -14,7 +14,6 @@ package com.google.devtools.common.options; import com.google.common.base.Joiner; -import com.google.common.base.Preconditions; import com.google.common.base.Verify; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; @@ -27,6 +26,7 @@ import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.Fl import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy; import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.SetValue; import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.UseDefault; +import com.google.devtools.common.options.OptionPriority.PriorityCategory; import com.google.devtools.common.options.OptionsParser.OptionDescription; import java.util.ArrayList; import java.util.Collections; @@ -35,15 +35,14 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.annotation.Nullable; /** - * Enforces the {@link FlagPolicy}s (from an {@link InvocationPolicy} proto) on an - * {@link OptionsParser} by validating and changing the flag values in the given - * {@link OptionsParser}. + * Enforces the {@link FlagPolicy}s (from an {@link InvocationPolicy} proto) on an {@link + * OptionsParser} by validating and changing the flag values in the given {@link OptionsParser}. * * <p>"Flag" and "Option" are used interchangeably in this file. */ @@ -52,9 +51,8 @@ public final class InvocationPolicyEnforcer { private static final Logger logger = Logger.getLogger(InvocationPolicyEnforcer.class.getName()); private static final String INVOCATION_POLICY_SOURCE = "Invocation policy"; - private static final Function<OptionDefinition, String> INVOCATION_POLICY_SOURCE_FUNCTION = - o -> INVOCATION_POLICY_SOURCE; @Nullable private final InvocationPolicy invocationPolicy; + private final Level loglevel; /** * Creates an InvocationPolicyEnforcer that enforces the given policy. @@ -63,7 +61,33 @@ public final class InvocationPolicyEnforcer { * nothing in calls to enforce(). */ public InvocationPolicyEnforcer(@Nullable InvocationPolicy invocationPolicy) { + this(invocationPolicy, Level.FINE); + } + + /** + * Creates an InvocationPolicyEnforcer that enforces the given policy. + * + * @param invocationPolicy the policy to enforce. A null policy means this enforcer will do + * nothing in calls to enforce(). + * @param loglevel the level at which to log informational statements. Warnings and errors will + * still be logged at the appropriate level. + */ + public InvocationPolicyEnforcer(@Nullable InvocationPolicy invocationPolicy, Level loglevel) { this.invocationPolicy = invocationPolicy; + this.loglevel = loglevel; + } + + private static final class FlagPolicyWithContext { + private final FlagPolicy policy; + private final OptionDescription description; + private final OptionInstanceOrigin origin; + + public FlagPolicyWithContext( + FlagPolicy policy, OptionDescription description, OptionInstanceOrigin origin) { + this.policy = policy; + this.description = description; + this.origin = origin; + } } public InvocationPolicy getInvocationPolicy() { @@ -97,10 +121,11 @@ public final class InvocationPolicyEnforcer { // The effective policy returned is expanded, filtered for applicable commands, and cleaned of // redundancies and conflicts. - List<FlagPolicy> effectivePolicies = getEffectivePolicies(invocationPolicy, parser, command); + List<FlagPolicyWithContext> effectivePolicies = + getEffectivePolicies(invocationPolicy, parser, command, loglevel); - for (FlagPolicy flagPolicy : effectivePolicies) { - String flagName = flagPolicy.getFlagName(); + for (FlagPolicyWithContext flagPolicy : effectivePolicies) { + String flagName = flagPolicy.policy.getFlagName(); OptionValueDescription valueDescription; try { @@ -109,48 +134,53 @@ public final class InvocationPolicyEnforcer { // This flag doesn't exist. We are deliberately lenient if the flag policy has a flag // we don't know about. This is for better future proofing so that as new flags are added, // new policies can use the new flags without worrying about older versions of Bazel. - logger.info( + logger.log( + loglevel, String.format("Flag '%s' specified by invocation policy does not exist", flagName)); continue; } - OptionDescription optionDescription = - parser.getOptionDescription( - flagName, OptionPriority.INVOCATION_POLICY, INVOCATION_POLICY_SOURCE); // getOptionDescription() will return null if the option does not exist, however // getOptionValueDescription() above would have thrown an IllegalArgumentException if that // were the case. - Verify.verifyNotNull(optionDescription); + Verify.verifyNotNull(flagPolicy.description); - switch (flagPolicy.getOperationCase()) { + switch (flagPolicy.policy.getOperationCase()) { case SET_VALUE: - applySetValueOperation(parser, flagPolicy, valueDescription, optionDescription); + applySetValueOperation(parser, flagPolicy, valueDescription, loglevel); break; case USE_DEFAULT: - applyUseDefaultOperation(parser, "UseDefault", optionDescription.getOptionDefinition()); + applyUseDefaultOperation( + parser, "UseDefault", flagPolicy.description.getOptionDefinition(), loglevel); break; case ALLOW_VALUES: - AllowValues allowValues = flagPolicy.getAllowValues(); - FilterValueOperation.ALLOW_VALUE_OPERATION.apply( + AllowValues allowValues = flagPolicy.policy.getAllowValues(); + FilterValueOperation.AllowValueOperation allowValueOperation = + new FilterValueOperation.AllowValueOperation(loglevel); + allowValueOperation.apply( parser, + flagPolicy.origin, allowValues.getAllowedValuesList(), allowValues.hasNewValue() ? allowValues.getNewValue() : null, allowValues.hasUseDefault(), valueDescription, - optionDescription); + flagPolicy.description); break; case DISALLOW_VALUES: - DisallowValues disallowValues = flagPolicy.getDisallowValues(); - FilterValueOperation.DISALLOW_VALUE_OPERATION.apply( + DisallowValues disallowValues = flagPolicy.policy.getDisallowValues(); + FilterValueOperation.DisallowValueOperation disallowValueOperation = + new FilterValueOperation.DisallowValueOperation(loglevel); + disallowValueOperation.apply( parser, + flagPolicy.origin, disallowValues.getDisallowedValuesList(), disallowValues.hasNewValue() ? disallowValues.getNewValue() : null, disallowValues.hasUseDefault(), valueDescription, - optionDescription); + flagPolicy.description); break; case OPERATION_NOT_SET: @@ -160,7 +190,7 @@ public final class InvocationPolicyEnforcer { logger.warning( String.format( "Unknown operation '%s' from invocation policy for flag '%s'", - flagPolicy.getOperationCase(), flagName)); + flagPolicy.policy.getOperationCase(), flagName)); break; } } @@ -182,13 +212,27 @@ public final class InvocationPolicyEnforcer { return !Collections.disjoint(policy.getCommandsList(), applicableCommands); } + /** Returns the expanded and filtered policy that would be enforced for the given command. */ + public static InvocationPolicy getEffectiveInvocationPolicy( + InvocationPolicy invocationPolicy, OptionsParser parser, String command, Level loglevel) + throws OptionsParsingException { + ImmutableList<FlagPolicyWithContext> effectivePolicies = + getEffectivePolicies(invocationPolicy, parser, command, loglevel); + + InvocationPolicy.Builder builder = InvocationPolicy.newBuilder(); + for (FlagPolicyWithContext policyWithContext : effectivePolicies) { + builder.addFlagPolicies(policyWithContext.policy); + } + return builder.build(); + } + /** * Takes the provided policy and processes it to the form that can be used on the user options. * * <p>Expands any policies on expansion flags. */ - public static ImmutableList<FlagPolicy> getEffectivePolicies( - InvocationPolicy invocationPolicy, OptionsParser parser, String command) + private static ImmutableList<FlagPolicyWithContext> getEffectivePolicies( + InvocationPolicy invocationPolicy, OptionsParser parser, String command, Level loglevel) throws OptionsParsingException { if (invocationPolicy == null) { return ImmutableList.of(); @@ -200,20 +244,42 @@ public final class InvocationPolicyEnforcer { : CommandNameCache.CommandNameCacheInstance.INSTANCE.get(command); // Expand all policies to transfer policies on expansion flags to policies on the child flags. - List<FlagPolicy> expandedPolicies = new ArrayList<>(); + List<FlagPolicyWithContext> expandedPolicies = new ArrayList<>(); + OptionPriority nextPriority = + OptionPriority.lowestOptionPriorityAtCategory(PriorityCategory.INVOCATION_POLICY); for (FlagPolicy policy : invocationPolicy.getFlagPoliciesList()) { + // These policies are high-level, before expansion, and so are not the implicitDependents or + // expansions of any other flag, other than in an obtuse sense from --invocation_policy. + OptionPriority currentPriority = nextPriority; + OptionInstanceOrigin origin = + new OptionInstanceOrigin(currentPriority, INVOCATION_POLICY_SOURCE, null, null); + nextPriority = OptionPriority.nextOptionPriority(currentPriority); if (!policyApplies(policy, commandAndParentCommands)) { // Only keep and expand policies that are applicable to the current command. continue; } - List<FlagPolicy> policies = expandPolicy(policy, parser); + + OptionDescription optionDescription = parser.getOptionDescription(policy.getFlagName()); + if (optionDescription == null) { + // InvocationPolicy ignores policy on non-existing flags by design, for version + // compatibility. + logger.log( + loglevel, + String.format( + "Flag '%s' specified by invocation policy does not exist, and will be ignored", + policy.getFlagName())); + continue; + } + FlagPolicyWithContext policyWithContext = + new FlagPolicyWithContext(policy, optionDescription, origin); + List<FlagPolicyWithContext> policies = expandPolicy(policyWithContext, parser, loglevel); expandedPolicies.addAll(policies); } // Only keep that last policy for each flag. - Map<String, FlagPolicy> effectivePolicy = new HashMap<>(); - for (FlagPolicy expandedPolicy : expandedPolicies) { - String flagName = expandedPolicy.getFlagName(); + Map<String, FlagPolicyWithContext> effectivePolicy = new HashMap<>(); + for (FlagPolicyWithContext expandedPolicy : expandedPolicies) { + String flagName = expandedPolicy.policy.getFlagName(); effectivePolicy.put(flagName, expandedPolicy); } @@ -232,78 +298,6 @@ public final class InvocationPolicyEnforcer { String.format("Disallow_Values on expansion flags like %s is not allowed.", flagName)); } - private static ImmutableList<ParsedOptionDescription> getExpansionsFromFlagPolicy( - FlagPolicy expansionPolicy, OptionDescription optionDescription, OptionsParser parser) - throws OptionsParsingException { - if (!optionDescription.isExpansion()) { - return ImmutableList.of(); - } - - Preconditions.checkArgument( - expansionPolicy - .getFlagName() - .equals(optionDescription.getOptionDefinition().getOptionName()), - String.format( - "The optionDescription provided (for flag %s) does not match the policy for flag %s.", - optionDescription.getOptionDefinition().getOptionName(), - expansionPolicy.getFlagName())); - - ImmutableList.Builder<ParsedOptionDescription> resultsBuilder = ImmutableList.builder(); - switch (expansionPolicy.getOperationCase()) { - case SET_VALUE: - { - SetValue setValue = expansionPolicy.getSetValue(); - if (setValue.getFlagValueCount() > 0) { - for (String value : setValue.getFlagValueList()) { - resultsBuilder.addAll( - parser.getExpansionOptionValueDescriptions( - optionDescription.getOptionDefinition(), - value, - OptionPriority.INVOCATION_POLICY, - INVOCATION_POLICY_SOURCE)); - } - } else { - resultsBuilder.addAll( - parser.getExpansionOptionValueDescriptions( - optionDescription.getOptionDefinition(), - null, - OptionPriority.INVOCATION_POLICY, - INVOCATION_POLICY_SOURCE)); - } - } - break; - case USE_DEFAULT: - resultsBuilder.addAll( - parser.getExpansionOptionValueDescriptions( - optionDescription.getOptionDefinition(), - null, - OptionPriority.INVOCATION_POLICY, - INVOCATION_POLICY_SOURCE)); - break; - case ALLOW_VALUES: - // All expansions originally given to the parser have been expanded by now, so these two - // cases aren't necessary (the values given in the flag policy shouldn't need to be - // checked). If you care about blocking specific flag values you should block the behavior - // on the specific ones, not the expansion that contains them. - throwAllowValuesOnExpansionFlagException(expansionPolicy.getFlagName()); - break; - case DISALLOW_VALUES: - throwDisallowValuesOnExpansionFlagException(expansionPolicy.getFlagName()); - break; - case OPERATION_NOT_SET: - throw new PolicyOperationNotSetException(expansionPolicy.getFlagName()); - default: - logger.warning( - String.format( - "Unknown operation '%s' from invocation policy for flag '%s'", - expansionPolicy.getOperationCase(), - optionDescription.getOptionDefinition().getOptionName())); - break; - } - - return resultsBuilder.build(); - } - /** * Expand a single policy. If the policy is not about an expansion flag, this will simply return a * list with a single element, oneself. If the policy is for an expansion flag, the policy will @@ -311,83 +305,95 @@ public final class InvocationPolicyEnforcer { * * <p>None of the flagPolicies returned should be on expansion flags. */ - private static List<FlagPolicy> expandPolicy( - FlagPolicy originalPolicy, - OptionsParser parser) + private static List<FlagPolicyWithContext> expandPolicy( + FlagPolicyWithContext originalPolicy, + OptionsParser parser, + Level loglevel) throws OptionsParsingException { - List<FlagPolicy> expandedPolicies = new ArrayList<>(); - - OptionDescription originalOptionDescription = - parser.getOptionDescription( - originalPolicy.getFlagName(), - OptionPriority.INVOCATION_POLICY, - INVOCATION_POLICY_SOURCE); - if (originalOptionDescription == null) { - // InvocationPolicy ignores policy on non-existing flags by design, for version compatibility. - return expandedPolicies; - } + List<FlagPolicyWithContext> expandedPolicies = new ArrayList<>(); - ImmutableList<ParsedOptionDescription> expansions = - getExpansionsFromFlagPolicy(originalPolicy, originalOptionDescription, parser); - ImmutableList.Builder<ParsedOptionDescription> subflagBuilder = ImmutableList.builder(); + boolean isExpansion = originalPolicy.description.isExpansion(); ImmutableList<ParsedOptionDescription> subflags = - subflagBuilder - .addAll(originalOptionDescription.getImplicitRequirements()) - .addAll(expansions) - .build(); - boolean isExpansion = originalOptionDescription.isExpansion(); - - if (!subflags.isEmpty() && logger.isLoggable(Level.FINE)) { - // Log the expansion. Since this is logged regardless of user provided command line, it is - // only really useful for understanding the invocation policy itself. Most of the time, - // invocation policy does not change, so this can be a log level fine. + parser.getExpansionValueDescriptions( + originalPolicy.description.getOptionDefinition(), originalPolicy.origin); + + // If we have nothing to expand to, no need to do any further work. + if (subflags.isEmpty()) { + return ImmutableList.of(originalPolicy); + } + + if (logger.isLoggable(loglevel)) { + // Log the expansion. This is only really useful for understanding the invocation policy + // itself. List<String> subflagNames = new ArrayList<>(subflags.size()); for (ParsedOptionDescription subflag : subflags) { subflagNames.add("--" + subflag.getOptionDefinition().getOptionName()); } logger.logp( - Level.FINE, + loglevel, "InvocationPolicyEnforcer", "expandPolicy", String.format( "Expanding %s on option %s to its %s: %s.", - originalPolicy.getOperationCase(), - originalPolicy.getFlagName(), + originalPolicy.policy.getOperationCase(), + originalPolicy.policy.getFlagName(), isExpansion ? "expansions" : "implied flags", Joiner.on("; ").join(subflagNames))); } // Repeated flags are special, and could set multiple times in an expansion, with the user // expecting both values to be valid. Collect these separately. - Multimap<OptionDefinition, ParsedOptionDescription> repeatableSubflagsInSetValues = + Multimap<OptionDescription, ParsedOptionDescription> repeatableSubflagsInSetValues = ArrayListMultimap.create(); // Create a flag policy for the child that looks like the parent's policy "transferred" to its // child. Note that this only makes sense for SetValue, when setting an expansion flag, or // UseDefault, when preventing it from being set. for (ParsedOptionDescription currentSubflag : subflags) { + OptionDescription subflagOptionDescription = + parser.getOptionDescription(currentSubflag.getOptionDefinition().getOptionName()); + if (currentSubflag.getOptionDefinition().allowsMultiple() - && originalPolicy.getOperationCase().equals(OperationCase.SET_VALUE)) { - repeatableSubflagsInSetValues.put(currentSubflag.getOptionDefinition(), currentSubflag); + && originalPolicy.policy.getOperationCase().equals(OperationCase.SET_VALUE)) { + repeatableSubflagsInSetValues.put(subflagOptionDescription, currentSubflag); } else { - FlagPolicy subflagAsPolicy = - getSingleValueSubflagAsPolicy(currentSubflag, originalPolicy, isExpansion); + FlagPolicyWithContext subflagAsPolicy = + getSingleValueSubflagAsPolicy( + subflagOptionDescription, currentSubflag, originalPolicy, isExpansion); // In case any of the expanded flags are themselves expansions, recurse. - expandedPolicies.addAll(expandPolicy(subflagAsPolicy, parser)); + expandedPolicies.addAll(expandPolicy(subflagAsPolicy, parser, loglevel)); } } // If there are any repeatable flag SetValues, deal with them together now. // Note that expansion flags have no value, and so cannot have multiple values either. // Skipping the recursion above is fine. - for (OptionDefinition repeatableFlag : repeatableSubflagsInSetValues.keySet()) { + for (OptionDescription repeatableFlag : repeatableSubflagsInSetValues.keySet()) { int numValues = repeatableSubflagsInSetValues.get(repeatableFlag).size(); ArrayList<String> newValues = new ArrayList<>(numValues); + ArrayList<OptionInstanceOrigin> origins = new ArrayList<>(numValues); for (ParsedOptionDescription setValue : repeatableSubflagsInSetValues.get(repeatableFlag)) { newValues.add(setValue.getUnconvertedValue()); + origins.add(setValue.getOrigin()); } - expandedPolicies.add(getSetValueSubflagAsPolicy(repeatableFlag, newValues, originalPolicy)); + // These options come from expanding a single policy, so they have effectively the same + // priority. They could have come from different expansions or implicit requirements in the + // recursive resolving of the option list, so just pick the first one. Do collapse the source + // strings though, in case there are different sources. + OptionInstanceOrigin arbitraryFirstOptionOrigin = origins.get(0); + OptionInstanceOrigin originOfSubflags = + new OptionInstanceOrigin( + arbitraryFirstOptionOrigin.getPriority(), + origins + .stream() + .map(OptionInstanceOrigin::getSource) + .distinct() + .collect(Collectors.joining(", ")), + arbitraryFirstOptionOrigin.getImplicitDependent(), + arbitraryFirstOptionOrigin.getExpandedFrom()); + expandedPolicies.add( + getSetValueSubflagAsPolicy(repeatableFlag, newValues, originOfSubflags, originalPolicy)); } // Don't add the original policy if it was an expansion flag, which have no value, but do add @@ -404,16 +410,20 @@ public final class InvocationPolicyEnforcer { * policies that set the flag, and so interact with repeatable flags, flags that can be set * multiple times, in subtle ways. * - * @param subflag, the definition of the flag the SetValue'd expansion flag expands to. + * @param subflagDesc, the description of the flag the SetValue'd expansion flag expands to. * @param subflagValue, the values that the SetValue'd expansion flag expands to for this flag. * @param originalPolicy, the original policy on the expansion flag. * @return the flag policy for the subflag given, this will be part of the expanded form of the * SetValue policy on the original flag. */ - private static FlagPolicy getSetValueSubflagAsPolicy( - OptionDefinition subflag, List<String> subflagValue, FlagPolicy originalPolicy) { + private static FlagPolicyWithContext getSetValueSubflagAsPolicy( + OptionDescription subflagDesc, + List<String> subflagValue, + OptionInstanceOrigin subflagOrigin, + FlagPolicyWithContext originalPolicy) { // Some sanity checks. - Verify.verify(originalPolicy.getOperationCase().equals(OperationCase.SET_VALUE)); + OptionDefinition subflag = subflagDesc.getOptionDefinition(); + Verify.verify(originalPolicy.policy.getOperationCase().equals(OperationCase.SET_VALUE)); if (!subflag.allowsMultiple()) { Verify.verify(subflagValue.size() <= 1); } @@ -425,28 +435,34 @@ public final class InvocationPolicyEnforcer { setValueExpansion.addFlagValue(value); } if (subflag.allowsMultiple()) { - setValueExpansion.setAppend(originalPolicy.getSetValue().getOverridable()); + setValueExpansion.setAppend(originalPolicy.policy.getSetValue().getOverridable()); } else { - setValueExpansion.setOverridable(originalPolicy.getSetValue().getOverridable()); + setValueExpansion.setOverridable(originalPolicy.policy.getSetValue().getOverridable()); } // Commands from the original policy, flag name of the expansion - return FlagPolicy.newBuilder() - .addAllCommands(originalPolicy.getCommandsList()) - .setFlagName(subflag.getOptionName()) - .setSetValue(setValueExpansion) - .build(); + return new FlagPolicyWithContext( + FlagPolicy.newBuilder() + .addAllCommands(originalPolicy.policy.getCommandsList()) + .setFlagName(subflag.getOptionName()) + .setSetValue(setValueExpansion) + .build(), + subflagDesc, + subflagOrigin); } /** * For an expansion flag in an invocation policy, each flag it expands to must be given a * corresponding policy. */ - private static FlagPolicy getSingleValueSubflagAsPolicy( - ParsedOptionDescription currentSubflag, FlagPolicy originalPolicy, boolean isExpansion) + private static FlagPolicyWithContext getSingleValueSubflagAsPolicy( + OptionDescription subflagContext, + ParsedOptionDescription currentSubflag, + FlagPolicyWithContext originalPolicy, + boolean isExpansion) throws OptionsParsingException { - FlagPolicy subflagAsPolicy = null; - switch (originalPolicy.getOperationCase()) { + FlagPolicyWithContext subflagAsPolicy = null; + switch (originalPolicy.policy.getOperationCase()) { case SET_VALUE: if (currentSubflag.getOptionDefinition().allowsMultiple()) { throw new AssertionError( @@ -463,22 +479,25 @@ public final class InvocationPolicyEnforcer { } subflagAsPolicy = getSetValueSubflagAsPolicy( - currentSubflag.getOptionDefinition(), subflagValue, originalPolicy); + subflagContext, subflagValue, currentSubflag.getOrigin(), originalPolicy); break; case USE_DEFAULT: // Commands from the original policy, flag name of the expansion subflagAsPolicy = - FlagPolicy.newBuilder() - .addAllCommands(originalPolicy.getCommandsList()) - .setFlagName(currentSubflag.getOptionDefinition().getOptionName()) - .setUseDefault(UseDefault.getDefaultInstance()) - .build(); + new FlagPolicyWithContext( + FlagPolicy.newBuilder() + .addAllCommands(originalPolicy.policy.getCommandsList()) + .setFlagName(currentSubflag.getOptionDefinition().getOptionName()) + .setUseDefault(UseDefault.getDefaultInstance()) + .build(), + subflagContext, + currentSubflag.getOrigin()); break; case ALLOW_VALUES: if (isExpansion) { - throwAllowValuesOnExpansionFlagException(originalPolicy.getFlagName()); + throwAllowValuesOnExpansionFlagException(originalPolicy.policy.getFlagName()); } // If this flag is an implicitRequirement, and some values for the parent flag are // allowed, nothing needs to happen on the implicitRequirement that is set for all @@ -487,7 +506,7 @@ public final class InvocationPolicyEnforcer { case DISALLOW_VALUES: if (isExpansion) { - throwDisallowValuesOnExpansionFlagException(originalPolicy.getFlagName()); + throwDisallowValuesOnExpansionFlagException(originalPolicy.policy.getFlagName()); } // If this flag is an implicitRequirement, and some values for the parent flag are // disallowed, that implies that all others are allowed, so nothing needs to happen @@ -495,7 +514,7 @@ public final class InvocationPolicyEnforcer { break; case OPERATION_NOT_SET: - throw new PolicyOperationNotSetException(originalPolicy.getFlagName()); + throw new PolicyOperationNotSetException(originalPolicy.policy.getFlagName()); default: return null; @@ -503,11 +522,12 @@ public final class InvocationPolicyEnforcer { return subflagAsPolicy; } - private static void logInApplySetValueOperation(String formattingString, Object... objects) { + private static void logInApplySetValueOperation( + Level loglevel, String formattingString, Object... objects) { // Finding the caller here is relatively expensive and shows up in profiling, so provide it // manually. logger.logp( - Level.INFO, + loglevel, "InvocationPolicyEnforcer", "applySetValueOperation", String.format(formattingString, objects)); @@ -515,85 +535,86 @@ public final class InvocationPolicyEnforcer { private static void applySetValueOperation( OptionsParser parser, - FlagPolicy flagPolicy, + FlagPolicyWithContext flagPolicy, OptionValueDescription valueDescription, - OptionDescription optionDescription) + Level loglevel) throws OptionsParsingException { - SetValue setValue = flagPolicy.getSetValue(); - OptionDefinition optionDefinition = optionDescription.getOptionDefinition(); + SetValue setValue = flagPolicy.policy.getSetValue(); + OptionDefinition optionDefinition = flagPolicy.description.getOptionDefinition(); // SetValue.flag_value must have at least 1 value. if (setValue.getFlagValueCount() == 0) { throw new OptionsParsingException( String.format( - "SetValue operation from invocation policy for flag '%s' does not have a value", - optionDefinition.getOptionName())); + "SetValue operation from invocation policy for %s does not have a value", + optionDefinition)); } // Flag must allow multiple values if multiple values are specified by the policy. if (setValue.getFlagValueCount() > 1 - && !optionDescription.getOptionDefinition().allowsMultiple()) { + && !flagPolicy.description.getOptionDefinition().allowsMultiple()) { throw new OptionsParsingException( String.format( - "SetValue operation from invocation policy sets multiple values for flag '%s' which " + "SetValue operation from invocation policy sets multiple values for %s which " + "does not allow multiple values", - optionDefinition.getOptionName())); + optionDefinition)); } if (setValue.getOverridable() && valueDescription != null) { // The user set the value for the flag but the flag policy is overridable, so keep the user's // value. logInApplySetValueOperation( - "Keeping value '%s' from source '%s' for flag '%s' " - + "because the invocation policy specifying the value(s) '%s' is overridable", + loglevel, + "Keeping value '%s' from source '%s' for %s because the invocation policy specifying " + + "the value(s) '%s' is overridable", valueDescription.getValue(), valueDescription.getSourceString(), - optionDefinition.getOptionName(), + optionDefinition, setValue.getFlagValueList()); } else { if (!setValue.getAppend()) { // Clear the value in case the flag is a repeated flag so that values don't accumulate. - parser.clearValue(optionDescription.getOptionDefinition()); + parser.clearValue(flagPolicy.description.getOptionDefinition()); } // Set all the flag values from the policy. for (String flagValue : setValue.getFlagValueList()) { if (valueDescription == null) { logInApplySetValueOperation( - "Setting value for flag '%s' from invocation policy to '%s', overriding the " - + "default value '%s'", - optionDefinition.getOptionName(), flagValue, optionDefinition.getDefaultValue()); + loglevel, + "Setting value for %s from invocation policy to '%s', overriding the default value " + + "'%s'", + optionDefinition, + flagValue, + optionDefinition.getDefaultValue()); } else { logInApplySetValueOperation( - "Setting value for flag '%s' from invocation policy to '%s', overriding " - + "value '%s' from '%s'", - optionDefinition.getOptionName(), + loglevel, + "Setting value for %s from invocation policy to '%s', overriding value '%s' from " + + "'%s'", + optionDefinition, flagValue, valueDescription.getValue(), valueDescription.getSourceString()); } - setFlagValue(parser, optionDefinition, flagValue); + + parser.addOptionValueAtSpecificPriority(flagPolicy.origin, optionDefinition, flagValue); } } } private static void applyUseDefaultOperation( - OptionsParser parser, String policyType, OptionDefinition option) + OptionsParser parser, String policyType, OptionDefinition option, Level loglevel) throws OptionsParsingException { OptionValueDescription clearedValueDescription = parser.clearValue(option); if (clearedValueDescription != null) { // Log the removed value. String clearedFlagName = clearedValueDescription.getOptionDefinition().getOptionName(); - - OptionDescription desc = - parser.getOptionDescription( - clearedFlagName, OptionPriority.INVOCATION_POLICY, INVOCATION_POLICY_SOURCE); - Object clearedFlagDefaultValue = null; - if (desc != null) { - clearedFlagDefaultValue = desc.getOptionDefinition().getDefaultValue(); - } - logger.info( + Object clearedFlagDefaultValue = + clearedValueDescription.getOptionDefinition().getDefaultValue(); + logger.log( + loglevel, String.format( "Using default value '%s' for flag '%s' as specified by %s invocation policy, " + "overriding original value '%s' from '%s'", @@ -605,34 +626,40 @@ public final class InvocationPolicyEnforcer { } } - /** - * Checks the user's flag values against a filtering function. - */ + /** Checks the user's flag values against a filtering function. */ private abstract static class FilterValueOperation { - private static final FilterValueOperation ALLOW_VALUE_OPERATION = - new FilterValueOperation("Allow") { - @Override - boolean isFlagValueAllowed(Set<Object> convertedPolicyValues, Object value) { - return convertedPolicyValues.contains(value); - } - }; - - private static final FilterValueOperation DISALLOW_VALUE_OPERATION = - new FilterValueOperation("Disallow") { - @Override - boolean isFlagValueAllowed(Set<Object> convertedPolicyValues, Object value) { - // In a disallow operation, the values that the flag policy specifies are not allowed, - // so the value is allowed if the set of policy values does not contain the current - // flag value. - return !convertedPolicyValues.contains(value); - } - }; + private static final class AllowValueOperation extends FilterValueOperation { + AllowValueOperation(Level loglevel) { + super("Allow", loglevel); + } + + @Override + boolean isFlagValueAllowed(Set<Object> convertedPolicyValues, Object value) { + return convertedPolicyValues.contains(value); + } + } + + private static final class DisallowValueOperation extends FilterValueOperation { + DisallowValueOperation(Level loglevel) { + super("Disalllow", loglevel); + } + + @Override + boolean isFlagValueAllowed(Set<Object> convertedPolicyValues, Object value) { + // In a disallow operation, the values that the flag policy specifies are not allowed, + // so the value is allowed if the set of policy values does not contain the current + // flag value. + return !convertedPolicyValues.contains(value); + } + } private final String policyType; + private final Level loglevel; - FilterValueOperation(String policyType) { + FilterValueOperation(String policyType, Level loglevel) { this.policyType = policyType; + this.loglevel = loglevel; } /** @@ -646,6 +673,7 @@ public final class InvocationPolicyEnforcer { void apply( OptionsParser parser, + OptionInstanceOrigin origin, List<String> policyValues, String newValue, boolean useDefault, @@ -684,11 +712,9 @@ public final class InvocationPolicyEnforcer { if (!defaultValueAllowed && useDefault) { throw new OptionsParsingException( String.format( - "%sValues policy disallows the default value '%s' for flag '%s' but also " - + "specifies to use the default value", - policyType, - optionDefinition.getDefaultValue(), - optionDefinition.getOptionName())); + "%sValues policy disallows the default value '%s' for %s but also specifies to " + + "use the default value", + policyType, optionDefinition.getDefaultValue(), optionDefinition)); } } @@ -698,10 +724,12 @@ public final class InvocationPolicyEnforcer { // the flag allowing multiple values, however, flags that allow multiple values cannot have // default values, and their value is always the empty list if they haven't been specified, // which is why new_default_value is not a repeated field. - checkDefaultValue(parser, optionDescription, policyValues, newValue, convertedPolicyValues); + checkDefaultValue( + parser, origin, optionDescription, policyValues, newValue, convertedPolicyValues); } else { checkUserValue( parser, + origin, optionDescription, valueDescription, policyValues, @@ -713,6 +741,7 @@ public final class InvocationPolicyEnforcer { void checkDefaultValue( OptionsParser parser, + OptionInstanceOrigin origin, OptionDescription optionDescription, List<String> policyValues, String newValue, @@ -723,26 +752,27 @@ public final class InvocationPolicyEnforcer { if (!isFlagValueAllowed( convertedPolicyValues, optionDescription.getOptionDefinition().getDefaultValue())) { if (newValue != null) { - // Use the default value from the policy. - logger.info( + // Use the default value from the policy, since the original default is not allowed + logger.log( + loglevel, String.format( - "Overriding default value '%s' for flag '%s' with value '%s' specified by " - + "invocation policy. %sed values are: %s", + "Overriding default value '%s' for %s with value '%s' specified by invocation " + + "policy. %sed values are: %s", optionDefinition.getDefaultValue(), - optionDefinition.getOptionName(), + optionDefinition, newValue, policyType, policyValues)); parser.clearValue(optionDefinition); - setFlagValue(parser, optionDefinition, newValue); + parser.addOptionValueAtSpecificPriority(origin, optionDefinition, newValue); } else { // The operation disallows the default value, but doesn't supply a new value. throw new OptionsParsingException( String.format( - "Default flag value '%s' for flag '%s' is not allowed by invocation policy, but " + "Default flag value '%s' for %s is not allowed by invocation policy, but " + "the policy does not provide a new value. %sed values are: %s", optionDescription.getOptionDefinition().getDefaultValue(), - optionDefinition.getOptionName(), + optionDefinition, policyType, policyValues)); } @@ -751,6 +781,7 @@ public final class InvocationPolicyEnforcer { void checkUserValue( OptionsParser parser, + OptionInstanceOrigin origin, OptionDescription optionDescription, OptionValueDescription valueDescription, List<String> policyValues, @@ -766,13 +797,13 @@ public final class InvocationPolicyEnforcer { for (Object value : optionValues) { if (!isFlagValueAllowed(convertedPolicyValues, value)) { if (useDefault) { - applyUseDefaultOperation(parser, policyType + "Values", option); + applyUseDefaultOperation(parser, policyType + "Values", option, loglevel); } else { throw new OptionsParsingException( String.format( - "Flag value '%s' for flag '%s' is not allowed by invocation policy. " - + "%sed values are: %s", - value, option.getOptionName(), policyType, policyValues)); + "Flag value '%s' for %s is not allowed by invocation policy. %sed values " + + "are: %s", + value, option, policyType, policyValues)); } } } @@ -781,38 +812,25 @@ public final class InvocationPolicyEnforcer { if (!isFlagValueAllowed(convertedPolicyValues, valueDescription.getValue())) { if (newValue != null) { - logger.info( + logger.log( + loglevel, String.format( - "Overriding disallowed value '%s' for flag '%s' with value '%s' " + "Overriding disallowed value '%s' for %s with value '%s' " + "specified by invocation policy. %sed values are: %s", - valueDescription.getValue(), - option.getOptionName(), - newValue, - policyType, - policyValues)); + valueDescription.getValue(), option, newValue, policyType, policyValues)); parser.clearValue(option); - setFlagValue(parser, option, newValue); + parser.addOptionValueAtSpecificPriority(origin, option, newValue); } else if (useDefault) { - applyUseDefaultOperation(parser, policyType + "Values", option); + applyUseDefaultOperation(parser, policyType + "Values", option, loglevel); } else { throw new OptionsParsingException( String.format( - "Flag value '%s' for flag '%s' is not allowed by invocation policy and the " + "Flag value '%s' for %s is not allowed by invocation policy and the " + "policy does not specify a new value. %sed values are: %s", - valueDescription.getValue(), option.getOptionName(), policyType, policyValues)); + valueDescription.getValue(), option, policyType, policyValues)); } } } } } - - private static void setFlagValue(OptionsParser parser, OptionDefinition flag, String flagValue) - throws OptionsParsingException { - - parser.parseWithSourceFunction( - OptionPriority.INVOCATION_POLICY, - INVOCATION_POLICY_SOURCE_FUNCTION, - ImmutableList.of(String.format("--%s=%s", flag.getOptionName(), flagValue))); - } } - |