@@ -57,6 +57,13 @@ def _nixpkgs_package_impl(ctx):
57
57
"-A" , ctx .attr .attribute_path
58
58
if ctx .attr .nix_file or ctx .attr .nix_file_content
59
59
else ctx .attr .attribute_path or ctx .attr .name ,
60
+ # Creating an out link prevents nix from garbage collecting the store path.
61
+ # nixpkgs uses `nix-support/` for such house-keeping files, so we mirror them
62
+ # and use `bazel-support/`, under the assumption that no nix package has
63
+ # a file named `bazel-support` in its root.
64
+ # A `bazel clean` deletes the symlink and thus nix is free to garbage collect
65
+ # the store path.
66
+ "--out-link" , "bazel-support/nix-out-link"
60
67
])
61
68
62
69
# If neither repository or path are set, leave empty which will use
@@ -75,10 +82,10 @@ def _nixpkgs_package_impl(ctx):
75
82
elif not (ctx .attr .nix_file or ctx .attr .nix_file_content ):
76
83
fail (strFailureImplicitNixpkgs )
77
84
78
- nix_build_path = ctx . which ( "nix-build" )
79
- if nix_build_path == None :
80
- fail ( "Could not find nix-build on the path. Please install it. See: https://nixos.org/nix/")
81
-
85
+ nix_build_path = _executable_path (
86
+ "nix-build" , ctx ,
87
+ extra_msg = " See: https://nixos.org/nix/"
88
+ )
82
89
nix_build = [nix_build_path ] + expr_args
83
90
84
91
# Large enough integer that Bazel can still parse. We don't have
@@ -91,24 +98,13 @@ def _nixpkgs_package_impl(ctx):
91
98
if res .return_code == 0 :
92
99
output_path = res .stdout .splitlines ()[- 1 ]
93
100
else :
94
- fail ("Cannot build Nix attribute %s.\n stdout:\n %s\n \n stderr:\n %s\n " %
95
- (ctx .attr .name , res .stdout , res .stderr )
96
- )
101
+ _execute_error (res , "Cannot build Nix attribute `{}`"
102
+ .format (ctx .attr .attribute_path ))
97
103
98
104
# Build a forest of symlinks (like new_local_package() does) to the
99
105
# Nix store.
106
+ _symlink_children (output_path , ctx )
100
107
101
- find_path = ctx .which ("find" )
102
- if find_path == None :
103
- fail ("Could not find the 'find' command. Please ensure it is in your PATH." )
104
-
105
- res = ctx .execute (["find" , output_path , "-maxdepth" , "1" ])
106
- if res .return_code == 0 :
107
- for i in res .stdout .splitlines ():
108
- basename = i .rpartition ("/" )[- 1 ]
109
- ctx .symlink (i , ctx .path (basename ))
110
- else :
111
- fail (res .stderr )
112
108
113
109
nixpkgs_package = repository_rule (
114
110
implementation = _nixpkgs_package_impl ,
@@ -124,3 +120,49 @@ nixpkgs_package = repository_rule(
124
120
},
125
121
local = True ,
126
122
)
123
+
124
+
125
+ def _symlink_children (target_dir , rep_ctx ):
126
+ """Create a symlink to all children of `target_dir` in the current
127
+ build directory."""
128
+ find_args = [
129
+ _executable_path ("find" , rep_ctx ),
130
+ target_dir ,
131
+ "-maxdepth" , "1" ,
132
+ # otherwise the directory is printed as well
133
+ "-mindepth" , "1" ,
134
+ # filenames can contain \n
135
+ "-print0" ,
136
+ ]
137
+ find_res = rep_ctx .execute (find_args )
138
+ if find_res .return_code == 0 :
139
+ for target in find_res .stdout .rstrip ("\0 " ).split ("\0 " ):
140
+ basename = target .rpartition ("/" )[- 1 ]
141
+ rep_ctx .symlink (target , basename )
142
+ else :
143
+ _execute_error (find_res )
144
+
145
+
146
+ def _executable_path (exe_name , rep_ctx , extra_msg = "" ):
147
+ """Try to find the executable, fail with an error."""
148
+ path = rep_ctx .which (exe_name )
149
+ if path == None :
150
+ fail ("Could not find the `{}` executable in PATH.{}\n "
151
+ .format (exe_name , " " + extra_msg if extra_msg else "" ))
152
+ return path
153
+
154
+
155
+ def _execute_error (exec_result , msg ):
156
+ """Print a nice error message for a failed `execute`."""
157
+ fail ("""
158
+ execute() error: {msg}
159
+ status code: {code}
160
+ stdout:
161
+ {stdout}
162
+ stderr:
163
+ {stderr}
164
+ """ .format (
165
+ msg = msg ,
166
+ code = exec_result .return_code ,
167
+ stdout = exec_result .stdout ,
168
+ stderr = exec_result .stderr ))
0 commit comments