From: David Thompson <dthompson2@worcester.edu>
To: "Ludovic Courtès" <ludo@gnu.org>
Cc: guix-devel@gnu.org
Subject: Re: [PATCH] scripts: Add 'environment' command.
Date: Thu, 09 Oct 2014 22:32:02 -0400 [thread overview]
Message-ID: <87r3yg1wpp.fsf@izanagi.i-did-not-set--mail-host-address--so-tickle-me> (raw)
In-Reply-To: <87siix59cs.fsf@gnu.org>
[-- Attachment #1: Type: text/plain, Size: 3575 bytes --]
Ludovic Courtès <ludo@gnu.org> writes:
[...]
>> +(define (purify-environment)
>> + "Unset almost all environment variables. A small number of variables such
>> +as 'HOME' and 'USER' are left untouched."
>> + (for-each unsetenv
>> + (filter (lambda (variable)
>> + ;; Protect some env vars from purification. Borrowed
>> + ;; from nix-shell.
>> + (not (member variable
>> + '("HOME" "USER" "LOGNAME" "DISPLAY"
>> + "TERM" "TZ" "PAGER"))))
>> + (map car (get-environment-variables)))))
>
> Maybe put the list of env. vars in a global variable, say
> ‘%precious-variables’, and then:
>
> (remove (cut member <> %precious-variables)
> (match (get-environment-variables)
> (((names . values) ...)
> names)))
>
> (My allergy to ‘car’ goes this far. ;-))
>
Done. Very elegant.
>> + (display (_ "
>> + -e, --exec shell command to execute"))
>
> Perhaps make it -E, and use -e consistently with ‘guix build’? It may
> be possible to reuse ‘options/resolve-packages’ for that.
>
Done. Flag changed to '-E', and '-e'/'--expression' flag added. I
wrote my another version of 'options/resolve-packages' since my needs
are different than 'guix build'.
>> +(define %default-options
>> + ;; Default to opening a new shell.
>> + `((exec . ,(getenv "SHELL"))
>
> (or (getenv "SHELL") "/bin/sh")
>
Done.
>> +(define (build-inputs inputs opts)
>> + "Build the packages in INPUTS using the build options in OPTS."
>> + (with-store store
>> + (run-with-store store
>> + (mlet* %store-monad ((drvs (sequence %store-monad
>> + (map package->derivation inputs))))
>> + (mbegin %store-monad
>> + (show-what-to-build* drvs
>> + #:use-substitutes? (assoc-ref opts 'substitutes?)
>> + #:dry-run? #f)
>> + (set-build-options-from-command-line* opts)
>> + (built-derivations drvs)
>> + (return drvs))))))
>
> The store should rather be kept open around (system command). Otherwise
> the above derivations and their outputs could be GC’d (it should be
> possible to check that by trying to run ‘guix gc -d XXX’ on one of them
> from within the sub-shell.)
>
Great point. Done.
>> +;; Entry point.
>> +(define (guix-environment . args)
>> + (define (parse-options)
>> + (args-fold* args %options
>> + (lambda (opt name arg result)
>> + (leave (_ "~A: unrecognized option~%") name))
>> + (lambda (arg result)
>> + (alist-cons 'package arg result))
>> + %default-options))
>> +
>> + (let* ((opts (parse-options))
>> + (pure? (assoc-ref opts 'pure))
>> + (command (assoc-ref opts 'exec))
>> + ;; Load from file if given, otherwise search for packages.
>> + (inputs (packages->transitive-inputs
>> + (or (and=> (assoc-ref opts 'load) load)
>> + (map specification->package
>> + (pick-all opts 'package)))))
>> + (drvs (build-inputs inputs opts)))
>
> Would be good to honor --dry-run as well. It would just print what
> needs to be built and exit.
>
Done.
Updated patch attached. Thanks for the feedback!
[-- Attachment #2: 0001-scripts-Add-environment-command.patch --]
[-- Type: text/x-diff, Size: 10831 bytes --]
From 3dffa5a126173d46c22750a7d7f454e2c24ce4ea Mon Sep 17 00:00:00 2001
From: David Thompson <dthompson2@worcester.edu>
Date: Sun, 21 Sep 2014 13:40:05 -0400
Subject: [PATCH] scripts: Add 'environment' command.
* guix/scripts/environment.scm: New file.
* Makefile.am (MODULES): Add it.
---
Makefile.am | 1 +
guix/scripts/environment.scm | 237 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 238 insertions(+)
create mode 100644 guix/scripts/environment.scm
diff --git a/Makefile.am b/Makefile.am
index 7eecef2..4b823ec 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -94,6 +94,7 @@ MODULES = \
guix/scripts/system.scm \
guix/scripts/lint.scm \
guix/scripts/import/nix.scm \
+ guix/scripts/environment.scm \
guix.scm \
$(GNU_SYSTEM_MODULES)
diff --git a/guix/scripts/environment.scm b/guix/scripts/environment.scm
new file mode 100644
index 0000000..5c0d2b8
--- /dev/null
+++ b/guix/scripts/environment.scm
@@ -0,0 +1,237 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2014 David Thompson <davet@gnu.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix scripts environment)
+ #:use-module (guix ui)
+ #:use-module (guix store)
+ #:use-module (guix derivations)
+ #:use-module (guix packages)
+ #:use-module (guix profiles)
+ #:use-module (guix utils)
+ #:use-module (guix monads)
+ #:use-module (guix build utils)
+ #:use-module (guix build-system gnu)
+ #:use-module (guix scripts build)
+ #:use-module (gnu packages)
+ #:use-module (gnu packages commencement)
+ #:use-module (ice-9 format)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-26)
+ #:use-module (srfi srfi-37)
+ #:use-module (srfi srfi-98)
+ #:export (guix-environment))
+
+(define (for-each-search-path proc inputs derivations pure?)
+ "Apply PROC for each native search path in INPUTS in addition to 'PATH'.
+Use the output paths of DERIVATIONS to build each search path. When PURE? is
+#t, the existing search path value is ignored. Otherwise, the existing search
+path value is appended."
+ (let ((paths (map derivation->output-path derivations)))
+ (for-each (match-lambda
+ (($ <search-path-specification>
+ variable directories separator)
+ (let* ((current (getenv variable))
+ (path ((@@ (guix build utils) search-path-as-list)
+ directories paths))
+ (value (list->search-path-as-string path separator)))
+ (proc variable
+ (if (and current (not pure?))
+ (string-append value separator current)
+ value)))))
+ (cons* (search-path-specification
+ (variable "PATH")
+ (directories '("bin" "sbin"))
+ (separator ":"))
+ (delete-duplicates
+ (append-map package-native-search-paths inputs))))))
+
+;; Protect some env vars from purification. Borrowed from nix-shell.
+(define %precious-variables
+ '("HOME" "USER" "LOGNAME" "DISPLAY" "TERM" "TZ" "PAGER"))
+
+(define (purify-environment)
+ "Unset almost all environment variables. A small number of variables such
+as 'HOME' and 'USER' are left untouched."
+ (for-each unsetenv
+ (remove (cut member <> %precious-variables)
+ (match (get-environment-variables)
+ (((names . _) ...)
+ names)))))
+
+(define (create-environment inputs derivations pure?)
+ "Set the needed environment variables for all packages within INPUTS. When
+PURE? is #t, unset the variables in the current environment. Otherwise,
+augment existing enviroment variables with additional search paths."
+ (when pure? (purify-environment))
+ (for-each-search-path setenv inputs derivations pure?))
+
+(define (show-search-paths inputs derivations pure?)
+ "Display the needed search paths to build an environment that contains the
+packages within INPUTS. When PURE? is #t, do not augment existing environment
+variables with additional search paths."
+ (for-each-search-path (lambda (variable value)
+ (format #t "export ~a=\"~a\"~%" variable value))
+ inputs derivations pure?))
+
+(define (show-help)
+ (display (_ "Usage: guix environment [OPTION]... PACKAGE...
+Build an environment that includes the dependencies of PACKAGE and execute a
+shell command in that environment.\n"))
+ (display (_ "
+ -e, --expression=EXPR create environment for the package that EXPR
+ evaluates to"))
+ (display (_ "
+ -l, --load=FILE create environment for the package that the code within
+ FILE evaluates to"))
+ (display (_ "
+ -E, --exec shell command to execute"))
+ (display (_ "
+ --pure unset existing environment variables"))
+ (display (_ "
+ --search-paths display needed environment variable definitions"))
+ (newline)
+ (show-build-options-help)
+ (newline)
+ (display (_ "
+ -h, --help display this help and exit"))
+ (display (_ "
+ -V, --version display version information and exit"))
+ (show-bug-report-information))
+
+(define %default-options
+ ;; Default to opening a new shell.
+ `((exec . ,(or (getenv "SHELL") "/bin/sh"))
+ (substitutes? . #t)
+ (max-silent-time . 3600)
+ (verbosity . 0)))
+
+(define %options
+ ;; Specification of the command-line options.
+ (cons* (option '(#\h "help") #f #f
+ (lambda args
+ (show-help)
+ (exit 0)))
+ (option '(#\V "version") #f #f
+ (lambda args
+ (show-version-and-exit "guix environment")))
+ (option '("pure") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'pure #t result)))
+ (option '(#\E "exec") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'exec arg result)))
+ (option '("search-paths") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'search-paths #t result)))
+ (option '(#\l "load") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'load arg result)))
+ (option '(#\e "expression") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'expression arg result)))
+ (option '(#\n "dry-run") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'dry-run? #t result)))
+ %standard-build-options))
+
+(define (pick-all alist key)
+ "Return a list of values in ALIST associated with KEY."
+ (define same-key? (cut eq? key <>))
+
+ (fold (lambda (pair memo)
+ (match pair
+ (((? same-key? k) . v)
+ (cons v memo))
+ (_ memo)))
+ '() alist))
+
+(define (options/resolve-packages opts)
+ "Return OPTS with package specification strings replaced by actual
+packages."
+ (map (match-lambda
+ (('package . (? string? spec))
+ `(package . ,(specification->package spec)))
+ (('expression . str)
+ (match (read/eval str)
+ ((? package? p)
+ `(package . ,p))))
+ (('load . file)
+ `(package . ,(load (string-append (getcwd) "/" file))))
+ (opt opt))
+ opts))
+
+(define (packages->transitive-inputs packages)
+ "Return a list of the transitive inputs for all PACKAGES."
+ (define (transitive-inputs package)
+ (filter-map (match-lambda
+ ((_ (? package? package)) package)
+ (_ #f))
+ (bag-transitive-inputs
+ (package->bag package))))
+ (delete-duplicates
+ (append-map transitive-inputs packages)))
+
+;; TODO: Deduplicate these.
+(define show-what-to-build*
+ (store-lift show-what-to-build))
+
+(define set-build-options-from-command-line*
+ (store-lift set-build-options-from-command-line))
+
+(define (build-inputs inputs opts)
+ "Build the packages in INPUTS using the build options in OPTS."
+ (let ((substitutes? (assoc-ref opts 'substitutes?))
+ (dry-run? (assoc-ref opts 'dry-run?)))
+ (mlet* %store-monad ((drvs (sequence %store-monad
+ (map package->derivation inputs))))
+ (mbegin %store-monad
+ (show-what-to-build* drvs
+ #:use-substitutes? substitutes?
+ #:dry-run? dry-run?)
+ (if dry-run?
+ (return #f)
+ (mbegin %store-monad
+ (set-build-options-from-command-line* opts)
+ (built-derivations drvs)
+ (return drvs)))))))
+
+;; Entry point.
+(define (guix-environment . args)
+ (define (parse-options)
+ (args-fold* args %options
+ (lambda (opt name arg result)
+ (leave (_ "~A: unrecognized option~%") name))
+ (lambda (arg result)
+ (alist-cons 'package arg result))
+ %default-options))
+
+ (with-store store
+ (let* ((opts (parse-options))
+ (pure? (assoc-ref opts 'pure))
+ (command (assoc-ref opts 'exec))
+ (inputs (packages->transitive-inputs
+ (pick-all (options/resolve-packages opts) 'package)))
+ (drvs (run-with-store store (build-inputs inputs opts))))
+ (cond ((assoc-ref opts 'dry-run?)
+ #t)
+ ((assoc-ref opts 'search-paths)
+ (show-search-paths inputs drvs pure?))
+ (else
+ (create-environment inputs drvs pure?)
+ (system command))))))
--
2.1.0
[-- Attachment #3: Type: text/plain, Size: 136 bytes --]
--
David Thompson
Web Developer - Free Software Foundation - http://fsf.org
GPG Key: 0FF1D807
Support the FSF: https://fsf.org/donate
next prev parent reply other threads:[~2014-10-10 2:32 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-10-08 22:48 [PATCH] scripts: Add 'environment' command David Thompson
2014-10-09 6:44 ` Alex Kost
2014-10-09 16:50 ` Eric Bavier
2014-10-09 16:54 ` Thompson, David
2014-10-09 17:26 ` Eric Bavier
2014-10-09 19:30 ` Ludovic Courtès
2014-10-10 2:32 ` David Thompson [this message]
2014-10-10 12:09 ` David Thompson
2014-10-10 16:37 ` Ludovic Courtès
2014-10-10 18:09 ` David Thompson
2014-10-10 20:47 ` Ludovic Courtès
2014-10-10 22:16 ` David Thompson
2014-10-11 10:35 ` Ludovic Courtès
2014-10-11 12:27 ` David Thompson
2014-10-11 21:52 ` Ludovic Courtès
2014-10-12 4:43 ` Setting environment variables in .bashrc vs .bash_profile Mark H Weaver
2014-10-12 21:10 ` Ludovic Courtès
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://guix.gnu.org/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87r3yg1wpp.fsf@izanagi.i-did-not-set--mail-host-address--so-tickle-me \
--to=dthompson2@worcester.edu \
--cc=guix-devel@gnu.org \
--cc=ludo@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/guix.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).