~ chicken-core (chicken-5) f3370bc522ff80ef435d8dc0afc65cec3222246c
commit f3370bc522ff80ef435d8dc0afc65cec3222246c Author: felix <felix@call-with-current-continuation.org> AuthorDate: Wed Jun 15 14:39:15 2016 +0200 Commit: felix <felix@call-with-current-continuation.org> CommitDate: Sun Nov 13 11:32:19 2016 +0100 various changes to make a basic chicken-install compile diff --git a/egg-compile.scm b/egg-compile.scm index 419a2c4e..e7102ee1 100644 --- a/egg-compile.scm +++ b/egg-compile.scm @@ -46,6 +46,20 @@ info)) +;;; load egg-info from file and perform validation + +(define (load-egg-info fname) + (with-input-from-file fname + (lambda () (validate-egg-info (read))))) + + +;;; lookup specific entries in egg-information + +(define (get-egg-property info prop #!optional default) + (let ((p (assq prop info))) + (or (and p (cadr p)) default))) + + ;;; some utilities (define (object-extension platform) @@ -58,11 +72,13 @@ ((unix) unix-executable-extension) ((windows) windows-executable-extension))) -(define (install-command platform) +(define (copy-directory-command platform) (case platform ((unix) "cp") ((windows) "xcopy /y"))) +(define install-command copy-directory-command) + (define (destination-repository mode) (case mode ((target) target-repo) diff --git a/egg-download.scm b/egg-download.scm index 2414d20b..2a1f5bb3 100644 --- a/egg-download.scm +++ b/egg-download.scm @@ -24,11 +24,18 @@ "/")))) (define (http-fetch host port locn dest proxy-host proxy-port proxy-user-pass) - (let-values (((in out) + (let-values (((in out _) (http-connect host port locn proxy-host proxy-port proxy-user-pass))) (http-retrieve-files in out dest))) +(define (http-query host port locn proxy-host proxy-port proxy-user-pass) + (let-values (((in out len) + (http-connect host port locn proxy-host proxy-port + proxy-user-pass))) + (close-output-port out) + (http-retrieve-response in len))) + (define (http-connect host port locn proxy-host proxy-port proxy-user-pass) (d "connecting to host ~s, port ~a ~a...~%" host port (if proxy-host @@ -44,6 +51,7 @@ (flush-output out) (d "reading response ...~%") (let* ((chunked #f) + (datalen #f) (h1 (read-line in)) (response-match (match-http-response h1))) (d "~a~%" h1) @@ -63,15 +71,18 @@ (let loop () (let ((ln (read-line in))) (unless (equal? ln "") - (when (match-chunked-transfer-encoding ln) (set! chunked #t)) + (cond ((match-chunked-transfer-encoding ln) + (set! chunked #t)) + ((match-content-length ln) => + (lambda (sz) (set! datalen sz)))) (d "~a~%" ln) (loop) ) ) ) ) (when chunked (d "reading chunks ") (let ((data (read-chunks in))) (close-input-port in) - (set! in (open-input-string data))) ) ) - (values in out)))) + (set! in (open-input-string data))) ) + (values in out datalen))))) (define (http-retrieve-files in out dest) (d "reading files ...~%") @@ -123,6 +134,12 @@ (cut display data) #:binary ) ) (get-files (cons name files)) ) ) ) ) )) +(define (http-retrieve-response in len) + (d "reading response ...~%") + (let ((data (read-string len in))) + (close-input-port in) + data)) + (define (server-error msg args) (abort (make-composite-condition @@ -157,6 +174,10 @@ (define (match-chunked-transfer-encoding ln) (irregex-match "[Tt]ransfer-[Ee]ncoding:\\s*chunked.*" ln) ) +(define (match-content-length ln) + (let ((m (irregex-match "[Cc]ontent-[Ll]ength:\\s*([0-9]+).*" ln))) + (and m (string->number (irregex-match-substring m 1))))) + (define (make-HTTP-GET/1.1 location user-agent host #!key (port 80) @@ -190,34 +211,58 @@ (make-property-condition 'http-fetch))) ) -;; entry point +;; entry points + +(define (list-versions egg url) + (receive (host port locn) (deconstruct-url url) + (let ((locn (conc locn + "?name=" egg + "&release=" (##sys#fudge 41) + "&mode=default" + "&listversions=1"))) + (let ((data (http-query host port locn proxy-host + proxy-port proxy-user-pass))) + (string-split data))))) + +(define (try-list-versions name url #!key + proxy-host proxy-port proxy-user-pass) + (condition-case (list-versions name url) + ((exn net) + (print "TCP connect timeout") + #f) + ((exn http-fetch) + (print "HTTP protocol error") + #f) + (e (exn setup-download-error) + (print "Server error:") + (print-error-message e) + #f) + (e () (abort e) ))) (define (download-egg egg url #!key version destination tests proxy-host proxy-port proxy-user-pass) (receive (host port locn) (deconstruct-url url) (let* ((locn (conc locn - "?name=" egg - "&release=" (##sys#fudge 41) - (if version (string-append "&version=" version) "") - "&mode=default" - (if tests "&tests=yes" ""))) + "?name=" egg + "&release=" (##sys#fudge 41) + (if version (string-append "&version=" version) "") + "&mode=default" + (if tests "&tests=yes" ""))) (eggdir (make-pathname destination egg))) (let ((fversion (http-fetch host port locn eggdir proxy-host proxy-port proxy-user-pass))) ;; If we get here then version of egg exists (values eggdir (or fversion version "")) )) ) ) -(define (try-download name url #!key version destination tests username - password proxy-host proxy-port proxy-user-pass) +(define (try-download name url #!key version destination tests + proxy-host proxy-port proxy-user-pass) (condition-case (download-egg name url version: version destination: destination tests: tests - username: username - password: password - proxy-host: proxy-host + proxy-host: proxy-host proxy-port: proxy-port proxy-user-pass: proxy-user-pass) ((exn net) diff --git a/new-install.scm b/new-install.scm index 1e5a3598..094f2bd6 100644 --- a/new-install.scm +++ b/new-install.scm @@ -13,12 +13,17 @@ (import (chicken tcp)) (import (chicken posix)) (import (chicken io)) +(import (chicken time)) (import (chicken pretty-print)) (define +defaults-version+ 1) (define +module-db+ "modules.db") (define +defaults-file+ "setup.defaults") -(define +short-options+ '(#\h)) +(define +short-options+ '(#\r #\h)) +(define +one-hour+ (* 60 60)) +(define +timestamp-file+ "TIMESTAMP") +(define +status-file+ "STATUS") +(define +egg-extension+ "egg") (include "mini-srfi-1.scm") (include "egg-environment.scm") @@ -26,7 +31,8 @@ (include "egg-download.scm") (define quiet #f) -(define default-sources '()) +(define default-servers '()) +(define default-locations '()) (define mappings '()) (define aliases '()) (define override '()) @@ -34,14 +40,34 @@ (define proxy-host #f) (define proxy-port #f) (define proxy-user-pass #f) +(define retrieve-only #f) +(define canonical-eggs '()) +(define run-tests #f) +(define platform + (if (eq? 'mingw (build-platform)) + 'windows + 'unix)) + +(define current-status + (list (get-environment-variable "CSC_OPTIONS"))) ;XXX more? + +(define (probe-dir dir) + (and dir (file-exists? dir) (directory? dir) dir)) -;; usage information +(define cache-directory + (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 (usage code) - ;;XXX +;; usage information +(define (usage code) + ;;XXX (exit code)) @@ -60,6 +86,25 @@ (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))))))) + ;; load defaults file ("setup.defaults") @@ -86,8 +131,8 @@ ;; others are ignored )) ((server) - (set! default-sources - (append default-sources (list (cdr x))))) + (set! default-servers + (append default-servers (list (cdr x))))) ((map) (set! mappings (append @@ -112,12 +157,17 @@ (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)))))) (else (broken x)))) - (call-with-input-file deff read-all)))) - (pair? default-sources) )) + (call-with-input-file deff read-all)))))) + +;; set variables with HTTP proxy information + (define (setup-proxy uri) (and-let* (((string? uri)) (m (irregex-match "(http://)?([^:]+):?([0-9]*)" uri)) @@ -126,6 +176,9 @@ (set! proxy-host (irregex-match-substring m 2)) (set! proxy-port (or (string->number port) 80)))) + +;; apply egg->egg mappings loaded from defaults + (define (apply-mappings eggs) (define (canonical x) (cond ((symbol? x) (cons (symbol->string x) #f)) @@ -139,19 +192,140 @@ (append-map (lambda (egg) (cond ((find (lambda (m) (find (cut same? egg <>) (car m))) - *mappings*) => + 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)) + (d "mapped ~s to ~s~%" eggs eggs2)) eggs2)) + +;; 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)) + (fetch-egg-sources name version cached) + (with-output-to-file status (cut write current-status))) + (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-server-version name version lversion))) + (fetch) + (let ((info (load-egg-info eggfile))) + (values cached (get-egg-property info 'version)))) + (else (values cached version)))))) + +(define (fetch-egg-sources name version dest) + (let loop ((locs default-locations)) + (cond ((null? locs) + (let loop ((srvs default-servers)) + (receive (dir ver) + (try-download name (car srvs) + version: version + destination: dest + tests: run-tests + proxy-host: proxy-host + proxy-port: proxy-port + proxy-user-pass: proxy-user-pass) + (cond (dir + (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-server-version name version lversion) + (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))))))) + + +;; 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) (locate-egg name version))) + (when (or (not dir) + (null? (directory dir))) + (error "extension or version not found")) + (d " ~a located at ~a~%") + (set! canonical-eggs + (cons (list name dir ver) canonical-eggs))))))) + eggs) + (unless retrieve-only + (error "to be implemented"))) ; XXX + + +;; command line parsing and selection of operations + (define (perform-actions eggs) (let ((eggs (apply-mappings eggs))) - + ;;XXX... + (retrieve-eggs eggs))) (define (main args) (setup-proxy (get-environment-variable "http_proxy")) @@ -163,6 +337,8 @@ (let ((arg (car args))) (cond ((member arg '("-h" "-help" "--help")) (usage 0)) + ((equal? arg "-test") + (set! run-tests #t)) ;;XXX @@ -173,8 +349,7 @@ (if (every (cut memq <> +short-options+) sos) (loop (append (map (cut string #\- <>) sos) - (cdr args)) - eggs) + (cdr args))) (usage 1))) (usage 1))) ((irregex-match rx arg) =>Trap