@ -18,6 +18,8 @@ environment.pop('MSYSTEM', None)
for name in [ ' CC ' , ' CFLAGS ' , ' CPPFLAGS ' , ' LDFLAGS ' , ' LIBS ' , ' DEBUG_FLAGS ' ] :
environment . pop ( name , None )
checks = [ ]
default_compiler = ' clang ' if sys . platform . startswith ( ' freebsd ' ) else ' gcc '
make_tool = environment . get ( ' MAKE ' ) or ( ' gmake ' if sys . platform . startswith ( ' freebsd ' ) else ' make ' )
def write ( name , content ) :
path = base / name
@ -26,6 +28,8 @@ def write(name, content):
return path
def run ( args , ok = True , extra_environment = None ) :
if args [ 0 ] == ' make ' :
args = [ make_tool , * args [ 1 : ] ]
result = subprocess . run ( args , cwd = base , env = { * * environment , * * ( extra_environment or { } ) } , text = True , capture_output = True )
with log . open ( ' a ' ) as output :
output . write ( f ' { args !r} \n exit= { result . returncode } \n { result . stdout } { result . stderr } \n ' )
@ -46,28 +50,33 @@ def times():
return { str ( p . relative_to ( base ) ) : p . stat ( ) . st_mtime_ns for p in base . rglob ( ' *.o ' ) }
def value ( ) :
return run ( [ ' ./src/utun ' ] ) . stdout . strip ( )
binary = base / ' src/utun '
if not binary . exists ( ) :
binary = binary . with_suffix ( ' .exe ' )
return run ( [ str ( binary ) ] ) . stdout . strip ( )
try :
shutil . copyfile ( script , base / ' build.sh ' )
actual_configuration = ( script . parent / ' configure.ac ' ) . read_text ( )
hardening_start = actual_configuration . index ( ' if test " x$enable_hardening " = " xyes " ; then ' )
hardening_end = actual_configuration . index ( ' # ==================== НАДЁЖНЫЙ ДЕТЕКТ WINDOWS ' , hardening_start )
hardening_rules = actual_configuration [ hardening_start : hardening_end ]
write ( ' configure.ac ' , ''' AC_INIT([build-test], [1.0])
AC_CONFIG_SRCDIR ( [ src / main . c ] )
AC_CONFIG_HEADERS ( [ config . h ] )
AC_CONFIG_MACRO_DIR ( [ m4 ] )
AC_CANONICAL_HOST
AM_INIT_AUTOMAKE ( [ foreign subdir - objects ] )
AC_PROG_CC
AC_PROG_RANLIB
AM_PROG_AR
AC_ARG_WITH ( [ ffmpeg ] , [ ] , [ ] , [ with_ffmpeg = no ] )
AC_ARG_ENABLE ( [ hardening ] , [ ] , [ ] , [ enable_hardening = no ] )
if test " x$enable_hardening " = xyes ; then
CFLAGS = " $CFLAGS -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fPIE "
LDFLAGS = " $LDFLAGS -Wl,-z,relro -Wl,-z,now -pie "
fi
__HARDENING_RULES__
TEST_CONFIGURE_OPTION
AC_CONFIG_FILES ( [ Makefile lib / Makefile lib / libopus / Makefile src / Makefile tests / Makefile ] )
AC_OUTPUT
''' )
''' .replace( ' __HARDENING_RULES__ ' , hardening_rules) )
write ( ' m4/test.m4 ' , ' AC_DEFUN([TEST_CONFIGURE_OPTION], [AC_DEFINE([TEST_OPTION], [1], [Test option])]) \n ' )
write ( ' Makefile.am ' , ' SUBDIRS = lib lib/libopus src tests \n ' )
write ( ' lib/Makefile.am ' , ' noinst_LIBRARIES = libfixture.a \n libfixture_a_SOURCES = fixture.c \n ' )
@ -96,7 +105,7 @@ int main(void) { printf("%d\\n", worker() + fixture() + opus_fixture()); return
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 \n probe_SOURCES = probe.c \n ' + config_rules )
write ( ' tests/probe.c ' , ' int main(void) { return 0; } \n ' )
package_tool = write ( ' bin/pkg-config ' , ''' #!/bin/bash
package_tool = write ( ' bin/pkg-config ' , ''' #!/usr/ bin/env bash
[ [ " $ {BUILD_TEST_FFMPEG:-0} " = 1 ] ] | | exit 1
case " $1 " in
- - exists ) exit 0 ; ;
@ -110,7 +119,7 @@ esac
result = build ( )
check ( ' fresh build generates, configures and builds dependencies ' , value ( ) == ' 11 ' and ( base / ' lib/libopus/libopus_internal.a ' ) . exists ( ) )
check ( ' normal build includes tests ' , ( base / ' tests/probe ' ) . exists ( ) )
check ( ' normal build includes tests ' , ( base / ' tests/probe ' ) . exists ( ) or ( base / ' tests/probe.exe ' ) . exists ( ) )
initial = times ( )
result = build ( )
check ( ' repeat build does not regenerate, configure or compile ' , initial == times ( ) and ' INFO: configuring: ' not in result . stdout and ' INFO: regenerating ' not in result . stdout )
@ -125,14 +134,19 @@ esac
build ( )
check ( ' unrelated header does not recompile ' , initial == times ( ) )
for mode in [ ' asan ' , ' hardened ' , ' normal ' ] :
write ( ' asan_probe.c ' , ' int main(void) { return 0; } \n ' )
asan_supported = run ( [ default_compiler , ' -fsanitize=address ' , ' -fno-pie ' , ' -no-pie ' , ' asan_probe.c ' , ' -o ' , ' asan_probe ' ] , ok = False ) . returncode == 0
modes = [ ' asan ' , ' hardened ' , ' normal ' ] if asan_supported else [ ' hardened ' , ' normal ' ]
if not asan_supported :
print ( ' [SKIP] build.sh: ASAN runtime is unavailable in this compiler toolchain ' , flush = True )
for mode in modes :
args = [ ] if mode == ' normal ' else [ ' -- ' + mode ]
result = build ( * args )
symbols = run ( [ ' nm ' , ' -u ' , ' src/worker.o ' ] ) . stdout
check ( f ' { mode } : changing mode reconfigures and cleans ' , ' INFO: configuring: ' in result . stdout and ' INFO: cleaning objects: ' in result . stdout )
check ( f ' { mode } : object instrumentation matches mode ' , ( ' __asan_ ' in symbols ) == ( mode == ' asan ' ) )
if mode == ' asan ' :
check ( ' ASAN skips tests and preserves unsanitized Opus ' , not ( base / ' tests/probe ' ) . exists ( ) and ' __asan_ ' not in run ( [ ' nm ' , ' -u ' , ' lib/libopus/opus.o ' ] ) . stdout )
check ( ' ASAN skips tests and preserves unsanitized Opus ' , not ( base / ' tests/probe ' ) . exists ( ) and not ( base / ' tests/probe.exe ' ) . exists ( ) and ' __asan_ ' not in run ( [ ' nm ' , ' -u ' , ' lib/libopus/opus.o ' ] ) . stdout )
initial = times ( )
result = build ( * args )
check ( f ' { mode } : repeat build is incremental ' , initial == times ( ) and ' INFO: configuring: ' not in result . stdout and ' INFO: regenerating ' not in result . stdout )
@ -143,9 +157,9 @@ esac
build ( ' CFLAGS=-g -O0 -DUSER_OPTION=1 ' )
check ( ' repeat build with quoted flags is incremental ' , initial == times ( ) )
build ( )
gcc = shutil . which ( ' gcc ' )
compiler = shutil . which ( default_compiler )
for directory in [ ' compiler-first ' , ' compiler-second ' ] :
wrapper = write ( directory + ' /gcc ' , ' #!/bin/bash \n exec ' + shlex . quote ( gcc ) + ' " $@ " \n ' )
wrapper = write ( directory + ' / ' + default_compiler , ' #!/usr/ bin/env bash \n exec ' + shlex . quote ( compiler ) + ' " $@ " \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 ' ] } )
@ -199,7 +213,7 @@ esac
write ( ' configure.ac ' , original )
build ( )
write ( ' tools/chatgui/build/CMakeCache.txt ' , ' placeholder \n ' )
cmake = write ( ' bin/cmake ' , ' #!/bin/bash \n printf " %s \\ n " " $* " > gui-arguments \n exit 9 \n ' )
cmake = write ( ' bin/cmake ' , ' #!/usr/ bin/env bash \n printf " %s \\ n " " $* " > gui-arguments \n exit 9 \n ' )
cmake . chmod ( 0o755 )
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 )
@ -207,13 +221,13 @@ esac
run ( [ ' make ' , ' distclean ' ] )
output_dir = base / ' out '
output_dir . mkdir ( )
for args in [ [ ' ../configure ' ] , [ ' make ' , ' -j2 ' ] , [ ' make ' , ' -C ' , ' tests ' , ' clean-local ' ] ] :
for args in [ [ ' ../configure ' ] , [ make_tool , ' -j2 ' ] , [ make_tool , ' -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} \n exit= { 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 :
if args [ 0 ] == make_tool 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 ( ) )