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