// Copyright 2017 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.constraints; import com.google.common.base.Predicates; import com.google.common.base.Verify; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; import com.google.devtools.build.lib.analysis.ViewCreationFailedException; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.configuredtargets.OutputFileConfiguredTarget; import com.google.devtools.build.lib.analysis.constraints.SupportedEnvironmentsProvider.RemovedEnvironmentCulprit; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.ExtendedEventHandler; import com.google.devtools.build.lib.packages.EnvironmentGroup; import com.google.devtools.build.lib.packages.NoSuchPackageException; import com.google.devtools.build.lib.packages.NoSuchTargetException; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.pkgcache.PackageManager; import com.google.devtools.build.lib.skyframe.BuildConfigurationValue.Key; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringJoiner; import java.util.function.Function; import java.util.stream.Collectors; import javax.annotation.Nullable; /** * Constraint semantics that apply to top-level targets. * *

Top-level targets are "special" because they have no parents that can assert expected * environment compatibility. So these expectations have to be declared by other means. * *

For all other targets see {@link ConstraintSemantics}. */ public class TopLevelConstraintSemantics { private final PackageManager packageManager; private final Function configurationProvider; private final ExtendedEventHandler eventHandler; /** * Constructor with helper classes for loading targets. * * @param packageManager object for retrieving loaded targets * @param eventHandler the build's event handler */ public TopLevelConstraintSemantics( PackageManager packageManager, Function configurationProvider, ExtendedEventHandler eventHandler) { this.packageManager = packageManager; this.configurationProvider = configurationProvider; this.eventHandler = eventHandler; } private static class MissingEnvironment { private final Label environment; @Nullable // If null, the top-level target just didn't declare a required environment. If not null, that // means the declaration got "refined" away due to some select() somewhere in its deps. See // ConstraintSemantics's documentation for an explanation of refinement. private final RemovedEnvironmentCulprit culprit; private MissingEnvironment(Label environment, RemovedEnvironmentCulprit culprit) { this.environment = environment; this.culprit = culprit; } } /** * Checks that if this is an environment-restricted build, all top-level targets support expected * top-level environments. Expected top-level environments can be declared explicitly through * {@code --target_environment} or implicitly through {@code --auto_cpu_environment_group}. For * the latter, top-level targets must be compatible with the build's target configuration CPU. * *

If any target doesn't support an explicitly expected environment declared through {@link * BuildConfiguration.Options#targetEnvironments}, the entire build fails with an error. * *

If any target doesn't support an implicitly expected environment declared through {@link * BuildConfiguration.Options#autoCpuEnvironmentGroup}, the target is skipped during execution * while remaining targets execute as normal. * * @param topLevelTargets the build's top-level targets * @return the set of bad top-level targets. * @throws ViewCreationFailedException if any target doesn't support an explicitly expected * environment declared through {@link BuildConfiguration.Options#targetEnvironments} */ public Set checkTargetEnvironmentRestrictions( ImmutableList topLevelTargets) throws ViewCreationFailedException, InterruptedException { ImmutableSet.Builder badTargets = ImmutableSet.builder(); // Maps targets that are missing *explicitly* required environments to the set of environments // they're missing. These targets trigger a ViewCreationFailedException, which halts the build. // Targets with missing *implicitly* required environments don't belong here, since the build // continues while skipping them. Multimap exceptionInducingTargets = ArrayListMultimap.create(); for (ConfiguredTarget topLevelTarget : topLevelTargets) { BuildConfiguration config = configurationProvider.apply(topLevelTarget.getConfigurationKey()); Target target = null; try { target = packageManager.getTarget(eventHandler, topLevelTarget.getLabel()); } catch (NoSuchPackageException | NoSuchTargetException e) { eventHandler.handle( Event.error( "Unable to get target from package when checking environment restrictions. " + e)); continue; } if (config == null) { // TODO(bazel-team): support file targets (they should apply package-default constraints). continue; } else if (!config.enforceConstraints()) { continue; // Constraint checking is disabled for all targets. } else if (target.getAssociatedRule() == null) { continue; } else if (!target.getAssociatedRule().getRuleClassObject().supportsConstraintChecking()) { continue; // This target doesn't participate in constraints. } // Check explicitly expected environments. exceptionInducingTargets.putAll(topLevelTarget, // This is a no-op on empty collections. getMissingEnvironments(topLevelTarget, config.getTargetEnvironments())); // Check auto-detected CPU environments. try { if (!getMissingEnvironments(topLevelTarget, autoConfigureTargetEnvironments(config, config.getAutoCpuEnvironmentGroup())) .isEmpty()) { badTargets.add(topLevelTarget); } } catch (NoSuchPackageException | NoSuchTargetException e) { throw new ViewCreationFailedException("invalid target environment", e); } } if (!exceptionInducingTargets.isEmpty()) { throw new ViewCreationFailedException(getBadTargetsUserMessage(exceptionInducingTargets)); } return ImmutableSet.copyOf( badTargets .addAll(exceptionInducingTargets.keySet()) .build()); } /** * Helper method for {@link #checkTargetEnvironmentRestrictions} that populates inferred * expected environments. */ private List