Skip to content

Commit 7466637

Browse files
John Elliottfacebook-github-bot
authored andcommitted
Add rust_cxx_bridge CMake function
Summary: We need a better way to create cxxbridges - something that uses the recommended method of cxxbridge-cmd. This function creates C++ bindings using the [cxx] crate. Original function found here: https://github.com/corrosion-rs/corrosion/blob/master/cmake/Corrosion.cmake#L1390 Differential Revision: D51160627
1 parent 8e4c6e5 commit 7466637

File tree

1 file changed

+188
-0
lines changed

1 file changed

+188
-0
lines changed

build/fbcode_builder/CMake/RustStaticLibrary.cmake

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,3 +314,191 @@ function(install_rust_static_library TARGET)
314314
DESTINATION ${ARG_INSTALL_DIR}
315315
)
316316
endfunction()
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+
execute_process(COMMAND
378+
${CARGO_COMMAND} tree -i cxx --depth=0
379+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
380+
RESULT_VARIABLE cxx_version_result
381+
OUTPUT_VARIABLE cxx_version_output
382+
)
383+
384+
if(NOT "${cxx_version_result}" EQUAL "0")
385+
message(FATAL_ERROR "Crate ${crate_name} does not depend on cxx.")
386+
endif()
387+
if(cxx_version_output MATCHES "cxx v([0-9]+.[0-9]+.[0-9]+)")
388+
set(cxx_required_version "${CMAKE_MATCH_1}")
389+
else()
390+
message(
391+
FATAL_ERROR
392+
"Failed to parse cxx version from cargo tree output: `cxx_version_output`")
393+
endif()
394+
395+
# First check if a suitable version of cxxbridge is installed
396+
find_program(INSTALLED_CXXBRIDGE cxxbridge PATHS "$ENV{HOME}/.cargo/bin/")
397+
mark_as_advanced(INSTALLED_CXXBRIDGE)
398+
if(INSTALLED_CXXBRIDGE)
399+
execute_process(
400+
COMMAND ${INSTALLED_CXXBRIDGE}
401+
--version OUTPUT_VARIABLE cxxbridge_version_output)
402+
if(cxxbridge_version_output MATCHES "cxxbridge ([0-9]+.[0-9]+.[0-9]+)")
403+
set(cxxbridge_version "${CMAKE_MATCH_1}")
404+
else()
405+
set(cxxbridge_version "")
406+
endif()
407+
endif()
408+
409+
set(cxxbridge "")
410+
if(cxxbridge_version)
411+
if(cxxbridge_version VERSION_EQUAL cxx_required_version)
412+
set(cxxbridge "${INSTALLED_CXXBRIDGE}")
413+
if(NOT TARGET "cxxbridge_v${cxx_required_version}")
414+
# Add an empty target.
415+
add_custom_target("cxxbridge_v${cxx_required_version}")
416+
endif()
417+
endif()
418+
endif()
419+
420+
# No suitable version of cxxbridge was installed,
421+
# so use custom target to install correct version.
422+
if(NOT cxxbridge)
423+
if(NOT TARGET "cxxbridge_v${cxx_required_version}")
424+
add_custom_command(
425+
OUTPUT
426+
"${CMAKE_BINARY_DIR}/cxxbridge_v${cxx_required_version}/bin/cxxbridge"
427+
COMMAND
428+
${CMAKE_COMMAND} -E make_directory
429+
"${CMAKE_BINARY_DIR}/cxxbridge_v${cxx_required_version}"
430+
COMMAND
431+
${CARGO_COMMAND} install cxxbridge-cmd
432+
--version "${cxx_required_version}"
433+
--root "${CMAKE_BINARY_DIR}/cxxbridge_v${cxx_required_version}"
434+
--quiet
435+
# todo: use --target-dir to potentially reuse artifacts
436+
COMMENT "Installing cxxbridge (version ${cxx_required_version})"
437+
)
438+
add_custom_target(
439+
"cxxbridge_v${cxx_required_version}"
440+
DEPENDS
441+
"${CMAKE_BINARY_DIR}/cxxbridge_v${cxx_required_version}/bin/cxxbridge"
442+
)
443+
endif()
444+
set(
445+
cxxbridge
446+
"${CMAKE_BINARY_DIR}/cxxbridge_v${cxx_required_version}/bin/cxxbridge")
447+
endif()
448+
449+
add_library(${crate_name} STATIC)
450+
target_include_directories(
451+
${crate_name}
452+
PUBLIC
453+
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
454+
$<INSTALL_INTERFACE:include>
455+
)
456+
457+
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/rust")
458+
add_custom_command(
459+
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/rust/cxx.h"
460+
COMMAND
461+
${cxxbridge}
462+
--header --output "${CMAKE_CURRENT_BINARY_DIR}/rust/cxx.h"
463+
DEPENDS "cxxbridge_v${cxx_required_version}"
464+
COMMENT "Generating rust/cxx.h header"
465+
)
466+
467+
get_filename_component(filename_component ${CXX_BRIDGE_FILE} NAME)
468+
get_filename_component(directory_component ${CXX_BRIDGE_FILE} DIRECTORY)
469+
set(directory "")
470+
if(directory_component)
471+
set(directory "${directory_component}")
472+
endif()
473+
474+
set(cxx_header ${directory}/${filename_component}.h)
475+
set(cxx_source ${directory}/${filename_component}.cc)
476+
set(rust_source_path "${CMAKE_CURRENT_SOURCE_DIR}/${CXX_BRIDGE_FILE}")
477+
478+
file(
479+
MAKE_DIRECTORY
480+
"${CMAKE_CURRENT_BINARY_DIR}/${directory_component}"
481+
)
482+
483+
add_custom_command(
484+
OUTPUT
485+
"${CMAKE_CURRENT_BINARY_DIR}/${cxx_header}"
486+
"${CMAKE_CURRENT_BINARY_DIR}/${cxx_source}"
487+
COMMAND
488+
${cxxbridge} ${rust_source_path}
489+
--header --output "${CMAKE_CURRENT_BINARY_DIR}/${cxx_header}"
490+
COMMAND
491+
${cxxbridge} ${rust_source_path}
492+
--output "${CMAKE_CURRENT_BINARY_DIR}/${cxx_source}"
493+
--include "${cxx_header}"
494+
DEPENDS "cxxbridge_v${cxx_required_version}" "${rust_source_path}"
495+
COMMENT "Generating cxx bindings for crate ${crate_name}"
496+
)
497+
498+
target_sources(${crate_name}
499+
PRIVATE
500+
"${CMAKE_CURRENT_BINARY_DIR}/${cxx_header}"
501+
"${CMAKE_CURRENT_BINARY_DIR}/rust/cxx.h"
502+
"${CMAKE_CURRENT_BINARY_DIR}/${cxx_source}"
503+
)
504+
endfunction()

0 commit comments

Comments
 (0)