reorganize pkg/lib implementation

Split the module into several (smaller) modules.
This commit is contained in:
Matthew Flatt 2014-10-14 17:14:46 -06:00
parent 1cc86d3cea
commit 5650e8fc03
45 changed files with 4436 additions and 4048 deletions

View File

@ -8,7 +8,6 @@
racket/runtime-path
racket/path
racket/list
pkg/util
"shelly.rkt"
"util.rkt")

View File

@ -8,7 +8,6 @@
racket/runtime-path
racket/path
racket/list
pkg/util
"shelly.rkt"
"util.rkt")

View File

@ -9,7 +9,6 @@
racket/runtime-path
racket/path
racket/list
pkg/util
"shelly.rkt"
"util.rkt")

View File

@ -8,7 +8,6 @@
racket/runtime-path
racket/path
racket/list
pkg/util
"shelly.rkt"
"util.rkt")

View File

@ -8,7 +8,6 @@
racket/runtime-path
racket/path
racket/list
pkg/util
"shelly.rkt"
"util.rkt")

View File

@ -1,7 +1,6 @@
#lang racket/base
(require rackunit
racket/system
pkg/util
"shelly.rkt"
"util.rkt")

View File

@ -12,7 +12,6 @@
file/zip
file/unzip
net/url
pkg/util
setup/dirs
"shelly.rkt"
"util.rkt")

View File

@ -8,7 +8,6 @@
racket/runtime-path
racket/path
racket/list
pkg/util
"shelly.rkt"
"util.rkt")

View File

@ -2,7 +2,6 @@
(require rackunit
racket/file
racket/format
pkg/util
(prefix-in db: pkg/db)
"shelly.rkt"
"util.rkt")

View File

@ -1,7 +1,6 @@
#lang racket/base
(require rackunit
racket/system
pkg/util
"shelly.rkt"
"util.rkt")

View File

@ -8,7 +8,6 @@
racket/runtime-path
racket/path
racket/list
pkg/util
"shelly.rkt"
"util.rkt")

View File

@ -9,7 +9,6 @@
racket/path
racket/list
racket/format
pkg/util
"shelly.rkt"
"util.rkt")

View File

@ -8,7 +8,6 @@
racket/runtime-path
racket/path
racket/list
pkg/util
"shelly.rkt"
"util.rkt")

View File

