Toggle quote (467 lines)
> From c1af5a6ea0e56d0764969e118f0c55942db90e7b Mon Sep 17 00:00:00 2001
> From: Fulbert <fulbert@bluewin.ch>
> 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