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 @@ (define %cargo-utils-modules
(define %cargo-build-system-modules
;; Build-side modules imported by default.
`((guix build cargo-build-system)
(json parser)
,@%cargo-utils-modules))
(define* (cargo-build store name inputs
#:key
(tests? #t)
(test-target #f)
(vendor-dir "guix-vendor")
(cargo-build-flags ''("--release"))
(cargo-test-flags ''("--release"))
(skip-build? #f)
(phases '(@ (guix build cargo-build-system)
%standard-phases))
(outputs '("out"))
@ -90,8 +94,11 @@ (define builder
source))
#:system ,system
#:test-target ,test-target
#:vendor-dir ,vendor-dir
#: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
#:outputs %outputs
#:search-paths ',(map search-path-specification->sexp

View file

@ -1,6 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2016 David Craven <david@craven.ch>
;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
;;; Copyright © 2019 Ivan Petkov <ivanppetkov@gmail.com>
;;;
;;; This file is part of GNU Guix.
;;;
@ -26,6 +27,7 @@ (define-module (guix build cargo-build-system)
#:use-module (ice-9 ftw)
#:use-module (ice-9 format)
#:use-module (ice-9 match)
#:use-module (json parser)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-26)
#:export (%standard-phases
@ -37,81 +39,86 @@ (define-module (guix build cargo-build-system)
;;
;; Code:
;; FIXME: Needs to be parsed from url not package name.
(define (package-name->crate-name name)
"Return the crate name of NAME."
(match (string-split name #\-)
(("rust" rest ...)
(string-join rest "-"))
(_ #f)))
(define (manifest-targets)
"Extract all targets from the Cargo.toml manifest"
(let* ((port (open-input-pipe "cargo read-manifest"))
(data (json->scm port))
(targets (hash-ref data "targets" '())))
(close-port port)
targets))
(define* (configure #:key inputs #:allow-other-keys)
"Replace Cargo.toml [dependencies] section with guix inputs."
;; Make sure Cargo.toml is writeable when the crate uses git-fetch.
(chmod "Cargo.toml" #o644)
(define (has-executable-target?)
"Check if the current cargo project declares any binary targets."
(let* ((bin? (lambda (kind) (string=? kind "bin")))
(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)
(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.
;; It's necessary to do this (instead of just using /gnu/store as the
;; directory) because we want to hide the libraries in subdirectories
;; share/rust-source/... instead of polluting the user's profile root.
(mkdir "vendor")
(for-each
(match-lambda
((name . path)
(let ((crate (package-name->crate-name name)))
(when (and crate path)
(match (string-split (basename path) #\-)
((_ ... version)
(symlink (string-append path "/share/rust-source")
(string-append "vendor/" (basename path)))))))))
inputs)
;; Configure cargo to actually use this new directory.
(mkdir-p ".cargo")
(let ((port (open-file ".cargo/config" "w" #:encoding "utf-8")))
(display "
;; Prepare one new directory with all the required dependencies.
;; It's necessary to do this (instead of just using /gnu/store as the
;; directory) because we want to hide the libraries in subdirectories
;; share/rust-source/... instead of polluting the user's profile root.
(mkdir-p vendor-dir)
(for-each
(match-lambda
((name . path)
(let* ((rust-share (string-append path "/share/rust-source"))
(basepath (basename path))
(link-dir (string-append vendor-dir "/" basepath)))
(and (file-exists? rust-share)
;; Gracefully handle duplicate inputs
(not (file-exists? link-dir))
(symlink rust-share link-dir)))))
inputs)
;; Configure cargo to actually use this new directory.
(mkdir-p ".cargo")
(let ((port (open-file ".cargo/config" "w" #:encoding "utf-8")))
(display "
[source.crates-io]
registry = 'https://github.com/rust-lang/crates.io-index'
replace-with = 'vendored-sources'
[source.vendored-sources]
directory = '" port)
(display (getcwd) port)
(display "/vendor" port)
(display "'
(display (string-append (getcwd) "/" vendor-dir) port)
(display "'
" port)
(close-port port)))))
(setenv "CC" (string-append (assoc-ref inputs "gcc") "/bin/gcc"))
(close-port port))
;(setenv "CARGO_HOME" "/gnu/store")
; (setenv "CMAKE_C_COMPILER" cc)
;; Lift restriction on any lints: a crate author may have decided to opt
;; 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)
(define* (build #:key (cargo-build-flags '("--release"))
(define* (build #:key
skip-build?
(cargo-build-flags '("--release"))
#:allow-other-keys)
"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."
(if (and tests? (file-exists? "Cargo.lock"))
(zero? (system* "cargo" "test"))
(if tests?
(zero? (apply system* `("cargo" "test" ,@cargo-test-flags)))
#t))
(define (touch file-name)
(call-with-output-file file-name (const #t)))
(define* (install #:key inputs outputs #:allow-other-keys)
"Install a given Cargo package."
(define* (install-source #:key inputs outputs #:allow-other-keys)
"Install the source for a given Cargo package."
(let* ((out (assoc-ref outputs "out"))
(src (assoc-ref inputs "source"))
(rsrc (string-append (assoc-ref outputs "src")
@ -120,24 +127,36 @@ (define* (install #:key inputs outputs #:allow-other-keys)
;; Rust doesn't have a stable ABI yet. Because of this
;; Cargo doesn't have a search path for binaries yet.
;; Until this changes we are working around this by
;; distributing crates as source and replacing
;; references in Cargo.toml with store paths.
(copy-recursively "src" (string-append rsrc "/src"))
;; vendoring the crates' sources by symlinking them
;; to store paths.
(copy-recursively "." rsrc)
(touch (string-append rsrc "/.cargo-ok"))
(generate-checksums rsrc src)
(generate-checksums rsrc "/dev/null")
(install-file "Cargo.toml" rsrc)
;; When the package includes executables we install
;; it using cargo install. This fails when the crate
;; doesn't contain an executable.
(if (file-exists? "Cargo.lock")
(zero? (system* "cargo" "install" "--root" out))
(begin
(mkdir out)
#t))))
#t))
(define* (install #:key inputs outputs skip-build? #:allow-other-keys)
"Install a given Cargo package."
(let* ((out (assoc-ref outputs "out")))
(mkdir-p out)
;; 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
(modify-phases gnu:%standard-phases
(delete 'bootstrap)
(add-before 'configure 'install-source install-source)
(replace 'configure configure)
(replace 'build build)
(replace 'check check)