Browse Source

Detect compiler path changes and verify safe configuration cleanup

master
evgeny 1 week ago
parent
commit
62a853e73c
  1. 4
      build.sh
  2. 30
      tests/test_build_script.py

4
build.sh

@ -52,7 +52,7 @@ for tool in make sha256sum; do
command -v "$tool" >/dev/null || { echo "ERROR: required tool not found: $tool" >&2; exit 1; } command -v "$tool" >/dev/null || { echo "ERROR: required tool not found: $tool" >&2; exit 1; }
done done
read -r -a COMPILER <<< "${CC:-gcc}" read -r -a COMPILER <<< "${CC:-gcc}"
command -v "${COMPILER[0]}" >/dev/null || { echo "ERROR: compiler not found: ${CC:-gcc}" >&2; exit 1; } COMPILER_PATH=$(command -v "${COMPILER[0]}") || { echo "ERROR: compiler not found: ${CC:-gcc}" >&2; exit 1; }
COMPILER_ID=$("${COMPILER[@]}" --version) || { echo "ERROR: cannot identify compiler: ${CC:-gcc}" >&2; exit 1; } COMPILER_ID=$("${COMPILER[@]}" --version) || { echo "ERROR: cannot identify compiler: ${CC:-gcc}" >&2; exit 1; }
# Хеши учитывают содержимое и имена файлов, включая добавление/удаление макросов. # Хеши учитывают содержимое и имена файлов, включая добавление/удаление макросов.
@ -91,7 +91,7 @@ if command -v pkg-config >/dev/null && pkg-config --exists "${FFMPEG_PACKAGES[@]
FFMPEG_LDFLAGS=$(pkg-config --libs-only-L "${FFMPEG_PACKAGES[@]}") FFMPEG_LDFLAGS=$(pkg-config --libs-only-L "${FFMPEG_PACKAGES[@]}")
fi fi
request_sum=$({ request_sum=$({
printf '%s\0' "$MODE" "${OSTYPE:-}" "${MSYSTEM:-}" "${MSYSTEM_PREFIX:-}" "${CC:-gcc}" "$COMPILER_ID" printf '%s\0' "$MODE" "${OSTYPE:-}" "${MSYSTEM:-}" "${MSYSTEM_PREFIX:-}" "${CC:-gcc}" "$COMPILER_PATH" "$COMPILER_ID"
printf '%s\0' "${CFLAGS+x}" "${CFLAGS-}" "${CPPFLAGS+x}" "${CPPFLAGS-}" "${LDFLAGS+x}" "${LDFLAGS-}" printf '%s\0' "${CFLAGS+x}" "${CFLAGS-}" "${CPPFLAGS+x}" "${CPPFLAGS-}" "${LDFLAGS+x}" "${LDFLAGS-}"
printf '%s\0' "${LIBS+x}" "${LIBS-}" "${MAKEFLAGS-}" "${DEBUG_FLAGS+x}" "${DEBUG_FLAGS-}" printf '%s\0' "${LIBS+x}" "${LIBS-}" "${MAKEFLAGS-}" "${DEBUG_FLAGS+x}" "${DEBUG_FLAGS-}"
printf '%s\0' "$FFMPEG_OPTION" "$FFMPEG_ID" "$FFMPEG_CFLAGS" "$FFMPEG_LDFLAGS" printf '%s\0' "$FFMPEG_OPTION" "$FFMPEG_ID" "$FFMPEG_CFLAGS" "$FFMPEG_LDFLAGS"

30
tests/test_build_script.py

@ -1,8 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Проверка настоящего build.sh на маленьком проекте Autotools, без изменения исходников uTun.""" """Проверка настоящего build.sh на маленьком проекте Autotools, без изменения исходников uTun."""
from pathlib import Path from pathlib import Path
import json
import os import os
import shlex
import shutil import shutil
import subprocess import subprocess
import sys import sys
@ -92,7 +92,9 @@ int main(void) { printf("%d\\n", worker() + fixture() + opus_fixture()); return
write('src/worker.c', '#include "api.h"\nint worker(void) { return VALUE + LOCAL_OPTION; }\n') write('src/worker.c', '#include "api.h"\nint worker(void) { return VALUE + LOCAL_OPTION; }\n')
write('src/api.h', '#include "nested.h"\n') write('src/api.h', '#include "nested.h"\n')
write('src/nested.h', '#define VALUE 10\n') write('src/nested.h', '#define VALUE 10\n')
write('tests/Makefile.am', 'noinst_PROGRAMS = probe\nprobe_SOURCES = probe.c\n') test_rules = (script.parent / 'tests/Makefile.am').read_text()
config_rules = test_rules[test_rules.index('all-local: copy-test-configs'):test_rules.index('# Test logs directory')]
write('tests/Makefile.am', 'noinst_PROGRAMS = probe\nprobe_SOURCES = probe.c\n' + config_rules)
write('tests/probe.c', 'int main(void) { return 0; }\n') write('tests/probe.c', 'int main(void) { return 0; }\n')
package_tool = write('bin/pkg-config', '''#!/bin/bash package_tool = write('bin/pkg-config', '''#!/bin/bash
[[ "${BUILD_TEST_FFMPEG:-0}" = 1 ]] || exit 1 [[ "${BUILD_TEST_FFMPEG:-0}" = 1 ]] || exit 1
@ -141,6 +143,14 @@ esac
build('CFLAGS=-g -O0 -DUSER_OPTION=1') build('CFLAGS=-g -O0 -DUSER_OPTION=1')
check('repeat build with quoted flags is incremental', initial == times()) check('repeat build with quoted flags is incremental', initial == times())
build() build()
gcc = shutil.which('gcc')
for directory in ['compiler-first', 'compiler-second']:
wrapper = write(directory + '/gcc', '#!/bin/bash\nexec ' + shlex.quote(gcc) + ' "$@"\n')
wrapper.chmod(0o755)
build(extra_environment={'PATH': str(base / 'compiler-first') + os.pathsep + environment['PATH']})
result = build(extra_environment={'PATH': str(base / 'compiler-second') + os.pathsep + environment['PATH']})
check('compiler path change is detected even with identical version', 'INFO: configuring:' in result.stdout)
build()
time.sleep(1.1) time.sleep(1.1)
write('src/Makefile.am', (base / 'src/Makefile.am').read_text().replace('LOCAL_OPTION=1', 'LOCAL_OPTION=2')) write('src/Makefile.am', (base / 'src/Makefile.am').read_text().replace('LOCAL_OPTION=1', 'LOCAL_OPTION=2'))
result = build() result = build()
@ -161,8 +171,11 @@ esac
write('src/.deps/worker.Po', '# dummy\n') write('src/.deps/worker.Po', '# dummy\n')
result = build() result = build()
check('dummy dependencies force rebuild', 'missing_dependencies=1' in result.stdout) check('dummy dependencies force rebuild', 'missing_dependencies=1' in result.stdout)
write('tests/test_preserve.conf', 'preserve configuration\n')
write('tests/test_preserve.json', '{"preserve":true}\n')
result = build('--clean') result = build('--clean')
check('explicit clean does not reconfigure', 'INFO: cleaning objects:' in result.stdout and 'INFO: configuring:' not in result.stdout) check('explicit clean does not reconfigure', 'INFO: cleaning objects:' in result.stdout and 'INFO: configuring:' not in result.stdout)
check('in-source clean preserves configuration files', (base / 'tests/test_preserve.conf').exists() and (base / 'tests/test_preserve.json').exists())
result = build('--full') result = build('--full')
check('explicit full regenerates, configures and rebuilds', 'INFO: regenerating' in result.stdout and 'INFO: configuring:' in result.stdout) check('explicit full regenerates, configures and rebuilds', 'INFO: regenerating' in result.stdout and 'INFO: configuring:' in result.stdout)
(base / 'src/Makefile').unlink() (base / 'src/Makefile').unlink()
@ -191,6 +204,19 @@ esac
result = run(['bash', 'build.sh', '-j', '3'], ok=False) result = run(['bash', 'build.sh', '-j', '3'], ok=False)
check('GUI failure propagates and success is not printed', result.returncode != 0 and 'Build completed successfully!' not in result.stdout) check('GUI failure propagates and success is not printed', result.returncode != 0 and 'Build completed successfully!' not in result.stdout)
check('GUI receives selected job count', (base / 'gui-arguments').read_text().strip() == '--build tools/chatgui/build --parallel 3') check('GUI receives selected job count', (base / 'gui-arguments').read_text().strip() == '--build tools/chatgui/build --parallel 3')
run(['make', 'distclean'])
output_dir = base / 'out'
output_dir.mkdir()
for args in [['../configure'], ['make', '-j2'], ['make', '-C', 'tests', 'clean-local']]:
result = subprocess.run(args, cwd=output_dir, env=environment, capture_output=True, text=True)
with log.open('a') as output:
output.write(f'OUT-OF-TREE {args!r}\nexit={result.returncode}\n{result.stdout}{result.stderr}\n')
if result.returncode:
raise AssertionError(f'out-of-tree {args!r}: see {log}')
if args[0] == 'make' and '-j2' in args:
check('out-of-tree build copies test configurations', (output_dir / 'tests/test_preserve.conf').exists())
check('out-of-tree clean removes copies and preserves source configurations',
not (output_dir / 'tests/test_preserve.conf').exists() and (base / 'tests/test_preserve.conf').exists())
print(f'[PASS] build.sh: {len(checks)} checks completed', flush=True) print(f'[PASS] build.sh: {len(checks)} checks completed', flush=True)
shutil.rmtree(base) shutil.rmtree(base)
except Exception: except Exception:

Loading…
Cancel
Save