daml/bazel_tools/nixpkgs-hermetic-cc-toolchain.patch
Andreas Herrmann 4b1438276c
Update Bazel 2.1.0 --> 3.3.1 (#6761)
* Upgrade nixpkgs revision

* Remove unused minio

It used to be used as a gateway to push the Nix cache to GCS, but has
since been replaced by nix-store-gcs-proxy.

* Update Bazel on Windows

changelog_begin
changelog_end

* Fix hlint warnings

The nixpkgs update implied an hlint update which enabled new warnings.

* Fix "Error applying patch"

Since Bazel 2.2.0 the order of generating `WORKSPACE` and `BUILD` files
and applying patches has been reversed. The allows users to define
patches to these files that will not be immediately overwritten.
However, it also means that patches on another repository's original
`WORKSPACE` file will likely become invalid.

* a948eb7255
* https://github.com/bazelbuild/bazel/issues/10681

Hint: If you're generating a patch with `git` then you can use the
following command to exclude the `WORKSPACE` file.

```
git diff ':(exclude)WORKSPACE'
```

* Update rules_nixpkgs

* nixpkgs location expansion escaping

* Drop --noincompatible_windows_native_test_wrapper

* client_server_test using sh_inline_test

client_server_test used to produce an executable shell script in form of
a text file output. However, since the removal of
`--noincompatible_windows_native_test_wrapper` this no longer works on
Windows since `.sh` files are not directly executable on Windows.

This change fixes the issue by producing the script file in a dedicated
rule and then wrapping it in a `sh_test` rule which also works on
Windows.

* daml_test using sh_inline_test

* daml_doc_test using sh_inline_test

* _daml_validate_test using sh_inline_test

* damlc_compile_test using sh_inline_test

* client_server_test find .exe on Windows

* Bump Windows cache for Bazel update

Remove `clean --expunge` after merge.

Co-authored-by: Andreas Herrmann <andreas.herrmann@tweag.io>
2020-07-23 09:46:04 +02:00

1289 lines
45 KiB
Diff

diff --git a/.bazelrc b/.bazelrc
index d226e65..687c136 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -1 +1,7 @@
build --host_platform=@io_tweag_rules_nixpkgs//nixpkgs/platforms:host
+build --crosstool_top=@nixpkgs_config_cc//:toolchain
+# Using toolchain resolution can lead to spurious dependencies on
+# `@local_config_cc//:builtin_include_directory_paths`. This needs to be
+# resolved before `--incompatible_enable_cc_toolchain_resolution` can be
+# recommended for `nixpkgs_cc_configure_hermetic`.
+# build --incompatible_enable_cc_toolchain_resolution
diff --git a/BUILD.bazel b/BUILD.bazel
index e69de29..efcf274 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -0,0 +1,4 @@
+exports_files([
+ "nixpkgs.json",
+ "nixpkgs.nix",
+])
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8596633..234c41b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/).
+## [Unreleased]
+
+[Unreleased]: https://github.com/tweag/rules_nixpkgs/compare/v0.7.0...HEAD
+
+### Added
+
+- Add `expand_location` attribute to `nixpkgs_package`. When enabled instances
+ of `$(location LABEL)` in the `nixopts` attribute will be expanded to the
+ file path of the file referenced by `LABEL`.
+ See [#128][#128].
+- Define `nixpkgs_cc_configure_hermetic` in `//nixpkgs:repositories.bzl`.
+ See [#128][#128].
+
+### Deprecated
+
+- `nixpkgs_cc_configure` has been deprecated in favor of
+ `nixpkgs_cc_configure_hermetic` and will be replaced by it in future.
+ See [#128][#128].
+
+[#128]: https://github.com/tweag/rules_nixpkgs/pull/128
+
## [0.7.0] - 2020-04-20
[0.7.0]: https://github.com/tweag/rules_nixpkgs/compare/v0.6.0...v0.7.0
diff --git a/README.md b/README.md
index 721f64d..3ee682b 100644
--- a/README.md
+++ b/README.md
@@ -176,7 +176,7 @@ Make the content of a Nixpkgs package available in the Bazel workspace.
nixpkgs_package(
name, attribute_path, nix_file, nix_file_deps, nix_file_content,
repository, repositories, build_file, build_file_content, nixopts,
- fail_not_supported,
+ expand_location, fail_not_supported,
)
```
@@ -306,6 +306,18 @@ filegroup(
<p>Extra flags to pass when calling Nix.</p>
</td>
</tr>
+ <tr>
+ <td><code>nixopts</code></td>
+ <td>
+ <p><code>Bool; optional</code></p>
+ <p>
+ If set to <code>True</code> any instance of
+ <code>$(location LABEL)</code> in <code>nixopts</code>
+ will be replaced by the path to the file referenced by
+ <code>LABEL</code> relative to the workspace root.
+ </p>
+ </td>
+ </tr>
<tr>
<td><code>fail_not_supported</code></td>
<td>
@@ -321,6 +333,99 @@ filegroup(
</tbody>
</table>
+### nixpkgs_cc_configure_hermetic
+
+Use a CC toolchain from Nixpkgs. No-op if not a nix-based platform.
+
+By default, Bazel auto-configures a CC toolchain from commands (e.g.
+`gcc`) available in the environment. To make builds more hermetic, use
+this rule to specify explicitly which commands the toolchain should use.
+
+Specifically, it builds a Nix derivation that provides the CC toolchain tools
+in the `bin/` path and constructs a CC toolchain that uses those tools. The
+following tools are expected `ar`, `cpp`, `dwp`, `cc`, `gcov`, `ld`, `nm`,
+`objcopy`, `objdump`, `strip`. Tools that aren't found are replaced by
+`${coreutils}/bin/false`.
+
+Note:
+
+You need to configure `--crosstool_top=@<name>//:toolchain` to activate this
+toolchain.
+
+Example:
+
+```bzl
+nixpkgs_cc_configure_hermetic(repository = "@nixpkgs//:default.nix")
+```
+
+<table class="table table-condensed table-bordered table-params">
+ <colgroup>
+ <col class="col-param" />
+ <col class="param-description" />
+ </colgroup>
+ <thead>
+ <tr>
+ <th colspan="2">Attributes</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><code>attribute_path</code></td>
+ <td>
+ <p><code>String; optional</code></p>
+ <p>Obtain the toolchain from the Nix expression under this attribute path. Requires `nix_file` or `nix_file_content`.</p>
+ </td>
+ <td><code>nix_file</code></td>
+ <td>
+ <p><code>String; optional</code></p>
+ <p>Obtain the toolchain from the Nix expression defined in this file. Specify only one of `nix_file` or `nix_file_content`.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>nix_file_content</code></td>
+ <td>
+ <p><code>String; optional</code></p>
+ <p>Obtain the toolchain from the given Nix expression. Specify only one of `nix_file` or `nix_file_content`.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>nix_file_deps</code></td>
+ <td>
+ <p><code>List of labels; optional</code></p>
+ <p>Additional files that the Nix expression depends on.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>repository</code></td>
+ <td>
+ <p><code>Label; optional</code></p>
+ <p>Provides `<nixpkgs>`. Specify one of `repositories` or `repository`.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>repositories</code></td>
+ <td>
+ <p><code>String-keyed label dict; optional</code></p>
+ <p>Provides `<nixpkgs>` and other repositories. Specify one of `repositories` or `repository`.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>quiet</code></td>
+ <td>
+ <p><code>Bool; optional</code></p>
+ <p>Whether to hide `nix-build` output.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>fail_not_supported</code></td>
+ <td>
+ <p><code>Bool; optional</code></p>
+ <p>Whether to fail if `nix-build` is not available.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
### nixpkgs_cc_configure
Tells Bazel to use compilers and linkers from Nixpkgs for the CC
@@ -328,6 +433,16 @@ toolchain. By default, Bazel autodetects a toolchain on the current
`PATH`. Overriding this autodetection makes builds more hermetic and
is considered a best practice.
+Deprecated:
+
+Use `nixpkgs_cc_configure_hermetic` instead.
+
+While this improves upon Bazel's autoconfigure toolchain by picking tools from
+a Nix derivation rather than the environment, it is still not fully hermetic as
+it is affected by the environment. In particular, system include directories
+specified in the environment can leak in and affect the cache keys of targets
+depending on the cc toolchain leading to cache misses.
+
Example:
```bzl
diff --git a/nixpkgs/BUILD.bazel b/nixpkgs/BUILD.bazel
index 09fa6aa..f055a77 100644
--- a/nixpkgs/BUILD.bazel
+++ b/nixpkgs/BUILD.bazel
@@ -6,7 +6,11 @@ exports_files([
"nixpkgs.bzl",
])
-filegroup(name = "srcs", srcs = glob(["**"]), visibility = ["//visibility:public"])
+filegroup(
+ name = "srcs",
+ srcs = glob(["**"]),
+ visibility = ["//visibility:public"],
+)
# @bazel_tools//tools does not define a bzl_library itself, instead we are
# supposed to define our own using the @bazel_tools//tools:bzl_srcs filegroup.
diff --git a/nixpkgs/constraints/BUILD.bazel b/nixpkgs/constraints/BUILD.bazel
index 6662530..94678bd 100644
--- a/nixpkgs/constraints/BUILD.bazel
+++ b/nixpkgs/constraints/BUILD.bazel
@@ -3,7 +3,7 @@ package(default_visibility = ["//visibility:public"])
constraint_setting(name = "nix")
constraint_value(
- name = "support_nix",
+ name = "support_nix",
constraint_setting = ":nix",
)
diff --git a/nixpkgs/nixpkgs.bzl b/nixpkgs/nixpkgs.bzl
index 5b77c87..99754f5 100644
--- a/nixpkgs/nixpkgs.bzl
+++ b/nixpkgs/nixpkgs.bzl
@@ -1,12 +1,22 @@
"""Rules for importing Nixpkgs packages."""
+load("@bazel_skylib//lib:paths.bzl", "paths")
+load("@bazel_skylib//lib:sets.bzl", "sets")
+load("@bazel_skylib//lib:versions.bzl", "versions")
load("@bazel_tools//tools/cpp:cc_configure.bzl", "cc_autoconf_impl")
-load("@bazel_tools//tools/cpp:lib_cc_configure.bzl", "get_cpu_value")
+load(
+ "@bazel_tools//tools/cpp:lib_cc_configure.bzl",
+ "get_cpu_value",
+ "get_starlark_list",
+ "write_builtin_include_directory_paths",
+)
+load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
def _nixpkgs_git_repository_impl(repository_ctx):
repository_ctx.file(
"BUILD",
- content = 'filegroup(name = "srcs", srcs = glob(["**"]), visibility = ["//visibility:public"])')
+ content = 'filegroup(name = "srcs", srcs = glob(["**"]), visibility = ["//visibility:public"])',
+ )
# Make "@nixpkgs" (syntactic sugar for "@nixpkgs//:nixpkgs") a valid
# label for default.nix.
@@ -72,6 +82,73 @@ nixpkgs_local_repository = repository_rule(
def _is_supported_platform(repository_ctx):
return repository_ctx.which("nix-build") != None
+def _expand_location(repository_ctx, string, labels, attr = None):
+ """Expand `$(location label)` to a path.
+
+ Raises an error on unexpected occurrences of `$`.
+ Use `$$` to insert a verbatim `$`.
+
+ Attrs:
+ repository_ctx: The repository rule context.
+ string: string, Replace instances of `$(location )` in this string.
+ labels: dict from label to path: Known label to path mappings.
+ attr: string, The rule attribute to use for error reporting.
+
+ Returns:
+ The string with all instances of `$(location )` replaced by paths.
+ """
+ result = ""
+ offset = 0
+ # Step through occurrences of `$`. This is bounded by the length of the string.
+ for _ in range(len(string)):
+ start = string.find("$", offset)
+ if start == -1:
+ result += string[offset:]
+ break
+ else:
+ result += string[offset:start]
+ if start + 1 == len(string):
+ fail("Unescaped '$' in location expansion at end of input", attr)
+ elif string[start + 1] == "$":
+ # Insert verbatim '$'.
+ result += "$"
+ offset = start + 2
+ elif string[start + 1] == "(":
+ group_start = start + 2
+ group_end = string.find(")", group_start)
+ if group_end == -1:
+ fail("Unbalanced parentheses in location expansion for '{}'.".format(string[start:]), attr)
+ group = string[group_start:group_end]
+ if group.startswith("location "):
+ label_str = group[len("location "):]
+ label_candidates = [
+ (lbl, path)
+ for (lbl, path) in labels.items()
+ if lbl.relative(label_str) == lbl
+ ]
+ if len(label_candidates) == 0:
+ fail("Unknown label '{}' in location expansion for '{}'.".format(label_str, string), attr)
+ elif len(label_candidates) > 1:
+ fail(
+ "Ambiguous label '{}' in location expansion for '{}'. Candidates: {}".format(
+ label_str,
+ string,
+ ", ".join([str(lbl) for lbl in label_candidates]),
+ ),
+ attr,
+ )
+ location = paths.join(".", paths.relativize(
+ str(repository_ctx.path(label_candidates[0][1])),
+ str(repository_ctx.path(".")),
+ ))
+ result += location
+ else:
+ fail("Unrecognized location expansion '$({})'.".format(group), attr)
+ offset = group_end + 1
+ else:
+ fail("Unescaped '$' in location expansion at position {} of input.".format(start), attr)
+ return result
+
def _nixpkgs_package_impl(repository_ctx):
repository = repository_ctx.attr.repository
repositories = repository_ctx.attr.repositories
@@ -119,8 +196,9 @@ def _nixpkgs_package_impl(repository_ctx):
else:
expr_args = ["-E", "import <nixpkgs> { config = {}; overlays = []; }"]
+ nix_file_deps = {}
for dep in repository_ctx.attr.nix_file_deps:
- _cp(repository_ctx, dep)
+ nix_file_deps[dep] = _cp(repository_ctx, dep)
expr_args.extend([
"-A",
@@ -135,7 +213,13 @@ def _nixpkgs_package_impl(repository_ctx):
"bazel-support/nix-out-link",
])
- expr_args.extend(repository_ctx.attr.nixopts)
+ if repository_ctx.attr.expand_location:
+ expr_args.extend([
+ _expand_location(repository_ctx, opt, nix_file_deps, "nixopts")
+ for opt in repository_ctx.attr.nixopts
+ ])
+ else:
+ expr_args.extend(repository_ctx.attr.nixopts)
for repo in repositories.keys():
path = str(repository_ctx.path(repo).dirname) + "/nix-file-deps"
@@ -208,7 +292,7 @@ def _nixpkgs_package_impl(repository_ctx):
if create_build_file_if_needed:
p = repository_ctx.path("BUILD")
if not p.exists:
- repository_ctx.template("BUILD", Label("@io_tweag_rules_nixpkgs//nixpkgs:BUILD.pkg"))
+ repository_ctx.template("BUILD", Label("@io_tweag_rules_nixpkgs//nixpkgs:BUILD.pkg"))
_nixpkgs_package = repository_rule(
implementation = _nixpkgs_package_impl,
@@ -222,6 +306,7 @@ _nixpkgs_package = repository_rule(
"build_file": attr.label(),
"build_file_content": attr.string(),
"nixopts": attr.string_list(),
+ "expand_location": attr.bool(default = False),
"quiet": attr.bool(),
"fail_not_supported": attr.bool(default = True, doc = """
If set to True (default) this rule will fail on platforms which do not support Nix (e.g. Windows). If set to False calling this rule will succeed but no output will be generated.
@@ -246,6 +331,352 @@ def nixpkgs_package(*args, **kwargs):
else:
_nixpkgs_package(*args, **kwargs)
+def _parse_cc_toolchain_info(content, filename):
+ """Parses the `CC_TOOLCHAIN_INFO` file generated by Nix.
+
+ Attrs:
+ content: string, The content of the `CC_TOOLCHAIN_INFO` file.
+ filename: string, The path to the `CC_TOOLCHAIN_INFO` file, used for error reporting.
+
+ Returns:
+ struct, The substitutions for `@bazel_tools//tools/cpp:BUILD.tpl`.
+ """
+
+ # Parse the content of CC_TOOLCHAIN_INFO.
+ #
+ # Each line has the form
+ #
+ # <key>:<value1>:<value2>:...
+ info = {}
+ for line in content.splitlines():
+ fields = line.split(":")
+ if len(fields) == 0:
+ fail(
+ "Malformed CC_TOOLCHAIN_INFO '{}': Empty line encountered.".format(filename),
+ "cc_toolchain_info",
+ )
+ info[fields[0]] = fields[1:]
+
+ # Validate the keys in CC_TOOLCHAIN_INFO.
+ expected_keys = sets.make([
+ "TOOL_NAMES",
+ "TOOL_PATHS",
+ "CXX_BUILTIN_INCLUDE_DIRECTORIES",
+ "COMPILER_FLAGS",
+ "CXX_FLAGS",
+ "LINK_FLAGS",
+ "LINK_LIBS",
+ "OPT_COMPILE_FLAGS",
+ "OPT_LINK_FLAGS",
+ "UNFILTERED_COMPILE_FLAGS",
+ "DBG_COMPILE_FLAGS",
+ "COVERAGE_COMPILE_FLAGS",
+ "COVERAGE_LINK_FLAGS",
+ "SUPPORTS_START_END_LIB",
+ "IS_CLANG",
+ ])
+ actual_keys = sets.make(info.keys())
+ missing_keys = sets.difference(expected_keys, actual_keys)
+ unexpected_keys = sets.difference(actual_keys, expected_keys)
+ if sets.length(missing_keys) > 0:
+ fail(
+ "Malformed CC_TOOLCHAIN_INFO '{}': Missing entries '{}'.".format(
+ filename,
+ "', '".join(sets.to_list(missing_keys)),
+ ),
+ "cc_toolchain_info",
+ )
+ if sets.length(unexpected_keys) > 0:
+ fail(
+ "Malformed CC_TOOLCHAIN_INFO '{}': Unexpected entries '{}'.".format(
+ filename,
+ "', '".join(sets.to_list(unexpected_keys)),
+ ),
+ "cc_toolchain_info",
+ )
+
+ return struct(
+ tool_paths = {
+ tool: path
+ for (tool, path) in zip(info["TOOL_NAMES"], info["TOOL_PATHS"])
+ },
+ cxx_builtin_include_directories = info["CXX_BUILTIN_INCLUDE_DIRECTORIES"],
+ compiler_flags = info["COMPILER_FLAGS"],
+ cxx_flags = info["CXX_FLAGS"],
+ link_flags = info["LINK_FLAGS"],
+ link_libs = info["LINK_LIBS"],
+ opt_compile_flags = info["OPT_COMPILE_FLAGS"],
+ opt_link_flags = info["OPT_LINK_FLAGS"],
+ unfiltered_compile_flags = info["UNFILTERED_COMPILE_FLAGS"],
+ dbg_compile_flags = info["DBG_COMPILE_FLAGS"],
+ coverage_compile_flags = info["COVERAGE_COMPILE_FLAGS"],
+ coverage_link_flags = info["COVERAGE_LINK_FLAGS"],
+ supports_start_end_lib = info["SUPPORTS_START_END_LIB"] == ["True"],
+ is_clang = info["IS_CLANG"] == ["True"],
+ )
+
+def _nixpkgs_cc_toolchain_config_impl(repository_ctx):
+ cpu_value = get_cpu_value(repository_ctx)
+ darwin = cpu_value == "darwin"
+
+ cc_toolchain_info_file = repository_ctx.path(repository_ctx.attr.cc_toolchain_info)
+ if not cc_toolchain_info_file.exists and not repository_ctx.attr.fail_not_supported:
+ return
+ info = _parse_cc_toolchain_info(
+ repository_ctx.read(cc_toolchain_info_file),
+ cc_toolchain_info_file,
+ )
+
+ # Generate the cc_toolchain workspace following the example from
+ # `@bazel_tools//tools/cpp:unix_cc_configure.bzl`.
+ repository_ctx.symlink(
+ repository_ctx.path(repository_ctx.attr._unix_cc_toolchain_config),
+ "cc_toolchain_config.bzl",
+ )
+ repository_ctx.symlink(
+ repository_ctx.path(repository_ctx.attr._armeabi_cc_toolchain_config),
+ "armeabi_cc_toolchain_config.bzl",
+ )
+
+ # A module map is required for clang starting from Bazel version 3.3.0.
+ # https://github.com/bazelbuild/bazel/commit/8b9f74649512ee17ac52815468bf3d7e5e71c9fa
+ needs_module_map = info.is_clang and versions.is_at_least("3.3.0", versions.get())
+ if needs_module_map:
+ generate_system_module_map = [
+ repository_ctx.path(repository_ctx.attr._generate_system_module_map),
+ ]
+ repository_ctx.file(
+ "module.modulemap",
+ _execute_or_fail(
+ repository_ctx,
+ generate_system_module_map + info.cxx_builtin_include_directories,
+ "Failed to generate system module map.",
+ ).stdout.strip(),
+ executable = False,
+ )
+ cc_wrapper_src = (
+ repository_ctx.attr._osx_cc_wrapper if darwin else repository_ctx.attr._linux_cc_wrapper
+ )
+ repository_ctx.template(
+ "cc_wrapper.sh",
+ repository_ctx.path(cc_wrapper_src),
+ {
+ "%{cc}": info.tool_paths["gcc"],
+ "%{env}": "",
+ },
+ )
+ if darwin:
+ info.tool_paths["gcc"] = "cc_wrapper.sh"
+ info.tool_paths["ar"] = "/usr/bin/libtool"
+ write_builtin_include_directory_paths(
+ repository_ctx,
+ info.tool_paths["gcc"],
+ info.cxx_builtin_include_directories,
+ )
+ repository_ctx.template(
+ "BUILD.bazel",
+ repository_ctx.path(repository_ctx.attr._build),
+ {
+ "%{cc_toolchain_identifier}": "local",
+ "%{name}": cpu_value,
+ "%{modulemap}": ("\":module.modulemap\"" if needs_module_map else "None"),
+ "%{supports_param_files}": "0" if darwin else "1",
+ "%{cc_compiler_deps}": get_starlark_list(
+ [":builtin_include_directory_paths"] + (
+ [":cc_wrapper"] if darwin else []
+ ),
+ ),
+ "%{compiler}": "compiler",
+ "%{abi_version}": "local",
+ "%{abi_libc_version}": "local",
+ "%{host_system_name}": "local",
+ "%{target_libc}": "macosx" if darwin else "local",
+ "%{target_cpu}": cpu_value,
+ "%{target_system_name}": "local",
+ "%{tool_paths}": ",\n ".join(
+ ['"%s": "%s"' % (k, v) for (k, v) in info.tool_paths.items()],
+ ),
+ "%{cxx_builtin_include_directories}": get_starlark_list(info.cxx_builtin_include_directories),
+ "%{compile_flags}": get_starlark_list(info.compiler_flags),
+ "%{cxx_flags}": get_starlark_list(info.cxx_flags),
+ "%{link_flags}": get_starlark_list(info.link_flags),
+ "%{link_libs}": get_starlark_list(info.link_libs),
+ "%{opt_compile_flags}": get_starlark_list(info.opt_compile_flags),
+ "%{opt_link_flags}": get_starlark_list(info.opt_link_flags),
+ "%{unfiltered_compile_flags}": get_starlark_list(info.unfiltered_compile_flags),
+ "%{dbg_compile_flags}": get_starlark_list(info.dbg_compile_flags),
+ "%{coverage_compile_flags}": get_starlark_list(info.coverage_compile_flags),
+ "%{coverage_link_flags}": get_starlark_list(info.coverage_link_flags),
+ "%{supports_start_end_lib}": repr(info.supports_start_end_lib),
+ },
+ )
+
+_nixpkgs_cc_toolchain_config = repository_rule(
+ _nixpkgs_cc_toolchain_config_impl,
+ attrs = {
+ "cc_toolchain_info": attr.label(),
+ "fail_not_supported": attr.bool(),
+ "_unix_cc_toolchain_config": attr.label(
+ default = Label("@bazel_tools//tools/cpp:unix_cc_toolchain_config.bzl"),
+ ),
+ "_armeabi_cc_toolchain_config": attr.label(
+ default = Label("@bazel_tools//tools/cpp:armeabi_cc_toolchain_config.bzl"),
+ ),
+ "_generate_system_module_map": attr.label(
+ default = Label("@bazel_tools//tools/cpp:generate_system_module_map.sh"),
+ ),
+ "_osx_cc_wrapper": attr.label(
+ default = Label("@bazel_tools//tools/cpp:osx_cc_wrapper.sh.tpl"),
+ ),
+ "_linux_cc_wrapper": attr.label(
+ default = Label("@bazel_tools//tools/cpp:linux_cc_wrapper.sh.tpl"),
+ ),
+ "_build": attr.label(
+ default = Label("@bazel_tools//tools/cpp:BUILD.tpl"),
+ ),
+ },
+)
+
+def _nixpkgs_cc_toolchain_impl(repository_ctx):
+ cpu = get_cpu_value(repository_ctx)
+ repository_ctx.file(
+ "BUILD.bazel",
+ executable = False,
+ content = """\
+package(default_visibility = ["//visibility:public"])
+
+toolchain(
+ name = "cc-toolchain-{cpu}",
+ toolchain = "@{cc_toolchain_config}//:cc-compiler-{cpu}",
+ toolchain_type = "@rules_cc//cc:toolchain_type",
+ exec_compatible_with = [
+ "@platforms//cpu:x86_64",
+ "@platforms//os:{os}",
+ "@io_tweag_rules_nixpkgs//nixpkgs/constraints:support_nix",
+ ],
+ target_compatible_with = [
+ "@platforms//cpu:x86_64",
+ "@platforms//os:{os}",
+ ],
+)
+
+toolchain(
+ name = "cc-toolchain-armeabi-v7a",
+ toolchain = "@{cc_toolchain_config}//:cc-compiler-armeabi-v7a",
+ toolchain_type = "@rules_cc//cc:toolchain_type",
+ exec_compatible_with = [
+ "@platforms//cpu:x86_64",
+ "@platforms//os:{os}",
+ "@io_tweag_rules_nixpkgs//nixpkgs/constraints:support_nix",
+ ],
+ target_compatible_with = [
+ "@platforms//cpu:arm",
+ "@platforms//os:android",
+ ],
+)
+""".format(
+ cc_toolchain_config = repository_ctx.attr.cc_toolchain_config,
+ cpu = cpu,
+ os = {"darwin": "osx"}.get(cpu, "linux"),
+ ),
+ )
+
+_nixpkgs_cc_toolchain = repository_rule(
+ _nixpkgs_cc_toolchain_impl,
+ attrs = {
+ "cc_toolchain_config": attr.string(),
+ },
+)
+
+def nixpkgs_cc_configure_hermetic(
+ name = "nixpkgs_config_cc",
+ attribute_path = "",
+ nix_file = None,
+ nix_file_content = "",
+ nix_file_deps = [],
+ repositories = {},
+ repository = None,
+ nixopts = [],
+ quiet = False,
+ fail_not_supported = True):
+ """Use a CC toolchain from Nixpkgs. No-op if not a nix-based platform.
+
+ By default, Bazel auto-configures a CC toolchain from commands (e.g.
+ `gcc`) available in the environment. To make builds more hermetic, use
+ this rule to specify explicitly which commands the toolchain should use.
+
+ Specifically, it builds a Nix derivation that provides the CC toolchain
+ tools in the `bin/` path and constructs a CC toolchain that uses those
+ tools. The following tools are expected `ar`, `cpp`, `dwp`, `cc`, `gcov`,
+ `ld`, `nm`, `objcopy`, `objdump`, `strip`. Tools that aren't found are
+ replaced by `${coreutils}/bin/false`.
+
+ Note:
+ You need to configure `--crosstool_top=@<name>//:toolchain` to activate this
+ toolchain.
+
+ Attrs:
+ attribute_path: optional, string, Obtain the toolchain from the Nix expression under this attribute path. Requires `nix_file` or `nix_file_content`.
+ nix_file: optional, Label, Obtain the toolchain from the Nix expression defined in this file. Specify only one of `nix_file` or `nix_file_content`.
+ nix_file_content: optional, string, Obtain the toolchain from the given Nix expression. Specify only one of `nix_file` or `nix_file_content`.
+ nix_file_deps: optional, list of Label, Additional files that the Nix expression depends on.
+ repositories: dict of Label to string, Provides `<nixpkgs>` and other repositories. Specify one of `repositories` or `repository`.
+ repository: Label, Provides `<nixpkgs>`. Specify one of `repositories` or `repository`.
+ quiet: bool, Whether to hide `nix-build` output.
+ fail_not_supported: bool, Whether to fail if `nix-build` is not available.
+ """
+
+ if attribute_path and not (nix_file or nix_file_content):
+ fail("'attribute_path' requires one of 'nix_file' or 'nix_file_content'", "attribute_path")
+ if nix_file and nix_file_content:
+ fail("Cannot specify both 'nix_file' and 'nix_file_content'.")
+
+ nixopts = list(nixopts)
+ nix_file_deps = list(nix_file_deps)
+ if attribute_path:
+ # The `attribute_path` is forwarded to `cc.nix` as an argument.
+ nixopts.extend(["--argstr", "attribute_path", attribute_path])
+ if nix_file:
+ nixopts.extend(["--arg", "nix_expr", "import $(location {})".format(nix_file)])
+ nix_file_deps.append(nix_file)
+ if nix_file_content:
+ # The `nix_file_content` is forwarded to `cc.nix` as an argument.
+ nixopts.extend(["--arg", "nix_expr", nix_file_content])
+
+ # Invoke `toolchains/cc.nix` which generates `CC_TOOLCHAIN_INFO`.
+ nixpkgs_package(
+ name = "{}_info".format(name),
+ nix_file = "@io_tweag_rules_nixpkgs//nixpkgs:toolchains/cc.nix",
+ nix_file_deps = nix_file_deps,
+ build_file_content = "exports_files(['CC_TOOLCHAIN_INFO'])",
+ repositories = repositories,
+ repository = repository,
+ nixopts = nixopts,
+ expand_location = True,
+ quiet = quiet,
+ fail_not_supported = fail_not_supported,
+ )
+
+ # Generate the `cc_toolchain_config` workspace.
+ _nixpkgs_cc_toolchain_config(
+ name = "{}".format(name),
+ cc_toolchain_info = "@{}_info//:CC_TOOLCHAIN_INFO".format(name),
+ fail_not_supported = fail_not_supported,
+ )
+
+ # Generate the `cc_toolchain` workspace.
+ _nixpkgs_cc_toolchain(
+ name = "{}_toolchains".format(name),
+ cc_toolchain_config = name,
+ )
+
+ maybe(
+ native.bind,
+ name = "cc_toolchain",
+ actual = "@{}//:toolchain".format(name),
+ )
+ native.register_toolchains("@{}_toolchains//:all".format(name))
+
def _readlink(repository_ctx, path):
return repository_ctx.path(path).realpath
@@ -325,6 +756,16 @@ def nixpkgs_cc_configure(
nixopts = []):
"""Use a CC toolchain from Nixpkgs. No-op if not a nix-based platform.
+ Deprecated:
+ Use `nixpkgs_cc_configure_hermetic` instead.
+
+ While this improves upon Bazel's autoconfigure toolchain by picking tools
+ from a Nix derivation rather than the environment, it is still not fully
+ hermetic as it is affected by the environment. In particular, system
+ include directories specified in the environment can leak in and affect
+ the cache keys of targets depending on the cc toolchain leading to cache
+ misses.
+
By default, Bazel auto-configures a CC toolchain from commands (e.g.
`gcc`) available in the environment. To make builds more hermetic, use
this rule to specific explicitly which commands the toolchain should
diff --git a/nixpkgs/toolchains/cc.nix b/nixpkgs/toolchains/cc.nix
new file mode 100644
index 0000000..1e9e089
--- /dev/null
+++ b/nixpkgs/toolchains/cc.nix
@@ -0,0 +1,284 @@
+with import <nixpkgs> { config = {}; overlays = []; };
+
+{ attribute_path ? null
+, nix_expr ? null
+}:
+
+let
+ darwinCC =
+ # Work around https://github.com/NixOS/nixpkgs/issues/42059.
+ # See also https://github.com/NixOS/nixpkgs/pull/41589.
+ with darwin.apple_sdk.frameworks;
+ runCommand "bazel-nixpkgs-cc-wrapper"
+ {
+ buildInputs = [ stdenv.cc makeWrapper ];
+ }
+ ''
+ mkdir -p $out/bin
+
+ for i in ${stdenv.cc}/bin/*; do
+ ln -sf $i $out/bin
+ done
+
+ # Override cc
+ rm -f $out/bin/cc $out/bin/clang $out/bin/clang++
+ makeWrapper ${stdenv.cc}/bin/cc $out/bin/cc --add-flags \
+ "-isystem ${llvmPackages.libcxx}/include/c++/v1 \
+ -F${CoreFoundation}/Library/Frameworks \
+ -F${CoreServices}/Library/Frameworks \
+ -F${Security}/Library/Frameworks \
+ -F${Foundation}/Library/Frameworks \
+ -L${libcxx}/lib \
+ -L${darwin.libobjc}/lib"
+ '';
+ cc =
+ if isNull nix_expr then
+ buildEnv {
+ name = "bazel-nixpkgs-cc";
+ # XXX: `gcov` is missing in `/bin`.
+ # It exists in `stdenv.cc.cc` but that collides with `stdenv.cc`.
+ paths =
+ if stdenv.isDarwin then
+ [ (overrideCC stdenv darwinCC).cc darwin.binutils ]
+ else
+ [ stdenv.cc binutils ];
+ pathsToLink = [ "/bin" ];
+ }
+ else if isNull attribute_path then
+ nix_expr
+ else
+ lib.attrByPath (lib.splitString "." attribute_path) null nix_expr
+ ;
+in
+ runCommand "bazel-nixpkgs-cc-toolchain"
+ { executable = false;
+ # Pointless to do this on a remote machine.
+ preferLocalBuild = true;
+ allowSubstitutes = false;
+ }
+ ''
+ # This constructs the substitutions for
+ # `@bazel_tools//tools/cpp:BUILD.tpl` following the example of
+ # `@bazel_tools//tools/cpp:unix_cc_configure.bzl` as of Bazel v2.1.0 git
+ # revision 0f4c498a270f05b3896d57055b6489e824821eda.
+
+ # Determine toolchain tool paths.
+ #
+ # If a tool is not available then we use `bin/false` as a stand-in.
+ declare -A TOOLS=( [ar]=ar [cpp]=cpp [dwp]=dwp [gcc]=cc [gcov]=gcov [ld]=ld [nm]=nm [objcopy]=objcopy [objdump]=objdump [strip]=strip )
+ TOOL_NAMES=(''${!TOOLS[@]})
+ declare -A TOOL_PATHS=()
+ for tool_name in ''${!TOOLS[@]}; do
+ tool_path=${cc}/bin/''${TOOLS[$tool_name]}
+ if [[ -x $tool_path ]]; then
+ TOOL_PATHS[$tool_name]=$tool_path
+ else
+ TOOL_PATHS[$tool_name]=${coreutils}/bin/false
+ fi
+ done
+ cc=''${TOOL_PATHS[gcc]}
+
+ # Check whether a flag is supported by the compiler.
+ #
+ # The logic checks whether the flag causes an error message that contains
+ # the flag (or a pattern) verbatim. The assumption is that this will be a
+ # message of the kind `unknown argument: XYZ`. This logic is copied and
+ # adapted to bash from `@bazel_tools//tools/cpp:unix_cc_configure.bzl`.
+ is_compiler_option_supported() {
+ local option="$1"
+ local pattern="''${2-$1}"
+ { $cc "$option" -o /dev/null -c -x c++ - <<<"int main() {}" 2>&1 1>/dev/null || true; } \
+ | grep -qe "$pattern" && return 1 || return 0
+ }
+ is_linker_option_supported() {
+ local option="$1"
+ local pattern="''${2-$1}"
+ { $cc "$option" -o /dev/null -x c++ - <<<"int main() {}" 2>&1 1>/dev/null || true; } \
+ | grep -qe "$pattern" && return 1 || return 0
+ }
+ add_compiler_option_if_supported() {
+ if is_compiler_option_supported "$@"; then
+ echo "$1"
+ fi
+ }
+ add_linker_option_if_supported() {
+ if is_linker_option_supported "$@"; then
+ echo "$1"
+ fi
+ }
+
+ # Determine default include directories.
+ #
+ # This is copied and adapted to bash from
+ # `@bazel_tools//tools/cpp:unix_cc_configure.bzl`.
+ IFS=$'\n'
+ include_dirs_for() {
+ $cc -E -x "$1" - -v "''${@:2}" 2>&1 \
+ | sed '1,/^#include <...>/d;/^[^ ]/,$d;s/^ *//' \
+ | tr '\n' '\0' \
+ | xargs -0 realpath -ms
+ }
+ CXX_BUILTIN_INCLUDE_DIRECTORIES=($({
+ include_dirs_for c
+ include_dirs_for c++
+ if is_compiler_option_supported -fno-canonical-system-headers; then
+ include_dirs_for c -fno-canonical-system-headers
+ include_dirs_for c++ -std=c++0x -fno-canonical-system-headers
+ elif is_compiler_option_supported -no-canonical-prefixes; then
+ include_dirs_for c -no-canonical-prefixes
+ include_dirs_for c++ -std=c++0x -no-canonical-prefixes
+ fi
+ } 2>&1 | sort -u))
+ unset IFS
+
+ # Determine list of supported compiler and linker flags.
+ #
+ # This is copied and adapted to bash from
+ # `@bazel_tools//tools/cpp:unix_cc_configure.bzl`.
+ COMPILER_FLAGS=(
+ # Security hardening requires optimization.
+ # We need to undef it as some distributions now have it enabled by default.
+ -U_FORTIFY_SOURCE
+ -fstack-protector
+ # All warnings are enabled. Maybe enable -Werror as well?
+ -Wall
+ $(
+ # Enable a few more warnings that aren't part of -Wall.
+ add_compiler_option_if_supported -Wthread-safety
+ add_compiler_option_if_supported -Wself-assign
+ # Disable problematic warnings.
+ add_compiler_option_if_supported -Wunused-but-set-parameter
+ # has false positives
+ add_compiler_option_if_supported -Wno-free-nonheap-object
+ # Enable coloring even if there's no attached terminal. Bazel removes the
+ # escape sequences if --nocolor is specified.
+ add_compiler_option_if_supported -fcolor-diagnostics
+ )
+ # Keep stack frames for debugging, even in opt mode.
+ -fno-omit-frame-pointer
+ )
+ CXX_FLAGS=(-std=c++0x)
+ LINK_FLAGS=(
+ $(
+ if [[ -x ${cc}/bin/ld.gold ]]; then echo -fuse-ld=gold; fi
+ add_linker_option_if_supported -Wl,-no-as-needed -no-as-needed
+ add_linker_option_if_supported -Wl,-z,relro,-z,now -z
+ )
+ ${
+ if stdenv.isDarwin
+ then "-undefined dynamic_lookup -headerpad_max_install_names"
+ else "-B${cc}/bin"
+ }
+ $(
+ # Have gcc return the exit code from ld.
+ add_compiler_option_if_supported -pass-exit-codes
+ )
+ -lstdc++
+ -lm
+ )
+ LINK_LIBS=()
+ OPT_COMPILE_FLAGS=(
+ # No debug symbols.
+ # Maybe we should enable https://gcc.gnu.org/wiki/DebugFission for opt or
+ # even generally? However, that can't happen here, as it requires special
+ # handling in Bazel.
+ -g0
+
+ # Conservative choice for -O
+ # -O3 can increase binary size and even slow down the resulting binaries.
+ # Profile first and / or use FDO if you need better performance than this.
+ -O2
+
+ # Security hardening on by default.
+ # Conservative choice; -D_FORTIFY_SOURCE=2 may be unsafe in some cases.
+ -D_FORTIFY_SOURCE=1
+
+ # Disable assertions
+ -DNDEBUG
+
+ # Removal of unused code and data at link time (can this increase binary
+ # size in some cases?).
+ -ffunction-sections
+ -fdata-sections
+ )
+ OPT_LINK_FLAGS=(
+ ${
+ if stdenv.isDarwin
+ then ""
+ else "$(add_linker_option_if_supported -Wl,--gc-sections -gc-sections)"
+ }
+ )
+ UNFILTERED_COMPILE_FLAGS=(
+ $(
+ if is_compiler_option_supported -fno-canonical-system-headers; then
+ echo -fno-canonical-system-headers
+ elif is_compiler_option_supported -no-canonical-prefixes; then
+ echo -no-canonical-prefixes
+ fi
+ )
+ # Make C++ compilation deterministic. Use linkstamping instead of these
+ # compiler symbols.
+ -Wno-builtin-macro-redefined
+ -D__DATE__=\\\"redacted\\\"
+ -D__TIMESTAMP__=\\\"redacted\\\"
+ -D__TIME__=\\\"redacted\\\"
+ )
+ DBG_COMPILE_FLAGS=(-g)
+ COVERAGE_COMPILE_FLAGS=(
+ ${
+ if stdenv.isDarwin then
+ "-fprofile-instr-generate -fcoverage-mapping"
+ else
+ "--coverage"
+ }
+ )
+ COVERAGE_LINK_FLAGS=(
+ ${
+ if stdenv.isDarwin then
+ "-fprofile-instr-generate"
+ else
+ "--coverage"
+ }
+ )
+ SUPPORTS_START_END_LIB=(
+ $(
+ if [[ -x ${cc}/bin/ld.gold ]]; then echo True; else echo False; fi
+ )
+ )
+ IS_CLANG=(
+ $(
+ ${cc}/bin/cc -v 2>&1 | grep -q clang && echo True || echo False
+ )
+ )
+
+ # Write CC_TOOLCHAIN_INFO
+ #
+ # Each line has the following shape:
+ # <key>:<value1>:<value2>:...
+ # or
+ # <key>
+ # I.e. each line is a colon-separated list of the key and the values.
+ mkdir -p $out
+ write_info() {
+ local -n flags=$1
+ local output=( "$1" "''${flags[@]}" )
+ IFS=:
+ echo "''${output[*]}" >>$out/CC_TOOLCHAIN_INFO
+ unset IFS
+ }
+ write_info TOOL_NAMES
+ write_info TOOL_PATHS
+ write_info CXX_BUILTIN_INCLUDE_DIRECTORIES
+ write_info COMPILER_FLAGS
+ write_info CXX_FLAGS
+ write_info LINK_FLAGS
+ write_info LINK_LIBS
+ write_info OPT_COMPILE_FLAGS
+ write_info OPT_LINK_FLAGS
+ write_info UNFILTERED_COMPILE_FLAGS
+ write_info DBG_COMPILE_FLAGS
+ write_info COVERAGE_COMPILE_FLAGS
+ write_info COVERAGE_LINK_FLAGS
+ write_info SUPPORTS_START_END_LIB
+ write_info IS_CLANG
+ ''
diff --git a/nixpkgs/toolchains/go.bzl b/nixpkgs/toolchains/go.bzl
index 79cbb99..d8c6366 100644
--- a/nixpkgs/toolchains/go.bzl
+++ b/nixpkgs/toolchains/go.bzl
@@ -2,20 +2,19 @@ load(
"@io_bazel_rules_go//go:deps.bzl",
"go_wrap_sdk",
)
-
load(
"//nixpkgs:nixpkgs.bzl",
- "nixpkgs_package"
+ "nixpkgs_package",
)
def nixpkgs_go_configure(
- sdk_name = "go_sdk",
- repository = None,
- repositories = {},
- nix_file = None,
- nix_file_deps = None,
- nix_file_content = None,
- nixopts = []):
+ sdk_name = "go_sdk",
+ repository = None,
+ repositories = {},
+ nix_file = None,
+ nix_file_deps = None,
+ nix_file_content = None,
+ nixopts = []):
"""
Use go toolchain from Nixpkgs. Will fail if not a nix-based platform.
@@ -41,7 +40,6 @@ def nixpkgs_go_configure(
}
"""
-
nixpkgs_package(
name = "nixpkgs_go_toolchain",
repository = repository,
diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel
index 16866a3..f3812c8 100644
--- a/tests/BUILD.bazel
+++ b/tests/BUILD.bazel
@@ -1,6 +1,7 @@
package(default_testonly = 1)
load("@io_bazel_rules_go//go:def.bzl", "go_binary")
+load("cc-test.bzl", "cc_toolchain_test")
[
# All of these tests use the "hello" binary to see
@@ -54,12 +55,45 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary")
),
]
-# Test nixpkgs_cc_configure() by building some CC code.
+# Test nixopts location expansion
+sh_test(
+ name = "location-expansion-test",
+ srcs = ["location_expansion.sh"],
+ args = [
+ "$(POSIX_DIFF)",
+ "$(rootpath //:nixpkgs.json)",
+ "$(rootpath //:nixpkgs.nix)",
+ "$(rootpath //tests:relative_imports/nixpkgs.nix)",
+ "$(rootpath //tests:location_expansion/escaped_string)",
+ "$(rootpath @nixpkgs_location_expansion_test//:out/nixpkgs.json)",
+ "$(rootpath @nixpkgs_location_expansion_test//:out/nixpkgs.nix)",
+ "$(rootpath @nixpkgs_location_expansion_test//:out/relative_imports.nix)",
+ "$(rootpath @nixpkgs_location_expansion_test//:out/escaped_string)",
+ ],
+ data = [
+ "//:nixpkgs.json",
+ "//:nixpkgs.nix",
+ "//tests:relative_imports/nixpkgs.nix",
+ "//tests:location_expansion/escaped_string",
+ "@nixpkgs_location_expansion_test//:out/nixpkgs.json",
+ "@nixpkgs_location_expansion_test//:out/nixpkgs.nix",
+ "@nixpkgs_location_expansion_test//:out/relative_imports.nix",
+ "@nixpkgs_location_expansion_test//:out/escaped_string",
+ ],
+ toolchains = ["@rules_sh//sh/posix:make_variables"],
+)
+
+# Test nixpkgs_cc_configure_hermetic() by building some CC code.
cc_binary(
name = "cc-test",
srcs = ["cc-test.cc"],
)
+# Test that nixpkgs_cc_configure_hermetic is selected.
+cc_toolchain_test(
+ name = "cc-toolchain",
+)
+
# Test nixpkgs_python_configure() by running some Python code.
test_suite(
name = "python-test",
@@ -86,7 +120,7 @@ sh_test(
# Test nixpkgs_go_configure()
go_binary(
name = "go-test",
- srcs = ["go-test.go"]
+ srcs = ["go-test.go"],
)
sh_test(
@@ -97,6 +131,6 @@ sh_test(
"//nixpkgs:srcs",
"//tests/invalid_nixpkgs_package:srcs",
"@busybox_static//:bin",
- "@nix-unstable//:bin"
+ "@nix-unstable//:bin",
],
)
diff --git a/tests/cc-test.bzl b/tests/cc-test.bzl
new file mode 100644
index 0000000..8494cc8
--- /dev/null
+++ b/tests/cc-test.bzl
@@ -0,0 +1,59 @@
+load("@bazel_skylib//lib:paths.bzl", "paths")
+load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
+
+def _cc_toolchain_test_impl(ctx):
+ cc = find_cpp_toolchain(ctx)
+ executable = ctx.actions.declare_file(ctx.attr.name + ".sh")
+ cc_toolchain_info = ctx.file._cc_toolchain_info
+ cc_toolchain_info_path = ctx.expand_location(
+ "$(rootpath {})".format(str(ctx.attr._cc_toolchain_info.label)),
+ [ctx.attr._cc_toolchain_info],
+ )
+ ctx.actions.write(executable, content = """\
+# Find cc in CC_TOOLCHAIN_INFO
+while IFS=: read -a line; do
+ if [[ ${{line[0]}} = TOOL_PATHS ]]; then
+ for item in ${{line[@]:1}}; do
+ if [[ $item = */bin/cc ]]; then
+ CC=$item
+ fi
+ done
+ fi
+done <{cc_toolchain_info_path}
+if [[ {cc} = */cc_wrapper.sh ]]; then
+ grep -q "$CC" "{cc}" || {{
+ echo "Expected C compiler '$CC' in wrapper script '{cc}'." >&2
+ exit 1
+ }}
+else
+ if [[ {cc} != $CC ]]; then
+ echo "Expected C compiler '$CC', but found '{cc}'." >&2
+ exit 1
+ fi
+fi
+""".format(
+ cc = cc.compiler_executable,
+ cc_toolchain_info_path = cc_toolchain_info_path,
+ ))
+ return [DefaultInfo(
+ executable = executable,
+ runfiles = ctx.runfiles(
+ files = [ctx.file._cc_toolchain_info],
+ transitive_files = cc.all_files,
+ ),
+ )]
+
+cc_toolchain_test = rule(
+ _cc_toolchain_test_impl,
+ attrs = {
+ "_cc_toolchain": attr.label(
+ default = Label("@rules_cc//cc:current_cc_toolchain"),
+ ),
+ "_cc_toolchain_info": attr.label(
+ allow_single_file = True,
+ default = Label("@nixpkgs_config_cc_info//:CC_TOOLCHAIN_INFO"),
+ ),
+ },
+ test = True,
+ toolchains = ["@rules_cc//cc:toolchain_type"],
+)
diff --git a/tests/invalid_nixpkgs_package/BUILD.bazel b/tests/invalid_nixpkgs_package/BUILD.bazel
index 00cbfea..70ff958 100644
--- a/tests/invalid_nixpkgs_package/BUILD.bazel
+++ b/tests/invalid_nixpkgs_package/BUILD.bazel
@@ -1 +1,5 @@
-filegroup(name = "srcs", srcs = glob(["**"]), visibility = ["//visibility:public"])
+filegroup(
+ name = "srcs",
+ srcs = glob(["**"]),
+ visibility = ["//visibility:public"],
+)
diff --git a/tests/invalid_nixpkgs_package/workspace.bazel b/tests/invalid_nixpkgs_package/workspace.bazel
index c434649..2113a77 100644
--- a/tests/invalid_nixpkgs_package/workspace.bazel
+++ b/tests/invalid_nixpkgs_package/workspace.bazel
@@ -1,5 +1,9 @@
workspace(name = "io_tweag_rules_nixpkgs")
+load("//nixpkgs:repositories.bzl", "rules_nixpkgs_dependencies")
+
+rules_nixpkgs_dependencies()
+
load(
"//nixpkgs:nixpkgs.bzl",
"nixpkgs_local_repository",
diff --git a/tests/location_expansion.nix b/tests/location_expansion.nix
new file mode 100644
index 0000000..f5715c6
--- /dev/null
+++ b/tests/location_expansion.nix
@@ -0,0 +1,18 @@
+with import <nixpkgs> { config = {}; overlays = []; };
+
+{ attrs, relative_imports, escaped_string }:
+let
+ inherit (attrs) nixpkgs_json nixpkgs_nix;
+in
+ runCommand "location-expansion"
+ {
+ preferLocalBuild = true;
+ allowSubstitutes = false;
+ }
+ ''
+ mkdir -p $out/out
+ cp ${nixpkgs_json} $out/out/nixpkgs.json
+ cp ${nixpkgs_nix} $out/out/nixpkgs.nix
+ cp ${relative_imports} $out/out/relative_imports.nix
+ echo '${escaped_string}' >$out/out/escaped_string
+ ''
diff --git a/tests/location_expansion.sh b/tests/location_expansion.sh
new file mode 100755
index 0000000..ff62292
--- /dev/null
+++ b/tests/location_expansion.sh
@@ -0,0 +1,6 @@
+DIFF="$1"
+
+diff "$2" "$6"
+diff "$3" "$7"
+diff "$4" "$8"
+diff "$5" "$9"
diff --git a/tests/location_expansion/escaped_string b/tests/location_expansion/escaped_string
new file mode 100644
index 0000000..20c7f06
--- /dev/null
+++ b/tests/location_expansion/escaped_string
@@ -0,0 +1 @@
+$ $(location //:does-not-exist) )(