1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2014, 2015 David Thompson <davet@gnu.org>
3 ;;; Copyright © 2015 Ludovic Courtès <ludo@gnu.org>
5 ;;; This file is part of GNU Guix.
7 ;;; GNU Guix is free software; you can redistribute it and/or modify it
8 ;;; under the terms of the GNU General Public License as published by
9 ;;; the Free Software Foundation; either version 3 of the License, or (at
10 ;;; your option) any later version.
12 ;;; GNU Guix is distributed in the hope that it will be useful, but
13 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;;; GNU General Public License for more details.
17 ;;; You should have received a copy of the GNU General Public License
18 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20 (define-module (guix scripts environment)
21 #:use-module (guix ui)
22 #:use-module (guix store)
23 #:use-module (guix derivations)
24 #:use-module (guix packages)
25 #:use-module (guix profiles)
26 #:use-module (guix search-paths)
27 #:use-module (guix utils)
28 #:use-module (guix monads)
29 #:use-module ((guix gexp) #:select (lower-inputs))
30 #:use-module (guix scripts build)
31 #:use-module (gnu packages)
32 #:use-module (ice-9 format)
33 #:use-module (ice-9 match)
34 #:use-module (srfi srfi-1)
35 #:use-module (srfi srfi-26)
36 #:use-module (srfi srfi-37)
37 #:use-module (srfi srfi-98)
38 #:export (guix-environment))
40 (define (evaluate-input-search-paths inputs search-paths)
41 "Evaluate SEARCH-PATHS, a list of search-path specifications, for the
42 directories corresponding to INPUTS, a list of (DERIVATION) or (DERIVATION
44 (let ((directories (map (match-lambda
45 (((? derivation? drv))
46 (derivation->output-path drv))
47 (((? derivation? drv) output)
48 (derivation->output-path drv output))
52 (evaluate-search-paths search-paths directories)))
54 ;; Protect some env vars from purification. Borrowed from nix-shell.
55 (define %precious-variables
56 '("HOME" "USER" "LOGNAME" "DISPLAY" "TERM" "TZ" "PAGER"))
58 (define (purify-environment)
59 "Unset almost all environment variables. A small number of variables such
60 as 'HOME' and 'USER' are left untouched."
62 (remove (cut member <> %precious-variables)
63 (match (get-environment-variables)
67 (define (create-environment inputs paths pure?)
68 "Set the environment variables specified by PATHS for all the packages
69 within INPUTS. When PURE? is #t, unset the variables in the current
70 environment. Otherwise, augment existing enviroment variables with additional
72 (when pure? (purify-environment))
73 (for-each (match-lambda
74 ((($ <search-path-specification> variable _ separator) . value)
75 (let ((current (getenv variable)))
77 (if (and current (not pure?))
78 (string-append value separator current)
80 (evaluate-input-search-paths inputs paths)))
82 (define (show-search-paths inputs search-paths pure?)
83 "Display SEARCH-PATHS applied to the packages specified by INPUTS, a list of
84 (DERIVATION) or (DERIVATION OUTPUT) tuples. When PURE? is #t, do not augment
85 existing environment variables with additional search paths."
86 (for-each (match-lambda
87 ((search-path . value)
89 (search-path-definition search-path value
90 #:kind (if pure? 'exact 'prefix)))
92 (evaluate-input-search-paths inputs search-paths)))
94 (define (package+propagated-inputs package)
95 "Return the union of PACKAGE and its transitive propagated inputs."
96 `((,(package-name package) ,package)
97 ,@(package-transitive-propagated-inputs package)))
100 (display (_ "Usage: guix environment [OPTION]... PACKAGE...
101 Build an environment that includes the dependencies of PACKAGE and execute a
102 shell command in that environment.\n"))
104 -e, --expression=EXPR create environment for the package that EXPR
107 -l, --load=FILE create environment for the package that the code within
110 -E, --exec=COMMAND execute COMMAND in new environment"))
112 --ad-hoc include all specified packages in the environment instead
113 of only their inputs"))
115 --pure unset existing environment variables"))
117 --search-paths display needed environment variable definitions"))
119 (show-build-options-help)
122 -h, --help display this help and exit"))
124 -V, --version display version information and exit"))
126 (show-bug-report-information))
128 (define %default-options
129 ;; Default to opening a new shell.
130 `((exec . ,(or (getenv "SHELL") "/bin/sh"))
132 (max-silent-time . 3600)
136 ;; Specification of the command-line options.
137 (cons* (option '(#\h "help") #f #f
141 (option '(#\V "version") #f #f
143 (show-version-and-exit "guix environment")))
144 (option '("pure") #f #f
145 (lambda (opt name arg result)
146 (alist-cons 'pure #t result)))
147 (option '(#\E "exec") #t #f
148 (lambda (opt name arg result)
149 (alist-cons 'exec arg result)))
150 (option '("search-paths") #f #f
151 (lambda (opt name arg result)
152 (alist-cons 'search-paths #t result)))
153 (option '(#\l "load") #t #f
154 (lambda (opt name arg result)
155 (alist-cons 'load arg result)))
156 (option '(#\e "expression") #t #f
157 (lambda (opt name arg result)
158 (alist-cons 'expression arg result)))
159 (option '("ad-hoc") #f #f
160 (lambda (opt name arg result)
161 (alist-cons 'ad-hoc? #t result)))
162 (option '(#\n "dry-run") #f #f
163 (lambda (opt name arg result)
164 (alist-cons 'dry-run? #t result)))
165 %standard-build-options))
167 (define (pick-all alist key)
168 "Return a list of values in ALIST associated with KEY."
169 (define same-key? (cut eq? key <>))
171 (fold (lambda (pair memo)
173 (((? same-key? k) . v)
178 (define (options/resolve-packages opts)
179 "Return OPTS with package specification strings replaced by actual
182 (('package . (? string? spec))
183 `(package . ,(specification->package spec)))
185 (match (read/eval str)
189 `(package . ,(load (string-append (getcwd) "/" file))))
193 (define (build-inputs inputs opts)
194 "Build the derivations in INPUTS, a list of (DERIVATION) or (DERIVATION
195 OUTPUT) tuples, using the build options in OPTS."
196 (let ((substitutes? (assoc-ref opts 'substitutes?))
197 (dry-run? (assoc-ref opts 'dry-run?)))
199 (((derivations _ ...) ...)
201 (show-what-to-build* derivations
202 #:use-substitutes? substitutes?
207 (set-build-options-from-command-line* opts)
208 (built-derivations derivations)
209 (return derivations))))))))
212 (define (guix-environment . args)
213 (define (handle-argument arg result)
214 (alist-cons 'package arg result))
217 (let* ((opts (parse-command-line args %options (list %default-options)
218 #:argument-handler handle-argument))
219 (pure? (assoc-ref opts 'pure))
220 (ad-hoc? (assoc-ref opts 'ad-hoc?))
221 (command (assoc-ref opts 'exec))
222 (packages (pick-all (options/resolve-packages opts) 'package))
224 (append-map package+propagated-inputs packages)
225 (append-map (compose bag-transitive-inputs
228 (paths (delete-duplicates
230 (append-map (match-lambda
231 ((label (? package? p) _ ...)
232 (package-native-search-paths p))
238 (run-with-store store
239 (mlet %store-monad ((inputs (lower-inputs
246 #:system (%current-system))))
248 ;; First build INPUTS. This is necessary even for
250 (build-inputs inputs opts)
251 (cond ((assoc-ref opts 'dry-run?)
253 ((assoc-ref opts 'search-paths)
254 (show-search-paths inputs paths pure?)
257 (create-environment inputs paths pure?)
258 (return (system command)))))))))))