first commit
This commit is contained in:
		
							parent
							
								
									8c463b59fd
								
							
						
					
					
						commit
						4aabba91ef
					
				| 
						 | 
					@ -0,0 +1,41 @@
 | 
				
			||||||
 | 
					;;; Interfaces
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define-interface install-interface
 | 
				
			||||||
 | 
					  (export version->string
 | 
				
			||||||
 | 
					          string->version
 | 
				
			||||||
 | 
					          version-compare
 | 
				
			||||||
 | 
					          version<?
 | 
				
			||||||
 | 
					          version>?
 | 
				
			||||||
 | 
					          version=?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          installed-packages
 | 
				
			||||||
 | 
					          active-packages
 | 
				
			||||||
 | 
					          installed-packages&versions
 | 
				
			||||||
 | 
					          active-packages&versions
 | 
				
			||||||
 | 
					          active-version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          (define-package :syntax)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          install-file
 | 
				
			||||||
 | 
					          install-files
 | 
				
			||||||
 | 
					          install-directory
 | 
				
			||||||
 | 
					          install-directories
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          package-installation-dir
 | 
				
			||||||
 | 
					          package-installation-staging-dir
 | 
				
			||||||
 | 
					          with-optional-part?
 | 
				
			||||||
 | 
					          is-running-dry?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          install-main))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;; Structures
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define-structure install install-interface
 | 
				
			||||||
 | 
					  (open scheme-with-scsh
 | 
				
			||||||
 | 
					        fluids
 | 
				
			||||||
 | 
					        let-opt
 | 
				
			||||||
 | 
					        srfi-1
 | 
				
			||||||
 | 
					        srfi-9
 | 
				
			||||||
 | 
					        srfi-13
 | 
				
			||||||
 | 
					        srfi-37)
 | 
				
			||||||
 | 
					  (files install-lib))
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,508 @@
 | 
				
			||||||
 | 
					;; Time-stamp: <2003-11-27 11:06:57 schinz>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; TODO
 | 
				
			||||||
 | 
					;; - copy symbolic links as such, do not duplicate contents,
 | 
				
			||||||
 | 
					;; - add function to copy a directory contents,
 | 
				
			||||||
 | 
					;; - add support for image creation,
 | 
				
			||||||
 | 
					;; - add support to maintain a documentation index,
 | 
				
			||||||
 | 
					;; - add "--mode" option to specify permissions for files/directories.
 | 
				
			||||||
 | 
					;; - add "--verbose" to show whats being done.
 | 
				
			||||||
 | 
					;; - add "--log" option to specify a log file.
 | 
				
			||||||
 | 
					;; - decide what to do when target files already exist
 | 
				
			||||||
 | 
					;; - find out how to perform installation for several architectures
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					;; Utilities
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define default-perms #o755)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (parent-directory fname)
 | 
				
			||||||
 | 
					  (file-name-directory (directory-as-file-name fname)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (create-directory&parents fname . rest)
 | 
				
			||||||
 | 
					  (let-optionals rest ((perms default-perms))
 | 
				
			||||||
 | 
					    (let ((parent (parent-directory fname)))
 | 
				
			||||||
 | 
					      (if (not (file-exists? parent))
 | 
				
			||||||
 | 
					          (apply create-directory&parents parent rest))
 | 
				
			||||||
 | 
					      (if (not (file-exists? fname))
 | 
				
			||||||
 | 
					            (-create-directory fname perms)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (common-prefix-length l1 l2 . rest)
 | 
				
			||||||
 | 
					  (let-optionals rest ((pred equal?))
 | 
				
			||||||
 | 
					    (if (or (null? l1) (null? l2) (not (pred (first l1) (first l2))))
 | 
				
			||||||
 | 
					        0
 | 
				
			||||||
 | 
					        (+ 1 (apply common-prefix-length (cdr l1) (cdr l2) rest)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (relative-file-name name . rest)
 | 
				
			||||||
 | 
					  (let-optionals rest ((dir (cwd)))
 | 
				
			||||||
 | 
					    (let* ((abs-pl (split-file-name (absolute-file-name name)))
 | 
				
			||||||
 | 
					           (dir-pl (split-file-name (directory-as-file-name dir)))
 | 
				
			||||||
 | 
					           (cp-len (common-prefix-length abs-pl dir-pl)))
 | 
				
			||||||
 | 
					      (path-list->file-name (append (make-list (- (length dir-pl) cp-len) "..")
 | 
				
			||||||
 | 
					                                    (drop abs-pl cp-len))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (copy-file src target perms)
 | 
				
			||||||
 | 
					  (run (cp ,src ,target))
 | 
				
			||||||
 | 
					  (set-file-mode target perms))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (permissions->string perms)
 | 
				
			||||||
 | 
					  (let ((decode (lambda (mask str)
 | 
				
			||||||
 | 
					                  (if (zero? (bitwise-and perms mask)) "-" str))))
 | 
				
			||||||
 | 
					    (string-append (decode #o400 "r") (decode #o200 "w") (decode #o100 "x")
 | 
				
			||||||
 | 
					                   (decode #o040 "r") (decode #o020 "w") (decode #o010 "x")
 | 
				
			||||||
 | 
					                   (decode #o004 "r") (decode #o002 "w") (decode #o001 "x"))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; Like "load" but without printing anything.
 | 
				
			||||||
 | 
					(define load-quietly
 | 
				
			||||||
 | 
					  (let ((eval (lambda (expr t) (eval expr (interaction-environment)))))
 | 
				
			||||||
 | 
					    (lambda (file-name)
 | 
				
			||||||
 | 
					      (call-with-input-file file-name
 | 
				
			||||||
 | 
					        (lambda (port) (port-fold port read eval #f))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					;; Support for dry runs.
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define *dry-run* (make-fluid #f))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (wrap-for-dry-run real-fn dry-fn)
 | 
				
			||||||
 | 
					  (lambda args
 | 
				
			||||||
 | 
					    (apply (if (fluid *dry-run*) dry-fn real-fn) args)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (dry-run-print msg . args)
 | 
				
			||||||
 | 
					  (apply format #t msg args) (newline))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define -create-directory
 | 
				
			||||||
 | 
					  (wrap-for-dry-run
 | 
				
			||||||
 | 
					   create-directory
 | 
				
			||||||
 | 
					   (lambda (fname . rest)
 | 
				
			||||||
 | 
					     (let-optionals rest ((perms default-perms))
 | 
				
			||||||
 | 
					       (dry-run-print "creating directory ~a with permissions ~a"
 | 
				
			||||||
 | 
					                      fname
 | 
				
			||||||
 | 
					                      (permissions->string perms))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define -create-symlink
 | 
				
			||||||
 | 
					  (wrap-for-dry-run
 | 
				
			||||||
 | 
					   create-symlink
 | 
				
			||||||
 | 
					   (lambda (old-name new-name)
 | 
				
			||||||
 | 
					     (dry-run-print "creating symbolic link ~a pointing to ~a"
 | 
				
			||||||
 | 
					                    new-name
 | 
				
			||||||
 | 
					                    old-name))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define -copy-file
 | 
				
			||||||
 | 
					  (wrap-for-dry-run
 | 
				
			||||||
 | 
					   copy-file
 | 
				
			||||||
 | 
					   (lambda (source target perms)
 | 
				
			||||||
 | 
					     (dry-run-print "copying file ~a to ~a with permissions ~a"
 | 
				
			||||||
 | 
					                    source
 | 
				
			||||||
 | 
					                    target
 | 
				
			||||||
 | 
					                    (permissions->string perms)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define -delete-file
 | 
				
			||||||
 | 
					  (wrap-for-dry-run delete-file
 | 
				
			||||||
 | 
					                    (lambda (fname) (dry-run-print "deleting file ~a" fname))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					;; Regular expressions to match package names
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define number-rx (rx (+ digit)))
 | 
				
			||||||
 | 
					(define version-rx (rx ,number-rx (* "." ,number-rx)))
 | 
				
			||||||
 | 
					(define package-name-rx (rx (+ (| alphanum ("_")))))
 | 
				
			||||||
 | 
					(define package-full-name-rx (rx (submatch ,package-name-rx)
 | 
				
			||||||
 | 
					                                 "-"
 | 
				
			||||||
 | 
					                                 (submatch ,version-rx)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					;; Versions
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					;; Versions are represented as lists of integers, the most significant
 | 
				
			||||||
 | 
					;; being at the head.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (version->string version)
 | 
				
			||||||
 | 
					  (string-join (map number->string version) "."))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define string->version
 | 
				
			||||||
 | 
					  (let ((split-version (infix-splitter ".")))
 | 
				
			||||||
 | 
					    (lambda (version-string)
 | 
				
			||||||
 | 
					      (map string->number (split-version version-string)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (version-compare v1 v2)
 | 
				
			||||||
 | 
					  (cond ((and (null? v1) (null? v2)) 'equal)
 | 
				
			||||||
 | 
					        ((null? v1) 'smaller)
 | 
				
			||||||
 | 
					        ((null? v2) 'greater)
 | 
				
			||||||
 | 
					        (else (let ((v1h (car v1)) (v2h (car v2)))
 | 
				
			||||||
 | 
					                (cond ((< v1h v2h) 'smaller)
 | 
				
			||||||
 | 
					                      ((> v1h v2h) 'greater)
 | 
				
			||||||
 | 
					                      (else (version-compare (cdr v1) (cdr v2))))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (version<? v1 v2) (eq? (version-compare v1 v2) 'smaller))
 | 
				
			||||||
 | 
					(define (version>? v1 v2) (eq? (version-compare v1 v2) 'greater))
 | 
				
			||||||
 | 
					(define (version=? v1 v2) (eq? (version-compare v1 v2) 'equal))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					;; Packages
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define-record-type package
 | 
				
			||||||
 | 
					  (make-package name version extensions install-thunk)
 | 
				
			||||||
 | 
					  package?
 | 
				
			||||||
 | 
					  (name package:name)
 | 
				
			||||||
 | 
					  (version package:version)
 | 
				
			||||||
 | 
					  (extensions package:extensions)
 | 
				
			||||||
 | 
					  (install-thunk package:install-thunk))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; List of all defined packages
 | 
				
			||||||
 | 
					(define packages '())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; Add a package to the above list
 | 
				
			||||||
 | 
					(define (add-package pkg)
 | 
				
			||||||
 | 
					  (set! packages (cons pkg packages)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define-syntax define-package
 | 
				
			||||||
 | 
					  (syntax-rules ()
 | 
				
			||||||
 | 
					    ((define-package name version extensions body ...)
 | 
				
			||||||
 | 
					     (add-package (make-package name
 | 
				
			||||||
 | 
					                                (quote version)
 | 
				
			||||||
 | 
									(quote extensions)
 | 
				
			||||||
 | 
					                                (lambda () body ...))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					;; Names of various directories/links
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; Directory in which active versions of packages are "remembered".
 | 
				
			||||||
 | 
					(define (active-directory root)
 | 
				
			||||||
 | 
					  (absolute-file-name "active" root))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; Directory in which packages are installed.
 | 
				
			||||||
 | 
					(define (installed-directory root)
 | 
				
			||||||
 | 
					  (absolute-file-name "installed" root))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (package-dir-name root pkg)
 | 
				
			||||||
 | 
					  (absolute-file-name (package:name pkg) (installed-directory root)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (package-version-dir-name root pkg)
 | 
				
			||||||
 | 
					  (absolute-file-name (version->string (package:version pkg))
 | 
				
			||||||
 | 
					                      (package-dir-name root pkg)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (active-link-name root pkg)
 | 
				
			||||||
 | 
					  (absolute-file-name (package:name pkg) (active-directory root)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					;; Queries
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (packages-of dir)
 | 
				
			||||||
 | 
					  (with-cwd dir (filter file-directory? (directory-files))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; Get the name of all installed packages (without versions) as a list
 | 
				
			||||||
 | 
					;; of strings.
 | 
				
			||||||
 | 
					(define (installed-packages root)
 | 
				
			||||||
 | 
					  (packages-of (installed-directory root)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; Get the name of all active packages (without versions) as a list of
 | 
				
			||||||
 | 
					;; strings.
 | 
				
			||||||
 | 
					(define (active-packages root)
 | 
				
			||||||
 | 
					  (packages-of (active-directory root)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; Return the list of all installed packages and their version(s), as
 | 
				
			||||||
 | 
					;; a list of pairs. The CAR of each pair contains the name of the
 | 
				
			||||||
 | 
					;; package, the CDR contains the list of all available versions.
 | 
				
			||||||
 | 
					(define (installed-packages&versions root)
 | 
				
			||||||
 | 
					  (with-cwd (installed-directory root)
 | 
				
			||||||
 | 
					            (map (lambda (pkg-dir)
 | 
				
			||||||
 | 
					                   (cons pkg-dir
 | 
				
			||||||
 | 
					                         (map string->version (directory-files pkg-dir))))
 | 
				
			||||||
 | 
					                 (directory-files))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (read-version pkg-link)
 | 
				
			||||||
 | 
					  (string->version (file-name-nondirectory (read-symlink pkg-link))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; Return the list of all active packages and their version, as a list
 | 
				
			||||||
 | 
					;; of pairs. The CAR of each pair contains the name of the package,
 | 
				
			||||||
 | 
					;; and the CDR contains the active version.
 | 
				
			||||||
 | 
					(define (active-packages&versions root)
 | 
				
			||||||
 | 
					  (with-cwd (active-directory root)
 | 
				
			||||||
 | 
					            (map (lambda (pkg-dir)
 | 
				
			||||||
 | 
					                   (cons pkg-dir (read-version pkg-dir)))
 | 
				
			||||||
 | 
					                 (directory-files))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; Return the active version of package NAME.
 | 
				
			||||||
 | 
					(define (active-version root name)
 | 
				
			||||||
 | 
					  (read-version (absolute-file-name name (active-directory root))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					;; Actions
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; Perform all actions required to make the given version of the
 | 
				
			||||||
 | 
					;; package active (i.e. the default version for that package).
 | 
				
			||||||
 | 
					(define (activate-package root pkg)
 | 
				
			||||||
 | 
					  (let ((lnk-name (active-link-name root pkg)))
 | 
				
			||||||
 | 
					    (if (and (file-exists? lnk-name) (file-symlink? lnk-name))
 | 
				
			||||||
 | 
					        (-delete-file lnk-name))
 | 
				
			||||||
 | 
					    (-create-symlink (relative-file-name (package-version-dir-name root pkg)
 | 
				
			||||||
 | 
					                                         (file-name-directory lnk-name))
 | 
				
			||||||
 | 
					                     lnk-name)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (target-absolute-dir root pkg dir)
 | 
				
			||||||
 | 
					  (absolute-file-name (directory-as-file-name dir)
 | 
				
			||||||
 | 
					                      (package-version-dir-name root pkg)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (re-root-file file dir)
 | 
				
			||||||
 | 
					  (absolute-file-name (file-name-nondirectory (directory-as-file-name file))
 | 
				
			||||||
 | 
					                      dir))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; Copy the list of FILES to the TARGET-DIR and set their permission
 | 
				
			||||||
 | 
					;; to PERMS. The TARGET-DIR (default ".") is relative to the package
 | 
				
			||||||
 | 
					;; directory given by ROOT, NAME and VERSION.
 | 
				
			||||||
 | 
					(define (install-files% root pkg files . rest)
 | 
				
			||||||
 | 
					  (let-optionals rest ((target-dir ".") (perms default-perms))
 | 
				
			||||||
 | 
					    (let* ((target-abs-dir (target-absolute-dir root pkg target-dir)))
 | 
				
			||||||
 | 
					      (create-directory&parents target-abs-dir perms)
 | 
				
			||||||
 | 
					      (for-each (lambda (file)
 | 
				
			||||||
 | 
					                  (-copy-file file (re-root-file file target-abs-dir) perms))
 | 
				
			||||||
 | 
					                files))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; Copy SRC-DIR and all its contents in TARGET-DIR, and set the
 | 
				
			||||||
 | 
					;; permission for everything to PERMS. The TARGET-DIR (default ".") is
 | 
				
			||||||
 | 
					;; relative to the package directory given by ROOT, NAME and VERSION.
 | 
				
			||||||
 | 
					(define (install-directory% root pkg src-dir . rest)
 | 
				
			||||||
 | 
					  (let-optionals rest ((target-dir ".") (perms default-perms))
 | 
				
			||||||
 | 
					    (let* ((src-dir-name (file-name-nondirectory
 | 
				
			||||||
 | 
					                          (directory-as-file-name src-dir)))
 | 
				
			||||||
 | 
					           (full-target-dir (absolute-file-name src-dir-name target-dir))
 | 
				
			||||||
 | 
					           (target-abs-dir (target-absolute-dir root pkg full-target-dir)))
 | 
				
			||||||
 | 
					      (create-directory&parents target-abs-dir perms)
 | 
				
			||||||
 | 
					      (for-each
 | 
				
			||||||
 | 
					       (lambda (file)
 | 
				
			||||||
 | 
					         (let ((abs-file (absolute-file-name file src-dir)))
 | 
				
			||||||
 | 
					           (cond ((file-regular? abs-file)
 | 
				
			||||||
 | 
					                  (-copy-file abs-file
 | 
				
			||||||
 | 
					                              (absolute-file-name file target-abs-dir)
 | 
				
			||||||
 | 
					                              perms))
 | 
				
			||||||
 | 
					                 ((file-directory? abs-file)
 | 
				
			||||||
 | 
					                  (install-directory% root pkg abs-file full-target-dir perms))
 | 
				
			||||||
 | 
					                 (else
 | 
				
			||||||
 | 
					                  (display-error-and-exit "don't know what to do with file ~a"
 | 
				
			||||||
 | 
					                                          abs-file)))))
 | 
				
			||||||
 | 
					       (directory-files src-dir #t)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (install-empty-directory% root pkg target-dir . rest)
 | 
				
			||||||
 | 
					  (let-optionals rest ((perms default-perms))
 | 
				
			||||||
 | 
					    (-create-directory (target-absolute-dir root pkg target-dir) perms)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (install-empty-directory&parents% root pkg target-dir . rest)
 | 
				
			||||||
 | 
					  (let-optionals rest ((perms default-perms))
 | 
				
			||||||
 | 
					    (create-directory&parents (target-absolute-dir root pkg target-dir)
 | 
				
			||||||
 | 
					                              perms)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define *root* (make-fluid #f))
 | 
				
			||||||
 | 
					(define *dest-root* (make-fluid #f))
 | 
				
			||||||
 | 
					(define *package* (make-fluid #f))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (package-installation-dir)
 | 
				
			||||||
 | 
					  (package-version-dir-name (fluid *root*) (fluid *package*)))
 | 
				
			||||||
 | 
					(define (package-installation-staging-dir)
 | 
				
			||||||
 | 
					  (package-version-dir-name (fluid *dest-root*) (fluid *package*)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (forward-args-prepend-fluids target-fn args)
 | 
				
			||||||
 | 
					  (apply target-fn (fluid *dest-root*) (fluid *package*) args))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (install-file file . rest)
 | 
				
			||||||
 | 
					  (apply install-files (list file) rest))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (install-files . args)
 | 
				
			||||||
 | 
					  (forward-args-prepend-fluids install-files% args))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (install-empty-directory . args)
 | 
				
			||||||
 | 
					  (forward-args-prepend-fluids install-empty-directory% args))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (install-empty-directory&parents . args)
 | 
				
			||||||
 | 
					  (forward-args-prepend-fluids install-empty-directory&parents% args))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (install-directory . args)
 | 
				
			||||||
 | 
					  (forward-args-prepend-fluids install-directory% args))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (install-directories src-dirs . rest)
 | 
				
			||||||
 | 
					  (for-each (lambda (src-dir)
 | 
				
			||||||
 | 
					              (apply install-directory src-dir rest))
 | 
				
			||||||
 | 
					            src-dirs))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (install-package root dest-root pkg)
 | 
				
			||||||
 | 
					  (let-fluids *root* root
 | 
				
			||||||
 | 
					              *dest-root* dest-root
 | 
				
			||||||
 | 
					              *package* pkg
 | 
				
			||||||
 | 
					              (package:install-thunk pkg)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					;; Error handling
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (display-error-and-exit msg . args)
 | 
				
			||||||
 | 
					  (apply format (current-error-port) (string-append "Error: " msg) args)
 | 
				
			||||||
 | 
					  (newline)
 | 
				
			||||||
 | 
					  (exit 1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define usage #<<END
 | 
				
			||||||
 | 
					Usage: ~a [options]
 | 
				
			||||||
 | 
					  -h, --help             display this help message, then exit
 | 
				
			||||||
 | 
					  -r, --root <dir>       specify root directory
 | 
				
			||||||
 | 
					  -n, --dry-run          don't do anything, print what would have been done
 | 
				
			||||||
 | 
					  -i, --inactive         don't activate package after installing it
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					advanced options:
 | 
				
			||||||
 | 
					  -d, --dest-root <dir>  specify staging root directory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					END
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (display-usage-and-exit msg . args)
 | 
				
			||||||
 | 
					  (if msg (begin (apply format #t msg args) (newline)))
 | 
				
			||||||
 | 
					  (format #t usage (car (command-line)))
 | 
				
			||||||
 | 
					  (display-optional-parts-usage)
 | 
				
			||||||
 | 
					  (exit 1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (check-root-directory root)
 | 
				
			||||||
 | 
					  (for-each (lambda (dir)
 | 
				
			||||||
 | 
					              (if (not (and (file-exists? dir) (file-directory? dir)))
 | 
				
			||||||
 | 
					                  (display-error-and-exit
 | 
				
			||||||
 | 
					                   (string-append
 | 
				
			||||||
 | 
					                    "directory ~a does not exist or is not a directory.\n"
 | 
				
			||||||
 | 
					                    "It should be created before installing packages.")
 | 
				
			||||||
 | 
					                   dir)))
 | 
				
			||||||
 | 
					            (list root (active-directory root) (installed-directory root))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					;; Command line parsing
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define options
 | 
				
			||||||
 | 
					 (list (option '(#\h "help") #f #f
 | 
				
			||||||
 | 
					               (lambda args
 | 
				
			||||||
 | 
					                 (display-usage-and-exit #f)))
 | 
				
			||||||
 | 
					       (option '(#\r "root") #t #f
 | 
				
			||||||
 | 
					               (lambda (option name arg root dest-root activate? dry-run? opt-parts)
 | 
				
			||||||
 | 
					                 (values arg dest-root activate? dry-run? opt-parts)))
 | 
				
			||||||
 | 
					       (option '(#\d "dest-root") #t #f
 | 
				
			||||||
 | 
					               (lambda (option name arg root dest-root activate? dry-run? opt-parts)
 | 
				
			||||||
 | 
					                 (values root arg activate? dry-run? opt-parts)))
 | 
				
			||||||
 | 
					       (option '(#\n "dry-run") #f #f
 | 
				
			||||||
 | 
					               (lambda (option name arg root dest-root activate? dry-run? opt-parts)
 | 
				
			||||||
 | 
					                 (values root dest-root activate? #t opt-parts)))
 | 
				
			||||||
 | 
					       (option '(#\i "inactive") #f #f
 | 
				
			||||||
 | 
					               (lambda (option name arg root dest-root activate? dry-run? opt-parts)
 | 
				
			||||||
 | 
					                 (values root dest-root #f dry-run? opt-parts)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					;; optional parts stuff
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (booleanize s)
 | 
				
			||||||
 | 
					  (cond
 | 
				
			||||||
 | 
					   ((string=? s "yes") #t)
 | 
				
			||||||
 | 
					   ((string=? s "no") #f)
 | 
				
			||||||
 | 
					   (else (display-error-and-exit "unknown boolean value '~a'. Use 'yes' or 'no'." s))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (unbooleanize b)
 | 
				
			||||||
 | 
					  (if b "yes" "no"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (spaces min n)
 | 
				
			||||||
 | 
					  (make-string (if (< n min) min n) #\space))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (get-all-optional-parts packages)
 | 
				
			||||||
 | 
					  (if (null? packages)
 | 
				
			||||||
 | 
					      '()
 | 
				
			||||||
 | 
					      (let* ((p (car packages))
 | 
				
			||||||
 | 
					             (ext (package:extensions p))
 | 
				
			||||||
 | 
					             (op (assq 'optional-parts ext)))
 | 
				
			||||||
 | 
					        (append (if op (cdr op) '())
 | 
				
			||||||
 | 
					                (get-all-optional-parts (cdr packages))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define optional-part:name car)
 | 
				
			||||||
 | 
					(define optional-part:description cadr)
 | 
				
			||||||
 | 
					(define (optional-part:default part)
 | 
				
			||||||
 | 
					  (if (null? (cddr part)) #f (caddr part)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (optional-parts->options parts)
 | 
				
			||||||
 | 
					  (map (lambda (part)
 | 
				
			||||||
 | 
					         (let ((part-name (optional-part:name part)))
 | 
				
			||||||
 | 
					           (option (list (string-append "with-" (symbol->string part-name))) #t #f
 | 
				
			||||||
 | 
					                   (lambda (option name arg root dest-root activate? dry-run? opt-parts)
 | 
				
			||||||
 | 
					                     (values root dest-root activate? dry-run?
 | 
				
			||||||
 | 
					                             (cons (cons part-name (booleanize arg))
 | 
				
			||||||
 | 
					                                   opt-parts))))))
 | 
				
			||||||
 | 
					       parts))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (optional-parts-defaults parts)
 | 
				
			||||||
 | 
					  (map (lambda (part)
 | 
				
			||||||
 | 
					         (cons (optional-part:name part) (optional-part:default part)))
 | 
				
			||||||
 | 
					       parts))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define *all-optional-parts* (make-fluid '()))
 | 
				
			||||||
 | 
					(define *optional-parts-alist* (make-fluid '()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define *usage-description-column* 25)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (display-optional-parts-usage)
 | 
				
			||||||
 | 
					  (display "\noptionals parts:\n")
 | 
				
			||||||
 | 
					  (map (lambda (part)
 | 
				
			||||||
 | 
					         (let* ((sname (symbol->string (optional-part:name part)))
 | 
				
			||||||
 | 
					                (pf (string-append "  --with-" sname"=[yes|no]")))
 | 
				
			||||||
 | 
					         (display pf)
 | 
				
			||||||
 | 
					         (display (spaces 2 (- *usage-description-column* (string-length pf))))
 | 
				
			||||||
 | 
					         (display (optional-part:description part))
 | 
				
			||||||
 | 
					         (display (string-append " [" (unbooleanize (optional-part:default part)) "]"))
 | 
				
			||||||
 | 
					         (newline)))
 | 
				
			||||||
 | 
					       (fluid *all-optional-parts*)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (with-optional-part? name)
 | 
				
			||||||
 | 
					  (cdr (assq name (fluid *optional-parts-alist*))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define package-definition-file "pkg-def.scm")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (is-running-dry?)
 | 
				
			||||||
 | 
					  (fluid *dry-run*))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (install-main cmd-line)
 | 
				
			||||||
 | 
					  (let ((prog (car cmd-line))
 | 
				
			||||||
 | 
					        (args (cdr cmd-line)))
 | 
				
			||||||
 | 
					    (if (not (file-exists? package-definition-file))
 | 
				
			||||||
 | 
					        (display-error-and-exit
 | 
				
			||||||
 | 
					         "cannot find package definition file (~a)"
 | 
				
			||||||
 | 
					         package-definition-file))
 | 
				
			||||||
 | 
					    (load-quietly package-definition-file)
 | 
				
			||||||
 | 
					    (set-fluid! *all-optional-parts* (get-all-optional-parts packages))
 | 
				
			||||||
 | 
					    (receive (root maybe-dest-root activate? dry-run? opt-parts)
 | 
				
			||||||
 | 
					             (args-fold args
 | 
				
			||||||
 | 
					                        (append options
 | 
				
			||||||
 | 
					                                (optional-parts->options (fluid *all-optional-parts*)))
 | 
				
			||||||
 | 
					                        (lambda (option name arg . seeds)
 | 
				
			||||||
 | 
					                          (display-usage-and-exit "Unknown option ~a" name))
 | 
				
			||||||
 | 
					                        (lambda (operand . seeds) ; operand
 | 
				
			||||||
 | 
					                          (display-usage-and-exit
 | 
				
			||||||
 | 
					                           "Don't know what to do with ~a"
 | 
				
			||||||
 | 
					                           operand))
 | 
				
			||||||
 | 
					                        #f              ; default root
 | 
				
			||||||
 | 
					                        #f              ; default dest-root
 | 
				
			||||||
 | 
					                        #t              ; default activation
 | 
				
			||||||
 | 
					                        #f              ; default dry run
 | 
				
			||||||
 | 
								(optional-parts-defaults (fluid *all-optional-parts*)))
 | 
				
			||||||
 | 
					             (if (not root)
 | 
				
			||||||
 | 
					                 (display-error-and-exit
 | 
				
			||||||
 | 
					                  "No package root specified (use --root option)"))
 | 
				
			||||||
 | 
					             (let ((dest-root (or maybe-dest-root root)))
 | 
				
			||||||
 | 
					               (check-root-directory dest-root)
 | 
				
			||||||
 | 
					               (let-fluids *dry-run* dry-run?
 | 
				
			||||||
 | 
					                           *optional-parts-alist* opt-parts
 | 
				
			||||||
 | 
					                           (lambda ()
 | 
				
			||||||
 | 
					                             (for-each
 | 
				
			||||||
 | 
					                              (lambda (pkg)
 | 
				
			||||||
 | 
					                                (install-package root dest-root pkg)
 | 
				
			||||||
 | 
					                                (if activate?
 | 
				
			||||||
 | 
					                                    (activate-package dest-root pkg)))
 | 
				
			||||||
 | 
					                              packages)))))))
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					#!/bin/sh
 | 
				
			||||||
 | 
					exec scsh -lm install-lib-module.scm -o install -e install-main -s "$0" "$@"
 | 
				
			||||||
 | 
					!#
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					(define-package "scx" (0 2 1)
 | 
				
			||||||
 | 
					  ((optional-parts (xft "Compile with Xft support" #f)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (let* ((prefix (package-installation-staging-dir))
 | 
				
			||||||
 | 
						 (xft? (with-optional-part? 'xft))
 | 
				
			||||||
 | 
					         (configure (quasiquote ("./configure" ,(string-append "--prefix=" prefix)
 | 
				
			||||||
 | 
					                                 ,(if xft? "--with-xft" "--without-xft"))))
 | 
				
			||||||
 | 
					         (make '(make all install)))
 | 
				
			||||||
 | 
					     (if (is-running-dry?)
 | 
				
			||||||
 | 
					         (begin
 | 
				
			||||||
 | 
					          (display configure) (newline)
 | 
				
			||||||
 | 
					          (display make) (newline))
 | 
				
			||||||
 | 
					         (and (zero? (run ,configure))
 | 
				
			||||||
 | 
					              (zero? (run ,make)))))
 | 
				
			||||||
 | 
					  (install-file "load.scm")
 | 
				
			||||||
 | 
					  (install-directory "scheme")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
		Loading…
	
		Reference in New Issue