Skip to content

Commit 984750c

Browse files
authored
Merge pull request #1796 from martyall/validate-hidden_modules
Validate hidden modules
2 parents 013a1b4 + 552ea05 commit 984750c

File tree

5 files changed

+113
-1
lines changed

5 files changed

+113
-1
lines changed

haskell/defs.bzl

+1-1
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ def haskell_library(
615615
non_default_plugins: Like `plugins` but doesn't pass `-fplugin=...` to modules by default.
616616
tools: Extra tools needed at compile-time, like preprocessors.
617617
worker: Experimental. Worker binary employed by Bazel's persistent worker mode. See [use-cases documentation](https://rules-haskell.readthedocs.io/en/latest/haskell-use-cases.html#persistent-worker-mode-experimental).
618-
hidden_modules: Modules that should be unavailable for import by dependencies.
618+
hidden_modules: Modules that should be unavailable for import by any Haskell module outside of this library.
619619
reexported_modules: A dictionary mapping dependencies to module reexports that should be available for import by dependencies.
620620
exports: A list of other haskell libraries that will be transparently added as a dependency to every downstream rule
621621
linkstatic: Create a static library, not both a static and a shared library.

haskell/private/haskell_impl.bzl

+8
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,15 @@ def haskell_library_impl(ctx):
526526

527527
other_modules = ctx.attr.hidden_modules
528528
exposed_modules_reexports = _exposed_modules_reexports(ctx.attr.reexported_modules)
529+
529530
haskell_module_names = [haskell_module_from_target(m) for m in modules]
531+
532+
# Validate that hidden modules appear as modules in src list or modules list, depending which appears:
533+
declared_modules = haskell_module_names if modules else module_map.keys()
534+
hidden_minus_declared_modules = set.difference(set.from_list(ctx.attr.hidden_modules), set.from_list(declared_modules))
535+
if not hidden_minus_declared_modules == set.empty():
536+
fail("""Hidden modules must be a subset of all modules, found additional hidden modules {}""".format(set.to_list(hidden_minus_declared_modules)))
537+
530538
exposed_modules = set.from_list(module_map.keys() + exposed_modules_reexports + haskell_module_names)
531539
set.mutable_difference(exposed_modules, set.from_list(other_modules))
532540
exposed_modules = set.to_list(exposed_modules)

haskell/private/set.bzl

+17
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,22 @@ def _mutable_union(s0, s1):
8989
s0._set_items.update(s1._set_items)
9090
return s0
9191

92+
def _difference(s0, s1):
93+
"""Return the set of elements from s0 not appearing in s1.
94+
95+
Args:
96+
s0: One set.
97+
s1: Another set.
98+
99+
Result:
100+
set, difference of the two sets.
101+
"""
102+
s2 = _empty()
103+
for item in s0._set_items.keys():
104+
if not _is_member(s1, item):
105+
_mutable_insert(s2, item)
106+
return s2
107+
92108
def _mutable_difference(s0, s1):
93109
"""Modify set `s0` removing elements from `s1` from it.
94110
@@ -157,6 +173,7 @@ set = struct(
157173
mutable_insert = _mutable_insert,
158174
union = _union,
159175
mutable_union = _mutable_union,
176+
difference = _difference,
160177
mutable_difference = _mutable_difference,
161178
map = _map,
162179
from_list = _from_list,

tests/hidden-modules/BUILD.bazel

+8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ load(
22
"@rules_haskell//haskell:defs.bzl",
33
"haskell_library",
44
)
5+
load(
6+
"hidden_modules_test.bzl",
7+
"hidden_modules_test_suite",
8+
)
59

610
package(
711
default_testonly = 1,
@@ -34,6 +38,10 @@ haskell_library(
3438
],
3539
)
3640

41+
hidden_modules_test_suite(
42+
name = "test-missing-module",
43+
)
44+
3745
filegroup(
3846
name = "all_files",
3947
testonly = True,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
2+
load("@rules_haskell//haskell:defs.bzl", "haskell_library")
3+
load("@rules_haskell//haskell/experimental:defs.bzl", "haskell_module")
4+
5+
# Test for failure in the case of hiding non existent modules
6+
def _hidden_module_test_impl(ctx):
7+
env = analysistest.begin(ctx)
8+
9+
asserts.expect_failure(env, "Hidden modules must be a subset of all modules")
10+
11+
return analysistest.end(env)
12+
13+
hidden_module_test = analysistest.make(
14+
_hidden_module_test_impl,
15+
expect_failure = True,
16+
)
17+
18+
# test using the standard haskell library rules
19+
def _test_hidden_modules1():
20+
haskell_library(
21+
name = "lib-hidden-1",
22+
srcs = native.glob(["lib-a/*.hs"]),
23+
src_strip_prefix = "lib-a",
24+
hidden_modules = ["NotHere"],
25+
deps = ["//tests/hackage:base"],
26+
tags = ["manual"],
27+
)
28+
29+
hidden_module_test(
30+
name = "hidden_module_test-1",
31+
target_under_test = ":lib-hidden-1",
32+
)
33+
34+
# Test using haskell modules
35+
def _test_hidden_modules2():
36+
haskell_module(
37+
name = "FooModule",
38+
src = "lib-a/Foo.hs",
39+
module_name = "Foo",
40+
)
41+
42+
haskell_module(
43+
name = "BarModule",
44+
src = "lib-a/Bar.hs",
45+
module_name = "Bar",
46+
)
47+
48+
haskell_library(
49+
name = "lib-hidden-2",
50+
modules = [
51+
":FooModule",
52+
":BarModule",
53+
],
54+
# Should be [Bar] if you actually want to hide the Bar module
55+
hidden_modules = ["BarModule"],
56+
visibility = ["//visibility:public"],
57+
deps = [
58+
"//tests/hackage:base",
59+
],
60+
tags = ["manual"],
61+
)
62+
63+
hidden_module_test(
64+
name = "hidden_module_test-2",
65+
target_under_test = ":lib-hidden-2",
66+
)
67+
68+
# Run all of the expect failure tests
69+
def hidden_modules_test_suite(name):
70+
_test_hidden_modules1()
71+
_test_hidden_modules2()
72+
73+
native.test_suite(
74+
name = name,
75+
tests = [
76+
":hidden_module_test-1",
77+
":hidden_module_test-2",
78+
],
79+
)

0 commit comments

Comments
 (0)