Subject: [bug#58463] [PATCH] * doc: guix-cookbook: Add a "Guix API usage examples"

From: Liliana Marie Prikler <[email-masked]>
Date: Tue, 18 Oct 2022 14:09:57 +0200

Hi Fulbert,

Am Mittwoch, dem 12.10.2022 um 08:39 +0200 schrieb Fulbert:

> * doc/guix-cookbook.texi ("Guix API usage examples"): New chapter.
>   Transcript of examples given in "Guix REPL - to infinity and
> beyond"
>   by Simon Tournier at the "10 years of Guix" event.
> ---
New chapter suffices.

>  doc/guix-cookbook.texi | 426
> +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 426 insertions(+)
> 
> diff --git a/doc/guix-cookbook.texi b/doc/guix-cookbook.texi
> index b61adc06da..8c6954b049 100644
> --- a/doc/guix-cookbook.texi
> +++ b/doc/guix-cookbook.texi
> @@ -73,6 +73,7 @@ Weblate} (@pxref{Translating Guix,,, guix, GNU Guix
> reference manual}).
>  * System Configuration::        Customizing the GNU System
>  * Advanced package management:: Power to the users!
>  * Environment management::      Control environment
> +* API usage examples::          Using the API via REPL, scripts and
> extensions.
>  
>  * Acknowledgments::             Thanks!
>  * GNU Free Documentation License::  The license of this document.
> @@ -94,6 +95,11 @@ System Configuration
>  * Auto-Login to a Specific TTY:: Automatically Login a User to a
> Specific TTY
>  * Customizing the Kernel::       Creating and using a custom Linux
> kernel on Guix System.
>  
> +API usage examples
> +
> +* API usage examples::          Using Guix through its API via REPL,
> script and extensions.
> +                                Transcript of Simon Tournier
> presentation : Guix REPL - to infinity and beyond
> +
>  @end detailmenu
>  @end menu
>  
> @@ -2977,6 +2983,426 @@ will have predefined environment variables
> and procedures.
>  
>  Run @command{direnv allow} to setup the environment for the first
> time.
>  
> +@c
> *********************************************************************
> +@node API usage examples
> +@chapter API usage examples
> +
> +This chapter is a transcript of the examples given by Simon Tournier
> in
> +his presentation
> +@uref{
> https://10years.guix.gnu.org/video/guix-repl-to-infinity-and-beyond/,Guix
> +REPL - to infinity and beyond} (16 minutes video) at the
> +@uref{https://10years.guix.gnu.org/,10 years of Guix} event.
> +
> +Interactions with Guix as a library through its API via  REPL,
> script
> +and ``guix extension'' (API subject to changes,
> +script/extension might break at any update@dots{}).
> +
> +Throuthout this chapter :
Throughout this chapter, we'll be using the following abbreviations:

> +@itemize
> +@item
> +shell prompt shortened to : @code{'$ '}
> +@item
> +guix REPL prompt shortened to : @code{'> '}
> +@end itemize
> +
> +@menu
> +* Guix REPL::
> +* Guix script::
> +* Guix extension::
> +@end menu
You want more descriptive names here.

> +@node Guix REPL
> +@section Guix REPL
> +@anchor{#guix-repl}
> +
> +@menu
> +* 101::
Maybe "Getting started"?
> +* select count packages on criteria::
> +* table count/group on criteria::
Pseudo-SQL doesn't make for great titles.
> +@end menu
> +
> +@node 101
> +@subsection 101
> +@anchor{#101}
> +@quotation Note
> +the REPL command @code{,use (module path name)} (shortened
> +@code{,u}) can be used as an equivalent for
> +@code{(use-modules (module path name))}. @code{,use} alone will list
> +currently imported modules.
> +@end quotation
I find it a little odd to lead with a note.
> +
> +Simple Guix REPL session
Full sentences
> +@example sh
> +$ guix repl
> +GNU Guile 3.0.8
> +;;  [skipped licence preamble]
> +> (use-modules (gnu packages base))
> +> findutils
> +$1 = #<package findutils@@4.8.0 gnu/packages/base.scm:294
> 7fc88588ddc0>
> +> ,use(guix)
> +> (package-name findutils)
> +$2 = "findutils"
> +> (package-version findutils)
> +$3 = "4.8.0"
> +> (package? findutils)
> +$4 = #t
> +@end example
> +
> +@quotation Note
> +the REPL command @code{,describe} (can be shortened @code{,d}) :
> +Usage: describe OBJ@*
> +Show description/documentation. (#f if unavailable)
> +@example sh
> +> ,d package->derivation
> +Return the <derivation> object of PACKAGE for SYSTEM.
> +> ,describe package-name
> +#f
> +@end example
> +@end quotation
> +
> +@quotation Note
> +when a module is imported, its exported objects are listed in
> +TAB-@dots{} completions.
I think you might be using notes a little too much.
> +
> +@example sh
> +> package-in<TAB>
> +package-input-error?          package-input-rewriting/spec
> +package-input-rewriting       package-inputs
> +@end example
> +@end quotation
> +
> +@node select count packages on criteria
> +@subsection select count packages on criteria
> +@anchor{#select-count-packages-on-criteria}
> +(→ 4 min 20 s into Simon Tournier presentation)
I don't think it's useful to write out these timestamps in a way that
they're readable to the reader.  They might be fine as comments, but
I'd also suggest diverging from simon's structure to accomodate the
switch in media (and possibly target audience?)

> +In the following REPL session
> +
> +@itemize
> +@item
> +count of haskell packages and ;
> +
> +@item
> +count of haskell pacakges using the @code{#:cabal-revision} argument
> +@emph{(present in the package ghc-crypthohash-sha1, as shown below,
> for
> +instance)}
Full sentence
> +@lisp
> +(define-public ghc-cryptohash-sha1
> +  (package
> +  ;; skipped for clarity
> +    (build-system haskell-build-system)
> +    (arguments
> +     `(#:cabal-revision
> +       ("6" "10rpxrmqgwihmplczglwxf5q3l13z9j3kvi065z884y4dymmnkgc")
> +       ;; …
> +@end lisp
> +
> +@end itemize
> +
> +REPL session :
> +
> +@example sh
> +$ guix repl
> +GNU Guile 3.0.8
> +;;  [skipped licence preamble]
> +> (use-modules
> +  (guix)
> +  (guix build-system haskell)
> +  (gnu)
> +  (ice-9 match))
Even if it's just the REPL, indent properly.
> +>
> +> (define haskell-packages
> +  (fold-packages
> +    (lambda (package result)
> +      (if (eq? (package-build-system package) haskell-build-system)
> +         (cons package result)
> +         result))
> +    '()))
> +> (define (cabal-revision? package)
> +  (apply (lambda* (#:key cabal-revision #:allow-other-keys)
> +                  (match cabal-revision
> +                         ((revision hash) #t)
> +                         (_ #f)))
> +         (package-arguments package)))
> +> (define cabal-revision-packages
> +  (filter cabal-revision? haskell-packages))
> +> (length haskell-packages)
> +$1 = 721
> +> (length cabal-revision-packages)
> +$2 = 108
> +@end example
> +
> +@node table count/group on criteria
> +@subsection table count/group on criteria
> +@anchor{#table-countgroup-on-criteria}
> +(→ 5 min 20 s into Simon Tournier presentation)
> +
> +The @code{arguments-vs-import.scm} file shown below demonstrate some
> +more sophisticated selection and grouping of packages, and can be
> passed
> +to @code{guix repl} like so :
> +@example sh
> +$ guix repl -- arguments-vs-import.scm
> +@end example
> +Its interpretation will output a table similar to the one show below
> the
> +script-file content, giving the number of packages which ``tweak''
> their
> +arguments to the build and the number of packages which don't, all
> +grouped by build-system types.
> +
> +@code{arguments-vs-import.scm} file content :
> +
> +@lisp
> +(use-modules (guix)
> +             (gnu)
> +             (ice-9 match))
> +
> +(define table (make-hash-table))
> +
> +(fold-packages (lambda (package result)
> +                 (let ((bs (build-system-name
> +                             (package-build-system package)))
> +                       (arg (package-arguments package)))
> +                   (match (hash-ref result bs)
> +                          ((tot wo wi)
> +                           (if (null? arg)
> +                             (hash-set! result bs (list
> +                                                    (1+ tot)
> +                                                    (1+ wo)
> +                                                    wi))
> +                             (hash-set! result bs (list
> +                                                    (1+ tot)
> +                                                    wo
> +                                                    (1+ wi)))))
> +                          (#f (if (null? arg)
> +                                (hash-set! result bs (list 1 1 0))
> +                                (hash-set! result bs (list 1 0 1))))
> +                          (_ (format #t "Error: ~s~%" (package-name
> package))))
> +                   result))
> +               table)
> +
> +(define fmt "~13s: ~4s = ~4s = ~4s + ~4s~%")
> +(format #t fmt
> +        'key 'tot 'tot 'no-arguments 'arguments)
> +(hash-for-each-handle (lambda (kv)
> +                       (match kv
> +                              ((key . value)
> +                               (match value
> +                                      ((tot wo wi)
> +                                       (format #t fmt
> +                                               key
> +                                               (+ wo wi)
> +                                               tot wo wi))))))
> +                     table)
> +@end lisp
> +
> +call from shell and output :
> +
> +@example sh
> +$ cd ~/tmp/10-years-of-guix
> +$ guix repl -- guix-repl-and-beyond.scm
> +key          : tot  = tot  = no-arguments + arguments
> +ocaml        : 57   = 57   = 0    + 57
> +haskell      : 721  = 721  = 504  + 217
> +clojure      : 11   = 11   = 0    + 11
> +[skipping for clarity]
> +meson        : 442  = 442  = 89   + 353
> +texlive      : 143  = 143  = 0    + 143
> +python       : 2619 = 2619 = 797  + 1822
> +binary       : 14   = 14   = 0    + 14
> +@end example
> +
> +@node Guix script
> +@section Guix script
> +@anchor{#guix-script}
> +Simon Tournier does not say much about using guix through
> scripts@dots{}
> +probably because there is not much to say and the following links
> should
> +answer most questions.
> +
> +@itemize
> +@item
> +@uref{
> https://www.gnu.org/software/guile/manual/html_node/Running-Guile-Scripts.html#Running-Guile-Scripts,Running
> +Guile Scripts (Guile Reference Manual)}
> +@item
> +@uref{
> https://www.gnu.org/software/guile/manual/html_node/Guile-Scripting.html#Guile-Scripting,Guile
> +Scripting (Guile Reference Manual)}
> +@item
> +@uref{
> https://www.gnu.org/software/coreutils/manual/html_node/env-invocation.html#env-invocation,env
> +invocation (GNU Coreutils 9.1)}
> +@end itemize
That's not a good way to lead into the section.  Also, you can use
simple refs to reference the manuals in question.

> +Nevertheless, the script ``@code{explore}'', from Ludovic Courtès,
> used
> +in the next section to demonstrate a ``script-to-extension
> conversion'',
> +is probably an interesting example of using the guix API. See the
> links
> +below for more :
> +
> +@itemize
> +@item
> +@uref{https://10years.guix.gnu.org/video/explore-your-system/,Ten
> Years
> +of Guix --- Explore your system --- Ludovic Courtès}
> +@item
> +@uref{
> https://notabug.org/civodul/guix-explorer,civodul/guix-explorer:
> +Exploring Guix System. - NotABug.org: Free code hosting}
> +@end itemize
> +
> +@node Guix extension
> +@section Guix extension
> +@anchor{#guix-extension}
> +(→ 7 min 05 s into Simon Tournier presentation)
> +
> +@menu
> +* minimal example::
> +* Ludovic Courtès's explore.scm program::
> +@end menu
> +
> +@node minimal example
> +@subsection minimal example
> +@anchor{#minimal-example}
> +As a minimal example of a guix extension, the following file,
> +@code{~/tmp/10-years-of-guix/guix/extensions/hello.scm}, is used :
> +
> +@lisp
> +(define-module (guix extensions hello)
> +               #:export (guix-hello))
> +
> +(define-command (guix-hello . cmd-line)
> +                (category plumbing)
> +                (synopsis "hello world")
> +                (display (G_ "hello folks!")))
> +@end lisp
> +
> +The environment variable @code{GUIX_EXTENSIONS_PATH} has to include
> the
> +path to the scrip file.
> +
> +With this in place, we can see the @code{hello} extension integrated
> in
> +the @code{guix} CLI, as the following capture shows, with the new
> +command ``hello'' added to the ``plumbing'' category :
> +
> +@example sh
> +$ export GUIX_EXTENSIONS_PATH="$HOME/tmp/10-years-of-
> guix/guix/extensions"
> +$ guix help
> +Usage: guix OPTION | COMMAND ARGS...
> +Run COMMAND with ARGS, if given.
> +# [skipping for clarity …] 
> +  plumbing commands
> +    archive    manipulate, export, and import normalized archives
> (nars)
> +    copy       copy store items remotely over SSH
> +    git        operate on Git repositories
> +    offload    set up and operate build offloading
> +    processes  list currently running sessions
> +    repl       read-eval-print loop (REPL) for interactive
> programming
> +    hello      hello world
> +
> +Report bugs to: bug-guix@@gnu.org.
> +# …
> +@end example
> +
> +@node Ludovic Courtès's explore.scm program
> +@subsection Ludovic Courtès's explore.scm program
> +@anchor{#ludovic-courtèss-explore.scm-program}
> +The @code{explore.scm} script can then be modified as follows to
> have it work
> +as a Guix extension rather than a script.
> +
> +(→ 9 min 13 s into Simon Tournier presentation)
> +
> +@itemize
> +@item
> +removing the shebang call to guile
> +
> +@example
> +@verbatim
> +-#!/bin/sh
> +-exec "${GUILE:-guile}" -e "(@ (explore) guix-explore)" -s "$0" "$@"
> +-!#
> +@end verbatim
> +@end example
> +
> +@item
> +replacing the module declaration with appropriate path/name
> +@example
> +@verbatim
> +-(define-module (explore)
> ++(define-module (guix extensions explore)
> +@end verbatim
> +@end example
> +
> +@item
> +adding the module guix scripts to the use-module list
> +@example
> +@dots{}
> +@verbatim
> + #:use-module (guix ui)
> ++ #:use-module (guix scripts)
> + #:use-module (guix store)
> +@end verbatim
> +@dots{}
> +@end example
> +
> +@item
> +modifying the guix-explore definition
> +@example
> +@verbatim
> +-(define (guix-explore args)
> ++(define-command (guix-explore . args)
> ++ (category extension)
> ++ (synopsis ``explore your service'')
> +(define %user-module @dots{}
> +@end verbatim
> +@dots{}
> +@end example
> +@end itemize
> +
> +@quotation Note on the path to the script
I don't think that this series of diffs is a great idea.  Perhaps using
both copies and discussing the subtle differences between them might be
better?

> +[It seems that] to be used as a guix extension, Guix requires a
> script
> +to live under a
> +``[/@dots{}]/guix/extensions[/@dots{}]/<module-name>.scm'' tree
> +structure with the corresponding module declaration
> +@code{(define-module (guix extensions [@dots{}] <module-name>)
> @dots{}}.
Read the manual :)

> +This will work :
> +
> +@example sh
> +$ pwd
> +~/tmp/10-years-of-guix/guix/extensions/test
> +$ head -1 hello.scm
> +(define-module (guix extensions test hello)
> +@end example
> +
> +@dots{} while this won't work :
> +
> +@example sh
> +$ pwd
> +~/tmp/10-years-of-guix/guix/test
> +$ head -1 hello.scm
> +(define-module (guix test hello)
> +@end example
> +
> +@dots{} nor this :
> +
> +@example sh
> +$ pwd
> +~/tmp/10-years-of-guix/nono/
> +$ head -1 hello.scm
> +(define-module (nono hello)
> +@end example
> +@end quotation
> +
> +lauching
rocket

> +@quotation Note
> +@code{explore} produces a visual and interactive representation of
> +the services used in a OS declaration. The user has to provide a
> path to
> +the OS configuration file to explore.
> +@end quotation
> +
> +All set, explore can now be used as a Guix extension like so :
> +
> +@example sh
> +$ export GUIX_EXTENSIONS_PATH=/path/to/guix/extensions
> +$ guix explore -- /path/to/configure.scm
> +@end example
> +
Cheers