about summary refs log tree commit diff
path: root/third_party/bazel/rules_haskell/haskell/private/actions/repl.bzl
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/bazel/rules_haskell/haskell/private/actions/repl.bzl')
-rw-r--r--third_party/bazel/rules_haskell/haskell/private/actions/repl.bzl175
1 files changed, 175 insertions, 0 deletions
diff --git a/third_party/bazel/rules_haskell/haskell/private/actions/repl.bzl b/third_party/bazel/rules_haskell/haskell/private/actions/repl.bzl
new file mode 100644
index 000000000000..5de64955d0af
--- /dev/null
+++ b/third_party/bazel/rules_haskell/haskell/private/actions/repl.bzl
@@ -0,0 +1,175 @@
+"""GHCi REPL support"""
+
+load(":private/context.bzl", "render_env")
+load(":private/packages.bzl", "expose_packages", "pkg_info_to_compile_flags")
+load(
+    ":private/path_utils.bzl",
+    "get_lib_name",
+    "is_shared_library",
+    "link_libraries",
+    "ln",
+    "target_unique_name",
+)
+load(":providers.bzl", "get_libs_for_ghc_linker")
+load(
+    ":private/set.bzl",
+    "set",
+)
+load("@bazel_skylib//lib:paths.bzl", "paths")
+load("@bazel_skylib//lib:shell.bzl", "shell")
+
+def build_haskell_repl(
+        hs,
+        ghci_script,
+        ghci_repl_wrapper,
+        user_compile_flags,
+        repl_ghci_args,
+        hs_info,
+        output,
+        package_databases,
+        version,
+        lib_info = None):
+    """Build REPL script.
+
+    Args:
+      hs: Haskell context.
+      hs_info: HaskellInfo.
+
+      package_databases: package caches excluding the cache file of the package
+                      we're creating a REPL for.
+      lib_info: If we're building REPL for a library target, pass
+                HaskellLibraryInfo here, otherwise it should be None.
+
+    Returns:
+      None.
+    """
+
+    # The base and directory packages are necessary for the GHCi script we use
+    # (loads source files and brings in scope the corresponding modules).
+    args = ["-package", "base", "-package", "directory"]
+
+    pkg_ghc_info = expose_packages(
+        hs_info,
+        lib_info,
+        use_direct = False,
+        use_my_pkg_id = None,
+        custom_package_databases = package_databases,
+        version = version,
+    )
+    args += pkg_info_to_compile_flags(pkg_ghc_info)
+
+    lib_imports = []
+    if lib_info != None:
+        for idir in set.to_list(hs_info.import_dirs):
+            args += ["-i{0}".format(idir)]
+            lib_imports.append(idir)
+
+    link_ctx = hs_info.cc_dependencies.dynamic_linking
+    libs_to_link = link_ctx.dynamic_libraries_for_runtime.to_list()
+
+    # External C libraries that we need to make available to the REPL.
+    libraries = link_libraries(libs_to_link, args)
+
+    # Transitive library dependencies to have in runfiles.
+    (library_deps, ld_library_deps, ghc_env) = get_libs_for_ghc_linker(
+        hs,
+        hs_info.transitive_cc_dependencies,
+        path_prefix = "$RULES_HASKELL_EXEC_ROOT",
+    )
+    library_path = [paths.dirname(lib.path) for lib in library_deps]
+    ld_library_path = [paths.dirname(lib.path) for lib in ld_library_deps]
+
+    repl_file = hs.actions.declare_file(target_unique_name(hs, "repl"))
+
+    add_sources = ["*" + f.path for f in set.to_list(hs_info.source_files)]
+
+    ghci_repl_script = hs.actions.declare_file(
+        target_unique_name(hs, "ghci-repl-script"),
+    )
+    hs.actions.expand_template(
+        template = ghci_script,
+        output = ghci_repl_script,
+        substitutions = {
+            "{ADD_SOURCES}": " ".join(add_sources),
+            "{COMMANDS}": "",
+        },
+    )
+
+    # Extra arguments.
+    # `compiler flags` is the default set of arguments for the repl,
+    # augmented by `repl_ghci_args`.
+    # The ordering is important, first compiler flags (from toolchain
+    # and local rule), then from `repl_ghci_args`. This way the more
+    # specific arguments are listed last, and then have more priority in
+    # GHC.
+    # Note that most flags for GHCI do have their negative value, so a
+    # negative flag in `repl_ghci_args` can disable a positive flag set
+    # in `user_compile_flags`, such as `-XNoOverloadedStrings` will disable
+    # `-XOverloadedStrings`.
+    args += hs.toolchain.compiler_flags + user_compile_flags + hs.toolchain.repl_ghci_args + repl_ghci_args
+
+    hs.actions.expand_template(
+        template = ghci_repl_wrapper,
+        output = repl_file,
+        substitutions = {
+            "{ENV}": render_env(ghc_env),
+            "{TOOL}": hs.tools.ghci.path,
+            "{ARGS}": " ".join(
+                [
+                    "-ghci-script",
+                    paths.join("$RULES_HASKELL_EXEC_ROOT", ghci_repl_script.path),
+                ] + [
+                    shell.quote(a)
+                    for a in args
+                ],
+            ),
+        },
+        is_executable = True,
+    )
+
+    ghc_info = struct(
+        has_version = pkg_ghc_info.has_version,
+        library_path = library_path,
+        ld_library_path = ld_library_path,
+        packages = pkg_ghc_info.packages,
+        package_ids = pkg_ghc_info.package_ids,
+        package_dbs = pkg_ghc_info.package_dbs,
+        lib_imports = lib_imports,
+        libraries = libraries,
+        execs = struct(
+            ghc = hs.tools.ghc.path,
+            ghci = hs.tools.ghci.path,
+            runghc = hs.tools.runghc.path,
+        ),
+        flags = struct(
+            compiler = user_compile_flags,
+            toolchain_compiler = hs.toolchain.compiler_flags,
+            repl = repl_ghci_args,
+            toolchain_repl = hs.toolchain.repl_ghci_args,
+        ),
+    )
+    ghc_info_file = hs.actions.declare_file(
+        target_unique_name(hs, "ghc-info"),
+    )
+    hs.actions.write(
+        output = ghc_info_file,
+        content = ghc_info.to_json(),
+    )
+
+    # XXX We create a symlink here because we need to force
+    # hs.tools.ghci and ghci_script and the best way to do that is
+    # to use hs.actions.run. That action, in turn must produce
+    # a result, so using ln seems to be the only sane choice.
+    extra_inputs = depset(transitive = [
+        depset([
+            hs.tools.ghci,
+            ghci_repl_script,
+            repl_file,
+            ghc_info_file,
+        ]),
+        set.to_depset(package_databases),
+        depset(library_deps),
+        depset(ld_library_deps),
+        set.to_depset(hs_info.source_files),
+    ])
+    ln(hs, repl_file, output, extra_inputs)