about summary refs log tree commit diff
path: root/third_party/bazel/rules_haskell/haskell/c2hs.bzl
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/bazel/rules_haskell/haskell/c2hs.bzl')
-rw-r--r--third_party/bazel/rules_haskell/haskell/c2hs.bzl183
1 files changed, 183 insertions, 0 deletions
diff --git a/third_party/bazel/rules_haskell/haskell/c2hs.bzl b/third_party/bazel/rules_haskell/haskell/c2hs.bzl
new file mode 100644
index 000000000000..5dcf4a2cf31d
--- /dev/null
+++ b/third_party/bazel/rules_haskell/haskell/c2hs.bzl
@@ -0,0 +1,183 @@
+"""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,
+    )