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

From: Simon Tournier <[email-masked]>
Date: Fri, 21 Feb 2025 18:31:01 +0100

Hi,

Well this patch documents GUIX_EXTENSIONS_PATH.  It had fallen into the
cracks, my bad!  Somehow, I had been unhappy with the implementation of
GUIX_EXTENSIONS_PATH.

I think this patch should be included in the Cookbook.  I remember a
revamp of this patch but I’m not able to find it back. Cough Cough…

WDYT?  Does this patch would help about introducing more Guix extensions
instead of adding subcommands.  I think that a Guix extension is the
perfect way in order to test new ideas and CLI before introducing
upstream and thus claim strong backward compatibility and clear API
design.

https://fulbert.neocities.org/guix/10-years-of-guix/simon-tournier-guix-repl/guix-cookbook/API-usage-examples#API-usage-examples

On Wed, 12 Oct 2022 at 08:39, Fulbert <[email-masked]> wrote:

> From c1af5a6ea0e56d0764969e118f0c55942db90e7b Mon Sep 17 00:00:00 2001
> From: Fulbert <[email-masked]>
> Date: Wed, 12 Oct 2022 07:57:47 +0200
> Subject: [PATCH] * doc: guix-cookbook: Add a "Guix API usage examples"
>  chapter.
>
> * 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.
> ---
>  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 :
> +
> +@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
> +
> +@node Guix REPL
> +@section Guix REPL
> +@anchor{#guix-repl}
> +
> +@menu
> +* 101::
> +* select count packages on criteria::
> +* table count/group on criteria::
> +@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
> +
> +Simple Guix REPL session
> +@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.
> +
> +@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)
> +
> +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)}
> +
> +@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))
> +>
> +> (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
> +
> +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
> +
> +[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{}}.
> +
> +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
> +
> +@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
> +
>  @c *********************************************************************
>  @node Acknowledgments
>  @chapter Acknowledgments
> -- 
> 2.38.0

Cheers,
simon