build-system: Add pyproject-build-system.
[jackhill/guix/guix.git] / guix / build-system / pyproject.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2021 Lars-Dominik Braun <lars@6xq.net>
3 ;;; Copyright © 2022 Marius Bakke <marius@gnu.org>
4 ;;;
5 ;;; This file is part of GNU Guix.
6 ;;;
7 ;;; GNU Guix is free software; you can redistribute it and/or modify it
8 ;;; under the terms of the GNU General Public License as published by
9 ;;; the Free Software Foundation; either version 3 of the License, or (at
10 ;;; your option) any later version.
11 ;;;
12 ;;; GNU Guix is distributed in the hope that it will be useful, but
13 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;;; GNU General Public License for more details.
16 ;;;
17 ;;; You should have received a copy of the GNU General Public License
18 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19
20 (define-module (guix build-system pyproject)
21 #:use-module ((gnu packages) #:select (search-auxiliary-file))
22 #:use-module (guix gexp)
23 #:use-module (guix store)
24 #:use-module (guix utils)
25 #:use-module (guix memoization)
26 #:use-module (guix gexp)
27 #:use-module (guix monads)
28 #:use-module (guix packages)
29 #:use-module (guix derivations)
30 #:use-module (guix search-paths)
31 #:use-module (guix build-system)
32 #:use-module (guix build-system gnu)
33 #:use-module (guix build-system python)
34 #:use-module (ice-9 match)
35 #:use-module (srfi srfi-1)
36 #:use-module (srfi srfi-26)
37 #:export (%pyproject-build-system-modules
38 default-python
39 pyproject-build
40 pyproject-build-system))
41
42 ;; Commentary:
43 ;;
44 ;; Standard build procedure for Python packages using 'pyproject.toml'.
45 ;; This is implemented as an extension of 'python-build-system'.
46 ;;
47 ;; Code:
48
49 (define %pyproject-build-system-modules
50 ;; Build-side modules imported by default.
51 `((guix build pyproject-build-system)
52 (guix build json)
53 ,@%python-build-system-modules))
54
55 (define (default-python)
56 "Return the default Python package."
57 ;; Lazily resolve the binding to avoid a circular dependency.
58 (let ((python (resolve-interface '(gnu packages python))))
59 (module-ref python 'python-toolchain)))
60
61 (define sanity-check.py
62 ;; TODO: Merge with sanity-check.py in the next rebuild cycle.
63 (search-auxiliary-file "python/sanity-check-next.py"))
64
65 (define* (lower name
66 #:key source inputs native-inputs outputs system target
67 (python (default-python))
68 #:allow-other-keys
69 #:rest arguments)
70 "Return a bag for NAME."
71 (define private-keywords
72 '(#:target #:python #:inputs #:native-inputs))
73
74 (and (not target) ;XXX: no cross-compilation
75 (bag
76 (name name)
77 (system system)
78 (host-inputs `(,@(if source
79 `(("source" ,source))
80 '())
81 ,@inputs
82
83 ;; Keep the standard inputs of 'gnu-build-system'.
84 ,@(standard-packages)))
85 (build-inputs `(("python" ,python)
86 ("sanity-check.py" ,(local-file sanity-check.py))
87 ,@native-inputs))
88 (outputs (append outputs '(wheel)))
89 (build pyproject-build)
90 (arguments (strip-keyword-arguments private-keywords arguments)))))
91
92 (define* (pyproject-build name inputs
93 #:key source
94 (tests? #t)
95 (configure-flags ''())
96 (build-backend #f)
97 (test-backend #f)
98 (test-flags #f)
99 (phases '%standard-phases)
100 (outputs '("out" "wheel"))
101 (search-paths '())
102 (system (%current-system))
103 (guile #f)
104 (imported-modules %pyproject-build-system-modules)
105 (modules '((guix build pyproject-build-system)
106 (guix build utils))))
107 "Build SOURCE using PYTHON, and with INPUTS."
108 (define build
109 (with-imported-modules imported-modules
110 #~(begin
111 (use-modules #$@(sexp->gexp modules))
112
113 #$(with-build-variables inputs outputs
114 #~(pyproject-build
115 #:name #$name
116 #:source #+source
117 #:configure-flags #$configure-flags
118 #:system #$system
119 #:build-backend #$build-backend
120 #:test-backend #$test-backend
121 #:test-flags #$test-flags
122 #:tests? #$tests?
123 #:phases #$(if (pair? phases)
124 (sexp->gexp phases)
125 phases)
126 #:outputs %outputs
127 #:search-paths '#$(sexp->gexp
128 (map search-path-specification->sexp
129 search-paths))
130 #:inputs %build-inputs)))))
131
132
133 (mlet %store-monad ((guile (package->derivation (or guile (default-guile))
134 system #:graft? #f)))
135 (gexp->derivation name build
136 #:system system
137 #:graft? #f ;consistent with 'gnu-build'
138 #:target #f
139 #:guile-for-build guile)))
140
141 (define pyproject-build-system
142 (build-system
143 (name 'pyproject)
144 (description "The PEP517-compliant Python build system")
145 (lower lower)))
146
147 ;;; pyproject.scm ends here