From 69766f740fef60b561e460e16adda4d506d4eec9 Mon Sep 17 00:00:00 2001 From: Jan Moringen Date: Mon, 20 May 2019 19:27:30 +0200 Subject: [PATCH] Fast dependency resolution in src/{analysis,model/project}/*.lisp * src/analysis/dependencies.lisp (dependency-key): new function; construct an index key for a dependency (make-provider-index): new function; make a provider index for fast lookup (index-provider!): new function; add a provider to the index (lookup-providers): new function; find providers for a requirement in an indexed table * src/analysis/package.lisp (package jenkins.analysis): added exported symbols dependency-key, make-provider-index, index-provider! and find-providers * src/model/project/variables.lisp (find-provider/version): use new `lookup-providers' function; adapted to unified provider representation * src/model/project/classes-model.lisp (index-platform-provides): new function; make an index for the platform provides of a version (add-dependencies! version): use `index-platform-provides' * src/model/project/classes-spec.lisp (instantiate distribution-spec): use new index provider machinery to build per-distribution provider index and use it to resolve dependencies --- src/analysis/dependencies.lisp | 20 ++++++++++++++++++++ src/analysis/package.lisp | 7 ++++++- src/model/project/classes-model.lisp | 14 +++++++++++--- src/model/project/classes-spec.lisp | 25 +++++++++++++------------ src/model/project/variables.lisp | 11 +++-------- 5 files changed, 53 insertions(+), 24 deletions(-) diff --git a/src/analysis/dependencies.lisp b/src/analysis/dependencies.lisp index 5f443d7a..3ea7fb01 100644 --- a/src/analysis/dependencies.lisp +++ b/src/analysis/dependencies.lisp @@ -46,3 +46,23 @@ current))))) dependencies) (hash-table-values seen))) + +;;; Indexed dependencies + +(defun dependency-key (dependency) + (let+ (((nature target &optional version) dependency)) + (cond + ((eq nature :cmake) (list nature (string-downcase target))) + (version (list nature target)) + (t dependency)))) + +(defun make-provider-index () + (make-hash-table :test #'equal)) + +(defun index-provider! (provided provider index) + (let ((key (jenkins.analysis:dependency-key provided))) + (push (cons provided provider) (gethash key index '())) + index)) + +(defun lookup-providers (required index) + (gethash (dependency-key required) index)) diff --git a/src/analysis/package.lisp b/src/analysis/package.lisp index 876e38cd..b40ee5d3 100644 --- a/src/analysis/package.lisp +++ b/src/analysis/package.lisp @@ -46,7 +46,12 @@ #:dependency-matches? #:merge-dependencies - #:effective-requires) + #:effective-requires + + #:dependency-key + #:make-provider-index + #:index-provider! + #:lookup-providers) ;; Analysis protocol (:export diff --git a/src/model/project/classes-model.lisp b/src/model/project/classes-model.lisp index 02aaa464..2f2abca6 100644 --- a/src/model/project/classes-model.lisp +++ b/src/model/project/classes-model.lisp @@ -310,6 +310,13 @@ (defmethod direct-platform-dependencies ((thing version)) (map 'list #'car (%direct-platform-dependencies thing))) +(defun index-platform-provides (thing) + (with-simple-restart (continue "~@") + (reduce (lambda+ (index (dependency . provider)) + (jenkins.analysis:index-provider! dependency provider index)) + (platform-provides thing) + :initial-value (jenkins.analysis:make-provider-index)))) + (defmethod model:add-dependencies! ((thing version) &key (providers (missing-required-argument :providers))) @@ -318,7 +325,7 @@ (if platform-provides? platform-provides (setf platform-provides? t - platform-provides (platform-provides thing))))) + platform-provides (index-platform-provides thing))))) ((&flet add-dependency (required provider) (let ((cell (or (assoc provider (%direct-dependencies thing)) (let ((new (cons provider '()))) @@ -344,8 +351,9 @@ (add-dependency requires match)) t)) ;; Search in platform-provided features. - ((when-let ((provider (find-provider/version - requires (platform-provides)))) + ((when-let* ((platform-provides (platform-provides)) + (provider (find-provider/version + requires platform-provides))) (typecase provider (platform-dependency (add-platform-dependency requires provider)) diff --git a/src/model/project/classes-spec.lisp b/src/model/project/classes-spec.lisp index 0b990ce0..caf8b7e4 100644 --- a/src/model/project/classes-spec.lisp +++ b/src/model/project/classes-spec.lisp @@ -135,20 +135,21 @@ ;; and transitive) with include contexts. (versions (append (mapcan #'make-version (direct-versions spec)) (mappend #'one-distribution-include - (direct-includes spec)))) - (providers (make-hash-table :test #'equal))) + (direct-includes spec))))) ;; Build a table of provided things and providers. - (map nil (lambda (version) - (map nil (lambda (provided) - (push version (gethash provided providers '()))) - (provides version))) - versions) - - ;; After all `version' instances have been made, resolve - ;; dependencies among them. - (let ((providers (hash-table-alist providers))) - (map nil (rcurry #'model:add-dependencies! :providers providers) versions)) + (let ((provider-index (jenkins.analysis:make-provider-index))) + (map nil (lambda (version) + (map nil (rcurry #'jenkins.analysis:index-provider! + version provider-index) + (provides version))) + versions) + + ;; After all `version' instances have been made, resolve + ;; dependencies among them. + (map nil (rcurry #'model:add-dependencies! :providers provider-index) + versions)) + (reinitialize-instance distribution :versions versions))) ;;; `project-spec' class diff --git a/src/model/project/variables.lisp b/src/model/project/variables.lisp index c313ea20..5fc7b480 100644 --- a/src/model/project/variables.lisp +++ b/src/model/project/variables.lisp @@ -72,20 +72,15 @@ (version-better (third (car left)) (third (car right))))) ;; Find providers in PROVIDERS which can provide the required ;; NATURE, TARGET and VERSION of SPEC. + (candidates (jenkins.analysis:lookup-providers required providers)) (candidates (remove-if-not (curry #'jenkins.analysis:dependency-matches? required) - providers :key #'car)) + candidates :key #'car)) ;; Sort CANDIDATES according to PROVIDER-BETTER (which may ;; use ORDER). (candidates (stable-sort candidates #'provider-better))) ;; Take the best candidate or act according to IF-DOES-NOT-EXIST. - (or (when-let ((providers (cdr (first candidates)))) - (cond ((eq providers :system-package) - providers) - ((typep providers 'platform-dependency) - providers) - (t - (first providers)))) + (or (cdr (first candidates)) (error-behavior-restart-case (if-does-not-exist (jenkins.analysis:unfulfilled-project-dependency-error