Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define Unix shell toolchain #1117

Merged
merged 5 commits into from
Oct 15, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions haskell/cabal.bzl
Original file line number Diff line number Diff line change
@@ -88,12 +88,12 @@ def _cabal_tool_flag(tool):
def _make_path(hs, binaries):
return ":".join([binary.dirname for binary in binaries.to_list()] + ["$PATH"])

def _prepare_cabal_inputs(hs, cc, dep_info, cc_info, component, package_id, tool_inputs, tool_input_manifests, cabal, setup, srcs, flags, cabal_wrapper_tpl, package_database):
def _prepare_cabal_inputs(hs, cc, unix, dep_info, cc_info, component, package_id, tool_inputs, tool_input_manifests, cabal, setup, srcs, flags, cabal_wrapper_tpl, package_database):
"""Compute Cabal wrapper, arguments, inputs."""
with_profiling = is_profiling_enabled(hs)

(ghci_extra_libs, env) = get_ghci_extra_libs(hs, cc_info)
env["PATH"] = _make_path(hs, tool_inputs)
env["PATH"] = _make_path(hs, tool_inputs) + ":" + ":".join(unix.paths)
if hs.toolchain.is_darwin:
env["SDKROOT"] = "macosx" # See haskell/private/actions/link.bzl

@@ -109,6 +109,7 @@ def _prepare_cabal_inputs(hs, cc, dep_info, cc_info, component, package_id, tool
"%{ghc_pkg}": hs.tools.ghc_pkg.path,
"%{runghc}": hs.tools.runghc.path,
"%{ar}": cc.tools.ar,
"%{cc}": cc.tools.cc,
"%{strip}": cc.tools.strip,
# XXX Workaround
# https://github.com/bazelbuild/bazel/issues/5980.
@@ -176,6 +177,7 @@ def _haskell_cabal_library_impl(ctx):
cc_info = cc_common.merge_cc_infos(
cc_infos = [dep[CcInfo] for dep in ctx.attr.deps if CcInfo in dep],
)
unix = ctx.toolchains["@rules_haskell//haskell/private/unix:toolchain_type"]
package_id = "{}-{}".format(
ctx.attr.package_name if ctx.attr.package_name else hs.label.name,
ctx.attr.version,
@@ -218,6 +220,7 @@ def _haskell_cabal_library_impl(ctx):
c = _prepare_cabal_inputs(
hs,
cc,
unix,
dep_info,
cc_info,
component = "lib:{}".format(
@@ -334,7 +337,10 @@ haskell_cabal_library = rule(
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
),
},
toolchains = ["@rules_haskell//haskell:toolchain"],
toolchains = [
"@rules_haskell//haskell:toolchain",
"@rules_haskell//haskell/private/unix:toolchain_type",
],
fragments = ["cpp"],
)
"""Use Cabal to build a library.
@@ -373,6 +379,7 @@ def _haskell_cabal_binary_impl(ctx):
cc_info = cc_common.merge_cc_infos(
cc_infos = [dep[CcInfo] for dep in ctx.attr.deps if CcInfo in dep],
)
unix = ctx.toolchains["@rules_haskell//haskell/private/unix:toolchain_type"]

cabal = _find_cabal(hs, ctx.files.srcs)
setup = _find_setup(hs, cabal, ctx.files.srcs)
@@ -395,6 +402,7 @@ def _haskell_cabal_binary_impl(ctx):
c = _prepare_cabal_inputs(
hs,
cc,
unix,
dep_info,
cc_info,
component = "exe:{}".format(hs.label.name),
@@ -468,7 +476,10 @@ haskell_cabal_binary = rule(
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
),
},
toolchains = ["@rules_haskell//haskell:toolchain"],
toolchains = [
"@rules_haskell//haskell:toolchain",
"@rules_haskell//haskell/private/unix:toolchain_type",
],
fragments = ["cpp"],
)
"""Use Cabal to build a binary.
7 changes: 7 additions & 0 deletions haskell/ghc_bindist.bzl
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
"""Workspace rules (GHC binary distributions)"""

load("@bazel_tools//tools/build_defs/repo:utils.bzl", "patch")
load(
"@rules_haskell//haskell/private/unix:unix_configure.bzl",
"unix_configure",
)

_GHC_DEFAULT_VERSION = "8.6.5"

@@ -448,6 +452,9 @@ def haskell_register_ghc_bindists(
haddock_flags = haddock_flags,
repl_ghci_args = repl_ghci_args,
)
local_unix_repo_name = "rules_haskell_unix_local"
if local_unix_repo_name not in native.existing_rules():
unix_configure(name = local_unix_repo_name)

def _find_python(repository_ctx):
python = repository_ctx.which("python3")
21 changes: 21 additions & 0 deletions haskell/nixpkgs.bzl
Original file line number Diff line number Diff line change
@@ -4,6 +4,10 @@ load(
"@io_tweag_rules_nixpkgs//nixpkgs:nixpkgs.bzl",
"nixpkgs_package",
)
load(
"@rules_haskell//haskell/private/unix:unix_nixpkgs.bzl",
"unix_nixpkgs",
)

def _ghc_nixpkgs_haskell_toolchain_impl(repository_ctx):
compiler_flags_select = "select({})".format(
@@ -167,6 +171,7 @@ def haskell_register_ghc_nixpkgs(
repl_ghci_args = None,
locale_archive = None,
attribute_path = "haskellPackages.ghc",
unix_attributes = None,
nix_file = None,
nix_file_deps = [],
nixopts = None,
@@ -187,6 +192,7 @@ def haskell_register_ghc_nixpkgs(
Args:
compiler_flags_select: temporary workaround to pass conditional arguments.
See https://github.com/bazelbuild/bazel/issues/9199 for details.
unix_attributes: List of attribute paths to extract standard Unix shell tools from.


Example:
@@ -208,6 +214,7 @@ def haskell_register_ghc_nixpkgs(

"""
nixpkgs_ghc_repo_name = "rules_haskell_ghc_nixpkgs"
nixpkgs_unix_repo_name = "rules_haskell_unix_nixpkgs"
haskell_toolchain_repo_name = "rules_haskell_ghc_nixpkgs_haskell_toolchain"
toolchain_repo_name = "rules_haskell_ghc_nixpkgs_toolchain"

@@ -242,6 +249,20 @@ def haskell_register_ghc_nixpkgs(
_ghc_nixpkgs_toolchain(name = toolchain_repo_name)
native.register_toolchains("@{}//:toolchain".format(toolchain_repo_name))

# Unix tools toolchain required for Cabal packages
unix_nixpkgs_kwargs = dict(
nix_file_deps = nix_file_deps,
nixopts = nixopts,
repositories = repositories,
repository = repository,
)
if unix_attributes != None:
unix_nixpkgs_kwargs["packages"] = unix_attributes
unix_nixpkgs(
name = nixpkgs_unix_repo_name,
**unix_nixpkgs_kwargs
)

def _find_children(repository_ctx, target_dir):
find_args = [
"find",
11 changes: 11 additions & 0 deletions haskell/private/cabal_wrapper.sh.tpl
Original file line number Diff line number Diff line change
@@ -84,6 +84,7 @@ done
shift 1

ar=$(realpath %{ar})
cc=$(realpath %{cc})
strip=$(realpath %{strip})
distdir=$(mktemp -d)
libdir="$pkgroot/${name}_iface"
@@ -99,6 +100,15 @@ trap cleanup EXIT

%{ghc_pkg} recache --package-db=$package_database

WITH_GCC=
if [[ %{is_windows} != True ]]; then
# Use the cc-wrapper for Cabal builds.
# Note, we cannot currently use it on Windows because the solution to the
# following issue is not released, yet.
# https://github.com/bazelbuild/bazel/issues/9390
WITH_GCC="--with-gcc=$cc"
fi

ENABLE_RELOCATABLE=
if [[ %{is_windows} != True ]]; then
ENABLE_RELOCATABLE=--enable-relocatable
@@ -119,6 +129,7 @@ $execroot/%{runghc} $setup configure \
--with-compiler=$execroot/%{ghc} \
--with-hc-pkg=$execroot/%{ghc_pkg} \
--with-ar=$ar \
$WITH_GCC \
--with-strip=$strip \
--enable-deterministic \
$ENABLE_RELOCATABLE \
4 changes: 4 additions & 0 deletions haskell/private/unix/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
toolchain_type(
name = "toolchain_type",
visibility = ["//visibility:public"],
)
165 changes: 165 additions & 0 deletions haskell/private/unix/unix_commands.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# List of Unix commands as specified by IEEE Std 1003.1-2008.
# Extracted from https://en.wikipedia.org/wiki/List_of_Unix_commands.
unix_commands = [
"admin",
"alias",
"ar",
"asa",
"at",
"awk",
"basename",
"batch",
"bc",
"bg",
"cc",
"c99",
"cal",
"cat",
"cd",
"cflow",
"chgrp",
"chmod",
"chown",
"cksum",
"cmp",
"comm",
"command",
"compress",
"cp",
"crontab",
"csplit",
"ctags",
"cut",
"cxref",
"date",
"dd",
"delta",
"df",
"diff",
"dirname",
"du",
"echo",
"ed",
"env",
"ex",
"expand",
"expr",
"false",
"fc",
"fg",
"file",
"find",
"fold",
"fort77",
"fuser",
"gencat",
"get",
"getconf",
"getopts",
"grep",
"hash",
"head",
"iconv",
"id",
"ipcrm",
"ipcs",
"jobs",
"join",
"kill",
"lex",
"link",
"ln",
"locale",
"localedef",
"logger",
"logname",
"lp",
"ls",
"m4",
"mailx",
"make",
"man",
"mesg",
"mkdir",
"mkfifo",
"more",
"mv",
"newgrp",
"nice",
"nl",
"nm",
"nohup",
"od",
"paste",
"patch",
"pathchk",
"pax",
"pr",
"printf",
"prs",
"ps",
"pwd",
"qalter",
"qdel",
"qhold",
"qmove",
"qmsg",
"qrerun",
"qrls",
"qselect",
"qsig",
"qstat",
"qsub",
"read",
"renice",
"rm",
"rmdel",
"rmdir",
"sact",
"sccs",
"sed",
"sh",
"sleep",
"sort",
"split",
"strings",
"strip",
"stty",
"tabs",
"tail",
"talk",
"tee",
"test",
"time",
"touch",
"tput",
"tr",
"true",
"tsort",
"tty",
"type",
"ulimit",
"umask",
"unalias",
"uname",
"uncompress",
"unexpand",
"unget",
"uniq",
"unlink",
"uucp",
"uudecode",
"uuencode",
"uustat",
"uux",
"val",
"vi",
"wait",
"wc",
"what",
"who",
"write",
"xargs",
"yacc",
"zcat",
]
62 changes: 62 additions & 0 deletions haskell/private/unix/unix_configure.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
load("@bazel_tools//tools/cpp:lib_cc_configure.bzl", "get_cpu_value")
load(
"@rules_haskell//haskell/private/unix:unix_commands.bzl",
"unix_commands",
)

def _unix_config_impl(repository_ctx):
is_windows = repository_ctx.os.name.startswith("windows")
commands = {}
for cmd in unix_commands:
if is_windows:
cmd_path = repository_ctx.which(cmd + ".exe")
else:
cmd_path = repository_ctx.which(cmd)
commands[cmd] = cmd_path
cpu = get_cpu_value(repository_ctx)
repository_ctx.file("BUILD.bazel", executable = False, content = """
load(
"@rules_haskell//haskell/private/unix:unix_toolchain.bzl",
"unix_toolchain",
)
unix_toolchain(
name = "local_unix",
visibility = ["//visibility:public"],
{commands}
)
toolchain(
name = "local_unix_toolchain",
toolchain = ":local_unix",
toolchain_type = "@rules_haskell//haskell/private/unix:toolchain_type",
exec_compatible_with = [
"@bazel_tools//platforms:x86_64",
"@bazel_tools//platforms:{os}",
],
target_compatible_with = [
"@bazel_tools//platforms:x86_64",
"@bazel_tools//platforms:{os}",
],
)
""".format(
commands = ",\n ".join([
'{cmd} = "{path}"'.format(cmd = cmd, path = cmd_path)
for (cmd, cmd_path) in commands.items()
if cmd_path
]),
os = "osx" if cpu == "darwin" else "linux",
))

unix_config = repository_rule(
environ = ["PATH"],
local = True,
implementation = _unix_config_impl,
)

def unix_configure(name = "local_unix_config"):
"""Autodetect local Unix tools.
Scans the environment (`$PATH`) for standard shell tools, generates a
corresponding unix toolchain and registers the toolchain.
"""
unix_config(name = name)
native.register_toolchains("@{}//:local_unix_toolchain".format(name))
121 changes: 121 additions & 0 deletions haskell/private/unix/unix_nixpkgs.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
load("@bazel_tools//tools/cpp:lib_cc_configure.bzl", "get_cpu_value")
load(
"@io_tweag_rules_nixpkgs//nixpkgs:nixpkgs.bzl",
"nixpkgs_package",
)

def _unix_nixpkgs(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 = "unix-toolchain";
paths = flatten [ {} ];
}};
cmd_glob = "${{env}}/bin/*";
os = if stdenv.isDarwin then "osx" else "linux";
in
runCommand "bazel-unix-toolchain"
{{ executable = false;
# Pointless to do this on a remote machine.
preferLocalBuild = true;
allowSubstitutes = false;
}}
''
n=$out/unix_nixpkgs.bzl
mkdir -p "$(dirname "$n")"
cat >>$n <<EOF
load(
"@rules_haskell//haskell/private/unix:unix_commands.bzl",
"unix_commands",
)
load(
"@rules_haskell//haskell/private/unix:unix_toolchain.bzl",
"unix_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_unix_toolchain():
unix_toolchain(
name = "nixpkgs_unix",
**{{
cmd: discovered[cmd]
for cmd in unix_commands
if cmd in discovered
}}
)
EOF
''
""".format(" ".join(packages)),
build_file_content = """
load("//:unix_nixpkgs.bzl", "create_unix_toolchain")
create_unix_toolchain()
""",
**kwargs
)

def _unix_nixpkgs_toolchain_impl(repository_ctx):
cpu = get_cpu_value(repository_ctx)
repository_ctx.file("BUILD", executable = False, content = """
toolchain(
name = "nixpkgs_unix_toolchain",
toolchain = "@{workspace}//:nixpkgs_unix",
toolchain_type = "@rules_haskell//haskell/private/unix:toolchain_type",
exec_compatible_with = [
"@bazel_tools//platforms:x86_64",
"@bazel_tools//platforms:{os}",
"@rules_haskell//haskell/platforms:nixpkgs",
],
target_compatible_with = [
"@bazel_tools//platforms:x86_64",
"@bazel_tools//platforms:{os}",
],
)
""".format(
workspace = repository_ctx.attr.workspace,
os = "osx" if cpu == "darwin" else "linux",
))

_unix_nixpkgs_toolchain = repository_rule(
_unix_nixpkgs_toolchain_impl,
attrs = {
"workspace": attr.string(),
},
)

def unix_nixpkgs(name, packages = ["stdenv.initialPath"], **kwargs):
"""Create a unix toolchain from nixpkgs.
Loads the given Nix packages, scans them for standard Unix tools, and
generates a corresponding `unix_toolchain`.
"""
_unix_nixpkgs(
name = name,
packages = packages,
**kwargs
)

# The indirection is required to avoid errors when `nix-build` is not in `PATH`.
_unix_nixpkgs_toolchain(
name = name + "_toolchain",
workspace = name,
)
native.register_toolchains(
"@{}//:nixpkgs_unix_toolchain".format(name + "_toolchain"),
)
49 changes: 49 additions & 0 deletions haskell/private/unix/unix_toolchain.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
load("@bazel_skylib//lib:paths.bzl", "paths")
load(
"@rules_haskell//haskell/private/unix:unix_commands.bzl",
"unix_commands",
)

UnixCommandsInfo = provider(
doc = "Collection of Unix commands",
fields = {
cmd: "Absolute path to the {} command or None.".format(cmd)
for cmd in unix_commands
},
)

def _unix_toolchain_impl(ctx):
commands = {}
for cmd in unix_commands:
cmd_path = getattr(ctx.attr, cmd, None)
if not cmd_path:
cmd_path = None
commands[cmd] = cmd_path
cmd_paths = {
paths.dirname(cmd_path): None
for cmd_path in commands.values()
if cmd_path
}.keys()
return [platform_common.ToolchainInfo(
commands = commands,
paths = cmd_paths,
)]

unix_toolchain = rule(
attrs = {
cmd: attr.string(
doc = "Absolute path to the {} command.".format(cmd),
mandatory = False,
)
for cmd in unix_commands
},
implementation = _unix_toolchain_impl,
)
"""A toolchain capturing standard Unix shell tools.
These tools are required by the Cabal build system and ./configure style Cabal
packages.
Use `unix_nixpkgs_toolchain` or `unix_configure` to create an instance of this
toolchain.
"""