Remove most references to hydra.gnu.org.
[jackhill/guix/guix.git] / guix / scripts / size.scm
CommitLineData
fcc58db6 1;;; GNU Guix --- Functional package management for GNU
0744a9f0 2;;; Copyright © 2015, 2016, 2017, 2018 Ludovic Courtès <ludo@gnu.org>
fcc58db6
LC
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (guix scripts size)
20 #:use-module (guix ui)
88981dd3 21 #:use-module (guix scripts)
fcc58db6
LC
22 #:use-module (guix store)
23 #:use-module (guix monads)
958dd3ce 24 #:use-module (guix combinators)
3667bb6c 25 #:use-module (guix grafts)
fcc58db6
LC
26 #:use-module (guix packages)
27 #:use-module (guix derivations)
28 #:use-module (gnu packages)
29 #:use-module (srfi srfi-1)
30 #:use-module (srfi srfi-9)
31 #:use-module (srfi srfi-11)
da2d893e 32 #:use-module (srfi srfi-26)
fcc58db6
LC
33 #:use-module (srfi srfi-34)
34 #:use-module (srfi srfi-37)
fcc58db6
LC
35 #:use-module (ice-9 match)
36 #:use-module (ice-9 format)
37 #:export (profile?
38 profile-file
39 profile-self-size
40 profile-closure-size
41 store-profile
42
43 guix-size))
44
45;; Size profile of a store item.
46(define-record-type <profile>
47 (profile file self-size closure-size)
48 profile?
49 (file profile-file) ;store item
50 (self-size profile-self-size) ;size in bytes
51 (closure-size profile-closure-size)) ;size of dependencies in bytes
52
fcc58db6
LC
53(define substitutable-path-info*
54 (store-lift substitutable-path-info))
55
83bde59f
LC
56(define (file-size item)
57 "Return the size in bytes of ITEM, resorting to information from substitutes
58if ITEM is not in the store."
59 (mlet %store-monad ((info (query-path-info* item)))
60 (if info
61 (return (path-info-nar-size info))
fcc58db6
LC
62 (mlet %store-monad ((info (substitutable-path-info* (list item))))
63 (match info
64 ((info)
65 ;; The nar size is an approximation, but a good one.
66 (return (substitutable-nar-size info)))
67 (()
69daee23 68 (leave (G_ "no available substitute information for '~a'~%")
fcc58db6
LC
69 item)))))))
70
a6c1fe82
LC
71(define profile-closure<?
72 (match-lambda*
73 ((($ <profile> name1 self1 total1)
74 ($ <profile> name2 self2 total2))
75 (< total1 total2))))
76
77(define profile-self<?
78 (match-lambda*
79 ((($ <profile> name1 self1 total1)
80 ($ <profile> name2 self2 total2))
81 (< self1 self2))))
82
83(define* (display-profile profile #:optional (port (current-output-port))
84 #:key (profile<? profile-closure<?))
85 "Display PROFILE, a list of PROFILE objects, to PORT. Sort entries
86according to PROFILE<?."
fcc58db6
LC
87 (define MiB (expt 2 20))
88
89 (format port "~64a ~8a ~a\n"
69daee23 90 (G_ "store item") (G_ "total") (G_ "self"))
fcc58db6
LC
91 (let ((whole (reduce + 0 (map profile-self-size profile))))
92 (for-each (match-lambda
93 (($ <profile> name self total)
94 (format port "~64a ~6,1f ~6,1f ~5,1f%\n"
95 name (/ total MiB) (/ self MiB)
96 (* 100. (/ self whole 1.)))))
a6c1fe82 97 (sort profile (negate profile<?)))
69daee23 98 (format port (G_ "total: ~,1f MiB~%") (/ whole MiB 1.))))
fcc58db6
LC
99
100(define display-profile*
101 (lift display-profile %store-monad))
102
92ed837a
LC
103(define (substitutable-requisites store items)
104 "Return the list of requisites of ITEMS based on information available in
fcc58db6 105substitutes."
92ed837a 106 (let loop ((items items)
fcc58db6
LC
107 (result '()))
108 (match items
109 (()
110 (delete-duplicates result))
111 (items
112 (let ((info (substitutable-path-info store
113 (delete-duplicates items))))
114 (loop (remove (lambda (item) ;XXX: complexity
115 (member item result))
116 (append-map substitutable-references info))
117 (append (append-map substitutable-references info)
118 result)))))))
119
92ed837a 120(define (requisites* items)
fcc58db6
LC
121 "Return as a monadic value the requisites of ITEMS, based either on the
122information available in the local store or using information about
123substitutes."
124 (lambda (store)
da2d893e
LC
125 (let-values (((local missing)
126 (partition (cut valid-path? store <>) items)))
127 (values (delete-duplicates
128 (append (requisites store local)
129 (substitutable-requisites store missing)))
130 store))))
a371aa22
LC
131
132(define (store-profile items)
fcc58db6 133 "Return as a monadic value a list of <profile> objects representing the
a371aa22 134profile of ITEMS and their requisites."
92ed837a 135 (mlet* %store-monad ((refs (>>= (requisites* items)
fcc58db6
LC
136 (lambda (refs)
137 (return (delete-duplicates
a371aa22 138 (append items refs))))))
fcc58db6
LC
139 (sizes (mapm %store-monad
140 (lambda (item)
83bde59f 141 (>>= (file-size item)
fcc58db6
LC
142 (lambda (size)
143 (return (cons item size)))))
144 refs)))
145 (define (dependency-size item)
92ed837a 146 (mlet %store-monad ((deps (requisites* (list item))))
fcc58db6
LC
147 (foldm %store-monad
148 (lambda (item total)
149 (return (+ (assoc-ref sizes item) total)))
150 0
151 (delete-duplicates (cons item deps)))))
152
153 (mapm %store-monad
154 (match-lambda
155 ((item . size)
156 (mlet %store-monad ((dependencies (dependency-size item)))
157 (return (profile item size dependencies)))))
158 sizes)))
159
a8aa84af 160(define* (ensure-store-item spec-or-item)
fcc58db6
LC
161 "Return a store file name. If SPEC-OR-ITEM is a store file name, return it
162as is. Otherwise, assume SPEC-OR-ITEM is a package output specification such
163as \"guile:debug\" or \"gcc-4.8\" and return its store file name."
164 (with-monad %store-monad
165 (if (store-path? spec-or-item)
166 (return spec-or-item)
167 (let-values (((package output)
168 (specification->package+output spec-or-item)))
169 (mlet %store-monad ((drv (package->derivation package)))
170 ;; Note: we don't try building DRV like 'guix archive' does
171 ;; because we don't have to since we can instead rely on
172 ;; substitute meta-data.
173 (return (derivation->output-path drv output)))))))
174
175\f
a8f996c6
LC
176;;;
177;;; Charts.
178;;;
179
180;; Autoload Guile-Charting.
181;; XXX: Use this hack instead of #:autoload to avoid compilation errors.
182;; See <http://bugs.gnu.org/12202>.
183(module-autoload! (current-module)
184 '(charting) '(make-page-map))
185
186(define (profile->page-map profiles file)
187 "Write a 'page map' chart of PROFILES, a list of <profile> objects, to FILE,
188the name of a PNG file."
189 (define (strip name)
190 (string-drop name (+ (string-length (%store-prefix)) 28)))
191
192 (define data
193 (fold2 (lambda (profile result offset)
194 (match profile
195 (($ <profile> name self)
196 (let ((self (inexact->exact
197 (round (/ self (expt 2. 10))))))
198 (values `((,(strip name) ,offset . ,self)
199 ,@result)
200 (+ offset self))))))
201 '()
202 0
203 (sort profiles
204 (match-lambda*
9a3762a7
LC
205 ((($ <profile> name1 self1 total1)
206 ($ <profile> name2 self2 total2))
a8f996c6
LC
207 (> total1 total2))))))
208
209 ;; TRANSLATORS: This is the title of a graph, meaning that the graph
210 ;; represents a profile of the store (the "store" being the place where
211 ;; packages are stored.)
69daee23 212 (make-page-map (G_ "store profile") data
a8f996c6
LC
213 #:write-to-png file))
214
215\f
fcc58db6
LC
216;;;
217;;; Options.
218;;;
219
220(define (show-help)
69daee23 221 (display (G_ "Usage: guix size [OPTION]... PACKAGE
fcc58db6 222Report the size of PACKAGE and its dependencies.\n"))
69daee23 223 (display (G_ "
d490d06e
LC
224 --substitute-urls=URLS
225 fetch substitute from URLS if they are authorized"))
69daee23 226 (display (G_ "
fcc58db6 227 -s, --system=SYSTEM consider packages for SYSTEM--e.g., \"i686-linux\""))
a6c1fe82
LC
228 ;; TRANSLATORS: "closure" and "self" must not be translated.
229 (display (G_ "
230 --sort=KEY sort according to KEY--\"closure\" or \"self\""))
69daee23 231 (display (G_ "
d490d06e 232 -m, --map-file=FILE write to FILE a graphical map of disk usage"))
fcc58db6 233 (newline)
69daee23 234 (display (G_ "
fcc58db6 235 -h, --help display this help and exit"))
69daee23 236 (display (G_ "
fcc58db6
LC
237 -V, --version display version information and exit"))
238 (newline)
239 (show-bug-report-information))
240
241(define %options
242 ;; Specifications of the command-line options.
243 (list (option '(#\s "system") #t #f
244 (lambda (opt name arg result)
245 (alist-cons 'system arg
246 (alist-delete 'system result eq?))))
d490d06e
LC
247 (option '("substitute-urls") #t #f
248 (lambda (opt name arg result . rest)
249 (apply values
250 (alist-cons 'substitute-urls
251 (string-tokenize arg)
252 (alist-delete 'substitute-urls result))
253 rest)))
a6c1fe82
LC
254 (option '("sort") #t #f
255 (lambda (opt name arg result . rest)
256 (match arg
257 ("closure"
258 (alist-cons 'profile<? profile-closure<? result))
259 ("self"
260 (alist-cons 'profile<? profile-self<? result))
261 (_
262 (leave (G_ "~a: invalid sorting key~%") arg)))))
a8f996c6
LC
263 (option '(#\m "map-file") #t #f
264 (lambda (opt name arg result)
265 (alist-cons 'map-file arg result)))
fcc58db6
LC
266 (option '(#\h "help") #f #f
267 (lambda args
268 (show-help)
269 (exit 0)))
270 (option '(#\V "version") #f #f
271 (lambda args
272 (show-version-and-exit "guix size")))))
273
274(define %default-options
a6c1fe82 275 `((system . ,(%current-system))
fb255088 276 (profile<? . ,profile-self<?)))
fcc58db6
LC
277
278\f
279;;;
280;;; Entry point.
281;;;
282
283(define (guix-size . args)
284 (with-error-handling
a1ff7e1d
LC
285 (let* ((opts (parse-command-line args %options (list %default-options)
286 #:build-options? #f))
fcc58db6
LC
287 (files (filter-map (match-lambda
288 (('argument . file) file)
289 (_ #f))
290 opts))
a6c1fe82 291 (profile<? (assoc-ref opts 'profile<?))
a8f996c6 292 (map-file (assoc-ref opts 'map-file))
d490d06e
LC
293 (system (assoc-ref opts 'system))
294 (urls (assoc-ref opts 'substitute-urls)))
fcc58db6
LC
295 (match files
296 (()
69daee23 297 (leave (G_ "missing store item argument\n")))
db761534 298 ((files ..1)
d2f2c8f1 299 (leave-on-EPIPE
0bc02bec 300 ;; Turn off grafts because (1) substitute servers do not serve grafted
3667bb6c
LC
301 ;; packages, and (2) they do not make any difference on the
302 ;; resulting size.
303 (parameterize ((%graft? #f))
304 (with-store store
305 (set-build-options store
306 #:use-substitutes? #t
307 #:substitute-urls urls)
d490d06e 308
3667bb6c 309 (run-with-store store
db761534
LC
310 (mlet* %store-monad ((items (mapm %store-monad
311 ensure-store-item files))
312 (profile (store-profile items)))
3667bb6c
LC
313 (if map-file
314 (begin
315 (profile->page-map profile map-file)
316 (return #t))
a6c1fe82
LC
317 (display-profile* profile (current-output-port)
318 #:profile<? profile<?)))
db761534 319 #:system system)))))))))