| """ Custom rule to generate OSGi Manifest """ |
| |
| load("@rules_java//java:defs.bzl", "JavaInfo", "java_library") |
| |
| # Note that this rule is currently agnostic of protobuf concerns and could be |
| # pulled out as a general purpose helper to allow migrations from maven to bazel |
| # for OSS release builds. |
| # |
| # There are (at least) 3 things that would nice to fix about this rule: |
| # 1. `deps` are captured by wrapping the java_library target into the |
| # osgi_java_library target -- if possible, it would be better to get |
| # the deps from the JavaInfo or some other provider from any java_library |
| # target. |
| # 2. imports are probably not being calculated properly for deps that are more |
| # than 1 step deep in the dependency chain. For example: //java:core depends |
| # on //java/core:lite_runtime_only but does not calculate the need for |
| # "sun.misc" like the //java/core:lite target does (even though the same code |
| # is transitively included. Those imports can be explicitly added through |
| # `bundle_additional_imports`, but it would be better if the calculation |
| # applied correctly to transitive dependencies. |
| # 3. Versioned imports didn't work properly when an ijar is used as the |
| # "compile_jar". Thus, this rule uses the full jar as the compile_jar, |
| # which is probably sub-optimal. |
| def osgi_java_library( |
| name, |
| automatic_module_name, |
| bundle_description, |
| bundle_doc_url, |
| bundle_license, |
| bundle_name, |
| bundle_symbolic_name, |
| bundle_version, |
| bundle_additional_imports = [], |
| bundle_additional_exports = [], |
| deps = [], |
| exports = [], |
| exported_plugins = [], |
| neverlink = False, |
| runtime_deps = [], |
| visibility = [], |
| **kwargs): |
| """Extends `java_library` to add OSGi headers to the MANIFEST.MF using bndlib |
| |
| This macro should be usable as a drop-in replacement for java_library. |
| |
| The additional arguments are given the bndlib tool to generate an OSGi-compliant manifest file. |
| See [bnd documentation](https://bnd.bndtools.org/chapters/110-introduction.html) |
| |
| Args: |
| name: (required) A unique name for this target. |
| bundle_description: (required) The Bundle-Description header defines a short |
| description of this bundle. |
| bundle_doc_url: (required) The Bundle-DocURL headers must contain a URL pointing |
| to documentation about this bundle. |
| bundle_license: (required) The Bundle-License header provides an optional machine |
| readable form of license information. |
| bundle_name: (required) The Bundle-Name header defines a readable name for this |
| bundle. This should be a short, human-readable name that can |
| contain spaces. |
| bundle_symbolic_name: (required) The Bundle-SymbolicName header specifies a |
| non-localizable name for this bundle. The bundle symbolic name |
| together with a version must identify a unique bundle though it can |
| be installed multiple times in a framework. The bundle symbolic |
| name should be based on the reverse domain name convention. |
| bundle_version: (required) The Bundle-Version header specifies the version string |
| for this bundle. The version string is expected to follow semantic |
| versioning conventions MAJOR.MINOR.PATCH[.BUILD] |
| bundle_additional_exports: The Export-Package header contains a |
| declaration of exported packages. These are additional export |
| package statements to be added before the default wildcard export |
| "*;version={$Bundle-Version}". |
| bundle_additional_imports: The Import-Package header declares the |
| imported packages for this bundle. These are additional import |
| package statements to be added before the default wildcard import |
| "*". |
| deps: The list of libraries to link into this library. See general |
| comments about deps at Typical attributes defined by most build |
| rules. The jars built by java_library rules listed in deps will be |
| on the compile-time classpath of this rule. Furthermore the |
| transitive closure of their deps, runtime_deps and exports will be |
| on the runtime classpath. By contrast, targets in the data |
| attribute are included in the runfiles but on neither the |
| compile-time nor runtime classpath. |
| exports: Exported libraries. |
| exported_plugins: The list of java_plugins (e.g. annotation processors) |
| to export to libraries that directly depend on this library. The |
| specified list of java_plugins will be applied to any library which |
| directly depends on this library, just as if that library had |
| explicitly declared these labels in plugins. |
| neverlink: Whether this library should only be used for compilation and |
| not at runtime. Useful if the library will be provided by the runtime |
| environment during execution. Examples of such libraries are the IDE |
| APIs for IDE plug-ins or tools.jar for anything running on a standard |
| JDK. |
| runtime_deps: Libraries to make available to the final binary or test |
| at runtime only. Like ordinary deps, these will appear on the runtime |
| classpath, but unlike them, not on the compile-time classpath. |
| Dependencies needed only at runtime should be listed here. |
| Dependency-analysis tools should ignore targets that appear in both |
| runtime_deps and deps |
| visibility: The visibility attribute on a target controls whether the |
| target can be used in other packages. See the documentation for |
| visibility. |
| **kwargs: Additional key-word arguments that are passed to the internal |
| java_library target. |
| """ |
| |
| # Build the private jar without the OSGI manifest |
| private_library_name = "%s-no-manifest-do-not-use" % name |
| java_library( |
| name = private_library_name, |
| deps = deps, |
| runtime_deps = runtime_deps, |
| neverlink = True, |
| exported_plugins = exported_plugins, |
| visibility = ["//visibility:private"], |
| **kwargs |
| ) |
| |
| # Repackage the jar with an OSGI manifest |
| _osgi_jar( |
| name = name, |
| automatic_module_name = automatic_module_name, |
| bundle_description = bundle_description, |
| bundle_doc_url = bundle_doc_url, |
| bundle_license = bundle_license, |
| bundle_name = bundle_name, |
| bundle_symbolic_name = bundle_symbolic_name, |
| bundle_version = bundle_version, |
| export_package = bundle_additional_exports + ["*;version=${Bundle-Version}"], |
| import_package = bundle_additional_imports + ["*"], |
| target = private_library_name, |
| deps = deps, |
| runtime_deps = runtime_deps, |
| exported_plugins = exported_plugins, |
| neverlink = neverlink, |
| exports = exports, |
| visibility = visibility, |
| ) |
| |
| def _run_osgi_wrapper(ctx, input_jar, classpath_jars, output_jar): |
| args = ctx.actions.args() |
| args.add_joined("--classpath", classpath_jars, join_with = ":") |
| args.add("--input_jar", input_jar.path) |
| args.add("--output_jar", output_jar.path) |
| args.add("--automatic_module_name", ctx.attr.automatic_module_name) |
| args.add("--bundle_copyright", ctx.attr.bundle_copyright) |
| args.add("--bundle_description", ctx.attr.bundle_description) |
| args.add("--bundle_doc_url", ctx.attr.bundle_doc_url) |
| args.add("--bundle_license", ctx.attr.bundle_license) |
| args.add("--bundle_name", ctx.attr.bundle_name) |
| args.add("--bundle_version", ctx.attr.bundle_version) |
| args.add("--bundle_symbolic_name", ctx.attr.bundle_symbolic_name) |
| args.add_joined("--export_package", ctx.attr.export_package, join_with = ",") |
| args.add_joined("--import_package", ctx.attr.import_package, join_with = ",") |
| |
| ctx.actions.run( |
| inputs = [input_jar] + classpath_jars, |
| executable = ctx.executable._osgi_wrapper_exe, |
| arguments = [args], |
| outputs = [output_jar], |
| progress_message = "Generating OSGi bundle Manifest for %s" % input_jar.path, |
| ) |
| |
| def _osgi_jar_impl(ctx): |
| if len(ctx.attr.target[JavaInfo].java_outputs) != 1: |
| fail("osgi_jar rule can only be used on a single java target.") |
| target_java_output = ctx.attr.target[JavaInfo].java_outputs[0] |
| |
| # source_jars may be a list or a Depset due to: |
| # https://github.com/bazelbuild/bazel/issues/18966 |
| source_jars = target_java_output.source_jars |
| if hasattr(source_jars, "to_list"): |
| source_jars = source_jars.to_list() |
| if len(source_jars) > 1: |
| fail("osgi_jar rule doesn't know how to deal with more than one source jar.") |
| source_jar = source_jars[0] |
| |
| output_jar = ctx.outputs.output_jar |
| |
| input_jar = target_java_output.class_jar |
| classpath_jars = ctx.attr.target[JavaInfo].compilation_info.compilation_classpath.to_list() |
| |
| _run_osgi_wrapper(ctx, input_jar, classpath_jars, output_jar) |
| |
| return [ |
| DefaultInfo( |
| files = depset([output_jar]), |
| # Workaround for https://github.com/bazelbuild/bazel/issues/15043 |
| # Bazel's native rule such as sh_test do not pick up 'files' in |
| # DefaultInfo for a target in 'data'. |
| data_runfiles = ctx.runfiles([output_jar]), |
| ), |
| JavaInfo( |
| output_jar = output_jar, |
| |
| # compile_jar should be an ijar, but using an ijar results in |
| # missing protobuf import version. |
| compile_jar = output_jar, |
| source_jar = source_jar, |
| compile_jdeps = target_java_output.compile_jdeps, |
| generated_class_jar = target_java_output.generated_class_jar, |
| generated_source_jar = target_java_output.generated_source_jar, |
| native_headers_jar = target_java_output.native_headers_jar, |
| manifest_proto = target_java_output.manifest_proto, |
| neverlink = ctx.attr.neverlink, |
| deps = [dep[JavaInfo] for dep in ctx.attr.deps], |
| runtime_deps = [dep[JavaInfo] for dep in ctx.attr.runtime_deps], |
| exports = [exp[JavaInfo] for exp in ctx.attr.exports], |
| exported_plugins = ctx.attr.exported_plugins, |
| jdeps = target_java_output.jdeps, |
| ), |
| ] |
| |
| _osgi_jar = rule( |
| implementation = _osgi_jar_impl, |
| outputs = { |
| "output_jar": "lib%{name}.jar", |
| }, |
| attrs = { |
| "automatic_module_name": attr.string(), |
| "bundle_copyright": attr.string(), |
| "bundle_description": attr.string(), |
| "bundle_doc_url": attr.string(), |
| "bundle_license": attr.string(), |
| "bundle_name": attr.string(), |
| "bundle_version": attr.string(), |
| "bundle_symbolic_name": attr.string(), |
| "export_package": attr.string_list(), |
| "import_package": attr.string_list(), |
| "target": attr.label(), |
| "deps": attr.label_list(), |
| "runtime_deps": attr.label_list(), |
| "exports": attr.label_list(), |
| "neverlink": attr.bool(), |
| "exported_plugins": attr.label_list(), |
| "_osgi_wrapper_exe": attr.label( |
| executable = True, |
| cfg = "exec", |
| allow_files = True, |
| default = Label("//java/osgi:osgi_wrapper"), |
| ), |
| }, |
| ) |