make installers: support Mac OS X ".pkg" format

The ".pkg"-based installer doesn't provide the option of picking
an installation path, but it can add a path in "/etc/paths.d"
so that users do not have to explicitly set the `PATH` environment
variable.
This commit is contained in:
Matthew Flatt 2013-11-10 12:17:01 -07:00
parent 58261e3495
commit 7dd10fc9bd
8 changed files with 172 additions and 11 deletions

View File

@ -134,6 +134,10 @@ RELEASE_MODE =
# proper) on a client that has the run-time system in source form: # proper) on a client that has the run-time system in source form:
SOURCE_MODE = SOURCE_MODE =
# Set to "--mac-pkg" to create ".pkg"-based installers for Mac OS X,
# instead of a ".dmg" for drag-and-drop installation:
MAC_PKG_MODE =
# Set to "--source --no-setup" to include packages in an installer # Set to "--source --no-setup" to include packages in an installer
# (or archive) only in source form: # (or archive) only in source form:
PKG_SOURCE_MODE = PKG_SOURCE_MODE =
@ -380,7 +384,7 @@ binary-catalog-server:
COPY_ARGS = SERVER=$(SERVER) SERVER_PORT=$(SERVER_PORT) SERVER_HOSTS="$(SERVER_HOSTS)" \ COPY_ARGS = SERVER=$(SERVER) SERVER_PORT=$(SERVER_PORT) SERVER_HOSTS="$(SERVER_HOSTS)" \
PKGS="$(PKGS)" BUILD_STAMP="$(BUILD_STAMP)" \ PKGS="$(PKGS)" BUILD_STAMP="$(BUILD_STAMP)" \
RELEASE_MODE=$(RELEASE_MODE) SOURCE_MODE=$(SOURCE_MODE) \ RELEASE_MODE=$(RELEASE_MODE) SOURCE_MODE=$(SOURCE_MODE) MAC_PKG_MODE=$(MAC_PKG_MODE) \
PKG_SOURCE_MODE="$(PKG_SOURCE_MODE)" INSTALL_NAME="$(INSTALL_NAME)"\ PKG_SOURCE_MODE="$(PKG_SOURCE_MODE)" INSTALL_NAME="$(INSTALL_NAME)"\
DIST_NAME="$(DIST_NAME)" DIST_BASE=$(DIST_BASE) \ DIST_NAME="$(DIST_NAME)" DIST_BASE=$(DIST_BASE) \
DIST_DIR=$(DIST_DIR) DIST_SUFFIX=$(DIST_SUFFIX) \ DIST_DIR=$(DIST_DIR) DIST_SUFFIX=$(DIST_SUFFIX) \
@ -426,7 +430,7 @@ bundle-config:
$(RACKET) -l distro-build/set-config $(SET_BUNDLE_CONFIG_q) $(RACKET) -l distro-build/set-config $(SET_BUNDLE_CONFIG_q)
UPLOAD_q = --readme http://$(SVR_PRT)/$(README) --upload http://$(SVR_PRT)/ --desc "$(DIST_DESC)" UPLOAD_q = --readme http://$(SVR_PRT)/$(README) --upload http://$(SVR_PRT)/ --desc "$(DIST_DESC)"
DIST_ARGS_q = $(UPLOAD_q) $(RELEASE_MODE) $(SOURCE_MODE) \ DIST_ARGS_q = $(UPLOAD_q) $(RELEASE_MODE) $(SOURCE_MODE) $(MAC_PKG_MODE) \
"$(DIST_NAME)" $(DIST_BASE) $(DIST_DIR) "$(DIST_SUFFIX)" \ "$(DIST_NAME)" $(DIST_BASE) $(DIST_DIR) "$(DIST_SUFFIX)" \
"$(SIGN_IDENTITY)" "$(SIGN_IDENTITY)"

View File

