diff --git a/build.sh b/build.sh index 79e36a05..41d7940f 100755 --- a/build.sh +++ b/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; } done 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; } # Хеши учитывают содержимое и имена файлов, включая добавление/удаление макросов. @@ -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[@]}") fi 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' "${LIBS+x}" "${LIBS-}" "${MAKEFLAGS-}" "${DEBUG_FLAGS+x}" "${DEBUG_FLAGS-}" printf '%s\0' "$FFMPEG_OPTION" "$FFMPEG_ID" "$FFMPEG_CFLAGS" "$FFMPEG_LDFLAGS" diff --git a/tests/test_build_script.py b/tests/test_build_script.py index 8cdb1939..f216fc0e 100644 --- a/tests/test_build_script.py +++ b/tests/test_build_script.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 """Проверка настоящего build.sh на маленьком проекте Autotools, без изменения исходников uTun.""" from pathlib import Path -import json import os +import shlex import shutil import subprocess 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/api.h', '#include "nested.h"\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') package_tool = write('bin/pkg-config', '''#!/bin/bash [[ "${BUILD_TEST_FFMPEG:-0}" = 1 ]] || exit 1 @@ -141,6 +143,14 @@ esac build('CFLAGS=-g -O0 -DUSER_OPTION=1') check('repeat build with quoted flags is incremental', initial == times()) 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) write('src/Makefile.am', (base / 'src/Makefile.am').read_text().replace('LOCAL_OPTION=1', 'LOCAL_OPTION=2')) result = build() @@ -161,8 +171,11 @@ esac write('src/.deps/worker.Po', '# dummy\n') result = build() 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') 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') check('explicit full regenerates, configures and rebuilds', 'INFO: regenerating' in result.stdout and 'INFO: configuring:' in result.stdout) (base / 'src/Makefile').unlink() @@ -191,6 +204,19 @@ esac 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 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) shutil.rmtree(base) except Exception: