From b512dadfd603869ac009a432b56f55945841cce0 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Fri, 1 Jul 2022 16:24:03 +0200 Subject: upstream: 'guix refresh -u' no longer stops when upstream info is lacking. Fixes . Starting from 53b9c27aa59bebf955f0aa24fef60a101480ef5c, 'guix refresh -u' would stop upon the first failure to determine upstream releases. This fixes that. * guix/upstream.scm (package-update): Warn rather than update. --- guix/upstream.scm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'guix') diff --git a/guix/upstream.scm b/guix/upstream.scm index dac8153905..9b49d1641f 100644 --- a/guix/upstream.scm +++ b/guix/upstream.scm @@ -515,9 +515,10 @@ this method: ~s") #:key-download key-download)))) (values #f #f #f))) (#f - (raise (formatted-message - (G_ "updater failed to determine available releases for ~a~%") - (package-name package)))))) + ;; Warn rather than abort so that other updates can still take place. + (warning (G_ "updater failed to determine available releases for ~a~%") + (package-name package)) + (values #f #f #f)))) (define* (update-package-source package source hash) "Modify the source file that defines PACKAGE to refer to SOURCE, an -- cgit v1.2.3 From 4ff12d1de7cd617b791996ee7ca1240660b4c20e Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Tue, 31 May 2022 17:17:10 +0200 Subject: profiles: Do not repeat entries in 'manifest' file. Fixes . Reported by Ricardo Wurmus . With this change, the manifest file created for: guix install r r-seurat r-cistopic r-monocle3 r-cicero-monocle3 r-assertthat goes from 5.7M to 176K. Likewise, on this profile, wall-clock time of: GUIX_PROFILING=gc guix package -I goes from 0.7s to 0.1s, with heap usage going from 55M to 9M. * guix/profiles.scm (manifest->gexp)[optional]: New procedure. [entry->gexp]: Turn into a monadic procedure. Return a 'repeated' sexp if ENTRY was already visited before. Adjust caller accordingly. Bump manifest version. (sexp->manifest)[sexp->manifest-entry]: Turn into a monadic procedure. Add case for 'repeated' nodes. Add each entry to the current state vhash. Add clause for version 4 manifests. [sexp->manifest-entry/v3]: New procedure, with former 'sexp->manifest-entry' code. * tests/profiles.scm ("deduplication of repeated entries"): New test. * guix/build/profiles.scm (manifest-sexp->inputs+search-paths)[let-fields]: New macro. Use it. Expect version 4. Add clause for 'repeated' nodes. --- guix/build/profiles.scm | 32 ++++++++--- guix/profiles.scm | 139 +++++++++++++++++++++++++++++++++++++++--------- tests/profiles.scm | 42 +++++++++++++++ 3 files changed, 181 insertions(+), 32 deletions(-) (limited to 'guix') diff --git a/guix/build/profiles.scm b/guix/build/profiles.scm index f9875ca92e..2ab76bde74 100644 --- a/guix/build/profiles.scm +++ b/guix/build/profiles.scm @@ -149,19 +149,33 @@ instead make DIRECTORY a \"real\" directory containing symlinks." "Parse MANIFEST, an sexp as produced by 'manifest->gexp', and return two values: the list of store items of its manifest entries, and the list of search path specifications." + (define-syntax let-fields + (syntax-rules () + ;; Bind the fields NAME of LST to same-named variables in the lexical + ;; scope of BODY. + ((_ lst (name rest ...) body ...) + (let ((name (match (assq 'name lst) + ((_ value) value) + (#f '())))) + (let-fields lst (rest ...) body ...))) + ((_ lst () body ...) + (begin body ...)))) + (match manifest ;this must match 'manifest->gexp' - (('manifest ('version 3) + (('manifest ('version 4) ('packages (entries ...))) (let loop ((entries entries) (inputs '()) (search-paths '())) (match entries - (((name version output item - ('propagated-inputs deps) - ('search-paths paths) _ ...) . rest) - (loop (append rest deps) ;breadth-first traversal - (cons item inputs) - (append paths search-paths))) + (((name version output item fields ...) . rest) + (let ((paths search-paths)) + (let-fields fields (propagated-inputs search-paths properties) + (loop (append rest propagated-inputs) ;breadth-first traversal + (cons item inputs) + (append search-paths paths))))) + ((('repeated name version item) . rest) + (loop rest inputs search-paths)) (() (values (reverse inputs) (delete-duplicates @@ -212,4 +226,8 @@ search paths of MANIFEST's entries." ;; Write 'OUTPUT/etc/profile'. (build-etc/profile output search-paths))) +;;; Local Variables: +;;; eval: (put 'let-fields 'scheme-indent-function 2) +;;; End: + ;;; profile.scm ends here diff --git a/guix/profiles.scm b/guix/profiles.scm index bf50c00a1e..701852ae98 100644 --- a/guix/profiles.scm +++ b/guix/profiles.scm @@ -454,32 +454,58 @@ denoting a specific output of a package." (define (manifest->gexp manifest) "Return a representation of MANIFEST as a gexp." + (define (optional name value) + (if (null? value) + #~() + #~((#$name #$value)))) + (define (entry->gexp entry) - (match entry - (($ name version output (? string? path) - (deps ...) (search-paths ...) _ (properties ...)) - #~(#$name #$version #$output #$path - (propagated-inputs #$(map entry->gexp deps)) - (search-paths #$(map search-path-specification->sexp - search-paths)) - #$@(if (null? properties) - #~() - #~((properties . #$properties))))) - (($ name version output package - (deps ...) (search-paths ...) _ (properties ...)) - #~(#$name #$version #$output - (ungexp package (or output "out")) - (propagated-inputs #$(map entry->gexp deps)) - (search-paths #$(map search-path-specification->sexp - search-paths)) - #$@(if (null? properties) - #~() - #~((properties . #$properties))))))) + ;; Maintain in state monad a vhash of visited entries, indexed by their + ;; item, usually package objects (we cannot use the entry itself as an + ;; index since identical entries are usually not 'eq?'). Use that vhash + ;; to avoid repeating duplicate entries. This is particularly useful in + ;; the presence of propagated inputs, where we could otherwise end up + ;; repeating large trees. + (mlet %state-monad ((visited (current-state))) + (if (match (vhash-assq (manifest-entry-item entry) visited) + ((_ . previous-entry) + (manifest-entry=? previous-entry entry)) + (#f #f)) + (return #~(repeated #$(manifest-entry-name entry) + #$(manifest-entry-version entry) + (ungexp (manifest-entry-item entry) + (manifest-entry-output entry)))) + (mbegin %state-monad + (set-current-state (vhash-consq (manifest-entry-item entry) + entry visited)) + (mlet %state-monad ((deps (mapm %state-monad entry->gexp + (manifest-entry-dependencies entry)))) + (return + (match entry + (($ name version output (? string? path) + (_ ...) (search-paths ...) _ (properties ...)) + #~(#$name #$version #$output #$path + #$@(optional 'propagated-inputs deps) + #$@(optional 'search-paths + (map search-path-specification->sexp + search-paths)) + #$@(optional 'properties properties))) + (($ name version output package + (_deps ...) (search-paths ...) _ (properties ...)) + #~(#$name #$version #$output + (ungexp package (or output "out")) + #$@(optional 'propagated-inputs deps) + #$@(optional 'search-paths + (map search-path-specification->sexp + search-paths)) + #$@(optional 'properties properties)))))))))) (match manifest (($ (entries ...)) - #~(manifest (version 3) - (packages #$(map entry->gexp entries)))))) + #~(manifest (version 4) + (packages #$(run-with-state + (mapm %state-monad entry->gexp entries) + vlist-null)))))) (define (find-package name version) "Return a package from the distro matching NAME and possibly VERSION. This @@ -520,14 +546,15 @@ procedure is here for backward-compatibility and will eventually vanish." (item item) (parent parent)))) - (define* (sexp->manifest-entry sexp #:optional (parent (delay #f))) + (define* (sexp->manifest-entry/v3 sexp #:optional (parent (delay #f))) + ;; Read SEXP as a version 3 manifest entry. (match sexp ((name version output path ('propagated-inputs deps) ('search-paths search-paths) extra-stuff ...) ;; For each of DEPS, keep a promise pointing to ENTRY. - (letrec* ((deps* (map (cut sexp->manifest-entry <> (delay entry)) + (letrec* ((deps* (map (cut sexp->manifest-entry/v3 <> (delay entry)) deps)) (entry (manifest-entry (name name) @@ -542,6 +569,56 @@ procedure is here for backward-compatibility and will eventually vanish." '()))))) entry)))) + (define-syntax let-fields + (syntax-rules () + ;; Bind the fields NAME of LST to same-named variables in the lexical + ;; scope of BODY. + ((_ lst (name rest ...) body ...) + (let ((name (match (assq 'name lst) + ((_ value) value) + (#f '())))) + (let-fields lst (rest ...) body ...))) + ((_ lst () body ...) + (begin body ...)))) + + (define* (sexp->manifest-entry sexp #:optional (parent (delay #f))) + (match sexp + (('repeated name version path) + ;; This entry is the same as another one encountered earlier; look it + ;; up and return it. + (mlet %state-monad ((visited (current-state)) + (key -> (list name version path))) + (match (vhash-assoc key visited) + (#f + (raise (formatted-message + (G_ "invalid repeated entry in profile: ~s") + sexp))) + ((_ . entry) + (return entry))))) + ((name version output path fields ...) + (let-fields fields (propagated-inputs search-paths properties) + (mlet* %state-monad + ((entry -> #f) + (deps (mapm %state-monad + (cut sexp->manifest-entry <> (delay entry)) + propagated-inputs)) + (visited (current-state)) + (key -> (list name version path))) + (set! entry ;XXX: emulate 'letrec*' + (manifest-entry + (name name) + (version version) + (output output) + (item path) + (dependencies deps) + (search-paths (map sexp->search-path-specification + search-paths)) + (parent parent) + (properties properties))) + (mbegin %state-monad + (set-current-state (vhash-cons key entry visited)) + (return entry))))))) + (match sexp (('manifest ('version 0) ('packages ((name version output path) ...))) @@ -608,7 +685,15 @@ procedure is here for backward-compatibility and will eventually vanish." ;; Version 3 represents DEPS as full-blown manifest entries. (('manifest ('version 3 minor-version ...) ('packages (entries ...))) - (manifest (map sexp->manifest-entry entries))) + (manifest (map sexp->manifest-entry/v3 entries))) + + ;; Version 4 deduplicates repeated entries and makes manifest entry fields + ;; such as 'propagated-inputs' and 'search-paths' optional. + (('manifest ('version 4 minor-version ...) + ('packages (entries ...))) + (manifest (run-with-state + (mapm %state-monad sexp->manifest-entry entries) + vlist-null))) (_ (raise (condition (&message (message "unsupported manifest format"))))))) @@ -2317,4 +2402,8 @@ PROFILE refers to, directly or indirectly, or PROFILE." %known-shorthand-profiles) profile)) +;;; Local Variables: +;;; eval: (put 'let-fields 'scheme-indent-function 2) +;;; End: + ;;; profiles.scm ends here diff --git a/tests/profiles.scm b/tests/profiles.scm index a026f6e238..f002dfc5e4 100644 --- a/tests/profiles.scm +++ b/tests/profiles.scm @@ -586,6 +586,48 @@ #:locales? #f))) (return #f))))) +(test-assertm "deduplication of repeated entries" + ;; Make sure the 'manifest' file does not duplicate identical entries. + ;; See . + (mlet* %store-monad ((p0 -> (dummy-package "p0" + (build-system trivial-build-system) + (arguments + `(#:guile ,%bootstrap-guile + #:builder (mkdir (assoc-ref %outputs "out")))) + (propagated-inputs + `(("guile" ,%bootstrap-guile))))) + (p1 -> (package + (inherit p0) + (name "p1"))) + (drv (profile-derivation (packages->manifest + (list p0 p1)) + #:hooks '() + #:locales? #f))) + (mbegin %store-monad + (built-derivations (list drv)) + (let ((file (string-append (derivation->output-path drv) + "/manifest")) + (manifest (profile-manifest (derivation->output-path drv)))) + (define (contains-repeated? sexp) + (match sexp + (('repeated _ ...) #t) + ((lst ...) (any contains-repeated? sexp)) + (_ #f))) + + (return (and (contains-repeated? (call-with-input-file file read)) + + ;; MANIFEST has two entries for %BOOTSTRAP-GUILE since + ;; it's propagated both from P0 and from P1. When + ;; reading a 'repeated' node, 'read-manifest' should + ;; reuse the previously-read entry so the two + ;; %BOOTSTRAP-GUILE entries must be 'eq?'. + (match (manifest-entries manifest) + (((= manifest-entry-dependencies (dep0)) + (= manifest-entry-dependencies (dep1))) + (and (string=? (manifest-entry-name dep0) + (package-name %bootstrap-guile)) + (eq? dep0 dep1)))))))))) + (test-assertm "no collision" ;; Here we have an entry that is "lowered" (its 'item' field is a store file ;; name) and another entry (its 'item' field is a package) that is -- cgit v1.2.3 From e7ab3d33aec3993737cdbc4396a9c44a54a3ce84 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Sun, 19 Jun 2022 15:50:50 +0200 Subject: challenge: Do nothing when passed zero arguments. Previously, 'guix challenge' without arguments would list live store items that had been locally built. This was deemed confusing, especially since 'list-live' is an expensive operation. * guix/scripts/challenge.scm (guix-challenge): Warn and exit with 0 when FILES is empty. * doc/guix.texi (Invoking guix challenge): Update accordingly. --- doc/guix.texi | 38 ++++++++++++++++++++++---------------- guix/scripts/challenge.scm | 5 +++-- 2 files changed, 25 insertions(+), 18 deletions(-) (limited to 'guix') diff --git a/doc/guix.texi b/doc/guix.texi index cf373b6cbd..9d9cb3dc07 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -14941,7 +14941,9 @@ any given store item. The command output looks like this: @smallexample -$ guix challenge --substitute-urls="https://@value{SUBSTITUTE-SERVER-1} https://guix.example.org" +$ guix challenge \ + --substitute-urls="https://@value{SUBSTITUTE-SERVER-1} https://guix.example.org" \ + openssl git pius coreutils grep updating substitutes from 'https://@value{SUBSTITUTE-SERVER-1}'... 100.0% updating substitutes from 'https://guix.example.org'... 100.0% /gnu/store/@dots{}-openssl-1.0.2d contents differ: @@ -14968,18 +14970,20 @@ updating substitutes from 'https://guix.example.org'... 100.0% @dots{} -6,406 store items were analyzed: - - 4,749 (74.1%) were identical - - 525 (8.2%) differed - - 1,132 (17.7%) were inconclusive +5 store items were analyzed: + - 2 (40.0%) were identical + - 3 (60.0%) differed + - 0 (0.0%) were inconclusive @end smallexample @noindent -In this example, @command{guix challenge} first scans the store to -determine the set of locally-built derivations---as opposed to store -items that were downloaded from a substitute server---and then queries -all the substitute servers. It then reports those store items for which -the servers obtained a result different from the local build. +In this example, @command{guix challenge} queries all the substitute +servers for each of the fives packages specified on the command line. +It then reports those store items for which the servers obtained a +result different from the local build (if it exists) and/or different +from one another; here, the @samp{local hash} lines indicate that a +local build result was available for each of these packages and shows +its hash. @cindex non-determinism, in package builds As an example, @code{guix.example.org} always gets a different answer. @@ -15035,19 +15039,21 @@ whether @code{@value{SUBSTITUTE-SERVER-1}} and other substitute servers obtain t same build result as you did with: @example -$ guix challenge @var{package} +guix challenge @var{package} @end example -@noindent -where @var{package} is a package specification such as -@code{guile@@2.0} or @code{glibc:debug}. - The general syntax is: @example -guix challenge @var{options} [@var{packages}@dots{}] +guix challenge @var{options} @var{argument}@dots{} @end example +@noindent +where @var{argument} is a package specification such as +@code{guile@@2.0} or @code{glibc:debug} or, alternatively, a store file +name as returned, for example, by @command{guix build} or @command{guix +gc --list-live}. + When a difference is found between the hash of a locally-built item and that of a server-provided substitute, or among substitutes provided by different servers, the command displays it as in the example above and diff --git a/guix/scripts/challenge.scm b/guix/scripts/challenge.scm index 5c0f837d13..f1e5f67dab 100644 --- a/guix/scripts/challenge.scm +++ b/guix/scripts/challenge.scm @@ -537,8 +537,9 @@ Challenge the substitutes for PACKAGE... provided by one or more servers.\n")) (current-terminal-columns (terminal-columns))) (let ((files (match files (() - (filter (cut locally-built? store <>) - (live-paths store))) + (warning + (G_ "no arguments specified, nothing to do~%")) + (exit 0)) (x files)))) (set-build-options store -- cgit v1.2.3 From a3d86b341d361530127c36fbfbf77d638df2c1de Mon Sep 17 00:00:00 2001 From: Hartmut Goebel Date: Sun, 19 Jun 2022 10:26:30 +0200 Subject: style: Add option '--list-stylings'. * guix/scripts/style.scm (show-stylings): New procedure. (%options, show-help): Add "--list-stylings". * doc/guix.texi (Invoking guix style): Document "-l". --- doc/guix.texi | 4 ++++ guix/scripts/style.scm | 11 +++++++++++ 2 files changed, 15 insertions(+) (limited to 'guix') diff --git a/doc/guix.texi b/doc/guix.texi index b97ee9fa64..92346ab4ba 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -13993,6 +13993,10 @@ The @option{--input-simplification} option described below provides fine-grain control over when inputs should be simplified. @end table +@item --list-stylings +@itemx -l +List and describe the available styling rules and exit. + @item --load-path=@var{directory} @itemx -L @var{directory} Add @var{directory} to the front of the package module search path diff --git a/guix/scripts/style.scm b/guix/scripts/style.scm index ca3853af5e..09937d9e02 100644 --- a/guix/scripts/style.scm +++ b/guix/scripts/style.scm @@ -798,15 +798,26 @@ PACKAGE." (lambda args (show-help) (exit 0))) + (option '(#\l "list-stylings") #f #f + (lambda args + (show-stylings) + (exit 0))) (option '(#\V "version") #f #f (lambda args (show-version-and-exit "guix style"))))) +(define (show-stylings) + (display (G_ "Available styling rules:\n")) + (display (G_ "- format: Format the given package definition(s)\n")) + (display (G_ "- inputs: Rewrite package inputs to the “new style”\n"))) + (define (show-help) (display (G_ "Usage: guix style [OPTION]... [PACKAGE]... Update package definitions to the latest style.\n")) (display (G_ " -S, --styling=RULE apply RULE, a styling rule")) + (display (G_ " + -l, --list-stylings display the list of available style rules")) (newline) (display (G_ " -n, --dry-run display files that would be edited but do nothing")) -- cgit v1.2.3 From 8d9291bd2c36810be50ea340cefa481a42c60a2b Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Mon, 4 Jul 2022 23:29:55 +0200 Subject: style: For 'let' and similar forms, emit one binding per line. Previously, 'let' bindings could be rendered like this: (let ((x 1) (y 2) (z 3)) ...) With this change, each bindings goes in its own line. Partly fixes . Reported by Maxime Devos . * guix/scripts/style.scm (pretty-print-with-comments)[list-of-lists?]: New procedure. Use it. * tests/style.scm: Add tests with 'let' and 'substitute-keyword-arguments'. --- guix/scripts/style.scm | 13 ++++++++++++- tests/style.scm | 19 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) (limited to 'guix') diff --git a/guix/scripts/style.scm b/guix/scripts/style.scm index 09937d9e02..fd5f7f5c26 100644 --- a/guix/scripts/style.scm +++ b/guix/scripts/style.scm @@ -272,6 +272,16 @@ included in the output. Lists longer than LONG-LIST are written as one element per line. Comments are passed through FORMAT-COMMENT before being emitted; a useful value for FORMAT-COMMENT is 'canonicalize-comment'." + (define (list-of-lists? head tail) + ;; Return true if HEAD and TAIL denote a list of lists--e.g., a list of + ;; 'let' bindings. + (match head + ((thing _ ...) ;proper list + (and (not (memq thing + '(quote quasiquote unquote unquote-splicing))) + (pair? tail))) + (_ #f))) + (let loop ((indent indent) (column indent) (delimited? #t) ;true if comes after a delimiter @@ -436,7 +446,8 @@ FORMAT-COMMENT is 'canonicalize-comment'." (column (if overflow? (+ indent 1) (+ column (if delimited? 1 2)))) - (newline? (newline-form? head context)) + (newline? (or (newline-form? head context) + (list-of-lists? head tail))) ;'let' bindings (context (cons head context))) (if overflow? (begin diff --git a/tests/style.scm b/tests/style.scm index 48d975df94..55bad2b3ba 100644 --- a/tests/style.scm +++ b/tests/style.scm @@ -504,6 +504,25 @@ mnopqrstuvwxyz.\")" #:make-flags #~'(\"ANSWER=42\") #:tests? #f)))") +(test-pretty-print "\ +(let ((x 1) + (y 2) + (z (let* ((a 3) + (b 4)) + (+ a b)))) + (list x y z))") + +(test-pretty-print "\ +(substitute-keyword-arguments (package-arguments x) + ((#:phases phases) + `(modify-phases ,phases + (add-before 'build 'do-things + (lambda _ + #t)))) + ((#:configure-flags flags) + `(cons \"--without-any-problem\" + ,flags)))") + (test-equal "pretty-print-with-comments, canonicalize-comment" "\ (list abc -- cgit v1.2.3 From b4c4a6acb1204ee53e95744236ee89985db32f91 Mon Sep 17 00:00:00 2001 From: Christopher Baines Date: Sat, 25 Jun 2022 18:14:07 +0100 Subject: guix: inferior: Fix the behaviour of open-inferior #:error-port. I'm looking at this as the Guix Data Service uses this behaviour to record and display logs from inferior processes. * guix/inferior.scm (open-bidirectional-pipe): Call dup2 for file descriptor 2, passing either the file number for the current error port, or a file descriptor for /dev/null. * tests/inferior.scm ("#:error-port stderr", "#:error-port pipe"): Add two new tests that cover some of the #:error-port behaviour. --- guix/inferior.scm | 12 +++++++++--- tests/inferior.scm | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 4 deletions(-) (limited to 'guix') diff --git a/guix/inferior.scm b/guix/inferior.scm index 54200b75e4..20a86bbfda 100644 --- a/guix/inferior.scm +++ b/guix/inferior.scm @@ -156,12 +156,18 @@ custom binary port)." (close-port parent) (close-fdes 0) (close-fdes 1) + (close-fdes 2) (dup2 (fileno child) 0) (dup2 (fileno child) 1) ;; Mimic 'open-pipe*'. - (unless (file-port? (current-error-port)) - (close-fdes 2) - (dup2 (open-fdes "/dev/null" O_WRONLY) 2)) + (if (file-port? (current-error-port)) + (let ((error-port-fileno + (fileno (current-error-port)))) + (unless (eq? error-port-fileno 2) + (dup2 error-port-fileno + 2))) + (dup2 (open-fdes "/dev/null" O_WRONLY) + 2)) (apply execlp command command args)) (lambda () (primitive-_exit 127)))) diff --git a/tests/inferior.scm b/tests/inferior.scm index 56b2fcb7bc..963d405e33 100644 --- a/tests/inferior.scm +++ b/tests/inferior.scm @@ -30,7 +30,8 @@ #:use-module (srfi srfi-1) #:use-module (srfi srfi-34) #:use-module (srfi srfi-64) - #:use-module (ice-9 match)) + #:use-module (ice-9 match) + #:use-module (ice-9 rdelim)) (define %top-srcdir (dirname (search-path %load-path "guix.scm"))) @@ -315,4 +316,40 @@ (close-inferior inferior) (map manifest-entry->list (manifest-entries manifest)))) +(test-equal "#:error-port stderr" + 42 + ;; There's a special case in open-bidirectional-pipe for + ;; (current-error-port) being stderr, so this test just checks that + ;; open-inferior doesn't raise an exception + (let ((inferior (open-inferior %top-builddir + #:command "scripts/guix" + #:error-port (current-error-port)))) + (and (inferior? inferior) + (inferior-eval '(display "test" (current-error-port)) inferior) + (let ((result (inferior-eval '(apply * '(6 7)) inferior))) + (close-inferior inferior) + result)))) + +(test-equal "#:error-port pipe" + "42" + (match (pipe) + ((port-to-read-from . port-to-write-to) + + (setvbuf port-to-read-from 'line) + (setvbuf port-to-write-to 'line) + + (let ((inferior (open-inferior %top-builddir + #:command "scripts/guix" + #:error-port port-to-write-to))) + (and (inferior? inferior) + (begin + (inferior-eval '(display "42\n" (current-error-port)) inferior) + + (let loop ((line (read-line port-to-read-from))) + (if (string=? line "42") + (begin + (close-inferior inferior) + line) + (loop (read-line port-to-read-from)))))))))) + (test-end "inferior") -- cgit v1.2.3 From e7e04396c0e91569bf493e1352d6539babc15327 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Fri, 8 Jul 2022 11:23:00 +0200 Subject: profiles: Remove support for reading versions 0 and 1. Version 2 was introduced in commit dedb17ad010ee9ef67f3f4f3997dd17f226c8090 (May 2015), which made it into Guix 0.9.0. * guix/profiles.scm (find-package): Remove. (sexp->manifest)[infer-search-paths]: Remove. Remove clauses for versions 0 and 1. --- guix/profiles.scm | 65 +------------------------------------------------------ 1 file changed, 1 insertion(+), 64 deletions(-) (limited to 'guix') diff --git a/guix/profiles.scm b/guix/profiles.scm index 701852ae98..a21cc432dc 100644 --- a/guix/profiles.scm +++ b/guix/profiles.scm @@ -507,34 +507,8 @@ denoting a specific output of a package." (mapm %state-monad entry->gexp entries) vlist-null)))))) -(define (find-package name version) - "Return a package from the distro matching NAME and possibly VERSION. This -procedure is here for backward-compatibility and will eventually vanish." - (define find-best-packages-by-name ;break abstractions - (module-ref (resolve-interface '(gnu packages)) - 'find-best-packages-by-name)) - - ;; Use 'find-best-packages-by-name' and not 'find-packages-by-name'; the - ;; former traverses the module tree only once and then allows for efficient - ;; access via a vhash. - (match (find-best-packages-by-name name version) - ((p _ ...) p) - (_ - (match (find-best-packages-by-name name #f) - ((p _ ...) p) - (_ #f))))) - (define (sexp->manifest sexp) "Parse SEXP as a manifest." - (define (infer-search-paths name version) - ;; Infer the search path specifications for NAME-VERSION by looking up a - ;; same-named package in the distro. Useful for the old manifest formats - ;; that did not store search path info. - (let ((package (find-package name version))) - (if package - (package-native-search-paths package) - '()))) - (define (infer-dependency item parent) ;; Return a for ITEM. (let-values (((name version) @@ -620,44 +594,7 @@ procedure is here for backward-compatibility and will eventually vanish." (return entry))))))) (match sexp - (('manifest ('version 0) - ('packages ((name version output path) ...))) - (manifest - (map (lambda (name version output path) - (manifest-entry - (name name) - (version version) - (output output) - (item path) - (search-paths (infer-search-paths name version)))) - name version output path))) - - ;; Version 1 adds a list of propagated inputs to the - ;; name/version/output/path tuples. - (('manifest ('version 1) - ('packages ((name version output path deps) ...))) - (manifest - (map (lambda (name version output path deps) - ;; Up to Guix 0.7 included, dependencies were listed as ("gmp" - ;; "/gnu/store/...-gmp") for instance. Discard the 'label' in - ;; such lists. - (let ((deps (match deps - (((labels directories) ...) - directories) - ((directories ...) - directories)))) - (letrec* ((deps* (map (cute infer-dependency <> (delay entry)) - deps)) - (entry (manifest-entry - (name name) - (version version) - (output output) - (item path) - (dependencies deps*) - (search-paths - (infer-search-paths name version))))) - entry))) - name version output path deps))) + ;; Versions 0 and 1 are no longer produced since 2015. ;; Version 2 adds search paths and is slightly more verbose. (('manifest ('version 2 minor-version ...) -- cgit v1.2.3 From 89e22887510ba5d546a4d7e391462e648942a7b6 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Fri, 8 Jul 2022 12:26:50 +0200 Subject: profiles: Support the creation of profiles with version 3 manifests. * guix/profiles.scm (%manifest-format-version): New variable. (manifest->gexp): Add optional 'format-version' parameter. [optional, entry->gexp]: Honor it. (profile-derivation): Add #:format-version parameter and honor it. ()[format-version]: New field. (profile-compiler): Honor it. * guix/build/profiles.scm (manifest-sexp->inputs+search-paths): Support both versions 3 and 4. Remove unused 'properties' variable. * tests/profiles.scm ("profile-derivation format version 3"): New test. --- guix/build/profiles.scm | 6 +++--- guix/profiles.scm | 48 +++++++++++++++++++++++++++++++++++------------- tests/profiles.scm | 28 ++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 16 deletions(-) (limited to 'guix') diff --git a/guix/build/profiles.scm b/guix/build/profiles.scm index 2ab76bde74..0c92f222b4 100644 --- a/guix/build/profiles.scm +++ b/guix/build/profiles.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2015, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès +;;; Copyright © 2015, 2017-2022 Ludovic Courtès ;;; ;;; This file is part of GNU Guix. ;;; @@ -162,7 +162,7 @@ search path specifications." (begin body ...)))) (match manifest ;this must match 'manifest->gexp' - (('manifest ('version 4) + (('manifest ('version (or 3 4)) ('packages (entries ...))) (let loop ((entries entries) (inputs '()) @@ -170,7 +170,7 @@ search path specifications." (match entries (((name version output item fields ...) . rest) (let ((paths search-paths)) - (let-fields fields (propagated-inputs search-paths properties) + (let-fields fields (propagated-inputs search-paths) (loop (append rest propagated-inputs) ;breadth-first traversal (cons item inputs) (append search-paths paths))))) diff --git a/guix/profiles.scm b/guix/profiles.scm index a21cc432dc..d1dfa13e98 100644 --- a/guix/profiles.scm +++ b/guix/profiles.scm @@ -452,12 +452,23 @@ denoting a specific output of a package." packages) manifest-entry=?))) -(define (manifest->gexp manifest) - "Return a representation of MANIFEST as a gexp." +(define %manifest-format-version + ;; The current manifest format version. + 4) + +(define* (manifest->gexp manifest #:optional + (format-version %manifest-format-version)) + "Return a representation in FORMAT-VERSION of MANIFEST as a gexp." (define (optional name value) - (if (null? value) - #~() - #~((#$name #$value)))) + (match format-version + (4 + (if (null? value) + #~() + #~((#$name #$value)))) + (3 + (match name + ('properties #~((#$name #$@value))) + (_ #~((#$name #$value))))))) (define (entry->gexp entry) ;; Maintain in state monad a vhash of visited entries, indexed by their @@ -467,10 +478,11 @@ denoting a specific output of a package." ;; the presence of propagated inputs, where we could otherwise end up ;; repeating large trees. (mlet %state-monad ((visited (current-state))) - (if (match (vhash-assq (manifest-entry-item entry) visited) - ((_ . previous-entry) - (manifest-entry=? previous-entry entry)) - (#f #f)) + (if (and (= format-version 4) + (match (vhash-assq (manifest-entry-item entry) visited) + ((_ . previous-entry) + (manifest-entry=? previous-entry entry)) + (#f #f))) (return #~(repeated #$(manifest-entry-name entry) #$(manifest-entry-version entry) (ungexp (manifest-entry-item entry) @@ -500,9 +512,14 @@ denoting a specific output of a package." search-paths)) #$@(optional 'properties properties)))))))))) + (unless (memq format-version '(3 4)) + (raise (formatted-message + (G_ "cannot emit manifests formatted as version ~a") + format-version))) + (match manifest (($ (entries ...)) - #~(manifest (version 4) + #~(manifest (version #$format-version) (packages #$(run-with-state (mapm %state-monad entry->gexp entries) vlist-null)))))) @@ -1883,6 +1900,7 @@ MANIFEST." (allow-unsupported-packages? #f) (allow-collisions? #f) (relative-symlinks? #f) + (format-version %manifest-format-version) system target) "Return a derivation that builds a profile (aka. 'user environment') with the given MANIFEST. The profile includes additional derivations returned by @@ -1968,7 +1986,7 @@ are cross-built for TARGET." #+(if locales? set-utf8-locale #t) - (build-profile #$output '#$(manifest->gexp manifest) + (build-profile #$output '#$(manifest->gexp manifest format-version) #:extra-inputs '#$extra-inputs #:symlink #$(if relative-symlinks? #~symlink-relative @@ -2007,19 +2025,23 @@ are cross-built for TARGET." (allow-collisions? profile-allow-collisions? ;Boolean (default #f)) (relative-symlinks? profile-relative-symlinks? ;Boolean - (default #f))) + (default #f)) + (format-version profile-format-version ;integer + (default %manifest-format-version))) (define-gexp-compiler (profile-compiler (profile ) system target) "Compile PROFILE to a derivation." (match profile (($ name manifest hooks - locales? allow-collisions? relative-symlinks?) + locales? allow-collisions? relative-symlinks? + format-version) (profile-derivation manifest #:name name #:hooks hooks #:locales? locales? #:allow-collisions? allow-collisions? #:relative-symlinks? relative-symlinks? + #:format-version format-version #:system system #:target target)))) (define* (profile-search-paths profile diff --git a/tests/profiles.scm b/tests/profiles.scm index f002dfc5e4..7bed946bf3 100644 --- a/tests/profiles.scm +++ b/tests/profiles.scm @@ -286,6 +286,34 @@ (string=? (dirname (readlink bindir)) (derivation->output-path guile)))))) +(test-assertm "profile-derivation format version 3" + ;; Make sure we can create and read a version 3 manifest. + (mlet* %store-monad + ((entry -> (package->manifest-entry %bootstrap-guile + #:properties '((answer . 42)))) + (manifest -> (manifest (list entry))) + (drv1 (profile-derivation manifest + #:format-version 3 ;old version + #:hooks '() + #:locales? #f)) + (drv2 (profile-derivation manifest + #:hooks '() + #:locales? #f)) + (profile1 -> (derivation->output-path drv1)) + (profile2 -> (derivation->output-path drv2)) + (_ (built-derivations (list drv1 drv2)))) + (return (let ((manifest1 (profile-manifest profile1)) + (manifest2 (profile-manifest profile2))) + (match (manifest-entries manifest1) + ((entry1) + (match (manifest-entries manifest2) + ((entry2) + (and (manifest-entry=? entry1 entry2) + (equal? (manifest-entry-properties entry1) + '((answer . 42))) + (equal? (manifest-entry-properties entry2) + '((answer . 42)))))))))))) + (test-assertm "profile-derivation, ordering & collisions" ;; ENTRY1 and ENTRY2 both provide 'bin/guile'--a collision. Make sure ;; ENTRY1 "wins" over ENTRY2. See . -- cgit v1.2.3 From c9fbd40785a99e13a59d8e530830ce85220a9871 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Fri, 8 Jul 2022 12:31:25 +0200 Subject: channels: Emit version 3 profiles. Fixes . Reported by zimoun . Fixes a bug introduced in 4ff12d1de7cd617b791996ee7ca1240660b4c20e with version 4 of the manifest format. A new 'guix time-machine' would create a v4 manifest; when targeting an old revision (v3), 'generate-package-cache' would fail to read that manifest and abort. Furthermore, an old Guix living in a new profile with a v4 manifest would be unable to describe itself via (guix describe). * guix/channels.scm (package-cache-file): Add 'format-version' field to PROFILE. (channel-instances->derivation): Pass #:format-version to 'profile-derivation'. --- guix/channels.scm | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'guix') diff --git a/guix/channels.scm b/guix/channels.scm index ce1a60436f..689b30e0eb 100644 --- a/guix/channels.scm +++ b/guix/channels.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2018, 2019, 2020, 2021 Ludovic Courtès +;;; Copyright © 2018-2022 Ludovic Courtès ;;; Copyright © 2018 Ricardo Wurmus ;;; Copyright © 2019 Jan (janneke) Nieuwenhuizen ;;; Copyright © 2021 Brice Waegeneire @@ -896,7 +896,12 @@ specified." (define (package-cache-file manifest) "Build a package cache file for the instance in MANIFEST. This is meant to be used as a profile hook." - (let ((profile (profile (content manifest) (hooks '())))) + ;; Note: Emit a profile in format version 3, which was introduced in 2017 + ;; and is readable by Guix since before version 1.0. This ensures that the + ;; Guix in MANIFEST is able to read the manifest file created for its own + ;; profile below. See . + (let ((profile (profile (content manifest) (hooks '()) + (format-version 3)))) (define build #~(begin (use-modules (gnu packages)) @@ -937,8 +942,12 @@ be used as a profile hook." "Return the derivation of the profile containing INSTANCES, a list of channel instances." (mlet %store-monad ((manifest (channel-instances->manifest instances))) + ;; Emit a profile in format version so that, if INSTANCES denotes an old + ;; Guix, it can still read that profile, for instance for the purposes of + ;; 'guix describe'. (profile-derivation manifest - #:hooks %channel-profile-hooks))) + #:hooks %channel-profile-hooks + #:format-version 3))) (define latest-channel-instances* (store-lift latest-channel-instances)) -- cgit v1.2.3 From 9fdc4b6c283c5aa5cf10205d87fb2c58b829b9d0 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Sun, 10 Jul 2022 12:39:44 +0200 Subject: monads: Add 'mparameterize'. * etc/system-tests.scm (mparameterize): Move to... * guix/monads.scm (mparameterize): ... here. * tests/monads.scm ("mparameterize"): New test. * .dir-locals.el (c-mode): Add it. --- .dir-locals.el | 1 + etc/system-tests.scm | 15 --------------- guix/monads.scm | 18 +++++++++++++++++- tests/monads.scm | 15 ++++++++++++++- 4 files changed, 32 insertions(+), 17 deletions(-) (limited to 'guix') diff --git a/.dir-locals.el b/.dir-locals.el index 565f7c48e7..e4c1da8026 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -118,6 +118,7 @@ (eval . (put 'munless 'scheme-indent-function 1)) (eval . (put 'mlet* 'scheme-indent-function 2)) (eval . (put 'mlet 'scheme-indent-function 2)) + (eval . (put 'mparameterize 'scheme-indent-function 2)) (eval . (put 'run-with-store 'scheme-indent-function 1)) (eval . (put 'run-with-state 'scheme-indent-function 1)) (eval . (put 'wrap-program 'scheme-indent-function 1)) diff --git a/etc/system-tests.scm b/etc/system-tests.scm index de6f592dee..cd22b7e6d3 100644 --- a/etc/system-tests.scm +++ b/etc/system-tests.scm @@ -43,21 +43,6 @@ determined." (repository-close! repository)) #f)))) -(define-syntax mparameterize - (syntax-rules () - "This form implements dynamic scoping, similar to 'parameterize', but in a -monadic context." - ((_ monad ((parameter value) rest ...) body ...) - (let ((old-value (parameter))) - (mbegin monad - ;; XXX: Non-local exits are not correctly handled. - (return (parameter value)) - (mlet monad ((result (mparameterize monad (rest ...) body ...))) - (parameter old-value) - (return result))))) - ((_ monad () body ...) - (mbegin monad body ...)))) - (define (tests-for-current-guix source commit) "Return a list of tests for perform, using Guix built from SOURCE, a channel instance." diff --git a/guix/monads.scm b/guix/monads.scm index 6ae616aca9..0bd8ac9315 100644 --- a/guix/monads.scm +++ b/guix/monads.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2013, 2014, 2015, 2017 Ludovic Courtès +;;; Copyright © 2013, 2014, 2015, 2017, 2022 Ludovic Courtès ;;; ;;; This file is part of GNU Guix. ;;; @@ -40,6 +40,7 @@ mbegin mwhen munless + mparameterize lift0 lift1 lift2 lift3 lift4 lift5 lift6 lift7 lift listm foldm @@ -398,6 +399,21 @@ expression." (mbegin %current-monad mexp0 mexp* ...))))) +(define-syntax mparameterize + (syntax-rules () + "This form implements dynamic scoping, similar to 'parameterize', but in a +monadic context." + ((_ monad ((parameter value) rest ...) body ...) + (let ((old-value (parameter))) + (mbegin monad + ;; XXX: Non-local exits are not correctly handled. + (return (parameter value)) + (mlet monad ((result (mparameterize monad (rest ...) body ...))) + (parameter old-value) + (return result))))) + ((_ monad () body ...) + (mbegin monad body ...)))) + (define-syntax define-lift (syntax-rules () ((_ liftn (args ...)) diff --git a/tests/monads.scm b/tests/monads.scm index 18bf4119be..19b74f4fb9 100644 --- a/tests/monads.scm +++ b/tests/monads.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2013, 2014, 2015, 2016 Ludovic Courtès +;;; Copyright © 2013, 2014, 2015, 2016, 2022 Ludovic Courtès ;;; ;;; This file is part of GNU Guix. ;;; @@ -137,6 +137,19 @@ %monads %monad-run)) +(test-assert "mparameterize" + (let ((parameter (make-parameter 'outside))) + (every (lambda (monad run) + (equal? + (run (mlet monad ((outer (return (parameter))) + (inner + (mparameterize monad ((parameter 'inside)) + (return (parameter))))) + (return (list outer inner (parameter))))) + '(outside inside outside))) + %monads + %monad-run))) + (test-assert "mlet* + text-file + package-file" (run-with-store %store (mlet* %store-monad ((guile (package-file %bootstrap-guile "bin/guile")) -- cgit v1.2.3 From e8cd9e3d12c8c24b7c81e8787207f21e277da540 Mon Sep 17 00:00:00 2001 From: Hartmut Goebel Date: Wed, 29 Jun 2022 12:54:38 +0200 Subject: import: github: Use correct URL scheme. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This effects packages hosted at github with source-uri like …/releases/download/REPO-VERSION/REPO-VERSION.EXT. E.g. ‘guix refresh udisks’ would fail to find the new release of ‘udisks’ before this change. * guix/import/github.scm(updated-url): For one one of the cases add missing 'prefix' and set new version. --- guix/import/github.scm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'guix') diff --git a/guix/import/github.scm b/guix/import/github.scm index 51118d1d39..e1a1af7133 100644 --- a/guix/import/github.scm +++ b/guix/import/github.scm @@ -5,6 +5,7 @@ ;;; Copyright © 2019 Arun Isaac ;;; Copyright © 2019 Efraim Flashner ;;; Copyright © 2022 Maxime Devos +;;; Copyright © 2022 Hartmut Goebel ;;; ;;; This file is part of GNU Guix. ;;; @@ -95,8 +96,8 @@ false if none is recognized" ((string-suffix? (string-append "/releases/download/" repo "-" version "/" repo "-" version ext) url) - (string-append "/releases/download/" repo "-" version "/" repo "-" - version ext)) + (string-append prefix "/releases/download/" repo "-" new-version "/" + repo "-" new-version ext)) (#t #f))) ; Some URLs are not recognised. #f)) -- cgit v1.2.3 From b8b02f4de5136d4385f36a79e727c90cb0ba1951 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Mon, 11 Jul 2022 23:02:48 +0200 Subject: style: Gracefully handle failure to locate a source file. * guix/scripts/style.scm (absolute-location): Raise an error when 'search-path' returns #f. --- guix/scripts/style.scm | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'guix') diff --git a/guix/scripts/style.scm b/guix/scripts/style.scm index fd5f7f5c26..9fd652beb1 100644 --- a/guix/scripts/style.scm +++ b/guix/scripts/style.scm @@ -44,6 +44,7 @@ #:use-module (srfi srfi-1) #:use-module (srfi srfi-9) #:use-module (srfi srfi-26) + #:use-module (srfi srfi-34) #:use-module (srfi srfi-37) #:export (pretty-print-with-comments read-with-comments @@ -683,7 +684,16 @@ doing it." "Replace the file name in LOC by an absolute location." (location (if (string-prefix? "/" (location-file loc)) (location-file loc) - (search-path %load-path (location-file loc))) + + ;; 'search-path' might return #f in obscure cases, such as + ;; when %LOAD-PATH includes "." or ".." and LOC comes from a + ;; file in a subdirectory thereof. + (match (search-path %load-path (location-file loc)) + (#f + (raise (formatted-message + (G_ "file '~a' not found on load path") + (location-file loc)))) + (str str))) (location-line loc) (location-column loc))) -- cgit v1.2.3 From 30915a7419d48c6a5dcfdc3a1547268ac406a9ef Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Fri, 15 Jul 2022 12:45:32 +0200 Subject: shell: Ignore cached profiles when using '--export-manifest'. Fixes . Fixes a bug where "guix shell -D pkg --export-manifest" would provide the expansion of PKG's dependencies instead of a call to 'package-development-manifest' if that profile happened to be cached. * guix/scripts/shell.scm (profile-cached-gc-root): Add clause for 'export-manifest?. --- guix/scripts/shell.scm | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'guix') diff --git a/guix/scripts/shell.scm b/guix/scripts/shell.scm index 004ed7af2e..c115a00320 100644 --- a/guix/scripts/shell.scm +++ b/guix/scripts/shell.scm @@ -390,6 +390,11 @@ return #f and #f." ;; If the user already specified a profile, there's nothing more to ;; cache. (values #f #f)) + ((('export-manifest? . #t) . _) + ;; When exporting a manifest, compute it anew so that '-D' packages + ;; lead to 'package-development-manifest' expressions rather than an + ;; expanded list of inputs. + (values #f #f)) ((('system . system) . rest) (loop rest system file specs)) ((_ . rest) (loop rest system file specs))))) -- cgit v1.2.3 From 4ce7f1fb24a111f3e92d5b889d1271bebf109d09 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Fri, 15 Jul 2022 17:27:08 +0200 Subject: monad-repl: Add "build", "lower", and "verbosity" commands. Fixes . Reported by Maxime Devos . * guix/monad-repl.scm (%build-verbosity): New variable. (evaluate/print-with-store): New procedure. (run-in-store): Rewrite in terms of 'evaluate/print-with-store'. (verbosity, lower, build): New meta-commands. * doc/guix.texi (Using Guix Interactively): New node. (The Store Monad): Link to it. (Invoking guix repl): Likewise. * doc/contributing.texi (Running Guix Before It Is Installed): Refer to it. (The Perfect Setup): Suggest 'guix install' rather than 'guix package -i'. --- doc/contributing.texi | 5 +- doc/guix.texi | 137 ++++++++++++++++++++++++++++++++++++++++++++++++-- guix/monad-repl.scm | 64 ++++++++++++++++++++--- 3 files changed, 192 insertions(+), 14 deletions(-) (limited to 'guix') diff --git a/doc/contributing.texi b/doc/contributing.texi index 6a2564b07d..ad312ddeb6 100644 --- a/doc/contributing.texi +++ b/doc/contributing.texi @@ -225,8 +225,7 @@ $ ./pre-inst-env guile -c '(use-modules (guix utils)) (pk (%current-system))' @noindent @cindex REPL @cindex read-eval-print loop -@dots{} and for a REPL (@pxref{Using Guile Interactively,,, guile, Guile -Reference Manual}): +@dots{} and for a REPL (@pxref{Using Guix Interactively}): @example $ ./pre-inst-env guile @@ -292,7 +291,7 @@ Manual}). First, you need more than an editor, you need wonderful @url{https://nongnu.org/geiser/, Geiser}. To set that up, run: @example -guix package -i emacs guile emacs-geiser emacs-geiser-guile +guix install emacs guile emacs-geiser emacs-geiser-guile @end example Geiser allows for interactive and incremental development from within diff --git a/doc/guix.texi b/doc/guix.texi index 8b09bcd4eb..8fc8f53d0e 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -299,6 +299,7 @@ Programming Interface * The Store Monad:: Purely functional interface to the store. * G-Expressions:: Manipulating build expressions. * Invoking guix repl:: Programming Guix in Guile. +* Using Guix Interactively:: Fine-grain interaction at the REPL. Defining Packages @@ -7100,6 +7101,7 @@ package definitions. * The Store Monad:: Purely functional interface to the store. * G-Expressions:: Manipulating build expressions. * Invoking guix repl:: Programming Guix in Guile +* Using Guix Interactively:: Fine-grain interaction at the REPL. @end menu @node Package Modules @@ -10860,8 +10862,9 @@ So, to exit the monad and get the desired effect, one must use @end lisp Note that the @code{(guix monad-repl)} module extends the Guile REPL with -new ``meta-commands'' to make it easier to deal with monadic procedures: -@code{run-in-store}, and @code{enter-store-monad}. The former is used +new ``commands'' to make it easier to deal with monadic procedures: +@code{run-in-store}, and @code{enter-store-monad} (@pxref{Using Guix +Interactively}). The former is used to ``run'' a single monadic value through the store: @example @@ -10886,6 +10889,9 @@ scheme@@(guile-user)> Note that non-monadic values cannot be returned in the @code{store-monad} REPL. +Other meta-commands are available at the REPL, such as @code{,build} to +build a file-like object (@pxref{Using Guix Interactively}). + The main syntactic forms to deal with monads in general are provided by the @code{(guix monads)} module and are described below. @@ -11778,7 +11784,8 @@ lines at the top of the script: @code{!#} @end example -Without a file name argument, a Guile REPL is started: +Without a file name argument, a Guile REPL is started, allowing for +interactive use (@pxref{Using Guix Interactively}): @example $ guix repl @@ -11834,6 +11841,130 @@ Inhibit loading of the @file{~/.guile} file. By default, that configuration file is loaded when spawning a @code{guile} REPL. @end table +@node Using Guix Interactively +@section Using Guix Interactively + +The @command{guix repl} command gives you access to a warm and friendly +@dfn{read-eval-print loop} (REPL) (@pxref{Invoking guix repl}). If +you're getting into Guix programming---defining your own packages, +writing manifests, defining services for Guix System or Guix Home, +etc.---you will surely find it convenient to toy with ideas at the REPL. + +If you use Emacs, the most convenient way to do that is with Geiser +(@pxref{The Perfect Setup}), but you do not have to use Emacs to enjoy +the REPL@. When using @command{guix repl} or @command{guile} in the +terminal, we recommend using Readline for completion and Colorized to +get colorful output. To do that, you can run: + +@example +guix install guile guile-readline guile-colorized +@end example + +@noindent +... and then create a @file{.guile} in your home directory containing +this: + +@lisp +(use-modules (ice-9 readline) (ice-9 colorized)) + +(activate-readline) +(activate-colorized) +@end lisp + +The REPL lets you evaluate Scheme code; you type a Scheme expression at +the prompt, and the REPL prints what it evaluates to: + +@example +$ guix repl +scheme@@(guix-user)> (+ 2 3) +$1 = 5 +scheme@@(guix-user)> (string-append "a" "b") +$2 = "ab" +@end example + +It becomes interesting when you start fiddling with Guix at the REPL. +The first thing you'll want to do is to ``import'' the @code{(guix)} +module, which gives access to the main part of the programming +interface, and perhaps a bunch of useful Guix modules. You could type +@code{(use-modules (guix))}, which is valid Scheme code to import a +module (@pxref{Using Guile Modules,,, guile, GNU Guile Reference +Manual}), but the REPL provides the @code{use} @dfn{command} as a +shorthand notation (@pxref{REPL Commands,,, guile, GNU Guile Reference +Manual}): + +@example +scheme@@(guix-user)> ,use (guix) +scheme@@(guix-user)> ,use (gnu packages base) +@end example + +Notice that REPL commands are introduced by a leading comma. A REPL +command like @code{use} is not valid Scheme code; it's interpreted +specially by the REPL. + +Guix extends the Guile REPL with additional commands for convenience. +Among those, the @code{build} command comes in handy: it ensures that +the given file-like object is built, building it if needed, and returns +its output file name(s). In the example below, we build the +@code{coreutils} and @code{grep} packages, as well as a ``computed +file'' (@pxref{G-Expressions, @code{computed-file}}), and we use the +@code{scandir} procedure to list the files in Grep's @code{/bin} +directory: + +@example +scheme@@(guix-user)> ,build coreutils +$1 = "/gnu/store/@dots{}-coreutils-8.32-debug" +$2 = "/gnu/store/@dots{}-coreutils-8.32" +scheme@@(guix-user)> ,build grep +$3 = "/gnu/store/@dots{}-grep-3.6" +scheme@@(guix-user)> ,build (computed-file "x" #~(mkdir #$output)) +building /gnu/store/@dots{}-x.drv... +$4 = "/gnu/store/@dots{}-x" +scheme@@(guix-user)> ,use(ice-9 ftw) +scheme@@(guix-user)> (scandir (string-append $3 "/bin")) +$5 = ("." ".." "egrep" "fgrep" "grep") +@end example + +At a lower-level, a useful command is @code{lower}: it takes a file-like +object and ``lowers'' it into a derivation (@pxref{Derivations}) or a +store file: + +@example +scheme@@(guix-user)> ,lower grep +$6 = # /gnu/store/@dots{}-grep-3.6 7f0e639115f0> +scheme@@(guix-user)> ,lower (plain-file "x" "Hello!") +$7 = "/gnu/store/@dots{}-x" +@end example + +The full list of REPL commands can be seen by typing @code{,help guix} +and is given below for reference. + +@deffn {REPL command} build @var{object} +Lower @var{object} and build it if it's not already built, returning its +output file name(s). +@end deffn + +@deffn {REPL command} lower @var{object} +Lower @var{object} into a derivation or store file name and return it. +@end deffn + +@deffn {REPL command} verbosity @var{level} +Change build verbosity to @var{level}. + +This is similar to the @option{--verbosity} command-line option +(@pxref{Common Build Options}): level 0 means total silence, level 1 +shows build events only, and higher levels print build logs. +@end deffn + +@deffn {REPL command} run-in-store @var{exp} +Run @var{exp}, a monadic expresssion, through the store monad. +@xref{The Store Monad}, for more information. +@end deffn + +@deffn {REPL command} enter-store-monad +Enter a new REPL to evaluate monadic expressions (@pxref{The Store +Monad}). You can quit this ``inner'' REPL by typing @code{,q}. +@end deffn + @c ********************************************************************* @node Utilities @chapter Utilities diff --git a/guix/monad-repl.scm b/guix/monad-repl.scm index aefabdeebb..8a6053edd5 100644 --- a/guix/monad-repl.scm +++ b/guix/monad-repl.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2014, 2015, 2016 Ludovic Courtès +;;; Copyright © 2014, 2015, 2016, 2022 Ludovic Courtès ;;; ;;; This file is part of GNU Guix. ;;; @@ -21,6 +21,12 @@ #:use-module (guix monads) #:use-module (guix utils) #:use-module (guix packages) + #:use-module (guix status) + #:autoload (guix gexp) (lower-object) + #:use-module ((guix derivations) + #:select (derivation? + derivation->output-paths built-derivations)) + #:use-module (ice-9 match) #:use-module (ice-9 pretty-print) #:use-module (system repl repl) #:use-module (system repl common) @@ -69,16 +75,58 @@ #:guile-for-build guile) 'store-monad))) +(define %build-verbosity + ;; Current build verbosity level. + 1) + +(define* (evaluate/print-with-store mvalue #:key build?) + "Run monadic value MVALUE in the store monad and print its value." + (with-store store + (set-build-options store + #:print-build-trace #t + #:print-extended-build-trace? #t + #:multiplexed-build-output? #t) + (with-status-verbosity %build-verbosity + (let* ((guile (or (%guile-for-build) + (default-guile-derivation store))) + (values (run-with-store store + (if build? + (mlet %store-monad ((obj mvalue)) + (if (derivation? obj) + (mbegin %store-monad + (built-derivations (list obj)) + (return + (match (derivation->output-paths obj) + (((_ . files) ...) files)))) + (return (list obj)))) + (mlet %store-monad ((obj mvalue)) + (return (list obj)))) + #:guile-for-build guile))) + (for-each (lambda (value) + (run-hook before-print-hook value) + (pretty-print value)) + values))))) + (define-meta-command ((run-in-store guix) repl (form)) "run-in-store EXP Run EXP through the store monad." - (with-store store - (let* ((guile (or (%guile-for-build) - (default-guile-derivation store))) - (value (run-with-store store (repl-eval repl form) - #:guile-for-build guile))) - (run-hook before-print-hook value) - (pretty-print value)))) + (evaluate/print-with-store (repl-eval repl form))) + +(define-meta-command ((verbosity guix) repl (level)) + "verbosity LEVEL +Change build verbosity to LEVEL." + (set! %build-verbosity (repl-eval repl level))) + +(define-meta-command ((lower guix) repl (form)) + "lower OBJECT +Lower OBJECT into a derivation or store file and return it." + (evaluate/print-with-store (lower-object (repl-eval repl form)))) + +(define-meta-command ((build guix) repl (form)) + "build OBJECT +Lower OBJECT and build it, returning its output file name(s)." + (evaluate/print-with-store (lower-object (repl-eval repl form)) + #:build? #t)) (define-meta-command ((enter-store-monad guix) repl) "enter-store-monad -- cgit v1.2.3 From af025d99f4cd9ab6643f63f587faf0ba2d65d862 Mon Sep 17 00:00:00 2001 From: Hartmut Goebel Date: Tue, 28 Jun 2022 21:01:11 +0200 Subject: import: egg: Fix updater. 'egg-source-url' did not return the URL, but the quoted expression. This did break the updater, which expects the URL as a string. * guix/import/egg.scm(egg-source-url): Remove. (egg->guix-package)[egg-content]: Use quoted expression directly. (latest-release): Call egg-uri instead of egg-source-url. --- guix/import/egg.scm | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'guix') diff --git a/guix/import/egg.scm b/guix/import/egg.scm index 0b88020554..52196583c4 100644 --- a/guix/import/egg.scm +++ b/guix/import/egg.scm @@ -85,11 +85,6 @@ (define %eggs-home-page (make-parameter "https://wiki.call-cc.org/egg")) -(define (egg-source-url name version) - "Return the URL to the source tarball for version VERSION of the CHICKEN egg -NAME." - `(egg-uri ,name version)) - (define (egg-name->guix-name name) "Return the package name for CHICKEN egg NAME." (string-append package-name-prefix name)) @@ -196,7 +191,7 @@ not work." (let* ((version* (or (assoc-ref egg-content 'version) (find-latest-version name))) (version (if (list? version*) (first version*) version*)) - (source-url (if source #f (egg-source-url name version))) + (source-url (if source #f `(egg-uri ,name version))) (tarball (if source #f (with-store store @@ -342,7 +337,7 @@ not work." "Return an @code{} for the latest release of PACKAGE." (let* ((egg-name (guix-package->egg-name package)) (version (find-latest-version egg-name)) - (source-url (egg-source-url egg-name version))) + (source-url (egg-uri egg-name version))) (upstream-source (package (package-name package)) (version version) -- cgit v1.2.3 From 2e0b7867fe89fcfb0523a85635ecc3e1f9484fcd Mon Sep 17 00:00:00 2001 From: Hartmut Goebel Date: Thu, 7 Jul 2022 11:28:45 +0200 Subject: import: pypi: Add special treatment for Tryton package names, MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trytond modules are Python packages, and treated like this in guix. Anyhow, since they are add-ons for the “Trytond“ application, their guix package name do not get the "python-" prefix like other Python modules, (see also https://issues.guix.gnu.org/46057#1). This change disables adding the "python-" prefix to the guix package name for Trytond modules when importing and updating, thus inhibiting irritating messages like in this example: $ guix refresh -u trytond-party … trytond-party: consider adding this propagated input: python-trytond-country trytond-party: consider removing this propagated input: trytond-country Handling this special case seems appropriate since (as of now) there are about 165 packages for Trytond and the number is growing. * guix/import/pypi.scm(python->package-name): Don't add "python-" prefix for trytond packages. --- guix/import/pypi.scm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'guix') diff --git a/guix/import/pypi.scm b/guix/import/pypi.scm index 392fc9700b..2cb270620e 100644 --- a/guix/import/pypi.scm +++ b/guix/import/pypi.scm @@ -161,9 +161,11 @@ or #f if there isn't any." (define (python->package-name name) "Given the NAME of a package on PyPI, return a Guix-compliant name for the package." - (if (string-prefix? "python-" name) - (snake-case name) - (string-append "python-" (snake-case name)))) + (cond + ((string-prefix? "python-" name) (snake-case name)) + ((or (string=? "trytond" name) + (string-prefix? "trytond-" name)) (snake-case name)) + (#t (string-append "python-" (snake-case name))))) (define (guix-package->pypi-name package) "Given a Python PACKAGE built from pypi.org, return the name of the -- cgit v1.2.3 From bf82f7cbe3067ea6a638655b65f8cfff7b7fd940 Mon Sep 17 00:00:00 2001 From: Hartmut Goebel Date: Fri, 15 Jul 2022 21:19:08 +0200 Subject: import: pypi: Fix coding style. I missed this remark from the review when pushing the last change. * guix/import/pypi.scm(python->package-name): Replace the trailing '#t'-case by a 'else'. --- guix/import/pypi.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'guix') diff --git a/guix/import/pypi.scm b/guix/import/pypi.scm index 2cb270620e..4760fc3dae 100644 --- a/guix/import/pypi.scm +++ b/guix/import/pypi.scm @@ -165,7 +165,7 @@ package." ((string-prefix? "python-" name) (snake-case name)) ((or (string=? "trytond" name) (string-prefix? "trytond-" name)) (snake-case name)) - (#t (string-append "python-" (snake-case name))))) + (else (string-append "python-" (snake-case name))))) (define (guix-package->pypi-name package) "Given a Python PACKAGE built from pypi.org, return the name of the -- cgit v1.2.3 From e87c6fb95a2898df3eb5b557407a4504977182da Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Mon, 18 Jul 2022 13:16:04 +0200 Subject: upstream: Sort '%updaters' alphabetically. Previously the output of 'guix refresh --list-updaters' would be non-deterministic, and likewise the order in which updaters are tried would be non-deterministic. Reported by zimoun . * guix/upstream.scm (%updaters): Add call to 'sort'. --- guix/upstream.scm | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'guix') diff --git a/guix/upstream.scm b/guix/upstream.scm index 9b49d1641f..cbfd1aa609 100644 --- a/guix/upstream.scm +++ b/guix/upstream.scm @@ -251,13 +251,17 @@ correspond to the same version." #:warn warn-about-load-error))) (define %updaters - ;; The list of publically-known updaters. - (delay (fold-module-public-variables (lambda (obj result) - (if (upstream-updater? obj) - (cons obj result) - result)) - '() - (importer-modules)))) + ;; The list of publically-known updaters, alphabetically sorted. + (delay + (sort (fold-module-public-variables (lambda (obj result) + (if (upstream-updater? obj) + (cons obj result) + result)) + '() + (importer-modules)) + (lambda (updater1 updater2) + (stringstring (upstream-updater-name updater1)) + (symbol->string (upstream-updater-name updater2))))))) ;; Tests need to mock this variable so mark it as "non-declarative". (set! %updaters %updaters) -- cgit v1.2.3 From 55725724dd0891e1e195158d0774a3f9a8619361 Mon Sep 17 00:00:00 2001 From: Antero Mejr Date: Tue, 12 Jul 2022 22:50:07 +0000 Subject: home: Add -I, --list-installed option. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * guix/scripts/package.scm (list-installed): New procedure. * guix/scripts/home.scm (%options, show-help): Add '--list-installed'. (process-command): For 'describe' and 'list-generations', honor the 'list-installed option. (display-home-environment-generation): Add #:list-installed-regex and honor it. (list-generations): Likewise. * guix/scripts/utils.scm (pretty-print-table): New argument "left-pad". * doc/guix.texi (Invoking Guix Home): Add information and example for --list-installed flag. Co-authored-by: Ludovic Courtès --- doc/guix.texi | 18 +++++++++++++- guix/scripts/home.scm | 64 ++++++++++++++++++++++++++++++++++-------------- guix/scripts/package.scm | 33 +++++++++++++++---------- guix/utils.scm | 6 ++--- 4 files changed, 85 insertions(+), 36 deletions(-) (limited to 'guix') diff --git a/doc/guix.texi b/doc/guix.texi index b47a0c17e8..c348760dae 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -40495,6 +40495,17 @@ install anything. Describe the current home generation: its file name, as well as provenance information when available. +To show installed packages in the current home generation's profile, the +@code{--list-installed} flag is provided, with the same syntax that is +used in @command{guix package --list-installed} (@pxref{Invoking guix +package}). For instance, the following command shows a table of all the +packages with ``emacs'' in their name that are installed in the current +home generation's profile: + +@example +guix home describe --list-installed=emacs +@end example + @item list-generations List a summary of each generation of the home environment available on disk, in a human-readable way. This is similar to the @@ -40507,9 +40518,14 @@ generations displayed. For instance, the following command displays generations that are up to 10 days old: @example -$ guix home list-generations 10d +guix home list-generations 10d @end example +The @code{--list-installed} flag may also be specified, with the same +syntax that is used in @command{guix home describe}. This may be +helpful if trying to determine when a package was added to the home +profile. + @item import Generate a @dfn{home environment} from the packages in the default profile and configuration files found in the user's home directory. The diff --git a/guix/scripts/home.scm b/guix/scripts/home.scm index 0f5c3388a1..4add7e7c69 100644 --- a/guix/scripts/home.scm +++ b/guix/scripts/home.scm @@ -4,6 +4,7 @@ ;;; Copyright © 2021 Pierre Langlois ;;; Copyright © 2021 Oleg Pykhalov ;;; Copyright © 2022 Ludovic Courtès +;;; Copyright © 2022 Antero Mejr ;;; ;;; This file is part of GNU Guix. ;;; @@ -143,6 +144,11 @@ Some ACTIONS support additional ARGS.\n")) use BACKEND for 'extension-graph' and 'shepherd-graph'")) (newline) (display (G_ " + -I, --list-installed[=REGEXP] + for 'describe' or 'list-generations', list installed + packages matching REGEXP")) + (newline) + (display (G_ " -h, --help display this help and exit")) (display (G_ " -V, --version display version information and exit")) @@ -183,6 +189,9 @@ Some ACTIONS support additional ARGS.\n")) (option '("graph-backend") #t #f (lambda (opt name arg result) (alist-cons 'graph-backend arg result))) + (option '(#\I "list-installed") #f #t + (lambda (opt name arg result) + (alist-cons 'list-installed (or arg "") result))) ;; Container options. (option '(#\N "network") #f #f @@ -569,17 +578,20 @@ Run @command{guix home reconfigure ~a/home-configuration.scm} to effectively deploy the home environment described by these files.\n") destination)))) ((describe) - (match (generation-number %guix-home) - (0 - (leave (G_ "no home environment generation, nothing to describe~%"))) - (generation - (display-home-environment-generation generation)))) + (let ((list-installed-regex (assoc-ref opts 'list-installed))) + (match (generation-number %guix-home) + (0 + (leave (G_ "no home environment generation, nothing to describe~%"))) + (generation + (display-home-environment-generation + generation #:list-installed-regex list-installed-regex))))) ((list-generations) - (let ((pattern (match args + (let ((list-installed-regex (assoc-ref opts 'list-installed)) + (pattern (match args (() #f) ((pattern) pattern) (x (leave (G_ "wrong number of arguments~%")))))) - (list-generations pattern))) + (list-generations pattern #:list-installed-regex list-installed-regex))) ((switch-generation) (let ((pattern (match args ((pattern) pattern) @@ -748,9 +760,11 @@ description matches REGEXPS sorted by relevance, and their score." (define* (display-home-environment-generation number - #:optional (profile %guix-home)) - "Display a summary of home-environment generation NUMBER in a -human-readable format." + #:optional (profile %guix-home) + #:key (list-installed-regex #f)) + "Display a summary of home-environment generation NUMBER in a human-readable +format. List packages in that home environment that match +LIST-INSTALLED-REGEX." (define (display-channel channel) (format #t " ~a:~%" (channel-name channel)) (format #t (G_ " repository URL: ~a~%") (channel-url channel)) @@ -782,24 +796,36 @@ human-readable format." (format #t (G_ " configuration file: ~a~%") (if (supports-hyperlinks?) (file-hyperlink config-file) - config-file)))))) - -(define* (list-generations pattern #:optional (profile %guix-home)) - "Display in a human-readable format all the home environment -generations matching PATTERN, a string. When PATTERN is #f, display -all the home environment generations." + config-file))) + (when list-installed-regex + (format #t (G_ " packages:\n")) + (pretty-print-table (list-installed + list-installed-regex + (list (string-append generation "/profile"))) + #:left-pad 4))))) + +(define* (list-generations pattern #:optional (profile %guix-home) + #:key (list-installed-regex #f)) + "Display in a human-readable format all the home environment generations +matching PATTERN, a string. When PATTERN is #f, display all the home +environment generations. List installed packages that match +LIST-INSTALLED-REGEX." (cond ((not (file-exists? profile)) ; XXX: race condition (raise (condition (&profile-not-found-error (profile profile))))) ((not pattern) - (for-each display-home-environment-generation (profile-generations profile))) + (for-each (cut display-home-environment-generation <> + #:list-installed-regex list-installed-regex) + (profile-generations profile))) ((matching-generations pattern profile) => (lambda (numbers) (if (null-list? numbers) (exit 1) - (leave-on-EPIPE - (for-each display-home-environment-generation numbers))))))) + (leave-on-EPIPE (for-each + (cut display-home-environment-generation <> + #:list-installed-regex list-installed-regex) + numbers))))))) ;;; diff --git a/guix/scripts/package.scm b/guix/scripts/package.scm index 99a6cfaa29..7d92598efa 100644 --- a/guix/scripts/package.scm +++ b/guix/scripts/package.scm @@ -11,6 +11,7 @@ ;;; Copyright © 2020 Simon Tournier ;;; Copyright © 2018 Steve Sprang ;;; Copyright © 2022 Josselin Poiret +;;; Copyright © 2022 Antero Mejr ;;; ;;; This file is part of GNU Guix. ;;; @@ -67,6 +68,7 @@ delete-generations delete-matching-generations guix-package + list-installed search-path-environment-variables manifest-entry-version-prefix @@ -773,6 +775,22 @@ doesn't need it." (add-indirect-root store absolute)) +(define (list-installed regexp profiles) + "Write to the current output port the list of packages matching REGEXP in +PROFILES." + (let* ((regexp (and regexp (make-regexp* regexp regexp/icase))) + (manifest (concatenate-manifests + (map profile-manifest profiles))) + (installed (manifest-entries manifest))) + (leave-on-EPIPE + (let ((rows (filter-map + (match-lambda + (($ name version output path _) + (and (regexp-exec regexp name) + (list name (or version "?") output path)))) + installed))) + rows)))) + ;;; ;;; Queries and actions. @@ -824,19 +842,8 @@ processed, #f otherwise." #t) (('list-installed regexp) - (let* ((regexp (and regexp (make-regexp* regexp regexp/icase))) - (manifest (concatenate-manifests - (map profile-manifest profiles))) - (installed (manifest-entries manifest))) - (leave-on-EPIPE - (let ((rows (filter-map - (match-lambda - (($ name version output path _) - (and (regexp-exec regexp name) - (list name (or version "?") output path)))) - installed))) - ;; Show most recently installed packages last. - (pretty-print-table (reverse rows))))) + ;; Show most recently installed packages last. + (pretty-print-table (reverse (list-installed regexp profiles))) #t) (('list-available regexp) diff --git a/guix/utils.scm b/guix/utils.scm index 745da98a79..329ef62dde 100644 --- a/guix/utils.scm +++ b/guix/utils.scm @@ -1124,11 +1124,11 @@ according to THRESHOLD, then #f is returned." ;;; Prettified output. ;;; -(define* (pretty-print-table rows #:key (max-column-width 20)) +(define* (pretty-print-table rows #:key (max-column-width 20) (left-pad 0)) "Print ROWS in neat columns. All rows should be lists of strings and each row should have the same length. The columns are separated by a tab character, and aligned using spaces. The maximum width of each column is -bound by MAX-COLUMN-WIDTH." +bound by MAX-COLUMN-WIDTH. Each row is prefixed with LEFT-PAD spaces." (let* ((number-of-columns-to-pad (if (null? rows) 0 (1- (length (first rows))))) @@ -1143,7 +1143,7 @@ bound by MAX-COLUMN-WIDTH." (map (cut min <> max-column-width) column-widths))) (fmt (string-append (string-join column-formats "\t") "\t~a"))) - (for-each (cut format #t "~?~%" fmt <>) rows))) + (for-each (cut format #t "~v_~?~%" left-pad fmt <>) rows))) ;;; Local Variables: ;;; eval: (put 'call-with-progress-reporter 'scheme-indent-function 1) -- cgit v1.2.3 From 95acd67dd3d4f1667b97561099ea66f36ee6485e Mon Sep 17 00:00:00 2001 From: Antero Mejr Date: Wed, 13 Jul 2022 15:01:22 +0000 Subject: system: Add -I, --list-installed option. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * guix/scripts/system.scm (display-system-generation): Add #:list-installed-regex and honor it. (list-generations): Likewise. (show-help, %options): Add "--list-installed". (process-command): For 'describe' and 'list-generation', honor the 'list-installed option. * doc/guix.texi (Invoking Guix System): Add information for --list-installed flag. Signed-off-by: Ludovic Courtès --- doc/guix.texi | 12 +++++++++ guix/scripts/system.scm | 67 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 59 insertions(+), 20 deletions(-) (limited to 'guix') diff --git a/doc/guix.texi b/doc/guix.texi index c348760dae..d8a3d2e90c 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -37781,6 +37781,13 @@ bootloader boot menu: Describe the running system generation: its file name, the kernel and bootloader used, etc., as well as provenance information when available. +The @code{--list-installed} flag is available, with the same +syntax that is used in @command{guix package --list-installed} +(@pxref{Invoking guix package}). When the flag is used, +the description will include a list of packages that are currently +installed in the system profile, with optional filtering based on a +regular expression. + @quotation Note The @emph{running} system generation---referred to by @file{/run/current-system}---is not necessarily the @emph{current} @@ -37808,6 +37815,11 @@ generations that are up to 10 days old: $ guix system list-generations 10d @end example +The @code{--list-installed} flag may also be specified, with the same +syntax that is used in @command{guix package --list-installed}. This +may be helpful if trying to determine when a package was added to the +system. + @end table The @command{guix system} command has even more to offer! The following diff --git a/guix/scripts/system.scm b/guix/scripts/system.scm index b9084a401c..bfde0a88ca 100644 --- a/guix/scripts/system.scm +++ b/guix/scripts/system.scm @@ -50,7 +50,8 @@ #:use-module (guix channels) #:use-module (guix scripts build) #:autoload (guix scripts package) (delete-generations - delete-matching-generations) + delete-matching-generations + list-installed) #:autoload (guix scripts pull) (channel-commit-hyperlink) #:autoload (guix graph) (export-graph node-type graph-backend-name lookup-backend) @@ -480,8 +481,10 @@ list of services." ;;; (define* (display-system-generation number - #:optional (profile %system-profile)) - "Display a summary of system generation NUMBER in a human-readable format." + #:optional (profile %system-profile) + #:key (list-installed-regex #f)) + "Display a summary of system generation NUMBER in a human-readable format. +List packages in that system that match LIST-INSTALLED-REGEX." (define (display-channel channel) (format #t " ~a:~%" (channel-name channel)) (format #t (G_ " repository URL: ~a~%") (channel-url channel)) @@ -544,23 +547,35 @@ list of services." (format #t (G_ " configuration file: ~a~%") (if (supports-hyperlinks?) (file-hyperlink config-file) - config-file)))))) - -(define* (list-generations pattern #:optional (profile %system-profile)) + config-file))) + (when list-installed-regex + (format #t (G_ " packages:\n")) + (pretty-print-table (list-installed + list-installed-regex + (list (string-append generation "/profile"))) + #:left-pad 4))))) + +(define* (list-generations pattern #:optional (profile %system-profile) + #:key (list-installed-regex #f)) "Display in a human-readable format all the system generations matching -PATTERN, a string. When PATTERN is #f, display all the system generations." +PATTERN, a string. When PATTERN is #f, display all the system generations. +List installed packages that match LIST-INSTALLED-REGEX." (cond ((not (file-exists? profile)) ; XXX: race condition (raise (condition (&profile-not-found-error (profile profile))))) ((not pattern) - (for-each display-system-generation (profile-generations profile))) + (for-each (cut display-system-generation <> + #:list-installed-regex list-installed-regex) + (profile-generations profile))) ((matching-generations pattern profile) => (lambda (numbers) (if (null-list? numbers) (exit 1) (leave-on-EPIPE - (for-each display-system-generation numbers))))))) + (for-each (cut display-system-generation <> + #:list-installed-regex list-installed-regex) + numbers))))))) ;;; @@ -1032,6 +1047,11 @@ Some ACTIONS support additional ARGS.\n")) use BACKEND for 'extension-graphs' and 'shepherd-graph'")) (newline) (display (G_ " + -I, --list-installed[=REGEXP] + for 'describe' and 'list-generations', list installed + packages matching REGEXP")) + (newline) + (display (G_ " -h, --help display this help and exit")) (display (G_ " -V, --version display version information and exit")) @@ -1135,6 +1155,9 @@ Some ACTIONS support additional ARGS.\n")) (option '("graph-backend") #t #f (lambda (opt name arg result) (alist-cons 'graph-backend arg result))) + (option '(#\I "list-installed") #f #t + (lambda (opt name arg result) + (alist-cons 'list-installed (or arg "") result))) %standard-build-options)) (define %default-options @@ -1322,25 +1345,29 @@ argument list and OPTS is the option alist." ;; The following commands do not need to use the store, and they do not need ;; an operating system configuration file. ((list-generations) - (let ((pattern (match args + (let ((list-installed-regex (assoc-ref opts 'list-installed)) + (pattern (match args (() #f) ((pattern) pattern) (x (leave (G_ "wrong number of arguments~%")))))) - (list-generations pattern))) + (list-generations pattern #:list-installed-regex list-installed-regex))) ((describe) ;; Describe the running system, which is not necessarily the current ;; generation. /run/current-system might point to ;; /var/guix/profiles/system-N-link, or it might point directly to ;; /gnu/store/…-system. Try both. - (match (generation-number "/run/current-system" %system-profile) - (0 - (match (generation-number %system-profile) - (0 - (leave (G_ "no system generation, nothing to describe~%"))) - (generation - (display-system-generation generation)))) - (generation - (display-system-generation generation)))) + (let ((list-installed-regex (assoc-ref opts 'list-installed))) + (match (generation-number "/run/current-system" %system-profile) + (0 + (match (generation-number %system-profile) + (0 + (leave (G_ "no system generation, nothing to describe~%"))) + (generation + (display-system-generation + generation #:list-installed-regex list-installed-regex)))) + (generation + (display-system-generation + generation #:list-installed-regex list-installed-regex))))) ((search) (apply (resolve-subcommand "search") args)) ((edit) -- cgit v1.2.3 From be7b314f3fe22273e935accac22f313e44d3d970 Mon Sep 17 00:00:00 2001 From: Ricardo Wurmus Date: Tue, 19 Jul 2022 23:44:11 +0200 Subject: import: Enable recursive import for texlive packages. * guix/import/texlive.scm (tlpdb->package): Add VERSION argument; include explicit version field in output. (texlive->guix-package): Set default value for VERSION argument; adjust call of tlpdb->package. (texlive-recursive-import): Accept REPO and VERSION keyword arguments. * guix/import/utils.scm (package->definition): Add a clause to deal with output from tlpdb->package. * guix/scripts/import/texlive.scm (%options): Add "recursive" option. (guix-import-texlive): Honor "recursive" option. * doc/guix.texi (Using TeX and LaTeX): Mention "recursive" option. --- doc/guix.texi | 10 ++++++++++ guix/import/texlive.scm | 20 +++++++++++++++----- guix/import/utils.scm | 2 ++ guix/scripts/import/texlive.scm | 25 +++++++++++++++++++------ 4 files changed, 46 insertions(+), 11 deletions(-) (limited to 'guix') diff --git a/doc/guix.texi b/doc/guix.texi index d8a3d2e90c..3c5864ec1a 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -40965,6 +40965,16 @@ package, you can try and import it (@pxref{Invoking guix import}): guix import texlive @var{package} @end example +Additional options include: + +@table @code +@item --recursive +@itemx -r +Traverse the dependency graph of the given upstream package recursively +and generate package expressions for all those packages that are not yet +in Guix. +@end table + @quotation Note @TeX{} Live packaging is still very much work in progress, but you can help! @xref{Contributing}, for more information. diff --git a/guix/import/texlive.scm b/guix/import/texlive.scm index c741555928..116bd1f66a 100644 --- a/guix/import/texlive.scm +++ b/guix/import/texlive.scm @@ -246,7 +246,7 @@ of those files are returned that are unexpectedly installed." ;; entries with the same prefix. (lambda (x y) (every equal? x y))))) -(define (tlpdb->package name package-database) +(define (tlpdb->package name version package-database) (and-let* ((data (assoc-ref package-database name)) (dirs (files->directories (map (lambda (dir) @@ -255,7 +255,9 @@ of those files are returned that are unexpectedly installed." (or (assoc-ref data 'runfiles) (list)) (or (assoc-ref data 'srcfiles) (list)))))) (name (guix-name name)) - (version (number->string %texlive-revision)) + ;; TODO: we're ignoring the VERSION argument because that + ;; information is distributed across %texlive-tag and + ;; %texlive-revision. (ref (svn-multi-reference (url (string-append "svn://www.tug.org/texlive/tags/" %texlive-tag "/Master/texmf-dist")) @@ -276,6 +278,9 @@ of those files are returned that are unexpectedly installed." (force-output port) (get-hash)))) ,@(if (assoc-ref data 'srcfiles) '() '(#:trivial? #true)))) + ;; package->definition in (guix import utils) expects to see a + ;; version field. + (version ,version) ,@(or (and=> (assoc-ref data 'depend) (lambda (inputs) `((propagated-inputs @@ -297,13 +302,18 @@ of those files are returned that are unexpectedly installed." (define texlive->guix-package (memoize - (lambda* (name #:key repo version (package-database tlpdb)) + (lambda* (name #:key + repo + (version (number->string %texlive-revision)) + (package-database tlpdb)) "Find the metadata for NAME in the tlpdb and return the `package' s-expression corresponding to that package, or #f on failure." - (tlpdb->package name (package-database))))) + (tlpdb->package name version (package-database))))) -(define (texlive-recursive-import name) +(define* (texlive-recursive-import name #:key repo version) (recursive-import name + #:repo repo + #:version version #:repo->guix-package texlive->guix-package #:guix-name guix-name)) diff --git a/guix/import/utils.scm b/guix/import/utils.scm index 26eebfece5..668b8c8083 100644 --- a/guix/import/utils.scm +++ b/guix/import/utils.scm @@ -341,6 +341,8 @@ APPEND-VERSION?/string is a string, append this string." (match guix-package ((or ('package ('name name) ('version version) . rest) + ('package ('inherit ('simple-texlive-package name . _)) + ('version version) . rest) ('let _ ('package ('name name) ('version version) . rest))) `(define-public ,(string->symbol diff --git a/guix/scripts/import/texlive.scm b/guix/scripts/import/texlive.scm index c5dcc07ea1..203386e31c 100644 --- a/guix/scripts/import/texlive.scm +++ b/guix/scripts/import/texlive.scm @@ -22,11 +22,13 @@ #:use-module (guix utils) #:use-module (guix scripts) #:use-module (guix import texlive) + #:use-module (guix import utils) #:use-module (guix scripts import) #:use-module (srfi srfi-1) #:use-module (srfi srfi-11) #:use-module (srfi srfi-37) #:use-module (srfi srfi-41) + #:use-module (srfi srfi-71) #:use-module (ice-9 match) #:use-module (ice-9 format) #:export (guix-import-texlive)) @@ -58,6 +60,9 @@ Import and convert the Texlive package for PACKAGE-NAME.\n")) (option '(#\V "version") #f #f (lambda args (show-version-and-exit "guix import texlive"))) + (option '(#\r "recursive") #f #f + (lambda (opt name arg result) + (alist-cons 'recursive #t result))) %standard-import-options)) @@ -78,12 +83,20 @@ Import and convert the Texlive package for PACKAGE-NAME.\n")) (_ #f)) (reverse opts)))) (match args - ((name) - (let ((sexp (texlive->guix-package name))) - (unless sexp - (leave (G_ "failed to import package '~a'~%") - name)) - sexp)) + ((spec) + (let ((name version (package-name->name+version spec))) + (if (assoc-ref opts 'recursive) + ;; Recursive import + (with-error-handling + (map package->definition + (filter identity (texlive-recursive-import name + #:version version)))) + ;; Single import + (let ((sexp (texlive->guix-package name #:version version))) + (unless sexp + (leave (G_ "failed to download description for package '~a'~%") + name)) + sexp)))) (() (leave (G_ "too few arguments~%"))) ((many ...) -- cgit v1.2.3