From unknown Fri Jun 13 11:11:48 2025 X-Loop: help-debbugs@gnu.org Subject: [bug#50350] [PATCH core-updates] utils: Add helpers for list environment variables. Resent-From: Sarah Morgensen Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Fri, 03 Sep 2021 05:30:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 50350 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 50350@debbugs.gnu.org X-Debbugs-Original-To: guix-patches@gnu.org Received: via spool by submit@debbugs.gnu.org id=B.163064700020186 (code B ref -1); Fri, 03 Sep 2021 05:30:02 +0000 Received: (at submit) by debbugs.gnu.org; 3 Sep 2021 05:30:00 +0000 Received: from localhost ([127.0.0.1]:42543 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1mM1m3-0005FV-Of for submit@debbugs.gnu.org; Fri, 03 Sep 2021 01:30:00 -0400 Received: from lists.gnu.org ([209.51.188.17]:35854) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1mM1m1-0005FM-14 for submit@debbugs.gnu.org; Fri, 03 Sep 2021 01:29:58 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58858) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mM1m0-0007Bn-R2 for guix-patches@gnu.org; Fri, 03 Sep 2021 01:29:56 -0400 Received: from out0.migadu.com ([94.23.1.103]:25584) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mM1lw-0006sS-Ds for guix-patches@gnu.org; Fri, 03 Sep 2021 01:29:56 -0400 X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mgsn.dev; s=key1; t=1630646988; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=iQC+mZYN4Rsdj+aDnx39efA31j+S0LX28mc7cOdntl8=; b=BqNDgTvCsOPXpEARgWmNnV0M5IZ35RLdD7yXgz2CCx7BU9xLi91TfNFoRq1A9SdE86E/wY taHTySylJA9nxBYbCso4B2barBfh8tDUGbpD7I9Rs2WzpT31ieWLNBnp8zaGdKLNhdD67V O+w/K2ezcUpISgZnpRgeVvN8/UgwAt8= From: Sarah Morgensen Date: Thu, 2 Sep 2021 22:29:46 -0700 Message-Id: <40d8e502f754fd7bbbe3a916950a85d1f1c57073.1630646500.git.iskarian@mgsn.dev> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Migadu-Flow: FLOW_OUT X-Migadu-Auth-User: iskarian@mgsn.dev Received-SPF: pass client-ip=94.23.1.103; envelope-from=iskarian@mgsn.dev; helo=out0.migadu.com X-Spam_score_int: -23 X-Spam_score: -2.4 X-Spam_bar: -- X-Spam_report: (-2.4 / 5.0 requ) BAYES_00=-1.9, DKIM_INVALID=0.1, DKIM_SIGNED=0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Spam-Score: -1.4 (-) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -2.4 (--) Add helpers 'getenv/list', 'setenv/list', 'setenv/list-extend', and 'setenv/list-delete' for list environment variables (such as search paths). * guix/build/utils.scm (getenv/list, setenv/list) (setenv/list-extend, setenv/list-delete): New procedures. * .dir-locals.el (scheme-mode): Indent them. * tests/build-utils.scm ("getenv/list", "getenv/list: unset") ("setenv/list: ignore empty elements") ("setenv/list: unset if empty") ("setenv/list-extend: single element, prepend") ("setenv/list-extend: multiple elements, prepend") ("setenv/list-extend: multiple elements, append") ("setenv/list-delete: single deletion") ("setenv/list-delete: multiple deletions"): New tests. --- Hello Guix, I noticed that there are over 200 occurrences of this pattern in packages: --8<---------------cut here---------------start------------->8--- (setenv "PYTHONPATH" (string-append (getcwd) ":" (getenv "PYTHONPATH"))) --8<---------------cut here---------------end--------------->8--- This patch introduces some helper procedures for these kinds of cases. With this patch, instead of the above you could write: (setenv/list-extend "PYTHONPATH" (getcwd)) Sometimes you want to add to the end of the path: --8<---------------cut here---------------start------------->8--- (setenv "GEM_PATH" (string-append (getenv "GEM_PATH") ":" new-gem)) --8<---------------cut here---------------end--------------->8--- With this patch, you could write instead: (setenv/list-extend "GEM_PATH" new-gem #:prepend? #f) Adding include paths becomes much more readable in conjunction with search-input-directory, with this: --8<---------------cut here---------------start------------->8--- (setenv "CPATH" (string-append (assoc-ref inputs "libtirpc") "/include/tirpc/:" (or (getenv "CPATH") ""))) --8<---------------cut here---------------end--------------->8--- becoming this: (setenv/list-extend "CPATH" (search-input-directory "/include/tirpc")) A less common case, of removing a path: --8<---------------cut here---------------start------------->8--- (setenv "CPLUS_INCLUDE_PATH" (string-join (delete (string-append gcc "/include/c++") (string-split (getenv "CPLUS_INCLUDE_PATH") #\:)) ":")) --8<---------------cut here---------------end--------------->8--- becomes this: (setenv/list-delete "CPLUS_INCLUDE_PATH" (string-append gcc "/include/c++")) What do you all think? (Bikeshed opportunity: I'm not in love with the names. I originally named these 'setenv/path' rather than 'setenv/list', because I wanted to avoid confusion with Guix's search paths, but I'm not sure 'setenv/list' is actually more clear. I considered getenv*, setenv*, and so on, but I didn't think they were quite clear enough either. I did consider 'setenv/list-extend!' and 'setenv/list-delete!' since they do modify the env var in place, but "setenv" should already imply that. Finally, it might be better to have e.g. 'setenv/path-prepend!' and 'setenv/path-append!' rather than the single 'setenv/path-extend', but I could not settle on memorable, representative names. Using 'append' carries a connotation that you are dealing with lists, because of 'append', but it also accepts a single element. Using 'extend'/'prepend' together seems confusing to me, because I might reach for 'extend' to add to the beginning of the list if I forget about 'prepend'.) -- Sarah .dir-locals.el | 4 ++++ guix/build/utils.scm | 56 +++++++++++++++++++++++++++++++++++++++++++ tests/build-utils.scm | 53 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) diff --git a/.dir-locals.el b/.dir-locals.el index 919ed1d1c4..4b58220526 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -69,6 +69,10 @@ (eval . (put 'add-before 'scheme-indent-function 2)) (eval . (put 'add-after 'scheme-indent-function 2)) + (eval . (put 'setenv/list 'scheme-indent-function 1)) + (eval . (put 'setenv/list-extend 'scheme-indent-function 1)) + (eval . (put 'setenv/list-delete 'scheme-indent-function 1)) + (eval . (put 'modify-services 'scheme-indent-function 1)) (eval . (put 'with-directory-excursion 'scheme-indent-function 1)) (eval . (put 'with-file-lock 'scheme-indent-function 1)) diff --git a/guix/build/utils.scm b/guix/build/utils.scm index 3beb7da67a..d0ac33a64f 100644 --- a/guix/build/utils.scm +++ b/guix/build/utils.scm @@ -8,6 +8,7 @@ ;;; Copyright © 2020 Efraim Flashner ;;; Copyright © 2020, 2021 Maxim Cournoyer ;;; Copyright © 2021 Maxime Devos +;;; Copyright © 2021 Sarah Morgensen ;;; ;;; This file is part of GNU Guix. ;;; @@ -75,6 +76,11 @@ find-files false-if-file-not-found + getenv/list + setenv/list + setenv/list-extend + setenv/list-delete + search-path-as-list set-path-environment-variable search-path-as-string->list @@ -521,6 +527,56 @@ also be included. If FAIL-ON-ERROR? is true, raise an exception upon error." #f (apply throw args))))) + +;;; +;;; Multiple-valued environment variables. +;;; + +(define* (setenv/list env-var lst #:key (separator #\:)) + "Set environment variable ENV-VAR to the elements of LST separated by +SEPARATOR. Empty elements are ignored. If ENV-VAR would be set to the empty +string, unset ENV-VAR." + (let ((path (string-join (delete "" lst) (string separator)))) + (if (string-null? path) + (unsetenv env-var) + (setenv env-var path)))) + +(define* (getenv/list env-var #:key (separator #\:)) + "Return a list of the SEPARATOR-separated elements of environment variable +ENV-VAR, or the empty list if ENV-VAR is unset." + (or (and=> (getenv env-var) + (cut string-split <> separator)) + '())) + +(define* (setenv/list-extend env-var list-or-str + #:key (separator #\:) (prepend? #t)) + "Add the element(s) LIST-OR-STR to the environment variable ENV-VAR using +SEPARATOR between elements. Empty elements are ignored. Elements are placed +at the beginning if PREPEND? is #t, or at the end otherwise." + (let* ((elements (match list-or-str + ((? string? str) (list str)) + ((? list? lst) lst))) + (original (or (getenv env-var) "")) + (path-list (if prepend? + (append elements (list original)) + (cons original elements)))) + (when (not (null? elements)) + (setenv/list env-var path-list #:separator separator)))) + +(define* (setenv/list-delete env-var list-or-str #:key (separator #\:)) + "Remove the element(s) LIST-OR-STR from the SEPARATOR-separated environment +variable ENV-VAR, and set ENV-VAR to that value. If ENV-VAR would be set to +the empty string, unset ENV-VAR." + (let* ((elements (match list-or-str + ((? string? str) (list str)) + ((? list? lst) lst))) + (original (getenv/list env-var #:separator separator)) + (path-list (lset-difference string=? original elements)) + (path (string-join path-list (string separator)))) + (if (string-null? path) + (unsetenv env-var) + (setenv env-var path)))) + ;;; ;;; Search paths. diff --git a/tests/build-utils.scm b/tests/build-utils.scm index 6b131c0af8..b26bffd9a8 100644 --- a/tests/build-utils.scm +++ b/tests/build-utils.scm @@ -3,6 +3,7 @@ ;;; Copyright © 2019 Ricardo Wurmus ;;; Copyright © 2021 Maxim Cournoyer ;;; Copyright © 2021 Maxime Devos +;;; Copyright © 2021 Sarah Morgensen ;;; ;;; This file is part of GNU Guix. ;;; @@ -264,6 +265,58 @@ print('hello world')")) (lambda _ (get-string-all (current-input-port)))))))) +(test-equal "setenv/list: ignore empty elements" + "one:three" + (with-environment-variable "TEST_SETENV" #f + (setenv/list "TEST_SETENV" '("one" "" "three")) + (getenv "TEST_SETENV"))) + +(test-equal "setenv/list: unset if empty" + #f + (with-environment-variable "TEST_SETENV" #f + (setenv/list "TEST_SETENV" '()) + (getenv "TEST_SETENV"))) + +(test-equal "getenv/list" + '("one" "two" "three") + (with-environment-variable "TEST_SETENV" "one:two:three" + (getenv/list "TEST_SETENV"))) + +(test-equal "getenv/list: unset" + '() + (with-environment-variable "TEST_SETENV" #f + (getenv/list "TEST_SETENV"))) + +(test-equal "setenv/list-extend: single element, prepend" + "new:one:two" + (with-environment-variable "TEST_SETENV" "one:two" + (setenv/list-extend "TEST_SETENV" "new") + (getenv "TEST_SETENV"))) + +(test-equal "setenv/list-extend: multiple elements, prepend" + "first:second:one:two" + (with-environment-variable "TEST_SETENV" "one:two" + (setenv/list-extend "TEST_SETENV" '("first" "second")) + (getenv "TEST_SETENV"))) + +(test-equal "setenv/list-extend: multiple elements, append" + "one:two:first:second" + (with-environment-variable "TEST_SETENV" "one:two" + (setenv/list-extend "TEST_SETENV" '("first" "second") #:prepend? #f) + (getenv "TEST_SETENV"))) + +(test-equal "setenv/list-delete: single deletion" + "one:two:three" + (with-environment-variable "TEST_SETENV" "bad:one:two:bad:three:bad" + (setenv/list-delete "TEST_SETENV" "bad") + (getenv "TEST_SETENV"))) + +(test-equal "setenv/list-delete: multiple deletions" + "one:three" + (with-environment-variable "TEST_SETENV" "bad:one:two:bad:three:bad" + (setenv/list-delete "TEST_SETENV" '("bad" "two")) + (getenv "TEST_SETENV"))) + (test-equal "search-input-file: exception if not found" `((path) (file . "does-not-exist")) base-commit: 693d75e859150601145b7f7303f61d4f48e76927 -- 2.31.1 From unknown Fri Jun 13 11:11:48 2025 X-Loop: help-debbugs@gnu.org Subject: [bug#50350] [PATCH core-updates] utils: Add helpers for list environment variables. Resent-From: Xinglu Chen Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Thu, 16 Sep 2021 07:41:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 50350 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: Sarah Morgensen , 50350@debbugs.gnu.org Received: via spool by 50350-submit@debbugs.gnu.org id=B50350.16317780343218 (code B ref 50350); Thu, 16 Sep 2021 07:41:02 +0000 Received: (at 50350) by debbugs.gnu.org; 16 Sep 2021 07:40:34 +0000 Received: from localhost ([127.0.0.1]:53678 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1mQm0Y-0000pp-2m for submit@debbugs.gnu.org; Thu, 16 Sep 2021 03:40:34 -0400 Received: from h87-96-130-155.cust.a3fiber.se ([87.96.130.155]:54898 helo=mail.yoctocell.xyz) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1mQm0T-0000pV-SA for 50350@debbugs.gnu.org; Thu, 16 Sep 2021 03:40:32 -0400 From: Xinglu Chen DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=yoctocell.xyz; s=mail; t=1631778020; bh=8XXyyz4mJ2Qa46vCHmrzKD0XqxTIiAA0+Aub3Hf6/LE=; h=From:To:Subject:In-Reply-To:References:Date; b=b1fB6agyyiXKaUDh3/Hj0tIl/Y5+VGPWIi5vACaIhxpQg9PsIhJWGWOdzF6wyGe1F aqBjH+CefVVUTkNyBaY+mXdY3W6yryADlFCRRQ/bfKTbe9S8y7CskeRXZGaTO2AS6T RVlE3Cc9XrBDqoqXfAb46JZaDVKfORqyGBi/W2Js= In-Reply-To: <40d8e502f754fd7bbbe3a916950a85d1f1c57073.1630646500.git.iskarian@mgsn.dev> References: <40d8e502f754fd7bbbe3a916950a85d1f1c57073.1630646500.git.iskarian@mgsn.dev> Date: Thu, 16 Sep 2021 09:40:17 +0200 Message-ID: <874kalrmse.fsf@yoctocell.xyz> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha256; protocol="application/pgp-signature" X-Spam-Score: 2.9 (++) X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: On Thu, Sep 02 2021, Sarah Morgensen wrote: > Add helpers 'getenv/list', 'setenv/list', 'setenv/list-extend', and > 'setenv/list-delete' for list environment variables (such as search > paths). > > * guix/build/utils.scm (getenv/list, setenv/li [...] Content analysis details: (2.9 points, 10.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record 2.0 PDS_OTHER_BAD_TLD Untrustworthy TLDs [URI: yoctocell.xyz (xyz)] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 0.5 FROM_SUSPICIOUS_NTLD From abused NTLD 0.4 RDNS_DYNAMIC Delivered to internal network by host with dynamic-looking rDNS 0.0 PDS_RDNS_DYNAMIC_FP RDNS_DYNAMIC with FP steps X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: 2.9 (++) X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: On Thu, Sep 02 2021, Sarah Morgensen wrote: > Add helpers 'getenv/list', 'setenv/list', 'setenv/list-extend', and > 'setenv/list-delete' for list environment variables (such as search > paths). > > * guix/build/utils.scm (getenv/list, setenv/li [...] Content analysis details: (2.9 points, 10.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record 2.0 PDS_OTHER_BAD_TLD Untrustworthy TLDs [URI: yoctocell.xyz (xyz)] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 0.5 FROM_SUSPICIOUS_NTLD From abused NTLD 0.4 RDNS_DYNAMIC Delivered to internal network by host with dynamic-looking rDNS 1.0 BULK_RE_SUSP_NTLD Precedence bulk and RE: from a suspicious TLD -1.0 MAILING_LIST_MULTI Multiple indicators imply a widely-seen list manager 0.0 PDS_RDNS_DYNAMIC_FP RDNS_DYNAMIC with FP steps --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Thu, Sep 02 2021, Sarah Morgensen wrote: > Add helpers 'getenv/list', 'setenv/list', 'setenv/list-extend', and > 'setenv/list-delete' for list environment variables (such as search > paths). > > * guix/build/utils.scm (getenv/list, setenv/list) > (setenv/list-extend, setenv/list-delete): New procedures. > * .dir-locals.el (scheme-mode): Indent them. > * tests/build-utils.scm ("getenv/list", "getenv/list: unset") > ("setenv/list: ignore empty elements") > ("setenv/list: unset if empty") > ("setenv/list-extend: single element, prepend") > ("setenv/list-extend: multiple elements, prepend") > ("setenv/list-extend: multiple elements, append") > ("setenv/list-delete: single deletion") > ("setenv/list-delete: multiple deletions"): New tests. > --- > Hello Guix, > > I noticed that there are over 200 occurrences of this pattern in packages: > > --8<---------------cut here---------------start------------->8--- > (setenv "PYTHONPATH" > (string-append (getcwd) ":" (getenv "PYTHONPATH"))) > --8<---------------cut here---------------end--------------->8--- > > This patch introduces some helper procedures for these kinds of cases. Wi= th > this patch, instead of the above you could write: > > (setenv/list-extend "PYTHONPATH" (getcwd)) When I hear the word =E2=80=9Cextend=E2=80=9D, I would usually think that I= am adding something to the end of something else (appending). In this case, it is prepending the value rather than appending the value. But since prepending something to an environment variable seems more common, it might not be worth it to change it from prepending to appending by default. > Sometimes you want to add to the end of the path: > > --8<---------------cut here---------------start------------->8--- > (setenv "GEM_PATH" > (string-append (getenv "GEM_PATH") ":" new-gem)) > --8<---------------cut here---------------end--------------->8--- > > With this patch, you could write instead: > > (setenv/list-extend "GEM_PATH" new-gem #:prepend? #f) > > Adding include paths becomes much more readable in conjunction with > search-input-directory, with this: > > --8<---------------cut here---------------start------------->8--- > (setenv "CPATH" > (string-append (assoc-ref inputs "libtirpc") > "/include/tirpc/:" > (or (getenv "CPATH") ""))) > --8<---------------cut here---------------end--------------->8--- > > becoming this: > > (setenv/list-extend "CPATH" > (search-input-directory "/include/tirpc")) > > A less common case, of removing a path: > > --8<---------------cut here---------------start------------->8--- > (setenv "CPLUS_INCLUDE_PATH" > (string-join > (delete (string-append gcc "/include/c++") > (string-split (getenv "CPLUS_INCLUDE_PATH") #\:)) > ":")) > --8<---------------cut here---------------end--------------->8--- > > becomes this: > > (setenv/list-delete "CPLUS_INCLUDE_PATH" > (string-append gcc "/include/c++")) > > What do you all think? Thanks for working on this! I have running into situations like these lately. > (Bikeshed opportunity: I'm not in love with the names. I originally named > these 'setenv/path' rather than 'setenv/list', because I wanted to avoid > confusion with Guix's search paths, but I'm not sure 'setenv/list' is act= ually > more clear. > > I considered getenv*, setenv*, and so on, but I didn't think they were qu= ite > clear enough either. > > I did consider 'setenv/list-extend!' and 'setenv/list-delete!' since they= do > modify the env var in place, but "setenv" should already imply that. I would prefer =E2=80=98setenv/extend=E2=80=99 and =E2=80=98setenv/delete= =E2=80=99; the second argument can be a string or a list, so I don=E2=80=99t think it needs to have =E2=80= =98list-=E2=80=99 in the name. > Finally, it might be better to have e.g. 'setenv/path-prepend!' and > 'setenv/path-append!' rather than the single 'setenv/path-extend', but I = could > not settle on memorable, representative names. Using 'append' carries a > connotation that you are dealing with lists, because of 'append', but it = also > accepts a single element. Using 'extend'/'prepend' together seems confus= ing > to me, because I might reach for 'extend' to add to the beginning of the = list > if I forget about 'prepend'.) > > -- > Sarah > > .dir-locals.el | 4 ++++ > guix/build/utils.scm | 56 +++++++++++++++++++++++++++++++++++++++++++ > tests/build-utils.scm | 53 ++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 113 insertions(+) > > diff --git a/.dir-locals.el b/.dir-locals.el > index 919ed1d1c4..4b58220526 100644 > --- a/.dir-locals.el > +++ b/.dir-locals.el > @@ -69,6 +69,10 @@ > (eval . (put 'add-before 'scheme-indent-function 2)) > (eval . (put 'add-after 'scheme-indent-function 2)) >=20=20 > + (eval . (put 'setenv/list 'scheme-indent-function 1)) > + (eval . (put 'setenv/list-extend 'scheme-indent-function 1)) > + (eval . (put 'setenv/list-delete 'scheme-indent-function 1)) > + > (eval . (put 'modify-services 'scheme-indent-function 1)) > (eval . (put 'with-directory-excursion 'scheme-indent-function 1)) > (eval . (put 'with-file-lock 'scheme-indent-function 1)) > diff --git a/guix/build/utils.scm b/guix/build/utils.scm > index 3beb7da67a..d0ac33a64f 100644 > --- a/guix/build/utils.scm > +++ b/guix/build/utils.scm > @@ -8,6 +8,7 @@ > ;;; Copyright =C2=A9 2020 Efraim Flashner > ;;; Copyright =C2=A9 2020, 2021 Maxim Cournoyer > ;;; Copyright =C2=A9 2021 Maxime Devos > +;;; Copyright =C2=A9 2021 Sarah Morgensen > ;;; > ;;; This file is part of GNU Guix. > ;;; > @@ -75,6 +76,11 @@ > find-files > false-if-file-not-found >=20=20 > + getenv/list > + setenv/list > + setenv/list-extend > + setenv/list-delete > + > search-path-as-list > set-path-environment-variable > search-path-as-string->list > @@ -521,6 +527,56 @@ also be included. If FAIL-ON-ERROR? is true, raise = an exception upon error." > #f > (apply throw args))))) >=20=20 > + > +;;; > +;;; Multiple-valued environment variables. > +;;; > + > +(define* (setenv/list env-var lst #:key (separator #\:)) > + "Set environment variable ENV-VAR to the elements of LST separated by > +SEPARATOR. Empty elements are ignored. If ENV-VAR would be set to the = empty > +string, unset ENV-VAR." > + (let ((path (string-join (delete "" lst) (string separator)))) > + (if (string-null? path) > + (unsetenv env-var) > + (setenv env-var path)))) > + > +(define* (getenv/list env-var #:key (separator #\:)) > + "Return a list of the SEPARATOR-separated elements of environment vari= able > +ENV-VAR, or the empty list if ENV-VAR is unset." It might be worth mentioning that SEPARATOR has be a char-set; a string won=E2=80=99t cut it. ;-) > + (or (and=3D> (getenv env-var) > + (cut string-split <> separator)) > + '())) > + > +(define* (setenv/list-extend env-var list-or-str > + #:key (separator #\:) (prepend? #t)) > + "Add the element(s) LIST-OR-STR to the environment variable ENV-VAR us= ing > +SEPARATOR between elements. Empty elements are ignored. Elements are p= laced > +at the beginning if PREPEND? is #t, or at the end otherwise." > + (let* ((elements (match list-or-str > + ((? string? str) (list str)) > + ((? list? lst) lst))) > + (original (or (getenv env-var) "")) > + (path-list (if prepend? > + (append elements (list original)) > + (cons original elements)))) > + (when (not (null? elements)) Use =E2=80=98unless=E2=80=99 instead of (when (not ...)) > + (setenv/list env-var path-list #:separator separator)))) > + > +(define* (setenv/list-delete env-var list-or-str #:key (separator #\:)) > + "Remove the element(s) LIST-OR-STR from the SEPARATOR-separated enviro= nment > +variable ENV-VAR, and set ENV-VAR to that value. If ENV-VAR would be se= t to > +the empty string, unset ENV-VAR." > + (let* ((elements (match list-or-str > + ((? string? str) (list str)) > + ((? list? lst) lst))) > + (original (getenv/list env-var #:separator separator)) > + (path-list (lset-difference string=3D? original elements)) > + (path (string-join path-list (string separator)))) > + (if (string-null? path) > + (unsetenv env-var) > + (setenv env-var path)))) The new procedures should probably be documented in the manual. The rest LGTM! --=-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQJJBAEBCAAzFiEEAVhh4yyK5+SEykIzrPUJmaL7XHkFAmFC9OEVHHB1YmxpY0B5 b2N0b2NlbGwueHl6AAoJEKz1CZmi+1x5gL8P/2aRYRhQZbEULq4n6C+0CGRySJTL zOBvvbvJU+b5xrZ85Lffo90vAEaHIm+OkWH+NJ0UDdaWUwW/7+tUoRNOgxKHXPVy I1XWua57IjbkjtouG0CZk14Z8zYhNyumiIUwWnbg4d/MIT5VvcSqZ7fK1g4ovu57 JsdnVBjn0PtDj1f1mi3mcN+uTQrlWr4te/8qkpusbelu4tHP/ABZ7KKxV+QQKuVM 4tdyGCBA2sk2SWoSytJLvEJjK04jPR/gTkGvpt5M/jRxu7WmpGGOgDeh2o5GFOp0 GhxXGA3WClvyGk9ojKzxLphonDEIYCNSxeuCiV4Fmar1ePpFQ8QdgM5rYcxjPALP 62f5l64J14VFRPcLLpIB9xmuw4kVbr2Z/4i2L7B9FWrkG6jMv3zn1TP31u8K3enR +tjPEVQ6wdgsuMPCpMCmEJMlKPu3tMAIAsrLVpjorizh6M2EGA6QEuvQOR98rQq4 jFQS1ZQ0cS9zTC8+kbsvTSqFotIYbUKqA4nHbNY4o/5tE20BnnK7maPKZttyTuZS r1C1JQMG6Ogbg3cVroc5Ck+SSlCdc3H25qp2GDdarOkaJSUP+dHfXZVqqlNMc3d0 otYF0htJLgicKHVv0zAf+mQqAnlPsU64FRYfVjUkAqLdkH1ZMDsqlBsFEEdNCCKk NIkkmt5O1RsaykPm =dlF2 -----END PGP SIGNATURE----- --=-=-=--