diff --git a/scala/private/macros/bzlmod.bzl b/scala/private/macros/bzlmod.bzl new file mode 100644 index 000000000..db8e8385f --- /dev/null +++ b/scala/private/macros/bzlmod.bzl @@ -0,0 +1,21 @@ +"""Utilities for working with Bazel modules""" + +def apparent_repo_name(repository_ctx): + """Generates a repository's apparent name from a repository_ctx object. + + Args: + repository_ctx: a repository_ctx object + + Returns: + An apparent repo name derived from repository_ctx.name + """ + repo_name = repository_ctx.name + + # Based on this pattern from the Bazel source: + # com.google.devtools.build.lib.cmdline.RepositoryName.VALID_REPO_NAME + for i in range(len(repo_name) - 1, -1, -1): + c = repo_name[i] + if not (c.isalnum() or c in "_-."): + return repo_name[i + 1:] + + return repo_name diff --git a/scala/private/phases/phase_dependency.bzl b/scala/private/phases/phase_dependency.bzl index d8ae91e7e..ccf629270 100644 --- a/scala/private/phases/phase_dependency.bzl +++ b/scala/private/phases/phase_dependency.bzl @@ -1,13 +1,13 @@ # Gathers information about dependency mode and analysis load( - "@io_bazel_rules_scala//scala/private:dependency.bzl", + "//scala/private:dependency.bzl", "get_compiler_deps_mode", "get_strict_deps_mode", "new_dependency_info", ) load( - "@io_bazel_rules_scala//scala/private:paths.bzl", + "//scala/private:paths.bzl", _get_files_with_extension = "get_files_with_extension", _java_extension = "java_extension", ) @@ -35,7 +35,7 @@ def _phase_dependency( p, unused_deps_always_off, strict_deps_always_off): - toolchain = ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"] + toolchain = ctx.toolchains[Label("//scala:toolchain_type")] target_label = str(ctx.label) diff --git a/scala/private/resources.bzl b/scala/private/resources.bzl index 1db14a822..eaaf12bcb 100644 --- a/scala/private/resources.bzl +++ b/scala/private/resources.bzl @@ -44,6 +44,13 @@ def _target_path_by_default_prefixes(resource): if rel_path: return rel_path + # Looking inside an external repository. Trim off both the "external/" and + # the repository name components. Especially important under Bzlmod, because + # the canonical repository name may change between versions. + (dir_1, dir_2, rel_path) = path.partition("external/") + if rel_path: + return rel_path[rel_path.index("/"):] + # Both short_path and path have quirks we wish to avoid, in short_path there are times where # it is prefixed by `../` instead of `external/`. And in .path it will instead return the entire # bazel-out/... path, which is also wanting to be avoided. So instead, we return the short-path if diff --git a/scala/scala_maven_import_external.bzl b/scala/scala_maven_import_external.bzl index e69388ced..1a7bf8145 100644 --- a/scala/scala_maven_import_external.bzl +++ b/scala/scala_maven_import_external.bzl @@ -35,6 +35,7 @@ the following macros are defined below that utilize jvm_import_external: - java_import_external - to demonstrate that the original functionality of `java_import_external` stayed intact. """ +load("//scala/private:macros/bzlmod.bzl", "apparent_repo_name") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "read_netrc", "read_user_netrc", "use_netrc") # https://github.com/bazelbuild/bazel/issues/13709#issuecomment-1336699672 @@ -64,19 +65,20 @@ def _jvm_import_external(repository_ctx): if (repository_ctx.attr.generated_linkable_rule_name and not repository_ctx.attr.neverlink): fail("Only use generated_linkable_rule_name if neverlink is set") - name = repository_ctx.attr.generated_rule_name or repository_ctx.name + repo_name = apparent_repo_name(repository_ctx) + name = repository_ctx.attr.generated_rule_name or repo_name urls = repository_ctx.attr.jar_urls if repository_ctx.attr.jar_sha256: print("'jar_sha256' is deprecated. Please use 'artifact_sha256'") sha = repository_ctx.attr.jar_sha256 or repository_ctx.attr.artifact_sha256 - path = repository_ctx.name + ".jar" + path = repo_name + ".jar" for url in urls: if url.endswith(".jar"): path = url[url.rindex("/") + 1:] break srcurls = repository_ctx.attr.srcjar_urls srcsha = repository_ctx.attr.srcjar_sha256 - srcpath = repository_ctx.name + "-src.jar" if srcurls else "" + srcpath = repo_name + "-src.jar" if srcurls else "" coordinates = repository_ctx.attr.coordinates for url in srcurls: if url.endswith(".jar"): @@ -136,7 +138,7 @@ def _jvm_import_external(repository_ctx): "", "alias(", " name = \"jar\",", - " actual = \"@%s\"," % repository_ctx.name, + " actual = \"//:%s\"," % repo_name, ")", "", ])) diff --git a/scala/scala_toolchain.bzl b/scala/scala_toolchain.bzl index a09e03f73..60da0783a 100644 --- a/scala/scala_toolchain.bzl +++ b/scala/scala_toolchain.bzl @@ -1,7 +1,4 @@ -load( - "@io_bazel_rules_scala//scala:providers.bzl", - _DepsInfo = "DepsInfo", -) +load("//scala:providers.bzl", _DepsInfo = "DepsInfo") load( "@io_bazel_rules_scala_config//:config.bzl", "ENABLE_COMPILER_DEPENDENCY_TRACKING", @@ -108,17 +105,17 @@ def _scala_toolchain_impl(ctx): def _default_dep_providers(): dep_providers = [ - "@io_bazel_rules_scala//scala:scala_xml_provider", - "@io_bazel_rules_scala//scala:parser_combinators_provider", - "@io_bazel_rules_scala//scala:scala_compile_classpath_provider", - "@io_bazel_rules_scala//scala:scala_library_classpath_provider", - "@io_bazel_rules_scala//scala:scala_macro_classpath_provider", + "scala_xml", + "parser_combinators", + "scala_compile_classpath", + "scala_library_classpath", + "scala_macro_classpath", ] - if SCALA_MAJOR_VERSION.startswith("2"): - dep_providers.append("@io_bazel_rules_scala//scala:semanticdb_provider") - return dep_providers + if SCALA_MAJOR_VERSION.startswith("2."): + dep_providers.append("semanticdb") + return [Label("//scala:%s_provider" % p) for p in dep_providers] -scala_toolchain = rule( +_scala_toolchain = rule( _scala_toolchain_impl, attrs = { "scalacopts": attr.string_list(), @@ -179,3 +176,31 @@ scala_toolchain = rule( }, fragments = ["java"], ) + +def _expand_patterns(patterns): + """Expands string patterns to match actual Label values.""" + result = [] + + for p in patterns: + exclude = p.startswith("-") + p = p.lstrip("-") + expanded = str(native.package_relative_label(p)) if p else "" + + # If the original pattern doesn't contain ":", match any target + # beginning with the pattern prefix. + if expanded and ":" not in p: + expanded = expanded[:expanded.rindex(":")] + + result.append(("-" if exclude else "") + expanded) + + return result + +def scala_toolchain(**kwargs): + """Creates a Scala toolchain target.""" + strict = kwargs.pop("dependency_tracking_strict_deps_patterns", [""]) + unused = kwargs.pop("dependency_tracking_unused_deps_patterns", [""]) + _scala_toolchain( + dependency_tracking_strict_deps_patterns = _expand_patterns(strict), + dependency_tracking_unused_deps_patterns = _expand_patterns(unused), + **kwargs + ) diff --git a/test/src/main/scala/scalarules/test/resources/ScalaLibResourcesFromExternalDepTest.scala b/test/src/main/scala/scalarules/test/resources/ScalaLibResourcesFromExternalDepTest.scala index 10e4564f0..088ec71a3 100644 --- a/test/src/main/scala/scalarules/test/resources/ScalaLibResourcesFromExternalDepTest.scala +++ b/test/src/main/scala/scalarules/test/resources/ScalaLibResourcesFromExternalDepTest.scala @@ -8,7 +8,7 @@ class ScalaLibResourcesFromExternalDepTest extends SpecWithJUnit { "allow to load resources" >> { val expectedString = String.format("A resource%n"); //Using platform dependent newline (%n) - get("/external/test_new_local_repo/resource.txt") must beEqualTo(expectedString) + get("/resource.txt") must beEqualTo(expectedString) } } diff --git a/test/src/main/scala/scalarules/test/resources/ScalaLibResourcesFromExternalScalaTest.scala b/test/src/main/scala/scalarules/test/resources/ScalaLibResourcesFromExternalScalaTest.scala index 99a9e8e7f..599ca7edf 100644 --- a/test/src/main/scala/scalarules/test/resources/ScalaLibResourcesFromExternalScalaTest.scala +++ b/test/src/main/scala/scalarules/test/resources/ScalaLibResourcesFromExternalScalaTest.scala @@ -6,7 +6,7 @@ class ScalaLibResourcesFromExternalScalaTest extends AnyFunSuite { test("Scala library depending on resources from external resource-only jar should allow to load resources") { val expectedString = String.format("A resource%n"); //Using platform dependent newline (%n) - assert(get("/external/test_new_local_repo/resource.txt") === expectedString) + assert(get("/resource.txt") === expectedString) } private def get(s: String): String = diff --git a/third_party/repositories/repositories.bzl b/third_party/repositories/repositories.bzl index 88239c012..1e0529c1e 100644 --- a/third_party/repositories/repositories.bzl +++ b/third_party/repositories/repositories.bzl @@ -1,3 +1,4 @@ +load("//scala/private:macros/bzlmod.bzl", "apparent_repo_name") load( "//third_party/repositories:scala_2_11.bzl", _artifacts_2_11 = "artifacts", @@ -102,14 +103,24 @@ def repositories( default_artifacts = artifacts_by_major_scala_version[major_scala_version] artifacts = dict(default_artifacts.items() + overriden_artifacts.items()) for id in for_artifact_ids: + if id not in artifacts: + fail("artifact %s not in third_party/repositories/scala_%s.bzl" % ( + id, + major_scala_version.replace(".", "_"), + )) + + artifact_repo_name = id + suffix _scala_maven_import_external( - name = id + suffix, + name = artifact_repo_name, artifact = artifacts[id]["artifact"], artifact_sha256 = artifacts[id]["sha256"], licenses = ["notice"], server_urls = maven_servers, deps = [dep + suffix for dep in artifacts[id].get("deps", [])], - runtime_deps = artifacts[id].get("runtime_deps", []), + runtime_deps = [ + dep + suffix + for dep in artifacts[id].get("runtime_deps", []) + ], testonly_ = artifacts[id].get("testonly", False), fetch_sources = fetch_sources, ) @@ -118,13 +129,13 @@ def repositories( # See: https://github.com/bazelbuild/rules_scala/pull/1573 # Hopefully we can deprecate and remove it one day. if suffix and scala_version == SCALA_VERSION: - _alias_repository(name = id, target = id + suffix) + _alias_repository(name = id, target = artifact_repo_name) def _alias_repository_impl(rctx): """ Builds a repository containing just two aliases to the Scala Maven artifacts in the `target` repository. """ format_kwargs = { - "name": rctx.name, + "name": apparent_repo_name(rctx), "target": rctx.attr.target, } rctx.file("BUILD", """alias(