Skip to content

Commit 200bff4

Browse files
committed
Implement nixpkgs_cc_configure
This macro calls cc_autoconf_impl like before, but ahead of then also defines a nixpkgs_package with all the tools we want for the CC toolchain.
1 parent f1975f6 commit 200bff4

File tree

6 files changed

+143
-77
lines changed

6 files changed

+143
-77
lines changed

README.md

+55
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,61 @@ filegroup(
226226
</tbody>
227227
</table>
228228

229+
### nixpkgs_cc_configure
230+
231+
Tells Bazel to use compilers and linkers from Nixpkgs for the CC
232+
toolchain. By default, Bazel autodetects a toolchain on the current
233+
`PATH`. Overriding this autodetection makes builds more hermetic and
234+
is considered a best practice.
235+
236+
Example:
237+
238+
```bzl
239+
nixpkgs_cc_configure(repository = "@nixpkgs//:default.nix")
240+
```
241+
242+
<table class="table table-condensed table-bordered table-params">
243+
<colgroup>
244+
<col class="col-param" />
245+
<col class="param-description" />
246+
</colgroup>
247+
<thead>
248+
<tr>
249+
<th colspan="2">Attributes</th>
250+
</tr>
251+
</thead>
252+
<tbody>
253+
<tr>
254+
<td><code>nix_file_content</code></td>
255+
<td>
256+
<p><code>String; optional</code></p>
257+
<p>An expression for a Nix environment derivation.</p>
258+
</td>
259+
</tr>
260+
<tr>
261+
<td><code>repository</code></td>
262+
<td>
263+
<p><code>Label; optional</code></p>
264+
<p>A repository label identifying which Nixpkgs to use.</p>
265+
</td>
266+
</tr>
267+
<tr>
268+
<td><code>repositories</code></td>
269+
<td>
270+
<p><code>String-keyed label dict; optional</code></p>
271+
<p>A dictionary mapping `NIX_PATH` entries to repository labels.</p>
272+
<p>Setting it to
273+
<pre><code>repositories = { "myrepo" : "//:myrepo" }</code></pre>
274+
for example would replace all instances
275+
of <code>&lt;myrepo&gt;</code> in the called nix code by the
276+
path to the target <code>"//:myrepo"</code>. See the
277+
<a href="https://nixos.org/nix/manual/#env-NIX_PATH">relevant
278+
section in the nix manual</a> for more information.</p>
279+
<p>Specify one of `path` or `repositories`.</p>
280+
</td>
281+
</tr>
282+
</tbody>
283+
</table>
229284

230285
## Migration
231286

WORKSPACE

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
workspace(name = "io_tweag_rules_nixpkgs")
22

3-
load("//nixpkgs:nixpkgs.bzl", "nixpkgs_git_repository", "nixpkgs_package")
3+
load(
4+
"//nixpkgs:nixpkgs.bzl",
5+
"nixpkgs_cc_configure",
6+
"nixpkgs_git_repository",
7+
"nixpkgs_package"
8+
)
49

510
# For tests
611

@@ -74,3 +79,5 @@ filegroup(
7479
)
7580
""",
7681
)
82+
83+
nixpkgs_cc_configure(repository = "@remote_nixpkgs//:default.nix")

nixpkgs/BUILD.pkg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package(default_visibility = [ "//visibility:public" ])
1+
package(default_visibility = ["//visibility:public"])
22

33
filegroup(
44
name = "bin",

nixpkgs/nixpkgs.bzl

+72-75
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@ def _nixpkgs_package_impl(repository_ctx):
112112
output_path = exec_result.stdout.splitlines()[-1]
113113
# Build a forest of symlinks (like new_local_package() does) to the
114114
# Nix store.
115-
_symlink_children(repository_ctx, output_path)
115+
for target in _find_children(repository_ctx, output_path):
116+
basename = target.rpartition("/")[-1]
117+
repository_ctx.symlink(target, basename)
116118

117119

118120
_nixpkgs_package = repository_rule(
@@ -130,6 +132,7 @@ _nixpkgs_package = repository_rule(
130132
local = True,
131133
)
132134

135+
133136
def nixpkgs_package(*args, **kwargs):
134137
# Because of https://github.com/bazelbuild/bazel/issues/5356 we can't
135138
# directly pass a dict from strings to labels to the rule (which we'd like
@@ -147,6 +150,70 @@ def nixpkgs_package(*args, **kwargs):
147150
else:
148151
_nixpkgs_package(*args, **kwargs)
149152

153+
154+
def _nixpkgs_cc_autoconf_impl(repository_ctx):
155+
# Calling repository_ctx.path() on anything but a regular file
156+
# fails. So the roundabout way to do the same thing is to find
157+
# a regular file we know is in the workspace (i.e. the WORKSPACE
158+
# file itself) and then use dirname to get the path of the workspace
159+
# root.
160+
workspace_file_path = repository_ctx.path(
161+
Label("@nixpkgs_cc_toolchain//:WORKSPACE")
162+
)
163+
workspace_root = _execute_or_fail(
164+
repository_ctx,
165+
["dirname", workspace_file_path],
166+
).stdout.rstrip()
167+
168+
# Make a list of all available tools in the Nix derivation. Override
169+
# the Bazel autoconfiguration with the tools we found.
170+
bin_contents = _find_children(repository_ctx, workspace_root + "/bin")
171+
overriden_tools = {
172+
tool: repository_ctx.path(Label("@nixpkgs_cc_toolchain//:bin/" + tool))
173+
for entry in bin_contents
174+
for tool in [entry.rpartition("/")[-1]] # Compute basename
175+
}
176+
cc_autoconf_impl(repository_ctx, overriden_tools = overriden_tools)
177+
178+
_nixpkgs_cc_autoconf = repository_rule(
179+
implementation = _nixpkgs_cc_autoconf_impl
180+
)
181+
182+
183+
def nixpkgs_cc_configure(
184+
repository = None,
185+
repositories = None,
186+
nix_file_content = """
187+
with import <nixpkgs> {}; buildEnv {
188+
name = "bazel-cc-toolchain";
189+
paths = [ gcc binutils ];
190+
}
191+
"""):
192+
"""Use a CC toolchain from Nixpkgs.
193+
194+
By default, Bazel auto-configures a CC toolchain from commands (e.g.
195+
`gcc`) available in the environment. To make builds more hermetic, use
196+
this rule to specific explicitly which commands the toolchain should
197+
use.
198+
"""
199+
if repository and repositories or not repository and not repositories:
200+
fail("Specify one of repository or repositories (but not both).")
201+
elif repository:
202+
repositories = {"nixpkgs": repository}
203+
204+
nixpkgs_package(
205+
name = "nixpkgs_cc_toolchain",
206+
repositories = repositories,
207+
nix_file_content = nix_file_content,
208+
build_file_content = """exports_files(glob(["bin/*"]))""",
209+
)
210+
# Following lines should match
211+
# https://github.com/bazelbuild/bazel/blob/master/tools/cpp/cc_configure.bzl#L93.
212+
_nixpkgs_cc_autoconf(name = "local_config_cc")
213+
native.bind(name = "cc_toolchain", actual = "@local_config_cc//:toolchain")
214+
native.register_toolchains("@local_config_cc//:all")
215+
216+
150217
def _execute_or_fail(repository_ctx, arguments, failure_message = "", *args, **kwargs):
151218
"""Call repository_ctx.execute() and fail if non-zero return code."""
152219
result = repository_ctx.execute(arguments, *args, **kwargs)
@@ -167,11 +234,10 @@ Error output:
167234
return result
168235

169236

170-
def _symlink_children(repository_ctx, target_dir):
171-
"""Create a symlink to all children of `target_dir` in the current
172-
build directory."""
237+
def _find_children(repository_ctx, target_dir):
173238
find_args = [
174239
_executable_path(repository_ctx, "find"),
240+
"-L",
175241
target_dir,
176242
"-maxdepth", "1",
177243
# otherwise the directory is printed as well
@@ -180,10 +246,8 @@ def _symlink_children(repository_ctx, target_dir):
180246
"-print0",
181247
]
182248
exec_result = _execute_or_fail(repository_ctx, find_args)
183-
for target in exec_result.stdout.rstrip("\0").split("\0"):
184-
basename = target.rpartition("/")[-1]
185-
repository_ctx.symlink(target, basename)
186-
249+
return exec_result.stdout.rstrip("\0").split("\0")
250+
187251

188252
def _executable_path(repository_ctx, exe_name, extra_msg=""):
189253
"""Try to find the executable, fail with an error."""
@@ -192,70 +256,3 @@ def _executable_path(repository_ctx, exe_name, extra_msg=""):
192256
fail("Could not find the `{}` executable in PATH.{}\n"
193257
.format(exe_name, " " + extra_msg if extra_msg else ""))
194258
return path
195-
196-
197-
def _cc_configure_custom(ctx):
198-
overriden_tools = {
199-
"gcc": ctx.path(ctx.attr.gcc),
200-
"ld": ctx.path(ctx.attr.ld),
201-
}
202-
return cc_autoconf_impl(ctx, overriden_tools)
203-
204-
205-
cc_configure_custom = repository_rule(
206-
implementation = _cc_configure_custom,
207-
attrs = {
208-
"gcc": attr.label(
209-
executable=True,
210-
cfg="host",
211-
allow_single_file=True,
212-
doc="`gcc` to use in cc toolchain",
213-
),
214-
"ld": attr.label(
215-
executable=True,
216-
cfg="host",
217-
allow_single_file=True,
218-
doc="`ld` to use in cc toolchain",
219-
),
220-
},
221-
local = True,
222-
environ = [
223-
"ABI_LIBC_VERSION",
224-
"ABI_VERSION",
225-
"BAZEL_COMPILER",
226-
"BAZEL_HOST_SYSTEM",
227-
"BAZEL_LINKOPTS",
228-
"BAZEL_PYTHON",
229-
"BAZEL_SH",
230-
"BAZEL_TARGET_CPU",
231-
"BAZEL_TARGET_LIBC",
232-
"BAZEL_TARGET_SYSTEM",
233-
"BAZEL_USE_CPP_ONLY_TOOLCHAIN",
234-
"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN",
235-
"BAZEL_USE_LLVM_NATIVE_COVERAGE",
236-
"BAZEL_VC",
237-
"BAZEL_VS",
238-
"CC",
239-
"CC_CONFIGURE_DEBUG",
240-
"CC_TOOLCHAIN_NAME",
241-
"CPLUS_INCLUDE_PATH",
242-
"CUDA_COMPUTE_CAPABILITIES",
243-
"CUDA_PATH",
244-
"GCOV",
245-
"HOMEBREW_RUBY_PATH",
246-
"NO_WHOLE_ARCHIVE_OPTION",
247-
"SYSTEMROOT",
248-
"USE_DYNAMIC_CRT",
249-
"USE_MSVC_WRAPPER",
250-
"VS90COMNTOOLS",
251-
"VS100COMNTOOLS",
252-
"VS110COMNTOOLS",
253-
"VS120COMNTOOLS",
254-
"VS140COMNTOOLS",
255-
],
256-
)
257-
"""Overwrite cc toolchain by supplying custom `gcc` and `ld` (e.g. from
258-
Nix). This allows to fix mismatch of `gcc` versions between what is used by
259-
packages that come from Nix (e.g. `ghc`) and what Bazel detects
260-
automatically (i.e. system-level `gcc`).
261-
"""

tests/BUILD

+6
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,9 @@ package(default_testonly = 1)
4747
timeout = "short",
4848
)
4949
]
50+
51+
# Test nixpkgs_cc_configure() by building some CC code.
52+
cc_binary(
53+
name = "cc-test",
54+
srcs = ["cc-test.cc"],
55+
)

tests/cc-test.cc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int main() { return 0; }

0 commit comments

Comments
 (0)