1# Copyright (C) 2022 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14load("@bazel_skylib//lib:paths.bzl", "paths") 15 16SystemInfo = provider(fields = ["system", "java_info"]) 17 18def _gen_module_info_java(ctx, jars_to_module_info, jars, module_info): 19 ctx.actions.run_shell( 20 inputs = jars, 21 outputs = [module_info], 22 command = "{} java.base {} > {}".format( 23 jars_to_module_info.path, 24 " ".join([jar.path for jar in jars]), 25 module_info.path, 26 ), 27 tools = [jars_to_module_info], 28 ) 29 30def _gen_module_info_class(ctx, java_runtime, module_info, java_base_patch_jars, module_info_class): 31 ctx.actions.run_shell( 32 inputs = depset([module_info], transitive = [java_base_patch_jars]), 33 outputs = [module_info_class], 34 tools = java_runtime.files, 35 command = "{} -d {} --system=none --patch-module=java.base={} {}".format( 36 paths.join(java_runtime.java_home, "bin", "javac"), 37 module_info_class.dirname, 38 ":".join([jar.path for jar in java_base_patch_jars.to_list()]), 39 module_info.path, 40 ), 41 ) 42 43def _gen_module_info_jar(ctx, soong_zip, module_info_class, module_info_jar): 44 args = ctx.actions.args() 45 args.add("-jar") 46 args.add("--symlinks=false") 47 args.add("-o", module_info_jar) 48 args.add("-C", module_info_class.dirname) 49 args.add("-f", module_info_class) 50 ctx.actions.run( 51 inputs = [module_info_class], 52 outputs = [module_info_jar], 53 arguments = [args], 54 executable = soong_zip, 55 ) 56 57def _gen_merged_module_jar(ctx, merge_zips, module_info_jar, jars, merged_module_jar): 58 args = ctx.actions.args() 59 args.add("-j", merged_module_jar) 60 args.add_all(depset([module_info_jar], transitive = [jars])) 61 ctx.actions.run( 62 inputs = depset([module_info_jar], transitive = [jars]), 63 outputs = [merged_module_jar], 64 arguments = [args], 65 executable = merge_zips, 66 ) 67 68def _gen_jmod(ctx, java_runtime, merged_module_jar, jmod): 69 ctx.actions.run_shell( 70 inputs = [merged_module_jar], 71 outputs = [jmod], 72 tools = java_runtime.files, 73 command = ( 74 "{} create --module-version $({} --version) " + 75 "--target-platform android --class-path {} {}" 76 ).format( 77 paths.join(java_runtime.java_home, "bin", "jmod"), 78 paths.join(java_runtime.java_home, "bin", "jlink"), 79 merged_module_jar.path, 80 jmod.path, 81 ), 82 ) 83 84def _gen_system(ctx, java_runtime, jmod, system): 85 ctx.actions.run_shell( 86 inputs = depset([jmod], transitive = [java_runtime.files]), 87 outputs = [system], 88 tools = java_runtime.files, 89 command = ( 90 "rm -rf {} && " + 91 "{} --module-path {} --add-modules java.base --output {} " + 92 "--disable-plugin system-modules && " + 93 "cp {} {}/lib/" 94 ).format( 95 system.path, 96 paths.join(java_runtime.java_home, "bin", "jlink"), 97 jmod.dirname, 98 system.path, 99 paths.join(java_runtime.java_home, "lib", "jrt-fs.jar"), 100 system.path, 101 ), 102 ) 103 104def _java_system_modules_impl(ctx): 105 java_info = java_common.merge([d[JavaInfo] for d in ctx.attr.deps]) 106 module_info = ctx.actions.declare_file("%s/src/module-info.java" % ctx.label.name) 107 _gen_module_info_java(ctx, ctx.executable._jars_to_module_info, java_info.compile_jars.to_list(), module_info) 108 109 java_runtime = ctx.attr._runtime[java_common.JavaRuntimeInfo] 110 module_info_class = ctx.actions.declare_file("%s/class/module-info.class" % ctx.label.name) 111 _gen_module_info_class(ctx, java_runtime, module_info, java_info.compile_jars, module_info_class) 112 113 module_info_jar = ctx.actions.declare_file("%s/jar/classes.jar" % ctx.label.name) 114 _gen_module_info_jar(ctx, ctx.executable._soong_zip, module_info_class, module_info_jar) 115 116 merged_module_jar = ctx.actions.declare_file("%s/merged/module.jar" % ctx.label.name) 117 _gen_merged_module_jar( 118 ctx, 119 ctx.executable._merge_zips, 120 module_info_jar, 121 java_info.full_compile_jars, 122 merged_module_jar, 123 ) 124 125 jmod = ctx.actions.declare_file("%s/jmod/java.base.jmod" % ctx.label.name) 126 _gen_jmod(ctx, java_runtime, merged_module_jar, jmod) 127 128 system = ctx.actions.declare_directory("%s/system" % ctx.label.name) 129 _gen_system(ctx, java_runtime, jmod, system) 130 131 return [ 132 SystemInfo( 133 system = system, 134 java_info = java_info, 135 ), 136 DefaultInfo(files = depset([system])), 137 ] 138 139java_system_modules = rule( 140 implementation = _java_system_modules_impl, 141 attrs = { 142 "_jars_to_module_info": attr.label( 143 allow_files = True, 144 executable = True, 145 cfg = "exec", 146 default = "//build/soong/scripts:jars-to-module-info-java", 147 ), 148 "_soong_zip": attr.label( 149 cfg = "exec", 150 allow_single_file = True, 151 doc = "The tool soong_zip", 152 default = "//build/soong/zip/cmd:soong_zip", 153 executable = True, 154 ), 155 "_merge_zips": attr.label( 156 cfg = "exec", 157 allow_single_file = True, 158 doc = "The tool merge_zips.", 159 default = "//build/soong/cmd/merge_zips", 160 executable = True, 161 ), 162 "_runtime": attr.label( 163 default = Label("@bazel_tools//tools/jdk:current_java_runtime"), 164 cfg = "exec", 165 providers = [java_common.JavaRuntimeInfo], 166 ), 167 "deps": attr.label_list( 168 providers = [JavaInfo], 169 doc = "Libraries to be converted into a system module directory structure.", 170 ), 171 }, 172 doc = """Generates a system module directory from Java libraries. 173 174Starting from version 1.9, Java requires a subset of java.* classes to be 175provided via system modules. This rule encapsulates the set of steps necessary 176to convert a jar file into the directory structure of system modules. 177""", 178) 179