guix: Build support for Windows

This commit is contained in:
Carl Dong
2020-01-16 14:40:48 -05:00
parent 0d71395848
commit 570d769c6c
3 changed files with 244 additions and 104 deletions

View File

@@ -20,26 +20,32 @@ time-machine() {
} }
# Deterministically build Bitcoin Core for HOSTs (overriable by environment) # Deterministically build Bitcoin Core for HOSTs (overriable by environment)
for host in ${HOSTS=x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu riscv64-linux-gnu}; do for host in ${HOSTS=x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu riscv64-linux-gnu x86_64-w64-mingw32}; do
# Display proper warning when the user interrupts the build # Display proper warning when the user interrupts the build
trap 'echo "** INT received while building ${host}, you may want to clean up the relevant output and distsrc-* directories before rebuilding"' INT trap 'echo "** INT received while building ${host}, you may want to clean up the relevant output and distsrc-* directories before rebuilding"' INT
# Run the build script 'contrib/guix/libexec/build.sh' in the build (
# container specified by 'contrib/guix/manifest.scm' # Required for 'contrib/guix/manifest.scm' to output the right manifest
# shellcheck disable=SC2086 # for the particular $HOST we're building for
time-machine environment --manifest="${PWD}/contrib/guix/manifest.scm" \ export HOST="$host"
--container \
--pure \ # Run the build script 'contrib/guix/libexec/build.sh' in the build
--no-cwd \ # container specified by 'contrib/guix/manifest.scm'.
--share="$PWD"=/bitcoin \ # shellcheck disable=SC2086
${SOURCES_PATH:+--share="$SOURCES_PATH"} \ time-machine environment --manifest="${PWD}/contrib/guix/manifest.scm" \
${ADDITIONAL_GUIX_ENVIRONMENT_FLAGS} \ --container \
-- env HOST="$host" \ --pure \
MAX_JOBS="$MAX_JOBS" \ --no-cwd \
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:?unable to determine value}" \ --share="$PWD"=/bitcoin \
${V:+V=1} \ ${SOURCES_PATH:+--share="$SOURCES_PATH"} \
${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"} \ ${ADDITIONAL_GUIX_ENVIRONMENT_FLAGS} \
bash -c "cd /bitcoin && bash contrib/guix/libexec/build.sh" -- env HOST="$host" \
MAX_JOBS="$MAX_JOBS" \
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:?unable to determine value}" \
${V:+V=1} \
${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"} \
bash -c "cd /bitcoin && bash contrib/guix/libexec/build.sh"
)
done done

View File

