summary refs log tree commit diff
diff options
context:
space:
mode:
authorEfraim Flashner <efraim@flashner.co.il>2023-11-20 12:21:52 +0200
committerEfraim Flashner <efraim@flashner.co.il>2023-11-28 07:59:43 +0200
commite604972d9c697302691aeb22e9c50c933a1a3c72 (patch)
tree5bd17c1b429dd19e34027fab5dcd136f377d7a23
parent584bd0bb3d88a69933b3d4e4974564a91adc6816 (diff)
downloadguix-e604972d9c697302691aeb22e9c50c933a1a3c72.tar.gz
build/cargo-build-system: Enable cross-compiling.
* guix/build-system/cargo.scm (default-rust): Accept an argument.
(default-rust-sysroot, cargo-cross-build): New procedures.
(lower): Accept a rust-sysroot.
[private-kewords]: Add rust-sysroot.  Remove target if cross-compiling.
[bag]: Allow cross-compiling.  In host-inputs only have inputs when
cross-compiling, move crate sources to here, remove standard-packages.
In build-inputs add the inputs when not cross-compiling, add the
standard-cross-packages when cross-compiling, add the standard-packages
to here.  Add target-inputs with the standard-cross-packages and
rust-sysroot when cross-compiling.
* guix/build/cargo-build-system.scm (configure): Accept target argument.
When cross-compiling set some environment variables.  Adjust the
.config/cargo.toml to have configure options for cross-compiling.

Change-Id: I388d1e1f48943e45ff01f55af8efc0746f383b4a
-rw-r--r--guix/build-system/cargo.scm139
-rw-r--r--guix/build/cargo-build-system.scm66
2 files changed, 172 insertions, 33 deletions
diff --git a/guix/build-system/cargo.scm b/guix/build-system/cargo.scm
index 912400a191..c029cc1dda 100644
--- a/guix/build-system/cargo.scm
+++ b/guix/build-system/cargo.scm
@@ -55,12 +55,18 @@
 to NAME and VERSION."
   (string-append crate-url name "/" version "/download"))
 
-(define (default-rust)
+(define (default-rust target)
   "Return the default Rust package."
   ;; Lazily resolve the binding to avoid a circular dependency.
   (let ((rust (resolve-interface '(gnu packages rust))))
     (module-ref rust 'rust)))
 
+(define (default-rust-sysroot target)
+  "Return the default Rust sysroot for <target>."
+  ;; Lazily resolve the binding to avoid a circular dependency.
+  (let ((module (resolve-interface '(gnu packages rust))))
+    (module-ref module 'make-rust-sysroot)))
+
 (define %cargo-utils-modules
   ;; Build-side modules imported by default.
   `((guix build cargo-utils)
@@ -126,6 +132,69 @@ to NAME and VERSION."
                     #:graft? #f
                     #:guile-for-build guile))
 
+(define* (cargo-cross-build name
+                            #:key
+                            source target
+                            build-inputs target-inputs host-inputs
+                            (tests? #f)
+                            (test-target #f)
+                            (vendor-dir "guix-vendor")
+                            (cargo-build-flags ''("--release"))
+                            (cargo-test-flags ''("--release"))
+                            (cargo-package-flags ''("--no-metadata" "--no-verify"))
+                            (features ''())
+                            (skip-build? #f)
+                            (install-source? (not (target-mingw? target)))
+                            (phases '%standard-phases)
+                            (outputs '("out"))
+                            (search-paths '())
+                            (native-search-paths '())
+                            (system (%current-system))
+                            (guile #f)
+                            (imported-modules %cargo-build-system-modules)
+                            (modules '((guix build cargo-build-system)
+                                       (guix build utils))))
+  "Cross-build SOURCE using CARGO, and with INPUTS."
+
+  (define builder
+    (with-imported-modules imported-modules
+      #~(begin
+          (use-modules #$@(sexp->gexp modules))
+
+          (cargo-build #:name #$name
+                       #:source #+source
+                       #:target #+target
+                       #:system #$system
+                       #:test-target #$test-target
+                       #:vendor-dir #$vendor-dir
+                       #:cargo-build-flags #$(sexp->gexp cargo-build-flags)
+                       #:cargo-test-flags #$(sexp->gexp cargo-test-flags)
+                       #:cargo-package-flags #$(sexp->gexp cargo-package-flags)
+                       #:features #$(sexp->gexp features)
+                       #:skip-build? #$skip-build?
+                       #:install-source? #$install-source?
+                       #:tests? #$(and tests? (not skip-build?))
+                       #:phases #$(if (pair? phases)
+                                      (sexp->gexp phases)
+                                      phases)
+                       #:outputs #$(outputs->gexp outputs)
+                       #:inputs (append #$(input-tuples->gexp host-inputs)
+                                        #+(input-tuples->gexp target-inputs))
+                       #:native-inputs #+(input-tuples->gexp build-inputs)
+                       #:make-dynamic-linker-cache? #f ;cross-compiling
+                       #:search-paths '#$(sexp->gexp
+                                          (map search-path-specification->sexp
+                                               search-paths))
+                       #:native-search-paths '#$(sexp->gexp
+                                          (map search-path-specification->sexp
+                                               native-search-paths))))))
+
+  (gexp->derivation name builder
+                    #:system system
+                    #:target target
+                    #:graft? #f
+                    #:guile-for-build guile))
+
 (define (package-cargo-inputs p)
   (apply
     (lambda* (#:key (cargo-inputs '()) #:allow-other-keys)
@@ -235,7 +304,8 @@ any dependent crates. This can be a benefits:
 
 (define* (lower name
                 #:key source inputs native-inputs outputs system target
-                (rust (default-rust))
+                (rust (default-rust target))
+                (rust-sysroot (default-rust-sysroot target))
                 (cargo-inputs '())
                 (cargo-development-inputs '())
                 #:allow-other-keys
@@ -243,28 +313,49 @@ any dependent crates. This can be a benefits:
   "Return a bag for NAME."
 
   (define private-keywords
-    '(#:target #:rust #:inputs #:native-inputs #:outputs
-      #:cargo-inputs #:cargo-development-inputs))
-
-  (and (not target) ;; TODO: support cross-compilation
-       (bag
-         (name name)
-         (system system)
-         (target target)
-         (host-inputs `(,@(if source
-                              `(("source" ,source))
-                              '())
-                        ,@inputs
-
-                        ;; Keep the standard inputs of 'gnu-build-system'
-                        ,@(standard-packages)))
-         (build-inputs `(("cargo" ,rust "cargo")
-                         ("rustc" ,rust)
-                         ,@(expand-crate-sources cargo-inputs cargo-development-inputs)
-                         ,@native-inputs))
-         (outputs outputs)
-         (build cargo-build)
-         (arguments (strip-keyword-arguments private-keywords arguments)))))
+    `(#:rust #:inputs #:native-inputs #:outputs
+      #:cargo-inputs #:cargo-development-inputs
+      #:rust-sysroot
+      ,@(if target '() '(#:target))))
+
+  (bag
+    (name name)
+    (system system)
+    (target target)
+    (host-inputs `(,@(if source
+                       `(("source" ,source))
+                       '())
+
+                    ;,@(if target '() inputs)
+                    ,@(if target inputs '())
+
+                    ,@(expand-crate-sources cargo-inputs cargo-development-inputs)))
+    (build-inputs `(("cargo" ,rust "cargo")
+                    ("rustc" ,rust)
+
+                    ,@native-inputs
+                    ;,@(if target inputs '())
+                    ,@(if target '() inputs)
+                    ;,@inputs
+
+                    ,@(if target
+                        ;; Use the standard cross inputs of
+                        ;; 'gnu-build-system'.
+                        (standard-cross-packages target 'host)
+                        '())
+                    ;; Keep the standard inputs of 'gnu-build-system'
+                    ,@(standard-packages)))
+    (target-inputs `(,@(if target
+                         (standard-cross-packages target 'target)
+                         '())
+
+                     ;; This provides a separate sysroot for the regular rustc
+                     ,@(if target
+                         `(("rust-sysroot" ,(rust-sysroot target)))
+                         '())))
+    (outputs outputs)
+    (build (if target cargo-cross-build cargo-build))
+    (arguments (strip-keyword-arguments private-keywords arguments))))
 
 (define cargo-build-system
   (build-system
diff --git a/guix/build/cargo-build-system.scm b/guix/build/cargo-build-system.scm
index 1694ab973c..2e6aeb78be 100644
--- a/guix/build/cargo-build-system.scm
+++ b/guix/build/cargo-build-system.scm
@@ -119,6 +119,7 @@ libraries or executables."
       (error "Possible pre-generated files found:" pregenerated-files))))
 
 (define* (configure #:key inputs
+                    target
                     (vendor-dir "guix-vendor")
                     #:allow-other-keys)
   "Vendor Cargo.toml dependencies as guix inputs."
@@ -146,27 +147,74 @@ libraries or executables."
               (invoke "tar" "xf" path "-C" crate-dir "--strip-components" "1")))))
     inputs)
 
-  ;; Configure cargo to actually use this new directory.
+  ;; For cross-building
+  (when target
+    (setenv "CARGO_BUILD_TARGET"
+            ;; Can this be replaced with platform-rust-architecture?
+            ;; Keep this synchronized with (guix platforms *)
+            (match target
+                   ("aarch64-linux-gnu" "aarch64-unknown-linux-gnu")
+                   ("arm-linux-gnueabihf" "armv7-unknown-linux-gnueabihf")
+                   ("i686-linux-gnu" "i686-unknown-linux-gnu")
+                   ("mips64el-linux-gnu" "mips64el-unknown-linux-gnuabi64")
+                   ("powerpc-linux-gnu" "powerpc-unknown-linux-gnu")
+                   ("powerpc64-linux-gnu" "powerpc64-unknown-linux-gnu")
+                   ("powerpc64le-linux-gnu" "powerpc64le-unknown-linux-gnu")
+                   ("riscv64-linux-gnu" "riscv64gc-unknown-linux-gnu")
+                   ("x86_64-linux-gnu" "x86_64-unknown-linux-gnu")
+                   ("i586-pc-gnu" "i686-unknown-hurd-gnu")
+                   ("i686-w64-mingw32" "i686-pc-windows-gnu")
+                   ("x86_64-w64-mingw32" "x86_64-pc-windows-gnu")
+                   (else #f)))
+    (setenv "RUSTFLAGS" (string-append
+                          (or (getenv "RUSTFLAGS") "")
+                          " --sysroot " (assoc-ref inputs "rust-sysroot")))
+
+    (setenv "PKG_CONFIG" (string-append target "-pkg-config"))
+
+    ;; We've removed all the bundled libraries, don't look for them.
+    (setenv "WINAPI_NO_BUNDLED_LIBRARIES" "1")
+
+    ;; Prevent targeting the build machine.
+    (setenv "CRATE_CC_NO_DEFAULTS" "1"))
+
+  ;; Configure cargo to actually use this new directory with all the crates.
   (setenv "CARGO_HOME" (string-append (getcwd) "/.cargo"))
   (mkdir-p ".cargo")
-  (let ((port (open-file ".cargo/config" "w" #:encoding "utf-8")))
-    (display "
+  (let ((port (open-file ".cargo/config.toml" "w" #:encoding "utf-8")))
+    ;; Placed here so it doesn't cause random rebuilds.  Neither of these work.
+    ;; sysroot = '" (assoc-ref inputs "rust-sysroot") "'
+    ;; rustflags = ['--sysroot', '" (assoc-ref inputs "rust-sysroot") "']
+    (when target
+      (display (string-append "
+[target." (getenv "CARGO_BUILD_TARGET") "]
+linker = '" target "-gcc'
+
+[build]
+target = ['" (getenv "CARGO_BUILD_TARGET") "']") port))
+    (display (string-append "
 [source.crates-io]
 replace-with = 'vendored-sources'
 
 [source.vendored-sources]
-directory = '" port)
-    (display (string-append (getcwd) "/" vendor-dir) port)
-    (display "'
-" port)
+directory = '" vendor-dir "'") port)
     (close-port port))
 
   ;; 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"))
+  (setenv "RUSTFLAGS" (string-append (or (getenv "RUSTFLAGS") "")
+                                     " --cap-lints allow"))
+
+  (if (assoc-ref inputs "cross-gcc")
+    (begin
+      (setenv "HOST_CC" "gcc")
+      (setenv "TARGET_CC" (string-append target "-gcc"))
+      (setenv "TARGET_AR" (string-append target "-ar"))
+      (setenv "TARGET_PKG_CONFIG" (string-append target "-pkg-config")))
+    (setenv "CC" (string-append (assoc-ref inputs "gcc") "/bin/gcc")))
+
   (setenv "LIBGIT2_SYS_USE_PKG_CONFIG" "1")
   (setenv "LIBSSH2_SYS_USE_PKG_CONFIG" "1")
   (when (assoc-ref inputs "openssl")