Skip to content

Commit 364ef4e

Browse files
[CDRIVER-5391] Backport 2.x CMake Package Names (#1985)
* Don't use LOCALLY in Earthfile root * [CDRIVER-5931] New forward-compatible CMake package files (Cherry-pick for #1955) * Add transitional packages for the major version change The new CMake packages will support importing either version using the same package name, by either omitting the version specifier, or by specifying a version range to be imported. The package files, installed for 1.x, are just shims that import the xyz-1.0 packages and add some compatibility target names. These packages also properly handle version (range) specifiers for the 2.x version, and can detect package component requests. * Add CTest test cases for importing CMake packages * Prevent racing during installation. The install process writes temporary files into the build tree. To allow parallel installs to execute (e.g. for parallelized tests), we need to lock these files while they are being used by the install scripts. * Fix test fixture control dir naming * Apply a CTest label to test-libmongoc tests This label will allow us to (de)select tests for execution on the CTest CLI. * Update CMake examples to use new package names * Add NEWS items mentioning the new package names * Rename MONGOC_INSTALL_CMAKEDIR to match the 2.x name
1 parent abde842 commit 364ef4e

23 files changed

+738
-16
lines changed

CMakeLists.txt

+15-1
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,22 @@ if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
293293
endif ()
294294

295295
include (InstallRequiredSystemLibraries)
296-
include (GNUInstallDirs)
297296
include (CMakeDependentOption)
298297

298+
# Installation paths
299+
include (GNUInstallDirs)
300+
# The directory where CMake config packages will be installed, based on the libdir from GNUInstallDirs
301+
mongo_setting(
302+
MONGO_C_DRIVER_INSTALL_CMAKEDIR "The directory in which CMake package files will be installed"
303+
TYPE STRING
304+
DEFAULT VALUE "${CMAKE_INSTALL_LIBDIR}/cmake"
305+
VALIDATE CODE [[
306+
if(IS_ABSOLUTE "${MONGO_C_DRIVER_INSTALL_CMAKEDIR}")
307+
message(SEND_ERROR "The CMake installation directory must be a relative path (Got “${MONGO_C_DRIVER_INSTALL_CMAKEDIR}”)")
308+
endif()
309+
]]
310+
)
311+
299312
include(MongoC-Warnings)
300313

301314
# Enable "maintainer flags," which are supplementary but not mandatory.
@@ -372,6 +385,7 @@ set (BUILD_SOURCE_DIR ${CMAKE_BINARY_DIR})
372385
include (CTest)
373386
if (BUILD_TESTING)
374387
include (TestFixtures)
388+
include (src/libmongoc/tests/import-tests.cmake)
375389
endif ()
376390

377391
# Ensure the default behavior: don't ignore RPATH settings.

Earthfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
VERSION --arg-scope-and-set --pass-args 0.7
2-
LOCALLY
2+
FROM alpine:3.21
33

44
IMPORT ./tools/ AS tools
55

NEWS

+21
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
libmongoc 1.x (Unreleased)
2+
==========================
3+
4+
New Features:
5+
6+
* In anticipation of the 2.0 release of mongo-c-driver, new CMake packages and
7+
imported targets have been defined (for both `bson` and `mongoc`). To import
8+
`mongoc` with the new names, call `find_package` for the `mongoc` package. The
9+
new imported targets are named `mongoc::static`, `mongoc::shared`, and
10+
`mongoc::mongoc` (which points to either the static or the shared library,
11+
depending on an import-time configuration option).
12+
13+
The new package and target names will remain unchanged when upgrading to the
14+
2.0 release, allowing consumers to support both major versions without
15+
modifying their CMake project. The current imported target names will be
16+
removed from the 2.0 release, and should not be used for
17+
forward-compatibility.
18+
19+
Programs that link to BSON libraries directly should also use the new target
20+
names `bson::static`, `bson::shared`, or `bson::bson`.
21+
122
libmongoc 1.30.2
223
================
324

build/cmake/GeneratePkgConfig.cmake

+2-1
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@ function(mongo_generate_pkg_config target)
225225
file(READ [[@gx_tmpfile@]] content)
226226
# Insert the install prefix:
227227
string(REPLACE "%INSTALL_PLACEHOLDER%" "${CMAKE_INSTALL_PREFIX}" content "${content}")
228-
# Write it before installing again:
228+
# Write it before installing again. Lock the file to sync with parallel installs.
229+
file(LOCK [[@[email protected]]] GUARD PROCESS)
229230
file(WRITE [[@inst_tmp@]] "${content}")
230231
>
231232
$<$<NOT:@gx_cond@>:

build/cmake/GenerateUninstaller.cmake

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ if(NOT DEFINED UNINSTALL_WRITE_FILE)
6060
message(FATAL_ERROR "Expected a variable “UNINSTALL_WRITE_FILE” to be defined")
6161
endif()
6262

63+
# Lock the uninstall file to synchronize with parallel install processes.
64+
file(LOCK "${UNINSTALL_WRITE_FILE}.lock" GUARD PROCESS RESULT_VARIABLE lockres)
6365
# Clear out the uninstall script before we begin writing:
6466
file(WRITE "${UNINSTALL_WRITE_FILE}" "")
6567

build/cmake/LoadTests.cmake

+3
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,8 @@ foreach (line IN LISTS lines)
5252
TIMEOUT 45
5353
FIXTURES_REQUIRED "${all_fixtures}"
5454
ENVIRONMENT "${all_env}"
55+
# Mark all tests generated from the executable, so they can be (de)selected
56+
# for execution separately.
57+
LABELS "test-libmongoc-generated"
5558
)
5659
endforeach ()

