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