Skip to content

Commit

Permalink
Bazelify compiler's global package database
Browse files Browse the repository at this point in the history
Exposing GHC's global package database to Bazel enables us to stop
treating prebuilt libraries specially. They now export
a `HaskellLibraryInfo` like any other library. Even better, they now
also export a `CcInfo` provider like any other native code library,
which means that CC rules have a full view of all static archives
needed for linking a binary statically (see the `cc_haskell_import`
test, which no longer requires `linkstatic = False`).

`HaskellPrebuiltPackageInfo` is still there but now has no content.
It's only there to prevent aspects from reaching too far. We'll get
rid of it in future commits.

Fixes #838
  • Loading branch information
mboes committed May 4, 2019
1 parent 4644008 commit 86495d5
Show file tree
Hide file tree
Showing 21 changed files with 436 additions and 26 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
change, because nearly no one uses these functions directly. They
are normally only used transitively via
`haskell_register_toolchains` and related functions.
* It is now possible to statically link Haskell libraries in CC
binaries.

## [0.8] - 2019-01-28

Expand Down
1 change: 1 addition & 0 deletions haskell/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ exports_files(
"private/haddock_wrapper.sh.tpl",
"private/coverage_wrapper.sh.tpl",
"private/osx_cc_wrapper.sh.tpl",
"private/pkgdb_to_bzl.py",
],
)

Expand Down
3 changes: 2 additions & 1 deletion haskell/doctest.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ load(
"@io_tweag_rules_haskell//haskell:providers.bzl",
"HaskellInfo",
"HaskellLibraryInfo",
"HaskellPrebuiltPackageInfo",
)

def _doctest_toolchain_impl(ctx):
Expand Down Expand Up @@ -80,7 +81,7 @@ def _haskell_doctest_single(target, ctx):
File: the doctest log.
"""

if HaskellInfo not in target:
if HaskellInfo not in target or HaskellPrebuiltPackageInfo in target:
return []

hs = haskell_context(ctx, ctx.attr)
Expand Down
21 changes: 21 additions & 0 deletions haskell/ghc_bindist.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -225,25 +225,46 @@ grep -lZ {bindist_dir} bin/* | xargs -0 --verbose \\
))
_execute_fail_loudly(ctx, ["./patch_bins"])

# Generate BUILD file entries describing each prebuilt package.
pkgdb_to_bzl = ctx.path(Label("@io_tweag_rules_haskell//haskell:private/pkgdb_to_bzl.py"))
result = ctx.execute([
pkgdb_to_bzl,
ctx.attr.name,
"lib/ghc-{}".format(ctx.attr.version),
])
if result.return_code:
fail("Error executing pkgdb_to_bzl.py: {stderr}".format(stderr = result.stderr))
toolchain_libraries = result.stdout

ctx.template(
"BUILD",
executable = False,
content = """
load(
"@io_tweag_rules_haskell//haskell:haskell.bzl",
"haskell_import",
"haskell_toolchain",
)
{toolchain_libraries}
filegroup(
name = "bin",
srcs = glob(["bin/*"]),
)
haskell_toolchain(
name = "toolchain",
tools = [":bin"],
libraries = toolchain_libraries,
version = "{version}",
compiler_flags = {compiler_flags},
haddock_flags = {haddock_flags},
repl_ghci_args = {repl_ghci_args},
visibility = ["//visibility:public"],
)
""".format(
toolchain_libraries = toolchain_libraries,
version = ctx.attr.version,
compiler_flags = ctx.attr.compiler_flags,
haddock_flags = ctx.attr.haddock_flags,
Expand Down
3 changes: 2 additions & 1 deletion haskell/haddock.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ load(
"HaddockInfo",
"HaskellInfo",
"HaskellLibraryInfo",
"HaskellPrebuiltPackageInfo",
)
load(":private/context.bzl", "haskell_context", "render_env")
load(":private/set.bzl", "set")
Expand All @@ -22,7 +23,7 @@ def _get_haddock_path(package_id):
return package_id + ".haddock"

def _haskell_doc_aspect_impl(target, ctx):
if HaskellInfo not in target or HaskellLibraryInfo not in target:
if HaskellInfo not in target or HaskellLibraryInfo not in target or HaskellPrebuiltPackageInfo in target:
return []

# Packages imported via `//haskell:import.bzl%haskell_import` already
Expand Down
23 changes: 23 additions & 0 deletions haskell/haskell.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ load(
load(
":private/haskell_impl.bzl",
_haskell_binary_impl = "haskell_binary_impl",
_haskell_import_impl = "haskell_import_impl",
_haskell_library_impl = "haskell_library_impl",
_haskell_test_impl = "haskell_test_impl",
_haskell_toolchain_library_impl = "haskell_toolchain_library_impl",
Expand Down Expand Up @@ -279,6 +280,24 @@ not built by default, but can be built on request. It works the same way as
for `haskell_binary`.
"""

haskell_import = rule(
_haskell_import_impl,
attrs = {
"id": attr.string(),
"version": attr.string(),
"deps": attr.label_list(),
"static_library": attr.label(allow_single_file = [".a"]),
"shared_library": attr.label(allow_single_file = [".dll", ".dylib", ".so"]),
"static_profiling_library": attr.label(allow_single_file = ["_p.a"]),
"linkopts": attr.string_list(),
"hdrs": attr.label_list(allow_files = True),
"includes": attr.string_list(),
"_cc_toolchain": attr.label(
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
),
},
)

