aboutsummaryrefslogtreecommitdiff
path: root/pypi/dependency-solver.scm
diff options
context:
space:
mode:
Diffstat (limited to 'pypi/dependency-solver.scm')
-rw-r--r--pypi/dependency-solver.scm475
1 files changed, 475 insertions, 0 deletions
diff --git a/pypi/dependency-solver.scm b/pypi/dependency-solver.scm
new file mode 100644
index 0000000..b4aa1f2
--- /dev/null
+++ b/pypi/dependency-solver.scm
@@ -0,0 +1,475 @@
+(define-module (pypi dependency-solver)
+ #:use-module (ice-9 match)
+ #:use-module (ice-9 receive)
+ #:use-module (ice-9 hash-table)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-9)
+ #:use-module (logging logger)
+ #:use-module (pyguile)
+ #:use-module (pypi requirement)
+ #:use-module (pypi sdist)
+ #:use-module (pypi sdist-store)
+ #:use-module (pypi build-configuration-graph)
+ #:use-module (pypi package)
+ #:export (generate-solver-hash-tables
+ solve-build-configuration-graph
+ get-scoped-releases-to-versions
+
+ release
+ release?
+ release-name
+ release-version
+
+ root-scope
+
+ scope
+ scope?
+ scope-release
+ scope-parent
+
+ scoped-release
+ scoped-release?
+ scoped-release-release
+ scoped-release-scope
+
+ scoped-requirement
+ scoped-requirement?
+ scopred-requirement-requirement
+ scopred-requirement-scope))
+
+(define root-scope '())
+
+(define-record-type <release>
+ (release name version)
+ release?
+ (name release-name)
+ (version release-version))
+
+(define (release->string r)
+ (string-join
+ (list
+ (normalise-requirement-name (release-name r))
+ "@"
+ (release-version r))
+ ""))
+
+(define-record-type <scope>
+ (scope release parent)
+ scope?
+ (release scope-release)
+ (parent scope-parent))
+
+(define (scope->string s)
+ (if (null? s)
+ ""
+ (string-join
+ (list
+ "("
+ (release->string (scope-release s))
+ (if (null? (scope-parent s))
+ ""
+ (string-join
+ (list
+ " -> "
+ (scope->string
+ (scope-parent s)))
+ ""))
+ ")")
+ "")))
+
+(define (scope-tree-contains-release? r s)
+ (if (null? s)
+ #f
+ (or
+ (equal?
+ (scope-release s)
+ r)
+ (scope-tree-contains-release?
+ r
+ (scope-parent s)))))
+
+(define-record-type <scoped-release>
+ (scoped-release release scope)
+ scoped-release?
+ (release scoped-release-release)
+ (scope scoped-release-scope))
+
+(define-record-type <scoped-build-configuration>
+ (scoped-release release scope)
+ scoped-release?
+ (release scoped-release-release)
+ (scope scoped-release-scope))
+
+(define (scoped-release->string sr)
+ (string-join
+ (list
+ (release->string
+ (scoped-release-release sr))
+ (if (null? (scoped-release-scope sr))
+ ""
+ (string-join
+ (list
+ " "
+ (scope->string
+ (scoped-release-scope sr)))
+ "")))
+ ""))
+
+(define-record-type <scoped-requirement>
+ (scoped-requirement requirement scope)
+ scoped-requirement?
+ (requirement scoped-requirement-requirement)
+ (scope scoped-requirement-scope))
+
+(define (scoped-requirement->solver-package-name sr)
+ (string-append
+ (normalise-requirement-name
+ (requirement-name
+ (scoped-requirement-requirement sr)))
+ (if (null? (scoped-requirement-scope sr))
+ ""
+ (string-append
+ " "
+ (scope->string
+ (scoped-requirement-scope sr))))))
+
+(define (scoped-release->solver-package-name sr)
+ (string-append
+ (normalise-requirement-name
+ (release-name (scoped-release-release sr)))
+ (if (null? (scoped-release-scope sr))
+ ""
+ (string-append
+ " "
+ (scope->string
+ (scoped-release-scope sr))))))
+
+(define (scoped-build-configuration->solver-name sr)
+ (string-append
+ (normalise-requirement-name
+ (release-name (scoped-release-release sr)))
+ (if (null? (scoped-release-scope sr))
+ ""
+ (string-append
+ " "
+ (scope->string
+ (scoped-release-scope sr))))))
+
+(define (scoped-requirement->solver-output-string sr)
+ (string-append
+ (normalise-requirement-name
+ (requirement-name
+ (scoped-requirement-requirement sr)))
+ (if (null? (scoped-requirement-scope sr))
+ ""
+ (string-append
+ " "
+ (scope->string
+ (scoped-requirement-scope sr))))))
+
+(define (scoped-requirement->string sr)
+ (string-append
+ (requirement->string
+ (scoped-requirement-requirement sr))
+ (if (null? (scoped-requirement-scope sr))
+ ""
+ (string-append
+ " "
+ (scope->string
+ (scoped-requirement-scope sr))))))
+
+(define (build-configuration->release build-configuration)
+ (let*
+ ((sdist
+ (build-configuration-sdist build-configuration))
+ (info (sdist-info sdist)))
+ (release
+ (pkg-info-name info)
+ (pkg-info-version info))))
+
+(define (process-build-configuration!
+ build-configuration-graph
+ scoped-releases-to-scoped-requirements
+ result-string-to-requirement-options
+ current-scope build-configuration)
+ (let*
+ ((requirement-options-list
+ (hash-ref
+ (build-configuration-graph-edges build-configuration-graph)
+ build-configuration))
+ (build-configuration-release
+ (build-configuration->release build-configuration))
+ (build-configuration-scoped-release
+ (scoped-release
+ build-configuration-release
+ current-scope))
+ (inner-scope
+ (scope
+ build-configuration-release
+ current-scope))
+ (requirement-options-and-scopes
+ (map
+ (lambda (requirement-options)
+ (let*
+ ((type
+ (requirement-options-type requirement-options))
+ (scope-for-requirement-options
+ (cond
+ ((member type '(() install))
+ current-scope)
+ ((member type '(test build))
+ inner-scope)
+ (else
+ (error "Unrecognised requirement type " type)))))
+ (cons requirement-options scope-for-requirement-options)))
+ requirement-options-list))
+ (scoped-requirements
+ (map
+ (match-lambda
+ ((requirement-options . scope)
+ (let
+ ((sr
+ (scoped-requirement
+ (requirement-options-requirement
+ requirement-options)
+ scope)))
+ (hash-set!
+ result-string-to-requirement-options
+ (scoped-requirement->solver-output-string sr)
+ (cons
+ requirement-options
+ (or
+ (hash-ref
+ result-string-to-requirement-options
+ (scoped-requirement->solver-output-string sr))
+ '())))
+ sr)))
+ requirement-options-and-scopes)))
+ (if (hash-ref
+ scoped-releases-to-scoped-requirements
+ build-configuration-scoped-release)
+ (begin
+ (log-msg 'DEBUG "Duplicate scoped-release " build-configuration-scoped-release)
+ (log-msg 'DEBUG "existing: " (hash-ref scoped-releases-to-scoped-requirements build-configuration-scoped-release))
+ (log-msg 'DEBUG "new: " scoped-requirements)
+ ;(error "Duplicate scoped-release") TODO: Check this
+ ))
+ (if (not (hash-ref scoped-releases-to-scoped-requirements
+ build-configuration-scoped-release))
+ (hash-set!
+ scoped-releases-to-scoped-requirements
+ build-configuration-scoped-release
+ scoped-requirements))
+ (for-each
+ (match-lambda
+ ((requirement-options . scope)
+ (for-each
+ (lambda (build-configuration)
+ (process-build-configuration!
+ build-configuration-graph
+ scoped-releases-to-scoped-requirements
+ result-string-to-requirement-options
+ scope
+ build-configuration))
+ (requirement-options-options requirement-options))))
+ requirement-options-and-scopes)))
+
+(define (generate-solver-hash-tables
+ build-configuration-graph)
+ (let
+ ((scoped-releases-to-scoped-requirements (make-hash-table))
+ (result-string-to-requirement-options (make-hash-table)))
+ (for-each
+ (lambda (requirement-options)
+ (let
+ ((solver-name
+ (normalise-requirement-name
+ (requirement-name (requirement-options-requirement requirement-options)))))
+ (hash-set!
+ result-string-to-requirement-options
+ solver-name
+ (cons
+ requirement-options
+ (or
+ (hash-ref
+ result-string-to-requirement-options
+ solver-name)
+ '())))
+ (for-each
+ (lambda (build-configuration)
+ (process-build-configuration!
+ build-configuration-graph
+ scoped-releases-to-scoped-requirements
+ result-string-to-requirement-options
+ root-scope
+ build-configuration))
+ (requirement-options-options requirement-options))))
+ (build-configuration-graph-roots build-configuration-graph))
+ (log-msg 'DEBUG
+ "generate-solver-hash-tables result \n"
+ (string-join
+ (hash-map->list
+ (lambda (k v)
+ (string-join
+ (cons
+ (scoped-release->string k)
+ (map
+ (lambda (sr)
+ (string-append
+ " "
+ (scoped-requirement->string sr)
+ "\n"))
+ v))
+ "\n"))
+ scoped-releases-to-scoped-requirements))
+ "\n")
+ (values
+ scoped-releases-to-scoped-requirements
+ result-string-to-requirement-options)))
+
+(define (scoped-releases-to-scoped-requirements->string-to-scoped-releases
+ ht)
+ (hash-map->list
+ (lambda (scoped-release scoped-requirements)
+ (cons
+ (scoped-release->solver-package-name scoped-release)
+ scoped-release))
+ ht))
+
+(define (solve-build-configuration-graph
+ build-configuration-graph)
+ (receive (scoped-releases-to-scoped-requirements
+ result-string-to-requirement-options)
+ (generate-solver-hash-tables
+ build-configuration-graph)
+ (let*
+ ((requirements
+ (map
+ (lambda (requirement-options)
+ (requirement-options-requirement requirement-options))
+ (build-configuration-graph-roots build-configuration-graph)))
+ (result
+ (get-versions-or-error
+ (hash-map->list
+ (lambda (scoped-release scoped-requirements)
+ (list
+ (scoped-release->solver-package-name scoped-release)
+ (release-version
+ (scoped-release-release scoped-release))
+ (map
+ (lambda (sr)
+ (list
+ (scoped-requirement->solver-package-name sr)
+ (filter
+ (lambda (s)
+ (not (equal? s "")))
+ (string-split
+ (requirement-specifiers
+ (scoped-requirement-requirement sr))
+ #\,))))
+ scoped-requirements)))
+ scoped-releases-to-scoped-requirements)
+ (concatenate
+ (map
+ (lambda (r)
+ (map
+ (lambda (s)
+ (string-append
+ (normalise-requirement-name (requirement-name r))
+ s))
+ (string-split
+ (requirement-specifiers r)
+ #\,)))
+ requirements)))))
+ (if (string? result)
+ (begin
+ (log-msg 'ERROR "Error during dependency resolution")
+ (hash-for-each
+ (lambda (s-release s-requirements)
+ (log-msg 'ERROR
+ (scoped-release->string s-release) ":")
+ (for-each
+ (lambda (s-requirement)
+ (log-msg 'ERROR " " (scoped-requirement->string s-requirement)))
+ s-requirements))
+ scoped-releases-to-scoped-requirements)
+ (log-msg 'ERROR "Requirements:")
+ (for-each
+ (lambda (r)
+ (log-msg 'ERROR " " (requirement->string r)))
+ requirements)
+ (error result))
+ (alist->hash-table
+ (concatenate
+ (map
+ (match-lambda
+ ((result-string version)
+ (log-msg 'DEBUG result-string " - " version)
+ (map
+ (lambda (requirement-options)
+ (cons requirement-options version))
+ (or
+ (hash-ref
+ result-string-to-requirement-options
+ result-string)
+ (begin
+ (log-msg 'ERROR "|" result-string "| not found")
+ #f)))))
+ result)))))))
+
+(define (select-build-configuration-from-requirement-options
+ requirement-options
+ version)
+ (find
+ (lambda (build-configuration)
+ (equal?
+ version
+ (pkg-info-version
+ (sdist-info
+ (build-configuration-sdist build-configuration)))))
+ (requirement-options-options requirement-options)))
+
+(define (filter-build-configuration-graph
+ graph
+ requirement-options-to-version)
+ (build-configuration-graph
+ (map
+ (lambda (requirement-options)
+ (select-build-configuration-from-requirement-options
+ requirement-options
+ (hash-ref
+ requirement-options-to-version
+ requirement-options)))
+ (build-configuration-graph-roots graph))
+ (alist->hash-table
+ (hash-map->list
+ (lambda (build-configuration requirement-options-list)
+ (cons
+ build-configuration
+ (map
+ (lambda (requirement-options)
+ (select-build-configuration-from-requirement-options
+ requirement-options
+ (hash-ref
+ requirement-options-to-version
+ requirement-options)))
+ requirement-options-list)))
+ (build-configuration-graph-edges graph)))))
+
+(define %solver-location "")
+
+(define (get-versions-or-error name-version-requirements overall-requirements)
+ (log-msg 'DEBUG "get-versions-or-error\n")
+ (if (not (string-null? %solver-location))
+ (python-eval (string-append "import sys; sys.path.append(\"" %solver-location "\");")))
+ (python-eval "from solver import get_versions_or_error")
+ (log-msg 'DEBUG "name-version-requirements\n")
+ (log-msg 'DEBUG name-version-requirements)
+ (log-msg 'DEBUG "\n")
+ (log-msg 'DEBUG "overall-requirements\n")
+ (log-msg 'DEBUG overall-requirements)
+ (log-msg 'DEBUG "\n")
+ (python-apply
+ '(solver get_versions_or_error)
+ (list name-version-requirements overall-requirements)
+ '()))