[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
02/14: gpce-2017: Write, write, write.
From: |
Ludovic Courtčs |
Subject: |
02/14: gpce-2017: Write, write, write. |
Date: |
Fri, 1 Sep 2017 11:57:54 -0400 (EDT) |
civodul pushed a commit to branch master
in repository maintenance.
commit ba736f621a54af89390c92304b2ca1de9c0979bf
Author: Ludovic Courtès <address@hidden>
Date: Tue Jul 4 18:19:36 2017 +0200
gpce-2017: Write, write, write.
---
.gitignore | 1 +
doc/gpce-2017/code/gexp-compilers.scm | 14 +
doc/gpce-2017/code/gexp-expansion.scm | 19 +-
doc/gpce-2017/code/gexp-modules.scm | 8 +
doc/gpce-2017/code/initrd.scm | 11 +
doc/gpce-2017/gpce.skb | 500 +++++++++++++++++++++++++++++-----
doc/gpce-2017/staging.sbib | 67 +++++
7 files changed, 549 insertions(+), 71 deletions(-)
diff --git a/.gitignore b/.gitignore
index 948e1f9..a80c8ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -126,3 +126,4 @@
/doc/gpce-2017/gpce.log
/doc/gpce-2017/gpce.out
/doc/gpce-2017/gpce.pdf
+/doc/gpce-2017/gpce.tex
diff --git a/doc/gpce-2017/code/gexp-compilers.scm
b/doc/gpce-2017/code/gexp-compilers.scm
new file mode 100644
index 0000000..3989c73
--- /dev/null
+++ b/doc/gpce-2017/code/gexp-compilers.scm
@@ -0,0 +1,14 @@
+(define-gexp-compiler (package-compiler (package <package>)
+ system target)
+ ;; Compile PACKAGE to a derivation for SYSTEM, optionally
+ ;; cross-compiled for TARGET.
+ (if target
+ (package->cross-derivation package target system)
+ (package->derivation package system)))
+
+(define-gexp-compiler (local-file-compiler (file <local-file>)
+ system target)
+ ;; "Compile" FILE by adding it to the store.
+ (match file
+ (($ <local-file> file name)
+ (interned-file file name))))
diff --git a/doc/gpce-2017/code/gexp-expansion.scm
b/doc/gpce-2017/code/gexp-expansion.scm
index aab1365..003b767 100644
--- a/doc/gpce-2017/code/gexp-expansion.scm
+++ b/doc/gpce-2017/code/gexp-expansion.scm
@@ -1,18 +1,18 @@
(use-modules (guix))
-;!begin-condensed
+;;!begin-condensed
#~(list (string-append #$imagemagick "/bin/convert")
(string-append #$emacs "/bin/emacs"))
-;!end-condensed
+;;!end-condensed
-;!begin-gexp-without-reader-macro
+;;!begin-gexp-without-reader-macro
(gexp (list (string-append (ungexp imagemagick)
"/bin/convert")
(string-append (ungexp emacs)
"/bin/emacs")))
-;!end-gexp-without-reader-macro
+;;!end-gexp-without-reader-macro
-;!begin-expansion
+;;!begin-expansion
(let ((references
(list (gexp-input imagemagick)
(gexp-input emacs)))
@@ -23,4 +23,11 @@
(list 'string-append b
"/bin/emacs")))))
(make-gexp references proc))
-;!end-expansion
+;;!end-expansion
+
+;;!begin-staged-sexp
+(list (string-append "/gnu/store/65qrc…-imagemagick-6.9"
+ "/bin/convert")
+ (string-append "/gnu/store/825n3…-emacs-25.2"
+ "/bin/emacs"))
+;;!end-staged-sexp
diff --git a/doc/gpce-2017/code/gexp-modules.scm
b/doc/gpce-2017/code/gexp-modules.scm
new file mode 100644
index 0000000..75cdf5a
--- /dev/null
+++ b/doc/gpce-2017/code/gexp-modules.scm
@@ -0,0 +1,8 @@
+(with-imported-modules (source-module-closure
+ '((guix build utils)))
+ #~(begin
+ ;; Import the module in scope.
+ (use-modules (guix build utils))
+
+ ;; Use a function from (guix build utils).
+ (mkdir-p #$output)))
diff --git a/doc/gpce-2017/code/initrd.scm b/doc/gpce-2017/code/initrd.scm
new file mode 100644
index 0000000..f88bd4c
--- /dev/null
+++ b/doc/gpce-2017/code/initrd.scm
@@ -0,0 +1,11 @@
+;;!begin-initrd
+(expression->initrd
+ (with-imported-modules (source-module-closure
+ '((gnu build linux-boot)))
+ #~(begin
+ (use-modules (gnu build linux-boot))
+
+ (boot-system #:mounts '#$file-systems
+ #:linux-modules '#$linux-modules
+ #:linux-module-directory '#$kodir)))
+;;!end-initrd
diff --git a/doc/gpce-2017/gpce.skb b/doc/gpce-2017/gpce.skb
index 7c8aae6..48b2733 100644
--- a/doc/gpce-2017/gpce.skb
+++ b/doc/gpce-2017/gpce.skb
@@ -3,9 +3,14 @@
(skribilo engine latex)
(skribilo ast)
(skribilo writer)
+ (skribilo output)
+ (skribilo utils strings)
+ (skribilo lib)
(skribilo evaluator)
(skribilo biblio author)
- (skribilo source lisp))
+ (skribilo source)
+ (skribilo source lisp)
+ (skribilo source parameters))
(define (---) ; emdash
(resolve (lambda (n e env)
@@ -37,6 +42,28 @@
(define (=>)
(symbol "=>"))
+(define %guix-keywords
+ (append (map (lambda (x) (cons x '&source-keyword))
+ '(set! if let let* letrec quote cond case else begin do lambda
+ gexp ungexp match))
+ (map (lambda (x) (cons x '&source-define))
+ '(define define-syntax define-gexp-compiler
+ with-imported-modules use-modules))))
+
+(define (guix-fontifier s)
+ (parameterize ((*the-keys* %guix-keywords)
+ (*bracket-highlight* #f)
+ (*class-highlight* #f))
+ ((@@ (skribilo source lisp) lisp-family-fontifier) s)))
+
+
+(define guix
+ (new language
+ (name "guix")
+ (fontifier guix-fontifier)
+ (extractor (language-extractor scheme))))
+
+
;; For pdflatex.
(engine-custom-set! (find-engine 'latex) 'image-format '("pdf"))
@@ -60,12 +87,35 @@
(let* ((latex (find-engine 'latex))
(usep (engine-custom latex 'usepackage)))
-;; (engine-custom-set! latex 'usepackage
-;; (string-append usep "
-;; \\usepackage{floatrow}
-;; \\floatsetup[figure]{font=footnotesize}
-;; \\setlength{\\intextsep}{5pt plus 1.0pt minus 2.0pt}
-;; "))
+ (markup-writer 'figure latex
+ :options '(:legend :number :multicolumns)
+ :action (lambda (n e)
+ (let ((ident (markup-ident n))
+ (legend (markup-option n :legend))
+ (mc (markup-option n :multicolumns)))
+ (display "\\begin{figure}[ht]\n\\begin{footnotesize}\n")
+ (output (markup-body n) e)
+ (display "\n\\end{footnotesize}\n")
+ (format #t "\\caption{\\label{~a}"
+ (string-canonicalize ident))
+ (output legend e)
+ (display "}\\end{figure}\n"))))
+
+ (markup-writer '&source-keyword latex
+ :action (lambda (n e)
+ (display "\\textbf{\\textrm{")
+ (output (markup-body n) e)
+ (display "}}")))
+
+ (markup-writer '&source-line-comment latex
+ :action (lambda (n e)
+ (let* ((cc (engine-custom e 'source-comment-color))
+ (n1 (it (! "\\textrm{$1}" (markup-body n))))
+ (n2 (if (and (engine-custom e 'source-color) cc)
+ (color :fg cc n1)
+ n1)))
+ (evaluate-document n2 e))))
+
(engine-custom-set! latex 'source-comment-color
"#776600"))
@@ -73,6 +123,7 @@
(bibliography "../reppar-2015/reppar.sbib")
(bibliography "./staging.sbib")
+
(document :title [Code Staging in GNUÂ Guix]
(abstract
@@ -142,17 +193,47 @@ definitions to the latest version of a package, user
interfaces in
Emacs and in Web browsers have been developed, tools to rewrite a
package’s dependency graph are available from the command line and
through the API, and so on.])
+
+ (figure :legend [A package definition using the high-level
+interface.]
+ :ident "fig-package-def"
+ :multicolumns #t
+
+ (prog
+ (source :language guix [
+(define hello
+ (package
+ (name "hello")
+ (version "2.8")
+ (source (origin ;!origin
+ (method url-fetch)
+ (uri (string-append "mirror://gnu/hello/hello-"
+ version ".tar.gz")) ;!package-uri
+ (sha256 (base32 "0wqd8...")))) ;!base32
+ (build-system gnu-build-system) ;!build-system
+ (arguments
+ '(#:configure-flags ;!config-flags
+ `("--disable-color"
+ \,(string-append "--with-gawk="
+ (assoc-ref %build-inputs "gawk")))))
+ (inputs `(("gawk" ,gawk))) ;!inputs
+ (synopsis "GNU Hello")
+ (description "An illustration of GNU's engineering practices.")
+ (home-page "http://www.gnu.org/software/hello/")
+ (license gpl3+)))
+])))
+
(p [In Guix, high-level package definitions like the one shown
-in Figure XXX are compiled to ,(emph [derivations]), the low-level
-representation of build actions inherited from Nix. A derivation
-specifies: a command to run to perform the build (the “build
-program”), environment variables to be defined, and derivations whose
-build result it depends on. Derivations are sent to a privileged
-daemon, which is responsible for building them on behalf of clients.
-The build daemon creates isolated environments (isolated “containers”
-in a chroot) in which it spawns the build program; since build
-environments are isolated, this ensures that build programs do not
-depend on undeclared inputs.])
+in Figure ,(ref :figure "fig-package-def") are compiled to ,(emph
+[derivations]), the low-level representation of build actions
+inherited from Nix. A derivation specifies: a command to run to
+perform the build (the “build program”), environment variables to be
+defined, and derivations whose build result it depends on.
+Derivations are sent to a privileged daemon, which is responsible for
+building them on behalf of clients. The build daemon creates isolated
+environments (isolated “containers” in a chroot) in which it spawns
+the build program; since build environments are isolated, this ensures
+that build programs do not depend on undeclared inputs.])
(p [The second way in which Guix departs from Nix is by using
the same language, Scheme, for all its functionality. While package
definitions in Nix can embed Bash or Perl snippets to refine build
@@ -196,27 +277,29 @@ with gexps as an “augmented” version of sexps.])
:ident "fig-build-sexp"
(prog :line #f
- (source :language scheme :file "code/build-sexp.scm"
+ (source :language guix :file "code/build-sexp.scm"
:start ";!begin-build-sexp"
:stop ";!end-build-sexp")))
(p [In previous work ,(ref :bib 'courtes2013:functional), we
-presented our first attempt at writing build expressions in Scheme.
-Figure ,(ref :figure "fig-build-sexp") shows an example that creates a
-derivation that, when built, converts the input image to JPEG, using
-the ,(tt [convert]) program from the ImageMagick package. In this
-example, variable ,(tt [store]) represents the connection to the build
-daemon. The ,(tt [package-derivation]) function takes the ,(tt
-[imagemagick]) package object and computes its corresponding
-derivation, while the ,(tt [add-to-store]) remote procedure call (RPC)
-instructs the daemon to add the file ,(tt [GuixSD.png]) to ,(tt
-[/gnu/store]). The variable ,(tt [build]) contains our build program
-as an sexp, thanks to the apostrophe, which means “quote” in Lisp.
-Finally, ,(tt [build-expression->derivation]) takes the build program
-and computes the corresponding derivation without building it. The
-user can then make an RPC to the build daemon asking it to build this
-derivation; only then will the daemon create an isolated environment
-and run our build program.])
+presented our first attempt at writing build expressions in Scheme,
+which relied solely on Lisp’s famous quotation mechanism ,(ref :bib
+'bawden1999:quasiquotation). Figure ,(ref :figure "fig-build-sexp")
+shows an example that creates a derivation that, when built, converts
+the input image to JPEG, using the ,(tt [convert]) program from the
+ImageMagick package. In this example, variable ,(tt [store])
+represents the connection to the build daemon. The ,(tt
+[package-derivation]) function takes the ,(tt [imagemagick]) package
+object and computes its corresponding derivation, while the ,(tt
+[add-to-store]) remote procedure call (RPC) instructs the daemon to
+add the file ,(tt [GuixSD.png]) to ,(tt [/gnu/store]). The variable
+,(tt [build]) contains our build program as an sexp, thanks to the
+apostrophe, which means “quote” in Lisp. Finally, ,(tt
+[build-expression->derivation]) takes the build program and computes
+the corresponding derivation without building it. The user can then
+make an RPC to the build daemon asking it to build this derivation;
+only then will the daemon create an isolated environment and run our
+build program.])
(p [,(tt [build-expression->derivation]) arranges so that the
build program, ,(tt [build]), is evaluated in a context where two
extra variables are defined: ,(tt [%build-inputs]) contains a list
@@ -230,16 +313,16 @@ of ImageMagick.]))
(p [Needless to say, this interface was extremely verbose and
inconvenient—fortunately, users usually only had to deal with the more
-pleasant ,(tt [package]) interface shown in Figure XXX. All these
-steps are necessary to define the derivation and its dependencies, but
-where does the verbosity come from? First, we have to explicitly call
-,(tt [package-derivation]) for each package the expression refers to.
-Second, we have to explicitly the inputs with labels at the call site.
-Third, the build code has to use this ,(tt [assoc-ref]) call just to
-retrieve the ,(tt [/gnu/store]) file name of its inputs. It is also
-error-prone: if we omit the ,(tt [#:inputs]) parameter, of if we
-mispell an input label, we will only find out when we build the
-derivation.])
+pleasant ,(tt [package]) interface shown in Figure ,(ref :figure
+"fig-package-def"). All these steps are necessary to define the
+derivation and its dependencies, but where does the verbosity come
+from? First, we have to explicitly call ,(tt [package-derivation])
+for each package the expression refers to. Second, we have to
+explicitly the inputs with labels at the call site. Third, the build
+code has to use this ,(tt [assoc-ref]) call just to retrieve the ,(tt
+[/gnu/store]) file name of its inputs. It is also error-prone: if we
+omit the ,(tt [#:inputs]) parameter, of if we mispell an input label,
+we will only find out when we build the derivation.])
(p [Another limitation not visible on a toy example but that
became clear as we developed GuixSD it the cost of carrying this ,(tt
[#:inputs]) argument down to the call site. It forces programmers to
@@ -268,7 +351,7 @@ cases.])
:ident "fig-build-gexp"
(prog :line #f
- (source :language scheme :file "code/build-sexp.scm"
+ (source :language guix :file "code/build-sexp.scm"
:start ";!begin-imagemagick-gexp"
:stop ";!end-imagemagick-gexp")))
@@ -332,23 +415,30 @@ is created.]))
:ident "fig-gexp-expansion"
(prog :line #f
- (source :language scheme :file "code/gexp-expansion.scm"
- :start ";!begin-condensed"
- :stop ";!end-condensed"))
+ (source :language guix :file "code/gexp-expansion.scm"
+ :start ";;!begin-condensed"
+ :stop ";;!end-condensed"))
(p [… is equivalent to:])
(prog :line #f
- (source :language scheme :file "code/gexp-expansion.scm"
- :start ";!begin-gexp-without-reader-macro"
- :stop ";!end-gexp-without-reader-macro"))
+ (source :language guix :file "code/gexp-expansion.scm"
+ :start ";;!begin-gexp-without-reader-macro"
+ :stop ";;!end-gexp-without-reader-macro"))
(p [… which expands to:])
(prog :line #f
- (source :language scheme :file "code/gexp-expansion.scm"
- :start ";!begin-expansion"
- :stop ";!end-expansion")))
+ (source :language guix :file "code/gexp-expansion.scm"
+ :start ";;!begin-expansion"
+ :stop ";;!end-expansion"))
+
+ (p [Once staged, this generates an sexp along these lines:])
+
+ (prog :line #f
+ (source :language guix :file "code/gexp-expansion.scm"
+ :start ";;!begin-staged-sexp"
+ :stop ";;!end-staged-sexp")))
(p [As can be seen from the example above, gexps are
first-class Scheme values: a variable can be bound to a gexp, and
@@ -376,33 +466,313 @@ substitutions.])
given gexp, store, and system type, the derivations that the gexp
depends on. In this example, it returns the derivations for
ImageMagick and Emacs, as computed by the ,(tt [package-derivation])
-function seen earlier.])
- (p [XXX: compilers & expanders]))
+function seen earlier. Gexps can be nested, as in ,(tt
+[#~#$#~(string-append #$emacs "/bin/emacs")]). The input list
+returned by ,(tt [gexp-inputs]) for the outermost gexp is the sum of
+the inputs of outermost gexp and the inputs nested gexps.])
+
+ (figure
+ :legend [The gexp compilers for package objects and for
+,(tt [local-file]) objects.]
+ :ident "fig-gexp-compilers"
+
+ (prog :line #f
+ (source :language guix :file "code/gexp-compilers.scm")))
+
+ (p [We have seen that gexps often refer to package objects,
+but we want users to be able to refer to other types of high-level
+objects, such as the ,(tt [local-file]) object that appears in Figure
+,(ref :figure "fig-gexp-expansion"). Therefore, the gexp mechanism
+can be extended by defining ,(emph [gexp compilers]). Gexp compilers
+define, for a given data type, how objects of that type can be lowered
+to a derivation or to a literal file in the store. Figure ,(ref
+:figure "fig-gexp-compilers") shows the compilers for objects of the
+package and local-file types. All these compilers do is call the
+(monadic) function that computes the corresponding derivation, in the
+case of packages, or that simply adds the file to the store in the
+case of ,(tt [local-file]). Internally, these compilers are invoked
+by ,(tt [gexp->sexp]) when it encounters instances of the relevant
+type in a gexp that is being processed.])
+ (p [Gexp compilers can also have an associated ,(emph
+[expander]), which specifies how objects should be “rendered” in the
+final sexp. The default expander simply produces the store file name
+that corresponds to the output of the derivation. For example,
+assuming the variable ,(tt [emacs]) is bound to a package object, ,(tt
+[#~(string-append #$emacs "/bin/emacs")]) expands to ,(tt
+[(string-append "/gnu/store/…-emacs-25.2" "/bin/emacs")]), as we have
+seen earlier. We defined a ,(tt [file-append]) function that returns
+objects with a custom expander: one that performs string concatenation
+when generating the sexp. We can now write gexps like:
+
+,(prog :line #f
+ (source :language guix [#~(list #$(file-name emacs "/bin/emacs"))]))
+
+which expands to this sexp:
+
+,(prog :line #f
+ (source :language guix [(list "/gnu/store/…-emacs-25.2/bin/emacs")]))
+
+This is convenient in situations where we do not want or cannot impose
+a build-side ,(tt [string-append]) code.]))
(section :title [Extensions]
- (p [cross-compilation, modules])))
+ (figure
+ :legend [Specifying importing modules in a gexp.]
+ :ident "fig-gexp-modules"
+
+ (prog :line #f
+ (source :language guix :file "code/gexp-modules.scm")))
+
+ (p [,(bold [Modules.]) One of the reasons for using the same
+language uniformly is the ability to reuse Guile modules among in
+several contexts. Since builds are performed in an isolated
+environment, Scheme modules that are needed must be explicitly ,(emph
+[imported]) in that environment; in other words, the modules must be
+added as inputs to the derivation that needs them. To that end, gexp
+objects embed information about the modules they need; the ,(tt
+[with-imported-modules]) syntactic form allows users to specify
+modules to import in the gexps that appear in its body. The example
+in Figure ,(ref :figure "fig-gexp-modules") creates a gexp that
+requires the ,(tt [(guix build utils)]) module and the modules it
+depends on in its execution environment. The source of these module
+is taken from the user’s search path and added to the store when ,(tt
+[gexp->derivation]) is called.])
+ (p [Note that, to actually bring the module in scope, we
+still need to use Guile’s ,(tt [use-modules]) form. We choose to not
+conflate the notion of modules-in-scope and that of imported-modules
+because some of the modules come with Guile itself; importing those
+from the user’s environment would make derivations sensitive to the
+version of Guile used on the “host side”.])
+ (p [,(bold [Cross-compilation.]) Guix supports
+cross-compilation to other hardware architectures or operating
+systems, where packages are built by a ,(emph [cross-compiler]) that
+generates code for the target platform. To support this, we must be
+able to distinguish between ,(emph [native]) dependencies—dependencies
+that run on the build system—and ,(emph [target])
+dependencies—dependencies that can only run on the target system. At
+the level of package definitions, the distinction is made by having
+two fields: ,(tt [inputs]) and ,(tt [native-inputs]). To allow gexps
+to express this distinction, we introduced a new “unquote” form, ,(tt
+[ungexp-native]), abbreviated as ,(tt [#+]).])
+ (p [In a native build context, ,(tt [#+]) is strictly
+equivalent to ,(tt [#$]). However, when cross-compiling, elements
+introduced with ,(tt [#+]) are lowered to their ,(emph [native])
+derivation, while elements introduced with ,(tt [#$]) are lowered to a
+derivation that ,(emph [cross-compiles]) them for the target. To
+illustrate this, consider the gexp used to convert the bootloader’s
+background image to a suitable format, which resembles that of Figure
+,(ref :figure "fig-build-gexp"). In a cross-compilation context, the
+expression that converts the image should use the ,(emph [native])
+ImageMagick, not the target ImageMagick, which it would not be able to
+run anyway. Thus, we write ,(tt [#+imagemagick]) rather than ,(tt
+[#$imagemagick]).])))
(chapter :title [Experience]
:ident "experience"
- (section :title [Package Build Procedures])
- (section :title [initrd])
- (section :title [System Services]))
+ (p [Guix is used in production by individuals and organizations.
+This section reports on our experience using gexps in Guix.])
+
+ (section :title [Package Build Procedures]
+
+ (p [As explained earlier, gexps appeared quite recently in
+the history of Guix. Package definitions like that of Figure ,(ref
+:figure "fig-package-def") relied on the previous ad-hoc staging
+mechanism. This can be seen in the use of labels in the ,(tt
+[inputs]) field of definitions. Guix today includes more than 5,500
+packages, which still use this old, pre-gexp style. We are
+considering a migration to the new style but given the size of the
+repository, this is a challenging task and we must make sure every use
+case is correctly addressed in the new model.])
+ (p [In theory, labels are no longer needed with the use of
+gexps since one can now use a ,(tt [#$]) escape when they need to
+refer to the absolute file name of an inputs. The indirection that
+labels introduced had one benefit though: one could create a package
+variant with a different input, and ,(tt [(assoc-ref %build-inputs …])
+calls in build-side code would automatically resolve to the new
+package. If we instead allow for direct use of ,(tt [#$]) in package
+,(tt [arguments]), those will be unaffected by changes in ,(tt
+[inputs]), which would break this particular use case. It remains to
+be seen how we can allow ,(tt [#$]) forms while not sacrificing this
+flexibility.)]))
+
+ (section :title [Program Generation]
+
+ (p []))
+
+ (section :title [System Services]
+
+ (p [GuixSD, the Guix-based GNU/Linux distribution, was one of
+the main motivations behind gexps. The principle behind GuixSD is
+that, given a high-level ,(tt [operating-system]) declaration that
+specifies user accounts, system services, and other OS settings, the
+complete system is instantiated to the store. To achieve that, a lot
+of files must be generated: start/stop script for all the system
+services (daemons, operations such as file system mounts, etc.),
+configuration files, and an initial RAM disk (or ,(emph [initrd])) for
+the kernel Linux.])
+
+ (figure
+ :legend [Creating a derivation that builds an initrd.]
+ :ident "fig-initrd"
+
+ (prog :line #f
+ (source :language guix :file "code/initrd.scm")))
+
+ (p [The initrd is a small file system image that the kernel
+Linux mounts as its initial file system. It then runs the ,(tt
+[/init]) program therein; this program is responsible for mounting the
+real root file system and for loading any drivers needed to achieve
+that. If the file system is encrypted, this is also the place where a
+“mapped device” is set up to decrypt it. On GuixSD, this init program
+is a Scheme program that we generate based on the OS configuration,
+using gexps. Figure ,(ref :figure "fig-initrd") illustrates the
+creation of an initrd. Here ,(tt [expression->initrd]) returns a
+derivation that builds an initrd containing the given gexp as the ,(tt
+[/init]) program. The staged program in this examples calls the ,(tt
+[boot-system]) function from the ,(tt [(gnu build linux-boot)])
+module. The initrd is automatically populated with Guile and its
+dependencies, the closure of the ,(tt [(gnu build linux-boot)])
+module, and the ,(tt [kodir]) store item which contains kernel modules
+(drivers). That all the relevant store items referred to by the gexp,
+directly or indirectly, are “pulled” in the initrd comes for free.])
+ (p [Once the root file system is mounted, the initrd passes
+control to the Shepherd, our daemon-managing daemon,(ref :url (url
+"https://gnu.org/s/shepherd/")). The Shepherd is responsible for
+starting system services—from the email or SSH daemon to the X
+graphical display server—and for doing other initialization operations
+such as mounting additional file systems. The Shepherd is written in
+Scheme; its “configuration file” is a small Scheme program that
+instantiates service objects, each of which has a ,(tt [start]) and a
+,(tt [stop]) method. In GuixSD, service definitions take the form of
+a host-side structure with a ,(tt [start]) and ,(tt [stop]) field,
+both of which are gexps. Those gexps are eventually ,(emph [spliced])
+into the Shepherd configuration file.])
+ (p [Since this is all Scheme, and since Guix has a ,(tt [(gnu
+build linux-container)]) module to create Linux ,(emph [containers])
+(isolated execution environments), we were able to reuse this
+container module within the Shepherd ,(ref :bib
+'courtes2017:servicecontainers). Essentially, the only thing we had
+to do to achieve this was to (1) wrap our ,(tt [start]) gexp in ,(tt
+[with-imported-modules]) so that it has access to the container
+functionality, and (2) use our start-process-in-container function
+lieu of the Shepherd’s own start-process function. This is a good
+example of cross-stage code sharing, where the second stage in this
+case is the operating system’s run-time environment.])))
(chapter :title [Limitations]
:ident "limitations"
- (p [hygiene, modules in scope, cross-stage debugging info]))
+ (p [,(bold [Hygiene.]) A limitation of gexp that should be
+visible to anyone used to the wonders of Scheme macros is the lack of
+“hygiene”. Namely, our implementation of gexps does not preserve
+scope in its output. For example, in ,(tt [(let ((inner #~x))
+#~(lambda (x) #$inner))]), the ,(tt [inner]) staged expression refers
+to the argument of the staged lambda even though it is not in scope.
+This example illustrated ,(emph [unintended variable capture]): ,(tt
+[x]) in ,(tt [inner]) should be seen as a free variable, but it
+instead ends up referring to the ,(tt [x]) that is in scope in the
+staged code.])
+ (p [A well-documented approach to the problem is ,(symbol
+"alpha")-renaming binding that are introduced ,(ref :bib
+'(dybvig1992:syntax-case kiselyov2008:metascheme)). However, to do
+that, we must first be able to identify binding constructs. This
+turns out to be hard to achieve in Scheme because macros can define
+new bindings constructs. The macro expander, of course, does this and
+more already. However, we do not want to macro-expand staged code;
+instead, macro expansion should be performed “the normal way”, by the
+Guile program that compiles or evaluate the staged code. Again, this
+ensures reproducibility across Guix installations since we control
+precisely the Guile variant used in derivations whereas we do not
+control the Guile variant used to evaluate “host-side” code.])
+ (p [,(bold [Modules in scope.])])
+ (p [,(bold [Cross-stage debugging.])]))
(chapter :title [Related Work]
:ident "related"
+ (p [Nix shares the same concerns as Guix: its language must be
+able to include references to store items (derivation results) in
+generated code while not keeping track of derivations this generated
+code depends on. However, Nix is a single-stage language, only used
+on the “host side”, which describes package derivations and their
+composition, while the “build side” is left to other languages such as
+Bash or Perl. Nix provides a ,(emph [string interpolation]) mechanism
+that allows users to splice arbitrary Nix expressions in strings ,(ref
+:bib 'dolstra2010:nixos); when such an expression refers to a
+derivation, the Nix interpreter records this dependency in the string
+context and substitutes the reference with the output file name of the
+derivation.])
+ (p [Because Nix views this generated code as mere strings, it
+does provide any guarantee on the generated code (notably syntactic
+correctness). The string interpolation syntax (,(tt [${])…,(tt [}])
+sequences), often clashes with the target’s language syntax (e.g.,
+Bash uses dollar-brace syntax to reference variables), which can lead
+to subtle errors and contrain developers to resort to non-trivial
+escaping syntax. The “code-as-string” paradigm also has other side
+effects: comments and whitespace in those strings is preserved, which
+means those can trigger a rebuild of the derivation, which is
+inconvenient.])
+ (p [Code staging in Scheme has been studied in the context of
+,(emph [macros]). Dybvig’s work ,(ref :bib 'dybvig1992:syntax-case)
+introduced “hygienic” macros in Scheme—i.e., macros that generate
+well-scoped code, without unintended capture of variables—which later
+made it into the Sixth Report on Scheme (R6RS). MacroML achieves
+something similar in the context of ML, which is statically-typed
+,(ref :bib 'ganz2001:macroml). Both tools allow uses to define new
+binding constructs; the macro expander recognizes those bindings
+constructs, which allows it to track bindings and preserve hygiene,
+notably by ,(symbol "alpha")-renaming introduced bindings.])
+ (p [Kiselyov wrote MetaScheme as a translation of MetaOCaml’s
+staging primitives, ,(tt [bracket]), ,(tt [escape]), and ,(tt [lift])
+,(ref :bib 'kiselyov2008:metascheme). The beauty of MetaScheme is
+that it extends Scheme through a set of macros and does not
+necessitate any modification to the host Scheme implementation.
+MetaScheme demonstrates an implementation of hygience through ,(symbol
+"alpha")-renaming; however, it only considers a few core binding
+constructs and does not address hygiene in the presence of
+user-defined binding constructs (macros). This strategy is
+appropriate in a macro-less language with a fixed set of binding
+constructs like OCaml, but unsuitable for Scheme programs that use
+macros. MetaScheme supports cross-stage persistence (CSP) but the
+implementation provided assumes that an array of cross-stage values is
+shared among stages, which is hard to transpose to concrete
+settings.])
+ (p [Staged Scheme, or S,(sup [2]), also improved on Lisp
+quasiquotations by providing bracket, escape, and lift forms separate
+from ,(tt [quasiquote]) and ,(tt [unquote]) ,(ref :bib 'wang2002:s2).
+Therefore, as with ,(tt [syntax-case]) and gexps, quoted code has a
+disjoint type as opposed to being a list. S,(sup [2]) supports CSP,
+whereby staged code can seemlessly refer to variables of the previous
+stage that are in scope; the article does not explain the
+implementation strategy. S,(sup [2])’s focus is on programs with
+possibly more than two stages, whereas gexp are, in practice, used for
+two-stage programs. The article discusses ,(emph [code regeneration])
+at run time; gexps have a similar requirement here: at run time a
+given gexp may be instantiated for different system types, for
+instance ,(tt [x86_64-linux]) and ,(tt [i686-linux]).])
+ (p [While Guix uses “homogeneous” staging, where the source and
+staged language are the same, Hop instead performs ,(emph
+[heterogenous staging]): the source language is Scheme, but the
+generated code is JavaScript ,(ref :bib 'serrano2010:multitier). Hop
+has a ,(tt [~]) (tilde) form to introduce staged expressions, and a
+,(tt [$]) (dollar) form to escape to unstaged code. Hop involves two
+code stages: server-side code and client-side code. Unlike
+G-expressions, support for tilde forms is built in the Hop compiler,
+and tild forms are not first-class objects. Hop comes with useful
+multi-stage debugging facilities not found in Guix, such as the
+ability to display cross-stage stack traces with correct source
+location information.])
+
;; See refs at
https://www.researchgate.net/publication/2632322_Writing_Hygienic_Macros_in_Scheme_with_Syntax-Case
- (p (ref :bib 'dybvig1992:syntax-case)
- (ref :bib 'kiselyov2008:metascheme)
- (ref :bib 'serrano2010:multitier)
- (ref :bib 'dolstra2010:nixos)))
+
+ ;; "Implicitly Heterogeneous Multi-Stage Programming"
+ ;; https://link.springer.com/article/10.1007/s00354-007-0020-x
+
+ ;; http://dl.acm.org/citation.cfm?doid=2543728.2543740
+
+ )
(chapter :title [Conclusion]
:ident "conclusion")
diff --git a/doc/gpce-2017/staging.sbib b/doc/gpce-2017/staging.sbib
index fadb80c..328f733 100644
--- a/doc/gpce-2017/staging.sbib
+++ b/doc/gpce-2017/staging.sbib
@@ -20,3 +20,70 @@
(url "http://dx.doi.org/10.1007/s10990-010-9061-9")
(publisher "Kluwer Academic Publishers")
(address "Hingham, MA, USA"))
+
+(misc kiselyov2008:metascheme
+ (author "Oleg Kiselyov")
+ (year "2008")
+ (month "August")
+ (url "http://okmij.org/ftp/meta-programming/#meta-scheme")
+ (title "MetaScheme, or untyped MetaOCaml"))
+
+(inbook wang2002:s2
+ (author "Zhenghao Wang and Richard R. Muntz")
+ (editor "Batory, Don and Consel, Charles and Taha, Walid")
+ (title "Managing Dynamic Changes in Multi-stage Program Generation Systems")
+ (booktitle "Proceedings of Generative Programming and Component Engineering
(GPCE)")
+ (year "2002")
+ (month "October")
+ (publisher "Springer Berlin Heidelberg")
+ (address "Berlin, Heidelberg")
+ (pages "316--334")
+ (abstract "In a multi-stage program generation (MSPG) system, a stage-s
program generates a stage-s + 1 program when the values of some variables are
known. We call such variables program parameters for stage-s.")
+ (isbn "978-3-540-45821-0")
+ (doi "10.1007/3-540-45821-2_20")
+ (url "http://dx.doi.org/10.1007/3-540-45821-2_20"))
+
+(inproceedings ganz2001:macroml
+ (author "Steven E. Ganz, Amr Sabry, and Walid Taha")
+ (title "Macros As Multi-stage Computations: Type-safe, Generative, Binding
Macros in MacroML")
+ (booktitle "Proceedings of the Sixth ACM SIGPLAN International Conference on
Functional Programming")
+ (series "ICFP '01")
+ (year "2001")
+ (isbn "1-58113-415-0")
+ (location "Florence, Italy")
+ (pages "74--85")
+ (numpages "12")
+ (url "http://doi.acm.org/10.1145/507635.507646")
+ (doi "10.1145/507635.507646")
+ (acmid "507646")
+ (publisher "ACM")
+ (address "New York, NY, USA"))
+
+(misc courtes2017:servicecontainers
+ (author "Ludovic Courtès")
+ (title "Running system services in containers")
+ (url
"https://gnu.org/s/guix/news/running-system-services-in-containers.html")
+ (month "April")
+ (year "2017"))
+;Proceedings of the ACM SIGPLAN Workshop on Partial Evaluation and
Semantics-Based Program Manipulation (PEPM 1999), number NS-99-1 in BRICS Note
Series, pages 4--12, San Antonio, Texas, 1999.
+
+(inproceedings bawden1999:quasiquotation
+ (author "Alan Bawden")
+ (title "Quasiquotation in Lisp")
+ (booktitle "Proceedings of the ACM SIGPLAN Workshop on Partial
+Evaluation and Semantics-Based Program Manipulation (PEPM 1999)")
+ (pages "4--12")
+ (year "1999")
+ (editor "Olivier Danvy")
+ (address "San Antonio, Texas, USA")
+ (url "http://repository.readscheme.org/ftp/papers/pepm99/bawden.pdf"))
+
+;; http://dl.acm.org/citation.cfm?doid=2661103.2661109
+
+#|
+(defun skr-from-bibtex ()
+ "Vaguely convert the BibTeX snippets after POINT to SBibTeX."
+ (interactive)
+ (while (re-search-forward "\\([a-z_-]+\\) *= *[{\"]\\([^}\"]+\\)[}\"] *, *$"
nil nil)
+ (replace-match "(\\1 \"\\2\")")))
+|#
- 14/14: gpce-2017: Adjust ACM boilerplate., (continued)
- 14/14: gpce-2017: Adjust ACM boilerplate., Ludovic Courtčs, 2017/09/01
- 05/14: gpce-2017: Shrink., Ludovic Courtčs, 2017/09/01
- 01/14: doc: Add GPCE paper., Ludovic Courtčs, 2017/09/01
- 08/14: gpce-2017: Add an explicit license., Ludovic Courtčs, 2017/09/01
- 09/14: gpce-2017: Fix typo., Ludovic Courtčs, 2017/09/01
- 03/14: gpce-2017: Write some more., Ludovic Courtčs, 2017/09/01
- 04/14: gpce-2017: Fixlets., Ludovic Courtčs, 2017/09/01
- 07/14: gpce-2017: Deanonymize., Ludovic Courtčs, 2017/09/01
- 13/14: gpce-2017: Shrink to 7 pages (10pt font)., Ludovic Courtčs, 2017/09/01
- 06/14: gpce-2017: Tweak some more., Ludovic Courtčs, 2017/09/01
- 02/14: gpce-2017: Write, write, write.,
Ludovic Courtčs <=
- 12/14: gpce-2017: Use acmart v1.47., Ludovic Courtčs, 2017/09/01