Java toolchain selection

375 views
Skip to first unread message

Lukács T. Berki

unread,
Oct 28, 2020, 4:49:51 AM10/28/20
to John Cater, Ivo List, bazel-dev
Hey folks,

(Breaking out a discussion spanning multiple documents that's already getting too long)

My thoughts are that changing the WORKSPACE file to change the used JDK version is, while workable, isn't the best decision:
  • It makes it impossible to use multiple Java versions (or e.g. multiple Android API levels without additional work) anywhere in the same build on the same OS/CPU combination
  • Adding a multi-line stanza with long checksums isn't the most pleasant user experience. Sure, people are smart, they can read documentation, but I think it's indisputable that adding the command line flag "--java_version=11" or even something based on Starlark flags is a much better user experience than adding a long stanza in the WORKSPACE file.
Additional food for thought: it seems that a lot of the issues with deciding whether something should be a configuration flag or a platform constraint come from the fact that the constraints defined on the platform and the constraints used for resolving toolchains for a rule built for that platform are the same. If these two were separated, the "combinatorial explosion of platforms" and the "platform owner has to know about every rule set used to build for that platform" issues would go away. WDYT?

I looked at rules_rust, rules_scala and rules_go and it looks like none of these would be much influenced by giving rules the ability to  impose additional constraints on toolchain selection: 
Arguably, it's more API surface for Bazel, but it's not a lot and seems to make a bunch of new uses possible that are difficult now.

--
Lukács T. Berki | Software Engineer | lbe...@google.com | 

Google Germany GmbH | Erika-Mann-Str. 33  | 80636 München | Germany | Geschäftsführer: Paul Manicle, Halimah DeLaine Prado | Registergericht und -nummer: Hamburg, HRB 86891

Lukács T. Berki

unread,
Oct 28, 2020, 5:22:11 AM10/28/20
to Herrmann, Andreas, John Cater, Ivo List, bazel-dev
I think this proposal would be mostly a no-op for this use case. The cases that are affected are those where the language rule has different ideas as to what constraints the toolchains it wants should have than the current platform.

On Wed, Oct 28, 2020 at 10:09 AM Herrmann, Andreas <andreas....@tweag.io> wrote:
Hi Lukács,

Maybe this is a relevant example. rules_nixpkgs uses a platform constraint so that Bazel can select nixpkgs provided toolchains (e.g. posix toolchain, cc toolchain, go toolchain, python toolchain, ghc toolchain) if the current platform supports Nix.
Users define --host_platform=@io_tweag_rules_nixpkgs//nixpkgs/platforms:host to enable the Nix provided toolchain.
The Nix provided toolchain has to be defined before the non-Nix counterpart to be selected.

I'd be interested to hear if and how your proposal would affect that use-case.

Best, Andreas

--
You received this message because you are subscribed to the Google Groups "bazel-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bazel-dev+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/bazel-dev/CAOu%2B0LVAvctpLgyjO6O1RZwkpKNF%3DSMf0Gdfj50KMuFcES5RNg%40mail.gmail.com.

Herrmann, Andreas

unread,
Oct 28, 2020, 11:32:37 AM10/28/20
to Lukács T. Berki, John Cater, Ivo List, bazel-dev
Hi Lukács,

Maybe this is a relevant example. rules_nixpkgs uses a platform constraint so that Bazel can select nixpkgs provided toolchains (e.g. posix toolchain, cc toolchain, go toolchain, python toolchain, ghc toolchain) if the current platform supports Nix.
Users define --host_platform=@io_tweag_rules_nixpkgs//nixpkgs/platforms:host to enable the Nix provided toolchain.
The Nix provided toolchain has to be defined before the non-Nix counterpart to be selected.

I'd be interested to hear if and how your proposal would affect that use-case.

Best, Andreas

On Wed, Oct 28, 2020 at 9:49 AM 'Lukács T. Berki' via bazel-dev <baze...@googlegroups.com> wrote:
--

John Cater

unread,
Oct 28, 2020, 1:33:01 PM10/28/20
to Lukács T. Berki, Herrmann, Andreas, Ivo List, bazel-dev
Lukacs,

I agree that I'd like to get some clarity in this discussion. At this point I'm not entirely sure what specific change to toolchain resolution you are discussing: there have been several in various documents, as well as the (now dropped?) proposal by Ivo at https://github.com/bazelbuild/proposals/blob/master/designs/2020-09-29-new-default-target-constraint.md

I'd like to ask you some questions that I hope will clarify issues, and I'd like to consolidate information in one place:
1.  Are the only objections to using WORKSPACE to configure the Java toolchains that users will need to modify their WORKSPACE files?
  a. In what way is Java different from Go, Rust, Scala, or any other set of rules that currently require this?
  b. Have you seen the sample repository rules I created in https://github.com/katre/java_repository_rules to mock up example Java repository configuration?
2.  What specific changes are you proposing to toolchain resolution and the platforms systems?
  a. Why are these needed for Java and not for other languages?
  b. How will we migrate other languages that currently use platforms and toolchain resolution as-is? What new features will we gain?
3. I understand that the legacy behavior of Java toolchains (with the --javabase and --java_toolchain parameters) are difficult to reproduce with toolchain resolution. Is that behavior something we specifically wanted, or was it simply an accident of the previous implementation and the addition of remote execution?

I know some of these have already been answered, but I'd like to consolidate this into one place, because I have lost track of the documents, proposals, chat streams, etc. If you would rather consolidate into a single document, I would be fine with that as well.

John C

Lukács T. Berki

unread,
Oct 29, 2020, 4:41:35 AM10/29/20
to John Cater, Herrmann, Andreas, Ivo List, bazel-dev
On Wed, Oct 28, 2020 at 6:33 PM John Cater <jca...@google.com> wrote:
Lukacs,

I agree that I'd like to get some clarity in this discussion. At this point I'm not entirely sure what specific change to toolchain resolution you are discussing: there have been several in various documents, as well as the (now dropped?) proposal by Ivo at https://github.com/bazelbuild/proposals/blob/master/designs/2020-09-29-new-default-target-constraint.md

I'd like to ask you some questions that I hope will clarify issues, and I'd like to consolidate information in one place:
1.  Are the only objections to using WORKSPACE to configure the Java toolchains that users will need to modify their WORKSPACE files? 
  a. In what way is Java different from Go, Rust, Scala, or any other set of rules that currently require this?
  b. Have you seen the sample repository rules I created in https://github.com/katre/java_repository_rules to mock up example Java repository configuration?
I think there are three differences:
  1. The other rule sets don't have the concept "language version" and assume that there is just one canonical version of the toolchain they want to use
  2. Java sometimes requires multiple "language versions" to be used in the same build (e.g. if you build a server binary and an Android binary in the same build)
  3. Binaries for the other languages don't require a runtime that may or may not be preinstalled or piggyback on the Java one (like Scala) 
Consider that in any build, there are the following "Java versions" in play (there may be one or two more I forgot...):
  1. The version of JVM javac runs on
  2. The version of the JDK javac comes from
  3. The source language version you pass to javac
  4. The target language version you pass to javac
  5. The version of JVM the target code runs on
So it's a much more complicated situation.

There seem to be two differing models: one where the WORKSPACE file tells Bazel which toolchains to use and another where the WORKSPACE file tells Bazel which toolchains are available and the selection happens according to the needs of each individual configured target. Initially, I thought that we agreed that we want Bazel to follow the second model, but by now, it looks like that you favor the first one?

The main reason why I am personally in favor of the second model is that it lends itself better to composable builds; the more you decide globally, the more problems you will have when you have to make different decisions for different parts of your transitive sources.
 
2.  What specific changes are you proposing to toolchain resolution and the platforms systems?
My understanding is that this document describes it; reading between the lines, I think the main idea is to have some way to influence the toolchain resolution that one can change by configuration transitions and that one can influence for each configured target. Ivo, is this correct?

 
  a. Why are these needed for Java and not for other languages? 
  b. How will we migrate other languages that currently use platforms and toolchain resolution as-is? What new features will we gain? 
3. I understand that the legacy behavior of Java toolchains (with the --javabase and --java_toolchain parameters) are difficult to reproduce with toolchain resolution. Is that behavior something we specifically wanted, or was it simply an accident of the previous implementation and the addition of remote execution?
I don't think there is any obscure behavior we need to reproduce (Ivo, is there?) It's just that we need to cover a number of use cases with an user experience as simple as possible.

John Cater

unread,
Oct 29, 2020, 8:43:38 AM10/29/20
to Lukács T. Berki, Herrmann, Andreas, Ivo List, bazel-dev
On Thu, Oct 29, 2020 at 4:41 AM Lukács T. Berki <lbe...@google.com> wrote:
On Wed, Oct 28, 2020 at 6:33 PM John Cater <jca...@google.com> wrote:
1.  Are the only objections to using WORKSPACE to configure the Java toolchains that users will need to modify their WORKSPACE files? 
  a. In what way is Java different from Go, Rust, Scala, or any other set of rules that currently require this?
  b. Have you seen the sample repository rules I created in https://github.com/katre/java_repository_rules to mock up example Java repository configuration?
I think there are three differences:
  1. The other rule sets don't have the concept "language version" and assume that there is just one canonical version of the toolchain they want to use

Have you seen rules_scala's support for different scala versions? See their docs at https://github.com/bazelbuild/rules_scala#selecting-scala-version. I know that they iterated several times on this design, it might be good to ask them for feedback and suggestions.
 
  1. Java sometimes requires multiple "language versions" to be used in the same build (e.g. if you build a server binary and an Android binary in the same build)
How often does this happen for Bazel users? Is this a feature in high demand? Is it worth complicating the user experience in order to support this?
 
  1. Binaries for the other languages don't require a runtime that may or may not be preinstalled or piggyback on the Java one (like Scala) 
Consider that in any build, there are the following "Java versions" in play (there may be one or two more I forgot...):
  1. The version of JVM javac runs on
  2. The version of the JDK javac comes from
  3. The source language version you pass to javac
  4. The target language version you pass to javac
  5. The version of JVM the target code runs on
So it's a much more complicated situation.

Again: how often does this actually come up? Are there Bazel users who want to run several different versions of JVM in the same build? My understanding is that a JDK at version m can support all language levels <= m, is that not true?
 
There seem to be two differing models: one where the WORKSPACE file tells Bazel which toolchains to use and another where the WORKSPACE file tells Bazel which toolchains are available and the selection happens according to the needs of each individual configured target. Initially, I thought that we agreed that we want Bazel to follow the second model, but by now, it looks like that you favor the first one?

I favor the capability for the second. My main concern is that I don't want to complicate an already confusing system in order to support desired features that no one uses.
 
The main reason why I am personally in favor of the second model is that it lends itself better to composable builds; the more you decide globally, the more problems you will have when you have to make different decisions for different parts of your transitive sources.

I definitely agree with this principle.
  
2.  What specific changes are you proposing to toolchain resolution and the platforms systems?
My understanding is that this document describes it; reading between the lines, I think the main idea is to have some way to influence the toolchain resolution that one can change by configuration transitions and that one can influence for each configured target. Ivo, is this correct?

This is the proposal to add a new "config_value" rule, and to re-implement a platform as a collection of configuration values? I remain unconvinced that the value the proposal provides is worth the difficulty of migrating the world to a new form of platforms.
 
  a. Why are these needed for Java and not for other languages? 
  b. How will we migrate other languages that currently use platforms and toolchain resolution as-is? What new features will we gain? 

These are still unanswered questions about the new platforms proposal.
 
3. I understand that the legacy behavior of Java toolchains (with the --javabase and --java_toolchain parameters) are difficult to reproduce with toolchain resolution. Is that behavior something we specifically wanted, or was it simply an accident of the previous implementation and the addition of remote execution?
I don't think there is any obscure behavior we need to reproduce (Ivo, is there?) It's just that we need to cover a number of use cases with an user experience as simple as possible.

I agree with the goal of keeping the user experience as simple as possible. I just want to make sure that we are making the more commonly encountered user experiences the priority.

Lukács T. Berki

unread,
Oct 29, 2020, 10:03:26 AM10/29/20
to John Cater, Herrmann, Andreas, Ivo List, bazel-dev
On Thu, Oct 29, 2020 at 1:43 PM John Cater <jca...@google.com> wrote:
On Thu, Oct 29, 2020 at 4:41 AM Lukács T. Berki <lbe...@google.com> wrote:
On Wed, Oct 28, 2020 at 6:33 PM John Cater <jca...@google.com> wrote:
1.  Are the only objections to using WORKSPACE to configure the Java toolchains that users will need to modify their WORKSPACE files? 
  a. In what way is Java different from Go, Rust, Scala, or any other set of rules that currently require this?
  b. Have you seen the sample repository rules I created in https://github.com/katre/java_repository_rules to mock up example Java repository configuration?
I think there are three differences:
  1. The other rule sets don't have the concept "language version" and assume that there is just one canonical version of the toolchain they want to use

Have you seen rules_scala's support for different scala versions? See their docs at https://github.com/bazelbuild/rules_scala#selecting-scala-version. I know that they iterated several times on this design, it might be good to ask them for feedback and suggestions.
 
  1. Java sometimes requires multiple "language versions" to be used in the same build (e.g. if you build a server binary and an Android binary in the same build)
How often does this happen for Bazel users? Is this a feature in high demand? Is it worth complicating the user experience in order to support this? 
 
  1. Binaries for the other languages don't require a runtime that may or may not be preinstalled or piggyback on the Java one (like Scala) 
Consider that in any build, there are the following "Java versions" in play (there may be one or two more I forgot...):
  1. The version of JVM javac runs on
  2. The version of the JDK javac comes from
  3. The source language version you pass to javac
  4. The target language version you pass to javac
  5. The version of JVM the target code runs on
So it's a much more complicated situation.

Again: how often does this actually come up? Are there Bazel users who want to run several different versions of JVM in the same build? My understanding is that a JDK at version m can support all language levels <= m, is that not true?
The one huge data point I have is that google3 itself is far from using the latest JDK and JVM version. So it does look like there are reasons. It's true that a newer JDK supports older language levels, but you can have all sorts of reasons to choose older JVMs than the latest available to run your binary and one can make the same case for javac (although it's a weaker case there and I think it mostly rests on compiler bugs and Error Prone compatibility)
 
 
There seem to be two differing models: one where the WORKSPACE file tells Bazel which toolchains to use and another where the WORKSPACE file tells Bazel which toolchains are available and the selection happens according to the needs of each individual configured target. Initially, I thought that we agreed that we want Bazel to follow the second model, but by now, it looks like that you favor the first one?

