# ============================================================================= # first, get some info about the compiler (and bindgen) # get rustc binary if(NOT DEFINED RUSTC_BIN OR "${RUSTC_BIN}" STREQUAL "") find_program(RUSTC_BIN rustc REQUIRED) endif() set(RUSTC_BIN ${RUSTC_BIN} CACHE FILEPATH "rustc command") message(NOTICE "-- RUSTC_BIN is ${RUSTC_BIN}") # get cbindgen binary if(NOT DEFINED CBINDGEN_BIN OR "${CBINDGEN_BIN}" STREQUAL "") find_program(CBINDGEN_BIN cbindgen REQUIRED) endif() set(CBINDGEN_BIN ${CBINDGEN_BIN} CACHE FILEPATH "cbindgen command") message(NOTICE "-- CBINDGEN_BIN is ${CBINDGEN_BIN}") # get bindgen binary if(NOT DEFINED BINDGEN_BIN OR "${BINDGEN_BIN}" STREQUAL "") find_program(BINDGEN_BIN bindgen REQUIRED) endif() set(BINDGEN_BIN ${BINDGEN_BIN} CACHE FILEPATH "bindgen command") message(NOTICE "-- BINDGEN_BIN is ${BINDGEN_BIN}") # ============================================================================= # set compile options if(DEFINED RUST_TARGET) list(APPEND RUST_CFLAGS "--target=${RUST_TARGET}") endif() if(DEFINED RUST_CPU) list(APPEND RUST_CFLAGS "-C" "target-cpu=${RUST_CPU}") endif() if(CMAKE_BUILD_TYPE STREQUAL Debug) list(APPEND RUST_CFLAGS "-C" "opt-level=0" "-g") elseif(CMAKE_BUILD_TYPE STREQUAL Release) list(APPEND RUST_CFLAGS "-C" "opt-level=3") elseif(CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo) list(APPEND RUST_CFLAGS "-C" "opt-level=2" "-g") elseif(CMAKE_BUILD_TYPE STREQUAL MinSizeRel) list(APPEND RUST_CFLAGS "-C" "opt-level=z") endif() list(APPEND BINDGEN_CFLAGS "--explicit-padding" "--formatter=none") list(APPEND CBINDGEN_CFLAGS "--cpp-compat" "-l" "C") if(CMAKE_BUILD_TYPE STREQUAL Debug) list(APPEND CBINDGEN_CFLAGS "--profile" "Debug") elseif(CMAKE_BUILD_TYPE STREQUAL Release OR CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo OR CMAKE_BUILD_TYPE STREQUAL MinSizeRel) list(APPEND CBINDGEN_CFLAGS "--profile" "Release") endif() # ============================================================================= # now define the macro we'll be using function(add_rust_object TOP_PROJECT RUST_TOP_SOURCE_FILE) if(ARGC GREATER 3) set(TARGET_NAME ${ARGV3}) else() cmake_path(GET RUST_TOP_SOURCE_FILE STEM TARGET_NAME) endif() # directory the header file will be in file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/) ### C-to-rust bindings (generate .rs from C headers, bindgen) set(HEADER_PATH "") set(MAIN_RUST_DEP "") if(ARGC GREATER 2) set(HEADER_PATH ${ARGV2}) cmake_path(GET HEADER_PATH STEM HEADER_NAME) if(NOT(HEADER_PATH STREQUAL "")) set(_BINDGEN_CFLAGS $ $>,PREPEND,-D> $,PREPEND,-isystem> # $>,PREPEND,-isystem> ) # preprocess C headers with top_project C compiler -- might use different system dirs & compatibility stuff & whatnot add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen_pre.h # yeah, need two commands........ COMMAND ${CMAKE_C_COMPILER} ${_BINDGEN_CFLAGS} -E -MM -MF ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen_pre.h.d ${HEADER_PATH} COMMAND ${CMAKE_C_COMPILER} ${_BINDGEN_CFLAGS} -E -o ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen_pre.h ${HEADER_PATH} DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen_pre.h.d MAIN_DEPENDENCY ${HEADER_PATH} VERBATIM COMMAND_EXPAND_LISTS ) add_custom_target(c2rs_${TARGET_NAME}_src_tgt_hdr_pre DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen_pre.h) # generate rs from c headers if(DEFINED RUST_TARGET) # necessary for bindgen itself, but not for preprocessor stuff above set(_BINDGEN_CFLAGS -target ${RUST_TARGET}) #${_BINDGEN_CFLAGS} endif() set(_BINDGEN_COMMAND_START ${BINDGEN_BIN} ${BINDGEN_CFLAGS} --depfile ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs.d ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen_pre.h ) # need to prepend the generated rust source with some directives to not make the compiler blow up set(_PYTHON_SCRIPT "import sys\;f=open\(sys.argv[-1]+'.tmp','r'\).read\(\)\;open\(sys.argv[-1],'w'\).write\('#\![allow(non_camel_case_types,non_snake_case,nonstandard_style)]'+chr(10)+f\)") if("--use-core" IN_LIST BINDGEN_CFLAGS) set(_PYTHON_SCRIPT "import sys\;f=open\(sys.argv[-1]+'.tmp','r'\).read\(\)\;open\(sys.argv[-1],'w'\).write\('#\![no_std]'+chr(10)+'#\![allow(non_camel_case_types,non_snake_case,nonstandard_style)]'+chr(10)+f\)") endif() add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs COMMAND ${_BINDGEN_COMMAND_START} -o ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs.tmp -- ${_BINDGEN_CFLAGS} COMMAND python -c ${_PYTHON_SCRIPT} ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs.tmp DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs.d MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen_pre.h VERBATIM # COMMAND_EXPAND_LISTS ) add_custom_target(c2rs_${TARGET_NAME}_src_tgt DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs) # build crate from it so we can actually import it add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/lib${HEADER_NAME}_bindgen.rlib COMMAND ${RUSTC_BIN} ${RUST_CFLAGS} --crate-type=rlib --emit=link,dep-info=${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/lib${HEADER_NAME}_bindgen.rlib.d -o ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/lib${HEADER_NAME}_bindgen.rlib ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/lib${HEADER_NAME}_bindgen.rlib.d MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${HEADER_NAME}_bindgen.rs VERBATIM ) add_custom_target(c2rs_${TARGET_NAME}_tgt DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/lib${HEADER_NAME}_bindgen.rlib) add_library(c2rs_${TARGET_NAME} OBJECT $) list(APPEND RUST_CFLAGS "--extern" "${HEADER_NAME}_bindgen=${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/lib${HEADER_NAME}_bindgen.rlib") set(MAIN_RUST_DEP "c2rs_${TARGET_NAME}_tgt") endif() endif() ### rust compilation set(_SHELL_SCRIPT "${CMAKE_AR} vd \"${CMAKE_CURRENT_BINARY_DIR}/lib${TARGET_NAME}.a.tmp\" \"$(${CMAKE_AR} t ${CMAKE_CURRENT_BINARY_DIR}/lib${TARGET_NAME}.a.tmp | grep \"lib${TARGET_NAME}\.${TARGET_NAME}\..*\.o\")\"") # the compile command add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.obj ${CMAKE_CURRENT_BINARY_DIR}/lib${TARGET_NAME}.a COMMAND ${RUSTC_BIN} ${RUST_CFLAGS} --emit=link,dep-info=${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.obj.d,obj=${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.obj --crate-type=staticlib -o ${CMAKE_CURRENT_BINARY_DIR}/lib${TARGET_NAME}.a.tmp ${RUST_TOP_SOURCE_FILE} # remove object file itself from the static library, so two-way references are possible. # now the static library only contains dependency code from core etc COMMAND bash -c ${_SHELL_SCRIPT} COMMAND mv ${CMAKE_CURRENT_BINARY_DIR}/lib${TARGET_NAME}.a.tmp ${CMAKE_CURRENT_BINARY_DIR}/lib${TARGET_NAME}.a BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/lib${TARGET_NAME}.a.tmp DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.obj.d # ensures the bindgen happens before the rustc compilation #DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/lib${TARGET_NAME}_bindgen.rlib DEPENDS ${MAIN_RUST_DEP} MAIN_DEPENDENCY ${RUST_TOP_SOURCE_FILE} VERBATIM ) # add a virtual library that can be reused and referred to (not possible with a bare add_custom_command()) add_library(${TARGET_NAME} OBJECT ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.obj) add_library(rdep_${TARGET_NAME} OBJECT ${CMAKE_CURRENT_BINARY_DIR}/lib${TARGET_NAME}.a) # add generated object file from the library to the code to compile target_sources(${TOP_PROJECT} PUBLIC $) # also make top project depend on rdep, so stuff from eg the core crate will also get linked in target_link_libraries(${TOP_PROJECT} ${CMAKE_CURRENT_BINARY_DIR}/lib${TARGET_NAME}.a) ### rust-to-C bindings (generate C headers from .rs, cbindgen) # cbindgen call add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${TARGET_NAME}.h COMMAND ${CBINDGEN_BIN} ${CBINDGEN_CFLAGS} -d --depfile ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${TARGET_NAME}.h.d -o ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${TARGET_NAME}.h ${RUST_TOP_SOURCE_FILE} DEPFILE ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${TARGET_NAME}.h.d MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/lib${TARGET_NAME}.a #MAIN_DEPENDENCY ${RUST_TOP_SOURCE_FILE} VERBATIM ) # make a virtual library for the header files add_library(rs2c_${TARGET_NAME} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/${TARGET_NAME}.h) # ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.obj) # add dependency to ensure build order and stuff target_link_libraries(${TOP_PROJECT} rs2c_${TARGET_NAME}) # and add include directories target_include_directories(${TOP_PROJECT} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/generated/rust_${TARGET_NAME}/) endfunction()