@@ -36,23 +36,41 @@ store_path() {
--expression='s|"[[:space:]]*$||' --expression='s|"[[:space:]]*$||'
} }
# Determine output paths to use in CROSS_* environment variables
CROSS_GLIBC="$(store_path glibc-cross-${HOST})"
CROSS_GLIBC_STATIC="$(store_path glibc-cross-${HOST} static)"
CROSS_KERNEL="$(store_path linux-libre-headers-cross-${HOST})"
CROSS_GCC="$(store_path gcc-cross-${HOST})"
CROSS_GCC_LIBS=( "${CROSS_GCC}/lib/gcc/${HOST}"/* ) # This expands to an array of directories...
CROSS_GCC_LIB="${CROSS_GCC_LIBS[0]}" # ...we just want the first one (there should only be one)
# Set environment variables to point Guix's cross-toolchain to the right # Set environment variables to point Guix's cross-toolchain to the right
# includes/libs for $HOST # includes/libs for $HOST
# case "$HOST" in
# NOTE: CROSS_C_INCLUDE_PATH is missing ${CROSS_GCC_LIB}/include-fixed, because *mingw*)
# the limits.h in it is missing a '#include_next <limits.h>' # Determine output paths to use in CROSS_* environment variables
# CROSS_GLIBC="$(store_path "mingw-w64-x86_64-winpthreads")"
export CROSS_C_INCLUDE_PATH="${CROSS_GCC_LIB}/include:${CROSS_GLIBC}/include:${CROSS_KERNEL}/include" CROSS_GCC="$(store_path "gcc-cross-${HOST}")"
export CROSS_CPLUS_INCLUDE_PATH="${CROSS_GCC}/include/c++:${CROSS_GCC}/include/c++/${HOST}:${CROSS_GCC}/include/c++/backward:${CROSS_C_INCLUDE_PATH}" CROSS_GCC_LIBS=( "${CROSS_GCC}/lib/gcc/${HOST}"/* ) # This expands to an array of directories...
export CROSS_LIBRARY_PATH="${CROSS_GCC}/lib:${CROSS_GCC}/${HOST}/lib:${CROSS_GCC_LIB}:${CROSS_GLIBC}/lib:${CROSS_GLIBC_STATIC}/lib" CROSS_GCC_LIB="${CROSS_GCC_LIBS[0]}" # ...we just want the first one (there should only be one)
NATIVE_GCC="$(store_path gcc-glibc-2.27-toolchain)"
export LIBRARY_PATH="${NATIVE_GCC}/lib:${NATIVE_GCC}/lib64"
export CPATH="${NATIVE_GCC}/include"
export CROSS_C_INCLUDE_PATH="${CROSS_GCC_LIB}/include:${CROSS_GCC_LIB}/include-fixed:${CROSS_GLIBC}/include"
export CROSS_CPLUS_INCLUDE_PATH="${CROSS_GCC}/include/c++:${CROSS_GCC}/include/c++/${HOST}:${CROSS_GCC}/include/c++/backward:${CROSS_C_INCLUDE_PATH}"
export CROSS_LIBRARY_PATH="${CROSS_GCC}/lib:${CROSS_GCC}/${HOST}/lib:${CROSS_GCC_LIB}:${CROSS_GLIBC}/lib"
;;
*linux*)
CROSS_GLIBC="$(store_path glibc-cross-${HOST})"
CROSS_GLIBC_STATIC="$(store_path glibc-cross-${HOST} static)"
CROSS_KERNEL="$(store_path linux-libre-headers-cross-${HOST})"
CROSS_GCC="$(store_path gcc-cross-${HOST})"
CROSS_GCC_LIBS=( "${CROSS_GCC}/lib/gcc/${HOST}"/* ) # This expands to an array of directories...
CROSS_GCC_LIB="${CROSS_GCC_LIBS[0]}" # ...we just want the first one (there should only be one)
# NOTE: CROSS_C_INCLUDE_PATH is missing ${CROSS_GCC_LIB}/include-fixed, because
# the limits.h in it is missing a '#include_next <limits.h>'
export CROSS_C_INCLUDE_PATH="${CROSS_GCC_LIB}/include:${CROSS_GLIBC}/include:${CROSS_KERNEL}/include"
export CROSS_CPLUS_INCLUDE_PATH="${CROSS_GCC}/include/c++:${CROSS_GCC}/include/c++/${HOST}:${CROSS_GCC}/include/c++/backward:${CROSS_C_INCLUDE_PATH}"
export CROSS_LIBRARY_PATH="${CROSS_GCC}/lib:${CROSS_GCC}/${HOST}/lib:${CROSS_GCC_LIB}:${CROSS_GLIBC}/lib:${CROSS_GLIBC_STATIC}/lib"
;;
*)
exit 1 ;;
esac
# Sanity check CROSS_*_PATH directories # Sanity check CROSS_*_PATH directories
IFS=':' read -ra PATHS <<< "${CROSS_C_INCLUDE_PATH}:${CROSS_CPLUS_INCLUDE_PATH}:${CROSS_LIBRARY_PATH}" IFS=':' read -ra PATHS <<< "${CROSS_C_INCLUDE_PATH}:${CROSS_CPLUS_INCLUDE_PATH}:${CROSS_LIBRARY_PATH}"
@@ -74,16 +92,20 @@ export GUIX_LD_WRAPPER_DISABLE_RPATH=yes
[ -e /usr/bin/env ] || ln -s --no-dereference "$(command -v env)" /usr/bin/env [ -e /usr/bin/env ] || ln -s --no-dereference "$(command -v env)" /usr/bin/env
# Determine the correct value for -Wl,--dynamic-linker for the current $HOST # Determine the correct value for -Wl,--dynamic-linker for the current $HOST
glibc_dynamic_linker=$( case "$HOST" in
case "$HOST" in *linux*)
i686-linux-gnu) echo /lib/ld-linux.so.2 ;; glibc_dynamic_linker=$(
x86_64-linux-gnu) echo /lib64/ld-linux-x86-64.so.2 ;; case "$HOST" in
arm-linux-gnueabihf) echo /lib/ld-linux-armhf.so.3 ;; i686-linux-gnu) echo /lib/ld-linux.so.2 ;;
aarch64-linux-gnu) echo /lib/ld-linux-aarch64.so.1 ;; x86_64-linux-gnu) echo /lib64/ld-linux-x86-64.so.2 ;;
riscv64-linux-gnu) echo /lib/ld-linux-riscv64-lp64d.so.1 ;; arm-linux-gnueabihf) echo /lib/ld-linux-armhf.so.3 ;;
*) exit 1 ;; aarch64-linux-gnu) echo /lib/ld-linux-aarch64.so.1 ;;
esac riscv64-linux-gnu) echo /lib/ld-linux-riscv64-lp64d.so.1 ;;
) *) exit 1 ;;
esac
)
;;
esac
# Environment variables for determinism # Environment variables for determinism
export QT_RCC_TEST=1 export QT_RCC_TEST=1
@@ -111,7 +133,8 @@ make -C depends --jobs="$MAX_JOBS" HOST="$HOST" \
x86_64_linux_RANLIB=x86_64-linux-gnu-ranlib \ x86_64_linux_RANLIB=x86_64-linux-gnu-ranlib \
x86_64_linux_NM=x86_64-linux-gnu-nm \ x86_64_linux_NM=x86_64-linux-gnu-nm \
x86_64_linux_STRIP=x86_64-linux-gnu-strip \ x86_64_linux_STRIP=x86_64-linux-gnu-strip \
qt_config_opts_i686_linux='-platform linux-g++ -xplatform bitcoin-linux-g++' qt_config_opts_i686_linux='-platform linux-g++ -xplatform bitcoin-linux-g++' \
mingw32_CFLAGS='-DMINGW_HAS_SECURE_API=1 -pipe'
########################### ###########################
@@ -136,11 +159,26 @@ DISTNAME="$(basename "$SOURCEDIST" '.tar.gz')"
# Binary Tarball Building # # Binary Tarball Building #
########################### ###########################
# Similar flags to Gitian # CONFIGFLAGS
CONFIGFLAGS="--enable-glibc-back-compat --enable-reduce-exports --disable-bench --disable-gui-tests" CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests"
HOST_CFLAGS="-O2 -g -ffile-prefix-map=${PWD}=." case "$HOST" in
HOST_CXXFLAGS="-O2 -g -ffile-prefix-map=${PWD}=." *linux*) CONFIGFLAGS+=" --enable-glibc-back-compat" ;;
HOST_LDFLAGS="-Wl,--as-needed -Wl,--dynamic-linker=$glibc_dynamic_linker -static-libstdc++" esac
# CFLAGS
HOST_CFLAGS="-O2 -g"
case "$HOST" in
*linux*) HOST_CFLAGS+=" -ffile-prefix-map=${PWD}=." ;;
*mingw*) HOST_CFLAGS+=" -fno-ident" ;;
esac
# CXXFLAGS
HOST_CXXFLAGS="$HOST_CFLAGS"
# LDFLAGS
case "$HOST" in
*linux*) HOST_LDFLAGS="-Wl,--as-needed -Wl,--dynamic-linker=$glibc_dynamic_linker -static-libstdc++" ;;
esac
# Make $HOST-specific native binaries from depends available in $PATH # Make $HOST-specific native binaries from depends available in $PATH
export PATH="${BASEPREFIX}/${HOST}/native/bin:${PATH}" export PATH="${BASEPREFIX}/${HOST}/native/bin:${PATH}"
@@ -160,7 +198,7 @@ export PATH="${BASEPREFIX}/${HOST}/native/bin:${PATH}"
${CONFIGFLAGS} \ ${CONFIGFLAGS} \
CFLAGS="${HOST_CFLAGS}" \ CFLAGS="${HOST_CFLAGS}" \
CXXFLAGS="${HOST_CXXFLAGS}" \ CXXFLAGS="${HOST_CXXFLAGS}" \
LDFLAGS="${HOST_LDFLAGS}" ${HOST_LDFLAGS:+LDFLAGS="${HOST_LDFLAGS}"}
sed -i.old 's/-lstdc++ //g' config.status libtool src/univalue/config.status src/univalue/libtool sed -i.old 's/-lstdc++ //g' config.status libtool src/univalue/config.status src/univalue/libtool
@@ -169,9 +207,21 @@ export PATH="${BASEPREFIX}/${HOST}/native/bin:${PATH}"
# Perform basic ELF security checks on a series of executables. # Perform basic ELF security checks on a series of executables.
make -C src --jobs=1 check-security ${V:+V=1} make -C src --jobs=1 check-security ${V:+V=1}
# Check that executables only contain allowed gcc, glibc and libstdc++
# version symbols for Linux distro back-compatibility. case "$HOST" in
make -C src --jobs=1 check-symbols ${V:+V=1} *linux*)
# Check that executables only contain allowed gcc, glibc and libstdc++
# version symbols for Linux distro back-compatibility.
make -C src --jobs=1 check-symbols ${V:+V=1}
;;
esac
# Make the os-specific installers
case "$HOST" in
*mingw*)
make deploy ${V:+V=1}
;;
esac
# Setup the directory where our Bitcoin Core build for HOST will be # Setup the directory where our Bitcoin Core build for HOST will be
# installed. This directory will also later serve as the input for our # installed. This directory will also later serve as the input for our
@@ -180,9 +230,21 @@ export PATH="${BASEPREFIX}/${HOST}/native/bin:${PATH}"
mkdir -p "${INSTALLPATH}" mkdir -p "${INSTALLPATH}"
# Install built Bitcoin Core to $INSTALLPATH # Install built Bitcoin Core to $INSTALLPATH
make install DESTDIR="${INSTALLPATH}" ${V:+V=1} make install DESTDIR="${INSTALLPATH}" ${V:+V=1}
case "$HOST" in
*mingw*)
cp -f --target-directory="$OUTDIR" ./*-setup-unsigned.exe
;;
esac
( (
cd installed cd installed
case "$HOST" in
*mingw*)
mv --target-directory="$DISTNAME"/lib/ "$DISTNAME"/bin/*.dll
;;
esac
# Prune libtool and object archives # Prune libtool and object archives
find . -name "lib*.la" -delete find . -name "lib*.la" -delete
find . -name "lib*.a" -delete find . -name "lib*.a" -delete
@@ -196,19 +258,56 @@ export PATH="${BASEPREFIX}/${HOST}/native/bin:${PATH}"
find "${DISTNAME}/lib" -type f -print0 find "${DISTNAME}/lib" -type f -print0
} | xargs -0 -n1 -P"$MAX_JOBS" -I{} "${DISTSRC}/contrib/devtools/split-debug.sh" {} {} {}.dbg } | xargs -0 -n1 -P"$MAX_JOBS" -I{} "${DISTSRC}/contrib/devtools/split-debug.sh" {} {} {}.dbg
cp "${DISTSRC}/doc/README.md" "${DISTNAME}/" case "$HOST" in
*mingw*)
cp "${DISTSRC}/doc/README_windows.txt" "${DISTNAME}/readme.txt"
;;
*linux*)
cp "${DISTSRC}/doc/README.md" "${DISTNAME}/"
;;
esac
# Finally, deterministically produce {non-,}debug binary tarballs ready # Finally, deterministically produce {non-,}debug binary tarballs ready
# for release # for release
find "${DISTNAME}" -not -name "*.dbg" -print0 \ case "$HOST" in
| sort --zero-terminated \ *mingw*)
| tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ find "${DISTNAME}" -not -name "*.dbg" \
| gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" \ | sort \
|| ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" && exit 1 ) | zip -X@ "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}.zip" \
find "${DISTNAME}" -name "*.dbg" -print0 \ || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}.zip" && exit 1 )
| sort --zero-terminated \ find "${DISTNAME}" -name "*.dbg" \
| tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ | sort \
| gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}-debug.tar.gz" \ | zip -X@ "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}-debug.zip" \
|| ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}-debug.tar.gz" && exit 1 ) || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}-debug.zip" && exit 1 )
;;
*linux*)
find "${DISTNAME}" -not -name "*.dbg" -print0 \
| sort --zero-terminated \
| tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \
| gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" \
|| ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" && exit 1 )
find "${DISTNAME}" -name "*.dbg" -print0 \
| sort --zero-terminated \
| tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \
| gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}-debug.tar.gz" \
|| ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}-debug.tar.gz" && exit 1 )
;;
esac
) )
) )
case "$HOST" in
*mingw*)
cp -rf --target-directory=. contrib/windeploy
(
cd ./windeploy
mkdir unsigned
cp --target-directory=unsigned/ "$OUTDIR"/bitcoin-*-setup-unsigned.exe
find . -print0 \
| sort --zero-terminated \
| tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \
| gzip -9n > "${OUTDIR}/${DISTNAME}-win-unsigned.tar.gz" \
|| ( rm -f "${OUTDIR}/${DISTNAME}-win-unsigned.tar.gz" && exit 1 )
)
;;
esac

View File

@@ -10,11 +10,14 @@
(gnu packages file) (gnu packages file)
(gnu packages gawk) (gnu packages gawk)
(gnu packages gcc) (gnu packages gcc)
(gnu packages installers)
(gnu packages linux) (gnu packages linux)
(gnu packages mingw)
(gnu packages perl) (gnu packages perl)
(gnu packages pkg-config) (gnu packages pkg-config)
(gnu packages python) (gnu packages python)
(gnu packages shells) (gnu packages shells)
(guix build-system gnu)
(guix build-system trivial) (guix build-system trivial)
(guix gexp) (guix gexp)
(guix packages) (guix packages)
@@ -102,45 +105,77 @@ desirable for building Bitcoin Core release binaries."
base-libc base-libc
base-gcc)) base-gcc))
(define (make-gcc-with-pthreads gcc)
(package-with-extra-configure-variable gcc "--enable-threads" "posix"))
(define (make-mingw-pthreads-cross-toolchain target)
"Create a cross-compilation toolchain package for TARGET"
(let* ((xbinutils (cross-binutils target))
(pthreads-xlibc mingw-w64-x86_64-winpthreads)
(pthreads-xgcc (make-gcc-with-pthreads
(cross-gcc target
#:xgcc (make-ssp-fixed-gcc gcc)
#:xbinutils xbinutils
#:libc pthreads-xlibc))))
;; Define a meta-package that propagates the resulting XBINUTILS, XLIBC, and
;; XGCC
(package
(name (string-append target "-posix-toolchain"))
(version (package-version pthreads-xgcc))
(source #f)
(build-system trivial-build-system)
(arguments '(#:builder (begin (mkdir %output) #t)))
(propagated-inputs
`(("binutils" ,xbinutils)
("libc" ,pthreads-xlibc)
("gcc" ,pthreads-xgcc)))
(synopsis (string-append "Complete GCC tool chain for " target))
(description (string-append "This package provides a complete GCC tool
chain for " target " development."))
(home-page (package-home-page pthreads-xgcc))
(license (package-license pthreads-xgcc)))))
(packages->manifest (packages->manifest
(list ;; The Basics (append
bash-minimal (list ;; The Basics
which bash-minimal
coreutils which
util-linux coreutils
;; File(system) inspection util-linux
file ;; File(system) inspection
grep file
diffutils grep
findutils diffutils
;; File transformation findutils
patch ;; File transformation
gawk patch
sed gawk
;; Compression and archiving sed
tar ;; Compression and archiving
bzip2 tar
gzip bzip2
xz gzip
zlib xz
;; Build tools zlib
gnu-make ;; Build tools
libtool gnu-make
autoconf libtool
automake autoconf
pkg-config automake
;; Scripting pkg-config
perl ;; Scripting
python-3.7 perl
;; Native gcc 9 toolchain targeting glibc 2.27 python-3.7
(make-gcc-toolchain gcc-9 glibc-2.27) ;; Native gcc 9 toolchain targeting glibc 2.27
;; Cross gcc 9 toolchains targeting glibc 2.27 (make-gcc-toolchain gcc-9 glibc-2.27))
(make-bitcoin-cross-toolchain "i686-linux-gnu") (let ((target (getenv "HOST")))
(make-bitcoin-cross-toolchain "x86_64-linux-gnu") (cond ((string-suffix? "-mingw32" target)
(make-bitcoin-cross-toolchain "aarch64-linux-gnu") ;; Windows
(make-bitcoin-cross-toolchain "arm-linux-gnueabihf") (list zip (make-mingw-pthreads-cross-toolchain "x86_64-w64-mingw32") nsis-x86_64))
;; The glibc 2.27 for riscv64 needs gcc 7 to successfully build (see: ((string-contains target "riscv64-linux-")
;; https://www.gnu.org/software/gcc/gcc-7/changes.html#riscv). The final (list (make-bitcoin-cross-toolchain "riscv64-linux-gnu"
;; toolchain is still a gcc 9 toolchain targeting glibc 2.27. #:base-gcc-for-libc gcc-7)))
(make-bitcoin-cross-toolchain "riscv64-linux-gnu" ((string-contains target "-linux-")
#:base-gcc-for-libc gcc-7))) (list (make-bitcoin-cross-toolchain target)))
(else '())))))