Skip to content

Commit 7f58af4

Browse files
authored
Build system fixes - part 1 (#445)
This is the first part of the effort to make the build system more understandable, better maintainable, and usable. This PR focuses on improving the CMakeCargo bridge. Individual commits describe the steps taken. A lot of comments and helpful schemas are added.
2 parents bebfcdc + 0a3f3de commit 7f58af4

6 files changed

Lines changed: 260 additions & 167 deletions

File tree

CMakeLists.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,6 @@ set(SCYLLA_DRIVER_DEV_COMPONENT_NAME "${SCYLLA_DRIVER_COMPONENT_NAME}-dev")
8888
string(TOUPPER "${SCYLLA_DRIVER_RUNTIME_COMPONENT_NAME}" SCYLLA_DRIVER_RUNTIME_COMPONENT_NAME_UPPER)
8989
string(TOUPPER "${SCYLLA_DRIVER_DEV_COMPONENT_NAME}" SCYLLA_DRIVER_DEV_COMPONENT_NAME_UPPER)
9090

91-
if(CASS_BUILD_SHARED)
92-
set(BUILD_SHARED_LIBS ON)
93-
endif()
9491
# Handle testing dependencies
9592
if(CASS_BUILD_TESTS)
9693
# Enable integration and unit tests

cmake/CMakeCargo.cmake

Lines changed: 249 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,262 @@
1+
# ============================================================================
2+
# Dependency diagram — full picture (Linux names for concreteness)
3+
# ============================================================================
4+
#
5+
# This diagram shows how cargo_build() (this file) and the caller
6+
# (scylla-rust-wrapper/CMakeLists.txt) cooperate to produce the final
7+
# library artifacts. Arrows point from dependee to depender (i.e. A → B
8+
# means "B depends on A").
9+
#
10+
# Each crate type (staticlib, cdylib) is built by a separate
11+
# cargo_build() invocation using `cargo rustc --crate-type`. Both
12+
# invocations share the same CARGO_TARGET_DIR, so intermediate Rust
13+
# compilation artifacts are reused when feature flags match.
14+
#
15+
# ┌──────────────────────────┐ ┌──────────────────────────────┐
16+
# │ cargo rustc │ │ cargo rustc │
17+
# │ --crate-type staticlib │ │ --crate-type cdylib │
18+
# │ (add_custom_command) │ │ (add_custom_command) │
19+
# │ │ │ │
20+
# │ OUTPUT: │ │ OUTPUT: │
21+
# │ .../<profile>/ │ │ .../<profile>/ │
22+
# │ libscylla_cpp_driver.a │ │ libscylla_cpp_driver.so │
23+
# └────────────┬─────────────┘ └──────────────┬───────────────┘
24+
# │ │
25+
# ┌────────────▼─────────────┐ ┌──────────────▼───────────────┐
26+
# │ scylla_cpp_driver │ │ scylla_cpp_driver │
27+
# │ _staticlib_target │ │ _cdylib_target │
28+
# │ (custom target, ALL) │ │ (custom target, ALL) │
29+
# └────────────┬─────────────┘ └──────────────┬───────────────┘
30+
# │ │
31+
# ┌────────────▼─────────────┐ ┌──────────────▼───────────────┐
32+
# │ scylla_cpp_driver │ │ scylla_cpp_driver │
33+
# │ _staticlib │ │ _cdylib │
34+
# │ (STATIC IMPORTED) │ │ (SHARED IMPORTED) │
35+
# │ │ │ │
36+
# │ IMPORTED_LOCATION: │ │ IMPORTED_LOCATION: │
37+
# │ .../<profile>/ │ │ .../<profile>/ │
38+
# │ libscylla_cpp_driver.a │ │ libscylla_cpp_driver.so │
39+
# └────────────┬─────────────┘ └──────────────┬───────────────┘
40+
# │ │
41+
# $<TARGET_FILE:…> $<TARGET_FILE:…>
42+
# (used by create_copy) (used by create_copy)
43+
# │ │
44+
# ┌────────────▼─────────────┐ ┌──────────────▼───────────────┐
45+
# │ libscylla-cpp-driver │ │ libscylla-cpp-driver │
46+
# │ _static.a_copy │ │ .so.X.Y.Z_copy │
47+
# │ (custom target, ALL) │ │ (custom target, ALL) │
48+
# │ │ │ │
49+
# │ Copies + renames │ │ Copies + renames │
50+
# │ into build/ │ │ into build/ │
51+
# └────────────┬─────────────┘ └──────────────┬───────────────┘
52+
# │ │
53+
# ┌────────────▼─────────────┐ ┌──────────────▼───────────────┐
54+
# │ scylla-cpp-driver_static │ │ scylla-cpp-driver │
55+
# │ (STATIC IMPORTED) │ │ (SHARED IMPORTED) │
56+
# │ │ │ │
57+
# │ IMPORTED_LOCATION: │ │ IMPORTED_LOCATION: │
58+
# │ build/libscylla-cpp- │ │ build/libscylla-cpp- │
59+
# │ driver_static.a │ │ driver.so.X.Y.Z │
60+
# └────────────┬─────────────┘ │ │
61+
# │ │ + symlinks: │
62+
# │ │ .so.X → .so.X.Y.Z │
63+
# │ │ .so → .so.X.Y.Z │
64+
# │ └──────────────┬───────────────┘
65+
# │ │
66+
# ▼ ▼
67+
# tests, examples, install
68+
#
69+
# ============================================================================
70+
# Target layers — summary
71+
# ============================================================================
72+
#
73+
# Layer What Purpose
74+
# ───── ──────────────────────────── ────────────────────────────────────
75+
# (1) cargo rustc --crate-type … Separate custom command per crate
76+
# (custom commands) type. Shares intermediate artifacts
77+
# via a common CARGO_TARGET_DIR.
78+
#
79+
# (2) _staticlib_target / Custom targets that depend on the raw
80+
# _cdylib_target artifacts; give the build system a
81+
# (custom targets, ALL) named handle to order on.
82+
#
83+
# (3) scylla_cpp_driver_staticlib / IMPORTED targets wrapping the raw cargo
84+
# scylla_cpp_driver_cdylib output paths. Exist solely so that
85+
# (IMPORTED libraries) create_copy can use $<TARGET_FILE:…>
86+
# generator expressions to get the path.
87+
#
88+
# (4) _copy targets Copy + rename from Cargo's underscore
89+
# (custom targets, ALL) naming (libscylla_cpp_driver.so) to
90+
# conventional C library naming
91+
# (libscylla-cpp-driver.so.X.Y.Z) in
92+
# the build root.
93+
#
94+
# (5) scylla-cpp-driver_static / Final IMPORTED targets pointing at the
95+
# scylla-cpp-driver renamed copies. These are what the
96+
# (IMPORTED libraries) rest of the build system links against.
97+
#
98+
# ============================================================================
99+
100+
# cargo_build(NAME <crate_name> CRATE_TYPE <type> [FEATURES <feature> ...])
101+
#
102+
# Invokes `cargo rustc --crate-type <type>` as a custom command and creates
103+
# an IMPORTED library target wrapping the resulting artifact. The caller is
104+
# responsible for copying/renaming the artifact and creating a final IMPORTED
105+
# library target that the rest of the build system links against.
106+
#
107+
# Each crate type should be built by a separate cargo_build() call. Multiple
108+
# calls with the same NAME share CARGO_TARGET_DIR, so Rust intermediate
109+
# compilation artifacts are reused when feature flags match.
110+
#
111+
# The crate must declare the requested crate-type in its Cargo.toml.
112+
#
113+
# Created targets:
114+
# <crate_name>_<crate_type> — IMPORTED library (STATIC or SHARED)
115+
# <crate_name>_<crate_type>_target — custom target driving the cargo build
116+
#
117+
# The caller controls Rust compiler flags via CMAKE_Rust_FLAGS (passed as
118+
# RUSTFLAGS) and the build profile via CMAKE_BUILD_TYPE.
1119
function(cargo_build)
2-
cmake_parse_arguments(CARGO "" "NAME" "" ${ARGN})
3-
string(REPLACE "-" "_" LIB_NAME ${CARGO_NAME})
120+
cmake_parse_arguments(CARGO "" "NAME;CRATE_TYPE" "FEATURES" ${ARGN})
121+
string(REPLACE "-" "_" LIB_NAME ${CARGO_NAME})
4122

5-
set(CARGO_TARGET_DIR ${CMAKE_CURRENT_BINARY_DIR})
123+
if(NOT CARGO_CRATE_TYPE)
124+
message(FATAL_ERROR "cargo_build: CRATE_TYPE is required")
125+
endif()
6126

7-
if(WIN32)
8-
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
9-
set(LIB_TARGET "x86_64-pc-windows-msvc")
10-
else()
11-
set(LIB_TARGET "i686-pc-windows-msvc")
12-
endif()
13-
elseif(ANDROID)
14-
if(ANDROID_SYSROOT_ABI STREQUAL "x86")
15-
set(LIB_TARGET "i686-linux-android")
16-
elseif(ANDROID_SYSROOT_ABI STREQUAL "x86_64")
17-
set(LIB_TARGET "x86_64-linux-android")
18-
elseif(ANDROID_SYSROOT_ABI STREQUAL "arm")
19-
set(LIB_TARGET "arm-linux-androideabi")
20-
elseif(ANDROID_SYSROOT_ABI STREQUAL "arm64")
21-
set(LIB_TARGET "aarch64-linux-android")
22-
endif()
23-
elseif(IOS)
24-
set(LIB_TARGET "universal")
25-
elseif(CMAKE_SYSTEM_NAME STREQUAL Darwin)
26-
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
27-
set(LIB_TARGET "aarch64-apple-darwin")
28-
else()
29-
set(LIB_TARGET "${CMAKE_SYSTEM_PROCESSOR}-apple-darwin")
30-
endif()
31-
elseif(CMAKE_SYSTEM_NAME STREQUAL Linux)
32-
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
33-
set(LIB_TARGET "${CMAKE_SYSTEM_PROCESSOR}-unknown-linux-gnu")
34-
else()
35-
set(LIB_TARGET "i686-unknown-linux-gnu")
36-
endif()
127+
# ── 1. Resolve the Rust target triple ──────────────────────────────────
128+
#
129+
# Cargo needs an explicit --target <triple> argument. Map the current
130+
# CMake platform/architecture to the corresponding Rust triple.
131+
132+
set(CARGO_TARGET_DIR ${CMAKE_CURRENT_BINARY_DIR})
133+
134+
if(WIN32)
135+
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
136+
set(LIB_TARGET "x86_64-pc-windows-msvc")
37137
else()
38-
message(FATAL_ERROR "${CMAKE_SYSTEM_NAME} is unknown system")
138+
set(LIB_TARGET "i686-pc-windows-msvc")
39139
endif()
40-
41-
if(NOT CMAKE_BUILD_TYPE)
42-
set(LIB_BUILD_TYPE "debug")
43-
elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release")
44-
set(LIB_BUILD_TYPE "release")
45-
elseif(${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
46-
set(LIB_BUILD_TYPE "relwithdebinfo")
140+
elseif(CMAKE_SYSTEM_NAME STREQUAL Darwin)
141+
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
142+
set(LIB_TARGET "aarch64-apple-darwin")
143+
else()
144+
set(LIB_TARGET "${CMAKE_SYSTEM_PROCESSOR}-apple-darwin")
145+
endif()
146+
elseif(CMAKE_SYSTEM_NAME STREQUAL Linux)
147+
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
148+
set(LIB_TARGET "${CMAKE_SYSTEM_PROCESSOR}-unknown-linux-gnu")
47149
else()
48-
set(LIB_BUILD_TYPE "debug")
150+
set(LIB_TARGET "i686-unknown-linux-gnu")
49151
endif()
152+
else()
153+
message(FATAL_ERROR "${CMAKE_SYSTEM_NAME} is unknown system")
154+
endif()
50155

51-
set(LIB_FILE "${CARGO_TARGET_DIR}/${LIB_TARGET}/${LIB_BUILD_TYPE}/${CMAKE_STATIC_LIBRARY_PREFIX}${LIB_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX}")
156+
# ── 2. Map CMAKE_BUILD_TYPE to a Cargo profile ────────────────────────
157+
#
158+
# Cargo profile names don't match CMake's build types 1:1, so we
159+
# translate. The profile name also determines the output subdirectory
160+
# under target/<triple>/<profile>/.
161+
162+
if(NOT CMAKE_BUILD_TYPE)
163+
set(LIB_BUILD_TYPE "debug")
164+
elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release")
165+
set(LIB_BUILD_TYPE "release")
166+
elseif(${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
167+
set(LIB_BUILD_TYPE "relwithdebinfo")
168+
else()
169+
set(LIB_BUILD_TYPE "debug")
170+
endif()
171+
172+
# ── 3. Compute expected output path ────────────────────────────────────
173+
#
174+
# Cargo places artifacts at:
175+
# <CARGO_TARGET_DIR>/<triple>/<profile>/lib<name>.a (staticlib)
176+
# <CARGO_TARGET_DIR>/<triple>/<profile>/lib<name>.so (cdylib)
177+
178+
set(LIB_OUTPUT_DIR "${CARGO_TARGET_DIR}/${LIB_TARGET}/${LIB_BUILD_TYPE}")
179+
180+
if(CARGO_CRATE_TYPE STREQUAL "staticlib")
181+
set(LIB_FILE "${LIB_OUTPUT_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${LIB_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX}")
182+
set(LIB_OUTPUTS ${LIB_FILE})
183+
elseif(CARGO_CRATE_TYPE STREQUAL "cdylib")
184+
set(LIB_FILE "${LIB_OUTPUT_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}${LIB_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}")
52185
set(LIB_OUTPUTS ${LIB_FILE})
53-
if(BUILD_SHARED_LIBS)
54-
set(LIB_FILE_SHARED "${CARGO_TARGET_DIR}/${LIB_TARGET}/${LIB_BUILD_TYPE}/${CMAKE_SHARED_LIBRARY_PREFIX}${LIB_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}")
55-
list(APPEND LIB_OUTPUTS ${LIB_FILE_SHARED})
56-
if(WIN32)
57-
set(LIB_FILE_SHARED_IMPLIB "${LIB_FILE_SHARED}${CMAKE_IMPORT_LIBRARY_SUFFIX}")
58-
list(APPEND LIB_OUTPUTS ${LIB_FILE_SHARED_IMPLIB})
59-
endif()
186+
# On Windows, shared libraries also produce an import library (.lib).
187+
if(WIN32)
188+
set(LIB_FILE_IMPLIB "${LIB_FILE}${CMAKE_IMPORT_LIBRARY_SUFFIX}")
189+
list(APPEND LIB_OUTPUTS ${LIB_FILE_IMPLIB})
60190
endif()
191+
else()
192+
message(FATAL_ERROR "cargo_build: unsupported CRATE_TYPE '${CARGO_CRATE_TYPE}' (expected 'staticlib' or 'cdylib')")
193+
endif()
61194

62-
if(IOS)
63-
set(CARGO_ARGS "lipo")
64-
else()
65-
set(CARGO_ARGS "build")
66-
list(APPEND CARGO_ARGS "--target" ${LIB_TARGET})
67-
if (CMAKE_VERBOSE_MAKEFILE)
68-
list(APPEND CARGO_ARGS "--verbose")
69-
endif()
70-
endif()
71-
72-
if(${LIB_BUILD_TYPE} STREQUAL "release")
73-
list(APPEND CARGO_ARGS "--release")
74-
elseif(${LIB_BUILD_TYPE} STREQUAL "relwithdebinfo")
75-
list(APPEND CARGO_ARGS "--profile" "relwithdebinfo")
76-
elseif(${LIB_BUILD_TYPE} STREQUAL "debug")
77-
list(APPEND CARGO_CARGS "--profile" "dev")
78-
endif()
195+
# ── 4. Assemble the cargo command line ─────────────────────────────────
196+
#
197+
# We use `cargo rustc --crate-type <type>` instead of `cargo build` so
198+
# that each invocation produces only the requested artifact type.
199+
# Multiple invocations share the same CARGO_TARGET_DIR, so intermediate
200+
# Rust compilation artifacts are reused when feature flags match.
79201

80-
file(GLOB_RECURSE LIB_SOURCES "*.rs")
81-
82-
set(CARGO_ENV_COMMAND ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${CARGO_TARGET_DIR}" "RUSTFLAGS=${CMAKE_Rust_FLAGS}")
83-
84-
add_custom_command(
85-
OUTPUT ${LIB_OUTPUTS}
86-
COMMAND ${CARGO_ENV_COMMAND} ${CARGO_EXECUTABLE} ARGS ${CARGO_ARGS}
87-
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
88-
DEPENDS ${LIB_SOURCES}
89-
COMMENT "running cargo")
90-
add_custom_target(${CARGO_NAME}_target ALL DEPENDS ${LIB_FILE})
91-
add_library(${CARGO_NAME} STATIC IMPORTED GLOBAL)
92-
add_dependencies(${CARGO_NAME} ${CARGO_NAME}_target)
93-
set_target_properties(${CARGO_NAME} PROPERTIES IMPORTED_LOCATION ${LIB_FILE})
94-
if(BUILD_SHARED_LIBS)
95-
add_custom_target(${CARGO_NAME}_shared_target ALL DEPENDS ${LIB_FILE_SHARED})
96-
add_library(${CARGO_NAME}_shared SHARED IMPORTED GLOBAL)
97-
add_dependencies(${CARGO_NAME}_shared ${CARGO_NAME}_shared_target)
98-
set_target_properties(${CARGO_NAME}_shared PROPERTIES IMPORTED_LOCATION ${LIB_FILE_SHARED})
99-
if(WIN32)
100-
set_target_properties(${CARGO_NAME}_shared PROPERTIES IMPORTED_IMPLIB ${LIB_FILE_SHARED_IMPLIB})
101-
endif()
102-
endif()
202+
203+
set(CARGO_ARGS "rustc" "--crate-type" "${CARGO_CRATE_TYPE}")
204+
list(APPEND CARGO_ARGS "--target" ${LIB_TARGET})
205+
if (CMAKE_VERBOSE_MAKEFILE)
206+
list(APPEND CARGO_ARGS "--verbose")
207+
endif()
208+
209+
if(${LIB_BUILD_TYPE} STREQUAL "release")
210+
list(APPEND CARGO_ARGS "--release")
211+
elseif(${LIB_BUILD_TYPE} STREQUAL "relwithdebinfo")
212+
list(APPEND CARGO_ARGS "--profile" "relwithdebinfo")
213+
elseif(${LIB_BUILD_TYPE} STREQUAL "debug")
214+
list(APPEND CARGO_ARGS "--profile" "dev")
215+
endif()
216+
217+
if(CARGO_FEATURES)
218+
list(JOIN CARGO_FEATURES "," CARGO_FEATURES_STR)
219+
list(APPEND CARGO_ARGS "--features" "${CARGO_FEATURES_STR}")
220+
endif()
221+
222+
# ── 5. Register the custom command and target ──────────────────────────
223+
#
224+
# The custom command runs `cargo rustc` whenever any .rs source file
225+
# changes. The custom target provides a named handle that other CMake
226+
# targets can depend on.
227+
#
228+
# We then wrap the raw cargo output in an IMPORTED library target so
229+
# the caller can use $<TARGET_FILE:…> to obtain the artifact path.
230+
231+
# Glob all Rust sources so the command re-runs when any of them change.
232+
file(GLOB_RECURSE LIB_SOURCES "*.rs")
233+
list(APPEND LIB_SOURCES
234+
"${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml"
235+
"${CMAKE_CURRENT_SOURCE_DIR}/Cargo.lock"
236+
)
237+
238+
# Set CARGO_TARGET_DIR and RUSTFLAGS in the cargo process environment.
239+
set(CARGO_ENV_COMMAND ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${CARGO_TARGET_DIR}" "RUSTFLAGS=${CMAKE_Rust_FLAGS}")
240+
241+
add_custom_command(
242+
OUTPUT ${LIB_OUTPUTS}
243+
COMMAND ${CARGO_ENV_COMMAND} ${CARGO_EXECUTABLE} ARGS ${CARGO_ARGS}
244+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
245+
DEPENDS ${LIB_SOURCES}
246+
COMMENT "running cargo rustc --crate-type ${CARGO_CRATE_TYPE}"
247+
)
248+
249+
add_custom_target(${CARGO_NAME}_${CARGO_CRATE_TYPE}_target ALL DEPENDS ${LIB_OUTPUTS})
250+
251+
if(CARGO_CRATE_TYPE STREQUAL "staticlib")
252+
add_library(${CARGO_NAME}_${CARGO_CRATE_TYPE} STATIC IMPORTED GLOBAL)
253+
else()
254+
add_library(${CARGO_NAME}_${CARGO_CRATE_TYPE} SHARED IMPORTED GLOBAL)
255+
endif()
256+
add_dependencies(${CARGO_NAME}_${CARGO_CRATE_TYPE} ${CARGO_NAME}_${CARGO_CRATE_TYPE}_target)
257+
set_target_properties(${CARGO_NAME}_${CARGO_CRATE_TYPE} PROPERTIES IMPORTED_LOCATION ${LIB_FILE})
258+
259+
if(CARGO_CRATE_TYPE STREQUAL "cdylib" AND WIN32)
260+
set_target_properties(${CARGO_NAME}_${CARGO_CRATE_TYPE} PROPERTIES IMPORTED_IMPLIB ${LIB_FILE_IMPLIB})
261+
endif()
103262
endfunction()

0 commit comments

Comments
 (0)