Skip to content

Commit 451e815

Browse files
authored
Merge pull request #1478 from tweag/bazelci-settings
Track GHC's lib/settings as an action input
2 parents a652844 + 10f1b19 commit 451e815

12 files changed

+239
-126
lines changed

WORKSPACE

+2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ stack_snapshot(
7373
"language-c",
7474
"streaming",
7575
"void",
76+
"ghc-paths",
77+
"ghc-check-0.5.0.3",
7678
"hspec",
7779
"hspec-core",
7880
"lens-family-core",

haskell/cabal.bzl

+1
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ def _prepare_cabal_inputs(
282282
transitive = [
283283
depset(srcs),
284284
depset(cc.files),
285+
depset(hs.toolchain.files),
285286
package_databases,
286287
setup_dep_info.package_databases,
287288
transitive_headers,

haskell/ghc.BUILD.tpl

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ load(
1515

1616
package(default_visibility = ["//visibility:public"])
1717

18+
%{toolchain_libraries}
19+
1820
%{toolchain}
1921

2022
filegroup(

haskell/ghc_bindist.bzl

+62-72
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,20 @@
33
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "patch")
44
load("@bazel_tools//tools/cpp:lib_cc_configure.bzl", "get_cpu_value")
55
load("@rules_sh//sh:posix.bzl", "sh_posix_configure")
6-
load(":private/workspace_utils.bzl", "execute_or_fail_loudly")
6+
load(
7+
":private/pkgdb_to_bzl.bzl",
8+
"pkgdb_to_bzl",
9+
)
10+
load(
11+
":private/workspace_utils.bzl",
12+
"define_rule",
13+
"execute_or_fail_loudly",
14+
"find_python",
15+
"resolve_labels",
16+
)
717

8-
# If you change this, change stackage's version
9-
# in the start script (see stackage.org)
18+
# If you change this, change stackage's version in the start script
19+
# (see stackage.org).
1020
_GHC_DEFAULT_VERSION = "8.6.5"
1121

1222
# Generated with `bazel run @rules_haskell//haskell:gen-ghc-bindist`
@@ -271,14 +281,13 @@ GHC_BINDIST = \
271281
}
272282

273283
def _ghc_bindist_impl(ctx):
274-
# Avoid rule restart by resolving these labels early. See
275-
# https://github.com/bazelbuild/bazel/blob/master/tools/cpp/lib_cc_configure.bzl#L17.
276-
ghc_build = ctx.path(Label("@rules_haskell//haskell:ghc.BUILD.tpl"))
277-
284+
paths = resolve_labels(ctx, [
285+
"@rules_haskell//haskell:ghc.BUILD.tpl",
286+
"@rules_haskell//haskell:private/pkgdb_to_bzl.py",
287+
])
278288
version = ctx.attr.version
279289
target = ctx.attr.target
280290
os, _, arch = target.partition("_")
281-
python_bin = _find_python(ctx)
282291

283292
if GHC_BINDIST[version].get(target) == None:
284293
fail("Operating system {0} does not have a bindist for GHC version {1}".format(ctx.os.name, ctx.attr.version))
@@ -295,6 +304,13 @@ def _ghc_bindist_impl(ctx):
295304
stripPrefix = "ghc-" + version,
296305
)
297306

307+
if os == "windows":
308+
# These libraries cause linking errors on Windows when linking
309+
# pthreads, due to libwinpthread-1.dll not being loaded.
310+
execute_or_fail_loudly(ctx, ["rm", "mingw/lib/gcc/x86_64-w64-mingw32/7.2.0/libstdc++.dll.a"])
311+
execute_or_fail_loudly(ctx, ["rm", "mingw/x86_64-w64-mingw32/lib/libpthread.dll.a"])
312+
execute_or_fail_loudly(ctx, ["rm", "mingw/x86_64-w64-mingw32/lib/libwinpthread.dll.a"])
313+
298314
# We apply some patches, if needed.
299315
patch(ctx)
300316

@@ -332,73 +348,33 @@ DISTDIR="$( dirname "$(resolved="$0"; cd "$(dirname "$resolved")"; while tmp="$(
332348
))
333349
execute_or_fail_loudly(ctx, ["./patch_bins"])
334350

335-
# The default locale is OS specific.
336-
if ctx.attr.locale:
337-
locale = ctx.attr.locale
338-
else:
339-
locale = "en_US.UTF-8" if os == "darwin" else "C.UTF-8"
340-
341-
# Generate BUILD file entries describing each prebuilt package.
342-
# Cannot use //haskell:pkgdb_to_bzl because that's a generated
343-
# target. ctx.path() only works on source files.
344-
pkgdb_to_bzl = ctx.path(Label("@rules_haskell//haskell:private/pkgdb_to_bzl.py"))
345-
result = ctx.execute([
346-
python_bin,
347-
pkgdb_to_bzl,
348-
ctx.attr.name,
349-
"lib",
350-
])
351-
if result.return_code:
352-
fail("Error executing pkgdb_to_bzl.py: {stderr}".format(stderr = result.stderr))
353-
toolchain_libraries = result.stdout
354-
toolchain = """
355-
{toolchain_libraries}
356-
357-
haskell_toolchain(
358-
name = "toolchain-impl",
359-
tools = [":bin"],
360-
libraries = toolchain_libraries,
361-
version = "{version}",
362-
static_runtime = {static_runtime},
363-
fully_static_link = {fully_static_link},
364-
compiler_flags = {compiler_flags},
365-
haddock_flags = {haddock_flags},
366-
repl_ghci_args = {repl_ghci_args},
367-
cabalopts = {cabalopts},
368-
visibility = ["//visibility:public"],
369-
locale = "{locale}",
370-
)
371-
""".format(
372-
toolchain_libraries = toolchain_libraries,
373-
version = ctx.attr.version,
351+
toolchain_libraries = pkgdb_to_bzl(ctx, paths, "lib")
352+
locale = ctx.attr.locale or "en_US.UTF-8" if os == "darwin" else "C.UTF-8"
353+
toolchain = define_rule(
354+
"haskell_toolchain",
355+
name = "toolchain-impl",
356+
tools = [":bin"],
357+
libraries = "toolchain_libraries",
358+
# See Note [GHC toolchain files]
359+
files = [":lib/settings"],
360+
version = repr(ctx.attr.version),
374361
static_runtime = os == "windows",
375-
376-
# At present we don't support fully-statically-linked binaries with GHC
377-
# bindists.
378-
fully_static_link = False,
362+
fully_static_link = False, # XXX not yet supported for bindists.
379363
compiler_flags = ctx.attr.compiler_flags,
380364
haddock_flags = ctx.attr.haddock_flags,
381365
repl_ghci_args = ctx.attr.repl_ghci_args,
382366
cabalopts = ctx.attr.cabalopts,
383-
locale = locale,
367+
locale = repr(locale),
384368
)
385-
386-
if os == "windows":
387-
# These libraries cause linking errors on Windows when linking
388-
# pthreads, due to libwinpthread-1.dll not being loaded.
389-
execute_or_fail_loudly(ctx, ["rm", "mingw/lib/gcc/x86_64-w64-mingw32/7.2.0/libstdc++.dll.a"])
390-
execute_or_fail_loudly(ctx, ["rm", "mingw/x86_64-w64-mingw32/lib/libpthread.dll.a"])
391-
execute_or_fail_loudly(ctx, ["rm", "mingw/x86_64-w64-mingw32/lib/libwinpthread.dll.a"])
392-
393369
ctx.template(
394370
"BUILD",
395-
ghc_build,
371+
paths["@rules_haskell//haskell:ghc.BUILD.tpl"],
396372
substitutions = {
373+
"%{toolchain_libraries}": toolchain_libraries,
397374
"%{toolchain}": toolchain,
398375
},
399376
executable = False,
400377
)
401-
ctx.file("WORKSPACE")
402378

403379
_ghc_bindist = repository_rule(
404380
_ghc_bindist_impl,
@@ -590,18 +566,9 @@ def haskell_register_ghc_bindists(
590566
if local_python_repo_name not in native.existing_rules():
591567
_configure_python3_toolchain(name = local_python_repo_name)
592568

593-
def _find_python(repository_ctx):
594-
python = repository_ctx.which("python3")
595-
if not python:
596-
python = repository_ctx.which("python")
597-
result = repository_ctx.execute([python, "--version"])
598-
if not result.stdout.startswith("Python 3"):
599-
fail("rules_haskell requires Python >= 3.3.")
600-
return python
601-
602569
def _configure_python3_toolchain_impl(repository_ctx):
603570
cpu = get_cpu_value(repository_ctx)
604-
python3_path = _find_python(repository_ctx)
571+
python3_path = find_python(repository_ctx)
605572
repository_ctx.file("BUILD.bazel", executable = False, content = """
606573
load(
607574
"@bazel_tools//tools/python:toolchain.bzl",
@@ -683,3 +650,26 @@ def _configure_python3_toolchain(name):
683650
"""
684651
_config_python3_toolchain(name = name)
685652
native.register_toolchains("@{}//:toolchain".format(name))
653+
654+
# Note [GHC toolchain files]
655+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~
656+
#
657+
# The GHC distribution includes various files that may be required during
658+
# compilation, or may be referenced by template Haskell code. These files
659+
# need to be tracked by Bazel and declared as inputs to the relevant actions
660+
# to ensure that they are present in the build sandbox.
661+
#
662+
# Surprisingly, builds succeed with little to none of such files declared as
663+
# inputs. In case of the nixpkgs toolchain this is not surprising, as the
664+
# files will all be present in the Nix store. However, files of the bindist
665+
# toolchain are fully tracked by Bazel and one would expect errors due to
666+
# missing files if they are not declared.
667+
#
668+
# The first instance of such an error occurred with the GHC bindist on
669+
# BazelCI [1] and shortly after on the GitHub actions CI pipeline. However,
670+
# only the `lib/settings` file was reported missing.
671+
#
672+
# Do extend the `files` toolchain attributes with further files if such
673+
# errors occurr in the future.
674+
#
675+
# [1]: https://github.com/tweag/rules_haskell/issues/1470

haskell/nixpkgs.bzl

+36-47
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,25 @@ load(
55
"nixpkgs_package",
66
"nixpkgs_sh_posix_configure",
77
)
8+
load(
9+
":private/pkgdb_to_bzl.bzl",
10+
"pkgdb_to_bzl",
11+
)
12+
load(
13+
":private/workspace_utils.bzl",
14+
"define_rule",
15+
"resolve_labels",
16+
)
817

918
def _ghc_nixpkgs_haskell_toolchain_impl(repository_ctx):
19+
paths = resolve_labels(repository_ctx, [
20+
"@rules_haskell//haskell:private/pkgdb_to_bzl.py",
21+
])
1022
compiler_flags_select = "select({})".format(
1123
repository_ctx.attr.compiler_flags_select or {
1224
"//conditions:default": [],
1325
},
1426
)
15-
locale_archive = repository_ctx.attr.locale_archive
1627
nixpkgs_ghc_path = repository_ctx.path(repository_ctx.attr._nixpkgs_ghc).dirname.dirname
1728

1829
# Symlink content of ghc external repo. In effect, this repo has
@@ -22,8 +33,6 @@ def _ghc_nixpkgs_haskell_toolchain_impl(repository_ctx):
2233
basename = target.rpartition("/")[-1]
2334
repository_ctx.symlink(target, basename)
2435

25-
# Generate BUILD file entries describing each prebuilt package.
26-
pkgdb_to_bzl = repository_ctx.path(Label("@rules_haskell//haskell:private/pkgdb_to_bzl.py"))
2736
ghc_name = "ghc-{}".format(repository_ctx.attr.version)
2837
result = repository_ctx.execute(["ls", "lib"])
2938
if result.return_code or not ghc_name in result.stdout.splitlines():
@@ -35,23 +44,28 @@ Available versions:
3544
{actual}
3645
""".format(wanted = ghc_name, actual = result.stdout),
3746
)
38-
result = repository_ctx.execute([
39-
pkgdb_to_bzl,
40-
repository_ctx.attr.name,
41-
"lib/{}".format(ghc_name),
42-
])
43-
if result.return_code:
44-
fail("Error executing pkgdb_to_bzl.py: {stderr}".format(stderr = result.stderr))
45-
toolchain_libraries = result.stdout
46-
47-
# Haddock files on nixpkgs are stored outside of the ghc package
48-
# The pkgdb_to_bzl.py program generates bazel labels for theses files
49-
# and asks the parent process to generate the associated bazel symlink
50-
for line in result.stdout.split("\n"):
51-
if line.startswith("#SYMLINK:"):
52-
_, path, name = line.split(" ")
53-
repository_ctx.symlink(path, name)
54-
47+
toolchain_libraries = pkgdb_to_bzl(repository_ctx, paths, "lib/{}".format(ghc_name))
48+
locale_archive = repository_ctx.attr.locale_archive
49+
toolchain = define_rule(
50+
"haskell_toolchain",
51+
name = "toolchain-impl",
52+
libraries = "toolchain_libraries",
53+
# See Note [GHC toolchain files] in haskell/ghc_bindist.bzl
54+
files = [],
55+
tools = ["@rules_haskell_ghc_nixpkgs//:bin"],
56+
version = repr(repository_ctx.attr.version),
57+
static_runtime = repository_ctx.attr.static_runtime,
58+
fully_static_link = repository_ctx.attr.fully_static_link,
59+
compiler_flags = "{} + {}".format(
60+
repository_ctx.attr.compiler_flags,
61+
compiler_flags_select,
62+
),
63+
haddock_flags = repository_ctx.attr.haddock_flags,
64+
repl_ghci_args = repository_ctx.attr.repl_ghci_args,
65+
cabalopts = repository_ctx.attr.cabalopts,
66+
locale_archive = repr(locale_archive) if locale_archive else None,
67+
locale = repr(repository_ctx.attr.locale),
68+
)
5569
repository_ctx.file(
5670
"BUILD",
5771
executable = False,
@@ -71,35 +85,10 @@ filegroup(
7185
7286
{toolchain_libraries}
7387
74-
haskell_toolchain(
75-
name = "toolchain-impl",
76-
tools = {tools},
77-
libraries = toolchain_libraries,
78-
version = "{version}",
79-
static_runtime = {static_runtime},
80-
fully_static_link = {fully_static_link},
81-
compiler_flags = {compiler_flags} + {compiler_flags_select},
82-
haddock_flags = {haddock_flags},
83-
cabalopts = {cabalopts},
84-
repl_ghci_args = {repl_ghci_args},
85-
# On Darwin we don't need a locale archive. It's a Linux-specific
86-
# hack in Nixpkgs.
87-
{locale_archive_arg}
88-
locale = {locale},
89-
)
88+
{toolchain}
9089
""".format(
9190
toolchain_libraries = toolchain_libraries,
92-
tools = ["@rules_haskell_ghc_nixpkgs//:bin"],
93-
version = repository_ctx.attr.version,
94-
static_runtime = repository_ctx.attr.static_runtime,
95-
fully_static_link = repository_ctx.attr.fully_static_link,
96-
compiler_flags = repository_ctx.attr.compiler_flags,
97-
compiler_flags_select = compiler_flags_select,
98-
haddock_flags = repository_ctx.attr.haddock_flags,
99-
repl_ghci_args = repository_ctx.attr.repl_ghci_args,
100-
cabalopts = repository_ctx.attr.cabalopts,
101-
locale_archive_arg = "locale_archive = {},".format(repr(locale_archive)) if locale_archive else "",
102-
locale = repr(repository_ctx.attr.locale),
91+
toolchain = toolchain,
10392
),
10493
)
10594

haskell/private/pkgdb_to_bzl.bzl

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
load(":private/workspace_utils.bzl", "find_python")
2+
3+
def pkgdb_to_bzl(repository_ctx, paths, libdir):
4+
"""Generate a BUILD file from a package database.
5+
6+
Also creates symlinks to any resources stored outside of the GHC
7+
base directory.
8+
9+
Args:
10+
repository_ctx: repository context.
11+
paths: a dictionary of label names to paths.
12+
pkgdir: the relative location of GHC's libdir
13+
"""
14+
result = repository_ctx.execute([
15+
find_python(repository_ctx),
16+
paths["@rules_haskell//haskell:private/pkgdb_to_bzl.py"],
17+
repository_ctx.attr.name,
18+
libdir,
19+
])
20+
if result.return_code:
21+
fail("Error executing pkgdb_to_bzl.py: {stderr}".format(stderr = result.stderr))
22+
23+
# Haddock files on nixpkgs are stored outside of the ghc package
24+
# The pkgdb_to_bzl.py program generates bazel labels for theses files
25+
# and asks the parent process to generate the associated bazel symlink
26+
for line in result.stdout.split("\n"):
27+
if line.startswith("#SYMLINK:"):
28+
_, path, name = line.split(" ")
29+
repository_ctx.symlink(path, name)
30+
31+
return result.stdout

0 commit comments

Comments
 (0)