project(
'quickjs-ng',
'c',
version: '0.12.1',
default_options: [
'c_std=gnu11,c11',
'warning_level=3',
],
license: 'MIT',
license_files: 'LICENSE',
meson_version: '>=1.3.0',
)
host_system = host_machine.system()
cc = meson.get_compiler('c')
add_project_arguments('-DQUICKJS_NG_BUILD', language: '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 = []
if host_system != 'sunos'
qjs_gcc_args += '-funsigned-char'
endif
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(
'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
add_project_arguments(
'-DQJS_BUILD_LIBC',
language: 'c'
)
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_lib = library(
'qjs',
qjs_srcs,
qjs_libc ? qjs_libc_srcs : [],
dependencies: qjs_sys_deps,
c_args: qjs_c_args,
c_shared_args: ['-DBUILDING_QJS_SHARED'],
gnu_symbol_visibility: 'hidden',
install: true,
version: meson.project_version(),
)
qjs_export_variables = [
f'have_parser=@qjs_parser@'
]
# This note is only of interest on Windows.
# Be conservative here: only pass -DUSING_QJS_SHARED iff default_library ==
# 'shared', thus qjs is guaranteed to be a DLL. Not passing -DUSING_QJS_SHARED if
# qjs is a DLL is not harmful, but passing the definition for DLL qjs is desired.
# Conversely, if -DUSING_QJS_SHARED is passed for static qjs, the consumers
# will not compile.
qjs_dep_args = get_option('default_library') == 'shared' ? ['-DUSING_QJS_SHARED'] : []
qjs_dep = declare_dependency(
compile_args: qjs_dep_args,
link_with: qjs_lib,
dependencies: qjs_sys_deps,
include_directories: include_directories('.'),
variables: qjs_export_variables,
)
if host_system != 'windows'
qjs_module_dep = declare_dependency(
compile_args: [qjs_dep_args, '-DQUICKJS_NG_MODULE_BUILD'],
include_directories: include_directories('.'),
)
else
qjs_module_dep = declare_dependency(
compile_args: [qjs_dep_args, '-DQUICKJS_NG_MODULE_BUILD'],
include_directories: include_directories('.'),
# Windows can't resolve symbols at runtime thus we explicitly link to qjs.
dependencies: qjs_dep,
)
endif
if qjs_libc
qjs_libc_dep = declare_dependency(
include_directories: include_directories('.'),
dependencies: qjs_dep,
)
else
qjs_libc_lib = static_library(
'quickjs-libc',
qjs_libc_srcs,
c_args: qjs_c_args,
dependencies: [qjs_sys_deps, qjs_dep],
gnu_symbol_visibility: 'hidden',
)
qjs_libc_dep = declare_dependency(
link_with: qjs_libc_lib,
# Pick up quickjs-libc.h.
include_directories: include_directories('.'),
)
endif
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,
# Export -DUSING_QJS_SHARED, if needed, in the pkgconfig file.
extra_cflags: qjs_dep_args,
)
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,
dependencies: [qjs_dep, qjs_libc_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,
dependencies: [qjs_dep, qjs_libc_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',
c_args: qjs_c_args,
dependencies: [qjs_dep, qjs_libc_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,
),
)
# LRE test
test(
'lre',
executable(
'lre-test',
'lre-test.c',
'libregexp.c',
'libunicode.c',
c_args: qjs_c_args,
build_by_default: false,
),
)
# Function.toString() test
test(
'function_source',
executable(
'function_source',
'gen/function_source.c',
c_args: qjs_c_args,
dependencies: [qjs_dep, qjs_libc_dep],
build_by_default: false,
),
)
endif
# Unicode generator
unicode_gen = executable(
'unicode_gen',
'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()
if host_system == 'windows' and get_option('default_library') != 'shared'
warning('Binary modules used with static qjs on Windows.')
warning(
'There might be runtime errors when the modules are used due to',
'two copies of qjs in memory.'
)
warning('Please use "-Ddefault_library=shared" if possible.')
endif
executable(
'hello',
'gen/hello.c',
c_args: qjs_c_args,
dependencies: [qjs_dep, qjs_libc_dep]
)
executable(
'hello_module',
'gen/hello_module.c',
c_args: qjs_c_args,
dependencies: [qjs_dep, qjs_libc_dep]
)
subdir('examples')
executable(
'test_fib',
'gen/test_fib.c',
c_args: qjs_c_args,
dependencies: [qjs_dep, qjs_libc_dep],
export_dynamic: true,
)
endif