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,13 +20,18 @@ 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
(
# Required for 'contrib/guix/manifest.scm' to output the right manifest
# for the particular $HOST we're building for
export HOST="$host"
# Run the build script 'contrib/guix/libexec/build.sh' in the build # Run the build script 'contrib/guix/libexec/build.sh' in the build
# container specified by 'contrib/guix/manifest.scm' # container specified by 'contrib/guix/manifest.scm'.
# shellcheck disable=SC2086 # shellcheck disable=SC2086
time-machine environment --manifest="${PWD}/contrib/guix/manifest.scm" \ time-machine environment --manifest="${PWD}/contrib/guix/manifest.scm" \
--container \ --container \
@@ -41,5 +46,6 @@ for host in ${HOSTS=x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu riscv
${V:+V=1} \ ${V:+V=1} \
${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"} \ ${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"} \
bash -c "cd /bitcoin && bash contrib/guix/libexec/build.sh" bash -c "cd /bitcoin && bash contrib/guix/libexec/build.sh"
)
done done

View File

@@ -36,7 +36,25 @@ store_path() {
--expression='s|"[[:space:]]*$||' --expression='s|"[[:space:]]*$||'
} }
# Set environment variables to point Guix's cross-toolchain to the right
# includes/libs for $HOST
case "$HOST" in
*mingw*)
# Determine output paths to use in CROSS_* environment variables # Determine output paths to use in CROSS_* environment variables
CROSS_GLIBC="$(store_path "mingw-w64-x86_64-winpthreads")"
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)
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="$(store_path glibc-cross-${HOST})"
CROSS_GLIBC_STATIC="$(store_path glibc-cross-${HOST} static)" CROSS_GLIBC_STATIC="$(store_path glibc-cross-${HOST} static)"
CROSS_KERNEL="$(store_path linux-libre-headers-cross-${HOST})" CROSS_KERNEL="$(store_path linux-libre-headers-cross-${HOST})"
@@ -44,15 +62,15 @@ CROSS_GCC="$(store_path gcc-cross-${HOST})"
CROSS_GCC_LIBS=( "${CROSS_GCC}/lib/gcc/${HOST}"/* ) # This expands to an array of directories... 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) 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
# includes/libs for $HOST
#
# NOTE: CROSS_C_INCLUDE_PATH is missing ${CROSS_GCC_LIB}/include-fixed, because # 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>' # 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_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_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" 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,6 +92,8 @@ 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
case "$HOST" in
*linux*)
glibc_dynamic_linker=$( glibc_dynamic_linker=$(
case "$HOST" in case "$HOST" in
i686-linux-gnu) echo /lib/ld-linux.so.2 ;; i686-linux-gnu) echo /lib/ld-linux.so.2 ;;
@@ -84,6 +104,8 @@ glibc_dynamic_linker=$(
*) exit 1 ;; *) exit 1 ;;
esac 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}
case "$HOST" in
*linux*)
# Check that executables only contain allowed gcc, glibc and libstdc++ # Check that executables only contain allowed gcc, glibc and libstdc++
# version symbols for Linux distro back-compatibility. # version symbols for Linux distro back-compatibility.
make -C src --jobs=1 check-symbols ${V:+V=1} 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,10 +258,29 @@ 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
case "$HOST" in
*mingw*)
cp "${DISTSRC}/doc/README_windows.txt" "${DISTNAME}/readme.txt"
;;
*linux*)
cp "${DISTSRC}/doc/README.md" "${DISTNAME}/" 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
case "$HOST" in
*mingw*)
find "${DISTNAME}" -not -name "*.dbg" \
| sort \
| zip -X@ "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}.zip" \
|| ( rm -f "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}.zip" && exit 1 )
find "${DISTNAME}" -name "*.dbg" \
| sort \
| zip -X@ "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}-debug.zip" \
|| ( rm -f "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}-debug.zip" && exit 1 )
;;
*linux*)
find "${DISTNAME}" -not -name "*.dbg" -print0 \ find "${DISTNAME}" -not -name "*.dbg" -print0 \
| sort --zero-terminated \ | sort --zero-terminated \
| tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \
@@ -210,5 +291,23 @@ export PATH="${BASEPREFIX}/${HOST}/native/bin:${PATH}"
| tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \
| gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}-debug.tar.gz" \ | gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}-debug.tar.gz" \
|| ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}-debug.tar.gz" && exit 1 ) || ( 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,7 +105,39 @@ 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
(append
(list ;; The Basics (list ;; The Basics
bash-minimal bash-minimal
which which
@@ -133,14 +168,14 @@ desirable for building Bitcoin Core release binaries."
perl perl
python-3.7 python-3.7
;; Native gcc 9 toolchain targeting glibc 2.27 ;; Native gcc 9 toolchain targeting glibc 2.27
(make-gcc-toolchain gcc-9 glibc-2.27) (make-gcc-toolchain gcc-9 glibc-2.27))
;; Cross gcc 9 toolchains targeting glibc 2.27 (let ((target (getenv "HOST")))
(make-bitcoin-cross-toolchain "i686-linux-gnu") (cond ((string-suffix? "-mingw32" target)
(make-bitcoin-cross-toolchain "x86_64-linux-gnu") ;; Windows
(make-bitcoin-cross-toolchain "aarch64-linux-gnu") (list zip (make-mingw-pthreads-cross-toolchain "x86_64-w64-mingw32") nsis-x86_64))
(make-bitcoin-cross-toolchain "arm-linux-gnueabihf") ((string-contains target "riscv64-linux-")
;; The glibc 2.27 for riscv64 needs gcc 7 to successfully build (see: (list (make-bitcoin-cross-toolchain "riscv64-linux-gnu"
;; https://www.gnu.org/software/gcc/gcc-7/changes.html#riscv). The final
;; toolchain is still a gcc 9 toolchain targeting glibc 2.27.
(make-bitcoin-cross-toolchain "riscv64-linux-gnu"
#:base-gcc-for-libc gcc-7))) #:base-gcc-for-libc gcc-7)))
((string-contains target "-linux-")
(list (make-bitcoin-cross-toolchain target)))
(else '())))))