Skip to main content
Glama
cd_jar_creator_util.bzl31.9 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_providers.bzl", "JavaClasspathEntry", "JavaCompilingDepsTSet", "JavaLibraryInfo", "create_abi", "derive_compiling_deps", ) load("@prelude//java:java_toolchain.bzl", "AbiGenerationMode", "JavaToolchainInfo") load("@prelude//java/plugins:java_annotation_processor.bzl", "AnnotationProcessorProperties") # @unused Used as type load( "@prelude//java/plugins:java_plugin.bzl", "PluginParams", # @unused Used as type ) load("@prelude//java/utils:java_utils.bzl", "declare_prefixed_name") load("@prelude//utils:expect.bzl", "expect") def declare_prefixed_output(actions: AnalysisActions, prefix: [str, None], output: str, dir: bool = False) -> Artifact: return actions.declare_output(declare_prefixed_name(output, prefix), dir = dir) # The library and the toolchain can both set a specific abi generation # mode. The toolchain's setting is effectively the "highest" form of abi # that the toolchain supports and then the same for the target and we will choose # the "highest" that both support. def _resolve_abi_generation_mode(abi_generation_mode: [AbiGenerationMode, None], java_toolchain: JavaToolchainInfo) -> AbiGenerationMode: if abi_generation_mode == None: return java_toolchain.abi_generation_mode for mode in [AbiGenerationMode("none"), AbiGenerationMode("class"), AbiGenerationMode("source"), AbiGenerationMode("source_only")]: if mode in (java_toolchain.abi_generation_mode, abi_generation_mode): return mode fail("resolving abi generation mode failed. had `{}` and `{}`".format(java_toolchain.abi_generation_mode, abi_generation_mode)) def get_abi_generation_mode( abi_generation_mode: [AbiGenerationMode, None], java_toolchain: JavaToolchainInfo, srcs: list[Artifact], annotation_processor_properties: AnnotationProcessorProperties) -> AbiGenerationMode: resolved_mode = AbiGenerationMode("none") if not srcs else _resolve_abi_generation_mode(abi_generation_mode, java_toolchain) if resolved_mode == AbiGenerationMode("source_only"): def plugins_support_source_only_abi(): for ap in annotation_processor_properties.annotation_processors: if ap.affects_abi and not ap.supports_source_only_abi: return False return True if not plugins_support_source_only_abi(): resolved_mode = AbiGenerationMode("source") return resolved_mode # We need to construct a complex protobuf message. We do it by constructing # a bunch of nested structs and then use write_json to get a json-encoded # protobuf message. # # The definition is in xplat/toolchains/android/sdk/src/com/facebook/buck/cd/resources/proto/javacd.proto # and xplat/toolchains/android/sdk/src/com/facebook/buck/cd/resources/proto/kotlincd.proto and is, sadly, # poorly documented. # Our protobuf format mostly encodes paths in RelPath/AbsPath structs with a single "path" field. # Note that we don't actually use abspath and instead enable JAVACD_ABSOLUTE_PATHS_ARE_RELATIVE_TO_CWD TargetType = enum("library", "source_abi", "source_only_abi") BuildMode = enum("LIBRARY", "ABI") def encode_abi_generation_mode(mode: AbiGenerationMode) -> str: return { AbiGenerationMode("none"): "NONE", AbiGenerationMode("class"): "CLASS", AbiGenerationMode("source"): "SOURCE", AbiGenerationMode("source_only"): "SOURCE_ONLY", }[mode] def encode_target_type(target_type: TargetType) -> str: if target_type == TargetType("library"): return "LIBRARY" if target_type == TargetType("source_abi"): return "SOURCE_ABI" if target_type == TargetType("source_only_abi"): return "SOURCE_ONLY_ABI" fail() OutputPaths = record( jar = Artifact, classes = Artifact, annotations = Artifact, ) def qualified_name_with_subtarget(label: Label) -> str: if label.sub_target: return "{}:{}[{}]".format(label.path, label.name, label.sub_target[0]) return "{}:{}".format(label.path, label.name) # Converted to str so that we get the right result when written as json. def base_qualified_name(label: Label) -> str: return "{}:{}".format(label.path, label.name) def get_qualified_name(label: Label, target_type: TargetType) -> str: # These should match the names for subtargets in java_library.bzl return { TargetType("library"): base_qualified_name(label), TargetType("source_abi"): base_qualified_name(label) + "[source-abi]", TargetType("source_only_abi"): base_qualified_name(label) + "[source-only-abi]", }[target_type] def define_output_paths(actions: AnalysisActions, prefix: [str, None], label: Label) -> OutputPaths: # currently, javacd requires that at least some outputs are in the root # output dir. so we put all of them there. If javacd is updated we # could consolidate some of these into one subdir. return OutputPaths( jar = declare_prefixed_output(actions, prefix, "jar/{}.jar".format(label.name)), classes = declare_prefixed_output(actions, prefix, "__classes__", dir = True), annotations = declare_prefixed_output(actions, prefix, "__gen__", dir = True), ) def encode_output_paths(label: Label, paths: OutputPaths, target_type: TargetType) -> struct: paths = struct( classesDir = paths.classes.as_output(), outputJarDirPath = cmd_args(paths.jar.as_output(), parent = 1), annotationPath = paths.annotations.as_output(), outputJarPath = paths.jar.as_output(), ) return struct( libraryPaths = paths if target_type == TargetType("library") else None, sourceAbiPaths = paths if target_type == TargetType("source_abi") else None, sourceOnlyAbiPaths = paths if target_type == TargetType("source_only_abi") else None, libraryTargetFullyQualifiedName = base_qualified_name(label), ) def encode_jar_params(remove_classes: list[str], output_paths: OutputPaths, manifest_file: Artifact | None) -> struct: return struct( jarPath = output_paths.jar.as_output(), removeEntryPredicate = struct( patterns = remove_classes, ), entriesToJar = [output_paths.classes.as_output()], manifestFile = manifest_file, duplicatesLogLevel = "FINE", ) def command_abi_generation_mode(target_type: TargetType, abi_generation_mode: [AbiGenerationMode, None]) -> [AbiGenerationMode, None]: # We want the library target to have the real generation mode, but the source one's just use their own. # The generation mode will be used elsewhere to setup the target's classpath entry to use the correct abi. if target_type == TargetType("source_abi"): return AbiGenerationMode("source") if target_type == TargetType("source_only_abi"): return AbiGenerationMode("source_only") return abi_generation_mode def get_compiling_deps_tset( actions: AnalysisActions, deps: list[Dependency], additional_classpath_entries: JavaCompilingDepsTSet | None) -> [JavaCompilingDepsTSet, None]: compiling_deps_tset = derive_compiling_deps(actions, None, deps) if additional_classpath_entries: if compiling_deps_tset == None: compiling_deps_tset = additional_classpath_entries else: compiling_deps_tset = actions.tset(JavaCompilingDepsTSet, children = [compiling_deps_tset, additional_classpath_entries]) return compiling_deps_tset def _get_source_only_abi_compiling_deps(compiling_deps_tset: [JavaCompilingDepsTSet, None], source_only_abi_deps: list[Dependency]) -> list[JavaClasspathEntry]: source_only_abi_compiling_deps = [] if compiling_deps_tset: source_only_abi_deps_filter = {} for d in source_only_abi_deps: info = d.get(JavaLibraryInfo) if not info: fail("source_only_abi_deps must produce a JavaLibraryInfo but '{}' does not, please remove it".format(d.label)) if info.library_output: source_only_abi_deps_filter[info.library_output.abi] = True def filter_compiling_deps(dep): return dep.abi in source_only_abi_deps_filter or dep.required_for_source_only_abi source_only_abi_compiling_deps = [compiling_dep for compiling_dep in list(compiling_deps_tset.traverse()) if filter_compiling_deps(compiling_dep)] return source_only_abi_compiling_deps # buildifier: disable=unused-variable def encode_ap_params(annotation_processor_properties: AnnotationProcessorProperties, target_type: TargetType) -> [struct, None]: # buck1 oddly only inspects annotation processors, not plugins for # abi/source-only abi related things, even though the plugin rules # support the flags. we apply it to both. encoded_ap_params = None if annotation_processor_properties.annotation_processors: encoded_ap_params = struct( parameters = annotation_processor_properties.annotation_processor_params, pluginProperties = [], ) for ap in annotation_processor_properties.annotation_processors: # We should also filter out non-abi-affecting APs for source-abi, but buck1 doesn't and so we have lots that depend on not filtering them there. if target_type == TargetType("source_only_abi") and not ap.affects_abi: continue if ap.deps or ap.processors: encoded_ap_params.pluginProperties.append( struct( canReuseClassLoader = not ap.isolate_class_loader, doesNotAffectAbi = not ap.affects_abi, supportsAbiGenerationFromSource = ap.supports_source_only_abi, runsOnJavaOnly = ap.runs_on_java_only, processorNames = ap.processors, classpath = ap.deps.project_as_json("javacd_json") if ap.deps else [], pathParams = {}, ), ) return encoded_ap_params def encode_plugin_params(plugin_params: [PluginParams, None]) -> [struct, None]: encoded_plugin_params = None if plugin_params: encoded_plugin_params = struct( parameters = [], pluginProperties = [ encode_plugin_properties(processor, arguments, plugin_params) for processor, arguments in plugin_params.processors ], ) return encoded_plugin_params def encode_plugin_properties(processor: str, arguments: cmd_args, plugin_params: PluginParams) -> struct: return struct( canReuseClassLoader = False, doesNotAffectAbi = False, supportsAbiGenerationFromSource = False, runsOnJavaOnly = False, processorNames = [processor], classpath = plugin_params.deps.project_as_json("javacd_json") if plugin_params.deps else [], pathParams = {}, arguments = arguments, ) def encode_base_jar_command( javac_tool: [str, RunInfo, Artifact, None], target_type: TargetType, output_paths: OutputPaths, remove_classes: list[str], label: Label, compiling_deps_tset: [JavaCompilingDepsTSet, None], classpath_jars_tag: ArtifactTag, bootclasspath_entries: list[Artifact], system_image: Artifact | None, source_level: int, target_level: int, abi_generation_mode: [AbiGenerationMode, None], srcs: list[Artifact], resources_map: dict[str, Artifact], annotation_processor_properties: AnnotationProcessorProperties, plugin_params: [PluginParams, None], manifest_file: Artifact | None, extra_arguments: cmd_args, source_only_abi_compiling_deps: list[JavaClasspathEntry], track_class_usage: bool, provide_classpath_snapshot: bool = False) -> struct: jar_parameters = encode_jar_params(remove_classes, output_paths, manifest_file) qualified_name = get_qualified_name(label, target_type) if target_type == TargetType("source_only_abi"): compiling_classpath = classpath_jars_tag.tag_artifacts([dep.abi for dep in source_only_abi_compiling_deps]) compiling_classpath_snapshot = {} else: expect(len(source_only_abi_compiling_deps) == 0) compiling_deps_list = filter(None, list(compiling_deps_tset.traverse(ordering = "topological"))) if compiling_deps_tset else [] compiling_classpath = classpath_jars_tag.tag_artifacts( [dep.abi for dep in compiling_deps_list], ) # The snapshot inputs are tagged for association with dep_files, but they are not marked as used, # as they serve the incremental compiler's internal needs, # which are utilized after the build system has determined whether a rebuild is necessary. compiling_classpath_snapshot = classpath_jars_tag.tag_artifacts({dep.abi: dep.abi_jar_snapshot or "" for dep in compiling_deps_list}) if provide_classpath_snapshot else {} build_target_value = struct( fullyQualifiedName = qualified_name, type = encode_target_type(target_type), ) if javac_tool: resolved_javac = { "externalJavac": { "commandPrefix": [javac_tool], "shortName": str(javac_tool), }, } else: resolved_javac = {"jsr199Javac": {}} resolved_java_options = struct( bootclasspathList = bootclasspath_entries, languageLevelOptions = struct( sourceLevel = source_level, targetLevel = target_level, ), debug = True, javaAnnotationProcessorParams = encode_ap_params(annotation_processor_properties, target_type), standardJavacPluginParams = encode_plugin_params(plugin_params), extraArguments = extra_arguments, systemImage = system_image, ) return struct( outputPathsValue = encode_output_paths(label, output_paths, target_type), compileTimeClasspathPaths = compiling_classpath, compileTimeClasspathSnapshotPaths = compiling_classpath_snapshot, javaSrcs = srcs, # We use "class" abi compatibility to match buck1 (other compatibility modes are used for abi verification. abiCompatibilityMode = encode_abi_generation_mode(AbiGenerationMode("class")), abiGenerationMode = encode_abi_generation_mode(command_abi_generation_mode(target_type, abi_generation_mode)), trackClassUsage = track_class_usage, configuredBuckOut = "buck-out/v2", buildTargetValue = build_target_value, resourcesMap = [ { "key": v, "value": cmd_args([output_paths.classes.as_output(), "/", k], delimiter = ""), } for (k, v) in resources_map.items() ], resolvedJavac = resolved_javac, resolvedJavacOptions = resolved_java_options, jarParameters = jar_parameters, pathToClasses = output_paths.jar.as_output(), annotationsPath = output_paths.annotations.as_output(), ) def setup_dep_files( actions: AnalysisActions, actions_identifier: [str, None], cmd: cmd_args, post_build_params: dict, classpath_jars_tag: ArtifactTag, used_classes_json_outputs: list[cmd_args], used_jars_json_output: Artifact, abi_to_abi_dir_map: [TransitiveSetArgsProjection, list[cmd_args], None], hidden = ["artifact"]) -> cmd_args: dep_file = declare_prefixed_output(actions, actions_identifier, "dep_file.txt") new_cmd_args = [] new_cmd_hidden = [] new_cmd_args.append(cmd) post_build_params["usedClasses"] = used_classes_json_outputs post_build_params["depFile"] = classpath_jars_tag.tag_artifacts(dep_file.as_output()) post_build_params["usedJarsFile"] = used_jars_json_output.as_output() if abi_to_abi_dir_map: abi_to_abi_dir_map_file = declare_prefixed_output(actions, actions_identifier, "abi_to_abi_dir_map") actions.write(abi_to_abi_dir_map_file, abi_to_abi_dir_map) post_build_params["jarToJarDirMap"] = abi_to_abi_dir_map_file if isinstance(abi_to_abi_dir_map, TransitiveSetArgsProjection): new_cmd_hidden.append(classpath_jars_tag.tag_artifacts(abi_to_abi_dir_map)) for hidden_artifact in hidden: new_cmd_hidden.append(classpath_jars_tag.tag_artifacts(hidden_artifact)) return cmd_args(hidden = new_cmd_hidden) FORCE_PERSISTENT_WORKERS = read_root_config("build", "require_persistent_workers", "false").lower() == "true" def prepare_cd_exe( qualified_name: str, java: RunInfo, class_loader_bootstrapper: Artifact, compiler: Artifact, main_class: str, worker: WorkerInfo | None, target_specified_debug_port: [int, None], toolchain_specified_debug_port: [int, None], toolchain_specified_debug_target: [Label, None], extra_jvm_args: list[str], extra_jvm_args_target: list[Label]) -> tuple: local_only = False jvm_args = ["-XX:-MaxFDLimit"] # The variables 'extra_jvm_args' and 'extra_jvm_args_target' are generally used, but they are primarily designed for profiling use-cases. # The following section is configured with the profiling use-case in mind. if extra_jvm_args_target: if len(extra_jvm_args_target) == 1: # If there's only one target to profile, we want to isolate its compilation. # This target should be built in its own action, allowing the worker (if available) to handle the remaining targets. if qualified_name == qualified_name_with_subtarget(extra_jvm_args_target[0]): jvm_args = jvm_args + extra_jvm_args local_only = True # This flag ensures the target is not run on the worker. else: # If there are multiple targets to profile, they should be built on the worker to generate a single profiling data set. # The remaining targets should be built individually, either locally or on the Remote Execution (RE). local_only = True # By default, targets are not run on the worker. for target in extra_jvm_args_target: # If the current target matches the qualified name with subtarget, it is selected for profiling. if qualified_name == qualified_name_with_subtarget(target): jvm_args = jvm_args + extra_jvm_args local_only = False # This flag allows the target to run on the worker. break else: # If no specific target is provided, the extra JVM arguments are added to all targets that run on worker, local machine or RE. jvm_args = jvm_args + extra_jvm_args # Allow JVM compiler daemon to access internal jdk.compiler APIs jvm_args += [ "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", "--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", "--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", "--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED", "--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", ] if target_specified_debug_port: debug_port = target_specified_debug_port elif toolchain_specified_debug_port and qualified_name == qualified_name_with_subtarget(toolchain_specified_debug_target): debug_port = toolchain_specified_debug_port else: debug_port = None if debug_port: # Do not use a worker when debugging is enabled local_only = True jvm_args.extend(["-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address={}".format(debug_port)]) non_worker_args = cmd_args([java, jvm_args, "-cp", compiler, "-jar", class_loader_bootstrapper, main_class]) if local_only or not worker: return RunInfo(args = non_worker_args), local_only else: worker_run_info = WorkerRunInfo( # Specifies the command to compile using a non-worker process, on RE or if workers are disabled exe = non_worker_args, # Specifies the command to initialize a new worker process. # This is used for local execution if `build.use_persistent_workers=True` worker = worker, ) return worker_run_info, FORCE_PERSISTENT_WORKERS FinalJarOutput = record( final_jar = Artifact, # The same as final_jar unless there is a jar_postprocessor. preprocessed_jar = Artifact, ) # If there's additional compiled srcs, we need to merge them in and if the # caller specified an output artifact we need to make sure the jar is in that # location. def prepare_final_jar( actions: AnalysisActions, actions_identifier: [str, None], output: Artifact | None, output_paths: OutputPaths, additional_compiled_srcs: Artifact | None, jar_builder: RunInfo, jar_postprocessor: [RunInfo, None], jar_postprocessor_runner: RunInfo | None, zip_scrubber: RunInfo) -> FinalJarOutput: def make_output(jar: Artifact) -> FinalJarOutput: if jar_postprocessor: expect(jar_postprocessor_runner != None, "Must provide a jar_postprocessor_runner if jar_postprocessor is provided!") postprocessed_jar = postprocess_jar(actions, zip_scrubber, jar_postprocessor, jar_postprocessor_runner, jar, actions_identifier) return FinalJarOutput(final_jar = postprocessed_jar, preprocessed_jar = jar) else: return FinalJarOutput(final_jar = jar, preprocessed_jar = jar) if not additional_compiled_srcs: output_jar = output_paths.jar if output: actions.copy_file(output.as_output(), output_paths.jar) output_jar = output return make_output(output_jar) merged_jar = output if not merged_jar: merged_jar = declare_prefixed_output(actions, actions_identifier, "merged.jar") files_to_merge = [output_paths.jar, additional_compiled_srcs] files_to_merge_file = actions.write(declare_prefixed_name("files_to_merge.txt", actions_identifier), files_to_merge) actions.run( cmd_args([ jar_builder, "--output", merged_jar.as_output(), "--entries-to-jar", files_to_merge_file, ], hidden = files_to_merge), category = "merge_additional_srcs", identifier = actions_identifier, ) return make_output(merged_jar) def encode_command( javac_tool: [str, RunInfo, Artifact, None], label: Label, srcs: list[Artifact], remove_classes: list[str], annotation_processor_properties: AnnotationProcessorProperties, plugin_params: [PluginParams, None], manifest_file: Artifact | None, source_level: int, target_level: int, compiling_deps_tset: [JavaCompilingDepsTSet, None], bootclasspath_entries: list[Artifact], system_image: Artifact | None, abi_generation_mode: AbiGenerationMode, resources_map: dict[str, Artifact], extra_arguments: cmd_args, kotlin_extra_params: [struct, None], provide_classpath_snapshot: bool, build_mode: BuildMode, target_type: TargetType, output_paths: OutputPaths, classpath_jars_tag: ArtifactTag, source_only_abi_compiling_deps: list[JavaClasspathEntry], track_class_usage: bool) -> struct: base_jar_command = encode_base_jar_command( javac_tool, target_type, output_paths, remove_classes, label, compiling_deps_tset, classpath_jars_tag, bootclasspath_entries, system_image, source_level, target_level, abi_generation_mode, srcs, resources_map, annotation_processor_properties = annotation_processor_properties, plugin_params = plugin_params, manifest_file = manifest_file, extra_arguments = cmd_args(extra_arguments), source_only_abi_compiling_deps = source_only_abi_compiling_deps, track_class_usage = track_class_usage, provide_classpath_snapshot = provide_classpath_snapshot, ) if kotlin_extra_params: return struct( buildMode = build_mode, baseJarCommand = base_jar_command, kotlinExtraParams = kotlin_extra_params, ) else: return struct( buildMode = build_mode, baseJarCommand = base_jar_command, ) # buildifier: disable=unused-variable def generate_abi_jars( actions: AnalysisActions, actions_identifier: [str, None], label: Label, abi_generation_mode: [AbiGenerationMode, None], additional_compiled_srcs: Artifact | None, is_building_android_binary: bool, class_abi_generator: Dependency, final_jar: Artifact, compiling_deps_tset: [JavaCompilingDepsTSet, None], source_only_abi_deps: list[Dependency], class_abi_jar: Artifact | None, class_abi_output_dir: Artifact | None, track_class_usage: bool, encode_abi_command: typing.Callable, define_action: typing.Callable) -> tuple: class_abi = None source_abi = None source_only_abi = None classpath_abi = None classpath_abi_dir = None # If we are merging additional compiled_srcs, we can't produce source/source-only abis. Otherwise we # always generation the source/source-only abis and setup the classpath entry to use the appropriate # abi. This allows us to build/inspect/debug source/source-only abi for rules that don't have it enabled. if not additional_compiled_srcs: if abi_generation_mode == AbiGenerationMode("source") or not is_building_android_binary: source_abi_identifier = declare_prefixed_name("source_abi", actions_identifier) source_abi_target_type = TargetType("source_abi") source_abi_qualified_name = get_qualified_name(label, source_abi_target_type) source_abi_output_paths = define_output_paths(actions, source_abi_identifier, label) source_abi_classpath_jars_tag = actions.artifact_tag() source_abi_dir = declare_prefixed_output(actions, source_abi_identifier, "source-abi-dir", dir = True) source_abi_command = encode_abi_command( build_mode = BuildMode("ABI"), target_type = source_abi_target_type, output_paths = source_abi_output_paths, classpath_jars_tag = source_abi_classpath_jars_tag, source_only_abi_compiling_deps = [], track_class_usage = track_class_usage, ) define_action( "source_abi_", source_abi_identifier, source_abi_command, source_abi_qualified_name, source_abi_output_paths, source_abi_classpath_jars_tag, source_abi_dir, source_abi_target_type, ) source_abi = source_abi_output_paths.jar if abi_generation_mode == AbiGenerationMode("source"): classpath_abi = source_abi classpath_abi_dir = source_abi_dir if abi_generation_mode == AbiGenerationMode("source_only") or not is_building_android_binary: source_only_abi_identifier = declare_prefixed_name("source_only_abi", actions_identifier) source_only_abi_target_type = TargetType("source_only_abi") source_only_abi_qualified_name = get_qualified_name(label, source_only_abi_target_type) source_only_abi_output_paths = define_output_paths(actions, source_only_abi_identifier, label) source_only_abi_classpath_jars_tag = actions.artifact_tag() source_only_abi_dir = declare_prefixed_output(actions, source_only_abi_identifier, "dir", dir = True) source_only_abi_compiling_deps = _get_source_only_abi_compiling_deps(compiling_deps_tset, source_only_abi_deps) source_only_abi_command = encode_abi_command( build_mode = BuildMode("ABI"), target_type = source_only_abi_target_type, output_paths = source_only_abi_output_paths, classpath_jars_tag = source_only_abi_classpath_jars_tag, source_only_abi_compiling_deps = source_only_abi_compiling_deps, track_class_usage = track_class_usage, ) define_action( "source_only_abi_", source_only_abi_identifier, source_only_abi_command, source_only_abi_qualified_name, source_only_abi_output_paths, source_only_abi_classpath_jars_tag, source_only_abi_dir, source_only_abi_target_type, source_only_abi_compiling_deps = source_only_abi_compiling_deps, ) source_only_abi = source_only_abi_output_paths.jar if abi_generation_mode == AbiGenerationMode("source_only"): classpath_abi = source_only_abi classpath_abi_dir = source_only_abi_dir if abi_generation_mode == AbiGenerationMode("none"): classpath_abi = final_jar if classpath_abi == None or not is_building_android_binary: class_abi = class_abi_jar or create_abi(actions, class_abi_generator, final_jar) if classpath_abi == None: classpath_abi = class_abi if class_abi_output_dir: classpath_abi_dir = class_abi_output_dir return class_abi, source_abi, source_only_abi, classpath_abi, classpath_abi_dir def postprocess_jar( actions: AnalysisActions, zip_scrubber: RunInfo, jar_postprocessor: RunInfo, jar_postprocessor_runner: RunInfo, original_jar: Artifact, actions_identifier: [str, None]) -> Artifact: jar_path = original_jar.short_path postprocessed_output = actions.declare_output("postprocessed_{}".format(jar_path)) postprocess_jar_cmd = cmd_args( jar_postprocessor_runner, "--postprocessor_cmd", cmd_args([ jar_postprocessor, original_jar, postprocessed_output.as_output(), ], delimiter = " "), "--zip_scrubber", cmd_args(zip_scrubber, delimiter = " "), "--output", postprocessed_output.as_output(), ) identifier = actions_identifier if actions_identifier else "" actions.run(postprocess_jar_cmd, category = "postprocessed{}".format(identifier)) return postprocessed_output

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