From dee61b3ca28a3427151fb03ffd1a2b25afe36921 Mon Sep 17 00:00:00 2001 From: nmbro Date: Tue, 13 Oct 2026 17:28:29 +0200 Subject: [PATCH 1/8] scripts/Windows/dllldd.sh: list search dirs once per call dllldd_with_tools() ran a `find` per DLL name over DESTDIR and over each input directory. Once recursion reaches e.g. /mingw64/bin, this means hundreds of walks over a large directory, which is slow on MSYS2 (about 5 minutes of `make install-win-bundle` in AppVeyor CI). List those locations once per call and match base names with awk. Listing order is kept, so the first hit is the same file as before. Use of coding helper tools and AI disclosed: Claude Code was used. Co-Authored-By: Claude Opus 5.5 Signed-off-by: nmbro --- scripts/Windows/dllldd.sh | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/scripts/Windows/dllldd.sh b/scripts/Windows/dllldd.sh index 2ef3dcfe2f..b25fa7055c 100755 --- a/scripts/Windows/dllldd.sh +++ b/scripts/Windows/dllldd.sh @@ -85,6 +85,11 @@ filter_away_system_DLLs() { ${EGREP} -v -i '^(/.*/)?(msvcrt|userenv|bcrypt|bcryptprimitives|dnsapi|dwrite|iphlpapi|kernelbase|mswsock|shlwapi|winmm|rpcrt4|usp10|ntdll|api-ms-win-[^ ]*|(advapi|crypt|kernel|user|wsock|ws2_|gdi|ole|shell)(32|64))'"${DLLEXT_REGEX_EOL}" } +first_file_named() { + # Print the first line of stdin (a file path) whose base name is "$1" + awk -v f="$1" '{ n = $0; sub(/^.*\//, "", n); if (n == f) { print; exit } }' +} + filter_away_NUT_DLLs() { # Only use this in search via `strings|grep` (and if coupled with # a tools-based search) @@ -132,6 +137,20 @@ dllldd_with_tools() ( SEEN=0 NOTSEEN_OD="" if [ -n "${ARCH-}${MINGW_PREFIX-}${MSYSTEM_PREFIX-}" ] ; then + # List candidate locations once, rather than calling `find` for + # each DLL name: spawning processes and walking big directories + # (e.g. /mingw64/bin) is slow on MSYS2, and this adds up quickly + # in recursive searches. Listing order is kept as it was in the + # per-name searches, so the first hit is the same file as before. + DESTDIR_FILES="" + if [ -n "$DESTDIR" -a -d "${DESTDIR}" ] ; then + DESTDIR_FILES="`find \"$DESTDIR\" -type f \! -size 0 2>/dev/null`" + fi + SEARCH_INPUT_FILES="" + if [ -n "$SEARCH_INPUT_PATH" ] ; then + SEARCH_INPUT_FILES="`for D in ${SEARCH_INPUT_PATH_MULTILINE} ; do find \"$D\" -type f \! -size 0 2>/dev/null ; done`" + fi + for OD in objdump "$ARCH-objdump" ; do (command -v "$OD" >/dev/null 2>/dev/null) || continue @@ -139,17 +158,13 @@ dllldd_with_tools() ( && [ -n "$ODOUT" ] || continue for F in $ODOUT ; do - if [ -n "$DESTDIR" -a -d "${DESTDIR}" ] ; then - OUT="`find \"$DESTDIR\" -type f -name \"$F\" \! -size 0 2>/dev/null | head -1`" \ + if [ -n "$DESTDIR_FILES" ] ; then + OUT="`echo \"$DESTDIR_FILES\" | first_file_named \"$F\"`" \ && [ -n "$OUT" ] && { echo "$OUT" ; SEEN="`expr $SEEN + 1`" ; continue ; } fi - if [ -n "$SEARCH_INPUT_PATH" ] ; then - SEEN_INPUT=false - for D in ${SEARCH_INPUT_PATH_MULTILINE} ; do - OUT="`find \"$D\" -type f -name \"$F\" \! -size 0 2>/dev/null | head -1`" \ - && [ -n "$OUT" ] && { echo "$OUT" ; SEEN_INPUT=true ; break ; } - done - $SEEN_INPUT && { SEEN="`expr $SEEN + 1`" ; continue ; } + if [ -n "$SEARCH_INPUT_FILES" ] ; then + OUT="`echo \"$SEARCH_INPUT_FILES\" | first_file_named \"$F\"`" \ + && [ -n "$OUT" ] && { echo "$OUT" ; SEEN="`expr $SEEN + 1`" ; continue ; } fi if [ -n "$ARCH" -a -d "/usr/${ARCH}" ] ; then OUT="`ls -1 \"/usr/${ARCH}/bin/$F\" \"/usr/${ARCH}/lib/$F\" 2>/dev/null || true`" \ From 941834aa9c638bfcecc9f53c61aec64d328cc2f9 Mon Sep 17 00:00:00 2001 From: nmbro Date: Tue, 13 Oct 2026 17:28:37 +0200 Subject: [PATCH 2/8] appveyor.yml: store cache entries uncompressed, cap ccache size Single-threaded LZMA at -mx=6 took 1-2 minutes per run to re-pack the already-compressed ccache entry. Store entries as-is instead, and cap ccache at 750MB to stay below the 1GB build cache limit for PRs along with the other cached entries. Use of coding helper tools and AI disclosed: Claude Code was used. Co-Authored-By: Claude Opus 5.5 Signed-off-by: nmbro --- appveyor.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 3a77e286be..a8dcc8b4f1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -28,7 +28,13 @@ platform: x64 # https://www.appveyor.com/docs/build-cache/ environment: APPVEYOR_SAVE_CACHE_ON_ERROR: true - APPVEYOR_CACHE_ENTRY_ZIP_ARGS: -t7z -m0=lzma -mx=6 + # Store cache entries without (re-)compression: the data is mostly already + # compressed (ccache objects, pacman *.zst packages), and single-threaded + # LZMA at -mx=6 took 1-2 minutes per run for the ccache entry alone. + # The cache limit for PR builds is 1GB total across all entries, so + # ccache max_size is capped below (see install steps) to leave a margin + # for the other entries (pacman packages ~100MB, nut-win-deps, nut-ci): + APPVEYOR_CACHE_ENTRY_ZIP_ARGS: -t7z -mx=0 CCACHE_DIR: /home/appveyor/.ccache DO_CLEAN_NUTCI_CACHE: no DO_USE_NUTCI_CACHE: yes @@ -103,7 +109,7 @@ install: set CHERE_INVOKING=yes REM Start a 64 bit Mingw environment: set MSYSTEM=MINGW64 - C:\msys64\usr\bin\bash -lc 'PATH="/mingw64/bin:$PATH" ; export PATH ; pwd ; ccache -sv || echo "SKIP: Could not query ccache stats" ; ccache -o sloppiness=file_macro || true ; ccache -o compression=true || true ' + C:\msys64\usr\bin\bash -lc 'PATH="/mingw64/bin:$PATH" ; export PATH ; pwd ; ccache -sv || echo "SKIP: Could not query ccache stats" ; ccache -o sloppiness=file_macro || true ; ccache -o compression=true || true ; ccache -o max_size=750M || true ' # NOTE: The script aims to configure for minimal build results, e.g. From 065b834637e89e7341503ebcea3e677a374953de Mon Sep 17 00:00:00 2001 From: nmbro Date: Tue, 13 Oct 2026 17:47:09 +0200 Subject: [PATCH 3/8] ci_build.sh, appveyor.yml: stable AC_INIT version for ccache AC_INIT takes the version from gitlog2version.sh, which includes the number of commits in a PR (e.g. 2.8.5.1466.13), and puts it into config.h as PACKAGE_VERSION. With -g3 every object file embeds all macros it saw, so each new commit count meant a full rebuild on a 2-CPU AppVeyor worker (~50 min), and ccache hits only for PRs with a commit count seen before. Add opt-in CI_AUTOGEN_STABLE_VERSION=true to drop that component via NUT_VERSION_FORCED for autogen.sh only, and enable it for AppVeyor. `make` still generates nut_version.h from Git, so programs report the actual version. Use of coding helper tools and AI disclosed: Claude Code was used. Co-Authored-By: Claude Opus 5.5 Signed-off-by: nmbro --- appveyor.yml | 3 +++ ci_build.sh | 31 ++++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index a8dcc8b4f1..0a051883d3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -35,6 +35,9 @@ environment: # ccache max_size is capped below (see install steps) to leave a margin # for the other entries (pacman packages ~100MB, nut-win-deps, nut-ci): APPVEYOR_CACHE_ENTRY_ZIP_ARGS: -t7z -mx=0 + # Keep config.h stable across PRs, so ccache can reuse -g3 objects + # (see ci_build.sh for details): + CI_AUTOGEN_STABLE_VERSION: true CCACHE_DIR: /home/appveyor/.ccache DO_CLEAN_NUTCI_CACHE: no DO_USE_NUTCI_CACHE: yes diff --git a/ci_build.sh b/ci_build.sh index c781c42540..7905ac1adc 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -1135,11 +1135,32 @@ do_autogen_get_CONFIGURE_SCRIPT() { # older system) we have to remove it when we already have the script. # This matches the use-case of distro-building from release tarballs that # include all needed pre-generated files to rely less on OS facilities. - if [ "$CI_OS_NAME" = "windows" ] ; then - $CI_TIME ./autogen.sh || true - else - $CI_TIME ./autogen.sh ### 2>/dev/null - fi || exit + ( + # Optionally drop the PR-specific last component of the version + # (e.g. 2.8.5.1466.13 => 2.8.5.1466) which AC_INIT puts into + # config.h as PACKAGE_VERSION. With -g3 every object file embeds + # all macros it saw, so a version change means a full rebuild + # and defeats ccache across pull requests. Only autogen.sh gets + # this setting, so `make` still generates nut_version.h (and the + # UPS_VERSION reported by programs) from actual Git metadata. + if [ x"${CI_AUTOGEN_STABLE_VERSION-}" = xtrue ] && [ -z "${NUT_VERSION_FORCED-}" ] ; then + _NUT_VERSION_STABLE="`NUT_VERSION_QUERY=VER50 ./tools/gitlog2version.sh 2>/dev/null`" || _NUT_VERSION_STABLE="" + case "${_NUT_VERSION_STABLE}" in + *.*.*.*.*) _NUT_VERSION_STABLE="${_NUT_VERSION_STABLE%.*}" ;; + esac + if [ -n "${_NUT_VERSION_STABLE}" ] ; then + echo "INFO: CI_AUTOGEN_STABLE_VERSION: using NUT_VERSION_FORCED='${_NUT_VERSION_STABLE}' for autogen.sh" >&2 + NUT_VERSION_FORCED="${_NUT_VERSION_STABLE}" + export NUT_VERSION_FORCED + fi + fi + + if [ "$CI_OS_NAME" = "windows" ] ; then + $CI_TIME ./autogen.sh || true + else + $CI_TIME ./autogen.sh ### 2>/dev/null + fi + ) || exit fi # Retain the full path to configure script file From e41741a52c97ed59c737325419b1393d3f04b8f5 Mon Sep 17 00:00:00 2001 From: nmbro Date: Tue, 13 Oct 2026 17:53:04 +0200 Subject: [PATCH 4/8] appveyor.yml: fail before the job timeout, to save cache AppVeyor kills the job at 60 minutes without saving the build cache, so a cold-ccache build never warms up (and an interrupted cache save in build 5650 lost the ccache entry entirely). Set a deadline at 51 minutes after job start in `init`, and run the long bash steps (prereqs, build, check, spellcheck, bundle) under `timeout` with the time left. If they would run past it, the job fails instead, and APPVEYOR_SAVE_CACHE_ON_ERROR keeps what was built, with at least ~5 minutes left for 7z, artifacts and the cache save. Use of coding helper tools and AI disclosed: Claude Code was used. Co-Authored-By: Claude Opus 5.5 Signed-off-by: nmbro --- appveyor.yml | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 0a051883d3..05a752da69 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -66,6 +66,19 @@ notifications: # or look for the chapter in nearby lines in later (current) revisions. # Note: not using `time` in scripts currently - they did upset # AppVeyor console log scanner with a /^sys.*/ match (apparently) +# AppVeyor kills the job at 60 minutes, without saving the build cache: +# a cold-ccache build then never gets warm, and an interrupted cache save +# can even lose the old entry. So we set a deadline at 51 minutes after +# the job start, and our build/test/bundle steps fail (via `timeout`) if +# they would run past it. With APPVEYOR_SAVE_CACHE_ON_ERROR this leaves +# a few minutes for the 7z step, artifact upload and saving the cache. +# Each bash step below computes its time left like this (no CMD special +# characters, so it passes through CMD intact): +# R=$(( ${CI_DEADLINE_EPOCH:-EPOCHSECONDS+3600} - EPOCHSECONDS )) +init: + - ps: | + Set-AppveyorBuildVariable -Name CI_DEADLINE_EPOCH -Value ([DateTimeOffset]::UtcNow.ToUnixTimeSeconds() + 51 * 60) + install: - cmd: | REM Do not give pacman cause for complaints: @@ -129,7 +142,7 @@ before_build: set CHERE_INVOKING=yes REM Start a 64 bit Mingw environment: set MSYSTEM=MINGW64 - C:\msys64\usr\bin\bash -lc 'date -u; export MSYS2_PATH ; PATH="/mingw64/lib/ccache/bin:/mingw64/bin:$PATH" bash ./scripts/Windows/build-mingw-prereqs.sh' + C:\msys64\usr\bin\bash -lc 'date -u; R=$(( ${CI_DEADLINE_EPOCH:-EPOCHSECONDS+3600} - EPOCHSECONDS )) ; echo "CI deadline: $R seconds left" ; if [ "$R" -le 0 ] ; then exit 124 ; fi ; export MSYS2_PATH ; PATH="/mingw64/lib/ccache/bin:/mingw64/bin:$PATH" timeout -k 30 "$R" bash ./scripts/Windows/build-mingw-prereqs.sh' build_script: @@ -149,7 +162,7 @@ build_script: REM to find "nearby" program or configuration files (see common.c REM for current implementation). Hard-coded fallback strings may REM end up getting used in those cases. - C:\msys64\usr\bin\bash -lc 'date -u; PATH="/mingw64/bin:$PATH" CI_SKIP_CHECK=true CANBUILD_WITH_LIBMODBUS_USB=yes WITH_LIBNUTPRIVATE=true NUT_SSL_VARIANTS=nss ./ci_build.sh --with-docs=no' + C:\msys64\usr\bin\bash -lc 'date -u; R=$(( ${CI_DEADLINE_EPOCH:-EPOCHSECONDS+3600} - EPOCHSECONDS )) ; echo "CI deadline: $R seconds left" ; if [ "$R" -le 0 ] ; then exit 124 ; fi ; PATH="/mingw64/bin:$PATH" CI_SKIP_CHECK=true CANBUILD_WITH_LIBMODBUS_USB=yes WITH_LIBNUTPRIVATE=true NUT_SSL_VARIANTS=nss timeout -k 30 "$R" ./ci_build.sh --with-docs=no' after_build: @@ -168,9 +181,9 @@ test_script: REM Start a 64 bit Mingw environment: set MSYSTEM=MINGW64 REM Start Mingw-based integration and unit checks: - C:\msys64\usr\bin\bash -lc 'date -u; NUT_STATEPATH="C:\\Users\\appveyor\\AppData\\Local\\Temp\\nut-test"; mkdir -p "${NUT_STATEPATH}"; export NUT_STATEPATH; PATH="/mingw64/lib/ccache/bin:/mingw64/bin:$PATH" make -s check || bash -lc "for F in tests/*.log tests/*.trs ; do echo \"===---=== $F :\"; cat \"$F\"; done; exit 1;" ' + C:\msys64\usr\bin\bash -lc 'date -u; R=$(( ${CI_DEADLINE_EPOCH:-EPOCHSECONDS+3600} - EPOCHSECONDS )) ; echo "CI deadline: $R seconds left" ; if [ "$R" -le 0 ] ; then exit 124 ; fi ; NUT_STATEPATH="C:\\Users\\appveyor\\AppData\\Local\\Temp\\nut-test"; mkdir -p "${NUT_STATEPATH}"; export NUT_STATEPATH; PATH="/mingw64/lib/ccache/bin:/mingw64/bin:$PATH" timeout -k 30 "$R" make -s check || bash -lc "for F in tests/*.log tests/*.trs ; do echo \"===---=== $F :\"; cat \"$F\"; done; exit 1;" ' REM Start a Mingw-based documentation spellcheck (note that "make check" above could cover some documents in path of artifact delivery, but maybe not all that we have): - C:\msys64\usr\bin\bash -lc 'date -u; PATH="/mingw64/lib/ccache/bin:/mingw64/bin:$PATH" make -s -j 8 spellcheck' + C:\msys64\usr\bin\bash -lc 'date -u; R=$(( ${CI_DEADLINE_EPOCH:-EPOCHSECONDS+3600} - EPOCHSECONDS )) ; echo "CI deadline: $R seconds left" ; if [ "$R" -le 0 ] ; then exit 124 ; fi ; PATH="/mingw64/lib/ccache/bin:/mingw64/bin:$PATH" timeout -k 30 "$R" make -s -j 8 spellcheck' after_test: @@ -181,7 +194,7 @@ after_test: set MSYSTEM=MINGW64 REM Oh the joys of shell scripting with strings passed through CMD: REM Note: currently Python installation path with MSYS is buggy [#1584] - C:\msys64\usr\bin\bash -lc 'date -u; set -e ; if ! rm -rf ".inst" ; then echo "WARNING: Failed to clean away .inst" ; fi ; PATH="/mingw64/lib/ccache/bin:/mingw64/bin:$PATH" make -s -j 8 install-win-bundle DESTDIR="`pwd`/.inst/NUT-for-Windows-x86_64-SNAPSHOT-%APPVEYOR_BUILD_VERSION%"' + C:\msys64\usr\bin\bash -lc 'date -u; R=$(( ${CI_DEADLINE_EPOCH:-EPOCHSECONDS+3600} - EPOCHSECONDS )) ; echo "CI deadline: $R seconds left" ; if [ "$R" -le 0 ] ; then exit 124 ; fi ; set -e ; if ! rm -rf ".inst" ; then echo "WARNING: Failed to clean away .inst" ; fi ; PATH="/mingw64/lib/ccache/bin:/mingw64/bin:$PATH" timeout -k 30 "$R" make -s -j 8 install-win-bundle DESTDIR="`pwd`/.inst/NUT-for-Windows-x86_64-SNAPSHOT-%APPVEYOR_BUILD_VERSION%"' C:\msys64\usr\bin\bash -lc 'date -u; set -e ; rm -rf ./.inst/NUT-for-Windows-x86_64-SNAPSHOT || true ; ln -fs "NUT-for-Windows-x86_64-SNAPSHOT-%APPVEYOR_BUILD_VERSION%" ./.inst/NUT-for-Windows-x86_64-SNAPSHOT ; ( cd .inst/NUT-for-Windows-x86_64-SNAPSHOT ; find . -ls ; )' C:\msys64\usr\bin\bash -lc 'date -u' cd .inst From 5cab48ce8fd6b103da1c06b8c4b96ac6fe1ed90c Mon Sep 17 00:00:00 2001 From: nmbro Date: Tue, 13 Oct 2026 18:09:20 +0200 Subject: [PATCH 5/8] ci_build.sh: correct CI_AUTOGEN_STABLE_VERSION comment The cache misses come from the ccache direct mode hashing config.h contents, not from -g3 (ccache does not treat -g3 specially). Use of coding helper tools and AI disclosed: Claude Code was used. Co-Authored-By: Claude Opus 5.5 Signed-off-by: nmbro --- ci_build.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ci_build.sh b/ci_build.sh index 7905ac1adc..72efc0e490 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -1138,9 +1138,9 @@ do_autogen_get_CONFIGURE_SCRIPT() { ( # Optionally drop the PR-specific last component of the version # (e.g. 2.8.5.1466.13 => 2.8.5.1466) which AC_INIT puts into - # config.h as PACKAGE_VERSION. With -g3 every object file embeds - # all macros it saw, so a version change means a full rebuild - # and defeats ccache across pull requests. Only autogen.sh gets + # config.h as PACKAGE_VERSION. The ccache direct mode hashes all + # included headers, so a version change misses for nearly every + # file and defeats ccache across pull requests. Only autogen.sh gets # this setting, so `make` still generates nut_version.h (and the # UPS_VERSION reported by programs) from actual Git metadata. if [ x"${CI_AUTOGEN_STABLE_VERSION-}" = xtrue ] && [ -z "${NUT_VERSION_FORCED-}" ] ; then From c170100a9d639ff269878984056a0e015a769263 Mon Sep 17 00:00:00 2001 From: nmbro Date: Tue, 13 Oct 2026 20:29:46 +0200 Subject: [PATCH 6/8] tests/NIT/nit.sh: fix test certificate cache never repopulating When cached certificates failed check_NIT_certs, the cache dir was removed and immediately re-created empty. The cache is populated only if that dir is absent, so it stayed empty forever, and every run (seen on AppVeyor) generated new certificates after "GRAVE PROBLEM" noise. Do not re-create the cache dir there. Also fix a `|| \` typo (vs. `|| \` line continuation), which ran a command named `\` and then the `cp -L` fallback unconditionally. Use of coding helper tools and AI disclosed: Claude Code was used. Co-Authored-By: Claude Opus 5.5 Signed-off-by: nmbro --- tests/NIT/nit.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 738586886a..c1095f6c3d 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -1235,7 +1235,7 @@ if [ x"${DO_USE_NIT_TESTCERT_CACHE-}" = xyes ] ; then log_info "Found cached NIT certificates in ${CI_CACHE_NIT_HASHDIR}" mkdir -p "${TESTCERT_PATH_BASE}" # See comments above about no TESTCERT_PATH_SEP for shell globs. - cp -prf "${CI_CACHE_NIT_HASHDIR}"/* "${TESTCERT_PATH_BASE}${TESTCERT_PATH_SEP}" || \\ + cp -prf "${CI_CACHE_NIT_HASHDIR}"/* "${TESTCERT_PATH_BASE}${TESTCERT_PATH_SEP}" || \ cp -prfL "${CI_CACHE_NIT_HASHDIR}"/* "${TESTCERT_PATH_BASE}${TESTCERT_PATH_SEP}" # If there is a setup script there, source it to get variables @@ -1252,7 +1252,9 @@ if [ x"${DO_USE_NIT_TESTCERT_CACHE-}" = xyes ] ; then log_warn "FAILED check_NIT_certs with cached data, will generate anew. Removing:" find "${TESTCERT_PATH_BASE}" "${CI_CACHE_NIT_HASHDIR}" -ls || true rm -rf "${TESTCERT_PATH_BASE}" "${CI_CACHE_NIT_HASHDIR}" || true - mkdir -p "${TESTCERT_PATH_BASE}" "${CI_CACHE_NIT_HASHDIR}" + # Do not re-create the (now empty) cache dir: it is populated + # below only if absent, after we generate the certificates + mkdir -p "${TESTCERT_PATH_BASE}" fi else log_info "Did not find a CI_CACHE_NIT_HASHDIR, will populate a new one after generating certificates as '${CI_CACHE_NIT_HASHDIR}'" @@ -1362,7 +1364,8 @@ case "${WITH_SSL_CLIENT}${WITH_SSL_SERVER}" in log_warn "FAILED check_NIT_certs with cached data, will generate anew" rm -rf "${TESTCERT_PATH_BASE}" "${CI_CACHE_NIT_HASHDIR}" || true - mkdir -p "${TESTCERT_PATH_BASE}" "${CI_CACHE_NIT_HASHDIR}" + # Do not re-create the cache dir, see above + mkdir -p "${TESTCERT_PATH_BASE}" fi # if [ -n "${BASH_VERSION-}" ]; then From ab6fef1f973a78b2e18cd32aad2ab20547fdd3e2 Mon Sep 17 00:00:00 2001 From: nmbro Date: Tue, 13 Oct 2026 20:58:55 +0200 Subject: [PATCH 7/8] scripts/Windows/dllldd.sh: scan only data sections `strings` scanned whole files, including debug sections: for the NUT for Windows bundle (616MB of -g3 binaries) that produced 561MB of text, all piped through tr/grep/sort, which dominated the DLL scan of `make install-win-bundle` (~2 min in the first pass on AppVeyor). With `strings -d` (data sections only, as older binutils did by default) it is 2.8MB, and the same candidate DLL names. Fall back to a whole-file scan if `strings` lacks the option. Similarly use `objdump -p` (private headers, with the PE import table and ELF NEEDED entries) instead of `-x`, which also dumped symbol tables and relocations (37MB vs 9.8MB of text here). Checked against the AppVeyor build 5659 bundle: identical results and warnings to the original script. Use of coding helper tools and AI disclosed: Claude Code was used. Co-Authored-By: Claude Opus 5.5 Signed-off-by: nmbro --- scripts/Windows/dllldd.sh | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/scripts/Windows/dllldd.sh b/scripts/Windows/dllldd.sh index b25fa7055c..469e4e3254 100755 --- a/scripts/Windows/dllldd.sh +++ b/scripts/Windows/dllldd.sh @@ -81,6 +81,16 @@ discover_COMPILER_PATHS() { } discover_COMPILER_PATHS +# Only scan initialized data sections for DLL names (as older binutils did +# by default), not the whole file: with debug info (-g3) most of our binary +# size is debug sections, so a full scan produced ~200 times more text (all +# piped through filters below), with the same DLL names found. +# Fall back to a whole-file scan if this `strings` lacks the option. +STRINGS_DATA_ONLY="" +if (strings --help 2>&1 | ${GREP} -- '--data' >/dev/null) ; then + STRINGS_DATA_ONLY="-d" +fi + filter_away_system_DLLs() { ${EGREP} -v -i '^(/.*/)?(msvcrt|userenv|bcrypt|bcryptprimitives|dnsapi|dwrite|iphlpapi|kernelbase|mswsock|shlwapi|winmm|rpcrt4|usp10|ntdll|api-ms-win-[^ ]*|(advapi|crypt|kernel|user|wsock|ws2_|gdi|ole|shell)(32|64))'"${DLLEXT_REGEX_EOL}" } @@ -154,7 +164,10 @@ dllldd_with_tools() ( for OD in objdump "$ARCH-objdump" ; do (command -v "$OD" >/dev/null 2>/dev/null) || continue - ODOUT="`$OD -x \"$@\" 2>/dev/null | ${EGREP} -i '(DLL Name:|^'"${REGEX_WS}"'*NEEDED)' | awk '{print $NF}' | sort | uniq | filter_away_system_DLLs`" \ + # Private headers (-p) hold the PE import table ("DLL Name:") + # and ELF dynamic section ("NEEDED"); -x would also dump the + # (large, for debug builds) symbol tables and relocations. + ODOUT="`$OD -p \"$@\" 2>/dev/null | ${EGREP} -i '(DLL Name:|^'"${REGEX_WS}"'*NEEDED)' | awk '{print $NF}' | sort | uniq | filter_away_system_DLLs`" \ && [ -n "$ODOUT" ] || continue for F in $ODOUT ; do @@ -216,7 +229,7 @@ dllldd_with_tools() ( ) dllldd_with_strings() ( - strings "$@" | tr ' ' '\n' | tr '&' '\n' \ + strings ${STRINGS_DATA_ONLY} "$@" | tr ' ' '\n' | tr '&' '\n' \ | ${EGREP} -i '..*'"${DLLEXT_REGEX_EOL}" | sort | uniq \ | filter_away_system_DLLs \ | ${EGREP} -vi '^(lib)*%s'"${DLLEXT_REGEX_EOL}" \ From 39ffa4a76b89017d40d06c4f01e15f564e0b5f9f Mon Sep 17 00:00:00 2001 From: nmbro Date: Tue, 13 Oct 2026 23:03:22 +0200 Subject: [PATCH 8/8] scripts/Windows/dllldd.sh: filter more system DLLs, `\%s.dll` Add combase, hid, setupapi, shfolder, wship6 and xmllite to the list of OS-provided DLLs, which were reported as not found (strings matcher) for every NUT for Windows bundle on AppVeyor. The `%s.dll` format-string filter missed a `\%s.dll` hit (backslash path separator in a format string); `read` later stripped the backslash, so it was reported as not found as `%s.dll`. Allow a leading path. Checked against the AppVeyor build 5659 bundle: identical DLL list, these 7 warnings gone, no new ones. Use of coding helper tools and AI disclosed: Claude Code was used. Co-Authored-By: Claude Opus 5.5 Signed-off-by: nmbro --- scripts/Windows/dllldd.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/Windows/dllldd.sh b/scripts/Windows/dllldd.sh index 469e4e3254..88b056bef8 100755 --- a/scripts/Windows/dllldd.sh +++ b/scripts/Windows/dllldd.sh @@ -92,7 +92,7 @@ if (strings --help 2>&1 | ${GREP} -- '--data' >/dev/null) ; then fi filter_away_system_DLLs() { - ${EGREP} -v -i '^(/.*/)?(msvcrt|userenv|bcrypt|bcryptprimitives|dnsapi|dwrite|iphlpapi|kernelbase|mswsock|shlwapi|winmm|rpcrt4|usp10|ntdll|api-ms-win-[^ ]*|(advapi|crypt|kernel|user|wsock|ws2_|gdi|ole|shell)(32|64))'"${DLLEXT_REGEX_EOL}" + ${EGREP} -v -i '^(/.*/)?(msvcrt|userenv|bcrypt|bcryptprimitives|combase|dnsapi|dwrite|hid|iphlpapi|kernelbase|mswsock|setupapi|shfolder|shlwapi|winmm|wship6|xmllite|rpcrt4|usp10|ntdll|api-ms-win-[^ ]*|(advapi|crypt|kernel|user|wsock|ws2_|gdi|ole|shell)(32|64))'"${DLLEXT_REGEX_EOL}" } first_file_named() { @@ -232,7 +232,7 @@ dllldd_with_strings() ( strings ${STRINGS_DATA_ONLY} "$@" | tr ' ' '\n' | tr '&' '\n' \ | ${EGREP} -i '..*'"${DLLEXT_REGEX_EOL}" | sort | uniq \ | filter_away_system_DLLs \ - | ${EGREP} -vi '^(lib)*%s'"${DLLEXT_REGEX_EOL}" \ + | ${EGREP} -vi '^(.*[/\\])?(lib)*%s'"${DLLEXT_REGEX_EOL}" \ | while read DLL ; do ( # Skip out if we already reported this file # (The for/case loop below is surprisingly expensive on MSYS2)