gnu: emacs-helm: Update to 3.8.7.
[jackhill/guix/guix.git] / gnu / ci.scm
CommitLineData
59fb5c1c 1;;; GNU Guix --- Functional package management for GNU
688a4db0 2;;; Copyright © 2012-2021 Ludovic Courtès <ludo@gnu.org>
db2785cd 3;;; Copyright © 2017, 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
f71b0a00 4;;; Copyright © 2018, 2019 Clément Lassieur <clement@lassieur.org>
f43ec1c5 5;;; Copyright © 2020 Julien Lepiller <julien@lepiller.eu>
76bea3f8 6;;; Copyright © 2020, 2021 Mathieu Othacehe <othacehe@gnu.org>
59fb5c1c
LC
7;;;
8;;; This file is part of GNU Guix.
9;;;
10;;; GNU Guix is free software; you can redistribute it and/or modify it
11;;; under the terms of the GNU General Public License as published by
12;;; the Free Software Foundation; either version 3 of the License, or (at
13;;; your option) any later version.
14;;;
15;;; GNU Guix is distributed in the hope that it will be useful, but
16;;; WITHOUT ANY WARRANTY; without even the implied warranty of
17;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;;; GNU General Public License for more details.
19;;;
20;;; You should have received a copy of the GNU General Public License
21;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
22
23(define-module (gnu ci)
76bea3f8 24 #:use-module (guix channels)
59fb5c1c 25 #:use-module (guix config)
61a11653 26 #:use-module (guix describe)
59fb5c1c
LC
27 #:use-module (guix store)
28 #:use-module (guix grafts)
29 #:use-module (guix profiles)
30 #:use-module (guix packages)
6756c64a 31 #:autoload (guix transformations) (tunable-package? tuned-package)
7e6d8d36 32 #:use-module (guix channels)
f43ec1c5 33 #:use-module (guix config)
59fb5c1c 34 #:use-module (guix derivations)
7e6d8d36 35 #:use-module (guix build-system)
59fb5c1c 36 #:use-module (guix monads)
dd1ee160 37 #:use-module (guix gexp)
59fb5c1c 38 #:use-module (guix ui)
b5f8c2c8
LC
39 #:use-module ((guix licenses)
40 #:select (gpl3+ license? license-name))
59fb5c1c
LC
41 #:use-module ((guix utils) #:select (%current-system))
42 #:use-module ((guix scripts system) #:select (read-operating-system))
43 #:use-module ((guix scripts pack)
44 #:select (lookup-compressor self-contained-tarball))
45 #:use-module (gnu bootloader)
46 #:use-module (gnu bootloader u-boot)
f19cf27c 47 #:use-module (gnu image)
59fb5c1c
LC
48 #:use-module (gnu packages)
49 #:use-module (gnu packages gcc)
50 #:use-module (gnu packages base)
51 #:use-module (gnu packages gawk)
52 #:use-module (gnu packages guile)
53 #:use-module (gnu packages gettext)
54 #:use-module (gnu packages compression)
55 #:use-module (gnu packages multiprecision)
56 #:use-module (gnu packages make-bootstrap)
57 #:use-module (gnu packages package-management)
dab819d5 58 #:use-module (guix platform)
59fb5c1c 59 #:use-module (gnu system)
f19cf27c 60 #:use-module (gnu system image)
59fb5c1c
LC
61 #:use-module (gnu system vm)
62 #:use-module (gnu system install)
b06ba9e0 63 #:use-module (gnu system images hurd)
846e5240 64 #:use-module (gnu system images novena)
8a4f1eef 65 #:use-module (gnu system images pine64)
17d9a91e 66 #:use-module (gnu system images pinebook-pro)
59fb5c1c
LC
67 #:use-module (gnu tests)
68 #:use-module (srfi srfi-1)
69 #:use-module (srfi srfi-26)
70 #:use-module (ice-9 match)
996b5edf
MO
71 #:export (derivation->job
72 image->job
73
74 %core-packages
f292c501 75 channel-source->package
3034f3d0
MO
76
77 arguments->systems
76bea3f8 78 cuirass-jobs))
59fb5c1c
LC
79
80;;; Commentary:
81;;;
76bea3f8 82;;; This file defines build jobs for Cuirass.
59fb5c1c
LC
83;;;
84;;; Code:
85
76bea3f8
MO
86(define* (derivation->job name drv
87 #:key
76bea3f8 88 (max-silent-time 3600)
688a4db0 89 (timeout (* 5 3600)))
14ada964 90 "Return a Cuirass job called NAME and describing DRV.
76bea3f8
MO
91
92MAX-SILENT-TIME and TIMEOUT are build options passed to the daemon when
93building the derivation."
94 `((#:job-name . ,name)
95 (#:derivation . ,(derivation-file-name drv))
e4c02ac7
MO
96 (#:inputs . ,(map (compose derivation-file-name
97 derivation-input-derivation)
98 (derivation-inputs drv)))
76bea3f8
MO
99 (#:outputs . ,(filter-map
100 (lambda (res)
101 (match res
102 ((name . path)
103 `(,name . ,path))))
104 (derivation->output-paths drv)))
105 (#:nix-name . ,(derivation-name drv))
106 (#:system . ,(derivation-system drv))
76bea3f8
MO
107 (#:max-silent-time . ,max-silent-time)
108 (#:timeout . ,timeout)))
109
110(define* (package-job store job-name package system
6756c64a 111 #:key cross? target (suffix ""))
59fb5c1c 112 "Return a job called JOB-NAME that builds PACKAGE on SYSTEM."
6756c64a 113 (let ((job-name (string-append job-name "." system suffix)))
76bea3f8
MO
114 (parameterize ((%graft? #f))
115 (let* ((drv (if cross?
116 (package-cross-derivation store package target system
117 #:graft? #f)
118 (package-derivation store package system
119 #:graft? #f)))
120 (max-silent-time (or (assoc-ref (package-properties package)
121 'max-silent-time)
122 3600))
123 (timeout (or (assoc-ref (package-properties package)
124 'timeout)
125 72000)))
126 (derivation->job job-name drv
127 #:max-silent-time max-silent-time
128 #:timeout timeout)))))
59fb5c1c
LC
129
130(define (package-cross-job store job-name package target system)
131 "Return a job called TARGET.JOB-NAME that cross-builds PACKAGE for TARGET on
132SYSTEM."
39f1486e 133 (let ((name (string-append target "." job-name)))
76bea3f8
MO
134 (package-job store name package system
135 #:cross? #t
136 #:target target)))
59fb5c1c
LC
137
138(define %core-packages
139 ;; Note: Don't put the '-final' package variants because (1) that's
140 ;; implicit, and (2) they cannot be cross-built (due to the explicit input
141 ;; chain.)
9ac9df25 142 (list gcc-8 gcc-9 gcc-10 gcc-11 glibc binutils
59fb5c1c 143 gmp mpfr mpc coreutils findutils diffutils patch sed grep
32750e8c 144 gawk gnu-gettext hello guile-2.2 guile-3.0 zlib gzip xz guix
59fb5c1c
LC
145 %bootstrap-binaries-tarball
146 %binutils-bootstrap-tarball
147 (%glibc-bootstrap-tarball)
148 %gcc-bootstrap-tarball
149 %guile-bootstrap-tarball
150 %bootstrap-tarballs))
151
ac815ecd
LC
152(define (commencement-packages system)
153 "Return the list of bootstrap packages from the commencement module for
154SYSTEM."
155 ;; Only include packages supported on SYSTEM. For example, the Mes
156 ;; bootstrap graph is currently not supported on ARM so it should be
157 ;; excluded.
158 (filter (lambda (obj)
159 (and (package? obj)
160 (supported-package? obj system)))
161 (module-map (lambda (sym var)
162 (variable-ref var))
163 (resolve-module '(gnu packages commencement)))))
df49fe2a 164
668a5198
LC
165(define (packages-to-cross-build target)
166 "Return the list of packages to cross-build for TARGET."
167 ;; Don't cross-build the bootstrap tarballs for MinGW.
168 (if (string-contains target "mingw")
169 (drop-right %core-packages 6)
170 %core-packages))
59fb5c1c 171
3046e73b
LC
172(define (cross-jobs store system)
173 "Return a list of cross-compilation jobs for SYSTEM."
174 (define (from-32-to-64? target)
175 ;; Return true if SYSTEM is 32-bit and TARGET is 64-bit. This hack
176 ;; prevents known-to-fail cross-builds from i686-linux or armhf-linux to
177 ;; mips64el-linux-gnuabi64.
178 (and (or (string-prefix? "i686-" system)
179 (string-prefix? "i586-" system)
180 (string-prefix? "armhf-" system))
181 (string-contains target "64"))) ;x86_64, mips64el, aarch64, etc.
182
183 (define (same? target)
184 ;; Return true if SYSTEM and TARGET are the same thing. This is so we
185 ;; don't try to cross-compile to 'mips64el-linux-gnu' from
186 ;; 'mips64el-linux'.
187 (or (string-contains target system)
188 (and (string-prefix? "armhf" system) ;armhf-linux
189 (string-prefix? "arm" target)))) ;arm-linux-gnueabihf
190
191 (define (pointless? target)
192 ;; Return #t if it makes no sense to cross-build to TARGET from SYSTEM.
193 (match system
194 ((or "x86_64-linux" "i686-linux")
195 (if (string-contains target "mingw")
196 (not (string=? "x86_64-linux" system))
197 #f))
198 (_
199 ;; Don't try to cross-compile from non-Intel platforms: this isn't
200 ;; very useful and these are often brittle configurations.
201 #t)))
202
203 (define (either proc1 proc2 proc3)
204 (lambda (x)
205 (or (proc1 x) (proc2 x) (proc3 x))))
206
207 (append-map (lambda (target)
208 (map (lambda (package)
209 (package-cross-job store (job-name package)
210 package target system))
50b99c90 211 (packages-to-cross-build target)))
3046e73b 212 (remove (either from-32-to-64? same? pointless?)
dd970122 213 (targets))))
3046e73b 214
76bea3f8
MO
215(define* (guix-jobs store systems #:key source commit)
216 "Return a list of jobs for Guix itself."
217 (define build
218 (primitive-load (string-append source "/build-aux/build-self.scm")))
219
220 (map
221 (lambda (system)
222 (let ((name (string->symbol
223 (string-append "guix." system)))
224 (drv (run-with-store store
225 (build source #:version commit #:system system
226 #:pull-version 1
227 #:guile-version "2.2"))))
228 (derivation->job name drv)))
229 systems))
230
b06ba9e0
MO
231;; Architectures that are able to build or cross-build Guix System images.
232;; This does not mean that other architectures are not supported, only that
233;; they are often not fast enough to support Guix System images building.
234(define %guix-system-supported-systems
235 '("x86_64-linux" "i686-linux"))
59fb5c1c 236
b06ba9e0 237(define %guix-system-images
8a4f1eef 238 (list hurd-barebones-qcow2-image
17d9a91e 239 pine64-barebones-raw-image
846e5240
DM
240 pinebook-pro-barebones-raw-image
241 novena-barebones-raw-image))
59fb5c1c 242
fc2fa7ad
MO
243(define (hours hours)
244 (* 3600 hours))
245
996b5edf
MO
246(define* (image->job store image
247 #:key name system)
248 "Return the job for IMAGE on SYSTEM. If NAME is passed, use it as job name,
249otherwise use the IMAGE name."
250 (let* ((image-name (or name
251 (symbol->string (image-name image))))
252 (name (string-append image-name "." system))
253 (drv (run-with-store store
254 (mbegin %store-monad
255 (set-guile-for-build (default-guile))
256 (lower-object (system-image image))))))
257 (parameterize ((%graft? #f))
258 (derivation->job name drv))))
259
b06ba9e0 260(define (image-jobs store system)
14ada964 261 "Return a list of jobs that build images for SYSTEM."
59fb5c1c
LC
262 (define MiB
263 (expt 2 20))
264
b06ba9e0 265 (if (member system %guix-system-supported-systems)
996b5edf
MO
266 `(,(image->job store
267 (image
268 (inherit efi-disk-image)
269 (operating-system installation-os))
270 #:name "usb-image"
271 #:system system)
272 ,(image->job
273 store
274 (image
275 (inherit (image-with-label
276 iso9660-image
277 (string-append "GUIX_" system "_"
278 (if (> (string-length %guix-version) 7)
279 (substring %guix-version 0 7)
280 %guix-version))))
281 (operating-system installation-os))
282 #:name "iso9660-image"
283 #:system system)
b06ba9e0
MO
284 ;; Only cross-compile Guix System images from x86_64-linux for now.
285 ,@(if (string=? system "x86_64-linux")
996b5edf
MO
286 (map (cut image->job store <>
287 #:system system)
b06ba9e0
MO
288 %guix-system-images)
289 '()))
1189f405 290 '()))
59fb5c1c 291
7e6d8d36
LC
292(define channel-build-system
293 ;; Build system used to "convert" a channel instance to a package.
7c5f01d5
LC
294 (let* ((build (lambda* (name inputs
295 #:key source commit system
296 #:allow-other-keys)
297 (mlet* %store-monad ((source (if (string? source)
298 (return source)
299 (lower-object source)))
300 (instance
301 -> (checkout->channel-instance
302 source #:commit commit)))
303 (channel-instances->derivation (list instance)))))
dd1ee160
LC
304 (lower (lambda* (name #:key system source commit
305 #:allow-other-keys)
7e6d8d36
LC
306 (bag
307 (name name)
308 (system system)
309 (build build)
dd1ee160
LC
310 (arguments `(#:source ,source
311 #:commit ,commit))))))
7e6d8d36
LC
312 (build-system (name 'channel)
313 (description "Turn a channel instance into a package.")
314 (lower lower))))
315
dd1ee160
LC
316(define* (channel-source->package source #:key commit)
317 "Return a package for the given channel SOURCE, a lowerable object."
7e6d8d36
LC
318 (package
319 (inherit guix)
dd1ee160 320 (version (string-append (package-version guix) "+"))
7e6d8d36 321 (build-system channel-build-system)
dd1ee160
LC
322 (arguments `(#:source ,source
323 #:commit ,commit))
7e6d8d36
LC
324 (inputs '())
325 (native-inputs '())
326 (propagated-inputs '())))
327
328(define* (system-test-jobs store system
329 #:key source commit)
59fb5c1c 330 "Return a list of jobs for the system tests."
59fb5c1c 331 (define (->job test)
fc37346f
MO
332 (let ((name (string-append "test." (system-test-name test)
333 "." system))
334 (drv (run-with-store store
335 (mbegin %store-monad
336 (set-current-system system)
337 (set-grafting #f)
338 (set-guile-for-build (default-guile))
339 (system-test-value test)))))
76bea3f8 340
fc37346f 341 (derivation->job name drv)))
59fb5c1c 342
b06ba9e0 343 (if (member system %guix-system-supported-systems)
7e6d8d36
LC
344 ;; Override the value of 'current-guix' used by system tests. Using a
345 ;; channel instance makes tests that rely on 'current-guix' less
346 ;; expensive. It also makes sure we get a valid Guix package when this
347 ;; code is not running from a checkout.
fc37346f
MO
348 (parameterize ((current-guix-package
349 (channel-source->package source #:commit commit)))
350 (map ->job (all-system-tests)))
59fb5c1c
LC
351 '()))
352
353(define (tarball-jobs store system)
76bea3f8 354 "Return jobs to build the self-contained Guix binary tarball."
59fb5c1c 355 (define (->job name drv)
76bea3f8
MO
356 (let ((name (string-append name "." system)))
357 (parameterize ((%graft? #f))
14ada964 358 (derivation->job name drv))))
59fb5c1c
LC
359
360 ;; XXX: Add a job for the stable Guix?
76bea3f8
MO
361 (list
362 (->job "binary-tarball"
363 (run-with-store store
364 (mbegin %store-monad
365 (set-guile-for-build (default-guile))
366 (>>= (profile-derivation (packages->manifest (list guix)))
367 (lambda (profile)
368 (self-contained-tarball "guix-binary" profile
2ccb715a 369 #:profile-name "current-guix"
76bea3f8
MO
370 #:localstatedir? #t
371 #:compressor
372 (lookup-compressor "xz")))))
373 #:system system))))
59fb5c1c
LC
374
375(define job-name
376 ;; Return the name of a package's job.
76bea3f8 377 package-name)
59fb5c1c
LC
378
379(define package->job
380 (let ((base-packages
381 (delete-duplicates
382 (append-map (match-lambda
76bea3f8
MO
383 ((_ package _ ...)
384 (match (package-transitive-inputs package)
385 (((_ inputs _ ...) ...)
386 inputs))))
59fb5c1c 387 (%final-inputs)))))
6756c64a 388 (lambda* (store package system #:key (suffix ""))
59fb5c1c 389 "Return a job for PACKAGE on SYSTEM, or #f if this combination is not
6756c64a 390valid. Append SUFFIX to the job name."
59fb5c1c 391 (cond ((member package base-packages)
76bea3f8 392 (package-job store (string-append "base." (job-name package))
6756c64a 393 package system #:suffix suffix))
59fb5c1c
LC
394 ((supported-package? package system)
395 (let ((drv (package-derivation store package system
396 #:graft? #f)))
397 (and (substitutable-derivation? drv)
398 (package-job store (job-name package)
6756c64a 399 package system #:suffix suffix))))
59fb5c1c
LC
400 (else
401 #f)))))
402
6756c64a
LC
403(define %x86-64-micro-architectures
404 ;; Micro-architectures for which we build tuned variants.
405 '("westmere" "ivybridge" "haswell" "skylake" "skylake-avx512"))
406
407(define (tuned-package-jobs store package system)
408 "Return a list of jobs for PACKAGE tuned for SYSTEM's micro-architectures."
409 (filter-map (lambda (micro-architecture)
410 (define suffix
411 (string-append "." micro-architecture))
412
413 (package->job store
414 (tuned-package package micro-architecture)
415 system
416 #:suffix suffix))
417 (match system
418 ("x86_64-linux" %x86-64-micro-architectures)
419 (_ '()))))
420
59fb5c1c
LC
421(define (all-packages)
422 "Return the list of packages to build."
423 (define (adjust package result)
424 (cond ((package-replacement package)
f00ff8db
LC
425 ;; XXX: If PACKAGE and its replacement have the same name/version,
426 ;; then both Cuirass jobs will have the same name, which
427 ;; effectively means that the second one will be ignored. Thus,
428 ;; return the replacement first.
429 (cons* (package-replacement package) ;build both
430 package
59fb5c1c
LC
431 result))
432 ((package-superseded package)
433 result) ;don't build it
434 (else
435 (cons package result))))
436
437 (fold-packages adjust
438 (fold adjust '() ;include base packages
439 (match (%final-inputs)
440 (((labels packages _ ...) ...)
441 packages)))
442 #:select? (const #t))) ;include hidden packages
443
76bea3f8 444(define (arguments->manifests arguments channels)
59fb5c1c 445 "Return the list of manifests extracted from ARGUMENTS."
862af8c2
MO
446 (map (lambda (manifest)
447 (any (lambda (checkout)
448 (let ((path (in-vicinity checkout manifest)))
449 (and (file-exists? path)
450 path)))
451 (map channel-url channels)))
76bea3f8 452 arguments))
59fb5c1c 453
688a4db0
LC
454(define (manifests->jobs store manifests)
455 "Return the list of jobs for the entries in MANIFESTS, a list of file
456names."
59fb5c1c
LC
457 (define (load-manifest manifest)
458 (save-module-excursion
459 (lambda ()
460 (set-current-module (make-user-module '((guix profiles) (gnu))))
461 (primitive-load manifest))))
462
688a4db0
LC
463 (define (manifest-entry-job-name entry)
464 (string-append (manifest-entry-name entry) "-"
465 (manifest-entry-version entry)))
466
467 (define (manifest-entry->job entry)
468 (let* ((obj (manifest-entry-item entry))
469 (drv (parameterize ((%graft? #f))
470 (run-with-store store
471 (lower-object obj))))
472 (max-silent-time (or (and (package? obj)
473 (assoc-ref (package-properties obj)
474 'max-silent-time))
475 3600))
476 (timeout (or (and (package? obj)
477 (assoc-ref (package-properties obj) 'timeout))
478 (* 5 3600))))
479 (derivation->job (manifest-entry-job-name entry) drv
480 #:max-silent-time max-silent-time
481 #:timeout timeout)))
482
483 (map manifest-entry->job
484 (delete-duplicates
485 (append-map (compose manifest-entries load-manifest)
486 manifests)
487 manifest-entry=?)))
59fb5c1c 488
3034f3d0
MO
489(define (arguments->systems arguments)
490 "Return the systems list from ARGUMENTS."
491 (match (assoc-ref arguments 'systems)
492 (#f %cuirass-supported-systems)
493 ((lst ...) lst)
494 ((? string? str) (call-with-input-string str read))))
495
59fb5c1c
LC
496\f
497;;;
76bea3f8 498;;; Cuirass entry point.
59fb5c1c
LC
499;;;
500
76bea3f8
MO
501(define (cuirass-jobs store arguments)
502 "Register Cuirass jobs."
59fb5c1c 503 (define subset
76bea3f8 504 (assoc-ref arguments 'subset))
59fb5c1c
LC
505
506 (define systems
3034f3d0 507 (arguments->systems arguments))
59fb5c1c 508
76bea3f8
MO
509 (define channels
510 (let ((channels (assq-ref arguments 'channels)))
511 (map sexp->channel channels)))
512
513 (define guix
514 (find guix-channel? channels))
7e6d8d36
LC
515
516 (define commit
76bea3f8 517 (channel-commit guix))
7e6d8d36
LC
518
519 (define source
76bea3f8 520 (channel-url guix))
7e6d8d36 521
59fb5c1c
LC
522 ;; Turn off grafts. Grafting is meant to happen on the user's machines.
523 (parameterize ((%graft? #f))
524 ;; Return one job for each package, except bootstrap packages.
76bea3f8
MO
525 (append-map
526 (lambda (system)
527 (format (current-error-port)
528 "evaluating for '~a' (heap size: ~a MiB)...~%"
529 system
530 (round
531 (/ (assoc-ref (gc-stats) 'heap-size)
532 (expt 2. 20))))
533 (invalidate-derivation-caches!)
534 (match subset
535 ('all
536 ;; Build everything, including replacements.
537 (let ((all (all-packages))
6756c64a
LC
538 (jobs (lambda (package)
539 (match (package->job store package system)
540 (#f '())
541 (main-job
542 (cons main-job
543 (if (tunable-package? package)
544 (tuned-package-jobs store package system)
545 '())))))))
76bea3f8 546 (append
6756c64a 547 (append-map jobs all)
76bea3f8
MO
548 (cross-jobs store system))))
549 ('core
550 ;; Build core packages only.
551 (append
552 (map (lambda (package)
553 (package-job store (job-name package)
554 package system))
ac815ecd 555 (append (commencement-packages system) %core-packages))
76bea3f8
MO
556 (cross-jobs store system)))
557 ('guix
558 ;; Build Guix modules only.
559 (guix-jobs store systems
560 #:source source
561 #:commit commit))
562 ('hello
563 ;; Build hello package only.
564 (let ((hello (specification->package "hello")))
565 (list (package-job store (job-name hello)
566 hello system))))
2afc79b5
MO
567 ('images
568 ;; Build Guix System images only.
569 (image-jobs store system))
570 ('system-tests
571 ;; Build Guix System tests only.
572 (system-test-jobs store system
573 #:source source
574 #:commit commit))
575 ('tarball
576 ;; Build Guix tarball only.
577 (tarball-jobs store system))
f97e220b
MO
578 (('custom . modules)
579 ;; Build custom modules jobs only.
580 (append-map
581 (lambda (module)
582 (let ((proc (module-ref
583 (resolve-interface module)
584 'cuirass-jobs)))
585 (proc store arguments)))
586 modules))
61a11653
MO
587 (('channels . channels)
588 ;; Build only the packages from CHANNELS.
589 (let ((all (all-packages)))
590 (filter-map
591 (lambda (package)
cbfcbb79
MO
592 (any (lambda (channel)
593 (and (member (channel-name channel) channels)
594 (package->job store package system)))
595 (package-channels package)))
61a11653 596 all)))
76bea3f8
MO
597 (('packages . rest)
598 ;; Build selected list of packages only.
599 (let ((packages (map specification->package rest)))
600 (map (lambda (package)
601 (package-job store (job-name package)
602 package system))
603 packages)))
604 (('manifests . rest)
605 ;; Build packages in the list of manifests.
688a4db0
LC
606 (let ((manifests (arguments->manifests rest channels)))
607 (manifests->jobs store manifests)))
76bea3f8
MO
608 (else
609 (error "unknown subset" subset))))
610 systems)))