diff --git a/.github/workflows/gh-pages.yaml b/.github/workflows/gh-pages.yaml index 80ebbce9..fe860563 100644 --- a/.github/workflows/gh-pages.yaml +++ b/.github/workflows/gh-pages.yaml @@ -42,7 +42,7 @@ jobs: path: stable-v0.5 ref: 'stable/v0.5' - name: Setup Pages - uses: actions/configure-pages@v3 + uses: actions/configure-pages@v5 - name: Build mdbook for main branch working-directory: 'main/doc' run: mdbook build @@ -70,9 +70,9 @@ jobs: - name: Debug print run: ls -la main/doc/book/v0.5 - name: Upload artifact - uses: actions/upload-pages-artifact@v2 + uses: actions/upload-pages-artifact@v3 with: path: 'main/doc/book' - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v2 + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/linux.yaml b/.github/workflows/linux.yaml index 8c1b416e..b5cbfc9e 100644 --- a/.github/workflows/linux.yaml +++ b/.github/workflows/linux.yaml @@ -43,6 +43,7 @@ jobs: with: toolchain: ${{inputs.rust}} targets: ${{inputs.target_arch}}-unknown-linux-gnu + components: rust-src - name: Install Cross Compiler shell: bash run: | diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 3c6e0a71..fd60d466 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -61,6 +61,7 @@ jobs: compiler: - cl - clang-cl + - clang include: - os: windows-2022 vs_version: vs-2022 @@ -79,6 +80,10 @@ jobs: arch: i686 - compiler: clang-cl arch: aarch64 + - compiler: clang + arch: i686 + - compiler: clang + arch: aarch64 steps: - uses: actions/checkout@v4 @@ -93,6 +98,7 @@ jobs: with: toolchain: ${{matrix.rust}} targets: ${{matrix.arch}}-pc-windows-msvc + components: rust-src - name: Setup MSVC Development Environment uses: ilammy/msvc-dev-cmd@v1 with: @@ -137,6 +143,7 @@ jobs: with: toolchain: ${{matrix.rust}} targets: ${{matrix.arch}}-pc-windows-gnu + components: rust-src - name: Configure run: cmake -S. -Bbuild "-DRust_TOOLCHAIN=${{steps.install_rust.outputs.name}}" --preset "${{ matrix.generator }}-${{ matrix.arch }}-pc-windows-gnu-${{ matrix.compiler }}" - name: Run Tests @@ -176,6 +183,7 @@ jobs: with: toolchain: stable targets: ${{matrix.arch}}-pc-windows-gnullvm + components: rust-src - uses: msys2/setup-msys2@v2 with: msystem: ${{matrix.msystem}} @@ -211,6 +219,7 @@ jobs: with: toolchain: stable targets: aarch64-unknown-linux-gnu + components: rust-src - name: Configure run: cmake -S. -Bbuild "-DRust_TOOLCHAIN=${{steps.install_rust.outputs.name}}" -DRust_CARGO_TARGET=aarch64-unknown-linux-gnu - name: Run Tests @@ -295,6 +304,7 @@ jobs: with: toolchain: ${{matrix.rust}} targets: ${{matrix.arch}}-apple-darwin + components: rust-src - name: Configure run: cmake -S. -Bbuild --log-level=DEBUG -G "${{ matrix.generator }}" "-DRust_TOOLCHAIN=${{steps.install_rust.outputs.name}}" --preset "${{ matrix.arch }}-apple-darwin-${{ matrix.compiler }}" - name: Run Tests @@ -335,12 +345,13 @@ jobs: - name: Install CMake uses: lukka/get-cmake@519de0c7b4812477d74976b2523a9417f552d126 with: - cmakeVersion: "~3.22.0" + cmakeVersion: "~3.24.0" ninjaVersion: "~1.10.0" - name: Install Rust uses: dtolnay/rust-toolchain@master with: toolchain: stable minus 2 releases + components: rust-src - name: Configure run: > cmake @@ -353,6 +364,30 @@ jobs: working-directory: build run: ctest --output-on-failure --build-config Debug -j 3 -R "^cxxbridge" + autoinstall_cargo_target: + name: Test Auto-installing Cargo target via rustup + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Install CMake + uses: lukka/get-cmake@519de0c7b4812477d74976b2523a9417f552d126 + - name: Install Rust + id: install_rust + uses: dtolnay/rust-toolchain@stable + - name: Install Cross Compiler + shell: bash + run: | + echo "::group::apt-install" + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu + echo "::endgroup::" + - name: Assert rustup target is not installed + run: rustup show | ( ! grep aarch64) + - name: Configure Corrosion + run: cmake -S. -Bbuild -GNinja -DRust_RUSTUP_INSTALL_MISSING_TARGET=ON --preset "aarch64-unknown-linux-gnu-gcc" + - name: Check rustup target is installed after configuring + run: rustup show | grep aarch64 + install: name: Test Corrosion as a Library runs-on: ${{ matrix.os }} @@ -383,6 +418,7 @@ jobs: uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} + components: rust-src - name: Test Corrosion as installed module run: > cmake @@ -410,6 +446,7 @@ jobs: - linux_stage2 - darwin - test_cxxbridge + - autoinstall_cargo_target - install runs-on: ubuntu-latest # Step copied from: https://github.com/cross-rs/cross/blob/80c9f9109a719ffb0f694060ddc6e371d5b3a540/.github/workflows/ci.yml#L361 diff --git a/.github/workflows/visual_studio.yaml b/.github/workflows/visual_studio.yaml index 9c9d6c78..4b7beee5 100644 --- a/.github/workflows/visual_studio.yaml +++ b/.github/workflows/visual_studio.yaml @@ -37,15 +37,16 @@ jobs: with: toolchain: ${{inputs.rust}} targets: ${{inputs.target_arch}}-pc-windows-msvc + components: rust-src # The initial configure for MSVC is quite slow, so we cache the build directory # (including the build directories of the tests) since reconfiguring is # significantly faster. - - name: Cache MSVC build directory - id: cache-msvc-builddir - uses: actions/cache@v4 - with: - path: build - key: ${{ inputs.os }}-${{ inputs.target_arch }}-${{ inputs.rust }}-msvc-${{ inputs.vs_version}}-build + # - name: Cache MSVC build directory + # id: cache-msvc-builddir + # uses: actions/cache@v4 + # with: + # path: build + # key: ${{ inputs.os }}-${{ inputs.target_arch }}-${{ inputs.rust }}-msvc-${{ inputs.vs_version}}-build - name: Configure run: cmake -S. -Bbuild -DCORROSION_TESTS_KEEP_BUILDDIRS=ON "-DRust_TOOLCHAIN=${{steps.install_rust.outputs.name}}" --preset "vs-${{ inputs.vs_version }}-${{ inputs.target_arch }}" - name: Run Tests diff --git a/CMakePresets.json b/CMakePresets.json index 4a3bae55..1be20298 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -200,6 +200,10 @@ "name": "ninja-x86_64-pc-windows-msvc-clang-cl", "inherits": ["ninja", "x86_64-pc-windows-msvc", "clang-cl"] }, + { + "name": "ninja-x86_64-pc-windows-msvc-clang", + "inherits": ["ninja", "x86_64-pc-windows-msvc", "clang"] + }, { "name": "ninja-i686-pc-windows-msvc-cl", "inherits": ["ninja", "i686-pc-windows-msvc", "cl", "windows-10-cross"] @@ -208,6 +212,10 @@ "name": "ninja-i686-pc-windows-msvc-clang-cl", "inherits": ["ninja", "i686-pc-windows-msvc", "clang-cl", "windows-10-cross"] }, + { + "name": "ninja-i686-pc-windows-msvc-clang", + "inherits": ["ninja", "i686-pc-windows-msvc", "clang", "windows-10-cross"] + }, { "name": "ninja-aarch64-pc-windows-msvc-cl", "inherits": ["ninja", "aarch64-pc-windows-msvc", "cl", "windows-10-cross"] @@ -216,6 +224,10 @@ "name": "ninja-aarch64-pc-windows-msvc-clang-cl", "inherits": ["ninja", "aarch64-pc-windows-msvc", "clang-cl", "windows-10-cross"] }, + { + "name": "ninja-aarch64-pc-windows-msvc-clang", + "inherits": ["ninja", "aarch64-pc-windows-msvc", "clang", "windows-10-cross"] + }, { "name": "ninja-x86_64-pc-windows-gnullvm", "inherits": ["ninja", "windows-only", "clang"], diff --git a/RELEASES.md b/RELEASES.md index a1588a7c..8a6239fa 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -6,6 +6,15 @@ [v0.4.0 Release notes](#040-lts-2023-06-01) for more details. - Removed native tooling and the corresponding option `CORROSION_NATIVE_TOOLING`. Corrosion now always uses pure CMake. +- Fix Corrosion placing artifacts into the wrong directory when: + 1. using a Multi-Config Generator (e.g Visual Studio or XCode) AND + 2. `OUTPUT_DIRECTORY_` is not set AND + 3. `OUTPUT_DIRECTORY` is set AND + 4. `OUTPUT_DIRECTORY` does not contain a generator expression + + Corrosion now places artifacts into a `$` subdirectory of the + specified `OUTPUT_DIRECTORY`. This matches the [documented behavior][doc-cmake-rt-output-dir] + of CMake for regular CMake targets. ([#568]). ### New features @@ -14,8 +23,21 @@ the crate-types of Rust libraries (e.g. force building as a staticlib instead of an rlib). - Support *-windows-gnullvm targets. - experimental support in corrosion_install for installing libraries and header files +- Add `CORROSION_TOOLS_RUST_TOOLCHAIN` cache variable which allows users to select a different + rust toolchain for compiling build-tools used by corrosion (currently cbindgen and cxxbridge). + This mainly allows using a newer toolchain for such build-tools then for the actual project. +[doc-cmake-rt-output-dir]: https://cmake.org/cmake/help/latest/prop_tgt/RUNTIME_OUTPUT_DIRECTORY.html [#459]: https://github.com/corrosion-rs/corrosion/pull/459 +[#568]: https://github.com/corrosion-rs/corrosion/pull/568 + +# v0.5.1 (2024-12-29) + +### Fixes + +- Update FindRust to support `rustup` v1.28.0. Support for older rustup versions is retained, + so updating corrosion quickly is recommended to all rustup users. + # v0.5.0 (2024-05-11) @@ -62,7 +84,7 @@ # v0.4.9 (2024-05-01) -### New Features +### New Features - Automatically detect Rust target for OpenHarmony ([#510]). diff --git a/cmake/Corrosion.cmake b/cmake/Corrosion.cmake index 279e41f9..40b89c3a 100644 --- a/cmake/Corrosion.cmake +++ b/cmake/Corrosion.cmake @@ -33,29 +33,6 @@ option( find_package(Rust REQUIRED) -if(Rust_TOOLCHAIN_IS_RUSTUP_MANAGED) - execute_process(COMMAND rustup target list --toolchain "${Rust_TOOLCHAIN}" - OUTPUT_VARIABLE AVAILABLE_TARGETS_RAW - ) - string(REPLACE "\n" ";" AVAILABLE_TARGETS_RAW "${AVAILABLE_TARGETS_RAW}") - string(REPLACE " (installed)" "" "AVAILABLE_TARGETS" "${AVAILABLE_TARGETS_RAW}") - set(INSTALLED_TARGETS_RAW "${AVAILABLE_TARGETS_RAW}") - list(FILTER INSTALLED_TARGETS_RAW INCLUDE REGEX " \\(installed\\)") - string(REPLACE " (installed)" "" "INSTALLED_TARGETS" "${INSTALLED_TARGETS_RAW}") - list(TRANSFORM INSTALLED_TARGETS STRIP) - if("${Rust_CARGO_TARGET}" IN_LIST AVAILABLE_TARGETS) - message(DEBUG "Cargo target ${Rust_CARGO_TARGET} is an official target-triple") - message(DEBUG "Installed targets: ${INSTALLED_TARGETS}") - if(NOT ("${Rust_CARGO_TARGET}" IN_LIST INSTALLED_TARGETS)) - message(FATAL_ERROR "Target ${Rust_CARGO_TARGET} is not installed for toolchain ${Rust_TOOLCHAIN}.\n" - "Help: Run `rustup target add --toolchain ${Rust_TOOLCHAIN} ${Rust_CARGO_TARGET}` to install " - "the missing target." - ) - endif() - endif() - -endif() - if(CMAKE_GENERATOR MATCHES "Visual Studio" AND (NOT CMAKE_VS_PLATFORM_NAME STREQUAL CMAKE_VS_PLATFORM_NAME_DEFAULT) AND Rust_VERSION VERSION_LESS "1.54") @@ -76,6 +53,54 @@ get_property( TARGET Rust::Cargo PROPERTY IMPORTED_LOCATION ) +if(Rust_TOOLCHAIN_IS_RUSTUP_MANAGED AND DEFINED Rust_RUSTUP_TOOLCHAINS) + set(corrosion_tools_rust_toolchain_docstring "Rust toolchain to use for building helper tools such as cbindgen or cxx-bridge") + if(DEFINED CORROSION_TOOLS_RUST_TOOLCHAIN) + set(cor_default_tools_toolchain "${CORROSION_TOOLS_RUST_TOOLCHAIN}") + else() + set(cor_default_tools_toolchain "${Rust_TOOLCHAIN}") + endif() + set(CORROSION_TOOLS_RUST_TOOLCHAIN "${cor_default_tools_toolchain}" CACHE STRING + "${corrosion_tools_rust_toolchain_docstring}" FORCE) + set_property(CACHE CORROSION_TOOLS_RUST_TOOLCHAIN PROPERTY STRINGS "${Rust_RUSTUP_TOOLCHAINS}") + if(NOT "$CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN}" IN_LIST Rust_RUSTUP_TOOLCHAINS) + if("$CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN}-${Rust_CARGO_HOST_TARGET}" IN_LIST Rust_RUSTUP_TOOLCHAINS) + set(CORROSION_TOOLS_RUST_TOOLCHAIN "$CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN}-${Rust_CARGO_HOST_TARGET}" + CACHE PATH "${corrosion_tools_rust_toolchain_docstring}" FORCE) + else() + message(FATAL_ERROR "CORROSION_TOOLS_RUST_TOOLCHAIN must be set to a valid rustup managed toolchain path." + "Rust_RUSTUP_TOOLCHAINS contains a list of valid installed toolchains." + ) + endif() + endif() + foreach(toolchain tc_rustc tc_cargo IN ZIP_LISTS Rust_RUSTUP_TOOLCHAINS Rust_RUSTUP_TOOLCHAINS_RUSTC_PATH Rust_RUSTUP_TOOLCHAINS_CARGO_PATH) + if("${toolchain}" STREQUAL $CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN}) + # Minimum CMake version 3.29 for `IS_EXECUTABLE`. + if(NOT (tc_cargo AND tc_rustc )) + message(FATAL_ERROR "Failed to find executable rustc or cargo for toolchain `$CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN}`") + endif() + set(CORROSION_TOOLS_RUSTC "${tc_rustc}" CACHE INTERNAL "" FORCE) + set(CORROSION_TOOLS_CARGO "${tc_cargo}" CACHE INTERNAL "" FORCE) + break() + endif() + endforeach() + if(NOT DEFINED CACHE{CORROSION_TOOLS_CARGO}) + message(FATAL_ERROR "Internal error: Failed to find toolchain $CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN} in " + "list of rustup managed toolchains: ${Rust_RUSTUP_TOOLCHAINS}" + ) + endif() +else() + # Fallback to the default project toolchain if rust is not rustup managed. + if(DEFINED CORROSION_TOOLS_RUST_TOOLCHAIN) + message(DEBUG "Ignoring `CORROSION_TOOLS_RUST_TOOLCHAIN=${CORROSION_TOOLS_RUST_TOOLCHAIN}` " + "since the toolchains are not rustup managed. Falling back to the default rust toolchain" + " for this project." + ) + endif() + set(CORROSION_TOOLS_RUSTC "${RUSTC_EXECUTABLE}" CACHE INTERNAL "" FORCE) + set(CORROSION_TOOLS_CARGO "${CARGO_EXECUTABLE}" CACHE INTERNAL "" FORCE) +endif() + function(_corrosion_bin_target_suffix target_name out_var_suffix) get_target_property(hostbuild "${target_name}" ${_CORR_PROP_HOST_BUILD}) if((hostbuild AND CMAKE_HOST_WIN32) @@ -123,7 +148,14 @@ function(_corrosion_set_imported_location_deferred target_name base_property out if(output_dir_curr_config) set(curr_out_dir "${output_dir_curr_config}") elseif(output_directory) - set(curr_out_dir "${output_directory}") + string(GENEX_STRIP "${output_directory}" output_dir_no_genex) + # Only add config dir if there is no genex in here. See + # https://cmake.org/cmake/help/latest/prop_tgt/RUNTIME_OUTPUT_DIRECTORY.html + if(output_directory STREQUAL output_dir_no_genex) + set(curr_out_dir "${output_directory}/${config_type}") + else() + set(curr_out_dir "${output_directory}") + endif() else() set(curr_out_dir "${CMAKE_CURRENT_BINARY_DIR}") endif() @@ -230,7 +262,7 @@ function(_corrosion_copy_byproduct_deferred target_name output_dir_prop_names ca # Fallback to `output_dir` if specified # Note: Multi-configuration generators append a per-configuration subdirectory to the # specified directory unless a generator expression is used (from CMake documentation). - set(curr_out_dir "${output_dir}") + set(curr_out_dir "${output_dir}/${config_type}") else() # Fallback to the default directory. We do not append the configuration directory here # and instead let CMake do this, since otherwise the resolving of dynamic library @@ -605,11 +637,11 @@ function(_add_cargo_build out_cargo_build_out_dir) get_filename_component(workspace_toml_dir ${path_to_toml} DIRECTORY ) if (CMAKE_VS_PLATFORM_NAME) - set (build_dir "${CMAKE_VS_PLATFORM_NAME}/$") + set(build_dir "${CMAKE_VS_PLATFORM_NAME}/$") elseif(COR_IS_MULTI_CONFIG) - set (build_dir "$") + set(build_dir "$") else() - set (build_dir .) + unset(build_dir) endif() # If a CMake sysroot is specified, forward it to the linker rustc invokes, too. CMAKE_SYSROOT is documented @@ -624,6 +656,11 @@ function(_add_cargo_build out_cargo_build_out_dir) if(COR_NO_DEFAULT_FEATURES) set(no_default_features_arg --no-default-features) endif() + if(COR_NO_USES_TERMINAL) + unset(cor_uses_terminal) + else() + set(cor_uses_terminal USES_TERMINAL) + endif() set(global_rustflags_target_property "$>") set(local_rustflags_target_property "$>") @@ -649,7 +686,8 @@ function(_add_cargo_build out_cargo_build_out_dir) set(cargo_target_option "--target=$") # The target may be a filepath to custom target json file. For host targets we assume that they are built-in targets. - _corrosion_strip_target_triple(${_CORROSION_RUST_CARGO_TARGET} stripped_target_triple) + _corrosion_strip_target_triple("${_CORROSION_RUST_CARGO_TARGET}" stripped_target_triple) + _corrosion_strip_target_triple("${_CORROSION_RUST_CARGO_TARGET_UPPER}" stripped_target_triple_upper) set(target_artifact_dir "$") set(flags_genex "$>") @@ -697,7 +735,17 @@ function(_add_cargo_build out_cargo_build_out_dir) set(default_build_type_dir "$,$>,debug,release>") set(build_type_dir "$") - set(cargo_target_dir "${CMAKE_BINARY_DIR}/${build_dir}/cargo/build") + # We set a target folder based on the manifest path so if you build multiple workspaces (or standalone projects + # without workspace) they won't collide if they use a common dependency. This would confuse cargo and trigger + # unnecessary rebuilds + cmake_path(GET workspace_manifest_path PARENT_PATH parent_path) + cmake_path(GET parent_path PARENT_PATH grandparent_path) + string(REPLACE "${grandparent_path}/" "" cargo_folder_name "${parent_path}") + string(SHA1 cargo_path_hash ${workspace_manifest_path}) + # Include a hash of the full path in case there are multiple projects with the same folder name + string(SUBSTRING "${cargo_path_hash}" 0 5 cargo_path_hash) + cmake_path(APPEND CMAKE_BINARY_DIR ${build_dir} cargo "${cargo_folder_name}_${cargo_path_hash}" + OUTPUT_VARIABLE cargo_target_dir) set(cargo_build_dir "${cargo_target_dir}/${target_artifact_dir}/${build_type_dir}") set("${out_cargo_build_out_dir}" "${cargo_build_dir}" PARENT_SCOPE) @@ -707,15 +755,15 @@ function(_add_cargo_build out_cargo_build_out_dir) # This variable is read by cc-rs (often used in build scripts) to determine the c-compiler. # It can still be overridden if the user sets the non underscore variant via the environment variables # on the target. - list(APPEND corrosion_cc_rs_flags "CC_${_CORROSION_RUST_CARGO_TARGET_UNDERSCORE}=${CMAKE_C_COMPILER}") + list(APPEND corrosion_cc_rs_flags "CC_${stripped_target_triple}=${CMAKE_C_COMPILER}") endif() if(CMAKE_CXX_COMPILER) - list(APPEND corrosion_cc_rs_flags "CXX_${_CORROSION_RUST_CARGO_TARGET_UNDERSCORE}=${CMAKE_CXX_COMPILER}") + list(APPEND corrosion_cc_rs_flags "CXX_${stripped_target_triple}=${CMAKE_CXX_COMPILER}") endif() # cc-rs doesn't seem to support `llvm-ar` (commandline syntax), wo we might as well just use # the default AR. if(CMAKE_AR AND NOT (Rust_CARGO_TARGET_ENV STREQUAL "msvc")) - list(APPEND corrosion_cc_rs_flags "AR_${_CORROSION_RUST_CARGO_TARGET_UNDERSCORE}=${CMAKE_AR}") + list(APPEND corrosion_cc_rs_flags "AR_${stripped_target_triple}=${CMAKE_AR}") endif() # Since we instruct cc-rs to use the compiler found by CMake, it is likely one that requires also @@ -726,6 +774,11 @@ function(_add_cargo_build out_cargo_build_out_dir) list(APPEND corrosion_cc_rs_flags "SDKROOT=${CMAKE_OSX_SYSROOT}") endif() + # Ensure that cc-rs targets same Apple platform version as the CMake build + if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_DEPLOYMENT_TARGET) + list(APPEND corrosion_cc_rs_flags "MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") + endif() + corrosion_add_target_local_rustflags("${target_name}" "$<$:-Clink-args=${corrosion_link_args}>") # todo: this should probably also be guarded by if_not_host_build_condition. @@ -749,7 +802,7 @@ function(_add_cargo_build out_cargo_build_out_dir) set(default_linker "$,${CMAKE_CXX_COMPILER},${CMAKE_C_COMPILER}>") endif() # Used to set a linker for a specific target-triple. - set(cargo_target_linker_var "CARGO_TARGET_${_CORROSION_RUST_CARGO_TARGET_UPPER}_LINKER") + set(cargo_target_linker_var "CARGO_TARGET_${stripped_target_triple_upper}_LINKER") set(linker "$") set(cargo_target_linker $<$:${cargo_target_linker_var}=${linker}>) @@ -764,6 +817,7 @@ function(_add_cargo_build out_cargo_build_out_dir) endif() message(DEBUG "TARGET ${target_name} produces byproducts ${build_byproducts}") + message(DEBUG "corrosion_cc_rs_flags: ${corrosion_cc_rs_flags}") add_custom_target( _cargo-build_${target_name} @@ -803,7 +857,7 @@ function(_add_cargo_build out_cargo_build_out_dir) # The build is conducted in the directory of the Manifest, so that configuration files such as # `.cargo/config.toml` or `toolchain.toml` are applied as expected. WORKING_DIRECTORY "${workspace_toml_dir}" - USES_TERMINAL + ${cor_uses_terminal} COMMAND_EXPAND_LISTS VERBATIM ) @@ -831,9 +885,9 @@ function(_add_cargo_build out_cargo_build_out_dir) cargo-clean_${target_name} COMMAND "${cargo_bin}" clean ${cargo_target_option} - -p ${package_name} --manifest-path ${path_to_toml} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${build_dir} - USES_TERMINAL + -p ${package_name} --manifest-path "${path_to_toml}" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/${build_dir}" + ${cor_uses_terminal} ) if (NOT TARGET cargo-clean) @@ -851,6 +905,7 @@ corrosion_import_crate( [NO_DEFAULT_FEATURES] [NO_STD] [NO_LINKER_OVERRIDE] + [NO_USES_TERMINAL] [LOCKED] [FROZEN] [PROFILE ] @@ -867,6 +922,7 @@ corrosion_import_crate( * **NO_DEFAULT_FEATURES**: Equivalent to [--no-default-features] passed to cargo build * **NO_STD**: Disable linking of standard libraries (required for no_std crates). * **NO_LINKER_OVERRIDE**: Will let Rust/Cargo determine which linker to use instead of corrosion (when linking is invoked by Rust) +* **NO_USES_TERMINAL**: Don't pass the `USES_TERMINAL` flag when creating the custom CMake targets. * **LOCKED**: Pass [`--locked`] to cargo build and cargo metadata. * **FROZEN**: Pass [`--frozen`] to cargo build and cargo metadata. * **PROFILE**: Specify cargo build profile (`dev`/`release` or a [custom profile]; `bench` and `test` are not supported) @@ -891,7 +947,14 @@ corrosion_import_crate( ANCHOR_END: corrosion-import-crate #]=======================================================================] function(corrosion_import_crate) - set(OPTIONS ALL_FEATURES NO_DEFAULT_FEATURES NO_STD NO_LINKER_OVERRIDE LOCKED FROZEN) + set(OPTIONS + ALL_FEATURES + NO_DEFAULT_FEATURES + NO_STD + NO_LINKER_OVERRIDE + NO_USES_TERMINAL + LOCKED + FROZEN) set(ONE_VALUE_KEYWORDS MANIFEST_PATH PROFILE IMPORTED_CRATES) set(MULTI_VALUE_KEYWORDS CRATE_TYPES CRATES FEATURES FLAGS OVERRIDE_CRATE_TYPE) cmake_parse_arguments(COR "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}" ${ARGN}) @@ -1265,6 +1328,7 @@ function(corrosion_install) # Gather the arguments to this install type set(ARGS) + list(LENGTH ARGN ARGN_LENGTH) while(ARGN_LENGTH) # If the next keyword is an install target type, then break - arguments have been # gathered. @@ -1532,6 +1596,56 @@ set_target_properties(${INSTALL_TARGET}-shared endif() endfunction() +function(_corrosion_check_cxx_version_helper manifest_dir cxx_name out_required_version) + execute_process(COMMAND ${CMAKE_COMMAND} -E env + "CARGO_BUILD_RUSTC=${_CORROSION_RUSTC}" + ${_CORROSION_CARGO} tree -i "${cxx_name}" + # Usage of `cxx` could be gated behind a feature. Features can use Generator expressions, + # so we can't really know what features we will enable when building at this point. + # Features should be additive though, so simply enabling all-features should work for + # dependency resolution. + --all-features + --target all + --depth=0 + WORKING_DIRECTORY "${manifest_dir}" + RESULT_VARIABLE cxx_version_result + OUTPUT_VARIABLE cxx_version_output + ERROR_VARIABLE cxx_version_error + ) + if(NOT "${cxx_version_result}" EQUAL "0") + message(DEBUG "`cargo tree -i ${cxx_name}` returned an error: ${cxx_version_error}") + set("${out_required_version}" "${cxx_name}-NOTFOUND" PARENT_SCOPE) + return() + endif() + if(cxx_version_output MATCHES "${cxx_name} v([0-9]+.[0-9]+.[0-9]+)") + set("${out_required_version}" "${CMAKE_MATCH_1}" PARENT_SCOPE) + else() + message(DEBUG "Failed to parse `cargo tree -i ${cxx_name}` output: ${cxx_version_output}") + set("${out_required_version}" "${cxx_name}-NOTFOUND" PARENT_SCOPE) + endif() +endfunction() + +function(_corrosion_check_cxx_version manifest_dir out_required_version) + # cxxbridge-cmd is known to be available in lockfiles since cxx 1.0.131. + # We include `cxx` as a fallback to support older versions too. `cxxbridge` should always + # be exactly the same version as `cxx`, so falling back to `cxx` version should not cause issues. + foreach(cxxbridge_name cxxbridge-cmd cxx) + unset(cxx_required_version) + _corrosion_check_cxx_version_helper("${manifest_dir}" + "${cxxbridge_name}" + cxx_required_version) + if(cxx_required_version) + set("${out_required_version}" "${cxx_required_version}" PARENT_SCOPE) + break() + else() + set("${out_required_version}" "cxx-NOTFOUND" PARENT_SCOPE) + endif() + endforeach() + +endfunction() + + + #[=======================================================================[.md: ** EXPERIMENTAL **: This function is currently still considered experimental and is not officially released yet. Feedback and Suggestions are welcome. @@ -1541,6 +1655,7 @@ ANCHOR: corrosion_add_cxxbridge ```cmake corrosion_add_cxxbridge(cxx_target CRATE + REGEN_TARGET [FILES ] ) ``` @@ -1551,6 +1666,7 @@ Adds build-rules to create C++ bindings using the [cxx] crate. * `cxxtarget`: Name of the C++ library target for the bindings, which corrosion will create. * **FILES**: Input Rust source file containing #[cxx::bridge]. * **CRATE**: Name of an imported Rust target. Note: Parameter may be renamed before release +* **REGEN_TARGET**: Name of a custom target that will regenerate the cxx bindings **without** recompiling. Note: Parameter may be renamed before release #### Currently missing arguments @@ -1588,7 +1704,7 @@ ANCHOR_END: corrosion_add_cxxbridge #]=======================================================================] function(corrosion_add_cxxbridge cxx_target) set(OPTIONS) - set(ONE_VALUE_KEYWORDS CRATE) + set(ONE_VALUE_KEYWORDS CRATE REGEN_TARGET) set(MULTI_VALUE_KEYWORDS FILES) cmake_parse_arguments(PARSE_ARGV 1 _arg "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") @@ -1601,6 +1717,13 @@ function(corrosion_add_cxxbridge cxx_target) endif() endforeach() + if(DEFINED _arg_UNPARSED_ARGUMENTS) + message(AUTHOR_WARNING "corrosion_add_cxxbridge was called with the following unknown arguments: " + "`${_arg_UNPARSED_ARGUMENTS}`\n" + "Unknown arguments will be ignored." + ) + endif() + get_target_property(manifest_path "${_arg_CRATE}" INTERFACE_COR_PACKAGE_MANIFEST_PATH) if(NOT EXISTS "${manifest_path}") @@ -1609,20 +1732,12 @@ function(corrosion_add_cxxbridge cxx_target) get_filename_component(manifest_dir ${manifest_path} DIRECTORY) - execute_process(COMMAND ${CMAKE_COMMAND} -E env - "CARGO_BUILD_RUSTC=${_CORROSION_RUSTC}" - ${_CORROSION_CARGO} tree -i cxx --depth=0 - WORKING_DIRECTORY "${manifest_dir}" - RESULT_VARIABLE cxx_version_result - OUTPUT_VARIABLE cxx_version_output - ) - if(NOT "${cxx_version_result}" EQUAL "0") - message(FATAL_ERROR "Crate ${_arg_CRATE} does not depend on cxx.") - endif() - if(cxx_version_output MATCHES "cxx v([0-9]+.[0-9]+.[0-9]+)") - set(cxx_required_version "${CMAKE_MATCH_1}") - else() - message(FATAL_ERROR "Failed to parse cxx version from cargo tree output: `cxx_version_output`") + _corrosion_check_cxx_version("${manifest_dir}" cxx_required_version) + + if(NOT cxx_required_version) + message(FATAL_ERROR + "Failed to find a dependency on `cxxbridge-cmd` / `cxx` for crate ${_arg_CRATE}" + ) endif() # First check if a suitable version of cxxbridge is installed @@ -1656,19 +1771,21 @@ function(corrosion_add_cxxbridge cxx_target) if(Rust_CARGO_HOST_OS STREQUAL "windows") set(executable_postfix ".exe") endif() + add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}/bin/cxxbridge${executable_postfix}" COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}" COMMAND ${CMAKE_COMMAND} -E env - "CARGO_BUILD_RUSTC=${_CORROSION_RUSTC}" - ${_CORROSION_CARGO} install + "CARGO_BUILD_RUSTC=$CACHE{CORROSION_TOOLS_RUSTC}" + $CACHE{CORROSION_TOOLS_CARGO} install cxxbridge-cmd --version "${cxx_required_version}" + --locked --root "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}" --quiet # todo: use --target-dir to potentially reuse artifacts - COMMENT "Building cxxbridge (version ${cxx_required_version})" + COMMENT "Building cxxbridge (version ${cxx_required_version}) with Rust toolchain $CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN}" ) add_custom_target("cxxbridge_v${cxx_required_version}" DEPENDS "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}/bin/cxxbridge${executable_postfix}" @@ -1711,14 +1828,11 @@ function(corrosion_add_cxxbridge cxx_target) # cxx generated code is using c++11 features in headers, so propagate c++11 as minimal requirement target_compile_features(${cxx_target} PUBLIC cxx_std_11) - # Todo: target_link_libraries is only necessary for rust2c projects. - # It is possible that checking if the rust crate is an executable is a sufficient check, - # but some more thought may be needed here. - # Maybe we should also let the user do this, since for c2rust, the user also has to call - # corrosion_link_libraries() themselves. - get_target_property(crate_target_type ${_arg_CRATE} TYPE) - if (NOT crate_target_type STREQUAL "EXECUTABLE") - target_link_libraries(${cxx_target} PRIVATE ${_arg_CRATE}) + if (TARGET "${_arg_CRATE}-static") + target_link_libraries(${cxx_target} PRIVATE "${_arg_CRATE}-static") + endif() + if (TARGET "${_arg_CRATE}-shared") + target_link_libraries(${cxx_target} PRIVATE "${_arg_CRATE}-shared") endif() file(MAKE_DIRECTORY "${generated_dir}/include/rust") @@ -1730,6 +1844,9 @@ function(corrosion_add_cxxbridge cxx_target) COMMENT "Generating rust/cxx.h header" ) + set(GENERATED_SOURCES "") + set(GENERATED_HEADERS "${generated_dir}/include/rust/cxx.h") + foreach(filepath ${_arg_FILES}) get_filename_component(filename ${filepath} NAME_WE) get_filename_component(directory ${filepath} DIRECTORY) @@ -1757,51 +1874,102 @@ function(corrosion_add_cxxbridge cxx_target) --output "${source_placement_dir}/${cxx_source}" --include "${cxx_target}/${cxx_header}" DEPENDS "cxxbridge_v${cxx_required_version}" "${rust_source_path}" - COMMENT "Generating cxx bindings for crate ${_arg_CRATE}" + COMMENT "Generating cxx bindings for crate ${_arg_CRATE} and file src/${filepath}" ) - target_sources(${cxx_target} - PRIVATE - "${header_placement_dir}/${cxx_header}" - "${generated_dir}/include/rust/cxx.h" - "${source_placement_dir}/${cxx_source}" - ) + list(APPEND GENERATED_SOURCES "${source_placement_dir}/${cxx_source}") + list(APPEND GENERATED_HEADERS "${header_placement_dir}/${cxx_header}") endforeach() + target_sources(${cxx_target} PRIVATE ${GENERATED_SOURCES}) + # Make sure to export the headers with PUBLIC. + # This ensures that any target that depends on cxx_target also has these files as a dependency + # CMake will then make sure to generate the files before building either target, which is important + # in the presence of circular dependencies + target_sources(${cxx_target} PUBLIC ${GENERATED_HEADERS}) + + if(DEFINED _arg_REGEN_TARGET) + # Add only the headers to the regen target, as the sources are actually not needed + # For the IDE to pick everything up + add_custom_target(${_arg_REGEN_TARGET} + DEPENDS ${GENERATED_HEADERS} + COMMENT "Generated cxx bindings for crate ${_arg_CRATE}") + endif() + endfunction() #[=======================================================================[.md: ANCHOR: corrosion_cbindgen + +A helper function which uses [cbindgen] to generate C/C++ bindings for a Rust crate. +If `cbindgen` is not in `PATH` the helper function will automatically try to download +`cbindgen` and place the built binary into `CMAKE_BINARY_DIR`. The binary is shared +between multiple invocations of this function. + +The function comes with two different signatures. It's recommended to use the `TARGET` based signature when possible. + +### Auto mode (With a Rust target imported by corrosion) ```cmake -corrosion_cbindgen( +corrosion_experimental_cbindgen( TARGET HEADER_NAME - [CARGO_PACKAGE ] - [MANIFEST_DIRECTORY ] [CBINDGEN_VERSION ] [FLAGS ... ] ) ``` -A helper function which uses [cbindgen] to generate C/C++ bindings for a Rust crate. -If `cbindgen` is not in `PATH` the helper function will automatically try to download -`cbindgen` and place the built binary into `CMAKE_BINARY_DIR`. The binary is shared -between multiple invocations of this function. +### Auto-mode specific Arguments * **TARGET**: The name of an imported Rust library target, for which bindings should be generated. - If the target was not previously imported by Corrosion, because the crate only produces an - `rlib`, you must additionally specify `MANIFEST_DIRECTORY`. + If the target is not imported by Corrosion, because the crate only produces an + `rlib`, you can instead use the second signature and manually pass `MANIFEST_DIRECTORY`, + `CARGO_PACKAGE` and `BINDINGS_TARGET` -* **MANIFEST_DIRECTORY**: Directory of the package defining the library crate bindings should be generated for. +### Manual mode (Without a Rust target imported by corrosion) +```cmake +corrosion_experimental_cbindgen( + MANIFEST_DIRECTORY + CARGO_PACKAGE + BINDINGS_TARGET + [TARGET_TRIPLE ] + HEADER_NAME + [CBINDGEN_VERSION ] + [FLAGS ... ] +) +``` + +### Manual-mode specific Arguments + +* **MANIFEST_DIRECTORY**: Manual mode only. + Directory of the package defining the library crate bindings should be generated for. If you want to avoid specifying `MANIFEST_DIRECTORY` you could add a `staticlib` target to your package manifest as a workaround to make corrosion import the crate. +* **CARGO_PACKAGE**: Manual mode only. + The name of the cargo package that bindings should be generated for. + Note: This corresponds to the `cbindgen` `--crate` option, which actually wants a package name. + +* **BINDINGS_TARGET**: Manual mode only. + Name of an `INTERFACE` CMake target that the generated bindings should be attached to. + In auto mode, the generated headers will be attached to the imported rust CMake crate, + and corrosion will take care of adding the necessary build dependencies. + In manual mode, this target likely doesn't exist, so the user needs to specify an INTERFACE CMake + target, which the header files should be attached to. The user must create this target themselves and + ensure to add any necessary dependencies (e.g. via `add_dependencies()`) to ensure that consumers of the + `INTERFACE` library are not linked before the Rust library has been built. + +* **TARGET_TRIPLE**: Manual mode only. + Rust target triple (e.g. `x86_64-unknown-linux-gnu`) that cbindgen should use when generating the bindings. + Defaults to target triple that corrosion was confiured to compile for. + +### Common Arguments + * **HEADER_NAME**: The name of the generated header file. This will be the name which you include in your C/C++ code (e.g. `#include "myproject/myheader.h" if you specify `HEADER_NAME "myproject/myheader.h"`. * **CBINDGEN_VERSION**: Version requirement for cbindgen. Exact semantics to be specified. Currently not implemented. * **FLAGS**: Arbitrary other flags for `cbindgen`. Run `cbindgen --help` to see the possible flags. -[cbindgen]: https://github.com/eqrion/cbindgen +[cbindgen]: https://github.com/mozilla/cbindgen ### Current limitations @@ -1819,12 +1987,16 @@ function(corrosion_experimental_cbindgen) set(ONE_VALUE_KEYWORDS TARGET MANIFEST_DIRECTORY + CARGO_PACKAGE + BINDINGS_TARGET + TARGET_TRIPLE HEADER_NAME - CBINDGEN_VERSION) + CBINDGEN_VERSION + ) set(MULTI_VALUE_KEYWORDS "FLAGS") cmake_parse_arguments(PARSE_ARGV 0 CCN "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") - set(required_keywords TARGET HEADER_NAME) + set(required_keywords HEADER_NAME) foreach(keyword ${required_keywords}) if(NOT DEFINED "CCN_${keyword}") message(FATAL_ERROR "Missing required parameter `${keyword}`.") @@ -1832,37 +2004,65 @@ function(corrosion_experimental_cbindgen) message(FATAL_ERROR "Required parameter `${keyword}` may not be set to an empty string.") endif() endforeach() - set(rust_target "${CCN_TARGET}") + if(NOT (DEFINED CCN_TARGET + OR (DEFINED CCN_MANIFEST_DIRECTORY AND DEFINED CCN_BINDINGS_TARGET + AND DEFINED CCN_BINDINGS_TARGET) + ) + ) + message(FATAL_ERROR "Unknown signature for corrosion_experimental_cbindgen.\n" + "Either the `TARGET` or the `MANIFEST_DIRECTORY` based signature must be chosen.\n" + "Please view the documentation for details on the function signature.\n" + "Passed arguments where: `${ARGV}`" + ) + endif() + + if(DEFINED CCN_UNPARSED_ARGUMENTS) + message(AUTHOR_WARNING "corrosion_experimental_cbindgen was called with the following unknown arguments: " + "`${CCN_UNPARSED_ARGUMENTS}`\n" + "Unknown arguments will be ignored." + ) + endif() unset(package_manifest_dir) - set(hostbuild_override "$>") - set(cbindgen_target_triple "$") + if(TARGET "${CCN_TARGET}") + set(cbindgen_bindings_target "${CCN_TARGET}") + set(hostbuild_override "$>") + set(cbindgen_target_triple "$") - if(TARGET "${rust_target}") - get_target_property(package_manifest_path "${rust_target}" INTERFACE_COR_PACKAGE_MANIFEST_PATH) + get_target_property(package_manifest_path "${CCN_TARGET}" INTERFACE_COR_PACKAGE_MANIFEST_PATH) if(NOT EXISTS "${package_manifest_path}") message(FATAL_ERROR "Internal error: No package manifest found at ${package_manifest_path}") endif() get_filename_component(package_manifest_dir "${package_manifest_path}" DIRECTORY) + get_target_property(rust_cargo_package "${CCN_TARGET}" COR_CARGO_PACKAGE_NAME ) + if(NOT rust_cargo_package) + message(FATAL_ERROR "Internal Error: Could not determine cargo package name for cbindgen. ") + endif() # todo: as an optimization we could cache the cargo metadata output (but --no-deps makes that slightly more complicated) else() if(NOT DEFINED CCN_MANIFEST_DIRECTORY) message(FATAL_ERROR - "`${rust_target}` is not a target imported by corrosion and `MANIFEST_DIRECTORY` was not provided." - ) + "Internal error: There should have been a fatal error already if neither TARGET or " + "MANIFEST_DIRECTORY are specfied.") + endif() + cmake_path(ABSOLUTE_PATH CCN_MANIFEST_DIRECTORY NORMALIZE OUTPUT_VARIABLE package_manifest_dir) + if(DEFINED CCN_TARGET_TRIPLE) + set(cbindgen_target_triple "${CCN_TARGET_TRIPLE}") else() - set(package_manifest_dir "${CCN_MANIFEST_DIRECTORY}") + set(cbindgen_target_triple "${Rust_CARGO_TARGET}") + endif() + set(rust_cargo_package "${CCN_CARGO_PACKAGE}") + set(cbindgen_bindings_target "${CCN_BINDINGS_TARGET}") + get_target_property(type "${cbindgen_bindings_target}" TYPE) + if(NOT ${type} STREQUAL "INTERFACE_LIBRARY") + message(AUTHOR_WARNING "The CMake target for the cbindgen generated files is expected to be" + " an `INTERFACE` library, but was `${type}` instead." + ) endif() endif() - get_target_property(rust_cargo_package "${rust_target}" COR_CARGO_PACKAGE_NAME ) - if(NOT rust_cargo_package) - message(FATAL_ERROR "Internal Error: Could not determine cargo package name for cbindgen. " - ) - endif() - message(STATUS "Using package ${rust_cargo_package} as crate for cbindgen") - + message(STATUS "Using package `${rust_cargo_package}` as crate for cbindgen") set(output_header_name "${CCN_HEADER_NAME}") @@ -1878,17 +2078,21 @@ function(corrosion_experimental_cbindgen) set(executable_postfix ".exe") endif() set(cbindgen "${local_cbindgen_install_dir}/bin/cbindgen${executable_postfix}") + if(NOT TARGET "_corrosion_cbindgen") file(MAKE_DIRECTORY "${local_cbindgen_install_dir}") + add_custom_command(OUTPUT "${cbindgen}" COMMAND ${CMAKE_COMMAND} -E env - "CARGO_BUILD_RUSTC=${_CORROSION_RUSTC}" - ${_CORROSION_CARGO} install + "CARGO_BUILD_RUSTC=$CACHE{CORROSION_TOOLS_RUSTC}" + $CACHE{CORROSION_TOOLS_CARGO} install cbindgen + --locked --root "${local_cbindgen_install_dir}" ${_CORROSION_QUIET_OUTPUT_FLAG} - COMMENT "Building cbindgen" + COMMENT "Building cbindgen with Rust toolchain $CACHE{CORROSION_TOOLS_RUST_TOOLCHAIN}" + VERBATIM ) add_custom_target("_corrosion_cbindgen" DEPENDS "${cbindgen}" @@ -1897,14 +2101,14 @@ function(corrosion_experimental_cbindgen) endif() 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(generated_dir "${corrosion_generated_dir}/cbindgen/${cbindgen_bindings_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") - target_sources(${rust_target} + target_sources(${cbindgen_bindings_target} INTERFACE FILE_SET HEADERS BASE_DIRS "${header_placement_dir}" @@ -1912,7 +2116,7 @@ function(corrosion_experimental_cbindgen) ) else() # Note: not clear to me how install would best work before CMake 3.23 - target_include_directories(${rust_target} + target_include_directories(${cbindgen_bindings_target} INTERFACE $ $ @@ -1925,7 +2129,6 @@ function(corrosion_experimental_cbindgen) file(MAKE_DIRECTORY "${generated_header_dir}") unset(depfile_cbindgen_arg) - unset(depfile_cmake_arg) get_filename_component(generated_depfile_dir "${generated_depfile}" DIRECTORY) file(MAKE_DIRECTORY "${generated_depfile_dir}") set(depfile_cbindgen_arg "--depfile=${generated_depfile}") @@ -1936,6 +2139,9 @@ function(corrosion_experimental_cbindgen) COMMAND "${CMAKE_COMMAND}" -E env TARGET="${cbindgen_target_triple}" + # cbindgen invokes cargo-metadata and checks the CARGO environment variable + CARGO="${_CORROSION_CARGO}" + RUSTC="${_CORROSION_RUSTC}" "${cbindgen}" --output "${generated_header}" --crate "${rust_cargo_package}" @@ -1955,19 +2161,22 @@ function(corrosion_experimental_cbindgen) ) endif() - if(NOT TARGET "_corrosion_cbindgen_${rust_target}_bindings") - add_custom_target(_corrosion_cbindgen_${rust_target}_bindings + if(NOT TARGET "_corrosion_cbindgen_${cbindgen_bindings_target}_bindings") + add_custom_target(_corrosion_cbindgen_${cbindgen_bindings_target}_bindings COMMENT "Generate cbindgen bindings for package ${rust_cargo_package}" ) endif() # Users might want to call cbindgen multiple times, e.g. to generate separate C++ and C header files. string(MAKE_C_IDENTIFIER "${output_header_name}" header_identifier ) - add_custom_target("_corrosion_cbindgen_${rust_target}_bindings_${header_identifier}" + add_custom_target("_corrosion_cbindgen_${cbindgen_bindings_target}_bindings_${header_identifier}" DEPENDS "${generated_header}" - COMMENT "Generate ${generated_header} for ${rust_target}" + COMMENT "Generate ${generated_header} for ${cbindgen_bindings_target}" ) - add_dependencies("_corrosion_cbindgen_${rust_target}_bindings" "_corrosion_cbindgen_${rust_target}_bindings_${header_identifier}") - add_dependencies(${rust_target} "_corrosion_cbindgen_${rust_target}_bindings") + add_dependencies("_corrosion_cbindgen_${cbindgen_bindings_target}_bindings" "_corrosion_cbindgen_${cbindgen_bindings_target}_bindings_${header_identifier}") + add_dependencies(${cbindgen_bindings_target} "_corrosion_cbindgen_${cbindgen_bindings_target}_bindings") + if(TARGET "${CCN_TARGET}") + add_dependencies(cargo-build_${CCN_TARGET} "_corrosion_cbindgen_${cbindgen_bindings_target}_bindings") + endif() endfunction() # Parse the version of a Rust package from it's package manifest (Cargo.toml) diff --git a/cmake/FindRust.cmake b/cmake/FindRust.cmake index e5cbdb27..413645ad 100644 --- a/cmake/FindRust.cmake +++ b/cmake/FindRust.cmake @@ -12,6 +12,12 @@ concrete Rust version, not a rustup proxy. cmake_minimum_required(VERSION 3.12) +option( + Rust_RUSTUP_INSTALL_MISSING_TARGET + "Use Rustup to automatically install missing targets instead of giving up" + OFF +) + # search for Cargo here and set up a bunch of cool flags and stuff include(FindPackageHandleStandardArgs) @@ -178,7 +184,13 @@ function(_corrosion_determine_libs_new target_triple out_libs out_flags) # Flags start with / for MSVC if (lib MATCHES "^/" AND ${target_triple} MATCHES "msvc$") - list(APPEND flag_list "${lib}") + # Windows GNU uses the compiler to invoke the linker, so -Wl, prefix is needed + # https://gitlab.kitware.com/cmake/cmake/-/blob/9bed4f4d817f139f0c2e050d7420e1e247949fe4/Modules/Platform/Windows-GNU.cmake#L156 + if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU") + list(APPEND flag_list "-Wl,${lib}") + else() + list(APPEND flag_list "${lib}") + endif() else() # Strip leading `-l` (unix) and potential .lib suffix (windows) string(REGEX REPLACE "^-l" "" "stripped_lib" "${lib}") @@ -190,7 +202,7 @@ function(_corrosion_determine_libs_new target_triple out_libs out_flags) # We leave it up to the C/C++ executable that links in the Rust static-library # to determine which version of the msvc runtime library it should select. list(FILTER libs_list EXCLUDE REGEX "^msvcrtd?") - list(FILTER flag_list EXCLUDE REGEX "^/defaultlib:msvcrtd?") + list(FILTER flag_list EXCLUDE REGEX "^(-Wl,)?/defaultlib:msvcrtd?") else() message(DEBUG "Determining required native libraries - failed: Regex match failure.") message(DEBUG "`native-static-libs` not found in: `${cargo_build_error_message}`") @@ -243,7 +255,8 @@ else() else() find_program(_Rust_COMPILER_TEST rustc PATHS "$ENV{HOME}/.cargo/bin") if(NOT EXISTS "${_Rust_COMPILER_TEST}") - set(_ERROR_MESSAGE "`rustc` not found in PATH or `$ENV{HOME}/.cargo/bin`.\n" + cmake_path(CONVERT "$ENV{HOME}/.cargo/bin" TO_CMAKE_PATH_LIST _cargo_bin_dir) + set(_ERROR_MESSAGE "`rustc` not found in PATH or `${_cargo_bin_dir}`.\n" "Hint: Check if `rustc` is in PATH or manually specify the location " "by setting `Rust_COMPILER` to the path to `rustc`.") _findrust_failed(${_ERROR_MESSAGE}) @@ -321,18 +334,18 @@ if (Rust_RESOLVE_RUSTUP_TOOLCHAINS) set(_DISCOVERED_TOOLCHAINS_VERSION "") foreach(_TOOLCHAIN_RAW ${_TOOLCHAINS_RAW}) - if (_TOOLCHAIN_RAW MATCHES "([a-zA-Z0-9\\._\\-]+)[ \t\r\n]?(\\(default\\) \\(override\\)|\\(default\\)|\\(override\\))?[ \t\r\n]+(.+)") + if (_TOOLCHAIN_RAW MATCHES "([a-zA-Z0-9\\._\\-]+)[ \t\r\n]?(\\(active\\)|\\(active, default\\)|\\(default\\) \\(override\\)|\\(default\\)|\\(override\\))?[ \t\r\n]+(.+)") set(_TOOLCHAIN "${CMAKE_MATCH_1}") set(_TOOLCHAIN_TYPE "${CMAKE_MATCH_2}") set(_TOOLCHAIN_PATH "${CMAKE_MATCH_3}") set(_TOOLCHAIN_${_TOOLCHAIN}_PATH "${CMAKE_MATCH_3}") - if (_TOOLCHAIN_TYPE MATCHES ".*\\(default\\).*") + if (_TOOLCHAIN_TYPE MATCHES ".*\\((active, )?default\\).*") set(_TOOLCHAIN_DEFAULT "${_TOOLCHAIN}") endif() - if (_TOOLCHAIN_TYPE MATCHES ".*\\(override\\).*") + if (_TOOLCHAIN_TYPE MATCHES ".*\\((active|override)\\).*") set(_TOOLCHAIN_OVERRIDE "${_TOOLCHAIN}") endif() @@ -355,7 +368,11 @@ if (Rust_RESOLVE_RUSTUP_TOOLCHAINS) else() set(_TOOLCHAIN_${_TOOLCHAIN}_IS_NIGHTLY "FALSE") endif() - if(EXISTS "${_TOOLCHAIN_PATH}/bin/cargo") + set(_suffix "") + if(CMAKE_HOST_WIN32) + set(_suffix ".exe") + endif() + if(EXISTS "${_TOOLCHAIN_PATH}/bin/cargo${_suffix}") list(APPEND _DISCOVERED_TOOLCHAINS_CARGO_PATH "${_TOOLCHAIN_PATH}/bin/cargo") else() list(APPEND _DISCOVERED_TOOLCHAINS_CARGO_PATH "NOTFOUND") @@ -363,7 +380,7 @@ if (Rust_RESOLVE_RUSTUP_TOOLCHAINS) else() message(AUTHOR_WARNING "Unexpected output from `rustc --version` for Toolchain `${_TOOLCHAIN}`: " "`${_TOOLCHAIN_RAW_VERSION}`.\n" - "Ignoring this toolchain." + "Ignoring this toolchain (Path: `${_TOOLCHAIN_PATH}`)." ) endif() else() @@ -761,6 +778,46 @@ if (NOT Rust_CARGO_TARGET_CACHED) message(STATUS "Rust Target: ${Rust_CARGO_TARGET_CACHED}") endif() + +if(Rust_TOOLCHAIN_IS_RUSTUP_MANAGED) + execute_process(COMMAND rustup target list --toolchain "${Rust_TOOLCHAIN}" + OUTPUT_VARIABLE AVAILABLE_TARGETS_RAW + ) + string(REPLACE "\n" ";" AVAILABLE_TARGETS_RAW "${AVAILABLE_TARGETS_RAW}") + string(REPLACE " (installed)" "" "AVAILABLE_TARGETS" "${AVAILABLE_TARGETS_RAW}") + set(INSTALLED_TARGETS_RAW "${AVAILABLE_TARGETS_RAW}") + list(FILTER INSTALLED_TARGETS_RAW INCLUDE REGEX " \\(installed\\)") + string(REPLACE " (installed)" "" "INSTALLED_TARGETS" "${INSTALLED_TARGETS_RAW}") + list(TRANSFORM INSTALLED_TARGETS STRIP) + if("${Rust_CARGO_TARGET_CACHED}" IN_LIST AVAILABLE_TARGETS) + message(DEBUG "Cargo target ${Rust_CARGO_TARGET} is an official target-triple") + message(DEBUG "Installed targets: ${INSTALLED_TARGETS}") + if(NOT ("${Rust_CARGO_TARGET_CACHED}" IN_LIST INSTALLED_TARGETS)) + if(Rust_RUSTUP_INSTALL_MISSING_TARGET) + message(STATUS "Cargo target ${Rust_CARGO_TARGET_CACHED} is not installed. Installing via rustup.") + execute_process(COMMAND "${Rust_RUSTUP}" target add + --toolchain ${Rust_TOOLCHAIN} + ${Rust_CARGO_TARGET_CACHED} + RESULT_VARIABLE target_add_result + ) + if(NOT "${target_add_result}" EQUAL "0") + message(FATAL_ERROR "Target ${Rust_CARGO_TARGET_CACHED} is not installed for toolchain " + "${Rust_TOOLCHAIN} and automatically installing failed with ${target_add_result}.\n" + "You can try to manually install by running\n" + "`rustup target add --toolchain ${Rust_TOOLCHAIN} ${Rust_CARGO_TARGET}`." + ) + endif() + message(STATUS "Installed target ${Rust_CARGO_TARGET_CACHED} successfully.") + else() + message(FATAL_ERROR "Target ${Rust_CARGO_TARGET_CACHED} is not installed for toolchain ${Rust_TOOLCHAIN}.\n" + "Help: Run `rustup target add --toolchain ${Rust_TOOLCHAIN} ${Rust_CARGO_TARGET_CACHED}` to install " + "the missing target or configure corrosion with `Rust_RUSTUP_INSTALL_MISSING_TARGET=ON`." + ) + endif() + endif() + endif() +endif() + if(Rust_CARGO_TARGET_CACHED STREQUAL Rust_DEFAULT_HOST_TARGET) set(Rust_CROSSCOMPILING FALSE CACHE INTERNAL "Rust is configured for cross-compiling") else() diff --git a/doc/src/common_issues.md b/doc/src/common_issues.md index 62d3d3d9..c7af9a0b 100644 --- a/doc/src/common_issues.md +++ b/doc/src/common_issues.md @@ -6,6 +6,7 @@ - [Linking Rust static libraries into Debug C/C++ binaries fails on Windows MSVC targets](#linking-rust-static-libraries-into-debug-cc-binaries-fails-on-windows-msvc-targets) - [Missing `soname` on Linux for `cdylibs`](#missing-soname-on-linux-for-cdylibs) - [Missing `install_name` on MacOS for `ccdylibs` / Hardcoded references to the build-directory](#missing-installname-on-macos-for-ccdylibs--hardcoded-references-to-the-build-directory) +- [CMake Error (target_link_libraries): Cannot find source file](#cmake-error-target_link_libraries-cannot-find-source-file) ## Linking Debug C/C++ libraries into Rust fails on Windows MSVC targets @@ -86,3 +87,36 @@ When building binaries using this shared library, you should set the build rpath your shared library, e.g. by setting `set(CMAKE_BUILD_RPATH ${YOUR_CUSTOM_OUTPUT_DIRECTORY})` before adding executables. For a practical example, you may look at [Slint PR 2455](https://github.com/slint-ui/slint/pull/2455). + +## CMake Error (target_link_libraries): Cannot find source file + +When using `corrosion_add_cxxbridge`, you may encounter an error similar to this in targets that depend on the cxxbridge target: + +```diff +- CMake Error at ...../CMakeLists.txt:61 (target_link_libraries): +- Cannot find source file: +- +- ...../corrosion_generated/..../somefile.h +- +- Tried extensions .c .C .c++ .cc .cpp .cxx .cu .mpp .m .M .mm .ixx .cppm +- .ccm .cxxm .c++m .h .hh .h++ .hm .hpp .hxx .in .txx .f .F .for .f77 .f90 +- .f95 .f03 .hip .ispc +``` + +Where `somefile.h` should be generated by CXX via `corrosion_add_cxxbridge`. +In theory, CMake should already know that this is a generated file and just generate it when needed. + +However, in older versions of CMake the `GENERATED` property isn't correctly propagated. +See also: [https://gitlab.kitware.com/cmake/cmake/-/issues/18399](https://gitlab.kitware.com/cmake/cmake/-/issues/18399) + +This has since been fixed with CMake 3.20: [https://cmake.org/cmake/help/v3.20/policy/CMP0118.html](https://cmake.org/cmake/help/latest/command/cmake_policy.html#version) +However, the CMake policy CMP0118 must be enabled **in any dependent CMakeLists.txt** for the fix to work. + +The best fix is to call: +```cmake +cmake_minimium_required(VERSION 3.20 FATAL_ERROR) +# (or any other version above 3.20) +``` +As described [here](https://cmake.org/cmake/help/latest/command/cmake_policy.html#version), this implies a call to `cmake_policy` which enables CMP0118. + +Unfortunately this must be done in all (transitive) downstream dependencies that link to the bridge target, so cannot be done from within corrosion automatically. diff --git a/doc/src/ffi_bindings.md b/doc/src/ffi_bindings.md index dfb4f446..58077a14 100644 --- a/doc/src/ffi_bindings.md +++ b/doc/src/ffi_bindings.md @@ -29,11 +29,6 @@ the headers. This is not available on a stable released version yet, and the details are subject to change. {{#include ../../cmake/Corrosion.cmake:corrosion_cbindgen}} -### Current limitations - -- The current version regenerates the bindings more often then necessary to be on the safe side, - but an upstream PR is open to solve this in a future cbindgen version. - ## cxx integration ⚠️⚠️⚠️ **EXPERIMENTAL** ⚠️⚠️⚠️ diff --git a/doc/src/usage.md b/doc/src/usage.md index 50eb098c..0d59c33f 100644 --- a/doc/src/usage.md +++ b/doc/src/usage.md @@ -155,15 +155,16 @@ Some configuration options can be specified individually for each target. You ca ### Global Corrosion Options -All of the following variables are evaluated automatically in most cases. In typical cases you + +#### Selecting the Rust toolchain and target triple + +The following variables are evaluated automatically in most cases. In typical cases you shouldn't need to alter any of these. If you do want to specify them manually, make sure to set them **before** `find_package(Corrosion REQUIRED)`. - `Rust_TOOLCHAIN:STRING` - Specify a named rustup toolchain to use. Changes to this variable resets all other options. Default: If the first-found `rustc` is a `rustup` proxy, then the default rustup toolchain (see `rustup show`) is used. Otherwise, the variable is unset by default. -- `Rust_ROOT:STRING` - CMake provided. Path to a Rust toolchain to use. This is an alternative if - you want to select a specific Rust toolchain, but it's not managed by rustup. Default: Nothing - `Rust_COMPILER:STRING` - Path to `rustc`, which should be used for compiling or for toolchain detection (if it is a `rustup` proxy). Default: The `rustc` in the first-found toolchain, either from `rustup`, or from a toolchain available in the user's `PATH`. @@ -177,7 +178,15 @@ them **before** `find_package(Corrosion REQUIRED)`. - `Rust_CARGO_TARGET:STRING` - The default target triple to build for. Alter for cross-compiling. Default: On Visual Studio Generator, the matching triple for `CMAKE_VS_PLATFORM_NAME`. Otherwise, the default target triple reported by `${Rust_COMPILER} --version --verbose`. +- `CORROSION_TOOLS_RUST_TOOLCHAIN:STRING`: Specify a different toolchain (e.g. `stable`) to use for compiling helper + tools such as `cbindgen` or `cxxbridge`. This can be useful when you want to compile your project with an + older rust version (e.g. for checking the MSRV), but you can build build-tools with a newer installed rust version. + +#### Enable Convenience Options + +The following options are off by default, but may increase convenience: +- `Rust_RUSTUP_INSTALL_MISSING_TARGET:BOOL`: Automatically install a missing target via `rustup` instead of failing. #### Developer/Maintainer Options @@ -200,6 +209,10 @@ versions individually. - `Rust_LLVM_VERSION<_MAJOR|_MINOR|_PATCH>` - The LLVM version used by rustc. - `Rust_IS_NIGHTLY` - 1 if a nightly toolchain is used, otherwise 0. Useful for selecting an unstable feature for a crate, that is only available on nightly toolchains. +- `Rust_RUSTUP_TOOLCHAINS`, `Rust_RUSTUP_TOOLCHAINS_RUSTC_PATH`, `Rust_RUSTUP_TOOLCHAINS_CARGO_PATH` + and `Rust_RUSTUP_TOOLCHAINS_VERSION`: These variables are lists, which should be iterated over with + CMakes `foreach(var IN ZIP_LISTS list1 list2 ...)` iterator. They provide a list of installed rustup managed toolchains and + the associated rustc and cargo paths as well as the corresponding rustc version. - Cache variables containing information based on the target triple for the selected target as well as the default host target: - `Rust_CARGO_TARGET_ARCH`, `Rust_CARGO_HOST_ARCH`: e.g. `x86_64` or `aarch64` diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 20f4e77d..409872f2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -171,6 +171,9 @@ if(Rust_VERSION VERSION_GREATER_EQUAL "1.64.0") add_subdirectory(override_crate_type) endif() add_subdirectory(custom_profiles) +if(NOT Rust_CROSSCOMPILING AND Rust_VERSION VERSION_GREATER_EQUAL "1.60.0") + add_subdirectory(custom_target) +endif() add_subdirectory(cbindgen) add_subdirectory(corrosion_install) add_subdirectory(cxxbridge) diff --git a/test/TestFileExists.cmake b/test/TestFileExists.cmake new file mode 100644 index 00000000..a69f49a0 --- /dev/null +++ b/test/TestFileExists.cmake @@ -0,0 +1,27 @@ +# CMake script to test if a file exists. Errors if the file does not exist. +# Expect actual arguments to start at index 3 (cmake -P ) + +# Expect one argument +if(NOT (CMAKE_ARGC EQUAL "4")) + message(FATAL_ERROR "Test Internal Error: Unexpected ARGC Value: ${CMAKE_ARGC}.") +endif() + +set(FILE_PATH "${CMAKE_ARGV3}") + +if(NOT ( EXISTS "${FILE_PATH}" )) + set(error_details "File `${FILE_PATH}` does not exist!\n") + set(PARENT_TREE "${FILE_PATH}") + cmake_path(HAS_PARENT_PATH PARENT_TREE has_parent) + while(has_parent) + cmake_path(GET PARENT_TREE PARENT_PATH PARENT_TREE) + cmake_path(HAS_PARENT_PATH PARENT_TREE has_parent) + if(EXISTS "${PARENT_TREE}") + file(GLOB dir_contents LIST_DIRECTORIES true "${PARENT_TREE}/*") + list(APPEND error_details "Found Parent directory `${PARENT_TREE}` exists and contains:\n" ${dir_contents}) + break() + else() + list(APPEND error_details "Parent directory `${PARENT_TREE}` also does not exist!") + endif() + endwhile() + message(FATAL_ERROR "Test failed: ${error_details}") +endif() diff --git a/test/cbindgen/CMakeLists.txt b/test/cbindgen/CMakeLists.txt index 79566966..b91120d3 100644 --- a/test/cbindgen/CMakeLists.txt +++ b/test/cbindgen/CMakeLists.txt @@ -1,8 +1,50 @@ -corrosion_tests_add_test(cbindgen_rust2cpp "cpp-exe" TEST_SRC_DIR rust2cpp) +corrosion_tests_add_test(cbindgen_rust2cpp_auto "cpp-exe" TEST_SRC_DIR auto) +corrosion_tests_add_test(cbindgen_manual "cpp-exe" TEST_SRC_DIR manual) -set_tests_properties(cbindgen_rust2cpp_run_cpp-exe PROPERTIES PASS_REGULAR_EXPRESSION +set_tests_properties(cbindgen_rust2cpp_auto_run_cpp-exe cbindgen_manual_run_cpp-exe + PROPERTIES PASS_REGULAR_EXPRESSION "^add_point Result: Point { x: 100, y: 100 }\r?\n$" ) + +add_test(NAME "cbindgen_install_configure" + COMMAND + ${CMAKE_COMMAND} + -S "${CMAKE_CURRENT_SOURCE_DIR}/install_lib" + -B "${CMAKE_CURRENT_BINARY_DIR}/build_install_lib" + "-G${CMAKE_GENERATOR}" + --install-prefix "${CMAKE_CURRENT_BINARY_DIR}/build_install_lib/test_install_lib_install_dir" + + COMMAND_EXPAND_LISTS +) + +add_test(NAME "cbindgen_install" + COMMAND + ${CMAKE_COMMAND} + --build "${CMAKE_CURRENT_BINARY_DIR}/build_install_lib" + --target install + --config Debug +) + +add_test(NAME cbindgen_install_check_header_installed + COMMAND + "${CMAKE_COMMAND}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/../TestFileExists.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/build_install_lib/test_install_lib_install_dir/include/rust-lib.h" +) + +add_test(NAME cbindgen_install_clean + COMMAND + "${CMAKE_COMMAND}" + -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/build_install_lib/" +) +set_tests_properties("cbindgen_install_configure" PROPERTIES FIXTURES_SETUP "configure_fixture_cbindgen_install") +set_tests_properties("cbindgen_install" PROPERTIES FIXTURES_REQUIRED "configure_fixture_cbindgen_install") + +set_tests_properties("cbindgen_install" PROPERTIES FIXTURES_SETUP "install_fixture_cbindgen_install") +set_tests_properties("cbindgen_install_check_header_installed" PROPERTIES FIXTURES_REQUIRED "install_fixture_cbindgen_install") +set_tests_properties("cbindgen_install_check_header_installed" PROPERTIES FIXTURES_SETUP "fx_check_header_installed") +set_tests_properties(cbindgen_install_clean PROPERTIES FIXTURES_CLEANUP "configure_fixture_cbindgen_install;install_fixture_cbindgen_install;fx_check_header_installed") + # Todo: We also should add a cpp2rust test with the following setup: # - A rust lib that is used by a rust executable # - cbindgen creates bindings for the rust-lib diff --git a/test/cbindgen/rust2cpp/CMakeLists.txt b/test/cbindgen/auto/CMakeLists.txt similarity index 55% rename from test/cbindgen/rust2cpp/CMakeLists.txt rename to test/cbindgen/auto/CMakeLists.txt index 71967796..b316a048 100644 --- a/test/cbindgen/rust2cpp/CMakeLists.txt +++ b/test/cbindgen/auto/CMakeLists.txt @@ -1,10 +1,11 @@ cmake_minimum_required(VERSION 3.15) project(test_project VERSION 0.1.0) -include(../../test_header.cmake) +set(CORROSION_TOOLS_RUST_TOOLCHAIN "stable") +include(../../test_header.cmake) corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml) -corrosion_experimental_cbindgen(TARGET rust_lib HEADER_NAME "rust-lib.h") +corrosion_experimental_cbindgen(TARGET the_rust_lib_crate_name HEADER_NAME "rust-lib.h") add_executable(cpp-exe main.cpp) set_property(TARGET cpp-exe PROPERTY CXX_STANDARD 11) -target_link_libraries(cpp-exe PUBLIC rust_lib) +target_link_libraries(cpp-exe PUBLIC the_rust_lib_crate_name) diff --git a/test/cbindgen/rust2cpp/main.cpp b/test/cbindgen/auto/main.cpp similarity index 100% rename from test/cbindgen/rust2cpp/main.cpp rename to test/cbindgen/auto/main.cpp diff --git a/test/cbindgen/rust2cpp/rust/Cargo.toml b/test/cbindgen/auto/rust/Cargo.toml similarity index 63% rename from test/cbindgen/rust2cpp/rust/Cargo.toml rename to test/cbindgen/auto/rust/Cargo.toml index e8816af8..ff8d58fe 100644 --- a/test/cbindgen/rust2cpp/rust/Cargo.toml +++ b/test/cbindgen/auto/rust/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rust-lib" +name = "the_rust_package_name" version = "0.1.0" license = "MIT" edition = "2018" @@ -8,3 +8,4 @@ edition = "2018" [lib] crate-type=["staticlib"] +name = "the_rust_lib_crate_name" diff --git a/test/cbindgen/rust2cpp/rust/cbindgen.toml b/test/cbindgen/auto/rust/cbindgen.toml similarity index 100% rename from test/cbindgen/rust2cpp/rust/cbindgen.toml rename to test/cbindgen/auto/rust/cbindgen.toml diff --git a/test/cbindgen/rust2cpp/rust/src/ffi.rs b/test/cbindgen/auto/rust/src/ffi.rs similarity index 100% rename from test/cbindgen/rust2cpp/rust/src/ffi.rs rename to test/cbindgen/auto/rust/src/ffi.rs diff --git a/test/cbindgen/rust2cpp/rust/src/lib.rs b/test/cbindgen/auto/rust/src/lib.rs similarity index 100% rename from test/cbindgen/rust2cpp/rust/src/lib.rs rename to test/cbindgen/auto/rust/src/lib.rs diff --git a/test/cbindgen/rust2cpp/rust/src/other_mod/mod.rs b/test/cbindgen/auto/rust/src/other_mod/mod.rs similarity index 100% rename from test/cbindgen/rust2cpp/rust/src/other_mod/mod.rs rename to test/cbindgen/auto/rust/src/other_mod/mod.rs diff --git a/test/cbindgen/install_lib/CMakeLists.txt b/test/cbindgen/install_lib/CMakeLists.txt new file mode 100644 index 00000000..66bb81aa --- /dev/null +++ b/test/cbindgen/install_lib/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) + +set(CORROSION_TOOLS_RUST_TOOLCHAIN "stable") +include(../../test_header.cmake) + +corrosion_import_crate(MANIFEST_PATH rust_lib/Cargo.toml) + +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + corrosion_add_target_local_rustflags(rust_lib "-Clink-arg=-Wl,-soname,librust_lib.so") + set_target_properties(rust_lib-shared PROPERTIES IMPORTED_SONAME librust_lib.so) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") + corrosion_add_target_local_rustflags(rust_lib -Clink-arg=-Wl,-install_name,@rpath/librust_lib.dylib,-current_version,1.0,-compatibility_version,1.0) + set_target_properties(rust_lib-shared PROPERTIES IMPORTED_NO_SONAME 0) + set_target_properties(rust_lib-shared PROPERTIES IMPORTED_SONAME librust_lib.dylib) +endif() + +corrosion_experimental_cbindgen(TARGET rust_lib HEADER_NAME "rust-lib.h") + +corrosion_install(TARGETS rust_lib LIBRARY PUBLIC_HEADER) diff --git a/test/cbindgen/install_lib/rust_lib/Cargo.toml b/test/cbindgen/install_lib/rust_lib/Cargo.toml new file mode 100644 index 00000000..eb4a0752 --- /dev/null +++ b/test/cbindgen/install_lib/rust_lib/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rust_lib" +version = "0.1.0" +edition = "2018" + +[dependencies] + +[lib] +crate-type = ["staticlib", "cdylib"] diff --git a/test/cbindgen/install_lib/rust_lib/src/lib.rs b/test/cbindgen/install_lib/rust_lib/src/lib.rs new file mode 100644 index 00000000..76d40c6c --- /dev/null +++ b/test/cbindgen/install_lib/rust_lib/src/lib.rs @@ -0,0 +1,5 @@ + +#[no_mangle] +pub extern "C" fn add(left: u64, right: u64) -> u64 { + left + right +} diff --git a/test/cbindgen/manual/CMakeLists.txt b/test/cbindgen/manual/CMakeLists.txt new file mode 100644 index 00000000..da7b0193 --- /dev/null +++ b/test/cbindgen/manual/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0) + +set(CORROSION_TOOLS_RUST_TOOLCHAIN "stable") +include(../../test_header.cmake) +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml) + +add_library(cbindgen_rust_lib INTERFACE) +corrosion_experimental_cbindgen(MANIFEST_DIRECTORY rust + CARGO_PACKAGE the-rust-lib-package-name + BINDINGS_TARGET cbindgen_rust_lib + HEADER_NAME "rust-lib.h") + +# The interface library for the generated headers should link to the actual rust library +target_link_libraries(cbindgen_rust_lib INTERFACE the_actual_library_crate_name) + +add_executable(cpp-exe main.cpp) +set_property(TARGET cpp-exe PROPERTY CXX_STANDARD 11) +# The C/C++ bin needs to link to the cbindgen library with the generated sources. +target_link_libraries(cpp-exe PUBLIC cbindgen_rust_lib) +#add_dependencies(cpp-exe cbindgen_rust_lib) diff --git a/test/cbindgen/manual/main.cpp b/test/cbindgen/manual/main.cpp new file mode 100644 index 00000000..7ad201fc --- /dev/null +++ b/test/cbindgen/manual/main.cpp @@ -0,0 +1,22 @@ +#include "rust-lib.h" +#include + +int main(int argc, char **argv) { + assert(is_magic_number(MAGIC_NUMBER)); + struct Point p1, p2; + p1.x = 54; + p2.x = 46; + p1.y = 34; + p2.y = 66; + add_point(&p1, &p2); + assert(p1.x == 100); + assert(p2.x == 46); + assert(p1.y == 100); + assert(p2.y == 66); + add_point(&p1, NULL); + assert(p1.x == 100); + assert(p1.y == 100); + + assert(OTHER_MOD_MAGIC_NUMBER == 192312312); + assert(FFI_MAGIC_NUMBER == 0xFDA00184); +} diff --git a/test/cbindgen/manual/rust/Cargo.toml b/test/cbindgen/manual/rust/Cargo.toml new file mode 100644 index 00000000..604d7127 --- /dev/null +++ b/test/cbindgen/manual/rust/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "the-rust-lib-package-name" +version = "0.1.0" +license = "MIT" +edition = "2018" + +[dependencies] + +[lib] +crate-type=["staticlib"] +name = "the_actual_library_crate_name" diff --git a/test/cbindgen/manual/rust/cbindgen.toml b/test/cbindgen/manual/rust/cbindgen.toml new file mode 100644 index 00000000..86b1a9ae --- /dev/null +++ b/test/cbindgen/manual/rust/cbindgen.toml @@ -0,0 +1,4 @@ +language = "C++" +include_version = true + + diff --git a/test/cbindgen/manual/rust/src/ffi.rs b/test/cbindgen/manual/rust/src/ffi.rs new file mode 100644 index 00000000..01ad181c --- /dev/null +++ b/test/cbindgen/manual/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/manual/rust/src/lib.rs b/test/cbindgen/manual/rust/src/lib.rs new file mode 100644 index 00000000..0ad5c8f8 --- /dev/null +++ b/test/cbindgen/manual/rust/src/lib.rs @@ -0,0 +1,33 @@ +pub const MAGIC_NUMBER: u64 = 0xABCD_EFAB; + +pub mod ffi; +pub mod other_mod; + +#[derive(Debug)] +#[repr(C)] +pub struct Point { + x: u64, + y: u64, +} + +impl Point { + pub(crate) fn add(&mut self, rhs: &Point) { + self.x = self.x.wrapping_add(rhs.x); + self.y = self.y.wrapping_add(rhs.y); + } +} + +#[no_mangle] +pub extern "C" fn add_point(lhs: Option<&mut Point>, rhs: Option<&Point>) { + if let (Some(p1), Some(p2)) = (lhs, rhs) { + p1.add(p2); + // Print something so we can let Ctest assert the output. + println!("add_point Result: {:?}", p1); + } +} + +// simple test if the constant was exported by cbindgen correctly +#[no_mangle] +pub extern "C" fn is_magic_number(num: u64) -> bool { + num == MAGIC_NUMBER +} diff --git a/test/cbindgen/manual/rust/src/other_mod/mod.rs b/test/cbindgen/manual/rust/src/other_mod/mod.rs new file mode 100644 index 00000000..ca34458b --- /dev/null +++ b/test/cbindgen/manual/rust/src/other_mod/mod.rs @@ -0,0 +1 @@ +pub const OTHER_MOD_MAGIC_NUMBER: u32 = 192312312; diff --git a/test/cbindgen/rust2cpp/rust/Cargo.lock b/test/cbindgen/rust2cpp/rust/Cargo.lock deleted file mode 100644 index 0d2c5eac..00000000 --- a/test/cbindgen/rust2cpp/rust/Cargo.lock +++ /dev/null @@ -1,7 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "rust-lib" -version = "0.1.0" diff --git a/test/custom_target/CMakeLists.txt b/test/custom_target/CMakeLists.txt new file mode 100644 index 00000000..8b1c4550 --- /dev/null +++ b/test/custom_target/CMakeLists.txt @@ -0,0 +1,21 @@ +# Create a custom target triple, by taking the default host triple target spec. +# This way we don't actually need to cross-compile and just check if we can handle +# custom json target triples. +# Note: dashes and not underscore is important, at least if cc-rs is used by dependencies, +# since cc-rs will attempt to parse the target triple. +# A real-world toolchain .json file should be named the same as the corresponding c/c++ compiler target, +# in order for cc-rs to choose the correct target by default. +set(target_spec_file "${CMAKE_CURRENT_BINARY_DIR}/custom-target-triple.json") +execute_process(COMMAND + ${CMAKE_COMMAND} -E env RUSTC_BOOTSTRAP=1 + ${_CORROSION_RUSTC} -Z unstable-options --print target-spec-json + OUTPUT_FILE "${target_spec_file}" + COMMAND_ERROR_IS_FATAL ANY +) + +set(Rust_CARGO_TARGET "${target_spec_file}") +corrosion_tests_add_test(custom_target "test-exe;rust-bin") + +set_tests_properties("custom_target_run_test-exe" PROPERTIES PASS_REGULAR_EXPRESSION [[Hello, Cxx! I am Rust!]]) +set_tests_properties("custom_target_run_rust-bin" PROPERTIES PASS_REGULAR_EXPRESSION [[The answer is 42]]) + diff --git a/test/custom_target/custom_target/CMakeLists.txt b/test/custom_target/custom_target/CMakeLists.txt new file mode 100644 index 00000000..d6396bcf --- /dev/null +++ b/test/custom_target/custom_target/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.15) +project(test_project VERSION 0.1.0 LANGUAGES C CXX) +# The outer test driver gives the file a useless name +# Supress the warning, since we don't rely on this information anyway. +set(CORROSION_NO_WARN_PARSE_TARGET_TRIPLE_FAILED ON) +include(../../test_header.cmake) + +if(NOT "${Rust_CARGO_TARGET}" MATCHES ".json") + message(FATAL_ERROR "This test project expects to be configured with a custom rust target!" + "Actual target: ${Rust_CARGO_TARGET}" + ) +endif() + +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml) +corrosion_set_cargo_flags(rust_lib "-Zbuild-std") +# custom target triples require nightly features +corrosion_set_env_vars(rust_lib RUSTC_BOOTSTRAP=1) + +corrosion_set_cargo_flags(rust-bin "-Zbuild-std") +corrosion_set_env_vars(rust-bin RUSTC_BOOTSTRAP=1) + +add_executable(test-exe main.cpp) +target_link_libraries(test-exe PUBLIC rust_lib) diff --git a/test/custom_target/custom_target/main.cpp b/test/custom_target/custom_target/main.cpp new file mode 100644 index 00000000..b5fde3cf --- /dev/null +++ b/test/custom_target/custom_target/main.cpp @@ -0,0 +1,6 @@ +extern "C" void rust_function(char const *name); + + +int main(int argc, char **argv) { + rust_function("Cxx"); +} diff --git a/test/custom_target/custom_target/rust/Cargo.lock b/test/custom_target/custom_target/rust/Cargo.lock new file mode 100644 index 00000000..7299895a --- /dev/null +++ b/test/custom_target/custom_target/rust/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cc" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7db2f146208d7e0fbee761b09cd65a7f51ccc38705d4e7262dad4d73b12a76b1" + +[[package]] +name = "rust-lib" +version = "0.1.0" +dependencies = [ + "cc", +] diff --git a/test/custom_target/custom_target/rust/Cargo.toml b/test/custom_target/custom_target/rust/Cargo.toml new file mode 100644 index 00000000..8977e313 --- /dev/null +++ b/test/custom_target/custom_target/rust/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "rust-lib" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type=["staticlib", "lib"] + +[[bin]] +name = "rust-bin" +path = "src/bin.rs" + +[build-dependencies] +cc = "1" diff --git a/test/custom_target/custom_target/rust/build.rs b/test/custom_target/custom_target/rust/build.rs new file mode 100644 index 00000000..a12b8d81 --- /dev/null +++ b/test/custom_target/custom_target/rust/build.rs @@ -0,0 +1,10 @@ +fn main() { + let mut builder = cc::Build::new(); + // We override the target here, purely to make the testcase simpler. + // In a real-world project, the custom rust .json target triple file + // should have a filename matching the target-triple the c-compiler understands + // (if the c/c++ toolchain is llvm based) + builder.target(&std::env::var("HOST").unwrap()); + + builder.file("c_lib.c").compile("custom_target"); +} \ No newline at end of file diff --git a/test/custom_target/custom_target/rust/c_lib.c b/test/custom_target/custom_target/rust/c_lib.c new file mode 100644 index 00000000..df162481 --- /dev/null +++ b/test/custom_target/custom_target/rust/c_lib.c @@ -0,0 +1,5 @@ +#include + +uint32_t calculate_42(void) { + return 42; +} \ No newline at end of file diff --git a/test/custom_target/custom_target/rust/src/bin.rs b/test/custom_target/custom_target/rust/src/bin.rs new file mode 100644 index 00000000..0a64cec5 --- /dev/null +++ b/test/custom_target/custom_target/rust/src/bin.rs @@ -0,0 +1,6 @@ +use rust_lib::calculate_42; + +fn main() { + let answer = unsafe { calculate_42() } ; + println!("The answer is {}", answer); +} diff --git a/test/custom_target/custom_target/rust/src/lib.rs b/test/custom_target/custom_target/rust/src/lib.rs new file mode 100644 index 00000000..0a1c5381 --- /dev/null +++ b/test/custom_target/custom_target/rust/src/lib.rs @@ -0,0 +1,13 @@ +use std::os::raw::c_char; + +extern "C" { + pub fn calculate_42() -> u32; +} + +#[no_mangle] +pub extern "C" fn rust_function(name: *const c_char) { + let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }; + let res = unsafe { calculate_42() }; + assert_eq!(res, 42); + println!("Hello, {}! I am Rust!", name); +} diff --git a/test/cxxbridge/CMakeLists.txt b/test/cxxbridge/CMakeLists.txt index 23c34ca9..89a918bf 100644 --- a/test/cxxbridge/CMakeLists.txt +++ b/test/cxxbridge/CMakeLists.txt @@ -8,6 +8,9 @@ if(CORROSION_TESTS_CXXBRIDGE) PASS_THROUGH_ARGS -DTEST_CXXBRIDGE_VARIANT2=ON ) corrosion_tests_add_test(cxxbridge_rust2cpp "cxxbridge-exe") + if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") + corrosion_tests_add_test(cxxbridge_circular "cpp_bin") + endif() set_tests_properties("cxxbridge_cpp2rust_1_run_rust_bin" PROPERTIES PASS_REGULAR_EXPRESSION diff --git a/test/cxxbridge/cxxbridge_circular/CMakeLists.txt b/test/cxxbridge/cxxbridge_circular/CMakeLists.txt new file mode 100644 index 00000000..3ce40425 --- /dev/null +++ b/test/cxxbridge/cxxbridge_circular/CMakeLists.txt @@ -0,0 +1,44 @@ +# This CMake project tests the setup for circular dependencies that involve a CXX bridge +# While circular dependencies are usually discouraged, with CXX they are reasonbly natural, +# as ideally, Rust should be able to call C++ freely and vice-versa. +# +# The way this project is set up, the rust_lib target acts as the one target that includes +# the mixed C++ and Rust code with the cpp_lib and cxxbridge targets as implementation details, +# which shouldn't be used individually. +cmake_minimum_required(VERSION 3.24) +project(test_project VERSION 0.1.0 LANGUAGES CXX) +include(../../test_header.cmake) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED 1) + +corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml) +corrosion_add_cxxbridge(cxxbridge CRATE rust_lib FILES lib.rs) + +if(MSVC) + set_target_properties(cxxbridge PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreadedDLL") +endif() + +add_library(cpp_lib STATIC cpplib.cpp) +target_include_directories(cpp_lib PUBLIC "${CMAKE_CURRENT_LIST_DIR}/include") + +# Make sure the cpp library can access the headers from the bridge and vice-versa +target_link_libraries(cpp_lib PRIVATE cxxbridge) +target_link_libraries(cxxbridge PRIVATE cpp_lib) + +# The 3 libraries (rust, cpp, cxx) have a circular dependency, set this up in the linker +# so that the rust_lib target links to everything and circular references are resolved. +if (CMAKE_CXX_LINK_GROUP_USING_RESCAN_SUPPORTED) + target_link_libraries(rust_lib INTERFACE + "$") +else() + target_link_libraries(rust_lib INTERFACE cxxbridge cpp_lib) +endif() + +add_executable(cpp_bin main.cpp) +target_link_libraries(cpp_bin rust_lib) + +if(MSVC) + set_target_properties(cpp_lib PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreadedDLL") + set_target_properties(cxxbridge PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreadedDLL") + set_target_properties(cpp_bin PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreadedDLL") +endif() diff --git a/test/cxxbridge/cxxbridge_circular/cpplib.cpp b/test/cxxbridge/cxxbridge_circular/cpplib.cpp new file mode 100644 index 00000000..15549a42 --- /dev/null +++ b/test/cxxbridge/cxxbridge_circular/cpplib.cpp @@ -0,0 +1,18 @@ +#include "cpplib.h" +#include "cxxbridge/lib.h" +#include "rust/cxx.h" +#include + +RsImage read_image(rust::Str path) { + std::cout << "read_image called" << std::endl; + std::cout << path << std::endl; + Rgba c = {1.0, 2.0, 3.0, 4.0}; + RsImage v = {1, 1, c}; + return v; +} + +void assert_equality() { + if (!read_image("dummy_path").equal_to("dummy path")) { + throw std::runtime_error("equality_check failed"); + } +} diff --git a/test/cxxbridge/cxxbridge_circular/include/cpplib.h b/test/cxxbridge/cxxbridge_circular/include/cpplib.h new file mode 100644 index 00000000..cf3427d6 --- /dev/null +++ b/test/cxxbridge/cxxbridge_circular/include/cpplib.h @@ -0,0 +1,6 @@ +#pragma once +#include "cxxbridge/lib.h" + +::RsImage read_image(::rust::Str path); + +void assert_equality(); diff --git a/test/cxxbridge/cxxbridge_circular/main.cpp b/test/cxxbridge/cxxbridge_circular/main.cpp new file mode 100644 index 00000000..023e5a91 --- /dev/null +++ b/test/cxxbridge/cxxbridge_circular/main.cpp @@ -0,0 +1,17 @@ +#include "cpplib.h" + +#include + +int main(void) { + std::cout << "Testing roundtrip..." << std::endl; + + try { + assert_equality(); + } catch (const std::exception &e) { + std::cerr << "Error: " << e.what() << std::endl; + return 1; + } + + std::cout << "Roundtrip successful!"; + return 0; +} diff --git a/test/cxxbridge/cxxbridge_circular/rust/Cargo.lock b/test/cxxbridge/cxxbridge_circular/rust/Cargo.lock new file mode 100644 index 00000000..aadd14c8 --- /dev/null +++ b/test/cxxbridge/cxxbridge_circular/rust/Cargo.lock @@ -0,0 +1,89 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cc" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" + +[[package]] +name = "cxx" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "proc-macro2" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rust_lib" +version = "0.1.0" +dependencies = [ + "cxx", +] + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" diff --git a/test/cxxbridge/cxxbridge_circular/rust/Cargo.toml b/test/cxxbridge/cxxbridge_circular/rust/Cargo.toml new file mode 100644 index 00000000..d5fca1ea --- /dev/null +++ b/test/cxxbridge/cxxbridge_circular/rust/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rust_lib" +version = "0.1.0" +edition = "2018" + +[lib] +name = "rust_lib" +crate-type = ["staticlib"] + +[dependencies] +cxx = "1.0" diff --git a/test/cxxbridge/cxxbridge_circular/rust/src/lib.rs b/test/cxxbridge/cxxbridge_circular/rust/src/lib.rs new file mode 100644 index 00000000..024e6eae --- /dev/null +++ b/test/cxxbridge/cxxbridge_circular/rust/src/lib.rs @@ -0,0 +1,34 @@ +#[cxx::bridge] +pub mod ffi { + #[derive(Debug, PartialEq)] + pub struct Rgba { + r: f32, + g: f32, + b: f32, + a: f32, + } + + #[derive(Debug, PartialEq)] + pub struct RsImage { + width: usize, + height: usize, + raster: Rgba, + } + unsafe extern "C++" { + include!("cpplib.h"); + pub fn read_image(path: &str) -> RsImage; + } + + extern "Rust" { + pub fn equal_to(self: &RsImage, other: &str) -> bool; + } +} + +use ffi::*; + +impl RsImage { + pub fn equal_to(&self, path: &str) -> bool { + println!("equal_to"); + *self == read_image(path) + } +} diff --git a/test/cxxbridge/cxxbridge_rust2cpp/CMakeLists.txt b/test/cxxbridge/cxxbridge_rust2cpp/CMakeLists.txt index b8979b74..0111d546 100644 --- a/test/cxxbridge/cxxbridge_rust2cpp/CMakeLists.txt +++ b/test/cxxbridge/cxxbridge_rust2cpp/CMakeLists.txt @@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED 1) corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml) -corrosion_add_cxxbridge(cxxbridge-cpp CRATE cxxbridge_crate MANIFEST_PATH rust FILES lib.rs foo/mod.rs) +corrosion_add_cxxbridge(cxxbridge-cpp CRATE cxxbridge_crate FILES lib.rs foo/mod.rs) add_executable(cxxbridge-exe main.cpp) target_link_libraries(cxxbridge-exe PUBLIC cxxbridge-cpp) diff --git a/test/find_rust/CMakeLists.txt b/test/find_rust/CMakeLists.txt index 1dd96323..482e5aef 100644 --- a/test/find_rust/CMakeLists.txt +++ b/test/find_rust/CMakeLists.txt @@ -1,3 +1,7 @@ corrosion_tests_add_test(find_rust "") corrosion_tests_add_test(rustup_proxy "") +find_program(rustup rustup) +if(NOT rustup) + set_tests_properties(rustup_proxy_build rustup_proxy_cleanup PROPERTIES DISABLED 1) +endif() diff --git a/test/output directory/CMakeLists.txt b/test/output directory/CMakeLists.txt index 0f5cf723..07b67ffa 100644 --- a/test/output directory/CMakeLists.txt +++ b/test/output directory/CMakeLists.txt @@ -40,6 +40,14 @@ if(CORROSION_TESTS_INSTALL_CORROSION) set_tests_properties("output_directory_build" PROPERTIES FIXTURES_REQUIRED "fixture_corrosion_install") endif() +get_cmake_property(IS_MULTI_CONFIG GENERATOR_IS_MULTI_CONFIG) + +if (IS_MULTI_CONFIG) + set(config_path_str "$/") +else() + set(config_path_str "") +endif() + foreach(output_approach targetprop var targetprop_pdb_fallback) if(output_approach STREQUAL "targetprop") set(rust_proj_suffix "1") @@ -56,8 +64,8 @@ foreach(output_approach targetprop var targetprop_pdb_fallback) add_test(NAME output_directory_bin_${output_approach} COMMAND "${CMAKE_COMMAND}" - -P "${CMAKE_CURRENT_SOURCE_DIR}/TestFileExists.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/build/custom_bin_${output_approach}/${bin_name}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/../TestFileExists.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/build/custom_bin_${output_approach}/${config_path_str}${bin_name}" ) set_tests_properties("output_directory_bin_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory") @@ -68,8 +76,8 @@ foreach(output_approach targetprop var targetprop_pdb_fallback) add_test(NAME output_directory_staticlib_${output_approach} COMMAND "${CMAKE_COMMAND}" - -P "${CMAKE_CURRENT_SOURCE_DIR}/TestFileExists.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/build/custom_archive_${output_approach}/${static_lib_name}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/../TestFileExists.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/build/custom_archive_${output_approach}/${config_path_str}${static_lib_name}" ) set_tests_properties("output_directory_staticlib_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory") @@ -84,8 +92,8 @@ foreach(output_approach targetprop var targetprop_pdb_fallback) add_test(NAME output_directory_cdylib_${output_approach} COMMAND "${CMAKE_COMMAND}" - -P "${CMAKE_CURRENT_SOURCE_DIR}/TestFileExists.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/build/custom_lib_${output_approach}/${dynamic_lib_name}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/../TestFileExists.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/build/custom_lib_${output_approach}/${config_path_str}${dynamic_lib_name}" ) set_tests_properties("output_directory_cdylib_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory") @@ -95,10 +103,10 @@ foreach(output_approach targetprop var targetprop_pdb_fallback) add_test(NAME output_directory_implib_${output_approach} COMMAND "${CMAKE_COMMAND}" - -P "${CMAKE_CURRENT_SOURCE_DIR}/TestFileExists.cmake" + -P "${CMAKE_CURRENT_SOURCE_DIR}/../TestFileExists.cmake" # Implib is an ARCHIVE artifact, see: # https://cmake.org/cmake/help/v3.25/manual/cmake-buildsystem.7.html#archive-output-artifacts - "${CMAKE_CURRENT_BINARY_DIR}/build/custom_archive_${output_approach}/${implib_name}" + "${CMAKE_CURRENT_BINARY_DIR}/build/custom_archive_${output_approach}/${config_path_str}${implib_name}" ) set_tests_properties("output_directory_implib_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory") @@ -122,8 +130,8 @@ foreach(output_approach targetprop var targetprop_pdb_fallback) add_test(NAME output_directory_cdylib_pdb_${output_approach} COMMAND "${CMAKE_COMMAND}" - -P "${CMAKE_CURRENT_SOURCE_DIR}/TestFileExists.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/build/${expected_lib_pdb_path}/${lib_pdb_name}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/../TestFileExists.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/build/${expected_lib_pdb_path}/${config_path_str}${lib_pdb_name}" ) set_tests_properties("output_directory_cdylib_pdb_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory") @@ -131,8 +139,8 @@ foreach(output_approach targetprop var targetprop_pdb_fallback) add_test(NAME output_directory_bin_pdb_${output_approach} COMMAND "${CMAKE_COMMAND}" - -P "${CMAKE_CURRENT_SOURCE_DIR}/TestFileExists.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/build/${expected_bin_pdb_path}/${bin_pdb_name}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/../TestFileExists.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/build/${expected_bin_pdb_path}/${config_path_str}${bin_pdb_name}" ) set_tests_properties("output_directory_bin_pdb_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory") endif() @@ -143,7 +151,7 @@ endforeach() add_test(NAME postbuild_custom_command COMMAND "${CMAKE_COMMAND}" - -P "${CMAKE_CURRENT_SOURCE_DIR}/TestFileExists.cmake" + -P "${CMAKE_CURRENT_SOURCE_DIR}/../TestFileExists.cmake" "${CMAKE_CURRENT_BINARY_DIR}/build/another_dir/moved_bin" ) set_tests_properties("postbuild_custom_command" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory") diff --git a/test/output directory/TestFileExists.cmake b/test/output directory/TestFileExists.cmake deleted file mode 100644 index cfc33f2d..00000000 --- a/test/output directory/TestFileExists.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# CMake script to test if a file exists. Errors if the file does not exist. -# Expect actual arguments to start at index 3 (cmake -P ) - -# Expect one argument -if(NOT (CMAKE_ARGC EQUAL "4")) - message(FATAL_ERROR "Test Internal Error: Unexpected ARGC Value: ${CMAKE_ARGC}.") -endif() - -set(FILE_PATH "${CMAKE_ARGV3}") - -if(NOT ( EXISTS "${FILE_PATH}" )) - message(FATAL_ERROR "Test failed: File `${FILE_PATH}` does not exist.") -endif() diff --git a/test/output directory/output directory/CMakeLists.txt b/test/output directory/output directory/CMakeLists.txt index 9fbe6415..e0d29a3d 100644 --- a/test/output directory/output directory/CMakeLists.txt +++ b/test/output directory/output directory/CMakeLists.txt @@ -22,7 +22,7 @@ add_custom_command(TARGET cargo-build_rust_bin1 POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/another_dir" COMMAND - ${CMAKE_COMMAND} -E copy_if_different "$" "${CMAKE_CURRENT_BINARY_DIR}/another_dir/moved_bin" + ${CMAKE_COMMAND} -E copy_if_different "$>" "${CMAKE_CURRENT_BINARY_DIR}/another_dir/moved_bin" ) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_bin_var") diff --git a/test/parse_target_triple/CMakeLists.txt b/test/parse_target_triple/CMakeLists.txt index f98dd184..f9924242 100644 --- a/test/parse_target_triple/CMakeLists.txt +++ b/test/parse_target_triple/CMakeLists.txt @@ -2,9 +2,9 @@ corrosion_tests_add_test(parse_target_triple "") corrosion_tests_add_test(parse_target_triple_should_fail "") set_tests_properties("parse_target_triple_build" PROPERTIES FAIL_REGULAR_EXPRESSION - "CMake Warning" + "CMake Warning at [^\r\n]*FindRust\.cmake:.*(\r)?\n[ \t]*Failed to parse target-triple `" ) set_tests_properties("parse_target_triple_should_fail_build" PROPERTIES PASS_REGULAR_EXPRESSION - "CMake Warning" - ) \ No newline at end of file + "CMake Warning at [^\r\n]*FindRust\.cmake:.*(\r)?\n[ \t]*Failed to parse target-triple `" + )