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:
Ivan Petkov 2019-04-02 03:02:51 -07:00 committed by Chris Marusich
parent 23635b2ee9
commit 1d3acde508
No known key found for this signature in database
GPG Key ID: DD409A15D822469D
2 changed files with 94 additions and 68 deletions

View File

@ -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

View File

@ -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)