From mboxrd@z Thu Jan 1 00:00:00 1970 From: Mark H Weaver Subject: Re: Optimizing union.scm Date: Thu, 27 Mar 2014 03:09:12 -0400 Message-ID: <87eh1oi0dj.fsf@yeeloong.lan> References: <871tz8oldk.fsf@netris.org> <874n2oubuq.fsf@gnu.org> <8761n4mzvu.fsf@yeeloong.lan> <87siq8r77u.fsf@gnu.org> <87r45sl074.fsf_-_@yeeloong.lan> <87ha6nzp58.fsf@gnu.org> <87txamkbde.fsf@yeeloong.lan> <87bnwugptc.fsf@gnu.org> <87ob0tkj1p.fsf@yeeloong.lan> <87y4zx3mxz.fsf@gnu.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:40722) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WT4S3-0007ZF-4s for guix-devel@gnu.org; Thu, 27 Mar 2014 03:10:16 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WT4Rx-00088Z-QP for guix-devel@gnu.org; Thu, 27 Mar 2014 03:10:11 -0400 In-Reply-To: <87y4zx3mxz.fsf@gnu.org> ("Ludovic \=\?utf-8\?Q\?Court\=C3\=A8s\=22'\?\= \=\?utf-8\?Q\?s\?\= message of "Tue, 25 Mar 2014 23:58:32 +0100") List-Id: "Development of GNU Guix and the GNU System distribution." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-devel-bounces+gcggd-guix-devel=m.gmane.org@gnu.org Sender: guix-devel-bounces+gcggd-guix-devel=m.gmane.org@gnu.org To: Ludovic =?utf-8?Q?Court=C3=A8s?= Cc: guix-devel@gnu.org --=-=-= Content-Type: text/plain Here's a second draft of union.scm, incorporating your suggestions. I haven't yet replaced the use of the hash table, though. Mark --=-=-= Content-Type: text/plain; charset=utf-8 Content-Disposition: inline; filename=union.scm Content-Transfer-Encoding: quoted-printable Content-Description: Optimized union.scm v2 ;;; GNU Guix --- Functional package management for GNU ;;; Copyright =C2=A9 2012, 2013, 2014 Ludovic Court=C3=A8s ;;; Copyright =C2=A9 2014 Mark H Weaver ;;; ;;; 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 build union) #:use-module (ice-9 match) #:use-module (ice-9 format) #:use-module (srfi srfi-1) #:use-module (srfi srfi-26) #:use-module (rnrs bytevectors) #:use-module (rnrs io ports) #:export (union-build)) ;;; Commentary: ;;; ;;; Build a directory that is the union of a set of directories, using ;;; symbolic links. ;;; ;;; Code: (define (files-in-directory dirname) (let ((dir (opendir dirname))) (let loop ((files '())) (match (readdir dir) ((or "." "..") (loop files)) ((? eof-object?) (closedir dir) (sort files string `~a'~%" input output) (symlink input output)) (define (resolve-collisions output dirs files) (cond ((null? dirs) ;; The inputs are all files. (format (current-error-port) "warning: collision encountered: ~{~a ~}~%" files) (let ((file (first files))) ;; TODO: Implement smarter strategies. (format (current-error-port) "warning: arbitrarily choosing ~a~%" file) (symlink* file output))) (else ;; The inputs are a mixture of files and directories (error "union-build: collision between file and directories" `((files ,files) (dirs ,dirs)))))) (define (union output inputs) (match inputs ((input) ;; There's only one input, so just make a link. (symlink* input output)) (_ (call-with-values (lambda () (partition file-is-directory? inputs)) (match-lambda* ((dirs ()) ;; All inputs are directories. Create a new directory ;; where we will merge the input directories. (mkdir output) ;; Build a hash table mapping each file to a list of input ;; directories containing that file. (let ((table (make-hash-table))) (define (add-to-table! file dir) (hash-set! table file (cons dir (hash-ref table file '())))) ;; Populate the table. (for-each (lambda (dir) (for-each (cut add-to-table! <> dir) (files-in-directory dir))) dirs) ;; Now iterate over the table and recursively ;; perform a union for each entry. (hash-for-each (lambda (file dirs-with-file) (union (string-append output "/" file) (map (cut string-append <> "/" file) (reverse dirs-with-file)))) table))) ((() (file (? (cut file=3D? <> file)) ...)) ;; There are no directories, and all files have the same conten= ts, ;; so there's no conflict. (symlink* file output)) ((dirs files) (resolve-collisions output dirs files))))))) (setvbuf (current-output-port) _IOLBF) (setvbuf (current-error-port) _IOLBF) (when (file-port? log-port) (setvbuf log-port _IOLBF)) (union output (delete-duplicates inputs))) ;;; union.scm ends here --=-=-=--