Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
/.codex/
/CMakeUserPresets.json
/vcpkg_installed/
/.deps/
/.vendor/
*.user
*.VC.db
*.opendb
*.opendb
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ project(SafeCrowd
)

include(CTest)
include(cmake/SafeCrowdImportStack.cmake)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Expand Down Expand Up @@ -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)
Expand Down
17 changes: 17 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 본문에 이유를 남깁니다.

## 머지 규칙
Expand Down
266 changes: 266 additions & 0 deletions cmake/SafeCrowdImportStack.cmake
Original file line number Diff line number Diff line change
@@ -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 <boost/geometry.hpp>

#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()
8 changes: 8 additions & 0 deletions docs/설계 구조.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` 밖으로 새지 않게 유지한다.

---
8 changes: 7 additions & 1 deletion vcpkg.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
{
"dependencies": [
"qtbase"
{
"name": "qtbase",
"default-features": false,
"features": [
"widgets"
]
}
]
}
Loading