1
+ include (FBCMakeParseArgs )
2
+
3
+ set (
4
+ USE_CARGO_VENDOR AUTO CACHE STRING
5
+ "Download Rust Crates from an internally vendored location"
6
+ )
7
+ set_property (CACHE USE_CARGO_VENDOR PROPERTY STRINGS AUTO ON OFF )
8
+
9
+ set (RUST_VENDORED_CRATES_DIR "$ENV{RUST_VENDORED_CRATES_DIR} " )
10
+ if ("${USE_CARGO_VENDOR} " STREQUAL "AUTO" )
11
+ if (EXISTS "${RUST_VENDORED_CRATES_DIR} " )
12
+ set (USE_CARGO_VENDOR ON )
13
+ else ()
14
+ set (USE_CARGO_VENDOR OFF )
15
+ endif ()
16
+ endif ()
17
+
18
+ if (USE_CARGO_VENDOR )
19
+ if (NOT EXISTS "${RUST_VENDORED_CRATES_DIR} " )
20
+ message (
21
+ FATAL "vendored rust crates not present: "
22
+ "${RUST_VENDORED_CRATES_DIR} "
23
+ )
24
+ endif ()
25
+
26
+ set (RUST_CARGO_HOME "${CMAKE_BINARY_DIR} /_cargo_home" )
27
+ file (MAKE_DIRECTORY "${RUST_CARGO_HOME} " )
28
+
29
+ file (
30
+ TO_NATIVE_PATH "${RUST_VENDORED_CRATES_DIR} "
31
+ ESCAPED_RUST_VENDORED_CRATES_DIR
32
+ )
33
+ string (
34
+ REPLACE "\\ " "\\\\ "
35
+ ESCAPED_RUST_VENDORED_CRATES_DIR
36
+ "${ESCAPED_RUST_VENDORED_CRATES_DIR} "
37
+ )
38
+ file (
39
+ WRITE "${RUST_CARGO_HOME} /config"
40
+ "[source.crates-io]\n "
41
+ "replace-with = \" vendored-sources\"\n "
42
+ "\n "
43
+ "[source.vendored-sources]\n "
44
+ "directory = \" ${ESCAPED_RUST_VENDORED_CRATES_DIR} \"\n "
45
+ )
46
+ endif ()
47
+
48
+ # Cargo is a build system in itself, and thus will try to take advantage of all
49
+ # the cores on the system. Unfortunately, this conflicts with Ninja, since it
50
+ # also tries to utilize all the cores. This can lead to a system that is
51
+ # completely overloaded with compile jobs to the point where nothing else can
52
+ # be achieved on the system.
53
+ #
54
+ # Let's inform Ninja of this fact so it won't try to spawn other jobs while
55
+ # Rust being compiled.
56
+ set_property (GLOBAL APPEND PROPERTY JOB_POOLS rust_job_pool=1 )
57
+
58
+ # This function creates an interface library target based on the static library
59
+ # built by Cargo. It will call Cargo to build a staticlib and generate a CMake
60
+ # interface library with it.
61
+ #
62
+ # This function requires `find_package(Python COMPONENTS Interpreter)`.
63
+ #
64
+ # You need to set `lib:crate-type = ["staticlib"]` in your Cargo.toml to make
65
+ # Cargo build static library.
66
+ #
67
+ # ```cmake
68
+ # rust_static_library(<TARGET> [CRATE <CRATE_NAME>])
69
+ # ```
70
+ #
71
+ # Parameters:
72
+ # - TARGET:
73
+ # Name of the target name. This function will create an interface library
74
+ # target with this name.
75
+ # - CRATE_NAME:
76
+ # Name of the crate. This parameter is optional. If unspecified, it will
77
+ # fallback to `${TARGET}`.
78
+ #
79
+ # This function creates two targets:
80
+ # - "${TARGET}": an interface library target contains the static library built
81
+ # from Cargo.
82
+ # - "${TARGET}.cargo": an internal custom target that invokes Cargo.
83
+ #
84
+ # If you are going to use this static library from C/C++, you will need to
85
+ # write header files for the library (or generate with cbindgen) and bind these
86
+ # headers with the interface library.
87
+ #
88
+ function (rust_static_library TARGET )
89
+ fb_cmake_parse_args (ARG "" "CRATE" "" "${ARGN} " )
90
+
91
+ if (DEFINED ARG_CRATE )
92
+ set (crate_name "${ARG_CRATE} " )
93
+ else ()
94
+ set (crate_name "${TARGET} " )
95
+ endif ()
96
+
97
+ set (cargo_target "${TARGET} .cargo" )
98
+ set (target_dir $< IF:$< CONFIG:Debug> ,debug,release> )
99
+ set (staticlib_name "${CMAKE_STATIC_LIBRARY_PREFIX}${crate_name}${CMAKE_STATIC_LIBRARY_SUFFIX} " )
100
+ set (rust_staticlib "${CMAKE_CURRENT_BINARY_DIR} /${target_dir} /${staticlib_name} " )
101
+
102
+ set (cargo_cmd cargo )
103
+ if (WIN32 )
104
+ set (cargo_cmd cargo.exe )
105
+ endif ()
106
+
107
+ set (cargo_flags build $< IF:$< CONFIG:Debug> ,,--release> -p ${crate_name} )
108
+ if (USE_CARGO_VENDOR )
109
+ set (extra_cargo_env "CARGO_HOME=${RUST_CARGO_HOME} " )
110
+ set (cargo_flags ${cargo_flags} )
111
+ endif ()
112
+
113
+ add_custom_target (
114
+ ${cargo_target}
115
+ COMMAND
116
+ "${CMAKE_COMMAND} " -E remove -f "${CMAKE_CURRENT_SOURCE_DIR} /Cargo.lock"
117
+ COMMAND
118
+ "${CMAKE_COMMAND} " -E env
119
+ "CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR} "
120
+ ${extra_cargo_env}
121
+ ${cargo_cmd}
122
+ ${cargo_flags}
123
+ COMMENT "Building Rust crate '${crate_name} '..."
124
+ JOB_POOL rust_job_pool
125
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
126
+ BYPRODUCTS
127
+ "${CMAKE_CURRENT_BINARY_DIR} /debug/${staticlib_name} "
128
+ "${CMAKE_CURRENT_BINARY_DIR} /release/${staticlib_name} "
129
+ )
130
+
131
+ add_library (${TARGET} INTERFACE )
132
+ add_dependencies (${TARGET} ${cargo_target} )
133
+ set_target_properties (
134
+ ${TARGET}
135
+ PROPERTIES
136
+ INTERFACE_STATICLIB_OUTPUT_PATH "${rust_staticlib} "
137
+ INTERFACE_INSTALL_LIBNAME
138
+ "${CMAKE_STATIC_LIBRARY_PREFIX}${crate_name} _rs${CMAKE_STATIC_LIBRARY_SUFFIX} "
139
+ )
140
+ target_link_libraries (
141
+ ${TARGET}
142
+ INTERFACE "$<BUILD_INTERFACE:${rust_staticlib} >"
143
+ )
144
+ endfunction ()
145
+
146
+ # This function instructs cmake to define a target that will use `cargo build`
147
+ # to build a bin crate referenced by the Cargo.toml file in the current source
148
+ # directory.
149
+ # It accepts a single `TARGET` parameter which will be passed as the package
150
+ # name to `cargo build -p TARGET`. If binary has different name as package,
151
+ # use optional flag BINARY_NAME to override it.
152
+ # The cmake target will be registered to build by default as part of the
153
+ # ALL target.
154
+ function (rust_executable TARGET )
155
+ fb_cmake_parse_args (ARG "" "BINARY_NAME" "" "${ARGN} " )
156
+
157
+ set (crate_name "${TARGET} " )
158
+ set (cargo_target "${TARGET} .cargo" )
159
+ set (target_dir $< IF:$< CONFIG:Debug> ,debug,release> )
160
+
161
+ if (DEFINED ARG_BINARY_NAME )
162
+ set (executable_name "${ARG_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX} " )
163
+ else ()
164
+ set (executable_name "${crate_name}${CMAKE_EXECUTABLE_SUFFIX} " )
165
+ endif ()
166
+
167
+ set (cargo_cmd cargo )
168
+ if (WIN32 )
169
+ set (cargo_cmd cargo.exe )
170
+ endif ()
171
+
172
+ set (cargo_flags build $< IF:$< CONFIG:Debug> ,,--release> -p ${crate_name} )
173
+ if (USE_CARGO_VENDOR )
174
+ set (extra_cargo_env "CARGO_HOME=${RUST_CARGO_HOME} " )
175
+ set (cargo_flags ${cargo_flags} )
176
+ endif ()
177
+
178
+ add_custom_target (
179
+ ${cargo_target}
180
+ ALL
181
+ COMMAND
182
+ "${CMAKE_COMMAND} " -E remove -f "${CMAKE_CURRENT_SOURCE_DIR} /Cargo.lock"
183
+ COMMAND
184
+ "${CMAKE_COMMAND} " -E env
185
+ "CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR} "
186
+ ${extra_cargo_env}
187
+ ${cargo_cmd}
188
+ ${cargo_flags}
189
+ COMMENT "Building Rust executable '${crate_name} '..."
190
+ JOB_POOL rust_job_pool
191
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
192
+ BYPRODUCTS
193
+ "${CMAKE_CURRENT_BINARY_DIR} /debug/${executable_name} "
194
+ "${CMAKE_CURRENT_BINARY_DIR} /release/${executable_name} "
195
+ )
196
+
197
+ set_property (TARGET "${cargo_target} "
198
+ PROPERTY EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR} /${target_dir} /${executable_name} " )
199
+ endfunction ()
200
+
201
+ # This function can be used to install the executable generated by a prior
202
+ # call to the `rust_executable` function.
203
+ # It requires a `TARGET` parameter to identify the target to be installed,
204
+ # and an optional `DESTINATION` parameter to specify the installation
205
+ # directory. If DESTINATION is not specified then the `bin` directory
206
+ # will be assumed.
207
+ function (install_rust_executable TARGET )
208
+ # Parse the arguments
209
+ set (one_value_args DESTINATION )
210
+ set (multi_value_args )
211
+ fb_cmake_parse_args (
212
+ ARG "" "${one_value_args} " "${multi_value_args} " "${ARGN} "
213
+ )
214
+
215
+ if (NOT DEFINED ARG_DESTINATION )
216
+ set (ARG_DESTINATION bin )
217
+ endif ()
218
+
219
+ get_target_property (foo "${TARGET} .cargo" EXECUTABLE )
220
+
221
+ install (
222
+ PROGRAMS "${foo} "
223
+ DESTINATION "${ARG_DESTINATION} "
224
+ )
225
+ endfunction ()
226
+
227
+ # This function installs the interface target generated from the function
228
+ # `rust_static_library`. Use this function if you want to export your Rust
229
+ # target to external CMake targets.
230
+ #
231
+ # ```cmake
232
+ # install_rust_static_library(
233
+ # <TARGET>
234
+ # INSTALL_DIR <INSTALL_DIR>
235
+ # [EXPORT <EXPORT_NAME>]
236
+ # )
237
+ # ```
238
+ #
239
+ # Parameters:
240
+ # - TARGET: Name of the Rust static library target.
241
+ # - EXPORT_NAME: Name of the exported target.
242
+ # - INSTALL_DIR: Path to the directory where this library will be installed.
243
+ #
244
+ function (install_rust_static_library TARGET )
245
+ fb_cmake_parse_args (ARG "" "EXPORT;INSTALL_DIR" "" "${ARGN} " )
246
+
247
+ get_property (
248
+ staticlib_output_path
249
+ TARGET "${TARGET} "
250
+ PROPERTY INTERFACE_STATICLIB_OUTPUT_PATH
251
+ )
252
+ get_property (
253
+ staticlib_output_name
254
+ TARGET "${TARGET} "
255
+ PROPERTY INTERFACE_INSTALL_LIBNAME
256
+ )
257
+
258
+ if (NOT DEFINED staticlib_output_path )
259
+ message (FATAL_ERROR "Not a rust_static_library target." )
260
+ endif ()
261
+
262
+ if (NOT DEFINED ARG_INSTALL_DIR )
263
+ message (FATAL_ERROR "Missing required argument." )
264
+ endif ()
265
+
266
+ if (DEFINED ARG_EXPORT )
267
+ set (install_export_args EXPORT "${ARG_EXPORT} " )
268
+ endif ()
269
+
270
+ set (install_interface_dir "${ARG_INSTALL_DIR} " )
271
+ if (NOT IS_ABSOLUTE "${install_interface_dir} " )
272
+ set (install_interface_dir "\$ {_IMPORT_PREFIX}/${install_interface_dir} " )
273
+ endif ()
274
+
275
+ target_link_libraries (
276
+ ${TARGET} INTERFACE
277
+ "$<INSTALL_INTERFACE:${install_interface_dir} /${staticlib_output_name} >"
278
+ )
279
+ install (
280
+ TARGETS ${TARGET}
281
+ ${install_export_args}
282
+ LIBRARY DESTINATION ${ARG_INSTALL_DIR}
283
+ )
284
+ install (
285
+ FILES ${staticlib_output_path}
286
+ RENAME ${staticlib_output_name}
287
+ DESTINATION ${ARG_INSTALL_DIR}
288
+ )
289
+ endfunction ()
0 commit comments