Reuse PE cache during packaging and test installer completeness
This commit is contained in:
1 parent
0bee366d6b
commit
359c6bc843
3 files changed
+61
-16
No files matched your search
@@ -2,6 +2,8 @@
|
||||
# Чистый комплект из Windows-сборки и Inno Setup; ключи/БД/логи из build не копируются.
|
||||
set -euo pipefail
|
||||
|
||||
log() { echo "$1"; }
|
||||
|
||||
SRC_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
BUILD_DIR="$SRC_DIR/build"
|
||||
RUNTIME_PREFIX="${MSYSTEM_PREFIX:-/ucrt64}"
|
||||
@@ -70,6 +72,10 @@ cp "$SRC_DIR/../../lib/wintun-LICENSE.txt" "$SRC_DIR/../../lib/wintun-api-LICENS
|
||||
# Проверяем архитектуру/полноту уже собранного комплекта, включая динамические Qt-плагины.
|
||||
SYSTEM_DIR=$(cygpath -u "${WINDIR:-C:\\Windows}/System32")
|
||||
source "$SRC_DIR/compiler_runtime.sh"
|
||||
source "$SRC_DIR/runtime_cache.sh"
|
||||
runtime_cache_init
|
||||
IMPORT_DUMP="$STAGE/imports.txt"
|
||||
VERIFY_STARTED=$SECONDS
|
||||
declare -A LICENSE_PACKAGES=()
|
||||
declare -A INCLUDED=()
|
||||
FILES=(vibechat.exe wintun.dll platforms/qwindows.dll imageformats/qjpeg.dll imageformats/qwebp.dll)
|
||||
@@ -84,14 +90,19 @@ for ((i = 0; i < ${#FILES[@]}; i++)); do
|
||||
echo "ERROR: mixed Qt runtime: $relative does not belong to $QT_PACKAGE" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Ключ кеша относится к build, а не к новой временной копии установщика.
|
||||
runtime_inspect "$BUILD_DIR/$relative"
|
||||
[[ "$binary_format" = pei-x86-64 ]] || { echo "ERROR: $name is not a Windows x64 binary: $binary_format" >&2; exit 1; }
|
||||
file="$PACKAGE_DIR/$relative"
|
||||
mkdir -p "${file%/*}"
|
||||
cp "$BUILD_DIR/$relative" "$file"
|
||||
name=${file##*/}
|
||||
objdump -f "$file" > "$STAGE/headers.txt"
|
||||
binary_format=$(awk '/file format/ {print $NF}' "$STAGE/headers.txt")
|
||||
[[ "$binary_format" = pei-x86-64 ]] || { echo "ERROR: $name is not a Windows x64 binary: $binary_format" >&2; exit 1; }
|
||||
objdump -p "$file" > "$STAGE/imports.txt"
|
||||
# Успешный cp переносит проверенные байты; источник не должен меняться во время анализа/копирования.
|
||||
copied_stamp=$(stat -Lc '%n|%s|%y|%z' -- "$BUILD_DIR/$relative") || {
|
||||
echo "ERROR: cannot stat installer source: $relative" >&2; exit 1;
|
||||
}
|
||||
[[ "$copied_stamp" = "$binary_stamp" ]] || {
|
||||
echo "ERROR: installer source changed during packaging: $relative; rebuild and retry" >&2; exit 1;
|
||||
}
|
||||
while read -r dep; do
|
||||
[[ -n "$dep" ]] || continue
|
||||
if [[ -f "$BUILD_DIR/$dep" ]]; then FILES+=("$dep"); continue; fi
|
||||
@@ -99,7 +110,7 @@ for ((i = 0; i < ${#FILES[@]}; i++)); do
|
||||
if [[ -f "$SYSTEM_DIR/$dep" && "$dep" != msvcp*.dll && "$dep" != vcruntime*.dll ]]; then continue; fi
|
||||
echo "ERROR: incomplete installer runtime: $name requires $dep" >&2
|
||||
exit 1
|
||||
done < <(awk '/DLL Name:/ {print $3}' "$STAGE/imports.txt")
|
||||
done <<< "$import_list"
|
||||
echo "INFO: verified installer runtime: ${file#"$PACKAGE_DIR/"}"
|
||||
|
||||
# Лицензии внешних библиотек берём из пакетов той же UCRT64-сборки.
|
||||
@@ -119,6 +130,7 @@ for ((i = 0; i < ${#FILES[@]}; i++)); do
|
||||
[[ -n "$pkg" ]] || { echo "ERROR: no package owns runtime file: $runtime_file" >&2; exit 1; }
|
||||
LICENSE_PACKAGES["$pkg"]=1
|
||||
done
|
||||
echo "INFO: installer PE verification: scanned=$RUNTIME_SCANNED cached=$RUNTIME_HITS elapsed=$((SECONDS - VERIFY_STARTED))s"
|
||||
for pkg in "${!LICENSE_PACKAGES[@]}"; do
|
||||
pacman -Qql "$pkg" > "$STAGE/package-files.txt"
|
||||
while IFS= read -r file; do
|
||||
|
||||
@@ -27,6 +27,7 @@ cat > "$fixture_root/commands/objdump" <<'SH'
|
||||
#!/bin/bash
|
||||
printf '%s\n' "$2" >> "$DEPLOY_OBJDUMP_LOG"
|
||||
if [[ "$(< "$2")" = *BROKEN* ]]; then echo 'invalid PE fixture' >&2; exit 7; fi
|
||||
printf '%s: file format pei-x86-64\n' "$2"
|
||||
cat "$2"
|
||||
SH
|
||||
chmod +x "$fixture_root/commands/"*
|
||||
@@ -35,7 +36,7 @@ prepare_case() {
|
||||
case_dir="$fixture_root/project/Qt$1 package"; prefix="$case_dir/ucrt64"
|
||||
mkdir -p "$case_dir/rlottie_build" "$prefix/bin" "$prefix/share/qt$1/plugins/platforms" "$prefix/share/qt$1/plugins/imageformats"
|
||||
cp "$fixture_root/commands/objdump" "$prefix/bin/objdump"
|
||||
cp "$source_dir/build.sh" "$source_dir/compiler_runtime.sh" "$source_dir/runtime_cache.sh" "$case_dir/"
|
||||
cp "$source_dir/build.sh" "$source_dir/package_win.sh" "$source_dir/compiler_runtime.sh" "$source_dir/runtime_cache.sh" "$case_dir/"
|
||||
printf 'DLL Name: KERNEL32.dll\n' > "$case_dir/rlottie_build/librlottie.dll"
|
||||
printf 'fixture\n' > "$case_dir/setup_firewall.bat"
|
||||
printf 'DLL Name: AUTHZ.dll\n' > "$prefix/bin/Qt$1Core.dll"
|
||||
|
||||
@@ -14,7 +14,9 @@ gui = case / 'tools/chatgui'
|
||||
build = gui / 'build'
|
||||
commands = root / 'commands'
|
||||
prefix = root / 'ucrt64'
|
||||
environment = {**os.environ, 'MSYSTEM': 'UCRT64', 'MSYSTEM_PREFIX': str(prefix), 'WINDIR': str(root / 'Windows')}
|
||||
scan_log = root / 'objdump.log'
|
||||
environment = {**os.environ, 'MSYSTEM': 'UCRT64', 'MSYSTEM_PREFIX': str(prefix), 'WINDIR': str(root / 'Windows'),
|
||||
'OBJDUMP_CALLS': str(scan_log)}
|
||||
|
||||
|
||||
def write(path, text, executable=False):
|
||||
@@ -26,6 +28,7 @@ def write(path, text, executable=False):
|
||||
|
||||
|
||||
def run(*arguments, ok=True, extra=None):
|
||||
scan_log.write_text('')
|
||||
result = subprocess.run(['bash', str(gui / 'package_win.sh'), *arguments],
|
||||
env={**environment, **(extra or {})}, text=True, capture_output=True)
|
||||
with (root / 'checks.log').open('a') as log:
|
||||
@@ -46,7 +49,7 @@ def package():
|
||||
|
||||
|
||||
try:
|
||||
for name in ('package_win.sh', 'compiler_runtime.sh', 'build.sh', 'installer/vibechat.iss'):
|
||||
for name in ('package_win.sh', 'compiler_runtime.sh', 'runtime_cache.sh', 'build.sh', 'installer/vibechat.iss'):
|
||||
target = gui / name
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(source / name, target)
|
||||
@@ -62,12 +65,13 @@ try:
|
||||
write(commands / 'cygpath', '#!/bin/bash\nprintf "%s\\n" "$2"\n', executable=True)
|
||||
write(commands / 'objdump', '''#!/bin/bash
|
||||
set -eu
|
||||
if [[ "$1" = -f ]]; then
|
||||
if [[ $(head -1 "$2") = wrong-architecture ]]; then echo 'file format pei-i386'; else echo 'file format pei-x86-64'; fi
|
||||
else
|
||||
cat "$2"
|
||||
fi
|
||||
[[ "$1" = -p ]] || { echo 'unexpected extra PE inspection' >&2; exit 1; }
|
||||
printf '%s\\n' "$2" >> "$OBJDUMP_CALLS"
|
||||
if [[ $(head -1 "$2") = wrong-architecture ]]; then echo 'file format pei-i386'; else echo 'file format pei-x86-64'; fi
|
||||
cat "$2"
|
||||
''', executable=True)
|
||||
(prefix / 'bin').mkdir(parents=True)
|
||||
shutil.copy(commands / 'objdump', prefix / 'bin/objdump')
|
||||
write(commands / 'pacman', '''#!/bin/bash
|
||||
set -eu
|
||||
if [[ "$1" = -Qqo ]]; then echo fixture-runtime; else echo "$MSYSTEM_PREFIX/share/licenses/fixture/COPYING"; fi
|
||||
@@ -119,11 +123,30 @@ files = sorted(str(p.relative_to(package)) for p in package.rglob('*') if p.is_f
|
||||
'licenses/wintun/wintun-LICENSE.txt', 'licenses/wintun/wintun-api-LICENSE.txt',
|
||||
'licenses/rlottie/COPYING', 'licenses/rlottie/licenses/COPYING.MIT', 'licenses/runtime/fixture/COPYING'}
|
||||
assert set(result['files']) == expected, result['files']
|
||||
assert len(scan_log.read_text().splitlines()) == len(libraries) + 1, 'Expected one PE inspection per packaged binary'
|
||||
print('PASS: complete dependency graph, Qt plugin helpers and license notices; private files and stale DLLs excluded')
|
||||
|
||||
result2 = package()
|
||||
assert result2['files'] == result['files']
|
||||
print('PASS: repeated packaging and paths containing spaces')
|
||||
assert not scan_log.read_text(), 'Repeated packaging inspected unchanged PE files'
|
||||
print('PASS: repeated packaging reuses architecture and imports with no objdump calls; paths containing spaces')
|
||||
|
||||
write(build / 'vibechat.exe', (build / 'vibechat.exe').read_text() + '\nrelinked\n')
|
||||
assert package()['files'] == result['files']
|
||||
assert scan_log.read_text().splitlines() == [str(build / 'vibechat.exe')]
|
||||
print('PASS: standalone packaging inspects only a changed EXE')
|
||||
|
||||
# Проверенные импорты относятся к конкретным байтам: изменение источника при копировании — ошибка.
|
||||
write(commands / 'cp', '''#!/bin/bash
|
||||
set -eu
|
||||
/bin/cp "$@"
|
||||
if [[ ${MUTATE_COPY_SOURCE:-} = "$1" ]]; then printf '\\nchanged during copy\\n' >> "$1"; fi
|
||||
''', executable=True)
|
||||
source_before = (build / 'helper.dll').read_text()
|
||||
failed = run(ok=False, extra={'MUTATE_COPY_SOURCE': str(build / 'helper.dll')})
|
||||
assert 'installer source changed during packaging: helper.dll' in failed.stderr
|
||||
write(build / 'helper.dll', source_before)
|
||||
print('PASS: source mutation during staging rejects the installer')
|
||||
|
||||
driver = build / 'wintun.dll'
|
||||
saved_driver = driver.read_text()
|
||||
@@ -133,6 +156,9 @@ files = sorted(str(p.relative_to(package)) for p in package.rglob('*') if p.is_f
|
||||
write(driver, 'wrong-architecture\n')
|
||||
failed = run(ok=False)
|
||||
assert 'wintun.dll is not a Windows x64 binary' in failed.stderr
|
||||
failed = run(ok=False)
|
||||
assert 'wintun.dll is not a Windows x64 binary' in failed.stderr
|
||||
assert not scan_log.read_text(), 'Cached architecture rejection required another inspection'
|
||||
write(driver, saved_driver)
|
||||
print('PASS: dynamically loaded Wintun and its licenses are included; missing/wrong architecture DLL is rejected')
|
||||
|
||||
@@ -158,8 +184,9 @@ files = sorted(str(p.relative_to(package)) for p in package.rglob('*') if p.is_f
|
||||
missing.unlink()
|
||||
failed = run(ok=False)
|
||||
assert 'qjpeg.dll requires jpeg-helper.dll' in failed.stderr
|
||||
assert str(build / 'imageformats/qjpeg.dll') not in scan_log.read_text().splitlines()
|
||||
write(missing, saved)
|
||||
print('PASS: missing transitive plugin dependency rejects incomplete installer')
|
||||
print('PASS: cached plugin imports still reject a missing transitive dependency')
|
||||
|
||||
write(build / 'helper.dll', 'wrong-architecture\n')
|
||||
failed = run(ok=False)
|
||||
@@ -232,13 +259,18 @@ if [[ ${FFMPEG_FAIL:-0} = 1 ]]; then exit 1; fi
|
||||
''')
|
||||
environment['FFMPEG_CALLS'] = str(ffmpeg_calls)
|
||||
(build / 'wintun.dll').unlink()
|
||||
scan_log.write_text('')
|
||||
built = subprocess.run(['bash', str(gui / 'build.sh'), '--installer', '-j2'], cwd=gui,
|
||||
env={**environment, 'CMAKE_CALLS': str(calls)}, text=True, capture_output=True)
|
||||
assert built.returncode == 0, built.stdout + built.stderr
|
||||
assert '-DCMAKE_BUILD_TYPE=Release' in calls.read_text()
|
||||
assert 'installer created:' in built.stdout and 'Build SUCCESS' in built.stdout
|
||||
assert 'installer PE verification: scanned=0 cached=' in built.stdout, built.stdout
|
||||
scans = scan_log.read_text().splitlines()
|
||||
assert len(scans) == len(set(scans)), 'Packaging repeated a PE inspection performed by deployment'
|
||||
assert (build / 'wintun.dll').read_bytes() == (case / 'lib/wintun.dll').read_bytes()
|
||||
assert ffmpeg_calls.read_text().splitlines() == ['profile update']
|
||||
print('PASS: build --installer shares cached PE format/imports without rescanning DLLs in staging')
|
||||
cmake_before_failure = calls.read_text()
|
||||
codec_failed = subprocess.run(['bash', str(gui / 'build.sh'), '--installer', '-j2'], cwd=gui,
|
||||
env={**environment, 'CMAKE_CALLS': str(calls), 'FFMPEG_FAIL': '1'}, text=True, capture_output=True)
|
||||
|
||||
Reference in new issue
Block a user