I favor the capability for the second. My main concern is that I don't want to complicate an already confusing system in order to support desired features that no one uses.
 
The main reason why I am personally in favor of the second model is that it lends itself better to composable builds; the more you decide globally, the more problems you will have when you have to make different decisions for different parts of your transitive sources.

I definitely agree with this principle.
  
2.  What specific changes are you proposing to toolchain resolution and the platforms systems?
My understanding is that this document describes it; reading between the lines, I think the main idea is to have some way to influence the toolchain resolution that one can change by configuration transitions and that one can influence for each configured target. Ivo, is this correct?

This is the proposal to add a new "config_value" rule, and to re-implement a platform as a collection of configuration values? I remain unconvinced that the value the proposal provides is worth the difficulty of migrating the world to a new form of platforms.
That's one of the possible ways to realize this idea, yes. I don't think the world would need to be migrated since it's just an additional knob so the main counterargument is complexity. But if you agree that composability (being able to choose e.g. Java versions independently for different parts of your build) is important, what's the alternative?
 
  a. Why are these needed for Java and not for other languages? 
  b. How will we migrate other languages that currently use platforms and toolchain resolution as-is? What new features will we gain? 

These are still unanswered questions about the new platforms proposal.
A for (a), I thought the above few points about it having multiple axes Java versions was enough. (b) is not a problem to the best of my understanding since it would be a new feature that you can use or not, as you like.

 
3. I understand that the legacy behavior of Java toolchains (with the --javabase and --java_toolchain parameters) are difficult to reproduce with toolchain resolution. Is that behavior something we specifically wanted, or was it simply an accident of the previous implementation and the addition of remote execution?
I don't think there is any obscure behavior we need to reproduce (Ivo, is there?) It's just that we need to cover a number of use cases with an user experience as simple as possible.

