diff --git a/pkgs/racket-pkgs/racket-doc/scribblings/raco/setup.scrbl b/pkgs/racket-pkgs/racket-doc/scribblings/raco/setup.scrbl index e350c9b5ee..bf7ddd5ab6 100644 --- a/pkgs/racket-pkgs/racket-doc/scribblings/raco/setup.scrbl +++ b/pkgs/racket-pkgs/racket-doc/scribblings/raco/setup.scrbl @@ -170,11 +170,13 @@ flags: @item{@DFlag{avoid-main} --- refrain from any setup actions that affect the installation, as opposed to user-specific actions.} - @item{@DFlag{no-pkg-deps} or @Flag{K} --- refrain from checking whether dependencies among - libraries are properly reflected by package-level dependency - declarations. The check uses compiled bytecode and associated - @filepath{.dep} files, and it checks only files are setup against - only packages that include files that are setup.} + @item{@DFlag{no-pkg-deps} or @Flag{K} --- refrain from checking + whether dependencies among libraries are properly reflected by + package-level dependency declarations, whether modules are declared + by multiple packages, and whether package version dependencies are + satisfied. Dependency checking uses compiled bytecode and associated + @filepath{.dep} files, and it checks only files that are setup + against only packages that include files that are setup.} @item{@DFlag{fix-pkg-deps} --- attempt to correct dependency mismatches by adjusting package @filepath{info.rkt} files (which makes diff --git a/racket/collects/setup/private/pkg-deps.rkt b/racket/collects/setup/private/pkg-deps.rkt index 1891793136..3c3ec8c403 100644 --- a/racket/collects/setup/private/pkg-deps.rkt +++ b/racket/collects/setup/private/pkg-deps.rkt @@ -10,7 +10,8 @@ racket/file racket/path setup/dirs - setup/doc-db) + setup/doc-db + version/utils) (provide check-package-dependencies) @@ -37,6 +38,9 @@ (define pkg-implies (make-hash)) ; for checking unused (define pkg-reps (make-hash)) ; for union-find on external deps (define mod-pkg (make-hash)) + (define dup-mods (make-hash)) ; modules that are provided by multiple packages + (define pkg-version-deps (make-hash)) ; save version dependencies + (define pkg-versions (make-hash)) ; save declared versions (define path-cache (make-hash)) (define metadata-ns (make-base-namespace)) @@ -75,6 +79,25 @@ (hash-set! pkg-reps rep-a-pkg rep-b-pkg)) rep-b-pkg) + ;; ---------------------------------------- + ;; Check whether another package has already declared a module: + (define (check-module-declaration mod pkg) + (let ([already-pkg (hash-ref mod-pkg mod #f)]) + (when already-pkg + (setup-fprintf* (current-error-port) #f + (string-append + "module provided by multiple packages:\n" + " module: ~s\n" + " providing package: ~s\n" + " other providing package: ~s\n") + mod + pkg + already-pkg) + (hash-update! dup-mods mod + (lambda (ht) + (hash-set (hash-set ht pkg #t) already-pkg #t)) + #hash())))) + ;; ---------------------------------------- ;; Get a package's info, returning its deps and implies: (define (get-immediate-pkg-info! pkg) @@ -82,29 +105,39 @@ (unless dir (error 'check-dependencies "package not installed: ~s" pkg)) ;; Get package information: - (define-values (checksum mods deps+build-deps) + (define-values (checksum mods deps+build-deps+vers) (get-pkg-content (pkg-desc (if (path? dir) (path->string dir) dir) 'dir pkg #f #f) #:namespace metadata-ns #:extract-info (lambda (i) - (if (and i - (or (i 'deps (lambda () #f)) - (i 'build-deps (lambda () #f)))) - (cons - (extract-pkg-dependencies i - #:build-deps? #f - #:filter? #t) - (extract-pkg-dependencies i - #:filter? #t)) - #f)))) + (cons + (if (and i + (or (i 'deps (lambda () #f)) + (i 'build-deps (lambda () #f)))) + (cons + (extract-pkg-dependencies i + #:build-deps? #f + #:filter? #t + #:versions? #t) + (extract-pkg-dependencies i + #:filter? #t + #:versions? #t)) + #f) + (and i (i 'version (lambda () #f))))))) + (define vers (cdr deps+build-deps+vers)) + (define deps+build-deps (car deps+build-deps+vers)) (unless deps+build-deps (hash-set! skip-pkgs pkg #t) (setup-printf #f "package declares no dependencies: ~s" pkg)) - (define deps (if deps+build-deps - (filter-map package-source->name (cdr deps+build-deps)) - '())) + (define deps+vers (if deps+build-deps + (filter-map (lambda (p) + (define n (package-source->name (car p))) + (and n (cons n (cadr p)))) + (cdr deps+build-deps)) + '())) + (define deps (map car deps+vers)) (define runtime-deps (if deps+build-deps (list->set (filter-map package-source->name - (car deps+build-deps))) + (map car (car deps+build-deps)))) (set))) (define implies (list->set (let ([i (get-info/full dir #:namespace metadata-ns)]) @@ -123,19 +156,25 @@ pkg i)))) (for ([mod (in-list mods)]) + (check-module-declaration mod pkg) (hash-set! mod-pkg mod pkg)) ;; Save immediate dependencies, initialize external dependencies: (hash-set! pkg-reps pkg pkg) (hash-set! pkg-immediate-deps pkg (list (set-add runtime-deps pkg) - (set-add (list->set deps) + (set-add (list->set deps) pkg))) + (hash-set! pkg-version-deps pkg (for/list ([d (in-list deps+vers)] + #:when (cdr d)) + d)) (hash-set! pkg-external-deps pkg (set-add (set-intersect implies (set-add runtime-deps 'core)) pkg)) + (when vers + (hash-set! pkg-versions pkg vers)) (when check-unused? (hash-set! pkg-implies pkg implies)) (values deps implies)) @@ -402,6 +441,27 @@ ;; Treat all (direct) documentation links as 'build mode: (register-or-check-docs #t pkg path coll-main?)))) + ;; check version dependencies: + (hash-set! pkg-versions "racket" (version)) + (define bad-version-dependencies + (for*/fold ([ht #hash()]) ([(pkg deps) (in-hash pkg-version-deps)] + [d (in-list deps)]) + (define dep-pkg (car d)) + (define dep-vers (cdr d)) + (define decl-vers (hash-ref pkg-versions dep-pkg "1.0")) + (cond + [(version