aboutsummaryrefslogtreecommitdiff
path: root/pypi/package.scm
diff options
context:
space:
mode:
Diffstat (limited to 'pypi/package.scm')
-rw-r--r--pypi/package.scm524
1 files changed, 524 insertions, 0 deletions
diff --git a/pypi/package.scm b/pypi/package.scm
new file mode 100644
index 0000000..8cae990
--- /dev/null
+++ b/pypi/package.scm
@@ -0,0 +1,524 @@
+(define-module (pypi package)
+ #:use-module (logging logger)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-9)
+ #:use-module (srfi srfi-13)
+ #:use-module (ice-9 match)
+ #:use-module (ice-9 receive)
+ #:use-module (ice-9 hash-table)
+ #:use-module (ice-9 pretty-print)
+ #:use-module (guix ui)
+ #:use-module (guix records)
+ #:use-module (guix combinators)
+ #:use-module (guix packages)
+ #:use-module (gnu packages zip)
+ #:use-module (guix build-system python)
+ #:use-module (guix download)
+ #:use-module (pypi sdist)
+ #:use-module (pypi sdist-store)
+ #:use-module (pypi requirement)
+ #:use-module (pypi dependency-solver)
+ #:use-module (pypi build-configuration-graph)
+ #:use-module (guix utils)
+ #:export (<package-configuration>
+ package-configuration
+ package-configuration?
+ package-configuration-fix-function
+ package-configuration-python
+
+ requirement->package
+ requirements->package
+
+ build-configuration-graph->packages
+
+ unsatisfiable-requirements
+ unsatisfiable-requirements?
+ get-unsatisfiable-requirements))
+
+(define-record-type <package-configuration>
+ (package-configuration python fix-function)
+ package-configuration?
+ (python package-configuration-python)
+ (fix-function package-configuration-fix-function))
+
+(define-record-type <unsatisfiable-requirements>
+ (unsatisfiable-requirements requirements)
+ unsatisfiable-requirements?
+ (requirements get-unsatisfiable-requirements))
+
+(define (requirement-options->package
+ graph
+ package-configuration
+ requirement-options-to-versions
+ requirement-options)
+ (let
+ ((build-configuration
+ (select-build-configuration-from-requirement-options
+ requirement-options
+ (or
+ (hash-ref
+ requirement-options-to-versions
+ requirement-options)
+ (begin
+ (log-build-configuration-graph 'ERROR graph)
+ (error "Missing version error for " requirement-options))))))
+ (receive (native-inputs inputs propagated-inputs)
+ (get-inputs
+ graph
+ package-configuration
+ requirement-options-to-versions
+ (hash-ref
+ (build-configuration-graph-edges graph)
+ build-configuration))
+ (build-configuration->package
+ package-configuration
+ (if (string-contains
+ (origin-uri
+ (sdist-source
+ (build-configuration-sdist build-configuration)))
+ ".zip")
+ (cons
+ `("unzip" ,unzip)
+ native-inputs)
+ native-inputs)
+ inputs
+ propagated-inputs
+ build-configuration))))
+
+(define (debug-package package)
+ (log-msg 'DEBUG (package-name package) "@" (package-version package))
+ (log-msg 'DEBUG (package-native-inputs package))
+ (log-msg 'DEBUG (package-inputs package))
+ (log-msg 'DEBUG (package-propagated-inputs package))
+ (if (null? (origin-patch-guile (package-source package)))
+ (error "null patch-guile in own package"))
+ package)
+
+(define build-configuration->package
+ (memoize
+ (lambda (package-configuration
+ native-inputs
+ inputs
+ propagated-inputs
+ build-configuration)
+ (let*
+ ((sdist (build-configuration-sdist build-configuration))
+ (info (sdist-info sdist)))
+ ((package-configuration-fix-function package-configuration)
+ sdist
+ (package
+ (name (generate-package-name build-configuration))
+ (version (pkg-info-version info))
+ (source (sdist-source sdist))
+ (build-system python-build-system)
+ (native-inputs native-inputs)
+ (inputs inputs)
+ (propagated-inputs propagated-inputs)
+ (arguments
+ (append
+ `(#:python ,(package-configuration-python
+ package-configuration))
+ (if (build-configuration-tests? build-configuration)
+ '()
+ `(#:tests? #f))))
+ (home-page "")
+ (synopsis "")
+ (description (pkg-info-name info))
+ (license "")
+ (properties
+ `((sdist . ,sdist)
+ (build-configuration . ,build-configuration)))))))))
+
+(define (select-build-configuration-from-requirement-options
+ requirement-options
+ version)
+ (or
+ (find
+ (lambda (build-configuration)
+ (equal?
+ version
+ (pkg-info-version
+ (sdist-info
+ (build-configuration-sdist build-configuration)))))
+ (requirement-options-options requirement-options))
+ (begin
+ (log-msg 'ERROR "select-build-configuration-from-requirement-options")
+ (log-msg 'ERROR "version: " version)
+ (for-each
+ (lambda (build-configuration)
+ (let
+ ((info (sdist-info (build-configuration-sdist build-configuration))))
+ (log-msg 'ERROR " " (pkg-info-name info) "@" (pkg-info-version info))))
+ (requirement-options-options requirement-options))
+ (error "Could not find build-configuration matching version"))))
+
+(define (get-inputs
+ graph
+ package-configuration
+ requirement-options-to-versions
+ input-requirement-options)
+ (apply
+ values
+ (map
+ (lambda (type)
+ (packages->input-list
+ (map
+ (lambda (requirement-options)
+ (requirement-options->package
+ graph
+ package-configuration
+ requirement-options-to-versions
+ requirement-options))
+ (filter
+ (lambda (requirement-options)
+ (eq? type (requirement-options-type requirement-options)))
+ input-requirement-options))))
+ '(build test install))))
+
+(define (build-configuration-graph->packages
+ graph
+ package-configuration
+ requirement-options-to-versions)
+ (map
+ (lambda (requirement-options)
+ (requirement-options->package
+ graph
+ package-configuration
+ requirement-options-to-versions
+ requirement-options))
+ (build-configuration-graph-roots graph)))
+
+(define (requirements->packages
+ package-configuration
+ sdist-store
+ requirements)
+ (let
+ ((build-configuration-graph
+ (requirements->build-configuration-graph
+ sdist-store
+ requirements)))
+ (log-build-configuration-graph 'DEBUG build-configuration-graph)
+ (let
+ ((requirement-options-to-versions
+ (solve-build-configuration-graph
+ build-configuration-graph)))
+ (build-configuration-graph->packages
+ build-configuration-graph
+ package-configuration
+ requirement-options-to-versions))))
+
+(define (requirement->package
+ package-configuration
+ sdist-store
+ requirement)
+ (first
+ (requirements->packages
+ package-configuration
+ sdist-store
+ (list requirement))))
+
+(define (memorise-input->package f)
+ (lambda args
+ (let*
+ ((key (list-head args 4))
+ (store (second key))
+ (store-list (get-sdist-list store)))
+
+ (list-set! key 1 store-list)
+ (let
+ ((results (hash-ref input->package-cache key)))
+ (if results
+ (apply values results)
+ (let ((results (call-with-values (lambda ()
+ (apply f args))
+ list)))
+ (hash-set! input->package-cache key results)
+ (apply values results)))))))
+
+(define (get-inputs-for-input-requirements ipt dependants dependant-requirements store)
+ (receive (native-input-requirements
+ input-requirements
+ propagated-input-requirements)
+ (partition-requirements ipt)
+ (let*
+ ((new-dependant-requirements
+ (apply
+ lset-adjoin
+ (append
+ (list equal? propagated-input-requirements)
+ dependant-requirements)))
+ (native-input-sdists-and-extras
+ (get-inputs-for-requirements
+ store
+ native-input-requirements
+ new-dependant-requirements))
+ (input-sdists-and-extras
+ (get-inputs-for-requirements
+ store
+ input-requirements
+ new-dependant-requirements))
+ (propagated-input-sdists-and-extras
+ (get-inputs-for-requirements
+ store
+ propagated-input-requirements
+ new-dependant-requirements))
+ (unsatisfied-requirements
+ (filter
+ requirement?
+ (append native-input-sdists-and-extras
+ input-sdists-and-extras
+ propagated-input-sdists-and-extras))))
+ (values
+ native-input-sdists-and-extras
+ input-sdists-and-extras
+ propagated-input-sdists-and-extras
+ unsatisfied-requirements
+ new-dependant-requirements))))
+
+(define (get-input-inputs ipt fix-function python dependants dependant-requirements store)
+ (receive (native-input-inputs
+ input-inputs
+ propagated-input-inputs
+ unsatisfied-requirements
+ new-dependant-requirements)
+ (get-inputs-for-input-requirements ipt dependants dependant-requirements store)
+ (if (not (null? unsatisfied-requirements))
+ (throw
+ unsatisfied-requirements-error
+ unsatisfied-requirements)
+ (values
+ ; native-inputs
+ (append
+ (packages->input-list
+ (input-requirements->packages
+ ipt
+ fix-function
+ python
+ dependants
+ new-dependant-requirements
+ native-input-inputs
+ store
+ #f))
+ (if (string-contains
+ (origin-uri (sdist-source (input-sdist ipt)))
+ ".zip")
+ `(("unzip" ,unzip))
+ '()))
+ ; inputs
+ (packages->input-list
+ (input-requirements->packages
+ ipt
+ fix-function
+ python
+ dependants
+ new-dependant-requirements
+ input-inputs
+ store
+ #t))
+ ; propagated-inputs
+ (packages->input-list
+ (input-requirements->packages
+ ipt
+ fix-function
+ python
+ dependants
+ new-dependant-requirements
+ propagated-input-inputs
+ store
+ #t))))))
+
+(define (generate-package-name build-configuration)
+ (let* ((sd (build-configuration-sdist build-configuration))
+ (info (sdist-info sd)))
+ (string-append
+ (pkg-info-name info)
+ (if (null? (build-configuration-extras build-configuration))
+ ""
+ (string-append
+ "+"
+ (string-join
+ (stable-sort
+ (build-configuration-extras build-configuration)
+ string<?)
+ "+")))
+ (if (build-configuration-tests? build-configuration)
+ ""
+ "-no-tests"))))
+
+(define input->package
+ (memorise-input->package
+ ; A package is a function of an sdist, and a list of extras
+ ; dependants is used to detect dependency loops
+ (lambda (ipt store fix-function python dependants dependant-requirements)
+ (let* ((sd (input-sdist ipt))
+ (info (sdist-info sd)))
+ (receive (native-inputs
+ inputs
+ propagated-inputs)
+ (get-input-inputs ipt fix-function python dependants dependant-requirements store)
+ (fix-function
+ sd
+ (package
+ (name (generate-package-name ipt))
+ (version (pkg-info-version info))
+ (source (sdist-source sd))
+ (build-system python-build-system)
+ (native-inputs native-inputs)
+ (inputs inputs)
+ (propagated-inputs propagated-inputs)
+ (arguments
+ (append
+ `(#:python ,python)
+ (if (input-run-tests ipt)
+ '()
+ `(#:tests? #f))))
+ (home-page "")
+ (synopsis "")
+ (description (pkg-info-name info))
+ (license "")
+ (properties
+ `((sdist . ,sd))))))))))
+
+(define (input-requirement->package ipt req-input fix-function python dependants dependant-requirements store)
+ (if (not (dependency-loop? req-input dependants))
+ ; no dependency loop detected, so just continue
+ (input->package
+ req-input
+ store
+ fix-function
+ python
+ (cons
+ ipt
+ dependants)
+ dependant-requirements)
+ ; this package is a dependency on req-input already, so try
+ ; building it without tests
+ (let
+ ((req-input-no-tests
+ (input
+ (inherit req-input)
+ (run-tests #f))))
+ (if (not (dependency-loop? req-input-no-tests dependants))
+ (input->package
+ req-input-no-tests
+ store
+ fix-function
+ python
+ (cons
+ ipt
+ dependants)
+ dependant-requirements)
+ ; Building without tests has been tried already, and the loop is
+ ; still present, so try omitting the dependency entirely
+ (let
+ ((req-input-no-tests-no-prop
+ (input
+ (inherit req-input)
+ (run-tests #f)
+ (allow-missing-dependencies
+ ; Add the current input, but without the allow-missing-dependencies
+ ; as this is not relevant when deciding what to ignore
+ (cons (input
+ (inherit ipt)
+ (allow-missing-dependencies '()))
+ (input-allow-missing-dependencies ipt))))))
+ (if (and
+ (not (dependency-loop?
+ req-input-no-tests-no-prop dependants))
+ (not (> (length (input-allow-missing-dependencies ipt)) 4)))
+ (input->package
+ req-input-no-tests-no-prop
+ store
+ fix-function
+ python
+ (cons
+ ipt
+ dependants)
+ dependant-requirements)
+ (begin
+ (explain-dependency-loop ipt dependants req-input)
+ (error "dependency loop detected\n"))))))))
+
+(define (inputs-equivalent? ipt1 ipt2)
+ (let*
+ ((sd1 (input-sdist ipt1))
+ (sd2 (input-sdist ipt2))
+ (info1 (sdist-info sd1))
+ (info2 (sdist-info sd2)))
+ (and
+ (equal? (pkg-info-name info1)
+ (pkg-info-name info2))
+ (equal? (pkg-info-version info1)
+ (pkg-info-version info2)))))
+
+(define (input-requirements->packages ipt fix-function python dependants dependant-requirements inputs store do-filter)
+ (log-msg 'DEBUG "input-requirements->packages " (input-short-description ipt))
+ (map
+ (lambda (requirement-ipt)
+ (input-requirement->package ipt
+ requirement-ipt
+ fix-function
+ python
+ dependants
+ dependant-requirements
+ store))
+ ; Filter out any dependencies that are being ignored for this input.
+ ; If this happens, this is a partial package, being used to break a dependency
+ ; loop.
+ (if do-filter
+ (filter
+ (lambda (requirement-ipt)
+ (let*
+ ((allow-missing (input-allow-missing-dependencies ipt))
+ (result
+ (not
+ (find
+ (lambda (ipt2)
+ (inputs-equivalent? requirement-ipt ipt2))
+ allow-missing))))
+ (log-msg 'DEBUG
+ "considering "
+ (input-short-description requirement-ipt)
+ " in " (map input-short-description allow-missing) " " result)
+ result))
+ inputs)
+ inputs)))
+
+(define input->package-cache (make-hash-table))
+
+(define (packages->input-list packages)
+ (map (lambda (package)
+ (list (package-name package) package))
+ packages))
+
+(define (dependency-loop? ipt dependants)
+ (member ipt dependants))
+
+(define (partition-requirements ipt)
+ ; propagated-inputs-requirements is the install_requirements
+ ; native-inputs is the
+ ; (differece
+ ; (union
+ ; build requirements
+ ; test requirements)
+ ; install_requirements)
+ (let*
+ ((sd (input-sdist ipt))
+ (propagated-input-requirements
+ (filter-requirements-by-extras
+ (sdist-install-requires sd)
+ (input-extras ipt)))
+ (native-input-requirements
+ (lset-difference
+ equal?
+ (lset-union
+ equal?
+ (sdist-build-requires sd)
+ (if (input-run-tests ipt)
+ (sdist-tests-require sd)
+ '()))
+ propagated-input-requirements)))
+ (values
+ native-input-requirements
+ '()
+ propagated-input-requirements)))