project( 'quickjs-ng', 'c', version: '0.11.0', default_options: [ 'c_std=gnu11,c11', 'warning_level=3', 'default_library=static', ], license: 'MIT', license_files: 'LICENSE', meson_version: '>=1.3.0', ) host_system = host_machine.system() cc = meson.get_compiler('c') qjs_gcc_warning_args = [ '-Wno-unsafe-buffer-usage', '-Wno-sign-conversion', '-Wno-nonportable-system-include-path', '-Wno-implicit-int-conversion', '-Wno-shorten-64-to-32', '-Wno-reserved-macro-identifier', '-Wno-reserved-identifier', '-Wdeprecated-declarations', '-Wno-implicit-fallthrough', '-Wno-sign-compare', '-Wno-missing-field-initializers', '-Wno-unused-parameter', '-Wno-unused-but-set-variable', '-Wno-array-bounds', '-Wno-format-truncation', ] qjs_gcc_args = [ '-funsigned-char', ] if host_system == 'darwin' # https://github.com/quickjs-ng/quickjs/issues/453 qjs_gcc_warning_args += '-Wno-maybe-uninitialized' endif # https://github.com/microsoft/cpp-docs/tree/main/docs/error-messages/compiler-warnings qjs_msvc_warning_args = [ '/wd4018', # -Wno-sign-conversion '/wd4061', # -Wno-implicit-fallthrough '/wd4100', # -Wno-unused-parameter '/wd4200', # -Wno-zero-length-array '/wd4242', # -Wno-shorten-64-to-32 '/wd4244', # -Wno-shorten-64-to-32 '/wd4245', # -Wno-sign-compare '/wd4267', # -Wno-shorten-64-to-32 '/wd4388', # -Wno-sign-compare '/wd4389', # -Wno-sign-compare '/wd4710', # Function not inlined '/wd4711', # Function was inlined '/wd4820', # Padding added after construct '/wd4996', # -Wdeprecated-declarations '/wd5045', # Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified ] qjs_msvc_args = [ '/experimental:c11atomics', '/J', # -funsigned-char ] if cc.get_argument_syntax() == 'msvc' add_project_arguments( cc.get_supported_arguments(qjs_msvc_warning_args), cc.get_id().contains('clang') ? cc.get_supported_arguments(qjs_gcc_warning_args) : [], qjs_msvc_args, language: 'c', ) else add_project_arguments( cc.get_supported_arguments(qjs_gcc_warning_args), qjs_gcc_args, language: 'c', ) endif if host_system == 'windows' # Set a 8MB default stack size on Windows. # It defaults to 1MB on MSVC, which is the same as our current JS stack size, # so it will overflow and crash otherwise. # On MinGW it defaults to 2MB. stack_size = 8 * 1024 * 1024 if cc.get_argument_syntax() == 'msvc' add_project_link_arguments(f'/STACK:@stack_size@', language: 'c') else add_project_link_arguments(f'-Wl,--stack,@stack_size@', language: 'c') endif endif if meson.is_cross_build() native_cc = meson.get_compiler('c', native: true) if native_cc.get_argument_syntax() == 'msvc' # https://github.com/microsoft/cpp-docs/tree/main/docs/error-messages/compiler-warnings add_project_arguments( native_cc.get_supported_arguments(qjs_msvc_warning_args), native_cc.get_id().contains('clang') ? native_cc.get_supported_arguments(qjs_gcc_warning_args) : [], qjs_msvc_args, language: 'c', native: true, ) else add_project_arguments( native_cc.get_supported_arguments(qjs_gcc_warning_args), qjs_gcc_args, language: 'c', native: true, ) endif endif if get_option('debug') add_project_arguments( cc.get_supported_arguments('-fno-omit-frame-pointer'), language: 'c', ) endif qjs_sys_deps = [] m_dep = cc.find_library('m', required: false) qjs_sys_deps += m_dep qjs_sys_deps += dependency('threads', required: false) qjs_sys_deps += dependency('dl', required: false) qjs_srcs = files( 'cutils.c', 'dtoa.c', 'libregexp.c', 'libunicode.c', 'quickjs.c', ) qjs_hdrs = files( 'quickjs.h', ) qjs_libc = get_option('libc') qjs_libc_srcs = files('quickjs-libc.c') qjs_libc_hdrs = files('quickjs-libc.h') if qjs_libc qjs_hdrs += qjs_libc_hdrs endif qjs_parser = get_option('parser') qjs_c_args = ['-D_GNU_SOURCE'] if host_system == 'windows' qjs_c_args += ['-DWIN32_LEAN_AND_MEAN', '-D_WIN32_WINNT=0x0601'] endif if not qjs_parser qjs_c_args += ['-DQJS_DISABLE_PARSER'] endif qjs_libc_lib = static_library( 'quickjs-libc', qjs_libc_srcs, dependencies: qjs_sys_deps, c_args: qjs_c_args, gnu_symbol_visibility: 'hidden', ) qjs_lib = library( 'qjs', qjs_srcs, # export public headers generator( find_program('cp', 'xcopy'), output: ['@PLAINNAME@'], arguments: ['@INPUT@', '@OUTPUT@'], ).process(qjs_hdrs), dependencies: qjs_sys_deps, link_whole: qjs_libc ? qjs_libc_lib : [], c_args: qjs_c_args, gnu_symbol_visibility: 'hidden', install: true, version: meson.project_version(), ) qjs_export_variables = [ f'have_parser=@qjs_parser@' ] qjs_dep = declare_dependency( link_with: qjs_lib, dependencies: qjs_sys_deps, include_directories: qjs_lib.private_dir_include(), variables: qjs_export_variables, ) if host_system == 'emscripten' qjs_wasm_export_name = 'getQuickJs' executable( 'qjs_wasm', qjs_srcs, link_args: cc.get_supported_link_arguments( # in emscripten 3.x, this will be set to 16k which is too small for quickjs. '-sSTACK_SIZE=@0@'.format(2 * 1024 * 1024), # let it be 2m = 2 * 1024 * 1024, otherwise, stack overflow may be occured at bootstrap '-sNO_INVOKE_RUN', '-sNO_EXIT_RUNTIME', '-sMODULARIZE', # do not mess the global '-sEXPORT_ES6', # export js file to morden es module '-sEXPORT_NAME=@0@'.format(qjs_wasm_export_name), # give a name '-sTEXTDECODER=1', # it will be 2 if we use -Oz, and that will cause js -> c string convertion fail '-sNO_DEFAULT_TO_CXX', # this project is pure c project, no need for c plus plus handle '-sEXPORTED_RUNTIME_METHODS=ccall,cwrap', ), dependencies: m_dep, c_args: qjs_c_args, ) endif install_headers(qjs_hdrs, subdir: 'quickjs-ng') if not meson.is_subproject() docdir = get_option('docdir') datadir = get_option('datadir') if docdir == '' docdir = datadir / 'doc' / meson.project_name() endif install_data( 'LICENSE', install_dir: docdir, install_tag: 'doc', ) install_subdir( 'examples', install_dir: docdir, strip_directory: false, install_tag: 'doc', ) endif meson.override_dependency('quickjs-ng', qjs_dep) import('pkgconfig').generate( qjs_lib, subdirs: 'quickjs-ng', name: 'quickjs-ng', description: 'QuickJS, the Next Generation: a mighty JavaScript engine', url: 'https://github.com/quickjs-ng/quickjs', version: meson.project_version(), variables: qjs_export_variables, ) if not qjs_parser subdir_done() endif # QuickJS bytecode compiler qjsc_srcs = files( 'qjsc.c', ) qjsc_exe = executable( 'qjsc', qjsc_srcs, c_args: qjs_c_args, link_with: qjs_libc ? [] : qjs_libc_lib, dependencies: qjs_dep, install: true, ) mimalloc_dep = [] mimalloc_sys_dep = dependency('mimalloc', required: get_option('cli_mimalloc')) if mimalloc_sys_dep.found() mimalloc_dep = declare_dependency( dependencies: mimalloc_sys_dep, compile_args: '-DQJS_USE_MIMALLOC', ) endif # QuickJS CLI qjs_exe_srcs = files( 'gen/repl.c', 'gen/standalone.c', 'qjs.c', ) qjs_exe = executable( 'qjs', qjs_exe_srcs, c_args: qjs_c_args, link_with: qjs_libc ? [] : qjs_libc_lib, dependencies: [qjs_dep, mimalloc_dep], export_dynamic: true, install: true, ) if meson.is_cross_build() mimalloc_native_dep = [] mimalloc_sys_native_dep = dependency('mimalloc', required: get_option('cli_mimalloc'), native: true) if mimalloc_sys_dep.found() mimalloc_native_dep = declare_dependency( dependencies: mimalloc_sys_native_dep, compile_args: '-DQJS_USE_MIMALLOC', ) endif qjs_sys_native_deps = [ native_cc.find_library('m', required: false), dependency('threads', required: false, native: true), dependency('dl', required: false, native: true), ] qjs_native_lib = static_library( 'qjs_native', qjs_srcs, qjs_libc_srcs, dependencies: qjs_sys_native_deps, c_args: qjs_c_args, gnu_symbol_visibility: 'hidden', build_by_default: false, native: true, install: false, ) meson.override_find_program( 'qjsc', executable( 'qjsc_native', qjsc_srcs, c_args: qjs_c_args, link_with: qjs_native_lib, dependencies: qjs_sys_native_deps, build_by_default: false, native: true, install: false, ), ) meson.override_find_program( 'qjs', executable( 'qjs_native', qjs_exe_srcs, c_args: qjs_c_args, link_with: qjs_native_lib, dependencies: [qjs_sys_native_deps, mimalloc_native_dep], export_dynamic: true, build_by_default: false, native: true, install: false, ), ) else meson.override_find_program('qjsc', qjsc_exe) meson.override_find_program('qjs', qjs_exe) endif tests = get_option('tests').disable_auto_if(meson.is_subproject()) examples = get_option('examples').disable_auto_if(meson.is_subproject()) if tests.allowed() if host_system != 'emscripten' # Test262 runner run262_exe = executable( 'run-test262', 'run-test262.c', qjs_libc_srcs, c_args: qjs_c_args, dependencies: qjs_dep, ) test( 'test', run262_exe, args: ['-c', files('tests.conf')], workdir: meson.current_source_dir(), ) foreach bench : [ 'empty_loop', 'date_now', 'prop_read', 'prop_write', 'prop_create', 'prop_delete', 'array_read', 'array_write', 'array_prop_create', 'array_length_decr', 'array_hole_length_decr', 'array_push', 'array_pop', 'typed_array_read', 'typed_array_write', 'global_read', 'global_write', 'global_write_strict', 'local_destruct', 'global_destruct', 'global_destruct_strict', 'func_call', 'closure_var', 'int_arith', 'float_arith', 'map_set', 'map_delete', 'weak_map_set', 'weak_map_delete', 'array_for', 'array_for_in', 'array_for_of', 'math_min', 'object_null', 'regexp_ascii', 'regexp_utf16', 'string_build1', 'string_build2', 'string_slice1', 'string_slice2', 'string_slice3', 'sort_bench', 'int_to_string', 'int_toString', 'float_to_string', 'float_toString', 'float_toFixed', 'float_toPrecision', 'float_toExponential', 'string_to_int', 'string_to_float', 'bigint64_arith', 'bigint256_arith', ] benchmark( bench, qjs_exe, args: [files('tests/microbench.js'), bench], ) endforeach endif # API test test( 'api', executable( 'api-test', 'api-test.c', c_args: qjs_c_args, dependencies: qjs_dep, build_by_default: false, ), ) # Function.toString() test test( 'function_source', executable( 'function_source', 'gen/function_source.c', qjs_libc_srcs, c_args: qjs_c_args, dependencies: qjs_dep, build_by_default: false, ), ) endif # Unicode generator unicode_gen = executable( 'unicode_gen', 'cutils.c', 'unicode_gen.c', c_args: qjs_c_args, build_by_default: false, ) run_target( 'libunicode-table.h', command: [ unicode_gen, meson.current_source_dir() / 'unicode', files('libunicode-table.h'), ], ) # bytecode to c source code for builtin and examples alias_target('codegen', run_target( 'codegen_repl.c', command: [ qjsc_exe, '-ss', '-o', files('gen/repl.c'), '-m', '-n', 'repl.js', files('repl.js'), ], ), run_target( 'codegen_standalone.c', command: [ qjsc_exe, '-ss', '-o', files('gen/standalone.c'), '-m', '-n', 'standalone.js', files('standalone.js'), ], ), run_target( 'codegen_function_source.c', command: [ qjsc_exe, '-e', '-o', files('gen/function_source.c'), '-n', 'tests/function_source.js', files('tests/function_source.js'), ], ), run_target( 'codegen_hello.c', command: [ qjsc_exe, '-e', '-o', files('gen/hello.c'), '-n', 'examples/hello.js', files('examples/hello.js'), ], ), run_target( 'codegen_hello_module.c', command: [ qjsc_exe, '-e', '-o', files('gen/hello_module.c'), '-m', '-n', 'examples/hello_module.js', files('examples/hello_module.js'), ], ), run_target( 'codegen_test_fib.c', command: [ qjsc_exe, '-e', '-o', files('gen/test_fib.c'), '-m', '-n', 'examples/test_fib.js', files('examples/test_fib.js'), ], ), run_target( 'codegen_builtin-array-fromasync.h', command: [ qjsc_exe, '-C', '-ss', '-o', files('builtin-array-fromasync.h'), '-n', 'builtin-array-fromasync.js', files('builtin-array-fromasync.js'), ], ), ) if examples.allowed() executable( 'hello', 'gen/hello.c', qjs_libc_srcs, c_args: qjs_c_args, dependencies: qjs_dep, ) executable( 'hello_module', 'gen/hello_module.c', qjs_libc_srcs, c_args: qjs_c_args, dependencies: qjs_dep, ) subdir('examples') executable( 'test_fib', 'examples/fib.c', 'gen/test_fib.c', qjs_libc_srcs, c_args: qjs_c_args, dependencies: qjs_dep, export_dynamic: true, ) endif