I agree with the goal of keeping the user experience as simple as possible. I just want to make sure that we are making the more commonly encountered user experiences the priority.

John Cater

unread,
Oct 29, 2020, 11:28:41 AM10/29/20
to Lukács T. Berki, Herrmann, Andreas, Ivo List, bazel-dev
On Thu, Oct 29, 2020 at 10:03 AM Lukács T. Berki <lbe...@google.com> wrote:
On Thu, Oct 29, 2020 at 1:43 PM John Cater <jca...@google.com> wrote:
On Thu, Oct 29, 2020 at 4:41 AM Lukács T. Berki <lbe...@google.com> wrote:
On Wed, Oct 28, 2020 at 6:33 PM John Cater <jca...@google.com> wrote:
Again: how often does this actually come up? Are there Bazel users who want to run several different versions of JVM in the same build? My understanding is that a JDK at version m can support all language levels <= m, is that not true?
The one huge data point I have is that google3 itself is far from using the latest JDK and JVM version. So it does look like there are reasons. It's true that a newer JDK supports older language levels, but you can have all sorts of reasons to choose older JVMs than the latest available to run your binary and one can make the same case for javac (although it's a weaker case there and I think it mostly rests on compiler bugs and Error Prone compatibility)

So it is a requirement to be able to select the JVM, JDK, and language version independently? Since google's internal repo is already using toolchain resolution for Java, I'm not sure what features are required that don't currently exist.

