diff --git a/collects/setup/unixstyle-install.ss b/collects/setup/unixstyle-install.ss index edd0f86218..04a54c36d6 100644 --- a/collects/setup/unixstyle-install.ss +++ b/collects/setup/unixstyle-install.ss @@ -21,426 +21,418 @@ ;; * pltdir: The source plt directory ;; * Path names that should be moved/copied (bin, collects, doc, lib, ...) -(module unixstyle-install scheme/base +#lang scheme/base - (define args (vector->list (current-command-line-arguments))) +(define args (vector->list (current-command-line-arguments))) - (define (get-arg) - (when (null? args) (error "insufficient arguments")) - (begin0 (car args) (set! args (cdr args)))) +(define (get-arg) + (when (null? args) (error "insufficient arguments")) + (begin0 (car args) (set! args (cdr args)))) - (define op (string->symbol (get-arg))) - (define pltdir (get-arg)) - (define dirs (map (lambda (name) (list name (get-arg))) - '(bin collects doc lib includeplt libplt man #|src|#))) +(define op (string->symbol (get-arg))) +(define pltdir (get-arg)) +(define dirs (map (lambda (name) (list name (get-arg))) + '(bin collects doc lib includeplt libplt man #|src|#))) - (define (dir: name) - (cadr (or (assq name dirs) (error 'getdir "unknown dir name: ~e" name)))) +(define (dir: name) + (cadr (or (assq name dirs) (error 'getdir "unknown dir name: ~e" name)))) - ;; Configures level where we start owning stuff (in the sense that the - ;; generated uninstaller will remove it, and the installation will remove - ;; dirs instead of moving into them). For example, a 1-level for collectsdir - ;; means that we will overwrite collects/foo instead of adding files in it. - ;; A level of 999 means that we always add stuff if the directory exists; a - ;; level of 0 means that we always own the directory (should never be used). - ;; In any case, we start own stuff (= add it to the uninstaller) once we - ;; encounter a directory that does not already exist. #f means that we never - ;; own directories, only files. - (define (level-of dir) - (let ([dir (string->symbol (basename dir))]) - (case dir - [(bin) #f] - [(collects) 1] - [(doc) 1] - [(include) 1] - ;; if shared libraries are used, then these files should be moved - ;; independently, as if they had a level of #f - [(lib) 1] - [(man) #f] - [(src) 1] - [(readme.txt) #f] ; moved last - [else (error 'level-of "internal-error -- unknown dir: ~e" dir)]))) +;; Configures level where we start owning stuff (in the sense that the +;; generated uninstaller will remove it, and the installation will remove dirs +;; instead of moving into them). For example, a 1-level for collectsdir means +;; that we will overwrite collects/foo instead of adding files in it. A level +;; of 999 means that we always add stuff if the directory exists; a level of 0 +;; means that we always own the directory (should never be used). In any case, +;; we start own stuff (= add it to the uninstaller) once we encounter a +;; directory that does not already exist. #f means that we never own +;; directories, only files. +(define (level-of dir) + (let ([dir (string->symbol (basename dir))]) + (case dir + [(bin) #f] + [(collects) 1] + [(doc) 1] + [(include) 1] + ;; if shared libraries are used, then these files should be moved + ;; independently, as if they had a level of #f + [(lib) 1] + [(man) #f] + [(src) 1] + [(readme.txt) #f] ; moved last + [else (error 'level-of "internal-error -- unknown dir: ~e" dir)]))) - (define (make-path . args) ; like build-path but returns a string - (path->string (apply build-path args))) +(define (make-path . args) ; like build-path but returns a string + (path->string (apply build-path args))) - (define (basename path) ; returns a string - (let-values ([(dir name dir?) (split-path path)]) (path->string name))) +(define (basename path) ; returns a string + (let-values ([(dir name dir?) (split-path path)]) (path->string name))) - (define (dirname path) ; returns a string - (let-values ([(dir name dir?) (split-path path)]) dir)) +(define (dirname path) ; returns a string + (let-values ([(dir name dir?) (split-path path)]) dir)) - ;; like directory-list, but returns a sorted list of strings (this is a lot - ;; of code just to get the sorting, but it's better if an installer operates - ;; in a deterministic way) - (define (ls . dir) - (sort (map path->string (apply directory-list dir)) stringstring (apply directory-list dir)) string remove new copy (if can) and re-raise - (with-handlers ([exn? (lambda (e) #f)]) - (rm dst) - (raise e)))]) - ;; (cp src dst) (rm src) - ;; can't do all in Scheme, run mv instead - (run "mv" "--" src dst)))) +;; try to rename and if it fails (due to different fs) copy and remove +(define (mv src dst) + (unless (with-handlers ([(lambda (e) + (and (exn:fail:filesystem? e) + (not (exn:fail:filesystem:exists? e)))) + (lambda (e) #f)]) + (rename-file-or-directory src dst) #t) + ;; move failed: copy & remove + (with-handlers ([exn? (lambda (e) + ;; error => remove new copy (if can) and re-raise + (with-handlers ([exn? (lambda (e) #f)]) + (rm dst) + (raise e)))]) + ;; (cp src dst) (rm src) + ;; can't do all in Scheme, run mv instead + (run "mv" "--" src dst)))) - ;; list of changes, so we can undo them in case of an error and so we can - ;; create an uninstaller - (define path-changes '()) - (define (register-change! op . args) - (set! path-changes (cons (cons op args) path-changes))) +;; list of changes, so we can undo them in case of an error and so we can +;; create an uninstaller +(define path-changes '()) +(define (register-change! op . args) + (set! path-changes (cons (cons op args) path-changes))) - ;; like `mv', but also record moves - (define (mv* src dst) - (mv src dst) - (register-change! 'mv src dst)) +;; like `mv', but also record moves +(define (mv* src dst) + (mv src dst) + (register-change! 'mv src dst)) - ;; like `cp', but also record copies - (define (cp* src dst) - (cp src dst) - (register-change! 'cp src dst)) +;; like `cp', but also record copies +(define (cp* src dst) + (cp src dst) + (register-change! 'cp src dst)) - (define (fix-executable file) - (define (fix-binary file) - (let-values ([(i o) (open-input-output-file file 'update)]) - (let ([m (regexp-match-positions #rx#"coLLECTs dIRECTORy:" i)]) - (unless m - (error - (format "could not find collection-path label in executable: ~a" - file))) - (file-position o (cdar m)) - (display (dir: 'collects) o) - (write-byte 0 o) - (write-byte 0 o) - (close-input-port i) - (close-output-port o)))) - (define (fix-script file) - (let* ([size (file-size file)] - [buf (with-input-from-file file (lambda () (read-bytes size)))] - [m (or (regexp-match-positions - #rx#"\n# {{{ bindir\n(.*?\n)# }}} bindir\n" buf) - (error (format "could not find binpath block in script: ~a" - file)))]) - ;; 'truncate file to keep it executable - (with-output-to-file file - #:exists 'truncate - (lambda () - (write-bytes buf (current-output-port) 0 (caadr m)) - (printf "bindir=\"~a\"\n" - (regexp-replace* #rx"[\"`'$\\]" (dir: 'bin) "\\\\&")) - (write-bytes buf (current-output-port) (cdadr m)))))) - (let ([magic (with-input-from-file file (lambda () (read-bytes 10)))]) - (cond [(regexp-match #rx#"^\177ELF" magic) - (let ([temp (format "~a-temp-for-install" - (regexp-replace* #rx"/" file "_"))]) - (with-handlers ([exn? (lambda (e) (delete-file temp) (raise e))]) - ;; always copy so we never change the running executable - (rm temp) - (copy-file file temp) - (fix-binary temp) - (delete-file file) - (mv temp file)))] - [(regexp-match #rx#"^#!/bin/sh" magic) - (fix-script file)] - [else (error (format "unknown executable: ~a" file))]))) +(define (fix-executable file) + (define (fix-binary file) + (let-values ([(i o) (open-input-output-file file #:exists 'update)]) + (let ([m (regexp-match-positions #rx#"coLLECTs dIRECTORy:" i)]) + (unless m + (error + (format "could not find collection-path label in executable: ~a" + file))) + (file-position o (cdar m)) + (display (dir: 'collects) o) + (write-byte 0 o) + (write-byte 0 o) + (close-input-port i) + (close-output-port o)))) + (define (fix-script file) + (let* ([size (file-size file)] + [buf (with-input-from-file file (lambda () (read-bytes size)))] + [m (or (regexp-match-positions + #rx#"\n# {{{ bindir\n(.*?\n)# }}} bindir\n" buf) + (error (format "could not find binpath block in script: ~a" + file)))]) + ;; 'truncate file to keep it executable + (with-output-to-file file #:exists 'truncate + (lambda () + (write-bytes buf (current-output-port) 0 (caadr m)) + (printf "bindir=\"~a\"\n" + (regexp-replace* #rx"[\"`'$\\]" (dir: 'bin) "\\\\&")) + (write-bytes buf (current-output-port) (cdadr m)))))) + (let ([magic (with-input-from-file file (lambda () (read-bytes 10)))]) + (cond [(regexp-match #rx#"^\177ELF" magic) + (let ([temp (format "~a-temp-for-install" + (regexp-replace* #rx"/" file "_"))]) + (with-handlers ([exn? (lambda (e) (delete-file temp) (raise e))]) + ;; always copy so we never change the running executable + (rm temp) + (copy-file file temp) + (fix-binary temp) + (delete-file file) + (mv temp file)))] + [(regexp-match #rx#"^#!/bin/sh" magic) + (fix-script file)] + [else (error (format "unknown executable: ~a" file))]))) - (define (fix-executables bindir . binfiles) - (parameterize ([current-directory bindir]) - (let ([binfiles (if (pair? binfiles) (car binfiles) (ls))]) - (for-each (lambda (f) (when (file-exists? f) (fix-executable f))) - binfiles)))) +(define (fix-executables bindir . binfiles) + (parameterize ([current-directory bindir]) + (let ([binfiles (if (pair? binfiles) (car binfiles) (ls))]) + (for-each (lambda (f) (when (file-exists? f) (fix-executable f))) + binfiles)))) - ;; remove and record all empty dirs - (define (remove-empty-dirs dir) - (let loop ([dir dir] [recurse? #t]) - (when (and (directory-exists? dir) (not (link-exists? dir))) - (let ([ps (ls dir)]) - (cond [(null? ps) - (delete-directory dir) - (register-change! 'rd dir)] - [recurse? - (for-each (lambda (p) (loop (make-path dir p) #t)) ps) - (loop dir #f)] ; try again - ;; get here only on the 2nd round, so we cannot remove it - ))))) +;; remove and record all empty dirs +(define (remove-empty-dirs dir) + (let loop ([dir dir] [recurse? #t]) + (when (and (directory-exists? dir) (not (link-exists? dir))) + (let ([ps (ls dir)]) + (cond [(null? ps) + (delete-directory dir) + (register-change! 'rd dir)] + [recurse? + (for-each (lambda (p) (loop (make-path dir p) #t)) ps) + (loop dir #f)] ; try again + ;; get here only on the 2nd round, so we cannot remove it + ))))) - ;; called from an error handler, so avoid raising more errors - (define (undo-changes) - (printf "*** Error: undoing changes...\n") - (for-each - (lambda (p) - (apply (case (car p) - [(cp) (lambda (src dst) (rm dst))] - [(mv) (lambda (src dst) - (with-handlers - ([exn? - (lambda (e) - (fprintf (current-error-port) - " ** error during undo: ~a\n" - (exn-message e)) - #f)]) - (mv dst src)))] - [(rd) make-directory] - [(md) delete-directory] - [(file) delete-file] - [else (error 'undo-changes "internal-error: ~e" p)]) - (cdr p))) - path-changes)) +;; called from an error handler, so avoid raising more errors +(define (undo-changes) + (printf "*** Error: undoing changes...\n") + (for-each + (lambda (p) + (apply (case (car p) + [(cp) (lambda (src dst) (rm dst))] + [(mv) (lambda (src dst) + (with-handlers ([exn? + (lambda (e) + (fprintf (current-error-port) + " ** error during undo: ~a\n" + (exn-message e)) + #f)]) + (mv dst src)))] + [(rd) make-directory] + [(md) delete-directory] + [(file) delete-file] + [else (error 'undo-changes "internal-error: ~e" p)]) + (cdr p))) + path-changes)) - (define (write-uninstaller) - (define uninstaller (make-path (dir: 'bin) "plt-uninstall")) - (printf "Writing uninstaller at: ~a...\n" uninstaller) - (register-change! 'file uninstaller) - (with-output-to-file uninstaller - #:exists 'replace - (lambda () - (printf "#!/bin/sh\n") - (printf "\n# Remove files and dirs that we own\n") - (printf "while true; do read R || break; rm -rf -- \"$R\"; done \\\n") - (printf "<<::://E//O//F////O//N//E//:::\n") - ;; only moved/copied stuff are part of the distribution - (for-each (lambda (p) - (when (memq (car p) '(mv cp)) (printf "~a\n" (caddr p)))) - path-changes) - (printf "::://E//O//F////O//N//E//:::\n") - (printf "\n# Remove dirs that we created but not own only if empty\n") - (printf "while true; do read R || break; ~a" - "rmdir -- \"$R\" > /dev/null 2>&1; done \\\n") - (printf "<<::://E//O//F////T//W//O//:::\n") - (for-each (lambda (p) (when (eq? 'md (car p)) (printf "~a\n" (cadr p)))) - path-changes) - (printf "::://E//O//F////T//W//O//:::\n") - (printf "\n# Remove this script\n") - (printf "exec rm \"$0\"\n"))) - (run "chmod" "+x" uninstaller)) +(define (write-uninstaller) + (define uninstaller (make-path (dir: 'bin) "plt-uninstall")) + (printf "Writing uninstaller at: ~a...\n" uninstaller) + (register-change! 'file uninstaller) + (with-output-to-file uninstaller #:exists 'replace + (lambda () + (printf "#!/bin/sh\n") + (printf "\n# Remove files and dirs that we own\n") + (printf "while true; do read R || break; rm -rf -- \"$R\"; done \\\n") + (printf "<<::://E//O//F////O//N//E//:::\n") + ;; only moved/copied stuff are part of the distribution + (for ([p path-changes] #:when (memq (car p) '(mv cp))) + (printf "~a\n" (caddr p))) + (printf "::://E//O//F////O//N//E//:::\n") + (printf "\n# Remove dirs that we created but not own only if empty\n") + (printf "while true; do read R || break; ~a" + "rmdir -- \"$R\" > /dev/null 2>&1; done \\\n") + (printf "<<::://E//O//F////T//W//O//:::\n") + (for ([p path-changes] #:when (eq? 'md (car p))) + (printf "~a\n" (cadr p))) + (printf "::://E//O//F////T//W//O//:::\n") + (printf "\n# Remove this script\n") + (printf "exec rm \"$0\"\n"))) + (run "chmod" "+x" uninstaller)) - (define write-config - (case-lambda - [() (write-config #t (dir: 'collects))] - [(x) (if (boolean? x) - (write-config x (dir: 'collects)) - (write-config #t x))] - [(compile? collectsdir) - (define (cpath . xs) - (apply make-path collectsdir "config" xs)) - (define (ftime file) - (and (file-exists? file) (file-or-directory-modify-seconds file))) - (let* ([src (cpath "config.ss")] - [zo (cpath "compiled" "config.zo")] - [dep (cpath "compiled" "config.dep")] - [src-time (ftime src)] - [zo-time (ftime zo)]) - (printf "Rewriting configuration file at: ~a...\n" src) - (parameterize ([current-library-collection-paths ; for configtab.ss - (list collectsdir)]) - (with-output-to-file (cpath "config.ss") - #:exists 'truncate/replace - (lambda () - (printf ";; automatically generated by unixstyle-install\n") - (printf "(module config (lib \"configtab.ss\" \"setup\")\n") - (printf " (define doc-dir ~s)\n" (dir: 'doc)) - (when (eq? 'shared (system-type 'link)) ; never true for now - (printf " (define dll-dir ~s)\n" (dir: 'lib))) - (printf " (define lib-dir ~s)\n" (dir: 'libplt)) - (printf " (define include-dir ~s)\n" (dir: 'includeplt)) - (printf " (define bin-dir ~s)\n" (dir: 'bin)) - (printf " (define absolute-installation? #t))\n"))) - ;; recompile & set times as if nothing happened (don't remove .dep) - ;; this requires the file to look the same on all compilations, and - ;; configtab.ss generates bindings unhygienically for that reason. - (unless (and (pair? compile?) (not (car compile?))) - (when src-time (file-or-directory-modify-seconds src src-time)) - (when zo-time - (with-input-from-file src - (lambda () - (with-output-to-file zo - #:exists 'truncate/replace - (lambda () (write (compile (read-syntax))))))) - (file-or-directory-modify-seconds zo zo-time)))))])) +(define write-config + (case-lambda + [() (write-config #t (dir: 'collects))] + [(x) (if (boolean? x) + (write-config x (dir: 'collects)) + (write-config #t x))] + [(compile? collectsdir) + (define (cpath . xs) + (apply make-path collectsdir "config" xs)) + (define (ftime file) + (and (file-exists? file) (file-or-directory-modify-seconds file))) + (let* ([src (cpath "config.ss")] + [zo (cpath "compiled" "config.zo")] + [dep (cpath "compiled" "config.dep")] + [src-time (ftime src)] + [zo-time (ftime zo)]) + (printf "Rewriting configuration file at: ~a...\n" src) + (parameterize ([current-library-collection-paths ; for configtab.ss + (list collectsdir)]) + (with-output-to-file (cpath "config.ss") #:exists 'truncate/replace + (lambda () + (printf ";; automatically generated by unixstyle-install\n") + (printf "(module config (lib \"configtab.ss\" \"setup\")\n") + (printf " (define doc-dir ~s)\n" (dir: 'doc)) + (when (eq? 'shared (system-type 'link)) ; never true for now + (printf " (define dll-dir ~s)\n" (dir: 'lib))) + (printf " (define lib-dir ~s)\n" (dir: 'libplt)) + (printf " (define include-dir ~s)\n" (dir: 'includeplt)) + (printf " (define bin-dir ~s)\n" (dir: 'bin)) + (printf " (define absolute-installation? #t))\n"))) + ;; recompile & set times as if nothing happened (don't remove .dep) + ;; this requires the file to look the same on all compilations, and + ;; configtab.ss generates bindings unhygienically for that reason. + (unless (and (pair? compile?) (not (car compile?))) + (when src-time (file-or-directory-modify-seconds src src-time)) + (when zo-time + (with-input-from-file src + (lambda () + (with-output-to-file zo #:exists 'truncate/replace + (lambda () (write (compile (read-syntax))))))) + (file-or-directory-modify-seconds zo zo-time)))))])) - ;; creates a directory including its ancestors when needed - (define (make-dir* dir) - (unless (directory-exists? dir) - (make-dir* (dirname dir)) - (make-directory dir) - (register-change! 'md dir))) +;; creates a directory including its ancestors when needed +(define (make-dir* dir) + (unless (directory-exists? dir) + (make-dir* (dirname dir)) + (make-directory dir) + (register-change! 'md dir))) - (define yes-to-all? #f) - (define (ask-overwrite kind path) - (let ([rm (lambda () (rm path))]) - (if yes-to-all? - (rm) - (begin (printf "Overwrite ~a \"~a\"?\n" kind path) - (let loop () - (printf " [y]es / yes to [a]ll / [n]o=abort > ") - (case (cond [(regexp-match #rx"^[ \t]*([a-z])" - (string-downcase (read-line))) - => (lambda (m) (string->symbol (cadr m)))] - [else #f]) - [(y) (rm)] - [(a) (set! yes-to-all? #t) (rm)] - [(n) (error "Abort!")] - [else (loop)])))))) +(define yes-to-all? #f) +(define (ask-overwrite kind path) + (let ([rm (lambda () (rm path))]) + (if yes-to-all? + (rm) + (begin (printf "Overwrite ~a \"~a\"?\n" kind path) + (let loop () + (printf " [y]es / yes to [a]ll / [n]o=abort > ") + (case (cond [(regexp-match #rx"^[ \t]*([a-z])" + (string-downcase (read-line))) + => (lambda (m) (string->symbol (cadr m)))] + [else #f]) + [(y) (rm)] + [(a) (set! yes-to-all? #t) (rm)] + [(n) (error "Abort!")] + [else (loop)])))))) - (define ((move/copy-tree move?) src dst*) - (define dst (if (symbol? dst*) (dir: dst*) dst*)) - (printf "~aing ~a -> ~a\n" (if move? "Mov" "Copy") src dst) - (make-dir* (dirname dst)) - (let loop ([src (path->string (simplify-path src #f))] - [dst (path->string (simplify-path dst #f))] - [lvl (level-of src)]) ; see above - (let ([doit (let ([doit (if move? mv* cp*)]) (lambda () (doit src dst)))] - [src-d? (directory-exists? src)] - [dst-l? (link-exists? dst)] - [dst-d? (directory-exists? dst)] - [dst-f? (file-exists? dst)]) - (unless (skip-filter src) - (when (and src-d? (not lvl) (not dst-d?)) - (when (or dst-l? dst-f?) (ask-overwrite "file or link" dst)) - (make-directory dst) - (register-change! 'md dst) - (set! dst-d? #t) (set! dst-l? #f) (set! dst-f? #f)) - (cond [dst-l? (ask-overwrite "symlink" dst) (doit)] - [dst-d? (if (and src-d? (or (not lvl) (< 0 lvl))) - ;; recurse only when source is dir, & not too deep - (for-each (lambda (name) - (loop (make-path src name) - (make-path dst name) - (and lvl (sub1 lvl)))) - (ls src)) - (begin (ask-overwrite "dir" dst) (doit)))] - [dst-f? (ask-overwrite "file" dst) (doit)] - [else (doit)])))) - (when move? (remove-empty-dirs src))) +(define ((move/copy-tree move?) src dst*) + (define dst (if (symbol? dst*) (dir: dst*) dst*)) + (printf "~aing ~a -> ~a\n" (if move? "Mov" "Copy") src dst) + (make-dir* (dirname dst)) + (let loop ([src (path->string (simplify-path src #f))] + [dst (path->string (simplify-path dst #f))] + [lvl (level-of src)]) ; see above + (let ([doit (let ([doit (if move? mv* cp*)]) (lambda () (doit src dst)))] + [src-d? (directory-exists? src)] + [dst-l? (link-exists? dst)] + [dst-d? (directory-exists? dst)] + [dst-f? (file-exists? dst)]) + (unless (skip-filter src) + (when (and src-d? (not lvl) (not dst-d?)) + (when (or dst-l? dst-f?) (ask-overwrite "file or link" dst)) + (make-directory dst) + (register-change! 'md dst) + (set! dst-d? #t) (set! dst-l? #f) (set! dst-f? #f)) + (cond [dst-l? (ask-overwrite "symlink" dst) (doit)] + [dst-d? (if (and src-d? (or (not lvl) (< 0 lvl))) + ;; recurse only when source is dir, & not too deep + (for-each (lambda (name) + (loop (make-path src name) + (make-path dst name) + (and lvl (sub1 lvl)))) + (ls src)) + (begin (ask-overwrite "dir" dst) (doit)))] + [dst-f? (ask-overwrite "file" dst) (doit)] + [else (doit)])))) + (when move? (remove-empty-dirs src))) - ;; -------------------------------------------------------------------------- +;; -------------------------------------------------------------------------- - (define (move/copy-distribution move?) - (define do-tree (move/copy-tree move?)) - (current-directory pltdir) - (when (ormap (lambda (p) (regexp-match #rx"[.]so" p)) (ls "lib")) - (error "Cannot handle distribution of shared-libraries (yet)")) - (with-handlers ([exn? (lambda (e) (undo-changes) (raise e))]) - (define binfiles (ls "bin")) ; see below - (do-tree "bin" 'bin) - (do-tree "collects" 'collects) - (do-tree "doc" 'doc) - ;; (do-tree ??? 'lib) ; shared stuff goes here - (do-tree "include" 'includeplt) - (do-tree "lib" 'libplt) - (do-tree "man" 'man) - ;; (when (and (not (equal? (dir: 'src) "")) (directory-exists? "src")) - ;; (do-tree "src" 'src)) - ;; don't use the above -- it would be pointless to put the source tree in - ;; a place where it would not be usable. - (when (and (directory-exists? "src") move?) (rm "src")) - ;; part of the distribution: - (when (file-exists? "readme.txt") - (do-tree "readme.txt" (make-path (dir: 'doc) "readme.txt"))) - ;; nothing should be left now if this was a move - (when (and move? (not (null? (ls)))) - (error (format "leftovers in source tree: ~s" (ls)))) - ;; we need to know which files need fixing - (fix-executables (dir: 'bin) binfiles) - (write-uninstaller) - (write-config)) - (when move? - (current-directory (dirname pltdir)) - (delete-directory pltdir))) +(define (move/copy-distribution move?) + (define do-tree (move/copy-tree move?)) + (current-directory pltdir) + (when (ormap (lambda (p) (regexp-match #rx"[.]so" p)) (ls "lib")) + (error "Cannot handle distribution of shared-libraries (yet)")) + (with-handlers ([exn? (lambda (e) (undo-changes) (raise e))]) + (define binfiles (ls "bin")) ; see below + (do-tree "bin" 'bin) + (do-tree "collects" 'collects) + (do-tree "doc" 'doc) + ;; (do-tree ??? 'lib) ; shared stuff goes here + (do-tree "include" 'includeplt) + (do-tree "lib" 'libplt) + (do-tree "man" 'man) + ;; (when (and (not (equal? (dir: 'src) "")) (directory-exists? "src")) + ;; (do-tree "src" 'src)) + ;; don't use the above -- it would be pointless to put the source tree in + ;; a place where it would not be usable. + (when (and (directory-exists? "src") move?) (rm "src")) + ;; part of the distribution: + (when (file-exists? "readme.txt") + (do-tree "readme.txt" (make-path (dir: 'doc) "readme.txt"))) + ;; nothing should be left now if this was a move + (when (and move? (not (null? (ls)))) + (error (format "leftovers in source tree: ~s" (ls)))) + ;; we need to know which files need fixing + (fix-executables (dir: 'bin) binfiles) + (write-uninstaller) + (write-config)) + (when move? + (current-directory (dirname pltdir)) + (delete-directory pltdir))) - (define (make-install-copytree) - (define copytree (move/copy-tree #f)) - (define origtree? (equal? "yes" (get-arg))) - (current-directory pltdir) - (set! skip-filter ; skip all dot-names, CVS and compiled subdirs - (lambda (p) - (regexp-match #rx"^(?:[.].*|CVS|compiled)$" (basename p)))) - (with-handlers ([exn? (lambda (e) (undo-changes) (raise e))]) - (set! yes-to-all? #t) ; non-interactive - (copytree "collects" 'collects) - (copytree "doc" 'doc) - (copytree "man" 'man) - (unless origtree? (write-config #f)))) ; don't recompile +(define (make-install-copytree) + (define copytree (move/copy-tree #f)) + (define origtree? (equal? "yes" (get-arg))) + (current-directory pltdir) + (set! skip-filter ; skip all dot-names, CVS and compiled subdirs + (lambda (p) + (regexp-match #rx"^(?:[.].*|CVS|compiled)$" (basename p)))) + (with-handlers ([exn? (lambda (e) (undo-changes) (raise e))]) + (set! yes-to-all? #t) ; non-interactive + (copytree "collects" 'collects) + (copytree "doc" 'doc) + (copytree "man" 'man) + (unless origtree? (write-config #f)))) ; don't recompile - (define (make-install-destdir-fix) - (define destdir - (or (getenv "DESTDIR") - (error "missing DESTDIR value for make-install-destdir-fix"))) - (define destdirlen (string-length destdir)) - (define origtree? (equal? "yes" (get-arg))) - ;; grab paths before we change them - (define bindir (dir: 'bin)) - (define collectsdir (dir: 'collects)) - (define (remove-dest p) - (let ([pfx (and (< destdirlen (string-length p)) - (substring p 0 destdirlen))]) - (if (equal? pfx destdir) - (regexp-replace #rx"^/*" (substring p destdirlen) "/") - (error (format "expecting a DESTDIR prefix of ~s in ~s" destdir p))))) - (set! dirs (map (lambda (d) (list (car d) (remove-dest (cadr d)))) dirs)) - ;; no need to send an explicit binfiles argument -- this function is used - ;; only when DESTDIR is present, so we're installing to a directory that - ;; has only our binaries - (fix-executables bindir) - (unless origtree? (write-config collectsdir))) +(define (make-install-destdir-fix) + (define destdir + (or (getenv "DESTDIR") + (error "missing DESTDIR value for make-install-destdir-fix"))) + (define destdirlen (string-length destdir)) + (define origtree? (equal? "yes" (get-arg))) + ;; grab paths before we change them + (define bindir (dir: 'bin)) + (define collectsdir (dir: 'collects)) + (define (remove-dest p) + (let ([pfx (and (< destdirlen (string-length p)) + (substring p 0 destdirlen))]) + (if (equal? pfx destdir) + (regexp-replace #rx"^/*" (substring p destdirlen) "/") + (error (format "expecting a DESTDIR prefix of ~s in ~s" destdir p))))) + (set! dirs (map (lambda (d) (list (car d) (remove-dest (cadr d)))) dirs)) + ;; no need to send an explicit binfiles argument -- this function is used + ;; only when DESTDIR is present, so we're installing to a directory that + ;; has only our binaries + (fix-executables bindir) + (unless origtree? (write-config collectsdir))) - ;; -------------------------------------------------------------------------- +;; -------------------------------------------------------------------------- - (case op - [(move) (move/copy-distribution #t)] - [(copy) (move/copy-distribution #f)] - [(make-install-copytree) (make-install-copytree)] - [(make-install-destdir-fix) (make-install-destdir-fix)] - [else (error (format "unknown operation: ~e" op))]) - - ) +(case op + [(move) (move/copy-distribution #t)] + [(copy) (move/copy-distribution #f)] + [(make-install-copytree) (make-install-copytree)] + [(make-install-destdir-fix) (make-install-destdir-fix)] + [else (error (format "unknown operation: ~e" op))])