From ad241fbebd90a9f0ad65ccd0658838f57030db68 Mon Sep 17 00:00:00 2001 From: Philipp Schrader Date: Tue, 9 Feb 2021 13:41:33 -0800 Subject: [PATCH] Allow cquery to filter out incompatible targets This patch exposes the `IncompatiblePlatformProvider` to Starlark just enough that it can be used with `cquery`'s `platforms()` function. I added an example of this to the documentation. The motivation here is to let users filter out incompatible targets from queries that provide things like the list of targets to build for CI. This patch is minimal on purpose. It does not allow users to instantiate an `IncompatiblePlatformProvider` in Starlark. This may be added in a future patch to address a different use case. Fixes #12917. Closes #12935. PiperOrigin-RevId: 356580943 --- site/docs/platforms.md | 23 +++++++++++ .../google/devtools/build/lib/analysis/BUILD | 4 +- .../IncompatiblePlatformProvider.java | 26 ++++++++++++- .../RuleContextConstraintSemantics.java | 8 ++-- .../TopLevelConstraintSemantics.java | 2 +- .../IncompatiblePlatformProviderApi.java | 30 +++++++++++++++ .../target_compatible_with_test.sh | 38 +++++++++++++++++++ 7 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/google/devtools/build/lib/starlarkbuildapi/platform/IncompatiblePlatformProviderApi.java diff --git a/site/docs/platforms.md b/site/docs/platforms.md index 9d6466201548ee..2dbc583201eddf 100644 --- a/site/docs/platforms.md +++ b/site/docs/platforms.md @@ -225,3 +225,26 @@ cc_library( ) ``` +### Detecting incompatible targets using `bazel cquery` + +You can use the +[`IncompatiblePlatformProvider`](skylark/lib/IncompatiblePlatformProvider.html) +in `bazel cquery`'s [Starlark output +format](cquery.html#defining-the-output-format-using-starlark) to distinguish +incompatible targets from compatible ones. + +This can be used to filter out incompatible targets. The example below will +only print the labels for targets that are compatible. Incompatible targets are +not printed. + +```console +$ cat example.cquery + +def format(target): + if "IncompatiblePlatformProvider" not in providers(target): + return target.label + return "" + + +$ bazel cquery //... --output=starlark --starlark:file=example.cquery +``` diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BUILD b/src/main/java/com/google/devtools/build/lib/analysis/BUILD index 52ef94a4d78caa..6344f7300eefd5 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/BUILD +++ b/src/main/java/com/google/devtools/build/lib/analysis/BUILD @@ -797,8 +797,10 @@ java_library( srcs = ["IncompatiblePlatformProvider.java"], deps = [ ":configured_target", - ":transitive_info_provider", "//src/main/java/com/google/devtools/build/lib/analysis/platform", + "//src/main/java/com/google/devtools/build/lib/concurrent", + "//src/main/java/com/google/devtools/build/lib/packages", + "//src/main/java/com/google/devtools/build/lib/starlarkbuildapi/platform", "//third_party:auto_value", "//third_party:guava", "//third_party:jsr305", diff --git a/src/main/java/com/google/devtools/build/lib/analysis/IncompatiblePlatformProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/IncompatiblePlatformProvider.java index da72929f6c384c..ca5377457f2b28 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/IncompatiblePlatformProvider.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/IncompatiblePlatformProvider.java @@ -18,6 +18,10 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.analysis.platform.ConstraintValueInfo; +import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; +import com.google.devtools.build.lib.packages.BuiltinProvider; +import com.google.devtools.build.lib.packages.Info; +import com.google.devtools.build.lib.starlarkbuildapi.platform.IncompatiblePlatformProviderApi; import javax.annotation.Nullable; /** @@ -34,8 +38,23 @@ * target is incompatible because one of its dependencies is incompatible, then all the incompatible * dependencies are available via {@code getTargetResponsibleForIncompatibility()}. */ +@Immutable @AutoValue -public abstract class IncompatiblePlatformProvider implements TransitiveInfoProvider { +public abstract class IncompatiblePlatformProvider + implements Info, IncompatiblePlatformProviderApi { + /** Name used in Starlark for accessing this provider. */ + public static final String STARLARK_NAME = "IncompatiblePlatformProvider"; + + /** Provider singleton constant. */ + public static final BuiltinProvider PROVIDER = + new BuiltinProvider( + STARLARK_NAME, IncompatiblePlatformProvider.class) {}; + + @Override + public BuiltinProvider getProvider() { + return PROVIDER; + } + public static IncompatiblePlatformProvider incompatibleDueToTargets( ImmutableList targetsResponsibleForIncompatibility) { Preconditions.checkNotNull(targetsResponsibleForIncompatibility); @@ -50,6 +69,11 @@ public static IncompatiblePlatformProvider incompatibleDueToConstraints( return new AutoValue_IncompatiblePlatformProvider(null, constraints); } + @Override + public boolean isImmutable() { + return true; // immutable and Starlark-hashable + } + /** * Returns the incompatible dependencies that caused this provider to be present. * 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 bb94ee44debd30..580a336d2b6c96 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 @@ -880,7 +880,7 @@ public static IncompatibleCheckResult checkForIncompatibility(ConfiguredTarget t target = ((OutputFileConfiguredTarget) target).getGeneratingRule(); } return IncompatibleCheckResult.create( - target.getProvider(IncompatiblePlatformProvider.class) != null, target); + target.get(IncompatiblePlatformProvider.PROVIDER) != null, target); } /** @@ -996,13 +996,11 @@ private static ConfiguredTarget createIncompatibleConfiguredTarget( builder.setFilesToBuild(filesToBuild); if (targetsResponsibleForIncompatibility != null) { - builder.add( - IncompatiblePlatformProvider.class, + builder.addNativeDeclaredProvider( IncompatiblePlatformProvider.incompatibleDueToTargets( targetsResponsibleForIncompatibility)); } else if (violatedConstraints != null) { - builder.add( - IncompatiblePlatformProvider.class, + builder.addNativeDeclaredProvider( IncompatiblePlatformProvider.incompatibleDueToConstraints(violatedConstraints)); } else { throw new IllegalArgumentException( 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 539d988076df54..b17a40d543b554 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 @@ -195,7 +195,7 @@ private static String reportOnIncompatibility(ConfiguredTarget target) { // save the user bazel round trips. while (target != null) { message += "\n " + target.getLabel(); - provider = target.getProvider(IncompatiblePlatformProvider.class); + provider = target.get(IncompatiblePlatformProvider.PROVIDER); ImmutableList targetList = provider.targetsResponsibleForIncompatibility(); if (targetList == null) { target = null; diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/platform/IncompatiblePlatformProviderApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/platform/IncompatiblePlatformProviderApi.java new file mode 100644 index 00000000000000..624d6c14d3c9e5 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/platform/IncompatiblePlatformProviderApi.java @@ -0,0 +1,30 @@ +// Copyright 2021 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.starlarkbuildapi.platform; + +import com.google.devtools.build.docgen.annot.DocCategory; +import net.starlark.java.annot.StarlarkBuiltin; +import net.starlark.java.eval.StarlarkValue; + +/** Targets that are incompatible with the target platform. */ +@StarlarkBuiltin( + name = "IncompatiblePlatformProvider", + doc = + "A provider for targets that are incompatible with the target platform. See " + + "" + + "Detecting incompatible targets using bazel cquery for more " + + "information.", + category = DocCategory.PROVIDER) +public interface IncompatiblePlatformProviderApi extends StarlarkValue {} diff --git a/src/test/shell/integration/target_compatible_with_test.sh b/src/test/shell/integration/target_compatible_with_test.sh index b79f1ba5a869d9..375ca2a43561b9 100755 --- a/src/test/shell/integration/target_compatible_with_test.sh +++ b/src/test/shell/integration/target_compatible_with_test.sh @@ -912,6 +912,44 @@ function test_cquery_incompatible_target() { expect_log "target platform didn't satisfy constraint //target_skipping:foo1" } +# Runs a cquery and makes sure that we can properly distinguish between +# incompatible targets and compatible targets. +function test_cquery_with_starlark_formatting() { + cat > target_skipping/compatibility.cquery < "${TEST_log}" + + expect_log '^//target_skipping:pass_on_foo1 is compatible$' + expect_log '^//target_skipping:fail_on_foo2 is incompatible$' + expect_log '^//target_skipping:some_foo3_target is incompatible$' + + bazel cquery \ + --host_platform=//target_skipping:foo3_platform \ + --platforms=//target_skipping:foo3_platform \ + :all \ + --output=starlark --starlark:file=target_skipping/compatibility.cquery \ + &> "${TEST_log}" + + expect_log '^//target_skipping:pass_on_foo1 is incompatible$' + expect_log '^//target_skipping:fail_on_foo2 is incompatible$' + expect_log '^//target_skipping:some_foo3_target is compatible$' +} + # Run an aquery on a target that is compatible. This should pass. function test_aquery_compatible_target() { write_query_test_targets