Skip to content

Commit

Permalink
Allow cquery to filter out incompatible targets
Browse files Browse the repository at this point in the history
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 bazelbuild#12917.

Closes bazelbuild#12935.

PiperOrigin-RevId: 356580943
  • Loading branch information
philsc authored and Copybara-Service committed Feb 9, 2021
1 parent c6838bd commit ad241fb
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 8 deletions.
23 changes: 23 additions & 0 deletions site/docs/platforms.md
Expand Up @@ -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
```
4 changes: 3 additions & 1 deletion src/main/java/com/google/devtools/build/lib/analysis/BUILD
Expand Up @@ -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",
Expand Down
Expand Up @@ -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;

/**
Expand All @@ -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<IncompatiblePlatformProvider> PROVIDER =
new BuiltinProvider<IncompatiblePlatformProvider>(
STARLARK_NAME, IncompatiblePlatformProvider.class) {};

@Override
public BuiltinProvider<IncompatiblePlatformProvider> getProvider() {
return PROVIDER;
}

public static IncompatiblePlatformProvider incompatibleDueToTargets(
ImmutableList<ConfiguredTarget> targetsResponsibleForIncompatibility) {
Preconditions.checkNotNull(targetsResponsibleForIncompatibility);
Expand All @@ -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.
*
Expand Down
Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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(
Expand Down
Expand Up @@ -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<ConfiguredTarget> targetList = provider.targetsResponsibleForIncompatibility();
if (targetList == null) {
target = null;
Expand Down
@@ -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 "
+ "<a href='../../platforms.html#detecting-incompatible-targets-using-bazel-cquery'>"
+ "Detecting incompatible targets using <code>bazel cquery</code></a> for more "
+ "information.",
category = DocCategory.PROVIDER)
public interface IncompatiblePlatformProviderApi extends StarlarkValue {}
38 changes: 38 additions & 0 deletions src/test/shell/integration/target_compatible_with_test.sh
Expand Up @@ -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 <<EOF
def format(target):
if "IncompatiblePlatformProvider" in providers(target):
result = "incompatible"
else:
result = "compatible"
return "%s is %s" % (target.label, result)
EOF
cd target_skipping || fail "couldn't cd into workspace"
bazel cquery \
--host_platform=//target_skipping:foo1_bar1_platform \
--platforms=//target_skipping:foo1_bar1_platform \
:all \
--output=starlark --starlark:file=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
Expand Down

0 comments on commit ad241fb

Please sign in to comment.