project( 'sl', 'c', version: '0.999', meson_version: '>=1.1.0', default_options: [ 'warning_level=3', 'buildtype=release', 'b_ndebug=if-release' ], ) if ['minsize'].contains(get_option('buildtype')) add_project_arguments( '-DNDEBUG', language: 'c', ) endif sys = host_machine.system() cc = meson.get_compiler('c') hardened = false add_project_arguments( cc.get_supported_arguments( '-fdata-sections', '-ffunction-sections', '-fno-common', '-fno-math-errno', '-fno-trapping-math', ), language: 'c', ) add_project_link_arguments( cc.get_supported_link_arguments( '-Wl,--gc-sections', ), language: 'c', ) if get_option('hardened').enabled() and sys != 'dos' and sys != 'macos' and sys != 'sunos' hardened = true add_project_arguments( (sys == 'macos' ? [] : [ '-U_FORTIFY_SOURCE', '-D_FORTIFY_SOURCE=3', ]) + cc.get_supported_arguments( '-fstack-protector-strong', '-fstack-clash-protection', '-fno-strict-overflow', '-fno-delete-null-pointer-checks', ), language: 'c', ) add_project_link_arguments( cc.get_supported_link_arguments( '-Wl,-z,noexecstack', '-Wl,-z,relro', '-Wl,-z,now', '-Wl,--as-needed', '-Wl,--no-copy-dt-needed-entries', ), language: 'c', ) endif sl_c_args_w = cc.get_supported_arguments( '-Wformat=2', '-Werror=format-security', '-Werror=implicit', '-Werror=incompatible-pointer-types', '-Werror=int-conversion', '-Werror=odr', '-Werror=strict-aliasing', ) sl_c_args = sl_c_args_w + cc.get_supported_arguments( #'-Wcast-align=strict', #'-Wconversion', #'-Wfloat-equal', #'-Wsign-conversion', '-Waggregate-return', '-Walloca', '-Wimplicit-fallthrough', '-Wint-to-pointer-cast', '-Wmissing-declarations', '-Wmissing-noreturn', '-Wmissing-prototypes', '-Wpedantic', '-Wpointer-arith', '-Wpointer-to-int-cast', '-Wredundant-decls', '-Wsequence-point', '-Wshadow', '-Wstrict-prototypes', '-Wundef', '-Wunused-but-set-parameter', '-Wunused-macros', '-Wunused-parameter', '-Wwrite-strings', ) san = get_option('b_sanitize') if san == 'address,undefined' or san.contains('undefined') sl_c_args += ['-DUBSAN'] endif src_dir = meson.current_source_dir() src_sl = [ '3rd/fn.c', '3rd/iswprint.c', '3rd/tbl.c', '3rd/wcwidth.c', 'src/bitvector.c', 'src/builtins.c', 'src/compress.c', 'src/cvalues.c', 'src/equal.c', 'src/sl.c', 'src/slmain.c', 'src/slmath.c', 'src/hashing.c', 'src/htable.c', 'src/io.c', 'src/ios.c', 'src/opcodes.c', 'src/operators.c', 'src/print.c', 'src/ptrhash.c', 'src/random.c', 'src/read.c', 'src/str.c', 'src/table.c', 'src/types.c', ] loop = get_option('loop') if loop == 'funcs' src_sl += ['src/vm_funcs.c'] ops = custom_target( 'ops', capture: true, input: [ 'src/vm_jmptbl.c', ], output: [ 'vm_funcs.h', ], command: [ 'tools/ops2funcs.sh', '@INPUT@', ], ) else src_sl += ['src/vm_jmptbl.c'] if loop == 'jmptbl' sl_c_args += ['-DCOMPUTED_GOTO'] ops = custom_target( 'ops', capture: true, input: [ 'src/vm_jmptbl.c', ], output: [ 'vm_jmptbl.h', ], command: [ 'tools/ops2jmptbl.sh', '@INPUT@', ], ) else ops = [] endif endif extras = [] if get_option('xxhash').enabled() libxxhash = dependency('libxxhash', method: 'pkg-config', required: true) sl_c_args += [ '-DUSE_XXHASH', ] extras += [libxxhash] else src_sl += ['3rd/spooky.c'] endif sl_c_args += cc.get_supported_arguments( '-fstrict-flex-arrays=3', '-Wbidi-chars=any', '-Wduplicated-branches', '-Wduplicated-cond', '-Werror=lto-type-mismatch', '-Wformat-overflow=2', '-Wformat-pedantic', '-Wformat-truncation=2', '-Wformat-type-confusion', '-Wgcc-compat', '-Wimplicit-int-float-conversion', '-Wlogical-op', '-Wmissing-variable-declarations', '-Wmultistatement-macros', '-Wunused-const-variable', '-Wunused-const-variable=2', '-Wsuggest-attribute=const', '-Wsuggest-attribute=noreturn', '-Wtrailing-whitespace=any', '-Wtrampolines', '-Wzero-as-null-pointer-constant', ) src_common = [ 'src/nan.c', ] inc = [include_directories('src', '3rd', '3rd/mp', '3rd/utf', '3rd/brieflz', '3rd/pcg-c')] bs = build_machine.system() if bs != 'dos' and bs != 'windows' bs = 'posix' endif binc = inc + [include_directories('src'/bs)] cpu = host_machine.cpu_family() pic = true bcc_can = false bcc_in = false if sys == 'macos' platform = sys pic = false sl_exe_name = 'sl.code.bin' add_languages( 'cpp', native: false, required: true, ) cpp = meson.get_compiler('cpp') # don't expect NewPtr-returned pointers to be aligned by 8 bytes # hence just use dlmalloc, which is also a much faster way to go about it # this is only needed on m68k but is less cumbersome to always include src_common += [ 'src/mem.c', ] extras += [ cpp.find_library('RetroConsole', required: true), cpp.find_library('retrocrt', required: true), ] src_sl += ['src/process_null.c'] elif sys == 'dos' platform = sys pic = false sl_exe_name = 'sl.exe' src_sl += ['src/process_null.c'] elif sys == 'windows' platform = sys sl_exe_name = 'sl' bcc_can = true src_sl += ['src/process_null.c'] else platform = 'posix' sl_exe_name = 'sl' bcc_can = true src_sl += ['src/process.c'] if sys == 'haiku' extras += [ cc.find_library('network', required: true) ] elif sys == 'sunos' extras += [ cc.find_library('socket', required: true) ] bcc_can = false endif endif inc += [include_directories('src'/platform)] src_sl += ['src'/platform/'sys.c'] if platform == 'posix' src_sl += [ 'src'/platform/'parsetime.c', 'src'/platform/'sys_chdir.c', ] endif if not ['x86_64', 'x86', 'aarch64', 'riscv64'].contains(cpu) bcc_can = false endif if get_option('bcc').enabled() and not bcc_can error('builtin-in C compiler not supported on this OS and/or CPU') elif get_option('bcc').disabled() or not bcc_can bcc = [] src_sl += ['src/bcc_null.c'] add_project_link_arguments( cc.get_supported_link_arguments('-Wl,-z,nodlopen'), language: 'c', ) else if cpu == 'x86_64' bcc_src_arch = [ '3rd/tinycc/i386-asm.c', '3rd/tinycc/x86_64-gen.c', '3rd/tinycc/x86_64-link.c', ] elif cpu == 'x86' bcc_src_arch = [ '3rd/tinycc/i386-asm.c', '3rd/tinycc/i386-gen.c', '3rd/tinycc/i386-link.c', ] elif cpu == 'arm' bcc_src_arch = [ '3rd/tinycc/arm-asm.c', '3rd/tinycc/arm-gen.c', '3rd/tinycc/arm-link.c', ] elif cpu == 'aarch64' bcc_src_arch = [ '3rd/tinycc/arm64-asm.c', '3rd/tinycc/arm64-gen.c', '3rd/tinycc/arm64-link.c', ] elif cpu == 'riscv64' bcc_src_arch = [ '3rd/tinycc/riscv64-asm.c', '3rd/tinycc/riscv64-gen.c', '3rd/tinycc/riscv64-link.c', ] else error('bcc enabled with unsupported cpu?') endif bcc_in = true if sys == 'darwin' bcc_src_sys = [ '3rd/tinycc/tccmacho.c', ] elif sys == 'windows' bcc_src_sys = [ '3rd/tinycc/tccpe.c', ] else bcc_src_sys = [] endif bcc_conftest = executable( 'conftest', c_args: sl_c_args + ['-Wno-undef'], sources: [ '3rd/tinycc/conftest.c', ], native: true, ) bcc_c2str = executable( 'c2str', c_args: sl_c_args + ['-Wno-undef', '-DC2STR'], sources: [ '3rd/tinycc/conftest.c', ], native: true, ) ccmd = cc.cmd_array() if ccmd.length() <= 1 or not ccmd[0].contains('ccache') cc0 = ccmd[0] else cc0 = ccmd[1] endif bcc_config = custom_target( 'bcc_config', output: [ 'config.h', ], command: [ src_dir/'3rd'/'tinycc'/'configure', '--cc='+cc0, '--config-pie', '--config-predefs=yes', '--enable-static', '--cpu=' + cpu, ] + (sys == 'windows' ? [ '--targetos=WIN32', '--enable-cross', '--cross-prefix="dummy"', ] : [ '--config-musl', # FIXME(sigrid): strings on a test executable to find out '--with-selinux', ] ), depends: [ bcc_conftest, ], ) bcc_predefs = custom_target( 'bcc_predefs', input: [ src_dir/'3rd'/'tinycc'/'include'/'tccdefs.h', ], output: [ 'tccdefs_.h', ], command: [ bcc_c2str, '@INPUT@', '@OUTPUT@', ], depends: [ bcc_c2str, ], ) tcc_c_flags = sl_c_args_w + cc.get_supported_arguments( '-Wno-format-nonliteral', '-Wno-implicit-fallthrough', '-Wno-missing-field-initializers', '-Wno-old-style-declaration', '-Wno-overlength-strings', '-Wno-pedantic', '-Wno-sign-compare', '-Wno-type-limits', '-Wno-unterminated-string-initialization', '-Wno-unused-parameter', '-Wno-unused-result', ) + ['-DTCC_LIBTCC1=""'] bcc = static_library( 'tcc', c_args: tcc_c_flags + ['-DONE_SOURCE=0'], sources: [ bcc_config, bcc_predefs, '3rd/tinycc/libtcc.c', '3rd/tinycc/tccasm.c', '3rd/tinycc/tccdbg.c', '3rd/tinycc/tccelf.c', '3rd/tinycc/tccgen.c', '3rd/tinycc/tccpp.c', '3rd/tinycc/tccrun.c', ] + bcc_src_arch + bcc_src_sys, include_directories: ['3rd/tinycc'], dependencies: [ dependency('threads'), cc.find_library('dl', required: false), ], pic: pic, ) src_sl += [bcc_config, 'src/bcc_tcc.c'] inc += ['3rd/tinycc'] endif common = static_library( 'common', c_args: sl_c_args, sources: src_common, include_directories: inc, pic: pic, ) math = cc.find_library('m', required: false) rt = cc.find_library('rt', required: false) brieflz = static_library( 'brieflz', c_args: sl_c_args, sources: [ '3rd/brieflz/brieflz.c', '3rd/brieflz/depacks.c', ], include_directories: inc, pic: pic, ) pcg = static_library( 'pcg', c_args: sl_c_args, sources: [ '3rd/pcg-c/pcg-advance-16.c', '3rd/pcg-c/pcg-advance-32.c', '3rd/pcg-c/pcg-advance-64.c', '3rd/pcg-c/pcg-advance-8.c', '3rd/pcg-c/pcg-output-64.c', '3rd/pcg-c/pcg-output-32.c', '3rd/pcg-c/pcg-rngs-64.c', '3rd/pcg-c/pcg32x2.c', ], include_directories: inc, pic: pic, ) lzpack = executable( 'lzpack', c_args: sl_c_args, sources: [ '3rd/brieflz/brieflz.c', '3rd/brieflz/depacks.c', 'tools/lzpack.c', ], include_directories: binc, native: true, ) boot = custom_target( 'boot', capture: true, input: [ 'boot/sl.boot', ], output: [ 'sl.boot.h', ], command: [ 'tools/boot2h.sh', lzpack, '@INPUT@', ], ) builtins = custom_target( 'builtins', capture: true, input: [ src_sl, ], output: [ 'builtin_fns.h', ], command: [ 'tools/builtins2h.sh', '@INPUT@', ], ) mp = static_library( 'mp', c_args: sl_c_args, sources: [ '3rd/mp/betomp.c', '3rd/mp/mpadd.c', '3rd/mp/mpaux.c', '3rd/mp/mpcmp.c', '3rd/mp/mpdigdiv.c', '3rd/mp/mpdiv.c', '3rd/mp/mpexp.c', '3rd/mp/mpextendedgcd.c', '3rd/mp/mpfmt.c', '3rd/mp/mpinvert.c', '3rd/mp/mpleft.c', '3rd/mp/mplogic.c', '3rd/mp/mpmod.c', '3rd/mp/mpmodop.c', '3rd/mp/mpmul.c', '3rd/mp/mpnrand.c', '3rd/mp/mprand.c', '3rd/mp/mpright.c', '3rd/mp/mpsub.c', '3rd/mp/mptobe.c', '3rd/mp/mptober.c', '3rd/mp/mptod.c', '3rd/mp/mptoi.c', '3rd/mp/mptoui.c', '3rd/mp/mptouv.c', '3rd/mp/mptov.c', '3rd/mp/mpvecadd.c', '3rd/mp/mpveccmp.c', '3rd/mp/mpvecdigmuladd.c', '3rd/mp/mpvecsub.c', '3rd/mp/mpvectscmp.c', '3rd/mp/strtomp.c', '3rd/mp/u16.c', ], include_directories: inc, pic: pic, ) utf = static_library( 'utf', c_args: sl_c_args, sources: [ '3rd/utf/rune.c', '3rd/utf/runebreak.c', '3rd/utf/runeistype.c', '3rd/utf/runenorm.c', '3rd/utf/runetotype.c', '3rd/utf/utfnlen.c', ], include_directories: inc, pic: pic, ) sl = executable( sl_exe_name, c_args: sl_c_args, sources: [ src_sl, boot, builtins, ops, ], dependencies: [ math, rt, ] + extras, include_directories: inc, link_with: [ brieflz, common, mp, pcg, utf, bcc, ], pie: pic, install: true, ) if platform == 'macos' fs = import('fs') makepef = find_program( 'MakePEF', native: false, required: true, ) rez = find_program( 'Rez', native: false, required: true, ) rincludes = fs.parent(fs.parent(rez.full_path()))/'RIncludes' if cpu == 'm68k' template = 'm68k.r' rez_data = '--copy' rezflags = [] sl_code = sl else assert(cpu == 'ppc', 'unexpected cpu setting') template = 'ppc.r' rez_data = '--data' rezflags = ['-DTARGET_API_MAC_CARBON=0'] sl_code = custom_target( 'sl.pef', input: [ sl ], output: [ 'sl.pef', ], command: [ makepef, '@INPUT@', '-o', '@OUTPUT@', ], ) endif template = meson.global_source_root()/'src/macos'/template custom_target( 'sl.bin', input: [ sl_code, template, ], output: [ 'sl.bin', 'sl.dsk', ], command: [ rez, '-I' + rincludes, ] + rezflags + [ template, '-DCFRAG_NAME="sl"', '-o', 'sl.bin', '-t', 'APPL', '-c', '????', rez_data, sl_code, '--cc', 'sl.dsk', ], build_by_default: true, ) endif test3rd = get_option('test3rd') if test3rd.enabled() mptest = executable( 'mptest', c_args: sl_c_args, sources: [ '3rd/mp/test.c', ], include_directories: inc, link_with: [ common, mp, ], ) test('mp', mptest) mptest2 = executable( 'mptest2', c_args: sl_c_args, sources: [ '3rd/mp/test/convtest.c', '3rd/mp/test/gen.tab.c', '3rd/mp/test/ld.c', '3rd/mp/test/main.c', ], include_directories: inc, link_with: [ common, mp, ], ) test('mp2', mptest2, timeout: -1) endif tests_dir = src_dir/'test' # common sl args to run tests with # start with as small heap as possible # this will trigger more GC calls which might help catch certain bugs early ta = ['-H', '0'] test('100x100.sl', sl, args: ta+['100x100.sl'], workdir: tests_dir) test('argv.sl', sl, args: ta+['argv.sl'], workdir: tests_dir) test('exit0.sl', sl, args: ta+['exit0.sl'], workdir: tests_dir) test('exit1.sl', sl, args: ta+['exit1.sl'], workdir: tests_dir, should_fail: true) test('bench.sl', sl, args: ta+['bench.sl'], workdir: tests_dir, timeout: -1) test('hash.sl', sl, args: ta+['hash.sl'], workdir: tests_dir) test('numbers.sl', sl, args: ta+['numbers.sl'], workdir: tests_dir) test('mp.sl', sl, args: ta+['mp.sl'], workdir: tests_dir, timeout: -1) test('perf.sl', sl, args: ta+['perf.sl'], workdir: tests_dir, timeout: -1) test('tme.sl', sl, args: ta+['tme.sl'], workdir: tests_dir, timeout: -1) test('torture.sl', sl, args: ta+['torture.sl'], workdir: tests_dir, timeout: -1) test('torus.sl', sl, args: ta+['torus.sl'], workdir: tests_dir) test('unit.sl', sl, args: ta+['unit.sl'], workdir: tests_dir, timeout: -1) test('io.sl', sl, args: ta+['io.sl'], workdir: tests_dir) test('regexp.sl', sl, args: ta+['../lib/regexp/tests.sl'], workdir: tests_dir) test('struct.sl', sl, args: ta+['struct.sl'], workdir: tests_dir) test('crash.sl', sl, args: ta+['crash.sl'], workdir: tests_dir) test('read.sl', sl, args: ta+['read.sl'], workdir: tests_dir) test('assert-fail-exit.sl', sl, args: ta+['assert-fail-exit.sl'], workdir: tests_dir, should_fail: true) test('docvet.sl', sl, args: ta+['docvet.sl'], workdir: src_dir/'tools', is_parallel: false, priority: -9999, verbose: true) bootstrap = src_dir/'tools'/'bootstrap.sh' run_target( 'bootstrap', command: [bootstrap, sl.full_path()]+ta, depends: sl, ) if not meson.is_cross_build() bootstrap = find_program( bootstrap, native: true, required: true, ) test('bootstrap', bootstrap, args: [sl.full_path()]+ta, workdir: src_dir, is_parallel: false, priority: 9999, timeout: -1) endif summary( { 'xxhash': get_option('xxhash'), 'Hardening': hardened, 'Built-in C compiler': bcc_in, }, section: 'Configuration', )