The major difference between a monorepo is that all versions of Java (JVM, JDK, language level, etc) are checked in and known statically. I feel that this is also achievable for non-monorepo projects, by registering all JDKs (locally installed and downloaded), with some helpful repository macros to ease setup.

 
  a. Why are these needed for Java and not for other languages? 
  b. How will we migrate other languages that currently use platforms and toolchain resolution as-is? What new features will we gain? 

These are still unanswered questions about the new platforms proposal.
A for (a), I thought the above few points about it having multiple axes Java versions was enough. (b) is not a problem to the best of my understanding since it would be a new feature that you can use or not, as you like.

Perhaps I have misunderstood the proposal in https://docs.google.com/document/d/1cslJdSSD_6bVhlv5SRVQpomwGwk8D_LB42mbaEbS5Bc/edit (that does not seem to be publically shared, and should be so that external users can read and comment).

Copying the relevant example:

config_value(name = "x86", flag = "cpu", value = "x86")

config_value(name = "linux", flag = "os", value = "os")

config_value(name = "jvm8", flag = "java_release_version", value = "8")

config_value(name = "jvm8", flag = "java_release_version", value = "11")

platform(

   name = "autoconfigured_target_and_exec",

   configuration = [":x86", ":linux"],

)

platform(

   name = "jvm8_platform",

   parent = ":autoconfigured_target_and_exec",

   configuration = [":jvm8"],

)

