diff --git a/cmake/Corrosion.cmake b/cmake/Corrosion.cmake index 876ae5c1..ea176db6 100644 --- a/cmake/Corrosion.cmake +++ b/cmake/Corrosion.cmake @@ -1645,11 +1645,21 @@ between multiple invocations of this function. [cbindgen]: https://github.com/eqrion/cbindgen +### Current limitations + +- Cbindgens (optional) macro expansion feature internally actually builds the crate / runs the build script. + For this to work as expected in all cases, we probably need to set all the same environment variables + as when corrosion builds the crate. However the crate is a **library**, so we would need to figure out which + target builds it - and if there are multiple, potentially generate bindings per-target? + Alternatively we could add support of setting some environment variables on rlibs, and pulling that + information in when building the actual corrosion targets + Alternatively we could restrict corrosions support of this feature to actual imported staticlib/cdylib targets. ANCHOR_END: corrosion_cbindgen #]=======================================================================] function(corrosion_experimental_cbindgen) # Todo: # - set the target-triple via the TARGET env variable based on the target triple for the rust crate. + # Todo: require new version of cbindgen for --depfile set(OPTIONS "") set(ONE_VALUE_KEYWORDS TARGET MANIFEST_DIRECTORY HEADER_NAME CBINDGEN_VERSION) set(MULTI_VALUE_KEYWORDS "FLAGS") @@ -1706,6 +1716,8 @@ function(corrosion_experimental_cbindgen) ${_CORROSION_CARGO} install cbindgen --root "${local_cbindgen_install_dir}" + --git "https://github.com/jschwe/cbindgen" + --branch "depfile" ${_CORROSION_QUIET_OUTPUT_FLAG} COMMENT "Building cbindgen" ) @@ -1718,6 +1730,8 @@ function(corrosion_experimental_cbindgen) set(corrosion_generated_dir "${CMAKE_CURRENT_BINARY_DIR}/corrosion_generated") set(generated_dir "${corrosion_generated_dir}/cbindgen/${rust_target}") set(header_placement_dir "${generated_dir}/include/") + set(depfile_placement_dir "${generated_dir}/depfile") + set(generated_depfile "${depfile_placement_dir}/${output_header_name}.d") set(generated_header "${header_placement_dir}/${output_header_name}") message(STATUS "rust target is ${rust_target}") if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.23") @@ -1739,8 +1753,56 @@ function(corrosion_experimental_cbindgen) # This may be different from $header_placement_dir since the user specified HEADER_NAME may contain # relative directories. get_filename_component(generated_header_dir "${generated_header}" DIRECTORY) - file(MAKE_DIRECTORY ${generated_header_dir}) + file(MAKE_DIRECTORY "${generated_header_dir}") + if(CMAKE_GENERATOR STREQUAL "Ninja" OR CMAKE_GENERATOR STREQUAL "Ninja Multi-Config" ) + set(depfile_supported TRUE) + elseif(CMAKE_GENERATOR MATCHES "Makefiles" OR CMAKE_GENERATOR STREQUAL "Watcom WMake") + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.20") + set(depfile_supported TRUE) + else() + set(depfile_supported FALSE) + # Todo: add a cache variable for a "message once switch" + message(STATUS + "cbindgen will be rerun on every build because Makefile Generators " + "do not support depfiles until CMake 3.20. If you wish to avoid needlessly " + "regenerating, consider switching to the Ninja Generator or upgrading CMake " + "to at least CMake 3.20" + ) + endif() + elseif(CMAKE_GENERATOR MATCHES "Visual Studio" OR CMAKE_GENERATOR STREQUAL Xcode) + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.21") + set(depfile_supported TRUE) + else() + set(depfile_supported FALSE) + message(STATUS + "cbindgen will be rerun on every build because the Visual Studio and Xcode Generators " + "do not support depfiles until CMake 3.21. If you wish to avoid needlessly " + "regenerating, consider switching to the Ninja Generator or upgrading CMake " + "to at least CMake 3.21" + ) + endif() + else() + set(depfile_supported FALSE) + message(STATUS + "cbindgen will be rerun on every build because your selected Generator ${CMAKE_GENERATOR} " + "does not support depfiles. If you wish to avoid needlessly " + "regenerating, consider switching to the Ninja Generator or a different supported " + "Generator" + ) + endif() + + unset(depfile_cbindgen_arg) + unset(depfile_cmake_arg) + unset(always_regenerate) + if(depfile_supported) + get_filename_component(generated_depfile_dir "${generated_depfile}" DIRECTORY) + file(MAKE_DIRECTORY "${generated_depfile_dir}") + set(depfile_cbindgen_arg "--depfile=${generated_depfile}") + set(depfile_cmake_arg DEPFILE "${generated_depfile}") + else() + set(always_regenerate "${CMAKE_CURRENT_BINARY_DIR}/corrosion/non-existing-file.h") + endif() # Todo: Add dependencies on the source Rust files here to get proper dependency rules (hard). # For now we just specify a dummy file we won't generate as an additional output, so that the rule # always runs. @@ -1748,20 +1810,22 @@ function(corrosion_experimental_cbindgen) # c) ask the user to specify the dependencies (in order to relax the rules). add_custom_command( OUTPUT - "${generated_header}" "${CMAKE_CURRENT_BINARY_DIR}/corrosion/non-existing-file.h" + "${generated_header}" ${always_regenerate} COMMAND "${cbindgen}" --output "${generated_header}" --crate "${rust_target}" + ${depfile_cbindgen_arg} ${CCN_FLAGS} COMMENT "Generate cbindgen bindings for crate ${rust_target}" + ${depfile_cmake_arg} COMMAND_EXPAND_LISTS WORKING_DIRECTORY "${package_manifest_dir}" ) if(NOT installed_cbindgen) add_custom_command( - OUTPUT "${generated_header}" "${CMAKE_CURRENT_BINARY_DIR}/corrosion/non-existing-file.h" + OUTPUT "${generated_header}" ${always_regenerate} APPEND DEPENDS _corrosion_cbindgen ) diff --git a/test/cbindgen/CMakeLists.txt b/test/cbindgen/CMakeLists.txt index 86cabf5b..79566966 100644 --- a/test/cbindgen/CMakeLists.txt +++ b/test/cbindgen/CMakeLists.txt @@ -7,3 +7,5 @@ set_tests_properties(cbindgen_rust2cpp_run_cpp-exe PROPERTIES PASS_REGULAR_EXPRE # - A rust lib that is used by a rust executable # - cbindgen creates bindings for the rust-lib # - c++ code uses the rust lib and is used in turn by the rust bin. + +# todo: add a test for the DEPFILE and correct regenerating if the sources are touched. diff --git a/test/cbindgen/rust2cpp/main.cpp b/test/cbindgen/rust2cpp/main.cpp index 1085c4af..46a4489d 100644 --- a/test/cbindgen/rust2cpp/main.cpp +++ b/test/cbindgen/rust2cpp/main.cpp @@ -16,4 +16,8 @@ int main(int argc, char **argv) { add_point(&p1, NULL); assert(p1.x == 100); assert(p1.y == 100); + + assert(OTHER_MOD_MAGIC_NUMBER == 192312312); + assert(FFI_MAGIC_NUMBER == 0xFDA00184); + assert(OTHER_CRATE_MAGIC_NUMBER == 0xBACD3331); } diff --git a/test/cbindgen/rust2cpp/rust/Cargo.lock b/test/cbindgen/rust2cpp/rust/Cargo.lock index 5dc1732e..c7d8782b 100644 --- a/test/cbindgen/rust2cpp/rust/Cargo.lock +++ b/test/cbindgen/rust2cpp/rust/Cargo.lock @@ -1,5 +1,14 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + +[[package]] +name = "other-crate" +version = "0.1.0" + [[package]] name = "rust-lib" version = "0.1.0" +dependencies = [ + "other-crate", +] diff --git a/test/cbindgen/rust2cpp/rust/Cargo.toml b/test/cbindgen/rust2cpp/rust/Cargo.toml index e8816af8..99d90787 100644 --- a/test/cbindgen/rust2cpp/rust/Cargo.toml +++ b/test/cbindgen/rust2cpp/rust/Cargo.toml @@ -5,6 +5,7 @@ license = "MIT" edition = "2018" [dependencies] +other-crate = { path = "./path with spaces/other_crate"} [lib] crate-type=["staticlib"] diff --git a/test/cbindgen/rust2cpp/rust/cbindgen.toml b/test/cbindgen/rust2cpp/rust/cbindgen.toml index b358b7df..544b3096 100644 --- a/test/cbindgen/rust2cpp/rust/cbindgen.toml +++ b/test/cbindgen/rust2cpp/rust/cbindgen.toml @@ -1,2 +1,5 @@ language = "C++" include_version = true + +[parse] +parse_deps = true diff --git a/test/cbindgen/rust2cpp/rust/path with spaces/other_crate/Cargo.toml b/test/cbindgen/rust2cpp/rust/path with spaces/other_crate/Cargo.toml new file mode 100644 index 00000000..f62f5a15 --- /dev/null +++ b/test/cbindgen/rust2cpp/rust/path with spaces/other_crate/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "other-crate" +version = "0.1.0" +license = "MIT" +edition = "2018" diff --git a/test/cbindgen/rust2cpp/rust/path with spaces/other_crate/src/lib.rs b/test/cbindgen/rust2cpp/rust/path with spaces/other_crate/src/lib.rs new file mode 100644 index 00000000..205eb84a --- /dev/null +++ b/test/cbindgen/rust2cpp/rust/path with spaces/other_crate/src/lib.rs @@ -0,0 +1 @@ +pub const OTHER_CRATE_MAGIC_NUMBER: u32 = 0xBACD_3331; diff --git a/test/cbindgen/rust2cpp/rust/src/ffi.rs b/test/cbindgen/rust2cpp/rust/src/ffi.rs new file mode 100644 index 00000000..01ad181c --- /dev/null +++ b/test/cbindgen/rust2cpp/rust/src/ffi.rs @@ -0,0 +1,3 @@ +//! Just a module that contains some entries that should be parsed by cbindgen. + +pub const FFI_MAGIC_NUMBER: u64 = 0xFDA0_0184; diff --git a/test/cbindgen/rust2cpp/rust/src/lib.rs b/test/cbindgen/rust2cpp/rust/src/lib.rs index 435c05f7..0ad5c8f8 100644 --- a/test/cbindgen/rust2cpp/rust/src/lib.rs +++ b/test/cbindgen/rust2cpp/rust/src/lib.rs @@ -1,5 +1,8 @@ pub const MAGIC_NUMBER: u64 = 0xABCD_EFAB; +pub mod ffi; +pub mod other_mod; + #[derive(Debug)] #[repr(C)] pub struct Point { diff --git a/test/cbindgen/rust2cpp/rust/src/other_mod/mod.rs b/test/cbindgen/rust2cpp/rust/src/other_mod/mod.rs new file mode 100644 index 00000000..ca34458b --- /dev/null +++ b/test/cbindgen/rust2cpp/rust/src/other_mod/mod.rs @@ -0,0 +1 @@ +pub const OTHER_MOD_MAGIC_NUMBER: u32 = 192312312;