mirror of
https://github.com/citron-neo/emulator.git
synced 2026-07-05 15:21:57 -07:00
fix(windows): harden FFmpeg rebuild pipeline and fix Qt DLL deployment
- Add download_with_retry() helper with exponential back-off (5s→10s→20s) replacing bare wget calls throughout the script - Convert three silent return-0 failure paths in rebuild_ffmpeg_pthread_free (download failure, extraction failure, zero libs installed) to hard errors; previously these allowed cmake to be invoked with a -DCITRON_FFMPEG_STATIC_DIR pointing to a directory that was never created, producing a fatal but misleading CMake error about a missing RELEASE file - Gate -DCITRON_FFMPEG_STATIC_DIR in build_common_cmake_args on the .llvm_static_built sentinel rather than FFMPEG_VERSION being set - Add .ffmpeg_src_ready sentinel written after clean extraction; Priority 1 source cache reuse now requires this sentinel so partial extractions are detected and wiped rather than silently reused - Verify tarball integrity with tar -tjf before extraction - Tighten MSYS2 path filter in CopyMinGWDeps.cmake from ([/\])msys64([/\]) to require a known system subdirectory (usr|clang64|ucrt64|mingw64|mingw32) after msys64; previously the broad match silently excluded all Qt DLLs when CPM_SOURCE_CACHE resided under the default MSYS2 home directory - Replace sequential git ls-remote loop (up to 11 round-trips) for BOLT tag discovery with a single call filtered by sort -V - Replace grep -oP lookbehind patterns for stage sentinel reads with awk -F= for portability across MSYS2 grep variants
This commit is contained in:
+155
-152
@@ -1,40 +1,40 @@
|
||||
# CopyMinGWDeps.cmake
|
||||
# Recursively resolves and copies all MinGW DLL dependencies for a target executable.
|
||||
# Also deploys Qt6 plugins including TLS backends required for SSL/HTTPS.
|
||||
# Usage: copy_mingw_deps(target_name)
|
||||
|
||||
function(copy_mingw_deps target)
|
||||
set(options "")
|
||||
set(oneValueArgs "")
|
||||
set(multiValueArgs SEARCH_PATHS)
|
||||
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
# CopyMinGWDeps.cmake
|
||||
# Recursively resolves and copies all MinGW DLL dependencies for a target executable.
|
||||
# Also deploys Qt6 plugins including TLS backends required for SSL/HTTPS.
|
||||
# Usage: copy_mingw_deps(target_name)
|
||||
|
||||
function(copy_mingw_deps target)
|
||||
set(options "")
|
||||
set(oneValueArgs "")
|
||||
set(multiValueArgs SEARCH_PATHS)
|
||||
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
# Prefer llvm-readobj from the compiler toolchain.
|
||||
get_filename_component(COMPILER_BIN_DIR "${CMAKE_CXX_COMPILER}" DIRECTORY)
|
||||
find_program(READOBJ_EXECUTABLE NAMES llvm-readobj llvm-readobj-19 llvm-readobj-18 llvm-readobj-17
|
||||
HINTS "${COMPILER_BIN_DIR}")
|
||||
|
||||
if (READOBJ_EXECUTABLE)
|
||||
set(DUMP_TOOL "${READOBJ_EXECUTABLE}")
|
||||
set(DUMP_MODE "READOBJ")
|
||||
else()
|
||||
find_program(OBJDUMP_EXECUTABLE NAMES objdump x86_64-w64-mingw32-objdump
|
||||
HINTS "${COMPILER_BIN_DIR}")
|
||||
if (OBJDUMP_EXECUTABLE)
|
||||
set(DUMP_TOOL "${OBJDUMP_EXECUTABLE}")
|
||||
set(DUMP_MODE "OBJDUMP")
|
||||
else()
|
||||
message(WARNING \"Neither llvm-readobj nor objdump found. MinGW DLL deployment may fail.\")
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
get_filename_component(COMPILER_BIN_DIR "${CMAKE_CXX_COMPILER}" DIRECTORY)
|
||||
find_program(READOBJ_EXECUTABLE NAMES llvm-readobj llvm-readobj-19 llvm-readobj-18 llvm-readobj-17
|
||||
HINTS "${COMPILER_BIN_DIR}")
|
||||
|
||||
if (READOBJ_EXECUTABLE)
|
||||
set(DUMP_TOOL "${READOBJ_EXECUTABLE}")
|
||||
set(DUMP_MODE "READOBJ")
|
||||
else()
|
||||
find_program(OBJDUMP_EXECUTABLE NAMES objdump x86_64-w64-mingw32-objdump
|
||||
HINTS "${COMPILER_BIN_DIR}")
|
||||
if (OBJDUMP_EXECUTABLE)
|
||||
set(DUMP_TOOL "${OBJDUMP_EXECUTABLE}")
|
||||
set(DUMP_MODE "OBJDUMP")
|
||||
else()
|
||||
message(WARNING \"Neither llvm-readobj nor objdump found. MinGW DLL deployment may fail.\")
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Prefer bundled DLL locations before the toolchain bin dir.
|
||||
set(MINGW_SEARCH_PATHS "")
|
||||
if (ARG_SEARCH_PATHS)
|
||||
list(APPEND MINGW_SEARCH_PATHS ${ARG_SEARCH_PATHS})
|
||||
endif()
|
||||
|
||||
set(MINGW_SEARCH_PATHS "")
|
||||
if (ARG_SEARCH_PATHS)
|
||||
list(APPEND MINGW_SEARCH_PATHS ${ARG_SEARCH_PATHS})
|
||||
endif()
|
||||
|
||||
# Add Qt runtime bin if available.
|
||||
if (QT_TARGET_PATH AND EXISTS "${QT_TARGET_PATH}/bin")
|
||||
list(APPEND MINGW_SEARCH_PATHS "${QT_TARGET_PATH}/bin")
|
||||
@@ -63,14 +63,14 @@ function(copy_mingw_deps target)
|
||||
if (CMAKE_CROSSCOMPILING AND CMAKE_FIND_ROOT_PATH)
|
||||
list(APPEND MINGW_SEARCH_PATHS "${CMAKE_FIND_ROOT_PATH}/bin")
|
||||
endif()
|
||||
|
||||
list(REMOVE_DUPLICATES MINGW_SEARCH_PATHS)
|
||||
|
||||
set(DEPLOY_SCRIPT "${CMAKE_BINARY_DIR}/deploy_mingw_deps_${target}.cmake")
|
||||
file(WRITE "${DEPLOY_SCRIPT}" "
|
||||
# Auto-generated MinGW DLL deployment script
|
||||
set(DUMP_TOOL \"${DUMP_TOOL}\")
|
||||
set(DUMP_MODE \"${DUMP_MODE}\")
|
||||
|
||||
list(REMOVE_DUPLICATES MINGW_SEARCH_PATHS)
|
||||
|
||||
set(DEPLOY_SCRIPT "${CMAKE_BINARY_DIR}/deploy_mingw_deps_${target}.cmake")
|
||||
file(WRITE "${DEPLOY_SCRIPT}" "
|
||||
# Auto-generated MinGW DLL deployment script
|
||||
set(DUMP_TOOL \"${DUMP_TOOL}\")
|
||||
set(DUMP_MODE \"${DUMP_MODE}\")
|
||||
# Search local bin first.
|
||||
set(SEARCH_PATHS \"\${EXE_DIR};${MINGW_SEARCH_PATHS}\")
|
||||
set(EXE_DIR \"\${TARGET_DIR}\")
|
||||
@@ -78,10 +78,10 @@ set(TARGET_FILE \"\${TARGET_FILE}\")
|
||||
set(COMPILER_BIN_DIR \"${COMPILER_BIN_DIR}\")
|
||||
set(TOOLCHAIN_RUNTIME_PATHS \"${TOOLCHAIN_RUNTIME_PATHS}\")
|
||||
set(QT_TARGET_PLUGIN_DIR \"${QT_TARGET_PATH}/plugins\")
|
||||
|
||||
|
||||
# 1. Deploy whitelisted Qt6 plugins.
|
||||
set(QT_PLATFORMS_PLUGINS qdirect2d qminimal qoffscreen qwindows)
|
||||
set(QT_STYLES_PLUGINS qmodernwindowsstyle qwindowsvistastyle)
|
||||
set(QT_PLATFORMS_PLUGINS qdirect2d qminimal qoffscreen qwindows)
|
||||
set(QT_STYLES_PLUGINS qmodernwindowsstyle qwindowsvistastyle)
|
||||
set(QT_IMAGEFORMATS_PLUGINS qgif qicns qico qjpeg qsvg qtga qtiff qwbmp qwebp)
|
||||
set(QT_ICONENGINES_PLUGINS qsvgicon)
|
||||
set(QT_TLS_PLUGINS qcertonlybackend qopensslbackend qschannelbackend)
|
||||
@@ -98,7 +98,7 @@ set(ALLOWED_COMPILER_BIN_DLLS
|
||||
libva.dll
|
||||
libva_win32.dll
|
||||
zlib1.dll)
|
||||
|
||||
|
||||
set(QT_PLUGIN_BASE \"\${QT_TARGET_PLUGIN_DIR}\")
|
||||
if (NOT EXISTS \"\${QT_PLUGIN_BASE}\")
|
||||
set(QT_PLUGIN_BASE \"${Qt6_DIR}/../../../plugins\")
|
||||
@@ -106,72 +106,75 @@ endif()
|
||||
if (NOT EXISTS \"\${QT_PLUGIN_BASE}\")
|
||||
set(QT_PLUGIN_BASE \"\${COMPILER_BIN_DIR}/../share/qt6/plugins\")
|
||||
endif()
|
||||
|
||||
if (EXISTS \"\${QT_PLUGIN_BASE}\")
|
||||
set(PLUGIN_SUBDIRS platforms styles imageformats iconengines tls)
|
||||
foreach(subdir \${PLUGIN_SUBDIRS})
|
||||
if (EXISTS \"\${QT_PLUGIN_BASE}/\${subdir}\")
|
||||
file(MAKE_DIRECTORY \"\${EXE_DIR}/\${subdir}\")
|
||||
|
||||
|
||||
if (EXISTS \"\${QT_PLUGIN_BASE}\")
|
||||
set(PLUGIN_SUBDIRS platforms styles imageformats iconengines tls)
|
||||
foreach(subdir \${PLUGIN_SUBDIRS})
|
||||
if (EXISTS \"\${QT_PLUGIN_BASE}/\${subdir}\")
|
||||
file(MAKE_DIRECTORY \"\${EXE_DIR}/\${subdir}\")
|
||||
|
||||
# Use the matching whitelist for this plugin dir.
|
||||
string(TOUPPER \"\${subdir}\" subdir_upper)
|
||||
set(whitelist \${QT_\${subdir_upper}_PLUGINS})
|
||||
|
||||
set(pcount 0)
|
||||
foreach(plugin_name \${whitelist})
|
||||
set(plugin_path \"\${QT_PLUGIN_BASE}/\${subdir}/\${plugin_name}.dll\")
|
||||
if (EXISTS \"\${plugin_path}\")
|
||||
file(COPY \"\${plugin_path}\" DESTINATION \"\${EXE_DIR}/\${subdir}\")
|
||||
math(EXPR pcount \"\${pcount} + 1\")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if (pcount GREATER 0)
|
||||
message(STATUS \" Deployed \${pcount} whitelist plugin(s) to \${subdir}/\")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
string(TOUPPER \"\${subdir}\" subdir_upper)
|
||||
set(whitelist \${QT_\${subdir_upper}_PLUGINS})
|
||||
|
||||
set(pcount 0)
|
||||
foreach(plugin_name \${whitelist})
|
||||
set(plugin_path \"\${QT_PLUGIN_BASE}/\${subdir}/\${plugin_name}.dll\")
|
||||
if (EXISTS \"\${plugin_path}\")
|
||||
file(COPY \"\${plugin_path}\" DESTINATION \"\${EXE_DIR}/\${subdir}\")
|
||||
math(EXPR pcount \"\${pcount} + 1\")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if (pcount GREATER 0)
|
||||
message(STATUS \" Deployed \${pcount} whitelist plugin(s) to \${subdir}/\")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
file(WRITE \"\${EXE_DIR}/qt.conf\" \"[Paths]\nPlugins = .\n\")
|
||||
|
||||
# 2. Recursively collect all DLL dependencies
|
||||
function(resolve_deps file visited_var deps_var)
|
||||
if (DUMP_MODE STREQUAL \"READOBJ\")
|
||||
execute_process(
|
||||
COMMAND \${DUMP_TOOL} --coff-imports \"\${file}\"
|
||||
OUTPUT_VARIABLE dump_out
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
string(REGEX MATCHALL \"Name: [^\\r\\n]+\" dll_entries \"\${dump_out}\")
|
||||
set(REGEX_REPLACE \"Name: +\")
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND \${DUMP_TOOL} -p \"\${file}\"
|
||||
OUTPUT_VARIABLE dump_out
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
string(REGEX MATCHALL \"DLL Name: [^\\r\\n]+\" dll_entries \"\${dump_out}\")
|
||||
set(REGEX_REPLACE \"DLL Name: +\")
|
||||
endif()
|
||||
|
||||
foreach(entry \${dll_entries})
|
||||
string(REGEX REPLACE \"\${REGEX_REPLACE}\" \"\" dll_name \"\${entry}\")
|
||||
string(STRIP \"\${dll_name}\" dll_name)
|
||||
string(REGEX REPLACE \"[^ -~]\" \"\" dll_name \"\${dll_name}\")
|
||||
string(TOLOWER \"\${dll_name}\" dll_name_lower)
|
||||
list(FIND \${visited_var} \"\${dll_name_lower}\" idx)
|
||||
if (NOT idx EQUAL -1)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
if (DUMP_MODE STREQUAL \"READOBJ\")
|
||||
execute_process(
|
||||
COMMAND \${DUMP_TOOL} --coff-imports \"\${file}\"
|
||||
OUTPUT_VARIABLE dump_out
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
string(REGEX MATCHALL \"Name: [^\\r\\n]+\" dll_entries \"\${dump_out}\")
|
||||
set(REGEX_REPLACE \"Name: +\")
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND \${DUMP_TOOL} -p \"\${file}\"
|
||||
OUTPUT_VARIABLE dump_out
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
string(REGEX MATCHALL \"DLL Name: [^\\r\\n]+\" dll_entries \"\${dump_out}\")
|
||||
set(REGEX_REPLACE \"DLL Name: +\")
|
||||
endif()
|
||||
|
||||
foreach(entry \${dll_entries})
|
||||
string(REGEX REPLACE \"\${REGEX_REPLACE}\" \"\" dll_name \"\${entry}\")
|
||||
string(STRIP \"\${dll_name}\" dll_name)
|
||||
string(REGEX REPLACE \"[^ -~]\" \"\" dll_name \"\${dll_name}\")
|
||||
string(TOLOWER \"\${dll_name}\" dll_name_lower)
|
||||
list(FIND \${visited_var} \"\${dll_name_lower}\" idx)
|
||||
if (NOT idx EQUAL -1)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
# Search all candidate paths in order.
|
||||
set(dll_path \"\${dll_name}-NOTFOUND\")
|
||||
set(dll_path \"\${dll_name}-NOTFOUND\")
|
||||
foreach(search_path \${SEARCH_PATHS})
|
||||
# Skip host MSYS2 DLLs except the allowed leaves and toolchain runtimes.
|
||||
if (\"\${search_path}\" MATCHES \"([/\\\\])msys64([/\\\\])\")
|
||||
# Match only known MSYS2 system subdirectories, not user home paths such as
|
||||
# msys64/home/<user>/.cache/cpm which would incorrectly filter Qt DLLs
|
||||
# when CPM_SOURCE_CACHE lives under the default MSYS2 home directory.
|
||||
if (\"\${search_path}\" MATCHES \"([/\\\\])msys64([/\\\\])(usr|clang64|ucrt64|mingw64|mingw32)([/\\\\])\")
|
||||
list(FIND TOOLCHAIN_RUNTIME_DLLS \"\${dll_name_lower}\" toolchain_runtime_idx)
|
||||
if (NOT toolchain_runtime_idx EQUAL -1)
|
||||
# Keep Clang runtimes from the active toolchain.
|
||||
@@ -182,32 +185,32 @@ function(resolve_deps file visited_var deps_var)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (EXISTS \"\${search_path}/\${dll_name}\")
|
||||
set(dll_path \"\${search_path}/\${dll_name}\")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if (EXISTS \"\${dll_path}\" AND NOT IS_DIRECTORY \"\${dll_path}\")
|
||||
list(APPEND \${visited_var} \"\${dll_name_lower}\")
|
||||
list(APPEND \${deps_var} \"\${dll_path}\")
|
||||
|
||||
|
||||
if (EXISTS \"\${search_path}/\${dll_name}\")
|
||||
set(dll_path \"\${search_path}/\${dll_name}\")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if (EXISTS \"\${dll_path}\" AND NOT IS_DIRECTORY \"\${dll_path}\")
|
||||
list(APPEND \${visited_var} \"\${dll_name_lower}\")
|
||||
list(APPEND \${deps_var} \"\${dll_path}\")
|
||||
|
||||
# Log the chosen path for the DLL.
|
||||
message(STATUS \" Found \${dll_name} in: \${dll_path}\")
|
||||
|
||||
resolve_deps(\"\${dll_path}\" \${visited_var} \${deps_var})
|
||||
message(STATUS \" Found \${dll_name} in: \${dll_path}\")
|
||||
|
||||
resolve_deps(\"\${dll_path}\" \${visited_var} \${deps_var})
|
||||
elseif (NOT \"\${dll_name_lower}\" MATCHES \"^(advapi32|authz|avrt|bcrypt|cfgmgr32|comctl32|comdlg32|crypt32|cryptui|d2d1|d3d9|d3d11|d3d12|dbghelp|dnsapi|dsound|dwmapi|dxgi|dwrite|ext-ms-win-|gdi32|hid|imm32|iphlpapi|kernel32|mpr|msvcp_win|mswsock|ncrypt|netapi32|ntdll|ole32|oleaut32|onecore|powrprof|secur32|setupapi|shcore|shell32|shlwapi|user32|userenv|uxtheme|version|winhttp|wininet|winmm|winspool|wintrust|wtsapi32|ws2_32|api-ms-win-).*[.]dll$\")
|
||||
message(STATUS \" Warning: Could not find DLL: \${dll_name}\")
|
||||
endif()
|
||||
endforeach()
|
||||
set(\${visited_var} \${\${visited_var}} PARENT_SCOPE)
|
||||
set(\${deps_var} \${\${deps_var}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
set(visited \"\")
|
||||
set(all_deps \"\")
|
||||
|
||||
endforeach()
|
||||
set(\${visited_var} \${\${visited_var}} PARENT_SCOPE)
|
||||
set(\${deps_var} \${\${deps_var}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
set(visited \"\")
|
||||
set(all_deps \"\")
|
||||
|
||||
# 2.3 Pin core Clang runtimes first.
|
||||
foreach(runtime libc++.dll libunwind.dll libgcc_s_seh-1.dll libstdc++-6.dll)
|
||||
set(runtime_search_paths \"\${TOOLCHAIN_RUNTIME_PATHS};\${SEARCH_PATHS}\")
|
||||
@@ -228,37 +231,37 @@ endforeach()
|
||||
|
||||
# Resolve for the targeted executable
|
||||
resolve_deps(\"\${EXE_DIR}/\${TARGET_FILE}\" visited all_deps)
|
||||
|
||||
|
||||
# Resolve only the plugins we deployed above.
|
||||
foreach(subdir platforms styles imageformats iconengines tls)
|
||||
if (EXISTS \"\${EXE_DIR}/\${subdir}\")
|
||||
file(GLOB plugin_dlls \"\${EXE_DIR}/\${subdir}/*.dll\")
|
||||
foreach(dll \${plugin_dlls})
|
||||
resolve_deps(\"\${dll}\" visited all_deps)
|
||||
endforeach()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# 3. Deploy everything
|
||||
foreach(subdir platforms styles imageformats iconengines tls)
|
||||
if (EXISTS \"\${EXE_DIR}/\${subdir}\")
|
||||
file(GLOB plugin_dlls \"\${EXE_DIR}/\${subdir}/*.dll\")
|
||||
foreach(dll \${plugin_dlls})
|
||||
resolve_deps(\"\${dll}\" visited all_deps)
|
||||
endforeach()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# 3. Deploy everything
|
||||
if (all_deps)
|
||||
list(REMOVE_DUPLICATES all_deps)
|
||||
list(LENGTH all_deps dep_count)
|
||||
message(STATUS \"Deploying \${dep_count} MinGW DLL(s) to \${EXE_DIR}\")
|
||||
|
||||
set(files_to_copy \"\")
|
||||
foreach(dll \${all_deps})
|
||||
list(APPEND files_to_copy \"\${dll}\")
|
||||
endforeach()
|
||||
|
||||
if (files_to_copy)
|
||||
list(LENGTH files_to_copy copy_count)
|
||||
message(STATUS \" Copying \${copy_count} missing DLL(s)...\")
|
||||
foreach(f \${files_to_copy})
|
||||
get_filename_component(fn \"\${f}\" NAME)
|
||||
message(STATUS \" -> \${fn}\")
|
||||
file(COPY \"\${f}\" DESTINATION \"\${EXE_DIR}\")
|
||||
endforeach()
|
||||
message(STATUS \" Deployment complete.\")
|
||||
|
||||
set(files_to_copy \"\")
|
||||
foreach(dll \${all_deps})
|
||||
list(APPEND files_to_copy \"\${dll}\")
|
||||
endforeach()
|
||||
|
||||
if (files_to_copy)
|
||||
list(LENGTH files_to_copy copy_count)
|
||||
message(STATUS \" Copying \${copy_count} missing DLL(s)...\")
|
||||
foreach(f \${files_to_copy})
|
||||
get_filename_component(fn \"\${f}\" NAME)
|
||||
message(STATUS \" -> \${fn}\")
|
||||
file(COPY \"\${f}\" DESTINATION \"\${EXE_DIR}\")
|
||||
endforeach()
|
||||
message(STATUS \" Deployment complete.\")
|
||||
else()
|
||||
message(STATUS \" All DLLs are already up to date.\")
|
||||
endif()
|
||||
@@ -281,7 +284,7 @@ if (all_deps)
|
||||
endforeach()
|
||||
endif()
|
||||
")
|
||||
|
||||
|
||||
add_custom_command(TARGET ${target} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
"-DTARGET_DIR=$<TARGET_FILE_DIR:${target}>"
|
||||
|
||||
+77
-44
@@ -379,6 +379,33 @@ if [[ "${CPM_SOURCE_CACHE}" == *" "* ]]; then
|
||||
" export CPM_SOURCE_CACHE=\"/tmp/cpm-cache\""
|
||||
fi
|
||||
|
||||
# download_with_retry URL OUTPUT_FILE [MAX_RETRIES]
|
||||
# Downloads URL to OUTPUT_FILE using wget, retrying on failure with
|
||||
# exponential back-off (5 s → 10 s → 20 s …).
|
||||
# Returns 0 on success, 1 after all attempts are exhausted.
|
||||
download_with_retry() {
|
||||
local url="$1"
|
||||
local output="$2"
|
||||
local max_retries="${3:-3}"
|
||||
local attempt=1
|
||||
local delay=5
|
||||
|
||||
while [[ "${attempt}" -le "${max_retries}" ]]; do
|
||||
if [[ "${attempt}" -gt 1 ]]; then
|
||||
warn "Download retry ${attempt}/${max_retries} (waiting ${delay}s): $(basename "${output}")"
|
||||
sleep "${delay}"
|
||||
delay=$(( delay * 2 ))
|
||||
fi
|
||||
if wget -q --show-progress --timeout=60 --tries=1 \
|
||||
-O "${output}" "${url}" 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
rm -f "${output}"
|
||||
attempt=$(( attempt + 1 ))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# _sudo — portable sudo wrapper.
|
||||
# On Windows/MSYS2, sudo is unavailable; run privileged commands directly.
|
||||
# On Linux, delegate to the real sudo as usual.
|
||||
@@ -555,30 +582,16 @@ build_bolt_from_source() {
|
||||
local bolt_tag=""
|
||||
local install_dir="/usr/local/bin"
|
||||
|
||||
# Probe candidate tags in order: X.0.0 → X.1.0 → X.1.1 → … → X.1.9
|
||||
# LLVM releases often skip X.0.0 and go straight to X.1.0 for final releases.
|
||||
# Point releases are frequent — LLVM 21 reached 21.1.7. We probe the full
|
||||
# range and take the latest confirmed tag so this stays correct automatically.
|
||||
# Probe for the latest point-release tag with a single ls-remote call.
|
||||
# Fetches all refs/tags/llvmorg-<VER>.* in one round-trip, then picks
|
||||
# the highest version with sort -V.
|
||||
local found_tag=""
|
||||
for _candidate in \
|
||||
"llvmorg-${CLANG_VERSION}.0.0" \
|
||||
"llvmorg-${CLANG_VERSION}.1.0" \
|
||||
"llvmorg-${CLANG_VERSION}.1.1" \
|
||||
"llvmorg-${CLANG_VERSION}.1.2" \
|
||||
"llvmorg-${CLANG_VERSION}.1.3" \
|
||||
"llvmorg-${CLANG_VERSION}.1.4" \
|
||||
"llvmorg-${CLANG_VERSION}.1.5" \
|
||||
"llvmorg-${CLANG_VERSION}.1.6" \
|
||||
"llvmorg-${CLANG_VERSION}.1.7" \
|
||||
"llvmorg-${CLANG_VERSION}.1.8" \
|
||||
"llvmorg-${CLANG_VERSION}.1.9"; do
|
||||
info "Checking for LLVM tag ${_candidate}..."
|
||||
if git ls-remote --tags https://github.com/llvm/llvm-project.git "${_candidate}" \
|
||||
2>/dev/null | grep -q "${_candidate}"; then
|
||||
found_tag="${_candidate}"
|
||||
# Keep probing — we want the latest point-release tag
|
||||
fi
|
||||
done
|
||||
local _all_tags
|
||||
_all_tags="$(git ls-remote --tags https://github.com/llvm/llvm-project.git \
|
||||
"refs/tags/llvmorg-${CLANG_VERSION}.*" 2>/dev/null || true)"
|
||||
found_tag="$(printf '%s\n' "${_all_tags}" \
|
||||
| grep -o "llvmorg-${CLANG_VERSION}\.[0-9][0-9.]*" \
|
||||
| sort -V | tail -1 || true)"
|
||||
|
||||
if [[ -z "${found_tag}" ]]; then
|
||||
error "Could not find any LLVM ${CLANG_VERSION} release tag on GitHub.\n" \
|
||||
@@ -1671,14 +1684,20 @@ rebuild_ffmpeg_pthread_free() {
|
||||
|
||||
local ffmpeg_src=""
|
||||
|
||||
# Priority 1: previously downloaded/extracted source tree
|
||||
if [[ -f "${ffmpeg_src_dir}/configure" ]]; then
|
||||
# Priority 1: previously downloaded/extracted source tree.
|
||||
# Requires the .ffmpeg_src_ready sentinel written after a clean extraction —
|
||||
# a directory with configure but no sentinel means a partial/interrupted
|
||||
# extraction from a prior run and must not be reused.
|
||||
if [[ -f "${ffmpeg_src_dir}/.ffmpeg_src_ready" && -f "${ffmpeg_src_dir}/configure" ]]; then
|
||||
if _ffmpeg_abi_matches "${ffmpeg_src_dir}"; then
|
||||
ffmpeg_src="${ffmpeg_src_dir}"
|
||||
info "[ffmpeg-rebuild] Using cached FFmpeg ${FFMPEG_VERSION} source"
|
||||
else
|
||||
warn "[ffmpeg-rebuild] Cached source ABI does not match FFmpeg ${FFMPEG_VERSION} — ignoring"
|
||||
fi
|
||||
elif [[ -d "${ffmpeg_src_dir}" ]]; then
|
||||
warn "[ffmpeg-rebuild] Cached source dir exists but lacks .ffmpeg_src_ready sentinel — likely a partial extraction. Wiping and re-downloading."
|
||||
rm -rf "${ffmpeg_src_dir}"
|
||||
fi
|
||||
|
||||
# Priority 2: vendored submodule (only if sonames match)
|
||||
@@ -1709,16 +1728,19 @@ rebuild_ffmpeg_pthread_free() {
|
||||
local ffmpeg_url="https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2"
|
||||
info "[ffmpeg-rebuild] Downloading FFmpeg ${FFMPEG_VERSION} source from ffmpeg.org..."
|
||||
mkdir -p "${BUILD_ROOT}"
|
||||
if ! wget -q --show-progress -O "${tarball}" "${ffmpeg_url}"; then
|
||||
warn "[ffmpeg-rebuild] Download failed — FFmpeg DLLs will NOT be built (cmake will fail)"
|
||||
return 0
|
||||
fi
|
||||
download_with_retry "${ffmpeg_url}" "${tarball}" 3 \
|
||||
|| error "[ffmpeg-rebuild] Failed to download FFmpeg ${FFMPEG_VERSION} after 3 attempts.
|
||||
URL: ${ffmpeg_url}
|
||||
Check network connectivity or set CPM_SOURCE_CACHE to a pre-populated directory."
|
||||
info "[ffmpeg-rebuild] Extracting FFmpeg ${FFMPEG_VERSION}..."
|
||||
mkdir -p "${ffmpeg_src_dir}"
|
||||
tar -xjf "${tarball}" -C "${ffmpeg_src_dir}" --strip-components=1 || {
|
||||
warn "[ffmpeg-rebuild] Extraction failed"
|
||||
return 0
|
||||
}
|
||||
info "[ffmpeg-rebuild] Verifying tarball integrity..."
|
||||
tar -tjf "${tarball}" > /dev/null 2>&1 \
|
||||
|| error "[ffmpeg-rebuild] Tarball integrity check failed — download is corrupt.
|
||||
Delete ${tarball} and retry."
|
||||
tar -xjf "${tarball}" -C "${ffmpeg_src_dir}" --strip-components=1 \
|
||||
|| error "[ffmpeg-rebuild] Extraction failed — tarball may be corrupt. Delete ${tarball} and retry."
|
||||
touch "${ffmpeg_src_dir}/.ffmpeg_src_ready"
|
||||
ffmpeg_src="${ffmpeg_src_dir}"
|
||||
success "[ffmpeg-rebuild] FFmpeg ${FFMPEG_VERSION} source ready"
|
||||
fi
|
||||
@@ -1843,8 +1865,9 @@ rebuild_ffmpeg_pthread_free() {
|
||||
done
|
||||
|
||||
if [[ "${installed}" -eq 0 ]]; then
|
||||
warn "[ffmpeg-rebuild] No static libs installed — FFmpeg configure/make may have failed"
|
||||
return 0
|
||||
error "[ffmpeg-rebuild] No static libs were installed after make — FFmpeg build silently produced nothing.
|
||||
Check make output above for configuration errors.
|
||||
Try removing ${ffmpeg_bld} and re-running."
|
||||
fi
|
||||
|
||||
# ── Verify: static libs must NOT depend on libwinpthread ─────────────────
|
||||
@@ -2105,13 +2128,23 @@ build_common_cmake_args() {
|
||||
"-DVulkan_GLSLC_EXECUTABLE=${GLSLC_PATH}"
|
||||
"-DVulkan_GLSLANG_VALIDATOR_EXECUTABLE=${GLSLC_PATH}"
|
||||
)
|
||||
# Static FFmpeg dir (set by rebuild_ffmpeg_pthread_free; may not exist yet during first cmake args call)
|
||||
# Static FFmpeg dir — only passed when the sentinel confirms rebuild_ffmpeg_pthread_free
|
||||
# completed successfully. Passing a nonexistent dir causes CMake to fall through to
|
||||
# the legacy RELEASE-file path and then immediately fail with a fatal error.
|
||||
if [[ -n "${FFMPEG_VERSION:-}" ]]; then
|
||||
local _ffmpeg_static="${CMAKE_BUILD_ROOT}/externals/ffmpeg-${FFMPEG_VERSION}-static"
|
||||
if [[ "${_HOST_OS}" == "windows" ]]; then
|
||||
_ffmpeg_static="$(cygpath -m "${BUILD_ROOT}/externals/ffmpeg-${FFMPEG_VERSION}-static")"
|
||||
local _ffmpeg_ext_dir="${BUILD_ROOT}/externals/ffmpeg-${FFMPEG_VERSION}-static"
|
||||
local _ffmpeg_sentinel="${_ffmpeg_ext_dir}/lib/.llvm_static_built"
|
||||
if [[ -f "${_ffmpeg_sentinel}" ]]; then
|
||||
local _ffmpeg_static="${_ffmpeg_ext_dir}"
|
||||
if [[ "${_HOST_OS}" == "windows" ]]; then
|
||||
_ffmpeg_static="$(cygpath -m "${_ffmpeg_ext_dir}")"
|
||||
fi
|
||||
_CMAKE_ARGS+=("-DCITRON_FFMPEG_STATIC_DIR=${_ffmpeg_static}")
|
||||
else
|
||||
error "FFmpeg static libs not ready — sentinel missing: ${_ffmpeg_sentinel}
|
||||
rebuild_ffmpeg_pthread_free() must complete successfully before cmake is invoked.
|
||||
If this is unexpected, delete ${_ffmpeg_ext_dir} and re-run."
|
||||
fi
|
||||
_CMAKE_ARGS+=("-DCITRON_FFMPEG_STATIC_DIR=${_ffmpeg_static}")
|
||||
fi
|
||||
[[ -n "${CITRON_BUILD_TYPE:-}" ]] && _CMAKE_ARGS+=("-DCITRON_BUILD_TYPE=${CITRON_BUILD_TYPE}")
|
||||
[[ "${UNITY_BUILD}" == "ON" ]] && _CMAKE_ARGS+=("-DENABLE_UNITY_BUILD=ON")
|
||||
@@ -2395,8 +2428,8 @@ stage_csgenerate() {
|
||||
local _gen_cfg="${BUILD_ROOT}/.citron-gen-config"
|
||||
if [[ -f "${_gen_cfg}" ]]; then
|
||||
local _gen_lto _gen_pgo
|
||||
_gen_lto=$(grep -oP "(?<=LTO=)\S+" "${_gen_cfg}" || true)
|
||||
_gen_pgo=$(grep -oP "(?<=PGO=)\S+" "${_gen_cfg}" || true)
|
||||
_gen_lto=$(awk -F= '/^LTO=/{print $2; exit}' "${_gen_cfg}" 2>/dev/null || true)
|
||||
_gen_pgo=$(awk -F= '/^PGO=/{print $2; exit}' "${_gen_cfg}" 2>/dev/null || true)
|
||||
if [[ -n "${_gen_lto}" && "${_gen_lto}" != "${LTO_MODE}" ]]; then
|
||||
error "LTO mismatch: generate used LTO=${_gen_lto}, csgenerate has LTO=${LTO_MODE}.\n"\
|
||||
" IR PGO profiles are tied to the IR produced at generate time.\n"\
|
||||
@@ -2841,8 +2874,8 @@ stage_use() {
|
||||
local _gen_cfg="${BUILD_ROOT}/.citron-gen-config"
|
||||
if [[ -f "${_gen_cfg}" ]]; then
|
||||
local _gen_lto _gen_pgo
|
||||
_gen_lto=$(grep -oP "(?<=LTO=)\S+" "${_gen_cfg}" || true)
|
||||
_gen_pgo=$(grep -oP "(?<=PGO=)\S+" "${_gen_cfg}" || true)
|
||||
_gen_lto=$(awk -F= '/^LTO=/{print $2; exit}' "${_gen_cfg}" 2>/dev/null || true)
|
||||
_gen_pgo=$(awk -F= '/^PGO=/{print $2; exit}' "${_gen_cfg}" 2>/dev/null || true)
|
||||
if [[ -n "${_gen_lto}" && "${_gen_lto}" != "${LTO_MODE}" ]]; then
|
||||
error "LTO mismatch: generate used LTO=${_gen_lto}, use has LTO=${LTO_MODE}.\n"\
|
||||
" IR PGO profiles are tied to the IR produced at generate time.\n"\
|
||||
|
||||
Reference in New Issue
Block a user