Skip to content

Commit 53e593d

Browse files
authored
Merge pull request #419 from GaetanLepage/multi-systems
Feat: add support for multi-systems
2 parents 310f10b + 93732c7 commit 53e593d

File tree

12 files changed

+433
-215
lines changed

12 files changed

+433
-215
lines changed

README.md

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -175,19 +175,38 @@ done
175175
inside the nix-shell instead of an interactive session:
176176

177177
```console
178-
$ nixpkgs-review pr --run 'jq < report.json' 113814
178+
$ nixpkgs-review pr --run --systems all 'jq < report.json' 340297
179179
# ...
180180
{
181-
"blacklisted": [],
182-
"broken": [],
183-
"built": [
184-
"cargo-deny"
185-
],
186-
"failed": [],
187-
"non-existent": [],
188-
"pr": 113814,
189-
"system": "x86_64-linux",
190-
"tests": []
181+
"checkout": "merge",
182+
"extra-nixpkgs-config": null,
183+
"pr": 340297,
184+
"result": {
185+
"aarch64-linux": {
186+
"blacklisted": [],
187+
"broken": [],
188+
"built": [
189+
"forecast"
190+
],
191+
"failed": [],
192+
"non-existent": [],
193+
"tests": []
194+
},
195+
"x86_64-linux": {
196+
"blacklisted": [],
197+
"broken": [],
198+
"built": [
199+
"forecast"
200+
],
201+
"failed": [],
202+
"non-existent": [],
203+
"tests": []
204+
}
205+
},
206+
"systems": [
207+
"x86_64-linux",
208+
"aarch64-linux"
209+
]
191210
}
192211
```
193212

@@ -326,9 +345,17 @@ subcommand.
326345

327346
## Review changes for other operating systems/architectures
328347

329-
The `--system` flag allows setting a system different from the current one. Note
330-
that the result nix-shell may not be able to execute all hooks correctly since
331-
the architecture/operating system mismatches.
348+
The `--systems` flag allows setting a system different from the current one.
349+
Note that the result nix-shell may not be able to execute all hooks correctly
350+
since the architecture/operating system mismatches.
351+
352+
By default, `nixpkgs-review` targets only the current system
353+
(`--systems current`). You can also explicitly provide one or several systems to
354+
target (`--systems "x86_64-linux aarch64-darwin"`). The `--systems all` value
355+
will build for the four major platforms supported by hydra (`--x86_64-linux`,
356+
`aarch64-linux`, `x86_64-darwin` and `aarch64-darwin`). Ensure that your system
357+
is capable of building for the specified architectures, either locally or
358+
through the remote builder protocol.
332359

