raco pkg: support platform-specific package dependencies

A platform-specific dependency is useful for triggering
installation of a platform-specific library only on the
platform where its needed.
This commit is contained in:
Matthew Flatt 2013-04-14 15:43:22 -06:00
parent 37aa091e1c
commit b47c1857b5
6 changed files with 173 additions and 22 deletions

View File

@ -172,7 +172,28 @@
(and (list? dep)
(= 2 (length dep))
(package-source? (car dep))
(version? (cadr dep))))))
(version? (cadr dep)))
(and (list? dep)
((length dep) . >= . 1)
(odd? (length dep))
(package-source? (car dep))
(let loop ([saw (hash)] [dep (cdr dep)])
(cond
[(null? dep) #t]
[(hash-ref saw (car dep) #f) #f]
[else
(define kw (car dep))
(define val (cadr dep))
(and
(cond
[(eq? kw '#:version) (version? val)]
[(eq? kw '#:platform)
(or (string? val)
(regexp? val)
(memq val '(unix windows macosx)))]
[else #f])
(loop (hash-set saw (car dep) #t)
(cddr dep)))]))))))
(pkg-error (~a "invalid `deps' specification\n"
" specification: ~e")
deps)))
@ -187,9 +208,30 @@
(car dep)))
(define (dependency->version dep)
(if (string? dep)
#f
(cadr dep)))
(cond
[(string? dep) #f]
[(keyword? (cadr dep))
(dependency-lookup '#:version dep)]
[else (cadr dep)]))
(define (dependency-lookup kw dep)
(cond
[(string? dep) #f]
[(keyword? (cadr dep))
(define p (member kw (cdr dep)))
(and p (cadr p))]
[else #f]))
(define (dependency-this-platform? dep)
(define p (dependency-lookup '#:platform dep))
(if p
(if (symbol? p)
(eq? p (system-type))
(let ([s (path->string (system-library-subpath #f))])
(if (regexp? p)
(regexp-match? p s)
(equal? p s))))
#t))
(define (with-package-lock* read-only? t)
(define d (pkg-dir))
@ -892,6 +934,7 @@
(filter-not (λ (dep)
(define name (dependency->name dep))
(or (equal? name "racket")
(not (dependency-this-platform? dep))
(hash-ref simultaneous-installs name #f)
(hash-has-key? db name)))
deps)))
@ -948,8 +991,11 @@
(filter-map (λ (dep)
(define name (dependency->name dep))
(define req-vers (dependency->version dep))
(define this-platform? (dependency-this-platform? dep))
(define-values (inst-vers* can-try-update?)
(cond
[(not this-platform?)
(values #f #f)]
[(not req-vers)
(values #f #f)]
[(equal? name "racket")
@ -964,7 +1010,8 @@
(values (get-metadata metadata-ns (package-directory name)
'version (lambda () "0.0"))
#t)]))
(define inst-vers (if (and req-vers
(define inst-vers (if (and this-platform?
req-vers
(not (and (string? inst-vers*)
(valid-version? inst-vers*))))
(begin
@ -974,7 +1021,8 @@
inst-vers*)
"0.0")
inst-vers*))
(and req-vers
(and this-platform?
req-vers
((version->integer req-vers)
. > .
(version->integer inst-vers))

View File

@ -632,7 +632,15 @@ Package metadata, including dependencies on other packages, is reported
by an @filepath{info.rkt} module within the package. This module must be
implemented in the @racketmodname[setup/infotab] language.
The following fields are used by the package manager:
For example, a basic @filepath{info.rkt} file might be
@codeblock{
#lang setup/infotab
(define version "1.0")
(define deps (list _package-source-string ...))
}
The following @filepath{info.rkt} fields are used by the package manager:
@itemlist[
@ -640,9 +648,42 @@ The following fields are used by the package manager:
@tech{version} of a package is @racket["0.0"].}
@item{@racketidfont{deps} --- a list of dependencies, where each
dependency is either a @tech{package source} strings or a list
containing a @tech{package source} string and a
@tech{version} string.
dependency has one of the following forms:
@itemlist[
@item{A string for a @tech{package source}.}
@item{A list of the form
@racketblock[(list _package-source-string
_keyword-and-spec ...)]
where each @racket[_keyword-and-spec] has a
distinct keyword in the form
@racketgrammar*[#:literals (quote)
[keyword-and-spec
(code:line '#:version version-string)
(code:line '#:platform platform-spec)]
[platform-spec string symbol regexp]]
A @racket[_version-string] specifies a lower bound
on an acceptable @tech{version} of the needed package.
A @racket[_platform-spec] indicates that the dependency
applies only for platforms with a matching result from
@racket[(system-type)] when @racket[_platforms-spec] is
a symbol or @racket[(path->string
(system-library-subpath #f))] when
@racket[_platform-spec] is a regular expression. For
example, platform-specific binaries can be placed into
their own packages, with one separate package and one
dependency for each supported platform.}
@item{A list of the form
@racketblock[(list _package-source-string _version-string)]
which is deprecated and equivalent to
@racketblock[(list _package-source-string '#:version _version-string)]}
]
Each elements of the @racketidfont{deps} list determines a
dependency on the @tech{package} whose name is inferred from
@ -651,9 +692,6 @@ The following fields are used by the package manager:
indicates where to get the package if needed to satisfy the
dependency.
When provided, a @tech{version} string specifies a lower bound
on an acceptable @tech{version} of the package.
Use the package name @racket["racket"] to specify a dependency
on the version of the Racket installation.}
@ -667,14 +705,6 @@ The following fields are used by the package manager:
]
For example, a basic @filepath{info.rkt} file might be
@codeblock{
#lang setup/infotab
(define version "1.0")
(define deps (list _package-source-string ...))
}
@; ----------------------------------------
@section{@|Planet1| Compatibility}

View File

@ -1,4 +1,4 @@
#lang setup/infotab
(define deps '(("pkg-v" "2.0")
(define deps '(("pkg-v" #:version "2.0")
("racket" "5.3.1.10")))

View File

@ -52,5 +52,6 @@
"update-deps"
"update-auto"
"versions"
"platform"
"raco"
"indexes")

View File

@ -0,0 +1,71 @@
#lang racket/base
(require rackunit
racket/file
racket/format
pkg/util
(prefix-in db: pkg/pnr-db)
"shelly.rkt"
"util.rkt")
(pkg-tests
(shelly-begin
(define pkgs-dir (make-temporary-file "~a-pkgs" 'directory))
(define db (build-path pkgs-dir "pnr.sqlite"))
(define pkg-x-dir (build-path pkgs-dir "pkg-x"))
(make-directory* pkg-x-dir)
(call-with-output-file*
(build-path pkg-x-dir "info.rkt")
(lambda (o)
(displayln "#lang setup/infotab" o)
(write `(define deps '(("pkg-x-windows" #:platform windows)
("pkg-x-unix" #:platform unix)
("pkg-x-macosx" #:platform macosx)
("pkg-x-platform1"
#:platform
,(path->string (system-library-subpath #f)))
("pkg-x-platform2" #:platform #rx".")
("pkg-x-platform-no" #:platform #rx"no such platform")))
o)))
(parameterize ([db:current-pkg-index-file db])
(db:set-indexes! '("local"))
(db:set-pkgs! "local"
'("pkg-x" "pkg-x-windows" "pkg-x-unix" "pkg-x-macosx"
"pkg-x-platform1" "pkg-x-platform2")))
(define (create-package name)
(define pkg-name name)
(define dir (build-path pkgs-dir pkg-name))
(make-directory* dir)
(define coll-dir (build-path dir name))
(make-directory* coll-dir)
(call-with-output-file*
(build-path coll-dir "main.rkt")
(lambda (o)
(displayln "#lang racket/base" o)))
(parameterize ([db:current-pkg-index-file db])
(db:set-pkg! pkg-name "local" "author@place" (path->string dir) "123456" "")))
(create-package "pkg-x")
(create-package "pkg-x-unix")
(create-package "pkg-x-windows")
(create-package "pkg-x-macosx")
(create-package "pkg-x-platform1")
(create-package "pkg-x-platform2")
(with-fake-root
(shelly-begin
$ (~a "raco pkg config --set indexes file://" (path->string db))
$ "racket -e '(require pkg-x)'" =exit> 1
$ "raco pkg install --deps search-auto pkg-x" =exit> 0
$ "racket -e '(require pkg-x)'" =exit> 0
$ (~a "racket -e '(require pkg-x-" (system-type) ")'") =exit> 0
$ "racket -e '(require pkg-x-platform1)'" =exit> 0
$ "racket -e '(require pkg-x-platform2)'" =exit> 0
$ "racket -e '(require pkg-x-platform-no)'" =exit> 1
$ "raco pkg remove pkg-x"))
(delete-directory/files pkgs-dir)))

View File

@ -140,6 +140,7 @@
(file->string "test-pkgs/pkg-test2.zip.CHECKSUM")
'source
"http://localhost:9999/pkg-test2.zip"))
(hash-set! *index-ht-2* "pkg-test2-snd"
(hasheq 'checksum
(file->string "test-pkgs/pkg-test2.zip.CHECKSUM")