diff options
author | Ludovic Courtès <ludo@gnu.org> | 2013-04-30 17:06:00 +0200 |
---|---|---|
committer | Ludovic Courtès <ludo@gnu.org> | 2013-04-30 17:06:00 +0200 |
commit | 0734a9a8131525d6da2e7bf802402dc0350eda98 (patch) | |
tree | f43bef210f6513b12c14ee9494bb47e4f80e99d0 /guix | |
parent | e0fbbc889d724678e9e310432ad3a3fb8345cf9a (diff) | |
parent | 01155b1808b17f0a4f54388261ab0c6f5fee2f1b (diff) |
Merge branch 'core-updates'
Diffstat (limited to 'guix')
-rw-r--r-- | guix/build-system/cmake.scm | 6 | ||||
-rw-r--r-- | guix/build-system/gnu.scm | 45 | ||||
-rw-r--r-- | guix/build-system/perl.scm | 8 | ||||
-rw-r--r-- | guix/build-system/trivial.scm | 6 | ||||
-rw-r--r-- | guix/build/gnu-build-system.scm | 44 | ||||
-rw-r--r-- | guix/build/perl-build-system.scm | 4 | ||||
-rw-r--r-- | guix/build/utils.scm | 143 | ||||
-rw-r--r-- | guix/download.scm | 3 | ||||
-rw-r--r-- | guix/packages.scm | 49 | ||||
-rw-r--r-- | guix/scripts/package.scm | 66 | ||||
-rw-r--r-- | guix/utils.scm | 28 |
11 files changed, 331 insertions, 71 deletions
diff --git a/guix/build-system/cmake.scm b/guix/build-system/cmake.scm index 2a9db80cf8..3347dc502c 100644 --- a/guix/build-system/cmake.scm +++ b/guix/build-system/cmake.scm @@ -38,11 +38,11 @@ (define* (cmake-build store name source inputs #:key (guile #f) (outputs '("out")) (configure-flags ''()) + (search-paths '()) (make-flags ''()) (patches ''()) (patch-flags ''("--batch" "-p1")) (cmake (@ (gnu packages cmake) cmake)) (out-of-source? #f) - (path-exclusions ''()) (tests? #t) (test-target "test") (parallel-build? #t) (parallel-tests? #f) @@ -71,13 +71,15 @@ provides a 'CMakeLists.txt' file as its build system." #:system ,system #:outputs %outputs #:inputs %build-inputs + #:search-paths ',(map search-path-specification->sexp + (append search-paths + (standard-search-paths))) #:patches ,patches #:patch-flags ,patch-flags #:phases ,phases #:configure-flags ,configure-flags #:make-flags ,make-flags #:out-of-source? ,out-of-source? - #:path-exclusions ,path-exclusions #:tests? ,tests? #:test-target ,test-target #:parallel-build? ,parallel-build? diff --git a/guix/build-system/gnu.scm b/guix/build-system/gnu.scm index 8049e7510f..b64bce7dae 100644 --- a/guix/build-system/gnu.scm +++ b/guix/build-system/gnu.scm @@ -27,6 +27,7 @@ #:use-module (ice-9 match) #:export (gnu-build gnu-build-system + standard-search-paths standard-inputs package-with-explicit-inputs package-with-extra-configure-variable @@ -135,6 +136,24 @@ use `--strip-all' as the arguments to `strip'." ;; Store passed to STANDARD-INPUTS. (make-parameter #f)) +(define (standard-packages) + "Return the list of (NAME PACKAGE OUTPUT) or (NAME PACKAGE) tuples of +standard packages used as implicit inputs of the GNU build system." + + ;; Resolve (gnu packages base) lazily to hide circular dependency. + (let ((distro (resolve-module '(gnu packages base)))) + (module-ref distro '%final-inputs))) + +(define (standard-search-paths) + "Return the list of <search-path-specification> for the standard (implicit) +inputs." + (append-map (match-lambda + ((_ (? package? p) _ ...) + (package-native-search-paths p)) + (_ + '())) + (standard-packages))) + (define standard-inputs (memoize (lambda (system) @@ -148,9 +167,7 @@ System: GCC, GNU Make, Bash, Coreutils, etc." (z (error "invalid standard input" z))) - ;; Resolve (gnu packages base) lazily to hide circular dependency. - (let* ((distro (resolve-module '(gnu packages base))) - (inputs (module-ref distro '%final-inputs))) + (let ((inputs (standard-packages))) (append inputs (append-map (match-lambda ((name package _ ...) @@ -159,11 +176,12 @@ System: GCC, GNU Make, Bash, Coreutils, etc." (define* (gnu-build store name source inputs #:key (guile #f) - (outputs '("out")) (configure-flags ''()) + (outputs '("out")) + (search-paths '()) + (configure-flags ''()) (make-flags ''()) (patches ''()) (patch-flags ''("--batch" "-p1")) (out-of-source? #f) - (path-exclusions ''()) (tests? #t) (test-target "check") (parallel-build? #t) (parallel-tests? #t) @@ -190,6 +208,16 @@ the builder's environment, from the host. Note that we distinguish between both, because for Guile's own modules like (ice-9 foo), we want to use GUILE's own version of it, rather than import the user's one, which could lead to gratuitous input divergence." + (define implicit-inputs + (and implicit-inputs? + (parameterize ((%store store)) + (standard-inputs system)))) + + (define implicit-search-paths + (if implicit-inputs? + (standard-search-paths) + '())) + (define builder `(begin (use-modules ,@modules) @@ -199,13 +227,15 @@ which could lead to gratuitous input divergence." #:system ,system #:outputs %outputs #:inputs %build-inputs + #:search-paths ',(map search-path-specification->sexp + (append implicit-search-paths + search-paths)) #:patches ,patches #:patch-flags ,patch-flags #:phases ,phases #:configure-flags ,configure-flags #:make-flags ,make-flags #:out-of-source? ,out-of-source? - #:path-exclusions ,path-exclusions #:tests? ,tests? #:test-target ,test-target #:parallel-build? ,parallel-build? @@ -233,8 +263,7 @@ which could lead to gratuitous input divergence." '()) ,@inputs ,@(if implicit-inputs? - (parameterize ((%store store)) - (standard-inputs system)) + implicit-inputs '())) #:outputs outputs #:modules imported-modules diff --git a/guix/build-system/perl.scm b/guix/build-system/perl.scm index 537c29e799..1ff9fd2674 100644 --- a/guix/build-system/perl.scm +++ b/guix/build-system/perl.scm @@ -38,6 +38,7 @@ (define* (perl-build store name source inputs #:key (perl (@ (gnu packages perl) perl)) + (search-paths '()) (tests? #t) (make-maker-flags ''()) (phases '(@ (guix build perl-build-system) @@ -53,6 +54,10 @@ (guix build utils)))) "Build SOURCE using PERL, and with INPUTS. This assumes that SOURCE provides a `Makefile.PL' file as its build system." + (define perl-search-paths + (append (package-native-search-paths perl) + (standard-search-paths))) + (define builder `(begin (use-modules ,@modules) @@ -60,6 +65,9 @@ provides a `Makefile.PL' file as its build system." #:source ,(if (and source (derivation-path? source)) (derivation-path->output-path source) source) + #:search-paths ',(map search-path-specification->sexp + (append perl-search-paths + search-paths)) #:make-maker-flags ,make-maker-flags #:system ,system #:test-target "test" diff --git a/guix/build-system/trivial.scm b/guix/build-system/trivial.scm index e5bbeaa91d..2eb15aa2e0 100644 --- a/guix/build-system/trivial.scm +++ b/guix/build-system/trivial.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2012 Ludovic Courtès <ludo@gnu.org> +;;; Copyright © 2012, 2013 Ludovic Courtès <ludo@gnu.org> ;;; ;;; This file is part of GNU Guix. ;;; @@ -26,7 +26,9 @@ #:export (trivial-build-system)) (define* (trivial-build store name source inputs - #:key outputs guile system builder (modules '())) + #:key + outputs guile system builder (modules '()) + search-paths) "Run build expression BUILDER, an expression, for SYSTEM. SOURCE is ignored." (define guile-for-build diff --git a/guix/build/gnu-build-system.scm b/guix/build/gnu-build-system.scm index b7b9fdac95..47820aa02e 100644 --- a/guix/build/gnu-build-system.scm +++ b/guix/build/gnu-build-system.scm @@ -48,33 +48,22 @@ #f dir)) -(define* (set-paths #:key inputs (path-exclusions '()) +(define* (set-paths #:key inputs (search-paths '()) #:allow-other-keys) - (define (relevant-input-directories env-var) - ;; Return the subset of INPUTS that should be considered when setting - ;; ENV-VAR. - (match (assoc-ref path-exclusions env-var) - (#f - (map cdr inputs)) - ((excluded ...) - (filter-map (match-lambda - ((name . dir) - (and (not (member name excluded)) - dir))) - inputs)))) + (define input-directories + (match inputs + (((_ . dir) ...) + dir))) - (set-path-environment-variable "PATH" '("bin") - (relevant-input-directories "PATH")) - (set-path-environment-variable "CPATH" '("include") - (relevant-input-directories "CPATH")) - (set-path-environment-variable "LIBRARY_PATH" '("lib" "lib64") - (relevant-input-directories "LIBRARY_PATH")) + (set-path-environment-variable "PATH" '("bin" "sbin") + input-directories) - ;; FIXME: Eventually move this to the `search-paths' field of the - ;; `pkg-config' package. - (set-path-environment-variable "PKG_CONFIG_PATH" - '("lib/pkgconfig" "lib64/pkgconfig") - (relevant-input-directories "PKG_CONFIG_PATH")) + (for-each (match-lambda + ((env-var (directories ...) separator) + (set-path-environment-variable env-var directories + input-directories + #:separator separator))) + search-paths) ;; Dump the environment variables as a shell script, for handy debugging. (system "export > environment-variables")) @@ -120,9 +109,10 @@ makefiles." (base (basename out)) (dash (string-rindex base #\-))) ;; XXX: We'd rather use `package-name->name+version' or similar. - (if dash - (substring base 0 dash) - base))) + (string-drop (if dash + (substring base 0 dash) + base) + (+ 1 (string-index base #\-))))) (let* ((prefix (assoc-ref outputs "out")) (bindir (assoc-ref outputs "bin")) diff --git a/guix/build/perl-build-system.scm b/guix/build/perl-build-system.scm index d625ef3ed6..793b6aacb5 100644 --- a/guix/build/perl-build-system.scm +++ b/guix/build/perl-build-system.scm @@ -50,10 +50,6 @@ (define* (perl-build #:key inputs (phases %standard-phases) #:allow-other-keys #:rest args) "Build the given Perl package, applying all of PHASES in order." - (set-path-environment-variable "PERL5LIB" '("lib/perl5/site_perl") - (match inputs - (((_ . path) ...) - path))) (apply gnu:gnu-build #:inputs inputs #:phases phases args)) diff --git a/guix/build/utils.scm b/guix/build/utils.scm index 6921e31bdd..a4a82a5f8c 100644 --- a/guix/build/utils.scm +++ b/guix/build/utils.scm @@ -1,5 +1,7 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2012, 2013 Ludovic Courtès <ludo@gnu.org> +;;; Copyright © 2013 Andreas Enge <andreas@enge.fr> +;;; Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org> ;;; ;;; This file is part of GNU Guix. ;;; @@ -31,6 +33,7 @@ with-directory-excursion mkdir-p copy-recursively + delete-file-recursively find-files set-path-environment-variable @@ -49,9 +52,10 @@ patch-shebang patch-makefile-SHELL fold-port-matches - remove-store-references)) + remove-store-references + wrap-program)) + - ;;; ;;; Directories. ;;; @@ -120,8 +124,11 @@ return values of applying PROC to the port." (() #t)))) (define* (copy-recursively source destination - #:optional (log (current-output-port))) - "Copy SOURCE directory to DESTINATION." + #:key + (log (current-output-port)) + (follow-symlinks? #f)) + "Copy SOURCE directory to DESTINATION. Follow symlinks if FOLLOW-SYMLINKS? +is true; otherwise, just preserve them. Write verbose output to the LOG port." (define strip-source (let ((len (string-length source))) (lambda (file) @@ -132,7 +139,12 @@ return values of applying PROC to the port." (let ((dest (string-append destination (strip-source file)))) (format log "`~a' -> `~a'~%" file dest) - (copy-file file dest))) + (case (stat:type stat) + ((symlink) + (let ((target (readlink file))) + (symlink target dest))) + (else + (copy-file file dest))))) (lambda (dir stat result) ; down (mkdir-p (string-append destination (strip-source dir)))) @@ -144,7 +156,31 @@ return values of applying PROC to the port." file (strerror errno)) #f) #t - source)) + source + + (if follow-symlinks? + stat + lstat))) + +(define (delete-file-recursively dir) + "Delete DIR recursively, like `rm -rf', without following symlinks. Report +but ignore errors." + (file-system-fold (const #t) ; enter? + (lambda (file stat result) ; leaf + (delete-file file)) + (const #t) ; down + (lambda (dir stat result) ; up + (rmdir dir)) + (const #t) ; skip + (lambda (file stat errno result) + (format (current-error-port) + "warning: failed to delete ~a: ~a~%" + file (strerror errno))) + #t + dir + + ;; Don't follow symlinks. + lstat)) (define (find-files dir regexp) "Return the list of files under DIR whose basename matches REGEXP." @@ -426,7 +462,7 @@ bytes transferred and the continuation of the transfer as a thunk." (stat:mtimensec stat))) (define patch-shebang - (let ((shebang-rx (make-regexp "^[[:blank:]]*([[:graph:]]+)(.*)$"))) + (let ((shebang-rx (make-regexp "^[[:blank:]]*([[:graph:]]+)[[:blank:]]*([[:graph:]]*)(.*)$"))) (lambda* (file #:optional (path (search-path-as-string->list (getenv "PATH"))) @@ -465,16 +501,29 @@ FILE are kept unchanged." (let ((line (false-if-exception (read-line p)))) (and=> (and line (regexp-exec shebang-rx line)) (lambda (m) - (let* ((cmd (match:substring m 1)) - (bin (search-path path (basename cmd)))) + (let* ((interp (match:substring m 1)) + (arg1 (match:substring m 2)) + (rest (match:substring m 3)) + (has-env (string-suffix? "/env" interp)) + (cmd (if has-env arg1 (basename interp))) + (bin (search-path path cmd))) (if bin - (if (string=? bin cmd) + (if (string=? bin interp) #f ; nothing to do - (begin - (format (current-error-port) - "patch-shebang: ~a: changing `~a' to `~a'~%" - file cmd bin) - (patch p bin (match:substring m 2)))) + (if has-env + (begin + (format (current-error-port) + "patch-shebang: ~a: changing `~a' to `~a'~%" + file (string-append interp " " arg1) bin) + (patch p bin rest)) + (begin + (format (current-error-port) + "patch-shebang: ~a: changing `~a' to `~a'~%" + file interp bin) + (patch p bin + (if (string-null? arg1) + "" + (string-append " " arg1 rest)))))) (begin (format (current-error-port) "patch-shebang: ~a: warning: no binary for interpreter `~a' found in $PATH~%" @@ -605,6 +654,70 @@ known as `nuke-refs' in Nixpkgs." (put-u8 out (char->integer char)) result)))))) +(define* (wrap-program prog #:rest vars) + "Rename PROG to .PROG-real and make PROG a wrapper. VARS should look like +this: + + '(VARIABLE DELIMITER POSITION LIST-OF-DIRECTORIES) + +where DELIMITER is optional. ':' will be used if DELIMITER is not given. + +For example, this command: + + (wrap-program \"foo\" + '(\"PATH\" \":\" = (\"/nix/.../bar/bin\")) + '(\"CERT_PATH\" suffix (\"/nix/.../baz/certs\" + \"/qux/certs\"))) + +will copy 'foo' to '.foo-real' and create the file 'foo' with the following +contents: + + #!location/of/bin/bash + export PATH=\"/nix/.../bar/bin\" + export CERT_PATH=\"$CERT_PATH${CERT_PATH:+:}/nix/.../baz/certs:/qux/certs\" + exec location/of/.foo-real + +This is useful for scripts that expect particular programs to be in $PATH, for +programs that expect particular shared libraries to be in $LD_LIBRARY_PATH, or +modules in $GUILE_LOAD_PATH, etc." + (let ((prog-real (string-append (dirname prog) "/." (basename prog) "-real")) + (prog-tmp (string-append (dirname prog) "/." (basename prog) "-tmp"))) + (define (export-variable lst) + ;; Return a string that exports an environment variable. + (match lst + ((var sep '= rest) + (format #f "export ~a=\"~a\"" + var (string-join rest sep))) + ((var sep 'prefix rest) + (format #f "export ~a=\"~a${~a~a+~a}$~a\"" + var (string-join rest sep) var sep sep var)) + ((var sep 'suffix rest) + (format #f "export ~a=\"$~a${~a~a+~a}~a\"" + var var var sep sep (string-join rest sep))) + ((var '= rest) + (format #f "export ~a=\"~a\"" + var (string-join rest ":"))) + ((var 'prefix rest) + (format #f "export ~a=\"~a${~a:+:}$~a\"" + var (string-join rest ":") var var)) + ((var 'suffix rest) + (format #f "export ~a=\"$~a${~a:+:}~a\"" + var var var (string-join rest ":"))))) + + (copy-file prog prog-real) + + (with-output-to-file prog-tmp + (lambda () + (format #t + "#!~a~%~a~%exec \"~a\" \"$@\"~%" + (which "bash") + (string-join (map export-variable vars) + "\n") + (canonicalize-path prog-real)))) + + (chmod prog-tmp #o755) + (rename-file prog-tmp prog))) + ;;; Local Variables: ;;; eval: (put 'call-with-output-file/atomic 'scheme-indent-function 1) ;;; eval: (put 'with-throw-handler 'scheme-indent-function 1) diff --git a/guix/download.scm b/guix/download.scm index b315b4c1d0..99353be8b0 100644 --- a/guix/download.scm +++ b/guix/download.scm @@ -107,8 +107,7 @@ "http://mirrors.ircam.fr/pub/apache/" "http://apache-mirror.rbc.ru/pub/apache/") (xorg ; from http://www.x.org/wiki/Releases/Download - "http://xorg.freedesktop.org/releases/" ; main mirrors - "http://www.x.org/pub/" + "http://www.x.org/releases/" ; main mirrors "ftp://mirror.csclub.uwaterloo.ca/x.org/" ; North America "ftp://xorg.mirrors.pair.com/" "http://mirror.csclub.uwaterloo.ca/x.org/" diff --git a/guix/packages.scm b/guix/packages.scm index e8ae2fb817..1cbbd2ec47 100644 --- a/guix/packages.scm +++ b/guix/packages.scm @@ -37,6 +37,11 @@ origin-file-name base32 + <search-path-specification> + search-path-specification + search-path-specification? + search-path-specification->sexp + package package? package-name @@ -49,6 +54,7 @@ package-native-inputs package-propagated-inputs package-outputs + package-native-search-paths package-search-paths package-synopsis package-description @@ -105,8 +111,22 @@ representation." ((_ str) #'(nix-base32-string->bytevector str))))) -;; A package. +;; The specification of a search path. +(define-record-type* <search-path-specification> + search-path-specification make-search-path-specification + search-path-specification? + (variable search-path-specification-variable) + (directories search-path-specification-directories) + (separator search-path-specification-separator (default ":"))) + +(define (search-path-specification->sexp spec) + "Return an sexp representing SPEC, a <search-path-specification>. The sexp +corresponds to the arguments expected by `set-path-environment-variable'." + (match spec + (($ <search-path-specification> variable directories separator) + `(,variable ,directories ,separator)))) +;; A package. (define-record-type* <package> package make-package package? @@ -129,10 +149,13 @@ representation." (outputs package-outputs ; list of strings (default '("out"))) - (search-paths package-search-paths ; list of (ENV-VAR (DIRS ...)) - (default '())) ; tuples; see - ; `set-path-environment-variable' - ; (aka. "setup-hook") + + ; lists of + ; <search-path-specification>, + ; for native and cross + ; inputs + (native-search-paths package-native-search-paths (default '())) + (search-paths package-search-paths (default '())) (synopsis package-synopsis) ; one-line description (description package-description) ; one or two paragraphs @@ -328,16 +351,22 @@ PACKAGE for SYSTEM." (($ <package> name version source (= build-system-builder builder) args inputs propagated-inputs native-inputs self-native-input? outputs) - ;; TODO: For `search-paths', add a builder prologue that calls - ;; `set-path-environment-variable'. - (let ((inputs (map expand-input - (package-transitive-inputs package)))) + (let* ((inputs (package-transitive-inputs package)) + (input-drvs (map expand-input inputs)) + (paths (delete-duplicates + (append-map (match-lambda + ((_ (? package? p) _ ...) + (package-native-search-paths + p)) + (_ '())) + inputs)))) (apply builder store (package-full-name package) (and source (package-source-derivation store source system)) - inputs + input-drvs + #:search-paths paths #:outputs outputs #:system system (args)))))))) diff --git a/guix/scripts/package.scm b/guix/scripts/package.scm index cea49a57f4..5eddb7defe 100644 --- a/guix/scripts/package.scm +++ b/guix/scripts/package.scm @@ -330,6 +330,53 @@ but ~a is available upstream~%") ((getaddrinfo-error ftp-error) #f) (else (apply throw key args)))))) +(define* (search-path-environment-variables packages profile + #:optional (getenv getenv)) + "Return environment variable definitions that may be needed for the use of +PACKAGES in PROFILE. Use GETENV to determine the current settings and report +only settings not already effective." + + ;; The search path info is not stored in the manifest. Thus, we infer the + ;; search paths from same-named packages found in the distro. + + (define package-in-manifest->package + (match-lambda + ((name version _ ...) + (match (append (find-packages-by-name name version) + (find-packages-by-name name)) + ((p _ ...) p) + (_ #f))))) + + (define search-path-definition + (match-lambda + (($ <search-path-specification> variable directories separator) + (let ((values (or (and=> (getenv variable) + (cut string-tokenize* <> separator)) + '())) + (directories (filter file-exists? + (map (cut string-append profile + "/" <>) + directories)))) + (if (every (cut member <> values) directories) + #f + (format #f "export ~a=\"~a\"" + variable + (string-join directories separator))))))) + + (let* ((packages (filter-map package-in-manifest->package packages)) + (search-paths (delete-duplicates + (append-map package-native-search-paths + packages)))) + (filter-map search-path-definition search-paths))) + +(define (display-search-paths packages profile) + "Display the search path environment variables that may need to be set for +PACKAGES, in the context of PROFILE." + (let ((settings (search-path-environment-variables packages profile))) + (unless (null? settings) + (format #t (_ "The following environment variable definitions may be needed:~%")) + (format #t "~{ ~a~%~}" settings)))) + ;;; ;;; Command-line options. @@ -354,6 +401,8 @@ Install, remove, or upgrade PACKAGES in a single transaction.\n")) -u, --upgrade[=REGEXP] upgrade all the installed packages matching REGEXP")) (display (_ " --roll-back roll back to the previous generation")) + (display (_ " + --search-paths display needed environment variable definitions")) (newline) (display (_ " -p, --profile=PROFILE use PROFILE instead of the user's default profile")) @@ -408,6 +457,9 @@ Install, remove, or upgrade PACKAGES in a single transaction.\n")) (option '("roll-back") #f #f (lambda (opt name arg result) (alist-cons 'roll-back? #t result))) + (option '("search-paths") #f #f + (lambda (opt name arg result) + (cons `(query search-paths) result))) (option '(#\p "profile") #t #f (lambda (opt name arg result) (alist-cons 'profile arg @@ -728,7 +780,9 @@ Install, remove, or upgrade PACKAGES in a single transaction.\n")) (build-derivations (%store) (list prof-drv))) (begin (switch-symlinks name prof) - (switch-symlinks profile name)))))))))) + (switch-symlinks profile name) + (display-search-paths packages + profile)))))))))) (define (process-query opts) ;; Process any query specified by OPTS. Return #t when a query was @@ -776,6 +830,16 @@ Install, remove, or upgrade PACKAGES in a single transaction.\n")) (for-each (cute package->recutils <> (current-output-port)) (find-packages-by-description regexp)) #t)) + + (('search-paths) + (let* ((manifest (profile-manifest profile)) + (packages (manifest-packages manifest)) + (settings (search-path-environment-variables packages + profile + (const #f)))) + (format #t "~{~a~%~}" settings) + #t)) + (_ #f)))) (let ((opts (parse-options))) diff --git a/guix/utils.scm b/guix/utils.scm index aec07301da..7c8e914c01 100644 --- a/guix/utils.scm +++ b/guix/utils.scm @@ -61,6 +61,7 @@ version-compare version>? package-name->name+version + string-tokenize* file-extension call-with-temporary-output-file fold2 @@ -517,6 +518,33 @@ introduce the version part." (let ((dot (string-rindex file #\.))) (and dot (substring file (+ 1 dot) (string-length file))))) +(define (string-tokenize* string separator) + "Return the list of substrings of STRING separated by SEPARATOR. This is +like `string-tokenize', but SEPARATOR is a string." + (define (index string what) + (let loop ((string string) + (offset 0)) + (cond ((string-null? string) + #f) + ((string-prefix? what string) + offset) + (else + (loop (string-drop string 1) (+ 1 offset)))))) + + (define len + (string-length separator)) + + (let loop ((string string) + (result '())) + (cond ((index string separator) + => + (lambda (offset) + (loop (string-drop string (+ offset len)) + (cons (substring string 0 offset) + result)))) + (else + (reverse (cons string result)))))) + (define (call-with-temporary-output-file proc) "Call PROC with a name of a temporary file and open output port to that file; close the file and delete it when leaving the dynamic extent of this |