@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.21)
22project (MeshCraft VERSION 0.1.0 LANGUAGES C CXX )
33
44include (CTest )
5+ include (GNUInstallDirs )
56
67message (STATUS "Project: ${PROJECT_NAME } " )
78message (STATUS "Platform: ${CMAKE_SYSTEM_NAME } " )
@@ -366,6 +367,51 @@ FetchContent_MakeAvailable(sol2)
366367# mc3togltf — MC3 to glTF/GLB export tool (uses Mc3 + tinyobjloader already fetched above)
367368add_subdirectory (mc3togltf )
368369
370+ # SYS-W11-05: build only the two standalone tools and their format libraries
371+ # before creating the relocatable release archive. CPack's default `package`
372+ # target depends on the entire editor graph, which would make a CLI release
373+ # depend on unrelated CNA backends and tooling. This target deliberately
374+ # uses the normal install rules but keeps its build dependency narrow.
375+ add_custom_target (meshcraft_cli_release
376+ COMMAND "${CMAKE_COMMAND } "
377+ "-DMC_RELEASE_BUILD_DIR=${CMAKE_BINARY_DIR } "
378+ "-DMC_RELEASE_VERSION=${PROJECT_VERSION } "
379+ "-DMC_RELEASE_SYSTEM=${CMAKE_SYSTEM_NAME } "
380+ -P "${CMAKE_CURRENT_SOURCE_DIR } /cmake/CreateCliRelease.cmake"
381+ DEPENDS mc3tomcb mc3togltf
382+ COMMENT "Creating the relocatable MeshCraft CLI release archive" )
383+
384+ if (BUILD_TESTING)
385+ add_test (
386+ NAME package_consumer_smoke
387+ COMMAND "${CMAKE_COMMAND } "
388+ "-DMC_PACKAGE_BUILD_DIR=${CMAKE_BINARY_DIR } "
389+ "-DMC_PACKAGE_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR } /test/cmake/package_consumer"
390+ -P "${CMAKE_CURRENT_SOURCE_DIR } /test/cmake/package_consumer_smoke.cmake" )
391+ set_tests_properties (package_consumer_smoke PROPERTIES
392+ TIMEOUT 60
393+ LABELS "package" )
394+
395+ # SYS-W11-06: fixed binary hashes produced from a checked-in scene are a
396+ # compact cross-platform contract for both standalone CLI tools. The
397+ # Windows artifact job below runs the same source-backed fixture before
398+ # publishing its executables.
399+ find_program (MESHCRAFT_PYTHON3 NAMES python3 python )
400+ if (MESHCRAFT_PYTHON3)
401+ add_test (
402+ NAME cli_cross_platform_fixture
403+ COMMAND "${MESHCRAFT_PYTHON3} "
404+ "${CMAKE_CURRENT_SOURCE_DIR } /test/cross_platform_cli_fixture_test.py"
405+ "$<TARGET_FILE :mc3tomcb >"
406+ "$<TARGET_FILE :mc3togltf >"
407+ "${CMAKE_CURRENT_SOURCE_DIR } /test/house.mc3.xml"
408+ "${CMAKE_BINARY_DIR } /cli-cross-platform-fixture" )
409+ set_tests_properties (cli_cross_platform_fixture PROPERTIES
410+ TIMEOUT 30
411+ LABELS "export;portability" )
412+ endif ()
413+ endif ()
414+
369415# SQLite3 — model registry persistence (desktop builds only). Optional,
370416# not REQUIRED: ModelRegistry.cpp's MESHCRAFT_HAS_SQLITE3 guard already
371417# provides safe no-op stubs for its absence (STAB-0008/0366/0552/0563),
@@ -382,9 +428,25 @@ if(NOT EMSCRIPTEN AND NOT ANDROID)
382428 endif ()
383429endif ()
384430
431+ # mc3togltf is linked against these shared third-party libraries in the
432+ # default desktop build. Ship their runtime files in the same release
433+ # component so the CLI archive is usable outside the build tree.
434+ foreach (MESHCRAFT_CLI_RUNTIME_TARGET IN ITEMS manifold tinyobjloader)
435+ if (TARGET ${MESHCRAFT_CLI_RUNTIME_TARGET} )
436+ install (TARGETS ${MESHCRAFT_CLI_RUNTIME_TARGET}
437+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR} " COMPONENT release
438+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR} " COMPONENT release
439+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR} " COMPONENT release)
440+ endif ()
441+ endforeach ()
442+
385443# cpp-httplib + OpenSSL — AI Assistant (desktop builds only)
386444if (NOT EMSCRIPTEN AND NOT ANDROID )
387445 set (HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF CACHE BOOL "" FORCE )
446+ # MeshCraft packages its own CLI and format-library surface. Do not let
447+ # this FetchContent-only editor dependency add an unrelated install tree
448+ # or invoke CPack with its defaults.
449+ set (HTTPLIB_INSTALL OFF CACHE BOOL "" FORCE )
388450 FetchContent_Declare (httplib
389451 GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git
390452 GIT_TAG v0.18.3
@@ -625,10 +687,23 @@ if(MESH_CRAFT_BUILD_TESTING AND UNIX AND NOT ANDROID AND NOT EMSCRIPTEN)
625687 target_include_directories (coordinate_system_test PRIVATE
626688 ${CMAKE_CURRENT_SOURCE_DIR } /include
627689 ${CMAKE_CURRENT_SOURCE_DIR } /mc3/include )
690+ target_link_libraries (coordinate_system_test PRIVATE Mc3 )
628691 target_compile_features (coordinate_system_test PRIVATE cxx_std_23 )
629692 add_test (NAME coordinate_system COMMAND coordinate_system_test )
630693 set_tests_properties (coordinate_system PROPERTIES TIMEOUT 15 LABELS "unit" )
631694
695+ # SYS-W5-06: document-level degrees/radians and all six Euler orders are
696+ # interpreted by one CNA-free helper shared with mc3togltf. The test also
697+ # locks the degrees/XYZ normalization math used by the explicit command.
698+ add_executable (rotation_convention_algorithms_test
699+ ${CMAKE_CURRENT_SOURCE_DIR } /test/rotation_convention_algorithms_test.cpp )
700+ target_include_directories (rotation_convention_algorithms_test PRIVATE
701+ ${CMAKE_CURRENT_SOURCE_DIR } /include )
702+ target_compile_features (rotation_convention_algorithms_test PRIVATE cxx_std_23 )
703+ target_compile_options (rotation_convention_algorithms_test PRIVATE -Wall -Wextra )
704+ add_test (NAME rotation_convention_algorithms COMMAND rotation_convention_algorithms_test )
705+ set_tests_properties (rotation_convention_algorithms PROPERTIES TIMEOUT 15 LABELS "unit" )
706+
632707 # SYS-W3-01: Editor::KeybindingManager, extracted from
633708 # MeshCraftApplication (H9). No test existed for this subsystem before
634709 # the extraction. Needs real Microsoft::Xna::Framework::Input::Keys/
@@ -787,7 +862,8 @@ if(MESH_CRAFT_BUILD_TESTING AND UNIX AND NOT ANDROID AND NOT EMSCRIPTEN)
787862 # be tested without an editor window.
788863 add_executable (scene_history_test
789864 ${CMAKE_CURRENT_SOURCE_DIR } /test/scene_history_test.cpp
790- ${CMAKE_CURRENT_SOURCE_DIR } /src/MeshCraft/Editor/SceneHistory.cpp )
865+ ${CMAKE_CURRENT_SOURCE_DIR } /src/MeshCraft/Editor/SceneHistory.cpp
866+ ${CMAKE_CURRENT_SOURCE_DIR } /src/MeshCraft/Editor/UndoManager.cpp )
791867 target_include_directories (scene_history_test PRIVATE
792868 ${CMAKE_CURRENT_SOURCE_DIR } /include
793869 ${CMAKE_CURRENT_SOURCE_DIR } /mc3/include )
@@ -813,6 +889,35 @@ if(MESH_CRAFT_BUILD_TESTING AND UNIX AND NOT ANDROID AND NOT EMSCRIPTEN)
813889 add_test (NAME registry_insert_undo COMMAND registry_insert_undo_test )
814890 set_tests_properties (registry_insert_undo PROPERTIES TIMEOUT 15 LABELS "unit" )
815891
892+ # SYS-W3-03: RegistryWorkspace owns registry dialog lifecycle without
893+ # pulling MeshCraftApplication or ImGui into the controller-level test.
894+ add_executable (registry_workspace_test
895+ ${CMAKE_CURRENT_SOURCE_DIR } /test/registry_workspace_test.cpp
896+ ${CMAKE_CURRENT_SOURCE_DIR } /src/MeshCraft/Application/RegistryWorkspace.cpp
897+ ${CMAKE_CURRENT_SOURCE_DIR } /src/MeshCraft/ModelRegistry.cpp )
898+ target_include_directories (registry_workspace_test PRIVATE
899+ ${CMAKE_CURRENT_SOURCE_DIR } /include
900+ ${CMAKE_CURRENT_SOURCE_DIR } /src/MeshCraft )
901+ target_link_libraries (registry_workspace_test PRIVATE Mc3 )
902+ target_compile_features (registry_workspace_test PRIVATE cxx_std_23 )
903+ add_test (NAME registry_workspace COMMAND registry_workspace_test )
904+ set_tests_properties (registry_workspace PROPERTIES TIMEOUT 15 LABELS "unit;registry" )
905+
906+ # SYS-W3-04: Scripts/Triggers/States/Events now share one CNA-free
907+ # automation workspace. Its transient selection/preview lifecycle and
908+ # state-application semantics remain testable without an ImGui window.
909+ add_executable (automation_workspace_test
910+ ${CMAKE_CURRENT_SOURCE_DIR } /test/automation_workspace_test.cpp
911+ ${CMAKE_CURRENT_SOURCE_DIR } /src/MeshCraft/Application/AutomationWorkspace.cpp
912+ ${CMAKE_CURRENT_SOURCE_DIR } /src/MeshCraft/Editor/LuaScriptRunner.cpp )
913+ target_include_directories (automation_workspace_test PRIVATE
914+ ${CMAKE_CURRENT_SOURCE_DIR } /include
915+ ${CMAKE_CURRENT_SOURCE_DIR } /mc3/include )
916+ target_link_libraries (automation_workspace_test PRIVATE Mc3 lua54 sol2::sol2 )
917+ target_compile_features (automation_workspace_test PRIVATE cxx_std_23 )
918+ add_test (NAME automation_workspace COMMAND automation_workspace_test )
919+ set_tests_properties (automation_workspace PROPERTIES TIMEOUT 15 LABELS "unit;automation" )
920+
816921 # SYS-W3-01 Phase 6: Editor::WalkController, extracted from
817922 # MeshCraftApplication (H15, first-person walk mode). No test existed
818923 # for this subsystem before the extraction. Needs real
@@ -928,7 +1033,7 @@ if(MESH_CRAFT_BUILD_TESTING AND UNIX AND NOT ANDROID AND NOT EMSCRIPTEN)
9281033 target_include_directories (lua_script_runner_test PRIVATE
9291034 ${CMAKE_CURRENT_SOURCE_DIR } /include
9301035 ${CMAKE_CURRENT_SOURCE_DIR } /mc3/include )
931- target_link_libraries (lua_script_runner_test PRIVATE lua54 sol2::sol2 )
1036+ target_link_libraries (lua_script_runner_test PRIVATE Mc3 lua54 sol2::sol2 )
9321037 target_compile_features (lua_script_runner_test PRIVATE cxx_std_23 )
9331038 add_test (NAME lua_script_runner COMMAND lua_script_runner_test )
9341039 set_tests_properties (lua_script_runner PROPERTIES TIMEOUT 30 LABELS "unit" )
@@ -1122,7 +1227,7 @@ if(MESH_CRAFT_BUILD_TESTING AND UNIX AND NOT ANDROID AND NOT EMSCRIPTEN)
11221227 target_include_directories (trigger_fire_test PRIVATE
11231228 ${CMAKE_CURRENT_SOURCE_DIR } /include
11241229 ${CMAKE_CURRENT_SOURCE_DIR } /mc3/include )
1125- target_link_libraries (trigger_fire_test PRIVATE lua54 sol2::sol2 )
1230+ target_link_libraries (trigger_fire_test PRIVATE Mc3 lua54 sol2::sol2 )
11261231 target_compile_features (trigger_fire_test PRIVATE cxx_std_23 )
11271232 add_test (NAME trigger_fire COMMAND trigger_fire_test )
11281233 set_tests_properties (trigger_fire PROPERTIES TIMEOUT 15 LABELS "unit" )
@@ -1153,6 +1258,20 @@ if(MESH_CRAFT_BUILD_TESTING AND UNIX AND NOT ANDROID AND NOT EMSCRIPTEN)
11531258 add_test (NAME event_binding_dispatch COMMAND event_binding_dispatch_test )
11541259 set_tests_properties (event_binding_dispatch PROPERTIES TIMEOUT 15 LABELS "unit" )
11551260
1261+ # SYS-W14-40: explicit Preview/Play turns dry-run binding eligibility into
1262+ # an atomic batch. This CNA-free test covers Area enter/exit, timer
1263+ # bounds, click dispatch, state application, and failed-script rollback.
1264+ add_executable (event_preview_runner_test
1265+ ${CMAKE_CURRENT_SOURCE_DIR } /test/event_preview_runner_test.cpp
1266+ ${CMAKE_CURRENT_SOURCE_DIR } /src/MeshCraft/Editor/LuaScriptRunner.cpp )
1267+ target_include_directories (event_preview_runner_test PRIVATE
1268+ ${CMAKE_CURRENT_SOURCE_DIR } /include
1269+ ${CMAKE_CURRENT_SOURCE_DIR } /mc3/include )
1270+ target_link_libraries (event_preview_runner_test PRIVATE Mc3 lua54 sol2::sol2 )
1271+ target_compile_features (event_preview_runner_test PRIVATE cxx_std_23 )
1272+ add_test (NAME event_preview_runner COMMAND event_preview_runner_test )
1273+ set_tests_properties (event_preview_runner PROPERTIES TIMEOUT 15 LABELS "unit" )
1274+
11561275 # SYS-W14-21 (2026-07-20): the Imports tab's new "Resolve Imports" button
11571276 # and the automatic post-load resolution -- resolveImports() wires the
11581277 # already-tested Mc3ImportResolver (R101) into the editor. CNA-free
@@ -1258,6 +1377,15 @@ if(MESH_CRAFT_BUILD_TESTING AND UNIX AND NOT ANDROID AND NOT EMSCRIPTEN)
12581377 "${CMAKE_BINARY_DIR } " )
12591378 set_tests_properties (plan_consistency PROPERTIES TIMEOUT 30 LABELS "lint" )
12601379
1380+ # SYS-W13-02: bounded documentation/source drift detector. It protects
1381+ # a few claims that were demonstrably stale without turning volatile
1382+ # test totals or broad prose into a fragile static assertion suite.
1383+ add_test (NAME capability_documentation
1384+ COMMAND "${PYTHON3_FOR_LINT} "
1385+ "${CMAKE_CURRENT_SOURCE_DIR } /test/validate_capability_documentation.py"
1386+ "${CMAKE_CURRENT_SOURCE_DIR } " )
1387+ set_tests_properties (capability_documentation PROPERTIES TIMEOUT 30 LABELS "lint" )
1388+
12611389 # SYS-W5-01: machine-readable cross-layer MC3 field matrix. Extracts
12621390 # field/attribute names from XSD, the C++ model, the XML reader/
12631391 # writer, and the MCB reader/writer, and CI-fails if a field is
@@ -1282,15 +1410,15 @@ if(MESH_CRAFT_BUILD_TESTING AND UNIX AND NOT ANDROID AND NOT EMSCRIPTEN)
12821410 )
12831411 set_tests_properties (smoke_test PROPERTIES TIMEOUT 30 LABELS "render" )
12841412
1285- # SYS-W12-02: smoke test for the new `--benchmark` headless mode (see
1413+ # SYS-W12-02/03 : smoke test for the new `--benchmark` headless mode (see
12861414 # MeshCraftApplication_Benchmark.cpp) -- informational, same philosophy
12871415 # as SYS-W12-01's own test/benchmark.py (checks it runs to completion
12881416 # and prints every expected category, not that any timing is fast).
12891417 add_test (
12901418 NAME benchmark_editor
12911419 COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR } /test/benchmark_editor_test.sh
12921420 $<TARGET_FILE :MeshCraft >
1293- ${CMAKE_CURRENT_SOURCE_DIR } /test/house .mc3.xml
1421+ ${CMAKE_CURRENT_SOURCE_DIR } /test/uv_buffer_cache_large .mc3.xml
12941422 WORKING_DIRECTORY ${CMAKE_BINARY_DIR }
12951423 )
12961424 # This starts the editor and therefore needs an SDL video display even
0 commit comments