From a490271fd5f650f9f21f929c6725a13dead8e807 Mon Sep 17 00:00:00 2001 From: learncold Date: Thu, 2 Apr 2026 17:23:24 +0900 Subject: [PATCH] =?UTF-8?q?build:=20import=20stack=20=EB=A1=9C=EC=BB=AC=20?= =?UTF-8?q?=EB=B9=8C=EB=93=9C=20=EA=B2=BD=EA=B3=84=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- CMakeLists.txt | 3 + CONTRIBUTING.md | 17 ++ cmake/SafeCrowdImportStack.cmake | 266 ++++++++++++++++++ ...4\352\263\204 \352\265\254\354\241\260.md" | 8 + vcpkg.json | 8 +- 6 files changed, 304 insertions(+), 2 deletions(-) create mode 100644 cmake/SafeCrowdImportStack.cmake diff --git a/.gitignore b/.gitignore index 476481e..eb0edf2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ /.codex/ /CMakeUserPresets.json /vcpkg_installed/ +/.deps/ +/.vendor/ *.user *.VC.db -*.opendb \ No newline at end of file +*.opendb diff --git a/CMakeLists.txt b/CMakeLists.txt index 641992f..bfcb015 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ project(SafeCrowd ) include(CTest) +include(cmake/SafeCrowdImportStack.cmake) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -73,6 +74,8 @@ target_link_libraries(safecrowd_domain ecs_engine ) +safecrowd_configure_domain_import_stack(safecrowd_domain) + configure_project_target(safecrowd_domain) if (BUILD_TESTING) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index db7cf9c..283cbe9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -90,6 +90,23 @@ cmake --build --preset build-no-app-debug ctest --preset test-no-app-debug ``` +- `windows-debug` / `windows-release` 첫 configure는 `vcpkg`가 `qtbase`를 설치하는 동안 몇 분 이상 걸릴 수 있습니다. +- 현재 manifest는 `qtbase` 기본 feature를 끄고 `widgets`만 요청하므로, 불필요한 SQL/PostgreSQL 플러그인까지 끌어오지 않도록 유지합니다. + +로컬 import stack 검증이 필요하면 기본 경로를 유지한 채 아래 옵션을 추가합니다. + +```powershell +cmake --preset windows-debug-no-app ` + -DSAFECROWD_ENABLE_IMPORT_STACK=ON ` + -DSAFECROWD_IMPORT_STACK_ROOT=C:/sdk/import-stack ` + -DSAFECROWD_IFCOPENSHELL_ROOT=C:/sdk/IfcOpenShell +cmake --build --preset build-engine-domain-debug +``` + +- `SAFECROWD_ENABLE_IMPORT_STACK`는 기본값이 `OFF`이며, CI와 Sprint 1 기본 경로는 그대로 유지합니다. +- `SAFECROWD_IMPORT_STACK_ROOT`는 공통 prefix 용도이고, `SAFECROWD_LIBDXFRW_ROOT`, `SAFECROWD_IFCOPENSHELL_ROOT`, `SAFECROWD_CLIPPER2_ROOT`, `SAFECROWD_BOOST_ROOT`, `SAFECROWD_RECAST_ROOT`로 개별 경로를 덮어쓸 수 있습니다. +- 현재 smoke check는 `libdxfrw`, `IfcOpenShell` parser 헤더, `Clipper2`, `Boost.Geometry`, `Recast`, `Detour`의 include/lib 경로를 configure 단계에서 확인합니다. + 실행하지 못했다면 PR 본문에 이유를 남깁니다. ## 머지 규칙 diff --git a/cmake/SafeCrowdImportStack.cmake b/cmake/SafeCrowdImportStack.cmake new file mode 100644 index 0000000..1346b33 --- /dev/null +++ b/cmake/SafeCrowdImportStack.cmake @@ -0,0 +1,266 @@ +include_guard(GLOBAL) + +include(CheckCXXSourceCompiles) + +option(SAFECROWD_ENABLE_IMPORT_STACK "Enable the local domain import stack wiring." OFF) + +set(SAFECROWD_IMPORT_STACK_ROOT "" CACHE PATH "Common prefix that contains include/ and lib/ for the local import stack.") +set(SAFECROWD_LIBDXFRW_ROOT "" CACHE PATH "Install prefix or build root for libdxfrw.") +set(SAFECROWD_IFCOPENSHELL_ROOT "" CACHE PATH "Install prefix or build root for IfcOpenShell.") +set(SAFECROWD_CLIPPER2_ROOT "" CACHE PATH "Install prefix or build root for Clipper2.") +set(SAFECROWD_BOOST_ROOT "" CACHE PATH "Install prefix or build root for Boost.") +set(SAFECROWD_RECAST_ROOT "" CACHE PATH "Install prefix or build root for RecastNavigation.") + +function(_safecrowd_collect_import_hints out_var dependency_root dependency_folder) + set(_hints "") + + if (SAFECROWD_IMPORT_STACK_ROOT) + list(APPEND _hints + "${SAFECROWD_IMPORT_STACK_ROOT}" + "${SAFECROWD_IMPORT_STACK_ROOT}/${dependency_folder}" + "${SAFECROWD_IMPORT_STACK_ROOT}/${dependency_folder}/install" + "${SAFECROWD_IMPORT_STACK_ROOT}/${dependency_folder}/build/install" + "${SAFECROWD_IMPORT_STACK_ROOT}/${dependency_folder}/out/install/x64-windows" + ) + endif() + + if (dependency_root) + list(APPEND _hints + "${dependency_root}" + "${dependency_root}/install" + "${dependency_root}/build/install" + "${dependency_root}/out/install/x64-windows" + ) + endif() + + list(REMOVE_DUPLICATES _hints) + set(${out_var} "${_hints}" PARENT_SCOPE) +endfunction() + +function(_safecrowd_resolve_import_dependency dependency_key) + set(options HEADER_ONLY) + set(oneValueArgs ROOT_PATH ROOT_VAR_NAME INCLUDE_VAR LIBRARY_VAR) + set(multiValueArgs HEADER_NAMES LIB_NAMES) + cmake_parse_arguments(DEP "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + _safecrowd_collect_import_hints(_dependency_hints "${DEP_ROOT_PATH}" "${dependency_key}") + + find_path(${DEP_INCLUDE_VAR} + NAMES ${DEP_HEADER_NAMES} + HINTS ${_dependency_hints} + PATH_SUFFIXES include Include src + ) + + if (NOT DEFINED ${DEP_INCLUDE_VAR} OR NOT IS_DIRECTORY "${${DEP_INCLUDE_VAR}}") + message(FATAL_ERROR + "SafeCrowd import stack is enabled, but ${dependency_key} headers were not found. " + "Set ${DEP_ROOT_VAR_NAME} or SAFECROWD_IMPORT_STACK_ROOT." + ) + endif() + + set(${DEP_INCLUDE_VAR} "${${DEP_INCLUDE_VAR}}" PARENT_SCOPE) + + if (DEP_HEADER_ONLY) + return() + endif() + + find_library(${DEP_LIBRARY_VAR} + NAMES ${DEP_LIB_NAMES} + HINTS ${_dependency_hints} + PATH_SUFFIXES lib lib64 build build/Debug build/Release Debug Release + ) + + if (NOT DEFINED ${DEP_LIBRARY_VAR} OR NOT EXISTS "${${DEP_LIBRARY_VAR}}") + message(FATAL_ERROR + "SafeCrowd import stack is enabled, but ${dependency_key} libraries were not found. " + "Set ${DEP_ROOT_VAR_NAME} or SAFECROWD_IMPORT_STACK_ROOT." + ) + endif() + + set(${DEP_LIBRARY_VAR} "${${DEP_LIBRARY_VAR}}" PARENT_SCOPE) +endfunction() + +function(_safecrowd_validate_import_stack include_dirs link_libraries) + set(_saved_required_includes "${CMAKE_REQUIRED_INCLUDES}") + set(_saved_required_libraries "${CMAKE_REQUIRED_LIBRARIES}") + set(_saved_required_quiet "${CMAKE_REQUIRED_QUIET}") + + set(CMAKE_REQUIRED_INCLUDES "${include_dirs}") + set(CMAKE_REQUIRED_LIBRARIES "${link_libraries}") + set(CMAKE_REQUIRED_QUIET TRUE) + + unset(SAFECROWD_IMPORT_STACK_SMOKE_OK CACHE) + unset(SAFECROWD_IMPORT_STACK_SMOKE_OK) + + check_cxx_source_compiles( + [=[ + #if __has_include("libdxfrw.h") + #include "libdxfrw.h" + #elif __has_include("dxfrw.h") + #include "dxfrw.h" + #else + #error "Missing libdxfrw header" + #endif + + #if __has_include("ifcparse/IfcFile.h") + #include "ifcparse/IfcFile.h" + #elif __has_include("IfcParse.h") + #include "IfcParse.h" + #else + #error "Missing IfcOpenShell parser header" + #endif + + #if __has_include("clipper2/clipper.h") + #include "clipper2/clipper.h" + #elif __has_include("clipper.h") + #include "clipper.h" + #else + #error "Missing Clipper2 header" + #endif + + #include + + #if __has_include("Recast.h") + #include "Recast.h" + #elif __has_include("recast/Recast.h") + #include "recast/Recast.h" + #elif __has_include("recastnavigation/Recast.h") + #include "recastnavigation/Recast.h" + #else + #error "Missing Recast header" + #endif + + #if __has_include("DetourNavMesh.h") + #include "DetourNavMesh.h" + #elif __has_include("recast/DetourNavMesh.h") + #include "recast/DetourNavMesh.h" + #elif __has_include("recastnavigation/DetourNavMesh.h") + #include "recastnavigation/DetourNavMesh.h" + #else + #error "Missing Detour header" + #endif + + int main() { + return 0; + } + ]=] + SAFECROWD_IMPORT_STACK_SMOKE_OK + ) + + set(CMAKE_REQUIRED_INCLUDES "${_saved_required_includes}") + set(CMAKE_REQUIRED_LIBRARIES "${_saved_required_libraries}") + set(CMAKE_REQUIRED_QUIET "${_saved_required_quiet}") + + if (NOT SAFECROWD_IMPORT_STACK_SMOKE_OK) + message(FATAL_ERROR + "SafeCrowd import stack smoke validation failed. " + "Check the configured include/lib roots for libdxfrw, IfcOpenShell, Clipper2, Boost, Recast, and Detour." + ) + endif() +endfunction() + +function(safecrowd_configure_domain_import_stack target_name) + if (NOT TARGET ${target_name}) + message(FATAL_ERROR "Target ${target_name} does not exist.") + endif() + + if (NOT SAFECROWD_ENABLE_IMPORT_STACK) + target_compile_definitions(${target_name} PUBLIC SAFECROWD_IMPORT_STACK_ENABLED=0) + message(STATUS "SafeCrowd import stack: disabled") + return() + endif() + + _safecrowd_resolve_import_dependency( + libdxfrw + ROOT_PATH "${SAFECROWD_LIBDXFRW_ROOT}" + ROOT_VAR_NAME "SAFECROWD_LIBDXFRW_ROOT" + INCLUDE_VAR SAFECROWD_LIBDXFRW_INCLUDE_DIR + LIBRARY_VAR SAFECROWD_LIBDXFRW_LIBRARY + HEADER_NAMES libdxfrw.h dxfrw.h + LIB_NAMES dxfrw libdxfrw + ) + + _safecrowd_resolve_import_dependency( + ifcopenshell + ROOT_PATH "${SAFECROWD_IFCOPENSHELL_ROOT}" + ROOT_VAR_NAME "SAFECROWD_IFCOPENSHELL_ROOT" + INCLUDE_VAR SAFECROWD_IFCOPENSHELL_INCLUDE_DIR + LIBRARY_VAR SAFECROWD_IFCOPENSHELL_LIBRARY + HEADER_NAMES ifcparse/IfcFile.h IfcParse.h + LIB_NAMES IfcParse ifcparse libIfcParse + ) + + _safecrowd_resolve_import_dependency( + clipper2 + HEADER_ONLY + ROOT_PATH "${SAFECROWD_CLIPPER2_ROOT}" + ROOT_VAR_NAME "SAFECROWD_CLIPPER2_ROOT" + INCLUDE_VAR SAFECROWD_CLIPPER2_INCLUDE_DIR + HEADER_NAMES clipper2/clipper.h clipper.h + ) + + _safecrowd_resolve_import_dependency( + boost + HEADER_ONLY + ROOT_PATH "${SAFECROWD_BOOST_ROOT}" + ROOT_VAR_NAME "SAFECROWD_BOOST_ROOT" + INCLUDE_VAR SAFECROWD_BOOST_INCLUDE_DIR + HEADER_NAMES boost/geometry.hpp + ) + + _safecrowd_resolve_import_dependency( + recastnavigation + ROOT_PATH "${SAFECROWD_RECAST_ROOT}" + ROOT_VAR_NAME "SAFECROWD_RECAST_ROOT" + INCLUDE_VAR SAFECROWD_RECAST_INCLUDE_DIR + LIBRARY_VAR SAFECROWD_RECAST_LIBRARY + HEADER_NAMES Recast.h recast/Recast.h recastnavigation/Recast.h + LIB_NAMES Recast recast libRecast + ) + + _safecrowd_resolve_import_dependency( + recastnavigation + ROOT_PATH "${SAFECROWD_RECAST_ROOT}" + ROOT_VAR_NAME "SAFECROWD_RECAST_ROOT" + INCLUDE_VAR SAFECROWD_DETOUR_INCLUDE_DIR + LIBRARY_VAR SAFECROWD_DETOUR_LIBRARY + HEADER_NAMES DetourNavMesh.h recast/DetourNavMesh.h recastnavigation/DetourNavMesh.h + LIB_NAMES Detour detour libDetour + ) + + set(_import_include_dirs + "${SAFECROWD_LIBDXFRW_INCLUDE_DIR}" + "${SAFECROWD_IFCOPENSHELL_INCLUDE_DIR}" + "${SAFECROWD_CLIPPER2_INCLUDE_DIR}" + "${SAFECROWD_BOOST_INCLUDE_DIR}" + "${SAFECROWD_RECAST_INCLUDE_DIR}" + "${SAFECROWD_DETOUR_INCLUDE_DIR}" + ) + list(REMOVE_DUPLICATES _import_include_dirs) + + set(_import_link_libraries + "${SAFECROWD_LIBDXFRW_LIBRARY}" + "${SAFECROWD_IFCOPENSHELL_LIBRARY}" + "${SAFECROWD_RECAST_LIBRARY}" + "${SAFECROWD_DETOUR_LIBRARY}" + ) + + _safecrowd_validate_import_stack("${_import_include_dirs}" "${_import_link_libraries}") + + target_compile_definitions(${target_name} PUBLIC SAFECROWD_IMPORT_STACK_ENABLED=1) + target_include_directories(${target_name} + SYSTEM PRIVATE + ${_import_include_dirs} + ) + target_link_libraries(${target_name} + PRIVATE + ${_import_link_libraries} + ) + + message(STATUS "SafeCrowd import stack: enabled") + message(STATUS " libdxfrw: ${SAFECROWD_LIBDXFRW_INCLUDE_DIR}") + message(STATUS " IfcOpenShell: ${SAFECROWD_IFCOPENSHELL_INCLUDE_DIR}") + message(STATUS " Clipper2: ${SAFECROWD_CLIPPER2_INCLUDE_DIR}") + message(STATUS " Boost: ${SAFECROWD_BOOST_INCLUDE_DIR}") + message(STATUS " Recast/Detour: ${SAFECROWD_RECAST_INCLUDE_DIR}") +endfunction() diff --git "a/docs/\354\204\244\352\263\204 \352\265\254\354\241\260.md" "b/docs/\354\204\244\352\263\204 \352\265\254\354\241\260.md" index 21def0f..22f5c01 100644 --- "a/docs/\354\204\244\352\263\204 \352\265\254\354\241\260.md" +++ "b/docs/\354\204\244\352\263\204 \352\265\254\354\241\260.md" @@ -189,9 +189,17 @@ Qt viewport가 엔진 렌더러와 직접 연결되어야 하면 `application -> - `external/`은 직접 vendoring 해야 하거나 로컬 패치가 필요한 라이브러리만 둔다. - `external/`에 넣은 라이브러리는 실제로 연결할 때만 CMake에 등록한다. - 사용하지 않는 vendored 코드는 구조를 흐리므로 장기간 방치하지 않는다. +- 현재 앱 의존성인 `qtbase`는 기본 feature를 끄고 `widgets`만 요청해서, 사용하지 않는 SQL/PostgreSQL 경로까지 로컬 configure에 끌어오지 않는다. ### 7.4. 빌드 디렉터리 원칙 - generator가 바뀌어도 캐시 충돌이 나지 않도록 generator별 하위 빌드 디렉터리를 사용한다. - 현재 Visual Studio 프리셋은 `build/vs2022/...` 아래에 생성한다. +### 7.5. import stack 로컬 빌드 원칙 +- import 관련 오픈소스 스택 연결은 `safecrowd_domain`에서만 켜고, `ecs_engine`에는 연결하지 않는다. +- 기본값은 `SAFECROWD_ENABLE_IMPORT_STACK=OFF`로 두어 CI와 Sprint 1 기본 경로를 그대로 유지한다. +- 로컬 개발 환경에서 import 스택을 켤 때는 `SAFECROWD_IMPORT_STACK_ROOT`를 공통 prefix로 쓰고, 필요하면 `SAFECROWD_LIBDXFRW_ROOT`, `SAFECROWD_IFCOPENSHELL_ROOT`, `SAFECROWD_CLIPPER2_ROOT`, `SAFECROWD_BOOST_ROOT`, `SAFECROWD_RECAST_ROOT`로 개별 경로를 덮어쓴다. +- configure 단계 smoke check는 `libdxfrw`, `IfcOpenShell` parser 헤더, `Clipper2`, `Boost.Geometry`, `Recast`, `Detour`의 최소 include/lib 연결을 확인한다. +- 실제 DXF/IFC adapter 소스가 들어오면 같은 domain 경계 안에서 이 스위치를 재사용하고, 외부 SDK 헤더는 계속 `application`과 `engine` 밖으로 새지 않게 유지한다. + --- diff --git a/vcpkg.json b/vcpkg.json index 2fdc797..a6e6574 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,5 +1,11 @@ { "dependencies": [ - "qtbase" + { + "name": "qtbase", + "default-features": false, + "features": [ + "widgets" + ] + } ] }