bazelbuild / platforms

Constraint values for specifying platforms and toolchains
Apache License 2.0
108 stars 73 forks source link

Bazel Platforms: Add `//cpu:any` constraint value for arch-indep builds #75

Closed kmoffett closed 1 year ago

kmoffett commented 1 year ago

Some builds are expected to only produce architecture-independent data files, such as configuration files, database seed data, composited images, or even some kinds of interpreted scripts (Shell, Python, Perl, etc).

When such a build is being performed, this constraint value may be used to ensure that architecture-dependent builds cannot be performed (except by way of another transition).

As a final example, consider building a package of NIC firmware images for many different NIC models. The package overall is architecture-independent, and should be built with //cpu:any, then each individual image rule has a transition to the suitable architecture for that specific NIC model.

aiuto commented 1 year ago

The idea seems sound, but I would rather something like 'unspecified' or 'none' than 'any',

To clarify the intent. We might have a platform that adds the cpu:any constraint specifically so that no CPU based toolchains match and that selects on CPU fail unless you've gone to a specific transition. Right?

gregestren commented 1 year ago

Is one of the goals here to guarantee caching across top-level flag settings?

What's the pseudocode of a bazel invocation / consuming rule? I think I saw in a related chat you want to apply this as a self-transition onto some rules you own?

kmoffett commented 1 year ago

Yes, essentially I have some rules where the output is intended to be architecture-independent, and I want to prohibit them from accidentally depending on a platform-specific executable.

As one example, when building a Debian package with arch "all", generally no architecture-specific executables should be included (though firmware images are an exception). By transitioning to this proposed //cpu:any value, you can prevent any arch-specific toolchains from matching.

In my specific case, I have some internal rules that expect to produce arch-independent configuration outputs, and I want to prevent my users from accidentally depending on CPU-specific executables as data inputs; they wouldn't be able to execute them directly in the configs anyways, and they would break cross-platform compatibility of the configs if they tried anyways.

kmoffett commented 1 year ago

(E.G. this could be an outgoing transition from a .deb packaging rule for arch=all packages, or as an incoming transition on a rule that produces architecture-independent data, which should also improve the cache hit rate in multi-arch builds)

kmoffett commented 1 year ago

As for the naming, there's some relevant prior art:

I don't think //cpu:none makes sense, because this is still usable to produce executables, they just need to be architecture-independent.

I'm not sure about //cpu:unspecified either, because that sounds like it could get misused by accident for specialty compilers where we just don't know what CPU they target.

My intent with //cpu:any (or perhaps //cpu:all) is to reflect that this is building for all CPU architectures, it must not be specific to any single CPU, if that makes sense?

gregestren commented 1 year ago

So the top-level would have to set some platform that consumes this setting (since you can't set or transition on a constraint_value directly). What is that platform? Would there be multiple?

As I'm reading, you're saying the enforcement comes from no arch-specific toolchains matching. Like.. basically no cc targets could appear in the deps (without a transition) since they'd fundamentally require an arch-specific C++ toolchain?

Note I'm going OOO for two weeks. Hopefully @aiuto can pick up the thread.

kmoffett commented 1 year ago

Yes, there would need to be a platform created that specifies this constraint setting. And yes, the enforcement is from the fact that no arch-specific toolchain would match.

There will definitely be multiple platforms, because this CPU constraint doesn't have anything to do with which OS is used. For example, a shell-script is probably going to be cross-CPU compatible, but might still require //os:linux.

In some cases, it actually might be plausible to have both arch-specific and arch-agnostic variants of the same target. Consider Java compilation, which produces arch-agnostic bytecode, but in some cases can use JNI libraries for better performance.

An existing rule might do:

java_library(
    name = "foo",
    srcs = ["Foo.java"],
    deps = select({
        "@platforms//cpu:x86_64": [":foo_jni_impl"],
        "@platforms//cpu:armv7-mf": [":foo_jni_impl"],
        "//conditions:default": [":foo_impl"],
    }),
)

Even if the Java runtime doesn't support //cpu:any (since it requires a concrete execution platform), the Java compiler could easily declare that it can build for //cpu:any

Then, a user could build arch-independent Java binaries by targeting a platform with //cpu:any and be guaranteed not to rely on any JNI libraries.

aiuto commented 1 year ago

Long discussion. Agreed on :all

lexi-nadia commented 1 year ago

Doesn't :all conflict with the default Blaze-generated target that means "all targets in this package"?

BoleynSu commented 9 months ago

Do not think the current impl is a good idea, i.e. a cpu:any target cannot be used where a cpu:x86_64 target is needed. To achive what you want better use the below.

load("@bazel_skylib//lib:selects.bzl", "selects")

selects.config_setting_group(
    name = "any",
    match_any = [
        "@platforms//cpu:x86_64",
        "@platforms//cpu:list_all_possible_values_in_match_any",
    ],
    visibility = ["//visibility:private"],
)

alias(
   name = "noarch",
   actual = select({":any":"//:incompatible", "//conditions:default": "//conditions:default"})
)

We can have this noarch in //os:BUILD

BoleynSu commented 9 months ago

@aiuto for awareness.

BoleynSu commented 8 months ago

@kmoffett PTAL at the above comment