_GatheredModuleInfo = provider( fields = { "name": "Name of the module, may be `None`.", "module_path": "depset of jars that make up the module path so far.", "jars": "Jars to include in the module.", "source_jars": "Source jars to include in the module.", "java_info": "A merged JavaInfo of all the entries in `jars`.", }, ) JavaModuleInfo = provider( fields = { "name": "Name of the module.", "module_path": "depset of jars to include on the module path", }, ) # In order to construct the module path, we do this: # * `JavaModuleInfo`: use the module path from that # * maven coordinates: add the runtime output jars to the module path # * otherwise: just accumulate jars _ATTR_ASPECTS = [ "deps", "exports", "runtime_deps", ] def _infer_name(tags): names = [tag[len("maven_coordinates="):] for tag in tags if tag.startswith("maven_coordinates=")] if len(names) == 0: return None if len(names) > 1: fail("Only one set of maven coordinates may be specified") exploded = names[0].split(":") # We want the group id and artifact id. If the artifact id begins # with the last segment of the group id, then remove it. groups = exploded[0].split(".") final = groups[-1] + "-" if exploded[1].startswith(final): return (exploded[0] + "." + exploded[1][len(final):]).replace("-", "_") return (exploded[0] + "." + exploded[1]).replace("-", "_") def _java_module_aspect_impl(target, ctx): name = _infer_name(ctx.rule.attr.tags) all_deps = [] [all_deps.extend(getattr(ctx.rule.attr, attr, [])) for attr in _ATTR_ASPECTS] all_infos = [dep[_GatheredModuleInfo] for dep in all_deps] nameless_infos = [info for info in all_infos if not info.name] jars = depset(direct = target[JavaInfo].runtime_output_jars, transitive = [info.jars for info in nameless_infos]) source_jars = depset(direct = target[JavaInfo].source_jars, transitive = [info.source_jars for info in nameless_infos]) derived = "unknown" if JavaModuleInfo in target: module_path = target[JavaModuleInfo].module_path jars = depset(target[JavaInfo].runtime_output_jars) source_jars = target[JavaInfo].source_jars java_info = target[JavaInfo] elif name: module_path = depset(direct = target[JavaInfo].runtime_output_jars, transitive = [info.module_path for info in all_infos]) java_info = java_common.merge([info.java_info for info in all_infos if info.name] + [target[JavaInfo]]) else: module_path = depset(transitive = [info.module_path for info in all_infos]) java_info = java_common.merge([info.java_info for info in all_infos if info.name]) return [ _GatheredModuleInfo( name = name, module_path = module_path, jars = jars, source_jars = source_jars, java_info = java_info, ), ] _java_module_aspect = aspect( _java_module_aspect_impl, attr_aspects = _ATTR_ASPECTS, required_aspect_providers = [ [JavaInfo], ], provides = [ _GatheredModuleInfo, ], host_fragments = [ "java", ], ) def _java_module_impl(ctx): name = _infer_name(ctx.attr.tags) all_infos = [dep[_GatheredModuleInfo] for dep in ctx.attr.deps] + [dep[_GatheredModuleInfo] for dep in ctx.attr.exports] included_jars = depset(direct = [ctx.file.target], transitive = [info.jars for info in all_infos]) # Now that we have a single jar, derive the module info. all_jars = depset(transitive = [info.module_path for info in all_infos]).to_list() module_path_jars = depset(transitive = [info.module_path for info in all_infos]) module_info_jar = ctx.actions.declare_file("%s-module-info.jar" % ctx.attr.name) args = ctx.actions.args() args.add_all(["--module-name", name]) args.add_all(["--in", ctx.file.target]) args.add_all(["--output", module_info_jar]) args.add_all(ctx.attr.hides, before_each = "--hides") args.add_all(ctx.attr.uses, before_each = "--uses") args.add_all(module_path_jars, before_each = "--module-path") args.add_all(ctx.attr.opens_to, before_each = "--open-to") ctx.actions.run( executable = ctx.executable._module_generator, outputs = [module_info_jar], inputs = depset([ctx.file.target], transitive = [info.module_path for info in all_infos]), arguments = [args], ) # Now merge the input jars and the module info into a single unit. # Bazel's singlejar strips the manifest of all useful information, # which is suboptimal, so we don't use that. module_jar = ctx.actions.declare_file("lib%s.jar" % ctx.attr.name) args = ctx.actions.args() args.add_all(["--sources", ctx.file.target]) args.add_all(["--sources", module_info_jar]) args.add_all(["--output", module_jar]) ctx.actions.run( executable = ctx.executable._merge_jars, outputs = [module_jar], inputs = [ module_info_jar, ctx.file.target, ], arguments = [args], ) # Generate the ijar compile_jar = java_common.run_ijar( actions = ctx.actions, jar = module_jar, target_label = ctx.label, java_toolchain = ctx.attr._java_toolchain[java_common.JavaToolchainInfo], ) # Create the merged source jar src_jar = java_common.pack_sources( actions = ctx.actions, output_source_jar = ctx.actions.declare_file("lib%s-src.jar" % ctx.attr.name), source_jars = depset( direct = ctx.attr.target[JavaInfo].source_jars, transitive = [info.source_jars for info in all_infos if not info.name], ).to_list(), java_toolchain = ctx.attr._java_toolchain[java_common.JavaToolchainInfo], ) # TODO: This JavaInfo needs to have the JavaInfos of all jars included in module stripped java_info = JavaInfo( output_jar = module_jar, source_jar = src_jar, compile_jar = compile_jar, deps = [info.java_info for info in all_infos], exports = [ex[JavaInfo] for ex in ctx.attr.exports], runtime_deps = [dep[JavaInfo] for dep in ctx.attr.deps], ) return [ DefaultInfo(files = depset([module_jar])), JavaModuleInfo( name = name, module_path = depset(direct = [module_jar], transitive = [info.module_path for info in all_infos]), ), OutputGroupInfo( module_jar = [module_jar], module_source = [src_jar], _source_jars = [src_jar], ), java_info, ] java_module = rule( _java_module_impl, attrs = { "target": attr.label( mandatory = True, allow_single_file = True, providers = [ [_GatheredModuleInfo, JavaInfo], ], aspects = [ _java_module_aspect, ], ), "deps": attr.label_list( providers = [ [_GatheredModuleInfo], ], aspects = [ _java_module_aspect, ], ), "exports": attr.label_list( providers = [ [_GatheredModuleInfo, JavaInfo], ], aspects = [ _java_module_aspect, ], ), "hides": attr.string_list( doc = "List of package names to hide", default = [], ), "opens_to": attr.string_list( doc = "List of modules this module is open to", default = [], ), "uses": attr.string_list( doc = "List of classnames that the module uses", default = [], ), "_javabase": attr.label( cfg = "exec", default = "@bazel_tools//tools/jdk:current_java_runtime", providers = [java_common.JavaRuntimeInfo], ), "_java_toolchain": attr.label( default = "@bazel_tools//tools/jdk:current_java_toolchain", ), "_merge_jars": attr.label( default = "@rules_jvm_external//private/tools/java/com/github/bazelbuild/rules_jvm_external/jar:MergeJars", executable = True, cfg = "exec", ), "_module_generator": attr.label( default = "//java/src/dev/selenium/tools/modules:ModuleGenerator", executable = True, cfg = "exec", ), }, toolchains = ["@bazel_tools//tools/jdk:toolchain_type"], )