diff --git a/MODULE.bazel b/MODULE.bazel index 389809673..48e0d5732 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -45,6 +45,12 @@ single_version_override( version = "8.16.1", ) +bazel_dep(name = "rules_jvm_external", version = "6.9") +single_version_override( + module_name = "rules_jvm_external", + version = "6.9", +) + bazel_dep(name = "rules_proto", version = "6.0.0") single_version_override( module_name = "rules_proto", diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index e5307e400..4d21edbba 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -18,8 +18,6 @@ "https://bcr.bazel.build/modules/bazel_features/1.1.0/MODULE.bazel": "cfd42ff3b815a5f39554d97182657f8c4b9719568eb7fded2b9135f084bf760b", "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", - "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", - "https://bcr.bazel.build/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d", "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a", "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b", @@ -53,6 +51,7 @@ "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74", "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/source.json": "f448c6e8963fdfa7eb831457df83ad63d3d6355018f6574fb017e8169deb43a9", + "https://bcr.bazel.build/modules/package_metadata/0.0.3/MODULE.bazel": "77890552ecea9e284b5424c9de827a58099348763a4359e975c359a83d4faa83", "https://bcr.bazel.build/modules/package_metadata/0.0.5/MODULE.bazel": "ef4f9439e3270fdd6b9fd4dbc3d2f29d13888e44c529a1b243f7a31dfbc2e8e4", "https://bcr.bazel.build/modules/package_metadata/0.0.5/source.json": "2326db2f6592578177751c3e1f74786b79382cd6008834c9d01ec865b9126a85", "https://bcr.bazel.build/modules/platforms/1.0.0/MODULE.bazel": "f05feb42b48f1b3c225e4ccf351f367be0371411a803198ec34a389fb22aa580", @@ -92,9 +91,8 @@ "https://bcr.bazel.build/modules/rules_go/0.58.2/source.json": "90bb77dd3105fc3ac26d961a7d7969408c1a4952b0226ecb3fbe76d576b1a077", "https://bcr.bazel.build/modules/rules_java/8.16.1/MODULE.bazel": "0f20b1cecaa8e52f60a8f071e59a20b4e3b9a67f6c56c802ea256f6face692d3", "https://bcr.bazel.build/modules/rules_java/8.16.1/source.json": "072f8d11264edc499621be2dc9ea01d6395db5aa6f8799c034ae01a3e857f2e4", - "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0", - "https://bcr.bazel.build/modules/rules_jvm_external/6.7/MODULE.bazel": "e717beabc4d091ecb2c803c2d341b88590e9116b8bf7947915eeb33aab4f96dd", - "https://bcr.bazel.build/modules/rules_jvm_external/6.7/source.json": "5426f412d0a7fc6b611643376c7e4a82dec991491b9ce5cb1cfdd25fe2e92be4", + "https://bcr.bazel.build/modules/rules_jvm_external/6.9/MODULE.bazel": "07c5db05527db7744a54fcffd653e1550d40e0540207a7f7e6d0a4de5bef8274", + "https://bcr.bazel.build/modules/rules_jvm_external/6.9/source.json": "b12970214f3cc144b26610caeb101fa622d910f1ab3d98f0bae1058edbd00bd4", "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3", "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5", "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", diff --git a/scala/private/rules/scala_export.bzl b/scala/private/rules/scala_export.bzl new file mode 100644 index 000000000..d70d1be6b --- /dev/null +++ b/scala/private/rules/scala_export.bzl @@ -0,0 +1,132 @@ +load("@bazel_skylib//rules:run_binary.bzl", "run_binary") +load("@rules_jvm_external//:defs.bzl", "create_jar", "maven_export") +load("//scala/private:rules/scala_doc.bzl", "make_scala_doc_rule", "scaladoc_intransitive_aspect") +load("//scala/private:rules/scala_library.bzl", "scala_library") + +DEFAULT_EXCLUDED_WORKSPACES = [ + # Note: we choose to drop the dependency entirely because + # we can't be sure which coordinate the user has + # chosen for protobuf. + "com_google_protobuf", + "protobuf", # bzlmod module deps are in the form of '@protobuf~' +] + +scala_doc = make_scala_doc_rule(aspect = scaladoc_intransitive_aspect) + +SCALA_LIBS = [] + +def scala_export( + name, + maven_coordinates, + deploy_env = [], + excluded_workspaces = {name: None for name in DEFAULT_EXCLUDED_WORKSPACES}, + exclusions = {}, + pom_template = None, + visibility = None, + tags = [], + testonly = None, + publish_maven_metadata = False, + **kwargs): + """Extends `scala_library` to allow maven artifacts to be uploaded. This + rule is the Scala version of `java_export`. + + This macro can be used as a drop-in replacement for `scala_library`, but + also generates an implicit `name.publish` target that can be run to publish + maven artifacts derived from this macro to a maven repository. The publish + rule understands the following variables (declared using `--define` when + using `bazel run`): + + * `maven_repo`: A URL for the repo to use. May be "https" or "file". + * `maven_user`: The user name to use when uploading to the maven repository. + * `maven_password`: The password to use when uploading to the maven repository. + + This macro also generates a `name-pom` target that creates the `pom.xml` file + associated with the artifacts. The template used is derived from the (optional) + `pom_template` argument, and the following substitutions are performed on + the template file: + + * `{groupId}`: Replaced with the maven coordinates group ID. + * `{artifactId}`: Replaced with the maven coordinates artifact ID. + * `{version}`: Replaced by the maven coordinates version. + * `{type}`: Replaced by the maven coordintes type, if present (defaults to "jar") + * `{dependencies}`: Replaced by a list of maven dependencies directly relied upon + by scala_library targets within the artifact. + + The "edges" of the artifact are found by scanning targets that contribute to + runtime dependencies for the following tags: + + * `maven_coordinates=group:artifact:type:version`: Specifies a dependency of + this artifact. + * `maven:compile-only`: Specifies that this dependency should not be listed + as a dependency of the artifact being generated. + + To skip generation of the javadoc jar, add the `no-javadocs` tag to the target. + To skip generation of the scaladoc jar, add the `no-scaladocs` tag to the target. + + Generated rules: + * `name`: A `scala_library` that other rules can depend upon. + * `name-docs`: A javadoc jar file. + * `name-scaladocs`: A scaladoc jar file. + * `name-pom`: The pom.xml file. + * `name.publish`: To be executed by `bazel run` to publish to a maven repo. + + Args: + name: A unique name for this target + maven_coordinates: The maven coordinates for this target. + pom_template: The template to be used for the pom.xml file. + deploy_env: A list of labels of java targets to exclude from the generated jar + visibility: The visibility of the target + publish_maven_metadata: Whether to publish a maven-metadata.xml + kwargs: These are passed to [`scala_library`](https://github.com/bazelbuild/rules_scala/blob/master/docs/scala_library.md), + and so may contain any valid parameter for that rule. + """ + maven_coordinates_tags = ["maven_coordinates=%s" % maven_coordinates] + lib_name = "%s-lib" % name + + javadocopts = kwargs.pop("javadocopts", None) + doc_resources = kwargs.pop("doc_resources", []) + classifier_artifacts = kwargs.pop("classifier_artifacts", {}) + + updated_deploy_env = [] + deploy_env + for lib in SCALA_LIBS: + if lib not in deploy_env: + updated_deploy_env.append(lib) + + scala_library( + name = lib_name, + tags = tags + maven_coordinates_tags, + testonly = testonly, + **kwargs + ) + + if "no-scaladocs" not in tags: + scaladocs_name = name + "-scaladocs-html" + scala_doc( + name = scaladocs_name, + deps = [":" + lib_name], + ) + + scaladocs_jar_name = name + "-scaladocs" + create_jar( + name = scaladocs_jar_name, + inputs = [":" + scaladocs_name] + doc_resources, + out = name + "-scaladocs.jar", + ) + classifier_artifacts["scaladoc"] = scaladocs_jar_name + + maven_export( + name = name, + maven_coordinates = maven_coordinates, + classifier_artifacts = classifier_artifacts, + lib_name = lib_name, + deploy_env = updated_deploy_env, + excluded_workspaces = excluded_workspaces, + exclusions = exclusions, + pom_template = pom_template, + visibility = visibility, + tags = tags, + testonly = testonly, + javadocopts = javadocopts, + doc_resources = doc_resources, + publish_maven_metadata = publish_maven_metadata, + ) diff --git a/scala/scala.bzl b/scala/scala.bzl index 162ff3b60..8e3558f4a 100644 --- a/scala/scala.bzl +++ b/scala/scala.bzl @@ -12,6 +12,10 @@ load( _make_scala_doc_rule = "make_scala_doc_rule", _scaladoc_intransitive_aspect = "scaladoc_intransitive_aspect", ) +load( + "//scala/private:rules/scala_export.bzl", + _scala_export = "scala_export", +) load( "//scala/private:rules/scala_junit_test.bzl", _scala_junit_test = "scala_junit_test", @@ -58,6 +62,7 @@ make_scala_doc_rule = _make_scala_doc_rule scaladoc_intransitive_aspect = _scaladoc_intransitive_aspect scala_doc = _make_scala_doc_rule() ScaladocAspectInfo = _ScaladocAspectInfo +scala_export = _scala_export scala_junit_test = _scala_junit_test scala_library = _scala_library scala_library_for_plugin_bootstrapping = _scala_library_for_plugin_bootstrapping diff --git a/test/scala_export/BUILD b/test/scala_export/BUILD new file mode 100644 index 000000000..9d6ffe5c4 --- /dev/null +++ b/test/scala_export/BUILD @@ -0,0 +1,78 @@ +load("@bazel_skylib//rules:diff_test.bzl", "diff_test") +load("@rules_scala//scala:scala.bzl", "scala_export", "scala_library") + +scala_library( + name = "deploy_env_dep", + srcs = ["DeployEnvDependency.scala"], + deps = [], +) + +scala_library( + name = "dep", + srcs = ["Dependency.scala"], +) + +scala_export( + name = "external_dep", + srcs = ["ExternalDependency.scala"], + maven_coordinates = "com.example:external:1.0.0", +) + +scala_export( + name = "test", + srcs = [ + "Main.scala", + ], + deploy_env = [ + ":deploy_env_dep", + ], + maven_coordinates = "com.example:scala:1.0.0", + unused_dependency_checker_mode = "off", + deps = [ + ":dep", + ":external_dep", + ], +) + +genrule( + name = "list-classes", + srcs = [ + ":test-project", + ], + outs = ["classes.txt"], + cmd = "for SRC in $(SRCS); do jar tf $$SRC >> $@; done", +) + +sh_test( + name = "check-deploy-env", + srcs = [ + "check-deploy-env.sh", + ], + data = [ + ":classes.txt", + ], + deps = [ + "@bazel_tools//tools/bash/runfiles", + ], +) + +diff_test( + name = "validate-pom", + file1 = ":test-pom", + file2 = "pom.golden.xml", +) + +genrule( + name = "list-scaladocs", + srcs = [ + ":test-scaladocs", + ], + outs = ["scaladocs.txt"], + cmd = "for SRC in $(SRCS); do jar tf $$SRC >> $@; done", +) + +diff_test( + name = "validate-scaladocs", + file1 = ":scaladocs.txt", + file2 = "scaladocs.golden.txt", +) diff --git a/test/scala_export/Dependency.scala b/test/scala_export/Dependency.scala new file mode 100644 index 000000000..9cedafe52 --- /dev/null +++ b/test/scala_export/Dependency.scala @@ -0,0 +1,7 @@ +package scalarules.test.scala_export + +class Dependency { + def getName(): String = { + "scala_export test" + } +} \ No newline at end of file diff --git a/test/scala_export/DeployEnvDependency.scala b/test/scala_export/DeployEnvDependency.scala new file mode 100644 index 000000000..3d9504893 --- /dev/null +++ b/test/scala_export/DeployEnvDependency.scala @@ -0,0 +1,7 @@ +package scalarules.test.scala_export + +class DeployEnvDependency { + def getName(): String = { + "scala_export test" + } +} \ No newline at end of file diff --git a/test/scala_export/ExternalDependency.scala b/test/scala_export/ExternalDependency.scala new file mode 100644 index 000000000..9d9bac375 --- /dev/null +++ b/test/scala_export/ExternalDependency.scala @@ -0,0 +1,7 @@ +package scalarules.test.scala_export + +class ExternalDependency { + def getName(): String = { + "scala_export test" + } +} \ No newline at end of file diff --git a/test/scala_export/Main.scala b/test/scala_export/Main.scala new file mode 100644 index 000000000..97ecad3b7 --- /dev/null +++ b/test/scala_export/Main.scala @@ -0,0 +1,7 @@ +package scalarules.test.scala_export + +object Main { + def main(args: Array[String]): Unit = { + println("Hello, world") + } +} \ No newline at end of file diff --git a/test/scala_export/check-deploy-env.sh b/test/scala_export/check-deploy-env.sh new file mode 100755 index 000000000..d434eaf0d --- /dev/null +++ b/test/scala_export/check-deploy-env.sh @@ -0,0 +1,29 @@ +# --- begin runfiles.bash initialization v2 --- +# Copy-pasted from the Bazel Bash runfiles library v2. +set -uo pipefail; f=bazel_tools/tools/bash/runfiles/runfiles.bash +source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ + source "$0.runfiles/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e +# --- end runfiles.bash initialization v2 --- + +set -euox pipefail + +classes_file=$(rlocation rules_scala/test/scala_export/classes.txt) + +if grep -q DeployEnvDependency.class "$classes_file"; then + echo "Unexpectedly found DeployEnvDependency class in jar" + exit 1 +fi + +if ! grep -q Dependency.class "$classes_file"; then + echo "Missing Dependency class from jar" + exit 1 +fi + +if ! grep -q Main.class "$classes_file"; then + echo "Missing Main class from jar" + exit 1 +fi diff --git a/test/scala_export/pom.golden.xml b/test/scala_export/pom.golden.xml new file mode 100644 index 000000000..f75dff43f --- /dev/null +++ b/test/scala_export/pom.golden.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + com.example + scala + 1.0.0 + + + + com.example + external + 1.0.0 + runtime + + + diff --git a/test/scala_export/scaladocs.golden.txt b/test/scala_export/scaladocs.golden.txt new file mode 100644 index 000000000..7d2b00f54 --- /dev/null +++ b/test/scala_export/scaladocs.golden.txt @@ -0,0 +1,65 @@ +index.html +index.js +lib/ +lib/MaterialIcons-Regular.eot +lib/MaterialIcons-Regular.ttf +lib/MaterialIcons-Regular.woff +lib/abstract_type.svg +lib/class.svg +lib/class_comp.svg +lib/class_diagram.png +lib/diagrams.css +lib/diagrams.js +lib/index.css +lib/index.js +lib/jquery.min.js +lib/jquery.mousewheel.min.js +lib/jquery.panzoom.min.js +lib/lato-v11-latin-100.eot +lib/lato-v11-latin-100.ttf +lib/lato-v11-latin-100.woff +lib/lato-v11-latin-regular.eot +lib/lato-v11-latin-regular.ttf +lib/lato-v11-latin-regular.woff +lib/modernizr.custom.js +lib/object.svg +lib/object_comp.svg +lib/object_comp_trait.svg +lib/object_diagram.png +lib/open-sans-v13-latin-400i.eot +lib/open-sans-v13-latin-400i.ttf +lib/open-sans-v13-latin-400i.woff +lib/open-sans-v13-latin-700.eot +lib/open-sans-v13-latin-700.ttf +lib/open-sans-v13-latin-700.woff +lib/open-sans-v13-latin-700i.eot +lib/open-sans-v13-latin-700i.ttf +lib/open-sans-v13-latin-700i.woff +lib/open-sans-v13-latin-regular.eot +lib/open-sans-v13-latin-regular.ttf +lib/open-sans-v13-latin-regular.woff +lib/ownderbg2.gif +lib/ownerbg.gif +lib/ownerbg2.gif +lib/package.svg +lib/ref-index.css +lib/scheduler.js +lib/source-code-pro-v6-latin-700.eot +lib/source-code-pro-v6-latin-700.ttf +lib/source-code-pro-v6-latin-700.woff +lib/source-code-pro-v6-latin-regular.eot +lib/source-code-pro-v6-latin-regular.ttf +lib/source-code-pro-v6-latin-regular.woff +lib/template.css +lib/template.js +lib/trait.svg +lib/trait_comp.svg +lib/trait_diagram.png +lib/type_diagram.png +scalarules/ +scalarules/index.html +scalarules/test/ +scalarules/test/index.html +scalarules/test/scala_export/ +scalarules/test/scala_export/Main$.html +scalarules/test/scala_export/index.html