;;; GNU Guix --- Functional package management for GNU ;;; ;;; Copyright © 2023 Stefan ;;; ;;; This file is not 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 (embedded) #:use-module (gnu packages) #:use-module (gnu packages autotools) #:use-module (gnu packages autotools) #:use-module (gnu packages base) #:use-module ((gnu packages bootstrap) #:select (glibc-dynamic-linker)) #:use-module (gnu packages commencement) #:use-module (gnu packages compression) #:use-module (gnu packages dejagnu) #:use-module (gnu packages elf) #:use-module (gnu packages flex) #:use-module (gnu packages gcc) #:use-module (gnu packages gdb) #:use-module (gnu packages multiprecision) #:use-module (gnu packages perl) #:use-module (gnu packages pkg-config) #:use-module (gnu packages texinfo) #:use-module (guix build gnu-build-system) #:use-module ((guix build utils) #:select (alist-replace modify-phases)) #:use-module (guix build-system) #:use-module (guix build-system gnu) #:use-module (guix build-system meson) #:use-module (guix build-system trivial) #:use-module (guix download) #:use-module (guix gexp) #:use-module (guix git-download) #:use-module ((guix licenses) #:prefix license:) #:use-module (guix packages) #:use-module (guix utils) #:use-module (ice-9 optargs) #:use-module ((ice-9 string-fun) #:select (string-replace-substring)) #:use-module ((srfi srfi-1) #:select (second))) (define-public gcc-phase-fix-environment "This is a build-phase for the GCC compilation to fix environment variables. This is mostly necessary because Guix places standard headers into the C_INCLUDE_PATH variable and others. Ideally this phase would not be needed." #~(lambda* (#:key inputs #:allow-other-keys) (use-modules (srfi srfi-1)) ;; For cross-compilers the phase make-dynamic-linker-cache needs to find ;; the native ldconfig executable. (setenv "PATH" (string-append (getenv "PATH") ":" #$glibc "/bin")) (setenv "LD_LIBRARY_PATH" ;; The built GCC will have a workaround to ensure that glibc will always ;; find libgcc_s.so. Unfortunately during the configuration of libatomic ;; and other libraries, the libgcc_s.so is not yet available in its final ;; installation directory and this workaround causes trouble to the ;; configure script during "checking whether we are cross compiling". As ;; a mitigation we set LD_LIBRARY_PATH to the location of the not yet ;; installed libgcc_s.so. This would not be necessary, if glibc had a ;; reference to the GCC which has beed used to build glibc itself. (string-append (getcwd) "/build/gcc")) (format #t "environment variable `LD_LIBRARY_PATH' set to `~a'~%" (getenv "LD_LIBRARY_PATH")) (for-each (lambda (include-path-env) (when (getenv include-path-env) (let* ((libc (assoc-ref inputs "libc")) (gcc (assoc-ref inputs "gcc")) ; This is different to #$gcc! (paths-to-delete (map (lambda (package) (string-append package "/include")) (filter-map identity (list libc gcc))))) (setenv include-path-env ;; The gcc package used by the gnu-build-system to build this GCC ;; puts the include paths to the C++ headers and to the libc ;; headers into CPLUS_INCLUDE_PATH and OBJCPLUS_INCLUDE_PATH. ;; This causes trouble in the GCC build process when -nostdinc++ ;; is used. As a mitigation we remove them. This would not be ;; necessary, when using this built GCC instead. ;; Actually the same problem exists with C_INCLUDE_PATH and ;; OBJC_INCLUDE_PATH. Just by chance it makes no troubles. (string-join (remove (lambda (path) (member path paths-to-delete)) (string-split (getenv include-path-env) #\:)) ":")) (format #t "environment variable `~a' set to `~a'~%" include-path-env (getenv include-path-env))))) '("C_INCLUDE_PATH" "CPLUS_INCLUDE_PATH" "OBJC_INCLUDE_PATH" "OBJCPLUS_INCLUDE_PATH")))) (define*-public (make-gcc-phase-pre-configure #:key (libc glibc) (startfile-dir "lib")) "Make a build-phase for the GCC compilation to modify the source-code. Use the LIBC package for embedded search-paths. The path DYNAMIC-LINKER is appended to LIBC to form the absolute path to the dynamic-linker. The STARTFILE-DIR is a suffix for LIBC to form the path to startfiles like crt1.o from glibc or crt0.o from newlib. All default values match for glibc. For newlib set STARTFILE-DIR to (string-append target \"/lib\"). If LIBC is #f, then $output:lib is siffixed with STARTFILE-DIR." ;; In case libc is #f, we still need a valid path for substitutions. (let* ((libc (or libc #~#$output:lib)) (startfile-dir (string-append "/" startfile-dir "/")) (startfile-dir (string-replace-substring startfile-dir "//" "/"))) #~(lambda _ ;; When building a cross-compiler the path #$output:lib/include given ;; via --includedir to configure is expected to already exist when ;; building the stmp-fixinc target. But #$output:lib is only created ;; later in the install phase. So we create the path here to prevent ;; the following error: ;; The directory (BUILD_SYSTEM_HEADER_DIR) that should contain system ;; headers does not exist: ;; /gnu/store/…-GCC-cross-…-lib/include ;; See also . (mkdir-p (string-append #$output:lib "/include")) (substitute* "Makefile.in" ;; Don't store configure arguments, to avoid a cyclic reference in ;; #$output:lib/lib/gcc/…/include/configargs.h back to #$output and ;; retaining references to build-time dependencies like bash-minimal. (("@TOPLEVEL_CONFIGURE_ARGUMENTS@") "")) (substitute* (find-files "gcc/config") ;; Enforce any /lib64, /lib32, /libx32, /libo32 directory to just be ;; /lib. There is no other posibility than patching, even ;; --disable-multiarch does not help. Both Debian and LFS patch, too. ;; See . (("/lib[xo]?(64|32)") "/lib")) (substitute* "gcc/genmultilib" ;; Enforce proper invokations of sh. (("#! */bin/sh") (string-append "#!" (which "sh")))) (substitute* "gcc/gcc.cc" ;; The STARTFILE_PREFIX_SPEC defines where to find crt1.o and ;; other start files of libc. Replace it with a proper path to ;; the libc. Note: This path is relative to the sysroot which ;; therefore must be /. (("startfile_prefix_spec = STARTFILE_PREFIX_SPEC;") (string-append "startfile_prefix_spec = \"" #$libc #$startfile-dir "\";"))) (substitute* "libstdc++-v3/python/Makefile.in" ;; Change pythondir from #$output:out to #$output:lib to prevent ;; #$output:lib/lib/libstdc++.*-gdb.py to create a cyclic dependency ;; to #$output:out/share/…/python. This moves all python files to ;; #$output:lib. The configure option --with-python-dir stays usable. (("pythondir = \\$\\(datadir\\)") "pythondir = $(libdir)/share") (("pythondir = \\$\\(prefix\\)") "pythondir = $(libdir)")) #;(substitute* "libsanitizer/asan/asan_linux.cpp" ;; Ensure PATH_MAX is defined by including the proper header file. ;; See and ;; . ;; The gcc package used by the gnu-build-system is to blame here. ;; This patch is necessary when using --disable-bootstrap. (("#include ") #$(string-join (list "#include " "#if SANITIZER_LINUX" "#include " "#endif") "\n")))))) (define*-public (make-gcc-configure-flags #:key (libc glibc) (dynamic-linker (glibc-dynamic-linker)) (binutils binutils) (ld-wrapper ld-wrapper) (kernel-headers (car (assoc-ref (package-propagated-inputs glibc) "kernel-headers"))) #:allow-other-keys) "Make configure-flags for the GCC compilation referring to the LIBC and KERNEL-HEADERS packages. The defaults refer to glibc and the kernel-headers used to build glibc." #~(list ;; Prevent the built gcc to build itself again to save time. #;"--disable-bootstrap" ;; Prevent the generation of useless dependency files. "--disable-dependency-tracking" ;; Save space by disabling pre-compiled libstdc++ headers. "--disable-libstdcxx-pch" ;; Multiarch support is not a topic for Guix. "--disable-multiarch" ;; As libc is limited, gcc will not be usable for 32 and 64 bit builds. "--disable-multilib" ;; Disable all language frontends except for C, C++, Objective-C and ;; Objective-C++. "--enable-languages=c,c++,objc,obj-c++" ;; Avoid parallel linking to not crash on systems with limited memory. "--enable-link-serialization" ;; Prevent the C++ headers in #$output:lib, put them in #$output:out ;; instead. Use an unconventional path to prevent it from being added to ;; the environment variables C_INCLUDE_PATH, CPLUS_INCLUDE_PATH and ;; OBJC_INCLUDE_PATH, OBJCPLUS_INCLUDE_PATH. ;; TODO Will this work with clang? "--with-gxx-include-dir=$(prefix)/include-c++" ;; The first set of include paths consists of #$output/include-c++/… and ;; #$output:lib/lib/…/include. Second is usually /usr/local/include, which ;; is documented to be avoided, if the same value is used for both --prefix ;; and --with-local-prefix. (string-append "--with-local-prefix=" #$output) ;; Third set is #$output:lib/…/include-fixed, which expects #$libc/include ;; and #$kernel-headers/include to follow in this order. ;; Fourth and usually the last include path is /usr/include containing all ;; system headers. It is only possible to specify one path for this. Set ;; the #$libc/include path and prevent the use of /usr/include. (string-append "--with-native-system-header-dir=" #$libc "/include") ;; The final include path has to be #$kernel-headers/include, which has to ;; be after #$libc/include. There is only -idirafter left to achieve this. (string-append "--with-specs=" ;; Embed the path to the kernel-headers. "-idirafter " #$kernel-headers "/include " ;; Embed the program search path to the #$ld-wrapper before the binutils. "-B" #$ld-wrapper "/bin " ;; Embed the program search path to the #$binutils. "-B" #$binutils "/bin " ;; Instead of using the -B options below for #$output:lib/lib, #$libc/lib ;; and #$libc:static/lib, -L could be used. The -L option has the ;; advantage to only add a library search path, which is enough here. ;; However, as these paths are important when using --enable-multilib ;; (which is nomally the default) and as the multilib mechanism relies on ;; the use of -B (which is not clearly documented), we stick to the -B ;; option despite its disadvantage to add to program and header search ;; paths as well. ;; Embed the link-time search path to libgcc_s.so, libstdc++.so, etc. "-B" #$output:lib "/lib " ;; Embed the link-time search paths to #$libc/lib and #$libc:static/lib. "-B" #$libc "/lib -B" #$libc:static "/lib " ;; Embed the dynamic-linker to use. Otherwise files in gcc/config need ;; to be patched. This is a much simpler solution. "%x{--dynamic-linker=" #$libc #$dynamic-linker "} " ;; Embed the runtime search path to #$output:lib/lib and link libgcc_s.so, ;; unless statically linking or suppressing to link it with the ;; -nodefaultlibs or -nostdlib options, except -shared-libgcc has been ;; passed, although its documented for -nostdlib and -nodefaultlibs that ;; -shared-libgcc (and -static-libgcc) will be ignored and -lgcc_s (or - ;; lgcc) must be used. GCC itself uses -shared-libgcc, -nostdlib and ;; -lgcc_s to build libstdc++.so. Unfortunately it is impossible in specs ;; to check for -l options, as these are handled as compiler output files ;; instead. Therefore we check for -shared-libgcc as an exception to link ;; libstdc++.so with the needed -rpath option. We cannot rely on the ;; ld-wrapper, as the libgcc_s.so file is only installed later, when ;; libstdc++.so has been built. Linking libgcc_s.so whenever possible is ;; a workaround to ensure the pesence of a pre-loaded libgcc_s.so for GNU ;; libc. Otherwise the GNU libc may fail to load libgcc_s.so via dlopen() ;; during calls of e.g. pthread_exit() or pthread_cancel(). We do not ;; check for the option -static-libgcc, as the dlopen() call can happen ;; anyway. Actually the GNU libc needs a DT_RUNPATH entry to libgcc_s.so ;; of the GCC package used to build it. This is missing and may requires ;; a fix. "%{static|nodefaultlibs|nostdlib:" "%{shared-libgcc:%x{-rpath=" #$output:lib "/lib}}; " ":%x{-rpath=" #$output:lib "/lib} %x{-lgcc_s}}") ;; Use the zlib package instead of the bundled zlib. "--with-system-zlib")) (define-public (make-gcc phase-pre-configure configure-flags) "Make a GCC package using the PHASE-PRE-CONFIGURE and the CONFIGURE-FLAGS in the build-process." (package (name "GCC") (version "14.2.0") (source (origin (method url-fetch) (uri (string-append "mirror://gnu/gcc/gcc-" version "/gcc-" version ".tar.xz")) (sha256 (base32 "1j9wdznsp772q15w1kl5ip0gf0bh8wkanq2sdj12b7mzkk39pcx7")) (patches (search-patches "gcc-12-strmov-store-file-names.patch")))) (build-system gnu-build-system) (outputs '("out" "lib" "debug")) ;; The packages zstd and elfutils are needed for LTO support. (inputs (list elfutils gmp isl mpfr mpc zlib zstd)) ;; To generate man pages and info documents, perl and texinfo are needed. (native-inputs (list perl texinfo)) (arguments (list #:tests? #f #:out-of-source? #t #:configure-flags configure-flags #:phases #~(modify-phases %standard-phases (add-after 'set-paths 'fix-environment #$gcc-phase-fix-environment) (add-before 'configure 'pre-configure #$phase-pre-configure) (add-after 'install 'remove-install-tools (lambda _ ;; The programs in install-tools try to modify the store and have ;; retaining references to sed and bash-minimal. They are useless ;; in Guix and can be deleted to reduce references. (for-each (lambda (directory) (delete-file-recursively (car (find-files directory "^install-tools$" #:directories? #t)))) (list #$output #$output:lib))))))) (native-search-paths (list (search-path-specification (variable "C_INCLUDE_PATH") (files (list "include"))) (search-path-specification (variable "CPLUS_INCLUDE_PATH") (files (list "include"))) (search-path-specification (variable "OBJC_INCLUDE_PATH") (files (list "include"))) (search-path-specification (variable "OBJCPLUS_INCLUDE_PATH") (files (list "include"))) (search-path-specification (variable "LIBRARY_PATH") (files (list "lib"))))) (synopsis "GNU Compiler Collection") (description "The GNU Compiler Collection includes front ends for C, C++, Objective-C (optionally Fortran, Ada, Go, D and Modula-2) as well as libraries for these languages (libstdc++, …).") (home-page "https://gcc.gnu.org/") (license license:gpl3+))) (define-public GCC (make-gcc (make-gcc-phase-pre-configure) (make-gcc-configure-flags))) (define-public guix-locpath (package (name "guix-locpath") (version "1.0") (source #f) (build-system trivial-build-system) (arguments (list #:builder #~(mkdir #$output))) (native-search-paths (list (search-path-specification (variable "GUIX_LOCPATH") (files '("lib/locale"))))) (home-page #f) (synopsis "Access for glibc to locales") (description "The guix-locpath package sets the environment variable GUIX_LOCPATH to make all locale related functions of glibc usable without propagating glibc itself. This is usefull to prevent glibc include paths to be exposed via C_INCLUDE_PATH and similar environment variables, to keep a defined include order with embedded paths in GCC to glibc.") (license (package-license glibc-utf8-locales)))) (define*-public (make-c-toolchain gcc binutils #:optional ld-wrapper #:key (propagated-inputs '())) "Make a C-toolchain consisting of the packages GCC, BINUTILS, guix-locpath and optionally LD-WRAPPER. The result can be used by the transformation function 'package-with-c-toolchain' and to build a GCC-toolchain package with 'make-gcc-toolchain-package'. The guix-locpath package is used instead of the glibc package to prevent glibc and the kernel-headers from appearing in the C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, and similar environment variables. The GCC package is expected to have the necessary include paths built-in to preserve a necessary include-order. The GCC package is expected to know the paths to LD-WRAPPER and BINUTILS and therefore neither needs to be part of the created c-toolchain. However, some packages my invoke the assembler or linker directly, so these packages still need to be accessible via the PATH environment variable. The PROPAGATED_INPUTS argument is an alist of package-names and packages which take percedence over the usual toolchain packages. This might be needed to override tools from the toolchain." (append propagated-inputs (if ld-wrapper ;; The ld-wrapper has to be in front of binutils. (list (list "ld-wrapper" ld-wrapper)) '()) (list (list "binutils" binutils) (list "guix-locpath" guix-locpath) (list "gcc" gcc)))) (define-public gcc-c-toolchain (make-c-toolchain GCC binutils ld-wrapper)) (define-public (c-toolchain-input-package c-toolchain name) "Get the input package named NAME from the C-TOOLCHAIN package list." (car (assoc-ref c-toolchain name))) (define-public (make-gcc-toolchain-package c-toolchain) "Make a GCC-toolchain package from C-TOOLCHAIN. The C-TOOLCHAIN argument must be a list of inputs (label/package tuples) providing equivalent functionality as the 'gcc-toolchain' package as passed to 'package-with-c-toolchain'." (let ((gcc (c-toolchain-input-package c-toolchain "gcc"))) (package (name (string-append (package-name gcc) "-toolchain")) (version (package-version gcc)) (source #f) (build-system trivial-build-system) (arguments (list #:modules '((guix build union)) #:builder #~(begin (use-modules ((guix build union))) (union-build #$output (quote #$(map second c-toolchain)))))) (synopsis "Complete GCC toolchain for C/C++/Objective-C development") (description "This package provides a complete GCC toolchain for C/C++/Objective-C development to be installed in user profiles. This includes GCC and Binutils. GCC is the GNU Compiler Collection.") (home-page "https://gcc.gnu.org/") (license license:gpl3+)))) (define-public GCC-toolchain (make-gcc-toolchain-package gcc-c-toolchain)) (define*-public (make-binutils #:optional target) (package-with-c-toolchain (package (name (string-append "Binutils" (if target (string-append "-" target) ""))) (version "2.42") (source (origin (method url-fetch) (uri (string-append "mirror://gnu/binutils/binutils-" version ".tar.bz2")) (sha256 (base32 "04mgvzgsdcksm7cm0qs8j4ljb562asqdkhjfrmr4q1m5pl78am5a")) ;; The binutils-mingw-w64-timestamp.patch can't be applied. (patches (search-patches "binutils-mingw-w64-deterministic.patch")))) (build-system gnu-build-system) (arguments (list #:out-of-source? #t ; Recommended in binutils/README. #:phases #~(modify-phases %standard-phases (add-after 'unpack 'fix-testsuite (lambda _ ;; The configure flag --enable-deterministic-archives makes the ;; tests "replacing non-deterministic member" and ;; "replacing SOURCE_DATE_EPOCH deterministic member" fail due to ;; unexpected output. To fix the relevant parts we need to ;; temporarily concatenate two lines to limit the substitution. (substitute* "binutils/testsuite/binutils-all/ar.exp" (("^( set got \\[binutils_run \\$AR \"ru \\$archive \\$older_objfile\"\\])\n" _ first-line-without-newline) first-line-without-newline)) ;; Now do the actually needed substitutions. (substitute* "binutils/testsuite/binutils-all/ar.exp" ;; Correct the output to be a warning instead of being empty. (("^( set got \\[binutils_run \\$AR \"ru \\$archive \\$older_objfile\"\\]) .*$" _ first-line-without-newline) (string-append first-line-without-newline "\n if ![string match \"*`u' modifier ignored since `D' is the default*\" $got] {\n")) ;; With --enable-deterministic-archives ar takes the older ;; instead of the newer object file in the test ;; "replacing non-deterministic member". (("^ # set older_length ") " set older_length ") (("(\\$\\{?)newer_length" _ dereference) (string-append dereference "older_length"))) (substitute* "gold/testsuite/Makefile.in" ;; Embed the runtime search paths to #$zlib/lib and ;; #$zstd:lib/lib. (("_unittest_LDFLAGS =") (string-append "_unittest_LDFLAGS =" " -Wl,--rpath=" #$zlib "/lib" " -Wl,--rpath=" #$zstd:lib "/lib")))))) #:configure-flags #~(quote #$(cons* ;; Speed up the build. "--disable-dependency-tracking" ;; Disable separate-code as the testsuite unveils Arm bugs in ld: ;; FAIL: R_ARM_THM_JUMP24 Relocation veneers: Short 1 ;; FAIL: Cortex-A8 erratum fix, headers ;; FAIL: Cortex-A8 erratum fix, relocate bl.w to PLT ;; FAIL: Thumb only PLT and GOT ;; FAIL: Thumb only PLT and GOT LSB Symbol ;; TODO Use --enable-separat-code maybe with -rosegment with 2.43. "--disable-separate-code" ;; Build a 64-bit bfd lib also on 32-bit hosts. "--enable-64-bit-bfd" ;; Enable compressed debug sections with all supported formats. "--enable-compressed-debug-sections=all" ;; Ensure that generated archives will be reproducable. "--enable-deterministic-archives" ;; Build the faster gold linker. "--enable-gold" ;; Build gprofng. "--enable-gprofng" ;; Install the built libraries and headers. "--enable-install-libbfd" ;; Enable the use of DT_RUNPATH by default for elf targets. "--enable-new-dtags" ;; Enable support for plugins. "--enable-plugins" ;; Build shared libraries. "--enable-shared" ;; Enable multi-threaded linking in gold. "--enable-threads" ;; Enforce an empty LIB_PATH, a colon is the documented method, ;; to avoid the usual default of /lib:/usr/lib:/usr/local/lib. "--with-lib-path=:" ;; All paths to --with-… options are relative to the sysroot. As ;; store paths are absolute, the sysroot needs to be /. This is ;; also needed to make cross ld find absolute DT_RUNPATH entries ;; when searching for needed libraries, which are only searched ;; relative to the configured sysroot directory or not at all. ;; See . ;; This is also documented in the ld manual for the -rpath option: ;; "Searching -rpath in this way is only supported by native ;; linkers and cross linkers which have been configured with the ;; --with-sysroot option." "--with-sysroot=/" ;; Use the zlib package instead of the bundled zlib. "--with-system-zlib" ;; Support zstd compressed debug sections. "--with-zstd" (if target (list ;; Build for a specific target. Actually the configure ;; option --enable-targets=all could build executables usable ;; for any target, but this is not yet supported by the GNU ;; assembler. (string-append "--target=" target)) '()))))) (native-inputs (list perl pkg-config texinfo dejagnu)) (inputs (list zlib (list zstd "lib"))) (synopsis "The GNU binary utilities ld, as, gold and others") (description "The GNU Binutils are a collection of binary tools. The main ones are @itemize @item ld - the GNU linker @item as - the GNU assempler @item gold - a new, faster, elf only linker. @end itemize But they also include: @itemize @item addr2line - Converts addresses into filenames and line numbers. @item ar - A utility for creating, modifying and extracting from archives. @item c++filt - Filter to demangle encoded C++ symbols. @item dlltool - Creates files for building and using DLLs. @item elfedit - Allows alteration of ELF format files. @item gprof - Displays profiling information. @item gprofng - Collects and displays application performance data. @item nlmconv - Converts object code into an NLM. @item nm - Lists symbols from object files. @item objcopy - Copies and translates object files. @item objdump - Displays information from object files. @item ranlib - Generates an index to the contents of an archive. @item readelf - Displays information from any ELF format object file. @item size - Lists the section sizes of an object or archive file. @item strings - Lists printable strings from files. @item strip - Discards symbols. @item windmc - A Windows compatible message compiler. @item windres - A compiler for Windows resource files. @end itemize As well as some libraries: @itemize @item libbfd - A library for manipulating binary files in a variety of different formats. @item libctf - A library for manipulating the CTF debug format. @item libopcodes - A library for assembling and disassembling a variety of different assembler languages. @item libsframe - A library for manipulating the SFRAME debug format. @end itemize Most of these programs use BFD, the Binary File Descriptor library, to do low-level manipulation. Many of them also use the opcodes library to assemble and disassemble machine instructions.") (license license:gpl3+) (home-page "https://www.gnu.org/software/binutils/")) gcc-c-toolchain)) (define-public Binutils-arm-none-eabi (make-binutils "arm-none-eabi")) (define*-public (make-gcc-cross-sans-libc-configure-flags #:key target (configure-flags '()) binutils #:allow-other-keys) "Make configure-flags to build a GCC cross-compiler for TARGET without a libc, which can be used to build a C-library as newlib or picolibc. Additional CONFIGURE-FLAGS can be used for target or C-library specific GCC options." #~(cons* ;; Disable anything not needed to build a libc. "--disable-decimal-float" ;; Prevent the generation of useless dependency files. "--disable-dependency-tracking" ;; The libstdc++ requires a libc for a hosted build, which we do not have. "--disable-hosted-libstdcxx" ;; Disable anything not needed to build a libc. "--disable-libatomic" "--disable-libgomp" "--disable-libitm" "--disable-libmpx" "--disable-libmudflap" "--disable-libquadmath" "--disable-libsanitizer" "--disable-libssp" ;; Save space by disabling pre-compiled libstdc++ headers. "--disable-libstdcxx-pch" "--disable-libvtv" ;; Multiarch support is not a topic for Guix. "--disable-multiarch" ;; Disable anything not needed to build a libc. "--disable-shared" "--disable-threads" ;; Disable all language frontends except for C and C++. "--enable-languages=c,c++" ;; Avoid parallel linking to not crash on systems with limited memory. "--enable-link-serialization" ;; Enable support for thread-local-storage. "--enable-tls" (string-append "--target=" #$target) ;; All paths to --with-… options are relative to the sysroot. As store ;; paths are absolute and to get --without-headers working properly, ;; --with-sysroot must not be used, otherwise --with-newlib is needed. ;; The first set of include paths consists of #$output/include-c++/… and ;; #$output:lib/…/include. Second is usually /usr/local/include, which is ;; documented to be avoided, if the same value is used for both --prefix ;; and --with-local-prefix. (string-append "--with-local-prefix=" #$output) ;; Third set of include paths is normally for a libc, which usually needs a ;; fourth set of include paths for kernel and system headers, which we do ;; not want. ;; Using --without-headers prevents further sets of include paths. "--without-headers" ;; Embed further options. (string-append "--with-specs=" ;; Without a libc we have to ensure to not link missing files. The option ;; -nostlib removes -lgcc as well, which we want to preserve. Without the ;; startfiles there is no _start entry function, and the linker prints a ;; warning. To suppress this warning we use main instead as entry. "-nolibc -nostartfiles -Wl,-entry=main " ;; Ensure that the built GCC will find the right executables from ;; Binutils. Unfortunately GCC does not search for a target-prefixed ;; assembler but just 'as'. Therefore we need to embed a proper path to ;; the cross Binutils. Luckily collet2 also searches for the linker at ;; this path before searching for a target-prefixed 'ld' somewhere else. "-B" #$binutils "/" #$target "/bin") ;; Use the zlib package instead of the zlib bundled with gcc. "--with-system-zlib" '#$configure-flags)) (define*-public (make-cross-gcc gcc cross-binutils #:key cross-libc package-name-suffix) "Make a GCC cross-compiler package based on a modified but still native GCC package using the CROSS-BINUTILS and optional CROSS-LIBC packages. If a CROSS-LIBC is given, then its name, which usually ends with the target name, is appended to the GCC package name, else the string in PACKAGE-NAME-SUFFIX is appended and usually contains the target name." (package (inherit gcc) (name (string-append (package-name gcc) "-cross-" (if cross-libc (package-name cross-libc) package-name-suffix))) (inputs '()) ;; TODO Use (modify-inputs (package-inputs gcc) (prepend cross-binutils)). (native-inputs (append (list (list "cross-binutils" cross-binutils)) (package-inputs gcc))))) (define*-public (make-cross-c-toolchain target gcc make-gcc make-gcc-cross-configure-flags #:key (configure-flags '()) cross-libc (binutils binutils) (propagated-inputs '())) "Make a C-toolchain targeting TARGET consisting of a cross-compiler, cross-compiled BINUTILS, and optionally a C-library. The cross-compiler will be build with another toolchain using the given host GCC and BINUTILS. The functions MAKE-GCC and MAKE-GCC-CROSS-CONFIGURE-FLAGS are used to create a GCC cross-compiler package using an optional C-library package CROSS-LIBC. The result can be used by the transformation function 'package-with-c-toolchain' and to build a GCC-toolchain package with 'make-gcc-toolchain-package'. The additional CONFIGURE-FLAGS get passed to the MAKE-GCC-CROSS-CONFIGURE-FLAGS function to add target or C-library specific GCC options. The PROPAGATED_INPUTS argument is an alist of package-names and packages which take percedence over the usual toolchain packages. This might be needed to override tools from the toolchain." (let* ((c-toolchain (make-c-toolchain gcc binutils ld-wrapper)) (cross-binutils (make-binutils target)) ;; The startfile-dir is relative to #$cross-libc or #$cross-gcc:lib. ;; By default picolibc has no target specific directory and in newlib ;; we use a snippet to remove it. (startfile-dir (if cross-libc ;; This is relative to #$cross-libc. "/lib" ;; This is relative to #$cross-gcc:lib. (string-append "/lib/gcc/" target))) (cross-gcc (package-with-c-toolchain (make-cross-gcc (make-gcc (make-gcc-phase-pre-configure #:libc cross-libc #:startfile-dir startfile-dir) (make-gcc-cross-configure-flags #:target target #:configure-flags configure-flags #:binutils cross-binutils #:libc cross-libc)) cross-binutils #:cross-libc cross-libc #:package-name-suffix (string-append "sans-libc-" target)) c-toolchain))) (make-c-toolchain cross-gcc cross-binutils #:propagated-inputs propagated-inputs))) (define*-public (make-picolibc target c-toolchain #:key (configure-flags '())) "Make a picolibc package for TARGET with the given CONFIGURE-FLAGS, using the given C-TOOLCHAIN." (package-with-c-toolchain (package (name (string-append "picolibc-" target)) (version "1.8.6") (source (origin (method git-fetch) (uri (git-reference (url "https://keithp.com/cgit/picolibc.git") (commit version))) (sha256 (base32 "16y5xgz07hrlmc8abc1vqdcf953xr4qn6jaz1z6gyf71iazgwvz5")))) (build-system meson-build-system) (arguments (list #:configure-flags #~(cons* "--cross-file" (string-append (getcwd) "/source/scripts/cross-" #$target ".txt") "-Dspecsdir=lib" "-Dtests=true" '#$configure-flags) #:build-type "release" ;; As we do not use Guix' cross-build facilities to build picolibc, ;; the meson-build-system thinks (inherited from gnu-build-system) ;; that it is doing a native build and uses strip instead of the ;; target-prefixed strip. As a workaround we could add the target ;; specific bin directory of Binutils to PATH. Or we skip the strip ;; phase. #:strip-binaries? #f #:phases #~(modify-phases %standard-phases (add-after 'unpack 'remove-paths-from-specs (lambda _ (substitute* "picolibc.specs.in" ;; As we will get the include paths of GCC right, there is no ;; need to use the -isystem option for picolibc. Doing so ;; breaks the include-next chain of libstdc++. The picolibc ;; author provided a patch for GCC to crosstool-ng to mitigate ;; the negative effect of using -isystem. ;; ;; page 20, ;; . ;; The substitute offers additional --picolibc-prefix and ;; --picolibc-buildtype options, which we cannot support. (("@SPECS_ISYSTEM@ ") "") ;; The substitute for @SPECS_LIBPATH@ offers additional ;; --picolibc-prefix and --picolibc-buildtype options, which we ;; cannot support. As we will get the library search paths of ;; GCC right, there is no need to specify a library search path ;; in the picolibc.specs file. (("@SPECS_LIBPATH@") "") ;; The substitute for @PICOLIBC_LD@ offers additional ;; --picolibc-prefix and --picolibc-buildtype options, which we ;; cannot support. Instead we change it to the absolute path ;; of the picolibc.ld linker script. The used -T option ;; depends on preceeding -L options. If we were prepending an ;; -L option in @SPECS_LIBPATH@, then that path would get ;; precedence over multilib search paths and wrong libraries ;; would be linked. (("@PICOLIBC_LD@") (string-append #$output "/lib/@PICOLIBC_LD@")) ;; The substitute for @SPECS_STARTFILE@ offers additional ;; --picolibc-prefix and --picolibc-buildtype options, which we ;; cannot support. As we set the buildt-in spec string ;; *startfile_prefix_spec of GCC right, we do not need to add a ;; path. But we need to keep the startfile itself and the ;; picolibc specific --crt0 option to select a different one. (("@SPECS_STARTFILE@") "%{-crt0=*:crt0-%*%O%s; :crt0%O%s}")))) ;; The built picolibc has no shared objects with DT_RUNPATH entries ;; to shrink, and this phase fails, probably because the produced ;; elf-files are not native ones. So we simply delete the ;; shrink-runpath phase. (delete 'shrink-runpath)))) (native-inputs (list texinfo perl)) (home-page "https://www.sourceware.org/newlib/") (synopsis "C Libraries for smaller embedded systems") (description "Picolibc is a set of standard C libraries, both libc and libm, designed for smaller embedded systems with limited ROM and RAM.") (license (license:non-copyleft "file://COPYING.picolibc"))) c-toolchain)) (define*-public (make-newlib target c-toolchain #:key (configure-flags ;; The configure-flags are inspired by ;; newlib_configure found in ;; . ;; Got that link in section "Linaro ABE example ;; manifest files for Linux hosted cross ;; toolchains" form ;; . '("--disable-dependency-tracking" "--disable-newlib-supplied-syscalls" "--enable-newlib-io-long-long" "--enable-newlib-io-c99-formats" "--enable-newlib-mb" "--enable-newlib-reent-check-verify" "--enable-newlib-register-fini" "--enable-newlib-retargetable-locking"))) "Make a newlib package for TARGET with the given CONFIGURE-FLAGS, using the given C-TOOLCHAIN." (package-with-c-toolchain (package (name (string-append "newlib-" target)) (version "4.4.0") (source (origin (method url-fetch) (uri "ftp://sourceware.org/pub/newlib/newlib-4.4.0.20231231.tar.gz") (sha256 (base32 "04rg1wqwdvxf74pjiml1h1jx7dp4w2gr8s6dmzgm22dzw4wnl5hc")))) (build-system gnu-build-system) (arguments (list #:out-of-source? #t #:configure-flags #~(quote #$(cons* (string-append "--target=" target) configure-flags)) #:phases #~(modify-phases %standard-phases (add-before 'configure 'remove-target-directory (lambda _ ;; An intermediate target directory before the include and lib ;; directories of newlib is not needed. To keep the flags in ;; make-gcc-cross-configure-flags common for newlib and picolibc ;; and as the mesa-build-system used by picolibc prevents to set ;; a prefix containing a target folder, we remove the target ;; folder from the newlib output. Alternatively we could patch ;; an unneeded target directory into the meoson.build file of ;; picolibc. However, to keep the configure-flags for GCC simple ;; and close to the flags for the native GCC, we patch newlib. (substitute* "configure" (("^tooldir='\\$\\{exec_prefix\\}'/.*$") "tooldir='${exec_prefix}'\n")))) (add-after 'unpack 'fix-references-to-/bin/sh (lambda _ (substitute* (find-files "libgloss" "^Makefile\\.in$") ;; There are plenty Makefile.in below libgloss which ;; reference /bin/sh. They must all be fixed. (("/bin/sh") (which "sh"))))) (add-after 'install 'remove-nano (lambda _ (map delete-file (find-files #$output "nano"))))))) (native-inputs (list texinfo GCC)) (home-page "https://www.sourceware.org/newlib/") (synopsis "C library for use on embedded systems") (description "Newlib is a C library intended for use on embedded systems. It is a conglomeration of several library parts that are easily usable on embedded products.") (license (list license:gpl2+ license:lgpl2.1+ license:gpl3+ license:lgpl3 (license:non-copyleft "file://COPYING.LIBGLOSS") (license:non-copyleft "file://COPYING.NEWLIB")))) c-toolchain)) (define-public (make-newlib-nano target c-toolchain) "Make a newlib-nano package for TARGET using the given C-TOOLCHAIN." (package (inherit (make-newlib target c-toolchain ;; The configure-flags are inspired by newlib_configure found in ;; . ;; Got that link in section "Linaro ABE example manifest files for ;; Linux hosted cross toolchains" form ;; . #:configure-flags '("--disable-dependency-tracking" "--disable-newlib-fseek-optimization" "--disable-newlib-fvwrite-in-streamio" "--disable-newlib-supplied-syscalls" "--disable-newlib-unbuf-stream-opt" "--disable-newlib-wide-orient" "--enable-lite-exit" "--enable-newlib-global-atexit" "--enable-newlib-nano-formatted-io" "--enable-newlib-nano-malloc" "--enable-newlib-reent-check-verify" "--enable-newlib-reent-small" "--enable-newlib-retargetable-locking"))) (name (string-append "newlib-nano-" target)) ;; TODO Add nano suffix to installed files, keep nano related files. (synopsis "C library for use on embedded systems with limited memory, not ready"))) (define-public (make-cross-c-library make-cross-libc target configure-flags) "Make a C-library package for TARGET using the MAKE-CROSS-LIBC function and a GCC cross c-toolchain, which is built with additional CONFIGURE-FLAGS." (let ((c-toolchain-sans-libc (make-cross-c-toolchain target GCC make-gcc make-gcc-cross-sans-libc-configure-flags #:configure-flags configure-flags))) (make-cross-libc target c-toolchain-sans-libc))) (define*-public (make-gcc-cross-configure-flags #:key target (configure-flags '()) binutils libc #:allow-other-keys) "Make configure-flags to build a GCC cross-compiler for TARGET using LIBC. Additional CONFIGURE-FLAGS can be used for target or C-library specific GCC options." #~(cons* (string-append "--target=" #$target) ;; All paths to --with-… options are relative to the sysroot. As store ;; paths are absolute, the sysroot needs to be set to /. "--with-sysroot=/" ;; The first set of include paths consists of #$output/include-c++/… and ;; #$output:lib/…/include. Second is usually /usr/local/include, which is ;; documented to be avoided, if the same value is used for both --prefix ;; and --with-local-prefix. (string-append "--with-local-prefix=" #$output) ;; Third set is #$output:lib/…/include-fixed, which expects #$libc/include ;; and #$kernel-headers/include to follow in this order. ;; Fourth and usually the last include path is /usr/include containing all ;; system headers. It is only possible to specify one path for this. Set ;; the #$libc/include path and prevent the use of /usr/include. ;; Using newlib or picolibc for bare-metal does not require kernel-headers. (string-append "--with-native-system-header-dir=" #$libc "/include") ;; Embed further search paths. (string-append "--with-specs=" ;; Ensure that the built GCC will find the right executables from ;; Binutils. Unfortunately GCC does not search for a target-prefixed ;; assembler but just 'as'. Therefore we need to embed a proper path to ;; the cross Binutils. Luckily collet2 also searches for the linker at ;; this path before searching for a target-prefixed 'ld' somewhere else. "-B" #$binutils "/" #$target "/bin " ;; Embed the link-time search path to #$libc/lib for libraries, specs file ;; and the startfile. The -B option has to be used to make it a multilib ;; prefix (which is not clearly documented) and to search there for the ;; specs and startfile. "-B" #$libc "/lib " ;; Embed the link-time search path to libstdc++ and other libraries. ;; The -B option has to be used to make it a multilib prefix (which is not ;; clearly documented). "-B" #$output "/" #$target "/lib") ;; Prevent the generation of useless dependency files. "--disable-dependency-tracking" ;; Multiarch support is not a topic for Guix. "--disable-multiarch" ;; Save space by disabling pre-compiled libstdc++ headers. "--disable-libstdcxx-pch" ;; Disable all language frontends except for C, C++. It seems not to be ;; possible to configure libobjc with newlib or picolibc, therefore ;; Objective-C and Objective-C++ are not supported. "--enable-languages=c,c++" ;; Avoid parallel linking to not crash on systems with limited memory. "--enable-link-serialization" ;; Build multiple versions of target libraries. "--enable-multilib" ;; Prevent the C++ headers in #$output:lib, put them in #$output:out ;; instead. Use an unconventional path to prevent it from being added to ;; the environment variables C_INCLUDE_PATH, CPLUS_INCLUDE_PATH and ;; OBJC_INCLUDE_PATH, OBJCPLUS_INCLUDE_PATH. "--with-gxx-include-dir=$(prefix)/include-c++" ;; Use the zlib package instead of the zlib bundled with gcc. "--with-system-zlib" '#$configure-flags)) (define*-public (make-gcc-cross-libc-c-toolchain make-cross-libc target #:key (with-multilib-list "default") (configure-flags '())) "Make a cross GCC package for TARGET using a C-library build with MAKE-CROSS-LIBC. Both the cross GCC and the C-library can be build WITH-MULTILIB-LIST and the cross GCC with additional CONFIGURE-FLAGS to match needs of the C-library." (let* ((with-multilib-list (string-append "--with-multilib-list=" with-multilib-list)) (cross-libc (make-cross-c-library make-cross-libc target (list with-multilib-list)))) (make-cross-c-toolchain target GCC make-gcc make-gcc-cross-configure-flags #:configure-flags (cons* with-multilib-list configure-flags) #:cross-libc cross-libc))) (define*-public (make-gcc-cross-libc-arm-none-eabi-c-toolchain make-cross-libc #:key (configure-flags '())) "Make a cross GCC package for arm-none-eabi using a C-library build with MAKE-CROSS-LIBC. Both the cross GCC and the C-library are build using '--with-multilib-list=aprofile,rmprofile' and the GCC can be build with additional CONFIGURE-FLAGS to match needs of the C-library." (make-gcc-cross-libc-c-toolchain make-cross-libc "arm-none-eabi" #:with-multilib-list "aprofile,rmprofile" #:configure-flags configure-flags)) (define-public GCC-cross-picolibc-arm-none-eabi-c-toolchain (make-gcc-cross-libc-arm-none-eabi-c-toolchain make-picolibc #:configure-flags '(;; Disable the GCC stack smashing protection, as picolibc has its own. If ;; libssp is enabled, then there will be include/ssp inside GCC as well as ;; inside picolibc and the ones from GCC will be inluded. In the end there ;; would be an error from the preprocessor that ssp.h is not found. "--disable-libssp" ;; Ensure that libstdc++ is using the standard C stdio functions from ;; picolibc instead of POSIX implementations. "--enable-cstdio=stdio_pure" ;; Enable thread-local-storage in GCC, which is used by picolibc. "--enable-tls" ;; The picolibc implements __eprintf as newlib does. "--with-newlib"))) (define-public (make-gcc-cross-newlib-arm-none-eabi-c-toolchain make-newlib-variant) "Make a GCC cross-compiler c-toolchain with a variont of newlib, which is built with the MAKE-NEWLIB-VARIANT function." (make-gcc-cross-libc-arm-none-eabi-c-toolchain make-newlib-variant #:configure-flags '(;; Disable the GCC stack smashing protection, as newlib has its own. "--disable-libssp" ;; GCC requires __aeabi_read_tp for thread-local-storage, which is not ;; implemented by newlib, contrary to picolibc. "--disable-tls" ;; Ensure that GCC optimizes for newlib regarding __eprintf. "--with-newlib"))) (define-public GCC-cross-newlib-arm-none-eabi-c-toolchain (make-gcc-cross-newlib-arm-none-eabi-c-toolchain make-newlib)) (define-public GCC-cross-newlib-nano-arm-none-eabi-c-toolchain (make-gcc-cross-newlib-arm-none-eabi-c-toolchain make-newlib-nano)) (define-public GCC-cross-picolibc-arm-none-eabi-toolchain (package (inherit (make-gcc-toolchain-package GCC-cross-picolibc-arm-none-eabi-c-toolchain)) (synopsis "Complete GCC toolchain for C/C++ cross development on ARM Cortex-A and Cortex-M micro-controllers with picolibc") (description "This package provides a complete GCC toolchain for C/C++ cross development on Arm Cortex-A, Cortex-R and Cortex-M micro-controllers. This includes GCC, picolibc and Binutils. GCC is the GNU Compiler Collection."))) (define-public GCC-cross-newlib-arm-none-eabi-toolchain (package (inherit (make-gcc-toolchain-package GCC-cross-newlib-arm-none-eabi-c-toolchain)) (synopsis "Complete GCC toolchain for C/C++ cross development on ARM Cortex-A and Cortex-M micro-controllers with newlib") (description "This package provides a complete GCC toolchain for C/C++ cross development on Arm Cortex-A, Cortex-R and Cortex-M micro-controllers. This includes GCC, newlib and Binutils. GCC is the GNU Compiler Collection."))) (define-public GCC-cross-newlib-nano-arm-none-eabi-toolchain (package (inherit (make-gcc-toolchain-package GCC-cross-newlib-nano-arm-none-eabi-c-toolchain)) (synopsis "Complete GCC toolchain for C/C++ cross development on ARM Cortex-A and Cortex-M micro-controllers with newlib-nano") (description "This package provides a complete GCC toolchain for C/C++ cross development on Arm Cortex-A, Cortex-R and Cortex-M micro-controllers. This includes GCC, newlib-nano and Binutils. GCC is the GNU Compiler Collection."))) (define-public GCC-cross-picolibc-arm-none-eabi (c-toolchain-input-package GCC-cross-picolibc-arm-none-eabi-c-toolchain "gcc")) (define-public GCC-cross-newlib-arm-none-eabi (c-toolchain-input-package GCC-cross-newlib-arm-none-eabi-c-toolchain "gcc")) (define-public GCC-cross-newlib-nano-arm-none-eabi (c-toolchain-input-package GCC-cross-newlib-nano-arm-none-eabi-c-toolchain "gcc")) (define-public (c-toolchain gcc-toolchain-package) "Make a c-toolchain from a GCC-TOOLCHAIN-PACKAGE, a union of GCC, Binutils and possibly more packages. The result can be used by the transformation function 'package-with-c-toolchain'." (list (list "toolchain" gcc-toolchain-package)))