diff options
| author | Ludovic Courtès <ludo@gnu.org> | 2013-01-09 22:09:58 +0100 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2013-01-09 22:10:06 +0100 |
| commit | b2d58cd80a04ccab09a947d187ae55ff199eae08 (patch) | |
| tree | 0fc47cbad22bb432f4eee5db386e5bacbab52ca1 | |
| parent | b9e5c0a949fa627da55ea53fd71dfa96ad8a2b4b (diff) | |
union: Detect collisions, and delete duplicate leaves.
* guix/build/union.scm (delete-duplicate-leaves): New procedure.
(union-build)[leaf=?, resolve-collision]: New procedures.
Use `delete-duplicate-leaves' on the result of `tree-union'.
* tests/union.scm ("delete-duplicate-leaves, default",
"delete-duplicate-leaves, file names"): New tests.
| -rw-r--r-- | guix/build/union.scm | 66 | ||||
| -rw-r--r-- | tests/union.scm | 21 |
2 files changed, 83 insertions, 4 deletions
diff --git a/guix/build/union.scm b/guix/build/union.scm index ffd367917ae..d1578a6ef53 100644 --- a/guix/build/union.scm +++ b/guix/build/union.scm | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | ;;; Guix --- Nix package management from Guile. -*- coding: utf-8 -*- | 1 | ;;; Guix --- Nix package management from Guile. -*- coding: utf-8 -*- |
| 2 | ;;; Copyright (C) 2012 Ludovic Courtès <ludo@gnu.org> | 2 | ;;; Copyright (C) 2012, 2013 Ludovic Courtès <ludo@gnu.org> |
| 3 | ;;; | 3 | ;;; |
| 4 | ;;; This file is part of Guix. | 4 | ;;; This file is part of Guix. |
| 5 | ;;; | 5 | ;;; |
| @@ -19,9 +19,11 @@ | |||
| 19 | (define-module (guix build union) | 19 | (define-module (guix build union) |
| 20 | #:use-module (ice-9 ftw) | 20 | #:use-module (ice-9 ftw) |
| 21 | #:use-module (ice-9 match) | 21 | #:use-module (ice-9 match) |
| 22 | #:use-module (ice-9 format) | ||
| 22 | #:use-module (srfi srfi-1) | 23 | #:use-module (srfi srfi-1) |
| 23 | #:use-module (srfi srfi-26) | 24 | #:use-module (srfi srfi-26) |
| 24 | #:export (tree-union | 25 | #:export (tree-union |
| 26 | delete-duplicate-leaves | ||
| 25 | union-build)) | 27 | union-build)) |
| 26 | 28 | ||
| 27 | ;;; Commentary: | 29 | ;;; Commentary: |
| @@ -56,6 +58,48 @@ itself a tree. " | |||
| 56 | '() | 58 | '() |
| 57 | (delete-duplicates (map car dirs))))))))) | 59 | (delete-duplicates (map car dirs))))))))) |
| 58 | 60 | ||
| 61 | (define* (delete-duplicate-leaves tree | ||
| 62 | #:optional | ||
| 63 | (leaf=? equal?) | ||
| 64 | (delete-duplicates (match-lambda | ||
| 65 | ((head _ ...) head)))) | ||
| 66 | "Delete duplicate leaves from TREE. Two leaves are considered equal | ||
| 67 | when LEAF=? applied to them returns #t. Each collision (list of leaves | ||
| 68 | that are LEAF=?) is passed to DELETE-DUPLICATES, which must return a | ||
| 69 | single leaf." | ||
| 70 | (let loop ((tree tree)) | ||
| 71 | (match tree | ||
| 72 | ((dir children ...) | ||
| 73 | (let ((dirs (filter pair? children)) | ||
| 74 | (leaves (remove pair? children))) | ||
| 75 | (define collisions | ||
| 76 | (fold (lambda (leaf result) | ||
| 77 | (define same? | ||
| 78 | (cut leaf=? leaf <>)) | ||
| 79 | |||
| 80 | (if (any (cut find same? <>) result) | ||
| 81 | result | ||
| 82 | (match (filter same? leaves) | ||
| 83 | ((_) | ||
| 84 | result) | ||
| 85 | ((collision ...) | ||
| 86 | (cons collision result))))) | ||
| 87 | '() | ||
| 88 | leaves)) | ||
| 89 | |||
| 90 | (define non-collisions | ||
| 91 | (filter (lambda (leaf) | ||
| 92 | (match (filter (cut leaf=? leaf <>) leaves) | ||
| 93 | ((_) #t) | ||
| 94 | ((_ _ ..1) #f))) | ||
| 95 | leaves)) | ||
| 96 | |||
| 97 | `(,dir | ||
| 98 | ,@non-collisions | ||
| 99 | ,@(map delete-duplicates collisions) | ||
| 100 | ,@(map loop dirs)))) | ||
| 101 | (leaf leaf)))) | ||
| 102 | |||
| 59 | (define* (union-build output directories) | 103 | (define* (union-build output directories) |
| 60 | "Build in the OUTPUT directory a symlink tree that is the union of all | 104 | "Build in the OUTPUT directory a symlink tree that is the union of all |
| 61 | the DIRECTORIES." | 105 | the DIRECTORIES." |
| @@ -88,12 +132,28 @@ the DIRECTORIES." | |||
| 88 | (((? string?) leaves ...) | 132 | (((? string?) leaves ...) |
| 89 | leaves))) | 133 | leaves))) |
| 90 | 134 | ||
| 135 | (define (leaf=? a b) | ||
| 136 | (equal? (basename a) (basename b))) | ||
| 137 | |||
| 138 | (define (resolve-collision leaves) | ||
| 139 | ;; LEAVES all have the same basename, so choose one of them. | ||
| 140 | (format (current-error-port) "warning: collision encountered: ~{~a ~}~%" | ||
| 141 | leaves) | ||
| 142 | |||
| 143 | ;; TODO: Implement smarter strategies. | ||
| 144 | (format (current-error-port) "warning: arbitrarily choosing ~a~%" | ||
| 145 | (car leaves)) | ||
| 146 | (car leaves)) | ||
| 147 | |||
| 91 | (setvbuf (current-output-port) _IOLBF) | 148 | (setvbuf (current-output-port) _IOLBF) |
| 92 | (setvbuf (current-error-port) _IOLBF) | 149 | (setvbuf (current-error-port) _IOLBF) |
| 93 | 150 | ||
| 94 | (mkdir output) | 151 | (mkdir output) |
| 95 | (let loop ((tree (tree-union (append-map (compose tree-leaves file-tree) | 152 | (let loop ((tree (delete-duplicate-leaves |
| 96 | directories))) | 153 | (tree-union (append-map (compose tree-leaves file-tree) |
| 154 | directories)) | ||
| 155 | leaf=? | ||
| 156 | resolve-collision)) | ||
| 97 | (dir '())) | 157 | (dir '())) |
| 98 | (match tree | 158 | (match tree |
| 99 | ((? string?) | 159 | ((? string?) |
diff --git a/tests/union.scm b/tests/union.scm index 317d49dc35e..a3859434a23 100644 --- a/tests/union.scm +++ b/tests/union.scm | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | 1 | ;;; GNU Guix --- Functional package management for GNU |
| 2 | ;;; Copyright © 2012 Ludovic Courtès <ludo@gnu.org> | 2 | ;;; Copyright © 2012, 2013 Ludovic Courtès <ludo@gnu.org> |
| 3 | ;;; | 3 | ;;; |
| 4 | ;;; This file is part of GNU Guix. | 4 | ;;; This file is part of GNU Guix. |
| 5 | ;;; | 5 | ;;; |
| @@ -64,6 +64,25 @@ | |||
| 64 | (bin make) | 64 | (bin make) |
| 65 | (share (doc (make README)))))) | 65 | (share (doc (make README)))))) |
| 66 | 66 | ||
| 67 | (test-equal "delete-duplicate-leaves, default" | ||
| 68 | '(bin make touch ls) | ||
| 69 | (delete-duplicate-leaves '(bin ls make touch ls))) | ||
| 70 | |||
| 71 | (test-equal "delete-duplicate-leaves, file names" | ||
| 72 | '("doc" ("info" | ||
| 73 | "/binutils/ld.info" | ||
| 74 | "/gcc/gcc.info" | ||
| 75 | "/binutils/standards.info")) | ||
| 76 | (let ((leaf=? (lambda (a b) | ||
| 77 | (string=? (basename a) (basename b))))) | ||
| 78 | (delete-duplicate-leaves '("doc" | ||
| 79 | ("info" | ||
| 80 | "/binutils/ld.info" | ||
| 81 | "/binutils/standards.info" | ||
| 82 | "/gcc/gcc.info" | ||
| 83 | "/gcc/standards.info")) | ||
| 84 | leaf=?))) | ||
| 85 | |||
| 67 | (test-skip (if (and %store | 86 | (test-skip (if (and %store |
| 68 | (false-if-exception | 87 | (false-if-exception |
| 69 | (getaddrinfo "www.gnu.org" "80" AI_NUMERICSERV))) | 88 | (getaddrinfo "www.gnu.org" "80" AI_NUMERICSERV))) |
