From 3dc07f86d7a3ab5b55162795e106b0d9f4e972a8 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Fri, 28 Feb 2020 13:29:35 +0100 Subject: [PATCH 1/5] Factor out collecting GHC arguments --- haskell/repl.bzl | 75 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/haskell/repl.bzl b/haskell/repl.bzl index 897a001d8..f04fc6b21 100644 --- a/haskell/repl.bzl +++ b/haskell/repl.bzl @@ -220,33 +220,37 @@ def _create_HaskellReplInfo(from_source, from_binary, collect_info): dep_info = dep_info, ) -def _create_repl(hs, posix, ctx, repl_info, output): - """Build a multi target REPL. +def _compiler_flags_and_inputs(hs, repl_info, path_prefix = ""): + """Collect compiler flags and inputs. + + Compiler flags: + - Package databases and identifiers. + - Linker flags for C library dependencies. + - Haskell include directory flags. + + Inputs: + - Source files. + - Package databases. + - C library dependencies. + - Locale archive if required. Args: hs: Haskell context. - ctx: Rule context. - repl_info: HaskellReplInfo provider. - output: The output for the executable REPL script. + args: list of string, output, the arguments to extend. + path_prefix: string, optional, Prefix for package db paths. Returns: - List of providers: - DefaultInfo provider for the executable REPL script. - + (args, inputs): + args: list of string, the compiler flags. + inputs: depset of File, inputs required by the compiler. """ - - # The base and directory packages are necessary for the GHCi script we use - # (loads source files and brings in scope the corresponding modules). - args = ["-hide-all-packages", "-package", "base", "-package", "directory"] + args = [] # Load built dependencies (-package-id, -package-db) for package_id in repl_info.dep_info.package_ids: args.extend(["-package-id", package_id]) for package_cache in repl_info.dep_info.package_databases.to_list(): - args.extend([ - "-package-db", - paths.join("$RULES_HASKELL_EXEC_ROOT", package_cache.dirname), - ]) + args.extend(["-package-db", paths.join(path_prefix, package_cache.dirname)]) # Load C library dependencies cc_libraries_info = merge_HaskellCcLibrariesInfo(infos = [ @@ -258,7 +262,6 @@ def _create_repl(hs, posix, ctx, repl_info, output): repl_info.dep_info.cc_info, ]) all_libraries = cc_info.linking_context.libraries_to_link.to_list() - input_libraries = get_ghci_library_files(hs, cc_libraries_info, all_libraries) cc_libraries = get_cc_libraries(cc_libraries_info, all_libraries) link_libraries( get_ghci_library_files(hs, cc_libraries_info, cc_libraries), @@ -269,6 +272,37 @@ def _create_repl(hs, posix, ctx, repl_info, output): for import_dir in repl_info.load_info.import_dirs.to_list(): args.append("-i" + (import_dir if import_dir else ".")) + inputs = depset(transitive = [ + repl_info.load_info.source_files, + repl_info.dep_info.package_databases, + depset(get_ghci_library_files(hs, cc_libraries_info, all_libraries)), + depset([hs.toolchain.locale_archive] if hs.toolchain.locale_archive else []), + ]) + + return (args, inputs) + +def _create_repl(hs, posix, ctx, repl_info, output): + """Build a multi target REPL. + + Args: + hs: Haskell context. + ctx: Rule context. + repl_info: HaskellReplInfo provider. + output: The output for the executable REPL script. + + Returns: + List of providers: + DefaultInfo provider for the executable REPL script. + + """ + + # The base and directory packages are necessary for the GHCi script we use + # (loads source files and brings in scope the corresponding modules). + args = ["-hide-all-packages", "-package", "base", "-package", "directory"] + + compiler_flags, inputs = _compiler_flags_and_inputs(hs, repl_info, path_prefix = "$RULES_HASKELL_EXEC_ROOT") + args.extend(compiler_flags) + # Load source files # Force loading by source with `:add *...`. # See https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html#ghci-cmd-:add @@ -335,12 +369,7 @@ def _create_repl(hs, posix, ctx, repl_info, output): hs.tools.ghci, ghci_repl_script, ], - transitive_files = depset(transitive = [ - repl_info.load_info.source_files, - repl_info.dep_info.package_databases, - depset(input_libraries), - depset([hs.toolchain.locale_archive] if hs.toolchain.locale_archive else []), - ]), + transitive_files = inputs, collect_data = ctx.attr.collect_data, ).merge( hs.toolchain.cc_wrapper.runfiles, From 9a9be0061989c3589fa953528f78c04084cefb29 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Mon, 2 Mar 2020 19:48:12 +0100 Subject: [PATCH 2/5] Factor out cc_wrapper pgm flags These flags were duplicated in the toolchain, doctest, and the repl wrapper. Now they are defined in a single location. --- haskell/cc.bzl | 29 ++++++++++++++++++++++++++++ haskell/doctest.bzl | 22 ++------------------- haskell/private/ghci_repl_wrapper.sh | 7 +------ haskell/repl.bzl | 6 +++++- haskell/toolchain.bzl | 21 ++------------------ 5 files changed, 39 insertions(+), 46 deletions(-) diff --git a/haskell/cc.bzl b/haskell/cc.bzl index 91b531640..154babf90 100644 --- a/haskell/cc.bzl +++ b/haskell/cc.bzl @@ -159,3 +159,32 @@ def cc_interop_info(ctx): if CcInfo in dep ]).linking_context.libraries_to_link.to_list(), ) + +def ghc_cc_program_args(cc): + """Retruns the -pgm* flags required to override cc. + + Args: + cc: string, path to the C compiler (cc_wrapper). + + Returns: + list of string, GHC arguments. + """ + return [ + # GHC uses C compiler for assemly, linking and preprocessing as well. + "-pgma", + cc, + "-pgmc", + cc, + "-pgml", + cc, + "-pgmP", + cc, + # Setting -pgm* flags explicitly has the unfortunate side effect + # of resetting any program flags in the GHC settings file. So we + # restore them here. See + # https://ghc.haskell.org/trac/ghc/ticket/7929. + "-optc-fno-stack-protector", + "-optP-E", + "-optP-undef", + "-optP-traditional", + ] diff --git a/haskell/doctest.bzl b/haskell/doctest.bzl index 5e18c0868..94394aa4b 100644 --- a/haskell/doctest.bzl +++ b/haskell/doctest.bzl @@ -1,7 +1,7 @@ """Doctest support""" load("@bazel_skylib//lib:dicts.bzl", "dicts") -load(":cc.bzl", "cc_interop_info") +load(":cc.bzl", "cc_interop_info", "ghc_cc_program_args") load(":private/context.bzl", "haskell_context", "render_env") load(":private/set.bzl", "set") load( @@ -95,25 +95,7 @@ def _haskell_doctest_single(target, ctx): args.add("--no-magic") cc = cc_interop_info(ctx) - args.add_all([ - # GHC uses C compiler for assemly, linking and preprocessing as well. - "-pgma", - cc.tools.cc, - "-pgmc", - cc.tools.cc, - "-pgml", - cc.tools.cc, - "-pgmP", - cc.tools.cc, - # Setting -pgm* flags explicitly has the unfortunate side effect - # of resetting any program flags in the GHC settings file. So we - # restore them here. See - # https://ghc.haskell.org/trac/ghc/ticket/7929. - "-optc-fno-stack-protector", - "-optP-E", - "-optP-undef", - "-optP-traditional", - ]) + args.add_all(ghc_cc_program_args(cc.tools.cc)) doctest_log = ctx.actions.declare_file( "doctest-log-" + ctx.label.name + "-" + target.label.name, diff --git a/haskell/private/ghci_repl_wrapper.sh b/haskell/private/ghci_repl_wrapper.sh index f672671a6..cd6acefc7 100644 --- a/haskell/private/ghci_repl_wrapper.sh +++ b/haskell/private/ghci_repl_wrapper.sh @@ -54,11 +54,6 @@ cd "$BUILD_WORKSPACE_DIRECTORY" RULES_HASKELL_EXEC_ROOT=$(dirname $(readlink ${BUILD_WORKSPACE_DIRECTORY}/bazel-out)) TOOL_LOCATION="$RULES_HASKELL_EXEC_ROOT/{TOOL}" -# Setting -pgm* flags explicitly has the unfortunate side effect -# of resetting any program flags in the GHC settings file. So we -# restore them here. See -# https://ghc.haskell.org/trac/ghc/ticket/7929. -PGM_ARGS="-pgma {CC} -pgmc {CC} -pgml {CC} -pgmP {CC} -optc-fno-stack-protector -optP-E -optP-undef -optP-traditional" {ENV} -"$TOOL_LOCATION" $PGM_ARGS {ARGS} "$@" +"$TOOL_LOCATION" {ARGS} "$@" diff --git a/haskell/repl.bzl b/haskell/repl.bzl index f04fc6b21..549731550 100644 --- a/haskell/repl.bzl +++ b/haskell/repl.bzl @@ -3,6 +3,7 @@ load("@bazel_skylib//lib:dicts.bzl", "dicts") load("@bazel_skylib//lib:paths.bzl", "paths") load("@bazel_skylib//lib:shell.bzl", "shell") +load(":cc.bzl", "ghc_cc_program_args") load(":private/context.bzl", "haskell_context", "render_env") load( ":private/path_utils.bzl", @@ -268,6 +269,10 @@ def _compiler_flags_and_inputs(hs, repl_info, path_prefix = ""): args, ) + args.extend(ghc_cc_program_args( + paths.join(path_prefix, hs.toolchain.cc_wrapper.executable.path), + )) + # Add import directories for import_dir in repl_info.load_info.import_dirs.to_list(): args.append("-i" + (import_dir if import_dir else ".")) @@ -352,7 +357,6 @@ def _create_repl(hs, posix, ctx, repl_info, output): substitutions = { "{ENV}": render_env(hs.env), "{TOOL}": hs.tools.ghci.path, - "{CC}": hs.toolchain.cc_wrapper.executable.path, "{ARGS}": " ".join( args + [ shell.quote(a) diff --git a/haskell/toolchain.bzl b/haskell/toolchain.bzl index b57bbc3c9..936c65b57 100644 --- a/haskell/toolchain.bzl +++ b/haskell/toolchain.bzl @@ -15,6 +15,7 @@ load( "merge_parameter_files", ) load(":private/actions/package.bzl", "package") +load(":cc.bzl", "ghc_cc_program_args") _GHC_BINARIES = ["ghc", "ghc-pkg", "hsc2hs", "haddock", "ghci", "runghc", "hpc"] @@ -40,25 +41,7 @@ def _run_ghc(hs, cc, inputs, outputs, mnemonic, arguments, params_file = None, e # XXX: We should also tether Bazel's CC toolchain to GHC's, so that we can properly mix Bazel-compiled # C libraries with Haskell targets. - args.add_all([ - # GHC uses C compiler for assemly, linking and preprocessing as well. - "-pgma", - cc.tools.cc, - "-pgmc", - cc.tools.cc, - "-pgml", - cc.tools.cc, - "-pgmP", - cc.tools.cc, - # Setting -pgm* flags explicitly has the unfortunate side effect - # of resetting any program flags in the GHC settings file. So we - # restore them here. See - # https://ghc.haskell.org/trac/ghc/ticket/7929. - "-optc-fno-stack-protector", - "-optP-E", - "-optP-undef", - "-optP-traditional", - ]) + args.add_all(ghc_cc_program_args(cc.tools.cc)) compile_flags_file = hs.actions.declare_file("compile_flags_%s_%s" % (hs.name, mnemonic)) extra_args_file = hs.actions.declare_file("extra_args_%s_%s" % (hs.name, mnemonic)) From 4c030a02ce3b84b7ac14407af6a32ac9a2dff71c Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Fri, 28 Feb 2020 13:37:38 +0100 Subject: [PATCH 3/5] haskell_repl: Create hie-bios argument file --- haskell/repl.bzl | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/haskell/repl.bzl b/haskell/repl.bzl index 549731550..754481696 100644 --- a/haskell/repl.bzl +++ b/haskell/repl.bzl @@ -7,6 +7,7 @@ load(":cc.bzl", "ghc_cc_program_args") load(":private/context.bzl", "haskell_context", "render_env") load( ":private/path_utils.bzl", + "ln", "match_label", "parse_pattern", "target_unique_name", @@ -380,6 +381,30 @@ def _create_repl(hs, posix, ctx, repl_info, output): ), )] +def _create_hie_bios(hs, posix, ctx, repl_info): + """Build a hie-bios argument file. + + Args: + hs: Haskell context. + ctx: Rule context. + repl_info: HaskellReplInfo provider. + output: The output for the executable REPL script. + + Returns: + List of providers: + OutputGroupInfo provider for the hie-bios argument file. + """ + args, inputs = _compiler_flags_and_inputs(hs, repl_info) + args.extend(hs.toolchain.compiler_flags) + args.extend(repl_info.load_info.compiler_flags) + + args_file = ctx.actions.declare_file(".%s.hie-bios" % ctx.label.name) + args_link = ctx.actions.declare_file("%s@hie-bios" % ctx.label.name) + ctx.actions.write(args_file, "\n".join(args)) + ln(hs, posix, args_file, args_link, extra_inputs = inputs) + + return [OutputGroupInfo(hie_bios = [args_link])] + def _haskell_repl_aspect_impl(target, ctx): if HaskellInfo not in target: return [] @@ -424,7 +449,8 @@ def _haskell_repl_impl(ctx): repl_info = _create_HaskellReplInfo(from_source, from_binary, collect_info) hs = haskell_context(ctx) posix = ctx.toolchains["@rules_sh//sh/posix:toolchain_type"] - return _create_repl(hs, posix, ctx, repl_info, ctx.outputs.repl) + return _create_repl(hs, posix, ctx, repl_info, ctx.outputs.repl) + \ + _create_hie_bios(hs, posix, ctx, repl_info) haskell_repl = rule( implementation = _haskell_repl_impl, From 9dc672c2609db7b1a9b7f7ec07adb39cf149a59f Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Wed, 4 Mar 2020 14:13:06 +0100 Subject: [PATCH 4/5] Document the hie_bios output group --- haskell/repl.bzl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/haskell/repl.bzl b/haskell/repl.bzl index 754481696..7e718a5f5 100644 --- a/haskell/repl.bzl +++ b/haskell/repl.bzl @@ -549,5 +549,22 @@ Build a REPL for multiple targets. $ bazel run //:repl ``` +### IDE Support (Experimental) + +`haskell_repl` targets provide the `hie_bios` output group to optionally +generate GHCi flags for [hie-bios](https://github.com/mpickering/hie-bios)'s +`bios` cradle. You can use this for IDE support with +[ghcide](https://github.com/digital-asset/ghcide). + +Given a `haskell_repl` target `//:repl` an example `.hie-bios` script could +look as follows. Please refer to the `hie-bios` documentation for further +information. + + ```shell + #!/usr/bin/env bash + set -euo pipefail + bazel build //:repl --output_groups=hie_bios + cat bazel-bin/repl@hie-bios >"$HIE_BIOS_OUTPUT" + ``` """, ) From 8d3e486614f4163c6e16b5d1476d7ccccd7db1d6 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Thu, 5 Mar 2020 13:51:12 +0100 Subject: [PATCH 5/5] Fix typo: Retruns --> Returns --- haskell/cc.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/haskell/cc.bzl b/haskell/cc.bzl index 154babf90..1ad801c43 100644 --- a/haskell/cc.bzl +++ b/haskell/cc.bzl @@ -161,7 +161,7 @@ def cc_interop_info(ctx): ) def ghc_cc_program_args(cc): - """Retruns the -pgm* flags required to override cc. + """Returns the -pgm* flags required to override cc. Args: cc: string, path to the C compiler (cc_wrapper).