# cpp/vins_mono/CMakeLists.txt — VINS-Mono wrapper for C1 VIO (AZ-333).
#
# Builds the de-ROSified VINS-Mono upstream pin (cpp/vins_mono/upstream/, git
# submodule pointing at a community ROS-stripped fork OR an in-tree
# ROS-strip applied at configure time) plus a pybind11 binding that
# exposes the estimator to the Python facade at
# src/gps_denied_onboard/components/c1_vio/vins_mono.py.
#
# Gating: BUILD_VINS_MONO=ON only on the IT-12 research binary
# (research matrix kind in .github/workflows/ci.yml). Airborne /
# operator-tooling / replay-cli builds default BUILD_VINS_MONO=OFF per
# module-layout.md Build-Time Exclusion Map; CI's per-binary SBOM diff
# (ci/sbom_diff.py) fails if `vins_mono` appears in any non-research
# SBOM (Risk-3 mitigation).
#
# macOS dev builds default BUILD_VINS_MONO=OFF; unit tests use a fake
# pybind11 binding fixture installed at sys.modules boundary
# (tests/unit/c1_vio/conftest.py).
#
# Eigen / Ceres pinning: Risk-2 mitigation — the same Eigen pin is
# linked from cpp/_third_party/eigen/ as cpp/okvis2/CMakeLists.txt to
# avoid ABI mismatch when both load simultaneously inside the research
# binary. Ceres is linked from system apt (libceres-dev) on Linux to
# match VINS-Mono upstream's expected version surface.

if(NOT BUILD_VINS_MONO)
  return()
endif()

message(STATUS "[vins_mono] BUILD_VINS_MONO=ON — building VINS-Mono upstream + pybind11 binding")

# Tell VINS-Mono upstream to skip its bundled ROS shim (the de-ROSified
# port still ships a CMake hook that conditionally pulls roscpp; we keep
# it OFF). Upstream-source modifications beyond ROS-stripping require an
# explicit ADR addendum per task spec.
set(VINS_MONO_USE_ROS OFF CACHE BOOL "AZ-333: ROS-strip — Risk-1 mitigation" FORCE)

# Trim upstream's build surface — we link the estimator + feature_tracker
# only; demo apps / standalone runners are out.
set(BUILD_VINS_APPS OFF CACHE BOOL "AZ-333: skip VINS-Mono demo apps" FORCE)
set(BUILD_VINS_TESTS OFF CACHE BOOL "AZ-333: skip VINS-Mono gtests" FORCE)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "AZ-333: link VINS-Mono as static into the .so" FORCE)

# pybind11 (vendored at cpp/pybind11/upstream/) — guarded so a sibling
# native binding (okvis2_binding, gtsam_bindings, faiss_index) cannot
# double-add the subdirectory.
if(NOT TARGET pybind11::module)
  add_subdirectory(
    ${CMAKE_SOURCE_DIR}/cpp/pybind11/upstream
    ${CMAKE_BINARY_DIR}/pybind11_build
  )
endif()

# Vendored VINS-Mono upstream — EXCLUDE_FROM_ALL keeps unused targets
# out of the default build graph; we depend on the vins_estimator /
# feature_tracker libs we explicitly link below.
add_subdirectory(upstream EXCLUDE_FROM_ALL)

# pybind11 binding source — per module-layout.md rule #4 the binding
# code lives next to the Python facade, not under cpp/.
set(VINS_MONO_BINDING_SRC
  ${CMAKE_SOURCE_DIR}/src/gps_denied_onboard/components/c1_vio/_native/vins_mono_binding.cpp
)

pybind11_add_module(vins_mono_binding ${VINS_MONO_BINDING_SRC})

# VINS-Mono export targets — exact list confirmed by walking upstream
# CMakeLists in cpp/vins_mono/upstream/. If a target name changes
# upstream, the linker error on first CI run pinpoints which one.
target_link_libraries(vins_mono_binding
  PRIVATE
    vins_estimator
    feature_tracker
    camera_models
    ceres
)

target_compile_features(vins_mono_binding PRIVATE cxx_std_17)

# Install the .so next to the Python facade so the lazy import inside
# vins_mono.py (`from . import _native; _native.vins_mono_binding`)
# resolves at runtime without a sys.path shim.
install(TARGETS vins_mono_binding
        LIBRARY DESTINATION
          ${CMAKE_INSTALL_LIBDIR}/gps_denied_onboard/components/c1_vio/_native/
)
