"""Support for c2hs"""
load("@bazel_skylib//lib:paths.bzl", "paths")
load(
"@io_tweag_rules_haskell//haskell:providers.bzl",
"C2hsLibraryInfo",
"HaskellInfo",
)
load(":cc.bzl", "cc_interop_info")
load(":private/context.bzl", "haskell_context")
load(":private/dependencies.bzl", "gather_dep_info")
load(
":private/path_utils.bzl",
"declare_compiled",
"target_unique_name",
)
load(":private/set.bzl", "set")
load(":private/version_macros.bzl", "version_macro_includes")
def _c2hs_library_impl(ctx):
hs = haskell_context(ctx)
cc = cc_interop_info(ctx)
args = hs.actions.args()
c2hs = ctx.toolchains["@io_tweag_rules_haskell//haskell/c2hs:toolchain"].c2hs
if len(ctx.files.srcs) != 1:
fail("srcs field should contain exactly one file.")
chs_file = ctx.files.srcs[0]
# Output a Haskell source file.
chs_dir_raw = target_unique_name(hs, "chs")
hs_file = declare_compiled(hs, chs_file, ".hs", directory = chs_dir_raw)
chi_file = declare_compiled(hs, chs_file, ".chi", directory = chs_dir_raw)
args.add_all([chs_file.path, "-o", hs_file.path])
args.add("-C-E")
args.add_all(["--cpp", cc.tools.cpp])
args.add("-C-includeghcplatform.h")
args.add("-C-includeghcversion.h")
args.add_all(["-C" + x for x in cc.cpp_flags])
args.add_all(["-C" + x for x in cc.include_args])
dep_chi_files = [
dep[C2hsLibraryInfo].chi_file
for dep in ctx.attr.deps
if C2hsLibraryInfo in dep
]
chi_includes = [
"-i" + dep[C2hsLibraryInfo].import_dir
for dep in ctx.attr.deps
if C2hsLibraryInfo in dep
]
args.add_all(chi_includes)
version_macro_headers = set.empty()
if ctx.attr.version:
dep_info = gather_dep_info(ctx, ctx.attr.deps)
(version_macro_headers, version_macro_flags) = version_macro_includes(dep_info)
args.add_all(["-C" + x for x in version_macro_flags])
hs.actions.run_shell(
inputs = depset(transitive = [
depset(cc.hdrs),
depset([hs.tools.ghc, c2hs, chs_file]),
depset(dep_chi_files),
depset(cc.files),
set.to_depset(version_macro_headers),
]),
outputs = [hs_file, chi_file],
command = """
# Include libdir in include path just like hsc2hs does.
libdir=$({ghc} --print-libdir)
{c2hs} -C-I$libdir/include "$@"
""".format(
ghc = hs.tools.ghc.path,
c2hs = c2hs.path,
),
mnemonic = "HaskellC2Hs",
arguments = [args],
env = hs.env,
)
idir = paths.join(
hs.bin_dir.path,
hs.label.workspace_root,
hs.label.package,
chs_dir_raw,
)
return [
DefaultInfo(files = depset([hs_file])),
C2hsLibraryInfo(
chi_file = chi_file,
import_dir = idir,
),
]
c2hs_library = rule(
_c2hs_library_impl,
attrs = {
"deps": attr.label_list(),
"srcs": attr.label_list(allow_files = [".chs"]),
"src_strip_prefix": attr.string(
doc = "Directory in which module hierarchy starts.",
),
"version": attr.string(
doc = "Executable version. If this is specified, CPP version macros will be generated for this build.",
),
"_cc_toolchain": attr.label(
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
),
},
toolchains = [
"@io_tweag_rules_haskell//haskell:toolchain",
"@io_tweag_rules_haskell//haskell/c2hs:toolchain",
],
)
def _c2hs_toolchain_impl(ctx):
return [
platform_common.ToolchainInfo(
name = ctx.label.name,
c2hs = ctx.file.c2hs,
),
]
_c2hs_toolchain = rule(
_c2hs_toolchain_impl,
attrs = {
"c2hs": attr.label(
doc = "The c2hs executable.",
mandatory = True,
allow_single_file = True,
),
},
)
def c2hs_toolchain(name, c2hs, **kwargs):
"""Declare a Haskell c2hs toolchain.
You need at least one of these declared somewhere in your `BUILD`
files for the `chs_library` rule to work. Once declared, you then
need to *register* the toolchain using `register_toolchains` in
your `WORKSPACE` file (see example below).
Example:
In a `BUILD` file:
```bzl
c2hs_toolchain(
name = "c2hs",
c2hs = "@c2hs//:bin",
)
```
where `@c2hs` is an external repository defined in the
`WORKSPACE`, e.g. using:
```bzl
nixpkgs_package(
name = "c2hs",
attribute_path = "haskell.packages.ghc822.c2hs",
)
register_toolchains("//:c2hs")
```
"""
impl_name = name + "-impl"
_c2hs_toolchain(
name = impl_name,
c2hs = c2hs,
visibility = ["//visibility:public"],
**kwargs
)
native.toolchain(
name = name,
toolchain_type = "@io_tweag_rules_haskell//haskell/c2hs:toolchain",
toolchain = ":" + impl_name,
)