toolchain(

  name = "local_jvm8",

  toolchain_type = ":java_runtime_toolchain",

  target_config_values = [":jvm8"],

  toolchain = "@local_java//:jdk"

)

toolchain(

  name = "jvm11",

  toolchain_type = ":java_runtime_toolchain",

  target_config_values = [":jvm11"],

  toolchain = "@remotejdk11_linux//:jdk",

)

toolchain(

  name = "jdk8",

  toolchain_type = ":java_toolchain",

  target_config_value = [":jvm8"],

  exec_config_value = [":x86", ":linux"], 

  toolchain = "@toolchain_java_linux//:toolchain_8"

)


It is very unclear to me how a project would migrate from using the current constraint_setting/constraint_value version of platforms to this version without either needing every rule they use to migrate.

Lukács T. Berki

unread,
Oct 29, 2020, 12:27:01 PM10/29/20
to John Cater, Herrmann, Andreas, Ivo List, bazel-dev
On Thu, Oct 29, 2020 at 4:28 PM John Cater <jca...@google.com> wrote:
On Thu, Oct 29, 2020 at 10:03 AM Lukács T. Berki <lbe...@google.com> wrote:
On Thu, Oct 29, 2020 at 1:43 PM John Cater <jca...@google.com> wrote:
On Thu, Oct 29, 2020 at 4:41 AM Lukács T. Berki <lbe...@google.com> wrote:
On Wed, Oct 28, 2020 at 6:33 PM John Cater <jca...@google.com> wrote:
Again: how often does this actually come up? Are there Bazel users who want to run several different versions of JVM in the same build? My understanding is that a JDK at version m can support all language levels <= m, is that not true?
The one huge data point I have is that google3 itself is far from using the latest JDK and JVM version. So it does look like there are reasons. It's true that a newer JDK supports older language levels, but you can have all sorts of reasons to choose older JVMs than the latest available to run your binary and one can make the same case for javac (although it's a weaker case there and I think it mostly rests on compiler bugs and Error Prone compatibility)

So it is a requirement to be able to select the JVM, JDK, and language version independently? Since google's internal repo is already using toolchain resolution for Java, I'm not sure what features are required that don't currently exist.

The major difference between a monorepo is that all versions of Java (JVM, JDK, language level, etc) are checked in and known statically. I feel that this is also achievable for non-monorepo projects, by registering all JDKs (locally installed and downloaded), with some helpful repository macros to ease setup.
Are you envisioning those "helpful repository macros" to put together BUILD files that select e.g. java_runtime_version based on the knowledge of every available Java toolchain collated by some Starlark magic in the WORKSPACE file?
Why do you think migration is necessary? google3 doesn't need to change and Bazel currently doesn't have any Java toolchains that would need to be migrated.

Lukács T. Berki

