raco setup: add --unused-pkg-deps flag

This flag may be useful for checking for unnecessary dependencies, but
beware that it reports many false negatives, either because relevant
files are not compiled, because the dependency is dynamic, or because
unused packages are intentially listed as dependencies as a
convenience (as is the case for "main-distribution").
This commit is contained in:
Matthew Flatt 2013-10-15 16:35:12 -06:00
parent 1920ac59ab
commit 44ee51f22f
5 changed files with 69 additions and 3 deletions

View File

@ -180,6 +180,13 @@ flags:
mismatches by adjusting package @filepath{info.rkt} files (which makes
sense only for packages that are installed as links).}
@item{@DFlag{unused-pkg-deps} --- attempt to report dependencies that
are declared but are unused. Beware that some package dependencies
may be intentionally unused (e.g., declared to force installation of
other packages as a convenience), and beware that package
dependencies may be reported as unused only because compilation of
relevant modules has been suppressed.}
@item{@DFlag{mode} @nonterm{mode} --- use a @filepath{.zo} compiler
other than the default compiler, and put the resulting
@filepath{.zo} files in a subdirectory (of the usual place) named

View File

@ -68,6 +68,7 @@
(define-flag-param make-doc-index #f)
(define-flag-param check-dependencies #t)
(define-flag-param fix-dependencies #f)
(define-flag-param check-unused-dependencies #f)
(define-flag-param call-install #t)
(define-flag-param call-post-install #t)
(define-flag-param pause-on-errors #f)

View File

@ -26,13 +26,15 @@
coll-main?s
coll-modes
setup-printf setup-fprintf
fix? verbose?)
check-unused? fix? verbose?)
;; Tables
(define missing (make-hash))
(define skip-pkgs (make-hash))
(define pkg-internal-deps (make-hash)) ; dependencies available for a package's own use
(define pkg-immediate-deps (make-hash)) ; save immediate dependencies
(define pkg-external-deps (make-hash)) ; dependencies made available though `implies'
(define pkg-actual-deps (make-hash)) ; found dependencies (when checking for unused)
(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 path-cache (make-hash))
@ -131,6 +133,8 @@
(set-add runtime-deps
'core))
pkg))
(when check-unused?
(hash-set! pkg-implies pkg implies))
(values deps implies))
;; ----------------------------------------
@ -184,7 +188,11 @@
(let ([imm-depss (hash-ref pkg-immediate-deps pkg)])
(hash-set! pkg-internal-deps
pkg
(map flatten imm-depss)))
(map flatten imm-depss))
(when check-unused?
(hash-set! pkg-actual-deps
pkg
(map (lambda (ignored) (make-hash)) imm-depss))))
(when verbose?
(define (make-list s)
(apply
@ -196,7 +204,7 @@
" declared accesses, counting `implies'\n"
" for package: ~s\n"
" packages:~a\n"
" packages for build:~a")
" packages for build:~a\n")
pkg
(make-list (car (hash-ref pkg-internal-deps pkg)))
(make-list (cadr (hash-ref pkg-internal-deps pkg)))))))
@ -205,6 +213,11 @@
;; Check use of `src-pkg' (in `mode') from `pkg':
(define (check-dep! pkg src-pkg mode)
(define flat-depss (hash-ref pkg-internal-deps pkg))
(when check-unused?
(define actual-depss (hash-ref pkg-actual-deps pkg))
(hash-set! (if (eq? mode 'run) (car actual-depss) (cadr actual-depss))
src-pkg
#t))
(or (set-member? (if (eq? mode 'run)
(car flat-depss)
(cadr flat-depss))
@ -382,6 +395,47 @@
(check-mod! mod 'build pkg f dir)))))
;; Treat all (direct) documentation links as 'build mode:
(register-or-check-docs #t pkg path coll-main?))))
(when check-unused?
(for ([(pkg actuals) (in-hash pkg-actual-deps)])
(define availables (hash-ref pkg-internal-deps pkg))
(define unused
(for/hash ([actual (in-list actuals)]
[available (in-list availables)]
[mode '(run build)]
#:when #t
[i (in-set available)]
#:unless (or (equal? i pkg)
(equal? i core-pkg)
(equal? i 'core)
(hash-ref actual i #f)
;; If `i` is implied, then there's a
;; good reason for the dependency.
(set-member? (hash-ref pkg-implies pkg (set)) i)
;; If `i' is implied by a package
;; that is used directly, then there's
;; no way around the dependency, so don't
;; report it.
(for/or ([a (in-hash-keys actual)])
(set-member? (hash-ref pkg-implies a (set)) i))))
;; note that 'build override 'run
(values i mode)))
(unless (zero? (hash-count unused))
(setup-fprintf (current-error-port) #f
(apply
string-append
"unused dependenc~a detected\n"
" for package: ~s\n"
" on package~a:"
(for/list ([(i mode) (in-hash unused)])
(format "\n ~s~a"
i
(if (eq? mode 'run)
" for run"
""))))
(if (= (hash-count unused) 1) "y" "ies")
pkg
(if (= (hash-count unused) 1) "" "s")))))
;; Report result summary and (optionally) repair:
(unless (zero? (hash-count missing))

View File

@ -83,6 +83,9 @@
[("--fix-pkg-deps") "Auto-repair package-dependency declarations"
(add-flags '((check-dependencies #t)
(fix-dependencies #t)))]
[("--unused-pkg-deps") "Check for unused package-dependency declarations"
(add-flags '((check-dependencies #t)
(check-unused-dependencies #t)))]
[("--mode") mode "Select a compilation mode"
(add-flags `((compile-mode ,mode)))]
[("-v" "--verbose") "See names of compiled files and info printfs"

View File

@ -1723,6 +1723,7 @@
'build
'run))))
setup-printf setup-fprintf
(check-unused-dependencies)
(fix-dependencies)
(verbose))
(set! exit-code 1)))