From 8cb9314d0802de83a1b28d9907250dbbaa73d55c Mon Sep 17 00:00:00 2001 From: Philip Patsch Date: Wed, 29 Aug 2018 23:07:34 +0200 Subject: [PATCH 1/5] Remove nix-build out-link from the output directory. `nix-build` by default creates a symlink to the store path in the current directory. --- nixpkgs/nixpkgs.bzl | 1 + 1 file changed, 1 insertion(+) diff --git a/nixpkgs/nixpkgs.bzl b/nixpkgs/nixpkgs.bzl index 46ce80779..77871cf8e 100644 --- a/nixpkgs/nixpkgs.bzl +++ b/nixpkgs/nixpkgs.bzl @@ -57,6 +57,7 @@ def _nixpkgs_package_impl(ctx): "-A", ctx.attr.attribute_path if ctx.attr.nix_file or ctx.attr.nix_file_content else ctx.attr.attribute_path or ctx.attr.name, + "--no-out-link", ]) # If neither repository or path are set, leave empty which will use From c1acbbde081f6a433cdd1e1d7fac31ff99e35581 Mon Sep 17 00:00:00 2001 From: Philip Patsch Date: Wed, 29 Aug 2018 23:15:46 +0200 Subject: [PATCH 2/5] Factor out discovery of executables. --- nixpkgs/nixpkgs.bzl | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/nixpkgs/nixpkgs.bzl b/nixpkgs/nixpkgs.bzl index 77871cf8e..bd9227aa7 100644 --- a/nixpkgs/nixpkgs.bzl +++ b/nixpkgs/nixpkgs.bzl @@ -76,10 +76,10 @@ def _nixpkgs_package_impl(ctx): elif not (ctx.attr.nix_file or ctx.attr.nix_file_content): fail(strFailureImplicitNixpkgs) - nix_build_path = ctx.which("nix-build") - if nix_build_path == None: - fail("Could not find nix-build on the path. Please install it. See: https://nixos.org/nix/") - + nix_build_path = _executable_path( + "nix-build", ctx, + extra_msg = "See: https://nixos.org/nix/" + ) nix_build = [nix_build_path] + expr_args # Large enough integer that Bazel can still parse. We don't have @@ -99,11 +99,9 @@ def _nixpkgs_package_impl(ctx): # Build a forest of symlinks (like new_local_package() does) to the # Nix store. - find_path = ctx.which("find") - if find_path == None: - fail("Could not find the 'find' command. Please ensure it is in your PATH.") + find_path = _executable_path("find", ctx) - res = ctx.execute(["find", output_path, "-maxdepth", "1"]) + res = ctx.execute([find_path, output_path, "-maxdepth", "1"]) if res.return_code == 0: for i in res.stdout.splitlines(): basename = i.rpartition("/")[-1] @@ -125,3 +123,12 @@ nixpkgs_package = repository_rule( }, local = True, ) + + +def _executable_path(exe_name, rep_ctx, extra_msg=""): + """Try to find the executable, fail with an error.""" + path = rep_ctx.which(exe_name) + if path == None: + fail("Could not find the `{}` executable in PATH.{}\n" + .format(exe_name, " " + extra_msg if extra_msg else "")) + return path From 967db99e545a59cb8685dcf2dc8d132679033b9b Mon Sep 17 00:00:00 2001 From: Philip Patsch Date: Wed, 29 Aug 2018 23:28:53 +0200 Subject: [PATCH 3/5] Unify error messages for failing execute() calls. We print all parts of `exec_result` for nicer debugging. --- nixpkgs/nixpkgs.bzl | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/nixpkgs/nixpkgs.bzl b/nixpkgs/nixpkgs.bzl index bd9227aa7..9736a1632 100644 --- a/nixpkgs/nixpkgs.bzl +++ b/nixpkgs/nixpkgs.bzl @@ -92,9 +92,8 @@ def _nixpkgs_package_impl(ctx): if res.return_code == 0: output_path = res.stdout.splitlines()[-1] else: - fail("Cannot build Nix attribute %s.\nstdout:\n%s\n\nstderr:\n%s\n" % - (ctx.attr.name, res.stdout, res.stderr) - ) + _execute_error(res, "Cannot build Nix attribute `{}`" + .format(ctx.attr.attribute_path)) # Build a forest of symlinks (like new_local_package() does) to the # Nix store. @@ -107,7 +106,7 @@ def _nixpkgs_package_impl(ctx): basename = i.rpartition("/")[-1] ctx.symlink(i, ctx.path(basename)) else: - fail(res.stderr) + _execute_error(res, "find failed on {}".format(output_path)) nixpkgs_package = repository_rule( implementation = _nixpkgs_package_impl, @@ -132,3 +131,19 @@ def _executable_path(exe_name, rep_ctx, extra_msg=""): fail("Could not find the `{}` executable in PATH.{}\n" .format(exe_name, " " + extra_msg if extra_msg else "")) return path + + +def _execute_error(exec_result, msg): + """Print a nice error message for a failed `execute`.""" + fail(""" +execute() error: {msg} +status code: {code} +stdout: +{stdout} +stderr: +{stderr} +""".format( + msg=msg, + code=exec_result.return_code, + stdout=exec_result.stdout, + stderr=exec_result.stderr)) From 1451c7adf7dabf0af488f99bbb2b23680241008f Mon Sep 17 00:00:00 2001 From: Philip Patsch Date: Wed, 29 Aug 2018 23:36:23 +0200 Subject: [PATCH 4/5] Factor out & improve symlinking store paths. Adds `-mindepth 1` to find, because otherwise the parent directory would always be symlinked as well. `-print0` is used to be stable in the face of filenames that could possibly contain the `\n` character. --- nixpkgs/nixpkgs.bzl | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/nixpkgs/nixpkgs.bzl b/nixpkgs/nixpkgs.bzl index 9736a1632..8208e3f35 100644 --- a/nixpkgs/nixpkgs.bzl +++ b/nixpkgs/nixpkgs.bzl @@ -97,16 +97,8 @@ def _nixpkgs_package_impl(ctx): # Build a forest of symlinks (like new_local_package() does) to the # Nix store. + _symlink_children(output_path, ctx) - find_path = _executable_path("find", ctx) - - res = ctx.execute([find_path, output_path, "-maxdepth", "1"]) - if res.return_code == 0: - for i in res.stdout.splitlines(): - basename = i.rpartition("/")[-1] - ctx.symlink(i, ctx.path(basename)) - else: - _execute_error(res, "find failed on {}".format(output_path)) nixpkgs_package = repository_rule( implementation = _nixpkgs_package_impl, @@ -124,6 +116,27 @@ nixpkgs_package = repository_rule( ) +def _symlink_children(target_dir, rep_ctx): + """Create a symlink to all children of `target_dir` in the current + build directory.""" + find_args = [ + _executable_path("find", rep_ctx), + target_dir, + "-maxdepth", "1", + # otherwise the directory is printed as well + "-mindepth", "1", + # filenames can contain \n + "-print0", + ] + find_res = rep_ctx.execute(find_args) + if find_res.return_code == 0: + for target in find_res.stdout.rstrip("\0").split("\0"): + basename = target.rpartition("/")[-1] + rep_ctx.symlink(target, basename) + else: + _execute_error(find_res) + + def _executable_path(exe_name, rep_ctx, extra_msg=""): """Try to find the executable, fail with an error.""" path = rep_ctx.which(exe_name) From b8a32b60df0557bc0ba1dfd136e61c7ea1b2974a Mon Sep 17 00:00:00 2001 From: Philip Patsch Date: Fri, 31 Aug 2018 12:37:43 +0200 Subject: [PATCH 5/5] Create an out link to prevent garbage collection. It will create a garbage collection root as long as the symlink created by `--out-link` exists. --- nixpkgs/nixpkgs.bzl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/nixpkgs/nixpkgs.bzl b/nixpkgs/nixpkgs.bzl index 8208e3f35..1814beede 100644 --- a/nixpkgs/nixpkgs.bzl +++ b/nixpkgs/nixpkgs.bzl @@ -57,7 +57,13 @@ def _nixpkgs_package_impl(ctx): "-A", ctx.attr.attribute_path if ctx.attr.nix_file or ctx.attr.nix_file_content else ctx.attr.attribute_path or ctx.attr.name, - "--no-out-link", + # Creating an out link prevents nix from garbage collecting the store path. + # nixpkgs uses `nix-support/` for such house-keeping files, so we mirror them + # and use `bazel-support/`, under the assumption that no nix package has + # a file named `bazel-support` in its root. + # A `bazel clean` deletes the symlink and thus nix is free to garbage collect + # the store path. + "--out-link", "bazel-support/nix-out-link" ]) # If neither repository or path are set, leave empty which will use