1""" Custom rule to generate OSGi Manifest """ 2 3load("@rules_java//java:defs.bzl", "JavaInfo", "java_library") 4 5# Note that this rule is currently agnostic of protobuf concerns and could be 6# pulled out as a general purpose helper to allow migrations from maven to bazel 7# for OSS release builds. 8# 9# There are (at least) 3 things that would nice to fix about this rule: 10# 1. `deps` are captured by wrapping the java_library target into the 11# osgi_java_library target -- if possible, it would be better to get 12# the deps from the JavaInfo or some other provider from any java_library 13# target. 14# 2. imports are probably not being calculated properly for deps that are more 15# than 1 step deep in the dependency chain. For example: //java:core depends 16# on //java/core:lite_runtime_only but does not calculate the need for 17# "sun.misc" like the //java/core:lite target does (even though the same code 18# is transitively included. Those imports can be explicitly added through 19# `bundle_additional_imports`, but it would be better if the calculation 20# applied correctly to transitive dependencies. 21# 3. Versioned imports didn't work properly when an ijar is used as the 22# "compile_jar". Thus, this rule uses the full jar as the compile_jar, 23# which is probably sub-optimal. 24def osgi_java_library( 25 name, 26 automatic_module_name, 27 bundle_description, 28 bundle_doc_url, 29 bundle_license, 30 bundle_name, 31 bundle_symbolic_name, 32 bundle_version, 33 bundle_additional_imports = [], 34 bundle_additional_exports = [], 35 deps = [], 36 exports = [], 37 exported_plugins = [], 38 neverlink = False, 39 runtime_deps = [], 40 visibility = [], 41 **kwargs): 42 """Extends `java_library` to add OSGi headers to the MANIFEST.MF using bndlib 43 44 This macro should be usable as a drop-in replacement for java_library. 45 46 The additional arguments are given the bndlib tool to generate an OSGi-compliant manifest file. 47 See [bnd documentation](https://bnd.bndtools.org/chapters/110-introduction.html) 48 49 Args: 50 name: (required) A unique name for this target. 51 bundle_description: (required) The Bundle-Description header defines a short 52 description of this bundle. 53 bundle_doc_url: (required) The Bundle-DocURL headers must contain a URL pointing 54 to documentation about this bundle. 55 bundle_license: (required) The Bundle-License header provides an optional machine 56 readable form of license information. 57 bundle_name: (required) The Bundle-Name header defines a readable name for this 58 bundle. This should be a short, human-readable name that can 59 contain spaces. 60 bundle_symbolic_name: (required) The Bundle-SymbolicName header specifies a 61 non-localizable name for this bundle. The bundle symbolic name 62 together with a version must identify a unique bundle though it can 63 be installed multiple times in a framework. The bundle symbolic 64 name should be based on the reverse domain name convention. 65 bundle_version: (required) The Bundle-Version header specifies the version string 66 for this bundle. The version string is expected to follow semantic 67 versioning conventions MAJOR.MINOR.PATCH[.BUILD] 68 bundle_additional_exports: The Export-Package header contains a 69 declaration of exported packages. These are additional export 70 package statements to be added before the default wildcard export 71 "*;version={$Bundle-Version}". 72 bundle_additional_imports: The Import-Package header declares the 73 imported packages for this bundle. These are additional import 74 package statements to be added before the default wildcard import 75 "*". 76 deps: The list of libraries to link into this library. See general 77 comments about deps at Typical attributes defined by most build 78 rules. The jars built by java_library rules listed in deps will be 79 on the compile-time classpath of this rule. Furthermore the 80 transitive closure of their deps, runtime_deps and exports will be 81 on the runtime classpath. By contrast, targets in the data 82 attribute are included in the runfiles but on neither the 83 compile-time nor runtime classpath. 84 exports: Exported libraries. 85 exported_plugins: The list of java_plugins (e.g. annotation processors) 86 to export to libraries that directly depend on this library. The 87 specified list of java_plugins will be applied to any library which 88 directly depends on this library, just as if that library had 89 explicitly declared these labels in plugins. 90 neverlink: Whether this library should only be used for compilation and 91 not at runtime. Useful if the library will be provided by the runtime 92 environment during execution. Examples of such libraries are the IDE 93 APIs for IDE plug-ins or tools.jar for anything running on a standard 94 JDK. 95 runtime_deps: Libraries to make available to the final binary or test 96 at runtime only. Like ordinary deps, these will appear on the runtime 97 classpath, but unlike them, not on the compile-time classpath. 98 Dependencies needed only at runtime should be listed here. 99 Dependency-analysis tools should ignore targets that appear in both 100 runtime_deps and deps 101 visibility: The visibility attribute on a target controls whether the 102 target can be used in other packages. See the documentation for 103 visibility. 104 **kwargs: Additional key-word arguments that are passed to the internal 105 java_library target. 106 """ 107 108 # Build the private jar without the OSGI manifest 109 private_library_name = "%s-no-manifest-do-not-use" % name 110 java_library( 111 name = private_library_name, 112 deps = deps, 113 runtime_deps = runtime_deps, 114 neverlink = True, 115 exported_plugins = exported_plugins, 116 visibility = ["//visibility:private"], 117 **kwargs 118 ) 119 120 # Repackage the jar with an OSGI manifest 121 _osgi_jar( 122 name = name, 123 automatic_module_name = automatic_module_name, 124 bundle_description = bundle_description, 125 bundle_doc_url = bundle_doc_url, 126 bundle_license = bundle_license, 127 bundle_name = bundle_name, 128 bundle_symbolic_name = bundle_symbolic_name, 129 bundle_version = bundle_version, 130 export_package = bundle_additional_exports + ["*;version=${Bundle-Version}"], 131 import_package = bundle_additional_imports + ["*"], 132 target = private_library_name, 133 deps = deps, 134 runtime_deps = runtime_deps, 135 exported_plugins = exported_plugins, 136 neverlink = neverlink, 137 exports = exports, 138 visibility = visibility, 139 ) 140 141def _run_osgi_wrapper(ctx, input_jar, classpath_jars, output_jar): 142 args = ctx.actions.args() 143 args.add_joined("--classpath", classpath_jars, join_with = ":") 144 args.add("--input_jar", input_jar.path) 145 args.add("--output_jar", output_jar.path) 146 args.add("--automatic_module_name", ctx.attr.automatic_module_name) 147 args.add("--bundle_copyright", ctx.attr.bundle_copyright) 148 args.add("--bundle_description", ctx.attr.bundle_description) 149 args.add("--bundle_doc_url", ctx.attr.bundle_doc_url) 150 args.add("--bundle_license", ctx.attr.bundle_license) 151 args.add("--bundle_name", ctx.attr.bundle_name) 152 args.add("--bundle_version", ctx.attr.bundle_version) 153 args.add("--bundle_symbolic_name", ctx.attr.bundle_symbolic_name) 154 args.add_joined("--export_package", ctx.attr.export_package, join_with = ",") 155 args.add_joined("--import_package", ctx.attr.import_package, join_with = ",") 156 157 ctx.actions.run( 158 inputs = [input_jar] + classpath_jars, 159 executable = ctx.executable._osgi_wrapper_exe, 160 arguments = [args], 161 outputs = [output_jar], 162 progress_message = "Generating OSGi bundle Manifest for %s" % input_jar.path, 163 ) 164 165def _osgi_jar_impl(ctx): 166 if len(ctx.attr.target[JavaInfo].java_outputs) != 1: 167 fail("osgi_jar rule can only be used on a single java target.") 168 target_java_output = ctx.attr.target[JavaInfo].java_outputs[0] 169 170 # source_jars may be a list or a Depset due to: 171 # https://github.com/bazelbuild/bazel/issues/18966 172 source_jars = target_java_output.source_jars 173 if hasattr(source_jars, "to_list"): 174 source_jars = source_jars.to_list() 175 if len(source_jars) > 1: 176 fail("osgi_jar rule doesn't know how to deal with more than one source jar.") 177 source_jar = source_jars[0] 178 179 output_jar = ctx.outputs.output_jar 180 181 input_jar = target_java_output.class_jar 182 classpath_jars = ctx.attr.target[JavaInfo].compilation_info.compilation_classpath.to_list() 183 184 _run_osgi_wrapper(ctx, input_jar, classpath_jars, output_jar) 185 186 return [ 187 DefaultInfo( 188 files = depset([output_jar]), 189 # Workaround for https://github.com/bazelbuild/bazel/issues/15043 190 # Bazel's native rule such as sh_test do not pick up 'files' in 191 # DefaultInfo for a target in 'data'. 192 data_runfiles = ctx.runfiles([output_jar]), 193 ), 194 JavaInfo( 195 output_jar = output_jar, 196 197 # compile_jar should be an ijar, but using an ijar results in 198 # missing protobuf import version. 199 compile_jar = output_jar, 200 source_jar = source_jar, 201 compile_jdeps = target_java_output.compile_jdeps, 202 generated_class_jar = target_java_output.generated_class_jar, 203 generated_source_jar = target_java_output.generated_source_jar, 204 native_headers_jar = target_java_output.native_headers_jar, 205 manifest_proto = target_java_output.manifest_proto, 206 neverlink = ctx.attr.neverlink, 207 deps = [dep[JavaInfo] for dep in ctx.attr.deps], 208 runtime_deps = [dep[JavaInfo] for dep in ctx.attr.runtime_deps], 209 exports = [exp[JavaInfo] for exp in ctx.attr.exports], 210 exported_plugins = ctx.attr.exported_plugins, 211 jdeps = target_java_output.jdeps, 212 ), 213 ] 214 215_osgi_jar = rule( 216 implementation = _osgi_jar_impl, 217 outputs = { 218 "output_jar": "lib%{name}.jar", 219 }, 220 attrs = { 221 "automatic_module_name": attr.string(), 222 "bundle_copyright": attr.string(), 223 "bundle_description": attr.string(), 224 "bundle_doc_url": attr.string(), 225 "bundle_license": attr.string(), 226 "bundle_name": attr.string(), 227 "bundle_version": attr.string(), 228 "bundle_symbolic_name": attr.string(), 229 "export_package": attr.string_list(), 230 "import_package": attr.string_list(), 231 "target": attr.label(), 232 "deps": attr.label_list(), 233 "runtime_deps": attr.label_list(), 234 "exports": attr.label_list(), 235 "neverlink": attr.bool(), 236 "exported_plugins": attr.label_list(), 237 "_osgi_wrapper_exe": attr.label( 238 executable = True, 239 cfg = "exec", 240 allow_files = True, 241 default = Label("//java/osgi:osgi_wrapper"), 242 ), 243 }, 244) 245