diff --git a/haskell/BUILD.bazel b/haskell/BUILD.bazel index 8bdd99223..a39ed4967 100644 --- a/haskell/BUILD.bazel +++ b/haskell/BUILD.bazel @@ -12,6 +12,10 @@ load( "@rules_haskell//haskell:cabal_wrapper.bzl", "cabal_wrapper", ) +load( + "@rules_haskell//haskell:private/runghc.bzl", + "runghc", +) load( "@rules_haskell//haskell:toolchain_info.bzl", "haskell_toolchain_info", @@ -99,6 +103,11 @@ py_library( visibility = ["//tests/package_configuration:__subpackages__"], ) +runghc( + name = "runghc", + visibility = ["//visibility:public"], +) + py_binary( name = "ls_modules", srcs = ["private/ls_modules.py"], diff --git a/haskell/cabal.bzl b/haskell/cabal.bzl index 61d72a2b8..fd01f8c65 100644 --- a/haskell/cabal.bzl +++ b/haskell/cabal.bzl @@ -153,7 +153,7 @@ def _concat(sequences): def _uniquify(xs): return depset(xs).to_list() -def _cabal_toolchain_info(hs, cc, workspace_name): +def _cabal_toolchain_info(hs, cc, workspace_name, runghc): """Yields a struct containing the toolchain information needed by the cabal wrapper""" # If running on darwin but XCode is not installed (i.e., only the Command @@ -168,7 +168,7 @@ def _cabal_toolchain_info(hs, cc, workspace_name): return struct( ghc = hs.tools.ghc.path, ghc_pkg = hs.tools.ghc_pkg.path, - runghc = hs.tools.runghc.path, + runghc = runghc.path, ar = ar, cc = cc.tools.cc, strip = cc.tools.strip, @@ -198,6 +198,7 @@ def _prepare_cabal_inputs( flags, generate_haddock, cabal_wrapper, + runghc, package_database, verbose, transitive_haddocks, @@ -345,11 +346,11 @@ def _prepare_cabal_inputs( runghc_args = runghc_args, extra_args = extra_args, path_args = path_args, - toolchain_info = _cabal_toolchain_info(hs, cc, workspace_name), + toolchain_info = _cabal_toolchain_info(hs, cc, workspace_name, runghc), ) inputs = depset( - [setup, hs.tools.ghc, hs.tools.ghc_pkg, hs.tools.runghc], + [setup, hs.tools.ghc, hs.tools.ghc_pkg], transitive = [ depset(srcs), depset(cc.files), @@ -503,6 +504,7 @@ def _haskell_cabal_library_impl(ctx): flags = ctx.attr.flags, generate_haddock = ctx.attr.haddock, cabal_wrapper = ctx.executable._cabal_wrapper, + runghc = ctx.executable._runghc, package_database = package_database, verbose = ctx.attr.verbose, is_library = True, @@ -522,14 +524,15 @@ def _haskell_cabal_library_impl(ctx): if with_profiling: outputs.append(profiling_library) + (_, runghc_manifest) = ctx.resolve_tools(tools = [ctx.attr._runghc]) json_args = ctx.actions.declare_file("{}_cabal_wrapper_args.json".format(ctx.label.name)) ctx.actions.write(json_args, c.args.to_json()) ctx.actions.run( executable = c.cabal_wrapper, arguments = [json_args.path], inputs = depset([json_args], transitive = [c.inputs]), - input_manifests = c.input_manifests, - tools = [c.cabal_wrapper], + input_manifests = c.input_manifests + runghc_manifest, + tools = [c.cabal_wrapper, ctx.executable._runghc], outputs = outputs, env = c.env, mnemonic = "HaskellCabalLibrary", @@ -666,6 +669,11 @@ haskell_cabal_library = rule( cfg = "host", default = Label("@rules_haskell//haskell:cabal_wrapper"), ), + "_runghc": attr.label( + executable = True, + cfg = "host", + default = Label("@rules_haskell//haskell:runghc"), + ), "_cc_toolchain": attr.label( default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), ), @@ -789,24 +797,26 @@ def _haskell_cabal_binary_impl(ctx): flags = ctx.attr.flags, generate_haddock = False, cabal_wrapper = ctx.executable._cabal_wrapper, + runghc = ctx.executable._runghc, package_database = package_database, verbose = ctx.attr.verbose, dynamic_file = binary, transitive_haddocks = _gather_transitive_haddocks(ctx.attr.deps), ) + (_, runghc_manifest) = ctx.resolve_tools(tools = [ctx.attr._runghc]) json_args = ctx.actions.declare_file("{}_cabal_wrapper_args.json".format(ctx.label.name)) ctx.actions.write(json_args, c.args.to_json()) ctx.actions.run( executable = c.cabal_wrapper, arguments = [json_args.path], inputs = depset([json_args], transitive = [c.inputs]), - input_manifests = c.input_manifests, + input_manifests = c.input_manifests + runghc_manifest, outputs = [ package_database, binary, data_dir, ], - tools = [c.cabal_wrapper], + tools = [c.cabal_wrapper, ctx.executable._runghc], env = c.env, mnemonic = "HaskellCabalBinary", progress_message = "HaskellCabalBinary {}".format(hs.label), @@ -881,6 +891,11 @@ haskell_cabal_binary = rule( cfg = "host", default = Label("@rules_haskell//haskell:cabal_wrapper"), ), + "_runghc": attr.label( + executable = True, + cfg = "host", + default = Label("@rules_haskell//haskell:runghc"), + ), "_cc_toolchain": attr.label( default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), ), diff --git a/haskell/private/runghc.bzl b/haskell/private/runghc.bzl new file mode 100644 index 000000000..7029972e8 --- /dev/null +++ b/haskell/private/runghc.bzl @@ -0,0 +1,64 @@ +load("@bazel_skylib//lib:paths.bzl", "paths") +load("@rules_python//python:defs.bzl", "py_binary") + +# Note [Running Setup.hs when cross-compiling] +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# When cross compiling we interpret Setup.hs in the execution +# platform. In order to achieve this, we provide a runghc of a +# toolchain targeting the execution platform. +# +# Producing a runghc wrapper with a custom rule, as defined here, +# allows to use it in rules which are using a Haskell toolchain for +# the target platform. +# + +def _runghc_wrapper_impl(ctx): + hs_toolchain = ctx.toolchains["@rules_haskell//haskell:toolchain"] + + f = hs_toolchain.tools.runghc + runghc_runfile_path = paths.join(f.owner.workspace_name, f.owner.package, f.owner.name) + runghc_wrapper_file = ctx.actions.declare_file(ctx.label.name) + ctx.actions.write( + output = runghc_wrapper_file, + content = """\ +#!/usr/bin/env python3 + +import subprocess +import sys +from rules_python.python.runfiles import runfiles + +r = runfiles.Create() + +subprocess.run([r.Rlocation("{runghc}")] + sys.argv[1:], check=True) +""".format(runghc = runghc_runfile_path), + is_executable = True, + ) + + return [DefaultInfo( + executable = runghc_wrapper_file, + runfiles = hs_toolchain.cc_wrapper.runfiles.merge( + ctx.runfiles(files = [runghc_wrapper_file, hs_toolchain.tools.runghc]), + ), + )] + +_runghc_wrapper = rule( + executable = True, + implementation = _runghc_wrapper_impl, + toolchains = ["@rules_haskell//haskell:toolchain"], + doc = """Produces the runghc wrapper script.""", +) + +def runghc(name, **kwargs): + _runghc_wrapper(name = name + ".py") + py_binary( + name = name, + srcs = [name + ".py"], + data = [name + ".py"], + srcs_version = "PY3", + python_version = "PY3", + deps = [ + "@rules_python//python/runfiles", + ], + **kwargs + )