From 9c321ce1782a65abc5e7f972032e448a5d5f8f89 Mon Sep 17 00:00:00 2001 From: Yves-Stan Le Cornec Date: Wed, 7 Jun 2023 18:17:35 +0200 Subject: [PATCH] Make stack_snapshot usable in another module's extension With bzlmod, when using `stack_snapshot` in a module extension of another module, rules_haskell does not have visibility of generated repositories (such as `rules_haskell_stack`) and cannot build labels to its targets using the `Label` constructor. To work around this, an optional `label_builder` parameter can be passed to `stack_snapshot`, which is then used to canonicalize various labels. For the same reason, the `stack_update` attribute to `_stack_snapshot` is now always present (so we do not have to hardcode its label), and is made into a string so that we only fetch the `stack_update` repository when explicitly reading from it. --- haskell/cabal.bzl | 35 ++++++++++++++++------- rules_haskell_tests/non_module_deps_2.bzl | 7 +++++ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/haskell/cabal.bzl b/haskell/cabal.bzl index 1d72230e58..3ff9539e9b 100644 --- a/haskell/cabal.bzl +++ b/haskell/cabal.bzl @@ -1536,8 +1536,7 @@ def _download_packages(repository_ctx, snapshot, pinned): if stack_unpack: # Enforce dependency on stack_update. - # Hard-coded label since `stack_update` is `None` in this case. - repository_ctx.read(Label("@rules_haskell_stack_update//:stack_update")) + repository_ctx.read(repository_ctx.path(Label(repository_ctx.attr.stack_update))) _download_packages_unpinned(repository_ctx, snapshot, stack_unpack) def _download_packages_unpinned(repository_ctx, snapshot, resolved): @@ -1586,6 +1585,9 @@ def _to_string_keyed_label_list_dict(d): out.setdefault(string_key, []).append(label) return out +def _is_bzlmod_enabled(): + return str(Label("@rules_haskell//:BUILD.bazel")).startswith("@@") + def _label_to_string(label): if check_bazel_version("6.0.0")[0]: # `str` serializes the label to its canonical name starting from bazel 6 @@ -1913,7 +1915,7 @@ def _stack_snapshot_impl(repository_ctx): # Resolve and fetch packages if repository_ctx.attr.stack_snapshot_json == None: # Enforce dependency on stack_update - repository_ctx.read(repository_ctx.attr.stack_update) + repository_ctx.read(repository_ctx.path(Label(repository_ctx.attr.stack_update))) resolved = _resolve_packages( repository_ctx, snapshot, @@ -1986,7 +1988,7 @@ packages = { executables = all_components[name].exe, sublibs = all_components[name].sublibs, deps = [ - str(Label("@{}//:{}".format(repository_ctx.attr.unmangled_repo_name, dep))) + "@{}//:{}".format(repository_ctx.attr.unmangled_repo_name, dep) for dep in spec["dependencies"] if all_components[dep].lib ], @@ -2069,8 +2071,13 @@ haskell_library( for dep in spec["dependencies"] for exe in all_components[dep].exe ] + tools + setup_deps = [ - _label_to_string(Label("@{}//:{}".format(repository_ctx.attr.unmangled_repo_name, name)).relative(label)) + _label_to_string(Label("{}{}//:{}".format( + "@@" if _is_bzlmod_enabled() else "@", + repository_ctx.attr.name, + name, + )).relative(label)) for label in repository_ctx.attr.setup_deps.get(name, []) ] if all_components[name].lib: @@ -2223,7 +2230,7 @@ _stack_snapshot = repository_rule( "components": attr.string_list_dict(), "components_dependencies": attr.string_dict(), "stack": attr.label(), - "stack_update": attr.label(), + "stack_update": attr.string(), "verbose": attr.bool(default = False), "custom_toolchain_libraries": attr.string_list(default = []), "enable_custom_toolchain_libraries": attr.bool(default = False), @@ -2434,6 +2441,7 @@ def stack_snapshot( netrc = "", toolchain_libraries = None, setup_stack = True, + label_builder = lambda l: Label(l), **kwargs): """Use Stack to download and extract Cabal source distributions. @@ -2631,11 +2639,11 @@ def stack_snapshot( # Allow overriding stack binary at workspace level by `use_stack()`. # Otherwise this is a no-op. if native.existing_rule("rules_haskell_stack") or not setup_stack: - stack = Label("@rules_haskell_stack//:stack") + stack = label_builder("@rules_haskell_stack//:stack") if not stack: _fetch_stack(name = "rules_haskell_stack") - stack = Label("@rules_haskell_stack//:stack") + stack = label_builder("@rules_haskell_stack//:stack") # Execute stack update once before executing _stack_snapshot. # This is to avoid multiple concurrent executions of stack update, @@ -2663,12 +2671,19 @@ def stack_snapshot( custom_toolchain_libraries = toolchain_libraries, enable_custom_toolchain_libraries = toolchain_libraries != None, ) + canonical_setup_deps = { + k: [ + str(label_builder(label)) if label.startswith("@") else label + for label in labels + ] + for (k, labels) in setup_deps.items() + } _stack_snapshot( name = name, unmangled_repo_name = name, stack = stack, # Dependency for ordered execution, stack update before stack unpack. - stack_update = None if stack_snapshot_json else "@rules_haskell_stack_update//:stack_update", + stack_update = str(label_builder("@rules_haskell_stack_update//:stack_update")), # TODO Remove _from_string_keyed_label_list_dict once following issue # is resolved: https://github.com/bazelbuild/bazel/issues/7989. extra_deps = _from_string_keyed_label_list_dict(extra_deps), @@ -2681,7 +2696,7 @@ def stack_snapshot( packages = packages, flags = flags, haddock = haddock, - setup_deps = setup_deps, + setup_deps = canonical_setup_deps, tools = tools, components = components, components_dependencies = components_dependencies, diff --git a/rules_haskell_tests/non_module_deps_2.bzl b/rules_haskell_tests/non_module_deps_2.bzl index 860fcfd4f7..69f39aea7b 100644 --- a/rules_haskell_tests/non_module_deps_2.bzl +++ b/rules_haskell_tests/non_module_deps_2.bzl @@ -5,6 +5,8 @@ load("@os_info//:os_info.bzl", "is_linux", "is_windows") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@toolchains_libraries//:toolchain_libraries.bzl", "toolchain_libraries") +label_builder = lambda x: Label(x) + def repositories(*, bzlmod): # In a separate repo because not all platforms support zlib. stack_snapshot( @@ -13,6 +15,7 @@ def repositories(*, bzlmod): local_snapshot = "//:stackage_snapshot.yaml", packages = ["zlib"], stack_snapshot_json = "//:stackage-zlib-snapshot.json" if not is_windows else None, + label_builder = label_builder, ) # Vendor data-default-instances-containers and data-default-instances-old-local @@ -121,6 +124,7 @@ haskell_library( "data-default-instances-old-locale": "@data-default-ol//:lib", "ghc-paths": "@rules_haskell//tools/ghc-paths", }, + label_builder = label_builder, ) http_archive( @@ -196,6 +200,7 @@ haskell_cabal_library( "quickcheck-io": ["@Cabal//:Cabal"], }, stack_snapshot_json = "//:stackage-pinning-test_snapshot.json" if not is_windows else None, + label_builder = label_builder, ) stack_snapshot( @@ -296,6 +301,7 @@ haskell_cabal_library( vendored_packages = { "ghc-paths": "@rules_haskell//tools/ghc-paths", }, + label_builder = label_builder, ) stack_snapshot( @@ -307,6 +313,7 @@ haskell_cabal_library( ], stack_snapshot_json = "//tests/asterius/stack_toolchain_libraries:snapshot.json", toolchain_libraries = toolchain_libraries, + label_builder = label_builder, ) if is_linux else None def _non_module_deps_2_impl(ctx):