Skip to content

Commit

Permalink
Add haskell_toolchains module extension
Browse files Browse the repository at this point in the history
This extension is used to register the haskell bindists with bzlmod.
  • Loading branch information
ylecornec committed Jun 28, 2023
1 parent 32e06bb commit ff84233
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 39 deletions.
48 changes: 27 additions & 21 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,12 @@ rules_haskell_dependencies = use_extension(

use_repo(
rules_haskell_dependencies,
"rules_haskell_python_local",
"rules_haskell_worker_dependencies",
"rules_haskell_stack_update",
"rules_haskell_stack",
"Cabal",
)

register_toolchains("@rules_haskell_python_local//:toolchain")

asterius = use_extension("@rules_haskell//haskell/asterius:extension.bzl", "rules_haskell_asterius")

use_repo(
Expand All @@ -83,6 +80,33 @@ use_repo(
"rules_haskell_asterius_build_setting",
)

haskell_toolchains = use_extension(
"@rules_haskell//extensions:haskell_toolchains.bzl",
"haskell_toolchains",
)

haskell_toolchains.bindists()

use_repo(
haskell_toolchains,
"all_bindist_toolchains",
"rules_haskell_python_local",
"rules_haskell_sh_posix_local",
)

# We need to register the nix toolchain before the bindist ones so it
# take precedence when we use the nixpkgs config.
register_toolchains(
"@rules_haskell_ghc_nixpkgs_toolchain//:toolchain",
dev_dependency = True,
)

register_toolchains(
"@all_bindist_toolchains//:all",
"@rules_haskell_python_local//:toolchain",
"@rules_haskell_sh_posix_local//:all",
)

# Setup node toolchain and install webpack for asterius.
node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node")

Expand Down Expand Up @@ -165,24 +189,6 @@ use_repo(
"rules_haskell_ghc_nixpkgs",
"nixpkgs_default",
"rules_haskell_ghc_nixpkgs_toolchain",
"rules_haskell_ghc_darwin_amd64-toolchain",
"rules_haskell_ghc_darwin_arm64-toolchain",
"rules_haskell_ghc_linux_amd64-toolchain",
"rules_haskell_ghc_windows_amd64-toolchain",
"rules_haskell_ghc_linux_arm64-toolchain",
"rules_haskell_ghc_windows_amd64_cc_toolchain",
)

register_toolchains(
"@rules_haskell_ghc_nixpkgs_toolchain//:toolchain",
"@rules_haskell_ghc_darwin_amd64-toolchain//:toolchain",
"@rules_haskell_ghc_darwin_arm64-toolchain//:toolchain",
"@rules_haskell_ghc_linux_amd64-toolchain//:toolchain",
"@rules_haskell_ghc_linux_arm64-toolchain//:toolchain",
"@rules_haskell_ghc_windows_amd64-toolchain//:toolchain",
"@rules_haskell_ghc_windows_amd64-toolchain//:toolchain",
"@rules_haskell_ghc_windows_amd64_cc_toolchain//:windows_cc_toolchain",
dev_dependency = True,
)

use_repo(
Expand Down
165 changes: 165 additions & 0 deletions extensions/haskell_toolchains.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
""" Module extension to install bindist haskell toolchains"""

load(
"@rules_haskell//haskell:ghc_bindist.bzl",
"ghc_bindist",
"ghc_bindist_toolchain_declaration",
"ghc_bindists_toolchain_declarations",
"haskell_register_ghc_bindists",
)

_bindists_tag = tag_class(
attrs = {
"version": attr.string(
doc = "[see rules_haskell_toolchains](toolchain.html#rules_haskell_toolchains-version)",
),
"ghcopts": attr.string_list(
doc = "[see rules_haskell_toolchains](toolchain.html#rules_haskell_toolchains-ghcopts)",
),
"haddock_flags": attr.string_list(
doc = "haddock_flags: [see rules_haskell_toolchains](toolchain.html#rules_haskell_toolchains-haddock_flags)",
),
"repl_ghci_args": attr.string_list(
doc = "[see rules_haskell_toolchains](toolchain.html#rules_haskell_toolchains-repl_ghci_args)",
),
"cabalopts": attr.string_list(
doc = "[see rules_haskell_toolchains](toolchain.html#rules_haskell_toolchains-cabalopts)",
),
"locale": attr.string(
doc = "[see rules_haskell_toolchains](toolchain.html#rules_haskell_toolchains-locale)",
),
},
doc = """See [rules_haskell_toolchains](toolchain.html#rules_haskell_toolchains).
Only the first `bindists` tag is taken into account (according to the iteration order over modules).
""",
)

_bindist_tag = tag_class(
attrs = {
"name": attr.string(
mandatory = True,
doc = "A unique name for the repository",
),
"version": attr.string(
mandatory = True,
doc = "The desired GHC version",
),
"target": attr.string(
mandatory = True,
doc = "The desired architecture (See [ghc_bindist_generated.bzl](https://github.com/tweag/rules_haskell/blob/master/haskell/private/ghc_bindist_generated.bzl))",
),
"ghcopts": attr.string_list(
doc = "[see rules_haskell_toolchains](toolchain.html#rules_haskell_toolchains-ghcopts)",
),
"haddock_flags": attr.string_list(
doc = "[see rules_haskell_toolchains](toolchain.html#rules_haskell_toolchains-haddock_flags)",
),
"repl_ghci_args": attr.string_list(
doc = "[see rules_haskell_toolchains](toolchain.html#rules_haskell_toolchains-repl_ghci_args) ",
),
"cabalopts": attr.string_list(
doc = "[see rules_haskell_toolchains](toolchain.html#rules_haskell_toolchains-cabalopts)",
),
"locale": attr.string(
doc = "[see rules_haskell_toolchains](toolchain.html#rules_haskell_toolchains-locale)",
),
},
doc = "Declares and configure a bindist haskell toolchain. See [ghc_bindist](ghc_bindist.html#ghc_bindist).",
)

def _all_toolchains_impl(rctx):
content = "\n".join(rctx.attr.toolchains)
rctx.file("BUILD.bazel", content = content)

_all_toolchains = repository_rule(
implementation = _all_toolchains_impl,
attrs = {
"toolchains": attr.string_list(
doc = "All the `toolchain(...)` declarations for the bindist toolchains as string",
),
},
doc = """ Used to generate the `all_bindist_toolchains` external repository.
We can then invoque `register_toolchains("@all_bindist_toolchains//:all")` in the MODULE.bazel file.
""",
)

def _haskell_toolchains_impl(mctx):
# We gather the declarations of all the toolchains in the `toolchain_declarations` list
# in order to write them to the `@all_bindist_toolchains` repository.

# The code could be simplified a bit by registering aliases to the
# `toolchain` rules, once the following issue is resolved
# https://github.com/bazelbuild/bazel/issues/16298
toolchain_declarations = []

toolchain_names = []
found_bindists = False
for module in mctx.modules:
for bindist_tag in module.tags.bindist:
name = "bindist_{}_{}_{}".format(module.name, module.version, bindist_tag.name)
if name in toolchain_names:
fail(
"""Module "{module}~{version}" used the "bindist" tag twice with the "{tag_name}" name.""".format(
tag_name = bindist_tag.name,
module = module.name,
version = module.version,
),
)
else:
toolchain_names.append(name)
ghc_bindist(
name = name,
version = bindist_tag.version,
target = bindist_tag.target,
ghcopts = bindist_tag.ghcopts,
haddock_flags = bindist_tag.haddock_flags,
repl_ghci_args = bindist_tag.repl_ghci_args,
cabalopts = bindist_tag.cabalopts,
locale = bindist_tag.locale,
register = False,
)
toolchain_declarations.append(
ghc_bindist_toolchain_declaration(
target = bindist_tag.target,
bindist_name = name,
toolchain_name = name,
),
)

if len(module.tags.bindists) > 1:
fail(
"""Module "{module}~{version}" used the "bindists" tag more than once.""".format(
module = module.name,
version = module.version,
),
)
for bindists_tag in module.tags.bindists:
# We only consider the first `bindists` tag, because subsequent
# ones would have the same constraints and lower priority.
if not found_bindists:
found_bindists = True
haskell_register_ghc_bindists(
version = bindists_tag.version,
ghcopts = bindists_tag.ghcopts,
haddock_flags = bindists_tag.haddock_flags,
repl_ghci_args = bindists_tag.repl_ghci_args,
cabalopts = bindists_tag.cabalopts,
locale = bindists_tag.locale,
register = False,
)
toolchain_declarations.extend(
ghc_bindists_toolchain_declarations(bindists_tag.version),
)

_all_toolchains(
name = "all_bindist_toolchains",
toolchains = toolchain_declarations,
)

haskell_toolchains = module_extension(
implementation = _haskell_toolchains_impl,
tag_classes = {
"bindist": _bindist_tag,
"bindists": _bindists_tag,
},
)
3 changes: 0 additions & 3 deletions extensions/rules_haskell_dependencies.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
def repositories(*, bzlmod):
rules_haskell_dependencies_bzlmod()

if LOCAL_PYTHON_REPO_NAME not in native.existing_rules():
configure_python3_toolchain(name = LOCAL_PYTHON_REPO_NAME, register = not bzlmod)

# For persistent worker (tools/worker)
# TODO: make this customizable via a module extension so that users
# of persistant workers can use dependencies compatible with the
Expand Down
83 changes: 68 additions & 15 deletions rules_haskell_tests/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,7 @@ use_repo(
non_modules_deps_1,
"glibc_locales",
"nixpkgs_default",
"rules_haskell_ghc_darwin_amd64-toolchain",
"rules_haskell_ghc_darwin_arm64-toolchain",
"rules_haskell_ghc_linux_amd64-toolchain",
"rules_haskell_ghc_windows_amd64-toolchain",
"rules_haskell_ghc_linux_arm64-toolchain",
"rules_haskell_ghc_windows_amd64_cc_toolchain",
"nixpkgs_python_toolchain",
"rules_haskell_python_local",
"nixpkgs_config_cc",
"nixpkgs_config_cc_info",
"nixpkgs_config_cc_toolchains",
Expand All @@ -155,7 +148,6 @@ register_toolchains(
"@linux_amd64_asterius-toolchain//:toolchain",
"@linux_amd64_asterius-toolchain//:asterius_toolchain",
"@linux_amd64_asterius-toolchain//:wasm_cc_toolchain",
"@rules_haskell_python_local//:toolchain",
"@nixpkgs_python_toolchain//:toolchain",
)

Expand Down Expand Up @@ -195,13 +187,6 @@ use_repo(

register_toolchains(
"@rules_haskell_ghc_nixpkgs_toolchain//:toolchain",
"@rules_haskell_ghc_darwin_amd64-toolchain//:toolchain",
"@rules_haskell_ghc_darwin_arm64-toolchain//:toolchain",
"@rules_haskell_ghc_linux_amd64-toolchain//:toolchain",
"@rules_haskell_ghc_linux_arm64-toolchain//:toolchain",
"@rules_haskell_ghc_windows_amd64-toolchain//:toolchain",
"@rules_haskell_ghc_windows_amd64-toolchain//:toolchain",
"@rules_haskell_ghc_windows_amd64_cc_toolchain//:windows_cc_toolchain",
)

[
Expand Down Expand Up @@ -308,3 +293,71 @@ use_repo(
rules_haskell_dependencies,
"zlib",
)

haskell_toolchains = use_extension(
"@rules_haskell//extensions:haskell_toolchains.bzl",
"haskell_toolchains",
)

test_ghc_version = "9.2.5"

test_ghcopts = [
"-XStandaloneDeriving", # Flag used at compile time
"-threaded", # Flag used at link time
# Used by `tests/repl-flags`
"-DTESTS_TOOLCHAIN_COMPILER_FLAGS",
# this is the default, so it does not harm other tests
"-XNoOverloadedStrings",
]

test_haddock_flags = ["-U"]

test_repl_ghci_args = [
# The repl test will need this flag, but set by the local
# `repl_ghci_args`.
"-UTESTS_TOOLCHAIN_REPL_FLAGS",
# The repl test will need OverloadedString
"-XOverloadedStrings",
]

test_cabalopts = [
# Used by `tests/cabal-toolchain-flags`
"--ghc-option=-DTESTS_TOOLCHAIN_CABALOPTS",
"--haddock-option=--optghc=-DTESTS_TOOLCHAIN_CABALOPTS",
]

cabalopts_windows = test_cabalopts + [
# To avoid ghcide linking errors with heapsize on Windows of the form
#
# unknown symbol `heap_view_closurePtrs'
#
# See https://github.com/haskell/ghcide/pull/954
"--disable-library-for-ghci",
]

haskell_toolchains.bindists(
cabalopts = test_cabalopts,
ghcopts = test_ghcopts,
haddock_flags = test_haddock_flags,
repl_ghci_args = test_repl_ghci_args,
)

# We cannot configure cabalopts depending on the platform before calling haskell_toolchains.bindists:
# https://github.com/bazelbuild/bazel/issues/17880
# So we add toolchains for windows separately, they take precedence over the one generated by
# haskell_toolchains.bindists and use the `cabalopts_windows` variable.
[
haskell_toolchains.bindist(
name = "windows_{}".format(target),
cabalopts = cabalopts_windows,
ghcopts = test_ghcopts,
haddock_flags = test_haddock_flags,
repl_ghci_args = test_repl_ghci_args,
target = target,
version = test_ghc_version,
)
for target in [
"windows_amd64",
"windows_arm64",
]
]

0 comments on commit ff84233

Please sign in to comment.