;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2015 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2015, 2016, 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;
#:use-module (guix scripts)
#:use-module (guix store)
#:use-module (guix monads)
- #:use-module (guix utils)
+ #:use-module (guix combinators)
+ #:use-module (guix grafts)
#:use-module (guix packages)
#:use-module (guix derivations)
#:use-module (gnu packages)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-9)
#:use-module (srfi srfi-11)
+ #:use-module (srfi srfi-26)
#:use-module (srfi srfi-34)
#:use-module (srfi srfi-37)
#:use-module (ice-9 match)
#:use-module (ice-9 format)
+ #:use-module (ice-9 vlist)
#:export (profile?
profile-file
profile-self-size
(define substitutable-path-info*
(store-lift substitutable-path-info))
-(define (query-path-info* item)
- "Monadic version of 'query-path-info' that returns #f when ITEM is not in
-the store."
- (lambda (store)
- (guard (c ((nix-protocol-error? c)
- ;; ITEM is not in the store; return #f.
- (values #f store)))
- (values (query-path-info store item) store))))
-
(define (file-size item)
"Return the size in bytes of ITEM, resorting to information from substitutes
if ITEM is not in the store."
;; The nar size is an approximation, but a good one.
(return (substitutable-nar-size info)))
(()
- (leave (_ "no available substitute information for '~a'~%")
+ (leave (G_ "no available substitute information for '~a'~%")
item)))))))
-(define* (display-profile profile #:optional (port (current-output-port)))
- "Display PROFILE, a list of PROFILE objects, to PORT."
+(define profile-closure<?
+ (match-lambda*
+ ((($ <profile> name1 self1 total1)
+ ($ <profile> name2 self2 total2))
+ (< total1 total2))))
+
+(define profile-self<?
+ (match-lambda*
+ ((($ <profile> name1 self1 total1)
+ ($ <profile> name2 self2 total2))
+ (< self1 self2))))
+
+(define* (display-profile profile #:optional (port (current-output-port))
+ #:key (profile<? profile-closure<?))
+ "Display PROFILE, a list of PROFILE objects, to PORT. Sort entries
+according to PROFILE<?."
(define MiB (expt 2 20))
(format port "~64a ~8a ~a\n"
- (_ "store item") (_ "total") (_ "self"))
+ (G_ "store item") (G_ "total") (G_ "self"))
(let ((whole (reduce + 0 (map profile-self-size profile))))
(for-each (match-lambda
(($ <profile> name self total)
(format port "~64a ~6,1f ~6,1f ~5,1f%\n"
name (/ total MiB) (/ self MiB)
(* 100. (/ self whole 1.)))))
- (sort profile
- (match-lambda*
- ((($ <profile> _ _ total1) ($ <profile> _ _ total2))
- (> total1 total2)))))))
+ (sort profile (negate profile<?)))
+ (format port (G_ "total: ~,1f MiB~%") (/ whole MiB 1.))))
(define display-profile*
(lift display-profile %store-monad))
-(define (substitutable-requisites store item)
- "Return the list of requisites of ITEM based on information available in
+(define (substitutable-requisites store items)
+ "Return the list of requisites of ITEMS based on information available in
substitutes."
- (let loop ((items (list item))
+ (let loop ((items items)
(result '()))
(match items
(()
(append (append-map substitutable-references info)
result)))))))
-(define (requisites* item)
+(define (requisites* items)
"Return as a monadic value the requisites of ITEMS, based either on the
information available in the local store or using information about
substitutes."
(lambda (store)
- (guard (c ((nix-protocol-error? c)
- (values (substitutable-requisites store item)
- store)))
- (values (requisites store item) store))))
+ (let-values (((local missing)
+ (partition (cut valid-path? store <>) items)))
+ (values (delete-duplicates
+ (append (requisites store local)
+ (substitutable-requisites store missing)))
+ store))))
-(define (store-profile item)
+(define (store-profile items)
"Return as a monadic value a list of <profile> objects representing the
-profile of ITEM and its requisites."
- (mlet* %store-monad ((refs (>>= (requisites* item)
+profile of ITEMS and their requisites."
+ (mlet* %store-monad ((refs (>>= (requisites* items)
(lambda (refs)
(return (delete-duplicates
- (cons item refs))))))
+ (append items refs))))))
(sizes (mapm %store-monad
(lambda (item)
(>>= (file-size item)
(lambda (size)
(return (cons item size)))))
refs)))
+ (define size-table
+ (fold (lambda (pair result)
+ (match pair
+ ((item . size)
+ (vhash-cons item size result))))
+ vlist-null sizes))
+
(define (dependency-size item)
- (mlet %store-monad ((deps (requisites* item)))
+ (mlet %store-monad ((deps (requisites* (list item))))
(foldm %store-monad
(lambda (item total)
- (return (+ (assoc-ref sizes item) total)))
+ (return (+ (match (vhash-assoc item size-table)
+ ((_ . size) size))
+ total)))
0
(delete-duplicates (cons item deps)))))
0
(sort profiles
(match-lambda*
- ((($ <profile> _ _ total1) ($ <profile> _ _ total2))
+ ((($ <profile> name1 self1 total1)
+ ($ <profile> name2 self2 total2))
(> total1 total2))))))
;; TRANSLATORS: This is the title of a graph, meaning that the graph
;; represents a profile of the store (the "store" being the place where
;; packages are stored.)
- (make-page-map (_ "store profile") data
+ (make-page-map (G_ "store profile") data
#:write-to-png file))
\f
;;;
(define (show-help)
- (display (_ "Usage: guix size [OPTION]... PACKAGE
+ (display (G_ "Usage: guix size [OPTION]... PACKAGE
Report the size of PACKAGE and its dependencies.\n"))
- (display (_ "
+ (display (G_ "
--substitute-urls=URLS
fetch substitute from URLS if they are authorized"))
- (display (_ "
+ (display (G_ "
-s, --system=SYSTEM consider packages for SYSTEM--e.g., \"i686-linux\""))
- (display (_ "
+ ;; TRANSLATORS: "closure" and "self" must not be translated.
+ (display (G_ "
+ --sort=KEY sort according to KEY--\"closure\" or \"self\""))
+ (display (G_ "
-m, --map-file=FILE write to FILE a graphical map of disk usage"))
(newline)
- (display (_ "
+ (display (G_ "
-h, --help display this help and exit"))
- (display (_ "
+ (display (G_ "
-V, --version display version information and exit"))
(newline)
(show-bug-report-information))
(string-tokenize arg)
(alist-delete 'substitute-urls result))
rest)))
+ (option '("sort") #t #f
+ (lambda (opt name arg result . rest)
+ (match arg
+ ("closure"
+ (alist-cons 'profile<? profile-closure<? result))
+ ("self"
+ (alist-cons 'profile<? profile-self<? result))
+ (_
+ (leave (G_ "~a: invalid sorting key~%") arg)))))
(option '(#\m "map-file") #t #f
(lambda (opt name arg result)
(alist-cons 'map-file arg result)))
(define %default-options
`((system . ,(%current-system))
- (substitute-urls . ,%default-substitute-urls)))
+ (profile<? . ,profile-self<?)))
\f
;;;
(define (guix-size . args)
(with-error-handling
- (let* ((opts (parse-command-line args %options (list %default-options)))
+ (let* ((opts (parse-command-line args %options (list %default-options)
+ #:build-options? #f))
(files (filter-map (match-lambda
(('argument . file) file)
(_ #f))
opts))
+ (profile<? (assoc-ref opts 'profile<?))
(map-file (assoc-ref opts 'map-file))
(system (assoc-ref opts 'system))
(urls (assoc-ref opts 'substitute-urls)))
(match files
(()
- (leave (_ "missing store item argument\n")))
- ((file)
+ (leave (G_ "missing store item argument\n")))
+ ((files ..1)
(leave-on-EPIPE
- (with-store store
- (set-build-options store
- #:use-substitutes? #t
- #:substitute-urls urls)
+ ;; Turn off grafts because (1) substitute servers do not serve grafted
+ ;; packages, and (2) they do not make any difference on the
+ ;; resulting size.
+ (parameterize ((%graft? #f))
+ (with-store store
+ (set-build-options store
+ #:use-substitutes? #t
+ #:substitute-urls urls)
- (run-with-store store
- (mlet* %store-monad ((item (ensure-store-item file))
- (profile (store-profile item)))
- (if map-file
- (begin
- (profile->page-map profile map-file)
- (return #t))
- (display-profile* profile)))
- #:system system))))
- ((files ...)
- (leave (_ "too many arguments\n")))))))
+ (run-with-store store
+ (mlet* %store-monad ((items (mapm %store-monad
+ ensure-store-item files))
+ (profile (store-profile items)))
+ (if map-file
+ (begin
+ (profile->page-map profile map-file)
+ (return #t))
+ (display-profile* profile (current-output-port)
+ #:profile<? profile<?)))
+ #:system system)))))))))