Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ public boolean containsAll(Iterable<ConstraintValueInfo> expected) {
return findMissing(expected).isEmpty();
}

/**
* Returns the set of {@link ConstraintValueInfo constraints} from {@code expected} that are not
* present in this {@link ConstraintCollection}, either directly, or by being the default for
* their {@link ConstraintSettingInfo}.
*/
public ImmutableList<ConstraintValueInfo> findMissing(ConstraintCollection expected) {
return findMissing(expected.constraints().values());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be preexisting, but I think that parent on a platform isn't handled correctly by this as well as the "target to exec" logic. Could you add a test exercising that behavior and fix it if needed?

}

/**
* Returns the set of {@link ConstraintValueInfo constraints} from {@code expected} that are not
* present in this {@link ConstraintCollection}, either directly, or by being the default for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public record DeclaredToolchainInfo(
ToolchainTypeInfo toolchainType,
ConstraintCollection execConstraints,
ConstraintCollection targetConstraints,
boolean useTargetPlatformConstraints,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add an @param

ImmutableList<ConfigMatchingProvider> targetSettings,
Label targetLabel,
Label resolvedToolchainLabel)
Expand All @@ -58,27 +59,15 @@ public record DeclaredToolchainInfo(
}

public boolean hasTargetToExecConstraints() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can just be dropped/inlined

// This needs to check identity as the special ConstraintCollection is otherwise equal to the
// empty one. This avoids adding a new field or making ConstraintCollection more complex.
return execConstraints == USE_TARGET_PLATFORM_CONSTRAINTS
&& targetConstraints == USE_TARGET_PLATFORM_CONSTRAINTS;
}

private static final ConstraintCollection USE_TARGET_PLATFORM_CONSTRAINTS;

static {
try {
USE_TARGET_PLATFORM_CONSTRAINTS = ConstraintCollection.builder().build();
} catch (ConstraintCollection.DuplicateConstraintException e) {
throw new IllegalStateException(e);
}
return useTargetPlatformConstraints;
}

/** Builder class to assist in creating {@link DeclaredToolchainInfo} instances. */
public static class Builder {
private ToolchainTypeInfo toolchainType;
private final ConstraintCollection.Builder execConstraints = ConstraintCollection.builder();
private final ConstraintCollection.Builder targetConstraints = ConstraintCollection.builder();
private boolean useTargetPlatformConstraints = false;
private final ImmutableList.Builder<ConfigMatchingProvider> targetSettings =
new ImmutableList.Builder<>();
private Label targetLabel;
Expand Down Expand Up @@ -117,6 +106,13 @@ public Builder addTargetConstraints(ConstraintValueInfo... constraints) {
return addTargetConstraints(ImmutableList.copyOf(constraints));
}

/** Sets the flag to add unmatched target platform constraints as exec constraints. */
@CanIgnoreReturnValue
public Builder useTargetPlatformConstraints(boolean enabled) {
this.useTargetPlatformConstraints = enabled;
return this;
}

@CanIgnoreReturnValue
public Builder addTargetSettings(Iterable<ConfigMatchingProvider> targetSettings) {
this.targetSettings.addAll(targetSettings);
Expand Down Expand Up @@ -164,16 +160,7 @@ public DeclaredToolchainInfo build() throws DuplicateConstraintException {
toolchainType,
execConstraints,
targetConstraints,
targetSettings.build(),
targetLabel,
resolvedToolchainLabel);
}

public DeclaredToolchainInfo buildWithTargetToExecConstraints() {
return new DeclaredToolchainInfo(
toolchainType,
USE_TARGET_PLATFORM_CONSTRAINTS,
USE_TARGET_PLATFORM_CONSTRAINTS,
useTargetPlatformConstraints,
targetSettings.build(),
targetLabel,
resolvedToolchainLabel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,31 +62,19 @@ public ConfiguredTarget create(RuleContext ruleContext)
ruleContext
.attributes()
.get(ToolchainRule.USE_TARGET_PLATFORM_CONSTRAINTS_ATTR, Type.BOOLEAN);
if (targetToExecConstraints && !(execConstraints.isEmpty() && targetConstraints.isEmpty())) {
ruleContext.attributeError(
ToolchainRule.USE_TARGET_PLATFORM_CONSTRAINTS_ATTR,
"Cannot set use_target_platform_constraints to True and also set exec_compatible_with or "
+ "target_compatible_with");
return null;
}

DeclaredToolchainInfo registeredToolchain;
try {
var registeredToolchainBuilder =
registeredToolchain =
DeclaredToolchainInfo.builder()
.toolchainType(toolchainType)
.addTargetSettings(targetSettings)
.resolvedToolchainLabel(resolvedToolchainLabel)
.targetLabel(ruleContext.getLabel());
if (targetToExecConstraints) {
registeredToolchain = registeredToolchainBuilder.buildWithTargetToExecConstraints();
} else {
registeredToolchain =
registeredToolchainBuilder
.addExecConstraints(execConstraints)
.addTargetConstraints(targetConstraints)
.build();
}
.targetLabel(ruleContext.getLabel())
.addExecConstraints(execConstraints)
.addTargetConstraints(targetConstraints)
.useTargetPlatformConstraints(targetToExecConstraints)
.build();
} catch (DeclaredToolchainInfo.DuplicateConstraintException e) {
if (e.execConstraintsException() != null) {
ruleContext.attributeError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,8 @@ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env)
.allowedFileTypes(FileTypeSet.NO_FILE)
.nonconfigurable("part of toolchain configuration"))
/* <!-- #BLAZE_RULE(toolchain).ATTRIBUTE(use_target_platform_constraints) -->
If <code>True</code>, this toolchain behaves as if its <code>exec_compatible_with</code> and
<code>target_compatible_with</code> constraints are set to those of the current target
platform. <code>exec_compatible_with</code> and <code>target_compatible_with</code> must not
be set in that case.
If <code>True</code>, the constraints in the current target platform that are not part of
<code>target_compatible_with</code> will be added to <code>exec_compatible_with</code>.
<!-- #END_BLAZE_RULE.ATTRIBUTE --> */
.add(
attr(USE_TARGET_PLATFORM_CONSTRAINTS_ATTR, Type.BOOLEAN)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,8 @@ private static SingleToolchainResolutionValue resolveConstraints(
.collect(toImmutableList());

for (DeclaredToolchainInfo toolchain : filteredToolchains) {
// Make sure the target platform matches. A toolchain with use_target_platform_constraints
// matches
// any target platform.
if (!toolchain.hasTargetToExecConstraints()
&& !checkConstraints(
// Make sure the target platform matches.
if (!checkConstraints(
debugPrinter,
toolchain.targetConstraints(),
/* isTargetPlatform= */ true,
Expand All @@ -183,6 +180,17 @@ private static SingleToolchainResolutionValue resolveConstraints(
toolchain.resolvedToolchainLabel())) {
continue;
}
ConstraintCollection unmatchedTargetConstraints = null;
if (toolchain.useTargetPlatformConstraints()) {
try {
unmatchedTargetConstraints = ConstraintCollection.builder().addConstraints(
toolchain.targetConstraints().findMissing(targetPlatform.constraints())
).build();
} catch (ConstraintCollection.DuplicateConstraintException e) {
// This should never happen because this is a subset of targetPlatform.constraints().
throw new IllegalStateException("unreachable: " + e);
}
}

debugPrinter.reportCompatibleTargetPlatform(
toolchain.targetLabel(), toolchain.resolvedToolchainLabel());
Expand Down Expand Up @@ -213,9 +221,18 @@ private static SingleToolchainResolutionValue resolveConstraints(
// Check if the execution constraints match.
if (!checkConstraints(
debugPrinter,
toolchain.hasTargetToExecConstraints()
? targetPlatform.constraints()
: toolchain.execConstraints(),
toolchain.execConstraints(),
/* isTargetPlatform= */ false,
executionPlatform,
toolchain.targetLabel(),
toolchain.resolvedToolchainLabel())) {
// Keep looking for a valid toolchain for this exec platform
done = false;
continue;
}
if (toolchain.useTargetPlatformConstraints() && !checkConstraints(
debugPrinter,
unmatchedTargetConstraints,
/* isTargetPlatform= */ false,
executionPlatform,
toolchain.targetLabel(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,144 @@ def _some_test_impl(ctx):
assertThat(testAction.getExecProperties()).containsExactly("os", targetOs, "cpu", targetCpu);
}

/**

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to see a test that verifies that target_compatible_with is checked for a toolchain with use_target_platform_constraints.

* With the default test toolchain, a test action should run on a platform that matches all
* constraints of the target platform.
*/
@Test
public void testExecPlatformMatchesTargetConstraintsWithDefaultTestToolchainFRME() throws Exception {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name doesn't match the content (and FRME is probably not meant to be there either ;-))

scratch.file(
"some_test.bzl",
"""
def _some_test_impl(ctx):
script = ctx.actions.declare_file(ctx.attr.name + ".sh")
ctx.actions.write(script, "shell script goes here", is_executable = True)
return [
DefaultInfo(executable = script),
]

some_test = rule(
implementation = _some_test_impl,
test = True,
exec_groups = {
"test": exec_group(
toolchains = [Label(":my_test_toolchain_type")],
),
},
)
""");
scratch.file(
"BUILD",
"""
load(":some_test.bzl", "some_test")

toolchain_type(name = "my_test_toolchain_type")

constraint_setting(name = "exec")
constraint_value(
name = "is_exec",
constraint_setting = ":exec",
)

constraint_setting(name = "target")
constraint_value(
name = "is_device",
constraint_setting = ":target",
)

platform(
name = "target_on_device",
constraint_values = [
"%1$sos:linux",
"%1$scpu:aarch64",
":is_device",
],
)

toolchain(
name = "aarch64_test_toolchain",
target_compatible_with = [
"%1$scpu:aarch64",
"%1$sos:linux",
],
exec_compatible_with = [
"%1$scpu:aarch64",
"//:is_exec",
],
toolchain_type = ":my_test_toolchain_type",
use_target_platform_constraints = True,
toolchain = "%2$s//tools/test:empty_toolchain",
)

# Default test toolchain will require exec platform with:
# :linux
# :aarch64
# :is_device
# linux_gpu_test_toolchain will require exec platform with:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment needs to be adapted to this test case, there is no linux_gpu_test_toolchain.

# :aarch64 - Toolchain exec_compatible_with (and target_compatible with).
# :is_device - Requirement from target platform (use_target_platform_constraints).
# :is_exec - Toolchain exec_compatible_with.

platform(
name = "exec_missing_aarch64",
constraint_values = [
"%1$sos:linux",
":is_exec",
":is_device",
],
exec_properties = {"key": "bad"},
)

platform(
name = "exec_missing_is_device",
constraint_values = [
"%1$sos:linux",
"%1$scpu:aarch64",
":is_exec",
],
exec_properties = {"key": "bad"},
)

platform(
name = "exec_missing_is_exec",
constraint_values = [
"%1$sos:linux",
"%1$scpu:aarch64",
":is_device",
],
exec_properties = {"key": "bad"},
)

platform(
name = "exec_expected",
constraint_values = [
"%1$scpu:aarch64",
":is_exec",
":is_device",
# macos is not needed: Toolchain target_compatible_with Linux, not exec_compatible_with any os.
"%1$sos:macos",
],
exec_properties = {"key": "good"},
)

some_test(name = "some_test")
"""
.formatted(TestConstants.CONSTRAINTS_PACKAGE_ROOT, TestConstants.TOOLS_REPOSITORY.getCanonicalForm()));
useConfiguration(
String.format(
"--%s//tools/test:incompatible_use_default_test_toolchain",
TestConstants.TOOLS_REPOSITORY.getCanonicalForm()),
"--platforms=//:target_on_device",
"--extra_toolchains=//:aarch64_test_toolchain",
"--extra_execution_platforms=//:exec_missing_aarch64,//:exec_missing_is_device,//:exec_missing_is_exec,//:exec_expected");
ImmutableList<Artifact.DerivedArtifact> testStatusList = getTestStatusArtifacts("//:some_test");
assertThat(testStatusList).hasSize(1);
TestRunnerAction testAction = (TestRunnerAction) getGeneratingAction(testStatusList.get(0));
assertThat(testAction.getExecutionPlatform().label())
.isEqualTo(Label.parseCanonicalUnchecked("//:exec_expected"));
assertThat(testAction.getExecProperties()).containsExactly("key", "good");
}

/**
* With the default test toolchain, a failure to find a suitable execution platform will result in
* a toolchain resolution error.
Expand Down