haskell_toolchain_library = rule(
_haskell_toolchain_library_impl,
attrs = dict(
Expand All @@ -290,6 +309,10 @@ haskell_toolchain_library = rule(
cfg = "host",
default = Label("@io_tweag_rules_haskell//haskell:version_macros"),
),
# XXX We'll no longer need this once HaskellImportHack is removed.
_cc_toolchain = attr.label(
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
),
),
toolchains = [
"@io_tweag_rules_haskell//haskell:toolchain",
Expand Down
5 changes: 3 additions & 2 deletions haskell/lint.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ load(
"HaskellInfo",
"HaskellLibraryInfo",
"HaskellLintInfo",
"HaskellPrebuiltPackageInfo",
)
load(":private/context.bzl", "haskell_context", "render_env")
load(":private/packages.bzl", "expose_packages", "pkg_info_to_compile_flags")
Expand All @@ -30,7 +31,7 @@ def _haskell_lint_rule_impl(ctx):
def _haskell_lint_aspect_impl(target, ctx):
hs = haskell_context(ctx, ctx.rule.attr)

if HaskellInfo not in target:
if HaskellInfo not in target or HaskellPrebuiltPackageInfo in target:
return []

hs_info = target[HaskellInfo]
Expand Down Expand Up @@ -58,7 +59,7 @@ def _haskell_lint_aspect_impl(target, ctx):
use_direct = False,
use_my_pkg_id = None,
custom_package_databases = None,
version = ctx.rule.attr.version,
version = getattr(ctx.rule.attr, "version", None),
)))

sources = set.to_list(hs_info.source_files)
Expand Down
47 changes: 43 additions & 4 deletions haskell/nixpkgs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -237,20 +237,41 @@ def _ghc_nixpkgs_haskell_toolchain_impl(repository_ctx):
}
)
locale_archive = repr(repository_ctx.attr.locale_archive)

nixpkgs_ghc_path = repository_ctx.path(repository_ctx.attr._nixpkgs_ghc).dirname
# Symlink content of ghc external repo. In effect, this repo has
# the same content, but with a BUILD file that includes generated
# content (not a static one like nixpkgs_package supports).
for target in _find_children(repository_ctx, nixpkgs_ghc_path):
basename = target.rpartition("/")[-1]
repository_ctx.symlink(target, basename)

# Generate BUILD file entries describing each prebuilt package.
pkgdb_to_bzl = repository_ctx.path(Label("@io_tweag_rules_haskell//haskell:private/pkgdb_to_bzl.py"))
result = repository_ctx.execute([
pkgdb_to_bzl,
repository_ctx.attr.name,
"lib/ghc-{}".format(repository_ctx.attr.version),
])
if result.return_code:
fail("Error executing pkgdb_to_bzl.py: {stderr}".format(stderr = result.stderr))
toolchain_libraries = result.stdout

repository_ctx.file(
"BUILD",
executable = False,
content = """
load(
"@io_tweag_rules_haskell//haskell:haskell.bzl",
"haskell_import",
"haskell_toolchain",
)
{toolchain_libraries}
haskell_toolchain(
name = "toolchain-impl",
tools = {tools},
libraries = toolchain_libraries,
version = "{version}",
compiler_flags = {compiler_flags} + {compiler_flags_select},
haddock_flags = {haddock_flags},
Expand All @@ -261,6 +282,7 @@ haskell_toolchain(
visibility = ["//visibility:public"],
)
""".format(
toolchain_libraries = toolchain_libraries,
tools = ["@io_tweag_rules_haskell_ghc_nixpkgs//:bin"],
version = repository_ctx.attr.version,
compiler_flags = repository_ctx.attr.compiler_flags,
Expand All @@ -282,6 +304,7 @@ _ghc_nixpkgs_haskell_toolchain = repository_rule(
"haddock_flags": attr.string_list(),
"repl_ghci_args": attr.string_list(),
"locale_archive": attr.string(),
"_nixpkgs_ghc": attr.label(default = "@io_tweag_rules_haskell_ghc_nixpkgs//:BUILD"),
},
)

Expand Down Expand Up @@ -320,7 +343,6 @@ _ghc_nixpkgs_toolchain = repository_rule(_ghc_nixpkgs_toolchain_impl)

def haskell_register_ghc_nixpkgs(
version,
build_file = None,
compiler_flags = None,
compiler_flags_select = None,
haddock_flags = None,
Expand Down Expand Up @@ -365,12 +387,12 @@ def haskell_register_ghc_nixpkgs(
haskell_nixpkgs_package(
name = nixpkgs_ghc_repo_name,
attribute_path = attribute_path,
build_file = build_file or "@io_tweag_rules_haskell//haskell:ghc.BUILD",
build_file_content = "",
nix_file = nix_file,
nix_file_deps = nix_file_deps,
repositories = repositories,
)
# haskell_toolchain
# haskell_toolchain + haskell_import definitions.
_ghc_nixpkgs_haskell_toolchain(
name = haskell_toolchain_repo_name,
version = version,
Expand All @@ -383,3 +405,20 @@ def haskell_register_ghc_nixpkgs(
# toolchain definition.
_ghc_nixpkgs_toolchain(name = toolchain_repo_name)
native.register_toolchains("@{}//:toolchain".format(toolchain_repo_name))

def _find_children(repository_ctx, target_dir):
find_args = [
"find",
"-L",
target_dir,
"-maxdepth",
"1",
# otherwise the directory is printed as well
"-mindepth",
"1",
# filenames can contain \n
"-print0",
]
exec_result = repository_ctx.execute(find_args)
if exec_result.return_code: fail("_find_children() failed.")
return exec_result.stdout.rstrip("\0").split("\0")
Loading

0 comments on commit 86495d5

Please sign in to comment.