build-system/cargo: refactor phases to successfully build
* guix/build-system/cargo.scm (%cargo-build-system-modules): Add (json parser). (cargo-build): [vendor-dir]: Define flag and pass it to builder code. [cargo-test-flags]: Likewise. [skip-build?]: Likewise. * guix/build/cargo-build/system.scm (#:use-module): use (json parser). (package-name->crate-name): Delete it. (manifest-targets): Add it. (has-executable-target?): Add it. (configure): Add #:vendor-dir name and use it. Don't touch Cargo.toml. Don't symlink to duplicate inputs. Remove useless registry line from cargo config. Define RUSTFLAGS to lift lint restrictions. (build): Add #:skip-build? flag and use it. (check): Likewise. Add #:cargo-test-flags and pass it to cargo. (install): Factor source logic to install-source. Define #:skip-build? flag and use it. Only install if executable targets are present. (install-source): Copy entire crate directory not just src. [generate-checksums] pass dummy file for unused second argument. (%standard-phases): Add install-source phase. Signed-off-by: Chris Marusich <cmmarusich@gmail.com>
This commit is contained in:
parent
23635b2ee9
commit
1d3acde508
|
@ -59,13 +59,17 @@ to NAME and VERSION."
|
||||||
(define %cargo-build-system-modules
|
(define %cargo-build-system-modules
|
||||||
;; Build-side modules imported by default.
|
;; Build-side modules imported by default.
|
||||||
`((guix build cargo-build-system)
|
`((guix build cargo-build-system)
|
||||||
|
(json parser)
|
||||||
,@%cargo-utils-modules))
|
,@%cargo-utils-modules))
|
||||||
|
|
||||||
(define* (cargo-build store name inputs
|
(define* (cargo-build store name inputs
|
||||||
#:key
|
#:key
|
||||||
(tests? #t)
|
(tests? #t)
|
||||||
(test-target #f)
|
(test-target #f)
|
||||||
|
(vendor-dir "guix-vendor")
|
||||||
(cargo-build-flags ''("--release"))
|
(cargo-build-flags ''("--release"))
|
||||||
|
(cargo-test-flags ''("--release"))
|
||||||
|
(skip-build? #f)
|
||||||
(phases '(@ (guix build cargo-build-system)
|
(phases '(@ (guix build cargo-build-system)
|
||||||
%standard-phases))
|
%standard-phases))
|
||||||
(outputs '("out"))
|
(outputs '("out"))
|
||||||
|
@ -90,8 +94,11 @@ to NAME and VERSION."
|
||||||
source))
|
source))
|
||||||
#:system ,system
|
#:system ,system
|
||||||
#:test-target ,test-target
|
#:test-target ,test-target
|
||||||
|
#:vendor-dir ,vendor-dir
|
||||||
#:cargo-build-flags ,cargo-build-flags
|
#:cargo-build-flags ,cargo-build-flags
|
||||||
#:tests? ,tests?
|
#:cargo-test-flags ,cargo-test-flags
|
||||||
|
#:skip-build? ,skip-build?
|
||||||
|
#:tests? ,(and tests? (not skip-build?))
|
||||||
#:phases ,phases
|
#:phases ,phases
|
||||||
#:outputs %outputs
|
#:outputs %outputs
|
||||||
#:search-paths ',(map search-path-specification->sexp
|
#:search-paths ',(map search-path-specification->sexp
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
;;; GNU Guix --- Functional package management for GNU
|
;;; GNU Guix --- Functional package management for GNU
|
||||||
;;; Copyright © 2016 David Craven <david@craven.ch>
|
;;; Copyright © 2016 David Craven <david@craven.ch>
|
||||||
;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
|
;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
|
||||||
|
;;; Copyright © 2019 Ivan Petkov <ivanppetkov@gmail.com>
|
||||||
;;;
|
;;;
|
||||||
;;; This file is part of GNU Guix.
|
;;; This file is part of GNU Guix.
|
||||||
;;;
|
;;;
|
||||||
|
@ -26,6 +27,7 @@
|
||||||
#:use-module (ice-9 ftw)
|
#:use-module (ice-9 ftw)
|
||||||
#:use-module (ice-9 format)
|
#:use-module (ice-9 format)
|
||||||
#:use-module (ice-9 match)
|
#:use-module (ice-9 match)
|
||||||
|
#:use-module (json parser)
|
||||||
#:use-module (srfi srfi-1)
|
#:use-module (srfi srfi-1)
|
||||||
#:use-module (srfi srfi-26)
|
#:use-module (srfi srfi-26)
|
||||||
#:export (%standard-phases
|
#:export (%standard-phases
|
||||||
|
@ -37,81 +39,86 @@
|
||||||
;;
|
;;
|
||||||
;; Code:
|
;; Code:
|
||||||
|
|
||||||
;; FIXME: Needs to be parsed from url not package name.
|
(define (manifest-targets)
|
||||||
(define (package-name->crate-name name)
|
"Extract all targets from the Cargo.toml manifest"
|
||||||
"Return the crate name of NAME."
|
(let* ((port (open-input-pipe "cargo read-manifest"))
|
||||||
(match (string-split name #\-)
|
(data (json->scm port))
|
||||||
(("rust" rest ...)
|
(targets (hash-ref data "targets" '())))
|
||||||
(string-join rest "-"))
|
(close-port port)
|
||||||
(_ #f)))
|
targets))
|
||||||
|
|
||||||
(define* (configure #:key inputs #:allow-other-keys)
|
(define (has-executable-target?)
|
||||||
"Replace Cargo.toml [dependencies] section with guix inputs."
|
"Check if the current cargo project declares any binary targets."
|
||||||
;; Make sure Cargo.toml is writeable when the crate uses git-fetch.
|
(let* ((bin? (lambda (kind) (string=? kind "bin")))
|
||||||
(chmod "Cargo.toml" #o644)
|
(get-kinds (lambda (dep) (hash-ref dep "kind")))
|
||||||
|
(bin-dep? (lambda (dep) (find bin? (get-kinds dep)))))
|
||||||
|
(find bin-dep? (manifest-targets))))
|
||||||
|
|
||||||
|
(define* (configure #:key inputs
|
||||||
|
(vendor-dir "guix-vendor")
|
||||||
|
#:allow-other-keys)
|
||||||
|
"Vendor Cargo.toml dependencies as guix inputs."
|
||||||
(chmod "." #o755)
|
(chmod "." #o755)
|
||||||
(if (not (file-exists? "vendor"))
|
|
||||||
(if (not (file-exists? "Cargo.lock"))
|
|
||||||
(begin
|
|
||||||
(substitute* "Cargo.toml"
|
|
||||||
((".*32-sys.*") "
|
|
||||||
")
|
|
||||||
((".*winapi.*") "
|
|
||||||
")
|
|
||||||
((".*core-foundation.*") "
|
|
||||||
"))
|
|
||||||
;; Prepare one new directory with all the required dependencies.
|
;; Prepare one new directory with all the required dependencies.
|
||||||
;; It's necessary to do this (instead of just using /gnu/store as the
|
;; It's necessary to do this (instead of just using /gnu/store as the
|
||||||
;; directory) because we want to hide the libraries in subdirectories
|
;; directory) because we want to hide the libraries in subdirectories
|
||||||
;; share/rust-source/... instead of polluting the user's profile root.
|
;; share/rust-source/... instead of polluting the user's profile root.
|
||||||
(mkdir "vendor")
|
(mkdir-p vendor-dir)
|
||||||
(for-each
|
(for-each
|
||||||
(match-lambda
|
(match-lambda
|
||||||
((name . path)
|
((name . path)
|
||||||
(let ((crate (package-name->crate-name name)))
|
(let* ((rust-share (string-append path "/share/rust-source"))
|
||||||
(when (and crate path)
|
(basepath (basename path))
|
||||||
(match (string-split (basename path) #\-)
|
(link-dir (string-append vendor-dir "/" basepath)))
|
||||||
((_ ... version)
|
(and (file-exists? rust-share)
|
||||||
(symlink (string-append path "/share/rust-source")
|
;; Gracefully handle duplicate inputs
|
||||||
(string-append "vendor/" (basename path)))))))))
|
(not (file-exists? link-dir))
|
||||||
|
(symlink rust-share link-dir)))))
|
||||||
inputs)
|
inputs)
|
||||||
;; Configure cargo to actually use this new directory.
|
;; Configure cargo to actually use this new directory.
|
||||||
(mkdir-p ".cargo")
|
(mkdir-p ".cargo")
|
||||||
(let ((port (open-file ".cargo/config" "w" #:encoding "utf-8")))
|
(let ((port (open-file ".cargo/config" "w" #:encoding "utf-8")))
|
||||||
(display "
|
(display "
|
||||||
[source.crates-io]
|
[source.crates-io]
|
||||||
registry = 'https://github.com/rust-lang/crates.io-index'
|
|
||||||
replace-with = 'vendored-sources'
|
replace-with = 'vendored-sources'
|
||||||
|
|
||||||
[source.vendored-sources]
|
[source.vendored-sources]
|
||||||
directory = '" port)
|
directory = '" port)
|
||||||
(display (getcwd) port)
|
(display (string-append (getcwd) "/" vendor-dir) port)
|
||||||
(display "/vendor" port)
|
|
||||||
(display "'
|
(display "'
|
||||||
" port)
|
" port)
|
||||||
(close-port port)))))
|
(close-port port))
|
||||||
(setenv "CC" (string-append (assoc-ref inputs "gcc") "/bin/gcc"))
|
|
||||||
|
|
||||||
;(setenv "CARGO_HOME" "/gnu/store")
|
;; Lift restriction on any lints: a crate author may have decided to opt
|
||||||
; (setenv "CMAKE_C_COMPILER" cc)
|
;; into stricter lints (e.g. #![deny(warnings)]) during their own builds
|
||||||
|
;; but we don't want any build failures that could be caused later by
|
||||||
|
;; upgrading the compiler for example.
|
||||||
|
(setenv "RUSTFLAGS" "--cap-lints allow")
|
||||||
|
(setenv "CC" (string-append (assoc-ref inputs "gcc") "/bin/gcc"))
|
||||||
#t)
|
#t)
|
||||||
|
|
||||||
(define* (build #:key (cargo-build-flags '("--release"))
|
(define* (build #:key
|
||||||
|
skip-build?
|
||||||
|
(cargo-build-flags '("--release"))
|
||||||
#:allow-other-keys)
|
#:allow-other-keys)
|
||||||
"Build a given Cargo package."
|
"Build a given Cargo package."
|
||||||
(zero? (apply system* `("cargo" "build" ,@cargo-build-flags))))
|
(or skip-build?
|
||||||
|
(zero? (apply system* `("cargo" "build" ,@cargo-build-flags)))))
|
||||||
|
|
||||||
(define* (check #:key tests? #:allow-other-keys)
|
(define* (check #:key
|
||||||
|
tests?
|
||||||
|
(cargo-test-flags '("--release"))
|
||||||
|
#:allow-other-keys)
|
||||||
"Run tests for a given Cargo package."
|
"Run tests for a given Cargo package."
|
||||||
(if (and tests? (file-exists? "Cargo.lock"))
|
(if tests?
|
||||||
(zero? (system* "cargo" "test"))
|
(zero? (apply system* `("cargo" "test" ,@cargo-test-flags)))
|
||||||
#t))
|
#t))
|
||||||
|
|
||||||
(define (touch file-name)
|
(define (touch file-name)
|
||||||
(call-with-output-file file-name (const #t)))
|
(call-with-output-file file-name (const #t)))
|
||||||
|
|
||||||
(define* (install #:key inputs outputs #:allow-other-keys)
|
(define* (install-source #:key inputs outputs #:allow-other-keys)
|
||||||
"Install a given Cargo package."
|
"Install the source for a given Cargo package."
|
||||||
(let* ((out (assoc-ref outputs "out"))
|
(let* ((out (assoc-ref outputs "out"))
|
||||||
(src (assoc-ref inputs "source"))
|
(src (assoc-ref inputs "source"))
|
||||||
(rsrc (string-append (assoc-ref outputs "src")
|
(rsrc (string-append (assoc-ref outputs "src")
|
||||||
|
@ -120,24 +127,36 @@ directory = '" port)
|
||||||
;; Rust doesn't have a stable ABI yet. Because of this
|
;; Rust doesn't have a stable ABI yet. Because of this
|
||||||
;; Cargo doesn't have a search path for binaries yet.
|
;; Cargo doesn't have a search path for binaries yet.
|
||||||
;; Until this changes we are working around this by
|
;; Until this changes we are working around this by
|
||||||
;; distributing crates as source and replacing
|
;; vendoring the crates' sources by symlinking them
|
||||||
;; references in Cargo.toml with store paths.
|
;; to store paths.
|
||||||
(copy-recursively "src" (string-append rsrc "/src"))
|
(copy-recursively "." rsrc)
|
||||||
(touch (string-append rsrc "/.cargo-ok"))
|
(touch (string-append rsrc "/.cargo-ok"))
|
||||||
(generate-checksums rsrc src)
|
(generate-checksums rsrc "/dev/null")
|
||||||
(install-file "Cargo.toml" rsrc)
|
(install-file "Cargo.toml" rsrc)
|
||||||
;; When the package includes executables we install
|
#t))
|
||||||
;; it using cargo install. This fails when the crate
|
|
||||||
;; doesn't contain an executable.
|
(define* (install #:key inputs outputs skip-build? #:allow-other-keys)
|
||||||
(if (file-exists? "Cargo.lock")
|
"Install a given Cargo package."
|
||||||
(zero? (system* "cargo" "install" "--root" out))
|
(let* ((out (assoc-ref outputs "out")))
|
||||||
(begin
|
(mkdir-p out)
|
||||||
(mkdir out)
|
|
||||||
#t))))
|
;; Make cargo reuse all the artifacts we just built instead
|
||||||
|
;; of defaulting to making a new temp directory
|
||||||
|
(setenv "CARGO_TARGET_DIR" "./target")
|
||||||
|
;; Force cargo to honor our .cargo/config definitions
|
||||||
|
;; https://github.com/rust-lang/cargo/issues/6397
|
||||||
|
(setenv "CARGO_HOME" ".")
|
||||||
|
|
||||||
|
;; Only install crates which include binary targets,
|
||||||
|
;; otherwise cargo will raise an error.
|
||||||
|
(or skip-build?
|
||||||
|
(not (has-executable-target?))
|
||||||
|
(zero? (system* "cargo" "install" "--path" "." "--root" out)))))
|
||||||
|
|
||||||
(define %standard-phases
|
(define %standard-phases
|
||||||
(modify-phases gnu:%standard-phases
|
(modify-phases gnu:%standard-phases
|
||||||
(delete 'bootstrap)
|
(delete 'bootstrap)
|
||||||
|
(add-before 'configure 'install-source install-source)
|
||||||
(replace 'configure configure)
|
(replace 'configure configure)
|
||||||
(replace 'build build)
|
(replace 'build build)
|
||||||
(replace 'check check)
|
(replace 'check check)
|
||||||
|
|
Loading…
Reference in New Issue