summaryrefslogtreecommitdiff
path: root/guix
diff options
context:
space:
mode:
authorChristopher Baines <mail@cbaines.net>2023-03-02 10:37:28 +0000
committerChristopher Baines <mail@cbaines.net>2023-03-02 10:55:08 +0000
commit7df09ee0ab3e7962ef27859ce87e06a323059284 (patch)
treed81334f742ddcb9a1ee63961ca6410922980af1c /guix
parent2ac51ec99b58b50c08ba719a8c7e9dba0330b065 (diff)
parentaf95f2d8f98eb2c8c64954bb2fd0b70838899174 (diff)
Merge remote-tracking branch 'savannah/master' into core-updates
Conflicts: gnu/local.mk gnu/packages/autotools.scm gnu/packages/cmake.scm gnu/packages/gnuzilla.scm gnu/packages/haskell.scm gnu/packages/pdf.scm gnu/packages/python-xyz.scm gnu/packages/samba.scm gnu/packages/tex.scm gnu/packages/tls.scm gnu/packages/wxwidgets.scm
Diffstat (limited to 'guix')
-rw-r--r--guix/build-system/cargo.scm1
-rw-r--r--guix/build-system/copy.scm1
-rw-r--r--guix/build-system/dune.scm1
-rw-r--r--guix/build-system/font.scm1
-rw-r--r--guix/build-system/guile.scm2
-rw-r--r--guix/build-system/haskell.scm5
-rw-r--r--guix/build-system/meson.scm13
-rw-r--r--guix/build-system/ocaml.scm1
-rw-r--r--guix/build-system/ruby.scm1
-rw-r--r--guix/build-system/scons.scm1
-rw-r--r--guix/build-system/texlive.scm1
-rw-r--r--guix/build-system/tree-sitter.scm195
-rw-r--r--guix/build-system/waf.scm1
-rw-r--r--guix/build/haskell-build-system.scm136
-rw-r--r--guix/build/syscalls.scm8
-rw-r--r--guix/build/tree-sitter-build-system.scm153
-rw-r--r--guix/channels.scm9
-rw-r--r--guix/cpio.scm10
-rw-r--r--guix/download.scm6
-rw-r--r--guix/git.scm26
-rw-r--r--guix/import/cran.scm66
-rw-r--r--guix/import/hackage.scm21
-rw-r--r--guix/import/stackage.scm6
-rw-r--r--guix/licenses.scm2
-rw-r--r--guix/progress.scm45
-rw-r--r--guix/rpm.scm630
-rw-r--r--guix/scripts.scm4
-rw-r--r--guix/scripts/build.scm18
-rw-r--r--guix/scripts/container/exec.scm10
-rw-r--r--guix/scripts/describe.scm6
-rw-r--r--guix/scripts/environment.scm4
-rw-r--r--guix/scripts/home.scm8
-rw-r--r--guix/scripts/home/edit.scm6
-rw-r--r--guix/scripts/import.scm5
-rw-r--r--guix/scripts/offload.scm7
-rw-r--r--guix/scripts/pack.scm564
-rw-r--r--guix/scripts/package.scm6
-rw-r--r--guix/scripts/pull.scm6
-rw-r--r--guix/scripts/repl.scm1
-rw-r--r--guix/scripts/shell.scm10
-rw-r--r--guix/scripts/system.scm9
-rw-r--r--guix/scripts/system/edit.scm6
-rw-r--r--guix/ssh.scm2
-rw-r--r--guix/status.scm18
-rw-r--r--guix/ui.scm49
-rw-r--r--guix/utils.scm8
46 files changed, 1728 insertions, 361 deletions
diff --git a/guix/build-system/cargo.scm b/guix/build-system/cargo.scm
index 60c35eed07..912400a191 100644
--- a/guix/build-system/cargo.scm
+++ b/guix/build-system/cargo.scm
@@ -123,6 +123,7 @@ to NAME and VERSION."
(gexp->derivation name builder
#:system system
#:target #f
+ #:graft? #f
#:guile-for-build guile))
(define (package-cargo-inputs p)
diff --git a/guix/build-system/copy.scm b/guix/build-system/copy.scm
index e15dc9f616..6cd3ec0216 100644
--- a/guix/build-system/copy.scm
+++ b/guix/build-system/copy.scm
@@ -133,6 +133,7 @@
#:system system
#:target #f
#:substitutable? substitutable?
+ #:graft? #f
#:guile-for-build guile)))
(define copy-build-system
diff --git a/guix/build-system/dune.scm b/guix/build-system/dune.scm
index 05784feb32..dc280242fb 100644
--- a/guix/build-system/dune.scm
+++ b/guix/build-system/dune.scm
@@ -156,6 +156,7 @@ provides a 'setup.ml' file as its build system."
(gexp->derivation name builder
#:system system
#:target #f
+ #:graft? #f
#:guile-for-build guile))
(define dune-build-system
diff --git a/guix/build-system/font.scm b/guix/build-system/font.scm
index c43fb9a542..925933516c 100644
--- a/guix/build-system/font.scm
+++ b/guix/build-system/font.scm
@@ -112,6 +112,7 @@
(gexp->derivation name builder
#:system system
#:target #f
+ #:graft? #f
#:guile-for-build guile)))
(define font-build-system
diff --git a/guix/build-system/guile.scm b/guix/build-system/guile.scm
index 36a88e181a..ffc892260a 100644
--- a/guix/build-system/guile.scm
+++ b/guix/build-system/guile.scm
@@ -114,6 +114,7 @@
(gexp->derivation name builder
#:system system
#:target #f
+ #:graft? #f
#:guile-for-build guile)))
(define* (guile-cross-build name
@@ -170,6 +171,7 @@
(gexp->derivation name builder
#:system system
#:target target
+ #:graft? #f
#:guile-for-build guile)))
(define guile-build-system
diff --git a/guix/build-system/haskell.scm b/guix/build-system/haskell.scm
index dc83512d30..a37b3a938c 100644
--- a/guix/build-system/haskell.scm
+++ b/guix/build-system/haskell.scm
@@ -109,10 +109,7 @@ version REVISION."
,@(standard-packages)))
(build-inputs `(("haskell" ,haskell)
,@native-inputs))
- ;; XXX: this is a hack to get around issue #41569.
- (outputs (match outputs
- (("out") (cons "static" outputs))
- (_ outputs)))
+ (outputs outputs)
(build haskell-build)
(arguments
(substitute-keyword-arguments
diff --git a/guix/build-system/meson.scm b/guix/build-system/meson.scm
index 0948ad92b5..bf43303027 100644
--- a/guix/build-system/meson.scm
+++ b/guix/build-system/meson.scm
@@ -74,16 +74,9 @@ for TRIPLET."
;; for selecting optimisations, so set it to something
;; arbitrary.
(#t "strawberries")))
- (endian . ,(cond ((string-prefix? "powerpc64le-" triplet) "little")
- ((string-prefix? "mips64el-" triplet) "little")
- ((target-x86-32? triplet) "little")
- ((target-x86-64? triplet) "little")
- ;; At least in Guix. Aarch64 and 32-bit arm
- ;; have a big-endian mode as well.
- ((target-arm? triplet) "little")
- ((target-ppc32? triplet) "big")
- ((target-riscv64? triplet) "little")
- (#t (error "meson: unknown architecture"))))))
+ (endian . ,(if (target-little-endian? triplet)
+ "little"
+ "big"))))
(define (make-binaries-alist triplet)
"Make an associatoin list describing what should go into
diff --git a/guix/build-system/ocaml.scm b/guix/build-system/ocaml.scm
index 5f4308a46e..6e1fc62a62 100644
--- a/guix/build-system/ocaml.scm
+++ b/guix/build-system/ocaml.scm
@@ -309,6 +309,7 @@ provides a 'setup.ml' file as its build system."
(gexp->derivation name builder
#:system system
#:target #f
+ #:graft? #f
#:guile-for-build guile))
(define ocaml-build-system
diff --git a/guix/build-system/ruby.scm b/guix/build-system/ruby.scm
index 342daf7978..0aa273b4f4 100644
--- a/guix/build-system/ruby.scm
+++ b/guix/build-system/ruby.scm
@@ -114,6 +114,7 @@ NAME and VERSION."
(gexp->derivation name build
#:system system
#:target #f
+ #:graft? #f
#:modules imported-modules
#:guile-for-build guile)))
diff --git a/guix/build-system/scons.scm b/guix/build-system/scons.scm
index 7a02fa8a0f..9af24d40f8 100644
--- a/guix/build-system/scons.scm
+++ b/guix/build-system/scons.scm
@@ -121,6 +121,7 @@ provides a 'SConstruct' file as its build system."
(gexp->derivation name builder
#:system system
#:target #f
+ #:graft? #f
#:guile-for-build guile))
(define scons-build-system
diff --git a/guix/build-system/texlive.scm b/guix/build-system/texlive.scm
index dbb72cd24a..336e192d83 100644
--- a/guix/build-system/texlive.scm
+++ b/guix/build-system/texlive.scm
@@ -182,6 +182,7 @@ level package ID."
(gexp->derivation name builder
#:system system
#:target #f
+ #:graft? #f
#:substitutable? substitutable?
#:guile-for-build guile)))
diff --git a/guix/build-system/tree-sitter.scm b/guix/build-system/tree-sitter.scm
new file mode 100644
index 0000000000..21c4eb35b2
--- /dev/null
+++ b/guix/build-system/tree-sitter.scm
@@ -0,0 +1,195 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 Pierre Langlois <pierre.langlois@gmx.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix build-system tree-sitter)
+ #:use-module (guix store)
+ #:use-module (guix utils)
+ #:use-module (guix packages)
+ #:use-module (guix gexp)
+ #:use-module (guix monads)
+ #:use-module (guix search-paths)
+ #:use-module (guix build-system)
+ #:use-module (guix build-system gnu)
+ #:use-module (guix build-system node)
+ #:use-module (ice-9 match)
+ #:export (%tree-sitter-build-system-modules
+ tree-sitter-build
+ tree-sitter-build-system))
+
+(define %tree-sitter-build-system-modules
+ ;; Build-side modules imported by default.
+ `((guix build tree-sitter-build-system)
+ ,@%node-build-system-modules))
+
+(define* (lower name
+ #:key source inputs native-inputs outputs system target
+ #:allow-other-keys
+ #:rest arguments)
+ "Return a bag for NAME from the given arguments."
+ (define private-keywords
+ `(#:inputs #:native-inputs #:outputs ,@(if target
+ '()
+ '(#:target))))
+ (define node
+ (module-ref (resolve-interface '(gnu packages node))
+ 'node-lts))
+ (define tree-sitter
+ (module-ref (resolve-interface '(gnu packages tree-sitter))
+ 'tree-sitter))
+ (define tree-sitter-cli
+ (module-ref (resolve-interface '(gnu packages tree-sitter))
+ 'tree-sitter-cli))
+ ;; Grammars depend on each other via JS modules, which we package into a
+ ;; dedicated js output.
+ (define grammar-inputs
+ (map (match-lambda
+ ((name package)
+ `(,name ,package "js")))
+ inputs))
+ (bag
+ (name name)
+ (system system) (target target)
+ (build-inputs `(,@(if source
+ `(("source" ,source))
+ '())
+ ("node" ,node)
+ ("tree-sitter-cli" ,tree-sitter-cli)
+ ,@native-inputs
+ ,@(if target '() grammar-inputs)
+ ;; Keep the standard inputs of 'gnu-build-system'.
+ ,@(if target
+ (standard-cross-packages target 'host)
+ '())
+ ,@(standard-packages)))
+ (host-inputs `(("tree-sitter" ,tree-sitter)
+ ,@(if target grammar-inputs '())))
+ ;; Keep the standard inputs of 'gnu-buid-system'.
+ (target-inputs (if target
+ (standard-cross-packages target 'target)
+ '()))
+ ;; XXX: this is a hack to get around issue #41569.
+ (outputs (match outputs
+ (("out") (cons "js" outputs))
+ (_ outputs)))
+ (build (if target tree-sitter-cross-build tree-sitter-build))
+ (arguments (strip-keyword-arguments private-keywords arguments))))
+
+(define* (tree-sitter-build name inputs
+ #:key
+ source
+ (phases '%standard-phases)
+ (grammar-directories '("."))
+ (tests? #t)
+ (outputs '("out" "js"))
+ (search-paths '())
+ (system (%current-system))
+ (guile #f)
+ (imported-modules %tree-sitter-build-system-modules)
+ (modules '((guix build utils)
+ (guix build tree-sitter-build-system))))
+ (define builder
+ (with-imported-modules imported-modules
+ #~(begin
+ (use-modules #$@(sexp->gexp modules))
+ (tree-sitter-build #:name #$name
+ #:source #+source
+ #:system #$system
+ #:phases #$phases
+ #:tests? #$tests?
+ #:grammar-directories '#$grammar-directories
+ #:outputs #$(outputs->gexp outputs)
+ #:search-paths
+ '#$(sexp->gexp
+ (map search-path-specification->sexp
+ search-paths))
+ #:inputs #$(input-tuples->gexp inputs)))))
+
+ (mlet %store-monad ((guile (package->derivation (or guile (default-guile))
+ system #:graft? #f)))
+ (gexp->derivation name builder
+ #:system system
+ #:guile-for-build guile)))
+
+(define* (tree-sitter-cross-build name
+ #:key
+ target
+ build-inputs target-inputs host-inputs
+ guile source
+ (phases '%standard-phases)
+ (grammar-directories '("."))
+ (tests? #t)
+ (outputs '("out" "js"))
+ (search-paths '())
+ (native-search-paths '())
+ (system (%current-system))
+ (build (nix-system->gnu-triplet system))
+ (imported-modules
+ %tree-sitter-build-system-modules)
+ (modules
+ '((guix build utils)
+ (guix build tree-sitter-build-system))))
+ (define builder
+ (with-imported-modules imported-modules
+ #~(begin
+ (use-modules #$@(sexp->gexp modules))
+
+ (define %build-host-inputs
+ #+(input-tuples->gexp build-inputs))
+
+ (define %build-target-inputs
+ (append #$(input-tuples->gexp host-inputs)
+ #+(input-tuples->gexp target-inputs)))
+
+ (define %build-inputs
+ (append %build-host-inputs %build-target-inputs))
+
+ (tree-sitter-build #:name #$name
+ #:source #+source
+ #:system #$system
+ #:build #$build
+ #:target #$target
+ #:phases #$phases
+ #:tests? #$tests?
+ #:grammar-directories '#$grammar-directories
+ #:outputs #$(outputs->gexp outputs)
+ #:inputs %build-target-inputs
+ #:native-inputs %build-host-inputs
+ #:search-paths '
+ #$(sexp->gexp
+ (map search-path-specification->sexp
+ search-paths))
+ #:native-search-paths
+ '#$(sexp->gexp
+ (map
+ search-path-specification->sexp
+ native-search-paths))))))
+
+ (mlet %store-monad ((guile (package->derivation (or guile (default-guile))
+ system #:graft? #f)))
+ (gexp->derivation name builder
+ #:system system
+ #:target target
+ #:guile-for-build guile)))
+
+(define tree-sitter-build-system
+ (build-system
+ (name 'tree-sitter)
+ (description "The Tree-sitter grammar build system")
+ (lower lower)))
+
+;;; tree-sitter.scm ends here
diff --git a/guix/build-system/waf.scm b/guix/build-system/waf.scm
index e8cd5520b8..1d520050f6 100644
--- a/guix/build-system/waf.scm
+++ b/guix/build-system/waf.scm
@@ -111,6 +111,7 @@ as its build system."
(gexp->derivation name build
#:system system
#:target #f
+ #:graft? #f
#:modules imported-modules
#:guile-for-build guile)))
diff --git a/guix/build/haskell-build-system.scm b/guix/build/haskell-build-system.scm
index ef6cb316ee..0e94cf59a5 100644
--- a/guix/build/haskell-build-system.scm
+++ b/guix/build/haskell-build-system.scm
@@ -5,6 +5,8 @@
;;; Copyright © 2018, 2020 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2018 Alex Vong <alexvong1995@gmail.com>
;;; Copyright © 2021 John Kehayias <john.kehayias@protonmail.com>
+;;; Copyright © 2022 Simon Tournier <zimon.toutoune@gmail.com>
+;;; Copyright © 2022 Philip Munksgaard <philip@munksgaard.me>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -97,12 +99,14 @@ and parameters ~s~%"
,@(if tests?
'("--enable-tests")
'())
- ;; Build and link with shared libraries
+ ;; Build static and shared libraries.
"--enable-shared"
- "--enable-executable-dynamic"
+ "--enable-static"
+ ;; Link executables statically by default.
+ "--disable-executable-dynamic"
"--ghc-option=-fPIC"
- ,(string-append "--ghc-option=-optl=-Wl,-rpath=" (or lib out)
- "/lib/$compiler/$pkg-$version")
+ ;; Ensure static libraries can be used with -Wl,--gc-sections for size.
+ "--ghc-option=-split-sections"
,@configure-flags)))
;; Cabal errors if GHC_PACKAGE_PATH is set during 'configure', so unset
;; and restore it.
@@ -118,8 +122,7 @@ and parameters ~s~%"
(setenv "CONFIG_SHELL" "sh"))
(run-setuphs "configure" params)
- (setenv "GHC_PACKAGE_PATH" ghc-path)
- #t))
+ (setenv "GHC_PACKAGE_PATH" ghc-path)))
(define* (build #:key parallel-build? #:allow-other-keys)
"Build a given Haskell package."
@@ -130,18 +133,7 @@ and parameters ~s~%"
(define* (install #:key outputs #:allow-other-keys)
"Install a given Haskell package."
- (run-setuphs "copy" '())
- (when (assoc-ref outputs "static")
- (let ((static (assoc-ref outputs "static"))
- (lib (or (assoc-ref outputs "lib")
- (assoc-ref outputs "out"))))
- (for-each (lambda (static-lib)
- (let* ((subdir (string-drop static-lib (string-length lib)))
- (new (string-append static subdir)))
- (mkdir-p (dirname new))
- (rename-file static-lib new)))
- (find-files lib "\\.a$"))))
- #t)
+ (run-setuphs "copy" '()))
(define* (setup-compiler #:key system inputs outputs #:allow-other-keys)
"Setup the compiler environment."
@@ -175,8 +167,7 @@ and parameters ~s~%"
conf-files)
(invoke "ghc-pkg"
(string-append "--package-db=" %tmp-db-dir)
- "recache")
- #t))
+ "recache")))
(define* (register #:key name system inputs outputs #:allow-other-keys)
"Generate the compiler registration and binary package database files for a
@@ -215,15 +206,54 @@ given Haskell package."
(() #t) ;done
((id . tail)
(if (not (vhash-assoc id seen))
- (let ((dep-conf (string-append src "/" id ".conf"))
- (dep-conf* (string-append dest "/" id ".conf")))
- (when (not (file-exists? dep-conf))
+ (let* ((dep-conf (string-append src "/" id ".conf"))
+ (dep-conf* (string-append dest "/" id ".conf"))
+ (dep-conf-exists? (file-exists? dep-conf))
+ (dep-conf*-exists? (file-exists? dep-conf*))
+ (next-tail (append lst (if dep-conf-exists? (conf-depends dep-conf) '()))))
+ (unless dep-conf*-exists?
+ (unless dep-conf-exists?
(error (format #f "File ~a does not exist. This usually means the dependency ~a is missing. Was checking conf-file ~a." dep-conf id conf-file)))
- (copy-file dep-conf dep-conf*) ;XXX: maybe symlink instead?
- (loop (vhash-cons id #t seen)
- (append lst (conf-depends dep-conf))))
+ (copy-file dep-conf dep-conf*)) ;XXX: maybe symlink instead?
+ (loop (vhash-cons id #t seen) next-tail))
(loop seen tail))))))
+ (define (install-config-file conf-file dest output:doc output:lib)
+ ;; Copy CONF-FILE to DEST removing reference to OUTPUT:DOC from
+ ;; OUTPUT:LIB and using install-transitive-deps.
+ (let* ((contents (call-with-input-file conf-file read-string))
+ (id-rx (make-regexp "^id:[ \n\t]+([^ \t\n]+)$" regexp/newline))
+ (config-file-name+id
+ (match:substring (first (list-matches id-rx contents)) 1)))
+
+ (when (or
+ (and
+ (string? config-file-name+id)
+ (string-null? config-file-name+id))
+ (not config-file-name+id))
+ (error (format #f "The package id for ~a is empty. This is a bug." conf-file)))
+
+ ;; Remove reference to "doc" output from "lib" (or "out") by rewriting the
+ ;; "haddock-interfaces" field and removing the optional "haddock-html"
+ ;; field in the generated .conf file.
+ (when output:doc
+ (substitute* conf-file
+ (("^haddock-html: .*") "\n")
+ (((format #f "^haddock-interfaces: ~a" output:doc))
+ (string-append "haddock-interfaces: " output:lib)))
+ ;; Move the referenced file to the "lib" (or "out") output.
+ (match (find-files output:doc "\\.haddock$")
+ ((haddock-file . rest)
+ (let* ((subdir (string-drop haddock-file (string-length output:doc)))
+ (new (string-append output:lib subdir)))
+ (mkdir-p (dirname new))
+ (rename-file haddock-file new)))
+ (_ #f)))
+ (install-transitive-deps conf-file %tmp-db-dir dest)
+ (rename-file conf-file
+ (string-append dest "/"
+ config-file-name+id ".conf"))))
+
(let* ((out (assoc-ref outputs "out"))
(doc (assoc-ref outputs "doc"))
(haskell (assoc-ref inputs "haskell"))
@@ -233,7 +263,6 @@ given Haskell package."
(config-dir (string-append lib
"/ghc-" version
"/" name ".conf.d"))
- (id-rx (make-regexp "^id:[ \n\t]+([^ \t\n]+)$" regexp/newline))
(config-file (string-append out "/" name ".conf"))
(params
(list (string-append "--gen-pkg-config=" config-file))))
@@ -241,53 +270,24 @@ given Haskell package."
;; The conf file is created only when there is a library to register.
(when (file-exists? config-file)
(mkdir-p config-dir)
- (let* ((contents (call-with-input-file config-file read-string))
- (config-file-name+id (match:substring (first (list-matches id-rx contents)) 1)))
-
- (when (or
- (and
- (string? config-file-name+id)
- (string-null? config-file-name+id))
- (not config-file-name+id))
- (error (format #f "The package id for ~a is empty. This is a bug." config-file)))
-
- ;; Remove reference to "doc" output from "lib" (or "out") by rewriting the
- ;; "haddock-interfaces" field and removing the optional "haddock-html"
- ;; field in the generated .conf file.
- (when doc
- (substitute* config-file
- (("^haddock-html: .*") "\n")
- (((format #f "^haddock-interfaces: ~a" doc))
- (string-append "haddock-interfaces: " lib)))
- ;; Move the referenced file to the "lib" (or "out") output.
- (match (find-files doc "\\.haddock$")
- ((haddock-file . rest)
- (let* ((subdir (string-drop haddock-file (string-length doc)))
- (new (string-append lib subdir)))
- (mkdir-p (dirname new))
- (rename-file haddock-file new)))
- (_ #f)))
- (install-transitive-deps config-file %tmp-db-dir config-dir)
- (rename-file config-file
- (string-append config-dir "/"
- config-file-name+id ".conf"))
- (invoke "ghc-pkg"
- (string-append "--package-db=" config-dir)
- "recache")))
- #t))
+ (if (file-is-directory? config-file)
+ (for-each (cut install-config-file <> config-dir doc lib)
+ (find-files config-file))
+ (install-config-file config-file config-dir doc lib))
+ (invoke "ghc-pkg"
+ (string-append "--package-db=" config-dir)
+ "recache"))))
(define* (check #:key tests? test-target #:allow-other-keys)
"Run the test suite of a given Haskell package."
(if tests?
(run-setuphs test-target '())
- (format #t "test suite not run~%"))
- #t)
+ (format #t "test suite not run~%")))
(define* (haddock #:key outputs haddock? haddock-flags #:allow-other-keys)
"Generate the Haddock documentation of a given Haskell package."
(when haddock?
- (run-setuphs "haddock" haddock-flags))
- #t)
+ (run-setuphs "haddock" haddock-flags)))
(define* (patch-cabal-file #:key cabal-revision #:allow-other-keys)
(when cabal-revision
@@ -296,8 +296,7 @@ given Haskell package."
((original)
(format #t "replacing ~s with ~s~%" original cabal-revision)
(copy-file cabal-revision original))
- (_ (error "Could not find a Cabal file to patch."))))
- #t)
+ (_ (error "Could not find a Cabal file to patch.")))))
(define* (generate-setuphs #:rest empty)
"Generate a default Setup.hs if needed."
@@ -307,8 +306,7 @@ given Haskell package."
(with-output-to-file "Setup.hs"
(lambda ()
(format #t "import Distribution.Simple~%")
- (format #t "main = defaultMain~%"))))
- #t)
+ (format #t "main = defaultMain~%")))))
(define %standard-phases
(modify-phases gnu:%standard-phases
diff --git a/guix/build/syscalls.scm b/guix/build/syscalls.scm
index 0358960ff5..df9b9f6ac7 100644
--- a/guix/build/syscalls.scm
+++ b/guix/build/syscalls.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2014-2022 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2014-2023 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2015 David Thompson <davet@gnu.org>
;;; Copyright © 2015 Mark H Weaver <mhw@netris.org>
;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
@@ -1400,7 +1400,8 @@ exception if it's already taken."
thunk
(lambda ()
(when port
- (unlock-file port))))))
+ (unlock-file port)
+ (delete-file file))))))
(define (call-with-file-lock/no-wait file thunk handler)
(let ((port #f))
@@ -1428,7 +1429,8 @@ exception if it's already taken."
thunk
(lambda ()
(when port
- (unlock-file port))))))
+ (unlock-file port)
+ (delete-file file))))))
(define-syntax-rule (with-file-lock file exp ...)
"Wait to acquire a lock on FILE and evaluate EXP in that context."
diff --git a/guix/build/tree-sitter-build-system.scm b/guix/build/tree-sitter-build-system.scm
new file mode 100644
index 0000000000..4106728bdf
--- /dev/null
+++ b/guix/build/tree-sitter-build-system.scm
@@ -0,0 +1,153 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 Pierre Langlois <pierre.langlois@gmx.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix build tree-sitter-build-system)
+ #:use-module ((guix build node-build-system) #:prefix node:)
+ #:use-module (guix build json)
+ #:use-module (guix build utils)
+ #:use-module (ice-9 match)
+ #:use-module (ice-9 regex)
+ #:use-module (srfi srfi-1)
+ #:export (%standard-phases
+ tree-sitter-build))
+
+;; Commentary:
+;;
+;; Build procedures for tree-sitter grammar packages. This is the
+;; builder-side code, which builds on top of the node build-system.
+;;
+;; Tree-sitter grammars are written in JavaScript and compiled to a native
+;; shared object. The `tree-sitter generate' command invokes `node' in order
+;; to evaluate the grammar.js into a grammar.json file, which is then
+;; translated into C code. We then compile the C code ourselves. Packages
+;; also sometimes add extra manually written C/C++ code.
+;;
+;; In order to support grammars depending on each other, such as C and C++,
+;; JavaScript and TypeScript, this build-system installs the source of the
+;; node module in a dedicated "js" output.
+;;
+;; Code:
+
+(define* (patch-dependencies #:key inputs #:allow-other-keys)
+ "Rewrite dependencies in 'package.json'. We remove all runtime dependencies
+and replace development dependencies with tree-sitter grammar node modules."
+
+ (define (rewrite package.json)
+ (map (match-lambda
+ (("dependencies" @ . _)
+ '("dependencies" @))
+ (("devDependencies" @ . _)
+ `("devDependencies" @
+ ,@(filter-map (match-lambda
+ ((key . directory)
+ (let ((node-module
+ (string-append directory
+ "/lib/node_modules/"
+ key)))
+ (and (directory-exists? node-module)
+ `(,key . ,node-module)))))
+ (alist-delete "node" inputs))))
+ (other other))
+ package.json))
+
+ (node:with-atomic-json-file-replacement "package.json"
+ (match-lambda
+ (('@ . package.json)
+ (cons '@ (rewrite package.json))))))
+
+;; FIXME: The node build-system's configure phase does not support
+;; cross-compiling so we re-define it.
+(define* (configure #:key native-inputs inputs #:allow-other-keys)
+ (invoke (search-input-file (or native-inputs inputs) "/bin/npm")
+ "--offline" "--ignore-scripts" "install"))
+
+(define* (build #:key grammar-directories #:allow-other-keys)
+ (for-each (lambda (dir)
+ (with-directory-excursion dir
+ ;; Avoid generating binding code for other languages, we do
+ ;; not support this use-case yet and it relies on running
+ ;; `node-gyp' to build native addons.
+ (invoke "tree-sitter" "generate" "--no-bindings")))
+ grammar-directories))
+
+(define* (check #:key grammar-directories tests? #:allow-other-keys)
+ (when tests?
+ (for-each (lambda (dir)
+ (with-directory-excursion dir
+ (invoke "tree-sitter" "test")))
+ grammar-directories)))
+
+(define* (install #:key target grammar-directories outputs #:allow-other-keys)
+ (let ((lib (string-append (assoc-ref outputs "out")
+ "/lib/tree-sitter")))
+ (mkdir-p lib)
+ (define (compile-language dir)
+ (with-directory-excursion dir
+ (let ((lang (assoc-ref (call-with-input-file "src/grammar.json"
+ read-json)
+ "name"))
+ (source-file (lambda (path)
+ (if (file-exists? path)
+ path
+ #f))))
+ (apply invoke
+ `(,(if target
+ (string-append target "-g++")
+ "g++")
+ "-shared"
+ "-fPIC"
+ "-fno-exceptions"
+ "-O2"
+ "-g"
+ "-o" ,(string-append lib "/libtree-sitter-" lang ".so")
+ ;; An additional `scanner.{c,cc}' file is sometimes
+ ;; provided.
+ ,@(cond
+ ((source-file "src/scanner.c")
+ => (lambda (file) (list "-xc" "-std=c99" file)))
+ ((source-file "src/scanner.cc")
+ => (lambda (file) (list file)))
+ (else '()))
+ "-xc" "src/parser.c")))))
+ (for-each compile-language grammar-directories)))
+
+(define* (install-js #:key native-inputs inputs outputs #:allow-other-keys)
+ (invoke (search-input-file (or native-inputs inputs) "/bin/npm")
+ "--prefix" (assoc-ref outputs "js")
+ "--global"
+ "--offline"
+ "--loglevel" "info"
+ "--production"
+ ;; Skip scripts to prevent building bindings via GYP.
+ "--ignore-scripts"
+ "install" "../package.tgz"))
+
+(define %standard-phases
+ (modify-phases node:%standard-phases
+ (replace 'patch-dependencies patch-dependencies)
+ (replace 'configure configure)
+ (replace 'build build)
+ (replace 'check check)
+ (replace 'install install)
+ (add-after 'install 'install-js install-js)))
+
+(define* (tree-sitter-build #:key inputs (phases %standard-phases)
+ #:allow-other-keys #:rest args)
+ (apply node:node-build #:inputs inputs #:phases phases args))
+
+;;; tree-sitter-build-system.scm ends here
diff --git a/guix/channels.scm b/guix/channels.scm
index 40cbc4bb3a..d44e7a0a3a 100644
--- a/guix/channels.scm
+++ b/guix/channels.scm
@@ -952,6 +952,10 @@ be used as a profile hook."
(backtrace))))
(mkdir #$output))))
+ (define channels
+ (map (compose string->symbol manifest-entry-name)
+ (manifest-entries manifest)))
+
(gexp->derivation-in-inferior "guix-package-cache" build
profile
@@ -960,8 +964,9 @@ be used as a profile hook."
;; instead of failing.
#:silent-failure? #t
- #:properties '((type . profile-hook)
- (hook . package-cache))
+ #:properties `((type . profile-hook)
+ (hook . package-cache)
+ (channels . ,channels))
#:local-build? #t)))
(define %channel-profile-hooks
diff --git a/guix/cpio.scm b/guix/cpio.scm
index d4a7d5f1e0..876f61ea3c 100644
--- a/guix/cpio.scm
+++ b/guix/cpio.scm
@@ -170,7 +170,7 @@ using FILE-NAME as its file name."
#:size (stat:size st)
#:dev (stat:dev st)
#:rdev (stat:rdev st)
- #:name-size (string-length file-name))))
+ #:name-size (string-utf8-length file-name))))
(define* (file->cpio-header* file
#:optional (file-name file)
@@ -182,7 +182,7 @@ produced in a deterministic fashion."
(make-cpio-header #:mode (stat:mode st)
#:nlink (stat:nlink st)
#:size (stat:size st)
- #:name-size (string-length file-name))))
+ #:name-size (string-utf8-length file-name))))
(define* (special-file->cpio-header* file
device-type
@@ -201,7 +201,7 @@ The number of hard links is assumed to be 1."
permission-bits)
#:nlink 1
#:rdev (device-number device-major device-minor)
- #:name-size (string-length file-name)))
+ #:name-size (string-utf8-length file-name)))
(define %trailer
"TRAILER!!!")
@@ -237,7 +237,7 @@ produces with the '-H newc' option."
;; We're padding the header + following file name + trailing zero, and
;; the header is 110 byte long.
- (write-padding (+ 110 1 (string-length file)) port)
+ (write-padding (+ 110 (string-utf8-length file) 1) port)
(case (mode->type (cpio-header-mode header))
((regular)
@@ -246,7 +246,7 @@ produces with the '-H newc' option."
(dump-port input port))))
((symlink)
(let ((target (readlink file)))
- (put-string port target)))
+ (put-bytevector port (string->utf8 target))))
((directory)
#t)
((block-special)
diff --git a/guix/download.scm b/guix/download.scm
index fff54d7a17..561a893eee 100644
--- a/guix/download.scm
+++ b/guix/download.scm
@@ -387,7 +387,11 @@
file "/" (symbol->string algo) "/"
(bytevector->nix-base32-string hash))))
- (list (guix-publish "ci.guix.gnu.org")
+ (list (guix-publish
+ ;; bordeaux.guix.gnu.org uses the nar-herder rather than guix
+ ;; publish, but it supports the same style of requests
+ "bordeaux.guix.gnu.org")
+ (guix-publish "ci.guix.gnu.org")
(lambda (file algo hash)
;; 'tarballs.nixos.org' supports several algorithms.
(string-append "https://tarballs.nixos.org/"
diff --git a/guix/git.scm b/guix/git.scm
index 95630a5e69..4019323327 100644
--- a/guix/git.scm
+++ b/guix/git.scm
@@ -4,6 +4,7 @@
;;; Copyright © 2021 Kyle Meyer <kyle@kyleam.com>
;;; Copyright © 2021 Marius Bakke <marius@gnu.org>
;;; Copyright © 2022 Maxime Devos <maximedevos@telenet.be>
+;;; Copyright © 2023 Tobias Geerinckx-Rice <me@tobias.gr>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -141,11 +142,6 @@ the 'SSL_CERT_FILE' and 'SSL_CERT_DIR' environment variables."
(define total
(indexer-progress-total-objects progress))
- (define hundredth
- (match (quotient (indexer-progress-total-objects progress) 100)
- (0 1)
- (x x)))
-
(define-values (done label)
(if (< (indexer-progress-received-objects progress) total)
(values (indexer-progress-received-objects progress)
@@ -156,14 +152,22 @@ the 'SSL_CERT_FILE' and 'SSL_CERT_DIR' environment variables."
(define %
(* 100. (/ done total)))
- (when (and (< % 100) (zero? (modulo done hundredth)))
+ ;; TODO: Both should be handled & exposed by the PROGRESS-BAR API instead.
+ (define width
+ (max (- (current-terminal-columns)
+ (string-length label) 7)
+ 3))
+
+ (define grain
+ (match (quotient total (max 100 (* 8 width))) ; assume 1/8 glyph resolution
+ (0 1)
+ (x x)))
+
+ (when (and (< % 100) (zero? (modulo done grain)))
(erase-current-line (current-error-port))
- (let ((width (max (- (current-terminal-columns)
- (string-length label) 7)
- 3)))
- (format (current-error-port) "~a ~3,d% ~a"
+ (format (current-error-port) "~a ~3,d% ~a"
label (inexact->exact (round %))
- (progress-bar % width)))
+ (progress-bar % width))
(force-output (current-error-port)))
(when (= % 100.)
diff --git a/guix/import/cran.scm b/guix/import/cran.scm
index c4b36da12b..17c19a2dcf 100644
--- a/guix/import/cran.scm
+++ b/guix/import/cran.scm
@@ -55,6 +55,7 @@
#:use-module (guix ui)
#:use-module (guix upstream)
#:use-module (guix packages)
+ #:use-module (guix sets)
#:use-module (gnu packages)
#:export (%input-style
@@ -422,6 +423,7 @@ empty list when the FIELD cannot be found."
("libarchive_dev" "libarchive")
("libbz2" "bzip2")
("libexpat" "expat")
+ ("libjpeg" "libjpeg-turbo")
("liblz4" "lz4")
("liblzma" "xz")
("libzstd" "zstd")
@@ -447,6 +449,13 @@ empty list when the FIELD cannot be found."
(() #f)
(_ #t)))
+(define (directory-needs-esbuild? dir)
+ "Check if the directory DIR contains minified JavaScript files and thus
+needs a JavaScript compiler."
+ (match (find-files dir "\\.min.js$")
+ (() #f)
+ (_ #t)))
+
(define (files-match-pattern? directory regexp . file-patterns)
"Return #T if any of the files matching FILE-PATTERNS in the DIRECTORY match
the given REGEXP."
@@ -462,10 +471,49 @@ the given REGEXP."
(else (loop))))))))
(apply find-files directory file-patterns))))
-(define (directory-needs-zlib? dir)
- "Return #T if any of the Makevars files in the src directory DIR contain a
-zlib linker flag."
- (files-match-pattern? dir "-lz" "(Makevars.*|configure.*)"))
+(define packages-for-matches
+ '(("-lcrypto" . "openssl")
+ ("-lcurl" . "curl")
+ ("-lgit2" . "libgit2")
+ ("-lpcre" . "pcre2")
+ ("-lssh" . "openssh")
+ ("-lssl" . "openssl")
+ ("-ltbb" . "tbb")
+ ("-lz" . "zlib")
+ ("gsl-config" . "gsl")
+ ("xml2-config" . "libxml2")
+ ("CURL_LIBS" . "curl")))
+
+(define libraries-pattern
+ (make-regexp
+ (string-append "("
+ (string-join
+ (map (compose regexp-quote first) packages-for-matches) "|")
+ ")")))
+
+(define (needed-libraries-in-directory dir)
+ "Return a list of package names that correspond to libraries that are
+referenced in build system files."
+ (set->list
+ (fold
+ (lambda (file packages)
+ (call-with-input-file file
+ (lambda (port)
+ (let loop ((packages packages))
+ (let ((line (read-line port)))
+ (cond
+ ((eof-object? line) packages)
+ (else
+ (loop
+ (fold (lambda (match acc)
+ (or (and=> (assoc-ref packages-for-matches
+ (match:substring match))
+ (cut set-insert <> acc))
+ acc))
+ packages
+ (list-matches libraries-pattern line))))))))))
+ (set)
+ (find-files dir "(Makevars.in*|configure.*)"))))
(define (directory-needs-pkg-config? dir)
"Return #T if any of the Makevars files in the src directory DIR reference
@@ -477,8 +525,9 @@ the pkg-config tool."
"Guess dependencies of R package source in DIR and return two values: a list
of package names for INPUTS and another list of names of NATIVE-INPUTS."
(values
- (if (directory-needs-zlib? dir) '("zlib") '())
+ (needed-libraries-in-directory dir)
(append
+ (if (directory-needs-esbuild? dir) '("esbuild") '())
(if (directory-needs-pkg-config? dir) '("pkg-config") '())
(if (directory-needs-fortran? dir) '("gfortran") '()))))
@@ -493,8 +542,8 @@ by TARBALL?"
(source-dir->dependencies dir)))
(source-dir->dependencies source)))
-(define (needs-knitr? meta)
- (member "knitr" (listify meta "VignetteBuilder")))
+(define (vignette-builders meta)
+ (map cran-guix-name (listify meta "VignetteBuilder")))
(define* (description->package repository meta #:key (license-prefix identity)
(download-source download))
@@ -608,8 +657,7 @@ from the alist META, which was derived from the R package's DESCRIPTION file."
,@(maybe-inputs (map cran-guix-name propagate) 'propagated-inputs)
,@(maybe-inputs
`(,@source-native-inputs
- ,@(if (needs-knitr? meta)
- '("r-knitr") '()))
+ ,@(vignette-builders meta))
'native-inputs)
(home-page ,(if (string-null? home-page)
(string-append base-url name)
diff --git a/guix/import/hackage.scm b/guix/import/hackage.scm
index 7bc2908405..83ad85f3fe 100644
--- a/guix/import/hackage.scm
+++ b/guix/import/hackage.scm
@@ -52,7 +52,6 @@
hackage-recursive-import
%hackage-updater
- guix-package->hackage-name
hackage-name->package-name
hackage-fetch
hackage-source-url
@@ -76,6 +75,7 @@
"exceptions"
"filepath"
"ghc"
+ "ghc-bignum"
"ghc-boot"
"ghc-boot-th"
"ghc-compact"
@@ -126,17 +126,6 @@ version is returned."
(string-downcase name)
(string-append package-name-prefix (string-downcase name))))
-(define guix-package->hackage-name
- (let ((uri-rx (make-regexp "(https?://hackage.haskell.org|mirror://hackage)/package/([^/]+)/.*"))
- (name-rx (make-regexp "(.*)-[0-9\\.]+")))
- (lambda (package)
- "Given a Guix package name, return the corresponding Hackage name."
- (let* ((source-url (and=> (package-source package) origin-uri))
- (name (match:substring (regexp-exec uri-rx source-url) 2)))
- (match (regexp-exec name-rx name)
- (#f name)
- (m (match:substring m 1)))))))
-
(define (read-cabal-and-hash port)
"Read a Cabal file from PORT and return it and its hash in nix-base32
format as two values."
@@ -314,6 +303,7 @@ the hash of the Cabal file."
(bytevector->nix-base32-string (file-sha256 tarball))
"failed to download tar archive")))))
(build-system haskell-build-system)
+ (properties '((upstream-name . ,name)))
,@(maybe-inputs 'inputs dependencies)
,@(maybe-inputs 'native-inputs native-dependencies)
,@(maybe-arguments)
@@ -370,7 +360,7 @@ respectively."
(formatted-message
(G_ "~a updater doesn't support updating to a specific version, sorry.")
"hackage")))
- (let* ((hackage-name (guix-package->hackage-name package))
+ (let* ((hackage-name (package-upstream-name* package))
(cabal-meta (hackage-fetch hackage-name)))
(match cabal-meta
(#f
@@ -378,7 +368,10 @@ respectively."
"warning: failed to parse ~a~%"
(hackage-cabal-url hackage-name))
#f)
- ((_ *** ("version" (version)))
+ ;; Cabal files have no particular order and while usually the version
+ ;; as somewhere in the middle it can also be at the beginning,
+ ;; requiring two pattern.
+ ((or (_ *** ("version" (version))) (("version" (version)) _ ...))
(let ((url (hackage-uri hackage-name version)))
(upstream-source
(package (package-name package))
diff --git a/guix/import/stackage.scm b/guix/import/stackage.scm
index c0284e48a4..735eeb75f7 100644
--- a/guix/import/stackage.scm
+++ b/guix/import/stackage.scm
@@ -49,7 +49,7 @@
(make-parameter "https://www.stackage.org"))
;; Latest LTS version compatible with current GHC.
-(define %default-lts-version "18.14")
+(define %default-lts-version "20.5")
(define-json-mapping <stackage-lts> make-stackage-lts
stackage-lts?
@@ -149,7 +149,7 @@ PACKAGE or #f if the package is not included in the Stackage LTS release."
(formatted-message
(G_ "~a updater doesn't support updating to a specific version, sorry.")
"stackage")))
- (let* ((hackage-name (guix-package->hackage-name pkg))
+ (let* ((hackage-name (package-upstream-name* pkg))
(version (lts-package-version (packages) hackage-name))
(name-version (hackage-name-version hackage-name version)))
(match (and=> name-version hackage-fetch)
@@ -173,7 +173,7 @@ PACKAGE or #f if the package is not included in the Stackage LTS release."
(false-if-networking-error
(let ((packages (stackage-lts-packages
(stackage-lts-info-fetch %default-lts-version)))
- (hackage-name (guix-package->hackage-name package)))
+ (hackage-name (package-upstream-name* package)))
(find (lambda (package)
(string=? (stackage-package-name package) hackage-name))
packages)))))
diff --git a/guix/licenses.scm b/guix/licenses.scm
index 632c9174df..f7df5826bf 100644
--- a/guix/licenses.scm
+++ b/guix/licenses.scm
@@ -613,7 +613,7 @@ at URI, which may be a file:// URI pointing the package's tree."
(define nmap
(license "Nmap license"
- "https://svn.nmap.org/nmap/COPYING"
+ "https://svn.nmap.org/nmap/LICENSE"
"https://fedoraproject.org/wiki/Licensing/Nmap"))
(define ogl-psi1.0
diff --git a/guix/progress.scm b/guix/progress.scm
index 4f8e98edc0..33cf6f4a1a 100644
--- a/guix/progress.scm
+++ b/guix/progress.scm
@@ -166,16 +166,47 @@ INTERVAL (a time-duration object), otherwise does nothing and returns #f."
;; Number of columns of the terminal.
(make-parameter 80))
+(define-record-type* <progress-bar-style>
+ progress-bar-style make-progress-bar-style progress-bar-style?
+ (start progress-bar-style-start)
+ (stop progress-bar-style-stop)
+ (filled progress-bar-style-filled)
+ (steps progress-bar-style-steps))
+
+(define ascii-bar-style
+ (progress-bar-style
+ (start #\[)
+ (stop #\])
+ (filled #\#)
+ (steps '())))
+
+(define unicode-bar-style
+ (progress-bar-style
+ (start #\x2595)
+ (stop #\x258f)
+ (filled #\x2588)
+ (steps '(#\x258F #\x258E #\x258D #\x258C #\x258B #\x258A #\x2589))))
+
(define* (progress-bar % #:optional (bar-width 20))
"Return % as a string representing an ASCII-art progress bar. The total
width of the bar is BAR-WIDTH."
- (let* ((bar-width (max 3 (- bar-width 2)))
- (fraction (/ % 100))
- (filled (inexact->exact (floor (* fraction bar-width))))
- (empty (- bar-width filled)))
- (format #f "[~a~a]"
- (make-string filled #\#)
- (make-string empty #\space))))
+ (let* ((bar-style (if (equal? (port-encoding (current-output-port)) "UTF-8")
+ unicode-bar-style
+ ascii-bar-style))
+ (bar-width (max 3 (- bar-width 2)))
+ (intermediates (+ (length (progress-bar-style-steps bar-style)) 1))
+ (step (inexact->exact (floor (/ (* % bar-width intermediates) 100))))
+ (filled (quotient step intermediates))
+ (intermediate
+ (list-ref (cons #f (progress-bar-style-steps bar-style))
+ (modulo step intermediates)))
+ (empty (- bar-width filled (if intermediate 1 0))))
+ (simple-format #f "~a~a~a~a~a"
+ (string (progress-bar-style-start bar-style))
+ (make-string filled (progress-bar-style-filled bar-style))
+ (if intermediate (string intermediate) "")
+ (make-string empty #\space)
+ (string (progress-bar-style-stop bar-style)))))
(define (erase-current-line port)
"Write an ANSI erase-current-line sequence to PORT to erase the whole line and
diff --git a/guix/rpm.scm b/guix/rpm.scm
new file mode 100644
index 0000000000..734aef29c1
--- /dev/null
+++ b/guix/rpm.scm
@@ -0,0 +1,630 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix rpm)
+ #:autoload (gcrypt hash) (hash-algorithm file-hash md5)
+ #:use-module (guix build utils)
+ #:use-module (ice-9 format)
+ #:use-module (ice-9 match)
+ #:use-module (ice-9 textual-ports)
+ #:use-module (rnrs bytevectors)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-9)
+ #:use-module (srfi srfi-26)
+ #:use-module (srfi srfi-71)
+ #:use-module (srfi srfi-171)
+ #:export (generate-lead
+ generate-signature
+ generate-header
+ assemble-rpm-metadata
+
+ ;; XXX: These are internals, but the inline disabling trick
+ ;; doesn't work on them.
+ make-header-entry
+ header-entry?
+ header-entry-tag
+ header-entry-count
+ header-entry-value
+
+ bytevector->hex-string
+
+ fhs-directory?))
+
+;;; Commentary:
+;;;
+;;; This module provides the building blocks required to construct RPM
+;;; archives. It is intended to be importable on the build side, so shouldn't
+;;; depend on (guix diagnostics) or other host-side-only modules.
+;;;
+;;; Code:
+
+(define (gnu-system-triplet->machine-type triplet)
+ "Return the machine component of TRIPLET, a GNU system triplet."
+ (first (string-split triplet #\-)))
+
+(define (gnu-machine-type->rpm-arch type)
+ "Return the canonical RPM architecture string, given machine TYPE."
+ (match type
+ ("arm" "armv7hl")
+ ("powerpc" "ppc")
+ ("powerpc64le" "ppc64le")
+ (machine machine))) ;unchanged
+
+(define (gnu-machine-type->rpm-number type)
+ "Translate machine TYPE to its corresponding RPM integer value."
+ ;; Refer to the rpmrc.in file in the RPM source for the complete
+ ;; translation tables.
+ (match type
+ ((or "i486" "i586" "i686" "x86_64") 1)
+ ((? (cut string-prefix? "powerpc" <>)) 5)
+ ("mips64el" 11)
+ ((? (cut string-prefix? "arm" <>)) 12)
+ ("aarch64" 19)
+ ((? (cut string-prefix? "riscv" <>)) 22)
+ (_ (error "no RPM number known for machine type" type))))
+
+(define (u16-number->u8-list number)
+ "Return a list of byte values made of NUMBER, a 16 bit unsigned integer."
+ (let ((bv (uint-list->bytevector (list number) (endianness big) 2)))
+ (bytevector->u8-list bv)))
+
+(define (u32-number->u8-list number)
+ "Return a list of byte values made of NUMBER, a 32 bit unsigned integer."
+ (let ((bv (uint-list->bytevector (list number) (endianness big) 4)))
+ (bytevector->u8-list bv)))
+
+(define (s32-number->u8-list number)
+ "Return a list of byte values made of NUMBER, a 32 bit signed integer."
+ (let ((bv (sint-list->bytevector (list number) (endianness big) 4)))
+ (bytevector->u8-list bv)))
+
+(define (u8-list->u32-number lst)
+ "Return the 32 bit unsigned integer corresponding to the 4 bytes in LST."
+ (bytevector-u32-ref (u8-list->bytevector lst) 0 (endianness big)))
+
+
+;;;
+;;; Lead section.
+;;;
+
+;; Refer to the docs/manual/format.md file of the RPM source for the details
+;; regarding the binary format of an RPM archive.
+(define* (generate-lead name-version #:key (target %host-type))
+ "Generate a RPM lead u8-list that uses NAME-VERSION, the name and version
+string of the package, and TARGET, a GNU triplet used to derive the target
+machine type."
+ (define machine-type (gnu-system-triplet->machine-type target))
+ (define magic (list #xed #xab #xee #xdb))
+ (define file-format-version (list 3 0)) ;3.0
+ (define type (list 0 0)) ;0 for binary packages
+ (define arch-number (u16-number->u8-list
+ (gnu-machine-type->rpm-number machine-type)))
+ ;; The 66 bytes from 10 to 75 are for the name-version-release string.
+ (define name
+ (let ((padding-bytes (make-list (- 66 (string-length name-version)) 0)))
+ (append (bytevector->u8-list (string->utf8 name-version))
+ padding-bytes)))
+ ;; There is no OS number corresponding to GNU/Hurd (GNU), only Linux, per
+ ;; rpmrc.in.
+ (define os-number (list 0 1))
+
+ ;; For RPM format 3.0, the signature type is 5, which means a "Header-style"
+ ;; signature.
+ (define signature-type (list 0 5))
+
+ (define reserved-bytes (make-list 16 0))
+
+ (append magic file-format-version type arch-number name
+ os-number signature-type reserved-bytes))
+
+
+;;;
+;;; Header section.
+;;;
+
+(define header-magic (list #x8e #xad #xe8))
+(define header-version (list 1))
+(define header-reserved (make-list 4 0)) ;4 reserved bytes
+;;; Every header starts with 8 bytes made by the header magic number, the
+;;; header version and 4 reserved bytes.
+(define header-intro (append header-magic header-version header-reserved))
+
+;;; Header entry data types.
+(define NULL 0)
+(define CHAR 1)
+(define INT8 2)
+(define INT16 3) ;2-bytes aligned
+(define INT32 4) ;4-bytes aligned
+(define INT64 5) ;8-bytes aligned
+(define STRING 6)
+(define BIN 7)
+(define STRING_ARRAY 8)
+(define I18NSTRIN_TYPE 9)
+
+;;; Header entry tags.
+(define-record-type <rpm-tag>
+ (make-rpm-tag number type)
+ rpm-tag?
+ (number rpm-tag-number)
+ (type rpm-tag-type))
+
+;;; The following are internal tags used to identify the data sections.
+(define RPMTAG_HEADERSIGNATURES (make-rpm-tag 62 BIN)) ;signature header
+(define RPMTAG_HEADERIMMUTABLE (make-rpm-tag 63 BIN)) ;main/data header
+(define RPMTAG_HEADERI18NTABLE (make-rpm-tag 100 STRING_ARRAY))
+
+;;; Subset of RPM tags from include/rpm/rpmtag.h.
+(define RPMTAG_NAME (make-rpm-tag 1000 STRING))
+(define RPMTAG_VERSION (make-rpm-tag 1001 STRING))
+(define RPMTAG_RELEASE (make-rpm-tag 1002 STRING))
+(define RPMTAG_SUMMARY (make-rpm-tag 1004 STRING))
+(define RPMTAG_SIZE (make-rpm-tag 1009 INT32))
+(define RPMTAG_LICENSE (make-rpm-tag 1014 STRING))
+(define RPMTAG_OS (make-rpm-tag 1021 STRING))
+(define RPMTAG_ARCH (make-rpm-tag 1022 STRING))
+(define RPMTAG_PREIN (make-rpm-tag 1023 STRING))
+(define RPMTAG_POSTIN (make-rpm-tag 1024 STRING))
+(define RPMTAG_PREUN (make-rpm-tag 1025 STRING))
+(define RPMTAG_POSTUN (make-rpm-tag 1026 STRING))
+(define RPMTAG_FILESIZES (make-rpm-tag 1028 INT32))
+(define RPMTAG_FILEMODES (make-rpm-tag 1030 INT16))
+(define RPMTAG_FILEDIGESTS (make-rpm-tag 1035 STRING_ARRAY))
+(define RPMTAG_FILELINKTOS (make-rpm-tag 1036 STRING_ARRAY))
+(define RPMTAG_FILEUSERNAME (make-rpm-tag 1039 STRING_ARRAY))
+(define RPMTAG_GROUPNAME (make-rpm-tag 1040 STRING_ARRAY))
+(define RPMTAG_PREFIXES (make-rpm-tag 1098 STRING_ARRAY))
+(define RPMTAG_DIRINDEXES (make-rpm-tag 1116 INT32))
+(define RPMTAG_BASENAMES (make-rpm-tag 1117 STRING_ARRAY))
+(define RPMTAG_DIRNAMES (make-rpm-tag 1118 STRING_ARRAY))
+(define RPMTAG_PAYLOADFORMAT (make-rpm-tag 1124 STRING))
+(define RPMTAG_PAYLOADCOMPRESSOR (make-rpm-tag 1125 STRING))
+(define RPMTAG_LONGFILESIZES (make-rpm-tag 5008 INT64))
+(define RPMTAG_LONGSIZE (make-rpm-tag 5009 INT64))
+;;; The algorithm used to compute the digest of each file, e.g. RPM_HASH_MD5.
+(define RPMTAG_FILEDIGESTALGO (make-rpm-tag 5011 INT32))
+;;; RPMTAG_ENCODING specifies the encoding used for strings, e.g. "utf-8".
+(define RPMTAG_ENCODING (make-rpm-tag 5062 STRING))
+;;; Compressed payload digest. Its type is a string array, but currently in
+;;; practice it is equivalent to STRING, since only the first element is used.
+(define RPMTAG_PAYLOADDIGEST (make-rpm-tag 5092 STRING_ARRAY))
+;;; The algorithm used to compute the payload digest, e.g. RPM_HASH_SHA256.
+(define RPMTAG_PAYLOADDIGESTALGO (make-rpm-tag 5093 INT32))
+;;; The following are taken from the rpmHashAlgo_e enum in rpmcrypto.h.
+(define RPM_HASH_MD5 1)
+(define RPM_HASH_SHA256 8)
+
+;;; Other useful internal definitions.
+(define REGION_TAG_COUNT 16) ;number of bytes
+(define INT32_MAX (1- (expt 2 32))) ;4294967295 bytes (unsigned)
+
+(define (rpm-tag->u8-list tag)
+ "Return the u8 list corresponding to RPM-TAG, a <rpm-tag> object."
+ (append (u32-number->u8-list (rpm-tag-number tag))
+ (u32-number->u8-list (rpm-tag-type tag))))
+
+(define-record-type <header-entry>
+ (make-header-entry tag count value)
+ header-entry?
+ (tag header-entry-tag) ;<rpm-tag>
+ (count header-entry-count) ;number (u32)
+ (value header-entry-value)) ;string|number|list|...
+
+(define (entry-type->alignement type)
+ "Return the byte alignment of TYPE, an RPM header entry type."
+ (cond ((= INT16 type) 2)
+ ((= INT32 type) 4)
+ ((= INT64 type) 8)
+ (else 1)))
+
+(define (next-aligned-offset offset alignment)
+ "Return the next position from OFFSET which satisfies ALIGNMENT."
+ (if (= 0 (modulo offset alignment))
+ offset
+ (next-aligned-offset (1+ offset) alignment)))
+
+(define (header-entry->data entry)
+ "Return the data of ENTRY, a <header-entry> object, as a u8 list."
+ (let* ((tag (header-entry-tag entry))
+ (count (header-entry-count entry))
+ (value (header-entry-value entry))
+ (number (rpm-tag-number tag))
+ (type (rpm-tag-type tag)))
+ (cond
+ ((= STRING type)
+ (unless (string? value)
+ (error "expected string value for STRING type, got" value))
+ (unless (= 1 count)
+ (error "count must be 1 for STRING type"))
+ (let ((value (cond ((= (rpm-tag-number RPMTAG_VERSION) number)
+ ;; Hyphens are not allowed in version strings.
+ (string-map (match-lambda
+ (#\- #\+)
+ (c c))
+ value))
+ (else value))))
+ (append (bytevector->u8-list (string->utf8 value))
+ (list 0)))) ;strings must end with null byte
+ ((= STRING_ARRAY type)
+ (unless (list? value)
+ (error "expected a list of strings for STRING_ARRAY type, got" value))
+ (unless (= count (length value))
+ (error "expected count to be equal to" (length value) 'got count))
+ (append-map (lambda (s)
+ (append (bytevector->u8-list (string->utf8 s))
+ (list 0))) ;null byte separated
+ value))
+ ((member type (list INT8 INT16 INT32))
+ (if (= 1 count)
+ (unless (number? value)
+ (error "expected number value for scalar INT type; got" value))
+ (unless (list? value)
+ (error "expected list value for array INT type; got" value)))
+ (if (list? value)
+ (cond ((= INT8 type) value)
+ ((= INT16 type) (append-map u16-number->u8-list value))
+ ((= INT32 type) (append-map u32-number->u8-list value))
+ (else (error "unexpected type" type)))
+ (cond ((= INT8 type) (list value))
+ ((= INT16 type) (u16-number->u8-list value))
+ ((= INT32 type) (u32-number->u8-list value))
+ (else (error "unexpected type" type)))))
+ ((= BIN type)
+ (unless (list? value)
+ (error "expected list value for BIN type; got" value))
+ value)
+ (else (error "unimplemented type" type)))))
+
+(define (make-header-index+data entries)
+ "Return the index and data sections as u8 number lists, via multiple values.
+An index is composed of four u32 (16 bytes total) quantities, in order: tag,
+type, offset and count."
+ (match (fold (match-lambda*
+ ((entry (offset . (index . data)))
+ (let* ((tag (header-entry-tag entry))
+ (tag-number (rpm-tag-number tag))
+ (tag-type (rpm-tag-type tag))
+ (count (header-entry-count entry))
+ (data* (header-entry->data entry))
+ (alignment (entry-type->alignement tag-type))
+ (aligned-offset (next-aligned-offset offset alignment))
+ (padding (make-list (- aligned-offset offset) 0)))
+ (cons (+ aligned-offset (length data*))
+ (cons (append index
+ (u32-number->u8-list tag-number)
+ (u32-number->u8-list tag-type)
+ (u32-number->u8-list aligned-offset)
+ (u32-number->u8-list count))
+ (append data padding data*))))))
+ '(0 . (() . ()))
+ entries)
+ ((offset . (index . data))
+ (values index data))))
+
+;; Prevent inlining of the variables/procedures accessed by unit tests.
+(set! make-header-index+data make-header-index+data)
+(set! RPMTAG_ARCH RPMTAG_ARCH)
+(set! RPMTAG_LICENSE RPMTAG_LICENSE)
+(set! RPMTAG_NAME RPMTAG_NAME)
+(set! RPMTAG_OS RPMTAG_OS)
+(set! RPMTAG_RELEASE RPMTAG_RELEASE)
+(set! RPMTAG_SUMMARY RPMTAG_SUMMARY)
+(set! RPMTAG_VERSION RPMTAG_VERSION)
+
+(define (wrap-in-region-tags header region-tag)
+ "Wrap HEADER, a header provided as u8-list with REGION-TAG."
+ (let* ((type (rpm-tag-type region-tag))
+ (header-intro (take header 16))
+ (header-rest (drop header 16))
+ ;; Increment the existing index value to account for the added region
+ ;; tag index.
+ (index-length (1+ (u8-list->u32-number
+ (drop-right (drop header-intro 8) 4)))) ;bytes 8-11
+ ;; Increment the data length value to account for the added region
+ ;; tag data.
+ (data-length (+ REGION_TAG_COUNT
+ (u8-list->u32-number
+ (take-right header-intro 4))))) ;last 4 bytes of intro
+ (unless (member region-tag (list RPMTAG_HEADERSIGNATURES
+ RPMTAG_HEADERIMMUTABLE))
+ (error "expected RPMTAG_HEADERSIGNATURES or RPMTAG_HEADERIMMUTABLE, got"
+ region-tag))
+ (append (drop-right header-intro 8) ;strip existing index and data lengths
+ (u32-number->u8-list index-length)
+ (u32-number->u8-list data-length)
+ ;; Region tag (16 bytes).
+ (u32-number->u8-list (rpm-tag-number region-tag)) ;number
+ (u32-number->u8-list type) ;type
+ (u32-number->u8-list (- data-length REGION_TAG_COUNT)) ;offset
+ (u32-number->u8-list REGION_TAG_COUNT) ;count
+ ;; Immutable region.
+ header-rest
+ ;; Region tag trailer (16 bytes). Note: the trailer offset value
+ ;; is an enforced convention; it has no practical use.
+ (u32-number->u8-list (rpm-tag-number region-tag)) ;number
+ (u32-number->u8-list type) ;type
+ (s32-number->u8-list (* -1 index-length 16)) ;negative offset
+ (u32-number->u8-list REGION_TAG_COUNT)))) ;count
+
+(define (bytevector->hex-string bv)
+ (format #f "~{~2,'0x~}" (bytevector->u8-list bv)))
+
+(define (files->md5-checksums files)
+ "Return the MD5 checksums (formatted as hexadecimal strings) for FILES."
+ (let ((file-md5 (cut file-hash (hash-algorithm md5) <>)))
+ (map (lambda (f)
+ (or (and=> (false-if-exception (file-md5 f))
+ bytevector->hex-string)
+ ;; Only regular files (e.g., not directories) can have their
+ ;; checksum computed.
+ ""))
+ files)))
+
+(define (strip-leading-dot name)
+ "Remove the leading \".\" from NAME, if present. If a single \".\" is
+encountered, translate it to \"/\"."
+ (match name
+ ("." "/") ;special case
+ ((? (cut string-prefix? "." <>))
+ (string-drop name 1))
+ (x name)))
+
+;;; An extensive list of required and optional FHS directories, per its 3.0
+;;; revision.
+(define %fhs-directories
+ (list "/bin" "/boot" "/dev"
+ "/etc" "/etc/opt" "/etc/X11" "/etc/sgml" "/etc/xml"
+ "/home" "/root" "/lib" "/media" "/mnt"
+ "/opt" "/opt/bin" "/opt/doc" "/opt/include"
+ "/opt/info" "/opt/lib" "/opt/man"
+ "/run" "/sbin" "/srv" "/sys" "/tmp"
+ "/usr" "/usr/bin" "/usr/include" "/usr/libexec"
+ "/usr/share" "/usr/share/applications"
+ "/usr/share/color" "/usr/share/dict" "/usr/share/doc" "/usr/share/games"
+ "/usr/share/icons" "/usr/share/icons/hicolor"
+ "/usr/share/icons/hicolor/48x48"
+ "/usr/share/icons/hicolor/48x48/apps"
+ "/usr/share/icons/hicolor/scalable"
+ "/usr/share/icons/hicolor/scalable/apps"
+ "/usr/share/info" "/usr/share/locale" "/usr/share/man"
+ "/usr/share/metainfo" "/usr/share/misc"
+ "/usr/share/nls" "/usr/share/ppd" "/usr/share/sgml"
+ "/usr/share/terminfo" "/usr/share/tmac" "/usr/share/xml"
+ "/usr/share/zoneinfo" "/usr/local" "/usr/local/bin" "/usr/local/etc"
+ "/usr/local/games" "/usr/local/include" "/usr/local/lib"
+ "/usr/local/man" "/usr/local/sbin" "/usr/local/sbin" "/usr/local/share"
+ "/usr/local/src" "/var" "/var/account" "/var/backups"
+ "/var/cache" "/var/cache/fonts" "/var/cache/man" "/var/cache/www"
+ "/var/crash" "/var/cron" "/var/games" "/var/mail" "/var/msgs"
+ "/var/lib" "/var/lib/color" "/var/lib/hwclock" "/var/lib/misc"
+ "/var/local" "/var/lock" "/var/log" "/var/opt" "/var/preserve"
+ "/var/run" "/var/spool" "/var/spool/lpd" "/var/spool/mqueue"
+ "/var/spool/news" "/var/spool/rwho" "/var/spool/uucp"
+ "/var/tmp" "/var/yp"))
+
+(define (fhs-directory? file-name)
+ "Predicate to check if FILE-NAME is a known File Hierarchy Standard (FHS)
+directory."
+ (member (strip-leading-dot file-name) %fhs-directories))
+
+(define (directory->file-entries directory)
+ "Return the file lists triplet header entries for the files found under
+DIRECTORY."
+ (with-directory-excursion directory
+ ;; Skip the initial "." directory, as its name would get concatenated with
+ ;; the "./" dirname and fail to match "." in the payload.
+ (let* ((files (cdr (find-files "." #:directories? #t)))
+ (file-stats (map lstat files))
+ (directories
+ (append (list ".")
+ (filter-map (match-lambda
+ ((index . file)
+ (let ((st (list-ref file-stats index)))
+ (and (eq? 'directory (stat:type st))
+ file))))
+ (list-transduce (tenumerate) rcons files))))
+ ;; Omit any FHS directories found in FILES to avoid the RPM package
+ ;; from owning them. This can occur when symlinks directives such
+ ;; as "/usr/bin/hello -> bin/hello" are used.
+ (package-files package-file-stats
+ (unzip2 (reverse
+ (fold (lambda (file stat res)
+ (if (fhs-directory? file)
+ res
+ (cons (list file stat) res)))
+ '() files file-stats))))
+
+ ;; When provided with the index of a file, the directory index must
+ ;; return the index of the corresponding directory entry.
+ (dirindexes (map (lambda (d)
+ (list-index (cut string=? <> d) directories))
+ (map dirname package-files)))
+ ;; The files owned are those appearing in 'basenames'; own them
+ ;; all.
+ (basenames (map basename package-files))
+ ;; The directory names must end with a trailing "/".
+ (dirnames (map (compose strip-leading-dot (cut string-append <> "/"))
+ directories))
+ ;; Note: All the file-related entries must have the same length as
+ ;; the basenames entry.
+ (symlink-targets (map (lambda (f)
+ (if (symbolic-link? f)
+ (readlink f)
+ "")) ;unused
+ package-files))
+ (file-modes (map stat:mode package-file-stats))
+ (file-sizes (map stat:size package-file-stats))
+ (file-md5s (files->md5-checksums package-files)))
+ (let ((basenames-length (length basenames))
+ (dirindexes-length (length dirindexes)))
+ (unless (= basenames-length dirindexes-length)
+ (error "length mismatch for dirIndexes; expected/actual"
+ basenames-length dirindexes-length))
+ (append
+ (if (> (apply max file-sizes) INT32_MAX)
+ (list (make-header-entry RPMTAG_LONGFILESIZES (length file-sizes)
+ file-sizes)
+ (make-header-entry RPMTAG_LONGSIZE 1
+ (reduce + 0 file-sizes)))
+ (list (make-header-entry RPMTAG_FILESIZES (length file-sizes)
+ file-sizes)
+ (make-header-entry RPMTAG_SIZE 1 (reduce + 0 file-sizes))))
+ (list
+ (make-header-entry RPMTAG_FILEMODES (length file-modes) file-modes)
+ (make-header-entry RPMTAG_FILEDIGESTS (length file-md5s) file-md5s)
+ (make-header-entry RPMTAG_FILEDIGESTALGO 1 RPM_HASH_MD5)
+ (make-header-entry RPMTAG_FILELINKTOS (length symlink-targets)
+ symlink-targets)
+ (make-header-entry RPMTAG_FILEUSERNAME basenames-length
+ (make-list basenames-length "root"))
+ (make-header-entry RPMTAG_GROUPNAME basenames-length
+ (make-list basenames-length "root"))
+ ;; The dirindexes, basenames and dirnames tags form the so-called RPM
+ ;; "path triplet".
+ (make-header-entry RPMTAG_DIRINDEXES dirindexes-length dirindexes)
+ (make-header-entry RPMTAG_BASENAMES basenames-length basenames)
+ (make-header-entry RPMTAG_DIRNAMES (length dirnames) dirnames)))))))
+
+(define (make-header entries)
+ "Return the u8 list of a RPM header containing ENTRIES, a list of
+<rpm-entry> objects."
+ (let* ((entries (sort entries (lambda (x y)
+ (< (rpm-tag-number (header-entry-tag x))
+ (rpm-tag-number (header-entry-tag y))))))
+ (count (length entries))
+ (index data (make-header-index+data entries)))
+ (append header-intro ;8 bytes
+ (u32-number->u8-list count) ;4 bytes
+ (u32-number->u8-list (length data)) ;4 bytes
+ ;; Now starts the header index, which can contain up to 32 entries
+ ;; of 16 bytes each.
+ index data)))
+
+(define* (generate-header name version
+ payload-digest
+ payload-directory
+ payload-compressor
+ #:key
+ relocatable?
+ prein-file postin-file
+ preun-file postun-file
+ (target %host-type)
+ (release "0")
+ (license "N/A")
+ (summary "RPM archive generated by GNU Guix.")
+ (os "Linux")) ;see rpmrc.in
+ "Return the u8 list corresponding to the Header section. PAYLOAD-DIGEST is
+the SHA256 checksum string of the compressed payload. PAYLOAD-DIRECTORY is
+the directory containing the payload files. PAYLOAD-COMPRESSOR is the name of
+the compressor used to compress the CPIO payload, such as \"none\", \"gz\",
+\"xz\" or \"zstd\"."
+ (let* ((rpm-arch (gnu-machine-type->rpm-arch
+ (gnu-system-triplet->machine-type target)))
+ (file->string (cut call-with-input-file <> get-string-all))
+ (prein-script (and=> prein-file file->string))
+ (postin-script (and=> postin-file file->string))
+ (preun-script (and=> preun-file file->string))
+ (postun-script (and=> postun-file file->string)))
+ (wrap-in-region-tags
+ (make-header (append
+ (list (make-header-entry RPMTAG_HEADERI18NTABLE 1 (list "C"))
+ (make-header-entry RPMTAG_NAME 1 name)
+ (make-header-entry RPMTAG_VERSION 1 version)
+ (make-header-entry RPMTAG_RELEASE 1 release)
+ (make-header-entry RPMTAG_SUMMARY 1 summary)
+ (make-header-entry RPMTAG_LICENSE 1 license)
+ (make-header-entry RPMTAG_OS 1 os)
+ (make-header-entry RPMTAG_ARCH 1 rpm-arch))
+ (directory->file-entries payload-directory)
+ (if relocatable?
+ ;; Note: RPMTAG_PREFIXES must not have a trailing
+ ;; slash, unless it's '/'. This allows installing the
+ ;; package via 'rpm -i --prefix=/tmp', for example.
+ (list (make-header-entry RPMTAG_PREFIXES 1 (list "/")))
+ '())
+ (if prein-script
+ (list (make-header-entry RPMTAG_PREIN 1 prein-script))
+ '())
+ (if postin-script
+ (list (make-header-entry RPMTAG_POSTIN 1 postin-script))
+ '())
+ (if preun-script
+ (list (make-header-entry RPMTAG_PREUN 1 preun-script))
+ '())
+ (if postun-script
+ (list (make-header-entry RPMTAG_POSTUN 1 postun-script))
+ '())
+ (if (string=? "none" payload-compressor)
+ '()
+ (list (make-header-entry RPMTAG_PAYLOADCOMPRESSOR 1
+ payload-compressor)))
+ (list (make-header-entry RPMTAG_ENCODING 1 "utf-8")
+ (make-header-entry RPMTAG_PAYLOADFORMAT 1 "cpio")
+ (make-header-entry RPMTAG_PAYLOADDIGEST 1
+ (list payload-digest))
+ (make-header-entry RPMTAG_PAYLOADDIGESTALGO 1
+ RPM_HASH_SHA256))))
+ RPMTAG_HEADERIMMUTABLE)))
+
+
+;;;
+;;; Signature section
+;;;
+
+;;; Header sha256 checksum.
+(define RPMSIGTAG_SHA256 (make-rpm-tag 273 STRING))
+;;; Uncompressed payload size.
+(define RPMSIGTAG_PAYLOADSIZE (make-rpm-tag 1007 INT32))
+;;; Header and compressed payload combined size.
+(define RPMSIGTAG_SIZE (make-rpm-tag 1000 INT32))
+;;; Uncompressed payload size (when size > max u32).
+(define RPMSIGTAG_LONGARCHIVESIZE (make-rpm-tag 271 INT64))
+;;; Header and compressed payload combined size (when size > max u32).
+(define RPMSIGTAG_LONGSIZE (make-rpm-tag 270 INT64))
+;;; Extra space reserved for signatures (typically 32 bytes).
+(define RPMSIGTAG_RESERVEDSPACE (make-rpm-tag 1008 BIN))
+
+(define (generate-signature header-sha256
+ header+compressed-payload-size
+ ;; uncompressed-payload-size
+ )
+ "Return the u8 list representing a signature header containing the
+HEADER-SHA256 (a string) and the PAYLOAD-SIZE, which is the combined size of
+the header and compressed payload."
+ (define size-tag (if (> header+compressed-payload-size INT32_MAX)
+ RPMSIGTAG_LONGSIZE
+ RPMSIGTAG_SIZE))
+ (wrap-in-region-tags
+ (make-header (list (make-header-entry RPMSIGTAG_SHA256 1 header-sha256)
+ (make-header-entry size-tag 1
+ header+compressed-payload-size)
+ ;; (make-header-entry RPMSIGTAG_PAYLOADSIZE 1
+ ;; uncompressed-payload-size)
+ ;; Reserve 32 bytes of extra space in case users would
+ ;; like to add signatures, as done in rpmGenerateSignature.
+ (make-header-entry RPMSIGTAG_RESERVEDSPACE 32
+ (make-list 32 0))))
+ RPMTAG_HEADERSIGNATURES))
+
+(define (assemble-rpm-metadata lead signature header)
+ "Align and append the various u8 list components together, and return the
+result as a bytevector."
+ (let* ((offset (+ (length lead) (length signature)))
+ (header-offset (next-aligned-offset offset 8))
+ (padding (make-list (- header-offset offset) 0)))
+ ;; The Header is 8-bytes aligned.
+ (u8-list->bytevector (append lead signature padding header))))
diff --git a/guix/scripts.scm b/guix/scripts.scm
index 4de8bc23b3..395df864a3 100644
--- a/guix/scripts.scm
+++ b/guix/scripts.scm
@@ -321,11 +321,11 @@ THRESHOLDS is a pair (ABSOLUTE-THRESHOLD . RELATIVE-THRESHOLD)."
absolute-threshold-in-bytes))
(warning (G_ "only ~,1f GiB of free space available on ~a~%")
(/ available 1. GiB) (%store-prefix))
- (display-hint (format #f (G_ "Consider deleting old profile
+ (display-hint (G_ "Consider deleting old profile
generations and collecting garbage, along these lines:
@example
guix gc --delete-generations=1m
-@end example\n"))))))
+@end example\n")))))
;;; scripts.scm ends here
diff --git a/guix/scripts/build.scm b/guix/scripts/build.scm
index b4437172d7..6a4a32fc0a 100644
--- a/guix/scripts/build.scm
+++ b/guix/scripts/build.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012-2022 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2012-2023 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2013 Mark H Weaver <mhw@netris.org>
;;; Copyright © 2020 Marius Bakke <mbakke@fastmail.com>
;;; Copyright © 2020 Ricardo Wurmus <rekado@elephly.net>
@@ -377,12 +377,12 @@ use '--no-offload' instead~%")))
arg)
(if closest
(display-hint
- (format #f (G_ "Did you mean @code{~a}?
+ (G_ "Did you mean @code{~a}?
Try @option{--list-targets} to view available targets.~%")
- closest))
+ closest)
(display-hint
- (format #f (G_ "\
-Try @option{--list-targets} to view available targets.~%"))))
+ (G_ "\
+Try @option{--list-targets} to view available targets.~%")))
(exit 1))))))))
(define %standard-native-build-options
@@ -404,12 +404,12 @@ Try @option{--list-targets} to view available targets.~%"))))
arg)
(if closest
(display-hint
- (format #f (G_ "Did you mean @code{~a}?
+ (G_ "Did you mean @code{~a}?
Try @option{--list-systems} to view available system types.~%")
- closest))
+ closest)
(display-hint
- (format #f (G_ "\
-Try @option{--list-systems} to view available system types.~%"))))
+ (G_ "\
+Try @option{--list-systems} to view available system types.~%")))
(exit 1))))))))
diff --git a/guix/scripts/container/exec.scm b/guix/scripts/container/exec.scm
index 51b616b384..3e70b1d3c2 100644
--- a/guix/scripts/container/exec.scm
+++ b/guix/scripts/container/exec.scm
@@ -102,4 +102,12 @@ and the other containing arguments for the command to be executed."
environment)
(apply execlp program program program-args)))))))
(unless (zero? result)
- (leave (G_ "exec failed with status ~d~%") result)))))))
+ (match (status:exit-val result)
+ (#f
+ (if (status:term-sig result)
+ (leave (G_ "process terminated with signal ~a~%")
+ (status:term-sig result))
+ (leave (G_ "process stopped with signal ~a~%")
+ (status:stop-sig result))))
+ (code
+ (leave (G_ "process exited with status ~d~%") code)))))))))
diff --git a/guix/scripts/describe.scm b/guix/scripts/describe.scm
index 80cd0ce00a..5523aa0ec2 100644
--- a/guix/scripts/describe.scm
+++ b/guix/scripts/describe.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2018, 2019, 2020, 2021, 2023 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2018 Oleg Pykhalov <go.wigust@gmail.com>
;;; Copyright © 2020 Ekaitz Zarraga <ekaitz@elenq.tech>
;;; Copyright © 2021 Simon Tournier <zimon.toutoune@gmail.com>
@@ -154,10 +154,10 @@ within a Git checkout."
(channel (repository->guix-channel (dirname program))))
(unless channel
(report-error (G_ "failed to determine origin~%"))
- (display-hint (format #f (G_ "Perhaps this
+ (display-hint (G_ "Perhaps this
@command{guix} command was not obtained with @command{guix pull}? Its version
string is ~a.~%")
- %guix-version))
+ %guix-version)
(exit 1))
(match fmt
diff --git a/guix/scripts/environment.scm b/guix/scripts/environment.scm
index 46435ae48e..44cfcb4f76 100644
--- a/guix/scripts/environment.scm
+++ b/guix/scripts/environment.scm
@@ -664,8 +664,8 @@ command name."
(let ((closest (string-closest executable available
#:threshold 12)))
(unless (or (not closest) (string=? closest executable))
- (display-hint (format #f (G_ "Did you mean '~a'?~%")
- closest)))))))))
+ (display-hint (G_ "Did you mean '~a'?~%")
+ closest))))))))
(define* (launch-environment/fork command profile manifest
#:key pure? (white-list '()))
diff --git a/guix/scripts/home.scm b/guix/scripts/home.scm
index a37f059711..445853d01f 100644
--- a/guix/scripts/home.scm
+++ b/guix/scripts/home.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2021 Andrew Tropin <andrew@trop.in>
+;;; Copyright © 2021, 2023 Andrew Tropin <andrew@trop.in>
;;; Copyright © 2021 Xinglu Chen <public@yoctocell.xyz>
;;; Copyright © 2021 Pierre Langlois <pierre.langlois@gmx.com>
;;; Copyright © 2021 Oleg Pykhalov <go.wigust@gmail.com>
@@ -410,6 +410,7 @@ immediately. Return the exit status of the process in the container."
network?)
"Perform ACTION for home environment. "
+ (ensure-profile-directory)
(define println
(cut format #t "~a~%" <>))
@@ -474,7 +475,6 @@ ACTION must be one of the sub-commands that takes a home environment
declaration as an argument (a file name.) OPTS is the raw alist of options
resulting from command-line parsing."
(define (ensure-home-environment file-or-exp obj)
- (ensure-profile-directory)
(unless (home-environment? obj)
(leave (G_ "'~a' does not return a home environment~%")
file-or-exp))
@@ -573,10 +573,10 @@ argument list and OPTS is the option alist."
(cut import-manifest manifest destination <>))
(info (G_ "'~a' populated with all the Home configuration files~%")
destination)
- (display-hint (format #f (G_ "\
+ (display-hint (G_ "\
Run @command{guix home reconfigure ~a/home-configuration.scm} to effectively
deploy the home environment described by these files.\n")
- destination))))
+ destination)))
((describe)
(let ((list-installed-regex (assoc-ref opts 'list-installed)))
(match (generation-number %guix-home)
diff --git a/guix/scripts/home/edit.scm b/guix/scripts/home/edit.scm
index a6c05675b3..d039179a10 100644
--- a/guix/scripts/home/edit.scm
+++ b/guix/scripts/home/edit.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2022 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2022, 2023 Ludovic Courtès <ludo@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -40,8 +40,8 @@
'()))
(closest (string-closest type available)))
(unless (or (not closest) (string=? closest type))
- (display-hint (format #f (G_ "Did you mean @code{~a}?~%")
- closest))))
+ (display-hint (G_ "Did you mean @code{~a}?~%")
+ closest)))
(exit 1))
diff --git a/guix/scripts/import.scm b/guix/scripts/import.scm
index 2bca927d63..fe1d7a8dda 100644
--- a/guix/scripts/import.scm
+++ b/guix/scripts/import.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012-2014, 2020-2022 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2012-2014, 2020-2023 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2014 David Thompson <davet@gnu.org>
;;; Copyright © 2018 Kyle Meyer <kyle@kyleam.com>
;;; Copyright © 2019, 2022 Ricardo Wurmus <rekado@elephly.net>
@@ -106,6 +106,5 @@ Run IMPORTER with ARGS.\n"))
(let ((hint (string-closest importer importers #:threshold 3)))
(report-error (G_ "~a: invalid importer~%") importer)
(when hint
- (display-hint
- (format #f (G_ "Did you mean @code{~a}?~%") hint)))
+ (display-hint (G_ "Did you mean @code{~a}?~%") hint))
(exit 1))))))
diff --git a/guix/scripts/offload.scm b/guix/scripts/offload.scm
index 578b3b9888..8c6132e7c3 100644
--- a/guix/scripts/offload.scm
+++ b/guix/scripts/offload.scm
@@ -220,7 +220,12 @@ number of seconds after which the connection times out."
(session (make-session #:user (build-machine-user machine)
#:host (build-machine-name machine)
#:port (build-machine-port machine)
- #:timeout 10 ;initial timeout (seconds)
+ ;; Multiple derivations may be offloaded in
+ ;; parallel, and when there is a large amount
+ ;; of data to be sent, it can choke lower
+ ;; bandwidth connections and cause timeouts, so
+ ;; set it to a large enough value.
+ #:timeout 30 ;initial timeout (seconds)
;; #:log-verbosity 'protocol
#:identity (build-machine-private-key machine)
diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm
index f65642fb85..eb41eb5563 100644
--- a/guix/scripts/pack.scm
+++ b/guix/scripts/pack.scm
@@ -1,11 +1,11 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2015, 2017-2022 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2015, 2017-2023 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2017, 2018 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2018 Konrad Hinsen <konrad.hinsen@fastmail.net>
;;; Copyright © 2018 Chris Marusich <cmmarusich@gmail.com>
;;; Copyright © 2018 Efraim Flashner <efraim@flashner.co.il>
;;; Copyright © 2020 Tobias Geerinckx-Rice <me@tobias.gr>
-;;; Copyright © 2020, 2021, 2022 Maxim Cournoyer <maxim.cournoyer@gmail.com>
+;;; Copyright © 2020, 2021, 2022, 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;; Copyright © 2020 Eric Bavier <bavier@posteo.net>
;;; Copyright © 2022 Alex Griffin <a@ajgrf.com>
;;;
@@ -67,6 +67,7 @@
self-contained-tarball
debian-archive
+ rpm-archive
docker-image
squashfs-image
@@ -194,104 +195,150 @@ target the profile's @file{bin/env} file:
(leave (G_ "~a: invalid symlink specification~%")
arg))))
-
-;;;
-;;; Tarball format.
-;;;
-(define* (self-contained-tarball/builder profile
- #:key (profile-name "guix-profile")
- (compressor (first %compressors))
- localstatedir?
- (symlinks '())
- (archiver tar)
- (extra-options '()))
- "Return the G-Expression of the builder used for self-contained-tarball."
+(define (set-utf8-locale profile)
+ "Configure the environment to use the \"en_US.utf8\" locale provided by the
+GLIBC-UT8-LOCALES package."
+ ;; Arrange to not depend on 'glibc-utf8-locales' when using '--bootstrap'.
+ (and (or (not (profile? profile))
+ (profile-locales? profile))
+ #~(begin
+ (setenv "GUIX_LOCPATH"
+ #+(file-append glibc-utf8-locales "/lib/locale"))
+ (setlocale LC_ALL "en_US.utf8"))))
+
+(define* (populate-profile-root profile
+ #:key (profile-name "guix-profile")
+ target
+ localstatedir?
+ deduplicate?
+ (symlinks '()))
+ "Populate the root profile directory with SYMLINKS and a Guix database, when
+LOCALSTATEDIR? is set. When DEDUPLICATE? is true, deduplicate the store
+items, which relies on hard links."
(define database
(and localstatedir?
(file-append (store-database (list profile))
"/db/db.sqlite")))
- (define set-utf8-locale
- ;; Arrange to not depend on 'glibc-utf8-locales' when using '--bootstrap'.
- (and (or (not (profile? profile))
- (profile-locales? profile))
- #~(begin
- (setenv "GUIX_LOCPATH"
- #+(file-append glibc-utf8-locales "/lib/locale"))
- (setlocale LC_ALL "en_US.utf8"))))
+ (define bootstrap?
+ ;; Whether a '--bootstrap' environment is needed, for testing purposes.
+ ;; XXX: Infer that from available info.
+ (and (not database) (not (profile-locales? profile))))
(define (import-module? module)
;; Since we don't use deduplication support in 'populate-store', don't
;; import (guix store deduplication) and its dependencies, which includes
- ;; Guile-Gcrypt. That way we can run tests with '--bootstrap'.
+ ;; Guile-Gcrypt, unless DEDUPLICATE? is #t. This makes it possible to run
+ ;; tests with '--bootstrap'.
(and (not-config? module)
- (not (equal? '(guix store deduplication) module))))
-
- (with-imported-modules (source-module-closure
- `((guix build pack)
- (guix build store-copy)
- (guix build utils)
- (guix build union)
- (gnu build install))
- #:select? import-module?)
+ (or deduplicate? (not (equal? '(guix store deduplication) module)))))
+
+ (computed-file "profile-directory"
+ (with-imported-modules (source-module-closure
+ `((guix build pack)
+ (guix build store-copy)
+ (guix build utils)
+ (guix build union)
+ (gnu build install))
+ #:select? import-module?)
+ #~(begin
+ (use-modules (guix build pack)
+ (guix build store-copy)
+ (guix build utils)
+ ((guix build union) #:select (relative-file-name))
+ (gnu build install)
+ (srfi srfi-1)
+ (srfi srfi-26)
+ (ice-9 match))
+
+ (define symlink->directives
+ ;; Return "populate directives" to make the given symlink and its
+ ;; parent directories.
+ (match-lambda
+ ((source '-> target)
+ (let ((target (string-append #$profile "/" target))
+ (parent (dirname source)))
+ ;; Never add a 'directory' directive for "/" so as to
+ ;; preserve its ownership when extracting the archive (see
+ ;; below), and also because this would lead to adding the
+ ;; same entries twice in the tarball.
+ `(,@(if (string=? parent "/")
+ '()
+ `((directory ,parent)))
+ ;; Use a relative file name for compatibility with
+ ;; relocatable packs.
+ (,source -> ,(relative-file-name parent target)))))))
+
+ (define directives
+ ;; Fully-qualified symlinks.
+ (append-map symlink->directives '#$symlinks))
+
+ ;; Make sure non-ASCII file names are properly handled.
+ #+(set-utf8-locale profile)
+
+ ;; Note: there is not much to gain here with deduplication and there
+ ;; is the overhead of the '.links' directory, so turn it off by
+ ;; default. Furthermore GNU tar < 1.30 sometimes fails to extract
+ ;; tarballs with hard links:
+ ;; <http://lists.gnu.org/archive/html/bug-tar/2017-11/msg00009.html>.
+ (populate-store (list "profile") #$output
+ #:deduplicate? #$deduplicate?)
+
+ (when #+localstatedir?
+ (install-database-and-gc-roots #$output #+database #$profile
+ #:profile-name #$profile-name))
+
+ ;; Create SYMLINKS.
+ (for-each (cut evaluate-populate-directive <> #$output)
+ directives)))
+ #:local-build? #f
+ #:guile (if bootstrap? %bootstrap-guile (default-guile))
+ #:options (list #:references-graphs `(("profile" ,profile))
+ #:target target)))
+
+
+;;;
+;;; Tarball format.
+;;;
+(define* (self-contained-tarball/builder profile
+ #:key (profile-name "guix-profile")
+ target
+ localstatedir?
+ deduplicate?
+ symlinks
+ compressor
+ archiver)
+ "Return a GEXP that can build a self-contained tarball."
+
+ (define root (populate-profile-root profile
+ #:profile-name profile-name
+ #:target target
+ #:localstatedir? localstatedir?
+ #:deduplicate? deduplicate?
+ #:symlinks symlinks))
+
+ (with-imported-modules (source-module-closure '((guix build pack)
+ (guix build utils)))
#~(begin
(use-modules (guix build pack)
- (guix build store-copy)
- (guix build utils)
- ((guix build union) #:select (relative-file-name))
- (gnu build install)
- (srfi srfi-1)
- (srfi srfi-26)
- (ice-9 match))
-
- (define %root "root")
-
- (define symlink->directives
- ;; Return "populate directives" to make the given symlink and its
- ;; parent directories.
- (match-lambda
- ((source '-> target)
- (let ((target (string-append #$profile "/" target))
- (parent (dirname source)))
- ;; Never add a 'directory' directive for "/" so as to
- ;; preserve its ownership when extracting the archive (see
- ;; below), and also because this would lead to adding the
- ;; same entries twice in the tarball.
- `(,@(if (string=? parent "/")
- '()
- `((directory ,parent)))
- ;; Use a relative file name for compatibility with
- ;; relocatable packs.
- (,source -> ,(relative-file-name parent target)))))))
-
- (define directives
- ;; Fully-qualified symlinks.
- (append-map symlink->directives '#$symlinks))
+ (guix build utils))
;; Make sure non-ASCII file names are properly handled.
- #+set-utf8-locale
+ #+(set-utf8-locale profile)
(define tar #+(file-append archiver "/bin/tar"))
- ;; Note: there is not much to gain here with deduplication and there
- ;; is the overhead of the '.links' directory, so turn it off.
- ;; Furthermore GNU tar < 1.30 sometimes fails to extract tarballs
- ;; with hard links:
- ;; <http://lists.gnu.org/archive/html/bug-tar/2017-11/msg00009.html>.
- (populate-store (list "profile") %root #:deduplicate? #f)
+ (define %root (if #$localstatedir? "." #$root))
- (when #+localstatedir?
- (install-database-and-gc-roots %root #+database #$profile
- #:profile-name #$profile-name))
+ (when #$localstatedir?
+ ;; Fix the permission of the Guix database file, which was made
+ ;; read-only when copied to the store in populate-profile-root.
+ (copy-recursively #$root %root)
+ (chmod (string-append %root "/var/guix/db/db.sqlite") #o644))
- ;; Create SYMLINKS.
- (for-each (cut evaluate-populate-directive <> %root)
- directives)
-
- ;; Create the tarball.
(with-directory-excursion %root
;; GNU Tar recurses directories by default. Simply add the whole
- ;; current directory, which contains all the generated files so far.
+ ;; current directory, which contains all the files to be archived.
;; This avoids creating duplicate files in the archives that would
;; be stored as hard links by GNU Tar.
(apply invoke tar "-cvf" #$output "."
@@ -320,17 +367,16 @@ added to the pack."
(warning (G_ "entry point not supported in the '~a' format~%")
'tarball))
- (gexp->derivation
- (string-append name ".tar"
- (compressor-extension compressor))
- (self-contained-tarball/builder profile
- #:profile-name profile-name
- #:compressor compressor
- #:localstatedir? localstatedir?
- #:symlinks symlinks
- #:archiver archiver)
- #:target target
- #:references-graphs `(("profile" ,profile))))
+ (gexp->derivation (string-append name ".tar"
+ (compressor-extension compressor))
+ (self-contained-tarball/builder profile
+ #:profile-name profile-name
+ #:target target
+ #:localstatedir? localstatedir?
+ #:deduplicate? deduplicate?
+ #:symlinks symlinks
+ #:compressor compressor
+ #:archiver archiver)))
;;;
@@ -676,18 +722,19 @@ Valid compressors are: ~a~%") compressor-name %valid-compressors)))
'deb))
(define data-tarball
- (computed-file (string-append "data.tar"
- (compressor-extension compressor))
- (self-contained-tarball/builder
- profile
- #:profile-name profile-name
- #:compressor compressor
- #:localstatedir? localstatedir?
- #:symlinks symlinks
- #:archiver archiver)
- #:local-build? #f ;allow offloading
- #:options (list #:references-graphs `(("profile" ,profile))
- #:target target)))
+ (computed-file (string-append "data.tar" (compressor-extension
+ compressor))
+ (self-contained-tarball/builder profile
+ #:target target
+ #:profile-name profile-name
+ #:localstatedir? localstatedir?
+ #:deduplicate? deduplicate?
+ #:symlinks symlinks
+ #:compressor compressor
+ #:archiver archiver)
+ #:local-build? #f ;allow offloading
+ #:options (list #:references-graphs `(("profile" ,profile))
+ #:target target)))
(define build
(with-extensions (list guile-gcrypt)
@@ -702,6 +749,7 @@ Valid compressors are: ~a~%") compressor-name %valid-compressors)))
(guix build utils)
(guix profiles)
(ice-9 match)
+ (ice-9 optargs)
(srfi srfi-1))
(define machine-type
@@ -762,32 +810,23 @@ Valid compressors are: ~a~%") compressor-name %valid-compressors)))
(copy-file #+data-tarball data-tarball-file-name)
- (define (keyword-ref lst keyword)
- (match (memq keyword lst)
- ((_ value . _) value)
- (#f #f)))
-
;; Generate the control archive.
- (define control-file
- (keyword-ref '#$extra-options #:control-file))
-
- (define postinst-file
- (keyword-ref '#$extra-options #:postinst-file))
-
- (define triggers-file
- (keyword-ref '#$extra-options #:triggers-file))
-
- (define control-tarball-file-name
- (string-append "control.tar"
- #$(compressor-extension compressor)))
-
- ;; Write the compressed control tarball. Only the control file is
- ;; mandatory (see: 'man deb' and 'man deb-control').
- (if control-file
- (copy-file control-file "control")
- (call-with-output-file "control"
- (lambda (port)
- (format port "\
+ (let-keywords '#$extra-options #f
+ ((control-file #f)
+ (postinst-file #f)
+ (triggers-file #f))
+
+ (define control-tarball-file-name
+ (string-append "control.tar"
+ #$(compressor-extension compressor)))
+
+ ;; Write the compressed control tarball. Only the control file is
+ ;; mandatory (see: 'man deb' and 'man deb-control').
+ (if control-file
+ (copy-file control-file "control")
+ (call-with-output-file "control"
+ (lambda (port)
+ (format port "\
Package: ~a
Version: ~a
Description: Debian archive generated by GNU Guix.
@@ -797,36 +836,196 @@ Priority: optional
Section: misc
~%" package-name package-version architecture))))
- (when postinst-file
- (copy-file postinst-file "postinst")
- (chmod "postinst" #o755))
+ (when postinst-file
+ (copy-file postinst-file "postinst")
+ (chmod "postinst" #o755))
- (when triggers-file
- (copy-file triggers-file "triggers"))
+ (when triggers-file
+ (copy-file triggers-file "triggers"))
- (define tar (string-append #+archiver "/bin/tar"))
+ (define tar (string-append #+archiver "/bin/tar"))
- (apply invoke tar
- `(,@(tar-base-options
- #:tar tar
- #:compressor #+(and=> compressor compressor-command))
- "-cvf" ,control-tarball-file-name
- "control"
- ,@(if postinst-file '("postinst") '())
- ,@(if triggers-file '("triggers") '())))
+ (apply invoke tar
+ `(,@(tar-base-options
+ #:tar tar
+ #:compressor #+(and=> compressor compressor-command))
+ "-cvf" ,control-tarball-file-name
+ "control"
+ ,@(if postinst-file '("postinst") '())
+ ,@(if triggers-file '("triggers") '())))
- ;; Create the .deb archive using GNU ar.
- (invoke (string-append #+binutils "/bin/ar") "-rv" #$output
- "debian-binary"
- control-tarball-file-name data-tarball-file-name)))))
+ ;; Create the .deb archive using GNU ar.
+ (invoke (string-append #+binutils "/bin/ar") "-rv" #$output
+ "debian-binary"
+ control-tarball-file-name data-tarball-file-name))))))
- (gexp->derivation (string-append name ".deb")
- build
- #:target target
- #:references-graphs `(("profile" ,profile))))
+ (gexp->derivation (string-append name ".deb") build))
;;;
+;;; RPM archive format.
+;;;
+(define* (rpm-archive name profile
+ #:key target
+ (profile-name "guix-profile")
+ entry-point
+ (compressor (first %compressors))
+ deduplicate?
+ localstatedir?
+ (symlinks '())
+ archiver
+ (extra-options '()))
+ "Return a RPM archive (.rpm) containing a store initialized with the closure
+of PROFILE, a derivation. The archive contains /gnu/store. SYMLINKS must be
+a list of (SOURCE -> TARGET) tuples denoting symlinks to be added to the pack.
+ARCHIVER and ENTRY-POINT are not used. RELOCATABLE?, PREIN-FILE, POSTIN-FILE,
+PREUN-FILE and POSTUN-FILE can be provided via EXTRA-OPTIONS."
+ (when entry-point
+ (warning (G_ "entry point not supported in the '~a' format~%") 'rpm))
+
+ (define root (populate-profile-root profile
+ #:profile-name profile-name
+ #:target target
+ #:localstatedir? localstatedir?
+ #:deduplicate? deduplicate?
+ #:symlinks symlinks))
+
+ (define payload
+ (let* ((raw-cpio-file-name "payload.cpio")
+ (compressed-cpio-file-name (string-append raw-cpio-file-name
+ (compressor-extension
+ compressor))))
+ (computed-file compressed-cpio-file-name
+ (with-imported-modules (source-module-closure
+ '((guix build utils)
+ (guix cpio)
+ (guix rpm)))
+ #~(begin
+ (use-modules (guix build utils)
+ (guix cpio)
+ (guix rpm)
+ (srfi srfi-1))
+
+ ;; Make sure non-ASCII file names are properly handled.
+ #+(set-utf8-locale profile)
+
+ (define %root (if #$localstatedir? "." #$root))
+
+ (when #$localstatedir?
+ ;; Fix the permission of the Guix database file, which was made
+ ;; read-only when copied to the store in populate-profile-root.
+ (copy-recursively #$root %root)
+ (chmod (string-append %root "/var/guix/db/db.sqlite") #o644))
+
+ (call-with-output-file #$raw-cpio-file-name
+ (lambda (port)
+ (with-directory-excursion %root
+ ;; The first "." entry is discarded.
+ (write-cpio-archive
+ (remove fhs-directory?
+ (cdr (find-files "." #:directories? #t)))
+ port))))
+ (when #+(compressor-command compressor)
+ (apply invoke (append #+(compressor-command compressor)
+ (list #$raw-cpio-file-name))))
+ (copy-file #$compressed-cpio-file-name #$output)))
+ #:local-build? #f))) ;allow offloading
+
+ (define build
+ (with-extensions (list guile-gcrypt)
+ (with-imported-modules `(((guix config) => ,(make-config.scm))
+ ,@(source-module-closure
+ `((gcrypt hash)
+ (guix build utils)
+ (guix profiles)
+ (guix rpm))
+ #:select? not-config?))
+ #~(begin
+ (use-modules (gcrypt hash)
+ (guix build utils)
+ (guix profiles)
+ (guix rpm)
+ (ice-9 binary-ports)
+ (ice-9 match) ;for manifest->friendly-name
+ (ice-9 optargs)
+ (rnrs bytevectors)
+ (srfi srfi-1))
+
+ ;; Make sure non-ASCII file names are properly handled.
+ #+(set-utf8-locale profile)
+
+ (define machine-type
+ (and=> (or #$target %host-type)
+ (lambda (triplet)
+ (first (string-split triplet #\-)))))
+
+ #$(procedure-source manifest->friendly-name)
+
+ (define manifest (profile-manifest #$profile))
+
+ (define single-entry ;manifest entry
+ (match (manifest-entries manifest)
+ ((entry)
+ entry)
+ (_ #f)))
+
+ (define name
+ (or (and=> single-entry manifest-entry-name)
+ (manifest->friendly-name manifest)))
+
+ (define version
+ (or (and=> single-entry manifest-entry-version) "0.0.0"))
+
+ (define lead
+ (generate-lead (string-append name "-" version)
+ #:target (or #$target %host-type)))
+
+ (define payload-digest
+ (bytevector->hex-string (file-sha256 #$payload)))
+
+ (let-keywords '#$extra-options #f ((relocatable? #f)
+ (prein-file #f)
+ (postin-file #f)
+ (preun-file #f)
+ (postun-file #f))
+
+ (let ((header (generate-header name version
+ payload-digest
+ #$root
+ #$(compressor-name compressor)
+ #:target (or #$target %host-type)
+ #:relocatable? relocatable?
+ #:prein-file prein-file
+ #:postin-file postin-file
+ #:preun-file preun-file
+ #:postun-file postun-file)))
+
+ (define header-sha256
+ (bytevector->hex-string (sha256 (u8-list->bytevector header))))
+
+ (define payload-size (stat:size (stat #$payload)))
+
+ (define header+compressed-payload-size
+ (+ (length header) payload-size))
+
+ (define signature
+ (generate-signature header-sha256
+ header+compressed-payload-size))
+
+ ;; Serialize the archive components to a file.
+ (call-with-input-file #$payload
+ (lambda (in)
+ (call-with-output-file #$output
+ (lambda (out)
+ (put-bytevector out (assemble-rpm-metadata lead
+ signature
+ header))
+ (sendfile out in payload-size)))))))))))
+
+ (gexp->derivation (string-append name ".rpm") build))
+
+
+;;;
;;; Compiling C programs.
;;;
@@ -1158,7 +1357,8 @@ last resort for relocation."
`((tarball . ,self-contained-tarball)
(squashfs . ,squashfs-image)
(docker . ,docker-image)
- (deb . ,debian-archive)))
+ (deb . ,debian-archive)
+ (rpm . ,rpm-archive)))
(define (show-formats)
;; Print the supported pack formats.
@@ -1172,18 +1372,22 @@ last resort for relocation."
docker Tarball ready for 'docker load'"))
(display (G_ "
deb Debian archive installable via dpkg/apt"))
+ (display (G_ "
+ rpm RPM archive installable via rpm/yum"))
(newline))
+(define (required-option symbol)
+ "Return an SYMBOL option that requires a value."
+ (option (list (symbol->string symbol)) #t #f
+ (lambda (opt name arg result . rest)
+ (apply values
+ (alist-cons symbol arg result)
+ rest))))
+
(define %deb-format-options
- (let ((required-option (lambda (symbol)
- (option (list (symbol->string symbol)) #t #f
- (lambda (opt name arg result . rest)
- (apply values
- (alist-cons symbol arg result)
- rest))))))
- (list (required-option 'control-file)
- (required-option 'postinst-file)
- (required-option 'triggers-file))))
+ (list (required-option 'control-file)
+ (required-option 'postinst-file)
+ (required-option 'triggers-file)))
(define (show-deb-format-options)
(display (G_ "
@@ -1202,6 +1406,32 @@ last resort for relocation."
(newline)
(exit 0))
+(define %rpm-format-options
+ (list (required-option 'prein-file)
+ (required-option 'postin-file)
+ (required-option 'preun-file)
+ (required-option 'postun-file)))
+
+(define (show-rpm-format-options)
+ (display (G_ "
+ --help-rpm-format list options specific to the RPM format")))
+
+(define (show-rpm-format-options/detailed)
+ (display (G_ "
+ --prein-file=FILE
+ Embed the provided prein script"))
+ (display (G_ "
+ --postin-file=FILE
+ Embed the provided postin script"))
+ (display (G_ "
+ --preun-file=FILE
+ Embed the provided preun script"))
+ (display (G_ "
+ --postun-file=FILE
+ Embed the provided postun script"))
+ (newline)
+ (exit 0))
+
(define %options
;; Specifications of the command-line options.
(cons* (option '(#\h "help") #f #f
@@ -1278,7 +1508,12 @@ last resort for relocation."
(lambda args
(show-deb-format-options/detailed)))
+ (option '("help-rpm-format") #f #f
+ (lambda args
+ (show-rpm-format-options/detailed)))
+
(append %deb-format-options
+ %rpm-format-options
%transformation-options
%standard-build-options
%standard-cross-build-options
@@ -1296,6 +1531,7 @@ Create a bundle of PACKAGE.\n"))
(show-transformation-options-help)
(newline)
(show-deb-format-options)
+ (show-rpm-format-options)
(newline)
(display (G_ "
-f, --format=FORMAT build a pack in the given FORMAT"))
@@ -1454,6 +1690,16 @@ Create a bundle of PACKAGE.\n"))
(process-file-arg opts 'postinst-file)
#:triggers-file
(process-file-arg opts 'triggers-file)))
+ ('rpm
+ (list #:relocatable? relocatable?
+ #:prein-file
+ (process-file-arg opts 'prein-file)
+ #:postin-file
+ (process-file-arg opts 'postin-file)
+ #:preun-file
+ (process-file-arg opts 'preun-file)
+ #:postun-file
+ (process-file-arg opts 'postun-file)))
(_ '())))
(target (assoc-ref opts 'target))
(bootstrap? (assoc-ref opts 'bootstrap?))
diff --git a/guix/scripts/package.scm b/guix/scripts/package.scm
index 2f774621bb..cb58f56d5a 100644
--- a/guix/scripts/package.scm
+++ b/guix/scripts/package.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012-2022 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2012-2023 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org>
;;; Copyright © 2013, 2015 Mark H Weaver <mhw@netris.org>
;;; Copyright © 2014, 2016 Alex Kost <alezost@gmail.com>
@@ -323,7 +323,7 @@ of manifest entries, in the context of PROFILE."
(settings (search-path-environment-variables entries (list profile)
#:kind 'prefix)))
(unless (null? settings)
- (display-hint (format #f (G_ "Consider setting the necessary environment
+ (display-hint (G_ "Consider setting the necessary environment
variables by running:
@example
@@ -332,7 +332,7 @@ GUIX_PROFILE=\"~a\"
@end example
Alternately, see @command{guix package --search-paths -p ~s}.")
- profile profile)))))
+ profile profile))))
;;;
diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm
index 7b6c58dbc3..2be8de3b9c 100644
--- a/guix/scripts/pull.scm
+++ b/guix/scripts/pull.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013-2015, 2017-2022 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2013-2015, 2017-2023 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2017 Marius Bakke <mbakke@fastmail.com>
;;; Copyright © 2020, 2021 Tobias Geerinckx-Rice <me@tobias.gr>
;;;
@@ -469,9 +469,9 @@ true, display what would be built without actually building it."
;; Is the 'guix' command previously in $PATH the same as the new
;; one? If the answer is "no", then suggest 'hash guix'.
(unless (member guix-command new)
- (display-hint (format #f (G_ "After setting @code{PATH}, run
+ (display-hint (G_ "After setting @code{PATH}, run
@command{hash guix} to make sure your shell refers to @file{~a}.")
- (first new))))
+ (first new)))
(return #f))
(return #f)))))
diff --git a/guix/scripts/repl.scm b/guix/scripts/repl.scm
index 787c63d48e..0b978ae35f 100644
--- a/guix/scripts/repl.scm
+++ b/guix/scripts/repl.scm
@@ -211,6 +211,7 @@ call THUNK."
((guile)
(save-module-excursion
(lambda ()
+ (current-profile) ;populate (%package-module-path); see above
(set-user-module)
;; Do not exit repl on SIGINT.
((@@ (ice-9 top-repl) call-with-sigint)
diff --git a/guix/scripts/shell.scm b/guix/scripts/shell.scm
index 64b5c2e8e9..92bbfb04d0 100644
--- a/guix/scripts/shell.scm
+++ b/guix/scripts/shell.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2021-2022 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2021-2023 Ludovic Courtès <ludo@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -305,16 +305,16 @@ Return the modified OPTS."
(report-error
(G_ "not loading '~a' because not authorized to do so~%")
file)
- (display-hint (format #f (G_ "To allow automatic loading of
+ (display-hint (G_ "To allow automatic loading of
@file{~a} when running @command{guix shell}, you must explicitly authorize its
directory, like so:
@example
echo ~a >> ~a
@end example\n")
- file
- (dirname file)
- (authorized-directory-file)))
+ file
+ (dirname file)
+ (authorized-directory-file))
(exit 1)))))))
diff --git a/guix/scripts/system.scm b/guix/scripts/system.scm
index 6fd915cb5e..c0bc295c00 100644
--- a/guix/scripts/system.scm
+++ b/guix/scripts/system.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2014-2022 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2014-2023 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2016 Alex Kost <alezost@gmail.com>
;;; Copyright © 2016, 2017, 2018 Chris Marusich <cmmarusich@gmail.com>
;;; Copyright © 2017, 2019 Mathieu Othacehe <m.othacehe@gmail.com>
@@ -633,9 +633,9 @@ any, are available. Raise an error if they're not."
(G_ "device '~a' not found: ~a~%")
device (strerror errno))
(unless (string-prefix? "/" device)
- (display-hint (format #f (G_ "If '~a' is a file system
+ (display-hint (G_ "If '~a' is a file system
label, write @code{(file-system-label ~s)} in your @code{device} field.")
- device device)))))))
+ device device))))))
literal)
(for-each (lambda (fs)
(let ((label (file-system-label->string
@@ -1417,8 +1417,7 @@ argument list and OPTS is the option alist."
(let ((hint (string-closest arg actions #:threshold 3)))
(report-error (G_ "~a: unknown action~%") arg)
(when hint
- (display-hint
- (format #f (G_ "Did you mean @code{~a}?~%") hint)))
+ (display-hint (G_ "Did you mean @code{~a}?~%") hint))
(exit 1)))))
(define (match-pair car)
diff --git a/guix/scripts/system/edit.scm b/guix/scripts/system/edit.scm
index d966ee0aaa..0afb071650 100644
--- a/guix/scripts/system/edit.scm
+++ b/guix/scripts/system/edit.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2022 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2022, 2023 Ludovic Courtès <ludo@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -39,8 +39,8 @@
'()))
(closest (string-closest type available)))
(unless (or (not closest) (string=? closest type))
- (display-hint (format #f (G_ "Did you mean @code{~a}?~%")
- closest))))
+ (display-hint (G_ "Did you mean @code{~a}?~%")
+ closest)))
(exit 1))
diff --git a/guix/ssh.scm b/guix/ssh.scm
index 1b825a2573..5b35f664d9 100644
--- a/guix/ssh.scm
+++ b/guix/ssh.scm
@@ -477,7 +477,7 @@ Use SIZES to determine the size of ITEM, which is about to be sent."
(define (display-bar %)
(erase-current-line port)
(format port "~3@a% ~a"
- (inexact->exact (round (* 100. (/ sent total))))
+ (inexact->exact (round %))
(progress-bar % (- (max (current-terminal-columns) 5) 5)))
(force-output port))
diff --git a/guix/status.scm b/guix/status.scm
index 2c69f49fb5..a192cd789a 100644
--- a/guix/status.scm
+++ b/guix/status.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2017-2022 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2017-2023 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2018, 2019 Ricardo Wurmus <rekado@elephly.net>
;;;
;;; This file is part of GNU Guix.
@@ -22,6 +22,7 @@
#:use-module (guix i18n)
#:use-module (guix colors)
#:use-module (guix progress)
+ #:autoload (guix ui) (display-hint)
#:autoload (guix build syscalls) (terminal-columns)
#:autoload (guix build download) (nar-uri-abbreviation)
#:use-module (guix store)
@@ -526,6 +527,21 @@ substitutes being downloaded."
(erase-current-line*) ;erase spinner or progress bar
(format port (failure (G_ "build of ~a failed")) drv)
(newline port)
+ (let ((properties (and=> (false-if-exception
+ (read-derivation-from-file drv))
+ derivation-properties)))
+ (when (and (pair? properties)
+ (eq? (assq-ref properties 'type) 'profile-hook)
+ (eq? (assq-ref properties 'hook) 'package-cache))
+ (display-hint (G_ "This usually indicates a bug in one of
+the channels you are pulling from, or some incompatibility among them. You
+can check the build log and report the issue to the channel developers.
+
+The channels you are pulling from are: ~a.")
+ (string-join
+ (map symbol->string
+ (or (assq-ref properties 'channels)
+ '(guix)))))))
(match (derivation-log-file drv)
(#f
(format port (failure (G_ "Could not find build log for '~a'."))
diff --git a/guix/ui.scm b/guix/ui.scm
index 9f81ff3b8e..b6c3bd04ba 100644
--- a/guix/ui.scm
+++ b/guix/ui.scm
@@ -296,9 +296,22 @@ VARIABLE and return it, or #f if none was found."
(define %hint-color (color BOLD CYAN))
-(define* (display-hint message #:optional (port (current-error-port)))
- "Display MESSAGE, a l10n message possibly containing Texinfo markup, to
-PORT."
+(define (texinfo-quote str)
+ "Quote at signs and braces in STR to obtain its Texinfo represention."
+ (list->string
+ (string-fold-right (lambda (chr result)
+ (if (memq chr '(#\@ #\{ #\}))
+ (cons* #\@ chr result)
+ (cons chr result)))
+ '()
+ str)))
+
+(define* (display-hint message
+ #:key (port (current-error-port))
+ #:rest arguments)
+ "Display MESSAGE, a l10n message possibly containing Texinfo markup and
+'format' escape, to PORT. ARGUMENTS is a (possibly empty) list of strings or
+other objects that must match the 'format' escapes in MESSAGE."
(define colorize
(if (color-output? port)
(lambda (str)
@@ -309,7 +322,16 @@ PORT."
(display
;; XXX: We should arrange so that the initial indent is wider.
(parameterize ((%text-width (max 15 (- (terminal-columns) 5))))
- (texi->plain-text message))
+ (texi->plain-text (match arguments
+ (() message)
+ (_ (apply format #f message
+ (map (match-lambda
+ ((? string? str)
+ (texinfo-quote str))
+ (obj
+ (texinfo-quote
+ (object->string obj))))
+ arguments))))))
port))
(define* (report-unbound-variable-error args #:key frame)
@@ -324,8 +346,8 @@ arguments."
(#f
(display-hint (G_ "Did you forget a @code{use-modules} form?")))
((? module? module)
- (display-hint (format #f (G_ "Did you forget @code{(use-modules ~a)}?")
- (module-name module))))))))
+ (display-hint (G_ "Did you forget @code{(use-modules ~a)}?")
+ (module-name module)))))))
(define (check-module-matches-file module file)
"Check whether FILE starts with 'define-module MODULE' and print a hint if
@@ -334,10 +356,10 @@ it doesn't."
;; definitions and try loading them with 'guix build -L …', so help them
;; diagnose the problem.
(define (hint)
- (display-hint (format #f (G_ "File @file{~a} should probably start with:
+ (display-hint (G_ "File @file{~a} should probably start with:
@example\n(define-module ~a)\n@end example")
- file module)))
+ file module))
(catch 'system-error
(lambda ()
@@ -663,12 +685,12 @@ interpreted."
(name1 (manifest-entry-name (top-most-entry first)))
(name2 (manifest-entry-name (top-most-entry second))))
(if (string=? name1 name2)
- (display-hint (format #f (G_ "You cannot have two different versions
+ (display-hint (G_ "You cannot have two different versions
or variants of @code{~a} in the same profile.")
- name1))
- (display-hint (format #f (G_ "Try upgrading both @code{~a} and @code{~a},
+ name1)
+ (display-hint (G_ "Try upgrading both @code{~a} and @code{~a},
or remove one of them from the profile.")
- name1 name2)))))
+ name1 name2))))
;; On Guile 3.0, in 'call-with-error-handling' we need to re-raise. To
;; preserve useful backtraces in case of unhandled errors, we want that to
@@ -2226,8 +2248,7 @@ found."
(format (current-error-port)
(G_ "guix: ~a: command not found~%") command)
(when hint
- (display-hint (format #f (G_ "Did you mean @code{~a}?")
- hint)))
+ (display-hint (G_ "Did you mean @code{~a}?") hint))
(show-guix-usage)))))
(file
(load file)
diff --git a/guix/utils.scm b/guix/utils.scm
index 1a1cf673b8..f47c565ab5 100644
--- a/guix/utils.scm
+++ b/guix/utils.scm
@@ -16,6 +16,7 @@
;;; Copyright © 2022 Taiju HIGASHI <higashi@taiju.info>
;;; Copyright © 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>
;;; Copyright © 2022 Antero Mejr <antero@mailbox.org>
+;;; Copyright © 2023 Philip McGrath <philip@philipmcgrath.com>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -104,6 +105,7 @@
target-riscv64?
target-mips64el?
target-64bit?
+ target-little-endian?
ar-for-target
as-for-target
cc-for-target
@@ -743,6 +745,12 @@ architecture (x86_64)?"
(any (cut string-prefix? <> system) '("x86_64" "aarch64" "mips64"
"powerpc64" "riscv64")))
+(define* (target-little-endian? #:optional (target (or (%current-target-system)
+ (%current-system))))
+ "Is the architecture of TARGET little-endian?"
+ ;; At least in Guix. Aarch64 and 32-bit arm have a big-endian mode as well.
+ (not (target-ppc32? target)))
+
(define* (ar-for-target #:optional (target (%current-target-system)))
(if target
(string-append target "-ar")