;; Utilities for creating a .plt package (module pack mzscheme (require (lib "deflate.ss") (lib "base64.ss" "net") (lib "process.ss") (lib "list.ss") (lib "etc.ss") (lib "port.ss") (lib "file.ss") (lib "getinfo.ss" "setup")) (provide pack mztar std-filter pack-collections) (define pack (opt-lambda (dest name paths collections [filter std-filter] [encode? #t] [file-mode 'file] [unpack-unit #f] [plt-relative? #t] [requires null] [conflicts null] [plt-home-relative? #f]) (let*-values ([(file) (open-output-file dest 'truncate/replace)] [(fileout thd) (if encode? (let-values ([(b64-out b64-in) (make-pipe 4096)] [(gz-out gz-in) (make-pipe 4096)]) (thread (lambda () (let ([normal (lambda () (gzip-through-ports gz-out b64-in #f 0) (close-output-port b64-in))] [gzip (find-executable-path "gzip" #f)]) (if gzip (let ([p (process* gzip "-c")]) (if (eq? 'running ((list-ref p 4) 'status)) (begin ;; Use gzip process. ;; Errors to error port: (thread (lambda () (dynamic-wind void (lambda () (copy-port (cadddr p) (current-error-port))) (lambda () (close-input-port (cadddr p)))))) ;; Copy input to gzip: (thread (lambda () (dynamic-wind void (lambda () (copy-port gz-out (cadr p))) (lambda () (close-input-port gz-out) (close-output-port (cadr p)))))) ;; Copy input to b64: (dynamic-wind void (lambda () (copy-port (car p) b64-in)) (lambda () (close-input-port (car p)) (close-output-port b64-in)))) (normal))) (normal))))) (values gz-in (thread (lambda () (base64-encode-stream b64-out file) (close-output-port file))))) (values file (thread void)))]) (fprintf fileout "PLT~n") (write `(lambda (request failure) (case request [(name) ,name] [(unpacker) 'mzscheme] [(requires) ',requires] [(conflicts) ',conflicts] [(plt-relative?) ,plt-relative?] [(plt-home-relative?) ,(and plt-relative? plt-home-relative?)] [else (failure)])) fileout) (newline fileout) (write (or unpack-unit `(unit (import plthome mzuntar) (export) (mzuntar void) (quote ,collections))) fileout) (newline fileout) (for-each (lambda (path) (mztar path fileout filter file-mode)) paths) (close-output-port fileout) (thread-wait thd)))) (define (element->string x) (if (path? x) (path->string x) x)) (define (mztar path output filter file-mode) (define (path->list p) (if (eq? p 'same) null (let-values ([(base name dir?) (split-path p)]) (if (path? base) (append (path->list base) (list name)) (list name))))) (define-values (init-dir init-files) (if (file-exists? path) (let-values ([(base name dir?) (split-path path)]) (values (if (eq? base 'relative) 'same base) (list name))) (values (if (string? path) (string->path path) path) #f))) (let loop ([dir init-dir][dpath (path->list init-dir)][files init-files]) (printf "MzTarring ~a...~n" (path->string (if files (build-path dir (car files)) dir))) (fprintf output "~s~n~s~n" 'dir (map element->string dpath)) (for-each (lambda (f) (let* ([p (build-path dir f)] [filter-val (filter p)]) (when filter-val (if (directory-exists? p) (loop p (append dpath (list f)) #f) (let ([len (file-size p)]) ;; (printf "MzTarring ~a~n" p) (fprintf output "~s~n~s~n~s~n*" (case filter-val [(file) 'file] [(file-replace) 'file-replace] [else file-mode]) (map element->string (append dpath (list f))) len) (call-with-input-file* p (lambda (p) (copy-port p output)))))))) (or files (directory-list dir))))) (define (std-filter path) (let-values ([(base name dir?) (split-path path)]) (let ([name (path->bytes name)]) (not (or (regexp-match #rx#"^CVS$" name) (regexp-match #rx#"^[.]svn$" name) (regexp-match #rx#"^[.]cvsignore$" name) (regexp-match #rx#"^compiled$" name) (regexp-match #rx#"~$" name) (regexp-match #rx#"^#.*#$" name) (regexp-match #rx#"[.]plt$" name) (regexp-match #rx#"^[.]#" name)))))) (define pack-collections (opt-lambda (output name collections replace? extra-setup-collections [file-filter std-filter] [plt-home-relative? #f]) (let-values ([(dir source-files requires conflicts name) (let ([dirs (map (lambda (cp) (apply collection-path cp)) collections)]) ;; Figure out the base path: (let* ([base-path #f] [base-path-setter #f] [rel-paths (map (lambda (dir coll) (let-values ([(base c-name dir?) (split-path dir)]) (let-values ([(base subdir) (let loop ([l (cdr coll)][base base]) (let-values ([(base x-name dir?) (split-path base)]) (if (null? l) (values base x-name) (let-values ([(base subdir) (loop (cdr l) base)]) (values base (build-path subdir x-name))))))]) (if base-path (unless (equal? base base-path) (error 'mzc "cannot combine collections that live in different directories: \"~a\" and: \"~a\"" base-path-setter dir)) (begin (set! base-path-setter dir) (set! base-path base))) (build-path 'same subdir c-name)))) dirs collections)] [infos (map (lambda (cp) (get-info cp)) collections)] [coll-list? (lambda (cl) (and (list? cl) (andmap (lambda (c) (and (list? c) (andmap string? c) (andmap relative-path? c))) cl)))] [get-dep-coll (lambda (which) (apply append (map (lambda (i src-cp) (let ([rl (if i (i which (lambda () null)) null)]) (unless (coll-list? rl) (error 'mzc "bad ~a specification in info.ss for collection ~s" which src-cp)) rl)) infos collections)))]) (values base-path rel-paths (get-dep-coll 'requires) (append (if replace? null collections) (get-dep-coll 'conflicts)) (or name ((or (car infos) (lambda (n f) (caar collections))) 'name (lambda () (caar collections)))))))]) (let ([output (path->complete-path output)]) (parameterize ([current-directory dir]) (pack output name source-files (append extra-setup-collections (filter get-info collections)) file-filter #t (if replace? 'file-replace 'file) #f #t ; plt-relative ;; For each require, get current version (map (lambda (r) (let ([i (get-info r)]) (let ([v (and i (i 'version (lambda () #f)))]) (if v (begin (unless (and (list? v) (andmap number? v) (andmap exact? v) (andmap integer? v)) (error 'mzc "bad version specification in info.ss for collection ~s" r)) (list r v)) (list r null))))) (cons '("mzscheme") requires)) conflicts plt-home-relative?)))))))