-
Notifications
You must be signed in to change notification settings - Fork 54
/
helpers.cmake
345 lines (329 loc) · 13.7 KB
/
helpers.cmake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
#
# Copyright (c) 2023 ZettaScale Technology
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# ZettaScale Zenoh Team, <[email protected]>
#
#
# DO NOT add include_guard() to this file
# It have to be reincluded multiple times on each 'include_project' call to restore
# functions and macros definitions
#
#
# Show VARIABLE = value on configuration stage
#
macro(status_print var)
message(STATUS "${var} = ${${var}}")
endmacro()
#
# Declare cache variable and print VARIABLE = value on configuration stage
#
function(declare_cache_var var default_value type docstring)
set(${var} ${default_value} CACHE ${type} ${docstring})
status_print(${var})
endfunction()
#
# Declare cache variable which is set to TRUE if project is supposedly
# loaded as root project into vscode
#
function(declare_cache_var_true_if_vscode var docstring)
if(CMAKE_CURRENT_BINARY_DIR STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}/build")
set(in_vscode TRUE)
else()
set(in_vscode FALSE)
endif()
declare_cache_var(${var} ${in_vscode} BOOL ${docstring})
endfunction()
#
# Create target named 'debug_print' which prints VARIABLE = value
# when this target is built. Useful to debug generated expressions.
#`
function(debug_print var)
if(NOT TARGET debug_print)
add_custom_target(debug_print GLOBAL)
endif()
add_custom_command(
COMMAND ${CMAKE_COMMAND} -E echo ${var} = ${${var}}
TARGET debug_print
)
endfunction()
#
# initialize `dstvar` with `$<IF:genexpr_condition,srcvar_true,srcvar_false>` generator expression
# only if `srcvar_true` and `srcvar_false` are actually different and if multi-config generator is used.
# Otherwise `dstvar` is initialized with `srcvar_true` or `srcvar_false` value depending on `var_condition` variable.
#
# This is convenient to
# - remove visual garbage when generator expressions actually does nothing
# - avoid using generator expression if single-config generator is used
#
function(set_genexpr_condition dstvar var_condition genexpr_condition srcvar_true srcvar_false)
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(GENERATOR_IS_MULTI_CONFIG AND NOT("${srcvar_true}" STREQUAL "${srcvar_false}"))
list(JOIN srcvar_true "$<SEMICOLON>" srcvar_true)
list(JOIN srcvar_false "$<SEMICOLON>" srcvar_false)
set(${dstvar} $<IF:${genexpr_condition},${srcvar_true},${srcvar_false}> PARENT_SCOPE)
else()
if(DEFINED ${var_condition})
if (${${var_condition}})
set(${dstvar} "${srcvar_true}" PARENT_SCOPE)
else()
set(${dstvar} "${srcvar_false}" PARENT_SCOPE)
endif()
else()
set(${dstvar} "${srcvar_false}" PARENT_SCOPE)
endif()
endif()
endfunction()
#
# Select default build config with support of multi config generators
#
macro(set_default_build_type config_type)
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(GENERATOR_IS_MULTI_CONFIG)
if(${CMAKE_VERSION} VERSION_LESS "3.20")
message(FATAL_ERROR "\n"
"You are using multi config generator '${CMAKE_GENERATOR}' and cmake ${CMAKE_VERSION}\n"
"Unfortuinately multi config generators are not supported by this script for CMake < 3.20 due to insufficient support of generator expressions in old CMake versions\n"
"Please consider upgrading your cmake or switching to single-config generator with `-G` option, like `cmake -G Ninja` or `cmake -G \"Unix Makefiles\"\n")
endif()
if(NOT DEFINED CMAKE_BUILD_TYPE) # if user passed argument '-DCMAKE_BUILD_TYPE=value', use it
set(CMAKE_BUILD_TYPE ${config_type})
endif()
list(FIND CMAKE_CONFIGURATION_TYPES ${CMAKE_BUILD_TYPE} n)
if(n LESS 0)
message(FATAL_ERROR "Configuration ${CMAKE_BUILD_TYPE} is not in CMAKE_CONFIGURATION_TYPES")
else()
if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config")
set(CMAKE_DEFAULT_BUILD_TYPE ${CMAKE_BUILD_TYPE})
status_print(CMAKE_DEFAULT_BUILD_TYPE)
else()
message(STATUS "Default build type is not supported for generator '${CMAKE_GENERATOR}'")
message(STATUS "use cmake --build . --config ${config_type}")
endif()
endif()
else()
if(CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE ${config_type})
endif()
status_print(CMAKE_BUILD_TYPE)
endif()
endmacro()
#
# Add default set of libraries depending on platform
#
function(get_required_static_libs variable)
# actually required list of libraries can be obtained by executing
# cargo rustc -- --print native-static-libs
# This command is not intented to be used in autoamted build yet,
# so actaul libraries are hardcoded for now
if(APPLE)
find_library(FFoundation Foundation)
find_library(FSecurity Security)
set(native_static_libs ${FFoundation} ${FSecurity})
elseif(UNIX)
set(native_static_libs rt pthread m dl)
elseif(WIN32)
set(native_static_libs ws2_32 crypt32 secur32 bcrypt ncrypt userenv ntdll iphlpapi runtimeobject)
endif()
set(${variable} ${native_static_libs} PARENT_SCOPE)
message(STATUS "${variable} = ${native_static_libs}")
endfunction()
#
# Copy necessary dlls to target runtime directory
#
function(copy_dlls target)
if(WIN32)
add_custom_command(TARGET ${target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<IF:$<STREQUAL:$<TARGET_RUNTIME_DLLS:${target}>,>,nul,$<TARGET_RUNTIME_DLLS:${target}>> $<TARGET_FILE_DIR:${target}>
COMMAND_EXPAND_LISTS
)
endif()
endfunction()
#
# get property value avoiding CMake behavior - setting variable to <VAR>-NOTFOUND for undefined property
#
function(get_target_property_if_set var target property)
get_property(is_set TARGET ${target} PROPERTY ${property} SET)
if (NOT is_set)
unset(${var} PARENT_SCOPE)
return()
endif()
get_property(value TARGET ${target} PROPERTY ${property})
set(${var} ${value} PARENT_SCOPE)
endfunction()
#
# Unset variables if they have empty string value
#
macro(unset_if_empty)
foreach(var ${ARGN})
if("${${var}}" STREQUAL "")
unset(${var})
endif()
endforeach()
endmacro()
#
# Usage:
#
# include_project(<project_name> TARGET <target>
# < PATH <project_path>] [QUIET] |
# PACKAGE <package_name>] [QUIET] |
# GIT_URL <git_url> [GIT_TAG <git_tag>] >
# )
#
# includes CMake project with one of the following ways:
# add_subdirectory(project_path) or
# find_package(package_name) or
# FetchContent(git_url)
#
# If target <target> is already defined, does nothing. If parameter QUIET is passed, does nothing
# in case of failure to incude project from requested source. This allows to try to load project
# from first available source, like this:
#
# include_project(zenohc TARGET zenohc::lib PATH ..\zenoh_c QUIET)
# include_project(zenohc TARGET zenohc::lib PACKAGE zenohc QUIET)
# include_project(zenohc TARGET zenohc::lib GIT_URL https://github.com/eclipse-zenoh/zenoh-c)
#
# QUIET parameter not supported for GIT_URL due to lack of support of such mode in FetchContent
#
function(include_project)
__include_project(${ARGN})
# recover functions which may be replaced by included project
# Using here the policy https://cmake.org/cmake/help/v3.16/policy/CMP0017.html#policy:CMP0017
include(helpers OPTIONAL RESULT_VARIABLE helpers_included)
if (NOT helpers_included)
message(FATAL_ERROR "Failed to reinclude helpers.cmake after processing external project\n"
"You have to include helpers.cmake as a module to make the `include` operation above work:\n"
" set(CMAKE_MODULE_PATH \"path-to-this-helpers-cmake\" \${CMAKE_MODULE_PATH})\n"
" include(helpers)\n")
endif()
endfunction()
function(__include_project project_name)
include(FetchContent)
include(CMakeParseArguments)
cmake_parse_arguments(PARSE_ARGV 1 "ARG" "QUIET" "TARGET;PATH;PACKAGE;GIT_URL;GIT_TAG" "")
unset_if_empty(ARG_PATH ARG_TARGET ARG_PACKAGE ARG_GIT_URL ARG_GIT_TAG)
if(NOT DEFINED ARG_TARGET)
message(FATAL_ERROR "Non-empty TARGET parameter is required")
endif()
if(TARGET ${ARG_TARGET})
return()
endif()
if(DEFINED ARG_PATH)
message(STATUS "trying to include project '${project_name} from directory '${ARG_PATH}'")
file(GLOB cmakelists ${ARG_PATH}/CMakeLists.txt)
if(NOT(cmakelists STREQUAL ""))
message(STATUS "found cmake project in directory, including it")
list(APPEND CMAKE_MESSAGE_INDENT " ")
add_subdirectory(${ARG_PATH} ${project_name})
list(POP_BACK CMAKE_MESSAGE_INDENT)
if(TARGET ${ARG_TARGET} OR ARG_QUIET)
return()
endif()
message(FATAL_ERROR "Project at '${ARG_PATH}' should define target ${ARG_TARGET}")
elseif(ARG_QUIET)
return()
else()
message(FATAL_ERROR "no CMakeLists.txt file in '${ARG_PATH}'")
endif()
elseif(DEFINED ARG_PACKAGE)
message(STATUS "trying to include project '${project_name}' from package '${ARG_PACKAGE}'")
# Give priority to install directory
# Useful for development when older version of the project version may be installed in system
#
# TODO: "if( NOT TARGET" below should be not necessary
# (see https://cmake.org/cmake/help/latest/command/find_package.html, search for "override the order")
# but in fact cmake fails without it when zenohc is present both in CMAKE_INSTALL_PREFIX and in /usr/local.
# Consider is it still necessary after next bumping up cmake version
find_package(${ARG_PACKAGE} PATHS ${CMAKE_INSTALL_PREFIX} NO_DEFAULT_PATH QUIET)
if(NOT TARGET ${ARG_TARGET})
find_package(${ARG_PACKAGE} QUIET)
endif()
set(package_path ${${ARG_PACKAGE}_CONFIG})
if(TARGET ${ARG_TARGET})
message(STATUS "found the package on path '${package_path}'")
return()
endif()
if(ARG_QUIET)
return()
endif()
if("${package_path}" STREQUAL "")
message(FATAL_ERROR "Package '${ARG_PACKAGE}' not found")
else()
message(FATAL_ERROR "Package '${ARG_PACKAGE}' on path '${package_path}' doesn't define target '${ARG_TARGET}")
endif()
elseif(DEFINED ARG_GIT_URL)
if(DEFINED ARG_GIT_TAG)
set(git_url "${ARG_GIT_URL}#${ARG_GIT_TAG}")
else()
set(git_url ${ARG_GIT_URL})
endif()
message(STATUS "trying to include project '${project_name}' from git '${git_url}'")
list(APPEND CMAKE_MESSAGE_INDENT " ")
if(DEFINED ARG_GIT_TAG)
FetchContent_Declare(${project_name}
GIT_REPOSITORY ${ARG_GIT_URL}
GIT_TAG ${ARG_GIT_TAG}
)
else()
FetchContent_Declare(${project_name}
GIT_REPOSITORY ${ARG_GIT_URL}
)
endif()
FetchContent_MakeAvailable(${project_name})
list(POP_BACK CMAKE_MESSAGE_INDENT)
if(TARGET ${ARG_TARGET})
return()
endif()
message(FATAL_ERROR "Project at ${git_url} should define target ${ARG_TARGET}")
else()
message(FATAL_ERROR "No source for project '${project_name}' specified")
endif()
endfunction()
#
# Configure set of cache variables
# Include external project accordingly to these variables
#
# Example:
#
# configure_include_project(ZENOHC zenohc zenohc::lib ".." zenohc "https://github.com/eclipse-zenoh/zenoh-c" "")
#
# This command defines cache variables
#
# ZENOHC_SOURCE = ""
# ZENOHC_PATH = ".."
# ZENOHC_PACKAGE = "zenohc"
# ZENOHC_GIT_URL = "https://github.com/eclipse-zenoh/zenoh-c"
# ZENOHC_GIT_TAG = ""
#
# Then it tries to include the project with name 'zenohc' from first available source in order (PATH,PACKAGE,GIT_URL).
# Project should define target `zenohc::lib`, otherwise cmake configuration step fails with error.
#
# If ZENOHC_SOURCE is set by user to value PATH, PACKAGE or GIT_URL, then the project is included from this source only.
#
# For example:
#
# cmake ../zenoh-c/examples -DZENOHC_SOURCE=GIT_URL -DZENOHC_GIT_URL=https://github.com/username/zenoh-c
#
# makes 'examples' project to compile with zenoh-c from username's zenoh-c git repository
#
function(configure_include_project var_prefix project target path package git_url git_tag)
declare_cache_var(${var_prefix}_SOURCE "" STRING "Explicit ${project} source type. Can be PATH, PACKAGE or GIT_URL. If empty, tries all these variants in order")
declare_cache_var(${var_prefix}_PATH "${path}" STRING "PATH to ${project} source directory")
declare_cache_var(${var_prefix}_PACKAGE "${package}" STRING "name of ${project} PACKAGE")
declare_cache_var(${var_prefix}_GIT_URL "${git_url}" STRING "GIT_URL of ${project} repository")
declare_cache_var(${var_prefix}_GIT_TAG "${git_tag}" STRING "GIT_TAG of ${project} repository")
if(${var_prefix}_SOURCE STREQUAL "")
include_project(${project} TARGET ${target} PATH ${${var_prefix}_PATH} QUIET)
include_project(${project} TARGET ${target} PACKAGE ${${var_prefix}_PACKAGE} QUIET)
include_project(${project} TARGET ${target} GIT_URL ${${var_prefix}_GIT_URL} GIT_TAG ${${var_prefix}_GIT_TAG})
else()
include_project(${project} TARGET ${target} ${${var_prefix}_SOURCE} ${${var_prefix}_${${var_prefix}_SOURCE}} GIT_TAG ${${var_prefix}_GIT_TAG})
endif()
endfunction()