1

I wish to build disk images for an esoteric computing platform (CP/M, if you're interested!) with bazel.

Having looked at the documentation for doing cross-compilation with bazel, it looks like it's set up for the case where the project can compile for any platform, and you don't know what platform you're compiling on until you invoke bazel (the platform is passed in on the command line).

I don't have this situation. My code can only build for this platform, and I cannot cross-compile normal code on it. It needs a single very specific toolchain which is not at all gcc-like. This makes, as far as I can tell, most of bazel's toolchain selection logic useless to me. I should be able to do something with Skylark transition rules, but I'm finding the documentation very heavy going.

What I desperately want to do is this:

cc_binary(
  name = "some_host_binary",
  srcs = [...]
)

cc_binary(
  name = "some_target_binary",
  srcs = [...],
  ...some property here to generate a target binary...
)

genrule(
  name = "output_artifact",
  data = [":some_host_binary", ":some_target_binary"]
)

Confusingly, the page on toolchains describes a compiler attribute on cc_binary which looks like it's precisely what I want, but it seems that this doesn't actually exist and is only there as an example?

If, instead, I want to rely on bazel's automatic toolchain resolution, then I think that I need to be able to specify the platform for some_target_binary to force it to pick the right toolchain; but I feel like this would just cause a build failure as the genrule can't tell that I want the two dependencies to be binaries for different architectures.

I'm beginning to feel that I'm grossly overthinking this. Any suggestions, and preferably examples?

David Given
  • 13,277
  • 9
  • 76
  • 123

1 Answers1

1

Binaries used with genrule.tools will be built for the host platform, and genrule.data will be built for the target platform. I think that is the biggest piece you're looking for. Write it like this:

genrule(
  name = "output_artifact",
  tools = [":some_host_binary"],
  data = [":some_target_binary"],
  outs = ["x"],
  cmd = "$(location :some_host_binary) -in $(location :some_target_binary) -out $(location x)",
)

You can set the target platform on the command line with --platforms flag. Putting this as build --platforms=//my:target in the project-level .bazelrc will set it automatically when building.

If you ever build with a wildcard (like //...), you'll want to avoid including some_host_binary or bazel will try building it for the target platform (in addition to the host platform if that's necessary to build other targets for the target platform). Specifying target_compatible_with on all the host-only targets will automatically skip building them for the target platform, incompatible target skipping has details.

If you want to write tests that execute on the build system (for example), you'll also need to specify target_compatible_with for target-only things to skip them. If you never build without --platforms=//my:target, then letting Bazel think it can build target binaries for any platform (the default without a target_compatible_with) is harmless, because it will never do that unless you ask it to.

The Platforms documentation page has more details.

The more general form of what genrule is doing for the host binaries is called a transition. The documentation on user-defined transitions has some details on ways to do fancier things in custom rules.

cc_binary.compiler is for the old pre-platforms C++-only target selection, which is not where you should start for a new project. It's still supported for backwards compatibility, but actively being moved away from. You'd want to change --cpu anyways.

Brian Silverman
  • 3,085
  • 11
  • 13
  • This seems backwards compared to what I actually want to do, which is to point at a rule and say 'this needs to be built for X'. I actually think that host vs target is confusing the issue here; consider the case where I'm building a binary containing firmware for another device. The firmware will _always_ need to be built with a specific toolchain. Platforms seems like it's designed to solve a different problem, this feels like I'm somehow having to trick bazel into making the right decision regarding toolchain when it'd be so much easier just to specify it statically in the relevant rules. – David Given May 26 '22 at 10:37
  • (Clarification to above: I'm talking about a cross-compilable target binary containing firmware.) I've also discovered that bazel appears to get on very badly with non-gcc-like compilers, so I think that it would be much easier just to reimplement cc_binary and cc_library for my toolchain using genrules. This is all kinds of wrong, but I'll end up with much simpler and easier to reason about build files. If you know of an easier way to do this I'd love to know... – David Given May 26 '22 at 10:43
  • `target_compatible_with` is the way to say that a rule must be built for a certain platform. The target platform is the one that targets specified on the command line are built for, I don't see a way for Bazel to guess that which works in general, so you're going to have to specify it. The C++ toolchain configuration is flexible enough to handle MSVC, which very un-gcc-like. It might be enough to work with your toolchain. – Brian Silverman May 26 '22 at 18:50
  • Can you expand? `target_compatible_with` seems to be a toolchain flag and not a rule flag, so I don't see how it can be applied to specific binaries. I don't think the target should be supplied on the command line in this case: consider where the target is the host, but there are still artifacts which need to be built for a different platform (e.g. because they get embedded into the target binary). – David Given May 28 '22 at 21:28
  • `target_compatible_with` is an attribute on every rule, I see it on every rule in the latest copy of the documentation. It's one of the implicit attributes, like `tags` and `testonly` and `visibility`. If you want to build binaries for different platforms in the same build, you'll need to write custom rules with appropriate transitions. I went looking for examples and found https://stackoverflow.com/a/72421295/2263152 to answer. If you have more specific questions about transitions, I think a separate question will be easier to answer, this one is getting kind of unwieldy. – Brian Silverman May 29 '22 at 06:04