Skip to content
Open
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
83 changes: 83 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,89 @@ if(RF_USE_RERUN)
)
endif()

# Rust FFI for FCB output
find_program(CARGO cargo)
if(CARGO)
set(RUST_TARGET_DIR "${CMAKE_BINARY_DIR}/rust-target")
set(FCB_FFI_DIR "${CMAKE_SOURCE_DIR}/roofer-fcb-ffi")

# Determine library extension based on platform
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(FCB_FFI_LIB_EXT "dll")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(FCB_FFI_LIB_EXT "dylib")
else()
set(FCB_FFI_LIB_EXT "so")
endif()
set(FCB_FFI_LIB "${RUST_TARGET_DIR}/release/libroofer_fcb_ffi.${FCB_FFI_LIB_EXT}")
# cxx also builds a static runtime library (libcxxbridge1.a) that contains
# implementations like rust::cxxbridge1::Str::Str(const char*). We need to
# link this alongside the cdylib to satisfy those symbols.
file(GLOB CXXBRIDGE1_LIB "${RUST_TARGET_DIR}/release/build/cxx-*/out/libcxxbridge1.a")

# Prefer cargo from ~/.cargo/bin (rustup-managed, 1.93.0) over nix cargo (1.86.0)
# Nix provides cargo 1.86.0 which uses rustc 1.86.0, but we need 1.93.0 for time@0.3.46
find_program(CARGO_RUSTUP "$ENV{HOME}/.cargo/bin/cargo")
if(CARGO_RUSTUP)
set(CARGO_TO_USE ${CARGO_RUSTUP})
message(STATUS "Using rustup-managed cargo: ${CARGO_TO_USE}")
else()
set(CARGO_TO_USE ${CARGO})
message(STATUS "Using system cargo: ${CARGO_TO_USE}")
endif()

# Build Rust library
# The cxx header will be generated in: target/release/build/roofer-fcb-ffi-<hash>/out/cxxbridge/roofer-fcb-ffi/src/lib.rs.h
# Use rustup-managed cargo to get rustc 1.93.0 instead of nix's 1.86.0
add_custom_target(roofer_fcb_ffi_build
COMMAND ${CMAKE_COMMAND} -E env "PATH=$ENV{HOME}/.cargo/bin:$ENV{PATH}" "RUSTUP_TOOLCHAIN=stable" ${CARGO_TO_USE} build --release --manifest-path ${FCB_FFI_DIR}/Cargo.toml --target-dir ${RUST_TARGET_DIR}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Building Rust FCB FFI library"
BYPRODUCTS ${FCB_FFI_LIB}
)

# Find and copy the generated header and source to known locations
# cxx generates headers in target/cxxbridge/<crate>/src/lib.rs.h (standard location, symlink)
# The actual file is in target/release/build/<crate>-<hash>/out/cxxbridge/include/<crate>/src/lib.rs.h
set(FCB_FFI_HEADER_OUTPUT "${CMAKE_BINARY_DIR}/include/roofer-fcb-ffi/src/lib.rs.h")
set(FCB_FFI_CC_OUTPUT "${CMAKE_BINARY_DIR}/roofer-fcb-ffi/src/lib.rs.cc")
# Use the symlink location which is more predictable
# NOTE: cargo is invoked with --target-dir ${RUST_TARGET_DIR}, so cxxbridge outputs
# are emitted under that directory (not under ${FCB_FFI_DIR}/target).
set(FCB_FFI_HEADER_SOURCE "${RUST_TARGET_DIR}/cxxbridge/roofer-fcb-ffi/src/lib.rs.h")
set(FCB_FFI_CC_SOURCE "${RUST_TARGET_DIR}/cxxbridge/roofer-fcb-ffi/src/lib.rs.cc")
add_custom_command(TARGET roofer_fcb_ffi_build POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/include/roofer-fcb-ffi/src"
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/roofer-fcb-ffi/src"
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${FCB_FFI_HEADER_SOURCE}" "${FCB_FFI_HEADER_OUTPUT}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${FCB_FFI_CC_SOURCE}" "${FCB_FFI_CC_OUTPUT}"
COMMENT "Copying cxx-generated header and source to known location"
)

# Store paths for use in src/extra/io/CMakeLists.txt
set(FCB_FFI_CC_FILE "${FCB_FFI_CC_OUTPUT}" CACHE INTERNAL "Path to cxx-generated C++ source")

add_library(roofer_fcb_ffi SHARED IMPORTED)
get_filename_component(FCB_FFI_LIB_DIR ${FCB_FFI_LIB} DIRECTORY)
set_target_properties(roofer_fcb_ffi PROPERTIES
IMPORTED_LOCATION ${FCB_FFI_LIB}
IMPORTED_NO_SONAME ON
IMPORTED_IMPLIB ${FCB_FFI_LIB}
)