@ -10,7 +10,6 @@
racket/list
racket/format
setup/dirs
pkg/util
"shelly.rkt")
(define-runtime-path test-directory ".")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,154 @@
#lang racket/base
(require racket/set
setup/collection-name
setup/matching-platform
setup/getinfo
"../path.rkt"
"params.rkt"
"metadata.rkt"
"get-info.rkt")
(provide pkg-directory->additional-installs
directory->additional-installs
get-additional-installed)
(define (pkg-directory->additional-installs dir pkg-name
#:namespace [metadata-ns (make-metadata-namespace)]
#:system-type [sys-type #f]
#:system-library-subpath [sys-lib-subpath #f])
(set->list (directory->additional-installs dir pkg-name metadata-ns
#:system-type sys-type
#:system-library-subpath sys-lib-subpath)))
(define (directory->additional-installs dir pkg-name metadata-ns
#:system-type [sys-type #f]
#:system-library-subpath [sys-lib-subpath #f])
(define single-collect
(pkg-single-collection dir #:name pkg-name #:namespace metadata-ns))
(let loop ([s (set)] [f dir] [top? #t] [omits (set)])
(cond
[(and (directory-exists? f)
(not (set-member? omits (simplify-path f))))
(define i (get-pkg-info f metadata-ns))
(define omit-paths (if i
(i 'compile-omit-paths (lambda () null))
null))
(cond
[(eq? omit-paths 'all)
s]
[else
(define omit-files (if i
(i 'compile-omit-files (lambda () null))
null))
(define new-s
(if (and i (or single-collect (not top?)))
(set-union (extract-additional-installs i sys-type sys-lib-subpath)
s)
s))
(define new-omits
(set-union
omits
(for/set ([i (in-list (append omit-paths omit-files))])
(simplify-path (build-path f i)))))
(for/fold ([s new-s]) ([f (directory-list f #:build? #t)])
(loop s f #f new-omits))])]
[else s])))
(define (extract-additional-installs i sys-type sys-lib-subpath)
(define (extract-documents i)
(let ([s (i 'scribblings (lambda () null))])
(for/set ([doc (in-list (if (list? s) s null))]
#:when (and (list? doc)
(pair? doc)
(path-string? (car doc))
(or ((length doc) . < . 2)
(list? (cadr doc)))
(or ((length doc) . < . 4)
(collection-name-element? (list-ref doc 3)))))
(define flags (if ((length doc) . < . 2)
null
(cadr doc)))
(cond
[(member 'main-doc-root flags) '(main-doc-root . "root")]
[(member 'user-doc-root flags) '(user-doc-root . "root")]
[else
(cons 'doc
(string-foldcase
(if ((length doc) . < . 4)
(let-values ([(base name dir?) (split-path (car doc))])
(path->string (path-replace-suffix name #"")))
(list-ref doc 3))))]))))
(define (extract-paths i tag keys)
(define (get k)
(define l (i k (lambda () null)))
(if (and (list? l) (andmap path-string? l))
l
null))
(list->set (map (lambda (v) (cons tag
(let-values ([(base name dir?) (split-path v)])
;; Normalize case, because some platforms
;; have case-insensitive filesystems:
(string-foldcase (path->string name)))))
(apply
append
(for/list ([k (in-list keys)])
(get k))))))
(define (extract-launchers i)
(extract-paths i 'exe '(racket-launcher-names
mzscheme-launcher-names
gracket-launcher-names
mred-launcher-names)))
(define (extract-foreign-libs i)
(extract-paths i 'lib '(copy-foreign-libs
move-foreign-libs)))
(define (extract-shared-files i)
(extract-paths i 'share '(copy-shared-files
move-shared-files)))
(define (extract-man-pages i)
(extract-paths i 'man '(copy-man-pages
move-man-pages)))
(define (this-platform? i)
(define v (i 'install-platform (lambda () #rx"")))
(or (not (platform-spec? v))
(matching-platform? v
#:system-type sys-type
#:system-library-subpath sys-lib-subpath)))
(set-union (extract-documents i)
(extract-launchers i)
(if (this-platform? i)
(set-union
(extract-foreign-libs i)
(extract-shared-files i)
(extract-man-pages i))
(set))))
(define (get-additional-installed kind skip-ht-keys ai-cache metadata-ns path-pkg-cache)
(or (unbox ai-cache)
(let ()
(define skip-pkgs (list->set (hash-keys skip-ht-keys)))
(define dirs (find-relevant-directories '(scribblings
racket-launcher-names
mzscheme-launcher-names
gracket-launcher-names
mred-launcher-names
copy-foreign-libs
move-foreign-libs
copy-shared-files
move-shared-files
copy-man-pages
move-man-pages)
(if (eq? 'user (current-pkg-scope))
'all-available
'no-user)))
(define s (for/fold ([s (set)]) ([dir (in-list dirs)])
(cond
[(set-member? skip-pkgs (path->pkg dir #:cache path-pkg-cache))
s]
[else
(define i (get-pkg-info dir metadata-ns))
(if i
(set-union s (extract-additional-installs i #f #f))
s)])))
(set-box! ai-cache s)
s)))

View File

@ -0,0 +1,175 @@
#lang racket/base
(require racket/file
racket/set
racket/match
racket/list
setup/getinfo
"../path.rkt"
(prefix-in db: "../db.rkt")
"dirs.rkt"
"pkg-db.rkt"
"params.rkt"
"print.rkt"
"desc.rkt"
"stage.rkt"
"create.rkt"
"catalog-copy.rkt")
(provide pkg-archive-pkgs)
(define (pkg-archive-pkgs dest-dir pkg-names
#:include-deps? [include-deps? #f]
#:exclude [exclude null]
#:relative-sources? [relative-sources? #f]
#:quiet? [quiet? #f]
#:package-exn-handler [package-exn-handler
(λ (name exn) (raise exn))])
(struct pkg (deps build-deps) #:transparent)
(define (extract-pkg p) (if (string? p) p (car p)))
(define (add-package-from-dir src-f f-name pkgs)
(define i (get-info/full src-f))
(cond
[i
(hash-set pkgs f-name (pkg (map extract-pkg (i 'deps (lambda () null)))
(map extract-pkg (i 'build-deps (lambda () null)))))]
[else pkgs]))
(define unfiltered-pkgs
(for/fold ([pkgs (hash)]) ([pkg-scope (in-list (get-all-pkg-scopes))])
(define pkg-names (installed-pkg-names #:scope pkg-scope))
(parameterize ([current-pkg-scope pkg-scope])
(for/fold ([pkgs pkgs]) ([pkg (in-list pkg-names)])
(define dir (pkg-directory pkg))
(cond [dir (add-package-from-dir dir pkg pkgs)]
[else pkgs])))))
(define exclude+ (list* "base" "racket" exclude))
(for ([p (in-list pkg-names)])
(unless (hash-ref unfiltered-pkgs p #f)
(pkg-error "cannot archive package \"~a\" because it is not installed" p)))
;; Filter to roots:
(define pkgs/deps
(cond
[(not include-deps?)
(for/hash ([(k v) (in-hash unfiltered-pkgs)]
#:when (member k pkg-names))
(values k v))]
[else
(define seen (make-hash))
(define (loop pkg)
(cond
[(member pkg exclude+) (void)]
[(hash-ref seen pkg #f) (void)]
[else
(define p (hash-ref unfiltered-pkgs pkg #f))
(when p
(hash-set! seen pkg #t)
(for-each loop (pkg-deps p))
(for-each loop (pkg-build-deps p)))]))
(for-each loop pkg-names)
(for/hash ([(k v) (in-hash unfiltered-pkgs)]
#:when (hash-ref seen k #f))
(values k v))]))
(define all-pkg-names (hash-keys pkgs/deps))
;; The temporary catalog we'll create, simulating the current install
(define temp-catalog-file (make-temporary-file "pkg~a.sqlite"))
;; all the current installed packages
(define all-installed-pkgs
(for*/hash ([scope (in-list (get-all-pkg-scopes))]
[(k v) (in-hash (read-pkgs-db scope))])
(values k v)))
;; get the pkg descriptions we want
(define pkgs
(for/hash ([p (in-list all-pkg-names)])
(values p
(hash-ref all-installed-pkgs p
(λ _ (pkg-error
"cannot archive package \"~a\" because it is not installed" p))))))
;; set up temporary catalog with the right packages
(parameterize ([db:current-pkg-catalog-file temp-catalog-file])
(db:set-catalogs! '("local"))
(db:set-pkgs! "local" all-pkg-names))
;; Remove any package not in `pkgs`:
(define pkgs-dir (build-path dest-dir "pkgs"))
(when (directory-exists? pkgs-dir)
(define keep-pkgs (list->set all-pkg-names))
(for ([f (in-list (directory-list pkgs-dir))])
(cond
[(regexp-match #rx"^(.*)[.]zip(?:[.]CHECKSUM)?$" f)
=> (lambda (m)
(unless (set-member? keep-pkgs (cadr m))
(unless quiet?
(printf/flush "Removing old package file ~a\n" f))
(delete-file (build-path pkgs-dir f))))])))
(define (pkg->deps p)
(match-define (pkg deps build-deps) (hash-ref pkgs/deps p))
;; NOTE: This include deps that don't get archived. It's not
;; obvious which is the right decision but I've gone with
;; including them since for "base" keeping but not archiving
;; seems like the right choice.
(remove-duplicates (append deps build-deps)))
;; Check on each new package:
(for ([(name pkg-i) (in-hash pkgs)])
(match-define (pkg-info _ checksum _) pkg-i)
(with-handlers ([exn:fail? (λ (exn) (package-exn-handler name exn))])
(define pkg-file (build-path dest-dir "pkgs" (format "~a.zip" name)))
(define pkg-checksum-file (path-replace-suffix pkg-file #".zip.CHECKSUM"))
(define pkg-dir (pkg-directory name))
(unless pkg-dir
(pkg-error "no directory found for package \"~a\"" name))
(unless quiet?
(printf/flush "== Archiving ~a ==\nchecksum: ~a\n" name checksum))
;; Download/unpack existing package:
(define-values (staged-name staged-dir staged-checksum clean? staged-mods)
(pkg-stage
(pkg-desc (path->string pkg-dir) 'dir name checksum #f)
#:in-place? #f
#:use-cache? #t
#:quiet? quiet?))
(make-directory* (build-path dest-dir "pkgs"))
;; Repack:
(pkg-create 'zip
staged-dir
#:pkg-name name
#:dest (build-path dest-dir "pkgs")
#:quiet? quiet?)
(when clean? (delete-directory/files staged-dir))
;; Record packed result:
(define new-checksum (file->string pkg-checksum-file))
(parameterize ([db:current-pkg-catalog-file temp-catalog-file])
(db:set-pkg! name "local"
""
(path->string (path->complete-path pkg-file))
new-checksum
"")
(db:set-pkg-dependencies! name "local"
new-checksum
(pkg->deps name))
(db:set-pkg-modules! name "local"
new-checksum
(set->list staged-mods)))))
(define dest-catalog (build-path dest-dir "catalog"))
(unless quiet?
(printf/flush "Creating catalog ~a\n" dest-catalog))
(pkg-catalog-copy (list temp-catalog-file)
(build-path dest-dir "catalog")
#:force? #t
#:override? #t
#:relative-sources? relative-sources?)
(delete-file temp-catalog-file))

View File

@ -0,0 +1,125 @@
#lang racket/base
(require racket/format
racket/file
racket/set
openssl/sha1
(prefix-in db: "../db.rkt")
"catalog.rkt"
"catalog-copy.rkt"
"print.rkt"
"stage.rkt"
"desc.rkt"
"create.rkt")
(provide pkg-catalog-archive)
(define (pkg-catalog-archive dest-dir
src-catalogs
#:from-config? [from-config? #f]
#:state-catalog [state-catalog #f]
#:relative-sources? [relative-sources? #f]
#:quiet? [quiet? #f]
#:package-exn-handler [package-exn-handler (lambda (name exn) (raise exn))])
(when (and state-catalog
(not (db-path? (if (path? state-catalog)
state-catalog
(string->path state-catalog)))))
(pkg-error (~a "bad state file path\n"
" given: ~a\n"
" expected: path with \".sqlite\" extension")
state-catalog))
;; Take a snapshot of the source catalog:
(define temp-catalog-file (make-temporary-file "pkg~a.sqlite"))
(pkg-catalog-copy (map url-or-path->url-string
(map src->url-or-path src-catalogs))
temp-catalog-file
#:force? #t ; replaces temporary file
#:from-config? from-config?)
(define pkgs
(parameterize ([db:current-pkg-catalog-file temp-catalog-file])
(db:get-pkgs)))
;; Reset state catalog to new packages:
(when state-catalog
(parameterize ([db:current-pkg-catalog-file state-catalog])
(db:set-catalogs! '("local"))
(db:set-pkgs! "local" (map db:pkg-name pkgs))))
;; Remove any package not in `pkgs`:
(define pkgs-dir (build-path dest-dir "pkgs"))
(when (directory-exists? pkgs-dir)
(define keep-pkgs (list->set (map db:pkg-name pkgs)))
(for ([f (in-list (directory-list pkgs-dir))])
(cond
[(regexp-match #rx"^(.*)[.]zip(?:[.]CHECKSUM)?$" f)
=> (lambda (m)
(unless (set-member? keep-pkgs (cadr m))
(unless quiet?
(printf/flush "Removing old package file ~a\n" f))
(delete-file (build-path pkgs-dir f))))])))
;; Check on each new package:
(for ([pkg (in-list (sort pkgs string<? #:key db:pkg-name))])
(define name (db:pkg-name pkg))
(with-handlers ([exn:fail? (lambda (exn)
(package-exn-handler name exn))])
(define current-checksum (and state-catalog
(parameterize ([db:current-pkg-catalog-file state-catalog])
(define l (db:get-pkgs #:name (db:pkg-name pkg)))
(and (= 1 (length l))
(db:pkg-checksum (car l))))))
(define pkg-file (build-path dest-dir "pkgs" (format "~a.zip" name)))
(define pkg-checksum-file (path-replace-suffix pkg-file #".zip.CHECKSUM"))
(unless (and current-checksum
(equal? current-checksum (db:pkg-checksum pkg))
(file-exists? pkg-file)
(file-exists? pkg-checksum-file)
(equal? (file->string pkg-checksum-file)
(call-with-input-file* pkg-file sha1)))
(unless quiet?
(printf/flush "== Archiving ~a ==\nchecksum: ~a\n" (db:pkg-name pkg) (db:pkg-checksum pkg)))
;; Download/unpack existing package:
(define-values (staged-name staged-dir staged-checksum clean? staged-mods)
(pkg-stage
(pkg-desc (db:pkg-source pkg) #f (db:pkg-name pkg) (db:pkg-checksum pkg) #f)
#:in-place? #t
#:use-cache? #t
#:quiet? quiet?))
(make-directory* (build-path dest-dir "pkgs"))
;; Repack:
(pkg-create 'zip
staged-dir
#:pkg-name name
#:dest (build-path dest-dir "pkgs")
#:quiet? quiet?)
(when clean? (delete-directory/files staged-dir))
;; Record packed result:
(when state-catalog
(parameterize ([db:current-pkg-catalog-file state-catalog])
(db:set-pkg! name "local"
(db:pkg-author pkg)
(db:pkg-source pkg)
staged-checksum
(db:pkg-desc pkg)))))
;; Record packed result:
(define new-checksum (file->string pkg-checksum-file))
(parameterize ([db:current-pkg-catalog-file temp-catalog-file])
(define modules (db:get-pkg-modules name (db:pkg-catalog pkg) (db:pkg-checksum pkg)))
(define dependencies (db:get-pkg-dependencies name (db:pkg-catalog pkg) (db:pkg-checksum pkg)))
(db:set-pkg! name (db:pkg-catalog pkg)
(db:pkg-author pkg)
(path->string (path->complete-path pkg-file))
new-checksum
(db:pkg-desc pkg))
(db:set-pkg-modules! name (db:pkg-catalog pkg)
new-checksum
modules)
(db:set-pkg-dependencies! name (db:pkg-catalog pkg)
new-checksum
dependencies))))
(define dest-catalog (build-path dest-dir "catalog"))
(unless quiet?
(printf/flush "Creating catalog ~a\n" dest-catalog))
(pkg-catalog-copy (list temp-catalog-file)
(build-path dest-dir "catalog")
#:force? #t
#:override? #t
#:relative-sources? relative-sources?)
(delete-file temp-catalog-file))

View File

@ -0,0 +1,162 @@
#lang racket/base
(require racket/format
racket/file
net/url
(prefix-in db: "../db.rkt")
"config.rkt"
"catalog.rkt"
"print.rkt"
"params.rkt")
(provide pkg-catalog-copy
src->url-or-path
url-or-path->url-string)
(define (src->url-or-path src)
(cond
[(path? src) (path->complete-path src)]
[(regexp-match? #rx"^https?://" src)
(string->url src)]
[(regexp-match? #rx"^file://" src)
(url->path (string->url src))]
[(regexp-match? #rx"^[a-zA-Z]*://" src)
(pkg-error (~a "unrecognized URL scheme for a catalog\n"
" URL: ~a")
src)]
[else (path->complete-path src)]))
(define (url-or-path->url-string p)
(url->string (if (url? p)
p
(path->url p))))
(define (pkg-catalog-copy srcs dest
#:from-config? [from-config? #f]
#:merge? [merge? #f]
#:force? [force? #f]
#:override? [override? #f]
#:relative-sources? [relative-sources? #f])
(define src-paths
(for/list ([src (in-list (append srcs
(if from-config?
(pkg-config-catalogs)
null)))])
(define src-path (src->url-or-path src))
(when (path? src-path)
(cond
[(db-path? src-path)
(void)]
[(directory-exists? src-path)
(void)]
[(let-values ([(base name dir?) (split-path src-path)]) dir?)
(void)]
[else
(pkg-error (~a "bad source catalog path\n"
" path: ~a\n"
" expected: directory or path with \".sqlite\" extension")
src)]))
src-path))
(define dest-path
(cond
[(path? dest) (path->complete-path dest)]
[(regexp-match? #rx"^file://" dest)
(url->path (string->url dest))]
[(regexp-match? #rx"^[a-zA-Z]*://" dest)
(pkg-error (~a "cannot copy to a non-file destination catalog\n"
" given URL: ~a")
dest)]
[else (path->complete-path dest)]))
(define dest-dir
(and relative-sources?
(if (db-path? dest-path)
(let-values ([(base name dir?) (split-path dest-path)])
base)
dest-path)))
(unless (or force? merge?)
(when (or (file-exists? dest-path)
(directory-exists? dest-path)
(link-exists? dest-path))
(pkg-error (~a "destination exists\n"
" path: ~a")
dest-path)))
(define absolute-details
(let ([src-paths (if (and merge?
(or (file-exists? dest-path)
(directory-exists? dest-path)))
(if override?
(append src-paths
(list dest-path))
(cons dest-path
src-paths))
src-paths)])
(parameterize ([current-pkg-catalogs (for/list ([src-path src-paths])
(if (path? src-path)
(path->url src-path)
src-path))])
(get-all-pkg-details-from-catalogs))))
(define details
(if relative-sources?
(for/hash ([(k ht) (in-hash absolute-details)])
(values k (source->relative-source dest-dir ht)))
absolute-details))
(when (and force? (not merge?))
(cond
[(file-exists? dest-path)
(delete-file dest-path)]
[(directory-exists? dest-path)
(if (db-path? dest-path)
(delete-directory/files dest-path)
(for ([i (directory-list dest-path)])
(delete-directory/files (build-path dest-path i))))]
[(link-exists? dest-path)
(delete-file dest-path)]))
(cond
[(db-path? dest-path)
(define vers-details
(for/hash ([(k v) (in-hash details)])
(values k (select-info-version v))))
(parameterize ([db:current-pkg-catalog-file dest-path])
(db:set-catalogs! '("local"))
(db:set-pkgs! "local"
(for/list ([(k v) (in-hash vers-details)])
(db:pkg k "local"
(hash-ref v 'author "")
(hash-ref v 'source "")
(hash-ref v 'checksum "")
(hash-ref v 'description ""))))
(for ([(k v) (in-hash vers-details)])
(define t (hash-ref v 'tags '()))
(unless (null? t)
(db:set-pkg-tags! k "local" t)))
(for ([(k v) (in-hash vers-details)])
(define mods (hash-ref v 'modules '()))
(unless (null? mods)
(define cs (hash-ref v 'checksum ""))
(db:set-pkg-modules! k "local" cs mods)))
(for ([(k v) (in-hash vers-details)])
(define deps (hash-ref v 'dependencies '()))
(unless (null? deps)
(define cs (hash-ref v 'checksum ""))
(db:set-pkg-dependencies! k "local" cs deps))))]
[else
(define pkg-path (build-path dest-path "pkg"))
(make-directory* pkg-path)
(for ([(k v) (in-hash details)])
(call-with-output-file*
#:exists 'truncate/replace
(build-path pkg-path k)
(lambda (o) (write v o))))
(call-with-output-file*
#:exists 'truncate/replace
(build-path dest-path "pkgs")
(lambda (o) (write (hash-keys details) o)))
(call-with-output-file*
#:exists 'truncate/replace
(build-path dest-path "pkgs-all")
(lambda (o) (write details o)))]))

View File

@ -0,0 +1,82 @@
#lang racket/base
(require racket/format
"../name.rkt"
"catalog.rkt"
"dep.rkt"
"path.rkt"
"print.rkt")
(provide pkg-catalog-show)
(define (pkg-catalog-show names
#:all? [all? #f]
#:only-names? [only-names? #f]
#:modules? [modules? #f])
(for ([name (in-list names)])
(define-values (parsed-name type)
(package-source->name+type name #f))
(unless (eq? type 'name)
(pkg-error (~a "incorrect syntax for a package name\n"
" given: ~a")
name)))
(cond
[only-names?
(define all-names (if all?
(get-all-pkg-names-from-catalogs)
names))
(for ([name (in-list all-names)])
(unless all?
;; Make sure it's available:
(get-pkg-details-from-catalogs name))
(printf "~a\n" name))]
[else
(define all-details (and all?
(get-all-pkg-details-from-catalogs)))
(for ([name (in-list (if all?
(sort (hash-keys all-details) string<?)
names))]
[position (in-naturals)])
(define details (select-info-version
(if all?
(hash-ref all-details name)
(get-pkg-details-from-catalogs name))))
(unless (zero? position) (newline))
(printf "Package name: ~a\n" name)
(for ([key '(author source checksum tags description)])
(define v (hash-ref details key #f))
(when v
(printf " ~a: ~a\n"
(string-titlecase (symbol->string key))
(if (list? v)
(apply ~a #:separator ", " v)
v))))
(for ([key '(dependencies)])
(define v (hash-ref details key null))
(unless (null? v)
(printf " Dependencies:\n")
(for ([dep (in-list v)])
(define vers (dependency->version dep))
(define plat (dependency-lookup '#:platform dep))
(printf " ~a~a~a\n"
(dependency->name dep)
(if vers
(format " version ~a" vers)
"")
(if plat
(format " on platform ~v" plat)
"")))))
(when modules?
(printf " Modules:")
(for/fold ([col 72]) ([mod (in-list (hash-ref details 'modules null))])
(define pretty-mod (pretty-module-path mod))
(define mod-str (~a " " (~s pretty-mod)))
(define new-col (if ((+ col (string-length mod-str)) . > . 72)
(begin
(printf "\n ")
0)
col))
(display mod-str)
(+ new-col (string-length mod-str)))
(newline)))]))

View File

@ -0,0 +1,81 @@
#lang racket/base
(require racket/format
racket/list
net/url
(prefix-in db: "../db.rkt")
"params.rkt"
"catalog.rkt"
"content.rkt"
"config.rkt"
"print.rkt"
"desc.rkt")
(provide pkg-catalog-update-local)
(define (pkg-catalog-update-local #:catalogs [catalogs (pkg-config-catalogs)]
#:set-catalogs? [set-catalogs? #t]
#:catalog-file [catalog-file (db:current-pkg-catalog-file)]
#:quiet? [quiet? #f]
#:consult-packages? [consult-packages? #f]
#:skip-download-failures? [skip-download-failures? #f])
(parameterize ([db:current-pkg-catalog-file catalog-file])
(define current-catalogs (db:get-catalogs))
(cond
[set-catalogs?
(unless (equal? catalogs current-catalogs)
(db:set-catalogs! catalogs))]
[else
(unless (for/and ([catalog (in-list catalogs)])
(member catalog current-catalogs))
(error 'pkg-catalog-update-local
(~a "given catalog list is not a superset of recorded catalogs\n"
" given: ~s\n"
" recorded: ~s")
catalogs
current-catalogs))])
(for ([catalog (in-list catalogs)])
(unless quiet?
(printf/flush "Updating from ~a\n" catalog))
(parameterize ([current-pkg-catalogs (list (string->url catalog))])
(define details (for/hash ([(name ht) (get-all-pkg-details-from-catalogs)])
(values name (select-info-version ht))))
;; set packages:
(db:set-pkgs! catalog (for/list ([(name ht) (in-hash details)])
(db:pkg name
catalog
(hash-ref ht 'author "")
(hash-ref ht 'source "")
(hash-ref ht 'checksum "")
(hash-ref ht 'description ""))))
;; Add available module and dependency info:
(for/list ([(name ht) (in-hash details)])
(define checksum (hash-ref ht 'checksum ""))
(define mods (hash-ref ht 'modules #f))
(when mods
(db:set-pkg-modules! name catalog checksum mods))
(define tags (hash-ref ht 'tags #f))
(when tags
(db:set-pkg-tags! name catalog tags))
(define deps (hash-ref ht 'dependencies #f))
(when deps
(db:set-pkg-dependencies! name catalog checksum deps)))
(when consult-packages?
;; If module information isn't available for a package, download
;; the package to fill in that information:
(define need-modules (db:get-pkgs-without-modules #:catalog catalog))
(for ([(pkg) (in-list need-modules)])
(define name (db:pkg-name pkg))
(define ht (hash-ref details name))
(define source (hash-ref ht 'source))
(unless quiet?
(printf/flush "Downloading ~s\n" source))
(define-values (checksum modules deps)
(get-pkg-content (pkg-desc source
#f
name
(hash-ref ht 'checksum #f)
#f)))
(db:set-pkg-modules! name catalog checksum modules)
(db:set-pkg-dependencies! name catalog checksum deps)))))))

View File

@ -0,0 +1,306 @@
#lang racket/base
(require net/url
racket/path
racket/format
racket/port
(prefix-in db: "../db.rkt")
"../name.rkt"
"params.rkt"
"config.rkt"
"print.rkt")
(provide select-info-version
source->relative-source
package-catalog-lookup
get-all-pkg-names-from-catalogs
get-pkg-details-from-catalogs
get-all-pkg-details-from-catalogs
db-path?)
(define (db-path? p)
(regexp-match? #rx"[.]sqlite$" (path->bytes p)))
(define (catalog-dispatch i server db dir)
(cond
[(equal? "file" (url-scheme i))
(define path (url->path i))
(cond
[(db-path? path)
(parameterize ([db:current-pkg-catalog-file path])
(db))]
[(directory-exists? path) (dir path)]
[else #f])]
[else (server i)]))
;; Add current package version to a URL:
(define (add-version-query addr/no-query)
(struct-copy url addr/no-query
[query (append
(url-query addr/no-query)
(list
(cons 'version (current-pkg-lookup-version))))]))
;; Take a package-info hash table and lift any version-specific
;; information in 'versions.
(define (select-info-version ht)
(and ht
(let ([v (hash-ref ht 'versions #f)])
(cond
[(hash? v)
(or (for/or ([vers (in-list (list (current-pkg-lookup-version)
'default))])
(define ht2 (hash-ref v vers #f))
(and ht2
;; Override fields of `ht' with values from `ht2':
(for/fold ([ht ht]) ([(k v) (in-hash ht2)])
(hash-set ht k v))))
;; Keep ht as-is:
ht)]
[else ht]))))
;; If the 'source field in `ht` is a relative path, treat
;; it as relative to `i` and make it absolute:
(define (source->absolute-source i ht)
(cond
[ht
(define s (hash-ref ht 'source #f))
(define new-ht
(cond
[s
;; If `s' is a relative URL, then we rely on the pun
;; that it will parse as a relative path.
(define-values (name type) (package-source->name+type s #f))
(cond
[(and (or (eq? type 'dir) (eq? type 'file))
(not (regexp-match? #rx"^file://" s))
(relative-path? s))
(define i-for-combine
(cond
[(equal? "file" (url-scheme i))
(define i-path (url->path i))
(if (db-path? i-path)
i
;; Make sure we interpret `i' as a directory when
;; adding a relative path:
(path->url (path->directory-path (url->path i))))]
[else i]))
(define full-url
(url->string
(combine-url/relative i-for-combine s)))
(hash-set ht 'source full-url)]
[else ht])]
[else ht]))
(let ([v (hash-ref new-ht 'versions #f)])
(if v
;; Adjust version-specific sources:
(hash-set new-ht 'versions
(for/hash ([(k ht) (in-hash v)])
(values k (source->absolute-source i ht))))
;; No further adjustments:
new-ht))]
[else #f]))
;; Make sources in `ht` relative to `dir`, when possible:
(define (source->relative-source dir ht)
(define s (hash-ref ht 'source #f))
(define new-ht
(cond
[s
(define-values (name type) (package-source->name+type s #f))
(cond
[(or (eq? type 'dir) (eq? type 'file))
(hash-set ht
'source
(relative-path->relative-url-string
(find-relative-path
dir
(package-source->path s type))))]
[else ht])]
[else ht]))
(let ([v (hash-ref new-ht 'versions #f)])
(if v
;; Adjust version-specific sources:
(hash-set new-ht 'versions
(for/hash ([(k ht) (in-hash new-ht)])
(values k (source->relative-source dir ht))))
;; No further adjustments:
new-ht)))
(define (package-catalog-lookup pkg details? download-printf)
(or
(for/or ([i (in-list (pkg-catalogs))])
(if download-printf
(download-printf "Resolving ~s via ~a\n" pkg (url->string i))
(log-pkg-debug "consulting catalog ~a" (url->string i)))
(source->absolute-source
i
(select-info-version
(catalog-dispatch
i
;; Server:
(lambda (i)
(define addr (add-version-query
(combine-url/relative i (format "pkg/~a" pkg))))
(log-pkg-debug "resolving via ~a" (url->string addr))
(read-from-server
'package-catalog-lookup
addr
(lambda (v) (and (hash? v)
(for/and ([k (in-hash-keys v)])
(symbol? k))))
(lambda (s) #f)))
;; Local database:
(lambda ()
(define pkgs (db:get-pkgs #:name pkg))
(and (pair? pkgs)
(db-pkg-info (car pkgs) details?)))
;; Local directory:
(lambda (path)
(define pkg-path (build-path path "pkg" pkg))
(and (file-exists? pkg-path)
(call-with-input-file* pkg-path read)))))))
(pkg-error (~a "cannot find package on catalogs\n"
" package: ~a")
pkg)))
(define (read-from-server who url pred
[failure
(lambda (s)
(error who
(~a "bad response from server\n"
" url: ~a\n"
" response: ~v")
(url->string url)
s))])
(define bytes (call-with-url url port->bytes))
((if bytes
(with-handlers ([exn:fail:read? (lambda (exn)
(lambda () (failure bytes)))])
(define v (read (open-input-bytes bytes)))
(lambda ()
(if (pred v)
v
(failure bytes))))
(lambda () (failure #f)))))
;; uses a custodian to avoid leaks:
(define (call-with-url url handler)
(define c (make-custodian))
(dynamic-wind
void
(lambda ()
(define-values (p hs)
(parameterize ([current-custodian c])
(get-pure-port/headers url #:redirections 25 #:status? #t)))
(begin0
(and (string=? "200" (substring hs 9 12))
(handler p))
(close-input-port p)))
(lambda ()
(custodian-shutdown-all c))))
(define (db-pkg-info pkg details?)
(if details?
(let ([tags (db:get-pkg-tags (db:pkg-name pkg)
(db:pkg-catalog pkg))]
[mods (db:get-pkg-modules (db:pkg-name pkg)
(db:pkg-catalog pkg)
(db:pkg-checksum pkg))]
[deps (db:get-pkg-dependencies (db:pkg-name pkg)
(db:pkg-catalog pkg)
(db:pkg-checksum pkg))])
(hash 'name (db:pkg-name pkg)
'author (db:pkg-author pkg)
'source (db:pkg-source pkg)
'checksum (db:pkg-checksum pkg)
'description (db:pkg-desc pkg)
'tags tags
'modules mods
'dependencies deps))
(hash 'source (db:pkg-source pkg)
'checksum (db:pkg-checksum pkg))))
(define (get-all-pkg-names-from-catalogs)
(define ht
(for*/hash ([i (in-list (pkg-catalogs))]
[name
(catalog-dispatch
i
;; Server:
(lambda (i)
(read-from-server
'get-all-pkg-names-from-catalogs
(add-version-query
(combine-url/relative i "pkgs"))
(lambda (l) (and (list? l)
(andmap string? l)))))
;; Local database:
(lambda ()
(map db:pkg-name (db:get-pkgs)))
;; Local directory:
(lambda (path)
(define pkgs-path (build-path path "pkgs"))
(cond
[(file-exists? pkgs-path)
(call-with-input-file* pkgs-path read)]
[else
(define pkg-path (build-path path "pkg"))
(for/list ([i (directory-list pkg-path)]
#:when (file-exists? (build-path pkg-path i)))
(path-element->string i))])))])
(values name #t)))
(sort (hash-keys ht) string<?))
(define (get-pkg-details-from-catalogs name)
(for/or ([i (in-list (pkg-catalogs))])
(package-catalog-lookup name #t #f)))
(define (get-all-pkg-details-from-catalogs)
(for/fold ([ht (hash)]) ([i (in-list (pkg-catalogs))])
(define one-ht
(catalog-dispatch
i
;; Server:
(lambda (i)
(read-from-server
'get-all-pkg-details-from-catalogs
(add-version-query
(combine-url/relative i "pkgs-all"))
(lambda (v)
(and (hash? v)
(for/and ([(k v) (in-hash v)])
(and (string? k)
(hash? v)
(for/and ([k (in-hash-keys v)])
(symbol? k))))))))
;; Local database:
(lambda ()
(define pkgs (db:get-pkgs))
(for/fold ([ht (hash)]) ([p (in-list pkgs)])
(if (hash-ref ht (db:pkg-name p) #f)
ht
(hash-set ht
(db:pkg-name p)
(db-pkg-info p #t)))))
;; Local directory:
(lambda (path)
(define pkgs-all-path (build-path path "pkgs-all"))
(cond
[(file-exists? pkgs-all-path)
(call-with-input-file* pkgs-all-path read)]
[else
(define pkg-path (build-path path "pkg"))
(for/hash ([i (directory-list pkg-path)]
#:when (file-exists? (build-path pkg-path i)))
(values (path-element->string i)
(call-with-input-file* (build-path pkg-path i)
read)))]))))
(unless one-ht
(pkg-error (~a "could not read package catalog\n"
" catalog: ~a")
(url->string i)))
(for/fold ([ht ht]) ([(k v) (in-hash one-ht)])
(if (hash-ref ht k #f)
ht
(hash-set ht k (source->absolute-source i v))))))

View File

@ -0,0 +1,71 @@
#lang racket/base
(require setup/pack
racket/set
"dep.rkt"
"pkg-db.rkt"
"get-info.rkt"
"metadata.rkt"
"print.rkt")
;; Package collection and dependency information needed for
;; installation and removal of packages.
(provide package-collections
package-collection-directories
package-dependencies
get-setup-collects)
(define (package-collections pkg-dir metadata-ns)
(for/list ([d (directory-list pkg-dir)]
#:when (directory-exists? (build-path pkg-dir d))
#:when (std-filter d))
d))
(define (package-collection-directories pkg-dir metadata-ns)
(for/list ([c (in-list (package-collections pkg-dir metadata-ns))])
(build-path pkg-dir c)))
(define ((package-dependencies metadata-ns db all-platforms?
#:only-implies? [only-implies? #f])
pkg-name)
(define pkg-dir (pkg-directory* pkg-name #:db db))
(define deps
(map dependency->name
(let ([l (get-all-deps metadata-ns pkg-dir)])
(if all-platforms?
l
(filter dependency-this-platform? l)))))
(if only-implies?
(let ([implies (list->set (get-all-implies metadata-ns pkg-dir deps))])
(filter (lambda (dep)
(set-member? implies dep))
deps))
deps))
(define (maybe-append lists)
(and (for/and ([v (in-list lists)]) (not (eq? v 'all)))
(apply append lists)))
(define (get-setup-collects pkg-names db metadata-ns)
(maybe-append
(for/list ([pkg-name (in-list pkg-names)])
(define pkg-dir (pkg-directory* pkg-name #:db db))
(define single-collect
(and pkg-dir
(pkg-single-collection pkg-dir #:name pkg-name #:namespace metadata-ns)))
(or (and (not pkg-dir) null)
(and single-collect (list single-collect))
(get-metadata metadata-ns pkg-dir
'setup-collects (lambda () (package-collections
pkg-dir
metadata-ns))
#:checker (lambda (v)
(unless (or (eq? v 'all)
(and (list? v)
(for ([c (in-list v)])
(or (path-string? c)
(and (list? c)
(pair? c)
(andmap path-string? c))))))
(pkg-error "bad 'setup-collects value\n value: ~e"
v))))))))

View File

@ -0,0 +1,202 @@
#lang racket/base
(require setup/dirs
racket/file
racket/match
racket/format
net/url
"../path.rkt"
"dirs.rkt"
"params.rkt"
"lock.rkt"
"print.rkt")
;; Reading and writing the package-relevant configuration of
;; an installation or for the current user.
(provide (all-defined-out))
(define (get-download-cache-dir)
(or (current-pkg-download-cache-dir)
(read-pkg-cfg/def 'download-cache-dir)))
(define (get-download-cache-max-files)
(or (current-pkg-download-cache-max-files)
(read-pkg-cfg/def 'download-cache-max-files)))
(define (get-download-cache-max-bytes)
(or (current-pkg-download-cache-max-bytes)
(read-pkg-cfg/def 'download-cache-max-bytes)))
(define (read-pkg-cfg/def k)
;; Lock is held for the current scope, but if
;; the key is not found in the current scope,
;; get the next scope's lock and try there,
;; etc.
(define (get-default)
(match k
['catalogs
(list "http://pkgs.racket-lang.org"
"http://planet-compats.racket-lang.org")]
['default-scope "user"]
['installation-name (version)]
['download-cache-dir (build-path (find-system-path 'addon-dir)
"download-cache")]
['download-cache-max-files 1024]
['download-cache-max-bytes (* 64 1024 1024)]
[_ #f]))
(define c (read-pkg-file-hash (pkg-config-file)))
(define v (hash-ref c k 'none))
(cond
[(eq? v 'none)
;; Default from enclosing scope or hard-wired default:
(define s (current-pkg-scope))
(if (eq? s 'installation)
;; Hard-wided:
(get-default)
;; Enclosing:
(parameterize ([current-pkg-scope 'installation])
(read-pkg-cfg/def k)))]
[else
(match k
['catalogs
(if (member #f v)
;; Replace #f with default URLs:
(apply append (for/list ([i (in-list v)])
(if (not i)
(get-default)
(list i))))
v)]
[_ v])]))
(define (update-pkg-cfg! key val)
(define f (pkg-config-file))
(write-file-hash!
f
(hash-set (read-pkg-file-hash f) key val)))
(define (default-pkg-scope)
(match (default-pkg-scope-as-string)
["installation" 'installation]
[else 'user]))
(define (default-pkg-scope-as-string)
(read-pkg-cfg/def 'default-scope))
(define (pkg-config-catalogs)
(with-pkg-lock/read-only
(read-pkg-cfg/def 'catalogs)))
(define (pkg-catalogs)
(or (current-pkg-catalogs)
(map string->url (read-pkg-cfg/def 'catalogs))))
;; ----------------------------------------
(define (pkg-config config:set key+vals
#:from-command-line? [from-command-line? #f])
(cond
[config:set
(match key+vals
[(list)
(pkg-error "no config key given")]
[(list (and key
(or "default-scope"
"name"
"download-cache-max-files"
"download-cache-max-bytes"
"download-cache-dir"
"doc-open-url")))
(pkg-error (~a "missing value for config key\n"
" config key: ~a")
key)]
[(list* (and key
(or "default-scope"
"name"
"download-cache-max-files"
"download-cache-max-bytes"
"download-cache-dir"))
val
another-val
more-vals)
(pkg-error (~a "too many values provided for config key\n"
" config key: ~a\n"
" given values:~a")
key
(format-list (cons val more-vals)))]
[(list* (and key "catalogs") val)
(update-pkg-cfg! 'catalogs val)]
[(list (and key "default-scope") val)
(unless (member val '("installation" "user"))
(pkg-error (~a "invalid value for config key\n"
" config key: ~a\n"
" given value: ~a\n"
" valid values: installation, user")
key
val))
(update-pkg-cfg! 'default-scope val)]
[(list (and key "name") val)
(unless (eq? 'installation (current-pkg-scope))
(pkg-error (~a "setting `name' makes sense only in `installation' scope\n"
" current package scope: ~a")
(current-pkg-scope)))
(update-pkg-cfg! 'installation-name val)]
[(list (and key "download-cache-dir")
val)
(update-pkg-cfg! (string->symbol key) (if (complete-path? val)
val
(path->string
(path->complete-path val))))]
[(list (and key (or "download-cache-max-files"
"download-cache-max-bytes"))
val)
(unless (real? (string->number val))
(pkg-error (~a "invalid value for config key\n"
" config key: ~a\n"
" given value: ~a\n"
" valid values: real numbers")
key
val))
(update-pkg-cfg! (string->symbol key) (string->number val))]
[(list (and key "doc-open-url") val)
(unless (eq? 'installation (current-pkg-scope))
(pkg-error (~a "setting `doc-open-url' works only in `installation' scope\n"
" current package scope: ~a")
(current-pkg-scope)))
(update-pkg-cfg! 'doc-open-url (if (equal? val "") #f val))]
[(list* key args)
(pkg-error "unsupported config key\n key: ~a" key)])]
[else
(define (show key+vals indent)
(match key+vals
[(list key)
(match key
["catalogs"
(for ([s (in-list (read-pkg-cfg/def 'catalogs))])
(printf "~a~a\n" indent s))]
["default-scope"
(printf "~a~a\n" indent (read-pkg-cfg/def 'default-scope))]
["name"
(printf "~a~a\n" indent (read-pkg-cfg/def 'installation-name))]
[(or "download-cache-dir"
"download-cache-max-files"
"download-cache-max-bytes")
(printf "~a~a\n" indent (read-pkg-cfg/def (string->symbol key)))]
["doc-open-url"
(printf "~a~a\n" indent (or (read-pkg-cfg/def 'doc-open-url) ""))]
[_
(pkg-error "unsupported config key\n key: ~e" key)])]
[(list)
(pkg-error "config key not provided")]
[_
(pkg-error (~a "multiple config keys provided"
(if from-command-line?
";\n supply `--set' to set a config key's value"
"")))]))
(match key+vals
[(list)
(for ([key (in-list '("name"
"catalogs"
"default-scope"
"download-cache-dir"
"download-cache-max-files"
"download-cache-max-bytes"))])
(printf "~a:\n" key)
(show (list key) " "))]
[_ (show key+vals "")])]))

View File

@ -0,0 +1,50 @@
#lang racket/base
(require racket/list
racket/set
racket/file
setup/getinfo
"get-info.rkt"
"dep.rkt"
"metadata.rkt"
"stage.rkt")
(provide extract-pkg-dependencies
get-pkg-content)
(define (extract-pkg-dependencies get-info
#:build-deps? [build-deps? #t]
#:filter? [filter? #f]
#:versions? [versions? #f])
(define v (if get-info
(get-info 'deps (lambda () empty))
empty))
((check-dependencies 'deps) v)
(define v2 (if (and get-info build-deps?)
(get-info 'build-deps (lambda () empty))
empty))
((check-dependencies 'build-deps) v2)
(define all-v (append v v2))
(if filter?
(for/list ([dep (in-list all-v)]
#:when (dependency-this-platform? dep))
(define name
(if (pair? dep)
(car dep)
dep))
(if versions?
(list name (dependency->version dep))
name))
all-v))
(define (get-pkg-content desc
#:namespace [metadata-ns (make-metadata-namespace)]
#:extract-info [extract-info extract-pkg-dependencies])
(define-values (pkg-name dir cksum clean? module-paths)
(pkg-stage desc #:in-place? #t #:namespace metadata-ns))
(define get-info (get-info/full dir #:namespace metadata-ns))
(begin0
(values cksum
(set->list module-paths)
(extract-info get-info))
(when clean?
(delete-directory/files dir))))

View File

@ -0,0 +1,196 @@
#lang racket/base
(require racket/match
racket/format
racket/file
setup/pack
file/zip
file/tar
openssl/sha1
"../strip.rkt"
"metadata.rkt"
"print.rkt"
"params.rkt"
"pkg-db.rkt"
"lock.rkt")
(provide pkg-create)
(define (create-as-is create:format pkg-name dir orig-dir
#:quiet? [quiet? #f]
#:from-command-line? [from-command-line? #f]
#:hide-src? [hide-src? #f]
#:dest [dest-dir #f])
(begin
(unless (directory-exists? dir)
(pkg-error "directory does not exist\n path: ~a" dir))
(match create:format
['MANIFEST
(unless quiet?
(printf/flush "creating manifest for ~a\n"
orig-dir))
(with-output-to-file (build-path (or dest-dir dir) "MANIFEST")
#:exists 'replace
(λ ()
(for ([f (in-list (parameterize ([current-directory dir])
(find-files file-exists?)))])
(display f)
(newline))))]
[else
(define pkg (format "~a.~a" pkg-name create:format))
(define actual-dest-dir (if dest-dir
(path->complete-path dest-dir)
(let-values ([(base name dir?) (split-path dir)])
(cond
[(path? base) (path->complete-path base)]
[else (current-directory)]))))
(define pkg/complete (path->complete-path pkg actual-dest-dir))
;; To make checksums more consistent, set a directory's timestamp to
;; the latest time of any of its source files.
(define (use-real-timestamp? p)
(and (file-exists? p)
(regexp-match? #rx"[.](?:rkt|ss|scrbl|txt)$" p)))
(define latest-timestamp
(for/fold ([ts #f]) ([f (in-directory dir)])
(define fts (and (use-real-timestamp? f)
(file-or-directory-modify-seconds f)))
(if (and fts (or (not ts) (fts . > . ts)))
fts
ts)))
(define (file-or-directory-timestamp p)
(or (and (not (use-real-timestamp? p))
latest-timestamp)
(file-or-directory-modify-seconds p)))
(unless quiet?
(printf/flush "packing~a into ~a\n"
(if hide-src? "" (format " ~a" dir))
(if dest-dir
pkg/complete
pkg)))
(define (add-directory-layer? content)
;; We need to add a layer for zip/tgz if the package content
;; is a single directory, which is an unlikely case.
;; That mode is not compatble with Racket v60.0.1.12 and earlier.
;; When only Racket v6.0.1.12 is later is relevant,
;; we might prefer to always add a layer for consistency and
;; because it's nicer for manual unpacking.
(and (= 1 (length content))
(directory-exists? (car content))))
(match create:format
['tgz
(when (file-exists? pkg/complete)
(delete-file pkg/complete))
(parameterize ([current-directory dir])
(with-handlers ([exn? (lambda (exn)
(when (file-exists? pkg/complete)
(delete-file pkg/complete))
(raise exn))])
(define content (directory-list))
(apply tar-gzip pkg/complete content
#:path-prefix (and (add-directory-layer? content)
pkg-name)
#:get-timestamp file-or-directory-timestamp)))]
['zip
(when (file-exists? pkg/complete)
(delete-file pkg/complete))
(parameterize ([current-directory dir])
(with-handlers ([exn? (lambda (exn)
(when (file-exists? pkg/complete)
(delete-file pkg/complete))
(raise exn))])
(define content (directory-list))
(apply zip pkg/complete content
#:path-prefix (and (add-directory-layer? content)
pkg-name)
#:get-timestamp file-or-directory-timestamp
#:utc-timestamps? #t
#:round-timestamps-down? #t)))]
['plt
(define dest pkg/complete)
(when (pkg-single-collection #:name pkg-name dir)
(pkg-error (~a "single-collection package not supported in .plt format\n"
" directory: ~a")
dir))
(parameterize ([current-directory dir])
(define names (filter std-filter (directory-list)))
(define dirs (filter directory-exists? names))
(pack-plt dest pkg-name
names
#:plt-relative? #t
#:as-paths (map (lambda (v) (build-path "collects" v)) names)
#:collections (map list (map path->string dirs))))]
[x
(pkg-error "invalid package format\n format: ~a" x)])
(define chk (format "~a.CHECKSUM" pkg))
(define chk/complete (path->complete-path chk actual-dest-dir))
(unless quiet?
(printf/flush "writing package checksum to ~a\n"
(if dest-dir
chk/complete
chk)))
(with-output-to-file chk/complete
#:exists 'replace
(λ () (display (call-with-input-file pkg/complete sha1))))])))
(define (stripped-create mode name dir
#:format [create:format 'zip]
#:quiet? [quiet? #f]
#:from-command-line? [from-command-line? #f]
#:dest [archive-dest-dir #f])
(define tmp-dir (make-temporary-file "create-binary-~a" 'directory))
(dynamic-wind
void
(lambda ()
(define dest-dir (build-path tmp-dir name))
(make-directory dest-dir)
(generate-stripped-directory mode dir dest-dir)
(create-as-is create:format name dest-dir dir
#:hide-src? #t
#:quiet? quiet?
#:from-command-line? from-command-line?
#:dest (if archive-dest-dir
(path->complete-path archive-dest-dir)
(current-directory))))
(lambda ()
(delete-directory/files tmp-dir))))
(define (pkg-create create:format dir-or-name
#:pkg-name [given-pkg-name #f]
#:dest [dest-dir #f]
#:source [source 'dir]
#:mode [mode 'as-is]
#:quiet? [quiet? #f]
#:from-command-line? [from-command-line? #f])
(define pkg-name
(or given-pkg-name
(if (eq? source 'dir)
(path->string (let-values ([(base name dir?) (split-path dir-or-name)])
name))
dir-or-name)))
(define dir
(if (eq? source 'dir)
dir-or-name
(let ()
(define (get-dir scope)
(parameterize ([current-pkg-scope scope])
(with-pkg-lock/read-only
(pkg-directory* dir-or-name))))
(define dir (get-dir 'user))
(unless dir
(pkg-error (~a "package not installed in user scope\n"
" package name: ~a"
(if (get-dir 'installation)
"\n installed in scope: installation"
""))
dir-or-name))
dir)))
(case mode
[(as-is)
(create-as-is create:format pkg-name dir dir
#:dest dest-dir
#:quiet? quiet?
#:from-command-line? from-command-line?)]
[else (stripped-create mode pkg-name dir
#:dest dest-dir
#:format create:format
#:quiet? quiet?
#:from-command-line? from-command-line?)]))

View File

@ -0,0 +1,36 @@
#lang racket/base
(require setup/matching-platform
"../name.rkt")
(provide (all-defined-out))
(define (dependency->name dep)
(package-source->name
(dependency->source dep)))
(define (dependency->source dep)
(if (string? dep)
dep
(car dep)))
(define (dependency->version dep)
(cond
[(string? dep) #f]
[(null? (cdr dep)) #f]
[(keyword? (cadr dep))
(dependency-lookup '#:version dep)]
[else (cadr dep)]))
(define (dependency-lookup kw dep)
(cond
[(string? dep) #f]
[(null? (cdr 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))
(or (not p) (matching-platform? p)))

View File

@ -0,0 +1,15 @@
#lang racket/base
(provide (struct-out pkg-desc)
pkg-desc=?)
(struct pkg-desc (source type name checksum auto?))
(define (pkg-desc=? a b)
(define (->list a)
(list (pkg-desc-source a)
(pkg-desc-type a)
(pkg-desc-name a)
(pkg-desc-checksum a)
(pkg-desc-auto? a)))
(equal? (->list a) (->list b)))

View File

@ -0,0 +1,38 @@
#lang racket/base
(require setup/dirs
racket/file
racket/path
"../path.rkt"
"params.rkt")
;; Finding configurable files and directories
(provide (all-defined-out))
(define (pkg-dir config?)
(define scope (current-pkg-scope))
(if (and config?
(eq? scope 'installation))
(find-config-dir)
(get-pkgs-dir scope (current-pkg-scope-version))))
(define (pkg-config-file)
(build-path (pkg-dir #t) "config.rktd"))
(define (pkg-db-file)
(build-path (pkg-dir #f) "pkgs.rktd"))
(define (pkg-installed-dir)
(pkg-dir #f))
(define (pkg-lock-file)
(make-lock-file-name (pkg-db-file)))
(define (get-all-pkg-scopes)
(append (let ([main (find-pkgs-dir)])
(reverse
(for/list ([d (get-pkgs-search-dirs)])
(if (equal? d main)
'installation
(simple-form-path d)))))
'(user)))

View File

@ -0,0 +1,87 @@
#lang racket/base
(require file/cache
net/url
racket/match
racket/port
racket/format
"path.rkt"
"print.rkt"
"config.rkt")
(provide call/input-url+200
download-file!
url-path/no-slash
clean-cache)
(define (call/input-url+200 u fun
#:headers [headers '()]
#:failure [fail-k (lambda (s) #f)])
#;(printf "\t\tReading ~a\n" (url->string u))
(define-values (ip hs) (get-pure-port/headers u headers
#:redirections 25
#:status? #t))
(if (string=? "200" (substring hs 9 12))
(begin0
(fun ip)
(close-input-port ip))
(fail-k hs)))
(define (url-path/no-slash url)
(define p (url-path url))
(define rp (reverse p))
(reverse
(match rp
[(list* (path/param "" _) rest)
rest]
[_ rp])))
(define (download-file! url file checksum
#:download-printf [download-printf #f]
#:use-cache? [use-cache? #t]
#:fail-okay? [fail-okay? #f])
(with-handlers ([exn:fail?
(λ (x)
(unless fail-okay?
(raise x)))])
(make-parent-directory* file)
(log-pkg-debug "\t\tDownloading ~a to ~a" (url->string url) file)
(define (download!)
(when download-printf
(download-printf "Downloading ~a\n" (url->string url)))
(call-with-output-file file
(λ (op)
(call/input-url+200
url
(λ (ip) (copy-port ip op))
#:failure
(lambda (reply-s)
(pkg-error (~a "error downloading package\n"
" URL: ~a\n"
" server response: ~a")
(url->string url)
(read-line (open-input-string reply-s))))))))
(cond
[(and checksum use-cache?)
(cache-file file
(list (url->string url) checksum)
(get-download-cache-dir)
download!
#:log-error-string (lambda (s) (log-pkg-error s))
#:log-debug-string (lambda (s) (log-pkg-debug s))
#:notify-cache-use (lambda (s)
(when download-printf
(download-printf "Using ~a for ~a\n"
s
(url->string url))))
#:max-cache-files (get-download-cache-max-files)
#:max-cache-size (get-download-cache-max-bytes))]
[else (download!)])))
(define (clean-cache pkg-url checksum)
(when pkg-url
;; Something failed after download, so remove cached file (if any):
(with-handlers ([exn:fail? void]) ; any error is logged already
(cache-remove (list (url->string pkg-url) checksum)
(get-download-cache-dir)
#:log-error-string (lambda (s) (log-pkg-error s))
#:log-debug-string (lambda (s) (log-pkg-debug s))))))

View File

@ -0,0 +1,32 @@
#lang racket/base
(require setup/getinfo
"print.rkt")
;; Working with "info.rkt" files, typically (but not necessarily)
;; package-level "info.rkt" files.
(provide make-metadata-namespace
get-pkg-info
get-metadata)
(define (make-metadata-namespace)
(make-base-empty-namespace))
(define (get-pkg-info pkg-dir metadata-ns)
(with-handlers ([exn:fail? (λ (x)
(log-exn x "getting info")
#f)])
(get-info/full pkg-dir
#:namespace metadata-ns
#:bootstrap? #t)))
(define (get-metadata metadata-ns pkg-dir key get-default
#:checker [checker void])
(define get-info (get-pkg-info pkg-dir metadata-ns))
(define v
(if get-info
(get-info key get-default)
(get-default)))
(checker v)
v)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,71 @@
#lang racket/base
(require racket/file
racket/format
"params.rkt"
"print.rkt"
"dirs.rkt"
"path.rkt")
(provide pkg-lock-held
with-pkg-lock
with-pkg-lock/read-only
;; Checks that the lock is held:
write-file-hash!)
(define pkg-lock-held (make-parameter #f))
(define pkg-lock-scope (make-parameter #f))
;; Call `t' with lock held for the current scope. The intent is that
;; `t' reads and writes package information in the curent scope. It
;; may also *read* package information for wider package scopes
;; without a further lock --- which is questionable, but modification
;; of a shared scope while others are running can create trouble,
;; anyway.
(define (with-pkg-lock* read-only? t)
(define mode (if read-only? 'shared 'exclusive))
(define held-mode (pkg-lock-held))
(define now-scope (current-pkg-scope))
(define held-scope (pkg-lock-scope))
(when (and held-scope
(not (eq? held-scope now-scope)))
(pkg-error "lock mismatch\n held scope: ~a\n requested scope: ~a"
held-scope
now-scope))
(if (or (eq? mode held-mode)
(eq? 'exclusive held-mode))
(t)
(let ([d (pkg-dir #f)])
(unless read-only? (make-directory* d))
(if (directory-exists? d)
;; If the directory exists, assume that a lock file is
;; available or creatable:
(call-with-file-lock/timeout
#f
mode
(lambda ()
(parameterize ([pkg-lock-held mode]
[pkg-lock-scope now-scope]
[current-no-pkg-db #f])
(t)))
(λ () (pkg-error (~a "could not acquire package lock\n"
" lock file: ~a")
(pkg-lock-file)))
#:lock-file (pkg-lock-file))
;; Directory does not exist; we must be in read-only mode.
;; Run `t' under the claim that no database is available
;; (in case the database is created concurrently):
(parameterize ([current-no-pkg-db now-scope])
(parameterize ([pkg-lock-held mode])
(t)))))))
(define-syntax-rule (with-pkg-lock e ...)
(with-pkg-lock* #f (λ () e ...)))
(define-syntax-rule (with-pkg-lock/read-only e ...)
(with-pkg-lock* #t (λ () e ...)))
(define (write-file-hash! file new-db)
(unless (eq? (pkg-lock-held) 'exclusive)
(pkg-error "attempt to write package database without write lock"))
(make-parent-directory* file)
(call-with-atomic-output-file
file
(λ (o tmp-path) (write new-db o) (newline o))))

View File

@ -0,0 +1,120 @@
#lang racket/base
(require version/utils
racket/format
racket/list
racket/set
setup/collection-name
"../name.rkt"
"get-info.rkt"
"print.rkt"
"dep.rkt")
;; Extracting information from a package's "info.rkt" file.
(provide (all-defined-out))
(define ((check-dependencies which) deps)
(unless (and (list? deps)
(for/and ([dep (in-list deps)])
(define (package-source? dep)
(and (string? dep)
(package-source->name dep)))
(define (version? s)
(and (string? s)
(valid-version? s)))
(or (package-source? dep)
(and (list? dep)
(= 2 (length dep))
(package-source? (car 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 `" which "' specification\n"
" specification: ~e")
deps)))
(define (get-all-deps* metadata-ns pkg-dir)
(values
(get-metadata metadata-ns pkg-dir
'deps (lambda () empty)
#:checker (check-dependencies 'deps))
(get-metadata metadata-ns pkg-dir
'build-deps (lambda () empty)
#:checker (check-dependencies 'build-deps))))
(define (get-all-deps metadata-ns pkg-dir)
(define-values (deps build-deps) (get-all-deps* metadata-ns pkg-dir))
(append deps build-deps))
(define (get-all-deps-subset key metadata-ns pkg-dir deps)
(get-metadata metadata-ns pkg-dir
key (lambda () empty)
#:checker (lambda (l)
(unless (null? l)
(define deps-set (list->set
(map dependency->name deps)))
(unless (and (list? l)
(andmap (lambda (v)
(or (string? v)
(eq? v 'core)))
l))
(pkg-error (~a "invalid `~a' specification\n"
" specification: ~e")
key
l))
(unless (andmap (lambda (i)
(or (eq? i 'core)
(set-member? deps-set i)))
l)
(pkg-error (~a "`~a' is not a subset of dependencies\n"
" specification: ~e")
key
l))))))
(define (get-all-implies metadata-ns pkg-dir deps)
(get-all-deps-subset 'implies metadata-ns pkg-dir deps))
(define (get-all-update-implies metadata-ns pkg-dir deps)
(get-all-deps-subset 'update-implies metadata-ns pkg-dir deps))
(define (pkg-single-collection dir
#:name [pkg-name (let-values ([(base name dir?) (split-path dir)])
(path-element->string name))]
#:namespace [metadata-ns (make-metadata-namespace)])
(define i (get-pkg-info dir metadata-ns))
(if (not i)
pkg-name
(let ([s (i 'collection (lambda () 'use-pkg-name))])
(unless (or (collection-name-element? s)
(eq? s 'multi)
(eq? s 'use-pkg-name))
(log-error (format (~a "bad `collection' definition in \"info.rkt\";\n"
" definition will be ignored\n"
" path: ~a\n"
" found: ~e\n"
" expected: (or/c collection-name-element? 'multi 'use-pkg-name)")
(build-path dir "info.rkt")
s)))
(or (and (collection-name-element? s)
s)
(and (eq? s 'use-pkg-name)
pkg-name)))))

View File

@ -0,0 +1,71 @@
#lang racket/base
(require racket/match
"../path.rkt"
"config.rkt"
"lock.rkt"
"pkg-db.rkt"
"desc.rkt"
"params.rkt"
"install.rkt")
(provide pkg-migrate)
(define (pkg-migrate from-version
#:all-platforms? [all-platforms? #f]
#:force? [force? #f]
#:quiet? [quiet? #f]
#:from-command-line? [from-command-line? #f]
#:ignore-checksums? [ignore-checksums? #f]
#:strict-doc-conflicts? [strict-doc-conflicts? #f]
#:use-cache? [use-cache? #t]
#:dep-behavior [dep-behavior #f]
#:strip [strip-mode #f]
#:force-strip? [force-strip? #f])
(define from-db
(parameterize ([current-pkg-scope-version from-version])
(installed-pkg-table #:scope 'user)))
(define to-install
(sort
(for/list ([(name info) (in-hash from-db)]
#:unless (pkg-info-auto? info))
(define-values (source type)
(match (pkg-info-orig-pkg info)
[(list 'catalog name) (values name 'name)]
[(list 'url url) (values url #f)]
[(list 'link path) (values path 'link)]
[(list 'static-link path) (values path 'static-link)]))
(pkg-desc source type name #f #f))
string<?
#:key pkg-desc-name))
(unless quiet?
(cond
[(null? to-install)
(printf "No packages from ~s to install\n" from-version)]
[else
(printf "Packages to install:\n")
(for ([d (in-list to-install)])
(define t (pkg-desc-type d))
(define n (pkg-desc-name d))
(case t
[(name) (printf " ~a\n" n)]
[(link static-link)
(printf " ~a ~aed from ~a\n" n t (pkg-desc-source d))]
[else
(printf " ~a from ~a\n" n (pkg-desc-source d))]))]))
(if (null? to-install)
'skip
(begin0
(pkg-install to-install
#:all-platforms? all-platforms?
#:force? force?
#:ignore-checksums? ignore-checksums?
#:strict-doc-conflicts? strict-doc-conflicts?
#:use-cache? use-cache?
#:skip-installed? #t
#:dep-behavior (or dep-behavior 'search-auto)
#:quiet? quiet?
#:from-command-line? from-command-line?
#:strip strip-mode
#:force-strip? force-strip?)
(unless quiet?
(printf "Packages migrated\n")))))

View File

@ -0,0 +1,84 @@
#lang racket/base
(require racket/set
racket/format
syntax/modcollapse
"metadata.rkt"
"get-info.rkt")
(provide pkg-directory->module-paths
directory->module-paths)
(define (pkg-directory->module-paths dir pkg-name
#:namespace [metadata-ns (make-metadata-namespace)])
(set->list (directory->module-paths dir pkg-name metadata-ns)))
(define (directory->module-paths dir pkg-name metadata-ns)
(define dummy (build-path dir "dummy.rkt"))
(define compiled (string->path-element "compiled"))
(define single-collect (pkg-single-collection dir #:name pkg-name #:namespace metadata-ns))
(define (try-path s f)
(define mp
`(lib ,(apply ~a
#:separator "/"
(let ([l (map path-element->string
(explode-path f))])
(if single-collect
(if (eq? 'relative (car l))
(cons single-collect (cdr l))
(cons single-collect l))
l)))))
(if (module-path? mp)
(set-add s (collapse-module-path mp dummy))
s))
(parameterize ([current-directory dir])
(let loop ([s (set)] [f 'init] [check-zo? #f])
(cond
[(eq? f 'init)
(for/fold ([s s]) ([f (directory-list)])
(loop s f check-zo?))]
[(directory-exists? f)
;; Count ".zo" files toward the set of module paths only
;; if an "info.rkt" in an enclosing directory says to
;; assume virtual sources. Otherwise, the ".zo" file will
;; be discarded by `raco setup'.
(define sub-check-zo?
(or check-zo?
(let ([i (get-pkg-info f metadata-ns)])
(and i
(i 'assume-virtual-sources (lambda () #f))))))
(for/fold ([s s]) ([f (directory-list f #:build? #t)])
(loop s f sub-check-zo?))]
[(not (file-exists? f)) s]
[else
(define-values (base name dir?) (split-path f))
(cond
[(and (eq? 'relative base) (not single-collect)) s]
[else
(define bstr (path-element->bytes name))
(cond
[(or (equal? #"info.rkt" bstr)
(equal? #"info.ss" bstr))
;; don't count "info.rkt" as a conflict, because
;; splices may need their own "info.rkt"s, and
;; `raco setup' can handle that
s]
[(regexp-match? #rx#"[.](?:rkt|ss|scrbl)$" bstr)
(try-path s f)]
[(and check-zo?
(regexp-match? #rx#"_(?:rkt|ss|scrbl)[.]zo$" (path-element->bytes name)))
(define-values (dir-base dir-name dir?) (split-path base))
(cond
[(eq? 'relative dir-base) s]
[(equal? dir-name compiled)
(define bstr2 (regexp-replace
#rx#"_(?:rkt|ss|scrbl)[.]zo$"
(path-element->bytes name)
#".rkt"))
(if (equal? #"info.rkt" bstr2)
s
(try-path s (build-path dir-base
(bytes->path-element
bstr2))))]
[else s])]
[else s])])]))))

View File

@ -0,0 +1,29 @@
#lang racket/base
(require racket/path
setup/dirs)
(provide (all-defined-out))
(define current-pkg-scope
(make-parameter 'user (lambda (p)
(if (path? p)
(simple-form-path p)
p))))
(define current-pkg-scope-version
(make-parameter (get-installation-name)))
(define current-pkg-lookup-version
(make-parameter (version)))
(define current-pkg-error
(make-parameter (lambda args (apply error 'pkg args))))
(define current-no-pkg-db
(make-parameter #f))
(define current-pkg-catalogs
(make-parameter #f))
(define current-pkg-download-cache-dir
(make-parameter #f))
(define current-pkg-download-cache-max-files
(make-parameter #f))
(define current-pkg-download-cache-max-bytes
(make-parameter #f))

View File

@ -0,0 +1,85 @@
#lang racket/base
(require racket/path
racket/file
racket/list
racket/function)
(provide (all-defined-out))
(define (make-parent-directory* p)
(define parent (path-only p))
(make-directory* parent))
(define (path->bytes* pkg)
(cond
[(path? pkg)
(path->bytes pkg)]
[(string? pkg)
(path->bytes (string->path pkg))]
[(bytes? pkg)
pkg]))
(define (directory-path-no-slash pkg)
(bytes->path (regexp-replace* #rx#"/$" (path->bytes* pkg) #"")))
(define (directory-list* d)
(append-map
(λ (pp)
(define p (build-path d pp))
(if (directory-exists? p)
(map (curry build-path pp)
(directory-list* p))
(list pp)))
(directory-list d)))
(define (simple-form-path* p)
(path->string (simple-form-path p)))
(define (pretty-module-path mod)
(if (and (list? mod)
(= 2 (length mod))
(eq? (car mod) 'lib)
(regexp-match? #rx"[.]rkt$" (cadr mod)))
(string->symbol (regexp-replace #rx"[.]rkt$" (cadr mod) ""))
mod))
(define (lift-directory-content pkg-dir path)
(define orig-sub (let ([s (car path)])
(if (string? s)
(string->path s)
s)))
;; Delete everything except `orig-sub`:
(for ([f (in-list (directory-list pkg-dir))])
(unless (equal? f orig-sub)
(delete-directory/files (build-path pkg-dir f))))
;; Get list of files and directories to move:
(define sub-l (directory-list (apply build-path pkg-dir path)))
;; Make sure `sub` doesn't match a name we want to move here:
(define sub
(let loop ([sub orig-sub] [i 0])
(cond
[(member sub sub-l)
;; pick a new name:
(loop (string->path (format "sub~a" i)) (add1 i))]
[(not (equal? sub orig-sub))
(rename-file-or-directory (build-path pkg-dir orig-sub)
(build-path pkg-dir sub))
sub]
[else sub])))
;; Move content of `sub` out:
(define sub-path (apply build-path (cons sub (cdr path))))
(for ([f (in-list sub-l)])
(rename-file-or-directory (build-path pkg-dir sub-path f)
(build-path pkg-dir f)))
;; Remove directory that we moved files out of:
(delete-directory/files (build-path pkg-dir sub)))
(define (remove-extra-directory-layer pkg-dir)
;; Treat a single directory produced in `pkg-dir`
;; as having the content of the package, instead of
;; being included itself in the package content.
(define l (directory-list pkg-dir))
(when (= 1 (length l))
(define orig-sub (car l))
(when (directory-exists? (build-path pkg-dir orig-sub))
(lift-directory-content pkg-dir (list orig-sub)))))

View File

@ -0,0 +1,220 @@
#lang racket/base
(require setup/dirs
racket/format
racket/match
racket/set
"../path.rkt"
"params.rkt"
"lock.rkt"
"print.rkt"
"dirs.rkt"
"config.rkt")
;; Read and writing the database of installed packages.
(provide read-pkg-db
merge-pkg-dbs
find-pkg-installation-scope
package-info
update-pkg-db!
remove-from-pkg-db!
pkg-directory
pkg-directory*
pkg-directory**
make-pkg-info
update-auto
scope->links-file
installed-pkg-table
installed-pkg-names)
(define (read-pkg-db)
(unless (pkg-lock-held)
(pkg-error "attempt to read package database without lock"))
(define scope (current-pkg-scope))
(if (eq? (current-no-pkg-db) scope)
#hash()
(read-pkgs-db scope (current-pkg-scope-version))))
;; read all packages in this scope or wider
(define (merge-pkg-dbs [scope (current-pkg-scope)])
(define (merge-next-pkg-dbs scope)
(parameterize ([current-pkg-scope scope])
(merge-pkg-dbs scope)))
(if (path? scope)
(read-pkg-db)
(case scope
[(installation)
(for*/hash ([dir (in-list (get-pkgs-search-dirs))]
[(k v) (read-pkgs-db dir)])
(values k v))]
[(user)
(define db (read-pkgs-db 'user (current-pkg-scope-version)))
(for/fold ([ht (merge-next-pkg-dbs 'installation)]) ([(k v) (in-hash db)])
(hash-set ht k v))])))
;; Finds the scope, in which `pkg-name' is installed; returns 'dir,
;; 'installation, a path, or #f (where #f means "not installed"). If
;; `next?' is true, search only scopes wider than the current one.
(define (find-pkg-installation-scope pkg-name #:next? [next? #f])
(case (current-pkg-scope)
[(user)
(or (and (not next?)
(hash-ref (read-pkg-db) pkg-name #f)
'user)
(parameterize ([current-pkg-scope 'installation])
(find-pkg-installation-scope pkg-name)))]
[(installation)
(or (and (not next?)
(hash-ref (read-pkg-db) pkg-name #f)
'installation)
(for/or ([dir (in-list (get-pkgs-search-dirs))])
(and (hash-ref (read-pkgs-db dir) pkg-name #f)
dir)))]
[else
(and (not next?)
(and (hash-ref (read-pkgs-db (current-pkg-scope)) pkg-name #f)
(current-pkg-scope)))]))
(define (package-info pkg-name [fail? #t] #:db [given-db #f])
(define db (or given-db (read-pkg-db)))
(define pi (hash-ref db pkg-name #f))
(cond
[pi
pi]
[(not fail?)
#f]
[else
(pkg-not-installed pkg-name db)]))
;; prints an error for packages that are not installed
;; pkg-name db -> void
(define (pkg-not-installed pkg-name db)
;; This may read narrower package scopes without holding the
;; lock, but maybe that's ok for mere error reporting:
(define s (parameterize ([current-pkg-scope 'user])
(find-pkg-installation-scope pkg-name)))
(define not-installed-msg
(cond [s "package installed in a different scope"]
[else "package not currently installed"]))
(apply pkg-error (~a not-installed-msg
"\n package: ~a"
"\n current scope: ~a"
(if s
"\n installed in scope: ~a"
"")
;; Probably too much information:
#;
"\n packages in current scope:~a")
(append
(list
pkg-name
(current-scope->string))
(if s (list s) null)
#;
(list
(format-list (hash-keys db))))))
;; return the current scope as a string
(define (current-scope->string)
(define scope (current-pkg-scope))
(cond
[(path? scope) (path->string scope)]
[else (symbol->string scope)]))
;; ----------------------------------------
(define (update-pkg-db! pkg-name info)
(write-file-hash!
(pkg-db-file)
(hash-set (read-pkg-db) pkg-name info)))
(define (remove-from-pkg-db! pkg-name)
(write-file-hash!
(pkg-db-file)
(hash-remove (read-pkg-db) pkg-name)))
;; ----------------------------------------
(define (scope->links-file scope)
(and (path? scope)
(build-path scope "links.rktd")))
(define (get-scope-list)
;; Get a list of scopes suitable for searches with respect to
;; the current scope
(define current-scope (current-pkg-scope))
(if (path? current-scope)
(list current-scope)
(member current-scope
(append '(user)
(let ([main (find-pkgs-dir)])
(for/list ([d (get-pkgs-search-dirs)])
(if (equal? d main)
'installation
d)))))))
(define (pkg-directory pkg-name)
;; Warning: takes locks individually.
(pkg-directory** pkg-name
(lambda (f)
(with-pkg-lock/read-only
(f)))))
(define (pkg-directory** pkg-name [call-with-pkg-lock (lambda (f) (f))])
(for/or ([scope (in-list (get-scope-list))])
(parameterize ([current-pkg-scope scope])
(call-with-pkg-lock
(lambda ()
(pkg-directory* pkg-name))))))
(define (pkg-directory* pkg-name #:db [db #f])
(define info (package-info pkg-name #f #:db db))
(and info
(let ()
(match-define (pkg-info orig-pkg checksum _) info)
(match orig-pkg
[`(,(or 'link 'static-link) ,orig-pkg-dir)
(path->complete-path orig-pkg-dir (pkg-installed-dir))]
[_
(build-path (pkg-installed-dir)
(or (cond
[(pkg-info/alt? info)
(pkg-info/alt-dir-name info)]
[(sc-pkg-info/alt? info)
(sc-pkg-info/alt-dir-name info)]
[else #f])
pkg-name))]))))
(define (make-pkg-info orig-pkg checksum auto? single-collect alt-dir-name)
;; Picks the right structure subtype
(if single-collect
(if alt-dir-name
(sc-pkg-info/alt orig-pkg checksum auto? single-collect alt-dir-name)
(sc-pkg-info orig-pkg checksum auto? single-collect))
(if alt-dir-name
(pkg-info/alt orig-pkg checksum auto? alt-dir-name)
(pkg-info orig-pkg checksum auto?))))
(define (update-auto this-pkg-info auto?)
(match-define (pkg-info orig-pkg checksum _) this-pkg-info)
(make-pkg-info orig-pkg checksum auto?
(and (sc-pkg-info? this-pkg-info)
(sc-pkg-info-collect this-pkg-info))
(or (and (sc-pkg-info/alt? this-pkg-info)
(sc-pkg-info/alt-dir-name this-pkg-info))
(and (pkg-info/alt? this-pkg-info)
(pkg-info/alt-dir-name this-pkg-info)))))
;; ----------------------------------------
(define (installed-pkg-table #:scope [given-scope #f])
(parameterize ([current-pkg-scope
(or given-scope (default-pkg-scope))])
(with-pkg-lock/read-only
(read-pkg-db))))
(define (installed-pkg-names #:scope [given-scope #f])
(sort (hash-keys (installed-pkg-table #:scope given-scope))
string-ci<=?))

View File

@ -0,0 +1,37 @@
#lang racket/base
(require racket/format
"params.rkt")
;; Output and error helpers
(provide (all-defined-out))
(define-logger pkg)
(define (pkg-error . rest)
(apply (current-pkg-error) rest))
(define (format-list l)
(if (null? l)
" [none]"
(apply string-append
(for/list ([v (in-list l)])
(format "\n ~a" v)))))
(define (log-exn x what)
(log-pkg-error (~a "failure ~a\n"
" error: ~s")
what
(exn-message x)))
(define (printf/flush fmt . args)
;; For status reporting, flush immediately after printing
(apply printf fmt args)
(flush-output))
(define (complain-about-source s reason)
(pkg-error (~a "invalid package source;\n"
" ~a\n"
" given: ~a")
reason
s))

View File

@ -0,0 +1,152 @@
#lang racket/base
(require racket/match
setup/link
racket/file
racket/set
racket/list
racket/format
"../path.rkt"
"pkg-db.rkt"
"collects.rkt"
"params.rkt"
"print.rkt"
"get-info.rkt")
(provide remove-package
pkg-remove)
(define (demote-packages quiet? pkg-names)
(define db (read-pkg-db))
(for ([pkg-name (in-list pkg-names)])
(define pi (package-info pkg-name #:db db))
(unless (pkg-info-auto? pi)
(unless quiet?
(printf/flush "Demoting ~a to auto-installed\n" pkg-name))
(update-pkg-db! pkg-name (update-auto pi #t)))))
(define ((remove-package quiet?) pkg-name)
(unless quiet?
(printf/flush "Removing ~a\n" pkg-name))
(define db (read-pkg-db))
(define pi (package-info pkg-name #:db db))
(match-define (pkg-info orig-pkg checksum _) pi)
(define pkg-dir (pkg-directory* pkg-name #:db db))
(remove-from-pkg-db! pkg-name)
(define scope (current-pkg-scope))
(define user? (not (or (eq? scope 'installation)
(path? scope))))
(match orig-pkg
[`(,(or 'link 'static-link) ,_)
(links pkg-dir
#:remove? #t
#:user? user?
#:file (scope->links-file scope)
#:root? (not (sc-pkg-info? pi)))]
[_
(links pkg-dir
#:remove? #t
#:user? user?
#:file (scope->links-file scope)
#:root? (not (sc-pkg-info? pi)))
(delete-directory/files pkg-dir)]))
(define (pkg-remove given-pkgs
#:demote? [demote? #f]
#:force? [force? #f]
#:auto? [auto? #f]
#:quiet? [quiet? #f]
#:from-command-line? [from-command-line? #f])
(define db (read-pkg-db))
(define all-pkgs
(hash-keys db))
(define all-pkgs-set
(list->set all-pkgs))
(define metadata-ns (make-metadata-namespace))
(define in-pkgs (remove-duplicates given-pkgs))
(define remove-pkgs
(if auto?
;; compute fixpoint:
(let ([init-drop (set-union
(list->set
(filter
(λ (p) (pkg-info-auto? (hash-ref db p)))
all-pkgs))
(list->set in-pkgs))])
(let loop ([drop init-drop]
[keep (set-subtract
(list->set all-pkgs)
init-drop)])
(define deps
(list->set
(append-map (package-dependencies metadata-ns db #t)
(set->list keep))))
(define still-drop (set-subtract drop deps))
(define delta (set-subtract drop still-drop))
(if (set-empty? delta)
(set->list drop)
(loop still-drop
(set-union keep delta)))))
;; just given pkgs:
(if demote?
null
in-pkgs)))
(define setup-collects
(get-setup-collects remove-pkgs
db
metadata-ns))
(unless (or force? demote?)
;; Check dependencies on `in-pkgs' (not `pkgs', which has already
;; been filtered to remove package with dependencies if `auto?' is
;; true).
(define pkgs-set (list->set in-pkgs))
(define remaining-pkg-db-set
(set-subtract all-pkgs-set
(if auto?
(list->set remove-pkgs)
pkgs-set)))
(define deps-to-be-removed
(set-intersect
pkgs-set
(list->set
(append-map (package-dependencies metadata-ns db #t)
(set->list
remaining-pkg-db-set)))))
(unless (set-empty? deps-to-be-removed)
(pkg-error (~a "cannot remove packages that are dependencies of other packages\n"
" dependencies:~a")
(format-list
(map
(λ (p)
(define ds
(filter (λ (dp)
(member p ((package-dependencies metadata-ns db #t) dp)))
(set->list
remaining-pkg-db-set)))
(~a p " (required by: " ds ")"))
(set->list deps-to-be-removed))))))
(when demote?
;; Demote any package that is not going to be removed:
(demote-packages
quiet?
(set->list (set-subtract (list->set in-pkgs)
(list->set remove-pkgs)))))
(for-each (remove-package quiet?)
remove-pkgs)
(cond
[(or (null? remove-pkgs) demote?)
;; Did nothing, so no setup:
'skip]
[else
;; setup only collections that still exist:
(and setup-collects
(for/list ([c (in-list setup-collects)]
#:when (apply collection-path
(if (path-string? c) (list c) c)
#:fail (lambda (s) #f)))
c))]))

View File

@ -0,0 +1,69 @@
#lang racket/base
(require racket/list
racket/match
racket/format
racket/function
"../path.rkt"
"pkg-db.rkt")
(provide pkg-show)
(define (pkg-show indent
#:directory? [dir? #f]
#:auto? [show-auto? #f])
(let ()
(define db (read-pkg-db))
(define pkgs (sort (hash-keys db) string-ci<=?))
(if (null? pkgs)
(printf " [none]\n")
(begin
(table-display
(list*
(append
(list (format "~aPackage~a"
indent
(if show-auto? "[*=auto]" ""))
"Checksum"
"Source")
(if dir?
(list "Directory")
empty))
(for/list ([pkg (in-list pkgs)]
#:when (or show-auto?
(not (pkg-info-auto? (hash-ref db pkg)))))
(match-define (pkg-info orig-pkg checksum auto?) (hash-ref db pkg))
(append
(list (format "~a~a~a"
indent
pkg
(if auto? "*" ""))
(format "~a" checksum)
(format "~a" orig-pkg))
(if dir?
(list (~a (pkg-directory* pkg #:db db)))
empty)))))
(unless show-auto?
(define n (for/sum ([pkg (in-list pkgs)]
#:when (pkg-info-auto? (hash-ref db pkg)))
1))
(unless (zero? n)
(printf "~a[~a auto-installed package~a not shown]\n"
indent
n
(if (= n 1) "" "s"))))))))
(define (table-display l)
(define how-many-cols (length (first l)))
(define max-widths
(for/list ([col (in-range how-many-cols)])
(apply max (map (compose string-length (curryr list-ref col)) l))))
(for ([row (in-list l)])
(for ([col (in-list row)]
[i (in-naturals 1)]
[width (in-list max-widths)])
(printf "~a~a"
col
(if (= i how-many-cols)
""
(make-string (+ (- width (string-length col)) 4) #\space))))
(printf "\n")))

View File

@ -0,0 +1,598 @@
#lang racket/base
(require racket/format
racket/match
racket/list
racket/path
racket/file
racket/port
racket/string
setup/unpack
setup/dirs
net/url
file/untgz
file/unzip
openssl/sha1
json
"../name.rkt"
"../strip.rkt"
"catalog.rkt"
"download.rkt"
"print.rkt"
"path.rkt"
"dirs.rkt"
"desc.rkt"
"params.rkt"
"get-info.rkt"
"mod-paths.rkt"
"addl-installs.rkt")
(provide (struct-out install-info)
remote-package-checksum
stage-package/info
pkg-stage)
(struct install-info (name orig-pkg directory clean? checksum module-paths additional-installs))
(define (remote-package-checksum pkg download-printf pkg-name)
(match pkg
[`(catalog ,pkg-name)
(hash-ref (package-catalog-lookup pkg-name #f download-printf) 'checksum)]
[`(url ,pkg-url-str)
(package-url->checksum pkg-url-str
#:download-printf download-printf
#:pkg-name pkg-name)]))
;; Downloads a package (if needed) and unpacks it (if needed) into a
;; temporary directory.
(define (stage-package/info pkg
given-type
given-pkg-name
#:given-checksum [given-checksum #f]
#:cached-url [cached-url #f]
#:use-cache? use-cache?
check-sums?
download-printf
metadata-ns
#:strip [strip-mode #f]
#:force-strip? [force-strip? #f]
#:in-place? [in-place? #f]
#:in-place-clean? [in-place-clean? #f]
#:link-dirs? [link-dirs? #f])
(define-values (inferred-pkg-name type)
(if (path? pkg)
(package-source->name+type (path->string pkg)
(or given-type
(if (directory-exists? pkg)
(if link-dirs?
'link
'dir)
'file))
#:must-infer-name? (not given-pkg-name)
#:complain complain-about-source)
(package-source->name+type pkg given-type
#:link-dirs? link-dirs?
#:must-infer-name? (not given-pkg-name)
#:complain complain-about-source)))
(define pkg-name (or given-pkg-name inferred-pkg-name))
(when (and type (not pkg-name))
(pkg-error (~a "could not infer package name from source\n"
" source: ~a")
pkg))
(cond
[(and (eq? type 'github)
(not (regexp-match? #rx"^git(?:hub)?://" pkg)))
;; Add "git://github.com/"
(stage-package/info (string-append "git://github.com/" pkg) type
pkg-name
#:given-checksum given-checksum
#:use-cache? use-cache?
check-sums? download-printf
metadata-ns
#:strip strip-mode
#:force-strip? force-strip?)]
[(or (eq? type 'file-url) (eq? type 'dir-url) (eq? type 'github))
(define pkg-url (string->url pkg))
(define scheme (url-scheme pkg-url))
(define orig-pkg `(url ,pkg))
(define found-checksum
;; If a checksum is given, use that. In the case of a non-github
;; source, we could try to get the checksum from the source, and
;; then check whether it matches the expected one, but we choose
;; to avoid an extra trip to the server.
(or given-checksum
(remote-package-checksum orig-pkg download-printf pkg-name)))
(when check-sums?
(check-checksum given-checksum found-checksum "unexpected" pkg #f))
(define checksum (or found-checksum given-checksum))
(define downloaded-info
(match type
['github
(unless checksum
(pkg-error
(~a "could not find checksum for GitHub package source, which implies it doesn't exist\n"
" source: ~a")
pkg))
(when (equal? checksum "")
(pkg-error
(~a "cannot use empty checksum for GitHub package source\n"
" source: ~a")
pkg))
(match-define (list* user repo branch path)
(split-github-url pkg-url))
(define new-url
(url "https" #f "github.com" #f #t
(map (λ (x) (path/param x empty))
(list user repo "tarball" checksum))
empty
#f))
(define tmp.tgz
(make-temporary-file
(string-append
"~a-"
(format "~a.~a.tgz" repo branch))
#f))
(delete-file tmp.tgz)
(define tmp-dir
(make-temporary-file
(string-append
"~a-"
(format "~a.~a" repo branch))
'directory))
(dynamic-wind
void
(λ ()
(download-file! new-url tmp.tgz checksum
#:use-cache? use-cache?
#:download-printf download-printf)
(define staged? #f)
(dynamic-wind
void
(λ ()
(untar tmp.tgz tmp-dir #:strip-components 1)
(unless (null? path)
(unless (directory-exists? (apply build-path tmp-dir path))
(pkg-error
(~a "specified directory is not in GitHub respository archive\n"
" path: ~a"
(apply build-path path))))
(lift-directory-content tmp-dir path))
(begin0
(stage-package/info tmp-dir
'dir
pkg-name
#:given-checksum checksum
#:cached-url new-url
#:use-cache? use-cache?
check-sums?
download-printf
metadata-ns
#:strip strip-mode
#:force-strip? force-strip?
#:in-place? #t
#:in-place-clean? #t)
(set! staged? #t)))
(λ ()
(when (and use-cache? (not staged?))
(clean-cache new-url checksum))
(unless staged?
(delete-directory/files tmp-dir)))))
(λ ()
(delete-directory/files tmp.tgz)))]
[_
(define url-last-component
(path/param-path (last (url-path pkg-url))))
(define url-looks-like-directory? (eq? type 'dir-url))
(define-values
(package-path download-type download-package!)
(cond
[url-looks-like-directory?
(define package-path
(make-temporary-file
(string-append
"~a-"
pkg-name)
'directory))
(define (path-like f)
(build-path package-path f))
(define (url-like f)
(if (and (pair? (url-path pkg-url))
(equal? "" (path/param-path (last (url-path pkg-url)))))
;; normal relative path:
(combine-url/relative pkg-url f)
;; we're assuming that the last path element is
;; a directory, so just add f:
(struct-copy url pkg-url [path
(append
(url-path pkg-url)
(list (path/param f null)))])))
(values package-path
'dir
(λ ()
(download-printf "Cloning remote directory ~a\n"
(url->string pkg-url))
(make-directory* package-path)
(define manifest
(call/input-url+200
(url-like "MANIFEST")
port->lines))
(unless manifest
(pkg-error (~a "could not find MANIFEST for package source\n"
" source: ~a")
pkg))
(for ([f (in-list manifest)])
(download-file! (url-like f)
(path-like f)
#f
#:use-cache? use-cache?))))]
[else
(define package-path
(make-temporary-file
(string-append
"~a-"
url-last-component)
#f))
(delete-file package-path)
(values package-path
'file
(λ ()
(log-pkg-debug "\tAssuming URL names a file")
(download-file! pkg-url package-path checksum
#:use-cache? use-cache?
#:download-printf download-printf)))]))
(define staged? #f)
(dynamic-wind
void
(λ ()
(download-package!)
(log-pkg-debug "\tDownloading done, installing ~a as ~a"
package-path pkg-name)
(begin0
(stage-package/info package-path
download-type
pkg-name
#:given-checksum checksum
#:cached-url pkg-url
#:use-cache? use-cache?
check-sums?
download-printf
metadata-ns
#:strip strip-mode
#:force-strip? force-strip?)
(set! staged? #t)))
(λ ()
(when (or (file-exists? package-path)
(directory-exists? package-path))
(when (and use-cache? (not staged?))
(clean-cache pkg-url checksum))
(delete-directory/files package-path))))]))
(define info (update-install-info-orig-pkg downloaded-info
orig-pkg))
(when (and check-sums?
(install-info-checksum info)
(not checksum))
(pkg-error (~a "remote package had no checksum\n"
" package: ~a")
pkg))
(when check-sums?
(check-checksum checksum (install-info-checksum info)
"mismatched"
pkg
(and use-cache? cached-url)))
(update-install-info-checksum
info
checksum)]
[(eq? type 'file)
(define pkg-path (if (path? pkg)
pkg
(package-source->path pkg type)))
(unless (file-exists? pkg-path)
(pkg-error "no such file\n path: ~a" pkg-path))
(define checksum-pth (format "~a.CHECKSUM" pkg-path))
(define expected-checksum
(and (file-exists? checksum-pth)
check-sums?
(file->string checksum-pth)))
(check-checksum given-checksum expected-checksum "unexpected" pkg-path #f)
(define actual-checksum
(with-input-from-file pkg-path
(λ ()
(sha1 (current-input-port)))))
(check-checksum expected-checksum actual-checksum "mismatched" pkg-path
(and use-cache? cached-url))
(define checksum
actual-checksum)
(define pkg-format (filename-extension pkg-path))
(define pkg-dir
(make-temporary-file (string-append "~a-" pkg-name)
'directory))
(define staged? #f)
(dynamic-wind
void
(λ ()
(make-directory* pkg-dir)
(match pkg-format
[#"tgz"
(untar pkg-path pkg-dir)
(remove-extra-directory-layer pkg-dir)]
[#"tar"
(untar pkg-path pkg-dir)
(remove-extra-directory-layer pkg-dir)]
[#"gz" ; assuming .tar.gz
(untar pkg-path pkg-dir)
(remove-extra-directory-layer pkg-dir)]
[#"zip"
(unzip pkg-path (make-filesystem-entry-reader #:dest pkg-dir)
#:preserve-timestamps? #t
#:utc-timestamps? #t)
(remove-extra-directory-layer pkg-dir)]
[#"plt"
(make-directory* pkg-dir)
(unpack pkg-path pkg-dir
(lambda (x) (log-pkg-debug "~a" x))
(lambda () pkg-dir)
#f
(lambda (auto-dir main-dir file) pkg-dir))
(define info-path (build-path pkg-dir "info.rkt"))
(unless (file-exists? info-path)
;; Add in "info.rkt" file to make it multi-collection,
;; since a ".plt" archive is never single-collection. This
;; is needed for supporting old ".plt" archives as packages.
(call-with-output-file info-path
(lambda (o)
(fprintf o "#lang setup/infotab\n")
(write '(define collection 'multi) o)
(newline o))))]
[x
(pkg-error "invalid package format\n given: ~a" x)])
(begin0
(update-install-info-checksum
(update-install-info-orig-pkg
(stage-package/info pkg-dir
'dir
pkg-name
#:given-checksum checksum
#:cached-url cached-url
#:use-cache? use-cache?
check-sums?
download-printf
metadata-ns
#:strip strip-mode
#:force-strip? force-strip?
#:in-place? (not strip-mode)
#:in-place-clean? #t)
`(file ,(simple-form-path* pkg-path)))
checksum)
(unless strip-mode
(set! staged? #t))))
(λ ()
(unless staged?
(delete-directory/files pkg-dir))))]
[(or (eq? type 'dir)
(eq? type 'link)
(eq? type 'static-link))
(define pkg-path (if (path? pkg)
pkg
(package-source->path pkg type)))
(unless (directory-exists? pkg-path)
(pkg-error "no such directory\n path: ~a" pkg-path))
(let ([pkg-path (directory-path-no-slash pkg-path)])
(cond
[(or (eq? type 'link)
(eq? type 'static-link))
(install-info pkg-name
`(,type ,(path->string
(find-relative-path (pkg-installed-dir)
(simple-form-path pkg-path)
#:more-than-root? #t)))
pkg-path
#f
given-checksum ; if a checksum is provided, just use it
(directory->module-paths pkg pkg-name metadata-ns)
(directory->additional-installs pkg pkg-name metadata-ns))]
[else
(define pkg-dir
(if in-place?
(if strip-mode
(pkg-error "cannot strip directory in place")
pkg-path)
(let ([pkg-dir (make-temporary-file "pkg~a" 'directory)])
(delete-directory pkg-dir)
(if strip-mode
(begin
(unless force-strip?
(check-strip-compatible strip-mode pkg-name pkg pkg-error))
(make-directory* pkg-dir)
(generate-stripped-directory strip-mode pkg pkg-dir))
(begin
(make-parent-directory* pkg-dir)
(copy-directory/files pkg-path pkg-dir #:keep-modify-seconds? #t)))
pkg-dir)))
(when (or (not in-place?)
in-place-clean?)
(drop-redundant-files pkg-dir))
(install-info pkg-name
`(dir ,(simple-form-path* pkg-path))
pkg-dir
(or (not in-place?) in-place-clean?)
given-checksum ; if a checksum is provided, just use it
(directory->module-paths pkg-dir pkg-name metadata-ns)
(directory->additional-installs pkg-dir pkg-name metadata-ns))]))]
[(eq? type 'name)
(define catalog-info (package-catalog-lookup pkg #f download-printf))
(log-pkg-debug "catalog response: ~s" catalog-info)
(define source (hash-ref catalog-info 'source))
(define checksum (hash-ref catalog-info 'checksum))
(define info (stage-package/info source
#f
pkg-name
#:given-checksum checksum
#:use-cache? use-cache?
check-sums?
download-printf
metadata-ns
#:strip strip-mode
#:force-strip? force-strip?))
(when check-sums?
(check-checksum given-checksum checksum "unexpected" pkg #f)
(check-checksum checksum (install-info-checksum info) "incorrect" pkg #f))
(update-install-info-orig-pkg
(update-install-info-checksum
info
checksum)
`(catalog ,pkg))]
[else
(pkg-error "cannot infer package source type\n source: ~a" pkg)]))
(define (pkg-stage desc
#:namespace [metadata-ns (make-metadata-namespace)]
#:in-place? [in-place? #f]
#:strip [strip-mode #f]
#:force-strip? [force-strip? #f]
#:use-cache? [use-cache? #f]
#:quiet? [quiet? #t])
(define i (stage-package/info (pkg-desc-source desc)
(pkg-desc-type desc)
(pkg-desc-name desc)
#:given-checksum (pkg-desc-checksum desc)
#:use-cache? use-cache?
#t
(if quiet? void printf)
metadata-ns
#:in-place? in-place?
#:strip strip-mode
#:force-strip? force-strip?))
(values (install-info-name i)
(install-info-directory i)
(install-info-checksum i)
(install-info-clean? i)
(install-info-module-paths i)))
;; ----------------------------------------
(define (package-url->checksum pkg-url-str [query empty]
#:download-printf [download-printf void]
#:pkg-name [pkg-name "package"])
(define pkg-url
(string->url pkg-url-str))
(match (url-scheme pkg-url)
[(or "github" "git")
(match-define (list* user repo branch path)
(split-github-url pkg-url))
(or
(for/or ([kind '("branches" "tags")])
(define api-u
(url "https" #f "api.github.com" #f #t
(map (λ (x) (path/param x empty))
(list "repos" user repo kind))
(append query
(if (and (github-client_id)
(github-client_secret))
(list (cons 'client_id (github-client_id))
(cons 'client_secret (github-client_secret)))
empty))
#f))
(download-printf "Querying GitHub ~a\n" kind)
(log-pkg-debug "Querying GitHub at ~a" (url->string api-u))
(define api-bs
(call/input-url+200
api-u port->bytes
#:headers (list (format "User-Agent: raco-pkg/~a" (version)))))
(unless api-bs
(error 'package-url->checksum
"could not connect to GitHub\n URL: ~a"
(url->string
(struct-copy url api-u
[query query]))))
(define branches
(read-json (open-input-bytes api-bs)))
(unless (and (list? branches)
(andmap hash? branches)
(andmap (λ (b) (hash-has-key? b 'name)) branches)
(andmap (λ (b) (hash-has-key? b 'commit)) branches))
(error 'package-url->checksum
"Invalid response from Github: ~e"
api-bs))
(for/or ([b (in-list branches)])
(and (equal? (hash-ref b 'name) branch)
(hash-ref (hash-ref b 'commit) 'sha))))
;; no matching branch/tag found, so if `branch' matches the
;; syntax of a commit id, then assume that it refers to a commit
(and (regexp-match? #rx"[a-f0-9]+" branch)
branch))]
[_
(define u (string-append pkg-url-str ".CHECKSUM"))
(download-printf "Downloading checksum for ~a\n" pkg-name)
(log-pkg-debug "Downloading checksum as ~a" u)
(call/input-url+200 (string->url u)
port->string)]))
(define (check-checksum given-checksum checksum what pkg-src cached-url)
(when (and given-checksum
checksum
(not (equal? given-checksum checksum)))
(clean-cache cached-url checksum)
(pkg-error (~a "~a checksum on package\n"
" package source: ~a\n"
" expected: ~e\n"
" got: ~e")
what
pkg-src
given-checksum
checksum)))
;; ----------------------------------------
(define (update-install-info-orig-pkg if op)
(struct-copy install-info if
[orig-pkg op]))
(define (update-install-info-checksum if op)
(struct-copy install-info if
[checksum op]))
;; ----------------------------------------
(define github-client_id (make-parameter #f))
(define github-client_secret (make-parameter #f))
(define (split-github-url pkg-url)
(if (equal? (url-scheme pkg-url) "github")
;; github://
(map path/param-path (url-path/no-slash pkg-url))
;; git://
(let* ([paths (map path/param-path (url-path/no-slash pkg-url))])
(list* (car paths)
(regexp-replace* #rx"[.]git$" (cadr paths) "")
(or (url-fragment pkg-url) "master")
(let ([a (assoc 'path (url-query pkg-url))])
(or (and a (cdr a) (string-split (cdr a) "/"))
null))))))
;; ----------------------------------------
(define (untar pkg pkg-dir #:strip-components [strip-components 0])
(make-directory* pkg-dir)
(untgz pkg #:dest pkg-dir #:strip-count strip-components))
;; ----------------------------------------
(define (drop-redundant-files pkg-dir)
;; Ad hoc space-saving rule: for an installation-wide package, remove
;; any redundant "COPYING.txt" or "COPYING_LESSER.txt" files.
(when (and (eq? 'installation (current-pkg-scope))
(find-share-dir))
(for ([i (in-list '("COPYING.txt" "COPYING_LESSER.txt"))])
(define pkg-file (build-path pkg-dir i))
(define share-file (build-path (find-share-dir) i))
(when (and (file-exists? pkg-file)
(file-exists? share-file)
(equal? (file->bytes pkg-file)
(file->bytes share-file)))
;; This file would be redundant, so drop it
(delete-file pkg-file)))))

View File

@ -0,0 +1,39 @@
#lang racket/base
(require racket/set
racket/path
setup/dirs
syntax/modcollapse
(prefix-in db: "../db.rkt"))
(provide pkg-catalog-suggestions-for-module)
(define (choose-catalog-file)
(define default (db:current-pkg-catalog-file))
(if (file-exists? default)
default
(let ([installation (build-path (find-share-dir) "pkgs" (file-name-from-path default))])
(if (file-exists? installation)
installation
default))))
(define (pkg-catalog-suggestions-for-module module-path
#:catalog-file [catalog-file (choose-catalog-file)])
(if (file-exists? catalog-file)
(parameterize ([db:current-pkg-catalog-file catalog-file])
(let* ([mod (collapse-module-path
module-path
(lambda () (build-path (current-directory) "dummy.rkt")))]
[pkgs (db:get-module-pkgs mod)]
[more-pkgs (let ([rx:reader #rx"/lang/reader[.]rkt$"])
(if (and (pair? mod)
(eq? (car mod) 'lib)
(regexp-match rx:reader (cadr mod)))
(db:get-module-pkgs `(lib ,(regexp-replace rx:reader (cadr mod) "/main.rkt")))
null))])
(sort (set->list
(list->set
(map db:pkg-name (append pkgs more-pkgs))))
string<?)))
null))

View File

@ -1,208 +0,0 @@
#lang racket/base
(require racket/path
racket/list
racket/function
racket/file
racket/port
racket/match
racket/format
racket/string
racket/set
net/url
json)
(define-logger pkg)
(define (make-parent-directory* p)
(define parent (path-only p))
(make-directory* parent))
(define (table-display l)
(define how-many-cols (length (first l)))
(define max-widths
(for/list ([col (in-range how-many-cols)])
(apply max (map (compose string-length (curryr list-ref col)) l))))
(for ([row (in-list l)])
(for ([col (in-list row)]
[i (in-naturals 1)]
[width (in-list max-widths)])
(printf "~a~a"
col
(if (= i how-many-cols)
""
(make-string (+ (- width (string-length col)) 4) #\space))))
(printf "\n")))
(define (call/input-url+200 u fun
#:headers [headers '()]
#:failure [fail-k (lambda (s) #f)])
#;(printf "\t\tReading ~a\n" (url->string u))
(define-values (ip hs) (get-pure-port/headers u headers
#:redirections 25
#:status? #t))
(if (string=? "200" (substring hs 9 12))
(begin0
(fun ip)
(close-input-port ip))
(fail-k hs)))
(define (url-path/no-slash url)
(define p (url-path url))
(define rp (reverse p))
(reverse
(match rp
[(list* (path/param "" _) rest)
rest]
[_ rp])))
(define github-client_id (make-parameter #f))
(define github-client_secret (make-parameter #f))
(define (split-github-url pkg-url)
(if (equal? (url-scheme pkg-url) "github")
;; github://
(map path/param-path (url-path/no-slash pkg-url))
;; git://
(let* ([paths (map path/param-path (url-path/no-slash pkg-url))])
(list* (car paths)
(regexp-replace* #rx"[.]git$" (cadr paths) "")
(or (url-fragment pkg-url) "master")
(let ([a (assoc 'path (url-query pkg-url))])
(or (and a (cdr a) (string-split (cdr a) "/"))
null))))))
(define (package-url->checksum pkg-url-str [query empty]
#:download-printf [download-printf void]
#:pkg-name [pkg-name "package"])
(define pkg-url
(string->url pkg-url-str))
(match (url-scheme pkg-url)
[(or "github" "git")
(match-define (list* user repo branch path)
(split-github-url pkg-url))
(or
(for/or ([kind '("branches" "tags")])
(define api-u
(url "https" #f "api.github.com" #f #t
(map (λ (x) (path/param x empty))
(list "repos" user repo kind))
(append query
(if (and (github-client_id)
(github-client_secret))
(list (cons 'client_id (github-client_id))
(cons 'client_secret (github-client_secret)))
empty))
#f))
(download-printf "Querying GitHub ~a\n" kind)
(log-pkg-debug "Querying GitHub at ~a" (url->string api-u))
(define api-bs
(call/input-url+200
api-u port->bytes
#:headers (list (format "User-Agent: raco-pkg/~a" (version)))))
(unless api-bs
(error 'package-url->checksum
"could not connect to GitHub\n URL: ~a"
(url->string
(struct-copy url api-u
[query query]))))
(define branches
(read-json (open-input-bytes api-bs)))
(unless (and (list? branches)
(andmap hash? branches)
(andmap (λ (b) (hash-has-key? b 'name)) branches)
(andmap (λ (b) (hash-has-key? b 'commit)) branches))
(error 'package-url->checksum
"Invalid response from Github: ~e"
api-bs))
(for/or ([b (in-list branches)])
(and (equal? (hash-ref b 'name) branch)
(hash-ref (hash-ref b 'commit) 'sha))))
;; no matching branch/tag found, so if `branch' matches the
;; syntax of a commit id, then assume that it refers to a commit
(and (regexp-match? #rx"[a-f0-9]+" branch)
branch))]
[_
(define u (string-append pkg-url-str ".CHECKSUM"))
(download-printf "Downloading checksum for ~a\n" pkg-name)
(log-pkg-debug "Downloading checksum as ~a" u)
(call/input-url+200 (string->url u)
port->string)]))
;; uses a custodian to avoid leaks:
(define (call-with-url url handler)
(define c (make-custodian))
(dynamic-wind
void
(lambda ()
(define-values (p hs)
(parameterize ([current-custodian c])
(get-pure-port/headers url #:redirections 25 #:status? #t)))
(begin0
(and (string=? "200" (substring hs 9 12))
(handler p))
(close-input-port p)))
(lambda ()
(custodian-shutdown-all c))))
(define (read-from-server who url pred
[failure
(lambda (s)
(error who
(~a "bad response from server\n"
" url: ~a\n"
" response: ~v")
(url->string url)
s))])
(define bytes (call-with-url url port->bytes))
((if bytes
(with-handlers ([exn:fail:read? (lambda (exn)
(lambda () (failure bytes)))])
(define v (read (open-input-bytes bytes)))
(lambda ()
(if (pred v)
v
(failure bytes))))
(lambda () (failure #f)))))
(define (lift-directory-content pkg-dir path)
(define orig-sub (let ([s (car path)])
(if (string? s)
(string->path s)
s)))
;; Delete everything except `orig-sub`:
(for ([f (in-list (directory-list pkg-dir))])
(unless (equal? f orig-sub)
(delete-directory/files (build-path pkg-dir f))))
;; Get list of files and directories to move:
(define sub-l (directory-list (apply build-path pkg-dir path)))
;; Make sure `sub` doesn't match a name we want to move here:
(define sub
(let loop ([sub orig-sub] [i 0])
(cond
[(member sub sub-l)
;; pick a new name:
(loop (string->path (format "sub~a" i)) (add1 i))]
[(not (equal? sub orig-sub))
(rename-file-or-directory (build-path pkg-dir orig-sub)
(build-path pkg-dir sub))
sub]
[else sub])))
;; Move content of `sub` out:
(define sub-path (apply build-path (cons sub (cdr path))))
(for ([f (in-list sub-l)])
(rename-file-or-directory (build-path pkg-dir sub-path f)
(build-path pkg-dir f)))
;; Remove directory that we moved files out of:
(delete-directory/files (build-path pkg-dir sub)))
(define (remove-extra-directory-layer pkg-dir)
;; Treat a single directory produced in `pkg-dir`
;; as having the content of the package, instead of
;; being included itself in the package content.
(define l (directory-list pkg-dir))
(when (= 1 (length l))
(define orig-sub (car l))
(when (directory-exists? (build-path pkg-dir orig-sub))
(lift-directory-content pkg-dir (list orig-sub)))))
(provide (all-defined-out))