~ chicken-core (chicken-5) 0d9fe39a0b08271ec12ed9a212d04f97829847ed
commit 0d9fe39a0b08271ec12ed9a212d04f97829847ed Author: felix <felix@call-with-current-continuation.org> AuthorDate: Sun Sep 4 22:27:25 2016 +0200 Commit: felix <felix@call-with-current-continuation.org> CommitDate: Sun Nov 13 11:36:15 2016 +0100 replaced new chicken-install diff --git a/chicken-install.scm b/chicken-install.scm index 48132262..4e4d425b 100644 --- a/chicken-install.scm +++ b/chicken-install.scm @@ -23,135 +23,153 @@ ; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ; POSSIBILITY OF SUCH DAMAGE. -(module main () - - (import scheme chicken) - (import setup-download setup-api) - - (import chicken.data-structures - chicken.files - chicken.foreign - chicken.format - chicken.io - chicken.irregex - chicken.pathname - chicken.ports - chicken.posix - chicken.pretty-print - (only chicken.utils qs)) - - (include "mini-srfi-1.scm") - - (define +default-repository-files+ - ;;XXX keep this up-to-date! - '("chicken.bitwise.import.so" - "chicken.continuation.import.so" - "chicken.csi.import.so" - "chicken.data-structures.import.so" - "chicken.errno.import.so" - "chicken.eval.import.so" - "chicken.expand.import.so" - "chicken.files.import.so" - "chicken.fixnum.import.so" - "chicken.flonum.import.so" - "chicken.foreign.import.so" - "chicken.format.import.so" - "chicken.gc.import.so" - "chicken.import.so" - "chicken.internal.import.so" - "chicken.io.import.so" - "chicken.irregex.import.so" - "chicken.keyword.import.so" - "chicken.locative.import.so" - "chicken.lolevel.import.so" - "chicken.memory.import.so" - "chicken.pathname.import.so" - "chicken.ports.import.so" - "chicken.posix.import.so" - "chicken.pretty-print.import.so" - "chicken.random.import.so" - "chicken.repl.import.so" - "chicken.read-syntax.import.so" - "chicken.tcp.import.so" - "chicken.time.import.so" - "chicken.utils.import.so" - "setup-api.import.so" - "setup-api.so" - "setup-download.so" - "setup-download.import.so" - "srfi-4.import.so" - "types.db")) - (define-constant +defaults-version+ 1) - (define-constant +module-db+ "modules.db") - (define-constant +defaults-file+ "setup.defaults") - - (define-foreign-variable C_TARGET_LIB_HOME c-string) - (define-foreign-variable C_INSTALL_BIN_HOME c-string) - (define-foreign-variable C_TARGET_PREFIX c-string) - (define-foreign-variable C_BINARY_VERSION int) - (define-foreign-variable C_WINDOWS_SHELL bool) - (define-foreign-variable C_CSI_PROGRAM c-string) +(module main () - (define *program-path* - (or (and-let* ((p (get-environment-variable "CHICKEN_PREFIX"))) - (make-pathname p "bin") ) - C_INSTALL_BIN_HOME)) +(import (scheme)) +(import (chicken)) +(import (chicken foreign)) +(import (chicken data-structures)) +(import (chicken keyword)) +(import (chicken files)) +(import (chicken format)) +(import (chicken irregex)) +(import (chicken tcp)) +(import (chicken ports)) +(import (chicken posix)) +(import (chicken io)) +(import (chicken time)) +(import (chicken pretty-print)) + +(define +defaults-version+ 2) +(define +module-db+ "modules.db") +(define +defaults-file+ "setup.defaults") +(define +short-options+ '(#\r #\h)) +(define +one-hour+ (* 60 60)) +(define +timestamp-file+ "TIMESTAMP") +(define +status-file+ "STATUS") +(define +egg-extension+ "egg") +(define +egg-info-extension+ "egg.info") + +(include "mini-srfi-1.scm") +(include "egg-environment.scm") +(include "egg-compile.scm") +(include "egg-download.scm") + +(define user-defaults #f) +(define quiet #t) +(define default-servers '()) +(define default-locations '()) +(define mappings '()) +(define aliases '()) +(define override '()) +(define hacks '()) +(define proxy-host #f) +(define proxy-port #f) +(define proxy-user-pass #f) +(define retrieve-only #f) +(define do-not-build #f) +(define list-versions-only #f) +(define canonical-eggs '()) +(define dependencies '()) +(define checked-eggs '()) +(define run-tests #f) +(define force-install #f) +(define host-extension cross-chicken) +(define target-extension cross-chicken) +(define sudo-install #f) + +(define platform + (if (eq? 'mingw (build-platform)) + 'windows + 'unix)) + +(define current-status + (list ##sys#build-id + (get-environment-variable "CSC_OPTIONS") + (get-environment-variable "LD_LIBRARY_PATH") + (get-environment-variable "CHICKEN_INCLUDE_PATH") + (get-environment-variable "CHICKEN_REPOSITORY") + (get-environment-variable "DYLD_LIBRARY_PATH"))) + +(define (probe-dir dir) + (and dir (file-exists? dir) (directory? dir) dir)) + +(define cache-directory + (or (get-environment-variable "CHICKEN_EGG_CACHE") + (make-pathname (or (probe-dir (get-environment-variable "HOME")) + (probe-dir (get-environment-variable "USERPROFILE")) + (probe-dir "/tmp") + (probe-dir "/Temp") + ".") + ".chicken-install.cache"))) + +(define (repo-path) + (destination-repository + (if (and cross-chicken (not host-extension)) + 'target + 'host))) + +(define (build-script-extension mode platform) + (string-append "build" + (if (eq? mode 'target) ".target" "") + (if (eq? platform 'windows) ".bat" ".sh"))) + +(define (install-script-extension mode platform) + (string-append "install" + (if (eq? mode 'target) ".target" "") + (if (eq? platform 'windows) ".bat" ".sh"))) + + +;; usage information + +(define (usage code) + (print "usage: chicken-install [OPTION | EXTENSION[:VERSION]] ...") + ;;XXX + (exit code)) + - (define *keep* #f) - (define *keep-existing* #f) - (define *force* #f) - (define *run-tests* #f) - (define *retrieve-only* #f) - (define *no-install* #f) - (define *username* #f) - (define *password* #f) - (define *default-sources* '()) - (define *default-location* #f) - (define *default-transport* 'http) - (define *windows-shell* C_WINDOWS_SHELL) - (define *proxy-host* #f) - (define *proxy-port* #f) - (define *proxy-user-pass* #f) - (define *running-test* #f) - (define *mappings* '()) - (define *deploy* #f) - (define *trunk* #f) - (define *csc-features* '()) - (define *csc-nonfeatures* '()) - (define *prefix* #f) - (define *aliases* '()) - (define *cross-chicken* (feature? #:cross-chicken)) - (define *host-extension* *cross-chicken*) - (define *target-extension* *cross-chicken*) - (define *debug-setup* #f) - (define *keep-going* #f) - (define *override* '()) - (define *reinstall* #f) - (define *show-depends* #f) - (define *show-foreign-depends* #f) - (define *hacks* '()) +;; utilities + +;; Simpler replacement for SRFI-13's string-suffix? +(define (string-suffix? suffix s) + (let ((len-s (string-length s)) + (len-suffix (string-length suffix))) + (and (not (< len-s len-suffix)) + (string=? suffix + (substring s (- len-s len-suffix)))))) + +(define (d fstr . args) + (unless quiet + (let ((port (current-error-port))) + (apply fprintf port fstr args) + (flush-output port) ) )) + +(define (version>=? v1 v2) + (define (version->list v) + (map (lambda (x) (or (string->number x) x)) + (irregex-split "[-\\._]" (->string v)))) + (let loop ((p1 (version->list v1)) + (p2 (version->list v2))) + (cond ((null? p1) (null? p2)) + ((null? p2)) + ((number? (car p1)) + (and (number? (car p2)) + (or (> (car p1) (car p2)) + (and (= (car p1) (car p2)) + (loop (cdr p1) (cdr p2)))))) + ((number? (car p2))) + ((string>? (car p1) (car p2))) + (else + (and (string=? (car p1) (car p2)) + (loop (cdr p1) (cdr p2))))))) - (define (repo-path) - (if *deploy* - *prefix* - (if (and *cross-chicken* (not *host-extension*)) - (make-pathname C_TARGET_LIB_HOME (sprintf "chicken/~a" C_BINARY_VERSION)) - (if *prefix* - (make-pathname - *prefix* - (sprintf "lib/chicken/~a" C_BINARY_VERSION)) - (repository-path))))) - (define (get-prefix #!optional runtime) - (cond ((and *cross-chicken* - (not *host-extension*)) - (or (and (not runtime) *prefix*) - C_TARGET_PREFIX)) - (else *prefix*))) +;; load defaults file ("setup.defaults") - (define (load-defaults) - (let ((deff (make-pathname (chicken-home) +defaults-file+))) +(define (load-defaults) + (let ((deff (or user-defaults + (make-pathname host-sharedir +defaults-file+)))) (define (broken x) (error "invalid entry in defaults file" deff x)) (cond ((not (file-exists? deff)) '()) @@ -166,19 +184,19 @@ ((not (= (cadr x) +defaults-version+)) (error (sprintf - "version of installed `~a' does not match setup-API version (~a)" + "version of installed `~a' does not match chicken-install version (~a)" +defaults-file+ +defaults-version+) (cadr x))) - ;; ignored + ;; others are ignored )) ((server) - (set! *default-sources* - (append *default-sources* (list (cdr x))))) + (set! default-servers + (append default-servers (cdr x)))) ((map) - (set! *mappings* + (set! mappings (append - *mappings* + mappings (map (lambda (m) (let ((p (list-index (cut eq? '-> <>) m))) (unless p (broken x)) @@ -186,924 +204,547 @@ (cons from (cdr to))))) (cdr x))))) ((alias) - (set! *aliases* + (set! aliases (append - *aliases* + aliases (map (lambda (a) (if (and (list? a) (= 2 (length a)) (every string? a)) (cons (car a) (cadr a)) (broken x))) (cdr x))))) ((override) - (set! *override* + (set! override (if (and (pair? (cdr x)) (string? (cadr x))) (call-with-input-file (cadr x) read-all) (cdr x)))) + ((location) + (set! default-locations + (append default-locations (list (cdr x))))) ((hack) - (set! *hacks* (append *hacks* (list (eval (cadr x)))))) + (set! hacks (append hacks (list (eval (cadr x)))))) (else (broken x)))) - (call-with-input-file deff read-all)))) - (pair? *default-sources*) )) - - (define (resolve-location name) - (cond ((assoc name *aliases*) => - (lambda (a) - (let ((new (cdr a))) - ;(print "resolving alias `" name "' to: " new) - (resolve-location new)))) - (else name))) - - (define (known-default-sources) - (if (and *default-location* *default-transport*) - `(((location - ,(if (and (eq? *default-transport* 'local) - (not (absolute-pathname? *default-location*) )) - (make-pathname (current-directory) *default-location*) - *default-location*)) - (transport ,*default-transport*))) - *default-sources* ) ) - - (define (deps key meta) - (or (and-let* ((d (assq key meta))) - (cdr d)) - '())) - - (define (init-repository dir) - (let ((src (repo-path)) - (copy (if *windows-shell* - "copy" - "cp -r"))) - (create-directory dir #t) - (print "copying required files to " dir " ...") - (for-each - (lambda (f) - (command "~a ~a ~a" copy (shellpath (make-pathname src f)) (shellpath dir))) - +default-repository-files+))) - - (define (ext-version x) - (cond ((or (eq? x 'chicken) (equal? x "chicken")) - (chicken-version)) - ((let* ((ep (##sys#canonicalize-extension-path x 'ext-version)) - (sf (make-pathname (repo-path) ep "setup-info"))) - (and (file-exists? sf) - (with-input-from-file sf read))) => - (lambda (info) - (let ((a (assq 'version info))) - (if a - (->string (cadr a)) - "0.0.0")))) - (else #f))) - - (define (meta-dependencies meta) - (append - (deps 'depends meta) - (deps 'needs meta) - (if *run-tests* (deps 'test-depends meta) '()))) - - (define (check-dependency dep) - (cond ((or (symbol? dep) (string? dep)) - (values (and (not (ext-version dep)) - (->string dep)) - #f)) - ((and (list? dep) (eq? 'or (car dep))) - (let scan ((ordeps (cdr dep)) (bestm #f) (bestu #f)) - (if (null? ordeps) - (values - (cond (bestu #f) ; upgrade overrides new - (bestm bestm) - (else #f)) - bestu) - (let-values (((m u) (check-dependency (car ordeps)))) - (if (and (not m) (not u)) - (values #f #f) - (scan (cdr ordeps) - (if (and m (not bestm)) - m - bestm) - (if (and u (not bestu)) - u - bestu))))))) - ((and (list? dep) (= 2 (length dep)) - (or (string? (car dep)) (symbol? (car dep)))) - (let ((v (ext-version (car dep)))) - (cond ((not v) - (values (->string (car dep)) #f)) - ((not (version>=? v (->string (cadr dep)))) - (cond ((string=? "chicken" (->string (car dep))) - (if *force* - (values #f #f) - (error - (string-append - "Your CHICKEN version is not recent enough to use this extension - version " - (cadr dep) - " or newer is required")))) - (else - (values - #f - (cons (->string (car dep)) (->string (cadr dep))))))) - (else (values #f #f))))) - (else - (warning - "invalid dependency syntax in extension meta information" - dep) - (values #f #f)))) - - (define (outdated-dependencies egg meta) - (let ((ds (meta-dependencies meta))) - (for-each - (lambda (h) (set! ds (h egg ds))) - *hacks*) - (let loop ((deps ds) (missing '()) (upgrade '())) - (if (null? deps) - (values (reverse missing) (reverse upgrade)) - (let ((dep (car deps)) - (rest (cdr deps))) - (let-values (((m u) (check-dependency dep))) - (loop rest - (if m (cons m missing) missing) - (if u (cons u upgrade) upgrade)))))))) - - (define *eggs+dirs+vers* '()) - (define *dependencies* '()) - (define *checked* '()) - - (define *csi* - (shellpath (make-pathname *program-path* C_CSI_PROGRAM))) + (call-with-input-file deff read-all)))))) - (define (try-extension name version trans locn) - (condition-case - (retrieve-extension - name trans locn - version: version - destination: (and *retrieve-only* (current-directory)) - tests: *run-tests* - username: *username* - password: *password* - trunk: *trunk* - proxy-host: *proxy-host* - proxy-port: *proxy-port* - proxy-user-pass: *proxy-user-pass* - clean: (and (not *retrieve-only*) (not *keep*))) - [(exn net) - (print "TCP connect timeout") - (values #f "") ] - [(exn http-fetch) - (print "HTTP protocol error") - (values #f "") ] - [e (exn setup-download-error) - (print "Server error:") - (print-error-message e) - (values #f "")] - [e () - (abort e) ] ) ) - - (define (with-default-sources proc) - (let ((sources (known-default-sources))) - (let trying-sources ((defs sources)) - (if (null? defs) - (proc #f #f - (lambda () - (with-output-to-port (current-error-port) - (lambda () - (print "Could not determine a source of extensions. " - "Please specify a valid location and transport."))) - (exit 1))) - (let ((def (car defs))) - (if def - (let* ((locn (resolve-location - (cadr (or (assq 'location def) - (error "missing location entry" def))))) - (trans (cadr (or (assq 'transport def) - (error "missing transport entry" def))))) - (proc trans locn - (lambda () - (unless (eq? 'local trans) - ;; invalidate this entry in the list of sources - (set-car! defs #f)) - (trying-sources (cdr defs))))) - (trying-sources (cdr defs)))))))) - - (define (try-default-sources name version) - (with-default-sources - (lambda (trans locn next) - (if (not trans) - (values #f "") - (let-values (((dir ver) (try-extension name version trans locn))) - (if dir - (values dir ver) - (next))))))) - - (define (make-replace-extension-question e+d+v upgrade) - (string-intersperse - (append - (list "The following installed extensions are outdated, because `" - (car e+d+v) - "' requires later versions:\n") - (filter-map - (lambda (e) - (cond ((assq (string->symbol (car e)) *override*) => - (lambda (a) - (unless (equal? (cadr a) (cdr e)) - (warning - (sprintf "version `~a' of extension `~a' overrides required version `~a'" - (cadr a) (car a) (cdr e)))) - #f)) - (else - (conc - " " (car e) - " (" (let ((v (assq 'version (extension-information (car e))))) - (if v (cadr v) "???")) - " -> " (cdr e) ")" - #\newline) ))) - upgrade) - '("\nDo you want to replace the existing extensions?")) - "")) - - (define (override-version egg) - (let ((name (string->symbol (if (pair? egg) (car egg) egg)))) - (cond ((assq name *override*) => - (lambda (a) - (cond ((and (pair? egg) (not (equal? (cadr a) (cdr egg)))) - (warning - (sprintf - "version `~a' of extension `~a' overrides explicitly given version `~a'" - (cadr a) name (cdr egg)))) - (else - (print "overriding: " a))) - (cadr a))) - ((pair? egg) (cdr egg)) - (else #f)))) + +;; set variables with HTTP proxy information + +(define (setup-proxy uri) + (and-let* (((string? uri)) + (m (irregex-match "(http://)?([^:]+):?([0-9]*)" uri)) + (port (irregex-match-substring m 3))) + (set! proxy-user-pass (get-environment-variable "proxy_auth")) + (set! proxy-host (irregex-match-substring m 2)) + (set! proxy-port (or (string->number port) 80)))) - (define (show-depends eggs . type) - (print "fetching meta information...") - (retrieve eggs) - (let ((type (optional type 'depends))) - (printf "~a dependencies as reported in .meta:\n" - (case type ((depends) "Egg") - ((foreign-depends) "Foreign"))) - (for-each - (lambda (egg) - (and-let* ((meta-file (make-pathname (cadr egg) (car egg) "meta")) - (m (and (file-exists? meta-file) (with-input-from-file meta-file read))) - (ds (if (eq? type 'depends) - (append (deps 'needs m) (deps type m)) - (deps type m)))) - (unless (null? ds) - (print (car egg) ": ") - (for-each (cut print "\t" <>) ds)))) - *eggs+dirs+vers*) - (cleanup) - (exit 0))) + +;; apply egg->egg mappings loaded from defaults + +(define (apply-mappings eggs) + (define (canonical x) + (cond ((symbol? x) (cons (symbol->string x) #f)) + ((string? x) (cons x #f)) + ((pair? x) x) + (else (error "internal error - bad egg spec" x)))) + (define (same? e1 e2) + (equal? (car (canonical e1)) (car (canonical e2)))) + (let ((eggs2 + (delete-duplicates + (append-map + (lambda (egg) + (cond ((find (lambda (m) (find (cut same? egg <>) (car m))) + mappings) => + (lambda (m) (map ->string (cdr m)))) + (else (list egg)))) + eggs) + same?))) + (unless (and (= (length eggs) (length eggs2)) + (every (lambda (egg) (find (cut same? <> egg) eggs2)) eggs)) + (d "mapped ~s to ~s~%" eggs eggs2)) + eggs2)) - (define (retrieve eggs) - (print "retrieving ...") - (for-each - (lambda (egg) - (cond [(assoc egg *eggs+dirs+vers*) => - (lambda (a) - ;; push to front - (set! *eggs+dirs+vers* (cons a (delete a *eggs+dirs+vers* eq?))) ) ] - [else + +;; override versions, if specified in "overrides" file + +(define (override-version egg) + (let ((name (string->symbol (if (pair? egg) (car egg) egg)))) + (cond ((assq name override) => + (lambda (a) + (cond ((and (pair? egg) (not (equal? (cadr a) (cdr egg)))) + (warning + (sprintf + "version `~a' of extension `~a' overrides explicitly given version `~a'" + (cadr a) name (cdr egg)))) + (else (d "overriding: ~a~%" a))) + (cadr a))) + ((pair? egg) (cdr egg)) + (else #f)))) + + +;; "locate" egg: either perform HTTP download or copy from a file-system +;; location, also make sure it is up to date + +(define (locate-egg name version) + (let* ((cached (make-pathname cache-directory name)) + (now (current-seconds)) + (timestamp (make-pathname cached +timestamp-file+)) + (status (make-pathname cached +status-file+)) + (eggfile (make-pathname cached name +egg-extension+))) + (define (fetch) + (when (file-exists? cached) + (delete-directory cached #t)) + (create-directory cached) + (fetch-egg-sources name version cached) + (with-output-to-file status (cut write current-status))) + (unless (file-exists? cache-directory) + (create-directory cache-directory)) + (cond ((not (probe-dir cached)) (fetch)) + ((and (file-exists? status) + (not (equal? current-status + (with-input-from-file status read)))) + (fetch))) + (let* ((info (load-egg-info eggfile)) + (lversion (get-egg-property info 'version))) + (cond ((and (file-exists? timestamp) + (> (- now (with-input-from-file timestamp read)) +one-hour+) + (not (check-remote-version name version lversion))) + (fetch) + (let ((info (load-egg-info eggfile))) ; new egg info (fetched) + (values cached (get-egg-property info 'version)))) + (else (values cached version)))))) + +(define (resolve-location name) + (cond ((assoc name aliases) => + (lambda (a) + (let ((new (cdr a))) + (d "resolving alias `~a' to: ~a~%" name new) + (resolve-location new)))) + (else name))) + +(define (fetch-egg-sources name version dest) + (let loop ((locs default-locations)) + (cond ((null? locs) + (let ((tmpdir (create-temporary-directory))) + (let loop ((srvs default-servers)) + (receive (dir ver) + (try-download name (resolve-location (car srvs)) + version: version + destination: tmpdir + tests: run-tests + proxy-host: proxy-host + proxy-port: proxy-port + proxy-user-pass: proxy-user-pass) + (cond (dir + (rename-file tmpdir dest) + (with-output-to-file + (make-pathname dest +timestamp-file+) + (lambda () (write (current-seconds))))) + ((null? srvs) (error "extension or version not found")) + (else (loop (cdr srvs)))))))) + ((probe-dir (make-pathname (car locs) name)) + => (lambda (dir) + (let* ((eggfile (make-pathname dir name +egg-extension+)) + (info (load-egg-info eggfile)) + (rversion (get-egg-property info 'version))) + (if (or (not rversion) + (version>=? rversion version)) + (copy-egg-sources dir dest) + (loop (cdr locs)))))) + (else (loop (cdr locs)))))) + +(define (copy-egg-sources from to) + ;;XXX should probably be done manually, instead of calling tool + (let ((cmd (quote-all + (string-append + (copy-directory-command platform) + " " (quotearg from) " " (quotearg to)) + platform))) + (system cmd))) + +(define (check-remote-version name version lversion) + (let loop ((locs default-locations)) + (cond ((null? locs) + (let loop ((srvs default-servers)) + (and (pair? srvs) + (let ((versions (try-list-versions name (car srvs)))) + (or (and versions + (any (cut version>=? <> version) versions)) + (loop (cdr srvs))))))) + ((probe-dir (make-pathname (car locs) name)) + => (lambda (dir) + (let* ((eggfile (make-pathname dir name +egg-extension+)) + (info (load-egg-info eggfile)) + (rversion (get-egg-property info 'version))) + (or (and rversion + (version>=? rversion version)) + (loop (cdr locs)))))) + (else (loop (cdr locs)))))) + + +;; retrieve eggs, recursively (if needed) + +(define (retrieve-eggs eggs) + (for-each + (lambda (egg) + (cond ((assoc egg canonical-eggs) => + (lambda (a) + ;; push to front + (set! canonical-eggs (cons a (delete a canonical-eggs eq?))))) + (else (let ((name (if (pair? egg) (car egg) egg)) (version (override-version egg))) - (let-values (((dir ver) (try-default-sources name version))) + (let-values (((dir ver) (locate-egg name version))) (when (or (not dir) (null? (directory dir))) (error "extension or version not found")) - (print " " name " located at " dir) - (set! *eggs+dirs+vers* (cons (list name dir ver) *eggs+dirs+vers*)) ) ) ] ) ) + (d " ~a located at ~a~%") + (set! canonical-eggs + (cons (list name dir ver) canonical-eggs))))))) eggs) - (unless *retrieve-only* - (for-each - (lambda (e+d+v) - (unless (member (car e+d+v) *checked*) - (set! *checked* (cons (car e+d+v) *checked*)) - (let ((mfile (make-pathname (cadr e+d+v) (car e+d+v) "meta"))) - (cond [(file-exists? mfile) - (let ((meta (with-input-from-file mfile read))) - (print "checking platform for `" (car e+d+v) "' ...") - (check-platform (car e+d+v) meta) - (print "checking dependencies for `" (car e+d+v) "' ...") - (let-values (((missing upgrade) - (outdated-dependencies (car e+d+v) meta))) - (set! missing (apply-mappings missing)) ;XXX only missing - wrong? - (set! *dependencies* - (cons - (cons (car e+d+v) - (map (lambda (mu) - (if (pair? mu) - (car mu) - mu)) - (append missing upgrade))) - *dependencies*)) - (when (pair? missing) - (print " missing: " (string-intersperse missing ", ")) - (retrieve missing)) - (when (and (pair? upgrade) - (or *force* - (yes-or-no? - (make-replace-extension-question e+d+v upgrade) - default: "no" - abort: (abort-setup) ) ) ) - (let ([ueggs (unzip1 upgrade)]) - (print " upgrade: " (string-intersperse ueggs ", ")) - (for-each - (lambda (e) - (print "removing previously installed extension `" e "' ...") - (remove-extension e) ) - ueggs) - (retrieve ueggs) ) ) ) ) ] - [else - (warning - (string-append - "extension `" (car e+d+v) "' has no .meta file " - "- assuming it has no dependencies")) ] ) ) ) ) - *eggs+dirs+vers*) ) ) - - (define (check-platform name meta) - (define (fail) - (error "extension is not targeted for this system" name)) - (unless *cross-chicken* - (and-let* ((platform (assq 'platform meta))) - (let loop ((p (cadr platform))) - (cond ((symbol? p) - (or (feature? p) (fail))) - ((not (list? p)) - (error "invalid `platform' property" name (cadr platform))) - ((and (eq? 'not (car p)) (pair? (cdr p))) - (and (not (loop (cadr p))) (fail))) - ((eq? 'and (car p)) - (and (every loop (cdr p)) (fail))) - ((eq? 'or (car p)) - (and (not (any loop (cdr p))) (fail))) - (else (error "invalid `platform' property" name (cadr platform)))))))) - - (define (back-slash->forward-slash path) - (if *windows-shell* - (string-translate path #\\ #\/) - path)) - - (define (make-install-command egg-name egg-version dep?) - (conc - *csi* - " -bnq " - (if (or *deploy* - (and *cross-chicken* ; disable -setup-mode when cross-compiling, - (not *host-extension*))) ; host-repo must always take precedence - "" - "-setup-mode ") - "-e \"(import setup-api)\" " - (if *debug-setup* - "" - "-e \"(setup-error-handling)\" ") - (sprintf "-e \"(extension-name-and-version '(\\\"~a\\\" \\\"~a\\\"))\"" - egg-name egg-version) - (if (sudo-install) " -e \"(sudo-install #t)\"" "") - (if *keep* " -e \"(keep-intermediates #t)\"" "") - (if (and *no-install* (not dep?)) " -e \"(setup-install-mode #f)\"" "") - (if *host-extension* " -e \"(host-extension #t)\"" "") - (let ((prefix (get-prefix))) - (if prefix - (sprintf " -e \"(destination-prefix \\\"~a\\\")\"" - (back-slash->forward-slash (normalize-pathname prefix))) - "")) - (let ((prefix (get-prefix #t))) - (if prefix - (sprintf " -e \"(runtime-prefix \\\"~a\\\")\"" - (back-slash->forward-slash (normalize-pathname prefix))) - "")) - (if (pair? *csc-features*) - (sprintf " -e \"(extra-features '~s)\"" *csc-features*) - "") - (if (pair? *csc-nonfeatures*) - (sprintf " -e \"(extra-nonfeatures '~s)\"" *csc-nonfeatures*) - "") - (if *deploy* " -e \"(deployment-mode #t)\"" "") - #\space - (shellpath (string-append egg-name ".setup"))) ) - - (define-syntax keep-going - (syntax-rules () - ((_ (name mode) body ...) - (let ((tmp (lambda () body ...))) - (if *keep-going* - (handle-exceptions ex - (begin - (print mode " extension `" name "' failed:") - (print-error-message ex) - (print "\nnevertheless trying to continue ...") - #f) - (tmp)) - (tmp)))))) - - (define (install eggs) - (when *keep-existing* - (set! eggs - (filter - (lambda (egg) (not (extension-information (if (pair? egg) (car egg) egg)))) - eggs))) - (retrieve eggs) - (unless *retrieve-only* - (let* ((dag (reverse (topological-sort *dependencies* string=?))) - (num (length dag)) - (depinstall-ok *force*) - (eggs+dirs+vers (map (cut assoc <> *eggs+dirs+vers*) dag))) - (and-let* ((ibad (list-index not eggs+dirs+vers))) - ;; A dependency was left unretrieved, most likely because the user declined an upgrade. - (fprintf (current-error-port) "\nUnresolved dependency: ~a\n\n" (list-ref dag ibad)) - (cleanup) - (exit 1)) - (print "install order:") - (pp dag) - (for-each - (lambda (e+d+v i) - (let ((isdep (and (pair? eggs) - (not (find (lambda (e) - (equal? (if (pair? e) (car e) e) (car e+d+v))) - eggs))))) - (when (and (not depinstall-ok) isdep) - (when (and *no-install* - (not (yes-or-no? - (string-append - "You specified `-no-install', but this extension has dependencies" - " that are required for building.\nDo you still want to install them?") - abort: (abort-setup)))) - (print "aborting installation.") - (cleanup) - (exit 1))) - (print "installing " (car e+d+v) #\: (caddr e+d+v) " ...") - (let ((tmpcopy (and *target-extension* - *host-extension* - (create-temporary-directory))) - (eggdir (cadr e+d+v))) - (when tmpcopy - (print "copying sources for target installation") - (command - "~a ~a ~a" - (if *windows-shell* "xcopy" "cp -r") - (make-pathname eggdir "*") - tmpcopy)) - (let ((setup - (lambda (dir) - (print "changing current directory to " dir) - (let ((old-dir (current-directory))) - (dynamic-wind - (lambda () - (change-directory dir)) - (lambda () - (when *cross-chicken* - (delete-stale-binaries)) - (let ((cmd (make-install-command - (car e+d+v) (caddr e+d+v) (> i 1))) - (name (car e+d+v))) - (keep-going - (name "installing") - ($system cmd)) - (when (and *run-tests* - (not isdep) - (file-exists? "tests") - (directory? "tests") - (file-exists? "tests/run.scm") ) - (set! *running-test* #t) - (current-directory "tests") - (keep-going - (name "testing") - (command "~a -s run.scm ~a ~a" *csi* name (caddr e+d+v))) - (set! *running-test* #f)))) - (lambda () - (change-directory old-dir))))))) - (if (and *target-extension* *host-extension*) - (fluid-let ((*deploy* #f) - (*prefix* #f)) - (setup eggdir)) - (setup eggdir)) - (when (and *target-extension* *host-extension*) - (print "installing for target ...") - (fluid-let ((*host-extension* #f)) - (setup tmpcopy))))))) - eggs+dirs+vers - (let loop ((i num)) - (if (zero? i) - '() - (cons num (loop (fx- i 1))))))))) - - (define (delete-stale-binaries) - (print* "deleting stale binaries ...") - (print* "deleting stale binaries ...") - (find-files "." test: `(seq (* any) "." (or "o" "so" "dll" "a")) - action: (lambda (f _) - (print* " " f) - (delete-file* f))) - (newline)) - - (define (cleanup) - (unless *keep* - (and-let* ((tmpdir (temporary-directory))) - (remove-directory tmpdir)))) - - (define (update-db) - (let* ((files (glob (make-pathname (repo-path) "*.import.so") - (make-pathname (repo-path) "*.import.scm"))) - (tmpdir (create-temporary-directory)) - (dbfile (make-pathname tmpdir +module-db+))) - (print "loading import libraries ...") - (fluid-let ((##sys#warnings-enabled #f)) - (for-each - (lambda (path) - (let* ((file (pathname-strip-directory path)) - (import-name (pathname-strip-extension file)) - (module-name (pathname-strip-extension import-name))) - (handle-exceptions ex - (print-error-message - ex (current-error-port) - (sprintf "Failed to import from `~a'" file)) - (eval `(import-syntax ,(string->symbol module-name)))))) - files)) - (print "generating database") - (let ((db - (sort - (append-map - (lambda (m) - (let* ((mod (cdr m)) - (mname (##sys#module-name mod))) - (print* " " mname) - (let-values (((_ ve se) (##sys#module-exports mod))) - (append - (map (lambda (se) (list (car se) 'syntax mname)) se) - (map (lambda (ve) (list (car ve) 'value mname)) ve))))) - ##sys#module-table) - (lambda (e1 e2) - (string<? (symbol->string (car e1)) (symbol->string (car e2))))))) - (newline) - (with-output-to-file dbfile - (lambda () - (for-each (lambda (x) (write x) (newline)) db))) - (copy-file dbfile (make-pathname (repo-path) +module-db+)) - (remove-directory tmpdir)))) - - (define (apply-mappings eggs) - (define (canonical x) - (cond ((symbol? x) (cons (symbol->string x) #f)) - ((string? x) (cons x #f)) - ((pair? x) x) - (else (error "internal error - bad egg spec" x)))) - (define (same? e1 e2) - (equal? (car (canonical e1)) (car (canonical e2)))) - (let ((eggs2 - (delete-duplicates - (append-map - (lambda (egg) - (cond ((find (lambda (m) (find (cut same? egg <>) (car m))) - *mappings*) => - (lambda (m) (map ->string (cdr m)))) - (else (list egg)))) - eggs) - same?))) - (unless (and (= (length eggs) (length eggs2)) - (every (lambda (egg) (find (cut same? <> egg) eggs2)) eggs)) - (print "mapped " eggs " to " eggs2)) - eggs2)) - - (define (scan-directory dir) + (unless retrieve-only (for-each - (lambda (info) - (pp (cons (car info) (cadadr info)))) - (gather-egg-information dir))) - - (define ($system str) - (let ((str (cond (*windows-shell* - (string-append "\"" str "\"")) - ((and (eq? (software-version) 'macosx) - (get-environment-variable "DYLD_LIBRARY_PATH")) - => (lambda (path) - (string-append "/usr/bin/env DYLD_LIBRARY_PATH=" - (qs path) " " str))) - (else str)))) - (print " " str) - (let ((r (system str))) - (unless (zero? r) - (error "shell command terminated with nonzero exit code" r str))))) - - (define (installed-extensions) - (delete-duplicates - (filter-map - (lambda (sf) - (let* ((info (first (call-with-input-file sf read-all))) - (v (cond ((assq 'version info) => cadr) - (else "")))) - (cond ((assq 'egg-name info) => - (lambda (a) (cons (cadr a) (->string v)))) - (else - (warning - "installed extension has no information about which egg it belongs to" - (pathname-file sf)) - #f)))) - (glob (make-pathname (repo-path) "*" "setup-info"))) - equal?)) - - (define (list-available-extensions trans locn) - (with-default-sources - (lambda (trans locn next) - (if trans - (list-extensions - trans locn - quiet: #t - username: *username* - password: *password* - proxy-host: *proxy-host* - proxy-port: *proxy-port* - proxy-user-pass: *proxy-user-pass*) - (next))))) - - (define (command fstr . args) - (let ((cmd (apply sprintf fstr args))) - ($system cmd))) - - (define (usage code) - (print #<<EOF -usage: chicken-install [OPTION | EXTENSION[:VERSION]] ... - - -h -help show this message and exit - -version show version and exit - -force don't ask, install even if versions don't match - -k -keep keep temporary files - -x -keep-installed install only if not already installed - -reinstall reinstall all currently installed extensions - -l -location LOCATION install from given location instead of default - -t -transport TRANSPORT use given transport instead of default - -proxy HOST[:PORT] download via HTTP proxy - -s -sudo use external command to elevate privileges for filesystem operations - -r -retrieve only retrieve egg into current directory, don't install - -n -no-install do not install, just build (implies `-keep') - -p -prefix PREFIX change installation prefix to PREFIX - -list list extensions available over selected transport and location - -host when cross-compiling, compile extension only for host - -target when cross-compiling, compile extension only for target - -test run included test-cases, if available - -username USER set username for transports that require this - -password PASS set password for transports that require this - -i -init DIRECTORY initialize empty alternative repository - -u -update-db update export database - -repository print path used for egg installation - -deploy build extensions for deployment - -trunk build trunk instead of tagged version (only local) - -D -feature FEATURE features to pass to sub-invocations of `csc' - -debug enable full display of error message information - -keep-going continue installation even if dependency fails - -scan DIRECTORY scan local directory for highest available egg versions - -override FILENAME override versions for installed eggs with information from file - -csi FILENAME use given pathname for invocations of "csi" - -show-depends display a list of egg dependencies for the given egg(s) - -show-foreign-depends display a list of foreign dependencies for the given egg(s) - -chicken-install recognizes the SUDO, http_proxy and proxy_auth environment variables, if set. - -EOF -);| - (exit code)) - - (define (setup-proxy uri) - (and-let* (((string? uri)) - (m (irregex-match "(http://)?([^:]+):?([0-9]*)" uri)) - (port (irregex-match-substring m 3))) - (set! *proxy-user-pass* (get-environment-variable "proxy_auth")) - (set! *proxy-host* (irregex-match-substring m 2)) - (set! *proxy-port* (or (string->number port) 80)))) - - (define (info->egg info) - (if (member (cdr info) '("" "unknown" "trunk")) - (car info) - info)) + (lambda (e+d+v) + (unless (member (car e+d+v) checked-eggs) + (d "checking ~a ...~%" (car e+d+v)) + (set! checked-eggs (cons (car e+d+v) checked-eggs)) + (let* ((fname (make-pathname (cadr e+d+v) (car e+d+v) +egg-extension+)) + (info (load-egg-info fname))) + (d "checking platform for `~a'~%" (car e+d+v)) + (check-platform (car e+d+v) info) + (d "checking dependencies for `~a'~%" (car e+d+v)) + (let-values (((missing upgrade) + (outdated-dependencies (car e+d+v) info))) + (set! missing (apply-mappings missing)) + (set! dependencies + (cons (cons (car e+d+v) + (map (lambda (mu) + (if (pair? mu) + (car mu) + mu)) + (append missing upgrade))) + dependencies)) + (when (pair? missing) + (print " missing: " (string-intersperse missing ", ")) + (retrieve-eggs missing)) + (when (and (pair? upgrade) + (or force-install + (replace-extension-question e+d+v upgrade))) + (let ((ueggs (unzip1 upgrade))) + (d " upgrade: ~a~%" (string-intersperse ueggs ", ")) + ;; XXX think about this... + #;(for-each + (lambda (e) + (d "removing previously installed extension `~a'" e) + (remove-extension e) ) ; - not implemented yet + ueggs) + (retrieve-eggs ueggs) ) ) ) ) ) ) + canonical-eggs))) + +(define (outdated-dependencies egg info) + (let ((ds (get-egg-dependencies info))) + (for-each + (lambda (h) (set! ds (h egg ds))) + hacks) + (let loop ((deps ds) (missing '()) (upgrade '())) + (if (null? deps) + (values (reverse missing) (reverse upgrade)) + (let ((dep (car deps)) + (rest (cdr deps))) + (let-values (((m u) (check-dependency dep))) + (loop rest + (if m (cons m missing) missing) + (if u (cons u upgrade) upgrade)))))))) + +(define (get-egg-dependencies info) + (append (get-egg-property info 'dependencies '()) + (if run-tests (get-egg-property info 'test-dependencies '()) '()))) + +(define (check-dependency dep) + (cond ((or (symbol? dep) (string? dep)) + (values (and (not (ext-version dep)) (->string dep)) + #f)) + ((and (list? dep) (eq? 'or (car dep))) + (let scan ((ordeps (cdr dep)) (bestm #f) (bestu #f)) + (if (null? ordeps) + (values (cond (bestu #f) ; upgrade overrides new + (bestm bestm) + (else #f)) + bestu) + (let-values (((m u) (check-dependency (car ordeps)))) + (if (and (not m) (not u)) + (values #f #f) + (scan (cdr ordeps) + (if (and m (not bestm)) + m + bestm) + (if (and u (not bestu)) + u + bestu))))))) + ((and (list? dep) (= 2 (length dep)) + (or (string? (car dep)) (symbol? (car dep)))) + (let ((v (ext-version (car dep)))) + (cond ((not v) + (values (->string (car dep)) #f)) + ((not (version>=? v (->string (cadr dep)))) + (cond ((string=? "chicken" (->string (car dep))) + (if force-install + (values #f #f) + (error + (string-append + "Your CHICKEN version is not recent enough to use this extension - version " + (cadr dep) + " or newer is required")))) + (else + (values #f + (cons (->string (car dep)) (->string (cadr dep))))))) + (else (values #f #f))))) + (else + (warning "invalid dependency syntax in extension meta information" + dep) + (values #f #f)))) + +(define (ext-version x) + (cond ((or (eq? x 'chicken) (equal? x "chicken")) + (chicken-version)) + ((let* ((ep (##sys#canonicalize-extension-path x 'ext-version)) + (sf (make-pathname (repo-path) ep +egg-info-extension+))) + (and (file-exists? sf) + (load-egg-info sf #f))) => + (lambda (info) + (let ((a (assq 'version info))) + (if a + (->string (cadr a)) + "0.0.0")))) + (else #f))) + +(define (check-platform name info) + (define (fail) + (error "extension is not targeted for this system" name)) + (unless cross-chicken + (and-let* ((platform (get-egg-property info 'platform))) + (let loop ((p platform)) + (cond ((symbol? p) + (or (feature? p) (fail))) + ((not (list? p)) + (error "invalid `platform' property" name platform)) + ((and (eq? 'not (car p)) (pair? (cdr p))) + (and (not (loop (cadr p))) (fail))) + ((eq? 'and (car p)) + (and (every loop (cdr p)) (fail))) + ((eq? 'or (car p)) + (and (not (any loop (cdr p))) (fail))) + (else (error "invalid `platform' property" name platform))))))) + +(define (replace-extension-question e+d+v upgrade) + (print (string-intersperse + (append + (list "The following installed extensions are outdated, because `" + (car e+d+v) + "' requires later versions:\n") + (filter-map + (lambda (e) + (cond ((assq (string->symbol (car e)) override) => + (lambda (a) + (unless (equal? (cadr a) (cdr e)) + (warning + (sprintf "version `~a' of extension `~a' overrides required version `~a'" + (cadr a) (car a) (cdr e)))) + #f)) + (else + (conc + " " (car e) + " (" (let ((v (assq 'version (extension-information (car e))))) + (if v (cadr v) "???")) + " -> " (cdr e) ")" + #\newline) ))) + upgrade) + '("\nDo you want to replace the existing extensions ? (yes/no/abort) ")) + "")) + (flush-output) + (let loop () + (let ((r (trim (read-line)))) + (cond ((string=? r "yes")) + ((string=? r "no") #f) + ((string=? r "abort") (exit 1)) + (else (loop)))))) - (define *short-options* '(#\h #\k #\l #\t #\s #\p #\r #\n #\v #\i #\u #\D)) - - (define (main args) - (let ((update #f) - (scan #f) - (listeggs #f) - (print-repository #f) - (rx (irregex "([^:]+):(.+)"))) - (setup-proxy (get-environment-variable "http_proxy")) - (let loop ((args args) (eggs '())) - (cond ((null? args) - (when *deploy* - (unless *prefix* - (error - "`-deploy' only makes sense in combination with `-prefix DIRECTORY`"))) - (cond (print-repository (print (repo-path))) - (update (update-db)) - (scan (scan-directory scan)) - (else - (let ((defaults (load-defaults))) - (when (null? eggs) - (cond (*reinstall* - (let ((egginfos (installed-extensions))) - (if (or *force* - (yes-or-no? - (sprintf - "About to re-install all ~a currently installed extensions - do you want to proceed?" - (length egginfos)) - abort: #f)) - (set! eggs (map info->egg egginfos)) - (exit 1)))) - ((not listeggs) - (let ((setups (glob "*.setup"))) - (cond ((pair? setups) - (set! *eggs+dirs+vers* - (append - (map - (lambda (s) - (cons (pathname-file s) (list "." ""))) - setups) - *eggs+dirs+vers*))) - (else - (print "no setup-scripts to process") - (exit 1))) ) ))) - (unless defaults - (unless *default-transport* - (error - "no default transport defined - please use `-transport' option")) - (unless *default-location* - (error - "no default location defined - please use `-location' option"))) - (cond (listeggs - (display - (list-available-extensions - *default-transport* *default-location*))) - (*show-depends* - (show-depends eggs 'depends)) - (*show-foreign-depends* - (show-depends eggs 'foreign-depends)) - (else - (install (apply-mappings (reverse eggs))))) - )))) - (else - (let ((arg (car args))) - (cond ((or (string=? arg "-help") - (string=? arg "-h") - (string=? arg "--help")) - (usage 0)) - ((string=? arg "-repository") - (set! print-repository #t) - (loop (cdr args) eggs)) - ((string=? arg "-force") - (set! *force* #t) - (loop (cdr args) eggs)) - ((or (string=? arg "-k") (string=? arg "-keep")) - (set! *keep* #t) - (loop (cdr args) eggs)) - ((or (string=? arg "-s") (string=? arg "-sudo")) - (sudo-install #t) - (loop (cdr args) eggs)) - ((or (string=? arg "-r") (string=? arg "-retrieve")) - (set! *retrieve-only* #t) - (loop (cdr args) eggs)) - ((or (string=? arg "-l") (string=? arg "-location")) - (unless (pair? (cdr args)) (usage 1)) - (set! *default-location* (cadr args)) - (loop (cddr args) eggs)) - ((or (string=? arg "-t") (string=? arg "-transport")) - (unless (pair? (cdr args)) (usage 1)) - (set! *default-transport* (string->symbol (cadr args))) - (loop (cddr args) eggs)) - ((or (string=? arg "-p") (string=? arg "-prefix")) - (unless (pair? (cdr args)) (usage 1)) - (set! *prefix* - (let ((p (cadr args))) - (if (absolute-pathname? p) - p - (normalize-pathname - (make-pathname (current-directory) p) ) ) ) ) - (loop (cddr args) eggs)) - ((or (string=? arg "-n") (string=? arg "-no-install")) - (set! *keep* #t) - (set! *no-install* #t) - (loop (cdr args) eggs)) - ((string=? arg "-version") - (print (chicken-version)) - (exit 0)) - ((or (string=? arg "-u") (string=? arg "-update-db")) - (set! update #t) - (loop (cdr args) eggs)) - ((or (string=? arg "-i") (string=? arg "-init")) - (unless (pair? (cdr args)) (usage 1)) - (init-repository (cadr args)) - (exit 0)) - ((string=? "-proxy" arg) - (unless (pair? (cdr args)) (usage 1)) - (setup-proxy (cadr args)) - (loop (cddr args) eggs)) - ((or (string=? "-D" arg) (string=? "-feature" arg)) - (unless (pair? (cdr args)) (usage 1)) - (set! *csc-features* - (cons (string->symbol (cadr args)) *csc-features*)) - (loop (cddr args) eggs)) - ((string=? "-no-feature" arg) - (unless (pair? (cdr args)) (usage 1)) - (set! *csc-nonfeatures* - (cons (string->symbol (cadr args)) *csc-nonfeatures*)) - (loop (cddr args) eggs)) - ((string=? "-test" arg) - (set! *run-tests* #t) - (loop (cdr args) eggs)) - ((string=? "-host" arg) - (set! *target-extension* #f) - (loop (cdr args) eggs)) - ((string=? "-target" arg) - (set! *host-extension* #f) - (loop (cdr args) eggs)) - ((string=? "-debug" arg) - (set! *debug-setup* #t) - (loop (cdr args) eggs)) - ((string=? "-deploy" arg) - (set! *deploy* #t) - (loop (cdr args) eggs)) - ((string=? "-username" arg) - (unless (pair? (cdr args)) (usage 1)) - (set! *username* (cadr args)) - (loop (cddr args) eggs)) - ((string=? "-scan" arg) - (unless (pair? (cdr args)) (usage 1)) - (set! scan (cadr args)) - (loop (cddr args) eggs)) - ((string=? "-override" arg) - (unless (pair? (cdr args)) (usage 1)) - (set! *override* (call-with-input-file (cadr args) read-all)) - (loop (cddr args) eggs)) - ((or (string=? "-x" arg) (string=? "-keep-installed" arg)) - (set! *keep-existing* #t) - (loop (cdr args) eggs)) - ((string=? "-reinstall" arg) - (set! *reinstall* #t) - (loop (cdr args) eggs)) - ((string=? "-trunk" arg) - (set! *trunk* #t) - (loop (cdr args) eggs)) - ((string=? "-keep-going" arg) - (set! *keep-going* #t) - (loop (cdr args) eggs)) - ((string=? "-list" arg) - (set! listeggs #t) - (loop (cdr args) eggs)) - ((string=? "-csi" arg) - (unless (pair? (cdr args)) (usage 1)) - (set! *csi* (cadr args)) - (loop (cddr args) eggs)) - ((string=? "-password" arg) - (unless (pair? (cdr args)) (usage 1)) - (set! *password* (cadr args)) - (loop (cddr args) eggs)) - ((string=? "-show-depends" arg) - (set! *show-depends* #t) - (loop (cdr args) eggs)) - ((string=? "-show-foreign-depends" arg) - (set! *show-foreign-depends* #t) - (loop (cdr args) eggs)) - ((and (positive? (string-length arg)) - (char=? #\- (string-ref arg 0))) - (if (> (string-length arg) 2) - (let ((sos (string->list (substring arg 1)))) - (if (every (cut memq <> *short-options*) sos) - (loop (append - (map (cut string #\- <>) sos) - (cdr args)) - eggs) - (usage 1))) - (usage 1))) - ((equal? "setup" (pathname-extension arg)) - (let ((egg (pathname-file arg))) - (set! *eggs+dirs+vers* - (alist-cons - egg - (list - (let ((dir (pathname-directory arg))) - (if dir - (if (absolute-pathname? dir) - dir - (make-pathname (current-directory) dir) ) - (current-directory))) - "") - *eggs+dirs+vers*)) - (loop (cdr args) (cons egg eggs)))) - ((irregex-match rx arg) => - (lambda (m) - (loop - (cdr args) - (alist-cons - (irregex-match-substring m 1) - (irregex-match-substring m 2) - eggs)))) - (else (loop (cdr args) (cons arg eggs)))))))))) - - (register-feature! 'chicken-install) - - (handle-exceptions ex - (begin - (newline (current-error-port)) - (print-error-message ex (current-error-port)) - (cleanup) - (exit (if *running-test* 2 1))) - (main (command-line-arguments)) - (cleanup)) +(define (trim str) + (define (left lst) + (cond ((null? lst) '()) + ((char-whitespace? (car lst)) (left (cdr lst))) + (else (cons (car lst) (left (cdr lst)))))) + (list->string (reverse (left (reverse (left (string->list str))))))) + + +;; list available egg versions + +(define (list-egg-versions eggs) + (let ((srvs (map resolve-location default-servers))) + (let loop1 ((eggs eggs)) + (unless (null? eggs) + (let* ((egg (car eggs)) + (name (if (pair? egg) (car egg) egg))) + (let loop2 ((srvs srvs)) + (and (pair? srvs) + (let ((versions (try-list-versions name (car srvs)))) + (or (and versions + (begin + (printf "~a:" name) + (for-each (cut printf " ~a" <>) versions))) + (loop2 (cdr srvs)))))) + (loop1 (cdr eggs))))))) -) ;module main + +;; perform installation of retrieved eggs + +(define (install-eggs) + (for-each + (lambda (egg) + (let* ((name (car egg)) + (dir (cadr egg)) + (eggfile (make-pathname dir name +egg-extension+)) + (info (load-egg-info eggfile #f))) + (when (or host-extension + (and (not target-extension) + (not host-extension))) + (let-values (((build install info) (compile-egg-info info platform 'host))) + (let ((bscript (make-pathname dir name + (build-script-extension 'host platform))) + (iscript (make-pathname dir name + (install-script-extension 'host + platform)))) + (generate-shell-commands platform build bscript dir + (build-prefix 'host name info) + (build-suffix 'host name info)) + (generate-shell-commands platform install iscript dir + (install-prefix 'host name info) + (install-suffix 'host name info)) + (run-script dir bscript platform) + (run-script dir iscript platform)))) + (when target-extension + (let-values (((build install info) (compile-egg-info info platform 'target))) + (let ((bscript (make-pathname dir name + (build-script-extension 'target platform))) + (iscript (make-pathname dir name + (install-script-extension 'target + platform)))) + (generate-shell-commands platform build bscript dir + (build-prefix 'target name info) + (build-suffix 'target name info)) + (generate-shell-commands platform install iscript dir + (install-prefix 'target name info) + (install-suffix 'target name info)) + (run-script dir bscript platform #f) + (run-script dir iscript platform sudo-install)))))) + canonical-eggs)) + +(define (run-script dir script platform sudo?) + (if do-not-build + (print script) + (let ((old (current-directory))) + (change-directory dir) + (d "running script ~a~%" script) + (if (eq? platform 'windows) + (exec script) + (exec (string-append (if sudo? "sudo " "") "sh " script))) + (change-directory old)))) + +(define (write-info name info mode) + (d "writing info for egg ~a~%" name info) + (let ((infofile (make-pathname name (destination-repository mode)))) + (when (eq? platform 'unix) + (exec (string-append "chmod a+r " (quotearg infofile)))))) + +(define (exec cmd) + (d "executing: ~s~%" cmd) + (let ((r (system cmd))) + (unless (zero? r) + (error "shell command terminated with nonzero exit code" r cmd)))) + + +;; command line parsing and selection of operations + +(define (perform-actions eggs) + (load-defaults) + (cond ((null? eggs) + (set! canonical-eggs + (map (lambda (fname) + (list (pathname-file fname) (current-directory) #f)) + (glob "*.egg"))) + (retrieve-eggs '()) + (unless retrieve-only (install-eggs))) + (else + (let ((eggs (apply-mappings eggs))) + (cond (list-versions-only (list-egg-versions eggs)) + ;;XXX other actions... + (else + (retrieve-eggs eggs) + (unless retrieve-only (install-eggs)))))))) + +(define (main args) + (setup-proxy (get-environment-variable "http_proxy")) + (let ((eggs '()) + (rx (irregex "([^:]+):(.+)"))) + (let loop ((args args)) + (if (null? args) + (perform-actions (reverse eggs)) + (let ((arg (car args))) + (cond ((member arg '("-h" "-help" "--help")) + (usage 0)) + ((equal? arg "-test") + (set! run-tests #t) + (loop (cdr args))) + ((member arg '("-r" "-retrieve")) + (set! retrieve-only #t) + (loop (cdr args))) + ((equal? arg "-list-versions") + (set! list-versions-only #t) + (loop (cdr args))) + ((equal? arg "-defaults") + (set! user-defaults (cadr args)) + (loop (cddr args))) + ((equal? arg "-force") + (set! force-install #t) + (loop (cdr args))) + ((equal? arg "-host") + (set! target-extension #f) + (loop (cdr args))) + ((equal? arg "-target") + (set! host-extension #f) + (loop (cdr args))) + ((equal? arg "-n") + (set! do-not-build #t) + (loop (cdr args))) + ((equal? arg "-v") + (set! quiet #f) + (loop (cdr args))) + ((member arg '("-s" "-sudo")) + (set! sudo-install #t) + (loop (cdr args))) + + ;;XXX + + ((and (positive? (string-length arg)) + (char=? #\- (string-ref arg 0))) + (if (> (string-length arg) 2) + (let ((sos (string->list (substring arg 1)))) + (if (every (cut memq <> +short-options+) sos) + (loop (append + (map (cut string #\- <>) sos) + (cdr args))) + (usage 1))) + (usage 1))) + ((irregex-match rx arg) => + (lambda (m) + (set! eggs + (alist-cons + (irregex-match-substring m 1) + (irregex-match-substring m 2) + eggs)) + (loop (cdr args)))) + (else + (set! eggs (cons arg eggs)) + (loop (cdr args))))))))) + +(main (command-line-arguments)) + +)Trap