333360
```console
334361
$ nixpkgs-review pr --system aarch64-linux 98734

nixpkgs_review/cli/__init__.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from shutil import which
1010
from typing import Any, cast
1111

12-
from ..utils import current_system, nix_nom_tool
12+
from ..utils import nix_nom_tool
1313
from .approve import approve_command
1414
from .comments import show_comments
1515
from .merge import merge_command
@@ -213,11 +213,17 @@ def common_flags() -> list[CommonFlag]:
213213
type=regex_type,
214214
help="Regular expression that package attributes have not to match (can be passed multiple times)",
215215
),
216+
CommonFlag(
217+
"--systems",
218+
type=str,
219+
default="current",
220+
help="Nix 'systems' to evaluate and build packages for",
221+
),
216222
CommonFlag(
217223
"--system",
218224
type=str,
219-
default=current_system(),
220-
help="Nix 'system' to evaluate and build packages for",
225+
default="",
226+
help="[DEPRECATED] use `--systems` instead",
221227
),
222228
CommonFlag(
223229
"--token",
@@ -243,6 +249,12 @@ def common_flags() -> list[CommonFlag]:
243249
default="{ }",
244250
help="Extra nixpkgs config to pass to `import <nixpkgs>`",
245251
),
252+
CommonFlag(
253+
"--num-procs-eval",
254+
type=int,
255+
default=1,
256+
help="Number of parallel `nix-env` processes to run simultaneously (warning, can imply heavy RAM usage)",
257+
),
246258
]
247259

248260

nixpkgs_review/cli/pr.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
import re
33
import sys
44
from contextlib import ExitStack
5+
from pathlib import Path
56

67
from ..allow import AllowedFeatures
78
from ..builddir import Builddir
89
from ..buildenv import Buildenv
910
from ..errors import NixpkgsReviewError
11+
from ..nix import Attr
1012
from ..review import CheckoutOption, Review
11-
from ..utils import warn
13+
from ..utils import System, warn
1214
from .utils import ensure_github_token
1315

1416

@@ -32,16 +34,28 @@ def parse_pr_numbers(number_args: list[str]) -> list[int]:
3234

3335

3436
def pr_command(args: argparse.Namespace) -> str:
35-
prs = parse_pr_numbers(args.number)
37+
prs: list[int] = parse_pr_numbers(args.number)
3638
use_ofborg_eval = args.eval == "ofborg"
3739
checkout_option = (
3840
CheckoutOption.MERGE if args.checkout == "merge" else CheckoutOption.COMMIT
3941
)
4042

4143
if args.post_result:
4244
ensure_github_token(args.token)
45+
if args.system:
46+
warn("Warning: The `--system` is deprecated. Use `--systems` instead.")
47+
args.systems = args.system
4348

44-
contexts = []
49+
contexts: list[
50+
tuple[
51+
# PR number
52+
int,
53+
# builddir path
54+
Path,
55+
# Attrs to build for each system
56+
dict[System, list[Attr]],
57+
]
58+
] = []
4559

4660
allow = AllowedFeatures(args.allow)
4761

@@ -65,13 +79,14 @@ def pr_command(args: argparse.Namespace) -> str:
6579
package_regexes=args.package_regex,
6680
skip_packages=set(args.skip_package),
6781
skip_packages_regex=args.skip_package_regex,
68-
system=args.system,
82+
systems=args.systems.split(" "),
6983
allow=allow,
7084
checkout=checkout_option,
7185
sandbox=args.sandbox,
7286
build_graph=args.build_graph,
7387
nixpkgs_config=nixpkgs_config,
7488
extra_nixpkgs_config=args.extra_nixpkgs_config,
89+
n_procs_eval=args.num_procs_eval,
7590
)
7691
contexts.append((pr, builddir.path, review.build_pr(pr)))
7792
except NixpkgsReviewError as e:

nixpkgs_review/nix.py

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import json
2+
import multiprocessing as mp
23
import os
34
import shlex
45
import shutil
56
import subprocess
67
from dataclasses import dataclass, field
8+
from functools import partial
79
from pathlib import Path
810
from sys import platform
911
from tempfile import NamedTemporaryFile
1012
from typing import Any, Final
1113

1214
from .allow import AllowedFeatures
1315
from .errors import NixpkgsReviewError
14-
from .utils import ROOT, escape_attr, info, sh, warn
16+
from .utils import ROOT, System, info, sh, warn
1517

1618

1719
@dataclass
@@ -56,9 +58,9 @@ def is_test(self) -> bool:
5658

5759

5860
def nix_shell(
59-
attrs: list[str],
61+
attrs_per_system: dict[System, list[str]],
6062
cache_directory: Path,
61-
system: str,
63+
local_system: str,
6264
build_graph: str,
6365
nix_path: str,
6466
nixpkgs_config: Path,
@@ -71,7 +73,10 @@ def nix_shell(
7173
raise RuntimeError(f"{build_graph} not found in PATH")
7274

7375
shell_file_args = build_shell_file_args(
74-
cache_directory, attrs, system, nixpkgs_config
76+
cache_dir=cache_directory,
77+
attrs_per_system=attrs_per_system,
78+
local_system=local_system,
79+
nixpkgs_config=nixpkgs_config,
7580
)
7681
if sandbox:
7782
args = _nix_shell_sandbox(
@@ -266,28 +271,66 @@ def nix_eval(
266271
os.unlink(attr_json.name)
267272

268273

269-
def nix_build(
274+
def nix_eval_thread(
275+
system: System,
270276
attr_names: set[str],
277+
allow: AllowedFeatures,
278+
nix_path: str,
279+
) -> tuple[System, list[Attr]]:
280+
return system, nix_eval(attr_names, system, allow, nix_path)
281+
282+
283+
def multi_system_eval(
284+
attr_names_per_system: dict[System, set[str]],
285+
allow: AllowedFeatures,
286+
nix_path: str,
287+
n_procs: int,
288+
) -> dict[System, list[Attr]]:
289+
nix_eval_partial = partial(
290+
nix_eval_thread,
291+
allow=allow,
292+
nix_path=nix_path,
293+
)
294+
295+
args: list[tuple[System, set[str]]] = list(attr_names_per_system.items())
296+
with mp.Pool(n_procs) as pool:
297+
results: list[tuple[System, list[Attr]]] = pool.starmap(nix_eval_partial, args)
298+
299+
return {system: attrs for system, attrs in results}
300+
301+
302+
def nix_build(
303+
attr_names_per_system: dict[System, set[str]],
271304
args: str,
272305
cache_directory: Path,
273-
system: str,
306+
systems: set[System],
307+
local_system: System,
274308
allow: AllowedFeatures,
275309
build_graph: str,
276310
nix_path: str,
277311
nixpkgs_config: Path,
278-
) -> list[Attr]:
279-
if not attr_names:
312+
n_procs_eval: int,
313+
) -> dict[System, list[Attr]]:
314+
if not attr_names_per_system:
280315
info("Nothing to be built.")
281-
return []
316+
return {}
282317

283-
attrs = nix_eval(attr_names, system, allow, nix_path)
284-
filtered = []
285-
for attr in attrs:
286-
if not (attr.broken or attr.blacklisted):
287-
filtered.append(attr.name)
318+
attrs_per_system: dict[System, list[Attr]] = multi_system_eval(
319+
attr_names_per_system,
320+
allow,
321+
nix_path,
322+
n_procs=n_procs_eval,
323+
)
288324

289-
if len(filtered) == 0:
290-
return attrs
325+
filtered_per_system: dict[System, list[str]] = {}
326+
for system, attrs in attrs_per_system.items():
327+
filtered_per_system[system] = []
328+
for attr in attrs:
329+
if not (attr.broken or attr.blacklisted):
330+
filtered_per_system[system].append(attr.name)
331+
332+
if all(len(filtered) == 0 for filtered in filtered_per_system.values()):
333+
return attrs_per_system
291334

292335
command = [
293336
build_graph,
@@ -314,26 +357,37 @@ def nix_build(
314357
]
315358

316359
command += build_shell_file_args(
317-
cache_directory, filtered, system, nixpkgs_config
360+
cache_dir=cache_directory,
361+
attrs_per_system=filtered_per_system,
362+
local_system=local_system,
363+
nixpkgs_config=nixpkgs_config,
318364
) + shlex.split(args)
319365

320366
sh(command)
321-
return attrs
367+
return attrs_per_system
322368

323369

324370
def build_shell_file_args(
325-
cache_dir: Path, attrs: list[str], system: str, nixpkgs_config: Path
371+
cache_dir: Path,
372+
attrs_per_system: dict[System, list[str]],
373+
local_system: str,
374+
nixpkgs_config: Path,
326375
) -> list[str]:
327376
attrs_file = cache_dir.joinpath("attrs.nix")
328377
with open(attrs_file, "w+", encoding="utf-8") as f:
329-
f.write("pkgs: with pkgs; [\n")
330-
f.write("\n".join(f" {escape_attr(a)}" for a in attrs))
331-
f.write("\n]")
378+
f.write("{\n")
379+
for system, attrs in attrs_per_system.items():
380+
f.write(f" {system} = [\n")
381+
for attr in attrs:
382+
f.write(f' "{attr}"\n')
383+
f.write(" ];\n")
384+
f.write("}")
385+
print(f.read())
332386

333387
return [
334388
"--argstr",
335-
"system",
336-
system,
389+
"local-system",
390+
local_system,
337391
"--argstr",
338392
"nixpkgs-path",
339393
str(cache_dir.joinpath("nixpkgs/")),

nixpkgs_review/nix/review-shell.nix

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
1-
{ system
2-
, nixpkgs-config-path # Path to Nix file containing the Nixpkgs config
3-
, attrs-path # Path to Nix file containing a list of attributes to build
4-
, nixpkgs-path # Path to this review's nixpkgs
5-
, pkgs ? import nixpkgs-path { inherit system; config = import nixpkgs-config-path; }
6-
, lib ? pkgs.lib
1+
{ local-system
2+
, nixpkgs-config-path
3+
, # Path to Nix file containing the Nixpkgs config
4+
attrs-path
5+
, # Path to Nix file containing a list of attributes to build
6+
nixpkgs-path
7+
, # Path to this review's nixpkgs
8+
local-pkgs ? import nixpkgs-path {
9+
system = local-system;
10+
config = import nixpkgs-config-path;
11+
}
12+
, lib ? local-pkgs.lib
13+
,
714
}:
815

916
let
10-
attrs = import attrs-path pkgs;
11-
env = pkgs.buildEnv {
17+
18+
nixpkgs-config = import nixpkgs-config-path;
19+
extractPackagesForSystem =
20+
system: system-attrs:
21+
let
22+
system-pkg = import nixpkgs-path {
23+
inherit system;
24+
config = nixpkgs-config;
25+
};
26+
in
27+
map (attrString: lib.attrByPath (lib.splitString "." attrString) null system-pkg) system-attrs;
28+
attrs = lib.flatten (lib.mapAttrsToList extractPackagesForSystem (import attrs-path));
29+
env = local-pkgs.buildEnv {
1230
name = "env";
1331
paths = attrs;
1432
ignoreCollisions = true;

0 commit comments

Comments
 (0)