unread,
Oct 30, 2020, 8:30:34 AM10/30/20
to John Cater, Herrmann, Andreas, Ivo List, bazel-dev
Let me expound on this a bit more; do I understand correctly that what you are proposing is that the WORKSPACE file would look like this:

register_java_toolchain("@jre15//:jre")
register_java_toolchain("@jre11//:jre")
collate_java_toolchains()

And collate_java_toolchains() call would create a repository like this:

config_setting(name="java_runtime_v15", values={"java_runtime": "15"})
config_setting(name="java_runtime_v11", values={"java_runtime": "11"})

toolchain(
  name = "all_linux_jres",
  exec_compatible_with = ["@platforms//:linux"]),
  actual = select({
    "java_runtime_v15": "@jre15//:jre",
    "java_runtime_v11": "@jre11//:jre",
  })
)

This certainly works, although I would argue that it's a little clumsy because it involves piecing together BUILD files textually and because it you want to take more than one axis into account, you'll either end up with trees of alias() rules or a large number of config_setting rules. But most importantly, if we go this route, why not also include the OS and CPU into the mix and do the whole toolchain selection in Starlark? (we could probably even add the toolchain type there, but that would require configuration transitions and IMHO it would be definitely a bridge too far)

So I think there are two viable alternatives: either do the toolchain selection fully in Java code or fully in Starlark, but the approach where some axes are in Java and some are in Starlark strikes me as combining the disadvantages of both approaches while summing up their complexity (one could argue that it saves a bit of complexity in Java code, but that's deceptive; whether the code is in Starlark or not, it's still our code to maintain)

Greg Estren

unread,
Oct 30, 2020, 1:55:39 PM10/30/20
to Lukács T. Berki, John Cater, Herrmann, Andreas, Ivo List, bazel-dev
On Fri, Oct 30, 2020 at 8:30 AM 'Lukács T. Berki' via bazel-dev <baze...@googlegroups.com> wrote:
Let me expound on this a bit more; do I understand correctly that what you are proposing is that the WORKSPACE file would look like this:

register_java_toolchain("@jre15//:jre")
register_java_toolchain("@jre11//:jre")
collate_java_toolchains()

And collate_java_toolchains() call would create a repository like this:

config_setting(name="java_runtime_v15", values={"java_runtime": "15"})
config_setting(name="java_runtime_v11", values={"java_runtime": "11"})

toolchain(
  name = "all_linux_jres",
  exec_compatible_with = ["@platforms//:linux"]),
  actual = select({
    "java_runtime_v15": "@jre15//:jre",
    "java_runtime_v11": "@jre11//:jre",
  })
)


This model makes sense to me. In my view the combination of flags and platform-based resolution is a powerful combo that should be able to achieve nearly all desirable use cases without requiring yet-more-API. We should be cautious to the extreme about adding new API hooks. Don't add them unless we're absolutely sure we can't extract what we need from current mechanisms.

Both configuration and platform resolution offer a lot of generality. We can do a lot of interesting things with them. Let's be super-cautious about adding new concepts over these basic building blocks.

I'd also like to acknowledge that there's a distinction between intrinsic complexity and API complexity. We have enough experience to know that just modeling what kind of toolchain flexibility people want is complex, never mind what we do to implement it. That's one of the arguments for keeping simple and generic building blocks and having toolchain implementers model and own their intrinsic complexity on top of them (i.e. programming).

 
But most importantly, if we go this route, why not also include the OS and CPU into the mix and do the whole toolchain selection in Starlark? (we could probably even add the toolchain type there, but that would require configuration transitions and IMHO it would be definitely a bridge too far)


Because that brings us back to the per-language ad hoc toolchain resolution model we've been trying to move away from.

Historically: every toolchain makes up their own model for toolchain resolution. Everyone's re-inventing the same wheel. None of the implementations are compatible. Cross-language builds become a mess, and full of flags. One of the arguments of --platforms is that it consolidates what would otherwise be a larger mess of flags. I think this argument still stands with the above example.

Platform world: Generic, cross-language standardization is a thing. Wide-ranging concepts like CPU, OS, device type follow a much purer and inherently more consistent model, with clean interfaces and all the logic of connecting implementations handled magically by the system. This is all wonderful stuff. Aside from its direct benefits, it also opens the way for creating new features around the platforms API that *all rule sets* automatically benefit from for free. The most prominent recent example is incompatible target skipping. Mixed host builds (build with both Apple and Linux machines) is another great one. Having a standardized language is really a great thing.

