distro: Bootstrap via a cross-toolchain.

This allows the final toolchain to be completely independent of
%BOOTSTRAP-INPUTS.

* distro/base.scm (glibc-dynamic-linker): New procedure.
  (gcc-4.7): Remove #:path-exclusions argument.  Check whether LIBC is
  #f before using it.
  (glibc): Remove "libc_cv_as_needed" hack.  Patch `Makeconfig' to
  remove `-lgcc_s'.
  (nix-system->gnu-triplet, boot-triplet): New variables.
  (binutils-boot0): Turn into a cross-Binutils targeting (boot-triplet
  SYSTEM).
  (gcc-boot0): Likewise.  Add configure options to make a smaller
  build.  Remove "binutils-source" from the input, and use
  BINUTILS-BOOT0 instead.
  (glibc-final): Cross-build using GCC-BOOT0 and BINUTILS-BOOT0.
  (gcc-boot0-wrapped): New variable.
  (%boot2-inputs): Use it.
  (m4-boot2, gmp-boot2, mpfr-boot2, mpc-boot2): Remove.
  (binutils-final): New variable.
  (gcc-final): Turn into a joint build with GMP/MPFR/MPC.  Use
  BINUTILS-FINAL.
  (%boot3-inputs): Adjust accordingly.
  (%boot4-inputs): Remove.
  (%final-inputs): Use %BOOT3-INPUTS.
This commit is contained in:
Ludovic Courtès 2012-09-11 17:36:21 +02:00
parent be13fbfa83
commit 321b099643
1 changed files with 240 additions and 147 deletions

View File

