Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Cabal setup dependencies #1347

Merged
merged 8 commits into from Jun 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions WORKSPACE
Expand Up @@ -94,6 +94,8 @@ stack_snapshot(
"text",
"vector",
# For tests
"cabal-doctest",
"polysemy",
"network",
"language-c",
"streaming",
Expand All @@ -107,6 +109,7 @@ stack_snapshot(
"proto-lens-runtime",
"lens-family",
],
setup_deps = {"polysemy": ["cabal-doctest"]},
snapshot = test_stack_snapshot,
tools = [
"@alex",
Expand Down
46 changes: 46 additions & 0 deletions haskell/cabal.bzl
Expand Up @@ -28,6 +28,7 @@ load(
"HaddockInfo",
"HaskellInfo",
"HaskellLibraryInfo",
"all_dependencies_package_ids",
)
load(
":private/cc_libraries.bzl",
Expand Down Expand Up @@ -128,6 +129,8 @@ def _prepare_cabal_inputs(
tool_input_manifests,
cabal,
setup,
setup_deps,
setup_dep_info,
srcs,
compiler_flags,
flags,
Expand Down Expand Up @@ -170,6 +173,9 @@ def _prepare_cabal_inputs(
dynamic = True,
)

# Setup dependencies are loaded by runghc.
setup_libs = get_ghci_library_files(hs, cc.cc_libraries_info, cc.setup_libraries)

# The regular Haskell rules have separate actions for linking and
# compilation to which we pass different sets of libraries as inputs. The
# Cabal rules, in contrast, only have a single action for compilation and
Expand All @@ -195,6 +201,15 @@ def _prepare_cabal_inputs(
])
direct_lib_dirs = [file.dirname for file in direct_libs]
args.add_all([component, package_id, generate_haddock, setup, cabal.dirname, package_database.dirname])
args.add_joined([
arg
for package_id in setup_deps
for arg in ["-package-id", package_id]
] + [
arg
for package_db in setup_dep_info.package_databases.to_list()
for arg in ["-package-db", "./" + _dirname(package_db)]
], join_with = " ", format_each = "--ghc-arg=%s", omit_if_empty = False)
args.add("--flags=" + " ".join(flags))
args.add_all(compiler_flags, format_each = "--ghc-option=%s")
if dynamic_binary:
Expand Down Expand Up @@ -226,10 +241,15 @@ def _prepare_cabal_inputs(
depset(srcs),
depset(cc.files),
package_databases,
setup_dep_info.package_databases,
transitive_headers,
depset(setup_libs),
depset(transitive_compile_libs),
depset(transitive_link_libs),
depset(transitive_haddocks),
setup_dep_info.interface_dirs,
setup_dep_info.static_libraries,
setup_dep_info.dynamic_libraries,
dep_info.interface_dirs,
dep_info.static_libraries,
dep_info.dynamic_libraries,
Expand Down Expand Up @@ -260,6 +280,8 @@ def _gather_transitive_haddocks(deps):
def _haskell_cabal_library_impl(ctx):
hs = haskell_context(ctx)
dep_info = gather_dep_info(ctx, ctx.attr.deps)
setup_dep_info = gather_dep_info(ctx, ctx.attr.setup_deps)
setup_deps = all_dependencies_package_ids(ctx.attr.setup_deps)
cc = cc_interop_info(ctx)

# All C and Haskell library dependencies.
Expand Down Expand Up @@ -344,6 +366,8 @@ def _haskell_cabal_library_impl(ctx):
tool_input_manifests = tool_input_manifests,
cabal = cabal,
setup = setup,
setup_deps = setup_deps,
setup_dep_info = setup_dep_info,
srcs = ctx.files.srcs,
compiler_flags = user_compile_flags,
flags = ctx.attr.flags,
Expand Down Expand Up @@ -467,6 +491,10 @@ haskell_cabal_library = rule(
"deps": attr.label_list(
aspects = [haskell_cc_libraries_aspect],
),
"setup_deps": attr.label_list(
aspects = [haskell_cc_libraries_aspect],
doc = "Dependencies for custom setup Setup.hs.",
),
"compiler_flags": attr.string_list(
doc = """Flags to pass to Haskell compiler, in addition to those defined
the cabal file. Subject to Make variable substitution.""",
Expand Down Expand Up @@ -534,6 +562,8 @@ build times, and does not require drafting a `.cabal` file.
def _haskell_cabal_binary_impl(ctx):
hs = haskell_context(ctx)
dep_info = gather_dep_info(ctx, ctx.attr.deps)
setup_dep_info = gather_dep_info(ctx, ctx.attr.setup_deps)
setup_deps = all_dependencies_package_ids(ctx.attr.setup_deps)
cc = cc_interop_info(ctx)

# All C and Haskell library dependencies.
Expand Down Expand Up @@ -584,6 +614,8 @@ def _haskell_cabal_binary_impl(ctx):
tool_input_manifests = tool_input_manifests,
cabal = cabal,
setup = setup,
setup_deps = setup_deps,
setup_dep_info = setup_dep_info,
srcs = ctx.files.srcs,
compiler_flags = user_compile_flags,
flags = ctx.attr.flags,
Expand Down Expand Up @@ -644,6 +676,10 @@ haskell_cabal_binary = rule(
"deps": attr.label_list(
aspects = [haskell_cc_libraries_aspect],
),
"setup_deps": attr.label_list(
aspects = [haskell_cc_libraries_aspect],
doc = "Dependencies for custom setup Setup.hs.",
),
"compiler_flags": attr.string_list(
doc = """Flags to pass to Haskell compiler, in addition to those defined
the cabal file. Subject to Make variable substitution.""",
Expand Down Expand Up @@ -1034,6 +1070,7 @@ haskell_cabal_library(
flags = {flags},
srcs = glob(["{dir}/**"]),
deps = {deps},
setup_deps = {setup_deps},
tools = {tools},
visibility = {visibility},
compiler_flags = ["-w", "-optF=-w"],
Expand All @@ -1049,6 +1086,10 @@ haskell_cabal_library(
_label_to_string(label)
for label in extra_deps.get(package.name, [])
],
setup_deps = [
_label_to_string(Label("@{}//:{}".format(repository_ctx.name, package.name)).relative(label))
for label in repository_ctx.attr.setup_deps.get(package.name, [])
],
tools = tools,
visibility = visibility,
verbose = repr(repository_ctx.attr.verbose),
Expand Down Expand Up @@ -1078,6 +1119,7 @@ _stack_snapshot = repository_rule(
doc = "Whether to generate haddock documentation",
),
"extra_deps": attr.label_keyed_string_dict(),
"setup_deps": attr.string_list_dict(),
"tools": attr.label_list(),
"stack": attr.label(),
"stack_update": attr.label(),
Expand Down Expand Up @@ -1191,6 +1233,8 @@ def stack_snapshot(stack = None, extra_deps = {}, vendored_packages = {}, **kwar
system libraries or other external libraries, use the `extra_deps`
attribute to list them. This attribute works like the
`--extra-{include,lib}-dirs` flags for Stack and cabal-install do.
If a package has a custom setup with setup dependencies, use the
`setup_deps` attribute to list them.

Packages that are in the snapshot need not have their versions
specified. But any additional packages or version overrides will have
Expand Down Expand Up @@ -1276,6 +1320,8 @@ def stack_snapshot(stack = None, extra_deps = {}, vendored_packages = {}, **kwar
```
means `@postgresql//:include` is passed to the stackage package `postgresql-libpq`
while `@zlib.dev//:zlib` is passed to the stackage package `zlib`.
setup_deps: Setup dependencies of packages, e.g. `cabal-doctest`.
Dict of stackage package names to a list of targets in the same format as for `extra_deps`.
tools: Tool dependencies. They are built using the host configuration, since
the tools are executed as part of the build.
stack: The stack binary to use to enumerate package dependencies.
Expand Down
8 changes: 7 additions & 1 deletion haskell/cc.bzl
Expand Up @@ -32,6 +32,7 @@ CcInteropInfo = provider(
"cc_libraries": "depset, C libraries from direct linking dependencies.",
"transitive_libraries": "depset, C and Haskell libraries from transitive linking dependencies.",
"plugin_libraries": "depset, C and Haskell libraries from transitive plugin dependencies.",
"setup_libraries": "depset, C and Haskell libraries from Cabal setup dependencies.",
},
)

Expand Down Expand Up @@ -132,7 +133,7 @@ def cc_interop_info(ctx):
tools["ar"] = "/usr/bin/ar"

cc_libraries_info = deps_HaskellCcLibrariesInfo(
ctx.attr.deps + getattr(ctx.attr, "plugins", []),
ctx.attr.deps + getattr(ctx.attr, "plugins", []) + getattr(ctx.attr, "setup_deps", []),
)
return CcInteropInfo(
tools = struct(**tools),
Expand All @@ -159,6 +160,11 @@ def cc_interop_info(ctx):
for dep in plugin[GhcPluginInfo].deps
if CcInfo in dep
]).linking_context.libraries_to_link.to_list(),
setup_libraries = cc_common.merge_cc_infos(cc_infos = [
dep[CcInfo]
for dep in getattr(ctx.attr, "setup_deps", [])
if CcInfo in dep
]).linking_context.libraries_to_link.to_list(),
)

def ghc_cc_program_args(cc):
Expand Down
14 changes: 8 additions & 6 deletions haskell/private/cabal_wrapper.py.tpl
@@ -1,6 +1,6 @@
#!/usr/bin/env python3

# cabal_wrapper.py <COMPONENT> <PKG_NAME> <HADDOCK> <SETUP_PATH> <PKG_DIR> <PACKAGE_DB_PATH> [EXTRA_ARGS...] -- [PATH_ARGS...]
# cabal_wrapper.py <COMPONENT> <PKG_NAME> <HADDOCK> <SETUP_PATH> <PKG_DIR> <PACKAGE_DB_PATH> <RUNGHC_ARGS> [EXTRA_ARGS...] -- [PATH_ARGS...]
#
# This wrapper calls Cabal's configure/build/install steps one big
# action so that we don't have to track all inputs explicitly between
Expand Down Expand Up @@ -86,6 +86,7 @@ datadir = os.path.join(pkgroot, "{}_data".format(name))
package_database = os.path.join(pkgroot, "{}.conf.d".format(name))
haddockdir = os.path.join(pkgroot, "{}_haddock".format(name))
htmldir = os.path.join(pkgroot, "{}_haddock_html".format(name))
runghc_args = sys.argv.pop(1).split()

runghc = find_exe(r"%{runghc}")
ghc = find_exe(r"%{ghc}")
Expand Down Expand Up @@ -144,7 +145,8 @@ with tmpdir() as distdir:
os.putenv("TMP", os.path.join(distdir, "tmp"))
os.putenv("TEMP", os.path.join(distdir, "tmp"))
os.makedirs(os.path.join(distdir, "tmp"))
run([runghc, setup, "configure", \
runghc_args = [arg.replace("./", execroot + "/") for arg in runghc_args]
run([runghc] + runghc_args + [setup, "configure", \
component, \
"--verbose=0", \
"--user", \
Expand Down Expand Up @@ -182,16 +184,16 @@ with tmpdir() as distdir:
[ arg.replace("=", "=" + execroot + "/") for arg in path_args ] + \
[ "--package-db=" + package_database ], # This arg must come last.
)
run([runghc, setup, "build", "--verbose=0", "--builddir=" + distdir])
run([runghc] + runghc_args + [setup, "build", "--verbose=0", "--builddir=" + distdir])
if haddock:
run([runghc, setup, "haddock", "--verbose=0", "--builddir=" + distdir])
run([runghc, setup, "install", "--verbose=0", "--builddir=" + distdir])
run([runghc] + runghc_args + [setup, "haddock", "--verbose=0", "--builddir=" + distdir])
run([runghc] + runghc_args + [setup, "install", "--verbose=0", "--builddir=" + distdir])
# Bazel builds are not sandboxed on Windows and can be non-sandboxed on
# other OSs. Operations like executing `configure` scripts can modify the
# source tree. If the `srcs` attribute uses a glob like `glob(["**"])`,
# then these modified files will enter `srcs` on the next execution and
# invalidate the cache. To avoid this we remove generated files.
run([runghc, setup, "clean", "--verbose=0", "--builddir=" + distdir])
run([runghc] + runghc_args + [setup, "clean", "--verbose=0", "--builddir=" + distdir])
os.chdir(old_cwd)

# XXX Cabal has a bizarre layout that we can't control directly. It
Expand Down
17 changes: 17 additions & 0 deletions tests/haskell_cabal_doctest/BUILD.bazel
@@ -0,0 +1,17 @@
load("@rules_haskell//haskell:cabal.bzl", "haskell_cabal_library")
load("@rules_haskell//haskell:defs.bzl", "haskell_toolchain_library")

haskell_toolchain_library(name = "base")

haskell_cabal_library(
name = "uses-doctest",
package_name = "lib",
srcs = [
"Lib.hs",
"Setup.hs",
"lib.cabal",
],
setup_deps = ["@stackage//:cabal-doctest"],
version = "0.1.0.0",
deps = [":base"],
)
8 changes: 8 additions & 0 deletions tests/haskell_cabal_doctest/Lib.hs
@@ -0,0 +1,8 @@
module Lib where

-- | The string foo
--
-- >>> foo
-- "foo"
foo :: String
foo = "foo"
6 changes: 6 additions & 0 deletions tests/haskell_cabal_doctest/Setup.hs
@@ -0,0 +1,6 @@
module Main where

import Distribution.Extra.Doctest (defaultMainWithDoctests)

main :: IO ()
main = defaultMainWithDoctests "doctests"
15 changes: 15 additions & 0 deletions tests/haskell_cabal_doctest/lib.cabal
@@ -0,0 +1,15 @@
cabal-version: >=1.10
name: lib
version: 0.1.0.0
build-type: Custom

custom-setup
setup-depends:
base,
Cabal,
cabal-doctest

library
build-depends: base
default-language: Haskell2010
exposed-modules: Lib
2 changes: 2 additions & 0 deletions tests/stack-snapshot-deps/BUILD.bazel
Expand Up @@ -20,5 +20,7 @@ haskell_test(
# Packages using ./configure scripts are problematic on Windows.
"@stackage//:network",
"@stackage//:language-c",
# Package that has a setup dependency.
"@stackage//:polysemy",
],
)