filesToBuild = NestedSetBuilder.emptySet(Order.STABLE_ORDER);
+ FileProvider fileProvider = new FileProvider(filesToBuild);
+ FilesToRunProvider filesToRunProvider = new FilesToRunProvider(filesToBuild, null, null);
+
+ TransitiveInfoProviderMapBuilder providerBuilder =
+ new TransitiveInfoProviderMapBuilder()
+ .put(incompatiblePlatformProvider)
+ .add(RunfilesProvider.simple(Runfiles.EMPTY))
+ .add(fileProvider)
+ .add(filesToRunProvider);
+ if (configuration.hasFragment(TestConfiguration.class)) {
+ // Create a dummy TestProvider instance so that other parts of the code base stay happy. Even
+ // though this test will never execute, some code still expects the provider.
+ TestProvider.TestParams testParams = TestActionBuilder.createEmptyTestParams();
+ providerBuilder.put(TestProvider.class, new TestProvider(testParams));
+ }
+
+ RuleConfiguredTarget configuredTarget =
+ new RuleConfiguredTarget(
+ target.getLabel(),
+ configuration.getKey(),
+ convertVisibility(),
+ providerBuilder.build(),
+ configConditions.asProviders(),
+ ruleClassString);
+ return new RuleConfiguredTargetValue(
+ configuredTarget,
+ transitivePackagesForPackageRootResolution == null
+ ? null
+ : transitivePackagesForPackageRootResolution.build());
+ }
+
+ /**
+ * Generates visibility for an incompatible target.
+ *
+ * The intent is for this function is to match ConfiguredTargetFactory.convertVisibility().
+ * Since visibility is currently validated after incompatibility is evaluated, however, it doesn't
+ * matter what visibility we set here. To keep it simple, we pretend that all incompatible targets
+ * are public.
+ *
+ *
TODO(#16044): Set up properly validated visibility here.
+ */
+ private static NestedSet convertVisibility() {
+ return NestedSetBuilder.create(
+ Order.STABLE_ORDER,
+ PackageGroupContents.create(ImmutableList.of(PackageSpecification.everything())));
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/constraints/RuleContextConstraintSemantics.java b/src/main/java/com/google/devtools/build/lib/analysis/constraints/RuleContextConstraintSemantics.java
index ea888e9b6f4b62..d04b3cc2098adc 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/constraints/RuleContextConstraintSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/constraints/RuleContextConstraintSemantics.java
@@ -14,39 +14,24 @@
package com.google.devtools.build.lib.analysis.constraints;
-import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.auto.value.AutoValue;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Verify;
import com.google.common.collect.ImmutableCollection;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
-import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.actions.FailAction;
-import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
-import com.google.devtools.build.lib.analysis.DependencyKind;
import com.google.devtools.build.lib.analysis.IncompatiblePlatformProvider;
import com.google.devtools.build.lib.analysis.LabelAndLocation;
-import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
import com.google.devtools.build.lib.analysis.RuleContext;
-import com.google.devtools.build.lib.analysis.Runfiles;
-import com.google.devtools.build.lib.analysis.RunfilesProvider;
-import com.google.devtools.build.lib.analysis.RunfilesSupport;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.configuredtargets.OutputFileConfiguredTarget;
import com.google.devtools.build.lib.analysis.constraints.EnvironmentCollection.EnvironmentWithGroup;
import com.google.devtools.build.lib.analysis.constraints.SupportedEnvironmentsProvider.RemovedEnvironmentCulprit;
-import com.google.devtools.build.lib.analysis.platform.ConstraintValueInfo;
-import com.google.devtools.build.lib.analysis.platform.PlatformProviderUtils;
-import com.google.devtools.build.lib.analysis.test.AnalysisTestResultInfo;
import com.google.devtools.build.lib.cmdline.Label;
-import com.google.devtools.build.lib.collect.nestedset.NestedSet;
-import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.AttributeMap;
import com.google.devtools.build.lib.packages.BuildType;
@@ -58,9 +43,6 @@
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.packages.Type.LabelClass;
import com.google.devtools.build.lib.packages.Type.LabelVisitor;
-import com.google.devtools.build.lib.server.FailureDetails.FailAction.Code;
-import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData;
-import com.google.devtools.build.lib.util.OrderedSetMultimap;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
@@ -882,159 +864,4 @@ public static IncompatibleCheckResult checkForIncompatibility(ConfiguredTarget t
return IncompatibleCheckResult.create(
target.get(IncompatiblePlatformProvider.PROVIDER) != null, target);
}
-
- /**
- * Creates an incompatible {@link ConfiguredTarget} if the corresponding rule is incompatible.
- *
- * Returns null if the target is not incompatible.
- *
- *
"Incompatible" here means either:
- *
- *
- * - the corresponding
target_compatible_with
list contains a constraint that the
- * current platform doesn't satisfy, or
- * - a transitive dependency is incompatible.
- *
- *
- * @param ruleContext analysis context for the rule
- * @param prerequisiteMap the dependencies of the rule
- * @throws ActionConflictException if the underlying {@link RuleConfiguredTargetBuilder}
- * encounters a problem when assembling a dummy action for the incompatible {@link
- * ConfiguredTarget}.
- */
- @Nullable
- public static ConfiguredTarget incompatibleConfiguredTarget(
- RuleContext ruleContext,
- OrderedSetMultimap prerequisiteMap)
- throws ActionConflictException, InterruptedException {
- // The target (ruleContext) is incompatible if explicitly specified to be. Any rule that
- // provides its own meaning for the "target_compatible_with" attribute has to be excluded here.
- // For example, the "toolchain" rule uses "target_compatible_with" for bazel's toolchain
- // resolution.
- if (!ruleContext.getRule().getRuleClass().equals("toolchain")
- && ruleContext.attributes().has("target_compatible_with")) {
- ImmutableList invalidConstraintValues =
- PlatformProviderUtils.constraintValues(
- ruleContext.getPrerequisites("target_compatible_with"))
- .stream()
- .filter(cv -> !ruleContext.targetPlatformHasConstraint(cv))
- .collect(toImmutableList());
-
- if (!invalidConstraintValues.isEmpty()) {
- return createIncompatibleConfiguredTarget(ruleContext, null, invalidConstraintValues);
- }
- }
-
- // This is incompatible if one of the dependencies is as well.
- ImmutableList incompatibleDependencies =
- prerequisiteMap.values().stream()
- .map(value -> checkForIncompatibility(value.getConfiguredTarget()))
- .filter(IncompatibleCheckResult::isIncompatible)
- .map(IncompatibleCheckResult::underlyingTarget)
- .collect(toImmutableList());
- if (!incompatibleDependencies.isEmpty()) {
- return createIncompatibleConfiguredTarget(ruleContext, incompatibleDependencies, null);
- }
-
- return null;
- }
-
- /**
- * A helper function for incompatibleConfiguredTarget() to actually create the incompatible {@link
- * ConfiguredTarget}.
- *
- * @param ruleContext analysis context for the rule
- * @param targetsResponsibleForIncompatibility the targets that are responsible this target's
- * incompatibility. If null, that means that target is responsible for its own
- * incompatibility. I.e. it has constraints in target_compatible_with that were not satisfied
- * on the target platform. This must be null if violatedConstraints is set. This must be set
- * if violatedConstraints is null.
- * @param violatedConstraints the constraints that the target platform doesn't satisfy. This must
- * be null if targetRsesponsibleForIncompatibility is set.
- * @throws ActionConflictException if the underlying {@link RuleConfiguredTargetBuilder}
- * encounters a problem when assembling a dummy action for the incompatible {@link
- * ConfiguredTarget}.
- */
- private static ConfiguredTarget createIncompatibleConfiguredTarget(
- RuleContext ruleContext,
- @Nullable ImmutableList targetsResponsibleForIncompatibility,
- @Nullable ImmutableList violatedConstraints)
- throws ActionConflictException, InterruptedException {
- // Create a dummy ConfiguredTarget that has the IncompatiblePlatformProvider set.
- ImmutableList outputArtifacts = ruleContext.getOutputArtifacts();
-
- if (ruleContext.isTestTarget() && outputArtifacts.isEmpty()) {
- // Test targets require RunfilesSupport to be added to the RuleConfiguredTargetBuilder
- // which needs an "executable". Create one here if none exist already.
- //
- // It would be ideal if we could avoid this. Currently the problem is that some rules like
- // sh_test only declare an output artifact in the corresponding ConfiguredTarget factory
- // function (see ShBinary.java). Since this code path here replaces the factory function rules
- // like sh_test never get a chance to declare an output artifact.
- //
- // On top of that, the rest of the code base makes the assumption that test targets provide an
- // instance RunfilesSupport. This can be seen in the TestProvider, the TestActionBuilder, and
- // the RuleConfiguredTargetBuilder classes. There might be a way to break this assumption, but
- // it's currently too heavily baked in to work around it more nicely than this.
- //
- // Theoretically, this hack shouldn't be an issue because the corresponding actions will never
- // get executed. They cannot be queried either.
- outputArtifacts = ImmutableList.of(ruleContext.createOutputArtifact());
- }
-
- NestedSet filesToBuild =
- NestedSetBuilder.stableOrder().addAll(outputArtifacts).build();
-
- Runfiles.Builder runfilesBuilder =
- new Runfiles.Builder(
- ruleContext.getWorkspaceName(),
- ruleContext.getConfiguration().legacyExternalRunfiles());
- Runfiles runfiles =
- runfilesBuilder
- .addTransitiveArtifacts(filesToBuild)
- .addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES)
- .build();
-
- RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext);
- builder.setFilesToBuild(filesToBuild);
-
- if (targetsResponsibleForIncompatibility != null) {
- builder.addNativeDeclaredProvider(
- IncompatiblePlatformProvider.incompatibleDueToTargets(
- ruleContext.targetPlatform(), targetsResponsibleForIncompatibility));
- } else if (violatedConstraints != null) {
- builder.addNativeDeclaredProvider(
- IncompatiblePlatformProvider.incompatibleDueToConstraints(
- ruleContext.targetPlatform(), violatedConstraints));
- } else {
- throw new IllegalArgumentException(
- "Both violatedConstraints and targetsResponsibleForIncompatibility are null");
- }
-
- // If this is an analysis test, RuleConfiguredTargetBuilder performs some additional sanity
- // checks. Satisfy them with an appropriate provider.
- if (ruleContext.getRule().isAnalysisTest()) {
- builder.addNativeDeclaredProvider(
- new AnalysisTestResultInfo(
- /*success=*/ false,
- "This test is incompatible and should not have been run. Please file a bug"
- + " upstream."));
- }
-
- builder.add(RunfilesProvider.class, RunfilesProvider.simple(runfiles));
- if (!outputArtifacts.isEmpty()) {
- Artifact executable = outputArtifacts.get(0);
- RunfilesSupport runfilesSupport =
- RunfilesSupport.withExecutableButNoArgs(ruleContext, runfiles, executable);
- builder.setRunfilesSupport(runfilesSupport, executable);
-
- ruleContext.registerAction(
- new FailAction(
- ruleContext.getActionOwner(),
- outputArtifacts,
- "Can't build this. This target is incompatible. Please file a bug upstream.",
- Code.CANT_BUILD_INCOMPATIBLE_TARGET));
- }
- return builder.build();
- }
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/constraints/TopLevelConstraintSemantics.java b/src/main/java/com/google/devtools/build/lib/analysis/constraints/TopLevelConstraintSemantics.java
index 5a381ebb5d9ec9..7c4d15ec8b9281 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/constraints/TopLevelConstraintSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/constraints/TopLevelConstraintSemantics.java
@@ -325,9 +325,12 @@ private static String reportOnIncompatibility(ConfiguredTarget target) {
message += "s [";
+ // Print out a sorted list to make the output reproducible.
boolean first = true;
for (ConstraintValueInfo constraintValueInfo :
- provider.constraintsResponsibleForIncompatibility()) {
+ ImmutableList.sortedCopyOf(
+ (ConstraintValueInfo a, ConstraintValueInfo b) -> b.label().compareTo(a.label()),
+ provider.constraintsResponsibleForIncompatibility())) {
if (first) {
first = false;
} else {
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java
index efc890ba89db80..69539f7b5857fb 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java
@@ -106,6 +106,26 @@ public TestActionBuilder(RuleContext ruleContext) {
this.additionalTools = new ImmutableList.Builder<>();
}
+ /**
+ * Creates test actions for a test that will never be executed.
+ *
+ * This is only really useful for things like creating incompatible test actions.
+ */
+ public static TestParams createEmptyTestParams() {
+ NestedSet filesToBuild = NestedSetBuilder.emptySet(Order.STABLE_ORDER);
+ FilesToRunProvider filesToRunProvider = new FilesToRunProvider(filesToBuild, null, null);
+ return new TestProvider.TestParams(
+ 0,
+ 0,
+ false,
+ TestTimeout.ETERNAL,
+ "invalid",
+ ImmutableList.of(),
+ ImmutableList.of(),
+ filesToRunProvider,
+ ImmutableList.of());
+ }
+
/**
* Creates the test actions and artifacts using the previously set parameters.
*
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
index cae50895fa30c1..658a70b5cf848f 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -255,6 +255,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/analysis:configured_object_value",
"//src/main/java/com/google/devtools/build/lib/analysis:configured_target",
"//src/main/java/com/google/devtools/build/lib/analysis:configured_target_value",
+ "//src/main/java/com/google/devtools/build/lib/analysis:constraints/incompatible_target_checker",
"//src/main/java/com/google/devtools/build/lib/analysis:dependency",
"//src/main/java/com/google/devtools/build/lib/analysis:dependency_key",
"//src/main/java/com/google/devtools/build/lib/analysis:dependency_kind",
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
index ddcc8d29fbc390..d2c84731ae68a8 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
@@ -55,6 +55,7 @@
import com.google.devtools.build.lib.analysis.config.ToolchainTypeRequirement;
import com.google.devtools.build.lib.analysis.config.transitions.PatchTransition;
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget;
+import com.google.devtools.build.lib.analysis.constraints.IncompatibleTargetChecker;
import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
import com.google.devtools.build.lib.bugreport.BugReport;
import com.google.devtools.build.lib.causes.AnalysisFailedCause;
@@ -98,6 +99,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@@ -301,6 +303,8 @@ public SkyValue compute(SkyKey key, Environment env)
}
unloadedToolchainContexts = result.toolchainCollection;
execGroupCollectionBuilder = result.execGroupCollectionBuilder;
+ PlatformInfo platformInfo =
+ unloadedToolchainContexts != null ? unloadedToolchainContexts.getTargetPlatform() : null;
// Get the configuration targets that trigger this rule's configurable attributes.
ConfigConditions configConditions =
@@ -308,9 +312,7 @@ public SkyValue compute(SkyKey key, Environment env)
env,
targetAndConfiguration,
state.transitivePackagesForPackageRootResolution,
- unloadedToolchainContexts == null
- ? null
- : unloadedToolchainContexts.getTargetPlatform(),
+ platformInfo,
state.transitiveRootCauses);
if (env.valuesMissing()) {
return null;
@@ -335,6 +337,20 @@ public SkyValue compute(SkyKey key, Environment env)
getPrioritizedDetailedExitCode(causes)));
}
+ Optional incompatibleTarget =
+ IncompatibleTargetChecker.createDirectlyIncompatibleTarget(
+ targetAndConfiguration,
+ configConditions,
+ env,
+ platformInfo,
+ state.transitivePackagesForPackageRootResolution);
+ if (incompatibleTarget == null) {
+ return null;
+ }
+ if (incompatibleTarget.isPresent()) {
+ return incompatibleTarget.get();
+ }
+
// Calculate the dependencies of this target.
OrderedSetMultimap depValueMap =
computeDependencies(
@@ -367,6 +383,17 @@ public SkyValue compute(SkyKey key, Environment env)
}
Preconditions.checkNotNull(depValueMap);
+ incompatibleTarget =
+ IncompatibleTargetChecker.createIndirectlyIncompatibleTarget(
+ targetAndConfiguration,
+ depValueMap,
+ configConditions,
+ platformInfo,
+ state.transitivePackagesForPackageRootResolution);
+ if (incompatibleTarget.isPresent()) {
+ return incompatibleTarget.get();
+ }
+
// Load the requested toolchains into the ToolchainContext, now that we have dependencies.
ToolchainCollection toolchainContexts = null;
if (unloadedToolchainContexts != null) {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/RuleConfiguredTargetValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/RuleConfiguredTargetValue.java
index a1ca50cffc23c6..4eba615a10e3ec 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/RuleConfiguredTargetValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/RuleConfiguredTargetValue.java
@@ -49,7 +49,7 @@ public final class RuleConfiguredTargetValue
this(configuredTarget, /*transitivePackagesForPackageRootResolution=*/ null);
}
- RuleConfiguredTargetValue(
+ public RuleConfiguredTargetValue(
RuleConfiguredTarget configuredTarget,
@Nullable NestedSet transitivePackagesForPackageRootResolution) {
this.configuredTarget = Preconditions.checkNotNull(configuredTarget);
diff --git a/src/test/shell/integration/target_compatible_with_test.sh b/src/test/shell/integration/target_compatible_with_test.sh
index c9e83de68c36e9..042fe678ac8992 100755
--- a/src/test/shell/integration/target_compatible_with_test.sh
+++ b/src/test/shell/integration/target_compatible_with_test.sh
@@ -182,6 +182,14 @@ sh_binary(
":foo3",
],
)
+
+# Use this to let us change select() statements from the command line.
+config_setting(
+ name = "setting1",
+ define_values = {
+ "foo": "1",
+ },
+)
EOF
}
@@ -192,6 +200,77 @@ function tear_down() {
bazel shutdown
}
+function set_up_custom_toolchain() {
+ mkdir -p target_skipping/custom_tools/
+ cat > target_skipping/custom_tools/BUILD < target_skipping/custom_tools/toolchain.bzl <> target_skipping/BUILD < "${TEST_log}" \
+ || fail "Bazel build failed unexpectedly."
+ expect_log 'Target //target_skipping:some_library up-to-date'
+}
+
+function test_incompatible_with_aliased_target() {
+ cat >> target_skipping/BUILD < "${TEST_log}" \
+ && fail "Bazel passed unexpectedly."
+ expect_log 'ERROR: Target //target_skipping:also_some_foo3_target is incompatible and cannot be built, but was explicitly requested'
+ expect_log 'FAILED: Build did NOT complete successfully'
+}
+
+# Validate that an incompatible target with a toolchain not available for the
+# current platform will not cause an analysis error. This is a regression test
+# for https://github.com/bazelbuild/bazel/issues/12897.
+function test_incompatible_with_missing_toolchain() {
+ set_up_custom_toolchain
+
+ cat >> target_skipping/BUILD < target_skipping/custom.txt < "${TEST_log}" \
+ || fail "Bazel build failed unexpectedly."
+ expect_log 'Target //target_skipping:objc was skipped'
+ expect_log 'Target //target_skipping:custom1 was skipped'
+ expect_log 'Target //target_skipping:custom2 was skipped'
+}
+
+# Validates that if a target is "directly incompatible" then its dependencies
+# are not evaluated. I.e. there should be no need to guard the dependencies
+# with a select() statement.
+function test_invalid_deps_are_ignored_when_incompatible() {
+ cat >> target_skipping/BUILD < "${TEST_log}" \
+ || fail "Bazel build failed unexpectedly."
+ expect_log 'Target //target_skipping:incompatible_tool was skipped'
+}
+
# Validates that a tool compatible with the host platform, but incompatible
# with the target platform can still be used as a host tool.
function test_host_tool() {