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.
This commit is contained in:
parent
b9e5c0a949
commit
b2d58cd80a
|
@ -1,5 +1,5 @@
|
||||||
;;; Guix --- Nix package management from Guile. -*- coding: utf-8 -*-
|
;;; Guix --- Nix package management from Guile. -*- coding: utf-8 -*-
|
||||||
;;; Copyright (C) 2012 Ludovic Courtès <ludo@gnu.org>
|
;;; Copyright (C) 2012, 2013 Ludovic Courtès <ludo@gnu.org>
|
||||||
;;;
|
;;;
|
||||||
;;; This file is part of Guix.
|
;;; This file is part of Guix.
|
||||||
;;;
|
;;;
|
||||||
|
@ -19,9 +19,11 @@
|
||||||
(define-module (guix build union)
|
(define-module (guix build union)
|
||||||
#:use-module (ice-9 ftw)
|
#:use-module (ice-9 ftw)
|
||||||
#:use-module (ice-9 match)
|
#:use-module (ice-9 match)
|
||||||
|
#:use-module (ice-9 format)
|
||||||
#:use-module (srfi srfi-1)
|
#:use-module (srfi srfi-1)
|
||||||
#:use-module (srfi srfi-26)
|
#:use-module (srfi srfi-26)
|
||||||
#:export (tree-union
|
#:export (tree-union
|
||||||
|
delete-duplicate-leaves
|
||||||
union-build))
|
union-build))
|
||||||
|
|
||||||
;;; Commentary:
|
;;; Commentary:
|
||||||
|
@ -56,6 +58,48 @@ itself a tree. "
|
||||||
'()
|
'()
|
||||||
(delete-duplicates (map car dirs)))))))))
|
(delete-duplicates (map car dirs)))))))))
|
||||||
|
|
||||||
|
(define* (delete-duplicate-leaves tree
|
||||||
|
#:optional
|
||||||
|
(leaf=? equal?)
|
||||||
|
(delete-duplicates (match-lambda
|
||||||
|
((head _ ...) head))))
|
||||||
|
"Delete duplicate leaves from TREE. Two leaves are considered equal
|
||||||
|
when LEAF=? applied to them returns #t. Each collision (list of leaves
|
||||||
|
that are LEAF=?) is passed to DELETE-DUPLICATES, which must return a
|
||||||
|
single leaf."
|
||||||
|
(let loop ((tree tree))
|
||||||
|
(match tree
|
||||||
|
((dir children ...)
|
||||||
|
(let ((dirs (filter pair? children))
|
||||||
|
(leaves (remove pair? children)))
|
||||||
|
(define collisions
|
||||||
|
(fold (lambda (leaf result)
|
||||||
|
(define same?
|
||||||
|
(cut leaf=? leaf <>))
|
||||||
|
|
||||||
|
(if (any (cut find same? <>) result)
|
||||||
|
result
|
||||||
|
(match (filter same? leaves)
|
||||||
|
((_)
|
||||||
|
result)
|
||||||
|
((collision ...)
|
||||||
|
(cons collision result)))))
|
||||||
|
'()
|
||||||
|
leaves))
|
||||||
|
|
||||||
|
(define non-collisions
|
||||||
|
(filter (lambda (leaf)
|
||||||
|
(match (filter (cut leaf=? leaf <>) leaves)
|
||||||
|
((_) #t)
|
||||||
|
((_ _ ..1) #f)))
|
||||||
|
leaves))
|
||||||
|
|
||||||
|
`(,dir
|
||||||
|
,@non-collisions
|
||||||
|
,@(map delete-duplicates collisions)
|
||||||
|
,@(map loop dirs))))
|
||||||
|
(leaf leaf))))
|
||||||
|
|
||||||
(define* (union-build output directories)
|
(define* (union-build output directories)
|
||||||
"Build in the OUTPUT directory a symlink tree that is the union of all
|
"Build in the OUTPUT directory a symlink tree that is the union of all
|
||||||
the DIRECTORIES."
|
the DIRECTORIES."
|
||||||
|
@ -88,12 +132,28 @@ the DIRECTORIES."
|
||||||
(((? string?) leaves ...)
|
(((? string?) leaves ...)
|
||||||
leaves)))
|
leaves)))
|
||||||
|
|
||||||
|
(define (leaf=? a b)
|
||||||
|
(equal? (basename a) (basename b)))
|
||||||
|
|
||||||
|
(define (resolve-collision leaves)
|
||||||
|
;; LEAVES all have the same basename, so choose one of them.
|
||||||
|
(format (current-error-port) "warning: collision encountered: ~{~a ~}~%"
|
||||||
|
leaves)
|
||||||
|
|
||||||
|
;; TODO: Implement smarter strategies.
|
||||||
|
(format (current-error-port) "warning: arbitrarily choosing ~a~%"
|
||||||
|
(car leaves))
|
||||||
|
(car leaves))
|
||||||
|
|
||||||
(setvbuf (current-output-port) _IOLBF)
|
(setvbuf (current-output-port) _IOLBF)
|
||||||
(setvbuf (current-error-port) _IOLBF)
|
(setvbuf (current-error-port) _IOLBF)
|
||||||
|
|
||||||
(mkdir output)
|
(mkdir output)
|
||||||
(let loop ((tree (tree-union (append-map (compose tree-leaves file-tree)
|
(let loop ((tree (delete-duplicate-leaves
|
||||||
directories)))
|
(tree-union (append-map (compose tree-leaves file-tree)
|
||||||
|
directories))
|
||||||
|
leaf=?
|
||||||
|
resolve-collision))
|
||||||
(dir '()))
|
(dir '()))
|
||||||
(match tree
|
(match tree
|
||||||
((? string?)
|
((? string?)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
;;; GNU Guix --- Functional package management for GNU
|
;;; 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.
|
;;; This file is part of GNU Guix.
|
||||||
;;;
|
;;;
|
||||||
|
@ -64,6 +64,25 @@
|
||||||
(bin make)
|
(bin make)
|
||||||
(share (doc (make README))))))
|
(share (doc (make README))))))
|
||||||
|
|
||||||
|
(test-equal "delete-duplicate-leaves, default"
|
||||||
|
'(bin make touch ls)
|
||||||
|
(delete-duplicate-leaves '(bin ls make touch ls)))
|
||||||
|
|
||||||
|
(test-equal "delete-duplicate-leaves, file names"
|
||||||
|
'("doc" ("info"
|
||||||
|
"/binutils/ld.info"
|
||||||
|
"/gcc/gcc.info"
|
||||||
|
"/binutils/standards.info"))
|
||||||
|
(let ((leaf=? (lambda (a b)
|
||||||
|
(string=? (basename a) (basename b)))))
|
||||||
|
(delete-duplicate-leaves '("doc"
|
||||||
|
("info"
|
||||||
|
"/binutils/ld.info"
|
||||||
|
"/binutils/standards.info"
|
||||||
|
"/gcc/gcc.info"
|
||||||
|
"/gcc/standards.info"))
|
||||||
|
leaf=?)))
|
||||||
|
|
||||||
(test-skip (if (and %store
|
(test-skip (if (and %store
|
||||||
(false-if-exception
|
(false-if-exception
|
||||||
(getaddrinfo "www.gnu.org" "80" AI_NUMERICSERV)))
|
(getaddrinfo "www.gnu.org" "80" AI_NUMERICSERV)))
|
||||||
|
|
Loading…
Reference in New Issue