| // Copyright 2025 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.packages; |
| |
| 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.cmdline.StarlarkThreadContext; |
| import com.google.devtools.build.lib.events.Event; |
| import com.google.devtools.build.lib.events.ExtendedEventHandler; |
| import com.google.devtools.build.lib.starlarkbuildapi.StarlarkAspectPropagationContextApi; |
| import java.util.Objects; |
| import net.starlark.java.eval.EvalException; |
| import net.starlark.java.eval.Mutability; |
| import net.starlark.java.eval.Sequence; |
| import net.starlark.java.eval.Starlark; |
| import net.starlark.java.eval.StarlarkFunction; |
| import net.starlark.java.eval.StarlarkList; |
| import net.starlark.java.eval.StarlarkSemantics; |
| import net.starlark.java.eval.StarlarkThread; |
| |
| /** |
| * Supplies the list of edges (attribute names or toolchain types) that should be propagated by an |
| * aspect. It is extended by 2 classes: |
| * |
| * <ul> |
| * <li>FixedListSupplier: for the case when the list is fixed and known at the aspect definition |
| * time. |
| * <li>FunctionSupplier: for the case when the list is computed for each target that aspect |
| * visits. |
| * </ul> |
| * |
| * The type <T> is String for {@code attr_aspects} and {@link Label} for {@code toolchains_aspects}. |
| */ |
| public sealed interface AspectPropagationEdgesSupplier<T> { |
| |
| public static final AspectPropagationEdgesSupplier<String> DEFAULT_ATTR_ASPECTS_SUPPLIER = |
| new FixedListSupplier<>(ImmutableSet.of()); |
| |
| public static final FixedListSupplier<Label> DEFAULT_TOOLCHAINS_ASPECTS_SUPPLIER = |
| new FixedListSupplier<>(ImmutableSet.of()); |
| |
| /** A supplier of the edges that is fixed and known at the aspect definition time. */ |
| public static final class FixedListSupplier<T> implements AspectPropagationEdgesSupplier<T> { |
| |
| private final ImmutableSet<T> edges; |
| |
| private FixedListSupplier(ImmutableSet<T> edges) { |
| this.edges = edges; |
| } |
| |
| public ImmutableSet<T> getList() { |
| return edges; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (o == null || !(o instanceof FixedListSupplier<?> that)) { |
| return false; |
| } |
| |
| return Objects.equals(edges, that.edges); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hashCode(edges); |
| } |
| } |
| |
| /** A supplier of the edges that is computed for each target that aspect visits. */ |
| public abstract static sealed class FunctionSupplier<T> |
| implements AspectPropagationEdgesSupplier<T> { |
| private final StarlarkFunction function; |
| private final StarlarkSemantics semantics; |
| |
| private FunctionSupplier(StarlarkFunction function, StarlarkSemantics semantics) { |
| this.function = function; |
| this.semantics = semantics; |
| } |
| |
| public abstract ImmutableSet<T> computeList( |
| StarlarkAspectPropagationContextApi context, ExtendedEventHandler eventHandler) |
| throws InterruptedException, EvalException; |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (o == null || !(o instanceof FunctionSupplier<?> that)) { |
| return false; |
| } |
| |
| return Objects.equals(function, that.function) && Objects.equals(semantics, that.semantics); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(function, semantics); |
| } |
| |
| private static final class AspectPropagationEdgesThreadContext extends StarlarkThreadContext { |
| private AspectPropagationEdgesThreadContext() { |
| super(null); |
| } |
| } |
| |
| protected StarlarkList<?> runFunction( |
| StarlarkAspectPropagationContextApi context, ExtendedEventHandler eventHandler) |
| throws InterruptedException, EvalException { |
| try (Mutability mu = Mutability.create("aspect_propagation_edges")) { |
| StarlarkThread thread = StarlarkThread.createTransient(mu, semantics); |
| thread.setPrintHandler(Event.makeDebugPrintHandler(eventHandler)); |
| new AspectPropagationEdgesThreadContext().storeInThread(thread); |
| Object starlarkResult = Starlark.positionalOnlyCall(thread, function, context); |
| if (starlarkResult instanceof StarlarkList<?> listResult) { |
| return listResult; |
| } |
| throw new EvalException("Expected a list"); |
| } |
| } |
| } |
| |
| /** A function supplier for {@code attr_aspects}. */ |
| public static final class AttrAspectsFunctionSupplier extends FunctionSupplier<String> { |
| private AttrAspectsFunctionSupplier(StarlarkFunction function, StarlarkSemantics semantics) { |
| super(function, semantics); |
| } |
| |
| @Override |
| public ImmutableSet<String> computeList( |
| StarlarkAspectPropagationContextApi context, ExtendedEventHandler eventHandler) |
| throws InterruptedException, EvalException { |
| return parseAttrAspects(runFunction(context, eventHandler), /* allowAll= */ false); |
| } |
| } |
| |
| /** A function supplier for {@code toolchains_aspects}. */ |
| public static final class ToolchainsAspectsFunctionSupplier extends FunctionSupplier<Label> { |
| private ToolchainsAspectsFunctionSupplier( |
| StarlarkFunction function, StarlarkSemantics semantics) { |
| super(function, semantics); |
| } |
| |
| @Override |
| public ImmutableSet<Label> computeList( |
| StarlarkAspectPropagationContextApi context, ExtendedEventHandler eventHandler) |
| throws InterruptedException, EvalException { |
| StarlarkList<?> listResult = runFunction(context, eventHandler); |
| if (listResult == null || listResult.isEmpty()) { |
| return ImmutableSet.of(); |
| } |
| return ImmutableSet.copyOf(Sequence.cast(listResult, Label.class, "toolchains_aspects")); |
| } |
| } |
| |
| public static AspectPropagationEdgesSupplier<String> createForAttrAspects( |
| Object rawAttrAspects, StarlarkThread thread) throws EvalException { |
| if (rawAttrAspects instanceof StarlarkFunction attrAspectsFunction) { |
| return new AttrAspectsFunctionSupplier(attrAspectsFunction, thread.getSemantics()); |
| } else { |
| return new FixedListSupplier<>(parseAttrAspects(rawAttrAspects, /* allowAll= */ true)); |
| } |
| } |
| |
| public static AspectPropagationEdgesSupplier<Label> createForToolchainsAspects( |
| Object rawToolchainsAspects, StarlarkThread thread, LabelConverter labelConverter) |
| throws EvalException { |
| if (rawToolchainsAspects instanceof StarlarkFunction toolchainsAspectsFunction) { |
| return new ToolchainsAspectsFunctionSupplier( |
| toolchainsAspectsFunction, thread.getSemantics()); |
| } else { |
| return new FixedListSupplier<>(parseToolchainsAspects(rawToolchainsAspects, labelConverter)); |
| } |
| } |
| |
| private static ImmutableSet<String> parseAttrAspects(Object rawAttrAspects, boolean allowAll) |
| throws EvalException { |
| Sequence<String> attrAspects = Sequence.cast(rawAttrAspects, String.class, "attr_aspects"); |
| |
| ImmutableSet.Builder<String> attrAspectsBuilder = ImmutableSet.builder(); |
| for (String attrName : attrAspects) { |
| if (attrName.equals("*")) { |
| if (!allowAll) { |
| throw new EvalException("'*' is not allowed in 'attr_aspects' list"); |
| } else if (attrAspects.size() != 1) { |
| throw new EvalException("'*' must be the only string in 'attr_aspects' list"); |
| } |
| } |
| if (!attrName.startsWith("_")) { |
| attrAspectsBuilder.add(attrName); |
| } else { |
| // Implicit attribute names mean either implicit or late-bound attributes |
| // (``$attr`` or ``:attr``). Depend on both. |
| attrAspectsBuilder |
| .add(AttributeValueSource.COMPUTED_DEFAULT.convertToNativeName(attrName)) |
| .add(AttributeValueSource.LATE_BOUND.convertToNativeName(attrName)); |
| } |
| } |
| |
| return attrAspectsBuilder.build(); |
| } |
| |
| private static ImmutableSet<Label> parseToolchainsAspects( |
| Object rawToolchainsAspects, LabelConverter labelConverter) throws EvalException { |
| Sequence<String> toolchainsAspects = |
| Sequence.cast(rawToolchainsAspects, String.class, "toolchains_aspects"); |
| |
| ImmutableSet.Builder<Label> parsedLabels = new ImmutableSet.Builder<>(); |
| for (String input : toolchainsAspects) { |
| try { |
| Label label = labelConverter.convert(input); |
| parsedLabels.add(label); |
| } catch (LabelSyntaxException e) { |
| throw Starlark.errorf( |
| "Unable to parse label '%s' in attribute '%s': %s", |
| input, "toolchains_aspects", e.getMessage()); |
| } |
| } |
| return parsedLabels.build(); |
| } |
| } |