diff options
Diffstat (limited to 'third_party/bazel/rules_haskell/haskell/nixpkgs.bzl')
-rw-r--r-- | third_party/bazel/rules_haskell/haskell/nixpkgs.bzl | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/third_party/bazel/rules_haskell/haskell/nixpkgs.bzl b/third_party/bazel/rules_haskell/haskell/nixpkgs.bzl new file mode 100644 index 000000000000..20d77f72193a --- /dev/null +++ b/third_party/bazel/rules_haskell/haskell/nixpkgs.bzl @@ -0,0 +1,354 @@ +"""Workspace rules (Nixpkgs)""" + +load("@bazel_skylib//lib:dicts.bzl", "dicts") +load( + "@io_tweag_rules_nixpkgs//nixpkgs:nixpkgs.bzl", + "nixpkgs_package", +) + +def haskell_nixpkgs_package( + name, + attribute_path, + nix_file_deps = [], + repositories = {}, + build_file_content = None, + build_file = None, + **kwargs): + """Load a single haskell package. + The package is expected to be in the form of the packages generated by + `genBazelBuild.nix` + """ + repositories = dicts.add( + {"bazel_haskell_wrapper": "@io_tweag_rules_haskell//haskell:nix/default.nix"}, + repositories, + ) + + nixpkgs_args = dict( + name = name, + attribute_path = attribute_path, + build_file_content = build_file_content, + nix_file_deps = nix_file_deps + ["@io_tweag_rules_haskell//haskell:nix/default.nix"], + repositories = repositories, + **kwargs + ) + + if build_file_content: + nixpkgs_args["build_file_content"] = build_file_content + elif build_file: + nixpkgs_args["build_file"] = build_file + else: + nixpkgs_args["build_file_content"] = """ +package(default_visibility = ["//visibility:public"]) +load("@io_tweag_rules_haskell//haskell:import.bzl", haskell_import_new = "haskell_import") + +load(":BUILD.bzl", "targets") +targets() +""" + + nixpkgs_package( + **nixpkgs_args + ) + +def _bundle_impl(repository_ctx): + build_file_content = """ +package(default_visibility = ["//visibility:public"]) + """ + for package in repository_ctx.attr.packages: + build_file_content += """ +alias( + name = "{package}", + actual = "@{base_repo}-{package}//:pkg", +) + """.format( + package = package, + base_repo = repository_ctx.attr.base_repository, + ) + repository_ctx.file("BUILD", build_file_content) + +_bundle = repository_rule( + attrs = { + "packages": attr.string_list(), + "base_repository": attr.string(), + }, + implementation = _bundle_impl, +) +""" +Generate an alias from `@base_repo//:package` to `@base_repo-package//:pkg` for +each one of the input package +""" + +def haskell_nixpkgs_packages(name, base_attribute_path, packages, **kwargs): + """Import a set of haskell packages from nixpkgs. + + This takes as input the same arguments as + [nixpkgs_package](https://github.com/tweag/rules_nixpkgs#nixpkgs_package), + expecting the `attribute_path` to resolve to a set of haskell packages + (such as `haskellPackages` or `haskell.packages.ghc822`) preprocessed by + the `genBazelBuild` function. It also takes as input a list of packages to + import (which can be generated by the `gen_packages_list` function). + """ + for package in packages: + haskell_nixpkgs_package( + name = name + "-" + package, + attribute_path = base_attribute_path + "." + package, + **kwargs + ) + _bundle( + name = name, + packages = packages, + base_repository = name, + ) + +def _is_nix_platform(repository_ctx): + return repository_ctx.which("nix-build") != None + +def _gen_imports_impl(repository_ctx): + repository_ctx.file("BUILD", "") + extra_args_raw = "" + for foo, bar in repository_ctx.attr.extra_args.items(): + extra_args_raw += foo + " = " + bar + ", " + bzl_file_content = """ +load("{repo_name}", "packages") +load("@io_tweag_rules_haskell//haskell:nixpkgs.bzl", "haskell_nixpkgs_packages") + +def import_packages(name): + haskell_nixpkgs_packages( + name = name, + packages = packages, + {extra_args_raw} + ) + """.format( + repo_name = repository_ctx.attr.packages_list_file, + extra_args_raw = extra_args_raw, + ) + + # A dummy 'packages.bzl' file with a no-op 'import_packages()' on unsupported platforms + bzl_file_content_unsupported_platform = """ +def import_packages(name): + return + """ + if _is_nix_platform(repository_ctx): + repository_ctx.file("packages.bzl", bzl_file_content) + else: + repository_ctx.file("packages.bzl", bzl_file_content_unsupported_platform) + +_gen_imports_str = repository_rule( + implementation = _gen_imports_impl, + attrs = dict( + packages_list_file = attr.label(doc = "A list containing the list of packages to import"), + # We pass the extra arguments to `haskell_nixpkgs_packages` as strings + # since we can't forward arbitrary arguments in a rule and they will be + # converted to strings anyways. + extra_args = attr.string_dict(doc = "Extra arguments for `haskell_nixpkgs_packages`"), + ), +) +""" +Generate a repository containing a file `packages.bzl` which imports the given +packages list. +""" + +def _gen_imports(name, packages_list_file, extra_args): + """ + A wrapper around `_gen_imports_str` which allows passing an arbitrary set of + `extra_args` instead of a set of strings + """ + extra_args_str = {label: repr(value) for (label, value) in extra_args.items()} + _gen_imports_str( + name = name, + packages_list_file = packages_list_file, + extra_args = extra_args_str, + ) + +def haskell_nixpkgs_packageset(name, base_attribute_path, repositories = {}, **kwargs): + """Import all the available haskell packages. + The arguments are the same as the arguments of ``nixpkgs_package``, except + for the ``base_attribute_path`` which should point to an `haskellPackages` + set in the nix expression + + Example: + + In `haskellPackages.nix`: + + ```nix + with import <nixpkgs> {}; + + let wrapPackages = callPackage <bazel_haskell_wrapper> { }; in + { haskellPackages = wrapPackages haskell.packages.ghc822; } + ``` + + In your `WORKSPACE` + + ```bazel + # Define a nix repository to fetch the packages from + load("@io_tweag_rules_nixpkgs//nixpkgs:nixpkgs.bzl", + "nixpkgs_git_repository") + nixpkgs_git_repository( + name = "nixpkgs", + revision = "9a787af6bc75a19ac9f02077ade58ddc248e674a", + ) + + load("@io_tweag_rules_haskell//haskell:nixpkgs.bzl", + "haskell_nixpkgs_packageset", + + # Generate a list of all the available haskell packages + haskell_nixpkgs_packageset( + name = "hackage-packages", + repositories = {"@nixpkgs": "nixpkgs"}, + nix_file = "//haskellPackages.nix", + base_attribute_path = "haskellPackages", + ) + load("@hackage-packages//:packages.bzl", "import_packages") + import_packages(name = "hackage") + ``` + + Then in your `BUILD` files, you can access to the whole of hackage as + `@hackage//:{your-package-name}` + """ + + repositories = dicts.add( + {"bazel_haskell_wrapper": "@io_tweag_rules_haskell//haskell:nix/default.nix"}, + repositories, + ) + + nixpkgs_package( + name = name + "-packages-list", + attribute_path = base_attribute_path + ".packageNames", + repositories = repositories, + build_file_content = """ +exports_files(["all-haskell-packages.bzl"]) + """, + fail_not_supported = False, + **kwargs + ) + _gen_imports( + name = name, + packages_list_file = "@" + name + "-packages-list//:all-haskell-packages.bzl", + extra_args = dict( + repositories = repositories, + base_attribute_path = base_attribute_path, + **kwargs + ), + ) + +def _ghc_nixpkgs_toolchain_impl(repository_ctx): + # 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. + target_constraints = ["@bazel_tools//platforms:x86_64"] + if repository_ctx.os.name == "linux": + target_constraints.append("@bazel_tools//platforms:linux") + elif repository_ctx.os.name == "mac os x": + target_constraints.append("@bazel_tools//platforms:osx") + exec_constraints = list(target_constraints) + exec_constraints.append("@io_tweag_rules_haskell//haskell/platforms:nixpkgs") + + compiler_flags_select = repository_ctx.attr.compiler_flags_select or {"//conditions:default": []} + locale_archive = repr(repository_ctx.attr.locale_archive or None) + + repository_ctx.file( + "BUILD", + executable = False, + content = """ +load("@io_tweag_rules_haskell//haskell:toolchain.bzl", "haskell_toolchain") + +haskell_toolchain( + name = "toolchain", + tools = ["{tools}"], + version = "{version}", + compiler_flags = {compiler_flags} + {compiler_flags_select}, + haddock_flags = {haddock_flags}, + repl_ghci_args = {repl_ghci_args}, + # On Darwin we don't need a locale archive. It's a Linux-specific + # hack in Nixpkgs. + locale_archive = {locale_archive}, + exec_compatible_with = {exec_constraints}, + target_compatible_with = {target_constraints}, +) + """.format( + tools = "@io_tweag_rules_haskell_ghc-nixpkgs//:bin", + version = repository_ctx.attr.version, + compiler_flags = repository_ctx.attr.compiler_flags, + compiler_flags_select = "select({})".format(compiler_flags_select), + haddock_flags = repository_ctx.attr.haddock_flags, + repl_ghci_args = repository_ctx.attr.repl_ghci_args, + locale_archive = locale_archive, + exec_constraints = exec_constraints, + target_constraints = target_constraints, + ), + ) + +_ghc_nixpkgs_toolchain = repository_rule( + _ghc_nixpkgs_toolchain_impl, + local = False, + attrs = { + # These attributes just forward to haskell_toolchain. + # They are documented there. + "version": attr.string(), + "compiler_flags": attr.string_list(), + "compiler_flags_select": attr.string_list_dict(), + "haddock_flags": attr.string_list(), + "repl_ghci_args": attr.string_list(), + "locale_archive": attr.string(), + }, +) + +def haskell_register_ghc_nixpkgs( + version, + build_file = None, + compiler_flags = None, + compiler_flags_select = None, + haddock_flags = None, + repl_ghci_args = None, + locale_archive = None, + attribute_path = "haskellPackages.ghc", + nix_file = None, + nix_file_deps = [], + repositories = {}): + """Register a package from Nixpkgs as a toolchain. + + Toolchains can be used to compile Haskell code. To have this + toolchain selected during [toolchain + resolution][toolchain-resolution], set a host platform that + includes the `@io_tweag_rules_haskell//haskell/platforms:nixpkgs` + constraint value. + + [toolchain-resolution]: https://docs.bazel.build/versions/master/toolchains.html#toolchain-resolution + + Example: + + ``` + haskell_register_ghc_nixpkgs( + locale_archive = "@glibc_locales//:locale-archive", + atttribute_path = "haskellPackages.ghc", + version = "1.2.3", # The version of GHC + ) + ``` + + Setting the host platform can be done on the command-line like + in the following: + + ``` + --host_platform=@io_tweag_rules_haskell//haskell/platforms:linux_x86_64_nixpkgs + ``` + + """ + haskell_nixpkgs_package( + name = "io_tweag_rules_haskell_ghc-nixpkgs", + attribute_path = attribute_path, + build_file = build_file or "@io_tweag_rules_haskell//haskell:ghc.BUILD", + nix_file = nix_file, + nix_file_deps = nix_file_deps, + repositories = repositories, + ) + _ghc_nixpkgs_toolchain( + name = "io_tweag_rules_haskell_ghc-nixpkgs-toolchain", + version = version, + compiler_flags = compiler_flags, + compiler_flags_select = compiler_flags_select, + haddock_flags = haddock_flags, + repl_ghci_args = repl_ghci_args, + locale_archive = locale_archive, + ) + native.register_toolchains("@io_tweag_rules_haskell_ghc-nixpkgs-toolchain//:toolchain") |