Building JaiScript into a Host
JaiScript has no dependencies and builds with any C++20 compiler; MSVC 19.51 (Visual Studio 18), Clang 20 and GCC 16 are the verified ones. This page gives each way into a project in full: CMake, Visual Studio's MSBuild, Xcode, and an installed package. The quickstart has the short version of each.
What every build needs
| Setting | Value | Why |
|---|---|---|
| Sources | source/jaiscript.cpp, or every .cpp under source/implementation/ (never both) | The library is compiled with your program; see One file or per file. |
| Include path | include/ | Every header is included as <jaiscript/...>. |
| Language standard | C++20 | The headers are written in C++20. |
| Exceptions and RTTI | on (every compiler's default) | Errors reach the host as exceptions, and bound classes are matched by type. |
| MSVC | /bigobj; /EHsc and /permissive- (the defaults of a new Visual Studio project) | The binder's templates overflow the default object-file section limit, in your files as well as the library's. |
| GCC and Clang | -ffp-contract=off | Keeps the compiler from fusing a*b+c into one instruction, so floating-point results match on every platform and both backends. |
| MinGW GCC | -Wa,-mbig-obj | The COFF counterpart of /bigobj. |
| Windows libraries | ws2_32 and bcrypt | The debugger's socket transport, and the system random source behind crypto::. MSVC and clang targeting MSVC pick them up from the source; a GNU linker needs them named. |
| Optional define | JAISCRIPT_ENABLE_DEBUGGER | Compiles in the debugger's socket transport for VS Code attach (Debugging). Leave it out for shipping and mobile builds. Define it for every file or for none. |
| Optional define | JAISCRIPT_INTERPRETER | Adds the tree-walking interpreter, which only a program that checks the backends against each other needs (The interpreter). Define it for every file or for none. |
Compile JaiScript's files with the same settings as yours: the same runtime library, the same NDEBUG and the same defines. Setting them once at the project level does that. NDEBUG and the diagnostic builds (JAISCRIPT_DIAG_ATOMIC_RC, JAISCRIPT_DIAG_XTHREAD_RC, JAISCRIPT_VM_COVERAGE) change class layouts, so a mismatch in any of them fails to link: MSVC reports LNK2038: mismatch detected for 'jaiscript_build_config', and GCC and Clang an undefined jai::detail::build_config_debug1_atomic_rc0_xthread_rc0_coverage0 (or the like) naming the configuration the library was not built with.
One file or per file
source/jaiscript.cpp includes every implementation file, so adding that one file adds the whole library. It is the simplest route and the right default when you use JaiScript without changing it: the library compiles as one unit, once, and stays built.
The files under source/implementation/ are the same library split into about forty units. Add them all instead (a wildcard does it) when you work on JaiScript itself or want the library to compile in parallel: an edit to one implementation file rebuilds only that file. Turn on parallel compilation (MSBuild's /MP, Ninja and Xcode by default) to get the benefit. Adding both sets defines everything twice.
CMake
The library is one CMake target, JaiScript::jaiscript. Linking it brings everything in the table above. JaiScript::jaiscript_interpreter is the same library with the interpreter added (The interpreter).
As a subproject. Put the JaiScript folder (the one holding its top-level CMakeLists.txt) inside your project; a copy elsewhere takes a binary directory as well, add_subdirectory(path/to/JaiScript jaiscript). As a subproject only the library builds (the runner, tests, examples and language server stay off) and its MSVC runtime follows yours. A complete host:
cmake_minimum_required(VERSION 3.20)
project(my_game CXX)
add_subdirectory(JaiScript)
add_executable(my_game main.cpp)
target_link_libraries(my_game PRIVATE JaiScript::jaiscript)cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build
build/my_gameWith FetchContent. The same target, fetched at configure time instead of copied in:
include(FetchContent)
FetchContent_Declare(jaiscript GIT_REPOSITORY https://github.com/Devacor/jaiscript.git GIT_TAG v0.5.0)
FetchContent_MakeAvailable(jaiscript)
target_link_libraries(my_game PRIVATE JaiScript::jaiscript)Options set before the add_subdirectory or FetchContent_MakeAvailable (or with -D) turn the tools back on: JAISCRIPT_BUILD_RUNNER, JAISCRIPT_BUILD_TESTS, JAISCRIPT_BUILD_EXAMPLES, JAISCRIPT_BUILD_LSP, and JAISCRIPT_INTERPRETER for the jaiscript_interpreter target. JAISCRIPT_ENABLE_DEBUGGER=OFF compiles the debugger's socket code out.
Visual Studio (MSBuild)
In the IDE. Add JaiScript\source\jaiscript.cpp to the project (Add → Existing Item). Then open the project's Properties, choose All Configurations and your platform, and under C/C++:
- General → Additional Include Directories: add the
JaiScript\includefolder, for example$(SolutionDir)JaiScript\include. - Language → C++ Language Standard: ISO C++20 Standard (
/std:c++20). Leave Conformance mode at Yes (/permissive-). - Command Line → Additional Options:
/bigobj. - Code Generation → Enable C++ Exceptions: Yes (
/EHsc), the default. - Preprocessor → Preprocessor Definitions: add
JAISCRIPT_ENABLE_DEBUGGERif you want VS Code attach.
For the per-file route, add the .cpp files under JaiScript\source\implementation instead of jaiscript.cpp, and set General → Multi-processor Compilation to Yes.
In a property sheet. The same settings fit in one file a project imports, which keeps them in source control and out of the project file. Save this beside your project as jaiscript.props:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<JaiScriptDir Condition="'$(JaiScriptDir)'==''">$(MSBuildThisFileDirectory)JaiScript\</JaiScriptDir>
<JaiScriptPerFile Condition="'$(JaiScriptPerFile)'==''">false</JaiScriptPerFile>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(JaiScriptDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp20</LanguageStandard>
<ConformanceMode>true</ConformanceMode>
<ExceptionHandling>Sync</ExceptionHandling>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<PreprocessorDefinitions>JAISCRIPT_ENABLE_DEBUGGER;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup Condition="'$(JaiScriptPerFile)'!='true'">
<ClCompile Include="$(JaiScriptDir)source\jaiscript.cpp" />
</ItemGroup>
<ItemGroup Condition="'$(JaiScriptPerFile)'=='true'">
<ClCompile Include="$(JaiScriptDir)source\implementation\**\*.cpp" />
</ItemGroup>
</Project>Import it in the .vcxproj after Microsoft.Cpp.props:
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="jaiscript.props" />A Directory.Build.props can carry the ItemDefinitionGroup for every project in a folder; keep the ClCompile items in the one project that should compile the library. msbuild my_game.vcxproj /p:Configuration=Release /p:Platform=x64 builds it from a developer prompt, and /p:JaiScriptPerFile=true /p:MultiProcessorCompilation=true selects the per-file route.
Xcode
Add JaiScript/source/jaiscript.cpp to your target (it appears under Build Phases → Compile Sources), or the files under source/implementation/ for the per-file route. Then set, in the target's Build Settings (the setting name in parentheses is what an .xcconfig uses):
| Build setting | Value |
|---|---|
Header Search Paths (HEADER_SEARCH_PATHS) | $(SRCROOT)/JaiScript/include |
C++ Language Dialect (CLANG_CXX_LANGUAGE_STANDARD) | C++20 (c++20) |
C++ Standard Library (CLANG_CXX_LIBRARY) | libc++ (the default) |
Enable C++ Exceptions (GCC_ENABLE_CPP_EXCEPTIONS) | Yes (the default) |
Enable C++ Runtime Types (GCC_ENABLE_CPP_RTTI) | Yes (the default) |
Other C++ Flags (OTHER_CPLUSPLUSFLAGS) | $(inherited) -ffp-contract=off |
Preprocessor Macros (GCC_PREPROCESSOR_DEFINITIONS) | Release: NDEBUG=1. Add JAISCRIPT_ENABLE_DEBUGGER=1 to a desktop development build for VS Code attach; leave it out on iOS. |
Xcode's Release configuration does not define NDEBUG by itself. Add it to every target that compiles JaiScript or includes its headers: left out of all of them, the library's internal assertions stay on and cost speed; left out of some, the link fails with the configuration mismatch above.
Debug and Release
- A Debug build checks the library's internal invariants with assertions and runs a few times slower than Release. Profile and measure Release builds.
- Release defines
NDEBUG. In CMake and in Visual Studio's Release configuration that is automatic; in Xcode add it (above). - MSVC whole-program optimization (
/GLwith/LTCG) applies to JaiScript like any other code: it speeds the VM up a few percent and makes every Release link much slower. A CMake build of the library alone turns it off withJAISCRIPT_LTCG=OFF. - Deeply recursive scripts need native stack. A host thread that runs scripts with deep recursion wants a larger stack than the platform default (the library's own test runner links 128 MB on Windows); the engine raises a catchable error instead of crashing when the stack runs low.
An installed package
Build and install the library once per configuration (Debug and Release share a prefix; the Debug library is jaiscriptd), then find it from CMake:
cmake -S JaiScript -B build-jai -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/jaiscript -DJAISCRIPT_LTCG=OFF -DJAISCRIPT_BUILD_TESTS=OFF -DJAISCRIPT_BUILD_EXAMPLES=OFF -DJAISCRIPT_BUILD_RUNNER=OFF -DJAISCRIPT_BUILD_SPIDER=OFF -DJAISCRIPT_BUILD_LSP=OFF
cmake --build build-jai
cmake --install build-jaicmake_minimum_required(VERSION 3.20)
project(my_game CXX)
find_package(JaiScript 0.5 CONFIG REQUIRED) # configure with -DCMAKE_PREFIX_PATH=/opt/jaiscript
add_executable(my_game main.cpp)
target_link_libraries(my_game PRIVATE JaiScript::jaiscript)
set_property(TARGET my_game PROPERTY MSVC_RUNTIME_LIBRARY "${JaiScript_MSVC_RUNTIME_LIBRARY}")The last line matters on MSVC. A standalone JaiScript build uses the static runtime (/MT, /MTd) unless you pass CMAKE_MSVC_RUNTIME_LIBRARY, while a CMake project defaults to the DLL runtime; the package records the runtime its library was built with in JaiScript_MSVC_RUNTIME_LIBRARY, and without the line the link fails with LNK2038: mismatch detected for 'RuntimeLibrary'. Linking a prebuilt library from another build system, compile your files with the definitions it was built with: its runtime, NDEBUG for a Release library, and JAISCRIPT_ENABLE_DEBUGGER unless it was configured off.
The interpreter, for checking parity
The tree-walking interpreter (Backends) is the reference the VM is checked against, and nothing a host runs needs it, so the library leaves it out: source/implementation/ and source/jaiscript.cpp compile the VM alone, and in such a build set_backend(jai::backend_type::interpreter) throws set_backend(interpreter) is unsupported: this build has no interpreter (link jaiscript_interpreter). A program that compares the backends adds it:
- CMake: link
JaiScript::jaiscript_interpreterinstead ofJaiScript::jaiscript. The optionJAISCRIPT_INTERPRETERdefines the target; it is on in a standalone build, whose test suite, fuzzer, runner andjai_spiderlink it, and off in a subproject. The two libraries share every compiled file but the interpreter's. - Other build systems: define
JAISCRIPT_INTERPRETERand add the files undersource/interpreter/beside the per-file route's, or define it forsource/jaiscript.cpp, which then includes them.
The runner, for trying scripts
A standalone build of JaiScript builds the command-line runner as the target jaiscript_runner; its binary is jaiscript:
cmake -S JaiScript -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build --target jaiscript_runner
build/bin/jaiscript hello.jai # run a file; no argument starts a REPL
build/bin/jaiscript --backend=interp --warnings hello.jaiThe runner registers the whole standard library plus file and console functions, runs on the VM like every engine (the interpreter is one call away), and lifts the execution budget (--budget=N puts one back).
The headers a host includes
#include <jaiscript/core/engine.hpp> // engine, script_value, the error types
#include <jaiscript/core/dynamic_binder.hpp> // dynamic_binder, convert_script_function
#include <jaiscript/stdlib/stdlib.hpp> // jai::stdlib::register_all
#include <jaiscript/serialization/convenience.hpp> // jai::to_json / from_json / to_binary / from_binary
#include <jaiscript/core/engine_time.hpp> // eng->time(): the clock scripts read
#include <jaiscript/debug/connector.hpp> // jai::debug::listen (VS Code attach)<jaiscript/jaiscript.hpp> pulls the core, the binder and the archives in one include (not the standard library).