guix: python-build-system: Add option "#:use-setuptools?" (default true).
[jackhill/guix/guix.git] / guix / build / python-build-system.scm
CommitLineData
40506d5d 1;;; GNU Guix --- Functional package management for GNU
0beb65b4 2;;; Copyright © 2013, 2015, 2016 Ludovic Courtès <ludo@gnu.org>
7b96bf82 3;;; Copyright © 2013 Andreas Enge <andreas@enge.fr>
40506d5d 4;;; Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org>
3bacb7a6 5;;; Copyright © 2015 Mark H Weaver <mhw@netris.org>
7db40bce 6;;; Copyright © 2016 Hartmut Goebel <h.goebel@crazy-compilers.com>
40506d5d
NK
7;;;
8;;; This file is part of GNU Guix.
9;;;
10;;; GNU Guix is free software; you can redistribute it and/or modify it
11;;; under the terms of the GNU General Public License as published by
12;;; the Free Software Foundation; either version 3 of the License, or (at
13;;; your option) any later version.
14;;;
15;;; GNU Guix is distributed in the hope that it will be useful, but
16;;; WITHOUT ANY WARRANTY; without even the implied warranty of
17;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;;; GNU General Public License for more details.
19;;;
20;;; You should have received a copy of the GNU General Public License
21;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
22
23(define-module (guix build python-build-system)
b5b73a82 24 #:use-module ((guix build gnu-build-system) #:prefix gnu:)
40506d5d
NK
25 #:use-module (guix build utils)
26 #:use-module (ice-9 match)
27 #:use-module (ice-9 ftw)
28 #:use-module (srfi srfi-1)
29 #:use-module (srfi srfi-26)
30 #:export (%standard-phases
31 python-build))
32
33;; Commentary:
34;;
35;; Builder-side code of the standard Python package build procedure.
36;;
37;; Code:
38
46bcdcc2
MB
39(define setuptools-shim
40 ;; Run setup.py with "setuptools" being imported, which will patch
41 ;; "distutils". This is needed for packages using "distutils" instead of
42 ;; "setuptools" since the former does not understand the
43 ;; "--single-version-externally-managed" flag.
44 ;; Python code taken from pip 9.0.1 pip/utils/setuptools_build.py
45 (string-append
46 "import setuptools, tokenize;__file__='setup.py';"
47 "f=getattr(tokenize, 'open', open)(__file__);"
48 "code=f.read().replace('\\r\\n', '\\n');"
49 "f.close();"
50 "exec(compile(code, __file__, 'exec'))"))
40506d5d 51
5f7565d1 52(define (call-setuppy command params use-setuptools?)
b191f88e
AE
53 (if (file-exists? "setup.py")
54 (begin
55 (format #t "running \"python setup.py\" with command ~s and parameters ~s~%"
56 command params)
5f7565d1
HG
57 (if use-setuptools?
58 (zero? (apply system* "python" "-c" setuptools-shim
59 command params))
60 (zero? (apply system* "python" "./setup.py" command params))))
b191f88e
AE
61 (error "no setup.py found")))
62
5f7565d1 63(define* (build #:key use-setuptools? #:allow-other-keys)
b191f88e 64 "Build a given Python package."
5f7565d1 65 (call-setuppy "build" '() use-setuptools?))
b191f88e 66
5f7565d1 67(define* (check #:key tests? test-target use-setuptools? #:allow-other-keys)
842ded33 68 "Run the test suite of a given Python package."
7b96bf82 69 (if tests?
5f7565d1 70 (call-setuppy test-target '() use-setuptools?)
7b96bf82 71 #t))
842ded33 72
824af8ca 73(define (get-python-version python)
9f6509c6
MW
74 (let* ((version (last (string-split python #\-)))
75 (components (string-split version #\.))
76 (major+minor (take components 2)))
77 (string-join major+minor ".")))
824af8ca 78
5f7565d1 79(define* (install #:key outputs (configure-flags '()) use-setuptools?
b191f88e
AE
80 #:allow-other-keys)
81 "Install a given Python package."
82 (let* ((out (assoc-ref outputs "out"))
5f7565d1
HG
83 (params (append (list (string-append "--prefix=" out))
84 (if use-setuptools?
85 ;; distutils does not accept these flags
86 (list "--single-version-externally-managed"
87 "--root=/")
88 '())
7db40bce 89 configure-flags)))
5f7565d1 90 (call-setuppy "install" params use-setuptools?)))
b191f88e 91
3df47231 92(define* (wrap #:key inputs outputs #:allow-other-keys)
40506d5d
NK
93 (define (list-of-files dir)
94 (map (cut string-append dir "/" <>)
95 (or (scandir dir (lambda (f)
96 (let ((s (stat (string-append dir "/" f))))
97 (eq? 'regular (stat:type s)))))
98 '())))
99
100 (define bindirs
101 (append-map (match-lambda
102 ((_ . dir)
103 (list (string-append dir "/bin")
104 (string-append dir "/sbin"))))
105 outputs))
106
107 (let* ((out (assoc-ref outputs "out"))
3df47231 108 (python (assoc-ref inputs "python"))
40506d5d
NK
109 (var `("PYTHONPATH" prefix
110 ,(cons (string-append out "/lib/python"
824af8ca
AE
111 (get-python-version python)
112 "/site-packages")
40506d5d
NK
113 (search-path-as-string->list
114 (or (getenv "PYTHONPATH") ""))))))
115 (for-each (lambda (dir)
116 (let ((files (list-of-files dir)))
117 (for-each (cut wrap-program <> var)
118 files)))
119 bindirs)))
120
6f8fe4b2
FB
121(define* (rename-pth-file #:key name inputs outputs #:allow-other-keys)
122 "Rename easy-install.pth to NAME.pth to avoid conflicts between packages
123installed with setuptools."
124 (let* ((out (assoc-ref outputs "out"))
125 (python (assoc-ref inputs "python"))
126 (site-packages (string-append out "/lib/python"
127 (get-python-version python)
128 "/site-packages"))
129 (easy-install-pth (string-append site-packages "/easy-install.pth"))
130 (new-pth (string-append site-packages "/" name ".pth")))
131 (when (file-exists? easy-install-pth)
132 (rename-file easy-install-pth new-pth))
133 #t))
134
3bacb7a6
MW
135(define* (ensure-no-mtimes-pre-1980 #:rest _)
136 "Ensure that there are no mtimes before 1980-01-02 in the source tree."
137 ;; Rationale: patch-and-repack creates tarballs with timestamps at the POSIX
138 ;; epoch, 1970-01-01 UTC. This causes problems with Python packages,
139 ;; because Python eggs are ZIP files, and the ZIP format does not support
140 ;; timestamps before 1980.
141 (let ((early-1980 315619200)) ; 1980-01-02 UTC
142 (ftw "." (lambda (file stat flag)
143 (unless (<= early-1980 (stat:mtime stat))
144 (utime file early-1980 early-1980))
145 #t))
146 #t))
147
40506d5d 148(define %standard-phases
043a51c0 149 ;; 'configure' phase is not needed.
f84218ac 150 (modify-phases gnu:%standard-phases
3bacb7a6 151 (add-after 'unpack 'ensure-no-mtimes-pre-1980 ensure-no-mtimes-pre-1980)
f8503e2b
LC
152 (delete 'configure)
153 (replace 'install install)
154 (replace 'check check)
155 (replace 'build build)
156 (add-after 'install 'wrap wrap)
157 (add-before 'strip 'rename-pth-file rename-pth-file)))
40506d5d
NK
158
159(define* (python-build #:key inputs (phases %standard-phases)
160 #:allow-other-keys #:rest args)
161 "Build the given Python package, applying all of PHASES in order."
162 (apply gnu:gnu-build #:inputs inputs #:phases phases args))
163
164;;; python-build-system.scm ends here