monad-repl: Add REPL commands to inspect package arguments.

* guix/monad-repl.scm (keyword-argument-value, package-argument-command):
New procedures.
(phases, configure-flags, make-flags): New REPL commands.
* doc/guix.texi (package Reference): Link to “Using Guix Interactively”.
(Defining Package Variants): Add “Tips” quotation.
(Build Phases): Add “Tip” quotation.
(Using Guix Interactively): Document the new REPL commands.

Change-Id: I7049c1d8aa9241e07d7c921aa396e578a1b4ef16
This commit is contained in:
Ludovic Courtès 2023-11-27 18:09:23 +01:00 committed by Ludovic Courtès
parent 01361d46b8
commit 3178b1a442
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5
2 changed files with 131 additions and 4 deletions

View file

@ -7975,6 +7975,10 @@ The exact set of supported keywords depends on the build system
@code{#:phases}. The @code{#:phases} keyword in particular lets you @code{#:phases}. The @code{#:phases} keyword in particular lets you
modify the set of build phases for your package (@pxref{Build Phases}). modify the set of build phases for your package (@pxref{Build Phases}).
The REPL has dedicated commands to interactively inspect values of some
of these arguments, as a convenient debugging aid (@pxref{Using Guix
Interactively}).
@quotation Compatibility Note @quotation Compatibility Note
Until version 1.3.0, the @code{arguments} field would typically use Until version 1.3.0, the @code{arguments} field would typically use
@code{quote} (@code{'}) or @code{quasiquote} (@code{`}) and no @code{quote} (@code{'}) or @code{quasiquote} (@code{`}) and no
@ -8774,6 +8778,23 @@ when @var{cut?} returns true for a given package. When @var{deep?} is true, @va
applied to implicit inputs as well. applied to implicit inputs as well.
@end deffn @end deffn
@quotation Tips
Understanding what a variant really looks like can be difficult as one
starts combining the tools shown above. There are several ways to
inspect a package before attempting to build it that can prove handy:
@itemize
@item
You can inspect the package interactively at the REPL, for instance to
view its inputs, the code of its build phases, or its configure flags
(@pxref{Using Guix Interactively}).
@item
When rewriting dependencies, @command{guix graph} can often help
visualize the changes that are made (@pxref{Invoking guix graph}).
@end itemize
@end quotation
@node Writing Manifests @node Writing Manifests
@section Writing Manifests @section Writing Manifests
@ -10585,6 +10606,11 @@ we have seen before. @xref{Build Utilities}, for more about
the helpers used by this phase, and for more examples of the helpers used by this phase, and for more examples of
@code{modify-phases}. @code{modify-phases}.
@quotation Tip
You can inspect the code associated with a package's @code{#:phases}
argument interactively, at the REPL (@pxref{Using Guix Interactively}).
@end quotation
@cindex code staging @cindex code staging
@cindex staging, of code @cindex staging, of code
Keep in mind that build phases are code evaluated at the time the Keep in mind that build phases are code evaluated at the time the
@ -12763,6 +12789,30 @@ scheme@@(guix-user)> (scandir (string-append $3 "/bin"))
$5 = ("." ".." "egrep" "fgrep" "grep") $5 = ("." ".." "egrep" "fgrep" "grep")
@end example @end example
As a packager, you may be willing to inspect the build phases or flags
of a given package; this is particularly useful when relying a lot on
inheritance to define package variants (@pxref{Defining Package
Variants}) or when package arguments are a result of some computation,
both of which can make it harder to foresee what ends up in the package
arguments. Additional commands let you inspect those package arguments:
@example
scheme@@(guix-user)> ,phases grep
$1 = (modify-phases %standard-phases
(add-after 'install 'fix-egrep-and-fgrep
(lambda* (#:key outputs #:allow-other-keys)
(let* ((out (assoc-ref outputs "out"))
(bin (string-append out "/bin")))
(substitute* (list (string-append bin "/egrep")
(string-append bin "/fgrep"))
(("^exec grep")
(string-append "exec " bin "/grep")))))))
scheme@@(guix-user)> ,configure-flags findutils
$2 = (list "--localstatedir=/var")
scheme@@(guix-user)> ,make-flags binutils
$3 = '("MAKEINFO=true")
@end example
At a lower-level, a useful command is @code{lower}: it takes a file-like 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 object and ``lowers'' it into a derivation (@pxref{Derivations}) or a
store file: store file:
@ -12794,6 +12844,17 @@ This is similar to the @option{--verbosity} command-line option
shows build events only, and higher levels print build logs. shows build events only, and higher levels print build logs.
@end deffn @end deffn
@deffn {REPL command} phases @var{package}
@deffnx {REPL command} configure-flags @var{package}
@deffnx {REPL command} make-flags @var{package}
These REPL commands return the value of one element of the
@code{arguments} field of @var{package} (@pxref{package Reference}): the
first one show the staged code associated with @code{#:phases}
(@pxref{Build Phases}), the second shows the code for
@code{#:configure-flags}, and @code{,make-flags} returns the code for
@code{#:make-flags}.
@end deffn
@deffn {REPL command} run-in-store @var{exp} @deffn {REPL command} run-in-store @var{exp}
Run @var{exp}, a monadic expression, through the store monad. Run @var{exp}, a monadic expression, through the store monad.
@xref{The Store Monad}, for more information. @xref{The Store Monad}, for more information.

View file

@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU ;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2014, 2015, 2016, 2022 Ludovic Courtès <ludo@gnu.org> ;;; Copyright © 2014-2016, 2022-2023 Ludovic Courtès <ludo@gnu.org>
;;; ;;;
;;; This file is part of GNU Guix. ;;; This file is part of GNU Guix.
;;; ;;;
@ -21,13 +21,15 @@ (define-module (guix monad-repl)
#:use-module (guix monads) #:use-module (guix monads)
#:use-module (guix utils) #:use-module (guix utils)
#:use-module (guix packages) #:use-module (guix packages)
#:autoload (guix build-system) (bag)
#:use-module (guix status) #:use-module (guix status)
#:autoload (guix gexp) (lower-object) #:autoload (guix gexp) (gexp gexp? lower-gexp lowered-gexp-sexp lower-object)
#:use-module ((guix derivations) #:use-module ((guix derivations)
#:select (derivation? #:select (derivation?
derivation->output-paths built-derivations)) derivation->output-paths built-derivations))
#:autoload (guix read-print) (pretty-print-with-comments)
#:use-module (ice-9 match) #:use-module (ice-9 match)
#:use-module (ice-9 pretty-print) #:autoload (ice-9 pretty-print) (pretty-print)
#:use-module (system repl repl) #:use-module (system repl repl)
#:use-module (system repl common) #:use-module (system repl common)
#:use-module (system repl command) #:use-module (system repl command)
@ -138,4 +140,68 @@ (define-meta-command ((enter-store-monad guix) repl)
(repl-option-set! new 'interp #t) (repl-option-set! new 'interp #t)
(run-repl new)))) (run-repl new))))
;;; monad-repl.scm ends here
;;;
;;; Viewing package arguments.
;;;
(define (keyword-argument-value args keyword default)
"Return the value associated with KEYWORD in ARGS, a keyword/value sequence,
or DEFAULT if KEYWORD is missing from ARGS."
(let loop ((args args))
(match args
(()
default)
((kw value rest ...)
(if (eq? kw keyword)
value
(loop rest))))))
(define (package-argument-command repl form keyword default)
"Implement a command that display KEYWORD, a keyword such as #:phases, in
the arguments of the package FORM evaluates to. Return DEFAULT is KEYWORD is
missing from those arguments."
(match (repl-eval repl form)
((? package? package)
(let* ((bag* (bag
(inherit (package->bag package))
(build (lambda* (name inputs #:rest args)
(with-monad %store-monad
(return (keyword-argument-value args keyword
default))))))))
(define phases
(parameterize ((%graft? #f))
(with-store store
(set-build-options store
#:print-build-trace #t
#:print-extended-build-trace? #t
#:multiplexed-build-output? #t)
(run-with-store store
(mlet %store-monad ((exp (bag->derivation bag*)))
(if (gexp? exp)
(mlet %store-monad ((gexp (lower-gexp exp)))
(return (lowered-gexp-sexp gexp)))
(return exp)))))))
(run-hook before-print-hook phases)
(let ((column (port-column (current-output-port))))
(pretty-print-with-comments (current-output-port) phases
#:indent column)
(newline (current-output-port)))))
(_
(format #t ";; ERROR: This command only accepts package records.~%"))))
(define-meta-command ((phases guix) repl (form))
"phases
Return the build phases of the package defined by FORM."
(package-argument-command repl form #:phases #~%standard-phases))
(define-meta-command ((configure-flags guix) repl (form))
"configure-flags
Return the configure flags of the package defined by FORM."
(package-argument-command repl form #:configure-flags #~'()))
(define-meta-command ((make-flags guix) repl (form))
"make-flags
Return the make flags of the package defined by FORM."
(package-argument-command repl form #:make-flags #~'()))