Commit | Line | Data |
---|---|---|
4b3cb7f4 DC |
1 | ;;; GNU Guix --- Functional package management for GNU |
2 | ;;; Copyright © 2016 David Craven <david@craven.ch> | |
e60ee790 | 3 | ;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com> |
1d3acde5 | 4 | ;;; Copyright © 2019 Ivan Petkov <ivanppetkov@gmail.com> |
a13db7e2 | 5 | ;;; Copyright © 2019, 2020 Efraim Flashner <efraim@flashner.co.il> |
927c2518 | 6 | ;;; Copyright © 2020 Jakub Kądziołka <kuba@kadziolka.net> |
4b3cb7f4 DC |
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 cargo-build-system) | |
24 | #:use-module ((guix build gnu-build-system) #:prefix gnu:) | |
4fde0030 | 25 | #:use-module (guix build json) |
4b3cb7f4 | 26 | #:use-module (guix build utils) |
7d141788 | 27 | #:use-module (guix build cargo-utils) |
4ed64c53 DM |
28 | #:use-module (ice-9 popen) |
29 | #:use-module (ice-9 rdelim) | |
4b3cb7f4 DC |
30 | #:use-module (ice-9 ftw) |
31 | #:use-module (ice-9 format) | |
32 | #:use-module (ice-9 match) | |
33 | #:use-module (srfi srfi-1) | |
34 | #:use-module (srfi srfi-26) | |
35 | #:export (%standard-phases | |
7d141788 | 36 | cargo-build)) |
4b3cb7f4 DC |
37 | |
38 | ;; Commentary: | |
39 | ;; | |
40 | ;; Builder-side code of the standard Rust package build procedure. | |
41 | ;; | |
42 | ;; Code: | |
43 | ||
1d3acde5 IP |
44 | (define (manifest-targets) |
45 | "Extract all targets from the Cargo.toml manifest" | |
46 | (let* ((port (open-input-pipe "cargo read-manifest")) | |
4fde0030 RV |
47 | (data (read-json port)) |
48 | (targets (or (assoc-ref data "targets") '()))) | |
1d3acde5 IP |
49 | (close-port port) |
50 | targets)) | |
51 | ||
52 | (define (has-executable-target?) | |
53 | "Check if the current cargo project declares any binary targets." | |
54 | (let* ((bin? (lambda (kind) (string=? kind "bin"))) | |
4fde0030 | 55 | (get-kinds (lambda (dep) (assoc-ref dep "kind"))) |
1d3acde5 IP |
56 | (bin-dep? (lambda (dep) (find bin? (get-kinds dep))))) |
57 | (find bin-dep? (manifest-targets)))) | |
58 | ||
efdf2ae1 IP |
59 | (define (crate-src? path) |
60 | "Check if PATH refers to a crate source, namely a gzipped tarball with a | |
61 | Cargo.toml file present at its root." | |
a13db7e2 | 62 | (and (not (directory-exists? path)) ; not a tarball |
efdf2ae1 IP |
63 | ;; First we print out all file names within the tarball to see if it |
64 | ;; looks like the source of a crate. However, the tarball will include | |
65 | ;; an extra path component which we would like to ignore (since we're | |
66 | ;; interested in checking if a Cargo.toml exists at the root of the | |
67 | ;; archive, but not nested anywhere else). We do this by cutting up | |
68 | ;; each output line and only looking at the second component. We then | |
69 | ;; check if it matches Cargo.toml exactly and short circuit if it does. | |
2f43e5db EF |
70 | (apply invoke (list "sh" "-c" |
71 | (string-append "tar -tf " path | |
72 | " | cut -d/ -f2" | |
73 | " | grep -q '^Cargo.toml$'"))))) | |
efdf2ae1 | 74 | |
1d3acde5 IP |
75 | (define* (configure #:key inputs |
76 | (vendor-dir "guix-vendor") | |
77 | #:allow-other-keys) | |
78 | "Vendor Cargo.toml dependencies as guix inputs." | |
4ed64c53 | 79 | (chmod "." #o755) |
1d3acde5 IP |
80 | ;; Prepare one new directory with all the required dependencies. |
81 | ;; It's necessary to do this (instead of just using /gnu/store as the | |
82 | ;; directory) because we want to hide the libraries in subdirectories | |
83 | ;; share/rust-source/... instead of polluting the user's profile root. | |
84 | (mkdir-p vendor-dir) | |
85 | (for-each | |
86 | (match-lambda | |
87 | ((name . path) | |
43ffa11f | 88 | (let* ((basepath (strip-store-file-name path)) |
efdf2ae1 IP |
89 | (crate-dir (string-append vendor-dir "/" basepath))) |
90 | (and (crate-src? path) | |
1d3acde5 | 91 | ;; Gracefully handle duplicate inputs |
efdf2ae1 IP |
92 | (not (file-exists? crate-dir)) |
93 | (mkdir-p crate-dir) | |
94 | ;; Cargo crates are simply gzipped tarballs but with a .crate | |
95 | ;; extension. We expand the source to a directory name we control | |
96 | ;; so that we can generate any cargo checksums. | |
97 | ;; The --strip-components argument is needed to prevent creating | |
98 | ;; an extra directory within `crate-dir`. | |
ac6b7848 | 99 | (invoke "tar" "xvf" path "-C" crate-dir "--strip-components" "1"))))) |
1d3acde5 | 100 | inputs) |
efdf2ae1 | 101 | |
1d3acde5 | 102 | ;; Configure cargo to actually use this new directory. |
c82c16a6 | 103 | (setenv "CARGO_HOME" (string-append (getcwd) "/.cargo")) |
1d3acde5 IP |
104 | (mkdir-p ".cargo") |
105 | (let ((port (open-file ".cargo/config" "w" #:encoding "utf-8"))) | |
106 | (display " | |
4ed64c53 | 107 | [source.crates-io] |
4ed64c53 DM |
108 | replace-with = 'vendored-sources' |
109 | ||
110 | [source.vendored-sources] | |
111 | directory = '" port) | |
1d3acde5 IP |
112 | (display (string-append (getcwd) "/" vendor-dir) port) |
113 | (display "' | |
4ed64c53 | 114 | " port) |
1d3acde5 | 115 | (close-port port)) |
4ed64c53 | 116 | |
1d3acde5 IP |
117 | ;; Lift restriction on any lints: a crate author may have decided to opt |
118 | ;; into stricter lints (e.g. #![deny(warnings)]) during their own builds | |
119 | ;; but we don't want any build failures that could be caused later by | |
120 | ;; upgrading the compiler for example. | |
121 | (setenv "RUSTFLAGS" "--cap-lints allow") | |
122 | (setenv "CC" (string-append (assoc-ref inputs "gcc") "/bin/gcc")) | |
61b95c15 EF |
123 | (setenv "LIBGIT2_SYS_USE_PKG_CONFIG" "1") |
124 | (setenv "LIBSSH2_SYS_USE_PKG_CONFIG" "1") | |
4b3cb7f4 | 125 | |
3762e31b EF |
126 | ;; We don't use the Cargo.lock file to determine the package versions we use |
127 | ;; during building, and in any case if one is not present it is created | |
128 | ;; during the 'build phase by cargo. | |
ac6b7848 | 129 | (when (file-exists? "Cargo.lock") |
3762e31b | 130 | (delete-file "Cargo.lock")) |
ac6b7848 EF |
131 | #t) |
132 | ||
133 | ;; After the 'patch-generated-file-shebangs phase any vendored crates who have | |
134 | ;; their shebangs patched will have a mismatch on their checksum. | |
135 | (define* (patch-cargo-checksums #:key | |
136 | (vendor-dir "guix-vendor") | |
137 | #:allow-other-keys) | |
138 | "Patch the checksums of the vendored crates after patching their shebangs." | |
139 | (generate-all-checksums vendor-dir) | |
140 | #t) | |
141 | ||
1d3acde5 IP |
142 | (define* (build #:key |
143 | skip-build? | |
927c2518 | 144 | features |
1d3acde5 | 145 | (cargo-build-flags '("--release")) |
4b3cb7f4 DC |
146 | #:allow-other-keys) |
147 | "Build a given Cargo package." | |
1d3acde5 | 148 | (or skip-build? |
927c2518 JK |
149 | (apply invoke "cargo" "build" |
150 | "--features" (string-join features) | |
151 | cargo-build-flags))) | |
4b3cb7f4 | 152 | |
1d3acde5 IP |
153 | (define* (check #:key |
154 | tests? | |
155 | (cargo-test-flags '("--release")) | |
156 | #:allow-other-keys) | |
4b3cb7f4 | 157 | "Run tests for a given Cargo package." |
1d3acde5 | 158 | (if tests? |
927c2518 | 159 | (apply invoke "cargo" "test" cargo-test-flags) |
f1d13695 | 160 | #t)) |
4b3cb7f4 | 161 | |
927c2518 | 162 | (define* (install #:key inputs outputs skip-build? features #:allow-other-keys) |
1d3acde5 IP |
163 | "Install a given Cargo package." |
164 | (let* ((out (assoc-ref outputs "out"))) | |
165 | (mkdir-p out) | |
166 | ||
167 | ;; Make cargo reuse all the artifacts we just built instead | |
168 | ;; of defaulting to making a new temp directory | |
169 | (setenv "CARGO_TARGET_DIR" "./target") | |
1d3acde5 IP |
170 | |
171 | ;; Only install crates which include binary targets, | |
172 | ;; otherwise cargo will raise an error. | |
173 | (or skip-build? | |
174 | (not (has-executable-target?)) | |
927c2518 JK |
175 | (invoke "cargo" "install" "--path" "." "--root" out |
176 | "--features" (string-join features))))) | |
4b3cb7f4 DC |
177 | |
178 | (define %standard-phases | |
4b3cb7f4 | 179 | (modify-phases gnu:%standard-phases |
189be331 | 180 | (delete 'bootstrap) |
4b3cb7f4 DC |
181 | (replace 'configure configure) |
182 | (replace 'build build) | |
183 | (replace 'check check) | |
ac6b7848 | 184 | (replace 'install install) |
ac6b7848 | 185 | (add-after 'patch-generated-file-shebangs 'patch-cargo-checksums patch-cargo-checksums))) |
4b3cb7f4 DC |
186 | |
187 | (define* (cargo-build #:key inputs (phases %standard-phases) | |
188 | #:allow-other-keys #:rest args) | |
189 | "Build the given Cargo package, applying all of PHASES in order." | |
190 | (apply gnu:gnu-build #:inputs inputs #:phases phases args)) | |
191 | ||
192 | ;;; cargo-build-system.scm ends here |