@ -21,6 +21,7 @@
#:use-module (guix packages) #:use-module (guix packages)
#:use-module (guix http) #:use-module (guix http)
#:use-module (guix build-system gnu) #:use-module (guix build-system gnu)
#:use-module (guix build-system trivial)
#:use-module (guix utils) #:use-module (guix utils)
#:use-module (srfi srfi-1) #:use-module (srfi srfi-1)
#:use-module (srfi srfi-26) #:use-module (srfi srfi-26)
@ -647,6 +648,12 @@ BFD (Binary File Descriptor) library, `gprof', `nm', `strip', etc.")
(license "GPLv3+") (license "GPLv3+")
(home-page "http://www.gnu.org/software/binutils/"))) (home-page "http://www.gnu.org/software/binutils/")))
(define (glibc-dynamic-linker system)
"Return the name of Glibc's dynamic linker for SYSTEM."
(if (string=? system "x86_64-linux")
"ld-linux-x86-64.so.2"
(error "dynamic linker name not known for this system" system)))
(define-public gcc-4.7 (define-public gcc-4.7
(let ((stripped? #t)) ; TODO: make this a parameter (let ((stripped? #t)) ; TODO: make this a parameter
(package (package
@ -671,26 +678,23 @@ BFD (Binary File Descriptor) library, `gprof', `nm', `strip', etc.")
`("--enable-plugin" `("--enable-plugin"
"--enable-languages=c,c++" "--enable-languages=c,c++"
"--disable-multilib" "--disable-multilib"
,(string-append "--with-native-system-header-dir=" ,(let ((libc (assoc-ref %build-inputs "libc")))
(assoc-ref %build-inputs "libc") (if libc
"/include")) (string-append "--with-native-system-header-dir=" libc
"/include")
"--without-headers")))
#:make-flags #:make-flags
(let ((libc (assoc-ref %build-inputs "libc"))) (let ((libc (assoc-ref %build-inputs "libc")))
`(,(string-append "LDFLAGS_FOR_BUILD=" `(,@(if libc
"-L" libc "/lib " (list (string-append "LDFLAGS_FOR_BUILD="
"-Wl,-dynamic-linker " "-L" libc "/lib "
"-Wl," libc "/lib/ld-linux-x86-64.so.2") "-Wl,-dynamic-linker "
"-Wl," libc
"/lib/ld-linux-x86-64.so.2"))
'())
,(string-append "BOOT_CFLAGS=-O2 " ,(string-append "BOOT_CFLAGS=-O2 "
,(if stripped? "-g0" "-g")))) ,(if stripped? "-g0" "-g"))))
;; Exclude libc from $LIBRARY_PATH since the compiler being used
;; should know where its libc is, and to avoid linking build tools
;; like `genhooks' against the wrong libc (for instance, when
;; building a gcc-for-glibc-2.16 with a gcc-for-glibc-2.13,
;; `genhooks' could end up being linked with glibc-2.16 but using
;; crt*.o from glibc-2.13.)
#:path-exclusions '(("LIBRARY_PATH" "libc"))
#:tests? #f #:tests? #f
#:phases #:phases
(alist-cons-before (alist-cons-before
@ -698,23 +702,29 @@ BFD (Binary File Descriptor) library, `gprof', `nm', `strip', etc.")
(lambda* (#:key inputs outputs #:allow-other-keys) (lambda* (#:key inputs outputs #:allow-other-keys)
(let ((out (assoc-ref outputs "out")) (let ((out (assoc-ref outputs "out"))
(libc (assoc-ref inputs "libc"))) (libc (assoc-ref inputs "libc")))
;; Fix the dynamic linker's file name. (when libc
(substitute* "gcc/config/i386/linux64.h" ;; The following is not performed for `--without-headers'
(("#define GLIBC_DYNAMIC_LINKER([^ ]*).*$" _ suffix) ;; cross-compiler builds.
(format #f "#define GLIBC_DYNAMIC_LINKER~a \"~a\"~%"
suffix
(string-append libc "/lib/ld-linux-x86-64.so.2"))))
;; Tell where to find libstdc++, libc, and `?crt*.o', except ;; Fix the dynamic linker's file name.
;; `crt{begin,end}.o', which come with GCC. (substitute* "gcc/config/i386/linux64.h"
(substitute* ("gcc/config/gnu-user.h" (("#define GLIBC_DYNAMIC_LINKER([^ ]*).*$" _ suffix)
"gcc/config/i386/gnu-user.h" (format #f "#define GLIBC_DYNAMIC_LINKER~a \"~a\"~%"
"gcc/config/i386/gnu-user64.h") suffix
(("#define LIB_SPEC (.*)$" _ suffix) (string-append libc "/lib/ld-linux-x86-64.so.2"))))
(format #f "#define LIB_SPEC \"-L~a/lib -rpath=~a/lib64 -rpath=~a/lib \" ~a~%"
libc out out suffix)) ;; Tell where to find libstdc++, libc, and `?crt*.o', except
(("([^ ]*)crt([^\\.])\\.o" _ prefix suffix) ;; `crt{begin,end}.o', which come with GCC.
(string-append libc "/lib/" prefix "crt" suffix ".o"))) ;; XXX: For crt*.o, use `STANDARD_STARTFILE_PREFIX' instead? See
;; <http://www.linuxfromscratch.org/lfs/view/stable/chapter05/gcc-pass1.html>.
(substitute* ("gcc/config/gnu-user.h"
"gcc/config/i386/gnu-user.h"
"gcc/config/i386/gnu-user64.h")
(("#define LIB_SPEC (.*)$" _ suffix)
(format #f "#define LIB_SPEC \"-L~a/lib -rpath=~a/lib64 -rpath=~a/lib \" ~a~%"
libc out out suffix))
(("([^ ]*)crt([^\\.])\\.o" _ prefix suffix)
(string-append libc "/lib/" prefix "crt" suffix ".o"))))
;; Don't retain a dependency on the build-time sed. ;; Don't retain a dependency on the build-time sed.
(substitute* "fixincludes/fixincl.x" (substitute* "fixincludes/fixincl.x"
@ -1229,10 +1239,6 @@ call interface, and powerful string processing.")
;; GNU libc for details. ;; GNU libc for details.
"--enable-kernel=2.6.30" "--enable-kernel=2.6.30"
;; To avoid linking with -lgcc_s (dynamic link) so the libc does
;; not depend on its compiler store path.
"libc_cv_as_needed=no"
;; XXX: Work around "undefined reference to `__stack_chk_guard'". ;; XXX: Work around "undefined reference to `__stack_chk_guard'".
"libc_cv_ssp=no") "libc_cv_ssp=no")
#:tests? #f ; XXX #:tests? #f ; XXX
@ -1250,7 +1256,14 @@ call interface, and powerful string processing.")
(("^\\$\\(inst_sysconfdir\\)/rpc(.*)$" _ suffix) (("^\\$\\(inst_sysconfdir\\)/rpc(.*)$" _ suffix)
(string-append out "/etc/rpc" suffix "\n")) (string-append out "/etc/rpc" suffix "\n"))
(("^install-others =.*$") (("^install-others =.*$")
(string-append "install-others = " out "/etc/rpc\n"))))) (string-append "install-others = " out "/etc/rpc\n")))
(substitute* "Makeconfig"
;; According to
;; <http://www.linuxfromscratch.org/lfs/view/stable/chapter05/glibc.html>,
;; linking against libgcc_s is not needed with GCC
;; 4.7.1.
((" -lgcc_s") ""))))
%standard-phases))) %standard-phases)))
(description "The GNU C Library") (description "The GNU C Library")
(long-description (long-description
@ -1339,39 +1352,75 @@ previous value of the keyword argument."
("findutils" ,findutils-boot0) ("findutils" ,findutils-boot0)
,@%bootstrap-inputs)) ,@%bootstrap-inputs))
(define* (nix-system->gnu-triplet system #:optional (vendor "unknown"))
"Return an a guess of the GNU triplet corresponding to Nix system
identifier SYSTEM."
(let* ((dash (string-index system #\-))
(arch (substring system 0 dash))
(os (substring system (+ 1 dash))))
(string-append arch
"-" vendor "-"
(if (string=? os "linux")
"linux-gnu"
os))))
(define boot-triplet
;; Return the triplet used to create the cross toolchain needed in the
;; first bootstrapping stage.
(cut nix-system->gnu-triplet <> "guix"))
;; Following Linux From Scratch, build a cross-toolchain in stage 0. That
;; toolchain actually targets the same OS and arch, but it has the advantage
;; of being independent of the libc and tools in %BOOTSTRAP-INPUTS, since
;; GCC-BOOT0 (below) is built without any reference to the target libc.
(define binutils-boot0
(package (inherit binutils)
(name "binutils-cross-boot0")
(arguments
(lambda (system)
`(#:implicit-inputs? #f
,@(substitute-keyword-arguments (package-arguments binutils)
((#:configure-flags cf)
`(list ,(string-append "--target=" (boot-triplet system))))))))
(inputs %boot0-inputs)))
(define gcc-boot0 (define gcc-boot0
(package (inherit gcc-4.7) (package (inherit gcc-4.7)
(name "gcc-boot0") (name "gcc-cross-boot0")
(arguments (arguments
`(#:implicit-inputs? #f (lambda (system)
`(#:implicit-inputs? #f
#:modules ((guix build gnu-build-system)
(guix build utils)
(ice-9 regex)
(srfi srfi-1)
(srfi srfi-26))
,@(substitute-keyword-arguments (package-arguments gcc-4.7)
((#:configure-flags flags)
`(append (list ,(string-append "--target="
(boot-triplet system))
,@(substitute-keyword-arguments (package-arguments gcc-4.7) ;; No libc yet.
((#:phases phases) "--without-headers"
(let ((binutils-name (package-full-name binutils)))
;; Disable features not needed at this stage.
"--disable-shared"
"--enable-languages=c"
"--disable-libmudflap"
"--disable-libgomp"
"--disable-libssp"
"--disable-libquadmath"
"--disable-decimal-float")
(remove (cut string-match "--enable-languages.*" <>)
,flags)))
((#:phases phases)
`(alist-cons-after `(alist-cons-after
'unpack 'unpack-binutils&co 'unpack 'unpack-gmp&co
(lambda* (#:key inputs #:allow-other-keys) (lambda* (#:key inputs #:allow-other-keys)
(let ((binutils (assoc-ref %build-inputs "binutils-source")) (let ((gmp (assoc-ref %build-inputs "gmp-source"))
(gmp (assoc-ref %build-inputs "gmp-source")) (mpfr (assoc-ref %build-inputs "mpfr-source"))
(mpfr (assoc-ref %build-inputs "mpfr-source")) (mpc (assoc-ref %build-inputs "mpc-source")))
(mpc (assoc-ref %build-inputs "mpc-source")))
;; We want to make sure feature tests like the
;; `.init_array' one really look at the linker we're
;; targeting (especially since our target Glibc requires
;; these features.) Thus, make a joint GCC/Binutils
;; build.
(or (zero? (system* "tar" "xvf" binutils))
(error "failed to unpack tarball" binutils))
;; By default, `configure' looks for `ld' under `ld', not
;; `binutils/ld'. Thus add an additional symlink. Also
;; add links for its dependencies, so it can find BFD
;; headers & co.
,@(map (lambda (tool)
`(symlink ,(string-append binutils-name "/" tool)
,tool))
'("bfd" "ld"))
;; To reduce the set of pre-built bootstrap inputs, build ;; To reduce the set of pre-built bootstrap inputs, build
;; GMP & co. from GCC. ;; GMP & co. from GCC.
@ -1403,24 +1452,28 @@ previous value of the keyword argument."
"-I" b "/mpfr/src")) "-I" b "/mpfr/src"))
(("gmplibs='-L([^ ]+)/mpfr" _ a) (("gmplibs='-L([^ ]+)/mpfr" _ a)
(string-append "gmplibs='-L" a "/mpfr/src"))))) (string-append "gmplibs='-L" a "/mpfr/src")))))
,phases)))))) (alist-cons-after
'install 'symlink-libgcc_eh
(lambda* (#:key outputs #:allow-other-keys)
(let ((out (assoc-ref outputs "out")))
;; Glibc wants to link against libgcc_eh, so provide
;; it.
(with-directory-excursion
(string-append out "/lib/gcc/"
,(boot-triplet system)
"/" ,(package-version gcc-4.7))
(symlink "libgcc.a" "libgcc_eh.a"))))
,phases)))))))
(inputs `(("binutils-source" ,(package-source binutils)) (inputs `(("gmp-source" ,(package-source gmp))
("gmp-source" ,(package-source gmp))
("mpfr-source" ,(package-source mpfr)) ("mpfr-source" ,(package-source mpfr))
("mpc-source" ,(package-source mpc)) ("mpc-source" ,(package-source mpc))
,@%boot0-inputs)))) ("binutils-cross" ,binutils-boot0)
(define binutils-boot0 ;; Call it differently so that the builder can check whether
;; Since Binutils in GCC-BOOT0 does not get installed, we need another one ;; the "libc" input is #f.
;; here. ("libc-native" ,@(assoc-ref %boot0-inputs "libc"))
(package (inherit binutils) ,@(alist-delete "libc" %boot0-inputs)))))
(name "binutils-boot0")
(arguments
`(#:implicit-inputs? #f
,@(package-arguments binutils)))
(inputs `(("gcc" ,gcc-boot0)
,@(alist-delete "gcc" %boot0-inputs)))))
(define linux-headers-boot0 (define linux-headers-boot0
(package (inherit linux-headers) (package (inherit linux-headers)
@ -1432,96 +1485,135 @@ previous value of the keyword argument."
(define %boot1-inputs (define %boot1-inputs
;; 2nd stage inputs. ;; 2nd stage inputs.
`(("gcc" ,gcc-boot0) `(("gcc" ,gcc-boot0)
("binutils" ,binutils-boot0) ("binutils-cross" ,binutils-boot0)
,@(fold alist-delete %boot0-inputs
'("gcc" "binutils")))) ;; Keep "binutils" here because the cross-gcc invokes `as', not the
;; cross-`as'.
,@%boot0-inputs))
(define-public glibc-final (define-public glibc-final
;; The final libc. ;; The final libc, "cross-built". If everything went well, the resulting
;; FIXME: It depends on GCC-BOOT0, which depends on some of ;; store path has no dependencies.
;; %BOOTSTRAP-INPUTS.
(package (inherit glibc) (package (inherit glibc)
(arguments (arguments
`(#:implicit-inputs? #f (lambda (system)
`(#:implicit-inputs? #f
;; Leave /bin/sh as the interpreter for `ldd', `sotruss', etc. to ;; Leave /bin/sh as the interpreter for `ldd', `sotruss', etc. to
;; avoid keeping a reference to the bootstrap Bash. ;; avoid keeping a reference to the bootstrap Bash.
#:patch-shebangs? #f #:patch-shebangs? #f
,@(substitute-keyword-arguments (package-arguments glibc) ,@(substitute-keyword-arguments (package-arguments glibc)
((#:configure-flags flags) ((#:configure-flags flags)
`(cons "BASH_SHELL=/bin/sh" ,flags))))) `(append (list ,(string-append "--host=" (boot-triplet system))
,(string-append "--build="
(nix-system->gnu-triplet system))
"BASH_SHELL=/bin/sh"
;; cross-rpcgen fails to build, because it gets
;; built with the cross-compiler instead of the
;; native compiler. See also
;; <http://sourceware.org/ml/libc-alpha/2012-03/msg00325.html>.
"--disable-obsolete-rpc")
,flags))))))
(propagated-inputs `(("linux-headers" ,linux-headers-boot0))) (propagated-inputs `(("linux-headers" ,linux-headers-boot0)))
(inputs %boot1-inputs))) (inputs `(;; A native GCC is needed to build `cross-rpcgen'.
("native-gcc" ,@(assoc-ref %boot0-inputs "gcc"))
,@%boot1-inputs))))
(define gcc-boot0-wrapped
;; Make the cross-tools GCC-BOOT0 and BINUTILS-BOOT0 available under the
;; non-cross names.
(package (inherit gcc-4.7)
(name (string-append (package-name gcc-boot0) "-wrapped"))
(build-system trivial-build-system)
(arguments
(lambda (system)
`(#:modules ((guix build utils))
#:builder (begin
(use-modules (guix build utils))
(let* ((binutils (assoc-ref %build-inputs "binutils"))
(gcc (assoc-ref %build-inputs "gcc"))
(libc (assoc-ref %build-inputs "libc"))
(out (assoc-ref %outputs "out"))
(bindir (string-append out "/bin"))
(triplet ,(boot-triplet system)))
(mkdir out) (mkdir bindir)
(with-directory-excursion bindir
(for-each (lambda (tool)
(symlink (string-append binutils "/bin/"
triplet "-" tool)
tool))
'("ar" "ranlib"))
;; GCC-BOOT0 is a libc-less cross-compiler, so it
;; needs to be told where to find the crt files and
;; the dynamic linker.
(call-with-output-file "gcc"
(lambda (p)
(format p "#!/bin/sh
exec ~a/bin/~a-gcc -B~a/lib -Wl,-dynamic-linker -Wl,~a/lib/~a \"$@\"~%"
gcc triplet
libc libc
,(glibc-dynamic-linker system))))
(chmod "gcc" #o555)))))))
(native-inputs
`(("binutils" ,binutils-boot0)
("gcc" ,gcc-boot0)
("libc" ,glibc-final)))
(inputs '())))
(define %boot2-inputs (define %boot2-inputs
;; 3rd stage inputs. ;; 3rd stage inputs.
`(("libc" ,glibc-final) `(("libc" ,glibc-final)
,@(alist-delete "libc" %bootstrap-inputs))) ("gcc" ,gcc-boot0-wrapped)
,@(fold alist-delete %boot1-inputs '("libc" "gcc"))))
(define m4-boot2 (define binutils-final
(package (inherit m4) (package (inherit binutils)
(name "m4-boot2")
(arguments (lambda (system)
`(#:implicit-inputs? #f
,@((package-arguments m4) system))))
(inputs `(,@(package-inputs m4)
,@%boot2-inputs))))
(define gmp-boot2
(package (inherit gmp)
(name "gmp-boot2")
(arguments (arguments
`(#:implicit-inputs? #f (lambda (system)
,@(package-arguments gmp))) `(#:implicit-inputs? #f
(native-inputs `(("m4" ,m4-boot2) ,@(package-arguments binutils))))
,@%boot2-inputs)))) (inputs %boot2-inputs)))
(define mpfr-boot2 (define-public gcc-final
(package (inherit mpfr) ;; The final GCC.
(name "mpfr-boot2") (package (inherit gcc-boot0)
(name "gcc")
(arguments (arguments
`(#:implicit-inputs? #f (lambda (system)
,@(package-arguments mpfr))) `(#:implicit-inputs? #f
(inputs `(("gmp" ,gmp-boot2)
,@%boot2-inputs))))
(define mpc-boot2 ;; Build again GMP & co. within GCC's build process, because it's hard
(package (inherit mpc) ;; to do outside (because GCC-BOOT0 is a cross-compiler, and thus
(name "mpc-boot2") ;; doesn't honor $LIBRARY_PATH, which breaks `gnu-build-system'.)
(arguments ,@(substitute-keyword-arguments ((package-arguments gcc-boot0) system)
`(#:implicit-inputs? #f ((#:configure-flags boot-flags)
,@(package-arguments mpc))) (let loop ((args (package-arguments gcc-4.7)))
(inputs `(("gmp" ,gmp-boot2) (match args
("mpfr" ,mpfr-boot2) ((#:configure-flags normal-flags _ ...)
normal-flags)
((_ rest ...)
(loop rest)))))
((#:phases phases)
`(alist-delete 'symlink-libgcc_eh ,phases))))))
(inputs `(("gmp-source" ,(package-source gmp))
("mpfr-source" ,(package-source mpfr))
("mpc-source" ,(package-source mpc))
("binutils" ,binutils-final)
,@%boot2-inputs)))) ,@%boot2-inputs))))
(define %boot3-inputs (define %boot3-inputs
;; 4th stage inputs. ;; 4th stage inputs.
`(("libc" ,glibc-final)
("gmp" ,gmp-boot2)
("mpfr" ,mpfr-boot2)
("mpc" ,mpc-boot2)
,@(fold alist-delete
%boot2-inputs
'("libc" "gmp" "mpfr" "mpc"))))
(define-public gcc-final
;; The final GCC.
(package (inherit gcc-4.7)
(name "gcc")
(arguments `(#:implicit-inputs? #f
,@(package-arguments gcc-4.7)))
(inputs %boot3-inputs)))
(define %boot4-inputs
;; 5th stage inputs.
`(("gcc" ,gcc-final) `(("gcc" ,gcc-final)
,@(fold alist-delete %boot3-inputs ,@(alist-delete "gcc" %boot2-inputs)))
'("gcc" "gmp" "mpfr" "mpc"))))
(define-public %final-inputs (define-public %final-inputs
;; Final derivations used as implicit inputs by `gnu-build-system'. ;; Final derivations used as implicit inputs by `gnu-build-system'.
(let ((finalize (cut package-with-explicit-inputs <> %boot4-inputs (let ((finalize (cut package-with-explicit-inputs <> %boot3-inputs
(current-source-location)))) (current-source-location))))
`(,@(map (match-lambda `(,@(map (match-lambda
((name package) ((name package)
@ -1538,10 +1630,10 @@ previous value of the keyword argument."
("bash" ,bash) ("bash" ,bash)
("findutils" ,findutils) ("findutils" ,findutils)
("gawk" ,gawk) ("gawk" ,gawk)
("make" ,gnu-make) ("make" ,gnu-make)))
("binutils" ,binutils))) ("binutils" ,binutils-final)
("gcc" ,gcc-final) ("gcc" ,gcc-final)
("glibc" ,glibc-final)))) ("libc" ,glibc-final))))
;;; ;;;
@ -1702,4 +1794,5 @@ beginning.")
;;; eval: (put 'substitute* 'scheme-indent-function 1) ;;; eval: (put 'substitute* 'scheme-indent-function 1)
;;; eval: (put 'with-directory-excursion 'scheme-indent-function 1) ;;; eval: (put 'with-directory-excursion 'scheme-indent-function 1)
;;; eval: (put 'package 'scheme-indent-function 1) ;;; eval: (put 'package 'scheme-indent-function 1)
;;; eval: (put 'substitute-keyword-arguments 'scheme-indent-function 1)
;;; End: ;;; End: