Skip to content

Commit f519ba9

Browse files
John Elliottfacebook-github-bot
John Elliott
authored andcommitted
Add rust_cxx_bridge CMake function (#7518)
Summary: X-link: facebookincubator/hsthrift#120 X-link: facebook/folly#2096 X-link: facebookexperimental/edencommon#14 X-link: facebook/mvfst#322 X-link: facebookincubator/delos_core#9 X-link: facebook/fboss#166 X-link: facebookincubator/zstrong#617 X-link: facebookincubator/katran#208 X-link: facebookincubator/fizz#102 X-link: facebookexternal/traffixr#4 X-link: facebook/watchman#1173 X-link: facebook/proxygen#473 X-link: facebook/hhvm#9411 X-link: facebook/fbthrift#587 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 Reviewed By: xavierd Differential Revision: D51160627
1 parent 40f9dc3 commit f519ba9

File tree

1 file changed

+200
-0
lines changed

1 file changed

+200
-0
lines changed

build/fbcode_builder/CMake/RustStaticLibrary.cmake

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

0 commit comments

Comments
 (0)