;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2013, 2014, 2015, 2016 Ludovic Courtès ;;; Copyright © 2014, 2015 Mark H Weaver ;;; ;;; This file is part of GNU Guix. ;;; ;;; GNU Guix is free software; you can redistribute it and/or modify it ;;; under the terms of the GNU General Public License as published by ;;; the Free Software Foundation; either version 3 of the License, or (at ;;; your option) any later version. ;;; ;;; GNU Guix is distributed in the hope that it will be useful, but ;;; WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;;; GNU General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with GNU Guix. If not, see . (define-module (gnu packages cross-base) #:use-module ((guix licenses) #:prefix license:) #:use-module (gnu packages) #:use-module (gnu packages base) #:use-module (gnu packages bash) #:use-module (gnu packages gawk) #:use-module (gnu packages gcc) #:use-module (gnu packages commencement) #:use-module (gnu packages compression) #:use-module (gnu packages linux) #:use-module (gnu packages multiprecision) #:use-module (guix packages) #:use-module (guix download) #:use-module (guix utils) #:use-module (guix build-system gnu) #:use-module (guix build-system trivial) #:use-module (srfi srfi-1) #:use-module (srfi srfi-26) #:use-module (ice-9 match) #:export (cross-binutils cross-libc cross-gcc)) (define %xgcc ;; GCC package used as the basis for cross-compilation. It doesn't have to ;; be 'gcc' and can be a specific variant such as 'gcc-4.8'. gcc) (define (cross p target) (package (inherit p) (name (string-append (package-name p) "-cross-" target)) (arguments (substitute-keyword-arguments (package-arguments p) ((#:configure-flags flags) `(cons ,(string-append "--target=" target) ,flags)))))) (define (package-with-patch original patch) "Return package ORIGINAL with PATCH applied." (package (inherit original) (source (origin (inherit (package-source original)) (patches (list patch)))))) (define (cross-binutils target) "Return a cross-Binutils for TARGET." (let ((binutils (package (inherit binutils) (arguments (substitute-keyword-arguments (package-arguments binutils) ((#:configure-flags flags) ;; Build with `--with-sysroot' so that ld honors ;; DT_RUNPATH entries when searching for a needed ;; library. This works because as a side effect ;; `genscripts.sh' sets `USE_LIBPATH=yes', which tells ;; elf32.em to use DT_RUNPATH in its search list. ;; See . ;; ;; In theory choosing / as the sysroot could lead ld ;; to pick up native libs instead of target ones. In ;; practice the RUNPATH of target libs only refers to ;; target libs, not native libs, so this is safe. `(cons "--with-sysroot=/" ,flags))))))) ;; For Xtensa, apply Qualcomm's patch. (cross (if (string-prefix? "xtensa-" target) (package-with-patch binutils (search-patch "ath9k-htc-firmware-binutils.patch")) binutils) target))) (define (cross-gcc-arguments target libc) "Return build system arguments for a cross-gcc for TARGET, using LIBC (which may be either a libc package or #f.)" ;; Set the current target system so that 'glibc-dynamic-linker' returns the ;; right name. (parameterize ((%current-target-system target)) ;; Disable stripping as this can break binaries, with object files of ;; libgcc.a showing up as having an unknown architecture. See ;; ;; for instance. (let ((args `(#:strip-binaries? #f ,@(package-arguments %xgcc)))) (substitute-keyword-arguments args ((#:configure-flags flags) `(append (list ,(string-append "--target=" target) ,@(if libc `( ;; Disable libcilkrts because it is not ;; ported to GNU/Hurd. "--disable-libcilkrts") `( ;; Disable features not needed at this stage. "--disable-shared" "--enable-static" "--enable-languages=c,c++" ;; libstdc++ cannot be built at this stage ;; ("Link tests are not allowed after ;; GCC_NO_EXECUTABLES."). "--disable-libstdc++-v3" "--disable-threads" ;libgcc, would need libc "--disable-libatomic" "--disable-libmudflap" "--disable-libgomp" "--disable-libssp" "--disable-libquadmath" "--disable-decimal-float" ;would need libc "--disable-libcilkrts" )) ,@(if (equal? target "i686-w64-mingw32") '("--with-newlib" "--without-headers") '())) ,(if libc flags `(remove (cut string-match "--enable-languages.*" <>) ,flags)))) ((#:make-flags flags) (cond ((equal? target "i686-w64-mingw32") `(append (let ((runtime (assoc-ref %build-inputs "mingw-w64"))) (if runtime (list (string-append "FLAGS_FOR_TARGET=-B" runtime "/lib")) '())) ,flags)) (libc `(let ((libc (assoc-ref %build-inputs "libc"))) ;; FLAGS_FOR_TARGET are needed for the target libraries to receive ;; the -Bxxx for the startfiles. (cons (string-append "FLAGS_FOR_TARGET=-B" libc "/lib") ,flags))) (else flags))) ((#:phases phases) (let ((phases `(alist-cons-after 'install 'make-cross-binutils-visible (lambda* (#:key outputs inputs #:allow-other-keys) (let* ((out (assoc-ref outputs "out")) (libexec (string-append out "/libexec/gcc/" ,target)) (binutils (string-append (assoc-ref inputs "binutils-cross") "/bin/" ,target "-")) (wrapper (string-append (assoc-ref inputs "ld-wrapper-cross") "/bin/" ,target "-ld"))) (for-each (lambda (file) (symlink (string-append binutils file) (string-append libexec "/" file))) '("as" "nm")) (symlink wrapper (string-append libexec "/ld")) #t)) (alist-replace 'install (lambda _ ;; Unlike our 'strip' phase, this will do the right thing ;; for cross-compilers. (zero? (system* "make" "install-strip"))) ,phases)))) (cond ((equal? target "i686-w64-mingw32") `(modify-phases ,phases (add-before 'configure 'set-cross-path (lambda* (#:key inputs #:allow-other-keys) ;; Add the cross Linux headers to CROSS_CPATH, and remove them ;; from CPATH. (let ((libc (assoc-ref inputs "mingw-w64")) (gcc (assoc-ref inputs "gcc"))) (define (cross? x) (and libc (string-prefix? libc x))) (if libc (setenv "CROSS_CPATH" (string-append libc "/include" ":" libc "/i686-w64-mingw32/include"))) (if libc (setenv "CROSS_LIBRARY_PATH" (string-append libc "/lib" ":" libc "/i686-w64-mingw32/lib"))) (setenv "CPP" (string-append gcc "/bin/cpp")) (let ((cpath (search-path-as-string->list (getenv "C_INCLUDE_PATH"))) (libpath (search-path-as-string->list (getenv "LIBRARY_PATH")))) (setenv "CPATH" (list->search-path-as-string (remove cross? cpath) ":")) (for-each unsetenv '("C_INCLUDE_PATH" "CPLUS_INCLUDE_PATH")) (setenv "LIBRARY_PATH" (list->search-path-as-string (remove cross? libpath) ":")) #t)))))) (libc `(alist-cons-before 'configure 'set-cross-path (lambda* (#:key inputs #:allow-other-keys) ;; Add the cross Linux headers to CROSS_CPATH, and remove them ;; from CPATH. (let ((libc (assoc-ref inputs libc)) (linux (assoc-ref inputs "xlinux-headers"))) (define (cross? x) ;; Return #t if X is a cross-libc or cross Linux. (or (string-prefix? libc x) (string-prefix? linux x))) (setenv "CROSS_CPATH" (string-append libc "/include:" linux "/include")) (setenv "CROSS_LIBRARY_PATH" (string-append libc "/lib")) (let ((cpath (search-path-as-string->list (getenv "C_INCLUDE_PATH"))) (libpath (search-path-as-string->list (getenv "LIBRARY_PATH")))) (setenv "CPATH" (list->search-path-as-string (remove cross? cpath) ":")) (for-each unsetenv '("C_INCLUDE_PATH" "CPLUS_INCLUDE_PATH")) (setenv "LIBRARY_PATH" (list->search-path-as-string (remove cross? libpath) ":")) #t))) ,phases)) (else phases)))))))) (define (cross-gcc-patches target) "Return GCC patches needed for TARGET." (cond ((string-prefix? "xtensa-" target) ;; Patch by Qualcomm needed to build the ath9k-htc firmware. (list (search-patch "ath9k-htc-firmware-gcc.patch"))) (else '()))) (define* (cross-gcc target #:optional (xbinutils (cross-binutils target)) libc) "Return a cross-compiler for TARGET, where TARGET is a GNU triplet. Use XBINUTILS as the associated cross-Binutils. If LIBC is false, then build a GCC that does not target a libc; otherwise, target that libc." (package (inherit %xgcc) (name (string-append "gcc-cross-" (if libc "" "sans-libc-") target)) (source (origin (inherit (package-source %xgcc)) (patches (append (origin-patches (package-source %xgcc)) (cons (search-patch "gcc-cross-environment-variables.patch") (cross-gcc-patches target)))))) ;; For simplicity, use a single output. Otherwise libgcc_s & co. are not ;; found by default, etc. (outputs '("out")) (arguments `(#:implicit-inputs? #f #:modules ((guix build gnu-build-system) (guix build utils) (ice-9 regex) (srfi srfi-1) (srfi srfi-26)) ,@(cross-gcc-arguments target libc))) (native-inputs `(("ld-wrapper-cross" ,(make-ld-wrapper (string-append "ld-wrapper-" target) #:target target #:binutils xbinutils)) ("binutils-cross" ,xbinutils) ("gcc" ,gcc) ;; Call it differently so that the builder can check whether the "libc" ;; input is #f. ("libc-native" ,@(assoc-ref %final-inputs "libc")) ;; Remaining inputs. ,@(let ((inputs (append (package-inputs %xgcc) (alist-delete "libc" %final-inputs)))) (cond ((equal? target "i686-w64-mingw32") `(("mingw-w64" ,mingw-w64) ,@inputs)) (libc `(("libc" ,libc) ("xlinux-headers" ;the target headers ,@(assoc-ref (package-propagated-inputs libc) "linux-headers")) ,@inputs)) (else inputs))))) (inputs '()) ;; Only search target inputs, not host inputs. (search-paths (list (search-path-specification (variable "CROSS_CPATH") (files '("include"))) (search-path-specification (variable "CROSS_LIBRARY_PATH") (files '("lib" "lib64"))))) (native-search-paths '()))) (define* (cross-gcc-core target #:optional (xbinutils (cross-binutils target))) "Return a cross-compiler-core to be used to build the c library for TARGET, where TARGET is a GNU triplet. Use XBINUTILS as the associated cross-Binutils." (package (inherit (cross-gcc target xbinutils)) (name (string-append "gcc-core-cross-" target)) (inputs `()) ;; FIXME: no effect? must add inputs to mingw-source (propagated-inputs `(("gmp" ,gmp) ("mpfr" ,mpfr) ("mpc" ,mpc) ("zlib" ,zlib))) (native-inputs `(("ld-wrapper-cross" ,(make-ld-wrapper (string-append "ld-wrapper-" target) #:target target #:binutils xbinutils)) ("cross-binutils" ,xbinutils) ("tar" ,tar) ("xz" ,xz) ;;("static-bash" ,static-bash) ;; /bin/sh ("bash" ,bash) ("glibc" ,glibc) ("coreutils" ,coreutils) ("sed" ,sed) ("gcc" ,gcc) ("binutils" ,binutils) ("gawk" ,gawk) ("grep" ,grep) ("make" ,gnu-make) ("mingw-source" ,(package-source mingw-w64)) ("bzip2" ,bzip2) ;; unpack mingw .bz2 ("diffutils" ,diffutils) ;; cmp: are we fdl? ("gzip" ,gzip))) ;; info docs (arguments (let ((args `(,@(package-arguments (cross-gcc target xbinutils))))) (substitute-keyword-arguments args ((#:configure-flags flags) `(append (list ,(string-append "--target=" target)) (remove (cut string-match "--enable-languages.*" <>) ,flags) '("--with-newlib" "--without-headers"))) ((#:make-flags flags) `(append '("all-gcc" "all-target-libgcc") ,flags)) ((#:phases phases) `(modify-phases ,phases (add-before 'configure 'setenv (lambda* (#:key outputs inputs #:allow-other-keys) (let* ((gmp (assoc-ref inputs "gmp")) (mpfr (assoc-ref inputs "mpfr")) (mpc (assoc-ref inputs "mpc")) (zlib (assoc-ref inputs "zlib"))) (setenv "LD_LIBRARY_PATH" (string-append gmp "/lib" ":" mpfr "/lib" ":" mpc "/lib" ":" zlib "/lib")) (cond ((equal? ,target "i686-w64-mingw32") (let ((mingw-source (assoc-ref inputs "mingw-source")) (mingw-headers (string-append (getcwd) "/mingw-w64-v5.0-rc2/mingw-w64-headers"))) (system* "tar" "xf" mingw-source) (copy-file (string-append mingw-headers "/crt/_mingw.h.in") (string-append mingw-headers "/crt/_mingw.h")) (substitute* (string-append mingw-headers "/crt/_mingw.h") (("@MINGW_HAS_SECURE_API@") "#define MINGW_HAS_SECURE_API 1")) (setenv "CROSS_CPATH" (string-append mingw-headers "/include" ":" mingw-headers "/crt" ":" mingw-headers "/defaults/include")))))))) (replace 'install (lambda _ (zero? (system* "make" "install-gcc" "install-target-libgcc")))) (delete 'make-cross-binutils-visible) (delete 'validate-runpath)))))))) (define* (cross-libc target #:optional (xgcc (cross-gcc target)) (xbinutils (cross-binutils target))) "Return a libc cross-built for TARGET, a GNU triplet. Use XGCC and XBINUTILS and the cross tool chain." (cond ((equal? target "i686-w64-mingw32") mingw-w64) (else (let ((xlinux-headers (package (inherit linux-libre-headers) (name (string-append (package-name linux-libre-headers) "-cross-" target)) (arguments (substitute-keyword-arguments `(#:implicit-cross-inputs? #f ,@(package-arguments linux-libre-headers)) ((#:phases phases) `(alist-replace 'build (lambda _ (setenv "ARCH" ,(system->linux-architecture target)) (format #t "`ARCH' set to `~a' (cross compiling)~%" (getenv "ARCH")) (and (zero? (system* "make" "defconfig")) (zero? (system* "make" "mrproper" "headers_check")))) ,phases)))) (native-inputs `(("cross-gcc" ,xgcc) ("cross-binutils" ,xbinutils) ,@(package-native-inputs linux-libre-headers)))))) (package (inherit glibc) (name (string-append "glibc-cross-" target)) (arguments (substitute-keyword-arguments `(;; Disable stripping (see above.) #:strip-binaries? #f ;; This package is used as a target input, but it should not have ;; the usual cross-compilation inputs since that would include ;; itself. #:implicit-cross-inputs? #f ,@(package-arguments glibc)) ((#:configure-flags flags) `(cons ,(string-append "--host=" target) ,flags)) ((#:phases phases) `(alist-cons-before 'configure 'set-cross-linux-headers-path (lambda* (#:key inputs #:allow-other-keys) (let ((linux (assoc-ref inputs "linux-headers"))) (setenv "CROSS_CPATH" (string-append linux "/include")) #t)) ,phases)))) ;; Shadow the native "linux-headers" because glibc's recipe expects the ;; "linux-headers" input to point to the right thing. (propagated-inputs `(("linux-headers" ,xlinux-headers))) ;; FIXME: 'static-bash' should really be an input, not a native input, but ;; to do that will require building an intermediate cross libc. (inputs '()) (native-inputs `(("cross-gcc" ,xgcc) ("cross-binutils" ,xbinutils) ,@(package-inputs glibc) ;FIXME: static-bash ,@(package-native-inputs glibc)))))))) (define-public mingw-w64 (package (name "mingw-w64") (version "5.0-rc2") (source (origin (method url-fetch) (uri "https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/mingw-w64-v5.0-rc2.tar.bz2") (sha256 (base32 "0imdary8j07if8ih73pfgxiclpf2ax8h3mz8mxln07i8sbbd30c9")))) (build-system gnu-build-system) (search-paths (list (search-path-specification (variable "CROSS_CPATH") (files '("include" "i686-w64-mingw32/include"))) (search-path-specification (variable "CROSS_LIBRARY_PATH") (files '("lib" "lib64" "i686-w64-mingw32/lib" "i686-w64-mingw32/lib64"))))) (native-inputs `(("xgcc-core" ,xgcc-core-i686-w64-mingw32) ("xbinutils" ,xbinutils-i686-w64-mingw32) ;; FIXME: these do not propagate along ("gmp" ,gmp) ("mpfr" ,mpfr) ("mpc" ,mpc) ("zlib" ,zlib))) (arguments `(#:configure-flags (list "--host=i686-w64-mingw32" "--target=i686-w64-mingw32" "--with-as=i686-w64-mingw32-as" ;;"--with-cpp=i686-w64-mingw32-cpp" "--with-ld=i686-w64-mingw32-ld" "--with-nm=i686-w64-mingw32-nm") ;; FIXME: include _mingw_directx.h ? #:make-flags (list "DEFS=-DHAVE_CONFIG_H -D__MINGW_HAS_DXSDK=1") #:tests? #f ;; FIXME: compiles and includes glibc headers #:phases (modify-phases %standard-phases (add-before 'configure 'setenv (lambda _ (let (;; FIXME: these do not propagate along (xgcc-core (assoc-ref %build-inputs "xgcc-core")) (xbinutils (assoc-ref %build-inputs "xbinutils")) (gmp (assoc-ref %build-inputs "gmp")) (mpfr (assoc-ref %build-inputs "mpfr")) (mpc (assoc-ref %build-inputs "mpc")) (zlib (assoc-ref %build-inputs "zlib")) (mingw-headers (string-append (getcwd) "/mingw-w64-headers"))) ;; FIXME: LD_LIBRARY_PATH does not propagate along (setenv "LD_LIBRARY_PATH" (string-append gmp "/lib" ":" mpfr "/lib" ":" mpc "/lib" ":" zlib "/lib")) (format (current-error-port) "LD_LIBRARY_PATH:~a\n" (getenv "LD_LIBRARY_PATH")) (setenv "PATH" (string-append xbinutils "/" "i686-w64-mingw32" "/bin" ":" (getenv "PATH"))) (setenv "CPP" (string-append xgcc-core "/bin/i686-w64-mingw32-cpp")) (setenv "C_INCLUDE_PATH" (string-append ;;__MINGW_HAS_DXSDK 1 mingw-headers ":" mingw-headers "/include" ":" mingw-headers "/crt" ":" mingw-headers "/defaults/include" ":" mingw-headers "/direct-x/include")))))))) (home-page "http://mingw.org") (synopsis "Minimalist GNU for Windows") (description "MinGW provides a complete Open Source programming tool set which is suitable for the development of native MS-Windows applications, and which do not depend on any 3rd-party C-Runtime dlls.") (license license:fdl1.3+))) (define-public xgcc-core-i686-w64-mingw32 (let ((triplet "i686-w64-mingw32")) (cross-gcc-core triplet (cross-binutils triplet)))) (define-public xbinutils-i686-w64-mingw32 (let ((triplet "i686-w64-mingw32")) (cross-binutils triplet))) ;;; ;;; Concrete cross toolchains. ;;; (define-public xgcc-mips64el (let* ((triplet "mips64el-linux-gnuabi64") ;N64 ABI (xgcc (cross-gcc triplet (cross-binutils triplet) (cross-libc triplet)))) ;; Don't attempt to build this cross-compiler on i686; ;; see . (package (inherit xgcc) (supported-systems (fold delete (package-supported-systems xgcc) '("mips64el-linux" "i686-linux")))))) (define-public xgcc-avr ;; AVR cross-compiler, used to build AVR-Libc. (let ((triplet "avr")) (cross-gcc triplet (cross-binutils triplet)))) (define-public xgcc-xtensa ;; Bare-bones Xtensa cross-compiler, used to build the Atheros firmware. (cross-gcc "xtensa-elf")) (define-public xgcc-armhf (let* ((triplet "arm-linux-gnueabihf") (xgcc (cross-gcc triplet (cross-binutils triplet) (cross-libc triplet)))) (package (inherit xgcc) (supported-systems (delete "armhf-linux" %supported-systems))))) ;; (define-public xgcc-armel ;; (let ((triplet "armel-linux-gnueabi")) ;; (cross-gcc triplet ;; (cross-binutils triplet) ;; (cross-libc triplet)))) (define-public xgcc-i686-w64-mingw32 (let ((triplet "i686-w64-mingw32")) (cross-gcc triplet (cross-binutils triplet) #f))) (define-public xgcc-i686-w64-mingw32-sans-libc xgcc-i686-w64-mingw32)