aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build-aux/m4/bitcoin_qt.m465
-rwxr-xr-xcontrib/gitian-descriptors/assign_DISTNAME2
-rw-r--r--contrib/guix/README.md109
-rwxr-xr-xcontrib/guix/guix-build (renamed from contrib/guix/guix-build.sh)109
-rw-r--r--contrib/guix/libexec/build.sh82
-rw-r--r--contrib/guix/libexec/prelude.bash60
-rw-r--r--contrib/guix/manifest.scm6
-rw-r--r--contrib/shell/git-utils.bash14
-rw-r--r--contrib/shell/realpath.bash71
-rw-r--r--depends/Makefile4
-rw-r--r--depends/packages/native_libdmg-hfsplus.mk2
-rw-r--r--depends/packages/qt.mk3
-rw-r--r--depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch17
-rw-r--r--src/rpc/util.cpp63
-rw-r--r--src/rpc/util.h4
-rw-r--r--src/test/rpc_tests.cpp35
-rw-r--r--src/wallet/rpcwallet.cpp2
-rw-r--r--test/sanitizer_suppressions/ubsan6
18 files changed, 546 insertions, 108 deletions
diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4
index cce06e2fff..57b4c10d77 100644
--- a/build-aux/m4/bitcoin_qt.m4
+++ b/build-aux/m4/bitcoin_qt.m4
@@ -108,11 +108,10 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
BITCOIN_QT_CHECK([_BITCOIN_QT_FIND_LIBS])
dnl This is ugly and complicated. Yuck. Works as follows:
- dnl For Qt5, we can check a header to find out whether Qt is build
- dnl statically. When Qt is built statically, some plugins must be linked into
- dnl the final binary as well.
- dnl _BITCOIN_QT_CHECK_STATIC_PLUGIN does a quick link-check and appends the
- dnl results to QT_LIBS.
+ dnl We check a header to find out whether Qt is built statically.
+ dnl When Qt is built statically, some plugins must be linked into
+ dnl the final binary as well. _BITCOIN_QT_CHECK_STATIC_PLUGIN does
+ dnl a quick link-check and appends the results to QT_LIBS.
BITCOIN_QT_CHECK([
TEMP_CPPFLAGS=$CPPFLAGS
TEMP_CXXFLAGS=$CXXFLAGS
@@ -162,7 +161,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
_BITCOIN_QT_CHECK_STATIC_PLUGIN([QMacStylePlugin], [-lqmacstyle])
AC_DEFINE(QT_QPA_PLATFORM_COCOA, 1, [Define this symbol if the qt platform is cocoa])
elif test "x$TARGET_OS" = xandroid; then
- QT_LIBS="-Wl,--export-dynamic,--undefined=JNI_OnLoad -lqtforandroid -ljnigraphics -landroid -lqtfreetype -lQt5EglSupport $QT_LIBS"
+ QT_LIBS="-Wl,--export-dynamic,--undefined=JNI_OnLoad -lqtforandroid -ljnigraphics -landroid -lqtfreetype $QT_LIBS"
AC_DEFINE(QT_QPA_PLATFORM_ANDROID, 1, [Define this symbol if the qt platform is android])
fi
fi
@@ -171,7 +170,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
])
if test "x$qt_bin_path" = x; then
- qt_bin_path="`$PKG_CONFIG --variable=host_bins Qt5Core 2>/dev/null`"
+ qt_bin_path="`$PKG_CONFIG --variable=host_bins ${qt_lib_prefix}Core 2>/dev/null`"
fi
if test "x$use_hardening" != xno; then
@@ -282,12 +281,13 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
AC_SUBST(MOC_DEFS)
])
-dnl All macros below are internal and should _not_ be used from the main
-dnl configure.ac.
-dnl ----
+dnl All macros below are internal and should _not_ be used from configure.ac.
-dnl Internal. Check if the linked version of Qt was built as static libs.
-dnl Requires: Qt5.
+dnl Internal. Check if the linked version of Qt was built statically.
+dnl
+dnl _BITCOIN_QT_IS_STATIC
+dnl ---------------------
+dnl
dnl Requires: INCLUDES and LIBS must be populated as necessary.
dnl Output: bitcoin_cv_static_qt=yes|no
AC_DEFUN([_BITCOIN_QT_IS_STATIC],[
@@ -335,47 +335,54 @@ dnl
dnl _BITCOIN_QT_CHECK_STATIC_LIBS
dnl -----------------------------
dnl
-dnl Inputs: no inputs.
dnl Outputs: QT_LIBS is prepended.
AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_LIBS], [
- PKG_CHECK_MODULES([QTFONTDATABASE], [Qt5FontDatabaseSupport${qt_lib_suffix}], [QT_LIBS="-lQt5FontDatabaseSupport${qt_lib_suffix} $QT_LIBS"])
- PKG_CHECK_MODULES([QTEVENTDISPATCHER], [Qt5EventDispatcherSupport${qt_lib_suffix}], [QT_LIBS="-lQt5EventDispatcherSupport${qt_lib_suffix} $QT_LIBS"])
- PKG_CHECK_MODULES([QTTHEME], [Qt5ThemeSupport${qt_lib_suffix}], [QT_LIBS="-lQt5ThemeSupport${qt_lib_suffix} $QT_LIBS"])
- PKG_CHECK_MODULES([QTDEVICEDISCOVERY], [Qt5DeviceDiscoverySupport${qt_lib_suffix}], [QT_LIBS="-lQt5DeviceDiscoverySupport${qt_lib_suffix} $QT_LIBS"])
- PKG_CHECK_MODULES([QTACCESSIBILITY], [Qt5AccessibilitySupport${qt_lib_suffix}], [QT_LIBS="-lQt5AccessibilitySupport${qt_lib_suffix} $QT_LIBS"])
- PKG_CHECK_MODULES([QTFB], [Qt5FbSupport${qt_lib_suffix}], [QT_LIBS="-lQt5FbSupport${qt_lib_suffix} $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_ACCESSIBILITY], [${qt_lib_prefix}AccessibilitySupport${qt_lib_suffix}], [QT_LIBS="$QT_ACCESSIBILITY_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_DEVICEDISCOVERY], [${qt_lib_prefix}DeviceDiscoverySupport${qt_lib_suffix}], [QT_LIBS="$QT_DEVICEDISCOVERY_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_EDID], [${qt_lib_prefix}EdidSupport${qt_lib_suffix}], [QT_LIBS="$QT_EDID_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_EVENTDISPATCHER], [${qt_lib_prefix}EventDispatcherSupport${qt_lib_suffix}], [QT_LIBS="$QT_EVENTDISPATCHER_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_FB], [${qt_lib_prefix}FbSupport${qt_lib_suffix}], [QT_LIBS="$QT_FB_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_FONTDATABASE], [${qt_lib_prefix}FontDatabaseSupport${qt_lib_suffix}], [QT_LIBS="$QT_FONTDATABASE_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_THEME], [${qt_lib_prefix}ThemeSupport${qt_lib_suffix}], [QT_LIBS="$QT_THEME_LIBS $QT_LIBS"])
if test "x$TARGET_OS" = xlinux; then
- PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_INPUT], [${qt_lib_prefix}XcbQpa], [QT_LIBS="$QT_INPUT_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_SERVICE], [${qt_lib_prefix}ServiceSupport], [QT_LIBS="$QT_SERVICE_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_XCBQPA], [${qt_lib_prefix}XcbQpa], [QT_LIBS="$QT_XCBQPA_LIBS $QT_LIBS"])
elif test "x$TARGET_OS" = xdarwin; then
- PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport${qt_lib_suffix}], [QT_LIBS="-lQt5ClipboardSupport${qt_lib_suffix} $QT_LIBS"])
- PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport${qt_lib_suffix}], [QT_LIBS="-lQt5GraphicsSupport${qt_lib_suffix} $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_CLIPBOARD], [${qt_lib_prefix}ClipboardSupport${qt_lib_suffix}], [QT_LIBS="$QT_CLIPBOARD_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_GRAPHICS], [${qt_lib_prefix}GraphicsSupport${qt_lib_suffix}], [QT_LIBS="$QT_GRAPHICS_LIBS $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_SERVICE], [${qt_lib_prefix}ServiceSupport${qt_lib_suffix}], [QT_LIBS="$QT_SERVICE_LIBS $QT_LIBS"])
elif test "x$TARGET_OS" = xwindows; then
- PKG_CHECK_MODULES([QTWINDOWSUIAUTOMATION], [Qt5WindowsUIAutomationSupport${qt_lib_suffix}], [QT_LIBS="-lQt5WindowsUIAutomationSupport${qt_lib_suffix} $QT_LIBS"])
+ PKG_CHECK_MODULES([QT_WINDOWSUIAUTOMATION], [${qt_lib_prefix}WindowsUIAutomationSupport${qt_lib_suffix}], [QT_LIBS="$QT_WINDOWSUIAUTOMATION_LIBS $QT_LIBS"])
+ elif test "x$TARGET_OS" = xandroid; then
+ PKG_CHECK_MODULES([QT_EGL], [${qt_lib_prefix}EglSupport], [QT_LIBS="$QT_EGL_LIBS $QT_LIBS"])
fi
])
dnl Internal. Find Qt libraries using pkg-config.
+dnl
+dnl _BITCOIN_QT_FIND_LIBS
+dnl ---------------------
+dnl
dnl Outputs: All necessary QT_* variables are set.
dnl Outputs: have_qt_test and have_qt_dbus are set (if applicable) to yes|no.
AC_DEFUN([_BITCOIN_QT_FIND_LIBS],[
BITCOIN_QT_CHECK([
- PKG_CHECK_MODULES([QT_CORE], [${qt_lib_prefix}Core${qt_lib_suffix} $qt_version], [],
+ PKG_CHECK_MODULES([QT_CORE], [${qt_lib_prefix}Core${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_CORE_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_CORE_LIBS $QT_LIBS"],
[BITCOIN_QT_FAIL([${qt_lib_prefix}Core${qt_lib_suffix} $qt_version not found])])
])
BITCOIN_QT_CHECK([
- PKG_CHECK_MODULES([QT_GUI], [${qt_lib_prefix}Gui${qt_lib_suffix} $qt_version], [],
+ PKG_CHECK_MODULES([QT_GUI], [${qt_lib_prefix}Gui${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_GUI_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_GUI_LIBS $QT_LIBS"],
[BITCOIN_QT_FAIL([${qt_lib_prefix}Gui${qt_lib_suffix} $qt_version not found])])
])
BITCOIN_QT_CHECK([
- PKG_CHECK_MODULES([QT_WIDGETS], [${qt_lib_prefix}Widgets${qt_lib_suffix} $qt_version], [],
+ PKG_CHECK_MODULES([QT_WIDGETS], [${qt_lib_prefix}Widgets${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_WIDGETS_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_WIDGETS_LIBS $QT_LIBS"],
[BITCOIN_QT_FAIL([${qt_lib_prefix}Widgets${qt_lib_suffix} $qt_version not found])])
])
BITCOIN_QT_CHECK([
- PKG_CHECK_MODULES([QT_NETWORK], [${qt_lib_prefix}Network${qt_lib_suffix} $qt_version], [],
+ PKG_CHECK_MODULES([QT_NETWORK], [${qt_lib_prefix}Network${qt_lib_suffix} $qt_version], [QT_INCLUDES="$QT_NETWORK_CFLAGS $QT_INCLUDES" QT_LIBS="$QT_NETWORK_LIBS $QT_LIBS"],
[BITCOIN_QT_FAIL([${qt_lib_prefix}Network${qt_lib_suffix} $qt_version not found])])
])
- QT_INCLUDES="$QT_CORE_CFLAGS $QT_GUI_CFLAGS $QT_WIDGETS_CFLAGS $QT_NETWORK_CFLAGS"
- QT_LIBS="$QT_CORE_LIBS $QT_GUI_LIBS $QT_WIDGETS_LIBS $QT_NETWORK_LIBS"
BITCOIN_QT_CHECK([
PKG_CHECK_MODULES([QT_TEST], [${qt_lib_prefix}Test${qt_lib_suffix} $qt_version], [QT_TEST_INCLUDES="$QT_TEST_CFLAGS"; have_qt_test=yes], [have_qt_test=no])
diff --git a/contrib/gitian-descriptors/assign_DISTNAME b/contrib/gitian-descriptors/assign_DISTNAME
index a2ca768aaa..330fbc041b 100755
--- a/contrib/gitian-descriptors/assign_DISTNAME
+++ b/contrib/gitian-descriptors/assign_DISTNAME
@@ -4,7 +4,7 @@
#
# A helper script to be sourced into the gitian descriptors
-if RECENT_TAG="$(git describe --exact-match HEAD)"; then
+if RECENT_TAG="$(git describe --exact-match HEAD 2> /dev/null)"; then
VERSION="${RECENT_TAG#v}"
else
VERSION="$(git rev-parse --short=12 HEAD)"
diff --git a/contrib/guix/README.md b/contrib/guix/README.md
index 1122ec9ba5..2d128c7ed6 100644
--- a/contrib/guix/README.md
+++ b/contrib/guix/README.md
@@ -80,6 +80,50 @@ at the end of the `guix pull`)
export PATH="${HOME}/.config/guix/current/bin${PATH:+:}$PATH"
```
+### Controlling the number of threads used by `guix` build commands
+
+By default, the scripts under `./contrib/guix` will invoke all `guix` build
+commands with `--cores="$JOBS"`. Note that `$JOBS` defaults to `$(nproc)` if not
+specified. However, astute manual readers will also notice that there is a
+`--max-jobs=` flag (which defaults to 1 if unspecified).
+
+Here is the difference between `--cores=` and `--max-jobs=`:
+
+> Note: When I say "derivation," think "package"
+
+`--cores=`
+
+ - controls the number of CPU cores to build each derivation. This is the value
+ passed to `make`'s `--jobs=` flag.
+
+`--max-jobs=`
+
+ - controls how many derivations can be built in parallel
+ - defaults to 1
+
+Therefore, the default is for `guix` build commands to build one derivation at a
+time, utilizing `$JOBS` threads.
+
+Specifying the `$JOBS` environment variable will only modify `--cores=`, but you
+can also modify the value for `--max-jobs=` by specifying
+`$ADDITIONAL_GUIX_COMMON_FLAGS`. For example, if you have a LOT of memory, you
+may want to set:
+
+```sh
+export ADDITIONAL_GUIX_COMMON_FLAGS='--max-jobs=8'
+```
+
+Which allows for a maximum of 8 derivations to be built at the same time, each
+utilizing `$JOBS` threads.
+
+Or, if you'd like to avoid spurious build failures caused by issues with
+parallelism within a single package, but would still like to build multiple
+packages when the dependency graph allows for it, you may want to try:
+
+```sh
+export JOBS=1 ADDITIONAL_GUIX_COMMON_FLAGS='--max-jobs=8'
+```
+
## Usage
### As a Tool for Deterministic Builds
@@ -125,12 +169,16 @@ find output/ -type f -print0 | sort -z | xargs -r0 sha256sum
the actual SDK (e.g. SDK_PATH=$HOME/Downloads/macOS-SDKs instead of
$HOME/Downloads/macOS-SDKs/Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers).
-* _**MAX_JOBS**_
+* _**JOBS**_
+
+ Override the number of jobs to run simultaneously, you might want to do so on
+ a memory-limited machine. This may be passed to:
+
+ - `guix` build commands as in `guix environment --cores="$JOBS"`
+ - `make` as in `make --jobs="$JOBS"`
+ - `xargs` as in `xargs -P"$JOBS"`
- Override the maximum number of jobs to run simultaneously, you might want to
- do so on a memory-limited machine. This may be passed to `make` as in `make
- --jobs="$MAX_JOBS"` or `xargs` as in `xargs -P"$MAX_JOBS"`. _(defaults to the
- value of `nproc` outside the container)_
+ _(defaults to the value of `nproc` outside the container)_
* _**SOURCE_DATE_EPOCH**_
@@ -217,6 +265,57 @@ To use dongcarl's substitute server for Bitcoin Core builds after having
export SUBSTITUTE_URLS='https://guix.carldong.io https://ci.guix.gnu.org'
```
+## Troubleshooting
+
+### Derivation failed to build
+
+When you see a build failure like below:
+
+```
+building /gnu/store/...-foo-3.6.12.drv...
+/ 'check' phasenote: keeping build directory `/tmp/guix-build-foo-3.6.12.drv-0'
+builder for `/gnu/store/...-foo-3.6.12.drv' failed with exit code 1
+build of /gnu/store/...-foo-3.6.12.drv failed
+View build log at '/var/log/guix/drvs/../...-foo-3.6.12.drv.bz2'.
+cannot build derivation `/gnu/store/...-qux-7.69.1.drv': 1 dependencies couldn't be built
+cannot build derivation `/gnu/store/...-bar-3.16.5.drv': 1 dependencies couldn't be built
+cannot build derivation `/gnu/store/...-baz-2.0.5.drv': 1 dependencies couldn't be built
+guix time-machine: error: build of `/gnu/store/...-baz-2.0.5.drv' failed
+```
+
+It means that `guix` failed to build a package named `foo`, which was a
+dependency of `qux`, `bar`, and `baz`. Importantly, note that the last "failed"
+line is not necessarily the root cause, the first "failed" line is.
+
+Most of the time, the build failure is due to a spurious test failure or the
+package's build system/test suite breaking when running multi-threaded. To
+rebuild _just_ this derivation in a single-threaded fashion:
+
+```sh
+$ guix build --cores=1 /gnu/store/...-foo-3.6.12.drv
+```
+
+If the single-threaded rebuild stil did not succeed, you may need to dig deeper.
+You may view `foo`'s build logs in `less` like so (please replace paths with the
+path you see in the build failure output):
+
+```sh
+$ bzcat /var/log/guix/drvs/../...-foo-3.6.12.drv.bz2 | less
+```
+
+`foo`'s build directory is also preserved and available at
+`/tmp/guix-build-foo-3.6.12.drv-0`. However, if you fail to build `foo` multiple
+times, it may be `/tmp/...drv-1` or `/tmp/...drv-2`. Always consult the build
+failure output for the most accurate, up-to-date information.
+
+#### python(-minimal): [Errno 84] Invalid or incomplete multibyte or wide character
+
+This error occurs when your `$TMPDIR` (default: /tmp) exists on a filesystem
+which rejects characters not present in the UTF-8 character code set. An example
+is ZFS with the utf8only=on option set.
+
+More information: https://bugs.python.org/issue37584
+
## FAQ
### How can I trust the binary installation?
diff --git a/contrib/guix/guix-build.sh b/contrib/guix/guix-build
index 7c32fb5726..da127b54aa 100755
--- a/contrib/guix/guix-build.sh
+++ b/contrib/guix/guix-build
@@ -2,22 +2,26 @@
export LC_ALL=C
set -e -o pipefail
+# Source the common prelude, which:
+# 1. Checks if we're at the top directory of the Bitcoin Core repository
+# 2. Defines a few common functions and variables
+#
+# shellcheck source=libexec/prelude.bash
+source "$(dirname "${BASH_SOURCE[0]}")/libexec/prelude.bash"
+
+
###################
-## Sanity Checks ##
+## SANITY CHECKS ##
###################
################
-# Check 1: Make sure that we can invoke required tools
+# Required non-builtin commands should be invokable
################
-for cmd in git make guix cat mkdir curl; do
- if ! command -v "$cmd" > /dev/null 2>&1; then
- echo "ERR: This script requires that '$cmd' is installed and available in your \$PATH"
- exit 1
- fi
-done
+
+check_tools cat mkdir make git guix
################
-# Check 2: Make sure GUIX_BUILD_OPTIONS is empty
+# GUIX_BUILD_OPTIONS should be empty
################
#
# GUIX_BUILD_OPTIONS is an environment variable recognized by guix commands that
@@ -45,8 +49,9 @@ exit 1
fi
################
-# Check 3: Make sure that we're not in a dirty worktree
+# The git worktree should not be dirty
################
+
if ! git diff-index --quiet HEAD -- && [ -z "$FORCE_DIRTY_WORKTREE" ]; then
cat << EOF
ERR: The current git worktree is dirty, which may lead to broken builds.
@@ -60,12 +65,12 @@ Hint: To make your git worktree clean, You may want to:
using a dirty worktree
EOF
exit 1
-else
- GIT_COMMIT=$(git rev-parse --short=12 HEAD)
fi
+mkdir -p "$VERSION_BASE"
+
################
-# Check 4: Make sure that build directories do not exist
+# Build directories should not exist
################
# Default to building for all supported HOSTs (overridable by environment)
@@ -73,14 +78,12 @@ export HOSTS="${HOSTS:-x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu ri
x86_64-w64-mingw32
x86_64-apple-darwin18}"
-DISTSRC_BASE="${DISTSRC_BASE:-${PWD}}"
-
# Usage: distsrc_for_host HOST
#
# HOST: The current platform triple we're building for
#
distsrc_for_host() {
- echo "${DISTSRC_BASE}/distsrc-${GIT_COMMIT}-${1}"
+ echo "${DISTSRC_BASE}/distsrc-${VERSION}-${1}"
}
# Accumulate a list of build directories that already exist...
@@ -106,12 +109,11 @@ for host in $hosts_distsrc_exists; do
done
exit 1
else
-
mkdir -p "$DISTSRC_BASE"
fi
################
-# Check 5: When building for darwin, make sure that the macOS SDK exists
+# When building for darwin, the macOS SDK should exists
################
for host in $HOSTS; do
@@ -128,13 +130,40 @@ for host in $HOSTS; do
esac
done
+################
+# Check that we can connect to the guix-daemon
+################
+
+cat << EOF
+Checking that we can connect to the guix-daemon...
+
+Hint: If this hangs, you may want to try turning your guix-daemon off and on
+ again.
+
+EOF
+if ! guix gc --list-failures > /dev/null; then
+cat << EOF
+
+ERR: Failed to connect to the guix-daemon, please ensure that one is running and
+ reachable.
+EOF
+exit 1
+fi
+
+# Developer note: we could use `guix repl` for this check and run:
+#
+# (import (guix store)) (close-connection (open-connection))
+#
+# However, the internal API is likely to change more than the CLI invocation
+
+
#########
-# Setup #
+# SETUP #
#########
# Determine the maximum number of jobs to run simultaneously (overridable by
# environment)
-MAX_JOBS="${MAX_JOBS:-$(nproc)}"
+JOBS="${JOBS:-$(nproc)}"
# Usage: host_to_commonname HOST
#
@@ -152,7 +181,7 @@ host_to_commonname() {
# Download the depends sources now as we won't have internet access in the build
# container
for host in $HOSTS; do
- make -C "${PWD}/depends" -j"$MAX_JOBS" download-"$(host_to_commonname "$host")" ${V:+V=1} ${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"}
+ make -C "${PWD}/depends" -j"$JOBS" download-"$(host_to_commonname "$host")" ${V:+V=1} ${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"}
done
# Determine the reference time used for determinism (overridable by environment)
@@ -164,19 +193,29 @@ time-machine() {
# shellcheck disable=SC2086
guix time-machine --url=https://github.com/dongcarl/guix.git \
--commit=490e39ff303f4f6873a04bfb8253755bdae1b29c \
- --max-jobs="$MAX_JOBS" \
+ --cores="$JOBS" \
--keep-failed \
+ --fallback \
${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"} \
${ADDITIONAL_GUIX_COMMON_FLAGS} ${ADDITIONAL_GUIX_TIMEMACHINE_FLAGS} \
-- "$@"
}
# Make sure an output directory exists for our builds
-OUTDIR="${OUTDIR:-${PWD}/output}"
-[ -e "$OUTDIR" ] || mkdir -p "$OUTDIR"
+OUTDIR_BASE="${OUTDIR_BASE:-${VERSION_BASE}/output}"
+mkdir -p "$OUTDIR_BASE"
+
+# Usage: outdir_for_host HOST
+#
+# HOST: The current platform triple we're building for
+#
+outdir_for_host() {
+ echo "${OUTDIR_BASE}/${1}"
+}
+
#########
-# Build #
+# BUILD #
#########
# Function to be called when building for host ${1} and the user interrupts the
@@ -216,15 +255,15 @@ for host in $HOSTS; do
# shellcheck disable=SC2030
cat << EOF
-INFO: Building commit ${GIT_COMMIT:?not set} for platform triple ${HOST:?not set}:
+INFO: Building ${VERSION:?not set} for platform triple ${HOST:?not set}:
...using reference timestamp: ${SOURCE_DATE_EPOCH:?not set}
- ...running at most ${MAX_JOBS:?not set} jobs
+ ...running at most ${JOBS:?not set} jobs
...from worktree directory: '${PWD}'
...bind-mounted in container to: '/bitcoin'
...in build directory: '$(distsrc_for_host "$HOST")'
...bind-mounted in container to: '$(DISTSRC_BASE=/distsrc-base && distsrc_for_host "$HOST")'
- ...outputting in: '${OUTDIR:?not set}'
- ...bind-mounted in container to: '/outdir'
+ ...outputting in: '$(outdir_for_host "$HOST")'
+ ...bind-mounted in container to: '$(OUTDIR_BASE=/outdir-base && outdir_for_host "$HOST")'
EOF
# Run the build script 'contrib/guix/libexec/build.sh' in the build
@@ -299,24 +338,28 @@ EOF
--no-cwd \
--share="$PWD"=/bitcoin \
--share="$DISTSRC_BASE"=/distsrc-base \
- --share="$OUTDIR"=/outdir \
+ --share="$OUTDIR_BASE"=/outdir-base \
--expose="$(git rev-parse --git-common-dir)" \
${SOURCES_PATH:+--share="$SOURCES_PATH"} \
${BASE_CACHE:+--share="$BASE_CACHE"} \
${SDK_PATH:+--share="$SDK_PATH"} \
- --max-jobs="$MAX_JOBS" \
+ --cores="$JOBS" \
--keep-failed \
+ --fallback \
+ --link-profile \
${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"} \
${ADDITIONAL_GUIX_COMMON_FLAGS} ${ADDITIONAL_GUIX_ENVIRONMENT_FLAGS} \
-- env HOST="$host" \
- MAX_JOBS="$MAX_JOBS" \
+ DISTNAME="$DISTNAME" \
+ JOBS="$JOBS" \
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:?unable to determine value}" \
${V:+V=1} \
${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"} \
${BASE_CACHE:+BASE_CACHE="$BASE_CACHE"} \
${SDK_PATH:+SDK_PATH="$SDK_PATH"} \
DISTSRC="$(DISTSRC_BASE=/distsrc-base && distsrc_for_host "$HOST")" \
- OUTDIR=/outdir \
+ OUTDIR="$(OUTDIR_BASE=/outdir-base && outdir_for_host "$HOST")" \
+ DIST_ARCHIVE_BASE=/outdir-base/dist-archive \
bash -c "cd /bitcoin && bash contrib/guix/libexec/build.sh"
)
diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh
index 051066a6a0..4239c3d475 100644
--- a/contrib/guix/libexec/build.sh
+++ b/contrib/guix/libexec/build.sh
@@ -24,9 +24,11 @@ fi
# Check that required environment variables are set
cat << EOF
Required environment variables as seen inside the container:
+ DIST_ARCHIVE_BASE: ${DIST_ARCHIVE_BASE:?not set}
+ DISTNAME: ${DISTNAME:?not set}
HOST: ${HOST:?not set}
SOURCE_DATE_EPOCH: ${SOURCE_DATE_EPOCH:?not set}
- MAX_JOBS: ${MAX_JOBS:?not set}
+ JOBS: ${JOBS:?not set}
DISTSRC: ${DISTSRC:?not set}
OUTDIR: ${OUTDIR:?not set}
EOF
@@ -52,16 +54,36 @@ store_path() {
# Set environment variables to point the NATIVE toolchain to the right
# includes/libs
NATIVE_GCC="$(store_path gcc-toolchain)"
-export LIBRARY_PATH="${NATIVE_GCC}/lib:${NATIVE_GCC}/lib64"
-export CPATH="${NATIVE_GCC}/include"
+NATIVE_GCC_STATIC="$(store_path gcc-toolchain static)"
+
+unset LIBRARY_PATH
+unset CPATH
unset C_INCLUDE_PATH
unset CPLUS_INCLUDE_PATH
+unset OBJC_INCLUDE_PATH
+unset OBJCPLUS_INCLUDE_PATH
+
+export LIBRARY_PATH="${NATIVE_GCC}/lib:${NATIVE_GCC}/lib64:${NATIVE_GCC_STATIC}/lib:${NATIVE_GCC_STATIC}/lib64"
+export C_INCLUDE_PATH="${NATIVE_GCC}/include"
+export CPLUS_INCLUDE_PATH="${NATIVE_GCC}/include/c++:${NATIVE_GCC}/include"
+export OBJC_INCLUDE_PATH="${NATIVE_GCC}/include"
+export OBJCPLUS_INCLUDE_PATH="${NATIVE_GCC}/include/c++:${NATIVE_GCC}/include"
+
+prepend_to_search_env_var() {
+ export "${1}=${2}${!1:+:}${!1}"
+}
+
case "$HOST" in
*darwin*)
# When targeting darwin, zlib is required by native_libdmg-hfsplus.
zlib_store_path=$(store_path "zlib")
- export LIBRARY_PATH="${zlib_store_path}/lib:${LIBRARY_PATH}"
- export CPATH="${zlib_store_path}/include:${CPATH}"
+ zlib_static_store_path=$(store_path "zlib" static)
+
+ prepend_to_search_env_var LIBRARY_PATH "${zlib_static_store_path}/lib:${zlib_store_path}/lib"
+ prepend_to_search_env_var C_INCLUDE_PATH "${zlib_store_path}/include"
+ prepend_to_search_env_var CPLUS_INCLUDE_PATH "${zlib_store_path}/include"
+ prepend_to_search_env_var OBJC_INCLUDE_PATH "${zlib_store_path}/include"
+ prepend_to_search_env_var OBJCPLUS_INCLUDE_PATH "${zlib_store_path}/include"
esac
# Set environment variables to point the CROSS toolchain to the right
@@ -173,7 +195,7 @@ esac
####################
# Build the depends tree, overriding variables that assume multilib gcc
-make -C depends --jobs="$MAX_JOBS" HOST="$HOST" \
+make -C depends --jobs="$JOBS" HOST="$HOST" \
${V:+V=1} \
${SOURCES_PATH+SOURCES_PATH="$SOURCES_PATH"} \
${BASE_CACHE+BASE_CACHE="$BASE_CACHE"} \
@@ -198,11 +220,7 @@ make -C depends --jobs="$MAX_JOBS" HOST="$HOST" \
# Source Tarball Building #
###########################
-# Define DISTNAME variable.
-# shellcheck source=contrib/gitian-descriptors/assign_DISTNAME
-source contrib/gitian-descriptors/assign_DISTNAME
-
-GIT_ARCHIVE="${OUTDIR}/src/${DISTNAME}.tar.gz"
+GIT_ARCHIVE="${DIST_ARCHIVE_BASE}/${DISTNAME}.tar.gz"
# Create the source tarball if not already there
if [ ! -e "$GIT_ARCHIVE" ]; then
@@ -267,7 +285,7 @@ mkdir -p "$DISTSRC"
sed -i.old 's/-lstdc++ //g' config.status libtool src/univalue/config.status src/univalue/libtool
# Build Bitcoin Core
- make --jobs="$MAX_JOBS" ${V:+V=1}
+ make --jobs="$JOBS" ${V:+V=1}
# Perform basic ELF security checks on a series of executables.
make -C src --jobs=1 check-security ${V:+V=1}
@@ -275,6 +293,7 @@ mkdir -p "$DISTSRC"
# version symbols for Linux distro back-compatibility.
make -C src --jobs=1 check-symbols ${V:+V=1}
+ mkdir -p ${OUTDIR}
# Make the os-specific installers
case "$HOST" in
*mingw*)
@@ -306,9 +325,6 @@ mkdir -p "$DISTSRC"
osx_volname \
contrib/macdeploy/detached-sig-{apply,create}.sh \
"${BASEPREFIX}/${HOST}"/native/bin/dmg
- for util in codesign_allocate pagestuff; do
- cp --no-target-directory {"${BASEPREFIX}/${HOST}/native/bin/${HOST}-","unsigned-app-${HOST}/"}"$util"
- done
mv --target-directory="unsigned-app-${HOST}" dist
(
cd "unsigned-app-${HOST}"
@@ -344,7 +360,7 @@ mkdir -p "$DISTSRC"
{
find "${DISTNAME}/bin" -type f -executable -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"$JOBS" -I{} "${DISTSRC}/contrib/devtools/split-debug.sh" {} {} {}.dbg
;;
esac
@@ -394,21 +410,21 @@ mkdir -p "$DISTSRC"
|| ( rm -f "${OUTDIR}/${DISTNAME}-${HOST//x86_64-apple-darwin18/osx64}.tar.gz" && exit 1 )
;;
esac
- )
-)
+ ) # $DISTSRC/installed
-case "$HOST" in
- *mingw*)
- cp -rf --target-directory=. contrib/windeploy
- (
- cd ./windeploy
- mkdir unsigned
- cp --target-directory=unsigned/ "${OUTDIR}/${DISTNAME}-win64-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
+ case "$HOST" in
+ *mingw*)
+ cp -rf --target-directory=. contrib/windeploy
+ (
+ cd ./windeploy
+ mkdir -p unsigned
+ cp --target-directory=unsigned/ "${OUTDIR}/${DISTNAME}-win64-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
+) # $DISTSRC
diff --git a/contrib/guix/libexec/prelude.bash b/contrib/guix/libexec/prelude.bash
new file mode 100644
index 0000000000..33a319012c
--- /dev/null
+++ b/contrib/guix/libexec/prelude.bash
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+export LC_ALL=C
+set -e -o pipefail
+
+# shellcheck source=../../shell/realpath.bash
+source contrib/shell/realpath.bash
+
+# shellcheck source=../../shell/git-utils.bash
+source contrib/shell/git-utils.bash
+
+################
+# Required non-builtin commands should be invokable
+################
+
+check_tools() {
+ for cmd in "$@"; do
+ if ! command -v "$cmd" > /dev/null 2>&1; then
+ echo "ERR: This script requires that '$cmd' is installed and available in your \$PATH"
+ exit 1
+ fi
+ done
+}
+
+check_tools cat env readlink dirname basename git
+
+################
+# We should be at the top directory of the repository
+################
+
+same_dir() {
+ local resolved1 resolved2
+ resolved1="$(bash_realpath "${1}")"
+ resolved2="$(bash_realpath "${2}")"
+ [ "$resolved1" = "$resolved2" ]
+}
+
+if ! same_dir "${PWD}" "$(git_root)"; then
+cat << EOF
+ERR: This script must be invoked from the top level of the git repository
+
+Hint: This may look something like:
+ env FOO=BAR ./contrib/guix/guix-<blah>
+
+EOF
+exit 1
+fi
+
+################
+# Set common variables
+################
+
+VERSION="${VERSION:-$(git_head_version)}"
+DISTNAME="${DISTNAME:-bitcoin-${VERSION}}"
+
+version_base_prefix="${PWD}/guix-build-"
+VERSION_BASE="${version_base_prefix}${VERSION}" # TOP
+
+DISTSRC_BASE="${DISTSRC_BASE:-${VERSION_BASE}}"
+
+OUTDIR_BASE="${OUTDIR_BASE:-${VERSION_BASE}/output}"
diff --git a/contrib/guix/manifest.scm b/contrib/guix/manifest.scm
index 4228532cb1..910a9dd6f6 100644
--- a/contrib/guix/manifest.scm
+++ b/contrib/guix/manifest.scm
@@ -214,6 +214,7 @@ chain for " target " development."))
gzip
xz
zlib
+ (list zlib "static")
;; Build tools
gnu-make
libtool
@@ -227,7 +228,8 @@ chain for " target " development."))
;; Git
git
;; Native gcc 7 toolchain
- gcc-toolchain-7)
+ gcc-toolchain-7
+ (list gcc-toolchain-7 "static"))
(let ((target (getenv "HOST")))
(cond ((string-suffix? "-mingw32" target)
;; Windows
@@ -237,5 +239,5 @@ chain for " target " development."))
((string-contains target "-linux-")
(list (make-bitcoin-cross-toolchain target)))
((string-contains target "darwin")
- (list clang-8 binutils imagemagick libtiff librsvg font-tuffy cmake xorriso))
+ (list clang-toolchain-8 binutils imagemagick libtiff librsvg font-tuffy cmake xorriso))
(else '())))))
diff --git a/contrib/shell/git-utils.bash b/contrib/shell/git-utils.bash
new file mode 100644
index 0000000000..37bac1f38d
--- /dev/null
+++ b/contrib/shell/git-utils.bash
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+git_root() {
+ git rev-parse --show-toplevel 2> /dev/null
+}
+
+git_head_version() {
+ local recent_tag
+ if recent_tag="$(git describe --exact-match HEAD 2> /dev/null)"; then
+ echo "${recent_tag#v}"
+ else
+ git rev-parse --short=12 HEAD
+ fi
+}
diff --git a/contrib/shell/realpath.bash b/contrib/shell/realpath.bash
new file mode 100644
index 0000000000..389b77b562
--- /dev/null
+++ b/contrib/shell/realpath.bash
@@ -0,0 +1,71 @@
+#!/usr/bin/env bash
+
+# Based on realpath.sh written by Michael Kropat
+# Found at: https://github.com/mkropat/sh-realpath/blob/65512368b8155b176b67122aa395ac580d9acc5b/realpath.sh
+
+bash_realpath() {
+ canonicalize_path "$(resolve_symlinks "$1")"
+}
+
+resolve_symlinks() {
+ _resolve_symlinks "$1"
+}
+
+_resolve_symlinks() {
+ _assert_no_path_cycles "$@" || return
+
+ local dir_context path
+ if path=$(readlink -- "$1"); then
+ dir_context=$(dirname -- "$1")
+ _resolve_symlinks "$(_prepend_dir_context_if_necessary "$dir_context" "$path")" "$@"
+ else
+ printf '%s\n' "$1"
+ fi
+}
+
+_prepend_dir_context_if_necessary() {
+ if [ "$1" = . ]; then
+ printf '%s\n' "$2"
+ else
+ _prepend_path_if_relative "$1" "$2"
+ fi
+}
+
+_prepend_path_if_relative() {
+ case "$2" in
+ /* ) printf '%s\n' "$2" ;;
+ * ) printf '%s\n' "$1/$2" ;;
+ esac
+}
+
+_assert_no_path_cycles() {
+ local target path
+
+ target=$1
+ shift
+
+ for path in "$@"; do
+ if [ "$path" = "$target" ]; then
+ return 1
+ fi
+ done
+}
+
+canonicalize_path() {
+ if [ -d "$1" ]; then
+ _canonicalize_dir_path "$1"
+ else
+ _canonicalize_file_path "$1"
+ fi
+}
+
+_canonicalize_dir_path() {
+ (cd "$1" 2>/dev/null && pwd -P)
+}
+
+_canonicalize_file_path() {
+ local dir file
+ dir=$(dirname -- "$1")
+ file=$(basename -- "$1")
+ (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
+}
diff --git a/depends/Makefile b/depends/Makefile
index 4cd4d72fc2..dadb21515a 100644
--- a/depends/Makefile
+++ b/depends/Makefile
@@ -141,8 +141,8 @@ build_id_string+=system_clang
$(host_arch)_$(host_os)_id_string+=system_clang
endif
-build_id_string+=GUIX_ENVIRONMENT=$(GUIX_ENVIRONMENT)
-$(host_arch)_$(host_os)_id_string+=GUIX_ENVIRONMENT=$(GUIX_ENVIRONMENT)
+build_id_string+=GUIX_ENVIRONMENT=$(realpath $(GUIX_ENVIRONMENT))
+$(host_arch)_$(host_os)_id_string+=GUIX_ENVIRONMENT=$(realpath $(GUIX_ENVIRONMENT))
qrencode_packages_$(NO_QR) = $(qrencode_packages)
diff --git a/depends/packages/native_libdmg-hfsplus.mk b/depends/packages/native_libdmg-hfsplus.mk
index 035b767188..c7c8adef41 100644
--- a/depends/packages/native_libdmg-hfsplus.mk
+++ b/depends/packages/native_libdmg-hfsplus.mk
@@ -12,7 +12,7 @@ define $(package)_preprocess_cmds
endef
define $(package)_config_cmds
- $($(package)_cmake) -DCMAKE_C_FLAGS="$$($(1)_cflags) -Wl,--build-id=none" ..
+ $($(package)_cmake) -DCMAKE_C_FLAGS="$$($(1)_cflags) -Wl,--build-id=none" -DCMAKE_SKIP_RPATH="ON" -DCMAKE_EXE_LINKER_FLAGS="-static" -DCMAKE_FIND_LIBRARY_SUFFIXES=".a" ..
endef
define $(package)_build_cmds
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index ee6796e2ad..ae83eae911 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -10,7 +10,7 @@ $(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_no_printer.patch no
$(package)_patches+= fix_android_qmake_conf.patch fix_android_jni_static.patch dont_hardcode_pwd.patch
$(package)_patches+= drop_lrelease_dependency.patch no_sdk_version_check.patch
$(package)_patches+= fix_qpainter_non_determinism.patch fix_lib_paths.patch fix_android_pch.patch
-$(package)_patches+= fix_bigsur_drawing.patch
+$(package)_patches+= fix_bigsur_drawing.patch qtbase-moc-ignore-gcc-macro.patch
$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix)
$(package)_qttranslations_sha256_hash=e1de58ed108b7e0a138815ea60fd46a2c4e1fc31396a707e5630e92de79c53de
@@ -232,6 +232,7 @@ define $(package)_preprocess_cmds
patch -p1 -i $($(package)_patch_dir)/no_sdk_version_check.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_lib_paths.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_bigsur_drawing.patch && \
+ patch -p1 -i $($(package)_patch_dir)/qtbase-moc-ignore-gcc-macro.patch && \
sed -i.old "s|updateqm.commands = \$$$$\$$$$LRELEASE|updateqm.commands = $($(package)_extract_dir)/qttools/bin/lrelease|" qttranslations/translations/translations.pro && \
mkdir -p qtbase/mkspecs/macx-clang-linux &&\
cp -f qtbase/mkspecs/macx-clang/qplatformdefs.h qtbase/mkspecs/macx-clang-linux/ &&\
diff --git a/depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch b/depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch
new file mode 100644
index 0000000000..0358bea6e9
--- /dev/null
+++ b/depends/patches/qt/qtbase-moc-ignore-gcc-macro.patch
@@ -0,0 +1,17 @@
+The moc executable loops through headers on CPLUS_INCLUDE_PATH and stumbles
+on the GCC internal _GLIBCXX_VISIBILITY macro. Tell it to ignore it as it is
+not supposed to be looking there to begin with.
+
+Upstream report: https://bugreports.qt.io/browse/QTBUG-83160
+
+diff --git a/qtbase/src/tools/moc/main.cpp b/qtbase/src/tools/moc/main.cpp
+--- a/qtbase/src/tools/moc/main.cpp
++++ b/qtbase/src/tools/moc/main.cpp
+@@ -188,6 +188,7 @@ int runMoc(int argc, char **argv)
+ dummyVariadicFunctionMacro.arguments += Symbol(0, PP_IDENTIFIER, "__VA_ARGS__");
+ pp.macros["__attribute__"] = dummyVariadicFunctionMacro;
+ pp.macros["__declspec"] = dummyVariadicFunctionMacro;
++ pp.macros["_GLIBCXX_VISIBILITY"] = dummyVariadicFunctionMacro;
+
+ QString filename;
+ QString output;
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 52499cd1d6..ba3105ca01 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -113,17 +113,80 @@ std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
return ParseHexV(find_value(o, strKey), strKey);
}
+namespace {
+
+/**
+ * Quote an argument for shell.
+ *
+ * @note This is intended for help, not for security-sensitive purposes.
+ */
+std::string ShellQuote(const std::string& s)
+{
+ std::string result;
+ result.reserve(s.size() * 2);
+ for (const char ch: s) {
+ if (ch == '\'') {
+ result += "'\''";
+ } else {
+ result += ch;
+ }
+ }
+ return "'" + result + "'";
+}
+
+/**
+ * Shell-quotes the argument if it needs quoting, else returns it literally, to save typing.
+ *
+ * @note This is intended for help, not for security-sensitive purposes.
+ */
+std::string ShellQuoteIfNeeded(const std::string& s)
+{
+ for (const char ch: s) {
+ if (ch == ' ' || ch == '\'' || ch == '"') {
+ return ShellQuote(s);
+ }
+ }
+
+ return s;
+}
+
+}
+
std::string HelpExampleCli(const std::string& methodname, const std::string& args)
{
return "> bitcoin-cli " + methodname + " " + args + "\n";
}
+std::string HelpExampleCliNamed(const std::string& methodname, const RPCArgList& args)
+{
+ std::string result = "> bitcoin-cli -named " + methodname;
+ for (const auto& argpair: args) {
+ const auto& value = argpair.second.isStr()
+ ? argpair.second.get_str()
+ : argpair.second.write();
+ result += " " + argpair.first + "=" + ShellQuoteIfNeeded(value);
+ }
+ result += "\n";
+ return result;
+}
+
std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
{
return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", "
"\"method\": \"" + methodname + "\", \"params\": [" + args + "]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
}
+std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList& args)
+{
+ UniValue params(UniValue::VOBJ);
+ for (const auto& param: args) {
+ params.pushKV(param.first, param.second);
+ }
+
+ return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", "
+ "\"method\": \"" + methodname + "\", \"params\": " + params.write() + "}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
+}
+
// Converts a hex string to a public key if possible
CPubKey HexToPubKey(const std::string& hex_in)
{
diff --git a/src/rpc/util.h b/src/rpc/util.h
index db7f626e9c..6e783e19fb 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -78,8 +78,12 @@ extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strNa
extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
extern CAmount AmountFromValue(const UniValue& value);
+
+using RPCArgList = std::vector<std::pair<std::string, UniValue>>;
extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
+extern std::string HelpExampleCliNamed(const std::string& methodname, const RPCArgList& args);
extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
+extern std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList& args);
CPubKey HexToPubKey(const std::string& hex_in);
CPubKey AddrToPubKey(const FillableSigningProvider& keystore, const std::string& addr_in);
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index 67e70b3bc3..7c50c72041 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -431,4 +431,39 @@ BOOST_AUTO_TEST_CASE(rpc_getblockstats_calculate_percentiles_by_weight)
}
}
+BOOST_AUTO_TEST_CASE(help_example)
+{
+ // test different argument types
+ const RPCArgList& args = {{"foo", "bar"}, {"b", true}, {"n", 1}};
+ BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", args), "> bitcoin-cli -named test foo=bar b=true n=1\n");
+ BOOST_CHECK_EQUAL(HelpExampleRpcNamed("test", args), "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", \"method\": \"test\", \"params\": {\"foo\":\"bar\",\"b\":true,\"n\":1}}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n");
+
+ // test shell escape
+ BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", {{"foo", "b'ar"}}), "> bitcoin-cli -named test foo='b'''ar'\n");
+ BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", {{"foo", "b\"ar"}}), "> bitcoin-cli -named test foo='b\"ar'\n");
+ BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", {{"foo", "b ar"}}), "> bitcoin-cli -named test foo='b ar'\n");
+
+ // test object params
+ UniValue obj_value(UniValue::VOBJ);
+ obj_value.pushKV("foo", "bar");
+ obj_value.pushKV("b", false);
+ obj_value.pushKV("n", 1);
+ BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", {{"name", obj_value}}), "> bitcoin-cli -named test name='{\"foo\":\"bar\",\"b\":false,\"n\":1}'\n");
+ BOOST_CHECK_EQUAL(HelpExampleRpcNamed("test", {{"name", obj_value}}), "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", \"method\": \"test\", \"params\": {\"name\":{\"foo\":\"bar\",\"b\":false,\"n\":1}}}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n");
+
+ // test array params
+ UniValue arr_value(UniValue::VARR);
+ arr_value.push_back("bar");
+ arr_value.push_back(false);
+ arr_value.push_back(1);
+ BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", {{"name", arr_value}}), "> bitcoin-cli -named test name='[\"bar\",false,1]'\n");
+ BOOST_CHECK_EQUAL(HelpExampleRpcNamed("test", {{"name", arr_value}}), "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", \"method\": \"test\", \"params\": {\"name\":[\"bar\",false,1]}}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n");
+
+ // test types don't matter for shell
+ BOOST_CHECK_EQUAL(HelpExampleCliNamed("foo", {{"arg", true}}), HelpExampleCliNamed("foo", {{"arg", "true"}}));
+
+ // test types matter for Rpc
+ BOOST_CHECK_NE(HelpExampleRpcNamed("foo", {{"arg", true}}), HelpExampleRpcNamed("foo", {{"arg", "true"}}));
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index df26f039a0..45f292055e 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -2711,6 +2711,8 @@ static RPCHelpMan createwallet()
RPCExamples{
HelpExampleCli("createwallet", "\"testwallet\"")
+ HelpExampleRpc("createwallet", "\"testwallet\"")
+ + HelpExampleCliNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"descriptors", true}, {"load_on_startup", true}})
+ + HelpExampleRpcNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"descriptors", true}, {"load_on_startup", true}})
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan
index 97f0f45e7f..27885b094e 100644
--- a/test/sanitizer_suppressions/ubsan
+++ b/test/sanitizer_suppressions/ubsan
@@ -1,6 +1,10 @@
# -fsanitize=undefined suppressions
# =================================
-# No suppressions at the moment. Hooray!
+# This would be `signed-integer-overflow:CTxMemPool::PrioritiseTransaction`,
+# however due to a bug in clang the symbolizer is disabled and thus no symbol
+# names can be used.
+# See https://github.com/google/sanitizers/issues/1364
+signed-integer-overflow:txmempool.cpp
# -fsanitize=integer suppressions
# ===============================