Skip to main content
Glama
java_binary.bzl11.1 kB
# Copyright (c) Meta Platforms, Inc. and affiliates. # # This source code is licensed under both the MIT license found in the # LICENSE-MIT file in the root directory of this source tree and the Apache # License, Version 2.0 found in the LICENSE-APACHE file in the root directory # of this source tree. load("@prelude//java:java_toolchain.bzl", "JavaToolchainInfo") load("@prelude//java:proguard.bzl", "get_proguard_output") load("@prelude//java/utils:java_utils.bzl", "get_class_to_source_map_info", "get_classpath_subtarget") load( "@prelude//linking:shared_libraries.bzl", "SharedLibrary", # @unused used as type "SharedLibraryInfo", "merge_shared_libraries", "traverse_shared_library_info", ) load("@prelude//utils:expect.bzl", "expect") load( ":java_providers.bzl", "create_template_info", "derive_compiling_deps", "get_java_packaging_info", ) def _generate_script(generate_wrapper: bool, native_libs: list[SharedLibrary]) -> bool: # if `generate_wrapper` is set and no native libs then it should be a wrapper script as result, # otherwise fat jar will be generated (inner jar or script will be included inside a final fat jar) return generate_wrapper and len(native_libs) == 0 def _create_fat_jar( ctx: AnalysisContext, jars: cmd_args, native_libs: list[SharedLibrary] = [], name_prefix: str = "", concat_jars: bool = False, do_not_create_inner_jar: bool = True, generate_wrapper: bool = False, main_class: [str, None] = None, append_jar: [Artifact, None] = None) -> list[Artifact]: java_toolchain = ctx.attrs._java_toolchain[JavaToolchainInfo] extension = "sh" if _generate_script(generate_wrapper, native_libs) else "jar" output = ctx.actions.declare_output("{}{}.{}".format(name_prefix, ctx.label.name, extension)) args = [ java_toolchain.fat_jar[RunInfo], "--jar_builder_tool", cmd_args(java_toolchain.jar_builder, delimiter = " "), "--zip_scrubber_tool", cmd_args(java_toolchain.zip_scrubber, delimiter = " "), "--output", output.as_output(), "--jars_file", ctx.actions.write("{}jars_file".format(name_prefix), jars), ] if concat_jars: args += ["--concat_jars"] if append_jar: args += ["--append_jar", append_jar] if native_libs: expect( java_toolchain.is_bootstrap_toolchain == False, "Bootstrap java toolchain could not be used for java_binary() with native code.", ) args += [ "--native_libs_file", ctx.actions.write("{}native_libs".format(name_prefix), [cmd_args([native_lib.soname.ensure_str(), native_lib.lib.output], delimiter = " ") for native_lib in native_libs]), ] if do_not_create_inner_jar: args += [ "--do_not_create_inner_jar", ] else: args += [ "--fat_jar_lib", java_toolchain.fat_jar_main_class_lib, # fat jar's main class "--fat_jar_main_class", "com.facebook.buck.jvm.java.fatjar.FatJarMain", # native libraries directory name. Main class expects to find libraries packed inside this directory. "--fat_jar_native_libs_directory_name", "nativelibs", ] if main_class: args += ["--main_class", main_class] if ctx.attrs.manifest_file: args += ["--manifest", ctx.attrs.manifest_file] build_manifest = ctx.attrs.build_manifest if build_manifest: args += ["--build_manifest", build_manifest] blocklist = ctx.attrs.blocklist if blocklist: args += ["--blocklist", ctx.actions.write("{}blocklist_args".format(name_prefix), blocklist)] if ctx.attrs.meta_inf_directory: args += ["--meta_inf_directory", ctx.attrs.meta_inf_directory] outputs = [output] if generate_wrapper: classpath_args_output = ctx.actions.declare_output("classpath_args") args += [ "--generate_wrapper", "--classpath_args_output", classpath_args_output.as_output(), "--java_tool", java_toolchain.java[RunInfo], "--script_marker_file_name", "wrapper_script", ] outputs.append(classpath_args_output) fat_jar_cmd = cmd_args( args, hidden = [jars] + [native_lib.lib.output for native_lib in native_libs], ) ctx.actions.run( fat_jar_cmd, local_only = False, category = "{}fat_jar".format(name_prefix), allow_cache_upload = True, ) if generate_wrapper == False: expect( len(outputs) == 1, "expected exactly one output when creating a fat jar", ) # If `generate_wrapper` is not set then the result will contain only 1 item that represent fat jar artifact. # Else if `generate_wrapper` is set then the first item in the result list will be script or far jar, and the second one is for @classpath_args file return outputs def _get_run_cmd( attrs: struct, script_mode: bool, main_artifact: Artifact, java_toolchain: JavaToolchainInfo) -> cmd_args: if script_mode: return cmd_args(["/usr/bin/env", "bash", main_artifact]) else: return cmd_args([java_toolchain.java[RunInfo]] + attrs.java_args_for_run_info + ["-jar", main_artifact]) def _get_java_tool_artifacts(java_toolchain: JavaToolchainInfo) -> list: default_info = java_toolchain.java[DefaultInfo] return default_info.default_outputs + default_info.other_outputs def java_binary_impl(ctx: AnalysisContext) -> list[Provider]: """ java_binary() rule implementation Args: ctx: rule analysis context Returns: list of created providers (DefaultInfo and RunInfo) """ if ctx.attrs._build_only_native_code: return [ DefaultInfo(default_output = ctx.actions.write("{}/unused.jar".format(ctx.label.name), [])), RunInfo(), ] packaging_info = get_java_packaging_info(ctx, ctx.attrs.deps, None) first_order_deps = derive_compiling_deps(ctx.actions, None, ctx.attrs.deps) first_order_libs = [dep.full_library for dep in (list(first_order_deps.traverse()) if first_order_deps else [])] shared_library_info = merge_shared_libraries( ctx.actions, deps = filter(None, [x.get(SharedLibraryInfo) for x in ctx.attrs.deps]), ) native_deps = traverse_shared_library_info(shared_library_info) base_dep = ctx.attrs.base_dep java_toolchain = ctx.attrs._java_toolchain[JavaToolchainInfo] concat_deps = ctx.attrs.concat_deps need_to_generate_wrapper = ctx.attrs.generate_wrapper == True do_not_create_inner_jar = ctx.attrs.do_not_create_inner_jar == True packaging_deps = list(packaging_info.packaging_deps.traverse()) incremental_target_prefix = ctx.attrs.incremental_target_prefix main_class = ctx.attrs.main_class packaging_dep_infos = {dep.jar: dep.label.raw_target() for dep in packaging_deps if dep and dep.jar} other_outputs = [] if ctx.attrs.proguard_config: java_base = ([java_toolchain.java_base_jar] if java_toolchain.java_base_jar else []) library_jars = ctx.attrs.proguard_library_jars + java_base proguard_output = get_proguard_output( ctx = ctx, input_jars = packaging_dep_infos, java_packaging_deps = packaging_deps, additional_proguard_configs = [], additional_jars = library_jars, sdk_proguard_config_mode = None, sdk_proguard_config = None, sdk_optimized_proguard_config = None, proguard_jar = java_toolchain.proguard_jar, skip_proguard = False, ) packaging_dep_infos = proguard_output.jars_to_owners packaging_jar_args = cmd_args(packaging_dep_infos.keys()) if incremental_target_prefix: base_jar = None incremental_jars = [] dependency_jars = [] # separate jars in groups for (dep_jar, owner) in packaging_dep_infos.items(): # lookup for the base jar that can be used to append all other dependencies if base_dep and owner == base_dep.label.raw_target(): expect( base_jar == None, "JAR can only have one base JAR file.", ) base_jar = dep_jar elif str(owner).startswith(incremental_target_prefix): # if it's not a base jar, it can be an incremental jar or dependency only incremental_jars.append(dep_jar) else: dependency_jars.append(dep_jar) # collect incremental targets expect( len(incremental_jars) > 0, "No incremental dependencies found that starts with {}.".format(incremental_target_prefix), ) # generate intermediary jar only with dependencies deps_outputs = _create_fat_jar( ctx, cmd_args(dependency_jars), concat_jars = concat_deps, name_prefix = "deps_", append_jar = base_jar, ) other_outputs = [deps_outputs[0]] # generate final jar appending modules to the dependencies jar outputs = _create_fat_jar( ctx, cmd_args(incremental_jars), native_libs = native_deps, do_not_create_inner_jar = do_not_create_inner_jar, generate_wrapper = need_to_generate_wrapper, main_class = main_class, append_jar = deps_outputs[0], ) else: outputs = _create_fat_jar( ctx, packaging_jar_args, concat_jars = concat_deps, native_libs = native_deps, do_not_create_inner_jar = do_not_create_inner_jar, generate_wrapper = need_to_generate_wrapper, main_class = main_class, ) run_cmd = _get_run_cmd( attrs = ctx.attrs, script_mode = _generate_script(need_to_generate_wrapper, native_deps), main_artifact = outputs[0], java_toolchain = java_toolchain, ) if need_to_generate_wrapper: classpath_file = outputs[1] run_cmd.add(cmd_args(hidden = [ java_toolchain.java[RunInfo], classpath_file, packaging_jar_args, ])) other_outputs = [classpath_file] + [packaging_jar_args] + _get_java_tool_artifacts(java_toolchain) sub_targets = get_classpath_subtarget(ctx.actions, packaging_info) class_to_src_map, _, _ = get_class_to_source_map_info( ctx, outputs = None, deps = ctx.attrs.deps, ) return [ DefaultInfo(default_output = outputs[0], other_outputs = other_outputs, sub_targets = sub_targets), RunInfo(args = run_cmd), create_template_info(ctx, packaging_info, first_order_libs), class_to_src_map, ]

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server