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