rules_jvm_export/jvm_export/jvm_export.bzl (233 lines of code) (raw):

# Based on https://github.com/vaticle/bazel-distribution/blob/master/maven/rules.bzl # SPDX-License-Identifier: Apache-2.0 load(":jvm_assembly.bzl", "aggregate_dependency_info", "JarInfo", _jvm_assembly="jvm_assembly", "parse_maven_coordinates", "runtime_output_jar", "VersionInfo", ) JvmExportInfo = provider( fields={ "artifacts": "JAR file to deploy", "pom": "Accompanying pom.xml file", } ) SOURCES_CLASSIFIER = "sources" JAVADOC_CLASSIFIER = "javadoc" def _jvm_export_impl(ctx): version = ctx.attr.version[VersionInfo].value pom_file = _generate_pom_file(ctx, version) output_files = [pom_file] pom_xml_link = pom_file.basename symlinks = { pom_xml_link: pom_file, } source_jar = None # create a dictionary of class_jar symlinks and classifier artifacts = {} for artifact in ctx.attr.artifacts: this_coordinate = parse_maven_coordinates(artifact[JarInfo].name) classifier = getattr(this_coordinate, "classifier", "") class_jar = runtime_output_jar(artifact) output_files.append(class_jar) symlinks[class_jar.basename] = class_jar artifacts[classifier] = class_jar.basename if not classifier: source_jar = _source_jar(artifact) if source_jar: output_files.append(source_jar) src_jar_link = "lib.srcjar" symlinks[src_jar_link] = source_jar artifacts[SOURCES_CLASSIFIER] = src_jar_link # TODO(vmax): use real Javadoc instead of srcjar if (SOURCES_CLASSIFIER in artifacts) and (JAVADOC_CLASSIFIER not in artifacts): artifacts[JAVADOC_CLASSIFIER] = artifacts[SOURCES_CLASSIFIER] deploy_script = ctx.actions.declare_file( "jvm-export/{}-deploy.py".format(ctx.attr.name) ) pom_xml_link = pom_file.basename ctx.actions.expand_template( template=ctx.file._deployment, output=deploy_script, substitutions={ "$ARTIFACTS": str(artifacts), "$POM_PATH": pom_xml_link, "$PYTHON_PATH": ctx.attr.python_path, "{snapshot}": ctx.attr.snapshot_repo, "{release}": ctx.attr.release_repo, }, ) return [ DefaultInfo( executable=deploy_script, files=depset(output_files), runfiles=ctx.runfiles(files=output_files, symlinks=symlinks), ), JvmExportInfo( artifacts=artifacts, pom=pom_file, ), ] def _generate_pom_file(ctx, version): artifacts = ctx.attr.artifacts for artifact in artifacts: if not artifact[JarInfo].name: fail("{} missing 'maven_coordinates=' tags".format(artifact)) pom_file = ctx.actions.declare_file("{}_pom.xml".format(ctx.attr.name)) maven_coordinates0 = parse_maven_coordinates(artifacts[0][JarInfo].name) pom_deps = [] # Note that all dependencies in all artifacts will be appended to # the same POM as dependencies. for artifact in artifacts: this_coordinate = parse_maven_coordinates(artifact[JarInfo].name) for pom_dependency in [ dep[JarInfo] for dep in artifact[JarInfo].deps if dep[JarInfo].name ]: pom_dependency_coordinates = parse_maven_coordinates(pom_dependency.name, False) if pom_dependency == this_coordinate: continue pom_dependency_artifact = ( pom_dependency_coordinates.group_id + ":" + pom_dependency_coordinates.artifact_id ) pom_dependency_version = pom_dependency_coordinates.version pom_dependency_classifier = getattr(pom_dependency_coordinates, "classifier", "") pom_dependency_packaging = getattr(pom_dependency_coordinates, "packaging", "jar") pom_dependency_neverlink = getattr(pom_dependency, "neverlink", False) if pom_dependency_neverlink: pom_dependency_classifier = "provided" v = ctx.attr.version_overrides.get( pom_dependency_artifact, pom_dependency_version ) pom_dep = "{}:{}".format(pom_dependency_artifact, v) if pom_dependency_classifier: pom_dep = "{}:{}:{}:{}".format( pom_dependency_artifact, pom_dependency_packaging, pom_dependency_classifier, v) pom_deps.append(pom_dep) pom_gen_script = ctx.actions.declare_file( "jvm-export/{}-pom-gen.py".format(ctx.attr.name) ) ctx.actions.expand_template( template=ctx.file._pom_generator, output=pom_gen_script, substitutions={ "$PYTHON_PATH": ctx.attr.python_path, }, ) ctx.actions.run( executable=pom_gen_script, inputs=[], outputs=[pom_file], arguments=[ "--group_id=" + maven_coordinates0.group_id, "--artifact_id=" + maven_coordinates0.artifact_id, "--version=" + version, "--project_name=" + ctx.attr.project_name, "--project_description=" + ctx.attr.project_description, "--project_url=" + ctx.attr.project_url, "--license=" + ctx.attr.license, "--scm_url=" + ctx.attr.scm_url, "--target_deps_coordinates=" + ";".join(pom_deps), "--output_file=" + pom_file.path, ], ) return pom_file def _source_jar(target): if JavaInfo in target and target[JavaInfo].source_jars: return target[JavaInfo].source_jars[0] else: return None def make_jvm_export_rule(): return rule( attrs={ "artifacts": attr.label_list( mandatory=True, doc="Java target for subsequent deployment", aspects=[ aggregate_dependency_info, ], # providers=[JavaInfo], ), "version": attr.label( doc=""" A target that represents version string. Alternatively, pass --@twitter_rules_jvm_export//jvm_export:version=0.1.0 to Bazel invocation. """, default="@twitter_rules_jvm_export//jvm_export:version", providers=[VersionInfo], ), "version_overrides": attr.string_dict( default={}, doc="Dictionary of maven artifact : version to pin artifact versions to", ), "project_name": attr.string( mandatory=True, doc="Project name to fill into pom.xml", ), "project_description": attr.string( default="PROJECT_DESCRIPTION", doc="Project description to fill into pom.xml", ), "project_url": attr.string( default="PROJECT_URL", doc="Project URL to fill into pom.xml", ), "license": attr.string( values=["Apache-2.0", "MIT"], default="Apache-2.0", doc="Project license to fill into pom.xml", ), "scm_url": attr.string( default="PROJECT_URL", doc="Project source control URL to fill into pom.xml", ), "snapshot_repo": attr.string( mandatory=True, doc="Snapshot repository to release maven artifacts", ), "release_repo": attr.string( mandatory=True, doc="Release repository to release maven artifacts", ), "python_path": attr.string( default="/usr/bin/env python", doc="Path to python command", ), "_pom_generator": attr.label( default="@twitter_rules_jvm_export//jvm_export/support:pom_generator.py", executable=True, allow_single_file=True, cfg="host", ), "_deployment": attr.label( allow_single_file=True, default="@twitter_rules_jvm_export//jvm_export/support:deploy.py", ), }, executable=True, implementation=_jvm_export_impl, doc="Publish Java package to a Maven repo", ) jvm_export = make_jvm_export_rule() def _jvm_export_version_impl(ctx): value = ctx.build_setting_value return VersionInfo(value=value) _jvm_export_version = rule( implementation=_jvm_export_version_impl, build_setting=config.string(flag=True), doc="Version used to publish JVM artifacts", ) def jvm_export_version(name, version): return _jvm_export_version( name=name, build_setting_default=version, visibility=["//visibility:public"], ) jvm_assembly = _jvm_assembly