Skip to content

Commit 14e3d1b

Browse files
hvadehracopybara-github
authored andcommitted
Fix javacopts semantics broken by storing them as depsets
The final javacopts passed to `JavaBuilder`/`javac` is computed (in `java_common.compile`) by concatenating the following sets of options (in order): 1. The `javacopts` attribute on `java_toolchain` 2. The options provided with `--javacopt` on the commandline 3. The matching `compatible_javacopts` attribute on `java_toolchain` 4. The matching options from `java_toolchain.package_config` 5. The `javacopts` attribute on the `java_*` rule We already de-tokenize each of these sets into a single space-separated string before adding them to a `depset` (not doing so would mean `['-source', '8', '-target', '8', '-Xmx1G']` would result in getting back `['-source', '8', '-target', '-Xmx1G']` from the depset). In most cases, this de-tokenization is enough to ensure we get back the same set of options as we would if we stored them as a list. However, in the rare case that any of the above sets is *exactly* identical to another, it gets de-duped upon flattening. Since the right-most option should win, this change makes it so, that after de-duping, the right-most occurrence is preserved. A depset always performs left-to-right traversal, so all we need to do is add the above 5 sets of options in reverse order, and then reverse once more after flattening. PiperOrigin-RevId: 573895869 Change-Id: Id4758597e6d93fe5c71856a742915cafc1287a68
1 parent 4eada72 commit 14e3d1b

File tree

3 files changed

+24
-7
lines changed

3 files changed

+24
-7
lines changed

src/main/java/com/google/devtools/build/lib/rules/java/JavaHelper.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,24 @@ private static String filterLauncherForTarget(RuleContext ruleContext) {
9393
}
9494

9595
/**
96-
* Javac options require special processing - People use them and expect the options to be
97-
* tokenized.
96+
* Flattens a set of javacopts and tokenizes the contents.
97+
*
98+
* <p>Since javac allows passing the same option multiple times, and the right-most one wins,
99+
* multiple instances of the same option+value get de-duped by the NestedSet. So when combining
100+
* multiple NestedSets, we store them in reverse order, and reverse again after flattening. This
101+
* preserves the right-most occurrence in its correct position, thus achieving the correct
102+
* semantics.
103+
*
104+
* @param inOpts the set of opts to tokenize
98105
*/
99106
public static ImmutableList<String> tokenizeJavaOptions(NestedSet<String> inOpts) {
100-
return tokenizeJavaOptions(inOpts.toList());
107+
return tokenizeJavaOptions(inOpts.toList().reverse());
101108
}
102109

110+
/**
111+
* Javac options require special processing - People use them and expect the options to be
112+
* tokenized.
113+
*/
103114
public static ImmutableList<String> tokenizeJavaOptions(Iterable<String> inOpts) {
104115
// Ideally, this would be in the options parser. Unfortunately,
105116
// the options parser can't handle a converter that expands

src/main/starlark/builtins_bzl/common/java/java_common_internal_for_builtins.bzl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,11 @@ def compile(
150150
# detokenize target's javacopts, it will be tokenized before compilation
151151
all_javac_opts.append(helper.detokenize_javacopts(helper.tokenize_javacopts(ctx, javac_opts)))
152152

153-
all_javac_opts = depset(order = "preorder", transitive = all_javac_opts)
153+
# we reverse the list of javacopts depsets, so that we keep the right-most set
154+
# in case it's deduped. When this depset is flattened, we will reverse again,
155+
# and then tokenize before passing to javac. This way, right-most javacopts will
156+
# be retained and "win out".
157+
all_javac_opts = depset(order = "preorder", transitive = reversed(all_javac_opts))
154158

155159
# Optimization: skip this if there are no annotation processors, to avoid unnecessarily
156160
# disabling the direct classpath optimization if `enable_annotation_processor = False`

src/main/starlark/builtins_bzl/common/java/java_helper.bzl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,15 +378,17 @@ def _shell_escape(s):
378378
def _tokenize_javacopts(ctx, opts):
379379
"""Tokenizes a list or depset of options to a list.
380380
381+
Iff opts is a depset, we reverse the flattened list to ensure right-most
382+
duplicates are preserved in their correct position.
383+
381384
Args:
382385
ctx: (RuleContext) the rule context
383386
opts: (depset[str]|[str]) the javac options to tokenize
384-
385387
Returns:
386388
[str] list of tokenized options
387389
"""
388390
if hasattr(opts, "to_list"):
389-
opts = opts.to_list()
391+
opts = reversed(opts.to_list())
390392
return [
391393
token
392394
for opt in opts
@@ -397,7 +399,7 @@ def _detokenize_javacopts(opts):
397399
"""Detokenizes a list of options to a depset.
398400
399401
Args:
400-
opts: (depset[str]) the javac options to tokenize
402+
opts: ([str]) the javac options to detokenize
401403
402404
Returns:
403405
(depset[str]) depset of detokenized options

0 commit comments

Comments
 (0)