While it's still possible to define similar and mutually incompatible constraints, this model is less amenable to forking than ad hoc definitions. Mostly because the implementation cannot be forked, only the interface. In other words, with an ad hoc model you can both define "--cpu_for_me" and "--cpu_for_you" AND use different implementations to link them to toolchains (like different select() structures). The latter can't be forked with platforms because the implementation is built-in.

Buuuuut, language-specific constraints remain a thing. Which is exactly what we're debating in this thread. That's fine. Within a language it's fine to have ad hoc logic. Within a language, *all* logic is ad hoc - there's nothing to generalize to. In that case, it's fine for a language to model whatever it needs with whatever API tools are available to it. Flags are great for that.

I see no problem with mixing these concepts together: leverage the generic platforms API where it can effectively apply common standards, integrate ad hoc logic for stuff unique to your language. It's a mixed model that in my view brings out the best of both models, not the worst.


 
So I think there are two viable alternatives: either do the toolchain selection fully in Java code or fully in Starlark, but the approach where some axes are in Java and some are in Starlark strikes me as combining the disadvantages of both approaches while summing up their complexity (one could argue that it saves a bit of complexity in Java code, but that's deceptive; whether the code is in Starlark or not, it's still our code to maintain)


I tried to argue the counterpoint above. In summary: the platforms API yields real benefits over an ad hoc API. We can't push everything 100% to the platforms API. That's okay. Even 20% or 50% of 70% realizes value. Moving everything to Starlark adds the complexity of forcing individual toolchain maintainers to fully define their own ad hoc models which may or may not mix well with other languages and toolchains. That's an obvious step backward in my view.  $ bazel build //my:project --platforms=foo should work as universally as possible. It's fine and necessary to sometimes amend it with --java_runtime_version=11. That's a fine use of flags because the end user API closely matches the user's intentions without making them think about implementation details they don't care about: "I want to build with JDK 11 for platform foo".  We absolutely don't want to revert to $ bazel build //my:project --jdk_cpu=x86 --jdk_os=linux --cpp_cpu=intel_x86 --cpp_os=ubuntu ... .
 
--
You received this message because you are subscribed to the Google Groups "bazel-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bazel-dev+...@googlegroups.com.

Greg Estren

unread,
Oct 30, 2020, 1:58:47 PM10/30/20
to Lukács T. Berki, John Cater, Herrmann, Andreas, Ivo List, bazel-dev
A few more brief points:
  • In some of the proposals I see a desire for --java_runtime_version to both choose the desired JDK runtime and toggle remote vs. local JDKs. That seems like a weird mixture of intentions to me.
  • I'm not convinced that combinatorial proliferation of platform definitions is a big deal. Platform definitions are supposed to be specific. It's easy to create those combos with a macro, at which point the user experience becomes a simple --platforms=<foo>-<bar>-<baz>, and just fill in whichever value you want in each variable. 

Austin Schuh

unread,
Oct 30, 2020, 3:44:17 PM10/30/20
to Greg Estren, Lukács T. Berki, John Cater, Herrmann, Andreas, Ivo List, bazel-dev
Maybe I'm missing something, but can't this all be done today? Have a
"jdk_version" feature_flag which the user can provide, and use
select/starlark in the toolchain definition to adjust what is
registered? Transitions let you have multiple configurations in a
build, and you have all the flexibility you could want. Or, there was
a proposal from a year or two ago which showed how to use platforms to
set a minimum version for something like the JDK, and use that to pick
which toolchain to use? With Transitions to potentially allow
multiple per build?

Austin
> To view this discussion on the web visit https://groups.google.com/d/msgid/bazel-dev/CABdTCQVmbGJ%3DqsDe-p3nXboXGLFEnd0f9TrOdBF865pT2LFXLg%40mail.gmail.com.

Greg Estren

unread,
Oct 30, 2020, 3:59:54 PM10/30/20
to Austin Schuh, Lukács T. Berki, John Cater, Herrmann, Andreas, Ivo List, bazel-dev
On Fri, Oct 30, 2020 at 3:44 PM Austin Schuh <austin...@gmail.com> wrote:
Maybe I'm missing something, but can't this all be done today?  Have a
"jdk_version" feature_flag which the user can provide, and use
select/starlark in the toolchain definition to adjust what is
registered?  Transitions let you have multiple configurations in a
build, and you have all the flexibility you could want.  Or, there was
a proposal from a year or two ago which showed how to use platforms to
set a minimum version for something like the JDK, and use that to pick
which toolchain to use?  With Transitions to potentially allow
multiple per build?