# Make the imported library depend on the build target
add_dependencies(roofer_fcb_ffi roofer_fcb_ffi_build)

# Add the library directory to the linker search path for all targets
link_directories(${FCB_FFI_LIB_DIR})

# Add include directory for the copied header
include_directories(${CMAKE_BINARY_DIR}/include)
else()
message(WARNING "Cargo not found. FCB output support will not be available.")
add_library(roofer_fcb_ffi INTERFACE)
endif()

if(MSVC)
# windows.h breaks std::min/std::max, fix by define
add_definitions(-DNOMINMAX)
Expand Down
44 changes: 43 additions & 1 deletion apps/roofer-app/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,22 +1,64 @@
if(RF_BUILD_APPS)
set(APP_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/roofer-app.cpp")

set(ROOFER_LINK_LIBRARIES roofer-extra fmt::fmt cmake_git_version_tracking)
if(TARGET roofer_fcb_ffi)
list(APPEND ROOFER_LINK_LIBRARIES roofer_fcb_ffi)
endif()
if(CXXBRIDGE1_LIB)
# Also link cxx static runtime library for Str constructors, etc.
list(APPEND ROOFER_LINK_LIBRARIES "${CXXBRIDGE1_LIB}")
endif()

if(NOT CMAKE_SYSTEM_NAME STREQUAL "Windows")
find_package(mimalloc CONFIG REQUIRED)
list(APPEND ROOFER_LINK_LIBRARIES $<IF:$<TARGET_EXISTS:mimalloc-static>,mimalloc-static,mimalloc>)
endif()

add_executable("roofer" ${APP_SOURCES})

# Make roofer depend on Rust FFI library
if(TARGET roofer_fcb_ffi)
add_dependencies("roofer" roofer_fcb_ffi)
endif()
if(TARGET roofer_fcb_ffi_build)
add_dependencies("roofer" roofer_fcb_ffi_build)
endif()

if(RF_USE_RERUN)
list(APPEND ROOFER_LINK_LIBRARIES rerun_sdk)
target_compile_definitions("roofer" PRIVATE RF_USE_RERUN)
endif()

set_target_properties("roofer" PROPERTIES CXX_STANDARD 20)
# Link libraries - ensure roofer_fcb_ffi is linked after object files
target_link_libraries("roofer" PRIVATE ${ROOFER_LINK_LIBRARIES})

# Ensure Rust library and cxx static library are linked
if(TARGET roofer_fcb_ffi)
target_link_libraries("roofer" PRIVATE roofer_fcb_ffi)
endif()

# Set rpath for Rust library so it can be found at runtime
if(TARGET roofer_fcb_ffi)
get_target_property(FCB_LIB_LOC roofer_fcb_ffi IMPORTED_LOCATION)
if(FCB_LIB_LOC)
get_filename_component(FCB_LIB_DIR ${FCB_LIB_LOC} DIRECTORY)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set_target_properties("roofer" PROPERTIES
INSTALL_RPATH "${FCB_LIB_DIR}"
BUILD_WITH_INSTALL_RPATH TRUE
)
target_link_options("roofer" PRIVATE "-Wl,-rpath,${FCB_LIB_DIR}")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set_target_properties("roofer" PROPERTIES
INSTALL_RPATH "${FCB_LIB_DIR}"
BUILD_WITH_INSTALL_RPATH TRUE
)
target_link_options("roofer" PRIVATE "-Wl,-rpath,${FCB_LIB_DIR}")
endif()
endif()
endif()

# Used for getting the process memory usage
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
Expand Down
3 changes: 3 additions & 0 deletions apps/roofer-app/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ struct RooferConfig {
std::string index_file_spec = "{path}/index.gpkg";
std::string metadata_json_file_spec = "{path}/metadata.json";
std::string output_path;
std::string output_format = "jsonl"; // "jsonl" or "fcb"

// reconstruct
roofer::enums::TerrainStrategy h_terrain_strategy =
Expand Down Expand Up @@ -527,6 +528,8 @@ struct RooferConfigHandler {
output.add("tiling", "Enable or disable output tiling.", _tiling);
output.add("tilesize", "Tilesize for rectangular output tiles in meters.",
cfg_.tilesize, {check::HigherThan<roofer::arr2f>({0, 0})});
output.add("output-format", "Output format: 'jsonl' or 'fcb'",
cfg_.output_format);
output.add("split-cjseq",
"Output CityJSONSequence file for each building instead of one "
"file per tile.",
Expand Down
Loading