summaryrefslogtreecommitdiff
path: root/guix/scripts/shell.scm
diff options
context:
space:
mode:
Diffstat (limited to 'guix/scripts/shell.scm')
-rw-r--r--guix/scripts/shell.scm161
1 files changed, 103 insertions, 58 deletions
diff --git a/guix/scripts/shell.scm b/guix/scripts/shell.scm
index 546639818f1..a92932cbc95 100644
--- a/guix/scripts/shell.scm
+++ b/guix/scripts/shell.scm
@@ -1,5 +1,5 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2021 Ludovic Courtès <ludo@gnu.org> 2;;; Copyright © 2021-2022 Ludovic Courtès <ludo@gnu.org>
3;;; 3;;;
4;;; This file is part of GNU Guix. 4;;; This file is part of GNU Guix.
5;;; 5;;;
@@ -21,7 +21,8 @@
21 #:use-module ((guix diagnostics) #:select (location)) 21 #:use-module ((guix diagnostics) #:select (location))
22 #:use-module (guix scripts environment) 22 #:use-module (guix scripts environment)
23 #:autoload (guix scripts build) (show-build-options-help) 23 #:autoload (guix scripts build) (show-build-options-help)
24 #:autoload (guix transformations) (show-transformation-options-help) 24 #:autoload (guix transformations) (transformation-option-key?
25 show-transformation-options-help)
25 #:use-module (guix scripts) 26 #:use-module (guix scripts)
26 #:use-module (guix packages) 27 #:use-module (guix packages)
27 #:use-module (guix profiles) 28 #:use-module (guix profiles)
@@ -40,6 +41,7 @@
40 #:use-module ((guix build utils) #:select (mkdir-p)) 41 #:use-module ((guix build utils) #:select (mkdir-p))
41 #:use-module (guix cache) 42 #:use-module (guix cache)
42 #:use-module ((ice-9 ftw) #:select (scandir)) 43 #:use-module ((ice-9 ftw) #:select (scandir))
44 #:autoload (gnu packages) (cache-is-authoritative?)
43 #:export (guix-shell)) 45 #:export (guix-shell))
44 46
45(define (show-help) 47(define (show-help)
@@ -201,51 +203,35 @@ a hash-prefixed comment, or a blank line."
201 (const #f))) 203 (const #f)))
202 204
203(define (options-with-caching opts) 205(define (options-with-caching opts)
204 "If OPTS contains exactly one 'load' or one 'manifest' key, automatically 206 "If OPTS contains only options that allow us to compute a cache key,
205add a 'profile' key (when a profile for that file is already in cache) or a 207automatically add a 'profile' key (when a profile for that file is already in
206'gc-root' key (to add the profile to cache)." 208cache) or a 'gc-root' key (to add the profile to cache)."
207 (define (single-file-for-caching opts) 209 ;; Attempt to compute a file name for use as the cached profile GC root.
208 (let loop ((opts opts) 210 (let* ((root timestamp (profile-cached-gc-root opts))
209 (file #f)) 211 (stat (and root (false-if-exception (lstat root)))))
210 (match opts 212 (if (and (not (assoc-ref opts 'rebuild-cache?))
211 (() file) 213 stat
212 ((('package . _) . _) #f) 214 (<= timestamp (stat:mtime stat)))
213 ((('load . ('package candidate)) . rest) 215 (let ((now (current-time)))
214 (and (not file) (loop rest candidate))) 216 ;; Update the atime on ROOT to reflect usage.
215 ((('manifest . candidate) . rest) 217 (utime root
216 (and (not file) (loop rest candidate))) 218 now (stat:mtime stat) 0 (stat:mtimensec stat)
217 ((('expression . _) . _) #f) 219 AT_SYMLINK_NOFOLLOW)
218 ((_ . rest) (loop rest file))))) 220 (alist-cons 'profile root
219 221 (remove (match-lambda
220 ;; Check whether there's a single 'load' or 'manifest' option. When that is 222 (('load . _) #t)
221 ;; the case, arrange to automatically cache the resulting profile. 223 (('manifest . _) #t)
222 (match (single-file-for-caching opts) 224 (('package . _) #t)
223 (#f opts) 225 (('ad-hoc-package . _) #t)
224 (file 226 (_ #f))
225 (let* ((root (profile-cached-gc-root file)) 227 opts))) ;load right away
226 (stat (and root (false-if-exception (lstat root))))) 228 (if (and root (not (assq-ref opts 'gc-root)))
227 (if (and (not (assoc-ref opts 'rebuild-cache?)) 229 (begin
228 stat 230 (if stat
229 (<= (stat:mtime ((@ (guile) stat) file)) 231 (delete-file root)
230 (stat:mtime stat))) 232 (mkdir-p (dirname root)))
231 (let ((now (current-time))) 233 (alist-cons 'gc-root root opts))
232 ;; Update the atime on ROOT to reflect usage. 234 opts))))
233 (utime root
234 now (stat:mtime stat) 0 (stat:mtimensec stat)
235 AT_SYMLINK_NOFOLLOW)
236 (alist-cons 'profile root
237 (remove (match-lambda
238 (('load . _) #t)
239 (('manifest . _) #t)
240 (_ #f))
241 opts))) ;load right away
242 (if (and root (not (assq-ref opts 'gc-root)))
243 (begin
244 (if stat
245 (delete-file root)
246 (mkdir-p (dirname root)))
247 (alist-cons 'gc-root root opts))
248 opts))))))
249 235
250(define (auto-detect-manifest opts) 236(define (auto-detect-manifest opts)
251 "If OPTS do not specify packages or a manifest, load a \"guix.scm\" or 237 "If OPTS do not specify packages or a manifest, load a \"guix.scm\" or
@@ -308,28 +294,87 @@ echo ~a >> ~a
308 (make-parameter (string-append (cache-directory #:ensure? #f) 294 (make-parameter (string-append (cache-directory #:ensure? #f)
309 "/profiles"))) 295 "/profiles")))
310 296
311(define (profile-cache-key file) 297(define (profile-cache-primary-key)
298 "Return the \"primary key\" used when computing keys for the profile cache.
299Return #f if no such key can be obtained and caching cannot be
300performed--e.g., because the package cache is not authoritative."
301 (and (cache-is-authoritative?)
302 (match (current-channels)
303 (()
304 #f)
305 (((= channel-commit commits) ...)
306 (string-join commits)))))
307
308(define (profile-file-cache-key file system)
312 "Return the cache key for the profile corresponding to FILE, a 'guix.scm' or 309 "Return the cache key for the profile corresponding to FILE, a 'guix.scm' or
313'manifest.scm' file, or #f if we lack channel information." 310'manifest.scm' file, or #f if we lack channel information."
314 (match (current-channels) 311 (match (profile-cache-primary-key)
315 (() #f) 312 (#f #f)
316 (((= channel-commit commits) ...) 313 (primary-key
317 (let ((stat (stat file))) 314 (let ((stat (stat file)))
318 (bytevector->base32-string 315 (bytevector->base32-string
319 ;; Since FILE is not canonicalized, only include the device/inode 316 ;; Since FILE is not canonicalized, only include the device/inode
320 ;; numbers. XXX: In some rare cases involving Btrfs and NFS, this can 317 ;; numbers. XXX: In some rare cases involving Btrfs and NFS, this can
321 ;; be insufficient: <https://lwn.net/Articles/866582/>. 318 ;; be insufficient: <https://lwn.net/Articles/866582/>.
322 (sha256 (string->utf8 319 (sha256 (string->utf8
323 (string-append (string-join commits) ":" 320 (string-append primary-key ":" system ":"
324 (number->string (stat:dev stat)) ":" 321 (number->string (stat:dev stat)) ":"
325 (number->string (stat:ino stat)))))))))) 322 (number->string (stat:ino stat))))))))))
326 323
327(define (profile-cached-gc-root file) 324(define (profile-spec-cache-key specs system)
328 "Return the cached GC root for FILE, a 'guix.scm' or 'manifest.scm' file, or 325 "Return the cache key corresponding to SPECS built for SYSTEM, where SPECS
329#f if we lack information to cache it." 326is a list of package specs. Return #f if caching is not possible."
330 (match (profile-cache-key file) 327 (match (profile-cache-primary-key)
331 (#f #f) 328 (#f #f)
332 (key (string-append (%profile-cache-directory) "/" key)))) 329 (primary-key
330 (bytevector->base32-string
331 (sha256 (string->utf8
332 (string-append primary-key ":" system ":"
333 (object->string specs))))))))
334
335(define (profile-cached-gc-root opts)
336 "Return two values: the file name of a GC root for use as a profile cache
337for the options in OPTS, and a timestamp which, if greater than the GC root's
338mtime, indicates that the GC root is stale. If OPTS do not permit caching,
339return #f and #f."
340 (define (key->file key)
341 (string-append (%profile-cache-directory) "/" key))
342
343 (let loop ((opts opts)
344 (system (%current-system))
345 (file #f)
346 (specs '()))
347 (match opts
348 (()
349 (if file
350 (values (and=> (profile-file-cache-key file system) key->file)
351 (stat:mtime (stat file)))
352 (values (and=> (profile-spec-cache-key specs system) key->file)
353 0)))
354 (((and spec ('package . _)) . rest)
355 (if (not file)
356 (loop rest system file (cons spec specs))
357 (values #f #f)))
358 ((('load . ('package candidate)) . rest)
359 (if (and (not file) (null? specs))
360 (loop rest system candidate specs)
361 (values #f #f)))
362 ((('manifest . candidate) . rest)
363 (if (and (not file) (null? specs))
364 (loop rest system candidate specs)
365 (values #f #f)))
366 ((('expression . _) . _)
367 ;; Arbitrary expressions might be non-deterministic or otherwise depend
368 ;; on external state so do not cache when they're used.
369 (values #f #f))
370 ((((? transformation-option-key?) . _) . _)
371 ;; Transformation options are potentially "non-deterministic", or at
372 ;; least depending on external state (with-source, with-commit, etc.),
373 ;; so do not cache anything when they're used.
374 (values #f #f))
375 ((('system . system) . rest)
376 (loop rest system file specs))
377 ((_ . rest) (loop rest system file specs)))))
333 378
334 379
335;;; 380;;;