Yes, this all makes sense. Which inspires me to ask againn... what core problem are we trying to solve here?  The starting message is about a pure-WORKSPACE approach. But I don't think anyone's arguing for that now?

Ivo List

unread,
Oct 31, 2020, 3:27:46 PM10/31/20
to Greg Estren, Austin Schuh, Lukács T. Berki, John Cater, Herrmann, Andreas, bazel-dev
Dear all,

sorry for the confusion with multiple docs. The cleaned up problem statement and proposal is here: https://docs.google.com/document/d/13yXhOQeMZQmlW3obyZ7fJP3sm3eVLbK3Nuk2LZjcZiU/edit#

This should answer what the core problem is and why it cannot be solved (at least not nicely) with current platform+toolchain machinery.

It is not so much concerning generalization that platfomisation addresses, but the specifics that the toolchains need. And it may give a direction, how other language specific issues should be addressed, beyond Java.

Going through history of this thread, some things I'd like to address separately:
I understand that the legacy behavior of Java toolchains (with the --javabase and --java_toolchain parameters) are difficult to reproduce with toolchain resolution. Is that behavior something we specifically wanted, or was it simply an accident of the previous implementation and the addition of remote execution?

The behaviour that we need for Java is inherently complex and it is not due to reproduction of legacy behaviour. I would like to provide the simplest thing that works. Even building a single Java app needs two JDKs and we can't simplify here.
Building Android needs a specific version of Java and the right concept here is "build composability". We can't handwave out of this - saying use these lines in WORKSPACE when building Android and then use other lines when building Java. If we don't solve it, we are immediately down the path of poor user experience.

In some of the proposals I see a desire for --java_runtime_version to both choose the desired JDK runtime and toggle remote vs. local JDKs. That seems like a weird mixture of intentions to me.

I think this is not the most important problem and we should discuss which flags and how to name them later. I did consider using a special flag to toggle from local into remote. I didn't include it in the doc, because it seems it doesn't really describe an important quality of the toolchain.

The explanation why remote is selected is wrong. Correct one would be: remote is selected because it matches the flag and because it is the only one available.
If we follow the idea that "WORKSPACE file tells Bazel which toolchains are available and the selection happens according to the needs", then autodetection of local Java version should be able to add the toolchain pointing to local version that overrides the remote. We could even search and add more than one JDK.

Regards,
Ivo
--

Ivo List | Software Engineer | il...@google.com | 

Lukács T. Berki

unread,
Nov 2, 2020, 2:38:17 AM11/2/20
to Greg Estren, John Cater, Herrmann, Andreas, Ivo List, bazel-dev
Hey Greg,

I think I can put a finger on the disagreement we are having here: we are both arguing that we should be removing complexity and not adding it, but you draw the line around the core of Bazel and I am drawing the line around Bazel support for core languages. However, regardless of where you draw the line, I can't get behind the argumentation that partially using platform-based toolchain resolution is the best approach:
  1. If you draw the line at the core of Bazel, not having any built-in toolchain resolution in there makes it simpler
  2. If you draw the line at Bazel + core language support, having both built-in toolchain resolution AND language-specific toolchain resolution implemented in Starlark files is more mental load than having just one of them
I think re-implementing toolchain resolution in Starlark differently for each language is a concern, but that doesn't necessarily need to happen (that's what Skylib is for) and that's what you are proposing for Java, aren't you? I think this line of argumentation would work if we thought that specifically toolchain resolution for Java is the only place where more kinds of constraints [1] are needed than OS and CPU. Off the top of my head, here are a number of places where other constraints can come useful off the top of my head:
  1. Declaring that a target requires Java language level X to build using constraints on the target
  2. It's not only Java that has language levels (C++ comes to mind where this is salient, I don't know too much about the rest)
1: If I understand correctly, you are arguing that things like the Java language level should be configuration flags and not constraints, but you are writing "language-specific constraints", so maybe not?


Ivo List

unread,
Nov 30, 2020, 6:31:00 AM11/30/20
to bazel-dev, Greg Estren, John Cater, Herrmann, Andreas, Lukács T. Berki, Liam Miller-Cushon, Tony Aiuto
Hi everyone,

I've collected use cases and put together a proposal for Java platformization that is based on Java specific configuration flags.

Regards,
Ivo List
Reply all
Reply to author
Forward
0 new messages