From a8f3424b25cbbc585126420bcc1434c4a2398588 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Mon, 2 May 2016 08:59:57 +0200 Subject: syscalls: Wrap TCSA* constants in 'tcsetattr-action' macro. * guix/build/syscalls.scm (tcsetattr-action): New macro. (TCSANOW, TCSADRAIN, TCSAFLUSH): Remove. (tcsetattr): Adjust docstring accordingly. * tests/syscalls.scm ("tcsetattr"): Adjust accordingly. --- tests/syscalls.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/syscalls.scm b/tests/syscalls.scm index ab1e13984d..0b73fb4b0c 100644 --- a/tests/syscalls.scm +++ b/tests/syscalls.scm @@ -281,7 +281,7 @@ (test-assert "tcsetattr" (let ((first (tcgetattr 0))) - (tcsetattr 0 TCSANOW first) + (tcsetattr 0 (tcsetattr-action TCSANOW) first) (equal? first (tcgetattr 0)))) (test-assert "terminal-window-size ENOTTY" -- cgit 1.4.1 From 4b6fa8b33970be414ae035f63ed80b147dcd8200 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Wed, 4 May 2016 17:02:15 +0200 Subject: utils: Remove 'split'. This procedure was redundant with SRFI-1's 'break'. * guix/utils.scm (split): Remove. * tests/utils.scm ("split, element is in list") ("split, element is not in list"): Remove. --- guix/utils.scm | 18 ------------------ tests/utils.scm | 14 -------------- 2 files changed, 32 deletions(-) (limited to 'tests') diff --git a/guix/utils.scm b/guix/utils.scm index 6c01edde21..725f4346c3 100644 --- a/guix/utils.scm +++ b/guix/utils.scm @@ -85,7 +85,6 @@ fold2 fold-tree fold-tree-leaves - split cache-directory readlink* edit-expression @@ -788,23 +787,6 @@ are connected to NODE in the tree, or '() or #f if NODE is a leaf node." (else result))) init children roots)) -(define (split lst e) - "Return two values, a list containing the elements of the list LST that -appear before the first occurence of the object E and a list containing the -elements after E." - (define (same? x) - (equal? e x)) - - (let loop ((rest lst) - (acc '())) - (match rest - (() - (values lst '())) - (((? same?) . tail) - (values (reverse acc) tail)) - ((head . tail) - (loop tail (cons head acc)))))) - (define (cache-directory) "Return the cache directory for Guix, by default ~/.cache/guix." (or (getenv "XDG_CONFIG_HOME") diff --git a/tests/utils.scm b/tests/utils.scm index d0ee02a1cf..854999f670 100644 --- a/tests/utils.scm +++ b/tests/utils.scm @@ -122,20 +122,6 @@ '(0 1 2 3))) list)) -(test-equal "split, element is in list" - '((foo) (baz)) - (call-with-values - (lambda () - (split '(foo bar baz) 'bar)) - list)) - -(test-equal "split, element is not in list" - '((foo bar baz) ()) - (call-with-values - (lambda () - (split '(foo bar baz) 'quux)) - list)) - (test-equal "strip-keyword-arguments" '(a #:b b #:c c) (strip-keyword-arguments '(#:foo #:bar #:baz) -- cgit 1.4.1 From 958dd3ce68733bcd5c1231424c7e4ad39e67594a Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Wed, 4 May 2016 17:35:47 +0200 Subject: utils: Move combinators to (guix combinators). * guix/utils.scm (compile-time-value, memoize, fold2) (fold-tree, fold-tree-leaves): Move to... * guix/combinators: ... here. New file. * tests/utils.scm ("fold2, 1 list", "fold2, 2 lists") (fold-tree tests): Move to... * tests/combinators.scm: ... here. New file. * Makefile.am (MODULES, SCM_TESTS): Add them. * gnu/packages.scm, gnu/packages/bootstrap.scm, gnu/services/herd.scm, guix/build-system/gnu.scm, guix/build-system/python.scm, guix/derivations.scm, guix/gnu-maintenance.scm, guix/import/elpa.scm, guix/scripts/archive.scm, guix/scripts/build.scm, guix/scripts/graph.scm, guix/scripts/lint.scm, guix/scripts/size.scm, guix/scripts/substitute.scm, guix/serialization.scm, guix/store.scm, guix/ui.scm: Adjust imports accordingly. --- Makefile.am | 2 + gnu/packages.scm | 1 + gnu/packages/bootstrap.scm | 3 +- gnu/services/herd.scm | 2 +- guix/build-system/gnu.scm | 1 + guix/build-system/python.scm | 1 + guix/combinators.scm | 116 +++++++++++++++++++++++++++++++++++++++++++ guix/derivations.scm | 1 + guix/gnu-maintenance.scm | 3 +- guix/import/elpa.scm | 4 +- guix/scripts/archive.scm | 1 + guix/scripts/build.scm | 1 + guix/scripts/graph.scm | 2 +- guix/scripts/lint.scm | 1 + guix/scripts/size.scm | 2 +- guix/scripts/substitute.scm | 1 + guix/serialization.scm | 4 +- guix/store.scm | 1 + guix/ui.scm | 1 + guix/utils.scm | 98 +++--------------------------------- tests/combinators.scm | 85 +++++++++++++++++++++++++++++++ tests/utils.scm | 56 --------------------- 22 files changed, 231 insertions(+), 156 deletions(-) create mode 100644 guix/combinators.scm create mode 100644 tests/combinators.scm (limited to 'tests') diff --git a/Makefile.am b/Makefile.am index d0c1826782..4685fe1650 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,6 +38,7 @@ MODULES = \ guix/hash.scm \ guix/pk-crypto.scm \ guix/pki.scm \ + guix/combinators.scm \ guix/utils.scm \ guix/sets.scm \ guix/download.scm \ @@ -231,6 +232,7 @@ SCM_TESTS = \ tests/ui.scm \ tests/records.scm \ tests/upstream.scm \ + tests/combinators.scm \ tests/utils.scm \ tests/build-utils.scm \ tests/packages.scm \ diff --git a/gnu/packages.scm b/gnu/packages.scm index 1e3f383cbc..7130f58fdd 100644 --- a/gnu/packages.scm +++ b/gnu/packages.scm @@ -24,6 +24,7 @@ #:use-module (guix packages) #:use-module (guix ui) #:use-module (guix utils) + #:use-module (guix combinators) #:use-module ((guix build utils) #:select ((package-name->name+version . hyphen-separated-name->name+version))) diff --git a/gnu/packages/bootstrap.scm b/gnu/packages/bootstrap.scm index a3cd18519c..6a4eba99ef 100644 --- a/gnu/packages/bootstrap.scm +++ b/gnu/packages/bootstrap.scm @@ -27,7 +27,8 @@ #:use-module (guix build-system trivial) #:use-module ((guix store) #:select (add-to-store add-text-to-store)) #:use-module ((guix derivations) #:select (derivation)) - #:use-module (guix utils) + #:use-module ((guix utils) #:select (gnu-triplet->nix-system)) + #:use-module (guix combinators) #:use-module (srfi srfi-1) #:use-module (srfi srfi-26) #:use-module (ice-9 match) diff --git a/gnu/services/herd.scm b/gnu/services/herd.scm index c06e98800e..7a9db90012 100644 --- a/gnu/services/herd.scm +++ b/gnu/services/herd.scm @@ -17,7 +17,7 @@ ;;; along with GNU Guix. If not, see . (define-module (gnu services herd) - #:use-module (guix utils) + #:use-module (guix combinators) #:use-module (srfi srfi-1) #:use-module (srfi srfi-11) #:use-module (srfi srfi-34) diff --git a/guix/build-system/gnu.scm b/guix/build-system/gnu.scm index a7d1952b57..f6df183da4 100644 --- a/guix/build-system/gnu.scm +++ b/guix/build-system/gnu.scm @@ -19,6 +19,7 @@ (define-module (guix build-system gnu) #:use-module (guix store) #:use-module (guix utils) + #:use-module (guix combinators) #:use-module (guix derivations) #:use-module (guix search-paths) #:use-module (guix build-system) diff --git a/guix/build-system/python.scm b/guix/build-system/python.scm index 326e6fd429..c3d6c62404 100644 --- a/guix/build-system/python.scm +++ b/guix/build-system/python.scm @@ -21,6 +21,7 @@ (define-module (guix build-system python) #:use-module (guix store) #:use-module (guix utils) + #:use-module (guix combinators) #:use-module (guix packages) #:use-module (guix derivations) #:use-module (guix search-paths) diff --git a/guix/combinators.scm b/guix/combinators.scm new file mode 100644 index 0000000000..9e4689ba9c --- /dev/null +++ b/guix/combinators.scm @@ -0,0 +1,116 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2012, 2013, 2014, 2015, 2016 Ludovic Courtès +;;; Copyright © 2014 Eric Bavier +;;; +;;; 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 . + +(define-module (guix combinators) + #:use-module (ice-9 match) + #:use-module (ice-9 vlist) + #:export (memoize + fold2 + fold-tree + fold-tree-leaves + compile-time-value)) + +;;; Commentary: +;;; +;;; This module provides useful combinators that complement SRFI-1 and +;;; friends. +;;; +;;; Code: + +(define (memoize proc) + "Return a memoizing version of PROC." + (let ((cache (make-hash-table))) + (lambda args + (let ((results (hash-ref cache args))) + (if results + (apply values results) + (let ((results (call-with-values (lambda () + (apply proc args)) + list))) + (hash-set! cache args results) + (apply values results))))))) + +(define fold2 + (case-lambda + ((proc seed1 seed2 lst) + "Like `fold', but with a single list and two seeds." + (let loop ((result1 seed1) + (result2 seed2) + (lst lst)) + (if (null? lst) + (values result1 result2) + (call-with-values + (lambda () (proc (car lst) result1 result2)) + (lambda (result1 result2) + (loop result1 result2 (cdr lst))))))) + ((proc seed1 seed2 lst1 lst2) + "Like `fold', but with a two lists and two seeds." + (let loop ((result1 seed1) + (result2 seed2) + (lst1 lst1) + (lst2 lst2)) + (if (or (null? lst1) (null? lst2)) + (values result1 result2) + (call-with-values + (lambda () (proc (car lst1) (car lst2) result1 result2)) + (lambda (result1 result2) + (fold2 proc result1 result2 (cdr lst1) (cdr lst2))))))))) + +(define (fold-tree proc init children roots) + "Call (PROC NODE RESULT) for each node in the tree that is reachable from +ROOTS, using INIT as the initial value of RESULT. The order in which nodes +are traversed is not specified, however, each node is visited only once, based +on an eq? check. Children of a node to be visited are generated by +calling (CHILDREN NODE), the result of which should be a list of nodes that +are connected to NODE in the tree, or '() or #f if NODE is a leaf node." + (let loop ((result init) + (seen vlist-null) + (lst roots)) + (match lst + (() result) + ((head . tail) + (if (not (vhash-assq head seen)) + (loop (proc head result) + (vhash-consq head #t seen) + (match (children head) + ((or () #f) tail) + (children (append tail children)))) + (loop result seen tail)))))) + +(define (fold-tree-leaves proc init children roots) + "Like fold-tree, but call (PROC NODE RESULT) only for leaf nodes." + (fold-tree + (lambda (node result) + (match (children node) + ((or () #f) (proc node result)) + (else result))) + init children roots)) + +(define-syntax compile-time-value ;not quite at home + (syntax-rules () + "Evaluate the given expression at compile time. The expression must +evaluate to a simple datum." + ((_ exp) + (let-syntax ((v (lambda (s) + (let ((val exp)) + (syntax-case s () + (_ #`'#,(datum->syntax s val))))))) + v)))) + +;;; combinators.scm ends here diff --git a/guix/derivations.scm b/guix/derivations.scm index 2d8584e72d..d4f697477b 100644 --- a/guix/derivations.scm +++ b/guix/derivations.scm @@ -30,6 +30,7 @@ #:use-module (ice-9 vlist) #:use-module (guix store) #:use-module (guix utils) + #:use-module (guix combinators) #:use-module (guix monads) #:use-module (guix hash) #:use-module (guix base32) diff --git a/guix/gnu-maintenance.scm b/guix/gnu-maintenance.scm index 8021d99c8b..adb62aa68c 100644 --- a/guix/gnu-maintenance.scm +++ b/guix/gnu-maintenance.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2010, 2011, 2012, 2013, 2014, 2015 Ludovic Courtès +;;; Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016 Ludovic Courtès ;;; Copyright © 2012, 2013 Nikita Karetnikov ;;; ;;; This file is part of GNU Guix. @@ -30,6 +30,7 @@ #:use-module (guix http-client) #:use-module (guix ftp-client) #:use-module (guix utils) + #:use-module (guix combinators) #:use-module (guix records) #:use-module (guix upstream) #:use-module (guix packages) diff --git a/guix/import/elpa.scm b/guix/import/elpa.scm index ccc4063a53..320a09e8c6 100644 --- a/guix/import/elpa.scm +++ b/guix/import/elpa.scm @@ -35,8 +35,8 @@ #:use-module (guix base32) #:use-module (guix upstream) #:use-module (guix packages) - #:use-module ((guix utils) #:select (call-with-temporary-output-file - memoize)) + #:use-module ((guix combinators) #:select (memoize)) + #:use-module ((guix utils) #:select (call-with-temporary-output-file)) #:export (elpa->guix-package %elpa-updater)) diff --git a/guix/scripts/archive.scm b/guix/scripts/archive.scm index 3fb210ee91..e06c38aaab 100644 --- a/guix/scripts/archive.scm +++ b/guix/scripts/archive.scm @@ -19,6 +19,7 @@ (define-module (guix scripts archive) #:use-module (guix config) #:use-module (guix utils) + #:use-module (guix combinators) #:use-module ((guix build utils) #:select (mkdir-p)) #:use-module ((guix serialization) #:select (restore-file)) #:use-module (guix store) diff --git a/guix/scripts/build.scm b/guix/scripts/build.scm index 9a6b427fc5..320ec39be2 100644 --- a/guix/scripts/build.scm +++ b/guix/scripts/build.scm @@ -24,6 +24,7 @@ #:use-module (guix derivations) #:use-module (guix packages) #:use-module (guix grafts) + #:use-module (guix combinators) ;; Use the procedure that destructures "NAME-VERSION" forms. #:use-module ((guix utils) #:hide (package-name->name+version)) diff --git a/guix/scripts/graph.scm b/guix/scripts/graph.scm index b0d7c08582..ba63780e2b 100644 --- a/guix/scripts/graph.scm +++ b/guix/scripts/graph.scm @@ -21,7 +21,7 @@ #:use-module (guix graph) #:use-module (guix grafts) #:use-module (guix scripts) - #:use-module (guix utils) + #:use-module (guix combinators) #:use-module (guix packages) #:use-module (guix monads) #:use-module (guix store) diff --git a/guix/scripts/lint.scm b/guix/scripts/lint.scm index c581586ac3..06001d3eae 100644 --- a/guix/scripts/lint.scm +++ b/guix/scripts/lint.scm @@ -31,6 +31,7 @@ #:use-module (guix records) #:use-module (guix ui) #:use-module (guix utils) + #:use-module (guix combinators) #:use-module (guix scripts) #:use-module (guix gnu-maintenance) #:use-module (guix monads) diff --git a/guix/scripts/size.scm b/guix/scripts/size.scm index 8f0cb7decd..be1e8ca087 100644 --- a/guix/scripts/size.scm +++ b/guix/scripts/size.scm @@ -21,7 +21,7 @@ #:use-module (guix scripts) #:use-module (guix store) #:use-module (guix monads) - #:use-module (guix utils) + #:use-module (guix combinators) #:use-module (guix grafts) #:use-module (guix packages) #:use-module (guix derivations) diff --git a/guix/scripts/substitute.scm b/guix/scripts/substitute.scm index 1cfab81dbd..d46d610347 100755 --- a/guix/scripts/substitute.scm +++ b/guix/scripts/substitute.scm @@ -21,6 +21,7 @@ #:use-module (guix ui) #:use-module ((guix store) #:hide (close-connection)) #:use-module (guix utils) + #:use-module (guix combinators) #:use-module (guix config) #:use-module (guix records) #:use-module (guix serialization) diff --git a/guix/serialization.scm b/guix/serialization.scm index 7a3defc03d..286b4cbf30 100644 --- a/guix/serialization.scm +++ b/guix/serialization.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2012, 2013, 2014, 2015 Ludovic Courtès +;;; Copyright © 2012, 2013, 2014, 2015, 2016 Ludovic Courtès ;;; ;;; This file is part of GNU Guix. ;;; @@ -17,7 +17,7 @@ ;;; along with GNU Guix. If not, see . (define-module (guix serialization) - #:use-module (guix utils) + #:use-module (guix combinators) #:use-module (rnrs bytevectors) #:use-module (rnrs io ports) #:use-module (srfi srfi-1) diff --git a/guix/store.scm b/guix/store.scm index 8d1099dab2..f352a99cbd 100644 --- a/guix/store.scm +++ b/guix/store.scm @@ -19,6 +19,7 @@ (define-module (guix store) #:use-module (guix utils) #:use-module (guix config) + #:use-module (guix combinators) #:use-module (guix serialization) #:use-module (guix monads) #:autoload (guix base32) (bytevector->base32-string) diff --git a/guix/ui.scm b/guix/ui.scm index 04ac43723e..8310974ac7 100644 --- a/guix/ui.scm +++ b/guix/ui.scm @@ -30,6 +30,7 @@ #:use-module (guix packages) #:use-module (guix profiles) #:use-module (guix derivations) + #:use-module (guix combinators) #:use-module (guix build-system) #:use-module (guix serialization) #:use-module ((guix build utils) #:select (mkdir-p)) diff --git a/guix/utils.scm b/guix/utils.scm index 725f4346c3..f18bbd19ac 100644 --- a/guix/utils.scm +++ b/guix/utils.scm @@ -32,6 +32,7 @@ #:use-module (rnrs bytevectors) #:use-module (rnrs io ports) #:use-module ((rnrs bytevectors) #:select (bytevector-u8-set!)) + #:use-module (guix combinators) #:use-module ((guix build utils) #:select (dump-port)) #:use-module ((guix build syscalls) #:select (errno mkdtemp!)) #:use-module (ice-9 vlist) @@ -46,9 +47,7 @@ #:export (bytevector->base16-string base16-string->bytevector - compile-time-value fcntl-flock - memoize strip-keyword-arguments default-keyword-arguments substitute-keyword-arguments @@ -82,9 +81,6 @@ call-with-temporary-output-file call-with-temporary-directory with-atomic-file-output - fold2 - fold-tree - fold-tree-leaves cache-directory readlink* edit-expression @@ -97,22 +93,6 @@ call-with-compressed-output-port canonical-newline-port)) - -;;; -;;; Compile-time computations. -;;; - -(define-syntax compile-time-value - (syntax-rules () - "Evaluate the given expression at compile time. The expression must -evaluate to a simple datum." - ((_ exp) - (let-syntax ((v (lambda (s) - (let ((val exp)) - (syntax-case s () - (_ #`'#,(datum->syntax s val))))))) - v)))) - ;;; ;;; Base 16. @@ -432,22 +412,9 @@ exception if it's already taken." ;;; -;;; Miscellaneous. +;;; Keyword arguments. ;;; -(define (memoize proc) - "Return a memoizing version of PROC." - (let ((cache (make-hash-table))) - (lambda args - (let ((results (hash-ref cache args))) - (if results - (apply values results) - (let ((results (call-with-values (lambda () - (apply proc args)) - list))) - (hash-set! cache args results) - (apply values results))))))) - (define (strip-keyword-arguments keywords args) "Remove all of the keyword arguments listed in KEYWORDS from ARGS." (let loop ((args args) @@ -533,6 +500,11 @@ For instance: (#f (loop rest kw/values (cons* value kw result)))))))) + +;;; +;;; System strings. +;;; + (define* (nix-system->gnu-triplet #:optional (system (%current-system)) (vendor "unknown")) "Return a guess of the GNU triplet corresponding to Nix system @@ -731,62 +703,6 @@ output port, and PROC's result is returned." (lambda (key . args) (false-if-exception (delete-file template)))))) -(define fold2 - (case-lambda - ((proc seed1 seed2 lst) - "Like `fold', but with a single list and two seeds." - (let loop ((result1 seed1) - (result2 seed2) - (lst lst)) - (if (null? lst) - (values result1 result2) - (call-with-values - (lambda () (proc (car lst) result1 result2)) - (lambda (result1 result2) - (loop result1 result2 (cdr lst))))))) - ((proc seed1 seed2 lst1 lst2) - "Like `fold', but with a two lists and two seeds." - (let loop ((result1 seed1) - (result2 seed2) - (lst1 lst1) - (lst2 lst2)) - (if (or (null? lst1) (null? lst2)) - (values result1 result2) - (call-with-values - (lambda () (proc (car lst1) (car lst2) result1 result2)) - (lambda (result1 result2) - (fold2 proc result1 result2 (cdr lst1) (cdr lst2))))))))) - -(define (fold-tree proc init children roots) - "Call (PROC NODE RESULT) for each node in the tree that is reachable from -ROOTS, using INIT as the initial value of RESULT. The order in which nodes -are traversed is not specified, however, each node is visited only once, based -on an eq? check. Children of a node to be visited are generated by -calling (CHILDREN NODE), the result of which should be a list of nodes that -are connected to NODE in the tree, or '() or #f if NODE is a leaf node." - (let loop ((result init) - (seen vlist-null) - (lst roots)) - (match lst - (() result) - ((head . tail) - (if (not (vhash-assq head seen)) - (loop (proc head result) - (vhash-consq head #t seen) - (match (children head) - ((or () #f) tail) - (children (append tail children)))) - (loop result seen tail)))))) - -(define (fold-tree-leaves proc init children roots) - "Like fold-tree, but call (PROC NODE RESULT) only for leaf nodes." - (fold-tree - (lambda (node result) - (match (children node) - ((or () #f) (proc node result)) - (else result))) - init children roots)) - (define (cache-directory) "Return the cache directory for Guix, by default ~/.cache/guix." (or (getenv "XDG_CONFIG_HOME") diff --git a/tests/combinators.scm b/tests/combinators.scm new file mode 100644 index 0000000000..1e4bb236b7 --- /dev/null +++ b/tests/combinators.scm @@ -0,0 +1,85 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2012, 2013, 2014, 2015, 2016 Ludovic Courtès +;;; Copyright © 2014 Eric Bavier +;;; +;;; 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 . + +(define-module (test-combinators) + #:use-module (guix combinators) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-64) + #:use-module (ice-9 vlist)) + +(test-begin "combinators") + +(test-equal "fold2, 1 list" + (list (reverse (iota 5)) + (map - (reverse (iota 5)))) + (call-with-values + (lambda () + (fold2 (lambda (i r1 r2) + (values (cons i r1) + (cons (- i) r2))) + '() '() + (iota 5))) + list)) + +(test-equal "fold2, 2 lists" + (list (reverse '((a . 0) (b . 1) (c . 2) (d . 3))) + (reverse '((a . 0) (b . -1) (c . -2) (d . -3)))) + (call-with-values + (lambda () + (fold2 (lambda (k v r1 r2) + (values (alist-cons k v r1) + (alist-cons k (- v) r2))) + '() '() + '(a b c d) + '(0 1 2 3))) + list)) + +(let* ((tree (alist->vhash + '((0 2 3) (1 3 4) (2) (3 5 6) (4 6) (5) (6)) + hashq)) + (add-one (lambda (_ r) (1+ r))) + (tree-lookup (lambda (n) (cdr (vhash-assq n tree))))) + (test-equal "fold-tree, single root" + 5 (fold-tree add-one 0 tree-lookup '(0))) + (test-equal "fold-tree, two roots" + 7 (fold-tree add-one 0 tree-lookup '(0 1))) + (test-equal "fold-tree, sum" + 16 (fold-tree + 0 tree-lookup '(0))) + (test-equal "fold-tree, internal" + 18 (fold-tree + 0 tree-lookup '(3 4))) + (test-equal "fold-tree, cons" + '(1 3 4 5 6) + (sort (fold-tree cons '() tree-lookup '(1)) <)) + (test-equal "fold-tree, overlapping paths" + '(1 3 4 5 6) + (sort (fold-tree cons '() tree-lookup '(1 4)) <)) + (test-equal "fold-tree, cons, two roots" + '(0 2 3 4 5 6) + (sort (fold-tree cons '() tree-lookup '(0 4)) <)) + (test-equal "fold-tree-leaves, single root" + 2 (fold-tree-leaves add-one 0 tree-lookup '(1))) + (test-equal "fold-tree-leaves, single root, sum" + 11 (fold-tree-leaves + 0 tree-lookup '(1))) + (test-equal "fold-tree-leaves, two roots" + 3 (fold-tree-leaves add-one 0 tree-lookup '(0 1))) + (test-equal "fold-tree-leaves, two roots, sum" + 13 (fold-tree-leaves + 0 tree-lookup '(0 1)))) + +(test-end) + diff --git a/tests/utils.scm b/tests/utils.scm index 854999f670..a54482e94c 100644 --- a/tests/utils.scm +++ b/tests/utils.scm @@ -97,31 +97,6 @@ (string-replace-substring "/nix/store/chbouib" "/nix/" "/gnu/") (string-replace-substring "" "foo" "bar"))) -(test-equal "fold2, 1 list" - (list (reverse (iota 5)) - (map - (reverse (iota 5)))) - (call-with-values - (lambda () - (fold2 (lambda (i r1 r2) - (values (cons i r1) - (cons (- i) r2))) - '() '() - (iota 5))) - list)) - -(test-equal "fold2, 2 lists" - (list (reverse '((a . 0) (b . 1) (c . 2) (d . 3))) - (reverse '((a . 0) (b . -1) (c . -2) (d . -3)))) - (call-with-values - (lambda () - (fold2 (lambda (k v r1 r2) - (values (alist-cons k v r1) - (alist-cons k (- v) r2))) - '() '() - '(a b c d) - '(0 1 2 3))) - list)) - (test-equal "strip-keyword-arguments" '(a #:b b #:c c) (strip-keyword-arguments '(#:foo #:bar #:baz) @@ -136,37 +111,6 @@ (ensure-keyword-arguments '(#:foo 2) '(#:bar 3)) (ensure-keyword-arguments '(#:foo 2) '(#:bar 3 #:foo 42)))) -(let* ((tree (alist->vhash - '((0 2 3) (1 3 4) (2) (3 5 6) (4 6) (5) (6)) - hashq)) - (add-one (lambda (_ r) (1+ r))) - (tree-lookup (lambda (n) (cdr (vhash-assq n tree))))) - (test-equal "fold-tree, single root" - 5 (fold-tree add-one 0 tree-lookup '(0))) - (test-equal "fold-tree, two roots" - 7 (fold-tree add-one 0 tree-lookup '(0 1))) - (test-equal "fold-tree, sum" - 16 (fold-tree + 0 tree-lookup '(0))) - (test-equal "fold-tree, internal" - 18 (fold-tree + 0 tree-lookup '(3 4))) - (test-equal "fold-tree, cons" - '(1 3 4 5 6) - (sort (fold-tree cons '() tree-lookup '(1)) <)) - (test-equal "fold-tree, overlapping paths" - '(1 3 4 5 6) - (sort (fold-tree cons '() tree-lookup '(1 4)) <)) - (test-equal "fold-tree, cons, two roots" - '(0 2 3 4 5 6) - (sort (fold-tree cons '() tree-lookup '(0 4)) <)) - (test-equal "fold-tree-leaves, single root" - 2 (fold-tree-leaves add-one 0 tree-lookup '(1))) - (test-equal "fold-tree-leaves, single root, sum" - 11 (fold-tree-leaves + 0 tree-lookup '(1))) - (test-equal "fold-tree-leaves, two roots" - 3 (fold-tree-leaves add-one 0 tree-lookup '(0 1))) - (test-equal "fold-tree-leaves, two roots, sum" - 13 (fold-tree-leaves + 0 tree-lookup '(0 1)))) - (test-assert "filtered-port, file" (let* ((file (search-path %load-path "guix.scm")) (input (open-file file "r0b"))) -- cgit 1.4.1 From 4e0ea3eb288c2143b44bf324c64047762c72d3b3 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Fri, 6 May 2016 13:12:45 +0200 Subject: utils: Move 'fcntl-flock' to (guix build syscalls). * guix/utils.scm (%struct-flock, F_SETLKW, F_SETLK, F_xxLCK) (fcntl-flock): Move to... * guix/build/syscalls.scm: ... here. New variables. * guix/nar.scm: Adjust imports accordingly. * tests/utils.scm ("fcntl-flock wait", "fcntl-flock non-blocking"): Move to... * tests/syscalls.scm: ... here. New tests. (temp-file): New variable. --- guix/build/syscalls.scm | 69 ++++++++++++++++++++++++++++++++++++++ guix/nar.scm | 4 +-- guix/utils.scm | 75 +---------------------------------------- tests/syscalls.scm | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/utils.scm | 82 --------------------------------------------- 5 files changed, 160 insertions(+), 158 deletions(-) (limited to 'tests') diff --git a/guix/build/syscalls.scm b/guix/build/syscalls.scm index a9cd6e93c8..86723c23c7 100644 --- a/guix/build/syscalls.scm +++ b/guix/build/syscalls.scm @@ -65,6 +65,7 @@ processes mkdtemp! pivot-root + fcntl-flock CLONE_CHILD_CLEARTID CLONE_CHILD_SETTID @@ -637,6 +638,74 @@ system to PUT-OLD." (list new-root put-old (strerror err)) (list err))))))) + +;;; +;;; Advisory file locking. +;;; + +(define %struct-flock + ;; 'struct flock' from . + (list short ; l_type + short ; l_whence + size_t ; l_start + size_t ; l_len + int)) ; l_pid + +(define F_SETLKW + ;; On Linux-based systems, this is usually 7, but not always + ;; (exceptions include SPARC.) On GNU/Hurd, it's 9. + (cond ((string-contains %host-type "sparc") 9) ; sparc-*-linux-gnu + ((string-contains %host-type "linux") 7) ; *-linux-gnu + (else 9))) ; *-gnu* + +(define F_SETLK + ;; Likewise: GNU/Hurd and SPARC use 8, while the others typically use 6. + (cond ((string-contains %host-type "sparc") 8) ; sparc-*-linux-gnu + ((string-contains %host-type "linux") 6) ; *-linux-gnu + (else 8))) ; *-gnu* + +(define F_xxLCK + ;; The F_RDLCK, F_WRLCK, and F_UNLCK constants. + (cond ((string-contains %host-type "sparc") #(1 2 3)) ; sparc-*-linux-gnu + ((string-contains %host-type "hppa") #(1 2 3)) ; hppa-*-linux-gnu + ((string-contains %host-type "linux") #(0 1 2)) ; *-linux-gnu + (else #(1 2 3)))) ; *-gnu* + +(define fcntl-flock + (let ((proc (syscall->procedure int "fcntl" `(,int ,int *)))) + (lambda* (fd-or-port operation #:key (wait? #t)) + "Perform locking OPERATION on the file beneath FD-OR-PORT. OPERATION +must be a symbol, one of 'read-lock, 'write-lock, or 'unlock. When WAIT? is +true, block until the lock is acquired; otherwise, thrown an 'flock-error' +exception if it's already taken." + (define (operation->int op) + (case op + ((read-lock) (vector-ref F_xxLCK 0)) + ((write-lock) (vector-ref F_xxLCK 1)) + ((unlock) (vector-ref F_xxLCK 2)) + (else (error "invalid fcntl-flock operation" op)))) + + (define fd + (if (port? fd-or-port) + (fileno fd-or-port) + fd-or-port)) + + ;; XXX: 'fcntl' is a vararg function, but here we happily use the + ;; standard ABI; crossing fingers. + (let ((err (proc fd + (if wait? + F_SETLKW ; lock & wait + F_SETLK) ; non-blocking attempt + (make-c-struct %struct-flock + (list (operation->int operation) + SEEK_SET + 0 0 ; whole file + 0))))) + (or (zero? err) + + ;; Presumably we got EAGAIN or so. + (throw 'flock-error (errno))))))) + ;;; ;;; Network interfaces. diff --git a/guix/nar.scm b/guix/nar.scm index 43e5210752..739d3d3a57 100644 --- a/guix/nar.scm +++ b/guix/nar.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2012, 2013, 2014, 2015 Ludovic Courtès +;;; Copyright © 2012, 2013, 2014, 2015, 2016 Ludovic Courtès ;;; Copyright © 2014 Mark H Weaver ;;; ;;; This file is part of GNU Guix. @@ -18,8 +18,8 @@ ;;; along with GNU Guix. If not, see . (define-module (guix nar) - #:use-module (guix utils) #:use-module (guix serialization) + #:use-module (guix build syscalls) #:use-module ((guix build utils) #:select (delete-file-recursively with-directory-excursion)) #:use-module (guix store) diff --git a/guix/utils.scm b/guix/utils.scm index f18bbd19ac..d924e434bd 100644 --- a/guix/utils.scm +++ b/guix/utils.scm @@ -34,7 +34,7 @@ #:use-module ((rnrs bytevectors) #:select (bytevector-u8-set!)) #:use-module (guix combinators) #:use-module ((guix build utils) #:select (dump-port)) - #:use-module ((guix build syscalls) #:select (errno mkdtemp!)) + #:use-module ((guix build syscalls) #:select (mkdtemp!)) #:use-module (ice-9 vlist) #:use-module (ice-9 format) #:autoload (ice-9 popen) (open-pipe*) @@ -47,7 +47,6 @@ #:export (bytevector->base16-string base16-string->bytevector - fcntl-flock strip-keyword-arguments default-keyword-arguments substitute-keyword-arguments @@ -338,78 +337,6 @@ This procedure returns #t on success." (put-bytevector out post-bv)) #t)))))) - -;;; -;;; Advisory file locking. -;;; - -(define %struct-flock - ;; 'struct flock' from . - (list short ; l_type - short ; l_whence - size_t ; l_start - size_t ; l_len - int)) ; l_pid - -(define F_SETLKW - ;; On Linux-based systems, this is usually 7, but not always - ;; (exceptions include SPARC.) On GNU/Hurd, it's 9. - (compile-time-value - (cond ((string-contains %host-type "sparc") 9) ; sparc-*-linux-gnu - ((string-contains %host-type "linux") 7) ; *-linux-gnu - (else 9)))) ; *-gnu* - -(define F_SETLK - ;; Likewise: GNU/Hurd and SPARC use 8, while the others typically use 6. - (compile-time-value - (cond ((string-contains %host-type "sparc") 8) ; sparc-*-linux-gnu - ((string-contains %host-type "linux") 6) ; *-linux-gnu - (else 8)))) ; *-gnu* - -(define F_xxLCK - ;; The F_RDLCK, F_WRLCK, and F_UNLCK constants. - (compile-time-value - (cond ((string-contains %host-type "sparc") #(1 2 3)) ; sparc-*-linux-gnu - ((string-contains %host-type "hppa") #(1 2 3)) ; hppa-*-linux-gnu - ((string-contains %host-type "linux") #(0 1 2)) ; *-linux-gnu - (else #(1 2 3))))) ; *-gnu* - -(define fcntl-flock - (let* ((ptr (dynamic-func "fcntl" (dynamic-link))) - (proc (pointer->procedure int ptr `(,int ,int *)))) - (lambda* (fd-or-port operation #:key (wait? #t)) - "Perform locking OPERATION on the file beneath FD-OR-PORT. OPERATION -must be a symbol, one of 'read-lock, 'write-lock, or 'unlock. When WAIT? is -true, block until the lock is acquired; otherwise, thrown an 'flock-error' -exception if it's already taken." - (define (operation->int op) - (case op - ((read-lock) (vector-ref F_xxLCK 0)) - ((write-lock) (vector-ref F_xxLCK 1)) - ((unlock) (vector-ref F_xxLCK 2)) - (else (error "invalid fcntl-flock operation" op)))) - - (define fd - (if (port? fd-or-port) - (fileno fd-or-port) - fd-or-port)) - - ;; XXX: 'fcntl' is a vararg function, but here we happily use the - ;; standard ABI; crossing fingers. - (let ((err (proc fd - (if wait? - F_SETLKW ; lock & wait - F_SETLK) ; non-blocking attempt - (make-c-struct %struct-flock - (list (operation->int operation) - SEEK_SET - 0 0 ; whole file - 0))))) - (or (zero? err) - - ;; Presumably we got EAGAIN or so. - (throw 'flock-error (errno))))))) - ;;; ;;; Keyword arguments. diff --git a/tests/syscalls.scm b/tests/syscalls.scm index 0b73fb4b0c..73fa8a7acf 100644 --- a/tests/syscalls.scm +++ b/tests/syscalls.scm @@ -29,6 +29,10 @@ ;; Test the (guix build syscalls) module, although there's not much that can ;; actually be tested without being root. +(define temp-file + (string-append "t-utils-" (number->string (getpid)))) + + (test-begin "syscalls") (test-equal "mount, ENOENT" @@ -172,6 +176,88 @@ (status:exit-val status)))) (eq? #t result)))))))) +(false-if-exception (delete-file temp-file)) +(test-equal "fcntl-flock wait" + 42 ; the child's exit status + (let ((file (open-file temp-file "w0b"))) + ;; Acquire an exclusive lock. + (fcntl-flock file 'write-lock) + (match (primitive-fork) + (0 + (dynamic-wind + (const #t) + (lambda () + ;; Reopen FILE read-only so we can have a read lock. + (let ((file (open-file temp-file "r0b"))) + ;; Wait until we can acquire the lock. + (fcntl-flock file 'read-lock) + (primitive-exit (read file))) + (primitive-exit 1)) + (lambda () + (primitive-exit 2)))) + (pid + ;; Write garbage and wait. + (display "hello, world!" file) + (force-output file) + (sleep 1) + + ;; Write the real answer. + (seek file 0 SEEK_SET) + (truncate-file file 0) + (write 42 file) + (force-output file) + + ;; Unlock, which should let the child continue. + (fcntl-flock file 'unlock) + + (match (waitpid pid) + ((_ . status) + (let ((result (status:exit-val status))) + (close-port file) + result))))))) + +(test-equal "fcntl-flock non-blocking" + EAGAIN ; the child's exit status + (match (pipe) + ((input . output) + (match (primitive-fork) + (0 + (dynamic-wind + (const #t) + (lambda () + (close-port output) + + ;; Wait for the green light. + (read-char input) + + ;; Open FILE read-only so we can have a read lock. + (let ((file (open-file temp-file "w0"))) + (catch 'flock-error + (lambda () + ;; This attempt should throw EAGAIN. + (fcntl-flock file 'write-lock #:wait? #f)) + (lambda (key errno) + (primitive-exit (pk 'errno errno))))) + (primitive-exit -1)) + (lambda () + (primitive-exit -2)))) + (pid + (close-port input) + (let ((file (open-file temp-file "w0"))) + ;; Acquire an exclusive lock. + (fcntl-flock file 'write-lock) + + ;; Tell the child to continue. + (write 'green-light output) + (force-output output) + + (match (waitpid pid) + ((_ . status) + (let ((result (status:exit-val status))) + (fcntl-flock file 'unlock) + (close-port file) + result))))))))) + (test-assert "all-network-interface-names" (match (all-network-interface-names) (((? string? names) ..1) @@ -303,3 +389,5 @@ 0)) (test-end) + +(false-if-exception (delete-file temp-file)) diff --git a/tests/utils.scm b/tests/utils.scm index a54482e94c..6590ed91cf 100644 --- a/tests/utils.scm +++ b/tests/utils.scm @@ -168,88 +168,6 @@ (call-with-decompressed-port 'xz (open-file temp-file "r0b") get-bytevector-all)))) -(false-if-exception (delete-file temp-file)) -(test-equal "fcntl-flock wait" - 42 ; the child's exit status - (let ((file (open-file temp-file "w0b"))) - ;; Acquire an exclusive lock. - (fcntl-flock file 'write-lock) - (match (primitive-fork) - (0 - (dynamic-wind - (const #t) - (lambda () - ;; Reopen FILE read-only so we can have a read lock. - (let ((file (open-file temp-file "r0b"))) - ;; Wait until we can acquire the lock. - (fcntl-flock file 'read-lock) - (primitive-exit (read file))) - (primitive-exit 1)) - (lambda () - (primitive-exit 2)))) - (pid - ;; Write garbage and wait. - (display "hello, world!" file) - (force-output file) - (sleep 1) - - ;; Write the real answer. - (seek file 0 SEEK_SET) - (truncate-file file 0) - (write 42 file) - (force-output file) - - ;; Unlock, which should let the child continue. - (fcntl-flock file 'unlock) - - (match (waitpid pid) - ((_ . status) - (let ((result (status:exit-val status))) - (close-port file) - result))))))) - -(test-equal "fcntl-flock non-blocking" - EAGAIN ; the child's exit status - (match (pipe) - ((input . output) - (match (primitive-fork) - (0 - (dynamic-wind - (const #t) - (lambda () - (close-port output) - - ;; Wait for the green light. - (read-char input) - - ;; Open FILE read-only so we can have a read lock. - (let ((file (open-file temp-file "w0"))) - (catch 'flock-error - (lambda () - ;; This attempt should throw EAGAIN. - (fcntl-flock file 'write-lock #:wait? #f)) - (lambda (key errno) - (primitive-exit (pk 'errno errno))))) - (primitive-exit -1)) - (lambda () - (primitive-exit -2)))) - (pid - (close-port input) - (let ((file (open-file temp-file "w0"))) - ;; Acquire an exclusive lock. - (fcntl-flock file 'write-lock) - - ;; Tell the child to continue. - (write 'green-light output) - (force-output output) - - (match (waitpid pid) - ((_ . status) - (let ((result (status:exit-val status))) - (fcntl-flock file 'unlock) - (close-port file) - result))))))))) - ;; This is actually in (guix store). (test-equal "store-path-package-name" "bash-4.2-p24" -- cgit 1.4.1