Skip to content

Commit

Permalink
extract POSIX toolchain into module
Browse files Browse the repository at this point in the history
  • Loading branch information
fricklerhandwerk committed Mar 10, 2022
1 parent 6f5bea1 commit b680096
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 152 deletions.
1 change: 1 addition & 0 deletions nixpkgs/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ bzl_library(
"@rules_nixpkgs_cc//:cc.bzl",
"@rules_nixpkgs_java//:java.bzl",
"@rules_nixpkgs_python//:python.bzl",
"@rules_nixpkgs_posix//:posix.bzl",
":bazel_tools",
"@bazel_skylib//lib:new_sets",
"@bazel_skylib//lib:paths",
Expand Down
157 changes: 5 additions & 152 deletions nixpkgs/nixpkgs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ load(
"@rules_nixpkgs_cc//:cc.bzl",
_nixpkgs_cc_configure = "nixpkgs_cc_configure",
)
load(
"@rules_nixpkgs_posix//:posix.bzl",
_nixpkgs_sh_posix_configure = "nixpkgs_sh_posix_configure",
)

# aliases for backwards compatibility prior to `bzlmod`
nixpkgs_git_repository = _nixpkgs_git_repository
Expand All @@ -37,6 +41,7 @@ nixpkgs_package = _nixpkgs_package
nixpkgs_python_configure = _nixpkgs_python_configure
nixpkgs_java_configure = _nixpkgs_java_configure
nixpkgs_cc_configure = _nixpkgs_cc_configure
nixpkgs_sh_posix_configure = _nixpkgs_sh_posix_configure

def nixpkgs_cc_autoconf_impl(repository_ctx):
cpu_value = get_cpu_value(repository_ctx)
Expand Down Expand Up @@ -178,155 +183,3 @@ def nixpkgs_cc_configure_deprecated(
nixpkgs_cc_autoconf(name = "local_config_cc")
native.bind(name = "cc_toolchain", actual = "@local_config_cc//:toolchain")
native.register_toolchains("@local_config_cc//:all")

def nixpkgs_sh_posix_config(name, packages, **kwargs):
nixpkgs_package(
name = name,
nix_file_content = """
with import <nixpkgs> {{ config = {{}}; overlays = []; }};
let
# `packages` might include lists, e.g. `stdenv.initialPath` is a list itself,
# so we need to flatten `packages`.
flatten = builtins.concatMap (x: if builtins.isList x then x else [x]);
env = buildEnv {{
name = "posix-toolchain";
paths = flatten [ {} ];
}};
cmd_glob = "${{env}}/bin/*";
os = if stdenv.isDarwin then "osx" else "linux";
in
runCommand "bazel-nixpkgs-posix-toolchain"
{{ executable = false;
# Pointless to do this on a remote machine.
preferLocalBuild = true;
allowSubstitutes = false;
}}
''
n=$out/nixpkgs_sh_posix.bzl
mkdir -p "$(dirname "$n")"
cat >>$n <<EOF
load("@rules_sh//sh:posix.bzl", "posix", "sh_posix_toolchain")
discovered = {{
EOF
for cmd in ${{cmd_glob}}; do
if [[ -x $cmd ]]; then
echo " '$(basename $cmd)': '$cmd'," >>$n
fi
done
cat >>$n <<EOF
}}
def create_posix_toolchain():
sh_posix_toolchain(
name = "nixpkgs_sh_posix",
cmds = {{
cmd: discovered[cmd]
for cmd in posix.commands
if cmd in discovered
}}
)
EOF
''
""".format(" ".join(packages)),
build_file_content = """
load("//:nixpkgs_sh_posix.bzl", "create_posix_toolchain")
create_posix_toolchain()
""",
**kwargs
)

# Note [Target constraints for POSIX tools]
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# There at least three cases for POSIX tools.
#
# Case 1) The tools are used at build time in the execution platform.
#
# Case 2) The tools are used at runtime time in the target platform
# when the target platform is the same as the execution
# platform.
#
# Case 3) The tools are used at runtime time in the target platform
# when cross-compiling.
#
# At the moment, only (1) and (2) are supported by ignoring any target
# constraints when defining the toolchain. This makes available
# any tools that don't depend on the target platform like grep, find
# or sort. In case (2), the tools are still usable at runtime since
# the platforms match.
#
# POSIX tools that depend on the target platform, like cc and strip,
# are better taken from the Bazel cc toolchain instead, so they do
# match the target platform.
#
# TODO: In order to support (3), where the tools would be needed at
# runtime, nixpkgs_sh_posix_configure will need to be changed to take
# as parameter the constraints for the platform in which the tools
# should run.

def _nixpkgs_sh_posix_toolchain_impl(repository_ctx):
cpu = get_cpu_value(repository_ctx)
repository_ctx.file("BUILD", executable = False, content = """
toolchain(
name = "nixpkgs_sh_posix_toolchain",
toolchain = "@{workspace}//:nixpkgs_sh_posix",
toolchain_type = "@rules_sh//sh/posix:toolchain_type",
exec_compatible_with = [
"@platforms//cpu:x86_64",
"@platforms//os:{os}",
"@io_tweag_rules_nixpkgs//nixpkgs/constraints:support_nix",
],
# Leaving the target constraints empty matter for cross-compilation.
# See Note [Target constraints for POSIX tools]
target_compatible_with = [],
)
""".format(
workspace = repository_ctx.attr.workspace,
os = {"darwin": "osx"}.get(cpu, "linux"),
))

_nixpkgs_sh_posix_toolchain = repository_rule(
_nixpkgs_sh_posix_toolchain_impl,
attrs = {
"workspace": attr.string(),
},
)

def nixpkgs_sh_posix_configure(
name = "nixpkgs_sh_posix_config",
packages = ["stdenv.initialPath"],
**kwargs):
"""Create a POSIX toolchain from nixpkgs.
Loads the given Nix packages, scans them for standard Unix tools, and
generates a corresponding `sh_posix_toolchain`.
Make sure to call `nixpkgs_sh_posix_configure` before `sh_posix_configure`,
if you use both. Otherwise, the local toolchain will always be chosen in
favor of the nixpkgs one.
Args:
name: Name prefix for the generated repositories.
packages: List of Nix attribute paths to draw Unix tools from.
nix_file_deps: See nixpkgs_package.
repositories: See nixpkgs_package.
repository: See nixpkgs_package.
nixopts: See nixpkgs_package.
fail_not_supported: See nixpkgs_package.
"""
nixpkgs_sh_posix_config(
name = name,
packages = packages,
**kwargs
)

# The indirection is required to avoid errors when `nix-build` is not in `PATH`.
_nixpkgs_sh_posix_toolchain(
name = name + "_toolchain",
workspace = name,
)
native.register_toolchains(
"@{}//:nixpkgs_sh_posix_toolchain".format(name + "_toolchain"),
)
1 change: 1 addition & 0 deletions nixpkgs/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def rules_nixpkgs_dependencies(local = None):
("rules_nixpkgs_cc", "toolchains/cc"),
("rules_nixpkgs_java", "toolchains/java"),
("rules_nixpkgs_python", "toolchains/python"),
("rules_nixpkgs_posix", "toolchains/posix"),
]:
if not local:
# XXX: no way to use `sha256` here, but if this surrounding repo comes
Expand Down
1 change: 1 addition & 0 deletions tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ sh_test(
"@rules_nixpkgs_cc//:srcs",
"@rules_nixpkgs_java//:srcs",
"@rules_nixpkgs_python//:srcs",
"@rules_nixpkgs_posix//:srcs",
"@nix-unstable//:bin",
] + select({
"@platforms//os:linux": ["@busybox_static//:bin"],
Expand Down
1 change: 1 addition & 0 deletions tests/invalid_nixpkgs_package/workspace.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ load("//nixpkgs:repositories.bzl", "rules_nixpkgs_dependencies")
"rules_nixpkgs_cc",
"rules_nixpkgs_java",
"rules_nixpkgs_python",
"rules_nixpkgs_posix",
]]

rules_nixpkgs_dependencies()
Expand Down
11 changes: 11 additions & 0 deletions toolchains/posix/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package(default_visibility = ["//visibility:public"])

exports_files([
"posix.bzl",
])

filegroup(
name = "srcs",
srcs = glob(["**"]),
visibility = ["//visibility:public"],
)
7 changes: 7 additions & 0 deletions toolchains/posix/MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module(
name = "rules_nixpkgs_posix",
version = "0.8.1",
)

bazel_dep(name = "rules_nixpkgs_core", version = "0.8.1")
bazel_dep(name = "rules_sh", version = "0.2.0")
2 changes: 2 additions & 0 deletions toolchains/posix/WORKSPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# only temporary for compatibility with `WORKSPACE` setup
# TODO: remove when migration to `bzlmod` is complete
157 changes: 157 additions & 0 deletions toolchains/posix/posix.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
load(
"@bazel_tools//tools/cpp:lib_cc_configure.bzl",
"get_cpu_value",
)
load("@rules_nixpkgs_core//:nixpkgs.bzl", "nixpkgs_package")

def nixpkgs_sh_posix_config(name, packages, **kwargs):
nixpkgs_package(
name = name,
nix_file_content = """
with import <nixpkgs> {{ config = {{}}; overlays = []; }};
let
# `packages` might include lists, e.g. `stdenv.initialPath` is a list itself,
# so we need to flatten `packages`.
flatten = builtins.concatMap (x: if builtins.isList x then x else [x]);
env = buildEnv {{
name = "posix-toolchain";
paths = flatten [ {} ];
}};
cmd_glob = "${{env}}/bin/*";
os = if stdenv.isDarwin then "osx" else "linux";
in
runCommand "bazel-nixpkgs-posix-toolchain"
{{ executable = false;
# Pointless to do this on a remote machine.
preferLocalBuild = true;
allowSubstitutes = false;
}}
''
n=$out/nixpkgs_sh_posix.bzl
mkdir -p "$(dirname "$n")"
cat >>$n <<EOF
load("@rules_sh//sh:posix.bzl", "posix", "sh_posix_toolchain")
discovered = {{
EOF
for cmd in ${{cmd_glob}}; do
if [[ -x $cmd ]]; then
echo " '$(basename $cmd)': '$cmd'," >>$n
fi
done
cat >>$n <<EOF
}}
def create_posix_toolchain():
sh_posix_toolchain(
name = "nixpkgs_sh_posix",
cmds = {{
cmd: discovered[cmd]
for cmd in posix.commands
if cmd in discovered
}}
)
EOF
''
""".format(" ".join(packages)),
build_file_content = """
load("//:nixpkgs_sh_posix.bzl", "create_posix_toolchain")
create_posix_toolchain()
""",
**kwargs
)

# Note [Target constraints for POSIX tools]
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# There at least three cases for POSIX tools.
#
# Case 1) The tools are used at build time in the execution platform.
#
# Case 2) The tools are used at runtime time in the target platform
# when the target platform is the same as the execution
# platform.
#
# Case 3) The tools are used at runtime time in the target platform
# when cross-compiling.
#
# At the moment, only (1) and (2) are supported by ignoring any target
# constraints when defining the toolchain. This makes available
# any tools that don't depend on the target platform like grep, find
# or sort. In case (2), the tools are still usable at runtime since
# the platforms match.
#
# POSIX tools that depend on the target platform, like cc and strip,
# are better taken from the Bazel cc toolchain instead, so they do
# match the target platform.
#
# TODO: In order to support (3), where the tools would be needed at
# runtime, nixpkgs_sh_posix_configure will need to be changed to take
# as parameter the constraints for the platform in which the tools
# should run.

def _nixpkgs_sh_posix_toolchain_impl(repository_ctx):
cpu = get_cpu_value(repository_ctx)
repository_ctx.file("BUILD", executable = False, content = """
toolchain(
name = "nixpkgs_sh_posix_toolchain",
toolchain = "@{workspace}//:nixpkgs_sh_posix",
toolchain_type = "@rules_sh//sh/posix:toolchain_type",
exec_compatible_with = [
"@platforms//cpu:x86_64",
"@platforms//os:{os}",
"@rules_nixpkgs_core//constraints:support_nix",
],
# Leaving the target constraints empty matter for cross-compilation.
# See Note [Target constraints for POSIX tools]
target_compatible_with = [],
)
""".format(
workspace = repository_ctx.attr.workspace,
os = {"darwin": "osx"}.get(cpu, "linux"),
))

_nixpkgs_sh_posix_toolchain = repository_rule(
_nixpkgs_sh_posix_toolchain_impl,
attrs = {
"workspace": attr.string(),
},
)

def nixpkgs_sh_posix_configure(
name = "nixpkgs_sh_posix_config",
packages = ["stdenv.initialPath"],
**kwargs):
"""Create a POSIX toolchain from nixpkgs.
Loads the given Nix packages, scans them for standard Unix tools, and
generates a corresponding `sh_posix_toolchain`.
Make sure to call `nixpkgs_sh_posix_configure` before `sh_posix_configure`,
if you use both. Otherwise, the local toolchain will always be chosen in
favor of the nixpkgs one.
Args:
name: Name prefix for the generated repositories.
packages: List of Nix attribute paths to draw Unix tools from.
nix_file_deps: See nixpkgs_package.
repositories: See nixpkgs_package.
repository: See nixpkgs_package.
nixopts: See nixpkgs_package.
fail_not_supported: See nixpkgs_package.
"""
nixpkgs_sh_posix_config(
name = name,
packages = packages,
**kwargs
)

# The indirection is required to avoid errors when `nix-build` is not in `PATH`.
_nixpkgs_sh_posix_toolchain(
name = name + "_toolchain",
workspace = name,
)
native.register_toolchains(
"@{}//:nixpkgs_sh_posix_toolchain".format(name + "_toolchain"),
)

0 comments on commit b680096

Please sign in to comment.