build/cmake/TestFixtures.cmake

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ set (_MONGOC_PROC_CTL_COMMAND "$<TARGET_FILE:Python3::Interpreter>" -u -- "${_MO
1111

1212
function (mongo_define_subprocess_fixture name)
1313
cmake_parse_arguments(PARSE_ARGV 1 ARG "" "SPAWN_WAIT;STOP_WAIT;WORKING_DIRECTORY" "COMMAND")
14-
string (MAKE_C_IDENTIFIER ident "${name}")
14+
string (MAKE_C_IDENTIFIER "${name}" ident)
1515
if (NOT ARG_SPAWN_WAIT)
1616
set (ARG_SPAWN_WAIT 1)
1717
endif ()
@@ -40,7 +40,7 @@ endfunction ()
4040

4141
# Create a fixture that runs a fake Azure IMDS server
4242
mongo_define_subprocess_fixture(
43-
mongoc/fixtures/fake_imds
43+
mongoc/fixtures/fake_kms_provider_server
4444
SPAWN_WAIT 1
4545
COMMAND
4646
"$<TARGET_FILE:Python3::Interpreter>" -u --

build/cmake/TestProject.cmake

+261
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
#[[
2+
Add a CMake test that configures, builds, and tests a CMake project.
3+
4+
add_test_cmake_project(
5+
<name> <path>
6+
[INSTALL_PARENT]
7+
[BUILD_DIR <dir>]
8+
[GENERATOR <gen>]
9+
[CONFIG <config>]
10+
[PASSTHRU_VARS ...]
11+
[PASSTHRU_VARS_REGEX <regex>]
12+
[CONFIGURE_ARGS ...]
13+
[SETTINGS ...]
14+
)
15+
16+
The generated test will run CMake configure on the project at `<path>` (which
17+
is resolved relative to the caller's source directory).
18+
19+
If INSTALL_PARENT is specified, then the host CMake project will be installed to a
20+
temporary prefix, and that prefix will be passed along with CMAKE_PREFIX_PATH
21+
when the test project is configured.
22+
23+
PASSTHRU_VARS is a list of CMake variables visible in the current scope that should
24+
be made visible to the subproject with the same name and value. PASSTHRU_VARS_REGEX
25+
takes a single regular expression. Any variables currently defined which match
26+
the regex will be passed through as-if they were specified with PASSTHRU_VARS.
27+
28+
SETTINGS is list of a `[name]=[value]` strings for additional `-D` arguments to
29+
pass to the sub-project. List arguments *are* supported.
30+
31+
If CONFIG is unspecified, then the generated test will configure+build the project
32+
according to the configuration of CTest (passed with the `-C` argument).
33+
34+
The default for GENERATOR is to use the same generator as the host project.
35+
36+
The default BUILD_DIR will be a temporary directory that will be automatically
37+
deleted at the start of each test to ensure a fresh configure+build cycle.
38+
39+
Additional Variables
40+
####################
41+
42+
In addition to the variables specified explicitly in the call, all variables
43+
with the suffix `_PATH` or `_DIR` will be passed to the sub-project with
44+
`HOST_PROJECT_` prepended. For example, CMAKE_SOURCE_DIR will be passed to
45+
the sub-project as HOST_PROJECT_CMAKE_SOURCE_DIR
46+
]]
47+
function(add_test_cmake_project name path)
48+
# Logging context
49+
list(APPEND CMAKE_MESSAGE_CONTEXT "${CMAKE_CURRENT_FUNCTION}")
50+
51+
# Parse command arguments:
52+
set(options INSTALL_PARENT)
53+
set(args BUILD_DIR GENERATOR CONFIG PASSTHRU_VARS_REGEX)
54+
set(list_args SETTINGS PASSTHRU_VARS CONFIGURE_ARGS)
55+
cmake_parse_arguments(PARSE_ARGV 2 _tp_arg "${options}" "${args}" "${list_args}")
56+
foreach(unknown IN LISTS _tp_arg_UNPARSED_ARGUMENTS)
57+
message(SEND_ERROR "Unknown argument: “${unknown}”")
58+
endforeach()
59+
if(_tp_arg_UNPARSED_ARGUMENTS)
60+
message(FATAL_ERROR "Bad arguments (see above)")
61+
endif()
62+
63+
# Default values:
64+
if(NOT DEFINED _tp_arg_BUILD_DIR)
65+
set(dirname "${name}")
66+
if(WIN32)
67+
# Escape specials
68+
string(REPLACE "%" "%25" dirname "${dirname}")
69+
string(REPLACE ":" "%3A" dirname "${dirname}")
70+
string(REPLACE "?" "%3F" dirname "${dirname}")
71+
string(REPLACE "*" "%2A" dirname "${dirname}")
72+
string(REPLACE "\"" "%22" dirname "${dirname}")
73+
string(REPLACE "\\" "%5C" dirname "${dirname}")
74+
string(REPLACE "<" "%3C" dirname "${dirname}")
75+
string(REPLACE ">" "%3E" dirname "${dirname}")
76+
string(REPLACE "|" "%7C" dirname "${dirname}")
77+
endif()
78+
set(_tp_arg_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/TestProject/${dirname}")
79+
endif()
80+
if(NOT DEFINED _tp_arg_GENERATOR)
81+
set(_tp_arg_GENERATOR "${CMAKE_GENERATOR}")
82+
endif()
83+
# Normalize paths
84+
if(NOT IS_ABSOLUTE "${_tp_arg_BUILD_DIR}")
85+
set(_tp_arg_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${_tp_arg_BUILD_DIR}")
86+
endif()
87+
get_filename_component(path "${path}" ABSOLUTE)
88+
message(VERBOSE "Add test project [${path}]")
89+
90+
# Arguments that will be given during the CMake configure step:
91+
string(REPLACE ";" $<SEMICOLON> configure_args "${_tp_arg_CONFIGURE_ARGS}")
92+
93+
# Build the argument lists that will be passed-through to project configuration -D flags:
94+
set(settings_passthru)
95+
96+
# Pass through all _DIR and _PATH variables with a HOST_PROJECT_ prefix:
97+
get_directory_property(fwd_path_vars VARIABLES)
98+
list(FILTER fwd_path_vars INCLUDE REGEX "_DIR$|_PATH$")
99+
list(FILTER fwd_path_vars EXCLUDE REGEX "^_")
100+
list(SORT fwd_path_vars CASE INSENSITIVE)
101+
set(dir_passthrough)
102+
foreach(var IN LISTS fwd_path_vars)
103+
string(REPLACE ";" $<SEMICOLON> value "${${var}}")
104+
list(APPEND settings_passthru "HOST_PROJECT_${var}=${value}")
105+
endforeach()
106+
107+
# Pass through other variables without a prefix:
108+
set(passthru_vars "${_tp_arg_PASSTHRU_VARS}")
109+
# Some platform variables should always go through:
110+
list(APPEND passthru_vars
111+
CMAKE_C_COMPILER
112+
CMAKE_CXX_COMPILER
113+
)
114+
if(DEFINED _tp_arg_PASSTHRU_VARS_REGEX)
115+
# Pass through variables matching a certain pattern:
116+
get_directory_property(fwd_vars VARIABLES)
117+
list(FILTER fwd_vars INCLUDE REGEX "${_tp_arg_PASSTHRU_VARS_REGEX}")
118+
list(APPEND passthru_vars "${fwd_vars}")
119+
endif()
120+
121+
# Pass through all variables that we've marked to be forwarded
122+
foreach(var IN LISTS passthru_vars)
123+
string(REPLACE ";" $<SEMICOLON> value "${${var}}")
124+
list(APPEND settings_passthru "${var}=${value}")
125+
endforeach()
126+
127+
# Settings set with SETTINGS
128+
list(TRANSFORM _tp_arg_SETTINGS REPLACE ";" $<SEMICOLON> OUTPUT_VARIABLE settings_escaped)
129+
list(APPEND settings_passthru ${settings_escaped})
130+
131+
# Add a prefix to each variable to mark it as a pass-thru variable:
132+
list(TRANSFORM settings_passthru PREPEND "-D;TEST_PROJECT_SETTINGS/")
133+
134+
# Generate the test case:
135+
add_test(
136+
NAME "${name}"
137+
COMMAND ${CMAKE_COMMAND}
138+
--log-context
139+
-D "TEST_PROJECT_NAME=${name}"
140+
-D "TEST_PROJECT_PARENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}"
141+
-D "TEST_PROJECT_SOURCE_DIR=${path}"
142+
-D "TEST_PROJECT_BINARY_DIR=${_tp_arg_BUILD_DIR}"
143+
-D "TEST_PROJECT_GENERATOR=${_tp_arg_GENERATOR}"
144+
-D TEST_PROJECT_INSTALL_PARENT=${_tp_arg_INSTALL_PARENT}
145+
-D "TEST_PROJECT_CONFIGURE_ARGS=${_tp_arg_CONFIGURE_ARGS}"
146+
-D "TEST_PROJECT_CONFIG=$<CONFIG>"
147+
${settings_passthru}
148+
-D __test_project_run=1
149+
-P "${CMAKE_CURRENT_FUNCTION_LIST_FILE}"
150+
)
151+
endfunction()
152+
153+
# This function implements the actual test.
154+
function(__do_test_project)
155+
list(APPEND CMAKE_MESSAGE_CONTEXT "TestProject Execution")
156+
cmake_path(ABSOLUTE_PATH TEST_PROJECT_SOURCE_DIR NORMALIZE OUTPUT_VARIABLE src_dir)
157+
cmake_path(ABSOLUTE_PATH TEST_PROJECT_BINARY_DIR NORMALIZE OUTPUT_VARIABLE bin_dir)
158+
159+
string(MD5 test_name_hash "${TEST_PROJECT_NAME}")
160+
set(tmp_install_prefix "${TEST_PROJECT_PARENT_BINARY_DIR}/TestProject-install/${test_name_hash}")
161+
file(REMOVE_RECURSE "${tmp_install_prefix}")
162+
list(APPEND TEST_PROJECT_SETTINGS/CMAKE_INSTALL_PREFIX "${tmp_install_prefix}")
163+
164+
if(TEST_PROJECT_INSTALL_PARENT)
165+
cmake_path(ABSOLUTE_PATH tmp_install_prefix NORMALIZE)
166+
message(STATUS "Installing parent project into [${tmp_install_prefix}]")
167+
execute_process(
168+
COMMAND ${CMAKE_COMMAND}
169+
--install "${TEST_PROJECT_PARENT_BINARY_DIR}"
170+
--prefix "${tmp_install_prefix}"
171+
--config "${TEST_PROJECT_CONFIG}"
172+
COMMAND_ERROR_IS_FATAL LAST
173+
)
174+
endif()
175+
message(STATUS "Project source dir: [${src_dir}]")
176+
message(STATUS "Project build dir: [${bin_dir}]")
177+
message(STATUS "Deleting build directory …")
178+
file(REMOVE_RECURSE "${bin_dir}")
179+
file(MAKE_DIRECTORY "${bin_dir}")
180+
181+
# Grab settings passed-through from the parent project:
182+
get_directory_property(vars VARIABLES)
183+
set(fwd_settings)
184+
list(FILTER vars INCLUDE REGEX "^TEST_PROJECT_SETTINGS/")
185+
if(vars)
186+
message(STATUS "Configuration settings:")
187+
endif()
188+
foreach(var IN LISTS vars)
189+
set(value "${${var}}")
190+
# Remove our prefix
191+
string(REGEX REPLACE "^TEST_PROJECT_SETTINGS/" "" varname "${var}")
192+
# Print the value we received for debugging purposes
193+
message(STATUS " • ${varname}=${value}")
194+
# Escape nested lists
195+
string(REPLACE ";" "\\;" value "${value}")
196+
list(APPEND fwd_settings -D "${varname}=${value}")
197+
endforeach()
198+
199+
message(STATUS "Configuring project [${src_dir}] …")
200+
set(config_log "${bin_dir}/TestProject-configure.log")
201+
message(STATUS "CMake configure output will be written to [${config_log}]")
202+
execute_process(
203+
COMMAND_ECHO STDERR
204+
WORKING_DIRECTORY "${bin_dir}"
205+
RESULT_VARIABLE retc
206+
OUTPUT_VARIABLE output
207+
ERROR_VARIABLE output
208+
ECHO_OUTPUT_VARIABLE
209+
ECHO_ERROR_VARIABLE
210+
COMMAND ${CMAKE_COMMAND}
211+
-S "${src_dir}"
212+
-B "${bin_dir}"
213+
-G "${TEST_PROJECT_GENERATOR}"
214+
-D "CMAKE_BUILD_TYPE=${TEST_PROJECT_BUILD_TYPE}"
215+
--no-warn-unused-cli
216+
--log-context
217+
--log-level=debug
218+
${fwd_settings}
219+
${TEST_PROJECT_CONFIGURE_ARGS}
220+
)
221+
file(WRITE "${config_log}" "${output}")
222+
if(retc)
223+
message(FATAL_ERROR "Configure subcommand failed [${retc}]")
224+
endif()
225+
message(STATUS "CMake configure completed")
226+
227+
set(build_log "${bin_dir}/TestProject-build.log")
228+
message(STATUS "Build output will be written to [${build_log}]")
229+
message(STATUS "Building configured project [${bin_dir}] …")
230+
execute_process(
231+
COMMAND_ECHO STDERR
232+
WORKING_DIRECTORY "${bin_dir}"
233+
RESULT_VARIABLE retc
234+
OUTPUT_VARIABLE out
235+
ERROR_VARIABLE out
236+
ECHO_OUTPUT_VARIABLE
237+
ECHO_ERROR_VARIABLE
238+
COMMAND ${CMAKE_COMMAND}
239+
--build "${bin_dir}"
240+
--config "${TEST_PROJECT_CONFIG}"
241+
)
242+
file(WRITE "${build_log}" "${output}")
243+
if(retc)
244+
message(FATAL_ERROR "Project build failed [${retc}]")
245+
endif()
246+
message(STATUS "Project build completed")
247+
248+
execute_process(
249+
COMMAND ${CMAKE_CTEST_COMMAND}
250+
-T Start -T Test
251+
-C "${TEST_PROJECT_CONFIG}"
252+
--output-on-failure
253+
WORKING_DIRECTORY "${bin_dir}"
254+
COMMAND_ERROR_IS_FATAL LAST
255+
)
256+
endfunction()
257+
258+
if(__test_project_run)
259+
cmake_minimum_required(VERSION 3.20) # cmake_path/execute_process
260+
__do_test_project()
261+
endif()

0 commit comments

Comments
 (0)