distro: expect: Provide the right path to `stty'.
[jackhill/guix/guix.git] / guix-package.in
CommitLineData
0afdc485
LC
1#!/bin/sh
2# aside from this initial boilerplate, this is actually -*- scheme -*- code
3
4prefix="@prefix@"
5datarootdir="@datarootdir@"
6
7GUILE_LOAD_COMPILED_PATH="@guilemoduledir@:$GUILE_LOAD_COMPILED_PATH"
8export GUILE_LOAD_COMPILED_PATH
9
10main='(module-ref (resolve-interface '\''(guix-package)) '\'guix-package')'
11exec ${GUILE-@GUILE@} -L "@guilemoduledir@" -l "$0" \
12 -c "(apply $main (cdr (command-line)))" "$@"
13!#
233e7676
LC
14;;; GNU Guix --- Functional package management for GNU
15;;; Copyright © 2012, 2013 Ludovic Courtès <ludo@gnu.org>
0afdc485 16;;;
233e7676 17;;; This file is part of GNU Guix.
0afdc485 18;;;
233e7676 19;;; GNU Guix is free software; you can redistribute it and/or modify it
0afdc485
LC
20;;; under the terms of the GNU General Public License as published by
21;;; the Free Software Foundation; either version 3 of the License, or (at
22;;; your option) any later version.
23;;;
233e7676 24;;; GNU Guix is distributed in the hope that it will be useful, but
0afdc485
LC
25;;; WITHOUT ANY WARRANTY; without even the implied warranty of
26;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27;;; GNU General Public License for more details.
28;;;
29;;; You should have received a copy of the GNU General Public License
233e7676 30;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
0afdc485
LC
31
32(define-module (guix-package)
cdd5d6f9 33 #:use-module (guix ui)
0afdc485
LC
34 #:use-module (guix store)
35 #:use-module (guix derivations)
36 #:use-module (guix packages)
37 #:use-module (guix utils)
a020d2a9 38 #:use-module (guix config)
0afdc485
LC
39 #:use-module (ice-9 ftw)
40 #:use-module (ice-9 format)
41 #:use-module (ice-9 match)
42 #:use-module (ice-9 regex)
43 #:use-module (srfi srfi-1)
44 #:use-module (srfi srfi-11)
45 #:use-module (srfi srfi-26)
46 #:use-module (srfi srfi-34)
47 #:use-module (srfi srfi-37)
64fc89b6 48 #:use-module (distro)
87009d8a
LC
49 #:use-module ((distro packages base) #:select (guile-final))
50 #:use-module ((distro packages bootstrap) #:select (%bootstrap-guile))
0afdc485
LC
51 #:export (guix-package))
52
0afdc485
LC
53(define %store
54 (open-connection))
55
56\f
57;;;
58;;; User environment.
59;;;
60
61(define %user-environment-directory
62 (and=> (getenv "HOME")
63 (cut string-append <> "/.guix-profile")))
64
65(define %profile-directory
a020d2a9 66 (string-append %state-directory "/profiles/"
0afdc485
LC
67 (or (and=> (getenv "USER")
68 (cut string-append "per-user/" <>))
69 "default")))
70
71(define %current-profile
4aa52039
LC
72 ;; Call it `guix-profile', not `profile', to allow Guix profiles to
73 ;; coexist with Nix profiles.
74 (string-append %profile-directory "/guix-profile"))
0afdc485
LC
75
76(define (profile-manifest profile)
77 "Return the PROFILE's manifest."
78 (let ((manifest (string-append profile "/manifest")))
79 (if (file-exists? manifest)
80 (call-with-input-file manifest read)
81 '(manifest (version 0) (packages ())))))
82
83(define (manifest-packages manifest)
84 "Return the packages listed in MANIFEST."
85 (match manifest
86 (('manifest ('version 0) ('packages packages))
87 packages)
88 (_
89 (error "unsupported manifest format" manifest))))
90
91(define (latest-profile-number profile)
92 "Return the identifying number of the latest generation of PROFILE.
93PROFILE is the name of the symlink to the current generation."
94 (define %profile-rx
95 (make-regexp (string-append "^" (regexp-quote (basename profile))
96 "-([0-9]+)")))
97
98 (define* (scandir name #:optional (select? (const #t))
99 (entry<? (@ (ice-9 i18n) string-locale<?)))
100 ;; XXX: Bug-fix version introduced in Guile v2.0.6-62-g139ce19.
101 (define (enter? dir stat result)
102 (and stat (string=? dir name)))
103
104 (define (visit basename result)
105 (if (select? basename)
106 (cons basename result)
107 result))
108
109 (define (leaf name stat result)
110 (and result
111 (visit (basename name) result)))
112
113 (define (down name stat result)
114 (visit "." '()))
115
116 (define (up name stat result)
117 (visit ".." result))
118
119 (define (skip name stat result)
120 ;; All the sub-directories are skipped.
121 (visit (basename name) result))
122
123 (define (error name* stat errno result)
124 (if (string=? name name*) ; top-level NAME is unreadable
125 result
126 (visit (basename name*) result)))
127
128 (and=> (file-system-fold enter? leaf down up skip error #f name lstat)
129 (lambda (files)
130 (sort files entry<?))))
131
132 (match (scandir (dirname profile)
133 (cut regexp-exec %profile-rx <>))
134 (#f ; no profile directory
135 0)
136 (() ; no profiles
137 0)
138 ((profiles ...) ; former profiles around
139 (let ((numbers (map (compose string->number
140 (cut match:substring <> 1)
141 (cut regexp-exec %profile-rx <>))
142 profiles)))
143 (fold (lambda (number highest)
144 (if (> number highest)
145 number
146 highest))
147 0
148 numbers)))))
149
150(define (profile-derivation store packages)
151 "Return a derivation that builds a profile (a user environment) with
152all of PACKAGES, a list of name/version/output/path tuples."
153 (define builder
154 `(begin
155 (use-modules (ice-9 pretty-print)
156 (guix build union))
157
158 (setvbuf (current-output-port) _IOLBF)
159 (setvbuf (current-error-port) _IOLBF)
160
161 (let ((output (assoc-ref %outputs "out"))
162 (inputs (map cdr %build-inputs)))
163 (format #t "building user environment `~a' with ~a packages...~%"
164 output (length inputs))
165 (union-build output inputs)
166 (call-with-output-file (string-append output "/manifest")
167 (lambda (p)
168 (pretty-print '(manifest (version 0)
169 (packages ,packages))
170 p))))))
171
172 (build-expression->derivation store "user-environment"
173 (%current-system)
174 builder
175 (map (match-lambda
176 ((name version output path)
177 `(,name ,path)))
178 packages)
179 #:modules '((guix build union))))
180
181\f
182;;;
183;;; Command-line options.
184;;;
185
186(define %default-options
187 ;; Alist of default option values.
188 `((profile . ,%current-profile)))
189
0afdc485
LC
190(define (show-help)
191 (display (_ "Usage: guix-package [OPTION]... PACKAGES...
192Install, remove, or upgrade PACKAGES in a single transaction.\n"))
193 (display (_ "
194 -i, --install=PACKAGE install PACKAGE"))
195 (display (_ "
196 -r, --remove=PACKAGE remove PACKAGE"))
197 (display (_ "
198 -u, --upgrade=REGEXP upgrade all the installed packages matching REGEXP"))
199 (newline)
200 (display (_ "
201 -p, --profile=PROFILE use PROFILE instead of the user's default profile"))
202 (display (_ "
203 -n, --dry-run show what would be done without actually doing it"))
204 (display (_ "
cc57f25d 205 --bootstrap use the bootstrap Guile to build the profile"))
70915c1a
LC
206 (display (_ "
207 --verbose produce verbose output"))
0afdc485
LC
208 (newline)
209 (display (_ "
733b4130
LC
210 -I, --list-installed[=REGEXP]
211 list installed packages matching REGEXP"))
64fc89b6
LC
212 (display (_ "
213 -A, --list-available[=REGEXP]
214 list available packages matching REGEXP"))
733b4130
LC
215 (newline)
216 (display (_ "
0afdc485
LC
217 -h, --help display this help and exit"))
218 (display (_ "
219 -V, --version display version information and exit"))
220 (newline)
3441e164 221 (show-bug-report-information))
0afdc485
LC
222
223(define %options
224 ;; Specification of the command-line options.
225 (list (option '(#\h "help") #f #f
226 (lambda args
227 (show-help)
228 (exit 0)))
229 (option '(#\V "version") #f #f
230 (lambda args
cdd5d6f9 231 (show-version-and-exit "guix-package")))
0afdc485
LC
232
233 (option '(#\i "install") #t #f
234 (lambda (opt name arg result)
235 (alist-cons 'install arg result)))
236 (option '(#\r "remove") #t #f
237 (lambda (opt name arg result)
238 (alist-cons 'remove arg result)))
239 (option '(#\p "profile") #t #f
240 (lambda (opt name arg result)
241 (alist-cons 'profile arg
242 (alist-delete 'profile result))))
243 (option '(#\n "dry-run") #f #f
244 (lambda (opt name arg result)
245 (alist-cons 'dry-run? #t result)))
cc57f25d 246 (option '("bootstrap") #f #f
0afdc485 247 (lambda (opt name arg result)
733b4130 248 (alist-cons 'bootstrap? #t result)))
70915c1a
LC
249 (option '("verbose") #f #f
250 (lambda (opt name arg result)
251 (alist-cons 'verbose? #t result)))
733b4130
LC
252 (option '(#\I "list-installed") #f #t
253 (lambda (opt name arg result)
254 (cons `(query list-installed ,(or arg ""))
64fc89b6
LC
255 result)))
256 (option '(#\A "list-available") #f #t
257 (lambda (opt name arg result)
258 (cons `(query list-available ,(or arg ""))
733b4130 259 result)))))
0afdc485
LC
260
261\f
262;;;
263;;; Entry point.
264;;;
265
266(define (guix-package . args)
267 (define (parse-options)
268 ;; Return the alist of option values.
269 (args-fold args %options
270 (lambda (opt name arg result)
271 (leave (_ "~A: unrecognized option~%") name))
272 (lambda (arg result)
273 (alist-cons 'argument arg result))
274 %default-options))
275
9762706b
LC
276 (define (guile-missing?)
277 ;; Return #t if %GUILE-FOR-BUILD is not available yet.
278 (let ((out (derivation-path->output-path (%guile-for-build))))
279 (not (valid-path? %store out))))
280
0afdc485
LC
281 (define (show-what-to-build drv dry-run?)
282 ;; Show what will/would be built in realizing the derivations listed
283 ;; in DRV.
284 (let* ((req (append-map (lambda (drv-path)
285 (let ((d (call-with-input-file drv-path
286 read-derivation)))
287 (derivation-prerequisites-to-build %store d)))
288 drv))
289 (req* (delete-duplicates
290 (append (remove (compose (cut valid-path? %store <>)
291 derivation-path->output-path)
292 drv)
293 (map derivation-input-path req)))))
294 (if dry-run?
295 (format (current-error-port)
296 (N_ "~:[the following derivation would be built:~%~{ ~a~%~}~;~]"
297 "~:[the following derivations would be built:~%~{ ~a~%~}~;~]"
298 (length req*))
299 (null? req*) req*)
300 (format (current-error-port)
301 (N_ "~:[the following derivation will be built:~%~{ ~a~%~}~;~]"
302 "~:[the following derivations will be built:~%~{ ~a~%~}~;~]"
303 (length req*))
304 (null? req*) req*))))
305
306 (define (find-package name)
307 ;; Find the package NAME; NAME may contain a version number and a
308 ;; sub-derivation name.
309 (define request name)
0afdc485
LC
310
311 (let*-values (((name sub-drv)
312 (match (string-rindex name #\:)
313 (#f (values name "out"))
9518856b
LC
314 (colon (values (substring name 0 colon)
315 (substring name (+ 1 colon))))))
0afdc485 316 ((name version)
9b48fb88 317 (package-name->name+version name)))
0afdc485
LC
318 (match (find-packages-by-name name version)
319 ((p)
d9d05363 320 (list name (package-version p) sub-drv p))
c6f09dfa 321 ((p p* ...)
0afdc485
LC
322 (format (current-error-port)
323 (_ "warning: ambiguous package specification `~a'~%")
324 request)
325 (format (current-error-port)
d9d05363
LC
326 (_ "warning: choosing ~a from ~a~%")
327 (package-full-name p)
328 (location->string (package-location p)))
329 (list name (package-version p) sub-drv p))
0afdc485
LC
330 (()
331 (leave (_ "~a: package not found~%") request)))))
332
733b4130
LC
333 (define (process-actions opts)
334 ;; Process any install/remove/upgrade action from OPTS.
335 (let* ((dry-run? (assoc-ref opts 'dry-run?))
70915c1a 336 (verbose? (assoc-ref opts 'verbose?))
733b4130
LC
337 (profile (assoc-ref opts 'profile))
338 (install (filter-map (match-lambda
339 (('install . (? store-path?))
340 #f)
341 (('install . package)
342 (find-package package))
343 (_ #f))
344 opts))
345 (drv (filter-map (match-lambda
346 ((name version sub-drv
347 (? package? package))
348 (package-derivation %store package))
349 (_ #f))
350 install))
351 (install* (append
352 (filter-map (match-lambda
353 (('install . (? store-path? path))
5075e283
LC
354 (let-values (((name version)
355 (package-name->name+version
356 (store-path-package-name
357 path))))
358 `(,name ,version #f ,path)))
733b4130
LC
359 (_ #f))
360 opts)
361 (map (lambda (tuple drv)
362 (match tuple
363 ((name version sub-drv _)
364 (let ((output-path
365 (derivation-path->output-path
366 drv sub-drv)))
367 `(,name ,version ,sub-drv ,output-path)))))
368 install drv)))
369 (remove (filter-map (match-lambda
370 (('remove . package)
371 package)
372 (_ #f))
373 opts))
374 (packages (append install*
1c67d639
LC
375 (fold (lambda (package result)
376 (match package
377 ((name _ ...)
378 (alist-delete name result))))
379 (fold alist-delete
380 (manifest-packages
381 (profile-manifest profile))
382 remove)
383 install*))))
733b4130
LC
384
385 (show-what-to-build drv dry-run?)
386
387 (or dry-run?
388 (and (build-derivations %store drv)
389 (let* ((prof-drv (profile-derivation %store packages))
390 (prof (derivation-path->output-path prof-drv))
1c67d639
LC
391 (old-drv (profile-derivation
392 %store (manifest-packages
393 (profile-manifest profile))))
394 (old-prof (derivation-path->output-path old-drv))
733b4130
LC
395 (number (latest-profile-number profile))
396 (name (format #f "~a/~a-~a-link"
397 (dirname profile)
398 (basename profile) (+ 1 number))))
1c67d639
LC
399 (if (string=? old-prof prof)
400 (format (current-error-port) (_ "nothing to be done~%"))
70915c1a 401 (and (parameterize ((current-build-output-port
9762706b
LC
402 ;; Output something when Guile
403 ;; needs to be built.
404 (if (or verbose? (guile-missing?))
70915c1a
LC
405 (current-error-port)
406 (%make-void-port "w"))))
407 (build-derivations %store (list prof-drv)))
1c67d639
LC
408 (begin
409 (symlink prof name)
410 (when (file-exists? profile)
411 (delete-file profile))
412 (symlink name profile)))))))))
733b4130
LC
413
414 (define (process-query opts)
415 ;; Process any query specified by OPTS. Return #t when a query was
416 ;; actually processed, #f otherwise.
417 (let ((profile (assoc-ref opts 'profile)))
418 (match (assoc-ref opts 'query)
419 (('list-installed regexp)
420 (let* ((regexp (and regexp (make-regexp regexp)))
421 (manifest (profile-manifest profile))
422 (installed (manifest-packages manifest)))
423 (for-each (match-lambda
424 ((name version output path)
425 (when (or (not regexp)
426 (regexp-exec regexp name))
427 (format #t "~a\t~a\t~a\t~a~%"
428 name (or version "?") output path))))
64fc89b6
LC
429 installed)
430 #t))
431 (('list-available regexp)
432 (let* ((regexp (and regexp (make-regexp regexp)))
433 (available (fold-packages
434 (lambda (p r)
435 (let ((n (package-name p)))
436 (if regexp
437 (if (regexp-exec regexp n)
438 (cons p r)
439 r)
440 (cons p r))))
441 '())))
442 (for-each (lambda (p)
44b6be77 443 (format #t "~a\t~a\t~a\t~a~%"
64fc89b6
LC
444 (package-name p)
445 (package-version p)
44b6be77 446 (string-join (package-outputs p) ",")
64fc89b6
LC
447 (location->string (package-location p))))
448 (sort available
449 (lambda (p1 p2)
450 (string<? (package-name p1)
451 (package-name p2)))))
452 #t))
733b4130
LC
453 (_ #f))))
454
0afdc485
LC
455 (setlocale LC_ALL "")
456 (textdomain "guix")
457 (setvbuf (current-output-port) _IOLBF)
458 (setvbuf (current-error-port) _IOLBF)
459
460 (let ((opts (parse-options)))
b9e5c0a9
LC
461
462 ;; Create ~/.guix-profile if it doesn't exist yet.
463 (when (and %user-environment-directory
464 %current-profile
465 (not (file-exists? %user-environment-directory)))
466 (symlink %current-profile %user-environment-directory))
467
1275baeb 468 (with-error-handling
733b4130
LC
469 (or (process-query opts)
470 (parameterize ((%guile-for-build
471 (package-derivation %store
472 (if (assoc-ref opts 'bootstrap?)
87009d8a
LC
473 %bootstrap-guile
474 guile-final))))
733b4130 475 (process-actions opts))))))