Skip to content

Commit

Permalink
Update starlark apis that consume toolchain types to use
Browse files Browse the repository at this point in the history
ToolchainTypeRequirement.

Part of Optional Toolchains (#14726).

Closes #14948.

PiperOrigin-RevId: 439641733
  • Loading branch information
katre authored and copybara-github committed Apr 5, 2022
1 parent 1ebe027 commit 5f6a121
Show file tree
Hide file tree
Showing 10 changed files with 275 additions and 81 deletions.
1 change: 1 addition & 0 deletions src/main/java/com/google/devtools/build/lib/analysis/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -1837,6 +1837,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/cmdline",
"//src/main/java/com/google/devtools/build/lib/starlarkbuildapi/config:starlark_toolchain_type_requirement",
"//third_party:auto_value",
"//third_party:guava",
],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package com.google.devtools.build.lib.analysis.config;

import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.starlarkbuildapi.config.StarlarkToolchainTypeRequirement;

Expand All @@ -33,6 +34,24 @@ public static Builder builder(Label toolchainType) {
.mandatory(true);
}

/**
* Returns the ToolchainTypeRequirement with the strictest restriction, or else the first.
* Mandatory toolchain type requirements are stricter than optional.
*/
public static ToolchainTypeRequirement strictest(
ToolchainTypeRequirement first, ToolchainTypeRequirement second) {
Preconditions.checkArgument(
first.toolchainType().equals(second.toolchainType()),
"Cannot use strictest() for two instances with different type labels.");
if (first.mandatory()) {
return first;
}
if (second.mandatory()) {
return second;
}
return first;
}

/** Returns the label of the toolchain type that is requested. */
public abstract Label toolchainType();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

package com.google.devtools.build.lib.analysis.starlark;

import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.devtools.build.lib.analysis.BaseRuleClasses.RUN_UNDER;
import static com.google.devtools.build.lib.analysis.BaseRuleClasses.TEST_RUNNER_EXEC_GROUP;
import static com.google.devtools.build.lib.analysis.BaseRuleClasses.TIMEOUT_DEFAULT;
Expand Down Expand Up @@ -102,6 +101,7 @@
import com.google.devtools.build.lib.util.FileTypeSet;
import com.google.devtools.build.lib.util.Pair;
import com.google.errorprone.annotations.FormatMethod;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nullable;
Expand Down Expand Up @@ -401,7 +401,7 @@ public StarlarkCallable rule(
: Label.createUnvalidated(PackageIdentifier.EMPTY_PACKAGE_ID, "dummy_label"),
bzlModule != null ? bzlModule.bzlTransitiveDigest() : new byte[0]);

builder.addRequiredToolchains(parseToolchains(toolchains, thread));
builder.addToolchainTypes(parseToolchainTypes(toolchains, thread));
if (useToolchainTransition) {
builder.useToolchainTransition(ToolchainTransitionMode.ENABLED);
}
Expand Down Expand Up @@ -559,41 +559,21 @@ private static void checkAttributeName(String name) throws EvalException {
return attributes.build();
}

