From 62b6dc897d5b96e5caa0ecfc38b16488dfea4460 Mon Sep 17 00:00:00 2001 From: Yves-Stan Le Cornec Date: Fri, 30 Jun 2023 23:02:46 +0200 Subject: [PATCH 1/6] haskell_toolchains extension: Use padded index in toolchain names Because they are registered in alphabetical order. We wait until we know the total number of toolchains in the file to chose the names, in order to know the needed padding size. --- extensions/haskell_toolchains.bzl | 20 ++++++++++++++++---- haskell/ghc_bindist.bzl | 2 +- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/extensions/haskell_toolchains.bzl b/extensions/haskell_toolchains.bzl index 4bfd0630d..788621269 100644 --- a/extensions/haskell_toolchains.bzl +++ b/extensions/haskell_toolchains.bzl @@ -63,11 +63,23 @@ _bindist_tag = tag_class( doc = "Declares and configure a bindist haskell toolchain. See [ghc_bindist](ghc_bindist.html#ghc_bindist).", ) +def _left_pad_zero(index, length): + if index < 0: + fail("index must be non-negative") + return ("0" * length + str(index))[-length:] + def _all_toolchains_impl(rctx): - content = "\n".join(rctx.attr.toolchains) + # In the same BUILD file, toolchain are registered in alphabetical order, so we use a padded index in the toolchain name. + # https://github.com/bazelbuild/rules_go/blob/e116651f92a15f4e5dd63ea09dd9ae2b2dd8e2f3/go/private/extensions.bzl#L135-L139 + pad_size = len(str(len(rctx.attr.toolchains))) + toolchains = [ + f.format("toolchain_{}".format(_left_pad_zero(i, pad_size))) + for i, f in enumerate(rctx.attr.toolchains) + ] + content = "\n".join(toolchains) rctx.file("BUILD.bazel", content = content) -_all_toolchains = repository_rule( +all_toolchains = repository_rule( implementation = _all_toolchains_impl, attrs = { "toolchains": attr.string_list( @@ -118,7 +130,7 @@ def _haskell_toolchains_impl(mctx): ghc_bindist_toolchain_declaration( target = bindist_tag.target, bindist_name = name, - toolchain_name = name, + toolchain_name = "{}", # We wait before chosing the toolchain name in the all_toolchain rule. ), ) @@ -148,7 +160,7 @@ def _haskell_toolchains_impl(mctx): ghc_bindists_toolchain_declarations(bindists_tag.version), ) - _all_toolchains( + all_toolchains( name = "all_bindist_toolchains", toolchains = toolchain_declarations, ) diff --git a/haskell/ghc_bindist.bzl b/haskell/ghc_bindist.bzl index 4a1190d70..700e6cf8a 100644 --- a/haskell/ghc_bindist.bzl +++ b/haskell/ghc_bindist.bzl @@ -360,7 +360,7 @@ def ghc_bindists_toolchain_declarations(version): ghc_bindist_toolchain_declaration( target = target, bindist_name = "rules_haskell_ghc_{}".format(target), - toolchain_name = "rules_haskell_ghc_{}".format(target), + toolchain_name = "{}", ) for target in GHC_BINDIST[version] ] From 66c5d07fabd5613214f79b61abff80074dd7748d Mon Sep 17 00:00:00 2001 From: Yves-Stan Le Cornec Date: Fri, 30 Jun 2023 18:32:04 +0200 Subject: [PATCH 2/6] nixpkgs.bzl: separate toolchain definition of nix package installation - Add register_ghc_from_nixpkgs_package with is similar to haskell_register_ghc_nixpkgs, but relies on pre-existing nix packages. - new`ghc_nixpkgs_toolchain_declaration` function which generates a toolchain declaration snippet is public, to be used by the `declare_toolchains` extension. - Similarly for the `HASKELL_TOOLCHAIN_REPO_NAME_SUFFIX` variable --- rules_haskell_nix/nixpkgs.bzl | 202 ++++++++++++++++++++++++---------- 1 file changed, 145 insertions(+), 57 deletions(-) diff --git a/rules_haskell_nix/nixpkgs.bzl b/rules_haskell_nix/nixpkgs.bzl index a256921f7..345c26380 100644 --- a/rules_haskell_nix/nixpkgs.bzl +++ b/rules_haskell_nix/nixpkgs.bzl @@ -43,6 +43,9 @@ Available versions: """.format(wanted = ghc_name, actual = result.stdout), ) +def _is_bzlmod_enabled(): + return str(Label("@rules_haskell//:BUILD.bazel")).startswith("@@") + def _ghc_nixpkgs_haskell_toolchain_impl(repository_ctx): paths = resolve_labels(repository_ctx, [ "@rules_haskell//haskell:private/pkgdb_to_bzl.py", @@ -75,7 +78,10 @@ def _ghc_nixpkgs_haskell_toolchain_impl(repository_ctx): # See Note [GHC toolchain files] in haskell/ghc_bindist.bzl libdir_path = repr(libdir_path), docdir_path = repr(docdir_path), - tools = ["@{}//:bin".format(repository_ctx.attr.nixpkgs_ghc_repo_name)], + tools = ["{}{}//:bin".format( + "@@" if _is_bzlmod_enabled() else "@", + repository_ctx.attr.nixpkgs_ghc.workspace_name, + )], version = repr(repository_ctx.attr.version), static_runtime = repository_ctx.attr.static_runtime, fully_static_link = repository_ctx.attr.fully_static_link, @@ -143,43 +149,60 @@ _ghc_nixpkgs_haskell_toolchain = repository_rule( }, ) -def _ghc_nixpkgs_toolchain_impl(repository_ctx): +# This function is public so that the `declare_toolchains` extension +# can build toolchain declarations +def ghc_nixpkgs_toolchain_declaration( + target_constraints, + exec_constraints, + cpu_value, + os_name, + haskell_toolchain_repo_name, + toolchain_name): # These constraints might look tautological, because they always # match the host platform if it is the same as the target # platform. But they are important to state because Bazel # toolchain resolution prefers other toolchains with more specific # constraints otherwise. - if repository_ctx.attr.target_constraints == [] and repository_ctx.attr.exec_constraints == []: - cpu_value = get_cpu_value(repository_ctx) + if target_constraints == [] and exec_constraints == []: target_constraints = ["@platforms//cpu:{}".format( "arm64" if ("arm64" in cpu_value or "aarch64" in cpu_value) else "x86_64", )] - if repository_ctx.os.name == "linux": + if os_name == "linux": target_constraints.append("@platforms//os:linux") - elif repository_ctx.os.name == "mac os x": + elif os_name == "mac os x": target_constraints.append("@platforms//os:osx") exec_constraints = list(target_constraints) else: - target_constraints = repository_ctx.attr.target_constraints - exec_constraints = list(repository_ctx.attr.exec_constraints) + target_constraints = target_constraints + exec_constraints = list(exec_constraints) exec_constraints.append("@rules_nixpkgs_core//constraints:support_nix") - - repository_ctx.file( - "BUILD", - executable = False, - content = """ + return """ toolchain( - name = "toolchain", + name = "{toolchain_name}", toolchain_type = "@rules_haskell//haskell:toolchain", toolchain = "@{haskell_toolchain}//:toolchain-impl", exec_compatible_with = {exec_constraints}, target_compatible_with = {target_constraints}, ) - """.format( - exec_constraints = exec_constraints, - target_constraints = target_constraints, - haskell_toolchain = repository_ctx.attr.haskell_toolchain_repo_name, +""".format( + exec_constraints = exec_constraints, + target_constraints = target_constraints, + haskell_toolchain = haskell_toolchain_repo_name, + toolchain_name = toolchain_name, + ) + +def _ghc_nixpkgs_toolchain_impl(repository_ctx): + repository_ctx.file( + "BUILD", + executable = False, + content = ghc_nixpkgs_toolchain_declaration( + target_constraints = repository_ctx.attr.target_constraints, + exec_constraints = repository_ctx.attr.exec_constraints, + cpu_value = get_cpu_value(repository_ctx), + os_name = repository_ctx.os.name, + haskell_toolchain_repo_name = repository_ctx.attr.haskell_toolchain_repo_name, + toolchain_name = "toolchain", ), ) @@ -192,6 +215,89 @@ _ghc_nixpkgs_toolchain = repository_rule( }, ) +# Public so that the `nix_haskell_toolchain` extension can predict the repo name +# and register the toolchain +HASKELL_TOOLCHAIN_REPO_NAME_SUFFIX = "_ghc_nixpkgs_haskell_toolchain" + +def register_ghc_from_nixpkgs_package( + version, + nixpkgs_ghc, + name, + is_static = None, # DEPRECATED. See _check_static_attributes_compatibility. + static_runtime = None, + fully_static_link = None, + ghcopts = None, + compiler_flags_select = None, + haddock_flags = None, + repl_ghci_args = None, + cabalopts = None, + locale_archive = None, + locale = None, + exec_constraints = [], + target_constraints = [], + register = True): + """Register a pre-existing package from Nixpkgs as a toolchain. + + This rule is similar [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs), but it does not setup any nix package itself. + + Args: + version: The version of the provided ghc. + name: The name for this toolchain. + nixpkgs_ghc: The label of a nix-installed ghc_binary + is_static: Deprecated. The functionality it previously gated + (supporting GHC versions with static runtime systems) now sits under + static_runtime, a name chosen to avoid confusion with the new flag + fully_static_link, which controls support for fully-statically-linked + binaries. During the deprecation period, we rewrite is_static to + static_runtime in this macro as long as the new attributes aren't also + used. This argument and supporting code should be removed in a future release. + static_runtime: True if and only if a static GHC runtime is to be used. This is + required in order to use statically-linked Haskell libraries with GHCi + and Template Haskell. + fully_static_link: True if and only if fully-statically-linked binaries are to be built. + ghcopts: A collection of flags that will be passed to GHC + compiler_flags_select: temporary workaround to pass conditional arguments. + See https://github.com/bazelbuild/bazel/issues/9199 for details. + register: Whether to register the toolchain (must be set to False if bzlmod is enabled) + """ + nixpkgs_ghc_repo_name = "{}_ghc_nixpkgs".format(name) + haskell_toolchain_repo_name = "{}{}".format(name, HASKELL_TOOLCHAIN_REPO_NAME_SUFFIX) + toolchain_repo_name = "{}_ghc_nixpkgs_toolchain".format(name) + + static_runtime, fully_static_link = _check_static_attributes_compatibility( + is_static = is_static, + static_runtime = static_runtime, + fully_static_link = fully_static_link, + ) + + _ghc_nixpkgs_haskell_toolchain( + name = haskell_toolchain_repo_name, + version = version, + static_runtime = static_runtime, + fully_static_link = fully_static_link, + ghcopts = ghcopts, + compiler_flags_select = compiler_flags_select, + haddock_flags = haddock_flags, + cabalopts = cabalopts, + repl_ghci_args = repl_ghci_args, + locale_archive = locale_archive, + locale = locale, + nixpkgs_ghc_repo_name = nixpkgs_ghc_repo_name, + nixpkgs_ghc = nixpkgs_ghc, + ) + + # toolchain definition. + if (exec_constraints == []) != (target_constraints == []): + fail("Both exec_constraints and target_constraints need to be provided or none of them.") + _ghc_nixpkgs_toolchain( + name = toolchain_repo_name, + exec_constraints = exec_constraints, + target_constraints = target_constraints, + haskell_toolchain_repo_name = haskell_toolchain_repo_name, + ) + if register: + native.register_toolchains("@{}//:toolchain".format(toolchain_repo_name)) + def haskell_register_ghc_nixpkgs( version, name = "rules_haskell", @@ -216,8 +322,8 @@ def haskell_register_ghc_nixpkgs( repositories = {}, repository = None, nix_file_content = None, - exec_constraints = None, - target_constraints = None, + exec_constraints = [], + target_constraints = [], register = True): """Register a package from Nixpkgs as a toolchain. @@ -278,14 +384,6 @@ def haskell_register_ghc_nixpkgs( """ nixpkgs_ghc_repo_name = "{}_ghc_nixpkgs".format(name) nixpkgs_sh_posix_repo_name = "{}_sh_posix_nixpkgs".format(name) - haskell_toolchain_repo_name = "{}_ghc_nixpkgs_haskell_toolchain".format(name) - toolchain_repo_name = "{}_ghc_nixpkgs_toolchain".format(name) - - static_runtime, fully_static_link = _check_static_attributes_compatibility( - is_static = is_static, - static_runtime = static_runtime, - fully_static_link = fully_static_link, - ) # The package from the system. nixpkgs_package( @@ -301,7 +399,21 @@ def haskell_register_ghc_nixpkgs( repository = repository, ) - # haskell_toolchain + haskell_import definitions. + # Unix tools toolchain required for Cabal packages + sh_posix_nixpkgs_kwargs = dict( + nix_file_deps = nix_file_deps, + nixopts = nixopts, + repositories = repositories, + repository = repository, + ) + if sh_posix_attributes != None: + sh_posix_nixpkgs_kwargs["packages"] = sh_posix_attributes + nixpkgs_sh_posix_configure( + name = nixpkgs_sh_posix_repo_name, + register = register, + **sh_posix_nixpkgs_kwargs + ) + ghcopts = check_deprecated_attribute_usage( old_attr_name = "compiler_flags", old_attr_value = compiler_flags, @@ -309,9 +421,11 @@ def haskell_register_ghc_nixpkgs( new_attr_value = ghcopts, ) - _ghc_nixpkgs_haskell_toolchain( - name = haskell_toolchain_repo_name, + register_ghc_from_nixpkgs_package( version = version, + nixpkgs_ghc = "@{}//:bin/ghc".format(nixpkgs_ghc_repo_name), + name = name, + is_static = is_static, static_runtime = static_runtime, fully_static_link = fully_static_link, ghcopts = ghcopts, @@ -321,35 +435,9 @@ def haskell_register_ghc_nixpkgs( repl_ghci_args = repl_ghci_args, locale_archive = locale_archive, locale = locale, - nixpkgs_ghc_repo_name = nixpkgs_ghc_repo_name, - nixpkgs_ghc = "@{}//:bin/ghc".format(nixpkgs_ghc_repo_name), - ) - - # toolchain definition. - if (exec_constraints == None) != (target_constraints == None): - fail("Both exec_constraints and target_constraints need to be provided or none of them.") - _ghc_nixpkgs_toolchain( - name = toolchain_repo_name, exec_constraints = exec_constraints, target_constraints = target_constraints, - haskell_toolchain_repo_name = haskell_toolchain_repo_name, - ) - if register: - native.register_toolchains("@{}//:toolchain".format(toolchain_repo_name)) - - # Unix tools toolchain required for Cabal packages - sh_posix_nixpkgs_kwargs = dict( - nix_file_deps = nix_file_deps, - nixopts = nixopts, - repositories = repositories, - repository = repository, - ) - if sh_posix_attributes != None: - sh_posix_nixpkgs_kwargs["packages"] = sh_posix_attributes - nixpkgs_sh_posix_configure( - name = nixpkgs_sh_posix_repo_name, register = register, - **sh_posix_nixpkgs_kwargs ) def _check_static_attributes_compatibility(is_static, static_runtime, fully_static_link): From 8d9a3efc2fa7e67c41c26ebad7389e45cbc389d4 Mon Sep 17 00:00:00 2001 From: Yves-Stan Le Cornec Date: Fri, 30 Jun 2023 23:04:45 +0200 Subject: [PATCH 3/6] Add nix_haskell_toolchains extension --- rules_haskell_nix/extensions/BUILD.bazel | 0 .../extensions/nix_haskell_toolchains.bzl | 247 ++++++++++++++++++ rules_haskell_nix/private/BUILD | 0 .../private/declare_toolchains.bzl | 61 +++++ 4 files changed, 308 insertions(+) create mode 100644 rules_haskell_nix/extensions/BUILD.bazel create mode 100644 rules_haskell_nix/extensions/nix_haskell_toolchains.bzl create mode 100644 rules_haskell_nix/private/BUILD create mode 100644 rules_haskell_nix/private/declare_toolchains.bzl diff --git a/rules_haskell_nix/extensions/BUILD.bazel b/rules_haskell_nix/extensions/BUILD.bazel new file mode 100644 index 000000000..e69de29bb diff --git a/rules_haskell_nix/extensions/nix_haskell_toolchains.bzl b/rules_haskell_nix/extensions/nix_haskell_toolchains.bzl new file mode 100644 index 000000000..3fe836210 --- /dev/null +++ b/rules_haskell_nix/extensions/nix_haskell_toolchains.bzl @@ -0,0 +1,247 @@ +""" The `new` tag from this module extension follows the interface of the +`haskell_register_ghc_nixpkgs` repository rule to configure nix-based +haskell toolchains, as well as a companion posix toolchains. + +Once posix toolchains can be registered directly via the rules_nixpkgs_posix module, +the handling of these toolchains could be removed from this extension. +""" + +load( + "@rules_nixpkgs_posix//:posix.bzl", + "nixpkgs_sh_posix_configure", +) +load( + "@rules_nixpkgs_core//:util.bzl", + "default_constraints", + "ensure_constraints_pure", +) +load( + "@rules_nixpkgs_core//:nixpkgs.bzl", + "nixpkgs_package", +) +load("@rules_haskell//extensions:haskell_toolchains.bzl", "all_toolchains") + +# Based on _nixpkgs_sh_posix_toolchain from: +# https://github.com/tweag/rules_nixpkgs/blob/420370f64f03ed9c1ff9b5e2994d06c0439cb1f2/toolchains/posix/posix.bzl#LL109C1-L128C1 +# This is needed until rules_nixpkgs_posix can declare toolchains, +# or we can register the toolchains via an alias (https://github.com/bazelbuild/bazel/issues/16298) +def _nixpkgs_sh_posix_toolchain_declaration(mctx, workspace, exec_constraints = []): + exec_constraints, _ = ensure_constraints_pure( + default_constraints = default_constraints(mctx), + exec_constraints = exec_constraints, + ) + return """ +toolchain( + name = "{{}}", + toolchain = "@{workspace}//:nixpkgs_sh_posix", + toolchain_type = "@rules_sh//sh/posix:toolchain_type", + exec_compatible_with = {exec_constraints}, + target_compatible_with = [], +) + """.format( + workspace = workspace, + exec_constraints = exec_constraints, + ) + +_new_tag = tag_class( + attrs = { + "version": attr.string( + mandatory = True, + doc = "The version of ghc to install", + ), + "static_runtime": attr.bool( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-static_runtime)", + ), + "fully_static_link": attr.bool( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-fully_static_link)", + ), + "build_file": attr.label( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-build_file)", + ), + "build_file_content": attr.string( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-build_file_content)", + ), + "ghcopts": attr.string_list( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-ghcopts)", + ), + "compiler_flags_select": attr.string_list_dict( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-compiler_flags_select)", + ), + "haddock_flags": attr.string_list( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-haddock_flags)", + ), + "repl_ghci_args": attr.string_list( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-repl_ghci_args)", + ), + "cabalopts": attr.string_list( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-cabalopts)", + ), + "locale_archive": attr.string( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-locale_archive)", + ), + "attribute_path": attr.string( + default = "haskellPackages.ghc", + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-attribute_path)", + ), + "sh_posix_attributes": attr.string_list( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-sh_posix_attributes)", + ), + "nix_file": attr.label( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-nix_file)", + ), + "nix_file_deps": attr.label_list( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-nix_file_deps)", + ), + "nixopts": attr.string_list( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-nixopts)", + ), + "locale": attr.string( + default = "C.UTF-8", + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-locale)", + ), + "repositories": attr.string_dict( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-repositories)", + ), + "repository": attr.label( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-repository)", + ), + "nix_file_content": attr.string( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-nix_file_content)", + ), + "exec_constraints": attr.string_list( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-exec_constraints)", + ), + "target_constraints": attr.string_list( + doc = "See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs-target_constraints)", + ), + }, + doc = "Declare a new nix-based haskell toolchain. See [haskell_register_ghc_nixpkgs](nixpkgs.html#haskell_register_ghc_nixpkgs)", +) + +def _toolchains_configuration_impl(rctx): + rctx.file("BUILD") + + # The labels of ghc binaries need to be resolved in the `nix_haskell_toolchains_configurations` repository + # which has visibility over the repositories generated via nixpkgs_package + ghc_labels_items = ["{}:{}".format(k, v) for k, v in rctx.attr.ghc_labels.items()] + + rctx.file( + "nix_haskell_toolchains_configurations.bzl", + content = """ +ghc_labels = {{ {ghc_labels} }} +toolchain_ids = {toolchain_ids} +toolchains = {toolchains} +""".format( + ghc_labels = ",".join(ghc_labels_items), + toolchain_ids = rctx.attr.toolchain_ids, + toolchains = rctx.attr.toolchains, + ), + ) + +toolchains_configuration = repository_rule( + implementation = _toolchains_configuration_impl, + attrs = { + "toolchain_ids": attr.string( + doc = "The ids of the toolchains to declare", + ), + "ghc_labels": attr.string_dict( + doc = "The labels of the corresponding ghc binaries to resolve", + ), + "toolchains": attr.string( + doc = "The configuration of these toolchains", + ), + }, + doc = """ This repository is used to canonicalize the labels of the ghc binaries. + +Since the names of the repositories generated by `nixpkgs_package` depend on which tags are used, +we cannot call `use_repo()` in the `MODULE.bazel` file to make them visible to `rules_haskell_nix`. + +They are however visible from this `nix_haskell_toolchains_configurations` repository which is generated by the same module extension. +This `nix_haskell_toolchains_configurations` is then loaded by the `declare_toolchains` extension, which sets up and generates the `all_nix_toolchains` repository. +""", +) + +def _nix_haskell_toolchains_impl(mctx): + """ This extension generates the nix repositories and forwards haskell related configuration to the `declare_toolchain` extension (via the @nix_haskell_toolchains_configurations repository). """ + ghc_labels = {} + toolchains_configurations = {} + haskell_toolchain_ids = [] # Used to track the order of the toolchains + posix_toolchains = [] + for module in mctx.modules: + for (index, new_tag) in enumerate(module.tags.new): + name = "{}_{}_{}".format(module.name, module.version, index) + nixpkgs_ghc_repo_name = "{}_ghc_nixpkgs".format(name) + nixpkgs_sh_posix_repo_name = "{}_sh_posix_nixpkgs".format(name) + + nixpkgs_package( + name = nixpkgs_ghc_repo_name, + attribute_path = new_tag.attribute_path, + build_file = new_tag.build_file, + build_file_content = new_tag.build_file_content, + nix_file = new_tag.nix_file, + nix_file_deps = new_tag.nix_file_deps, + nix_file_content = new_tag.nix_file_content, + nixopts = new_tag.nixopts, + repositories = new_tag.repositories, + repository = new_tag.repository, + ) + + # Unix tools toolchain required for Cabal packages + sh_posix_nixpkgs_kwargs = dict( + nix_file_deps = new_tag.nix_file_deps, + nixopts = new_tag.nixopts, + repositories = new_tag.repositories, + repository = new_tag.repository, + ) + if new_tag.sh_posix_attributes != []: + sh_posix_nixpkgs_kwargs["packages"] = new_tag.sh_posix_attributes + nixpkgs_sh_posix_configure( + name = nixpkgs_sh_posix_repo_name, + register = False, + **sh_posix_nixpkgs_kwargs + ) + + posix_toolchains.append( + _nixpkgs_sh_posix_toolchain_declaration( + mctx = mctx, + workspace = nixpkgs_sh_posix_repo_name, + ), + ) + haskell_toolchain_ids.append((module.name, module.version, index)) + toolchains_configurations[(module.name, module.version, index)] = { + "version": new_tag.version, + "name": name, + "static_runtime": new_tag.static_runtime, + "fully_static_link": new_tag.fully_static_link, + "ghcopts": new_tag.ghcopts, + "compiler_flags_select": new_tag.compiler_flags_select, + "haddock_flags": new_tag.haddock_flags, + "repl_ghci_args": new_tag.repl_ghci_args, + "cabalopts": new_tag.cabalopts, + "locale_archive": new_tag.locale_archive, + "locale": new_tag.locale, + "exec_constraints": new_tag.exec_constraints, + "target_constraints": new_tag.target_constraints, + } + + # ghc_labels are handled separately to be resolved when loading the nix_haskell_toolchains_configurations.bzl file later + ghc_labels[(module.name, module.version, index)] = """str(Label("@{}//:bin/ghc"))""".format(nixpkgs_ghc_repo_name) + toolchains_configuration( + name = "nix_haskell_toolchains_configurations", + ghc_labels = {repr(k): v for k, v in ghc_labels.items()}, + toolchains = repr(toolchains_configurations), + toolchain_ids = repr(haskell_toolchain_ids), + ) + + all_toolchains( + name = "all_posix_toolchains", + toolchains = posix_toolchains, + ) + +nix_haskell_toolchains = module_extension( + implementation = _nix_haskell_toolchains_impl, + tag_classes = { + "new": _new_tag, + }, + doc = "This extension is used to install and declare nix-based haskell toolchains.", +) diff --git a/rules_haskell_nix/private/BUILD b/rules_haskell_nix/private/BUILD new file mode 100644 index 000000000..e69de29bb diff --git a/rules_haskell_nix/private/declare_toolchains.bzl b/rules_haskell_nix/private/declare_toolchains.bzl new file mode 100644 index 000000000..f80305640 --- /dev/null +++ b/rules_haskell_nix/private/declare_toolchains.bzl @@ -0,0 +1,61 @@ +""" This module extension declares the toolchains from the `@nix_haskell_toolchains_configurations//:nix_haskell_toolchains_configurations.bzl` file. +This file is produced by the `nix_haskell_toolchains` extension in order to canonicalize the labels of the ghc binaries. + +This indirection is needed because the names of the repositories generated by `nixpkgs_package` depend on which tags are used, +so we cannot call `use_repo()` in the `MODULE.bazel` file to make them visible to `rules_haskell_nix`. + +They are however visible from the `nix_haskell_toolchains_configurations` repository which is generated by the same module extension (`nix_haskell_toolchains`). + - `toolchain_ids` contains the ids of the toolchains in the correct order (the first ones take precedence). + - `ghc_labels` contains the canonicalized labels of the corresponding ghc binaries. + - `toolchains` contains the configuration of these toolchains. +""" + +load( + "@nix_haskell_toolchains_configurations//:nix_haskell_toolchains_configurations.bzl", + "ghc_labels", + "toolchain_ids", + "toolchains", +) +load( + "@rules_haskell_nix//:nixpkgs.bzl", + "HASKELL_TOOLCHAIN_REPO_NAME_SUFFIX", + "ghc_nixpkgs_toolchain_declaration", + "register_ghc_from_nixpkgs_package", +) +load("@bazel_tools//tools/cpp:lib_cc_configure.bzl", "get_cpu_value") +load("@rules_haskell//extensions:haskell_toolchains.bzl", "all_toolchains") + +def _declare_nix_toolchains_impl(mctx): + # Instead of creating one external repository for each `toolchain(...)` declaration, + # we recover them in a list and declare them all in the `all_bindist_toolchains` repository. + toolchain_declarations = [] + for toolchain in toolchain_ids: + kwargs = dict(toolchains[toolchain]) + kwargs["register"] = False + kwargs["nixpkgs_ghc"] = ghc_labels[toolchain] + register_ghc_from_nixpkgs_package(**kwargs) + + haskell_toolchain_repo_name = "{}{}".format( + kwargs["name"], + HASKELL_TOOLCHAIN_REPO_NAME_SUFFIX, + ) + toolchain_declarations.append( + ghc_nixpkgs_toolchain_declaration( + exec_constraints = kwargs["exec_constraints"], + target_constraints = kwargs["target_constraints"], + cpu_value = get_cpu_value(mctx), + os_name = mctx.os.name, + haskell_toolchain_repo_name = haskell_toolchain_repo_name, + toolchain_name = "{}", # The toolchains names are chosen in the all_toolchains rule + ), + ) + + all_toolchains( + name = "all_nix_toolchains", + toolchains = toolchain_declarations, + ) + +declare_nix_toolchains = module_extension( + implementation = _declare_nix_toolchains_impl, + doc = "This module extension creates the haskell toolchains required by the `rules_haskell_toolchains` extension", +) From 9ebfe271573212a51e394f1c04ba0d281e9cffb3 Mon Sep 17 00:00:00 2001 From: Yves-Stan Le Cornec Date: Fri, 30 Jun 2023 18:46:20 +0200 Subject: [PATCH 4/6] register toolchains in rules_haskell_nix MODULE.bazel file rules_haskell_nix also sets up a default toolchain --- rules_haskell_nix/MODULE.bazel | 47 ++++++++++++++++++++++++++- rules_haskell_nix/non_module_deps.bzl | 9 +++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/rules_haskell_nix/MODULE.bazel b/rules_haskell_nix/MODULE.bazel index f28b3750a..a55a02b63 100644 --- a/rules_haskell_nix/MODULE.bazel +++ b/rules_haskell_nix/MODULE.bazel @@ -8,6 +8,11 @@ bazel_dep( version = "0.16", ) +bazel_dep( + name = "rules_sh", + version = "0.3.0", +) + bazel_dep( name = "rules_nixpkgs_core", version = "0.9.0", @@ -25,4 +30,44 @@ bazel_dep( non_module_deps = use_extension("//:non_module_deps.bzl", "non_module_deps") -use_repo(non_module_deps, "os_info") +use_repo( + non_module_deps, + "os_info", + "nixpkgs_default", +) + +nix_haskell_toolchains = use_extension( + "//extensions:nix_haskell_toolchains.bzl", + "nix_haskell_toolchains", +) + +# Declare a default nix-based toolchain +nix_haskell_toolchains.new( + attribute_path = "", + nix_file_content = """with import {}; haskell.packages.ghc925.ghc""", + repository = "@nixpkgs_default", + version = "9.2.5", +) + +use_repo( + nix_haskell_toolchains, + "nix_haskell_toolchains_configurations", + # Once toochains can be registered directly via the rules_nixpkgs_posix module, + # This repository could be removed from the nix_haskell_toolchains extension. + "all_posix_toolchains", +) + +declare_nix_toolchains = use_extension( + "//private:declare_toolchains.bzl", + "declare_nix_toolchains", +) + +use_repo( + declare_nix_toolchains, + "all_nix_toolchains", +) + +register_toolchains( + "@all_nix_toolchains//:all", + "@all_posix_toolchains//:all", +) diff --git a/rules_haskell_nix/non_module_deps.bzl b/rules_haskell_nix/non_module_deps.bzl index f4b699838..b24144405 100644 --- a/rules_haskell_nix/non_module_deps.bzl +++ b/rules_haskell_nix/non_module_deps.bzl @@ -1,8 +1,17 @@ load("@rules_haskell//tools:os_info.bzl", "os_info") +load( + "@rules_nixpkgs_core//:nixpkgs.bzl", + "nixpkgs_local_repository", +) def repositories(*, bzlmod): os_info(name = "os_info") + nixpkgs_local_repository( + name = "nixpkgs_default", + nix_file = "@rules_haskell//nixpkgs:default.nix", + ) + def _non_module_deps_impl(_ctx): repositories(bzlmod = True) From b6d9e11c68976e8237c7b41fd2f24c35f7812044 Mon Sep 17 00:00:00 2001 From: Yves-Stan Le Cornec Date: Fri, 30 Jun 2023 18:55:54 +0200 Subject: [PATCH 5/6] Remove nix toolchain declaration from non_module_deps_bzlmod.bzl file Because we can now use the ones registered by the `nix_haskell_toolchains` extension. --- rules_haskell_tests/MODULE.bazel | 11 ------- .../non_module_deps_bzlmod.bzl | 30 ------------------- 2 files changed, 41 deletions(-) diff --git a/rules_haskell_tests/MODULE.bazel b/rules_haskell_tests/MODULE.bazel index 2eaf90b52..bc6e9cb26 100644 --- a/rules_haskell_tests/MODULE.bazel +++ b/rules_haskell_tests/MODULE.bazel @@ -176,16 +176,6 @@ non_modules_deps_bzlmod = use_extension( "non_module_deps_bzlmod", ) -use_repo( - non_modules_deps_bzlmod, - "rules_haskell_ghc_nixpkgs_toolchain", - "rules_haskell_ghc_nixpkgs", -) - -register_toolchains( - "@rules_haskell_ghc_nixpkgs_toolchain//:toolchain", -) - [ ( use_repo( @@ -364,7 +354,6 @@ nix_haskell_toolchains = use_extension( ) nix_haskell_toolchains.new( - name = "nix_toolchain_from_tests", attribute_path = "", cabalopts = test_cabalopts, ghcopts = test_ghcopts, diff --git a/rules_haskell_tests/non_module_deps_bzlmod.bzl b/rules_haskell_tests/non_module_deps_bzlmod.bzl index 8dfe527bc..6c6162dac 100644 --- a/rules_haskell_tests/non_module_deps_bzlmod.bzl +++ b/rules_haskell_tests/non_module_deps_bzlmod.bzl @@ -1,38 +1,8 @@ """ This module extension contains non module dependencies which are only used with bzlmod """ -load( - "@rules_haskell_nix//:nixpkgs.bzl", - "haskell_register_ghc_nixpkgs", -) -load( - "@rules_haskell_tests//:non_module_deps_1.bzl", - "test_cabalopts", - "test_ghcopts", - "test_haddock_flags", - "test_repl_ghci_args", -) -load( - "@rules_haskell//:constants.bzl", - "test_ghc_version", -) load("@rules_nixpkgs_nodejs//:nodejs.bzl", "nixpkgs_nodejs_configure_platforms") def _non_module_deps_bzlmod_impl(_ctx): - # Only used with bzlmod, because in WORKSPACE bzlmod we test the - # backward compatible "@rules_haskell//haskell:nixpkgs.bzl" - haskell_register_ghc_nixpkgs( - attribute_path = "", - cabalopts = test_cabalopts, - ghcopts = test_ghcopts, - haddock_flags = test_haddock_flags, - locale_archive = "@glibc_locales//:locale-archive", - nix_file_content = """with import {}; haskell.packages.ghc925.ghc""", - repl_ghci_args = test_repl_ghci_args, - repository = "@nixpkgs_default", - version = test_ghc_version, - register = False, - ) - # Workspace mode uses the asterius_dependencies_nix convenience # macro which sets up the node toolchain and npm. In bzlmod we # make use of rules_js module extension for npm and rely on From abf4e0b5766d32f7c0ca5511e19a20fa7f4df81d Mon Sep 17 00:00:00 2001 From: Yves-Stan Le Cornec Date: Fri, 30 Jun 2023 18:57:32 +0200 Subject: [PATCH 6/6] Add MODULE.bazel file to tutorial folder In bzlmod, the tutorial will use default toolchains registered by rules_haskell and rules_haskell_nix modules. --- .github/workflows/workflow.yaml | 4 ++-- tutorial/MODULE.bazel | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 tutorial/MODULE.bazel diff --git a/.github/workflows/workflow.yaml b/.github/workflows/workflow.yaml index 62493a4ca..46b1a8b82 100644 --- a/.github/workflows/workflow.yaml +++ b/.github/workflows/workflow.yaml @@ -85,8 +85,8 @@ jobs: ./tests/run-start-script.sh --use-nix bazel build //tests:run-tests if [ ${{ matrix.bzlmod }} == bzlmod ]; then - # TODO setup MODULE.bazel files for examples and tutorial folders - ./bazel-ci-bin/tests/run-tests --skip="bazel test examples" --skip="bazel test tutorial" + # TODO setup MODULE.bazel files for examples folder + ./bazel-ci-bin/tests/run-tests --skip="bazel test examples" else ./bazel-ci-bin/tests/run-tests fi diff --git a/tutorial/MODULE.bazel b/tutorial/MODULE.bazel new file mode 100644 index 000000000..e7f4ddab3 --- /dev/null +++ b/tutorial/MODULE.bazel @@ -0,0 +1,21 @@ +bazel_dep( + name = "platforms", + version = "0.0.6", +) + +# rules_haskell_nix must come before rules_haskell so that nix +# toolchains are considered first +bazel_dep( + name = "rules_haskell_nix", + version = "0.16", +) + +bazel_dep( + name = "rules_haskell", + version = "0.16", +) + +bazel_dep( + name = "rules_nixpkgs_core", + version = "0.9.0", +)