diff --git a/.github/actions/openvic-sim-build/action.yml b/.github/actions/openvic-sim-build/action.yml index ab5262cb6..326efdd74 100644 --- a/.github/actions/openvic-sim-build/action.yml +++ b/.github/actions/openvic-sim-build/action.yml @@ -1,63 +1,63 @@ name: Build OpenVic-Simulation -description: Setup and Build OpenVic-Simulation with the provided options +description: Configure, build, and test OpenVic-Simulation with CMake inputs: identifier: - description: Identifier of this build + description: Identifier of this build (artifact name) required: true - target: - description: Target type to build for (template_release, template_debug, editor) - required: true - platform: - description: Platform to build for (windows, linux, macos) + preset: + description: CMake configure preset required: true - arch: - description: Architecture to build for (x86_32, x86_64, universal) + configuration: + description: Build configuration (Debug, Release, RelWithDebInfo) required: true - scons-flags: - description: Additional flags to send to SCons - default: 'build_ovsim_benchmarks=yes' - build-library: - description: Whether to build the library file + target: + description: Godot target flavor (template_debug, template_release, editor) + default: 'template_debug' + cmake-args: + description: Additional -D flags passed to the CMake configure step + default: '' + run-tests: + description: Whether to run the unit tests after building default: 'true' - cache-base-branch: - description: Branch to base the cache upon - default: 'master' - disable-cache: - description: Whether to disable the build cache - default: 'false' runs: using: composite steps: - - name: Setup build cache - uses: OpenVicProject/openvic-cache@master - if: ${{ inputs.disable-cache != 'true' }} - with: - cache-name: ${{ inputs.identifier }} - base-branch: ${{ inputs.cache-base-branch }} - continue-on-error: true - - - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: "3.x" + - name: Setup MSVC environment + if: ${{ runner.os == 'Windows' }} + shell: pwsh + run: | + $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + $vsPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath + Import-Module (Join-Path $vsPath "Common7\Tools\Microsoft.VisualStudio.DevShell.dll") + Enter-VsDevShell -VsInstallPath $vsPath -SkipAutomaticLocation -DevCmdArguments '-arch=x64 -host_arch=x64' + Get-ChildItem env: | ForEach-Object { Add-Content -Path $env:GITHUB_ENV -Value "$($_.Name)=$($_.Value)" } - - name: Set up SCons + - name: Setup Ninja shell: bash run: | - python -c "import sys; print(sys.version)" - python -m pip install scons - scons --version + # Ninja is preinstalled on GitHub-hosted images; fall back to the + # image's package manager if a future image drops it. + if ! command -v ninja >/dev/null 2>&1; then + if [ "$RUNNER_OS" = "macOS" ]; then + brew install ninja + elif [ "$RUNNER_OS" = "Windows" ]; then + choco install ninja -y + else + sudo apt-get update -y && sudo apt-get install -y ninja-build + fi + fi + ninja --version - name: Install APT dependencies - if: ${{ inputs.platform == 'linux' }} - uses: awalsh128/cache-apt-pkgs-action@681749ae568c81c2037cb9185e38b709b261bd2f # v1.6.1 + if: ${{ runner.os == 'Linux' }} + uses: awalsh128/cache-apt-pkgs-action@553a35bb8ebd9fcabcb1c9451aa4c98e1b4ca8a9 # v1.6.3 with: packages: build-essential pkg-config libtbb-dev - name: Install and Set g++ to version 13 - if: ${{ inputs.platform == 'linux' }} + if: ${{ runner.os == 'Linux' }} shell: sh run: | g++ --version @@ -68,29 +68,37 @@ runs: sudo update-alternatives --set g++ /usr/bin/g++-13 g++ --version - - name: Compile with SCons - uses: OpenVicProject/openvic-build@master - with: - platform: ${{ inputs.platform }} - target: ${{ inputs.target }} - sconsflags: arch=${{ inputs.arch }} ${{ inputs.build-library && 'build_ovsim_library=yes' }} ${{ inputs.scons-flags }} + - name: Configure + shell: bash + run: | + cmake --preset ${{ inputs.preset }} \ + -DGODOTCPP_TARGET=${{ inputs.target }} \ + ${{ inputs.cmake-args }} - - name: Delete compilation files - if: ${{ inputs.platform == 'windows' }} - shell: pwsh + - name: Build + shell: bash + run: | + CONFIG_LOWER=$(echo "${{ inputs.configuration }}" | tr '[:upper:]' '[:lower:]') + cmake --build --preset ${{ inputs.preset }}-$CONFIG_LOWER + + - name: Run tests + if: ${{ inputs.run-tests == 'true' }} + shell: bash run: | - Remove-Item bin/* -Include *.exp,*.pdb -Force + CONFIG_LOWER=$(echo "${{ inputs.configuration }}" | tr '[:upper:]' '[:lower:]') + ctest --preset ${{ inputs.preset }}-$CONFIG_LOWER - name: Upload library artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ github.event.repository.name }}-${{ inputs.identifier }}-library path: | - ${{ github.workspace }}/bin/libopenvic-simulation.* + ${{ github.workspace }}/out/build/${{ inputs.preset }}/bin/${{ inputs.configuration }}/libopenvic-simulation.a + ${{ github.workspace }}/out/build/${{ inputs.preset }}/bin/${{ inputs.configuration }}/openvic-simulation.lib - name: Upload executable artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ github.event.repository.name }}-${{ inputs.identifier }}-executable path: | - ${{ github.workspace }}/bin/openvic-simulation.headless.* + ${{ github.workspace }}/out/build/${{ inputs.preset }}/bin/${{ inputs.configuration }}/openvic-simulation.headless* diff --git a/.github/changed-files.yml b/.github/changed-files.yml index f8fb54369..a06ff714c 100644 --- a/.github/changed-files.yml +++ b/.github/changed-files.yml @@ -4,23 +4,24 @@ everything: &everything - "**" # Determines if build actions should occur after static checks are ran. Broadly speaking, these -# files changing would result in SCons rebuilding the engine, or are otherwise pertinent to the +# files changing would result in CMake rebuilding, or are otherwise pertinent to the # buildsystem itself. sources: - .github/{actions/*,workflows}/*.yml - - "**/{SConstruct,SCsub,*.py}" + - "**/CMakeLists.txt" + - "**/*.cmake" + - "**/CMakePresets.json" + - "**/*.py" - "**/*.{hpp,cpp,inc}" - - scripts - deps/** - tests/** pre-commit: - *everything - - '!scripts/**' - - '!deps/*/**' - - '!tests/deps/*/**' - - '!tests/benchmarks/deps/*/**' + # First-party submodules (openvic-dataloader, lexy-vdf) run their own checks. + - '!deps/openvic-dataloader/**' + - '!deps/lexy-vdf/**' # Determines which files are appropriate for running clangd-tidy checks on. clangd: - - "**/*.{hpp,cpp,inc}" \ No newline at end of file + - "**/*.{hpp,cpp,inc}" diff --git a/.github/workflows/build-matrix.yml b/.github/workflows/build-matrix.yml index 0cb240549..40998fed2 100644 --- a/.github/workflows/build-matrix.yml +++ b/.github/workflows/build-matrix.yml @@ -17,46 +17,44 @@ jobs: - identifier: windows-debug os: windows-latest name: 🏁 Windows Debug + preset: windows-x64-md + configuration: Debug target: template_debug - platform: windows - arch: x86_64 - test-bin-suffix: .md.exe - identifier: windows-release os: windows-latest name: 🏁 Windows Release + preset: windows-x64-md + configuration: Release target: template_release - platform: windows - arch: x86_64 - test-bin-suffix: .md.exe - identifier: macos-debug os: macos-latest name: 🍎 macOS (universal) Debug + preset: macos-universal + configuration: Debug target: template_debug - platform: macos - arch: universal - identifier: macos-release os: macos-latest name: 🍎 macOS (universal) Release + preset: macos-universal + configuration: Release target: template_release - platform: macos - arch: universal - identifier: linux-debug os: ubuntu-22.04 name: 🐧 Linux Debug + preset: linux-x64 + configuration: Debug target: template_debug - platform: linux - arch: x86_64 - identifier: linux-release os: ubuntu-22.04 name: 🐧 Linux Release + preset: linux-x64 + configuration: Release target: template_release - platform: linux - arch: x86_64 steps: - name: Checkout project @@ -69,10 +67,8 @@ jobs: uses: ./.github/actions/openvic-sim-build with: identifier: ${{ matrix.identifier }} + preset: ${{ matrix.preset }} + configuration: ${{ matrix.configuration }} target: ${{ matrix.target }} - platform: ${{ matrix.platform }} - arch: ${{ matrix.arch }} - - - name: Run tests - run: | - ./tests/bin/openvic-simulation.tests.${{ matrix.platform }}.${{ matrix.target }}.${{ matrix.arch }}${{ matrix.test-bin-suffix || '' }} \ No newline at end of file + # Benchmarks are built (not run) for compile coverage. + cmake-args: -DOPENVIC_SIM_BUILD_BENCHMARKS=ON diff --git a/.github/workflows/nightly-releases.yml b/.github/workflows/nightly-releases.yml index 9d1aab142..ba6530b1e 100644 --- a/.github/workflows/nightly-releases.yml +++ b/.github/workflows/nightly-releases.yml @@ -89,13 +89,13 @@ jobs: relevant_files=( "deps/*" "misc/*" - "scripts" "src/*.hpp" "src/*.cpp" ':!src/openvic-simulation/pch.hpp' ':!src/openvic-simulation/pch.cpp' "pyproject.toml" - "SConstruct" + "CMakeLists.txt" + "CMakePresets.json" ".github/workflows/builds.yml" ".github/actions/openvic-sim-release/action.yml" ".github/workflows/nightly-releases.yml" @@ -131,27 +131,21 @@ jobs: - identifier: windows-release os: windows-latest name: 🏁 Windows Release - target: template_release - platform: windows - arch: x86_64 - scons-flags: '' + preset: windows-x64-md + cmake-args: '' - identifier: macos-release os: macos-latest name: 🍎 macOS (universal) Release - target: template_release - platform: macos - arch: universal - scons-flags: 'lto=full' + preset: macos-universal + cmake-args: -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON - identifier: linux-release os: ubuntu-latest name: 🐧 Linux Release runner: ubuntu-22.04 - target: template_release - platform: linux - arch: x86_64 - scons-flags: 'lto=full use_static_cpp=yes' + preset: linux-x64 + cmake-args: -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON steps: - name: Checkout project @@ -164,11 +158,13 @@ jobs: uses: ./.github/actions/openvic-sim-build with: identifier: ${{ matrix.identifier }} - target: ${{ matrix.target }} - platform: ${{ matrix.platform }} - arch: ${{ matrix.arch }} - disable-cache: true - scons-flags: use_hot_reload=no build_ovsim_tests=no debug_symbols=yes optimize=speed_trace ${{ matrix.scons-flags }} + preset: ${{ matrix.preset }} + # Optimized build with debug symbols (the old optimize=speed_trace + # debug_symbols=yes flavor). + configuration: RelWithDebInfo + target: template_release + run-tests: false + cmake-args: -DOPENVIC_SIM_BUILD_TESTS=OFF ${{ matrix.cmake-args }} publish-nightly-release: name: Publish Release diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index b92ed2bc9..c5e2838b1 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -60,27 +60,21 @@ jobs: - identifier: windows-release os: windows-latest name: 🏁 Build Windows - target: template_release - platform: windows - arch: x86_64 - scons-flags: '' + preset: windows-x64-md + cmake-args: '' - identifier: macos-release os: macos-latest name: 🍎 Build macOS - target: template_release - platform: macos - arch: universal - scons-flags: lto=full + preset: macos-universal + cmake-args: -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON - identifier: linux-release os: ubuntu-latest name: 🐧 Build Linux runner: ubuntu-22.04 - target: template_release - platform: linux - arch: x86_64 - scons-flags: lto=full use_static_cpp=yes + preset: linux-x64 + cmake-args: -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON steps: - name: Checkout project @@ -93,11 +87,11 @@ jobs: uses: ./.github/actions/openvic-sim-build with: identifier: ${{ matrix.identifier }} - target: ${{ matrix.target }} - platform: ${{ matrix.platform }} - arch: ${{ matrix.arch }} - disable-cache: true - scons-flags: use_hot_reload=no build_ovsim_tests=no ${{ matrix.scons-flags }} + preset: ${{ matrix.preset }} + configuration: Release + target: template_release + run-tests: false + cmake-args: -DOPENVIC_SIM_BUILD_TESTS=OFF ${{ matrix.cmake-args }} publish-release: name: Publish Release diff --git a/.gitignore b/.gitignore index deb314a91..d4417447e 100644 --- a/.gitignore +++ b/.gitignore @@ -44,7 +44,6 @@ # Godot 4+ specific ignores .godot/ game/bin/openvic/* -.sconsign*.dblite # Binaries *.o @@ -53,7 +52,6 @@ game/bin/openvic/* *.obj *.bc *.pyc -*.dblite *.pdb *.lib bin/* @@ -65,12 +63,6 @@ bin/* *.idb *.exp -tests/bin/* -tests/benchmarks/bin/* - -# Build configuarion. -/custom.py - # MacOS stuff .DS_Store @@ -80,16 +72,17 @@ tests/benchmarks/bin/* # JetBrains .idea -# scons stuff +# clangd .cache compile_commands.json src/openvic-simulation/testing/test_results/results.txt -# Out-of-source build directory -/build/ - # ccls .ccls-cache # Generated source files src/openvic-simulation/gen/* + +# CMake out-of-source build directory +out/ +CMakeUserPresets.json diff --git a/.gitmodules b/.gitmodules index f90bc2c3e..a7e16c92c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,44 +1,6 @@ [submodule "deps/openvic-dataloader"] path = deps/openvic-dataloader url = https://github.com/OpenVicProject/OpenVic-Dataloader -[submodule "scripts"] - path = scripts - url = https://github.com/OpenVicProject/scripts [submodule "deps/lexy-vdf"] path = deps/lexy-vdf url = https://github.com/OpenVicProject/lexy-vdf -[submodule "deps/ordered-map"] - path = deps/ordered-map - url = https://github.com/Tessil/ordered-map -[submodule "deps/plf_colony"] - path = deps/plf_colony - url = https://github.com/mattreecebentley/plf_colony -[submodule "tests/deps/snitch"] - path = tests/deps/snitch - url = https://github.com/snitch-org/snitch - ignore = dirty -[submodule "deps/function2"] - path = deps/function2 - url = https://github.com/Naios/function2 -[submodule "deps/std_function/func"] - path = deps/std_function/func - url = https://github.com/skarupke/std_function -[submodule "tests/benchmarks/deps/nanobench"] - path = tests/benchmarks/deps/nanobench - url = https://github.com/Spartan322/nanobench -[submodule "deps/memory"] - path = deps/memory - url = https://github.com/foonathan/memory - ignore = dirty -[submodule "deps/spdlog"] - path = deps/spdlog - url = https://github.com/gabime/spdlog -[submodule "deps/xoshiro"] - path = deps/xoshiro - url = https://github.com/reputeless/xoshiro-cpp -[submodule "deps/type_safe"] - path = deps/type_safe - url = https://github.com/foonathan/type_safe -[submodule "deps/int128"] - path = deps/int128 - url = https://github.com/cppalliance/int128 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 17ea48587..c3df7c22b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,10 +37,10 @@ repos: hooks: - id: ruff-check args: [--fix] - files: (\.py|SConstruct|SCsub)$ + files: \.py$ types_or: [text] - id: ruff-format - files: (\.py|SConstruct|SCsub)$ + files: \.py$ types_or: [text] - repo: https://github.com/pre-commit/mirrors-mypy diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..707a2babe --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,138 @@ +cmake_minimum_required(VERSION 3.28) +project(openvic-simulation LANGUAGES CXX) + +if(NOT COMMAND openvic_setup_base_flags) + # scripts is the shared build-tooling repo, fetched once as a pinned + # tarball (like the other third-party deps). In a composed build the + # outermost repo fetches + loads it first and this guard skips for the + # nested repos; a standalone repo fetches it itself. FetchContent also + # dedups by name, so either path yields exactly one fetch. + # + # Raw FetchContent (not openvic_declare_dep) on purpose: openvic_declare_dep + # lives INSIDE scripts (OpenVicDeps.cmake), so it isn't defined yet here. + # Local-dev escape hatch: -DFETCHCONTENT_SOURCE_DIR_OPENVIC_SCRIPTS= + # points this at a working-tree scripts checkout. + include(FetchContent) + FetchContent_Declare( + openvic_scripts + URL "https://github.com/OpenVicProject/scripts/archive/3fcac2ec942c152bd83b2c5ad279ef192e0125d7.tar.gz" + URL_HASH SHA256=c0759a8d5eb8c6a8fa7780f01a6bd2bf079a18ef2527f0c7ec7ecc9b87aed635 + ) + FetchContent_MakeAvailable(openvic_scripts) + include("${openvic_scripts_SOURCE_DIR}/cmake/OpenVicScripts.cmake") +endif() +openvic_setup_base_flags() +openvic_disable_rtti() + +option(OPENVIC_USE_PCH "Precompile openvic-simulation/pch.hpp" ON) + +add_subdirectory(deps) + +file(GLOB_RECURSE sim_sources CONFIGURE_DEPENDS src/openvic-simulation/*.cpp) +list(REMOVE_ITEM sim_sources ${CMAKE_CURRENT_SOURCE_DIR}/src/openvic-simulation/pch.cpp) + +add_library(openvic-simulation STATIC ${sim_sources}) +add_library(openvic::simulation ALIAS openvic-simulation) + +set(sim_gen_dir ${CMAKE_CURRENT_BINARY_DIR}/gen-include) +target_include_directories( + openvic-simulation + PUBLIC src $ + PRIVATE src/openvic-simulation +) + +openvic_generate_commit_info( + TARGET openvic-simulation + PREFIX sim + REPO_DIR ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT ${sim_gen_dir}/openvic-simulation/gen/commit_info.gen.hpp +) +openvic_generate_license_info( + TARGET openvic-simulation + PREFIX sim + COPYRIGHT ${CMAKE_CURRENT_SOURCE_DIR}/COPYRIGHT + LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.md + OUTPUT ${sim_gen_dir}/openvic-simulation/gen/license_info.gen.hpp +) +openvic_generate_author_info( + TARGET openvic-simulation + PREFIX sim + AUTHORS ${CMAKE_CURRENT_SOURCE_DIR}/AUTHORS.md + OUTPUT ${sim_gen_dir}/openvic-simulation/gen/author_info.gen.hpp + SECTIONS + "Senior Developers=AUTHORS_SENIOR_DEVELOPERS" + "Developers=AUTHORS_DEVELOPERS" + "Contributors=AUTHORS_CONTRIBUTORS" + "Consultants=AUTHORS_CONSULTANTS" +) + +if(OPENVIC_USE_PCH) + target_precompile_headers(openvic-simulation PRIVATE src/openvic-simulation/pch.hpp) +endif() + +# PUBLIC deps are exposed to consumers (everything except lexy, which is an +# implementation detail of the parsers and stays private). +target_link_libraries( + openvic-simulation + PUBLIC + openvic::dataloader + openvic::lexy-vdf + tsl::ordered_map + function2::function2 + type_safe + Boost::int128 + spdlog::spdlog + ov_plf_colony + ov_std_function + ov_xoshiro + ov_foonathan_memory +) + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + find_package(PkgConfig REQUIRED) + pkg_check_modules(TBB REQUIRED IMPORTED_TARGET tbb) + target_link_libraries(openvic-simulation PUBLIC PkgConfig::TBB) +endif() + +if(APPLE) + # clang emits the TLS init aliases (_ZTH*) for inline thread_local members + # as non-weak private-external symbols, one per TU that odr-uses them, and + # the Xcode 26 linker rejects the duplicates unless it runs with + # -dead_strip (the old scons build always linked with it). INTERFACE so + # every consumer linking this archive gets the flag. + target_link_options(openvic-simulation INTERFACE LINKER:-dead_strip) +elseif(MINGW) + # GCC on PE has the same problem: the TLS init function for those inline + # thread_local members is emitted non-COMDAT in every TU, and GNU ld + # errors on the duplicates. They are identical instantiations, so keep + # the first. + target_link_options(openvic-simulation INTERFACE LINKER:--allow-multiple-definition) +endif() + +set_target_properties(openvic-simulation PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$") + +# Headless executable, unit tests, and benchmarks: built by default only when +# openvic-simulation is the top-level project (composed builds just want the +# library). Benchmarks are opt-in even standalone, matching the old default. +option(OPENVIC_SIM_BUILD_HEADLESS "Build the openvic-simulation headless executable" ${PROJECT_IS_TOP_LEVEL}) +option(OPENVIC_SIM_BUILD_TESTS "Build the openvic-simulation unit tests" ${PROJECT_IS_TOP_LEVEL}) +option(OPENVIC_SIM_BUILD_BENCHMARKS "Build the openvic-simulation benchmarks" OFF) + +if(OPENVIC_SIM_BUILD_HEADLESS) + file(GLOB_RECURSE ovsim_headless_sources CONFIGURE_DEPENDS src/headless/*.cpp) + add_executable(openvic-simulation-headless ${ovsim_headless_sources}) + target_compile_definitions(openvic-simulation-headless PRIVATE OPENVIC_SIM_HEADLESS) + target_include_directories(openvic-simulation-headless PRIVATE src/headless) + target_link_libraries(openvic-simulation-headless PRIVATE openvic::simulation) + set_target_properties( + openvic-simulation-headless + PROPERTIES + OUTPUT_NAME "openvic-simulation.headless" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$" + ) +endif() + +if(OPENVIC_SIM_BUILD_TESTS OR OPENVIC_SIM_BUILD_BENCHMARKS) + enable_testing() + add_subdirectory(tests) +endif() diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 000000000..54856a603 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,183 @@ +{ + "version": 6, + "cmakeMinimumRequired": { + "major": 3, + "minor": 27 + }, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "generator": "Ninja Multi-Config", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "CMAKE_CONFIGURATION_TYPES": "Release;RelWithDebInfo;Debug" + } + }, + { + "name": "p-windows-x64", + "hidden": true, + "inherits": "base", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "architecture": { + "value": "x64", + "strategy": "external" + } + }, + { + "name": "windows-x64-md", + "inherits": "p-windows-x64", + "cacheVariables": { + "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDLL" + } + }, + { + "name": "windows-x64-mt", + "inherits": "p-windows-x64", + "cacheVariables": { + "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded" + } + }, + { + "name": "linux-x64", + "inherits": "base", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, + "cacheVariables": { + "CMAKE_CXX_COMPILER": "g++" + } + }, + { + "name": "macos-universal", + "inherits": "base", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + }, + "cacheVariables": { + "CMAKE_OSX_ARCHITECTURES": "x86_64;arm64" + } + } + ], + "buildPresets": [ + { + "name": "windows-x64-md-release", + "configurePreset": "windows-x64-md", + "configuration": "Release" + }, + { + "name": "windows-x64-md-debug", + "configurePreset": "windows-x64-md", + "configuration": "Debug" + }, + { + "name": "windows-x64-md-relwithdebinfo", + "configurePreset": "windows-x64-md", + "configuration": "RelWithDebInfo" + }, + { + "name": "windows-x64-mt-release", + "configurePreset": "windows-x64-mt", + "configuration": "Release" + }, + { + "name": "windows-x64-mt-debug", + "configurePreset": "windows-x64-mt", + "configuration": "Debug" + }, + { + "name": "windows-x64-mt-relwithdebinfo", + "configurePreset": "windows-x64-mt", + "configuration": "RelWithDebInfo" + }, + { + "name": "linux-x64-release", + "configurePreset": "linux-x64", + "configuration": "Release" + }, + { + "name": "linux-x64-debug", + "configurePreset": "linux-x64", + "configuration": "Debug" + }, + { + "name": "linux-x64-relwithdebinfo", + "configurePreset": "linux-x64", + "configuration": "RelWithDebInfo" + }, + { + "name": "macos-universal-release", + "configurePreset": "macos-universal", + "configuration": "Release" + }, + { + "name": "macos-universal-debug", + "configurePreset": "macos-universal", + "configuration": "Debug" + }, + { + "name": "macos-universal-relwithdebinfo", + "configurePreset": "macos-universal", + "configuration": "RelWithDebInfo" + } + ], + "testPresets": [ + { + "name": "windows-x64-md-debug", + "configurePreset": "windows-x64-md", + "configuration": "Debug", + "output": { "outputOnFailure": true } + }, + { + "name": "windows-x64-md-release", + "configurePreset": "windows-x64-md", + "configuration": "Release", + "output": { "outputOnFailure": true } + }, + { + "name": "windows-x64-mt-debug", + "configurePreset": "windows-x64-mt", + "configuration": "Debug", + "output": { "outputOnFailure": true } + }, + { + "name": "windows-x64-mt-release", + "configurePreset": "windows-x64-mt", + "configuration": "Release", + "output": { "outputOnFailure": true } + }, + { + "name": "linux-x64-debug", + "configurePreset": "linux-x64", + "configuration": "Debug", + "output": { "outputOnFailure": true } + }, + { + "name": "linux-x64-release", + "configurePreset": "linux-x64", + "configuration": "Release", + "output": { "outputOnFailure": true } + }, + { + "name": "macos-universal-debug", + "configurePreset": "macos-universal", + "configuration": "Debug", + "output": { "outputOnFailure": true } + }, + { + "name": "macos-universal-release", + "configurePreset": "macos-universal", + "configuration": "Release", + "output": { "outputOnFailure": true } + } + ] +} diff --git a/README.md b/README.md index d87bf9d27..ad2fbca6b 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,17 @@ Repo of the OpenVic-Simulation Library for [OpenVic](https://github.com/OpenVicP For detailed instructions, view the OpenVic Contributor Quickstart Guide [here](https://github.com/OpenVicProject/OpenVic/blob/master/docs/contribution-quickstart-guide.md) ## Required -* [scons](https://scons.org/) +* [CMake](https://cmake.org/) 3.28+ +* [Ninja](https://ninja-build.org/) +* Python 3 (used by the build for code generation) ## Build Instructions -1. Install [scons](https://scons.org/) for your system. -2. Run the command `git submodule update --init --recursive` to retrieve all related submodules. -3. Run `scons` in the project root, you should see a openvic-simulation.headless file in `bin`. +1. Run the command `git submodule update --init` to retrieve the first-party submodules (openvic-dataloader, lexy-vdf). Third-party dependencies are fetched automatically by CMake. +2. Pick a configure preset from `CMakePresets.json` (`windows-x64-md`, `windows-x64-mt`, `linux-x64`, `macos-universal`) and run `cmake --preset ` in the project root. +3. Run `cmake --build --preset -debug` (or `-release`). The static library, headless executable, and unit tests land in `out/build//bin//`. +4. Run the tests with `ctest --preset -debug`. -## Link Instructions -1. Call `ovsim_env = SConscript("openvic-simulation/SConstruct")` -2. Use the values stored in the `ovsim_env.openvic_simulation` to link and compile against: +The headless executable and tests are built by default in standalone builds; disable with `-DOPENVIC_SIM_BUILD_HEADLESS=OFF` / `-DOPENVIC_SIM_BUILD_TESTS=OFF`. Benchmarks are opt-in: `-DOPENVIC_SIM_BUILD_BENCHMARKS=ON`. -| Variable Name | Description | Correlated ENV variable | -| --- | --- | --- | -| `LIBPATH` | Library path list | `env["LIBPATH"]` | -| `LIBS` | Library files names in the library paths | `env["LIBS"]` | -| `INCPATH` | Library include files | `env["CPPPATH"]` | +## Link Instructions +Use CMake: `add_subdirectory(openvic-simulation)` and link against `openvic::simulation`. diff --git a/SConstruct b/SConstruct deleted file mode 100644 index 2fdc6203f..000000000 --- a/SConstruct +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env python - -import os - -BINDIR = "bin" - -env = SConscript("scripts/SConstruct") - -env.PrependENVPath("PATH", os.getenv("PATH")) - -opts = env.SetupOptions() - -opts.Add(BoolVariable("build_ovsim_tests", "Build the openvic simulation unit tests", env.is_standalone)) -opts.Add(BoolVariable("run_ovsim_tests", "Run the openvic simulation unit tests", False)) -opts.Add(BoolVariable("build_ovsim_benchmarks", "Build the openvic simulation benchmarks", False)) -opts.Add(BoolVariable("run_ovsim_benchmarks", "Run the openvic simulation benchmarks", False)) -opts.Add( - BoolVariable( - key="build_ovsim_library", - help="Build the openvic simulation library.", - default=env.get("build_ovsim_library", not env.is_standalone), - ) -) -opts.Add(BoolVariable("build_ovsim_headless", "Build the openvic simulation headless executable", env.is_standalone)) - -env.FinalizeOptions() - -suffix = ".{}.{}".format(env["platform"], env["target"]) -if env.dev_build: - suffix += ".dev" -if env["precision"] == "double": - suffix += ".double" -suffix += "." + env["arch"] -if env["platform"] == "windows": - if env.get("debug_crt", False): - suffix += ".mdd" - elif env.get("use_static_cpp", False): - suffix += ".mt" - else: - suffix += ".md" -if env.get("use_asan", False): - suffix += ".san" -env["suffix"] = suffix - -build_dir = env.Dir("build/" + suffix.lstrip(".")).abspath.replace("\\", "/") -env["build_dir"] = build_dir - -env.exposed_includes = [] - -SConscript("deps/SCsub", "env") - -env.openvic_simulation = {} - -# Tweak this if you want to use different folders, or more folders, to store your source code in. -source_path = "src/openvic-simulation" -include_path = "src" -# Out-of-source build: variant tree holds object files and generated headers, -# not copies of the source. Compile diagnostics therefore reference original -# source paths. -sim_variant = build_dir + "/" + source_path # forward slashes so VariantDir matches -sim_variant_parent = build_dir + "/" + include_path # variant of "src/" -env.VariantDir(sim_variant, source_path, duplicate=False) - -env.Append(CPPPATH=[[env.Dir(p) for p in [sim_variant, sim_variant_parent, source_path, include_path]]]) - -gen_commit_info = env.CommandNoCache( - sim_variant + "/gen/commit_info.gen.hpp", - env.Value(env.get_git_info("sim")), - env.Run(env.git_builder), - name_prefix="sim", -) -gen_license_info = env.CommandNoCache( - sim_variant + "/gen/license_info.gen.hpp", - ["COPYRIGHT", "LICENSE.md"], - env.Run(env.license_builder), - name_prefix="sim", -) -gen_author_info = env.CommandNoCache( - sim_variant + "/gen/author_info.gen.hpp", - "AUTHORS.md", - env.Run(env.author_builder), - name_prefix="sim", - sections={ - "Senior Developers": "AUTHORS_SENIOR_DEVELOPERS", - "Developers": "AUTHORS_DEVELOPERS", - "Contributors": "AUTHORS_CONTRIBUTORS", - "Consultants": "AUTHORS_CONSULTANTS", - }, -) -gen_files = gen_commit_info + gen_license_info + gen_author_info -Default(gen_commit_info, gen_license_info, gen_author_info) - -# For future reference: -# - CCFLAGS are compilation flags shared between C and C++ -# - CFLAGS are for C-specific compilation flags -# - CXXFLAGS are for C++-specific compilation flags -# - CPPFLAGS are for pre-processor flags -# - CPPDEFINES are for pre-processor defines -# - LINKFLAGS are for linking flags - -# Exclude pch.cpp from the regular source list so it isn't compiled twice -# (env.PCH below builds it once with /Yc). -pch_cpp_source = source_path + "/pch.cpp" -pch_cpp_variant = sim_variant + "/pch.cpp" -sources = env.GlobRecursiveVariant("*.cpp", source_path, sim_variant, pch_cpp_source) -env.simulation_sources = sources - -library = None -env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"] -library_name = "libopenvic-simulation{}{}".format(suffix, env["LIBSUFFIX"]) - -# Precompiled header -if env.get("is_msvc", False): - pch_header_rel = "openvic-simulation/pch.hpp" - env["PCHSTOP"] = pch_header_rel - pch_pch, pch_obj = env.PCH(pch_cpp_variant) - env["PCH"] = pch_pch - env.Append(CCFLAGS=["/FI" + pch_header_rel]) - sources.append(pch_obj) - -default_args = [] - -if env["run_ovsim_tests"]: - env["build_ovsim_tests"] = True - -if env["run_ovsim_benchmarks"]: - env["build_ovsim_benchmarks"] = True - -if env["build_ovsim_tests"] or env["build_ovsim_benchmarks"]: - env["build_ovsim_library"] = True - -if env["build_ovsim_library"]: - library = env.StaticLibrary(target=env.File(os.path.join(BINDIR, library_name)), source=sources) - default_args += [library] - - # Ensure `scons -c` wipes the per-config build dir, including any stragglers - # (gen headers, .pch caches) not directly attached to the library target. - env.Clean(library, env.Dir(build_dir)) - - env.Append(LIBPATH=[env.Dir(BINDIR)]) - env.Prepend(LIBS=[library_name]) - - env.openvic_simulation["LIBPATH"] = env["LIBPATH"] - env.openvic_simulation["LIBS"] = env["LIBS"] - # Variant parent for generated headers (gen/*.gen.hpp); source parent for - # authored headers (MSVC's preprocessor needs both physically in -I). - env.openvic_simulation["INCPATH"] = [env.Dir(sim_variant_parent), env.Dir(include_path)] + env.exposed_includes - env.openvic_simulation["GEN_FILES"] = gen_files - -headless_program = None -env["PROGSUFFIX"] = suffix + env["PROGSUFFIX"] - -if env["build_ovsim_headless"]: - headless_name = "openvic-simulation" - headless_env = env.Clone() - headless_src = "src/headless" - headless_variant = build_dir + "/" + headless_src - headless_env.VariantDir(headless_variant, headless_src, duplicate=False) - headless_env.Append(CPPDEFINES=["OPENVIC_SIM_HEADLESS"]) - headless_env.Append(CPPPATH=[headless_env.Dir(headless_variant), headless_env.Dir(headless_src)]) - headless_env.headless_sources = env.GlobRecursiveVariant("*.cpp", headless_src, headless_variant) - if not env["build_ovsim_library"]: - headless_env.headless_sources += sources - headless_program = headless_env.Program( - target=os.path.join(BINDIR, headless_name), - source=headless_env.headless_sources, - PROGSUFFIX=".headless" + env["PROGSUFFIX"], - ) - default_args += [headless_program] - - # Also wipe the per-config build dir when the headless target is cleaned, - # in case the library target isn't built in this configuration. - headless_env.Clean(headless_program, headless_env.Dir(build_dir)) - -if env["build_ovsim_tests"]: - tests_env = SConscript("tests/SCsub", "env") - - if env["run_ovsim_tests"]: - tests_env.RunUnitTest() - -if env["build_ovsim_benchmarks"]: - benchmarks_env = SConscript("tests/benchmarks/SCsub", {"env": tests_env if env["build_ovsim_tests"] else env}) - - if env["run_ovsim_benchmarks"]: - benchmarks_env.RunBenchmarks() - -# Add compiledb if the option is set -if env.get("compiledb", False): - default_args += ["compiledb"] - -Default(*default_args) - -if "env" in locals(): - # FIXME: This method mixes both cosmetic progress stuff and cache handling... - env.show_progress(env) - -Return("env") diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt new file mode 100644 index 000000000..db6917145 --- /dev/null +++ b/deps/CMakeLists.txt @@ -0,0 +1,185 @@ +# Dependency wiring for the simulation. Third-party deps are fetched as +# pinned source tarballs (see scripts/cmake/OpenVicDeps.cmake) so they build +# out of the build tree's _deps/ dir, immune to working-tree churn. +# They keep EXCLUDE_FROM_ALL (via openvic_declare_dep) so their install rules +# stay out of `cmake --install` (the only install rule in the whole build is +# the OpenVic root's copy of the extension library). + +# --- OpenVic libraries (first-party: own CMakeLists, composed from submodule +# so they rebuild on branch switches) --- +# openvic-dataloader first: its vendored lexy claims the lexy target names, +# so lexy-vdf reuses it (first FetchContent declaration wins). +add_subdirectory(openvic-dataloader) +add_subdirectory(lexy-vdf) + +# --- header-only deps with upstream CMake --- +openvic_declare_dep(ordered-map + REPO Tessil/ordered-map + SHA bd8d5ef4149cd40783a486011778a2e7eedde441 + SHA256 39f458bf3808b2a83371580bcbb48b0336fbaa95cb1228502346f934a4f5d49e +) +FetchContent_MakeAvailable(ordered-map) # tsl::ordered_map + +openvic_declare_dep(function2 + REPO Naios/function2 + SHA 43fc0ca473ecb081918709bd7d524d84c2ff8dce + SHA256 7bd09f73b1d0de46885e117e9ef094ade2c771917d126e13451e3cbb9b228c20 +) +FetchContent_MakeAvailable(function2) # function2::function2 + +openvic_declare_dep(int128 + REPO cppalliance/int128 + SHA ea0f85e693767ae667e26d66022d6ed68d3425de + SHA256 b0685286c9b5ff29e7f94772dbf9600b865dcdcf8da96009c553a1d1da23594a +) +FetchContent_MakeAvailable(int128) # Boost::int128 + +# --- header-only deps without upstream CMake: fetch source-only, hand-roll +# INTERFACE targets (${_SOURCE_DIR} is the extracted tarball root) --- +openvic_declare_dep(plf_colony + REPO mattreecebentley/plf_colony + SHA fbc8483fb785c431dc4410396815abf886728c40 + SHA256 d7006bebdc42694491d11e2d4d95417848a5fa2fff2caade2b123bb99479d098 + DOWNLOAD_ONLY +) +FetchContent_MakeAvailable(plf_colony) +add_library(ov_plf_colony INTERFACE) +target_include_directories(ov_plf_colony SYSTEM INTERFACE ${plf_colony_SOURCE_DIR}) + +# Consumers include std_function as . Extract into a `func/` +# subdir and point the include dir at its parent to preserve that prefix. +openvic_declare_dep(std_function + REPO skarupke/std_function + SHA dbf2df963e6d0a47ab5e21e93c838233cd8b6b03 + SHA256 b0f2d651ca6e0d4c112434ceab78785b4f5e19422821f78c19c57e77a84f7935 + DOWNLOAD_ONLY + SOURCE_DIR ${CMAKE_BINARY_DIR}/_deps/std_function-src/func +) +FetchContent_MakeAvailable(std_function) +add_library(ov_std_function INTERFACE) +get_filename_component(ov_std_function_include "${std_function_SOURCE_DIR}" DIRECTORY) +target_include_directories(ov_std_function SYSTEM INTERFACE ${ov_std_function_include}) + +openvic_declare_dep(xoshiro + REPO reputeless/xoshiro-cpp + SHA 19bcbb2ce0ed158233187f524fd0964c105a65b3 + SHA256 973884cfb77f2f979f9ac09e8846dfd758af4bc08422b7c92a0e676a3fb780ff + DOWNLOAD_ONLY +) +FetchContent_MakeAvailable(xoshiro) +add_library(ov_xoshiro INTERFACE) +target_include_directories(ov_xoshiro SYSTEM INTERFACE ${xoshiro_SOURCE_DIR}) + +# --- type_safe (assertions/wrapper enabled only on template_release; +# arithmetic policy defaults to checked for dev builds, ub otherwise) --- +if(GODOTCPP_TARGET STREQUAL "template_release") + set(TYPE_SAFE_ENABLE_ASSERTIONS ON) + set(TYPE_SAFE_ENABLE_WRAPPER ON) +else() + set(TYPE_SAFE_ENABLE_ASSERTIONS OFF) + set(TYPE_SAFE_ENABLE_WRAPPER OFF) +endif() +set(TYPE_SAFE_ENABLE_PRECONDITION_CHECKS ON) +set(OPENVIC_TYPE_SAFE_ARITHMETIC_POLICY + "" + CACHE STRING + "type_safe arithmetic policy: ub, checked, or default (empty = auto: checked for dev builds, ub otherwise)" +) +set_property(CACHE OPENVIC_TYPE_SAFE_ARITHMETIC_POLICY PROPERTY STRINGS ";ub;checked;default") +if(OPENVIC_TYPE_SAFE_ARITHMETIC_POLICY STREQUAL "") + if(GODOTCPP_DEV_BUILD) + set(TYPE_SAFE_ARITHMETIC_POLICY "checked") + else() + set(TYPE_SAFE_ARITHMETIC_POLICY "ub") + endif() +elseif(OPENVIC_TYPE_SAFE_ARITHMETIC_POLICY MATCHES "^(ub|checked|default)$") + set(TYPE_SAFE_ARITHMETIC_POLICY "${OPENVIC_TYPE_SAFE_ARITHMETIC_POLICY}") +else() + message(FATAL_ERROR "OPENVIC_TYPE_SAFE_ARITHMETIC_POLICY must be ub, checked, default, or empty") +endif() +openvic_declare_dep(type_safe + REPO foonathan/type_safe + SHA 292e8c127037e33d92f8f52dab0f1184993942d0 + SHA256 622337e530b5812fd65ef91bc6df580946b2ac8b0ba865253d1dad80394e88df +) +FetchContent_MakeAvailable(type_safe) # type_safe (+ vendored external/debug_assert) + +# --- foonathan/memory: hand-rolled. Upstream's CMake bakes container node +# sizes at configure time via try_compile (wrong for macOS universal builds); +# instead we fetch source-only and generate the config and node-size headers +# into the fetched tree ourselves (see OpenVicCodegen.cmake). --- +openvic_declare_dep(memory + REPO foonathan/memory + SHA 92a5bfc53b97955b3d685af679636e87d0f5d15a + SHA256 8601f99ec73a2f84a16447f3e320b76c62227530217c6bf2fff44aece67d110e + DOWNLOAD_ONLY +) +FetchContent_MakeAvailable(memory) + +openvic_generate_memory_headers( + MEMORY_DIR ${memory_SOURCE_DIR} + DEFINES + FOONATHAN_MEMORY_CHECK_ALLOCATION_SIZE=1 + FOONATHAN_MEMORY_IMPL_DEFAULT_ALLOCATOR=heap_allocator + FOONATHAN_MEMORY_DEBUG_ASSERT=0 + FOONATHAN_MEMORY_DEBUG_FILL=0 + FOONATHAN_MEMORY_DEBUG_FENCE=0 + FOONATHAN_MEMORY_DEBUG_LEAK_CHECK=0 + FOONATHAN_MEMORY_DEBUG_POINTER_CHECK=0 + FOONATHAN_MEMORY_DEBUG_DOUBLE_DEALLOC_CHECK=0 + FOONATHAN_MEMORY_EXTERN_TEMPLATE=1 + FOONATHAN_MEMORY_TEMPORARY_STACK_MODE=2 +) + +file(GLOB_RECURSE ov_memory_sources CONFIGURE_DEPENDS ${memory_SOURCE_DIR}/src/*.cpp) +add_library(ov_foonathan_memory STATIC ${ov_memory_sources}) +target_include_directories( + ov_foonathan_memory + PRIVATE ${memory_SOURCE_DIR}/src ${memory_SOURCE_DIR}/include/foonathan/memory +) +target_include_directories(ov_foonathan_memory SYSTEM PUBLIC ${memory_SOURCE_DIR}/include) +target_compile_definitions( + ov_foonathan_memory + PUBLIC + FOONATHAN_MEMORY=1 + FOONATHAN_MEMORY_VERSION_MAJOR=0 + FOONATHAN_MEMORY_VERSION_MINOR=7 + FOONATHAN_MEMORY_VERSION_PATCH=4 +) + +# --- spdlog (external fmt comes from openvic-dataloader's vendored copy) --- +set(OPENVIC_SPDLOG_LEVEL + "" + CACHE STRING + "Compile-time spdlog level: trace, debug, info, warn, error, critical, or off (empty = auto: debug for dev builds, warn for template_release, info otherwise)" +) +set_property(CACHE OPENVIC_SPDLOG_LEVEL PROPERTY STRINGS ";trace;debug;info;warn;error;critical;off") +if(NOT OPENVIC_SPDLOG_LEVEL STREQUAL "") + string(TOUPPER "${OPENVIC_SPDLOG_LEVEL}" _ov_spdlog_upper) + if(NOT _ov_spdlog_upper MATCHES "^(TRACE|DEBUG|INFO|WARN|ERROR|CRITICAL|OFF)$") + message(FATAL_ERROR "OPENVIC_SPDLOG_LEVEL must be trace, debug, info, warn, error, critical, off, or empty") + endif() + set(OV_SPDLOG_LEVEL "SPDLOG_LEVEL_${_ov_spdlog_upper}") +elseif(GODOTCPP_DEV_BUILD) + set(OV_SPDLOG_LEVEL "SPDLOG_LEVEL_DEBUG") +elseif(GODOTCPP_TARGET STREQUAL "template_release") + set(OV_SPDLOG_LEVEL "SPDLOG_LEVEL_WARN") +else() + set(OV_SPDLOG_LEVEL "SPDLOG_LEVEL_INFO") +endif() + +set(SPDLOG_FMT_EXTERNAL ON) +set(SPDLOG_NO_EXCEPTIONS ON) +set(SPDLOG_INSTALL OFF) +set(SPDLOG_BUILD_EXAMPLE OFF) +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(SPDLOG_FWRITE_UNLOCKED OFF) +endif() +openvic_declare_dep(spdlog + REPO gabime/spdlog + SHA f1d748e5e3edfa4b1778edea003bac94781bc7b7 + SHA256 0804ca200b3bb4ac5cfee7fbb9ca42069b4dbf21f5f1869465b4a5bf10b90ef4 +) +FetchContent_MakeAvailable(spdlog) # spdlog::spdlog +# Compile-time log level filter; not an upstream option, so applied directly. +target_compile_definitions(spdlog PUBLIC SPDLOG_ACTIVE_LEVEL=${OV_SPDLOG_LEVEL}) diff --git a/deps/SCsub b/deps/SCsub deleted file mode 100644 index 44677de4f..000000000 --- a/deps/SCsub +++ /dev/null @@ -1,291 +0,0 @@ -#!/usr/bin/env python - -Import("env") - - -def build_openvic_dataloader(env): - ovdl_env = SConscript("openvic-dataloader/SConstruct") - env.AppendUnique(CPPDEFINES=ovdl_env["CPPDEFINES"]) - env.Append(LIBPATH=ovdl_env.openvic_dataloader["LIBPATH"]) - env.Prepend(LIBS=ovdl_env.openvic_dataloader["LIBS"]) - env.Append(CPPPATH=ovdl_env.openvic_dataloader["INCPATH"]) - env.openvic_dataloader = ovdl_env.openvic_dataloader - env.exposed_includes += env.openvic_dataloader["INCPATH"] - - -def build_lexy_vdf(env): - lvdf_env = SConscript("lexy-vdf/SConstruct") - env.AppendUnique(CPPDEFINES=lvdf_env["CPPDEFINES"]) - env.Append(LIBPATH=lvdf_env.lexy_vdf["LIBPATH"]) - env.Prepend(LIBS=lvdf_env.lexy_vdf["LIBS"]) - env.Append(CPPPATH=lvdf_env.lexy_vdf["INCPATH"]) - env.lexy_vdf = lvdf_env.lexy_vdf - env.exposed_includes += env.lexy_vdf["INCPATH"] - - -def build_ordered_map(env): - include_path = "ordered-map/include" - env.ordered_map = {} - env.ordered_map["INCPATH"] = [env.Dir(include_path)] - env.Append(CPPPATH=env.ordered_map["INCPATH"]) - env.exposed_includes += env.ordered_map["INCPATH"] - - -def build_colony(env): - include_path = "plf_colony" - env.colony = {} - env.colony["INCPATH"] = [env.Dir(include_path)] - env.Append(CPPPATH=env.colony["INCPATH"]) - env.exposed_includes += env.colony["INCPATH"] - - -def build_function2(env): - include_path = "function2/include" - env.function2 = {} - env.function2["INCPATH"] = [env.Dir(include_path)] - env.Append(CPPPATH=env.function2["INCPATH"]) - env.exposed_includes += env.function2["INCPATH"] - - -def build_std_function(env): - include_path = "std_function" - env.std_function = {} - env.std_function["INCPATH"] = [env.Dir(include_path)] - env.Append(CPPPATH=env.std_function["INCPATH"]) - env.exposed_includes += env.std_function["INCPATH"] - - -def build_memory(env): - from pathlib import Path - - import methods - - include_path = "memory/include" - inner_include_path = include_path + "/foonathan/memory" - source_path = "memory/src" - - env.Append( - CPPDEFINES=[ - ("FOONATHAN_MEMORY", 1), - ("FOONATHAN_MEMORY_VERSION_MAJOR", 0), - ("FOONATHAN_MEMORY_VERSION_MINOR", 7), - ("FOONATHAN_MEMORY_VERSION_PATCH", 4), - ] - ) - - memory_env = env.Clone() - memory_env.Append( - BUILDERS={ - "GenerateMemoryConfig": memory_env.Builder(action=methods.generate_memory_config_header), - "GenerateMemoryContainerNodeSizes": memory_env.Builder( - action=methods.generate_memory_container_size_header - ), - } - ) - - memory_env.config_data = { - "FOONATHAN_MEMORY_CHECK_ALLOCATION_SIZE": 1, - "FOONATHAN_MEMORY_IMPL_DEFAULT_ALLOCATOR": "heap_allocator", - "FOONATHAN_MEMORY_DEBUG_ASSERT": 0, - "FOONATHAN_MEMORY_DEBUG_FILL": 0, - "FOONATHAN_MEMORY_DEBUG_FENCE": 0, - "FOONATHAN_MEMORY_DEBUG_LEAK_CHECK": 0, - "FOONATHAN_MEMORY_DEBUG_POINTER_CHECK": 0, - "FOONATHAN_MEMORY_DEBUG_DOUBLE_DEALLOC_CHECK": 0, - "FOONATHAN_MEMORY_EXTERN_TEMPLATE": 1, - "FOONATHAN_MEMORY_TEMPORARY_STACK_MODE": 2, - } - - config = memory_env.GenerateMemoryConfig(memory_env.File(Path(inner_include_path) / "config_impl.hpp"), "SCsub") - node_size_impl = memory_env.GenerateMemoryContainerNodeSizes( - memory_env.File(Path(inner_include_path) / "detail" / "container_node_sizes_impl.hpp"), "SCsub" - ) - - if memory_env.get("is_msvc", False): - if memory_env.get("use_clang_cl"): - memory_env.Append( - CXXFLAGS=["-pedantic-errors", "-Werror", "-Wall", "-Wextra", "-Wconversion", "-Wsign-conversion"] - ) - else: - memory_env.Append(CXXFLAGS=["/WX", "/W3", "/D", "_CRT_SECURE_NO_WARNINGS"]) - else: - memory_env.Append( - CXXFLAGS=["-pedantic-errors", "-Werror", "-Wall", "-Wextra", "-Wconversion", "-Wsign-conversion"] - ) - memory_env.Append(CPPPATH=[[memory_env.Dir(p) for p in [source_path, Path(inner_include_path)]]]) - # Mirror memory's source tree into the per-config build dir - build_root = (env.get("build_dir") or env.Dir("#").abspath).replace("\\", "/") - out_dir = build_root + "/memory/" + source_path - memory_env.VariantDir(out_dir, source_path, duplicate=True) - sources = env.GlobRecursive("*.cpp", [out_dir]) - env.memory_sources = sources - - library_name = "libmemory" + env.get("suffix", "") + env["LIBSUFFIX"] - library = memory_env.StaticLibrary(target=out_dir + "/" + library_name, source=sources) - Default([config, node_size_impl, library]) - - env.memory = {} - env.memory["INCPATH"] = [memory_env.Dir(include_path)] - - env.Append(CPPPATH=env.memory["INCPATH"]) - env.Append(LIBPATH=[memory_env.Dir(out_dir)]) - env.Prepend(LIBS=[library_name]) - - env.exposed_includes += env.memory["INCPATH"] - - -def build_spdlog(env): - import os - - spdlog_level = "SPDLOG_LEVEL_INFO" - if "SPDLOG_LEVEL" in os.environ: - spdlog_level = os.environ["SPDLOG_LEVEL"].upper() - if not spdlog_level.startswith("SPDLOG_LEVEL_"): - spdlog_level = "SPDLOG_LEVEL_" + spdlog_level - if spdlog_level not in [ - "SPDLOG_LEVEL_TRACE", - "SPDLOG_LEVEL_DEBUG", - "SPDLOG_LEVEL_INFO", - "SPDLOG_LEVEL_WARN", - "SPDLOG_LEVEL_ERROR", - "SPDLOG_LEVEL_CRITICAL", - "SPDLOG_LEVEL_OFF", - ]: - print("SPDLOG_LEVEL can only be trace, debug, info, warn, error, critical, or off") - exit(255) - elif env["dev_build"]: - spdlog_level = "SPDLOG_LEVEL_DEBUG" - elif env["target"] == "template_release" or env["optimize"] in ["speed", "size"]: - spdlog_level = "SPDLOG_LEVEL_WARN" - - env.Append( - CPPDEFINES=[ - ("SPDLOG_ACTIVE_LEVEL", spdlog_level), - "SPDLOG_FMT_EXTERNAL", - "SPDLOG_COMPILED_LIB", - "SPDLOG_NO_EXCEPTIONS", - ] - ) - if env["platform"] != "macos": - env.Append(CPPDEFINES=["SPDLOG_FWRITE_UNLOCKED"]) - spdlog_env = env.Clone() - - include_path = "spdlog/include" - source_path = "spdlog/src" - source_dir = spdlog_env.Dir(source_path) - - env.spdlog = {} - env.spdlog["INCPATH"] = [spdlog_env.Dir(include_path)] - - spdlog_env.Append(CPPPATH=[source_dir, env.spdlog["INCPATH"]]) - # Mirror spdlog's source tree into the per-config build dir. - build_root = (env.get("build_dir") or env.Dir("#").abspath).replace("\\", "/") - spdlog_out = build_root + "/spdlog/" + source_path - spdlog_env.VariantDir(spdlog_out, source_path, duplicate=True) - sources = env.GlobRecursive("*.cpp", [spdlog_out]) - env.spdlog_sources = sources - - library_name = "libspdlog" + env.get("suffix", "") + env["LIBSUFFIX"] - library = spdlog_env.StaticLibrary(target=spdlog_out + "/" + library_name, source=sources) - Default(library) - - env.Append(CPPPATH=[env.spdlog["INCPATH"]]) - if env.get("is_msvc", False): - env.Append(CXXFLAGS=[f"/external:I{env.spdlog['INCPATH'][0]}"]) - else: - env.Append(CXXFLAGS=["-isystem", env.spdlog["INCPATH"][0]]) - env.Append(LIBPATH=[spdlog_env.Dir(spdlog_out)]) - env.Prepend(LIBS=[library_name]) - - env.exposed_includes += env.spdlog["INCPATH"] - - -def build_xoshiro(env): - include_path = "xoshiro" - env.xoshiro = {} - env.xoshiro["INCPATH"] = [env.Dir(include_path)] - env.Append(CPPPATH=env.xoshiro["INCPATH"]) - env.exposed_includes += env.xoshiro["INCPATH"] - - -def build_type_safe(env): - import os - - arithmetic_policy = 1 - if "TYPE_SAFE_ARITHMETIC_POLICY" in os.environ: - arithmetic_policy = os.environ["TYPE_SAFE_ARITHMETIC_POLICY"].upper().replace(" ", "_") - if arithmetic_policy not in [ - "ub", - "undefined_behavior", - "undefined", - "checked", - "default", - "0", - "1", - "2", - ]: - print("TYPE_SAFE_ARITHMETIC_POLICY can only be undefined, checked, or default") - exit(255) - match arithmetic_policy: - case "default": - arithmetic_policy = 0 - case "ub", "undefined_behavior", "undefined": - arithmetic_policy = 1 - case "checked": - arithmetic_policy = 2 - case "0", "1", "2": - arithmetic_policy = int(arithmetic_policy) - elif env["dev_build"]: - arithmetic_policy = 2 - - env.Append( - CPPDEFINES=[ - ("TYPE_SAFE_ENABLE_ASSERTIONS", 1 if env["target"] == "template_release" else 0), - ("TYPE_SAFE_ENABLE_PRECONDITION_CHECKS", 1), - ("TYPE_SAFE_ENABLE_WRAPPER", 1 if env["target"] == "template_release" else 0), - ("TYPE_SAFE_ARITHMETIC_POLICY", arithmetic_policy), - ] - ) - - include_path = "type_safe/include" - debug_assert_include_path = "type_safe/external/debug_assert" - env.type_safe = {} - env.type_safe["INCPATH"] = [env.Dir(include_path), env.Dir(debug_assert_include_path)] - env.Append(CPPPATH=[env.type_safe["INCPATH"]]) - if env.get("is_msvc", False): - env.Append(CXXFLAGS=[f"/external:I{env.type_safe['INCPATH'][0]}"]) - else: - env.Append(CXXFLAGS=["-isystem", env.type_safe["INCPATH"][0]]) - env.exposed_includes += env.type_safe["INCPATH"] - - -def build_int128(env): - include_path = "int128/include" - env.int128 = {} - env.int128["INCPATH"] = [env.Dir(include_path)] - env.Append(CPPPATH=env.int128["INCPATH"]) - env.exposed_includes += env.int128["INCPATH"] - - -def link_tbb(env): - import sys - - if not env.get("is_msvc", False) and not env.get("use_mingw", False) and sys.platform != "darwin": - env.ParseConfig("pkg-config tbb --cflags --libs") - - -if env.get("is_msvc", False): - env.AppendUnique(CXXFLAGS=["/external:W0"]) - -build_openvic_dataloader(env) -build_lexy_vdf(env) -build_ordered_map(env) -build_colony(env) -build_function2(env) -build_std_function(env) -build_memory(env) -build_spdlog(env) -build_xoshiro(env) -build_type_safe(env) -build_int128(env) -link_tbb(env) diff --git a/deps/function2 b/deps/function2 deleted file mode 160000 index 43fc0ca47..000000000 --- a/deps/function2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 43fc0ca473ecb081918709bd7d524d84c2ff8dce diff --git a/deps/int128 b/deps/int128 deleted file mode 160000 index ea0f85e69..000000000 --- a/deps/int128 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ea0f85e693767ae667e26d66022d6ed68d3425de diff --git a/deps/lexy-vdf b/deps/lexy-vdf index c6d02d1a8..b121e98ee 160000 --- a/deps/lexy-vdf +++ b/deps/lexy-vdf @@ -1 +1 @@ -Subproject commit c6d02d1a8d982e3f05552492ac075415d00bd565 +Subproject commit b121e98ee59a3b76a946d7edb9882591d41d6c44 diff --git a/deps/memory b/deps/memory deleted file mode 160000 index 92a5bfc53..000000000 --- a/deps/memory +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 92a5bfc53b97955b3d685af679636e87d0f5d15a diff --git a/deps/openvic-dataloader b/deps/openvic-dataloader index 698b47c8f..56bc03550 160000 --- a/deps/openvic-dataloader +++ b/deps/openvic-dataloader @@ -1 +1 @@ -Subproject commit 698b47c8f93611af55d0eff664337802cb91c286 +Subproject commit 56bc03550db928ae5d43387ae8010eefbb38169b diff --git a/deps/ordered-map b/deps/ordered-map deleted file mode 160000 index bd8d5ef41..000000000 --- a/deps/ordered-map +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bd8d5ef4149cd40783a486011778a2e7eedde441 diff --git a/deps/plf_colony b/deps/plf_colony deleted file mode 160000 index fbc8483fb..000000000 --- a/deps/plf_colony +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fbc8483fb785c431dc4410396815abf886728c40 diff --git a/deps/spdlog b/deps/spdlog deleted file mode 160000 index f1d748e5e..000000000 --- a/deps/spdlog +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f1d748e5e3edfa4b1778edea003bac94781bc7b7 diff --git a/deps/std_function/func b/deps/std_function/func deleted file mode 160000 index dbf2df963..000000000 --- a/deps/std_function/func +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dbf2df963e6d0a47ab5e21e93c838233cd8b6b03 diff --git a/deps/type_safe b/deps/type_safe deleted file mode 160000 index 292e8c127..000000000 --- a/deps/type_safe +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 292e8c127037e33d92f8f52dab0f1184993942d0 diff --git a/deps/xoshiro b/deps/xoshiro deleted file mode 160000 index 19bcbb2ce..000000000 --- a/deps/xoshiro +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 19bcbb2ce0ed158233187f524fd0964c105a65b3 diff --git a/docs/ecs/determinism.md b/docs/ecs/determinism.md index bbe12639c..a857060ec 100644 --- a/docs/ecs/determinism.md +++ b/docs/ecs/determinism.md @@ -338,7 +338,7 @@ The existing gates, each guarding a different determinism risk: | `tests/src/ecs/Checksum.cpp` | Full-state checksum determinism, sensitivity, and the checksum-based worker-count gate above. | | `tests/src/ecs/IdentitySnapshotInvariance.cpp` | Save/load: `digest(tick^k(restore(snapshot(s)))) == digest(tick^k(s))` at every worker count, including exact `EntityID` reuse from the restored free list. | -Run them with `scons run_ovsim_tests=yes`. When you add game systems, extend the gate: a worker-count sweep over a realistic scenario, digested with `world_checksum`, is cheap to write and catches the whole class of races and order-dependencies that code review misses. Remember the gate's limits, though — it catches scheduling races only probabilistically. The declarations (rule 3) and the type-level checksum enforcement are what make determinism hold by construction; the gate is the alarm, not the lock. +Run them with `ctest --preset -debug` (after building with `cmake --build --preset -debug`). When you add game systems, extend the gate: a worker-count sweep over a realistic scenario, digested with `world_checksum`, is cheap to write and catches the whole class of races and order-dependencies that code review misses. Remember the gate's limits, though — it catches scheduling races only probabilistically. The declarations (rule 3) and the type-level checksum enforcement are what make determinism hold by construction; the gate is the alarm, not the lock. ## Cross-references diff --git a/pyproject.toml b/pyproject.toml index 7e60f8d31..c29baeb92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,6 @@ explicit_package_bases = true python_version = "3.10" [tool.ruff] -extend-include = ["SConstruct", "SCsub"] line-length = 120 target-version = "py310" @@ -21,12 +20,6 @@ extend-select = [ "I", # isort ] -[tool.ruff.lint.per-file-ignores] -"{SConstruct,SCsub}" = [ - "E402", # Module level import not at top of file - "F821", # Undefined name -] - [tool.typos] files.extend-exclude = [".mailmap", "*.gitignore", "*.po", "*.pot", "*.rc"] default.extend-ignore-re = [ diff --git a/scripts b/scripts deleted file mode 160000 index 9f8318fb9..000000000 --- a/scripts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9f8318fb9f34fc49709ad8227577438e488dc88b diff --git a/src/openvic-simulation/country/CountryDefinition.cpp b/src/openvic-simulation/country/CountryDefinition.cpp index cc7111d42..782463edf 100644 --- a/src/openvic-simulation/country/CountryDefinition.cpp +++ b/src/openvic-simulation/country/CountryDefinition.cpp @@ -239,5 +239,3 @@ bool CountryDefinitionManager::load_country_data_file( ); return ret; } - -template struct fmt::formatter; diff --git a/src/openvic-simulation/country/CountryDefinition.hpp b/src/openvic-simulation/country/CountryDefinition.hpp index 47a07c250..d61a62625 100644 --- a/src/openvic-simulation/country/CountryDefinition.hpp +++ b/src/openvic-simulation/country/CountryDefinition.hpp @@ -82,5 +82,3 @@ namespace OpenVic { ); }; } - -extern template struct fmt::formatter; diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp index 3978b10f7..28a78bf3f 100644 --- a/src/openvic-simulation/country/CountryInstance.cpp +++ b/src/openvic-simulation/country/CountryInstance.cpp @@ -2578,5 +2578,3 @@ CountryInstance::good_data_t& CountryInstance::get_good_data(GoodDefinition cons CountryInstance::good_data_t const& CountryInstance::get_good_data(GoodDefinition const& good_definition) const { return goods_data.at_index(good_definition.index); } - -template struct fmt::formatter; diff --git a/src/openvic-simulation/country/CountryInstance.hpp b/src/openvic-simulation/country/CountryInstance.hpp index ed9f5958f..06ff9f050 100644 --- a/src/openvic-simulation/country/CountryInstance.hpp +++ b/src/openvic-simulation/country/CountryInstance.hpp @@ -723,5 +723,3 @@ namespace OpenVic { fixed_point_t apply_tariff(const fixed_point_t money_spent_on_imports); }; } - -extern template struct fmt::formatter; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..0df122ed8 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,39 @@ +# snitch honors these as plain variables (CMP0077 NEW via +# openvic_setup_base_flags). Only deltas from upstream defaults are set; +# everything else (SNITCH_DEFINE_MAIN, timings, reporters, ...) already +# matches upstream. Exceptions are additionally forced off at compile time by +# snitch itself when _HAS_EXCEPTIONS=0 / -fno-exceptions is in effect. +# +# NOTE: openvic-dataloader pins an older snitch under the same FetchContent +# name; first declaration wins. The two test suites are never enabled in one +# build by default (tests default ON only for the top-level project), so in +# practice each repo's own pin applies. +set(SNITCH_WITH_EXCEPTIONS OFF) +set(SNITCH_WITH_MULTITHREADING OFF) +openvic_declare_dep(snitch + REPO snitch-org/snitch + SHA 2a594333016b9cfc1f90b116184cb0bdbf98a480 # v1.3.1 + SHA256 7512cd7ec34d02faea463e7588ccbcfddc229a7e2243dd49e421b7158741c103 +) +FetchContent_MakeAvailable(snitch) + +if(OPENVIC_SIM_BUILD_TESTS) + file(GLOB_RECURSE ovsim_tests_sources CONFIGURE_DEPENDS src/*.cpp) + + add_executable(openvic-simulation-tests ${ovsim_tests_sources}) + target_compile_definitions(openvic-simulation-tests PRIVATE OPENVIC_SIMULATION_TESTS) + target_include_directories(openvic-simulation-tests PRIVATE src) + target_link_libraries(openvic-simulation-tests PRIVATE openvic::simulation snitch::snitch) + set_target_properties( + openvic-simulation-tests + PROPERTIES + OUTPUT_NAME "openvic-simulation.tests" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$" + ) + + add_test(NAME openvic-simulation-tests COMMAND openvic-simulation-tests) +endif() + +if(OPENVIC_SIM_BUILD_BENCHMARKS) + add_subdirectory(benchmarks) +endif() diff --git a/tests/SCsub b/tests/SCsub deleted file mode 100644 index 201d98193..000000000 --- a/tests/SCsub +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python -import os -import subprocess - -from SCons.Script.SConscript import SConsEnvironment - - -def UnitTestPostAction(target=None, source=None, env=None): - print() - return subprocess.run([source[0].path]).returncode - - -def BuildUnitTest(env, **kwargs): - test = env.Program(**kwargs) - env.NoCache(test) - return test - - -def RunUnitTest(env): - # Alias-based runner: AlwaysBuild on the alias makes the run-action fire on - # every invocation, but the unit-test Program target underneath is only - # re-linked when its sources/lib deps actually changed. - run_alias = env.Alias("run_tests", env.unit_test, env.Action(UnitTestPostAction, None)) - env.AlwaysBuild(run_alias) - Default(run_alias) - - -SConsEnvironment.BuildUnitTest = BuildUnitTest -SConsEnvironment.RunUnitTest = RunUnitTest - -Import("env") - -BINDIR = "bin" - -env.openvic_simulation_tests = {} - -# For the reference: -# - CCFLAGS are compilation flags shared between C and C++ -# - CFLAGS are for C-specific compilation flags -# - CXXFLAGS are for C++-specific compilation flags -# - CPPFLAGS are for pre-processor flags -# - CPPDEFINES are for pre-processor defines -# - LINKFLAGS are for linking flags - -# tweak this if you want to use different folders, or more folders, to store your source code in. -source_path = "src" # relative to this SCsub (i.e. tests/src under the sim root) - -tests_name = "openvic-simulation" -tests_env = env.Clone() -# Out-of-source build: variant tree holds object files only. -tests_variant = env["build_dir"] + "/tests/" + source_path -tests_env.VariantDir(tests_variant, source_path, duplicate=False) -tests_env.Append(CPPDEFINES=["OPENVIC_SIMULATION_TESTS"]) -tests_env.Append(CPPPATH=[tests_env.Dir(tests_variant), tests_env.Dir(source_path)]) -tests_env.tests_sources = env.GlobRecursiveVariant("*.cpp", source_path, tests_variant) - -SConscript("deps/SCsub", {"env": tests_env}) - -tests_env.unit_test = tests_env.BuildUnitTest( - source=tests_env.tests_sources, target=os.path.join(BINDIR, tests_name), PROGSUFFIX=".tests" + env["PROGSUFFIX"] -) -Default(tests_env.unit_test) - -Return("tests_env") diff --git a/tests/benchmarks/CMakeLists.txt b/tests/benchmarks/CMakeLists.txt new file mode 100644 index 000000000..9db6e3cc3 --- /dev/null +++ b/tests/benchmarks/CMakeLists.txt @@ -0,0 +1,30 @@ +# nanobench (the OpenVic fork with exceptions/RTTI disabled): its own +# CMakeLists only supports standalone builds, so fetch source-only and +# hand-roll a static library from the single implementation TU. +openvic_declare_dep(nanobench + REPO Spartan322/nanobench + SHA f49b052a68bc05d5497acf1f6c0c2d6ef65c6eef # disable/exceptions-and-rtti + SHA256 513c81406c5dbc8e1840efbef41a924c7376d0538550bddde9bdf9ee5c62f320 + DOWNLOAD_ONLY +) +FetchContent_MakeAvailable(nanobench) +add_library(ov_nanobench STATIC ${nanobench_SOURCE_DIR}/src/test/app/nanobench.cpp) +target_include_directories(ov_nanobench SYSTEM PUBLIC ${nanobench_SOURCE_DIR}/src/include) + +file(GLOB_RECURSE ovsim_benchmarks_sources CONFIGURE_DEPENDS src/*.cpp) + +# Benchmarks use snitch's macros too; snitch comes from the parent +# tests/CMakeLists.txt, which always includes this directory. +add_executable(openvic-simulation-benchmarks ${ovsim_benchmarks_sources}) +target_compile_definitions(openvic-simulation-benchmarks PRIVATE OPENVIC_SIMULATION_BENCHMARKS) +target_include_directories(openvic-simulation-benchmarks PRIVATE src) +target_link_libraries(openvic-simulation-benchmarks PRIVATE openvic::simulation snitch::snitch ov_nanobench) +set_target_properties( + openvic-simulation-benchmarks + PROPERTIES + OUTPUT_NAME "openvic-simulation.benchmarks" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$" +) +# No add_test: benchmarks are built in CI for compile coverage but run +# manually (they take real time and their numbers are meaningless on shared +# runners). diff --git a/tests/benchmarks/SCsub b/tests/benchmarks/SCsub deleted file mode 100644 index 66e1fdf76..000000000 --- a/tests/benchmarks/SCsub +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python -import os -import subprocess - -from SCons.Script.SConscript import SConsEnvironment - - -def BenchmarkPostAction(target=None, source=None, env=None): - print() - return subprocess.run([source[0].path]).returncode - - -def BuildBenchmarks(env, **kwargs): - benchmark = env.Program(**kwargs) - env.NoCache(benchmark) - return benchmark - - -def RunBenchmarks(env): - # Alias-based runner: AlwaysBuild on the alias makes the run-action fire on - # every invocation, but the benchmark Program target underneath is only - # re-linked when its sources/lib deps actually changed. - run_alias = env.Alias("run_benchmarks", env.benchmark, env.Action(BenchmarkPostAction, None)) - env.AlwaysBuild(run_alias) - Default(run_alias) - - -SConsEnvironment.BuildBenchmarks = BuildBenchmarks -SConsEnvironment.RunBenchmarks = RunBenchmarks - -Import("env") - -BINDIR = "bin" - -env.openvic_simulation_benchmarks = {} - -# For the reference: -# - CCFLAGS are compilation flags shared between C and C++ -# - CFLAGS are for C-specific compilation flags -# - CXXFLAGS are for C++-specific compilation flags -# - CPPFLAGS are for pre-processor flags -# - CPPDEFINES are for pre-processor defines -# - LINKFLAGS are for linking flags - -# tweak this if you want to use different folders, or more folders, to store your source code in. -source_path = "src" # relative to this SCsub (i.e. tests/benchmarks/src under the sim root) - -benchmarks_name = "openvic-simulation" -benchmarks_env = env.Clone() -# Out-of-source build: variant tree holds object files only. -benchmarks_variant = env["build_dir"] + "/tests/benchmarks/" + source_path -benchmarks_env.VariantDir(benchmarks_variant, source_path, duplicate=False) -benchmarks_env.Append(CPPDEFINES=["OPENVIC_SIMULATION_BENCHMARKS"]) - -benchmarks_env.Append(CPPPATH=[benchmarks_env.Dir(benchmarks_variant), benchmarks_env.Dir(source_path)]) -benchmarks_env.benchmarks_sources = env.GlobRecursiveVariant("*.cpp", source_path, benchmarks_variant) - -SConscript("deps/SCsub", {"env": benchmarks_env, "parent_env": env}) - -benchmarks_env.benchmark = benchmarks_env.BuildBenchmarks( - source=benchmarks_env.benchmarks_sources, - target=os.path.join(BINDIR, benchmarks_name), - PROGSUFFIX=".benchmarks" + env["PROGSUFFIX"], -) -Default(benchmarks_env.benchmark) - -Return("benchmarks_env") diff --git a/tests/benchmarks/deps/SCsub b/tests/benchmarks/deps/SCsub deleted file mode 100644 index a93a3e90b..000000000 --- a/tests/benchmarks/deps/SCsub +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -import os - -Import("env") -Import("parent_env") - - -def build_nanobench(env): - nanobench_env = env.Clone() - - include_path = "nanobench/src/include" - source_path = "nanobench/src/test/app" - # Out-of-source build: variant tree holds object files and the static library, - # so the nanobench submodule working tree stays clean. - nanobench_variant = env["build_dir"] + "/tests/benchmarks/deps/nanobench" - variant_source_path = nanobench_variant + "/src/test/app" - nanobench_env.VariantDir(nanobench_variant, "nanobench", duplicate=False) - - nanobench_env.Append(CPPPATH=[nanobench_env.Dir(include_path)]) - sources = nanobench_env.GlobRecursiveVariant("nanobench.cpp", source_path, variant_source_path) - - library_name = "libnanobench" + env["LIBSUFFIX"] - library = nanobench_env.StaticLibrary(target=os.path.join(nanobench_variant, library_name), source=sources) - Default(library) - - env.Append(CPPPATH=[nanobench_env.Dir(include_path)]) - env.Append(LIBPATH=[nanobench_env.Dir(nanobench_variant)]) - env.Prepend(LIBS=[library_name]) - - -def build_snitch(env, parent_env): - try: - parent_env.snitch_env - SConscript("../../deps/SCsub", {"env": parent_env}) - except AttributeError: - SConscript("../../deps/SCsub", {"env": env}) - - -build_nanobench(env) -build_snitch(env, parent_env) diff --git a/tests/benchmarks/deps/nanobench b/tests/benchmarks/deps/nanobench deleted file mode 160000 index f49b052a6..000000000 --- a/tests/benchmarks/deps/nanobench +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f49b052a68bc05d5497acf1f6c0c2d6ef65c6eef diff --git a/tests/deps/SCsub b/tests/deps/SCsub deleted file mode 100644 index 67f2797da..000000000 --- a/tests/deps/SCsub +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/env python -import subprocess -from pathlib import Path - -Import("env") - - -def generate_snitch_config_header(target, source, env): - header = [] - - header.append("// THIS FILE IS GENERATED. EDITS WILL BE LOST.") - header.append("") - - header_file_path = Path(str(target[0])) - - include_gen_folder = Path(str(header_file_path.parent)) - include_gen_folder.mkdir(parents=True, exist_ok=True) - - header_guard = "SNITCH_CONFIG_HPP" - header.append(f"#ifndef {header_guard}") - header.append(f"#define {header_guard}") - header.append("") - header.append("#include // for C++ feature check macros") - - for key, val in env.config_data.items(): - if val is True: - val = 1 - elif val is False: - val = 0 - header += ( - f""" -#if !defined({key}) -# define {key} {val} -#endif""" - ).split("\n") - - header += """ -#if defined(_MSC_VER) -# if defined(_KERNEL_MODE) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS) -# define SNITCH_EXCEPTIONS_NOT_AVAILABLE -# endif -#elif defined(__clang__) || defined(__GNUC__) -# if !defined(__EXCEPTIONS) -# define SNITCH_EXCEPTIONS_NOT_AVAILABLE -# endif -#endif - -#if defined(SNITCH_EXCEPTIONS_NOT_AVAILABLE) -# undef SNITCH_WITH_EXCEPTIONS -# define SNITCH_WITH_EXCEPTIONS 0 -#endif - -#if SNITCH_WITH_MULTITHREADING -# define SNITCH_THREAD_LOCAL thread_local -#else -# define SNITCH_THREAD_LOCAL -#endif - -#if !defined(__cpp_lib_bit_cast) -# undef SNITCH_CONSTEXPR_FLOAT_USE_BITCAST -# define SNITCH_CONSTEXPR_FLOAT_USE_BITCAST 0 -#endif - -#if (!defined(__cpp_lib_to_chars)) || (defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE <= 11) || \ - (defined(_LIBCPP_VERSION) && _LIBCPP_VERSION <= 14000) || \ - (defined(_MSC_VER) && _MSC_VER <= 1924) -# undef SNITCH_APPEND_TO_CHARS -# define SNITCH_APPEND_TO_CHARS 0 -#endif - -#if SNITCH_SHARED_LIBRARY -# if defined(_MSC_VER) -# if defined(SNITCH_EXPORTS) -# define SNITCH_EXPORT __declspec(dllexport) -# else -# define SNITCH_EXPORT __declspec(dllimport) -# endif -# elif defined(__clang__) || defined(__GNUC__) -# define SNITCH_EXPORT [[gnu::visibility("default")]] -# else -# define SNITCH_EXPORT -# endif -#else -# define SNITCH_EXPORT -#endif - """.split("\n") - - header.append("") - header.append(f"#endif // {header_guard}") - header.append("") - - with header_file_path.open("w+", encoding="utf-8") as header_file: - header_file.write("\n".join(header)) - - -def build_snitch(env): - major_version = 1 - minor_version = 2 - patch_version = 5 - git_run = subprocess.run(["git", "describe", "--tags", "--abbrev=0"], text=True, cwd="snitch/", capture_output=True) - if git_run.returncode == 0: - tag = git_run.stdout.strip().removeprefix("v").split(".") - major_version = tag[0] - minor_version = tag[1] - patch_version = tag[2] - - SHARED_BUILD = False - HEADER_ONLY = False - UNITY_BUILD = True - - SNITCH_VERSION = "{}.{}.{}".format(major_version, minor_version, patch_version) - - snitch_env = None - try: - snitch_env = env.snitch_env - except AttributeError: - snitch_env = env.Clone() - - snitch_env.Append(BUILDERS={"GenerateSnitchConfig": snitch_env.Builder(action=generate_snitch_config_header)}) - - snitch_env.config_data = { - "SNITCH_ENABLE": True, # Enable snitch at build time. - "SNITCH_MAX_TEST_CASES": 5000, # "Maximum number of test cases in a test application." - "SNITCH_MAX_NESTED_SECTIONS": 8, # "Maximum depth of nested sections in a test case." - "SNITCH_MAX_EXPR_LENGTH": 1024, # "Maximum length of a printed expression when reporting failure." - "SNITCH_MAX_MESSAGE_LENGTH": 1024, # "Maximum length of error or status messages." - "SNITCH_MAX_TEST_NAME_LENGTH": 1024, # "Maximum length of a test case name." - "SNITCH_MAX_TAG_LENGTH": 256, # "Maximum length of a test tag." - "SNITCH_MAX_CAPTURES": 8, # "Maximum number of captured expressions in a test case." - "SNITCH_MAX_CAPTURE_LENGTH": 256, # "Maximum length of a captured expression." - "SNITCH_MAX_UNIQUE_TAGS": 1024, # "Maximum number of unique tags in a test application." - "SNITCH_MAX_COMMAND_LINE_ARGS": 1024, # "Maximum number of command line arguments to a test application." - "SNITCH_MAX_REGISTERED_REPORTERS": 8, # "Maximum number of registered reporter that can be selected from the command line." - "SNITCH_MAX_PATH_LENGTH": 1024, # "Maximum length of a file path when writing output to file." - "SNITCH_MAX_REPORTER_SIZE_BYTES": 128, # "Maximum size (in bytes) of a reporter object." - "SNITCH_DEFINE_MAIN": True, # "Define main() in snitch -- disable to provide your own main() function." - "SNITCH_WITH_EXCEPTIONS": False, # "Use exceptions in snitch implementation -- will be forced OFF if exceptions are not available." - "SNITCH_WITH_MULTITHREADING": False, # "Make the testing framework thread-safe -- disable if multithreading is not needed." - "SNITCH_WITH_TIMINGS": True, # "Measure the time taken by each test case -- disable to speed up tests." - "SNITCH_WITH_SHORTHAND_MACROS": True, # "Use short names for test macros -- disable if this causes conflicts." - "SNITCH_CONSTEXPR_FLOAT_USE_BITCAST": True, # "Use std::bit_cast if available to implement exact constexpr float-to-string conversion." - "SNITCH_APPEND_TO_CHARS": True, # "Use std::to_chars for string conversions -- disable for greater compatibility with a slight performance cost." - "SNITCH_DEFAULT_WITH_COLOR": True, # "Enable terminal colors by default -- can also be controlled by command line interface." - "SNITCH_DECOMPOSE_SUCCESSFUL_ASSERTIONS": False, # "Enable expression decomposition even for successful assertions -- more expensive." - "SNITCH_WITH_ALL_REPORTERS": True, # "Allow all built-in reporters to be selected from the command line -- disable for faster compilation." - "SNITCH_WITH_TEAMCITY_REPORTER": False, # "Allow the TeamCity reporter to be selected from the command line -- enable if needed." - "SNITCH_WITH_CATCH2_XML_REPORTER": False, # "Allow the Catch2 XML reporter to be selected from the command line -- enable if needed." - "SNITCH_HEADER_ONLY": False, # "Create a single-header header-only version of snitch." - "SNITCH_UNITY_BUILD": UNITY_BUILD, # "Build sources as single file instead of separate files (faster full build)." - "SNITCH_DO_TEST": True, # "Build tests." - } - - snitch_full_version = "" - git_run = subprocess.run(["git", "log", "-1", "--format=%h"], text=True, cwd="snitch/", capture_output=True) - if git_run.returncode == 0: - snitch_full_version = git_run.stdout.strip() - - snitch_full_version = SNITCH_VERSION + (".{}".format(snitch_full_version) if snitch_full_version else "") - - snitch_env.Append( - CPPDEFINES=[ - ("SNITCH_VERSION", f'\\"{SNITCH_VERSION}\\"'), - ("SNITCH_FULL_VERSION", f'\\"{snitch_full_version}\\"'), - ("SNITCH_VERSION_MAJOR", major_version), - ("SNITCH_VERSION_MINOR", minor_version), - ("SNITCH_VERSION_PATCH", patch_version), - ] - ) - snitch_env.Append(CPPDEFINES=[("SNITCH_SHARED_LIBRARY", 1 if not HEADER_ONLY and SHARED_BUILD else 0)]) - - include_path = "snitch/include" - source_path = "snitch/src" - unity_source = "snitch.cpp" - - config = snitch_env.GenerateSnitchConfig( - snitch_env.File(Path(include_path) / "snitch" / "snitch_config.hpp"), "SCsub" - ) - - snitch_env.Append(CPPPATH=[[snitch_env.Dir(p) for p in [source_path, include_path]]]) - # Mirror snitch's source tree into the per-config build dir - build_root = (env.get("build_dir") or env.Dir("#").abspath).replace("\\", "/") - snitch_out = build_root + "/snitch/" + source_path - snitch_env.VariantDir(snitch_out, source_path, duplicate=True) - sources = snitch_env.GlobRecursive("*.cpp", [snitch_out], snitch_out + "/" + unity_source) - - if UNITY_BUILD: - sources = [snitch_env.File(snitch_out + "/" + unity_source)] - - env.snitch_sources = sources - - if not HEADER_ONLY: - library = None - project_name = "snitch" - library_name = "lib" + project_name + env.get("suffix", "") + env["LIBSUFFIX"] - if SHARED_BUILD: - if snitch_env.get("is_msvc", False): - pass - else: - snitch_env.Append(CXXFLAGS=["-fvisibility=hidden", "-fvisibility-inlines-hidden"]) - - library = snitch_env.SharedLibrary(target=snitch_out + "/" + library_name, source=sources) - else: - library = snitch_env.StaticLibrary(target=snitch_out + "/" + library_name, source=sources) - env.Append(LIBPATH=[snitch_env.Dir(snitch_out)]) - env.Prepend(LIBS=[library_name]) - - Default([config, library]) - - env.Append(CPPPATH=[snitch_env.Dir(include_path)]) - else: - env.Append(CPPPATH=[[snitch_env.Dir(p) for p in [source_path, include_path]]]) - Default(config) - - env.snitch_env = snitch_env - - -build_snitch(env) diff --git a/tests/deps/snitch b/tests/deps/snitch deleted file mode 160000 index 2a5943330..000000000 --- a/tests/deps/snitch +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2a594333016b9cfc1f90b116184cb0bdbf98a480