@@ -314,3 +314,203 @@ function(install_rust_static_library TARGET)
314314 DESTINATION ${ARG_INSTALL_DIR}
315315 )
316316endfunction ()
317+
318+ # This function creates C++ bindings using the [cxx] crate.
319+ #
320+ # Original function found here: https://github.com/corrosion-rs/corrosion/blob/master/cmake/Corrosion.cmake#L1390
321+ # Simplified for use as part of RustStaticLibrary module. License below.
322+ #
323+ # MIT License
324+ #
325+ # Copyright (c) 2018 Andrew Gaspar
326+ #
327+ # Permission is hereby granted, free of charge, to any person obtaining a copy
328+ # of this software and associated documentation files (the "Software"), to deal
329+ # in the Software without restriction, including without limitation the rights
330+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
331+ # copies of the Software, and to permit persons to whom the Software is
332+ # furnished to do so, subject to the following conditions:
333+ #
334+ # The above copyright notice and this permission notice shall be included in all
335+ # copies or substantial portions of the Software.
336+ #
337+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
338+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
339+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
340+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
341+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
342+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
343+ # SOFTWARE.
344+ #
345+ # The rules approximately do the following:
346+ # - Check which version of `cxx` the Rust crate depends on.
347+ # - Check if the exact same version of `cxxbridge-cmd` is installed
348+ # - If not, create a rule to build the exact same version of `cxxbridge-cmd`.
349+ # - Create rules to run `cxxbridge` and generate
350+ # - The `rust/cxx.h` header
351+ # - A header and source file for the specified CXX_BRIDGE_FILE.
352+ # - The generated sources (and header include directories) are added to the
353+ # `${TARGET}` CMake library target.
354+ #
355+ # ```cmake
356+ # rust_cxx_bridge(<TARGET> <CXX_BRIDGE_FILE> [CRATE <CRATE_NAME>])
357+ # ```
358+ #
359+ # Parameters:
360+ # - TARGET:
361+ # Name of the target name. The target that the bridge will be included with.
362+ # - CXX_BRIDGE_FILE:
363+ # Name of the file that include the cxxbridge (e.g., "src/ffi.rs").
364+ # - CRATE_NAME:
365+ # Name of the crate. This parameter is optional. If unspecified, it will
366+ # fallback to `${TARGET}`.
367+ #
368+ function (rust_cxx_bridge TARGET CXX_BRIDGE_FILE)
369+ fb_cmake_parse_args(ARG "" "CRATE" "" "${ARGN} " )
370+
371+ if (DEFINED ARG_CRATE)
372+ set (crate_name "${ARG_CRATE} " )
373+ else ()
374+ set (crate_name "${TARGET} " )
375+ endif ()
376+
377+ if (USE_CARGO_VENDOR)
378+ set (extra_cargo_env "CARGO_HOME=${RUST_CARGO_HOME} " )
379+ endif ()
380+
381+ execute_process (
382+ COMMAND
383+ "${CMAKE_COMMAND} " -E env
384+ ${extra_cargo_env}
385+ "${CARGO_COMMAND} " tree -i cxx --depth=0
386+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
387+ RESULT_VARIABLE cxx_version_result
388+ OUTPUT_VARIABLE cxx_version_output
389+ )
390+
391+ if (NOT "${cxx_version_result} " EQUAL "0" )
392+ message (FATAL_ERROR "Crate ${crate_name} does not depend on cxx." )
393+ endif ()
394+ if (cxx_version_output MATCHES "cxx v([0-9]+.[0-9]+.[0-9]+)" )
395+ set (cxx_required_version "${CMAKE_MATCH_1} " )
396+ else ()
397+ message (
398+ FATAL_ERROR
399+ "Failed to parse cxx version from cargo tree output: `cxx_version_output`" )
400+ endif ()
401+
402+ # First check if a suitable version of cxxbridge is installed
403+ find_program (INSTALLED_CXXBRIDGE cxxbridge PATHS "$ENV{HOME} /.cargo/bin/" )
404+ mark_as_advanced (INSTALLED_CXXBRIDGE)
405+ if (INSTALLED_CXXBRIDGE)
406+ execute_process (
407+ COMMAND "${INSTALLED_CXXBRIDGE} " --version
408+ OUTPUT_VARIABLE cxxbridge_version_output
409+ )
410+ if (cxxbridge_version_output MATCHES "cxxbridge ([0-9]+.[0-9]+.[0-9]+)" )
411+ set (cxxbridge_version "${CMAKE_MATCH_1} " )
412+ else ()
413+ set (cxxbridge_version "" )
414+ endif ()
415+ endif ()
416+
417+ set (cxxbridge "" )
418+ if (cxxbridge_version)
419+ if (cxxbridge_version VERSION_EQUAL cxx_required_version)
420+ set (cxxbridge "${INSTALLED_CXXBRIDGE} " )
421+ if (NOT TARGET "cxxbridge_v${cxx_required_version} " )
422+ # Add an empty target.
423+ add_custom_target ("cxxbridge_v${cxx_required_version} " )
424+ endif ()
425+ endif ()
426+ endif ()
427+
428+ # No suitable version of cxxbridge was installed,
429+ # so use custom target to install correct version.
430+ if (NOT cxxbridge)
431+ if (NOT TARGET "cxxbridge_v${cxx_required_version} " )
432+ add_custom_command (
433+ OUTPUT
434+ "${CMAKE_BINARY_DIR} /cxxbridge_v${cxx_required_version} /bin/cxxbridge"
435+ COMMAND
436+ "${CMAKE_COMMAND} " -E make_directory
437+ "${CMAKE_BINARY_DIR} /cxxbridge_v${cxx_required_version} "
438+ COMMAND
439+ "${CMAKE_COMMAND} " -E remove -f "${CMAKE_CURRENT_SOURCE_DIR} /Cargo.lock"
440+ COMMAND
441+ "${CMAKE_COMMAND} " -E env
442+ ${extra_cargo_env}
443+ "${CARGO_COMMAND} " install cxxbridge-cmd
444+ --version "${cxx_required_version} "
445+ --root "${CMAKE_BINARY_DIR} /cxxbridge_v${cxx_required_version} "
446+ --quiet
447+ COMMAND
448+ "${CMAKE_COMMAND} " -E remove -f "${CMAKE_CURRENT_SOURCE_DIR} /Cargo.lock"
449+ COMMENT "Installing cxxbridge (version ${cxx_required_version} )"
450+ )
451+ add_custom_target (
452+ "cxxbridge_v${cxx_required_version} "
453+ DEPENDS "${CMAKE_BINARY_DIR} /cxxbridge_v${cxx_required_version} /bin/cxxbridge"
454+ )
455+ endif ()
456+ set (
457+ cxxbridge
458+ "${CMAKE_BINARY_DIR} /cxxbridge_v${cxx_required_version} /bin/cxxbridge"
459+ )
460+ endif ()
461+
462+ add_library (${crate_name} STATIC )
463+ target_include_directories (
464+ ${crate_name}
465+ PUBLIC
466+ $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR} >
467+ $<INSTALL_INTERFACE:include >
468+ )
469+
470+ file (MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR} /rust" )
471+ add_custom_command (
472+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR} /rust/cxx.h"
473+ COMMAND
474+ "${cxxbridge} " --header --output "${CMAKE_CURRENT_BINARY_DIR} /rust/cxx.h"
475+ DEPENDS "cxxbridge_v${cxx_required_version} "
476+ COMMENT "Generating rust/cxx.h header"
477+ )
478+
479+ get_filename_component (filename_component ${CXX_BRIDGE_FILE} NAME )
480+ get_filename_component (directory_component ${CXX_BRIDGE_FILE} DIRECTORY )
481+ set (directory "" )
482+ if (directory_component)
483+ set (directory "${directory_component} " )
484+ endif ()
485+
486+ set (cxx_header ${directory} /${filename_component} .h)
487+ set (cxx_source ${directory} /${filename_component} .cc)
488+ set (rust_source_path "${CMAKE_CURRENT_SOURCE_DIR} /${CXX_BRIDGE_FILE} " )
489+
490+ file (
491+ MAKE_DIRECTORY
492+ "${CMAKE_CURRENT_BINARY_DIR} /${directory_component} "
493+ )
494+
495+ add_custom_command (
496+ OUTPUT
497+ "${CMAKE_CURRENT_BINARY_DIR} /${cxx_header} "
498+ "${CMAKE_CURRENT_BINARY_DIR} /${cxx_source} "
499+ COMMAND
500+ ${cxxbridge} ${rust_source_path}
501+ --header --output "${CMAKE_CURRENT_BINARY_DIR} /${cxx_header} "
502+ COMMAND
503+ ${cxxbridge} ${rust_source_path}
504+ --output "${CMAKE_CURRENT_BINARY_DIR} /${cxx_source} "
505+ --include "${cxx_header} "
506+ DEPENDS "cxxbridge_v${cxx_required_version} " "${rust_source_path} "
507+ COMMENT "Generating cxx bindings for crate ${crate_name} "
508+ )
509+
510+ target_sources (${crate_name}
511+ PRIVATE
512+ "${CMAKE_CURRENT_BINARY_DIR} /${cxx_header} "
513+ "${CMAKE_CURRENT_BINARY_DIR} /rust/cxx.h"
514+ "${CMAKE_CURRENT_BINARY_DIR} /${cxx_source} "
515+ )
516+ endfunction ()
0 commit comments