Skip to content

Commit 8bf8bca

Browse files
hvadehrarules_java Copybara
authored andcommitted
Extract utility methods used in the loading phase from impl/java_helper.bzl into a separate bzl file
Methods used only by a single other load-affecting bzl file are inlined there instead. This way `impl/java_helper.bzl` is only loaded from bzl files used during rule implementations and should not be needed for the loading-phase, so making changes to this file don't invalidate the results of `blaze query`[^1]. PiperOrigin-RevId: 838701557 Change-Id: If462c46b1b8f2e5be12322a8af7d1a27e392bdfd
1 parent b685658 commit 8bf8bca

File tree

10 files changed

+311
-239
lines changed

10 files changed

+311
-239
lines changed

java/common/rules/BUILD

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ bzl_library(
2525
visibility = ["//visibility:private"],
2626
)
2727

28+
bzl_library(
29+
name = "java_helper_bzl",
30+
srcs = ["java_helper.bzl"],
31+
visibility = ["//java:__subpackages__"],
32+
)
33+
2834
bzl_library(
2935
name = "core_rules",
3036
srcs = [
@@ -58,6 +64,15 @@ bzl_library(
5864
"//java:__subpackages__",
5965
"@compatibility_proxy//:__pkg__",
6066
],
67+
deps = [
68+
":java_helper_bzl",
69+
"//java/common:semantics_bzl",
70+
"//java/private:boot_class_path_info_bzl",
71+
"//java/private:java_info_bzl",
72+
"//java/private:native_bzl",
73+
"@bazel_skylib//lib:paths",
74+
"@rules_cc//cc/common",
75+
],
6176
)
6277

6378
filegroup(

java/common/rules/impl/BUILD

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ bzl_library(
3030
name = "java_helper_bzl",
3131
srcs = ["java_helper.bzl"],
3232
visibility = ["//java:__subpackages__"],
33+
deps = [
34+
"//java/common:semantics_bzl",
35+
"//java/common/rules:java_helper_bzl",
36+
"@bazel_skylib//lib:paths",
37+
"@rules_cc//cc:find_cc_toolchain_bzl",
38+
"@rules_cc//cc/common",
39+
],
3340
)
3441

3542
filegroup(

java/common/rules/impl/java_helper.bzl

Lines changed: 12 additions & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
"""Common util functions for java_* rules"""
15+
"""Common util functions for java_* rules implementations"""
1616

1717
load("@bazel_skylib//lib:paths.bzl", "paths")
1818
load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain")
1919
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
2020
load("//java/common:java_semantics.bzl", "semantics")
21+
load("//java/common/rules:java_helper.bzl", _loading_phase_helper = "helper")
2122

2223
# copybara: rules_java visibility
2324

@@ -93,7 +94,7 @@ def _primary_class(ctx):
9394
for src in ctx.files.srcs:
9495
if src.basename == main:
9596
return _full_classname(_strip_extension(src))
96-
return _full_classname(_get_relative(ctx.label.package, ctx.label.name))
97+
return _full_classname(helper.get_relative(ctx.label.package, ctx.label.name))
9798

9899
def _strip_extension(file):
99100
return file.dirname + "/" + (
@@ -102,41 +103,9 @@ def _strip_extension(file):
102103

103104
# TODO(b/193629418): once out of builtins, create a canonical implementation and remove duplicates in depot
104105
def _full_classname(path):
105-
java_segments = _java_segments(path)
106+
java_segments = _loading_phase_helper.java_segments(path)
106107
return ".".join(java_segments) if java_segments != None else None
107108

108-
def _java_segments(path):
109-
if path.startswith("/"):
110-
fail("path must not be absolute: '%s'" % path)
111-
segments = path.split("/")
112-
root_idx = -1
113-
for idx, segment in enumerate(segments):
114-
if segment in ["java", "javatests", "src", "testsrc"]:
115-
root_idx = idx
116-
break
117-
if root_idx < 0:
118-
return None
119-
is_src = "src" == segments[root_idx]
120-
check_mvn_idx = root_idx if is_src else -1
121-
if (root_idx == 0 or is_src):
122-
for i in range(root_idx + 1, len(segments) - 1):
123-
segment = segments[i]
124-
if "src" == segment or (is_src and (segment in ["java", "javatests"])):
125-
next = segments[i + 1]
126-
if next in ["com", "org", "net"]:
127-
root_idx = i
128-
elif "src" == segment:
129-
check_mvn_idx = i
130-
break
131-
132-
if check_mvn_idx >= 0 and check_mvn_idx < len(segments) - 2:
133-
next = segments[check_mvn_idx + 1]
134-
if next in ["main", "test"]:
135-
next = segments[check_mvn_idx + 2]
136-
if next in ["java", "resources"]:
137-
root_idx = check_mvn_idx + 2
138-
return segments[(root_idx + 1):]
139-
140109
def _concat(*lists):
141110
result = []
142111
for list in lists:
@@ -250,19 +219,8 @@ def _get_java_executable(ctx, java_runtime_toolchain, launcher):
250219
java_executable = ctx.workspace_name + "/" + java_executable
251220
return paths.normalize(java_executable)
252221

253-
def _has_target_constraints(ctx, constraints):
254-
# Constraints is a label_list.
255-
for constraint in constraints:
256-
constraint_value = constraint[platform_common.ConstraintValueInfo]
257-
if ctx.target_platform_has_constraint(constraint_value):
258-
return True
259-
return False
260-
261-
def _is_target_platform_windows(ctx):
262-
return _has_target_constraints(ctx, ctx.attr._windows_constraints)
263-
264222
def _is_absolute_target_platform_path(ctx, path):
265-
if _is_target_platform_windows(ctx):
223+
if helper.is_target_platform_windows(ctx):
266224
return len(path) > 2 and path[1] == ":"
267225
return path.startswith("/")
268226

@@ -274,139 +232,6 @@ def _get_test_support(ctx):
274232
return ctx.attr._test_support
275233
return None
276234

277-
def _resource_mapper(file):
278-
root_relative_path = paths.relativize(
279-
path = file.path,
280-
start = paths.join(file.root.path, file.owner.workspace_root),
281-
)
282-
return "%s:%s" % (
283-
file.path,
284-
semantics.get_default_resource_path(root_relative_path, segment_extractor = _java_segments),
285-
)
286-
287-
def _create_single_jar(
288-
actions,
289-
toolchain,
290-
output,
291-
sources = depset(),
292-
resources = depset(),
293-
mnemonic = "JavaSingleJar",
294-
progress_message = "Building singlejar jar %{output}",
295-
build_target = None,
296-
output_creator = None):
297-
"""Register singlejar action for the output jar.
298-
299-
Args:
300-
actions: (actions) ctx.actions
301-
toolchain: (JavaToolchainInfo) The java toolchain
302-
output: (File) Output file of the action.
303-
sources: (depset[File]) The jar files to merge into the output jar.
304-
resources: (depset[File]) The files to add to the output jar.
305-
mnemonic: (str) The action identifier
306-
progress_message: (str) The action progress message
307-
build_target: (Label) The target label to stamp in the manifest. Optional.
308-
output_creator: (str) The name of the tool to stamp in the manifest. Optional,
309-
defaults to 'singlejar'
310-
Returns:
311-
(File) Output file which was used for registering the action.
312-
"""
313-
args = actions.args()
314-
args.set_param_file_format("shell").use_param_file("@%s", use_always = True)
315-
args.add("--output", output)
316-
args.add_all(
317-
[
318-
"--compression",
319-
"--normalize",
320-
"--exclude_build_data",
321-
"--warn_duplicate_resources",
322-
],
323-
)
324-
args.add_all("--sources", sources)
325-
args.add_all("--resources", resources, map_each = _resource_mapper)
326-
327-
args.add("--build_target", build_target)
328-
args.add("--output_jar_creator", output_creator)
329-
330-
actions.run(
331-
mnemonic = mnemonic,
332-
progress_message = progress_message,
333-
executable = toolchain.single_jar,
334-
toolchain = semantics.JAVA_TOOLCHAIN_TYPE,
335-
inputs = depset(transitive = [resources, sources]),
336-
tools = [toolchain.single_jar],
337-
outputs = [output],
338-
arguments = [args],
339-
use_default_shell_env = True,
340-
)
341-
return output
342-
343-
# TODO(hvd): use skylib shell.quote()
344-
def _shell_escape(s):
345-
"""Shell-escape a string
346-
347-
Quotes a word so that it can be used, without further quoting, as an argument
348-
(or part of an argument) in a shell command.
349-
350-
Args:
351-
s: (str) the string to escape
352-
353-
Returns:
354-
(str) the shell-escaped string
355-
"""
356-
if not s:
357-
# Empty string is a special case: needs to be quoted to ensure that it
358-
# gets treated as a separate argument.
359-
return "''"
360-
for c in s.elems():
361-
# We do this positively so as to be sure we don't inadvertently forget
362-
# any unsafe characters.
363-
if not c.isalnum() and c not in "@%-_+:,./":
364-
return "'" + s.replace("'", "'\\''") + "'"
365-
return s
366-
367-
def _detokenize_javacopts(opts):
368-
"""Detokenizes a list of options to a depset.
369-
370-
Args:
371-
opts: ([str]) the javac options to detokenize
372-
373-
Returns:
374-
(depset[str]) depset of detokenized options
375-
"""
376-
return depset(
377-
[" ".join([_shell_escape(opt) for opt in opts])],
378-
order = "preorder",
379-
)
380-
381-
def _derive_output_file(ctx, base_file, *, name_suffix = "", extension = None, extension_suffix = ""):
382-
"""Declares a new file whose name is derived from the given file
383-
384-
This method allows appending a suffix to the name (before extension), changing
385-
the extension or appending a suffix after the extension. The new file is declared
386-
as a sibling of the given base file. At least one of the three options must be
387-
specified. It is an error to specify both `extension` and `extension_suffix`.
388-
389-
Args:
390-
ctx: (RuleContext) the rule context.
391-
base_file: (File) the file from which to derive the resultant file.
392-
name_suffix: (str) Optional. The suffix to append to the name before the
393-
extension.
394-
extension: (str) Optional. The new extension to use (without '.'). By default,
395-
the base_file's extension is used.
396-
extension_suffix: (str) Optional. The suffix to append to the base_file's extension
397-
398-
Returns:
399-
(File) the derived file
400-
"""
401-
if not name_suffix and not extension_suffix and not extension:
402-
fail("At least one of name_suffix, extension or extension_suffix is required")
403-
if extension and extension_suffix:
404-
fail("only one of extension or extension_suffix can be specified")
405-
if extension == None:
406-
extension = base_file.extension
407-
new_basename = paths.replace_extension(base_file.basename, name_suffix + "." + extension + extension_suffix)
408-
return ctx.actions.declare_file(new_basename, sibling = base_file)
409-
410235
def _is_stamping_enabled(ctx, stamp):
411236
if ctx.configuration.is_tool_configuration():
412237
return 0
@@ -416,40 +241,6 @@ def _is_stamping_enabled(ctx, stamp):
416241
# stamp == -1 / auto
417242
return int(ctx.configuration.stamp_binaries())
418243

419-
def _get_relative(path_a, path_b):
420-
if paths.is_absolute(path_b):
421-
return path_b
422-
return paths.normalize(paths.join(path_a, path_b))
423-
424-
def _tokenize_javacopts(ctx = None, opts = []):
425-
"""Tokenizes a list or depset of options to a list.
426-
427-
Iff opts is a depset, we reverse the flattened list to ensure right-most
428-
duplicates are preserved in their correct position.
429-
430-
If the ctx parameter is omitted, a slow, but pure Starlark, implementation
431-
of shell tokenization is used. Otherwise, tokenization is performed using
432-
ctx.tokenize() which has significantly better performance (up to 100x for
433-
large options lists).
434-
435-
Args:
436-
ctx: (RuleContext|None) the rule context
437-
opts: (depset[str]|[str]) the javac options to tokenize
438-
Returns:
439-
[str] list of tokenized options
440-
"""
441-
if hasattr(opts, "to_list"):
442-
opts = reversed(opts.to_list())
443-
if ctx:
444-
return [
445-
token
446-
for opt in opts
447-
for token in ctx.tokenize(opt)
448-
]
449-
else:
450-
# TODO: optimize and use the pure Starlark implementation in cc_helper
451-
return semantics.tokenize_javacopts(opts)
452-
453244
helper = struct(
454245
collect_all_targets_as_deps = _collect_all_targets_as_deps,
455246
filter_launcher_for_target = _filter_launcher_for_target,
@@ -466,15 +257,14 @@ helper = struct(
466257
get_coverage_config = _get_coverage_config,
467258
get_java_executable = _get_java_executable,
468259
is_absolute_target_platform_path = _is_absolute_target_platform_path,
469-
is_target_platform_windows = _is_target_platform_windows,
260+
is_target_platform_windows = _loading_phase_helper.is_target_platform_windows,
470261
runfiles_enabled = _runfiles_enabled,
471262
get_test_support = _get_test_support,
472-
create_single_jar = _create_single_jar,
473-
shell_escape = _shell_escape,
474-
detokenize_javacopts = _detokenize_javacopts,
475-
tokenize_javacopts = _tokenize_javacopts,
476-
derive_output_file = _derive_output_file,
263+
create_single_jar = _loading_phase_helper.create_single_jar,
264+
shell_escape = _loading_phase_helper.shell_escape,
265+
detokenize_javacopts = _loading_phase_helper.detokenize_javacopts,
266+
tokenize_javacopts = _loading_phase_helper.tokenize_javacopts,
477267
is_stamping_enabled = _is_stamping_enabled,
478-
get_relative = _get_relative,
479-
has_target_constraints = _has_target_constraints,
268+
get_relative = _loading_phase_helper.get_relative,
269+
has_target_constraints = _loading_phase_helper.has_target_constraints,
480270
)

0 commit comments

Comments
 (0)