/**
* Parses a sequence of label strings with a repo mapping.
*
* @param inputs sequence of input strings
* @param thread repository mapping
* @param adjective describes the purpose of the label; used for errors
* @throws EvalException if the label can't be parsed
*/
private static ImmutableList<Label> parseLabels(
Iterable<String> inputs, StarlarkThread thread, String adjective) throws EvalException {
private static ImmutableList<Label> parseExecCompatibleWith(
Sequence<?> inputs, StarlarkThread thread) throws EvalException {
ImmutableList.Builder<Label> parsedLabels = new ImmutableList.Builder<>();
LabelConverter converter = LabelConverter.forThread(thread);
for (String input : inputs) {
for (String input : Sequence.cast(inputs, String.class, "exec_compatible_with")) {
try {
Label label = converter.convert(input);
parsedLabels.add(label);
} catch (LabelSyntaxException e) {
throw Starlark.errorf(
"Unable to parse %s label '%s': %s", adjective, input, e.getMessage());
throw Starlark.errorf("Unable to parse constraint label '%s': %s", input, e.getMessage());
}
}
return parsedLabels.build();
}

private static ImmutableList<Label> parseToolchains(Sequence<?> inputs, StarlarkThread thread)
throws EvalException {
return parseLabels(Sequence.cast(inputs, String.class, "toolchains"), thread, "toolchain");
}

private static ImmutableList<Label> parseExecCompatibleWith(
Sequence<?> inputs, StarlarkThread thread) throws EvalException {
return parseLabels(
Sequence.cast(inputs, String.class, "exec_compatible_with"), thread, "constraint");
}

@Override
public StarlarkAspect aspect(
StarlarkFunction implementation,
Expand Down Expand Up @@ -698,7 +678,6 @@ public StarlarkAspect aspect(
"An aspect cannot simultaneously have required providers and apply to generating rules.");
}

ImmutableList<Label> toolchainTypes = parseToolchains(toolchains, thread);
return new StarlarkDefinedAspect(
implementation,
attrAspects.build(),
Expand All @@ -712,9 +691,7 @@ public StarlarkAspect aspect(
ImmutableSet.copyOf(Sequence.cast(fragments, String.class, "fragments")),
HostTransition.INSTANCE,
ImmutableSet.copyOf(Sequence.cast(hostFragments, String.class, "host_fragments")),
toolchainTypes.stream()
.map(tt -> ToolchainTypeRequirement.create(tt))
.collect(toImmutableSet()),
parseToolchainTypes(toolchains, thread),
useToolchainTransition,
applyToGeneratingRules);
}
Expand Down Expand Up @@ -1049,13 +1026,62 @@ public ExecGroup execGroup(
return ExecGroup.copyFromDefault();
}

ImmutableSet<Label> toolchainTypes = ImmutableSet.copyOf(parseToolchains(toolchains, thread));
ImmutableSet<ToolchainTypeRequirement> toolchainTypes = parseToolchainTypes(toolchains, thread);
ImmutableSet<Label> constraints =
ImmutableSet.copyOf(parseExecCompatibleWith(execCompatibleWith, thread));
return ExecGroup.builder()
.requiredToolchains(toolchainTypes)
.toolchainTypes(toolchainTypes)
.execCompatibleWith(constraints)
.copyFrom(null)
.build();
}

private static ImmutableSet<ToolchainTypeRequirement> parseToolchainTypes(
Sequence<?> rawToolchains, StarlarkThread thread) throws EvalException {
Map<Label, ToolchainTypeRequirement> toolchainTypes = new HashMap<>();
LabelConverter converter = LabelConverter.forThread(thread);

for (Object rawToolchain : rawToolchains) {
ToolchainTypeRequirement toolchainType = parseToolchainType(converter, rawToolchain);
Label typeLabel = toolchainType.toolchainType();
ToolchainTypeRequirement previous = toolchainTypes.get(typeLabel);
if (previous != null) {
// Keep the one with the strictest requirements.
toolchainType = ToolchainTypeRequirement.strictest(previous, toolchainType);
}
toolchainTypes.put(typeLabel, toolchainType);
}

return ImmutableSet.copyOf(toolchainTypes.values());
}

private static ToolchainTypeRequirement parseToolchainType(
LabelConverter converter, Object rawToolchain) throws EvalException {
// Handle actual ToolchainTypeRequirement objects.
if (rawToolchain instanceof ToolchainTypeRequirement) {
return (ToolchainTypeRequirement) rawToolchain;
}

// Handle Label-like objects.
Label toolchainLabel = null;
if (rawToolchain instanceof Label) {
toolchainLabel = (Label) rawToolchain;
} else if (rawToolchain instanceof String) {
try {
toolchainLabel = converter.convert((String) rawToolchain);
} catch (LabelSyntaxException e) {
throw Starlark.errorf(
"Unable to parse toolchain_type label '%s': %s", rawToolchain, e.getMessage());
}
}

if (toolchainLabel != null) {
return ToolchainTypeRequirement.builder(toolchainLabel).mandatory(true).build();
}

// It's not a valid type.
throw Starlark.errorf(
"'toolchains' takes a toolchain_type, Label, or String, but instead got a %s",
rawToolchain.getClass().getSimpleName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

package com.google.devtools.build.lib.packages;

import static com.google.common.collect.ImmutableSet.toImmutableSet;

import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableMap;
Expand Down Expand Up @@ -83,16 +82,6 @@ public ExecGroup inheritFrom(ExecGroup other) {
@AutoValue.Builder
public interface Builder {

/** Sets the required toolchain types. */
// TODO(katre): Remove this once all callers use toolchainTypes.
default Builder requiredToolchains(ImmutableSet<Label> toolchainTypes) {
ImmutableSet<ToolchainTypeRequirement> toolchainTypeRequirements =
toolchainTypes.stream()
.map(label -> ToolchainTypeRequirement.create(label))
.collect(toImmutableSet());
return this.toolchainTypes(toolchainTypeRequirements);
}

/** Sets the toolchain type requirements. */
default Builder toolchainTypes(ImmutableSet<ToolchainTypeRequirement> toolchainTypes) {
toolchainTypes.forEach(this::addToolchainType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,13 +334,14 @@ Object provider(String doc, Object fields, Object init, StarlarkThread thread)
+ "Starlark rules. This flag may be removed in the future."),
@Param(
name = TOOLCHAINS_PARAM,
allowedTypes = {@ParamType(type = Sequence.class, generic1 = String.class)},
allowedTypes = {@ParamType(type = Sequence.class, generic1 = Object.class)},
named = true,
defaultValue = "[]",
doc =
"If set, the set of toolchains this rule requires. Toolchains will be "
+ "found by checking the current platform, and provided to the rule "
+ "implementation via <code>ctx.toolchain</code>."),
"If set, the set of toolchains this rule requires. The list can contain String,"
+ " Label, or StarlarkToolchainTypeApi objects, in any combination. Toolchains"
+ " will be found by checking the current platform, and provided to the rule"
+ " implementation via <code>ctx.toolchain</code>."),
@Param(
name = "incompatible_use_toolchain_transition",
defaultValue = "False",
Expand Down Expand Up @@ -610,13 +611,14 @@ StarlarkCallable rule(
+ "in host configuration."),
@Param(
name = TOOLCHAINS_PARAM,
allowedTypes = {@ParamType(type = Sequence.class, generic1 = String.class)},
allowedTypes = {@ParamType(type = Sequence.class, generic1 = Object.class)},
named = true,
defaultValue = "[]",
doc =
"If set, the set of toolchains this rule requires. Toolchains will be "
+ "found by checking the current platform, and provided to the rule "
+ "implementation via <code>ctx.toolchain</code>."),
"If set, the set of toolchains this rule requires. The list can contain String,"
+ " Label, or StarlarkToolchainTypeApi objects, in any combination. Toolchains"
+ " will be found by checking the current platform, and provided to the rule"
+ " implementation via <code>ctx.toolchain</code>."),
@Param(
name = "incompatible_use_toolchain_transition",
defaultValue = "False",
Expand Down Expand Up @@ -690,11 +692,13 @@ StarlarkAspectApi aspect(
parameters = {
@Param(
name = TOOLCHAINS_PARAM,
allowedTypes = {@ParamType(type = Sequence.class, generic1 = String.class)},
allowedTypes = {@ParamType(type = Sequence.class, generic1 = Object.class)},
named = true,
positional = false,
defaultValue = "[]",
doc = "The set of toolchains this execution group requires."),
doc =
"The set of toolchains this execution group requires. The list can contain String,"
+ " Label, or StarlarkToolchainTypeApi objects, in any combination."),
@Param(
name = EXEC_COMPATIBLE_WITH_PARAM,
allowedTypes = {@ParamType(type = Sequence.class, generic1 = String.class)},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ java_library(
"ExecGroupSubject.java",
"ResolvedToolchainContextSubject.java",
"RuleClassSubject.java",
"StarlarkDefinedAspectSubject.java",
"ToolchainCollectionSubject.java",
"ToolchainContextSubject.java",
"ToolchainInfoSubject.java",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2022 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.analysis.testing;

import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.truth.Truth.assertAbout;

import com.google.common.base.Functions;
import com.google.common.collect.ImmutableMap;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.MapSubject;
import com.google.common.truth.Subject;
import com.google.devtools.build.lib.analysis.config.ToolchainTypeRequirement;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.StarlarkDefinedAspect;
import java.util.Map;

/** A Truth {@link Subject} for {@link StarlarkDefinedAspect}. */
public class StarlarkDefinedAspectSubject extends Subject {
// Static data.

/** Entry point for test assertions related to {@link StarlarkDefinedAspect}. */
public static StarlarkDefinedAspectSubject assertThat(
StarlarkDefinedAspect starlarkDefinedAspect) {
return assertAbout(StarlarkDefinedAspectSubject::new).that(starlarkDefinedAspect);
}

/** Static method for getting the subject factory (for use with assertAbout()). */
public static Subject.Factory<StarlarkDefinedAspectSubject, StarlarkDefinedAspect>
starlarkDefinedAspects() {
return StarlarkDefinedAspectSubject::new;
}

// Instance fields.

private final StarlarkDefinedAspect actual;
private final Map<Label, ToolchainTypeRequirement> toolchainTypesMap;

protected StarlarkDefinedAspectSubject(
FailureMetadata failureMetadata, StarlarkDefinedAspect subject) {
super(failureMetadata, subject);
this.actual = subject;
this.toolchainTypesMap = makeToolchainTypesMap(subject);
}

private static ImmutableMap<Label, ToolchainTypeRequirement> makeToolchainTypesMap(
StarlarkDefinedAspect subject) {
return subject.getToolchainTypes().stream()
.collect(toImmutableMap(ToolchainTypeRequirement::toolchainType, Functions.identity()));
}

public MapSubject toolchainTypes() {
return check("getToolchainTypes()").that(toolchainTypesMap);
}

public ToolchainTypeRequirementSubject toolchainType(String toolchainTypeLabel) {
return toolchainType(Label.parseAbsoluteUnchecked(toolchainTypeLabel));
}

public ToolchainTypeRequirementSubject toolchainType(Label toolchainType) {
return check("toolchainType(%s)", toolchainType)
.about(ToolchainTypeRequirementSubject.toolchainTypeRequirements())
.that(toolchainTypesMap.get(toolchainType));
}

public void hasToolchainType(String toolchainTypeLabel) {
toolchainType(toolchainTypeLabel).isNotNull();
}

public void hasToolchainType(Label toolchainType) {
toolchainType(toolchainType).isNotNull();
}

// TODO(blaze-team): Add more useful methods.
}
Loading

0 comments on commit 5f6a121

Please sign in to comment.