03_test_script.sh raw
1 #!/usr/bin/env bash
2 #
3 # Copyright (c) 2018-present The Bitcoin Core developers
4 # Distributed under the MIT software license, see the accompanying
5 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
6
7 export LC_ALL=C.UTF-8
8
9 set -o errexit -o xtrace -o pipefail
10
11 if [ "${DANGER_RUN_CI_ON_HOST}" != "1" ]; then
12 echo "This script will make unsafe local and global modifications, so it can only be run inside a container and requires DANGER_RUN_CI_ON_HOST=1"
13 exit 1
14 fi
15
16 cd "${BASE_ROOT_DIR}"
17
18 export PATH="/path_with space:${PATH}"
19 export ASAN_OPTIONS="detect_leaks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1"
20 export LSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/lsan"
21 export TSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/tsan:halt_on_error=1:second_deadlock_stack=1"
22 export UBSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1:report_error_type=1"
23
24 echo "Number of available processing units: $(nproc)"
25 if [ "$CI_OS_NAME" == "macos" ]; then
26 top -l 1 -s 0 | awk ' /PhysMem/ {print}'
27 else
28 free -m -h
29 echo "System info: $(uname --kernel-name --kernel-release)"
30 lscpu
31 fi
32 echo "Free disk space:"
33 df -h
34
35 # We force an install of linux-headers again here via $PACKAGES to fix any
36 # kernel mismatch between a cached docker image and the underlying host.
37 # This can happen occasionally on hosted runners if the runner image is updated.
38 if [[ "$CONTAINER_NAME" == "ci_native_asan" ]]; then
39 $CI_RETRY_EXE apt-get update
40 ${CI_RETRY_EXE} bash -c "apt-get install --no-install-recommends --no-upgrade -y $PACKAGES"
41 fi
42
43 # What host to compile for. See also ./depends/README.md
44 # Tests that need cross-compilation export the appropriate HOST.
45 # Tests that run natively guess the host
46 export HOST=${HOST:-$("$BASE_ROOT_DIR/depends/config.guess")}
47
48 echo "=== BEGIN env ==="
49 env
50 echo "=== END env ==="
51
52 # The CI framework should be flexible where it is run from. For example, from
53 # a git-archive, a git-worktree, or a normal git repo.
54 # The iwyu task requires a working git repo, which may not always be
55 # available, so initialize one with force.
56 if [[ "${RUN_IWYU}" == true ]]; then
57 mv .git .git_ci_backup || true
58 git init
59 git add ./src # the git diff command used later for iwyu only cares about ./src
60 git config user.email "ci@ci"
61 git config user.name "CI"
62 git commit -m "dummy CI ./src init for IWYU"
63 fi
64
65 if [ "$RUN_FUZZ_TESTS" = "true" ]; then
66 export DIR_FUZZ_IN=${DIR_QA_ASSETS}/fuzz_corpora/
67 if [ ! -d "$DIR_FUZZ_IN" ]; then
68 ${CI_RETRY_EXE} git clone --depth=1 https://github.com/bitcoin-core/qa-assets "${DIR_QA_ASSETS}"
69 fi
70 (
71 cd "${DIR_QA_ASSETS}"
72 echo "Using qa-assets repo from commit ..."
73 git log -1
74 )
75 elif [ "$RUN_UNIT_TESTS" = "true" ]; then
76 export DIR_UNIT_TEST_DATA=${DIR_QA_ASSETS}/unit_test_data/
77 if [ ! -d "$DIR_UNIT_TEST_DATA" ]; then
78 mkdir -p "$DIR_UNIT_TEST_DATA"
79 ${CI_RETRY_EXE} curl --location --fail https://github.com/bitcoin-core/qa-assets/raw/main/unit_test_data/script_assets_test.json -o "${DIR_UNIT_TEST_DATA}/script_assets_test.json"
80 fi
81 fi
82
83 # Make sure default datadir does not exist and is never read by creating a dummy file
84 if [ "$CI_OS_NAME" == "macos" ]; then
85 echo > "${HOME}/Library/Application Support/Bitcoin"
86 else
87 echo > "${HOME}/.bitcoin"
88 fi
89
90 if [ -z "$NO_DEPENDS" ]; then
91 if [[ $CI_IMAGE_NAME_TAG == *alpine* ]]; then
92 SHELL_OPTS="CONFIG_SHELL=/usr/bin/dash"
93 else
94 SHELL_OPTS="CONFIG_SHELL="
95 fi
96 bash -c "$SHELL_OPTS make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS LOG=1"
97 fi
98 if [ "$DOWNLOAD_PREVIOUS_RELEASES" = "true" ]; then
99 test/get_previous_releases.py --target-dir "$PREVIOUS_RELEASES_DIR"
100 fi
101
102 BITCOIN_CONFIG_ALL="-DCMAKE_COMPILE_WARNING_AS_ERROR=ON -DBUILD_BENCH=ON -DBUILD_FUZZ_BINARY=ON"
103 if [ -z "$NO_DEPENDS" ]; then
104 BITCOIN_CONFIG_ALL="${BITCOIN_CONFIG_ALL} -DCMAKE_TOOLCHAIN_FILE=$DEPENDS_DIR/$HOST/toolchain.cmake"
105 fi
106
107 ccache --zero-stats
108
109 # Folder where the build is done.
110 BASE_BUILD_DIR=${BASE_BUILD_DIR:-$BASE_SCRATCH_DIR/build-$HOST}
111
112 BITCOIN_CONFIG_ALL="$BITCOIN_CONFIG_ALL '-DCMAKE_INSTALL_PREFIX=$BASE_OUTDIR' -Werror=dev"
113
114 if [[ "${RUN_IWYU}" == true || "${RUN_TIDY}" == true ]]; then
115 BITCOIN_CONFIG_ALL="$BITCOIN_CONFIG_ALL -DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
116 fi
117
118 eval "CMAKE_ARGS=($BITCOIN_CONFIG_ALL $BITCOIN_CONFIG)"
119 cmake -S "$BASE_ROOT_DIR" -B "$BASE_BUILD_DIR" "${CMAKE_ARGS[@]}" || (
120 cd "${BASE_BUILD_DIR}"
121 # shellcheck disable=SC2046
122 cat $(cmake -P "${BASE_ROOT_DIR}/ci/test/GetCMakeLogFiles.cmake")
123 false
124 )
125
126 if [[ "${GOAL}" != all && "${GOAL}" != codegen ]]; then
127 GOAL="all ${GOAL}"
128 fi
129
130 # shellcheck disable=SC2086
131 cmake --build "${BASE_BUILD_DIR}" "$MAKEJOBS" --target $GOAL || (
132 echo "Build failure. Verbose build follows."
133 # shellcheck disable=SC2086
134 cmake --build "${BASE_BUILD_DIR}" -j1 --target $GOAL --verbose
135 false
136 )
137
138 ccache --version | head -n 1 && ccache --show-stats --verbose
139 ccache --print-stats | python3 -c '
140 import os
141 import sys
142
143 for line in sys.stdin:
144 key, value = line.split("\t", 1)
145 # "primary storage" fallback only needed for ccache version 4.5.1
146 if key in ("local_storage_hit", "primary_storage_hit"):
147 hits = int(value)
148 elif key in ("local_storage_miss", "primary_storage_miss"):
149 miss = int(value)
150
151 calls = hits + miss
152 # codegen has no calls, so skip that here
153 if calls:
154 rate = (hits / calls * 100)
155 print(f"{rate:.2f}")
156 if rate < 75:
157 container = os.environ["CONTAINER_NAME"]
158 print(
159 "::notice title=low ccache hitrate::"
160 f"Ccache hit-rate in {container} was {rate:.2f}%"
161 )
162 '
163 du -sh "${DEPENDS_DIR}"/*/
164 du -sh "${PREVIOUS_RELEASES_DIR}"
165
166 if [ -n "${CI_LIMIT_STACK_SIZE}" ]; then
167 ulimit -s 512
168 fi
169
170 if [[ ${BARE_METAL_RISCV} == "true" ]]; then
171 export BASE_BUILD_DIR
172 "${BASE_ROOT_DIR}/ci/test/link-riscv.sh"
173 fi
174
175 if [ -n "$USE_VALGRIND" ]; then
176 "${BASE_ROOT_DIR}/ci/test/wrap-valgrind.py"
177 fi
178
179 if [ "$RUN_CHECK_DEPS" = "true" ]; then
180 "${BASE_ROOT_DIR}/contrib/devtools/check-deps.sh" "${BASE_BUILD_DIR}"
181 fi
182
183 if [[ "$CI_OS_NAME" == "macos" && "${GOAL}" = "install deploy" ]]; then
184 unzip "${BASE_BUILD_DIR}/bitcoin-macos-app.zip" -d "${BASE_BUILD_DIR}/deploy"
185 if ! ( codesign --verify "${BASE_BUILD_DIR}/deploy/Bitcoin-Qt.app" ); then
186 echo "Codesigning failed."
187 false
188 fi
189 fi
190
191 if [ "$RUN_UNIT_TESTS" = "true" ]; then
192 DIR_UNIT_TEST_DATA="${DIR_UNIT_TEST_DATA}" \
193 LD_LIBRARY_PATH="${DEPENDS_DIR}/${HOST}/lib" \
194 CTEST_OUTPUT_ON_FAILURE=ON \
195 ctest --test-dir "${BASE_BUILD_DIR}" \
196 --stop-on-failure \
197 "${MAKEJOBS}" \
198 --timeout $(( TEST_RUNNER_TIMEOUT_FACTOR * 60 ))
199 fi
200
201 if [ "$RUN_FUNCTIONAL_TESTS" = "true" ]; then
202 # parses TEST_RUNNER_EXTRA as an array which allows for multiple arguments such as TEST_RUNNER_EXTRA='--exclude "rpc_bind.py --ipv6"'
203 eval "TEST_RUNNER_EXTRA=($TEST_RUNNER_EXTRA)"
204 LD_LIBRARY_PATH="${DEPENDS_DIR}/${HOST}/lib" \
205 "${BASE_BUILD_DIR}/test/functional/test_runner.py" \
206 "${MAKEJOBS}" \
207 --tmpdirprefix "${BASE_SCRATCH_DIR}/test_runner/" \
208 --ansi \
209 --combinedlogslen=99999999 \
210 --timeout-factor="${TEST_RUNNER_TIMEOUT_FACTOR}" \
211 "${TEST_RUNNER_EXTRA[@]}" \
212 --quiet \
213 --failfast
214 fi
215
216 if [ "${RUN_TIDY}" = "true" ]; then
217 cmake -B /tidy-build -DLLVM_DIR=/usr/lib/llvm-"${TIDY_LLVM_V}"/cmake -DCMAKE_BUILD_TYPE=Release -S "${BASE_ROOT_DIR}"/contrib/devtools/bitcoin-tidy
218 cmake --build /tidy-build "$MAKEJOBS"
219 cmake --build /tidy-build --target bitcoin-tidy-tests "$MAKEJOBS"
220
221 set -eo pipefail
222 # Filter out:
223 # * qt qrc and moc generated files
224 jq 'map(select(.file | test("src/qt/.*_autogen/.*\\.cpp$") | not))' "${BASE_BUILD_DIR}/compile_commands.json" > tmp.json
225 mv tmp.json "${BASE_BUILD_DIR}/compile_commands.json"
226
227 cd "${BASE_BUILD_DIR}/src/"
228 if ! ( run-clang-tidy-"${TIDY_LLVM_V}" -config-file="${BASE_ROOT_DIR}/src/.clang-tidy" -quiet -load="/tidy-build/libbitcoin-tidy.so" "${MAKEJOBS}" | tee tmp.tidy-out.txt ); then
229 grep -C5 "error: " tmp.tidy-out.txt
230 echo "^^^ ⚠️ Failure generated from clang-tidy"
231 false
232 fi
233 fi
234
235 if [[ "${RUN_IWYU}" == true ]]; then
236 # TODO: Consider enforcing IWYU across the entire codebase.
237 FILES_WITH_ENFORCED_IWYU="/src/(((bench|crypto|index|kernel|primitives|script|univalue/(lib|test)|util|zmq)/.*|common/license_info|node/(blockstorage|interfaces|miner|mining_args|utxo_snapshot)|rpc/mining|clientversion|core_io|signet|init)\\.cpp)"
238 jq --arg patterns "$FILES_WITH_ENFORCED_IWYU" 'map(select(.file | test($patterns)))' "${BASE_BUILD_DIR}/compile_commands.json" > "${BASE_BUILD_DIR}/compile_commands_iwyu_errors.json"
239 jq --arg patterns "$FILES_WITH_ENFORCED_IWYU" 'map(select(.file | test($patterns) | not))' "${BASE_BUILD_DIR}/compile_commands.json" > "${BASE_BUILD_DIR}/compile_commands_iwyu_warnings.json"
240
241 cd "${BASE_ROOT_DIR}"
242
243 run_iwyu() {
244 mv "${BASE_BUILD_DIR}/$1" "${BASE_BUILD_DIR}/compile_commands.json"
245 {
246 python3 "/include-what-you-use/iwyu_tool.py" \
247 -p "${BASE_BUILD_DIR}" "${MAKEJOBS}" \
248 -- -Xiwyu --cxx17ns -Xiwyu --mapping_file="${BASE_ROOT_DIR}/contrib/devtools/iwyu/bitcoin.core.imp" \
249 -Xiwyu --max_line_length=160 \
250 -Xiwyu --check_also="*/primitives/*.h" \
251 2>&1 || true
252 } | tee /tmp/iwyu_ci.out
253 python3 "/include-what-you-use/fix_includes.py" --nosafe_headers < /tmp/iwyu_ci.out
254 git diff -U1 | ./contrib/devtools/clang-format-diff.py -binary="clang-format-${IWYU_LLVM_V}" -p1 -i -v
255 }
256
257 run_iwyu "compile_commands_iwyu_errors.json"
258 if ! ( git --no-pager diff --exit-code ); then
259 echo "^^^ ⚠️ Failure generated from IWYU"
260 false
261 fi
262
263 run_iwyu "compile_commands_iwyu_warnings.json"
264 git --no-pager diff
265 fi
266
267 if [ "$RUN_FUZZ_TESTS" = "true" ]; then
268 # shellcheck disable=SC2086
269 LD_LIBRARY_PATH="${DEPENDS_DIR}/${HOST}/lib" \
270 "${BASE_BUILD_DIR}/test/fuzz/test_runner.py" \
271 ${FUZZ_TESTS_CONFIG} \
272 "${MAKEJOBS}" \
273 -l DEBUG \
274 "${DIR_FUZZ_IN}" \
275 --empty_min_time=60
276 fi
277