// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.actions; import com.google.auto.value.AutoValue; import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; import com.google.devtools.build.lib.util.Preconditions; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Strings used to express requirements on action execution environments. */ public class ExecutionRequirements { /** An execution requirement that can be split into a key and a value part using a regex. */ @AutoValue public abstract static class ParseableRequirement { /** * Thrown when a {@link ParseableRequirement} feels responsible for a tag, but the {@link * #validator()} method returns an error. */ public static class ValidationException extends Exception { private final String tagValue; /** * Creates a new {@link ValidationException}. * * @param tagValue the erroneous value that was parsed from the tag. * @param errorMsg an error message that tells the user what's wrong with the value. */ public ValidationException(String tagValue, String errorMsg) { super(errorMsg); this.tagValue = tagValue; } /** * Returns the erroneous value of the parsed tag. * *

Useful to put in error messages shown to the user. */ public String getTagValue() { return tagValue; } } /** * Create a new parseable execution requirement definition. * *

If a tag doesn't match the detectionPattern, it will be ignored. If a tag matches the * detectionPattern, but not the validationPattern, it is assumed that the value is somehow * wrong (e.g. the user put a float or random string where we expected an integer). * * @param userFriendlyName a human readable name of the tag and its format, e.g. "cpu:" * @param detectionPattern a regex that will be used to detect whether a tag matches this * execution requirement. It should have one capture group that grabs the value of the tag. * This should be general enough to permit even wrong value types. Example: "cpu:(.+)". * @param validator a Function that will be used to validate the value of the tag. It should * return null if the value is fine to use or a human-friendly error message describing why * the value is not valid. */ static ParseableRequirement create( String userFriendlyName, Pattern detectionPattern, Function validator) { return new AutoValue_ExecutionRequirements_ParseableRequirement( userFriendlyName, detectionPattern, validator); } public abstract String userFriendlyName(); public abstract Pattern detectionPattern(); public abstract Function validator(); /** * Returns the parsed value from a tag, if this {@link ParseableRequirement} detects that it is * responsible for it, otherwise returns {@code null}. * * @throws ValidationException if the value parsed out of the tag doesn't pass the validator. */ public String parseIfMatches(String tag) throws ValidationException { Matcher matcher = detectionPattern().matcher(tag); if (!matcher.matches()) { return null; } String tagValue = matcher.group(1); String errorMsg = validator().apply(tagValue); if (errorMsg != null) { throw new ValidationException(tagValue, errorMsg); } return tagValue; } } /** If an action would not successfully run other than on Darwin. */ public static final String REQUIRES_DARWIN = "requires-darwin"; /** Whether we should disable prefetching of inputs before running a local action. */ public static final String DISABLE_LOCAL_PREFETCH = "disable-local-prefetch"; /** How many hardware threads an action requires for execution. */ public static final ParseableRequirement CPU = ParseableRequirement.create( "cpu:", Pattern.compile("cpu:(.+)"), s -> { Preconditions.checkNotNull(s); int value; try { value = Integer.parseInt(s); } catch (NumberFormatException e) { return "can't be parsed as an integer"; } // De-and-reserialize & compare to only allow canonical integer formats. if (!Integer.toString(value).equals(s)) { return "must be in canonical format (e.g. '4' instead of '+04')"; } if (value < 1) { return "can't be zero or negative"; } return null; }); /** If an action supports running in persistent worker mode. */ public static final String SUPPORTS_WORKERS = "supports-workers"; public static final ImmutableMap WORKER_MODE_ENABLED = ImmutableMap.of(SUPPORTS_WORKERS, "1"); /** * Whether we should disable remote caching of an action. This can be set to force a rerun of an * action even if there is a cache entry for it. */ public static final String NO_CACHE = "no-cache"; }