guix_mirror_bot pushed a commit to branch master
in repository guix.
commit 0c1ea038e9ddfc8feab972296c2f31050cbf3728
Author: Ludovic Courtès <[email protected]>
AuthorDate: Sun Nov 16 22:58:50 2025 +0100
serialization: Formally declare serializable types.
* guix/serialization.scm (write-boolean, read-boolean)
(read-base16, write-base16): New procedures.
(<substitutable>, <path-info>): New record types.
(read-substitutable-path-list, read-path-info): New procedures.
(define-serializable-types): New macro.
<top level>: Use it.
* guix/store.scm (<substitutable>, <path-info>)
(read-substitutable-path-list, read-path-info): Move to serialization.scm.
(read-arg, write-arg): Remove.
* guix/store.scm (open-connection, process-stderr)
(add-to-store, add-file-tree-to-store, run-gc)
(export-path, export-paths): Use ‘write-value’ and ‘read-value’.
(store-path): Rename to…
(make-store-path): … this.
(output-path, fixed-output-path): Adjust accordingly.
Change-Id: I0b8863e48cb59205fa7812e8202f9a175ec8606b
Signed-off-by: Ludovic Courtès <[email protected]>
---
guix/serialization.scm | 152 +++++++++++++++++++++++++++++--
guix/store.scm | 239 +++++++++++++++----------------------------------
2 files changed, 221 insertions(+), 170 deletions(-)
diff --git a/guix/serialization.scm b/guix/serialization.scm
index 9656e5ac2a..d6ebc27833 100644
--- a/guix/serialization.scm
+++ b/guix/serialization.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021
Ludovic Courtès <[email protected]>
+;;; Copyright © 2012-2021, 2025 Ludovic Courtès <[email protected]>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -17,8 +17,11 @@
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (guix serialization)
+ #:autoload (guix base16) (base16-string->bytevector
+ bytevector->base16-string)
#:use-module (rnrs bytevectors)
#:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-9)
#:use-module (srfi srfi-26)
#:use-module (srfi srfi-34)
#:use-module (srfi srfi-35)
@@ -27,16 +30,23 @@
#:use-module (ice-9 match)
#:use-module (ice-9 ftw)
#:use-module (system foreign)
- #:export (write-int read-int
+ #:export (write-value
+ read-value
+ write-bytevector
+ read-maybe-utf8-string
+ (dump . dump-port*)
+
+ ;; The following bindings are exported for backward compatibility
+ ;; but one should use 'read-value' and 'write-value' instead.
+ write-int read-int
write-long-long read-long-long
write-padding
- write-bytevector write-string
- read-string read-latin1-string read-maybe-utf8-string
+ write-string
+ read-string read-latin1-string
write-string-list read-string-list
write-string-pairs read-string-pairs
write-store-path read-store-path
write-store-path-list read-store-path-list
- (dump . dump-port*)
&nar-error
nar-error?
@@ -49,6 +59,21 @@
write-file
write-file-tree
+
+ substitutable?
+ substitutable-path
+ substitutable-deriver
+ substitutable-references
+ substitutable-download-size
+ substitutable-nar-size
+
+ path-info?
+ path-info-deriver
+ path-info-hash
+ path-info-references
+ path-info-registration-time
+ path-info-nar-size
+
fold-archive
restore-file
dump-file))
@@ -101,6 +126,12 @@
(let ((b (get-bytevector-n* p 8)))
(bytevector-u32-ref b 0 (endianness little))))
+(define (write-boolean b p)
+ (write-int (if b 1 0) p))
+
+(define (read-boolean p)
+ (not (zero? (read-int p))))
+
(define (write-long-long n p)
(let ((b (make-bytevector 8 0)))
(bytevector-u64-set! b 0 n (endianness little))
@@ -161,6 +192,12 @@ substitute invalid byte sequences with question marks.
This is a
(set-port-conversion-strategy! port 'substitute)
(rdelim:read-string port)))
+(define (read-base16 p)
+ (base16-string->bytevector (read-string p)))
+
+(define (write-base16 bv p)
+ (write-string (bytevector->base16-string bv) p))
+
(define (write-string-list l p)
(write-int (length l) p)
(for-each (cut write-string <> p) l))
@@ -229,6 +266,111 @@ any run-time allocations or computations."
bytes)
#`(put-bytevector port #,bv))))))
+;; Information about a substitutable store path.
+(define-record-type <substitutable>
+ (substitutable path deriver refs dl-size nar-size)
+ substitutable?
+ (path substitutable-path)
+ (deriver substitutable-deriver)
+ (refs substitutable-references)
+ (dl-size substitutable-download-size)
+ (nar-size substitutable-nar-size))
+
+(define (read-substitutable-path-list p)
+ (let loop ((len (read-int p))
+ (result '()))
+ (if (zero? len)
+ (reverse result)
+ (let ((path (read-store-path p))
+ (deriver (read-store-path p))
+ (refs (read-store-path-list p))
+ (dl-size (read-long-long p))
+ (nar-size (read-long-long p)))
+ (loop (- len 1)
+ (cons (substitutable path deriver refs dl-size nar-size)
+ result))))))
+
+;; Information about a store path.
+(define-record-type <path-info>
+ (make-path-info deriver hash references registration-time nar-size)
+ path-info?
+ (deriver path-info-deriver) ;string | #f
+ (hash path-info-hash)
+ (references path-info-references)
+ (registration-time path-info-registration-time)
+ (nar-size path-info-nar-size))
+
+(define (read-path-info p)
+ (let ((deriver (match (read-store-path p)
+ ("" #f)
+ (x x)))
+ (hash (base16-string->bytevector (read-string p)))
+ (refs (read-store-path-list p))
+ (registration-time (read-int p))
+ (nar-size (read-long-long p)))
+ (make-path-info deriver hash refs registration-time nar-size)))
+
+(define-syntax define-serializable-types
+ (syntax-rules ()
+ "Define READ-ANY and WRITE-ANY as macros that dispatch serialization and
+deserialization of known data types. These two macros can then be used like
so:
+
+ (READ-ANY integer PORT)
+
+and:
+
+ (WRITE-ANY store-path VALUE PORT)
+
+The former returns the value it read; the latter returns the unspecified
+value."
+ ((_ read-any write-any (type read write) ...)
+ (begin
+ ;; Define syntactic keywords.
+ (define-syntax type
+ (lambda (s)
+ #`(syntax-error "invalid use of serializable type name" #,s)))
+ ...
+ (export type ...)
+
+ (define-syntax write-any
+ (syntax-rules (type ...)
+ "Write the following TYPE value to the given port."
+ ((_ type arg port)
+ (write arg port))
+ ...))
+ (define-syntax read-any
+ (syntax-rules (type ...)
+ "Read from the given port a value of TYPE."
+ ((_ type port)
+ (read port))
+ ...))))))
+
+(define no-op (const #t))
+
+;; Serializable types known to the client/daemon protocol.
+(define-serializable-types read-value write-value
+ (integer read-int write-int)
+ (long-long read-long-long write-long-long)
+ (boolean read-boolean write-boolean)
+ (bytevector read-byte-string write-bytevector)
+ (string read-string write-string)
+ (string-list read-string-list write-string-list)
+ (string-pairs read-string-pairs write-string-pairs)
+ (store-path read-store-path write-store-path)
+ (store-path-list read-store-path-list write-store-path-list)
+ (base16 read-base16 write-base16)
+ (path-info read-path-info write-path-info/not-implemented)
+ (substitutable-path-list read-substitutable-path-list
+ write-substitutable-path-list/not-implemented)
+
+ ;; When reading a file, just return the input port and let the caller (a
+ ;; server) call 'restore-file' or whatever is relevant for the operation.
+ (file identity write-file)
+
+ ;; User-provided as used in the 'export-path' and 'import-paths' remote
+ ;; procedures.
+ (stream no-op no-op))
+
(define-condition-type &nar-read-error &nar-error
nar-read-error?
diff --git a/guix/store.scm b/guix/store.scm
index 42f3246f99..45dc275ab1 100644
--- a/guix/store.scm
+++ b/guix/store.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012-2024 Ludovic Courtès <[email protected]>
+;;; Copyright © 2012-2025 Ludovic Courtès <[email protected]>
;;; Copyright © 2018 Jan Nieuwenhuizen <[email protected]>
;;; Copyright © 2019, 2020 Mathieu Othacehe <[email protected]>
;;; Copyright © 2020 Florian Pelz <[email protected]>
@@ -55,6 +55,22 @@
uri-host
uri-port
uri-path)
+
+ ;; Bindings re-exported for backward compatibility.
+ #:re-export (substitutable?
+ substitutable-path
+ substitutable-deriver
+ substitutable-references
+ substitutable-download-size
+ substitutable-nar-size
+
+ path-info?
+ path-info-deriver
+ path-info-hash
+ path-info-references
+ path-info-registration-time
+ path-info-nar-size)
+
#:export (%daemon-socket-uri
%gc-roots-directory
%default-substitute-urls
@@ -133,23 +149,10 @@
add-permanent-root
remove-permanent-root
- substitutable?
- substitutable-path
- substitutable-deriver
- substitutable-references
- substitutable-download-size
- substitutable-nar-size
has-substitutes?
substitutable-paths
substitutable-path-info
- path-info?
- path-info-deriver
- path-info-hash
- path-info-references
- path-info-registration-time
- path-info-nar-size
-
built-in-builders
substitute-urls
references
@@ -194,7 +197,7 @@
grafting?
%store-prefix
- store-path
+ (make-store-path . store-path)
output-path
fixed-output-path
store-path?
@@ -291,100 +294,6 @@
(make-parameter (or (getenv "GUIX_DAEMON_SOCKET")
%default-socket-path)))
-
-
-;; Information about a substitutable store path.
-(define-record-type <substitutable>
- (substitutable path deriver refs dl-size nar-size)
- substitutable?
- (path substitutable-path)
- (deriver substitutable-deriver)
- (refs substitutable-references)
- (dl-size substitutable-download-size)
- (nar-size substitutable-nar-size))
-
-(define (read-substitutable-path-list p)
- (let loop ((len (read-int p))
- (result '()))
- (if (zero? len)
- (reverse result)
- (let ((path (read-store-path p))
- (deriver (read-store-path p))
- (refs (read-store-path-list p))
- (dl-size (read-long-long p))
- (nar-size (read-long-long p)))
- (loop (- len 1)
- (cons (substitutable path deriver refs dl-size nar-size)
- result))))))
-
-;; Information about a store path.
-(define-record-type <path-info>
- (path-info deriver hash references registration-time nar-size)
- path-info?
- (deriver path-info-deriver) ;string | #f
- (hash path-info-hash)
- (references path-info-references)
- (registration-time path-info-registration-time)
- (nar-size path-info-nar-size))
-
-(define (read-path-info p)
- (let ((deriver (match (read-store-path p)
- ("" #f)
- (x x)))
- (hash (base16-string->bytevector (read-string p)))
- (refs (read-store-path-list p))
- (registration-time (read-int p))
- (nar-size (read-long-long p)))
- (path-info deriver hash refs registration-time nar-size)))
-
-(define-syntax write-arg
- (syntax-rules (integer boolean bytevector
- string string-list string-pairs
- store-path store-path-list base16)
- ((_ integer arg p)
- (write-int arg p))
- ((_ boolean arg p)
- (write-int (if arg 1 0) p))
- ((_ bytevector arg p)
- (write-bytevector arg p))
- ((_ string arg p)
- (write-string arg p))
- ((_ string-list arg p)
- (write-string-list arg p))
- ((_ string-pairs arg p)
- (write-string-pairs arg p))
- ((_ store-path arg p)
- (write-store-path arg p))
- ((_ store-path-list arg p)
- (write-store-path-list arg p))
- ((_ base16 arg p)
- (write-string (bytevector->base16-string arg) p))))
-
-(define-syntax read-arg
- (syntax-rules (integer boolean string store-path
- store-path-list string-list string-pairs
- substitutable-path-list path-info base16)
- ((_ integer p)
- (read-int p))
- ((_ boolean p)
- (not (zero? (read-int p))))
- ((_ string p)
- (read-string p))
- ((_ store-path p)
- (read-store-path p))
- ((_ store-path-list p)
- (read-store-path-list p))
- ((_ string-list p)
- (read-string-list p))
- ((_ string-pairs p)
- (read-string-pairs p))
- ((_ substitutable-path-list p)
- (read-substitutable-path-list p))
- ((_ path-info p)
- (read-path-info p))
- ((_ base16 p)
- (base16-string->bytevector (read-string p)))))
-
;; remote-store.cc
@@ -596,23 +505,23 @@ daemon. Return a server object."
((output flush)
(buffering-output-port port
(make-bytevector 8192))))
- (write-int %worker-magic-1 port)
- (let ((r (read-int port)))
+ (write-value integer %worker-magic-1 port)
+ (let ((r (read-value integer port)))
(unless (= r %worker-magic-2)
(handshake-error))
- (let ((v (read-int port)))
+ (let ((v (read-value integer port)))
(unless (= (protocol-major %protocol-version)
(protocol-major v))
(handshake-error))
- (write-int %protocol-version port)
+ (write-value integer %protocol-version port)
(when (>= (protocol-minor v) 14)
- (write-int (if cpu-affinity 1 0) port)
+ (write-value integer (if cpu-affinity 1 0) port)
(when cpu-affinity
- (write-int cpu-affinity port)))
+ (write-value integer cpu-affinity port)))
(when (>= (protocol-minor v) 11)
- (write-int (if reserve-space? 1 0) port))
+ (write-value integer (if reserve-space? 1 0) port))
(letrec* ((actual-built-in-builders
(if built-in-builders
(delay built-in-builders)
@@ -744,10 +653,10 @@ encoding conversion errors."
(define %stderr-last #x616c7473) ; "alts", we're done
(define %stderr-error #x63787470) ; "cxtp", error reporting
- (let ((k (read-int p)))
+ (let ((k (read-value integer p)))
(cond ((= k %stderr-write)
;; Write a byte stream to USER-PORT.
- (let* ((len (read-int p))
+ (let* ((len (read-value integer p))
(m (modulo len 8)))
(dump-port p user-port len
#:buffer-size (if (<= len 16384) 16384 65536))
@@ -759,7 +668,7 @@ encoding conversion errors."
;; Read a byte stream from USER-PORT.
;; Note: Avoid 'get-bytevector-n' to work around
;; <http://bugs.gnu.org/17591> in Guile up to 2.0.11.
- (let* ((max-len (read-int p))
+ (let* ((max-len (read-value integer p))
(data (make-bytevector max-len))
(len (get-bytevector-n! user-port data 0 max-len)))
(write-bytevector data p len)
@@ -781,7 +690,7 @@ encoding conversion errors."
;; errors like DB schema version mismatches, so check for EOF.
(status (if (and (>= (store-connection-minor-version server)
8)
(not (eof-object? (lookahead-u8 p))))
- (read-int p)
+ (read-value integer p)
1)))
(raise (condition (&store-protocol-error
(message error)
@@ -864,9 +773,9 @@ encoding conversion errors."
(let-syntax ((send (syntax-rules ()
((_ (type option) ...)
(begin
- (write-arg type option buffered)
+ (write-value type option buffered)
...)))))
- (write-int (operation-id set-options) buffered)
+ (write-value integer (operation-id set-options) buffered)
(send (boolean keep-failed?) (boolean keep-going?)
(boolean fallback?) (integer verbosity))
(when (< (store-connection-minor-version server) #x61)
@@ -1018,15 +927,15 @@ bytevector) as its internal buffer, and a thunk to flush
this output port."
(let* ((s (store-connection-socket server))
(buffered (store-connection-output-port server)))
(record-operation 'name)
- (write-int (operation-id name) buffered)
- (write-arg type arg buffered)
+ (write-value integer (operation-id name) buffered)
+ (write-value type arg buffered)
...
(write-buffered-output server)
;; Loop until the server is done sending error output.
(let loop ((done? (process-stderr server)))
(or done? (loop (process-stderr server))))
- (values (read-arg return s) ...))))))
+ (values (read-value return s) ...))))))
(define-syntax-rule (define-operation (name args ...)
docstring return ...)
@@ -1144,16 +1053,16 @@ path."
(record-operation 'add-to-store)
(let ((port (store-connection-socket server))
(buffered (store-connection-output-port server)))
- (write-int (operation-id add-to-store) buffered)
- (write-string basename buffered)
- (write-int 1 buffered) ;obsolete, must be #t
- (write-int (if recursive? 1 0) buffered)
- (write-string hash-algo buffered)
+ (write-value integer (operation-id add-to-store) buffered)
+ (write-value string basename buffered)
+ (write-value integer 1 buffered) ;obsolete,
must be #t
+ (write-value boolean recursive? buffered)
+ (write-value string hash-algo buffered)
(write-file file-name buffered #:select? select?)
(write-buffered-output server)
(let loop ((done? (process-stderr server)))
(or done? (loop (process-stderr server))))
- (read-store-path port)))))
+ (read-value store-path port)))))
(lambda* (server basename recursive? hash-algo file-name
#:key (select? true))
"Add the contents of FILE-NAME under BASENAME to the store
@@ -1258,11 +1167,11 @@ an arbitrary directory layout in the store without
creating a derivation."
(record-operation 'add-to-store/tree)
(let ((port (store-connection-socket server))
(buffered (store-connection-output-port server)))
- (write-int (operation-id add-to-store) buffered)
- (write-string basename buffered)
- (write-int 1 buffered) ;obsolete, must be #t
- (write-int (if recursive? 1 0) buffered)
- (write-string hash-algo buffered)
+ (write-value integer (operation-id add-to-store) buffered)
+ (write-value string basename buffered)
+ (write-value integer 1 buffered) ;obsolete,
must be #t
+ (write-value integer (if recursive? 1 0) buffered)
+ (write-value string hash-algo buffered)
(write-file-tree basename buffered
#:file-type+size file-type+size
#:file-port file-port
@@ -1271,7 +1180,7 @@ an arbitrary directory layout in the store without
creating a derivation."
(write-buffered-output server)
(let loop ((done? (process-stderr server)))
(or done? (loop (process-stderr server))))
- (let ((result (read-store-path port)))
+ (let ((result (read-value store-path port)))
(hash-set! cache tree result)
result)))))
@@ -1688,25 +1597,25 @@ bytes, before the GC can stop. Return the list of
store paths delete,
and the number of bytes freed."
(let ((s (store-connection-socket server))
(buffered (store-connection-output-port server)))
- (write-int (operation-id collect-garbage) buffered)
- (write-int action buffered)
- (write-store-path-list to-delete buffered)
- (write-arg boolean #f buffered) ; ignore-liveness?
- (write-long-long min-freed buffered)
- (write-int 0 buffered) ; obsolete
+ (write-value integer (operation-id collect-garbage) buffered)
+ (write-value integer action buffered)
+ (write-value store-path-list to-delete buffered)
+ (write-value boolean #f buffered) ;ignore-liveness?
+ (write-value long-long min-freed buffered)
+ (write-value integer 0 buffered) ;obsolete
(when (>= (store-connection-minor-version server) 5)
;; Obsolete `use-atime' and `max-atime' parameters.
- (write-int 0 buffered)
- (write-int 0 buffered))
+ (write-value integer 0 buffered)
+ (write-value integer 0 buffered))
(write-buffered-output server)
;; Loop until the server is done sending error output.
(let loop ((done? (process-stderr server)))
(or done? (loop (process-stderr server))))
- (let ((paths (read-store-path-list s))
- (freed (read-long-long s))
- (obsolete (read-long-long s)))
+ (let ((paths (read-value store-path-list s))
+ (freed (read-value long-long s))
+ (obsolete (read-value long-long s)))
(unless (null? paths)
;; To be on the safe side, completely invalidate both caches.
;; Otherwise we could end up returning store paths that are no longer
@@ -1748,22 +1657,22 @@ collected, and the number of bytes freed."
is raised if the set of paths read from PORT is not signed (as per
'export-path #:sign? #t'.) Return the list of store paths imported."
(let ((s (store-connection-socket server)))
- (write-int (operation-id import-paths) s)
+ (write-value integer (operation-id import-paths) s)
(let loop ((done? (process-stderr server port)))
(or done? (loop (process-stderr server port))))
- (read-store-path-list s)))
+ (read-value store-path-list s)))
(define* (export-path server path port #:key (sign? #t))
"Export PATH to PORT. When SIGN? is true, sign it."
(let ((s (store-connection-socket server))
(buffered (store-connection-output-port server)))
- (write-int (operation-id export-path) buffered)
- (write-store-path path buffered)
- (write-arg boolean sign? buffered)
+ (write-value integer (operation-id export-path) buffered)
+ (write-value store-path path buffered)
+ (write-value boolean sign? buffered)
(write-buffered-output server)
(let loop ((done? (process-stderr server port)))
(or done? (loop (process-stderr server port))))
- (= 1 (read-int s))))
+ (= 1 (read-value integer s))))
(define* (export-paths server paths port #:key (sign? #t) recursive?
(start (const #f))
@@ -1792,9 +1701,9 @@ itself. FINISH is called when the last store item has
been called."
(match paths
(()
(apply finish state)
- (write-int 0 port))
+ (write-value integer 0 port))
((head tail ...)
- (write-int 1 port)
+ (write-value integer 1 port)
(and (export-path server head port #:sign? sign?)
(loop tail
(call-with-values
@@ -2273,7 +2182,7 @@ in SIZE bytes."
(logxor o (bytevector-u8-ref bv i)))
(loop (+ 1 i))))))
-(define (store-path type hash name) ; makeStorePath
+(define (make-store-path type hash name) ; makeStorePath
"Return the store path for NAME/HASH/TYPE."
(let* ((s (string-append type ":sha256:"
(bytevector->base16-string hash) ":"
@@ -2287,10 +2196,10 @@ in SIZE bytes."
(define (output-path output hash name) ; makeOutputPath
"Return an output path for OUTPUT (the name of the output as a string) of
the derivation called NAME with hash HASH."
- (store-path (string-append "output:" output) hash
- (if (string=? output "out")
- name
- (string-append name "-" output))))
+ (make-store-path (string-append "output:" output) hash
+ (if (string=? output "out")
+ name
+ (string-append name "-" output))))
(define* (fixed-output-path name hash
#:key
@@ -2301,14 +2210,14 @@ the derivation called NAME with hash HASH."
HASH-ALGO, of the derivation NAME. RECURSIVE? has the same meaning as for
'add-to-store'."
(if (and recursive? (eq? hash-algo 'sha256))
- (store-path "source" hash name)
+ (make-store-path "source" hash name)
(let ((tag (string-append "fixed:" output ":"
(if recursive? "r:" "")
(symbol->string hash-algo) ":"
(bytevector->base16-string hash) ":")))
- (store-path (string-append "output:" output)
- (sha256 (string->utf8 tag))
- name))))
+ (make-store-path (string-append "output:" output)
+ (sha256 (string->utf8 tag))
+ name))))
(define (store-path? path)
"Return #t if PATH is a store path."