blob: 16f91164e6fe7d34566223afe5ac51de474ca26b [file] [log] [blame]
// Copyright 2024 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.skyframe;
import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.collect.SimpleTargetPatternMatcher;
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import java.util.Collection;
import java.util.Objects;
import javax.annotation.Nullable;
/** A SkyValue representing the parsed definitions from a PROJECT.scl file. */
public final class ProjectValue implements SkyValue {
/**
* Represents the enforcement policy for a PROJECT.scl file.
*
* <p>"warn" (default) - warn if the user set any output-affecting options that are not present in
* the selected config in a blazerc or on the command line.
*
* <p>"compatible" - fail if the user set any options that are present in the selected config to a
* different value than the one in the config. Also warn for other output-affecting options
*
* <p>"strict" - fail if the user set any output-affecting options that are not present in the
* selected config.
*/
public enum EnforcementPolicy {
WARN("warn"), // Default, enforced in ProjectFunction#compute.
COMPATIBLE("compatible"),
STRICT("strict");
EnforcementPolicy(String value) {
this.value = value;
}
private final String value;
public static EnforcementPolicy fromString(String value) {
for (EnforcementPolicy policy : EnforcementPolicy.values()) {
if (policy.value.equals(value)) {
return policy;
}
}
throw new IllegalArgumentException(String.format("invalid enforcement_policy '%s'", value));
}
}
private final EnforcementPolicy enforcementPolicy;
private final ImmutableMap<String, Collection<String>> projectDirectories;
@Nullable private final ImmutableMap<String, BuildableUnit> buildableUnits;
@Nullable private final ImmutableList<String> alwaysAllowedConfigs;
@Nullable private final Label actualProjectFile;
/**
* A project's buildable units.
*
* <p>A buildable unit is a named pair of build flags and target patterns. The name is stored as a
* map key in {@link ProjectValue#getBuildableUnits()}
*
* <p>See {@code third_party/bazel/src/main/protobuf/project/project.proto} for precise
* definitions.
*/
@AutoValue
public abstract static class BuildableUnit {
/**
* Creates a buildable unit.
*
* @param targetPatterns the buildable unit's target patterns, or empty if they weren't set
* @param description the buildable unit's user-friendly description, or empty if not set
* @param flags the buildable unit's flags
* @param isDefault whether this is the default buildable unit
*/
public static BuildableUnit create(
ImmutableList<String> targetPatterns,
String description,
ImmutableList<String> flags,
boolean isDefault)
throws LabelSyntaxException {
return new AutoValue_ProjectValue_BuildableUnit(
SimpleTargetPatternMatcher.create(targetPatterns), description, flags, isDefault);
}
public abstract SimpleTargetPatternMatcher targetPatternMatcher();
public abstract String description();
public abstract ImmutableList<String> flags();
public abstract boolean isDefault();
}
public ProjectValue(
EnforcementPolicy enforcementPolicy,
ImmutableMap<String, Collection<String>> projectDirectories,
@Nullable ImmutableMap<String, BuildableUnit> buildableUnits,
@Nullable ImmutableList<String> alwaysAllowedConfigs,
@Nullable Label actualProjectFile) {
this.enforcementPolicy = enforcementPolicy;
this.projectDirectories = projectDirectories;
this.buildableUnits = buildableUnits;
this.alwaysAllowedConfigs = alwaysAllowedConfigs;
this.actualProjectFile = actualProjectFile;
}
/**
* Return the "default" {@code project_directories} map entry. If there are zero entries, returns
* an empty set.
*/
public ImmutableSet<String> getDefaultProjectDirectories() {
if (projectDirectories.isEmpty()) {
return ImmutableSet.of();
}
// TODO: b/409377907 - Make sure this check still makes sense with the new format.
Preconditions.checkArgument(
projectDirectories.containsKey("default"),
"project_directories must contain the 'default' key");
return ImmutableSet.copyOf(projectDirectories.get("default"));
}
/**
* If a project file has the content
*
* {@snippet :
* project = {
* "actual": "//other:PROJECT.scl"
* }
* }
*
* <p>then this is the same project defined canonically in {@code //other:PROJECT.scl} and this
* method returns {@code //other:PROJECT.scl}. Else returns the {@link ProjectValue.Key} label
* that produces this value.
*
* <p>Files that define "actual" cannot define any other content. That's considered a parsing
* error.
*/
public Label getActualProjectFile() {
return actualProjectFile;
}
public EnforcementPolicy getEnforcementPolicy() {
return enforcementPolicy;
}
/**
* Maps buildable unit names to definitions. Null if not specified. Note that an empty list is not
* the same as unspecified.
*
* <p>Builds can trigger a buildable unit by setting {@code --scl_config=<name>}.
*/
@Nullable
public ImmutableMap<String, BuildableUnit> getBuildableUnits() {
return buildableUnits;
}
@Nullable
public ImmutableList<String> getAlwaysAllowedConfigs() {
return alwaysAllowedConfigs;
}
/**
* Returns the map of named {@code project_directories} in the project. If the map is not defined
* in the file, returns an empty map.
*/
public ImmutableMap<String, Collection<String>> getProjectDirectories() {
return projectDirectories;
}
/** The SkyKey. Uses the label of the project file as the input. */
public static final class Key implements SkyKey {
private final Label projectFile;
public Key(Label projectFile) {
this.projectFile = Preconditions.checkNotNull(projectFile);
}
public Label getProjectFile() {
return projectFile;
}
@Override
public SkyFunctionName functionName() {
return SkyFunctions.PROJECT;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ProjectValue.Key key = (ProjectValue.Key) o;
return Objects.equals(projectFile, key.projectFile);
}
@Override
public int hashCode() {
return Objects.hashCode(projectFile);
}
}
}