@ -145,6 +145,7 @@
[(#:source?) (boolean? val)] [(#:source?) (boolean? val)]
[(#:source-runtime?) (boolean? val)] [(#:source-runtime?) (boolean? val)]
[(#:source-pkgs?) (boolean? val)] [(#:source-pkgs?) (boolean? val)]
[(#:mac-pkg?) (boolean? val)]
[(#:site-dest) (path-string? val)] [(#:site-dest) (path-string? val)]
[(#:pdf-doc?) (boolean? val)] [(#:pdf-doc?) (boolean? val)]
[(#:max-snapshots) (real? val)] [(#:max-snapshots) (real? val)]

View File

@ -244,7 +244,7 @@ Site-configuration keywords (where <string*> means no spaces, etc.):
makefile variable makefile variable
#:source? <boolean> --- determines the default value for #:source? <boolean> --- determines the default value for
`#:source-runtime' and `#:source-pkgs' `#:source-runtime?' and `#:source-pkgs'
#:source-runtime? <boolean> --- if true, then create an archive that #:source-runtime? <boolean> --- if true, then create an archive that
contains the run-time system in source form (possibly with built contains the run-time system in source form (possibly with built
@ -258,6 +258,9 @@ Site-configuration keywords (where <string*> means no spaces, etc.):
when the `#:source-runtime?' value is also #t; the default is the when the `#:source-runtime?' value is also #t; the default is the
value of `#:source?' value of `#:source?'
#:mac-pkg? --- if true, creates a ".pkg" for Mac OS X (in
single-file format) instead of a ".dmg"; the default is #f
#:max-snapshots <number> --- number of snapshots to keep, used by #:max-snapshots <number> --- number of snapshots to keep, used by
the `snapshot-site' makefile target the `snapshot-site' makefile target

View File

@ -277,6 +277,7 @@
(define source? (get-opt c '#:source? #f)) (define source? (get-opt c '#:source? #f))
(define source-pkgs? (get-opt c '#:source-pkgs? source?)) (define source-pkgs? (get-opt c '#:source-pkgs? source?))
(define source-runtime? (get-opt c '#:source-runtime? source?)) (define source-runtime? (get-opt c '#:source-runtime? source?))
(define mac-pkg? (get-opt c '#:mac-pkg? #f))
(define install-name (get-opt c '#:install-name (if release? (define install-name (get-opt c '#:install-name (if release?
"" ""
snapshot-install-name))) snapshot-install-name)))
@ -301,6 +302,7 @@
" PKG_SOURCE_MODE=" (if source-pkgs? " PKG_SOURCE_MODE=" (if source-pkgs?
(q "--source --no-setup") (q "--source --no-setup")
(q "")) (q ""))
" MAC_PKG_MODE=" (if mac-pkg? "--mac-pkg" (q ""))
" README=" (q (file-name-from-path readme)))) " README=" (q (file-name-from-path readme))))
(define (unix-build c platform host port user server server-port repo clean? pull? readme) (define (unix-build c platform host port user server server-port repo clean? pull? readme)

View File

@ -0,0 +1,134 @@
#lang at-exp racket/base
(require racket/system
racket/file
racket/format
racket/runtime-path
ds-store
ds-store/alias
xml)
(provide installer-pkg)
(define pkgbuild "/usr/bin/pkgbuild")
(define productbuild "/usr/bin/productbuild")
(define-runtime-path bg-image "macosx-installer/pkg-bg.png")
(define (system*/show . l)
(displayln (apply ~a #:separator " " l))
(flush-output)
(unless (apply system* l)
(error "failed")))
(define (gen-install-script install-dest)
(~a "#!/bin/sh\n"
"echo \"" (regexp-replace* #rx"\""
install-dest
"\"'\"'\"")
"\"/bin > /etc/paths.d/racket\n"))
(define (make-pkg human-name src-dir pkg-name readme sign-identity)
(define install-dest (string-append "/Applications/" human-name))
(define id (string-append "org.racket-lang."
(regexp-replace* #rx" "
human-name
"-")))
(define (make-rel dir-name)
(let-values ([(base name dir?) (split-path src-dir)])
(build-path base dir-name)))
(define work-dir (make-rel "work"))
(delete-directory/files work-dir #:must-exist? #f)
(define scripts-dir (make-rel "scripts"))
(delete-directory/files scripts-dir #:must-exist? #f)
(define resources-dir (make-rel "resources"))
(delete-directory/files resources-dir #:must-exist? #f)
(printf "Creating ~a\n" scripts-dir)
(make-directory* scripts-dir)
(define postinstall (build-path scripts-dir "postinstall"))
(call-with-output-file*
postinstall
(lambda (o)
(write-string (gen-install-script install-dest) o)))
(file-or-directory-permissions postinstall #o770)
(printf "Creating ~a\n" resources-dir)
(make-directory* resources-dir)
(copy-file bg-image (build-path resources-dir "background.png"))
(printf "Copying ~a\n" src-dir)
(define dest-dir work-dir)
(copy-directory/files src-dir dest-dir
#:keep-modify-seconds? #t)
(when readme
(call-with-output-file*
(build-path dest-dir "README.txt")
#:exists 'truncate
(lambda (o)
(display readme o))))
(copy-file (build-path dest-dir "README.txt")
(build-path resources-dir "README.txt"))
(apply system*/show
pkgbuild
(append
(list "--root" dest-dir
"--install-location" install-dest
"--scripts" scripts-dir
"--identifier" id
"--version" (version))
(if (string=? sign-identity "")
null
(list "--sign" sign-identity))
(list (make-rel "racket.pkg"))))
(define pkg-xml (make-rel "racket.xml"))
(system*/show productbuild
"--synthesize"
"--package" (make-rel "racket.pkg")
pkg-xml)
(define synthesized (call-with-input-file*
pkg-xml
read-xml))
(define updated
(struct-copy document synthesized
[element (let ([e (document-element synthesized)])
(struct-copy element e
[content
(list*
(element #f #f
'title
null
(list (pcdata #f #f human-name)))
(element #f #f
'readme
(list (attribute #f #f 'file "README.txt"))
null)
(element #f #f
'background
(list (attribute #f #f 'file "background.png")
(attribute #f #f 'alignment "topleft")
(attribute #f #f 'scaling "none"))
null)
(element-content e))]))]))
(call-with-output-file*
pkg-xml
#:exists 'truncate
(lambda (o)
(write-xml updated o)))
(system*/show productbuild
"--distribution" pkg-xml
"--package-path" (make-rel 'same)
"--resources" resources-dir
"--identifier" id
"--version" (version)
pkg-name))
(define (installer-pkg human-name base-name dist-suffix readme sign-identity)
(define pkg-name (format "bundle/~a-~a~a.pkg"
base-name
(system-library-subpath #f)
dist-suffix))
(make-pkg human-name "bundle/racket" pkg-name readme sign-identity)
pkg-name)

View File

@ -2,6 +2,7 @@
(require racket/cmdline (require racket/cmdline
"installer-sh.rkt" "installer-sh.rkt"
"installer-dmg.rkt" "installer-dmg.rkt"
"installer-pkg.rkt"
"installer-exe.rkt" "installer-exe.rkt"
"installer-tgz.rkt" "installer-tgz.rkt"
net/url net/url
@ -12,6 +13,7 @@
(define release? #f) (define release? #f)
(define source? #f) (define source? #f)
(define mac-pkg? #f)
(define upload-to #f) (define upload-to #f)
(define upload-desc "") (define upload-desc "")
(define download-readme #f) (define download-readme #f)
@ -23,6 +25,8 @@
(set! release? #t)] (set! release? #t)]
[("--source") "Create a source installer" [("--source") "Create a source installer"
(set! source? #t)] (set! source? #t)]
[("--mac-pkg") "Create a \".pkg\" installer on Mac OS X"
(set! mac-pkg? #t)]
[("--upload") url "Upload installer" [("--upload") url "Upload installer"
(set! upload-to url)] (set! upload-to url)]
[("--desc") desc "Description to accompany upload" [("--desc") desc "Description to accompany upload"
@ -58,7 +62,10 @@
(installer-tgz base-name dir-name dist-suffix readme) (installer-tgz base-name dir-name dist-suffix readme)
(case (system-type) (case (system-type)
[(unix) (installer-sh human-name base-name dir-name release? dist-suffix readme)] [(unix) (installer-sh human-name base-name dir-name release? dist-suffix readme)]
[(macosx) (installer-dmg human-name base-name dist-suffix readme sign-identity)] [(macosx) (if mac-pkg?
(installer-pkg (if release? short-human-name human-name)
base-name dist-suffix readme sign-identity)
(installer-dmg human-name base-name dist-suffix readme sign-identity))]
[(windows) (installer-exe short-human-name base-name release? dist-suffix readme)]))) [(windows) (installer-exe short-human-name base-name release? dist-suffix readme)])))
(call-with-output-file* (call-with-output-file*

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -105,10 +105,20 @@
@~a{The distribution includes any pre-installed packages in source form.}])) @~a{The distribution includes any pre-installed packages in source form.}]))
(define (make-macosx-notes config) (define (make-macosx-notes config)
(if (hash-ref config '#:mac-pkg? #f)
@~a{The installation directory is
/Applications/@(string-append
(hash-ref config '#:dist-name "Racket")
(if (hash-ref config '#:release? #f)
""
(string-append " v" (version))))
The installer also adjusts "/etc/paths.d/racket" to point to that
directory's "bin" directory, which adjusts the default PATH
environment variable for all users.}
@~a{Install by dragging the enclosing @~a{Install by dragging the enclosing
@|(hash-ref config '#:dist-name "Racket")| v@(version) @|(hash-ref config '#:dist-name "Racket")| v@(version)
folder to your Applications folder --- or wherever you like. You can folder to your Applications folder --- or wherever you like. You can
move the folder at any time, but do not move applications or other move the folder at any time, but do not move applications or other
files within the folder. If you want to use the Racket command-line files within the folder. If you want to use the Racket command-line
programs, then (optionally) add the path of the "bin" subdirectory to programs, then (optionally) add the path of the "bin" subdirectory to
your PATH environment variable.}) your PATH environment variable.}))