Add corrosion as a subdirectory, CMake fixes transitional_engine
authorunC0Rr
Wed, 28 Aug 2024 15:31:51 +0200
branchtransitional_engine
changeset 16021 6a3dc15b78b9
parent 16009 7544a7d7c819
child 16022 2003b466b279
Add corrosion as a subdirectory, CMake fixes
CMakeLists.txt
hedgewars/CMakeLists.txt
hedgewars/uLandUtils.pas
rust/lib-hwengine-future/Cargo.toml
tools/corrosion/.github/FUNDING.yml
tools/corrosion/.github/ISSUE_TEMPLATE/bug_report.yml
tools/corrosion/.github/actions/setup_test/action.yaml
tools/corrosion/.github/scripts/determine_compiler.sh
tools/corrosion/.github/workflows/gh-pages.yaml
tools/corrosion/.github/workflows/test.yaml
tools/corrosion/.github/workflows/test_legacy.yaml
tools/corrosion/.gitignore
tools/corrosion/CMakeLists.txt
tools/corrosion/LICENSE
tools/corrosion/README.md
tools/corrosion/RELEASES.md
tools/corrosion/cmake/Corrosion.cmake
tools/corrosion/cmake/CorrosionConfig.cmake.in
tools/corrosion/cmake/CorrosionGenerator.cmake
tools/corrosion/cmake/FindRust.cmake
tools/corrosion/doc/.gitignore
tools/corrosion/doc/book.toml
tools/corrosion/doc/src/SUMMARY.md
tools/corrosion/doc/src/advanced.md
tools/corrosion/doc/src/common_issues.md
tools/corrosion/doc/src/ffi_bindings.md
tools/corrosion/doc/src/introduction.md
tools/corrosion/doc/src/quick_start.md
tools/corrosion/doc/src/setup_corrosion.md
tools/corrosion/doc/src/usage.md
tools/corrosion/generator/CMakeLists.txt
tools/corrosion/generator/Cargo.toml
tools/corrosion/generator/Compat.Cargo.toml
tools/corrosion/generator/src/main.rs
tools/corrosion/generator/src/subcommands/gen_cmake.rs
tools/corrosion/generator/src/subcommands/gen_cmake/target.rs
tools/corrosion/test/CMakeLists.txt
tools/corrosion/test/ConfigureAndBuild.cmake
tools/corrosion/test/README.md
tools/corrosion/test/cargo_flags/CMakeLists.txt
tools/corrosion/test/cargo_flags/cargo_flags/CMakeLists.txt
tools/corrosion/test/cargo_flags/cargo_flags/main.cpp
tools/corrosion/test/cargo_flags/cargo_flags/rust/Cargo.toml
tools/corrosion/test/cargo_flags/cargo_flags/rust/src/lib.rs
tools/corrosion/test/cbindgen/CMakeLists.txt
tools/corrosion/test/cbindgen/rust2cpp/CMakeLists.txt
tools/corrosion/test/cbindgen/rust2cpp/main.cpp
tools/corrosion/test/cbindgen/rust2cpp/rust/Cargo.toml
tools/corrosion/test/cbindgen/rust2cpp/rust/cbindgen.toml
tools/corrosion/test/cbindgen/rust2cpp/rust/src/lib.rs
tools/corrosion/test/cpp2rust/CMakeLists.txt
tools/corrosion/test/cpp2rust/cpp2rust/CMakeLists.txt
tools/corrosion/test/cpp2rust/cpp2rust/lib.cpp
tools/corrosion/test/cpp2rust/cpp2rust/lib2.cpp
tools/corrosion/test/cpp2rust/cpp2rust/path with space/lib3.cpp
tools/corrosion/test/cpp2rust/cpp2rust/rust/Cargo.toml
tools/corrosion/test/cpp2rust/cpp2rust/rust/build.rs
tools/corrosion/test/cpp2rust/cpp2rust/rust/rust_dependency/Cargo.toml
tools/corrosion/test/cpp2rust/cpp2rust/rust/rust_dependency/src/lib.rs
tools/corrosion/test/crate_type/CMakeLists.txt
tools/corrosion/test/crate_type/crate_type/CMakeLists.txt
tools/corrosion/test/crate_type/crate_type/main.cpp
tools/corrosion/test/crate_type/crate_type/proj1/Cargo.toml
tools/corrosion/test/crate_type/crate_type/proj1/src/lib.rs
tools/corrosion/test/crate_type/crate_type/proj2/Cargo.toml
tools/corrosion/test/crate_type/crate_type/proj2/src/lib.rs
tools/corrosion/test/custom_profiles/CMakeLists.txt
tools/corrosion/test/custom_profiles/basic_profiles/CMakeLists.txt
tools/corrosion/test/custom_profiles/basic_profiles/main.cpp
tools/corrosion/test/custom_profiles/basic_profiles/rust/Cargo.toml
tools/corrosion/test/custom_profiles/basic_profiles/rust/src/lib.rs
tools/corrosion/test/custom_profiles/custom_profiles/CMakeLists.txt
tools/corrosion/test/custom_profiles/custom_profiles/main.cpp
tools/corrosion/test/custom_profiles/custom_profiles/rust/Cargo.toml
tools/corrosion/test/custom_profiles/custom_profiles/rust/src/lib.rs
tools/corrosion/test/cxxbridge/CMakeLists.txt
tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/CMakeLists.txt
tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/cpplib.cpp
tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/include/cpplib.h
tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/rust/Cargo.toml
tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/rust/src/lib.rs
tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/rust/src/main.rs
tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/CMakeLists.txt
tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/main.cpp
tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/rust/Cargo.toml
tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/rust/src/foo/mod.rs
tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/rust/src/lib.rs
tools/corrosion/test/envvar/CMakeLists.txt
tools/corrosion/test/envvar/envvar/.cargo/config.toml
tools/corrosion/test/envvar/envvar/CMakeLists.txt
tools/corrosion/test/envvar/envvar/Cargo.toml
tools/corrosion/test/envvar/envvar/build.rs
tools/corrosion/test/envvar/envvar/main.cpp
tools/corrosion/test/envvar/envvar/src/lib.rs
tools/corrosion/test/external_corrosion_generator/CMakeLists.txt
tools/corrosion/test/external_corrosion_generator/ExternalCorrosionGenerator/CMakeLists.txt
tools/corrosion/test/external_corrosion_generator/ExternalCorrosionGenerator/Test.cmake
tools/corrosion/test/features/CMakeLists.txt
tools/corrosion/test/features/features/CMakeLists.txt
tools/corrosion/test/features/features/main.cpp
tools/corrosion/test/features/features/rust/Cargo.toml
tools/corrosion/test/features/features/rust/src/lib.rs
tools/corrosion/test/find_rust/CMakeLists.txt
tools/corrosion/test/find_rust/find_rust/CMakeLists.txt
tools/corrosion/test/find_rust/rustup_proxy/CMakeLists.txt
tools/corrosion/test/gensource/CMakeLists.txt
tools/corrosion/test/gensource/gensource/.gitignore
tools/corrosion/test/gensource/gensource/CMakeLists.txt
tools/corrosion/test/gensource/gensource/Cargo.toml
tools/corrosion/test/gensource/gensource/generator/CMakeLists.txt
tools/corrosion/test/gensource/gensource/generator/Cargo.toml
tools/corrosion/test/gensource/gensource/generator/src/main.rs
tools/corrosion/test/gensource/gensource/src/lib.rs
tools/corrosion/test/hostbuild/CMakeLists.txt
tools/corrosion/test/hostbuild/hostbuild/CMakeLists.txt
tools/corrosion/test/hostbuild/hostbuild/Cargo.toml
tools/corrosion/test/hostbuild/hostbuild/build.rs
tools/corrosion/test/hostbuild/hostbuild/src/lib.c
tools/corrosion/test/hostbuild/hostbuild/src/main.rs
tools/corrosion/test/multitarget/CMakeLists.txt
tools/corrosion/test/multitarget/multitarget/CMakeLists.txt
tools/corrosion/test/multitarget/multitarget/Cargo.toml
tools/corrosion/test/multitarget/multitarget/lib.cpp
tools/corrosion/test/multitarget/multitarget/src/lib.rs
tools/corrosion/test/nostd/CMakeLists.txt
tools/corrosion/test/nostd/nostd/CMakeLists.txt
tools/corrosion/test/nostd/nostd/main.cpp
tools/corrosion/test/nostd/nostd/rust/Cargo.toml
tools/corrosion/test/nostd/nostd/rust/src/lib.rs
tools/corrosion/test/output directory/CMakeLists.txt
tools/corrosion/test/output directory/TestFileExists.cmake
tools/corrosion/test/output directory/output directory/CMakeLists.txt
tools/corrosion/test/output directory/output directory/consumer.cpp
tools/corrosion/test/output directory/output directory/proj1/Cargo.toml
tools/corrosion/test/output directory/output directory/proj1/src/lib.rs
tools/corrosion/test/output directory/output directory/proj2/Cargo.toml
tools/corrosion/test/output directory/output directory/proj2/src/lib.rs
tools/corrosion/test/parse_target_triple/CMakeLists.txt
tools/corrosion/test/parse_target_triple/parse_target_triple/CMakeLists.txt
tools/corrosion/test/parse_target_triple/parse_target_triple_should_fail/CMakeLists.txt
tools/corrosion/test/rust2cpp/CMakeLists.txt
tools/corrosion/test/rust2cpp/rust2cpp/CMakeLists.txt
tools/corrosion/test/rust2cpp/rust2cpp/main.cpp
tools/corrosion/test/rust2cpp/rust2cpp/rust/Cargo.toml
tools/corrosion/test/rust2cpp/rust2cpp/rust/build.rs
tools/corrosion/test/rust2cpp/rust2cpp/rust/src/lib.rs
tools/corrosion/test/rustflags/CMakeLists.txt
tools/corrosion/test/rustflags/cargo_config_rustflags/.cargo/config.toml
tools/corrosion/test/rustflags/cargo_config_rustflags/CMakeLists.txt
tools/corrosion/test/rustflags/cargo_config_rustflags/Cargo.toml
tools/corrosion/test/rustflags/cargo_config_rustflags/src/main.rs
tools/corrosion/test/rustflags/rustflags/CMakeLists.txt
tools/corrosion/test/rustflags/rustflags/main.cpp
tools/corrosion/test/rustflags/rustflags/rust/Cargo.toml
tools/corrosion/test/rustflags/rustflags/rust/some_dependency/Cargo.toml
tools/corrosion/test/rustflags/rustflags/rust/some_dependency/src/lib.rs
tools/corrosion/test/rustflags/rustflags/rust/src/lib.rs
tools/corrosion/test/workspace/CMakeLists.txt
tools/corrosion/test/workspace/workspace/CMakeLists.txt
tools/corrosion/test/workspace/workspace/Cargo.toml
tools/corrosion/test/workspace/workspace/main.cpp
tools/corrosion/test/workspace/workspace/member1/Cargo.toml
tools/corrosion/test/workspace/workspace/member1/src/lib.rs
tools/corrosion/test/workspace/workspace/member2/Cargo.toml
tools/corrosion/test/workspace/workspace/member2/src/lib.rs
tools/corrosion/test/workspace/workspace/member3/Cargo.toml
tools/corrosion/test/workspace/workspace/member3/src/main.rs
--- a/CMakeLists.txt	Fri Jan 19 13:07:22 2024 +0100
+++ b/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -1,9 +1,7 @@
-cmake_minimum_required(VERSION 2.6.4)
-
-project(hedgewars)
+cmake_minimum_required(VERSION 3.12.0)
 
 #initialise cmake environment
-foreach(hwpolicy CMP0003 CMP0012 CMP0017 CMP0018)
+foreach(hwpolicy CMP0003 CMP0012 CMP0017 CMP0018 CMP0048)
     if(POLICY ${hwpolicy})
         cmake_policy(SET ${hwpolicy} NEW)
     endif()
@@ -15,6 +13,8 @@
     endif()
 endforeach()
 
+project(hedgewars VERSION 1.1.0)
+
 set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules")
 include(${CMAKE_MODULE_PATH}/utils.cmake)
 
@@ -89,14 +89,11 @@
 set(FONTS_DIRS "" CACHE STRING "Additional paths to folders where required fonts can be found ( ; is separator)")
 
 #versioning
-set(CPACK_PACKAGE_VERSION_MAJOR 1)
-set(CPACK_PACKAGE_VERSION_MINOR 1)
-set(CPACK_PACKAGE_VERSION_PATCH 0)
 set(HEDGEWARS_PROTO_VER 60)
 if((CMAKE_BUILD_TYPE STREQUAL "Release") OR (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
-    set(HEDGEWARS_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
+    set(HEDGEWARS_VERSION "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}")
 else()
-    set(HEDGEWARS_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}-dev")
+    set(HEDGEWARS_VERSION "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}-dev")
 endif()
 include(${CMAKE_MODULE_PATH}/revinfo.cmake)
 
@@ -109,8 +106,6 @@
 #platform specific init code
 include(${CMAKE_MODULE_PATH}/platform.cmake)
 
-add_subdirectory(tools/corrosion)
-
 #when build type is not specified, assume Debug/Release according to build version information
 if(CMAKE_BUILD_TYPE)
     if(NOT((CMAKE_BUILD_TYPE STREQUAL "Release") OR
@@ -266,9 +261,16 @@
 
 find_package_or_disable_msg(LIBAV NOVIDEOREC "Video recording will not be built")
 
+
 #physfs helper library
 add_subdirectory(misc/libphyslayer)
 
+
+#rust libraries
+add_subdirectory(tools/corrosion)
+corrosion_import_crate(MANIFEST_PATH rust/lib-hedgewars-engine/Cargo.toml)
+
+
 #maybe this could be merged inside hedgewars/CMakeLists.txt
 if(BUILD_ENGINE_C)
     #pascal to c converter
--- a/hedgewars/CMakeLists.txt	Fri Jan 19 13:07:22 2024 +0100
+++ b/hedgewars/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -1,10 +1,10 @@
 enable_language(Pascal)
 
 find_package(SDL2 REQUIRED CONFIG)
-find_package(SDL2_image REQUIRED CONFIG)
-find_package(SDL2_net REQUIRED CONFIG)
-find_package(SDL2_ttf REQUIRED CONFIG)
-find_package(SDL2_mixer REQUIRED CONFIG)
+find_package(SDL2_image REQUIRED)
+find_package(SDL2_net REQUIRED)
+find_package(SDL2_ttf REQUIRED)
+find_package(SDL2_mixer REQUIRED)
 
 include(CheckLibraryExists)
 include(${CMAKE_MODULE_PATH}/utils.cmake)
--- a/hedgewars/uLandUtils.pas	Fri Jan 19 13:07:22 2024 +0100
+++ b/hedgewars/uLandUtils.pas	Wed Aug 28 15:31:51 2024 +0200
@@ -20,23 +20,21 @@
 implementation
 uses uUtils, uConsts, uVariables, uTypes;
 
-const LibFutureName = 'hwengine_future';
-
-function  create_empty_game_field(width, height: Longword): pointer; cdecl; external LibFutureName;
-procedure get_game_field_parameters(game_field: pointer; var width: LongInt; var height: LongInt; var play_width: LongInt; var play_height: LongInt); cdecl; external LibFutureName;
-procedure dispose_game_field(game_field: pointer); cdecl; external LibFutureName;
+function  create_empty_game_field(width, height: Longword): pointer; cdecl; external;
+procedure get_game_field_parameters(game_field: pointer; var width: LongInt; var height: LongInt; var play_width: LongInt; var play_height: LongInt); cdecl; external;
+procedure dispose_game_field(game_field: pointer); cdecl; external;
 
-function  land_get(game_field: pointer; x, y: LongInt): Word; cdecl; external LibFutureName;
-procedure land_set(game_field: pointer; x, y: LongInt; value: Word); cdecl; external LibFutureName;
-function  land_row(game_field: pointer; row: LongInt): PWordArray; cdecl; external LibFutureName;
-procedure land_fill(game_field: pointer; x, y: LongInt; border, fill: Word); cdecl; external LibFutureName;
+function  land_get(game_field: pointer; x, y: LongInt): Word; cdecl; external;
+procedure land_set(game_field: pointer; x, y: LongInt; value: Word); cdecl; external;
+function  land_row(game_field: pointer; row: LongInt): PWordArray; cdecl; external;
+procedure land_fill(game_field: pointer; x, y: LongInt; border, fill: Word); cdecl; external;
 
-function  land_pixel_get(game_field: pointer; x, y: LongInt): Longword; cdecl; external LibFutureName;
-procedure land_pixel_set(game_field: pointer; x, y: LongInt; value: Longword); cdecl; external LibFutureName;
-function  land_pixel_row(game_field: pointer; row: LongInt): PLongwordArray; cdecl; external LibFutureName;
+function  land_pixel_get(game_field: pointer; x, y: LongInt): Longword; cdecl; external;
+procedure land_pixel_set(game_field: pointer; x, y: LongInt; value: Longword); cdecl; external;
+function  land_pixel_row(game_field: pointer; row: LongInt): PLongwordArray; cdecl; external;
 
-function  generate_templated_game_field(feature_size: Longword; seed, template_type, data_path: PChar): pointer; cdecl; external LibFutureName;
-procedure apply_theme(game_field: pointer; data_path, theme_name: PChar); cdecl; external LibFutureName;
+function  generate_templated_game_field(feature_size: Longword; seed, template_type, data_path: PChar): pointer; cdecl; external;
+procedure apply_theme(game_field: pointer; data_path, theme_name: PChar); cdecl; external;
 
 var gameField: pointer;
 
--- a/rust/lib-hwengine-future/Cargo.toml	Fri Jan 19 13:07:22 2024 +0100
+++ b/rust/lib-hwengine-future/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -15,4 +15,4 @@
 
 [lib]
 name = "hwengine_future"
-crate-type = ["cdylib"]
+crate-type = ["staticlib"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/.github/FUNDING.yml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,1 @@
+github: ["jschwe"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/.github/ISSUE_TEMPLATE/bug_report.yml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,81 @@
+name: Bug Report
+description: File a bug report
+title: "[Bug]: "
+labels: ["bug", "triage"]
+assignees:
+  - jschwe
+body:
+  - type: markdown
+    attributes:
+      value: |
+        Thanks for taking the time to fill out this bug report!
+  - type: textarea
+    attributes:
+      label: Current Behavior
+      description: A concise description of what you're experiencing.
+    validations:
+      required: false
+  - type: textarea
+    attributes:
+      label: Expected Behavior
+      description: A concise description of what you expected to happen.
+    validations:
+      required: false
+  - type: textarea
+    attributes:
+      label: Steps To Reproduce
+      description: Steps to reproduce the behavior.
+      placeholder: |
+        1. In this environment...
+        2. With this config...
+        3. Run '...'
+        4. See error...
+    validations:
+      required: false
+  - type: textarea
+    attributes:
+      label: Environment
+      description: |
+        examples:
+          - **OS**: Ubuntu 22.04
+          - **CMake**: 3.22.0
+          - **CMake Generator**: Ninja 1.11
+      value: |
+        - OS:
+        - CMake:
+        - CMake Generator:
+      render: markdown
+    validations:
+      required: false
+  - type: textarea
+    attributes:
+      label: CMake configure log with Debug log-level
+      description: |
+        Output when configuring with `cmake -S<source_dir> -B<build_dir> --log-level=DEBUG <your_other_options>`:
+        <details><summary>CMake configure log</summary>
+        <p>
+        
+        ```
+        <log>
+        ```
+        
+        </p>
+        </details>
+    validations:
+      required: false
+  - type: textarea
+    attributes:
+      label: CMake Build step log
+      description: |
+        Output when building with `cmake --build <build_dir> --verbose`:
+        <details><summary>CMake build log</summary>
+        <p>
+
+        ```
+        <log>
+        ```
+
+        </p>
+        </details>
+    validations:
+      required: false
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/.github/actions/setup_test/action.yaml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,184 @@
+name: Setup Corrosion Tests
+description: "Internal helper action to setup the Environment for Corrosions tests"
+inputs:
+  target_arch:
+    required: true
+    description: CMake target architecture
+  abi:
+    required: false
+    description: msvc, gnu or darwin
+    default: default
+  cmake:
+    required: true
+    description: Cmake version
+  rust:
+    required: true
+    description: Rust version
+  generator:
+    required: true
+    description: CMake Generator (e.g Ninja)
+  build_dir:
+    required: true
+    description: Path of the CMake build directory
+  configure_params:
+    required: false
+    description: Additional parameters to pass to CMake configure step
+  install_path:
+    required: false
+    description: CMake install prefix
+    default: ""
+  compiler:
+    required: false
+    description: Compiler to use. Valid options are clang, gcc, cl, default, or an empty string.
+    default: "default"
+
+runs:
+  using: composite
+  steps:
+    - name: Cache Cargo registry
+      id: cache-registry
+      uses: actions/cache@v4
+      with:
+        path: ~/.cargo/registry
+        key: ${{ runner.os }}-cargo-registry
+    - name: Determine Rust OS
+      id: determine_rust_os
+      shell: bash
+      run: |
+        if [ "${{ runner.os }}" == "Windows" ]; then
+          echo "os=pc-windows" >> $GITHUB_OUTPUT
+          echo "host_abi=msvc" >> $GITHUB_OUTPUT
+        elif [ "${{ runner.os }}" == "Linux" ]; then
+          echo "os=unknown-linux" >> $GITHUB_OUTPUT
+          echo "host_abi=gnu" >> $GITHUB_OUTPUT
+        elif [ "${{ runner.os }}" == "macOS" ]; then
+          echo "os=apple" >> $GITHUB_OUTPUT
+          echo "host_abi=darwin" >> $GITHUB_OUTPUT
+        fi
+    - name: Determine Rust ABI
+      id: determine_abi
+      shell: bash
+      run: |
+        if [[ ! ( -z "${{ inputs.abi }}" || "${{ inputs.abi }}" == "default" ) ]]; then
+           echo "abi=${{ inputs.abi }}" >> $GITHUB_OUTPUT
+         elif [ "${{ runner.os }}" == "Linux" ]; then
+           echo "abi=gnu" >> $GITHUB_OUTPUT
+         elif [ "${{ runner.os }}" == "macOS" ]; then
+           echo "abi=darwin" >> $GITHUB_OUTPUT
+         else 
+           echo "abi=msvc" >> $GITHUB_OUTPUT
+         fi
+    - name: Determine if Cross-compiling
+      id: determine_cross_compile
+      shell: bash
+      run: |
+        # For now it is safe to assume that all github runners are x86_64
+        if [[ "${{ inputs.target_arch }}" != "x86_64" ]]; then
+          echo "Cross-Compiling to ${{ inputs.target_arch }}"
+          if [[ "${{ runner.os }}" == "macOS" ]]; then
+            echo "system_name=-DCMAKE_SYSTEM_NAME=Darwin" >> $GITHUB_OUTPUT
+          else
+            # Either `Linux` or `Windows`
+            echo "system_name=-DCMAKE_SYSTEM_NAME=${{ runner.os }}" >> $GITHUB_OUTPUT
+          fi
+        fi
+    - name: Pick Compiler
+      id: pick_compiler
+      shell: bash
+      run: > 
+        ./.github/scripts/determine_compiler.sh 
+        "${{ inputs.compiler }}"
+        "${{ runner.os }}"
+        "${{ steps.determine_abi.outputs.abi }}"
+        "${{steps.determine_cross_compile.outputs.system_name}}"
+        "${{inputs.target_arch}}"
+    - name: Pick Generator
+      id: pick_generator
+      shell: bash
+      run: |
+        if [ "${{ inputs.generator }}" == "ninja" ]; then
+          echo "generator=-GNinja" >> $GITHUB_OUTPUT
+        elif [ "${{ inputs.generator }}" == "ninja-multiconfig" ];then
+          echo "generator=-GNinja Multi-Config" >> $GITHUB_OUTPUT
+        fi
+    - name: Arch Flags
+      id: arch_flags
+      shell: bash
+      run: | # Cross-compiling is currently only supported on Windows+MSVC with the default generator
+        if [ "${{ runner.os }}" == "Windows" ]; then
+          if [ "${{inputs.generator}}" == "default" ]; then
+            if [ "${{ inputs.target_arch }}" == "x86_64" ]; then
+              echo "msvc=amd64" >> $GITHUB_OUTPUT
+              echo "cmake=-Ax64" >> $GITHUB_OUTPUT
+            elif [ "${{ inputs.target_arch }}" == "i686" ]; then
+              echo "msvc=amd64_x86" >> $GITHUB_OUTPUT
+              echo "cmake=-AWin32" >> $GITHUB_OUTPUT
+            elif [ "${{ inputs.target_arch }}" == "aarch64" ]; then
+              echo "msvc=amd64_arm64" >> $GITHUB_OUTPUT
+              echo "cmake=-AARM64" >> $GITHUB_OUTPUT
+            fi
+          elif [ "${{inputs.generator}}" == "ninja" ]; then
+            # We don't do cross-compiling builds with Ninja
+            # Todo: Why not (cross-compile)?
+            echo "msvc=amd64" >> $GITHUB_OUTPUT
+          fi
+        elif [ "${{ runner.os }}" == "Linux" ]; then
+          echo "cmake=-DRust_CARGO_TARGET=${{inputs.target_arch}}-${{steps.determine_rust_os.outputs.os}}-${{steps.determine_abi.outputs.abi}}" >> $GITHUB_OUTPUT
+        fi
+    - name: Determine Install Prefix
+      id: install_prefix
+      shell: bash
+      run: |
+        if [ ! -z "${{ inputs.install_path }}" ]; then
+          echo "install_path=-DCMAKE_INSTALL_PREFIX=${{ inputs.install_path }}" >> $GITHUB_OUTPUT
+        fi
+    - name: Setup MSVC Development Environment
+      uses: ilammy/msvc-dev-cmd@v1
+      with:
+        arch: ${{ steps.arch_flags.outputs.msvc }}
+      if: ${{ 'msvc' == steps.determine_abi.outputs.abi }}
+    - name: Install CMake
+      uses: lukka/get-cmake@519de0c7b4812477d74976b2523a9417f552d126
+      with:
+        cmakeVersion: "${{ inputs.cmake }}"
+        ninjaVersion: "~1.10.0"
+    - name: Install Rust
+      id: install_rust
+      uses: dtolnay/rust-toolchain@master
+      with:
+        toolchain: ${{inputs.rust}}
+        targets: ${{inputs.target_arch}}-${{steps.determine_rust_os.outputs.os}}-${{steps.determine_abi.outputs.abi}}
+    - name: Install Cross Compiler
+      shell: bash
+      run: |
+        if [[ "${{ inputs.target_arch }}" != 'x86_64' ]]; then
+          echo "::group::apt-install"
+          sudo apt-get update
+          sudo apt-get install -y g++-$(echo "${{inputs.target_arch}}" | tr _ -)-linux-gnu
+          echo "::endgroup::"
+        fi
+      if: ${{ 'Linux' == runner.os }}
+    - name: Determine Configure Shell
+      id: configure_shell
+      shell: bash
+      run: |
+        if [ "${{ runner.os }}" == "Windows" ]; then
+            echo "shell=pwsh" >> $GITHUB_OUTPUT
+        else
+            echo "shell=bash" >> $GITHUB_OUTPUT
+        fi
+    - name: Configure
+      shell: ${{steps.configure_shell.outputs.shell}}
+      run: >
+        cmake
+        "-S."
+        "-B${{inputs.build_dir}}"
+        "-DCORROSION_VERBOSE_OUTPUT=ON"
+        "${{steps.arch_flags.outputs.cmake}}"
+        "${{steps.pick_compiler.outputs.c_compiler}}"
+        "${{steps.pick_compiler.outputs.cxx_compiler}}"
+        "${{steps.determine_cross_compile.outputs.system_name}}"
+        "${{steps.pick_generator.outputs.generator}}"
+        ${{steps.install_prefix.outputs.install_path}}
+        "-DRust_TOOLCHAIN=${{steps.install_rust.outputs.name}}"
+        ${{ inputs.configure_params }}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/.github/scripts/determine_compiler.sh	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+
+compiler_kind="$1"
+runner_os="$2"
+target_abi="$3"
+target_system_name="$4"
+target_arch="$5"
+
+set -e
+
+if [[ -z "$GITHUB_OUTPUT" ]]; then
+  echo "Error: This script should only be run in github actions environment"
+  exit 1
+fi
+if [[ -z "${runner_os}" || -z "${target_abi}" || -z  "${target_arch}" ]]; then
+  echo "Error: Not all required parameters where set"
+  exit 1
+fi
+if [[ -z "${compiler_kind}" || "${compiler_kind}" == "default" ]]; then
+  echo "compiler option was not set. Determining default compiler."
+  if [[ "${runner_os}" == "Windows" ]]; then
+    if [[ "${target_abi}" == "msvc" ]]; then
+      compiler_kind=msvc
+    elif [[ "${target_abi}" == "gnu" ]]; then
+      compiler_kind=gcc
+    else
+      echo "Unknown abi for Windows: ${target_abi}"
+      exit 1
+    fi
+  elif [[ "${runner_os}" == "macOS" ]]; then
+    compiler_kind="clang"
+  elif [[ "${runner_os}" == "Linux" ]]; then
+    compiler_kind="gcc"
+  else
+    echo "Unknown Runner OS: ${runner_os}"
+    exit 1
+  fi
+fi
+echo "Compiler Family: '${compiler_kind}'"
+
+if [[ "${compiler_kind}" == "clang" ]]; then
+  c_compiler="clang"
+  cxx_compiler="clang++"
+elif [[ "${compiler_kind}" == "msvc" ]]; then
+  c_compiler="cl"
+  cxx_compiler="cl"
+elif [[ "${compiler_kind}" == "gcc" ]]; then
+  if [[ -z "${target_system_name}" ]]; then
+    c_compiler="gcc"
+    cxx_compiler="g++"
+  else
+    c_compiler="${target_arch}-linux-gnu-gcc"
+    cxx_compiler="${target_arch}-linux-gnu-g++"
+  fi
+fi
+echo "Chose C compiler: '${c_compiler}'"
+echo "Chose C++ compiler: '${cxx_compiler}'"
+echo "c_compiler=-DCMAKE_C_COMPILER=${c_compiler}" >> $GITHUB_OUTPUT
+echo "cxx_compiler=-DCMAKE_CXX_COMPILER=${cxx_compiler}" >> $GITHUB_OUTPUT
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/.github/workflows/gh-pages.yaml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,62 @@
+name: Deploy GH pages
+on:
+  push:
+    branches:
+      - master
+  # Allows you to run this workflow manually from the Actions tab
+  workflow_dispatch:
+
+# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
+permissions:
+  contents: read
+  pages: write
+  id-token: write
+
+# Allow one concurrent deployment
+concurrency:
+  group: "pages"
+  cancel-in-progress: true
+
+jobs:
+  deploy:
+    runs-on: ubuntu-latest
+    environment:
+      name: github-pages
+      url: ${{ steps.deployment.outputs.page_url }}
+    steps:
+      - uses: actions/checkout@v4
+      - name: Setup Pages
+        uses: actions/configure-pages@v3
+      - name: Install mdbook
+        env:
+          MDBOOK_VERSION: 'v0.4.27'
+        run: |
+          mkdir mdbook
+          curl -sSL https://github.com/rust-lang/mdBook/releases/download/${MDBOOK_VERSION}/mdbook-${MDBOOK_VERSION}-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
+          echo `pwd`/mdbook >> $GITHUB_PATH
+      - name: Build mdbook
+        run: |
+          cd doc
+          mdbook build
+      # Override mdbooks default highlight.js with a custom version containing CMake support.
+      - uses: actions/checkout@v4
+        with:
+          repository: 'highlightjs/highlight.js'
+          # mdbook currently (as of v0.4.27) does not support v11 yet.
+          ref: '10.7.3'
+          path: highlightjs
+      - name: Build custom highlight.js
+        run: |
+          npm install
+          node tools/build.js :common cmake yaml
+        working-directory: highlightjs
+      - name: Override highlightjs
+        run: |
+          cp highlightjs/build/highlight.min.js doc/book/highlight.js
+      - name: Upload artifact
+        uses: actions/upload-pages-artifact@v1
+        with:
+          path: 'doc/book'
+      - name: Deploy to GitHub Pages
+        id: deployment
+        uses: actions/deploy-pages@v1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/.github/workflows/test.yaml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,356 @@
+name: Tests
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    branches:
+      - 'master'
+      - 'stable/**'
+jobs:
+
+  test_legacy_linux:
+    name: Test Corrosion (CMake 3.15)
+    uses: ./.github/workflows/test_legacy.yaml
+    with :
+      os: ubuntu-20.04
+      rust: 1.46.0
+  test_legacy_mac:
+    name: Test Corrosion (CMake 3.15)
+    uses: ./.github/workflows/test_legacy.yaml
+    with:
+      os: macos-12
+      rust: 1.54.0
+  test_legacy_windows:
+    name: Test Corrosion (CMake 3.15)
+    uses: ./.github/workflows/test_legacy.yaml
+    with:
+      os: windows-2019
+      rust: 1.46.0
+
+  test_legacy_stable:
+    name: Legacy CMake + stable Rust
+    uses: ./.github/workflows/test_legacy.yaml
+    strategy:
+      fail-fast: false
+      matrix:
+        os:
+          - windows-2019 # windows-latest is currently not having a supported MSVC compiler
+          - ubuntu-20.04
+          - macos-12
+    with:
+      os: ${{ matrix.os }}
+      rust: stable
+
+  test_legacy_nightly:
+    name: Legacy CMake + nightly Rust
+    uses: ./.github/workflows/test_legacy.yaml
+    with:
+      os: ubuntu-20.04
+      rust: nightly
+
+  test_legacy_new_lockfile_msrv:
+    name: Test MSRV of the new lockfile
+    runs-on: ubuntu-20.04
+    steps:
+      - uses: actions/checkout@v4
+      - name: Install Rust
+        id: install_rust
+        uses: dtolnay/rust-toolchain@1.56
+      - name: Test Generator build with MSRV
+        run: cargo build
+        working-directory: generator
+
+  test:
+    name: Test Corrosion
+    runs-on: ${{ matrix.os }}
+    continue-on-error: ${{ matrix.rust == 'nightly' }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os:
+          - windows-2019 # windows-latest is currently not having a supported MSVC compiler
+          - ubuntu-latest
+          - macos-12
+        arch:
+          - x86_64
+          - i686
+          - aarch64
+          - powerpc64le
+        abi:
+          - gnu
+          - darwin
+          - msvc
+        cmake:
+          - 3.19.0
+        rust:
+          # Our MSRV is already tested with the legacy generator, so just test the current stable rust here.
+          - stable
+        generator:
+          - default # This is just whatever the platform default is
+          - ninja
+        compiler: [default]
+        include:
+          - rust: nightly
+            cmake: 3.19.0
+            generator: ninja
+            arch: x86_64
+            abi: msvc
+            os: windows-2019
+          - rust: nightly
+            cmake: 3.19.0
+            generator: ninja
+            arch: x86_64
+            abi: gnu
+            os: ubuntu-latest
+          - rust: nightly
+            cmake: 3.19.0
+            generator: ninja
+            arch: x86_64
+            abi: darwin
+            os: macos-12
+          - rust: 1.54
+            cmake: 3.19.0
+            generator: ninja
+            arch: x86_64
+            abi: msvc
+            os: windows-2019
+            compiler: clang
+          - os: ubuntu-latest
+            arch: x86_64
+            abi: gnu
+            cmake: 3.20.0
+            rust: 1.54
+            generator: ninja-multiconfig
+
+        exclude:
+
+          # We have a separate test Matrix for the Visual Studio Generator
+          - os: windows-2019
+            generator: default # Default generator is Visual Studio
+
+          # ARCH
+          - os: windows-2019
+            arch: i686
+            abi: gnu
+          - os: windows-2019
+            arch: aarch64
+            abi: gnu
+          - os: windows-2019
+            arch: i686
+            generator: ninja
+          - os: windows-2019
+            arch: aarch64
+            generator: ninja
+          - os: windows-2019
+            arch: powerpc64le
+          - os: macos-12
+            arch: i686
+          - os: macos-12
+            arch: aarch64
+          - os: macos-12
+            arch: powerpc64le
+
+          # ABI
+          - os: ubuntu-latest
+            abi: msvc
+          - os: ubuntu-latest
+            abi: darwin
+          - os: windows-2019
+            abi: darwin
+          - os: macos-12
+            abi: msvc
+          - os: macos-12
+            abi: gnu
+
+    steps:
+      - uses: actions/checkout@v4
+      - name: Setup Environment and Configure CMake
+        uses: "./.github/actions/setup_test"
+        with:
+          target_arch: ${{matrix.arch}}
+          abi: ${{matrix.abi}}
+          cmake: ${{matrix.cmake}}
+          rust: ${{matrix.rust}}
+          generator: ${{matrix.generator}}
+          build_dir: build
+          compiler: ${{matrix.compiler}}
+      - name: Run Tests
+        id: run_tests
+        working-directory: build
+        run: ctest --output-on-failure --build-config Debug -j 3
+
+  test_msvc:
+    name: Test MSVC Generator
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os:
+          - windows-2019
+          - windows-2022
+        arch:
+          - x86_64
+          - i686
+          - aarch64
+        include:
+          - rust: 1.54.0
+          # Override rust version for x86_64
+          - arch: x86_64
+            rust: 1.46.0
+          - os: windows-2019
+            cmake: 3.20.6  # Multi-config Generators require at least CMake 3.20
+          - os: windows-2022
+            cmake: 3.21.5 # VS on windows-2022 requires at least CMake 3.21
+
+    steps:
+      - uses: actions/checkout@v4
+      # 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: ${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.rust }}-msvc-build
+      - name: Setup Environment and Configure CMake
+        uses: "./.github/actions/setup_test"
+        with:
+          target_arch: ${{matrix.arch}}
+          abi: msvc
+          cmake: ${{matrix.cmake}}
+          rust: ${{matrix.rust}}
+          generator: default
+          build_dir: build
+          configure_params: "-DCORROSION_TESTS_KEEP_BUILDDIRS=ON"
+      - name: Run Tests
+        working-directory: build
+        run: ctest --output-on-failure --build-config Debug -j 3
+
+  test_cxxbridge:
+    name: Test cxxbridge integration
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os:
+          - windows-2019
+          - ubuntu-latest
+          - macos-12
+        include:
+          - abi: default
+        #  - os: windows-2019
+        #    abi: gnu
+    steps:
+      - uses: actions/checkout@v4
+      - uses: actions/cache@v4
+        id: cache_cxxbridge
+        with:
+          path: "~/.cargo/bin/cxxbridge*"
+          key: ${{ runner.os }}-cxxbridge_1_0_86
+      - name: Install cxxbridge
+        if: steps.cache_cxxbridge.outputs.cache-hit != 'true'
+        run: cargo install cxxbridge-cmd@1.0.86
+      - name: Install lld
+        run: sudo apt update && sudo apt install -y lld
+        if: ${{ 'Linux' == runner.os }}
+      - name: Setup Environment and Configure CMake
+        uses: "./.github/actions/setup_test"
+        with:
+          target_arch: x86_64
+          cmake: 3.15.7
+          rust: stable minus 2 releases
+          abi: ${{ matrix.abi }}
+          generator: ninja
+          build_dir: build
+          configure_params: -DCORROSION_TESTS_CXXBRIDGE=ON
+      - name: Run Tests
+        working-directory: build
+        run: ctest --output-on-failure --build-config Debug -j 3 -R "^cxxbridge"
+  install:
+    name: Test Corrosion as a Library
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os:
+          - windows-2019
+          - ubuntu-latest
+          - macos-12
+        include:
+          - rust: 1.46.0
+          - os: macos-12
+            rust: 1.54.0  # On MacOS-12 linking fails before Rust 1.54
+    steps:
+      - uses: actions/checkout@v4
+      - name: Setup MSVC Development Environment
+        uses: ilammy/msvc-dev-cmd@v1
+        if: runner.os == 'Windows'
+      - name: Install CMake
+        uses: lukka/get-cmake@519de0c7b4812477d74976b2523a9417f552d126
+        with:
+          cmakeVersion: "~3.18.0"
+          ninjaVersion: "~1.10.0"
+      - name: Install Rust
+        uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: ${{matrix.rust}}
+      - name: CMake Version
+        run: cmake --version
+      - name: Rust Version
+        run: rustc --version
+      - name: Test Corrosion as subdirectory
+        run: >
+          cmake
+          -S.
+          -Bbuild
+          -GNinja
+          -DCORROSION_VERBOSE_OUTPUT=ON
+          -DCORROSION_TESTS_INSTALL_CORROSION=OFF
+          &&
+          cd build
+          &&
+          ctest --output-on-failure -C Debug -j 3
+      - name: Test Corrosion as installed module
+        run: >
+          cmake -E remove_directory build
+          &&
+          cmake
+          -S.
+          -Bbuild
+          -GNinja
+          -DCORROSION_VERBOSE_OUTPUT=ON
+          -DCMAKE_BUILD_TYPE=Release
+          -DCORROSION_TESTS_INSTALL_CORROSION=ON
+          &&
+          cd build
+          &&
+          ctest --output-on-failure -C Release -j 3
+  # We need some "accumulation" job here because bors fails (timeouts) to
+  # listen on matrix builds.
+  # Hence, we have some kind of dummy here that bors can listen on
+  ci-success:
+    name: bors-ci-status
+    if: ${{ always() }}
+    needs:
+      - test_legacy_linux
+      - test_legacy_mac
+      - test_legacy_windows
+      - test_legacy_stable
+      - test_legacy_new_lockfile_msrv
+      - test
+      - test_msvc
+      - test_cxxbridge
+      - install
+    runs-on: ubuntu-latest
+    # Step copied from: https://github.com/cross-rs/cross/blob/80c9f9109a719ffb0f694060ddc6e371d5b3a540/.github/workflows/ci.yml#L361
+    steps:
+      - name: Result
+        run: |
+          jq -C <<< "${needs}"
+          # Check if all needs were successful or skipped.
+          "$(jq -r 'all(.result as $result | (["success", "skipped"] | contains([$result])))' <<< "${needs}")"
+        env:
+          needs: ${{ toJson(needs) }}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/.github/workflows/test_legacy.yaml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,80 @@
+name: Test Corrosion using prebuilt legacy generator
+
+on:
+  workflow_call:
+    inputs:
+      os:
+        required: true
+        type: string
+      rust:
+        required: false
+        type: string
+        default: 1.46.0
+      target_arch:
+        required: false
+        type: string
+        default: x86_64
+      generator:
+        required: false
+        type: string
+        default : ninja
+
+jobs:
+  test_legacy:
+    name: Test (${{inputs.os}})
+    runs-on: ${{ inputs.os }}
+    strategy:
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v4
+      - name: Cache Legacy Generator
+        id: cache_generator
+        uses: actions/cache@v4
+        with:
+          path: ${{github.workspace}}/corrosion-prebuilt-generator
+          key: ${{ runner.os }}-${{ inputs.rust }}-generator-${{ hashFiles('generator/src/**', 'generator/Cargo.toml', 'generator/Cargo.lock') }}
+      - name: Setup Environment and Configure CMake
+        uses: "./.github/actions/setup_test"
+        with:
+          target_arch: x86_64
+          cmake: 3.15.7
+          rust: ${{inputs.rust}}
+          generator: ninja
+          build_dir: build
+          install_path: ${{github.workspace}}/corrosion-prebuilt-generator
+          configure_params: "-DCMAKE_BUILD_TYPE=Release"
+        if: steps.cache_generator.outputs.cache-hit != 'true'
+      - name: Build corrosion
+        run: cmake --build build --config Release
+        if: steps.cache_generator.outputs.cache-hit != 'true'
+      - name: Install corrosion
+        run: cmake --install build --config Release
+        if: steps.cache_generator.outputs.cache-hit != 'true'
+      - name: Determine Corrosion Generator path
+        id: cor_gen
+        shell: bash
+        run: |
+          export base_generator_bin="${{github.workspace}}/corrosion-prebuilt-generator/libexec/corrosion-generator"
+          if [ "${{ runner.os }}" == "Windows" ]; then
+            echo "generator_bin=${base_generator_bin}.exe" >> $GITHUB_OUTPUT
+          else
+            echo "generator_bin=${base_generator_bin}" >> $GITHUB_OUTPUT
+            chmod +x "${base_generator_bin}"
+          fi
+      - name: Setup Environment and Configure CMake
+        uses: "./.github/actions/setup_test"
+        with:
+          target_arch: ${{inputs.target_arch}}
+          cmake: 3.15.7
+          rust: ${{inputs.rust}}
+          generator: ${{inputs.generator}}
+          build_dir: build
+          configure_params: "-DCORROSION_GENERATOR_EXECUTABLE=${{steps.cor_gen.outputs.generator_bin}}"
+      - name: Run Tests
+        id: run_tests
+        working-directory: build
+        run: ctest --build-config Debug -j 3
+      - name: Rerun failed tests verbose
+        working-directory: build
+        run: ctest --rerun-failed --verbose --build-config Debug
+        if: ${{ failure() && steps.run_tests.conclusion == 'failure' }}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/.gitignore	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,9 @@
+
+**/target/
+**/*.rs.bk
+build*/
+install*/
+.vscode
+.idea
+cmake-build-*
+test/test_header.cmake
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,135 @@
+cmake_minimum_required(VERSION 3.15)
+project(Corrosion
+    # Official releases will be major.minor.patch. When the `tweak` field is
+    # set it indicates that we are on a commit, that is not a officially
+    # tagged release. Users don't need to care about this, it is mainly to
+    # clearly see in configure logs which version was used, without needing to
+    # rely on `git`, since Corrosion may be installed or otherwise packaged.
+    VERSION 0.5.0
+    LANGUAGES NONE
+    HOMEPAGE_URL "https://corrosion-rs.github.io/corrosion/"
+)
+
+# Default behavior:
+# - If the project is being used as a subdirectory, then don't build tests and
+#   don't enable any languages.
+# - If this is a top level project, then build tests and enable the C++ compiler
+if (NOT CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
+    set(_CORROSION_TOP_LEVEL OFF)
+else()
+    set(_CORROSION_TOP_LEVEL ON)
+endif()
+
+# ==== Corrosion Configuration ====
+
+option(
+    CORROSION_DEV_MODE
+    "Enables some additional features if you're developing Corrosion"
+    ${_CORROSION_TOP_LEVEL}
+)
+
+option(
+    CORROSION_BUILD_TESTS
+    "Build Corrosion test project"
+    ${_CORROSION_TOP_LEVEL}
+)
+
+set(
+  CORROSION_GENERATOR_EXECUTABLE CACHE STRING
+  "Use prebuilt, non-bootstrapped corrosion-generator")
+mark_as_advanced(CORROSION_GENERATOR_EXECUTABLE)
+
+if (CORROSION_GENERATOR_EXECUTABLE)
+    add_executable(Corrosion::Generator IMPORTED GLOBAL)
+    set_property(
+        TARGET Corrosion::Generator
+        PROPERTY IMPORTED_LOCATION ${CORROSION_GENERATOR_EXECUTABLE})
+    set(CORROSION_INSTALL_EXECUTABLE_DEFAULT OFF)
+elseif(CORROSION_NATIVE_TOOLING OR CMAKE_VERSION VERSION_LESS 3.19.0)
+        set(CORROSION_INSTALL_EXECUTABLE_DEFAULT "ON")
+else()
+    set(CORROSION_INSTALL_EXECUTABLE_DEFAULT OFF)
+endif()
+
+option(
+    CORROSION_INSTALL_EXECUTABLE
+    "Controls whether corrosion-generator is installed with the package"
+    ${CORROSION_INSTALL_EXECUTABLE_DEFAULT}
+)
+mark_as_advanced(CORROSION_INSTALL_EXECUTABLE)
+
+if (_CORROSION_TOP_LEVEL)
+    # We need to enable a language for corrosions test to work.
+    # For projects using corrosion this is not needed
+    enable_language(C)
+endif()
+
+# This little bit self-hosts the Corrosion toolchain to build the generator
+# tool.
+#
+# It is strongly encouraged to install Corrosion separately and use
+# `find_package(Corrosion REQUIRED)` instead if that works with your workflow.
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
+include(Corrosion)
+
+# Testing
+if (CORROSION_BUILD_TESTS)
+    include(CTest)
+    add_subdirectory(test)
+endif()
+
+# If Corrosion is a subdirectory, do not enable its install code
+if (NOT _CORROSION_TOP_LEVEL)
+    return()
+endif()
+
+# Installation
+
+include(GNUInstallDirs)
+
+if(CORROSION_INSTALL_EXECUTABLE)
+    get_property(
+            _CORROSION_GENERATOR_EXE
+            TARGET Corrosion::Generator PROPERTY IMPORTED_LOCATION
+    )
+    install(PROGRAMS "${_CORROSION_GENERATOR_EXE}" DESTINATION "${CMAKE_INSTALL_FULL_LIBEXECDIR}")
+else()
+    message(DEBUG "Not installing corrosion-generator since "
+        "`CORROSION_INSTALL_EXECUTABLE` is set to ${CORROSION_INSTALL_EXECUTABLE}"
+    )
+endif()
+
+# Generate the Config file
+include(CMakePackageConfigHelpers)
+
+configure_package_config_file(
+    cmake/CorrosionConfig.cmake.in CorrosionConfig.cmake
+    INSTALL_DESTINATION
+        "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/Corrosion"
+)
+
+write_basic_package_version_file(
+    "${CMAKE_CURRENT_BINARY_DIR}/CorrosionConfigVersion.cmake"
+    VERSION ${PROJECT_VERSION}
+    COMPATIBILITY
+        SameMinorVersion # TODO: Should be SameMajorVersion when 1.0 is released
+    ARCH_INDEPENDENT
+)
+
+install(
+    FILES
+        "${CMAKE_CURRENT_BINARY_DIR}/CorrosionConfig.cmake"
+        "${CMAKE_CURRENT_BINARY_DIR}/CorrosionConfigVersion.cmake"
+    DESTINATION
+        "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/Corrosion"
+)
+
+# These CMake scripts are needed both for the install and as a subdirectory
+install(
+    FILES
+        cmake/Corrosion.cmake
+        cmake/CorrosionGenerator.cmake
+        cmake/FindRust.cmake
+    DESTINATION
+        "${CMAKE_INSTALL_FULL_DATADIR}/cmake"
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/LICENSE	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Andrew Gaspar
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/README.md	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,40 @@
+# Corrosion
+[![Build Status](https://github.com/corrosion-rs/corrosion/actions/workflows/test.yaml/badge.svg)](https://github.com/corrosion-rs/corrosion/actions?query=branch%3Amaster)
+[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://corrosion-rs.github.io/corrosion/)
+![License](https://img.shields.io/badge/license-MIT-blue)
+
+Corrosion, formerly known as cmake-cargo, is a tool for integrating Rust into an existing CMake
+project. Corrosion can automatically import executables, static libraries, and dynamic libraries
+from a workspace or package manifest (`Cargo.toml` file).
+
+## Features
+- Automatic Import of Executable, Static, and Shared Libraries from Rust Crate
+- Easy Installation of Rust Executables
+- Trivially Link Rust Executables to C/C++ Libraries in Tree
+- Multi-Config Generator Support
+- Simple Cross-Compilation
+
+## Sample Usage with FetchContent
+
+Using the CMake `FetchContent` module allows you to easily integrate corrosion into your build.
+Other methods including installing corrosion or adding it as a subdirectory are covered in the
+[setup chapter](https://corrosion-rs.github.io/corrosion/setup_corrosion.html) of the 
+corrosion [documentation](https://corrosion-rs.github.io/corrosion/).
+
+```cmake
+include(FetchContent)
+
+FetchContent_Declare(
+    Corrosion
+    GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
+    GIT_TAG v0.5 # Optionally specify a commit hash, version tag or branch here
+)
+FetchContent_MakeAvailable(Corrosion)
+
+# Import targets defined in a package or workspace manifest `Cargo.toml` file
+corrosion_import_crate(MANIFEST_PATH rust-lib/Cargo.toml)
+
+add_executable(your_cpp_bin main.cpp)
+target_link_libraries(your_cpp_bin PUBLIC rust-lib)
+```
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/RELEASES.md	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,458 @@
+# v0.5.0 (2024-05-11)
+
+### Breaking Changes
+
+- Dashes (`-`) in names of imported CMake **library** targets are now replaced with underscores (`_`).
+  See [issue #501] for details. Users on older Corrosion versions will experience the same
+  change when using Rust 1.79 or newer. `bin` targets are not affected by this change.
+
+[issue #501]: https://github.com/corrosion-rs/corrosion/issues/501
+
+# v0.4.10 (2024-05-11)
+
+### New features
+
+- `corrosion_experimental_cbindgen()` can now be called multiple times on the same Rust target,
+  as long as the output header name differs. This may be useful to generate separate C and C++
+  bindings. [#507]
+- If `corrosion_link_libraries()` is called on a Rust static library target, then
+  `target_link_libraries()` is called to propagate the dependencies to C/C++ consumers.
+  Previously a warning was emitted in this case and the arguments ignored. [#506]
+
+### Fixes
+
+- Combine `-framework` flags on macos to avoid linker deduplication errors [#455]
+- `corrosion_experimental_cbindgen()` will now correctly use the package name, instead of assuming that
+    the package and crate name are identical. ([11e27c])
+- Set the `AR_<triple>` variable for `cc-rs` (except for msvc targets) [#456]
+- Fix hostbuild when cross-compiling to windows [#477]
+- Consider vworks executable suffix [#504]
+- `corrosion_experimental_cbindgen()` now forwards the Rust target-triple (e.g. `aarch64-unknown-linux-gnu`)
+  to cbindgen via the `TARGET` environment variable. The `hostbuild` property is considered. [#507]
+- Fix linking errors with Rust >= 1.79 and `-msvc` targets.` [#511]
+
+
+[#455]: https://github.com/corrosion-rs/corrosion/pull/455
+[#456]: https://github.com/corrosion-rs/corrosion/pull/456
+[#477]: https://github.com/corrosion-rs/corrosion/pull/477
+[#504]: https://github.com/corrosion-rs/corrosion/pull/504
+[#506]: https://github.com/corrosion-rs/corrosion/pull/506
+[#507]: https://github.com/corrosion-rs/corrosion/pull/507
+[#511]: https://github.com/corrosion-rs/corrosion/pull/511
+[11e27c]: https://github.com/corrosion-rs/corrosion/pull/514/commits/11e27cde2cf32c7ed539c96eb03c2f10035de538
+
+# v0.4.9 (2024-05-01)
+
+### New Features 
+
+- Automatically detect Rust target for OpenHarmony ([#510]).
+
+### Fixes
+
+- Make find_package portable ([#509]).
+
+[#510]: https://github.com/corrosion-rs/corrosion/pull/510
+[#509]: https://github.com/corrosion-rs/corrosion/pull/509
+
+# v0.4.8 (2024-04-03)
+
+### Fixes
+
+- Fix an internal error when passing both the `PROFILE` and `CRATES` option to
+  `corrosion_import_crate()` ([#496]).
+
+[#496]: https://github.com/corrosion-rs/corrosion/pull/496
+
+# v0.4.7 (2024-01-19)
+
+### Fixes
+
+- The C/C++ compiler passed from corrosion to `cc-rs` can now be overriden by users setting
+  `CC_<target>` (e.g. `CC_x86_64-unknown-linux-gnu=/path/to/my-compiler`) environment variables ([#475]).
+
+[#475]: https://github.com/corrosion-rs/corrosion/pull/475
+
+# v0.4.6 (2024-01-17)
+
+### Fixes
+
+- Fix hostbuild executables when cross-compiling from non-windows to windows targets.
+  (Only with CMake >= 3.19).
+
+# v0.4.5 (2023-11-30)
+
+### Fixes
+
+- Fix hostbuild executables when cross-compiling on windows to non-windows targets
+  (Only with CMake >= 3.19).
+
+# v0.4.4 (2023-10-06)
+
+### Fixes
+
+- Add `chimera` ([#445]) and `unikraft` ([#446]) to the list of known vendors
+
+[#445]: https://github.com/corrosion-rs/corrosion/pull/445
+[#446]: https://github.com/corrosion-rs/corrosion/pull/446
+
+# v0.4.3 (2023-09-09)
+
+### Fixes
+
+- Fix the PROFILE option with CMake < 3.19 [#427]
+- Relax vendor parsing for espressif targets (removes warnings)
+- Fix an issue detecting required link libraries with Rust >= 1.71
+  when the cmake build directory is located in a Cargo workspace.
+
+# 0.4.2 (2023-07-16)
+
+### Fixes
+
+- Fix an issue when cross-compiling with clang
+- Fix detecting required libraries with cargo 1.71 
+
+### New features
+
+- Users can now set `Rust_RESOLVE_RUSTUP_TOOLCHAINS` to `OFF`, which will result in Corrosion
+  not attempting to resolve rustc/cargo.
+
+# 0.4.1 (2023-06-03)
+
+This is a bugfix release.
+
+### Fixes
+
+- Fixes a regression on multi-config Generators
+
+# 0.4.0 LTS (2023-06-01)
+
+No changes compared to v0.4.0-beta2.
+
+## Announcements
+
+The `v0.4.x` LTS series will be the last release to support older CMake and Rust versions.
+If necessary, fixes will be backported to the v0.4 branch. New features will not be
+actively backported after the next major release, but community contributions are possible.
+The `v0.4.x` series is currently planned to be maintained until the end of 2024.
+
+The following major release will increase the minimum required CMake version to 3.22. The 
+minimum supported Rust version will also be increased to make use of newly added flags, but 
+the exact version is not fixed yet. 
+
+
+## Changes compared to v0.3.5:
+
+### Breaking Changes
+
+- The Visual Studio Generators now require at least CMake 3.20.
+  This was previously announced in the 0.3.0 release notes and is the same
+  requirement as for the other Multi-Config Generators.
+- The previously deprecated function `corrosion_set_linker_language()`
+  will now raise an error when called and may be removed without further
+  notice in future stable releases. Use `corrosion_set_linker()` instead.
+- Improved the FindRust target triple detection, which may cause different behavior in some cases.
+  The detection does not require an enabled language anymore and will always fall back
+  to the default host target triple. A warning is issued if target triple detection failed.
+
+### Potentially Breaking Changes
+
+- Corrosion now sets the `IMPORTED_NO_SONAME` property for shared rust libraries, since by
+  default they won't have an `soname` field.
+  If you add a rustflag like `-Clink-arg=-Wl,-soname,libmycrate.so` in your project,
+  you should set this property to false on the shared rust library.
+- Corrosion now uses a mechanism to determine which native libraries need to be linked with
+  Rust `staticlib` targets into C/C++ targets. The previous mechanism contained a hardcoded list.
+  The new mechanism asks `rustc` which libraries are needed at minimum for a given
+  target triple (with `std` support). This should not be a breaking change, but if you
+  do encounter a new linking issue when upgrading with `staticlib` targets, please open an
+  issue.
+
+### New features
+
+- `corrosion_import_crate()` has two new options `LOCKED` and `FROZEN` which pass the 
+  `--locked` and `--frozen` flags to all invocations of cargo.
+- `FindRust` now provides cache variables containing information on the default host
+  target triple:
+  - `Rust_CARGO_HOST_ARCH`
+  - `Rust_CARGO_HOST_VENDOR`
+  - `Rust_CARGO_HOST_OS`
+  - `Rust_CARGO_HOST_ENV`
+
+### Other changes
+
+- When installing Corrosion with CMake >= 3.19, the legacy Generator tool is
+  no longer built and installed by default.
+- Corrosion now issues a warning when setting the linker or setting linker
+  options for a Rust static library.
+- Corrosion no longer enables the `C` language when CMake is in crosscompiling mode and
+  no languages where previously enabled. This is not considered a breaking change.
+- `corrosion_import_crate()` now warns about unexpected arguments.
+
+### Fixes
+
+- Fix building when the `dev` profile is explicitly set by the user.
+
+## Experimental features (may be changed or removed without a major version bump)
+
+- Experimental cxxbridge and cbindgen integration.
+- Add a helper function to parse the package version from a Cargo.toml file
+- Expose rustup toolchains discovered by `FindRust` in the following cache variables
+  which contain a list.
+  - `Rust_RUSTUP_TOOLCHAINS`: List of toolchains names
+  - `Rust_RUSTUP_TOOLCHAINS_VERSION`: List of `rustc` version of the toolchains
+  - `Rust_RUSTUP_TOOLCHAINS_RUSTC_PATH`: List of the path to `rustc`
+  - `Rust_RUSTUP_TOOLCHAINS_CARGO_PATH`: List of the path to `cargo`. Entries may be `NOTFOUND` if cargo
+    is not available for that toolchain.
+- Add target properties `INTERFACE_CORROSION_RUSTC` and `INTERFACE_CORROSION_CARGO`, which may
+  be set to paths to `rustc` and `cargo` respectively to override the toolchain for a specific
+  target.
+
+# 0.3.5 (2023-03-19)
+
+- Fix building the Legacy Generator on Rust toolchains < 1.56 ([#365])
+
+[#365]: https://github.com/corrosion-rs/corrosion/pull/365
+
+# 0.3.4 (2023-03-02)
+
+## Fixes
+
+- Fix hostbuild (when CMake/Cargo is configured for cross-compiling) if clang is used ([#338]).
+
+## Other
+
+- Pass `--no-deps` to cargo metadata ([#334]).
+- Bump the legacy generator dependencies
+
+[#334]: https://github.com/corrosion-rs/corrosion/pull/334
+[#338]: https://github.com/corrosion-rs/corrosion/pull/338
+
+
+# 0.3.3 (2023-02-17)
+
+## New features (Only available on CMake >= 3.19)
+
+- Add new `IMPORTED_CRATES` flag to `corrosion_import_crate()` to retrieve the list of imported crates in the current
+  scope ([#312](https://github.com/corrosion-rs/corrosion/pull/312)).
+
+## Fixes
+
+- Fix imported location target property when the rust target name contains dashes
+  and a custom OUTPUT_DIRECTORY was specified by the user ([#322](https://github.com/corrosion-rs/corrosion/pull/322)).
+- Fix building for custom rust target-triples ([#316](https://github.com/corrosion-rs/corrosion/pull/316))
+
+# 0.3.2 (2023-01-11)
+
+## New features (Only available on CMake >= 3.19)
+
+- Add new `CRATE_TYPES` flag to `corrosion_import_crate()` to restrict which
+  crate types should be imported ([#269](https://github.com/corrosion-rs/corrosion/pull/269)).
+- Add `NO_LINKER_OVERRIDE` flag to let Rust choose the default linker for the target
+  instead of what Corrosion thinks is the appropriate linker driver ([#272](https://github.com/corrosion-rs/corrosion/pull/272)).
+
+## Fixes
+
+- Fix clean target when cross-compiling ([#291](https://github.com/corrosion-rs/corrosion/pull/291)).
+- Don't set the linker for Rust static libraries ([#275](https://github.com/corrosion-rs/corrosion/pull/275)).
+- Minor fixes in FindRust [#297](https://github.com/corrosion-rs/corrosion/pull/297): 
+  - fix a logic error in the version detection
+  - fix a logic error in `QUIET` mode when rustup is not found.
+
+# 0.3.1 (2022-12-13)
+
+### Fixes
+
+- Fix a regression in detecting the MSVC abi ([#256])
+- Fix an issue on macOS 13 which affected rust crates compiling C++ code in build scripts ([#254]).
+- Fix corrosion not respecting `CMAKE_<XYZ>_OUTPUT_DIRECTORY` values ([#268]).
+- Don't override rusts linker choice for the msvc abi (previously this was only skipped for msvc generators) ([#271])
+
+[#254]: https://github.com/corrosion-rs/corrosion/pull/254
+[#256]: https://github.com/corrosion-rs/corrosion/pull/256
+[#268]: https://github.com/corrosion-rs/corrosion/pull/268
+[#271]: https://github.com/corrosion-rs/corrosion/pull/271
+
+# 0.3.0 (2022-10-31)
+
+## Breaking
+
+- The minimum supported rust version (MSRV) was increased to 1.46, due to a cargo issue that recently
+  surfaced on CI when using crates.io. On MacOS 12 and Windows-2022 at least Rust 1.54 is required.
+- MacOS 10 and 11 are no longer officially supported and untested in CI.
+- The minimum required CMake version is now 3.15.
+- Adding a `PRE_BUILD` custom command on a `cargo-build_<target_name>` CMake target will no 
+  longer work as expected. To support executing user defined commands before cargo build is
+  invoked users should use the newly added targets `cargo-prebuild` (before all cargo build invocations)
+  or `cargo-prebuild_<target_name>` as a dependency target. 
+  Example: `add_dependencies(cargo-prebuild code_generator_target)`
+
+### Breaking: Removed previously deprecated functionality
+- Removed `add_crate()` function. Use `corrosio_import_crate()` instead.
+- Removed `cargo_link_libraries()` function. Use `corrosion_link_libraries()` instead.
+- Removed experimental CMake option `CORROSION_EXPERIMENTAL_PARSER`.
+  The corresponding stable option is `CORROSION_NATIVE_TOOLING` albeit with inverted semantics.
+- Previously Corrosion would set the `HOST_CC` and `HOST_CXX` environment variables when invoking 
+  cargo build, if the environment variables `CC` and `CXX` outside of CMake where set.
+  However this did not work as expected in all cases and sometimes the `HOST_CC` variable would be set
+  to a cross-compiler for unknown reasons. For this reason `HOST_CC` and `HOST_CXX` are not set by
+  corrosion anymore, but users can still set them manually if required via `corrosion_set_env_vars()`.
+- The `CARGO_RUST_FLAGS` family of cache variables were removed. Corrosion does not internally use them
+  anymore.
+
+## Potentially breaking
+
+- The working directory when invoking `cargo build` was changed to the directory of the Manifest
+  file. This now allows cargo to pick up `.cargo/config.toml` files located in the source tree.
+  ([205](https://github.com/corrosion-rs/corrosion/pull/205))
+- Corrosion internally invokes `cargo build`. When passing arguments to `cargo build`, Corrosion
+  now uses the CMake `VERBATIM` option. In rare cases this may require you to change how you quote
+  parameters passed to corrosion (e.g. via `corrosion_add_target_rustflags()`).
+  For example setting a `cfg` option previously required double escaping the rustflag like this
+  `"--cfg=something=\\\"value\\\""`, but now it can be passed to corrosion without any escapes:
+  `--cfg=something="value"`.
+- Corrosion now respects the CMake `OUTPUT_DIRECTORY` target properties. More details in the "New features" section.
+
+## New features
+
+- Support setting rustflags for only the main target and none of its dependencies ([215](https://github.com/corrosion-rs/corrosion/pull/215)).
+  A new function `corrosion_add_target_local_rustflags(target_name rustc_flag [more_flags ...])`
+  is added for this purpose.
+  This is useful in cases where you only need rustflags on the main-crate, but need to set different
+  flags for different targets. Without "local" Rustflags this would require rebuilds of the
+  dependencies when switching targets.
+- Support explicitly selecting a linker ([208](https://github.com/corrosion-rs/corrosion/pull/208)).
+  The linker can be selected via `corrosion_set_linker(target_name linker)`.
+  Please note that this only has an effect for targets, where the final linker invocation is done
+  by cargo, i.e. targets where foreign code is linked into rust code and not the other way around.
+- Corrosion now respects the CMake `OUTPUT_DIRECTORY` target properties and copies build artifacts to the expected
+  locations ([217](https://github.com/corrosion-rs/corrosion/pull/217)), if the properties are set.
+  This feature requires at least CMake 3.19 and is enabled by default if supported. Please note that the `OUTPUT_NAME`
+  target properties are currently not supported.
+  Specifically, the following target properties are now respected:
+  -   [ARCHIVE_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.html)
+  -   [LIBRARY_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/LIBRARY_OUTPUT_DIRECTORY.html)
+  -   [RUNTIME_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/RUNTIME_OUTPUT_DIRECTORY.html)
+  -   [PDB_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/PDB_OUTPUT_DIRECTORY.html)
+- Corrosion now supports packages with potentially multiple binaries (bins) and a library (lib) at the
+  same time. The only requirement is that the names of all `bin`s and `lib`s in the whole project must be unique.
+  Users can set the names in the `Cargo.toml` by adding `name = <unique_name>` in the `[[bin]]` and `[lib]` tables.
+- FindRust now has improved support for the `VERSION` option of `find_package` and will now attempt to find a matching
+  toolchain version. Previously it was only checked if the default toolchain matched to required version.
+- For rustup managed toolchains a CMake error is issued with a helpful message if the required target for
+  the selected toolchain is not installed.
+
+## Fixes
+
+- Fix a CMake developer Warning when a Multi-Config Generator and Rust executable targets
+  ([#213](https://github.com/corrosion-rs/corrosion/pull/213)).
+- FindRust now respects the `QUIET` option to `find_package()` in most cases.
+
+## Deprecation notice
+
+- Support for the MSVC Generators with CMake toolchains before 3.20 is deprecated and will be removed in the next
+  release (v0.4). All other Multi-config Generators already require CMake 3.20.
+
+## Internal Changes
+
+- The CMake Generator written in Rust and `CorrosionGenerator.cmake` which are responsible for parsing 
+  `cargo metadata` output to create corresponding CMake targets for all Rust targets now share most code.
+  This greatly simplified the CMake generator written in Rust and makes it much easier maintaining and adding
+  new features regardless of how `cargo metadata` is parsed.
+
+# 0.2.2 (2022-09-01)
+
+## Fixes
+
+- Do not use C++17 in the tests (makes tests work with older C++ compilers) ([184](https://github.com/corrosion-rs/corrosion/pull/184))
+- Fix finding cargo on NixOS ([192](https://github.com/corrosion-rs/corrosion/pull/192))
+- Fix issue with Rustflags test when using a Build type other than Debug and Release ([203](https://github.com/corrosion-rs/corrosion/pull/203)).
+
+# 0.2.1 (2022-05-07)
+
+## Fixes
+
+- Fix missing variables provided by corrosion, when corrosion is used as a subdirectory ([181](https://github.com/corrosion-rs/corrosion/pull/181)):
+  Public [Variables](https://github.com/corrosion-rs/corrosion#information-provided-by-corrosion) set
+  by Corrosion were not visible when using Corrosion as a subdirectory, due to the wrong scope of
+  the variables. This was fixed by promoting the respective variables to Cache variables.
+
+# 0.2.0 (2022-05-05)
+
+## Breaking changes
+
+- Removed the integrator build script ([#156](https://github.com/corrosion-rs/corrosion/pull/156)).
+  The build script provided by corrosion (for rust code that links in foreign code) is no longer necessary,
+  so users can just remove the dependency.
+
+## Deprecations
+
+- Direct usage of the following target properties has been deprecated. The names of the custom properties are
+  no longer considered part of the public API and may change in the future. Instead, please use the functions
+  provided by corrosion. Internally different property names are used depending on the CMake version.
+  - `CORROSION_FEATURES`, `CORROSION_ALL_FEATURES`, `CORROSION_NO_DEFAULT_FEATURES`. Instead please use
+    `corrosion_set_features()`. See the updated Readme for details.
+  - `CORROSION_ENVIRONMENT_VARIABLES`. Please use `corrosion_set_env_vars()` instead.
+  - `CORROSION_USE_HOST_BUILD`. Please use `corrosion_set_hostbuild()` instead.
+- The Minimum CMake version will likely be increased for the next major release. At the very least we want to drop
+  support for CMake 3.12, but requiring CMake 3.16 or even 3.18 is also on the table. If you are using a CMake version
+  that would be no longer supported by corrosion, please comment on issue
+  [#168](https://github.com/corrosion-rs/corrosion/issues/168), so that we can gauge the number of affected users.
+
+## New features
+
+- Add `NO_STD` option to `corrosion_import_crate` ([#154](https://github.com/corrosion-rs/corrosion/pull/154)).
+- Remove the requirement of building the Rust based generator crate for CMake >= 3.19. This makes using corrosion as
+  a subdirectory as fast as the installed version (since everything is done in CMake).
+  ([#131](https://github.com/corrosion-rs/corrosion/pull/131), [#161](https://github.com/corrosion-rs/corrosion/pull/161))
+  If you do choose to install Corrosion, then by default the old Generator is still compiled and installed, so you can
+  fall back to using it in case you use multiple cmake versions on the same machine for different projects.
+
+## Fixes
+
+- Fix Corrosion on MacOS 11 and 12 ([#167](https://github.com/corrosion-rs/corrosion/pull/167) and
+  [#164](https://github.com/corrosion-rs/corrosion/pull/164)).
+- Improve robustness of parsing the LLVM version (exported in `Rust_LLVM_VERSION`). It now also works for
+  Rust versions, where the LLVM version is reported as `MAJOR.MINOR`. ([#148](https://github.com/corrosion-rs/corrosion/pull/148))
+- Fix a bug which occurred when Corrosion was added multiple times via `add_subdirectory()`
+  ([#143](https://github.com/corrosion-rs/corrosion/pull/143)).
+- Set `CC_<target_triple_undercore>` and `CXX_<target_triple_undercore>` environment variables for the invocation of
+  `cargo build` to the compilers selected by CMake  (if any)
+  ([#138](https://github.com/corrosion-rs/corrosion/pull/138) and [#161](https://github.com/corrosion-rs/corrosion/pull/161)).
+  This should ensure that C dependencies built in cargo buildscripts via [cc-rs](https://github.com/alexcrichton/cc-rs)
+  use the same compiler as CMake built dependencies. Users can override the compiler by specifying the higher
+  priority environment variable variants with dashes instead of underscores (See cc-rs documentation for details).
+- Fix Ninja-Multiconfig Generator support for CMake versions >= 3.20. Previous CMake versions are missing a feature,
+  which prevents us from supporting the Ninja-Multiconfig generator. ([#137](https://github.com/corrosion-rs/corrosion/pull/137))
+
+
+# 0.1.0 (2022-02-01)
+
+This is the first release of corrosion after it was moved to the new corrosion-rs organization.
+Since there are no previous releases, this is not a complete changelog but only lists changes since
+September 2021.
+
+## New features
+- [Add --profile support for rust >= 1.57](https://github.com/corrosion-rs/corrosion/pull/130):
+  Allows users to specify a custom cargo profile with
+  `corrosion_import_crate(... PROFILE <profilename>)`.
+- [Add support for specifying per-target Rustflags](https://github.com/corrosion-rs/corrosion/pull/127):
+  Rustflags can be added via `corrosion_add_target_rustflags(<target_name> [rustflags1...])`
+- [Add `Rust_IS_NIGHTLY` and `Rust_LLVM_VERSION` variables](https://github.com/corrosion-rs/corrosion/pull/123):
+  This may be useful if you want to conditionally enabled features when using a nightly toolchain
+  or a specific LLVM Version.
+- [Let `FindRust` fail gracefully if rustc is not found](https://github.com/corrosion-rs/corrosion/pull/111):
+  This allows using `FindRust` in a more general setting (without corrosion).
+- [Add support for cargo feature selection](https://github.com/corrosion-rs/corrosion/pull/108):
+  See the [README](https://github.com/corrosion-rs/corrosion#cargo-feature-selection) for details on
+  how to select features.
+
+
+## Fixes
+- [Fix the cargo-clean target](https://github.com/corrosion-rs/corrosion/pull/129)
+- [Fix #84: CorrosionConfig.cmake looks in wrong place for Corrosion::Generator when CMAKE_INSTALL_LIBEXEC is an absolute path](https://github.com/corrosion-rs/corrosion/pull/122/commits/6f29af3ac53917ca2e0638378371e715a18a532d)
+- [Fix #116: (Option CORROSION_INSTALL_EXECUTABLE not working)](https://github.com/corrosion-rs/corrosion/commit/97d44018fac1b1a2a7c095288c628f5bbd9b3184)
+- [Fix building on Windows with rust >= 1.57](https://github.com/corrosion-rs/corrosion/pull/120)
+
+## Known issues:
+- Corrosion is currently not working on macos-11 and newer. See issue [#104](https://github.com/corrosion-rs/corrosion/issues/104).
+  Contributions are welcome.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/cmake/Corrosion.cmake	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,1959 @@
+cmake_minimum_required(VERSION 3.15)
+
+list(APPEND CMAKE_MESSAGE_CONTEXT "Corrosion")
+
+message(DEBUG "Using Corrosion ${Corrosion_VERSION} with CMake ${CMAKE_VERSION} "
+        "and the `${CMAKE_GENERATOR}` Generator"
+)
+
+get_cmake_property(COR_IS_MULTI_CONFIG GENERATOR_IS_MULTI_CONFIG)
+set(COR_IS_MULTI_CONFIG "${COR_IS_MULTI_CONFIG}" CACHE BOOL "Do not change this" FORCE)
+mark_as_advanced(FORCE COR_IS_MULTI_CONFIG)
+
+if (COR_IS_MULTI_CONFIG AND CMAKE_VERSION VERSION_LESS 3.20.0)
+    message(FATAL_ERROR "Corrosion requires at least CMake 3.20 with Multi-Config Generators such as "
+        "\"Ninja Multi-Config\" or Visual Studio. "
+        "Please use a different generator or update to cmake >= 3.20.\n"
+        "Note: You are using CMake ${CMAKE_VERSION} (Path: `${CMAKE_COMMAND}`) with "
+        " the `${CMAKE_GENERATOR}` Generator."
+    )
+elseif(NOT COR_IS_MULTI_CONFIG AND DEFINED CMAKE_CONFIGURATION_TYPES)
+    message(WARNING "The Generator is ${CMAKE_GENERATOR}, which is not a multi-config "
+        "Generator, but CMAKE_CONFIGURATION_TYPES is set. Please don't set "
+        "CMAKE_CONFIGURATION_TYPES unless you are using a multi-config Generator."
+    )
+endif()
+
+option(CORROSION_VERBOSE_OUTPUT "Enables verbose output from Corrosion and Cargo" OFF)
+
+set(CORROSION_NATIVE_TOOLING_DESCRIPTION
+    "Use native tooling - Required on CMake < 3.19 and available as a fallback option for recent versions"
+    )
+
+set(CORROSION_RESPECT_OUTPUT_DIRECTORY_DESCRIPTION
+    "Respect the CMake target properties specifying the output directory of a target, such as
+    `RUNTIME_OUTPUT_DIRECTORY`. This requires CMake >= 3.19, otherwise this option is forced off."
+)
+
+option(
+    CORROSION_NATIVE_TOOLING
+    "${CORROSION_NATIVE_TOOLING_DESCRIPTION}"
+    OFF
+)
+
+option(CORROSION_RESPECT_OUTPUT_DIRECTORY
+    "${CORROSION_RESPECT_OUTPUT_DIRECTORY_DESCRIPTION}"
+    ON
+)
+
+option(
+    CORROSION_NO_WARN_PARSE_TARGET_TRIPLE_FAILED
+    "Surpresses a warning if the parsing the target triple failed."
+    OFF
+)
+
+# The native tooling is required on CMAke < 3.19 so we override whatever the user may have set.
+if (CMAKE_VERSION VERSION_LESS 3.19.0)
+    set(CORROSION_NATIVE_TOOLING ON CACHE INTERNAL "${CORROSION_NATIVE_TOOLING_DESCRIPTION}" FORCE)
+    set(CORROSION_RESPECT_OUTPUT_DIRECTORY OFF CACHE INTERNAL
+        "${CORROSION_RESPECT_OUTPUT_DIRECTORY_DESCRIPTION}" FORCE
+    )
+endif()
+
+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")
+    message(FATAL_ERROR "Due to a cargo issue, cross-compiling with a Visual Studio generator and rust versions"
+            " before 1.54 is not supported. Rust build scripts would be linked with the cross-compiler linker, which"
+            " causes the build to fail. Please upgrade your Rust version to 1.54 or newer.")
+endif()
+
+if (NOT TARGET Corrosion::Generator)
+    message(STATUS "Using Corrosion as a subdirectory")
+endif()
+
+get_property(
+    RUSTC_EXECUTABLE
+    TARGET Rust::Rustc PROPERTY IMPORTED_LOCATION
+)
+
+get_property(
+    CARGO_EXECUTABLE
+    TARGET Rust::Cargo PROPERTY IMPORTED_LOCATION
+)
+
+# Note: Legacy function, used when respecting the `XYZ_OUTPUT_DIRECTORY` target properties is not
+# possible.
+function(_corrosion_set_imported_location_legacy target_name base_property filename)
+    foreach(config_type ${CMAKE_CONFIGURATION_TYPES})
+        set(binary_root "${CMAKE_CURRENT_BINARY_DIR}/${config_type}")
+        string(TOUPPER "${config_type}" config_type_upper)
+        message(DEBUG "Setting ${base_property}_${config_type_upper} for target ${target_name}"
+                " to `${binary_root}/${filename}`.")
+        # For Multiconfig we want to specify the correct location for each configuration
+        set_property(
+            TARGET ${target_name}
+            PROPERTY "${base_property}_${config_type_upper}"
+                "${binary_root}/${filename}"
+        )
+    endforeach()
+    if(NOT COR_IS_MULTI_CONFIG)
+        set(binary_root "${CMAKE_CURRENT_BINARY_DIR}")
+    endif()
+
+    message(DEBUG "Setting ${base_property} for target ${target_name}"
+                " to `${binary_root}/${filename}`.")
+
+    # IMPORTED_LOCATION must be set regardless of possible overrides. In the multiconfig case,
+    # the last configuration "wins".
+    set_property(
+            TARGET ${target_name}
+            PROPERTY "${base_property}" "${binary_root}/${filename}"
+        )
+endfunction()
+
+
+# Sets out_var to true if the byproduct copying and imported location is done in a deferred
+# manner to respect target properties, etc. that may be set later.
+function(_corrosion_determine_deferred_byproduct_copying_and_import_location_handling out_var)
+    set(${out_var} ${CORROSION_RESPECT_OUTPUT_DIRECTORY} PARENT_SCOPE)
+endfunction()
+
+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)
+       OR ((NOT hostbuild) AND (Rust_CARGO_TARGET_OS STREQUAL "windows")))
+        set(_suffix ".exe")
+    elseif(Rust_CARGO_TARGET_OS STREQUAL "vxworks")
+        set(_suffix ".vxe")
+    else()
+        set(_suffix "")
+    endif()
+    set(${out_var_suffix} "${_suffix}" PARENT_SCOPE)
+endfunction()
+
+# Do not call this function directly!
+#
+# This function should be called deferred to evaluate target properties late in the configure stage.
+# IMPORTED_LOCATION does not support Generator expressions, so we must evaluate the output
+# directory target property value at configure time. This function must be deferred to the end of
+# the configure stage, so we can be sure that the output directory is not modified afterwards.
+function(_corrosion_set_imported_location_deferred target_name base_property output_directory_property filename)
+    # The output directory property is expected to be set on the exposed target (without postfix),
+    # but we need to set the imported location on the actual library target with postfix.
+    if("${target_name}" MATCHES "^(.+)-(static|shared)$")
+        set(output_dir_prop_target_name "${CMAKE_MATCH_1}")
+    else()
+        set(output_dir_prop_target_name "${target_name}")
+    endif()
+
+    # Append .exe suffix for executable by-products if the target is windows or if it's a host
+    # build and the host is Windows.
+    get_target_property(target_type ${target_name} TYPE)
+    if(${target_type} STREQUAL "EXECUTABLE" AND (NOT "${filename}" MATCHES "\.pdb$"))
+        _corrosion_bin_target_suffix(${target_name} "suffix")
+        if(suffix)
+            set(filename "${filename}${suffix}")
+        endif()
+    endif()
+
+    get_target_property(output_directory "${output_dir_prop_target_name}" "${output_directory_property}")
+    message(DEBUG "Output directory property (target ${output_dir_prop_target_name}): ${output_directory_property} dir: ${output_directory}")
+
+    foreach(config_type ${CMAKE_CONFIGURATION_TYPES})
+        string(TOUPPER "${config_type}" config_type_upper)
+        get_target_property(output_dir_curr_config ${output_dir_prop_target_name}
+            "${output_directory_property}_${config_type_upper}"
+        )
+        if(output_dir_curr_config)
+            set(curr_out_dir "${output_dir_curr_config}")
+        elseif(output_directory)
+            set(curr_out_dir "${output_directory}")
+        else()
+            set(curr_out_dir "${CMAKE_CURRENT_BINARY_DIR}")
+        endif()
+        message(DEBUG "Setting ${base_property}_${config_type_upper} for target ${target_name}"
+                " to `${curr_out_dir}/${filename}`.")
+        # For Multiconfig we want to specify the correct location for each configuration
+        set_property(
+            TARGET ${target_name}
+            PROPERTY "${base_property}_${config_type_upper}"
+                "${curr_out_dir}/${filename}"
+        )
+        set(base_output_directory "${curr_out_dir}")
+    endforeach()
+
+    if(NOT COR_IS_MULTI_CONFIG)
+        if(output_directory)
+            set(base_output_directory "${output_directory}")
+        else()
+            set(base_output_directory "${CMAKE_CURRENT_BINARY_DIR}")
+        endif()
+    endif()
+
+    message(DEBUG "Setting ${base_property} for target ${target_name}"
+                " to `${base_output_directory}/${filename}`.")
+
+    # IMPORTED_LOCATION must be set regardless of possible overrides. In the multiconfig case,
+    # the last configuration "wins" (IMPORTED_LOCATION is not documented to have Genex support).
+    set_property(
+            TARGET ${target_name}
+            PROPERTY "${base_property}" "${base_output_directory}/${filename}"
+        )
+endfunction()
+
+# Helper function to call _corrosion_set_imported_location_deferred while eagerly
+# evaluating arguments.
+# Refer to https://cmake.org/cmake/help/latest/command/cmake_language.html#deferred-call-examples
+function(_corrosion_call_set_imported_location_deferred target_name base_property output_directory_property filename)
+    cmake_language(EVAL CODE "
+        cmake_language(DEFER
+            CALL
+            _corrosion_set_imported_location_deferred
+            [[${target_name}]]
+            [[${base_property}]]
+            [[${output_directory_property}]]
+            [[${filename}]]
+        )
+    ")
+endfunction()
+
+# Set the imported location of a Rust target.
+#
+# Rust targets are built via custom targets / custom commands. The actual artifacts are exposed
+# to CMake as imported libraries / executables that depend on the cargo_build command. For CMake
+# to find the built artifact we need to set the IMPORTED location to the actual location on disk.
+# Corrosion tries to copy the artifacts built by cargo to standard locations. The IMPORTED_LOCATION
+# is set to point to the copy, and not the original from the cargo build directory.
+#
+# Parameters:
+# - target_name: Name of the Rust target
+# - base_property: Name of the base property - i.e. `IMPORTED_LOCATION` or `IMPORTED_IMPLIB`.
+# - output_directory_property: Target property name that determines the standard location for the
+#    artifact.
+# - filename of the artifact.
+function(_corrosion_set_imported_location target_name base_property output_directory_property filename)
+    _corrosion_determine_deferred_byproduct_copying_and_import_location_handling("defer")
+    if(defer)
+        _corrosion_call_set_imported_location_deferred("${target_name}" "${base_property}" "${output_directory_property}" "${filename}")
+    else()
+        _corrosion_set_imported_location_legacy("${target_name}" "${base_property}" "${filename}")
+    endif()
+endfunction()
+
+function(_corrosion_copy_byproduct_legacy target_name cargo_build_dir file_names)
+    if(ARGN)
+        message(FATAL_ERROR "Unexpected additional arguments")
+    endif()
+
+    if(COR_IS_MULTI_CONFIG)
+        set(output_dir "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>")
+    else()
+        set(output_dir "${CMAKE_CURRENT_BINARY_DIR}")
+    endif()
+
+    list(TRANSFORM file_names PREPEND "${cargo_build_dir}/" OUTPUT_VARIABLE src_file_names)
+    list(TRANSFORM file_names PREPEND "${output_dir}/" OUTPUT_VARIABLE dst_file_names)
+    message(DEBUG "Adding command to copy byproducts `${file_names}` to ${dst_file_names}")
+    add_custom_command(TARGET _cargo-build_${target_name}
+                        POST_BUILD
+                        COMMAND  ${CMAKE_COMMAND} -E make_directory "${output_dir}"
+                        COMMAND
+                        ${CMAKE_COMMAND} -E copy_if_different
+                            # tested to work with both multiple files and paths with spaces
+                            ${src_file_names}
+                            "${output_dir}"
+                        BYPRODUCTS ${dst_file_names}
+                        COMMENT "Copying byproducts `${file_names}` to ${output_dir}"
+                        VERBATIM
+                        COMMAND_EXPAND_LISTS
+    )
+endfunction()
+
+function(_corrosion_copy_byproduct_deferred target_name output_dir_prop_name cargo_build_dir file_names)
+    if(ARGN)
+        message(FATAL_ERROR "Unexpected additional arguments")
+    endif()
+    get_target_property(output_dir ${target_name} "${output_dir_prop_name}")
+
+    # A Genex expanding to the output directory depending on the configuration.
+    set(multiconfig_out_dir_genex "")
+
+    foreach(config_type ${CMAKE_CONFIGURATION_TYPES})
+        string(TOUPPER "${config_type}" config_type_upper)
+        get_target_property(output_dir_curr_config ${target_name} "${output_dir_prop_name}_${config_type_upper}")
+
+        if(output_dir_curr_config)
+            set(curr_out_dir "${output_dir_curr_config}")
+        elseif(output_dir)
+            # 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}")
+        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
+            # imported paths may fail.
+            set(curr_out_dir "${CMAKE_CURRENT_BINARY_DIR}")
+        endif()
+        set(multiconfig_out_dir_genex "${multiconfig_out_dir_genex}$<$<CONFIG:${config_type}>:${curr_out_dir}>")
+    endforeach()
+
+    if(COR_IS_MULTI_CONFIG)
+        set(output_dir "${multiconfig_out_dir_genex}")
+    else()
+        if(NOT output_dir)
+            # Fallback to default directory.
+            set(output_dir "${CMAKE_CURRENT_BINARY_DIR}")
+        endif()
+    endif()
+
+    # Append .exe suffix for executable by-products if the target is windows or if it's a host
+    # build and the host is Windows.
+    get_target_property(target_type "${target_name}" TYPE)
+    if (target_type STREQUAL "EXECUTABLE")
+        list(LENGTH file_names list_len)
+        if(NOT list_len EQUAL "1")
+            message(FATAL_ERROR
+                    "Internal error: Exactly one filename should be passed for executable types.")
+        endif()
+        _corrosion_bin_target_suffix(${target_name} "suffix")
+        if(suffix AND (NOT "${file_names}" MATCHES "\.pdb$"))
+            # For executable targets we know / checked that only one file will be passed.
+            string(APPEND file_names "${suffix}")
+        endif()
+    endif()
+
+    list(TRANSFORM file_names PREPEND "${cargo_build_dir}/" OUTPUT_VARIABLE src_file_names)
+    list(TRANSFORM file_names PREPEND "${output_dir}/" OUTPUT_VARIABLE dst_file_names)
+    message(DEBUG "Adding command to copy byproducts `${file_names}` to ${dst_file_names}")
+    add_custom_command(TARGET _cargo-build_${target_name}
+                        POST_BUILD
+                        # output_dir may contain a Generator expression.
+                        COMMAND  ${CMAKE_COMMAND} -E make_directory "${output_dir}"
+                        COMMAND
+                        ${CMAKE_COMMAND} -E copy_if_different
+                            # tested to work with both multiple files and paths with spaces
+                            ${src_file_names}
+                            "${output_dir}"
+                        BYPRODUCTS ${dst_file_names}
+                        COMMENT "Copying byproducts `${file_names}` to ${output_dir}"
+                        VERBATIM
+                        COMMAND_EXPAND_LISTS
+    )
+endfunction()
+
+function(_corrosion_call_copy_byproduct_deferred target_name output_dir_prop_name cargo_build_dir file_names)
+        cmake_language(EVAL CODE "
+            cmake_language(DEFER
+                CALL
+                _corrosion_copy_byproduct_deferred
+                [[${target_name}]]
+                [[${output_dir_prop_name}]]
+                [[${cargo_build_dir}]]
+                [[${file_names}]]
+            )
+        ")
+endfunction()
+
+# Copy the artifacts generated by cargo to the appropriate destination.
+#
+# Parameters:
+# - target_name: The name of the Rust target
+# - output_dir_prop_name: The property name controlling the destination (e.g.
+#   `RUNTIME_OUTPUT_DIRECTORY`)
+# - cargo_build_dir: the directory cargo build places it's output artifacts in.
+# - filenames: the file names of any output artifacts as a list.
+# - is_binary: TRUE if the byproducts are program executables.
+function(_corrosion_copy_byproducts target_name output_dir_prop_name cargo_build_dir filenames)
+    _corrosion_determine_deferred_byproduct_copying_and_import_location_handling("defer")
+    if(defer)
+        _corrosion_call_copy_byproduct_deferred("${target_name}" "${output_dir_prop_name}" "${cargo_build_dir}" "${filenames}")
+    else()
+        _corrosion_copy_byproduct_legacy("${target_name}" "${cargo_build_dir}" "${filenames}")
+    endif()
+endfunction()
+
+
+# Add targets for the static and/or shared libraries of the rust target.
+# The generated byproduct names are returned via the `OUT_<type>_BYPRODUCTS` arguments.
+function(_corrosion_add_library_target)
+    set(OPTIONS "")
+    set(ONE_VALUE_KEYWORDS
+        WORKSPACE_MANIFEST_PATH
+        TARGET_NAME
+        OUT_ARCHIVE_OUTPUT_BYPRODUCTS
+        OUT_SHARED_LIB_BYPRODUCTS
+        OUT_PDB_BYPRODUCT
+    )
+    set(MULTI_VALUE_KEYWORDS LIB_KINDS)
+    cmake_parse_arguments(PARSE_ARGV 0 CALT "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}")
+
+    if(DEFINED CALT_UNPARSED_ARGUMENTS)
+        message(FATAL_ERROR "Internal error - unexpected arguments: ${CALT_UNPARSED_ARGUMENTS}")
+    elseif(DEFINED CALT_KEYWORDS_MISSING_VALUES)
+        message(FATAL_ERROR "Internal error - the following keywords had no associated value(s):"
+            "${CALT_KEYWORDS_MISSING_VALUES}")
+    endif()
+    list(TRANSFORM ONE_VALUE_KEYWORDS PREPEND CALT_ OUTPUT_VARIABLE required_arguments)
+    foreach(required_argument ${required_arguments} )
+        if(NOT DEFINED "${required_argument}")
+            message(FATAL_ERROR "Internal error: Missing required argument ${required_argument}."
+                "Complete argument list: ${ARGN}"
+            )
+        endif()
+    endforeach()
+    if("staticlib" IN_LIST CALT_LIB_KINDS)
+        set(has_staticlib TRUE)
+    endif()
+    if("cdylib" IN_LIST CALT_LIB_KINDS)
+        set(has_cdylib TRUE)
+    endif()
+
+    if(NOT (has_staticlib OR has_cdylib))
+        message(FATAL_ERROR "Unknown library type(s): ${CALT_LIB_KINDS}")
+    endif()
+    set(workspace_manifest_path "${CALT_WORKSPACE_MANIFEST_PATH}")
+    set(target_name "${CALT_TARGET_NAME}")
+
+    set(is_windows "")
+    set(is_windows_gnu "")
+    set(is_windows_msvc "")
+    set(is_macos "")
+    if(Rust_CARGO_TARGET_OS STREQUAL "windows")
+        set(is_windows TRUE)
+        if(Rust_CARGO_TARGET_ENV STREQUAL "msvc")
+            set(is_windows_msvc TRUE)
+        elseif(Rust_CARGO_TARGET_ENV STREQUAL "gnu")
+            set(is_windows_gnu TRUE)
+        endif()
+    elseif(Rust_CARGO_TARGET_OS STREQUAL "darwin")
+        set(is_macos TRUE)
+    endif()
+
+    # target file names
+    string(REPLACE "-" "_" lib_name "${target_name}")
+
+    if(is_windows_msvc)
+        set(static_lib_name "${lib_name}.lib")
+    else()
+        set(static_lib_name "lib${lib_name}.a")
+    endif()
+
+    if(is_windows)
+        set(dynamic_lib_name "${lib_name}.dll")
+    elseif(is_macos)
+        set(dynamic_lib_name "lib${lib_name}.dylib")
+    else()
+        set(dynamic_lib_name "lib${lib_name}.so")
+    endif()
+
+    if(is_windows_msvc)
+        set(implib_name "${lib_name}.dll.lib")
+    elseif(is_windows_gnu)
+        set(implib_name "lib${lib_name}.dll.a")
+    elseif(is_windows)
+        message(FATAL_ERROR "Unknown windows environment - Can't determine implib name")
+    endif()
+
+
+    set(pdb_name "${lib_name}.pdb")
+
+    set(archive_output_byproducts "")
+    if(has_staticlib)
+        list(APPEND archive_output_byproducts ${static_lib_name})
+    endif()
+
+    if(has_cdylib)
+        set("${CALT_OUT_SHARED_LIB_BYPRODUCTS}" "${dynamic_lib_name}" PARENT_SCOPE)
+        if(is_windows)
+            list(APPEND archive_output_byproducts ${implib_name})
+        endif()
+        if(is_windows_msvc)
+            set("${CALT_OUT_PDB_BYPRODUCT}" "${pdb_name}" PARENT_SCOPE)
+        endif()
+    endif()
+    set("${CALT_OUT_ARCHIVE_OUTPUT_BYPRODUCTS}" "${archive_output_byproducts}" PARENT_SCOPE)
+
+    add_library(${target_name} INTERFACE)
+
+    if(has_staticlib)
+        add_library(${target_name}-static STATIC IMPORTED GLOBAL)
+        add_dependencies(${target_name}-static cargo-build_${target_name})
+
+        _corrosion_set_imported_location("${target_name}-static" "IMPORTED_LOCATION"
+                "ARCHIVE_OUTPUT_DIRECTORY"
+                "${static_lib_name}")
+
+        # Todo: NO_STD target property?
+        if(NOT COR_NO_STD)
+            set_property(
+                    TARGET ${target_name}-static
+                    PROPERTY INTERFACE_LINK_LIBRARIES ${Rust_CARGO_TARGET_LINK_NATIVE_LIBS}
+            )
+            set_property(
+                    TARGET ${target_name}-static
+                    PROPERTY INTERFACE_LINK_OPTIONS ${Rust_CARGO_TARGET_LINK_OPTIONS}
+            )
+            if(is_macos)
+                set_property(TARGET ${target_name}-static
+                        PROPERTY INTERFACE_LINK_DIRECTORIES "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib"
+                        )
+            endif()
+        endif()
+    endif()
+
+    if(has_cdylib)
+        add_library(${target_name}-shared SHARED IMPORTED GLOBAL)
+        add_dependencies(${target_name}-shared cargo-build_${target_name})
+
+        # Todo: (Not new issue): What about IMPORTED_SONAME and IMPORTED_NO_SYSTEM?
+        _corrosion_set_imported_location("${target_name}-shared" "IMPORTED_LOCATION"
+                "LIBRARY_OUTPUT_DIRECTORY"
+                "${dynamic_lib_name}"
+        )
+        # In the future we would probably prefer to let Rust set the soname for packages >= 1.0.
+        # This is tracked in issue #333.
+        set_target_properties(${target_name}-shared PROPERTIES IMPORTED_NO_SONAME TRUE)
+
+        if(is_windows)
+            _corrosion_set_imported_location("${target_name}-shared" "IMPORTED_IMPLIB"
+                    "ARCHIVE_OUTPUT_DIRECTORY"
+                    "${implib_name}"
+            )
+        endif()
+
+        if(is_macos)
+            set_property(TARGET ${target_name}-shared
+                    PROPERTY INTERFACE_LINK_DIRECTORIES "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib"
+                    )
+        endif()
+    endif()
+
+    if(has_cdylib AND has_staticlib)
+        if(BUILD_SHARED_LIBS)
+            target_link_libraries(${target_name} INTERFACE ${target_name}-shared)
+        else()
+            target_link_libraries(${target_name} INTERFACE ${target_name}-static)
+        endif()
+    elseif(has_cdylib)
+        target_link_libraries(${target_name} INTERFACE ${target_name}-shared)
+    else()
+        target_link_libraries(${target_name} INTERFACE ${target_name}-static)
+    endif()
+endfunction()
+
+function(_corrosion_add_bin_target workspace_manifest_path bin_name out_bin_byproduct out_pdb_byproduct)
+    if(NOT bin_name)
+        message(FATAL_ERROR "No bin_name in _corrosion_add_bin_target for target ${target_name}")
+    endif()
+
+    string(REPLACE "-" "_" bin_name_underscore "${bin_name}")
+
+    set(pdb_name "${bin_name_underscore}.pdb")
+
+    if(Rust_CARGO_TARGET_ENV STREQUAL "msvc")
+        set(${out_pdb_byproduct} "${pdb_name}" PARENT_SCOPE)
+    endif()
+
+    set(bin_filename "${bin_name}")
+    _corrosion_determine_deferred_byproduct_copying_and_import_location_handling("defer")
+    if(defer)
+        # .exe suffix will be added later, also depending on possible hostbuild
+        # target property
+    else()
+        if(Rust_CARGO_TARGET_OS STREQUAL "windows")
+            set(bin_filename "${bin_name}.exe")
+        endif()
+    endif()
+    set(${out_bin_byproduct} "${bin_filename}" PARENT_SCOPE)
+
+
+    # Todo: This is compatible with the way corrosion previously exposed the bin name,
+    # but maybe we want to prefix the exposed name with the package name?
+    add_executable(${bin_name} IMPORTED GLOBAL)
+    add_dependencies(${bin_name} cargo-build_${bin_name})
+
+    if(Rust_CARGO_TARGET_OS STREQUAL "darwin")
+        set_property(TARGET ${bin_name}
+                PROPERTY INTERFACE_LINK_DIRECTORIES "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib"
+                )
+    endif()
+
+    _corrosion_set_imported_location("${bin_name}" "IMPORTED_LOCATION"
+                        "RUNTIME_OUTPUT_DIRECTORY"
+                        "${bin_filename}"
+    )
+
+endfunction()
+
+
+if (NOT CORROSION_NATIVE_TOOLING)
+    include(CorrosionGenerator)
+endif()
+
+# Note: `cmake_language(GET_MESSAGE_LOG_LEVEL <output_variable>)` requires CMake 3.25,
+# so we offer our own option to control verbosity of downstream commands (e.g. cargo build)
+if (CORROSION_VERBOSE_OUTPUT)
+    set(_CORROSION_VERBOSE_OUTPUT_FLAG --verbose CACHE INTERNAL "")
+else()
+    # We want to silence some less important commands by default.
+    set(_CORROSION_QUIET_OUTPUT_FLAG --quiet CACHE INTERNAL "")
+endif()
+
+if(CORROSION_NATIVE_TOOLING)
+    if (NOT TARGET Corrosion::Generator )
+        add_subdirectory(generator)
+    endif()
+    get_property(
+        _CORROSION_GENERATOR_EXE
+        TARGET Corrosion::Generator PROPERTY IMPORTED_LOCATION
+    )
+    set(
+        _CORROSION_GENERATOR
+        ${CMAKE_COMMAND} -E env
+            CARGO_BUILD_RUSTC=${RUSTC_EXECUTABLE}
+            ${_CORROSION_GENERATOR_EXE}
+            --cargo ${CARGO_EXECUTABLE}
+            ${_CORROSION_VERBOSE_OUTPUT_FLAG}
+        CACHE INTERNAL "corrosion-generator runner"
+    )
+endif()
+
+set(_CORROSION_CARGO_VERSION ${Rust_CARGO_VERSION} CACHE INTERNAL "cargo version used by corrosion")
+set(_CORROSION_RUST_CARGO_TARGET ${Rust_CARGO_TARGET} CACHE INTERNAL "target triple used by corrosion")
+set(_CORROSION_RUST_CARGO_HOST_TARGET ${Rust_CARGO_HOST_TARGET} CACHE INTERNAL "host triple used by corrosion")
+set(_CORROSION_RUSTC "${RUSTC_EXECUTABLE}" CACHE INTERNAL  "Path to rustc used by corrosion")
+set(_CORROSION_CARGO "${CARGO_EXECUTABLE}" CACHE INTERNAL "Path to cargo used by corrosion")
+
+string(REPLACE "-" "_" _CORROSION_RUST_CARGO_TARGET_UNDERSCORE "${Rust_CARGO_TARGET}")
+set(_CORROSION_RUST_CARGO_TARGET_UNDERSCORE "${_CORROSION_RUST_CARGO_TARGET_UNDERSCORE}" CACHE INTERNAL "lowercase target triple with underscores")
+string(TOUPPER "${_CORROSION_RUST_CARGO_TARGET_UNDERSCORE}" _CORROSION_TARGET_TRIPLE_UPPER)
+set(_CORROSION_RUST_CARGO_TARGET_UPPER
+        "${_CORROSION_TARGET_TRIPLE_UPPER}"
+        CACHE INTERNAL
+        "target triple in uppercase with underscore"
+)
+
+# We previously specified some Custom properties as part of our public API, however the chosen names prevented us from
+# supporting CMake versions before 3.19. In order to both support older CMake versions and not break existing code
+# immediately, we are using a different property name depending on the CMake version. However users avoid using
+# any of the properties directly, as they are no longer part of the public API and are to be considered deprecated.
+# Instead use the corrosion_set_... functions as documented in the Readme.
+if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.19.0)
+    set(_CORR_PROP_FEATURES CORROSION_FEATURES CACHE INTERNAL "")
+    set(_CORR_PROP_ALL_FEATURES CORROSION_ALL_FEATURES CACHE INTERNAL "")
+    set(_CORR_PROP_NO_DEFAULT_FEATURES CORROSION_NO_DEFAULT_FEATURES CACHE INTERNAL "")
+    set(_CORR_PROP_ENV_VARS CORROSION_ENVIRONMENT_VARIABLES CACHE INTERNAL "")
+    set(_CORR_PROP_HOST_BUILD CORROSION_USE_HOST_BUILD CACHE INTERNAL "")
+else()
+    set(_CORR_PROP_FEATURES INTERFACE_CORROSION_FEATURES CACHE INTERNAL "")
+    set(_CORR_PROP_ALL_FEATURES INTERFACE_CORROSION_ALL_FEATURES CACHE INTERNAL "")
+    set(_CORR_PROP_NO_DEFAULT_FEATURES INTERFACE_NO_DEFAULT_FEATURES CACHE INTERNAL "")
+    set(_CORR_PROP_ENV_VARS INTERFACE_CORROSION_ENVIRONMENT_VARIABLES CACHE INTERNAL "")
+    set(_CORR_PROP_HOST_BUILD INTERFACE_CORROSION_USE_HOST_BUILD CACHE INTERNAL "")
+endif()
+
+# Add custom command to build one target in a package (crate)
+#
+# A target may be either a specific bin
+function(_add_cargo_build out_cargo_build_out_dir)
+    set(options NO_LINKER_OVERRIDE)
+    set(one_value_args PACKAGE TARGET MANIFEST_PATH WORKSPACE_MANIFEST_PATH)
+    set(multi_value_args BYPRODUCTS TARGET_KINDS)
+    cmake_parse_arguments(
+        ACB
+        "${options}"
+        "${one_value_args}"
+        "${multi_value_args}"
+        ${ARGN}
+    )
+
+    if(DEFINED ACB_UNPARSED_ARGUMENTS)
+        message(FATAL_ERROR "Internal error - unexpected arguments: "
+            ${ACB_UNPARSED_ARGUMENTS})
+    elseif(DEFINED ACB_KEYWORDS_MISSING_VALUES)
+        message(FATAL_ERROR "Internal error - missing values for the following arguments: "
+                ${ACB_KEYWORDS_MISSING_VALUES})
+    endif()
+
+    set(package_name "${ACB_PACKAGE}")
+    set(target_name "${ACB_TARGET}")
+    set(path_to_toml "${ACB_MANIFEST_PATH}")
+    set(target_kinds "${ACB_TARGET_KINDS}")
+    set(workspace_manifest_path "${ACB_WORKSPACE_MANIFEST_PATH}")
+
+
+    if(NOT target_kinds)
+        message(FATAL_ERROR "TARGET_KINDS not specified")
+    elseif("staticlib" IN_LIST target_kinds OR "cdylib" IN_LIST target_kinds)
+        set(cargo_rustc_filter "--lib")
+    elseif("bin" IN_LIST target_kinds)
+        set(cargo_rustc_filter "--bin=${target_name}")
+    else()
+        message(FATAL_ERROR "TARGET_KINDS contained unknown kind `${target_kind}`")
+    endif()
+
+    if (NOT IS_ABSOLUTE "${path_to_toml}")
+        set(path_to_toml "${CMAKE_SOURCE_DIR}/${path_to_toml}")
+    endif()
+    get_filename_component(workspace_toml_dir ${path_to_toml} DIRECTORY )
+
+    if (CMAKE_VS_PLATFORM_NAME)
+        set (build_dir "${CMAKE_VS_PLATFORM_NAME}/$<CONFIG>")
+    elseif(COR_IS_MULTI_CONFIG)
+        set (build_dir "$<CONFIG>")
+    else()
+        set (build_dir .)
+    endif()
+
+    # If a CMake sysroot is specified, forward it to the linker rustc invokes, too. CMAKE_SYSROOT is documented
+    # to be passed via --sysroot, so we assume that when it's set, the linker supports this option in that style.
+    if(CMAKE_CROSSCOMPILING AND CMAKE_SYSROOT)
+        set(corrosion_link_args "--sysroot=${CMAKE_SYSROOT}")
+    endif()
+
+    if(COR_ALL_FEATURES)
+        set(all_features_arg --all-features)
+    endif()
+    if(COR_NO_DEFAULT_FEATURES)
+        set(no_default_features_arg --no-default-features)
+    endif()
+
+    set(global_rustflags_target_property "$<TARGET_GENEX_EVAL:${target_name},$<TARGET_PROPERTY:${target_name},INTERFACE_CORROSION_RUSTFLAGS>>")
+    set(local_rustflags_target_property  "$<TARGET_GENEX_EVAL:${target_name},$<TARGET_PROPERTY:${target_name},INTERFACE_CORROSION_LOCAL_RUSTFLAGS>>")
+
+    # todo: this probably should be TARGET_GENEX_EVAL
+    set(features_target_property "$<GENEX_EVAL:$<TARGET_PROPERTY:${target_name},${_CORR_PROP_FEATURES}>>")
+    set(features_genex "$<$<BOOL:${features_target_property}>:--features=$<JOIN:${features_target_property},$<COMMA>>>")
+
+    # target property overrides corrosion_import_crate argument
+    set(all_features_target_property "$<GENEX_EVAL:$<TARGET_PROPERTY:${target_name},${_CORR_PROP_ALL_FEATURES}>>")
+    set(all_features_arg "$<$<BOOL:${all_features_target_property}>:--all-features>")
+
+    set(no_default_features_target_property "$<GENEX_EVAL:$<TARGET_PROPERTY:${target_name},${_CORR_PROP_NO_DEFAULT_FEATURES}>>")
+    set(no_default_features_arg "$<$<BOOL:${no_default_features_target_property}>:--no-default-features>")
+
+    set(build_env_variable_genex "$<GENEX_EVAL:$<TARGET_PROPERTY:${target_name},${_CORR_PROP_ENV_VARS}>>")
+    set(hostbuild_override "$<BOOL:$<TARGET_PROPERTY:${target_name},${_CORR_PROP_HOST_BUILD}>>")
+    set(if_not_host_build_condition "$<NOT:${hostbuild_override}>")
+
+    set(corrosion_link_args "$<${if_not_host_build_condition}:${corrosion_link_args}>")
+    # We always set `--target`, so that cargo always places artifacts into a directory with the
+    # target triple.
+    set(cargo_target_option "--target=$<IF:${hostbuild_override},${_CORROSION_RUST_CARGO_HOST_TARGET},${_CORROSION_RUST_CARGO_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)
+    set(target_artifact_dir "$<IF:${hostbuild_override},${_CORROSION_RUST_CARGO_HOST_TARGET},${stripped_target_triple}>")
+
+    set(flags_genex "$<GENEX_EVAL:$<TARGET_PROPERTY:${target_name},INTERFACE_CORROSION_CARGO_FLAGS>>")
+
+    set(explicit_linker_property "$<TARGET_PROPERTY:${target_name},INTERFACE_CORROSION_LINKER>")
+    set(explicit_linker_defined "$<BOOL:${explicit_linker_property}>")
+
+    set(cargo_profile_target_property "$<TARGET_GENEX_EVAL:${target_name},$<TARGET_PROPERTY:${target_name},INTERFACE_CORROSION_CARGO_PROFILE>>")
+
+    # Option to override the rustc/cargo binary to something other than the global default
+    set(rustc_override "$<TARGET_PROPERTY:${target_name},INTERFACE_CORROSION_RUSTC>")
+    set(cargo_override "$<TARGET_PROPERTY:${target_name},INTERFACE_CORROSION_CARGO>")
+    set(rustc_bin "$<IF:$<BOOL:${rustc_override}>,${rustc_override},${_CORROSION_RUSTC}>")
+    set(cargo_bin "$<IF:$<BOOL:${cargo_override}>,${cargo_override},${_CORROSION_CARGO}>")
+
+
+    # Rust will add `-lSystem` as a flag for the linker on macOS. Adding the -L flag via RUSTFLAGS only fixes the
+    # problem partially - buildscripts still break, since they won't receive the RUSTFLAGS. This seems to only be a
+    # problem if we specify the linker ourselves (which we do, since this is necessary for e.g. linking C++ code).
+    # We can however set `LIBRARY_PATH`, which is propagated to the build-script-build properly.
+    if(NOT CMAKE_CROSSCOMPILING AND CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+        # not needed anymore on macos 13 (and causes issues)
+        if(${CMAKE_SYSTEM_VERSION} VERSION_LESS 22)
+        set(cargo_library_path "LIBRARY_PATH=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib")
+        endif()
+    elseif(CMAKE_CROSSCOMPILING AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
+        if(${CMAKE_HOST_SYSTEM_VERSION} VERSION_LESS 22)
+            set(cargo_library_path "$<${hostbuild_override}:LIBRARY_PATH=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib>")
+        endif()
+    endif()
+
+    set(cargo_profile_set "$<BOOL:${cargo_profile_target_property}>")
+    # In the default case just specify --release or nothing to stay compatible with
+    # older rust versions.
+    set(default_profile_option "$<$<NOT:$<OR:$<CONFIG:Debug>,$<CONFIG:>>>:--release>")
+    # evaluates to either `--profile=<custom_profile>`, `--release` or nothing (for debug).
+    set(cargo_profile "$<IF:${cargo_profile_set},--profile=${cargo_profile_target_property},${default_profile_option}>")
+
+    # If the profile name is `dev` change the dir name to `debug`.
+    set(is_dev_profile "$<STREQUAL:${cargo_profile_target_property},dev>")
+    set(profile_dir_override "$<${is_dev_profile}:debug>")
+    set(profile_dir_is_overridden "$<BOOL:${profile_dir_override}>")
+    set(custom_profile_build_type_dir "$<IF:${profile_dir_is_overridden},${profile_dir_override},${cargo_profile_target_property}>")
+
+    set(default_build_type_dir "$<IF:$<OR:$<CONFIG:Debug>,$<CONFIG:>>,debug,release>")
+    set(build_type_dir "$<IF:${cargo_profile_set},${custom_profile_build_type_dir},${default_build_type_dir}>")
+
+    set(cargo_target_dir "${CMAKE_BINARY_DIR}/${build_dir}/cargo/build")
+    set(cargo_build_dir "${cargo_target_dir}/${target_artifact_dir}/${build_type_dir}")
+    set("${out_cargo_build_out_dir}" "${cargo_build_dir}" PARENT_SCOPE)
+
+    set(corrosion_cc_rs_flags)
+
+    if(CMAKE_C_COMPILER)
+        # 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}")
+    endif()
+    if(CMAKE_CXX_COMPILER)
+        list(APPEND corrosion_cc_rs_flags "CXX_${_CORROSION_RUST_CARGO_TARGET_UNDERSCORE}=${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}")
+    endif()
+
+    # Since we instruct cc-rs to use the compiler found by CMake, it is likely one that requires also
+    # specifying the target sysroot to use. CMake's generator makes sure to pass --sysroot with
+    # CMAKE_OSX_SYSROOT. Fortunately the compilers Apple ships also respect the SDKROOT environment
+    # variable, which we can set for use when cc-rs invokes the compiler.
+    if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_SYSROOT)
+        list(APPEND corrosion_cc_rs_flags "SDKROOT=${CMAKE_OSX_SYSROOT}")
+    endif()
+
+    corrosion_add_target_local_rustflags("${target_name}" "$<$<BOOL:${corrosion_link_args}>:-Clink-args=${corrosion_link_args}>")
+
+    # todo: this should probably also be guarded by if_not_host_build_condition.
+    if(COR_NO_STD)
+        corrosion_add_target_local_rustflags("${target_name}" "-Cdefault-linker-libraries=no")
+    else()
+        corrosion_add_target_local_rustflags("${target_name}" "-Cdefault-linker-libraries=yes")
+    endif()
+
+    set(global_joined_rustflags "$<JOIN:${global_rustflags_target_property}, >")
+    set(global_rustflags_genex "$<$<BOOL:${global_rustflags_target_property}>:RUSTFLAGS=${global_joined_rustflags}>")
+    set(local_rustflags_delimiter "$<$<BOOL:${local_rustflags_target_property}>:-->")
+    set(local_rustflags_genex "$<$<BOOL:${local_rustflags_target_property}>:${local_rustflags_target_property}>")
+
+    set(deps_link_languages_prop "$<TARGET_PROPERTY:_cargo-build_${target_name},CARGO_DEPS_LINKER_LANGUAGES>")
+    set(deps_link_languages "$<TARGET_GENEX_EVAL:_cargo-build_${target_name},${deps_link_languages_prop}>")
+    set(target_uses_cxx  "$<IN_LIST:CXX,${deps_link_languages}>")
+    unset(default_linker)
+    # With the MSVC ABI rustc only supports directly invoking the linker - Invoking cl as the linker driver is not supported.
+    if(NOT (Rust_CARGO_TARGET_ENV STREQUAL "msvc" OR COR_NO_LINKER_OVERRIDE))
+        set(default_linker "$<IF:$<BOOL:${target_uses_cxx}>,${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(linker "$<IF:${explicit_linker_defined},${explicit_linker_property},${default_linker}>")
+    set(cargo_target_linker $<$<BOOL:${linker}>:${cargo_target_linker_var}=${linker}>)
+
+    if(Rust_CROSSCOMPILING AND (CMAKE_C_COMPILER_TARGET OR CMAKE_CXX_COMPILER_TARGET))
+        set(linker_target_triple "$<IF:$<BOOL:${target_uses_cxx}>,${CMAKE_CXX_COMPILER_TARGET},${CMAKE_C_COMPILER_TARGET}>")
+        set(rustflag_linker_arg "-Clink-args=--target=${linker_target_triple}")
+        set(rustflag_linker_arg "$<${if_not_host_build_condition}:${rustflag_linker_arg}>")
+        # Skip adding the linker argument, if the linker is explicitly set, since the
+        # explicit_linker_property will not be set when this function runs.
+        # Passing this rustflag is necessary for clang.
+        corrosion_add_target_local_rustflags("${target_name}" "$<$<NOT:${explicit_linker_defined}>:${rustflag_linker_arg}>")
+    endif()
+
+    message(DEBUG "TARGET ${target_name} produces byproducts ${byproducts}")
+
+    add_custom_target(
+        _cargo-build_${target_name}
+        # Build crate
+        COMMAND
+            ${CMAKE_COMMAND} -E env
+                "${build_env_variable_genex}"
+                "${global_rustflags_genex}"
+                "${cargo_target_linker}"
+                "${corrosion_cc_rs_flags}"
+                "${cargo_library_path}"
+                "CORROSION_BUILD_DIR=${CMAKE_CURRENT_BINARY_DIR}"
+                "CARGO_BUILD_RUSTC=${rustc_bin}"
+            "${cargo_bin}"
+                rustc
+                ${cargo_rustc_filter}
+                ${cargo_target_option}
+                ${_CORROSION_VERBOSE_OUTPUT_FLAG}
+                ${all_features_arg}
+                ${no_default_features_arg}
+                ${features_genex}
+                --package ${package_name}
+                --manifest-path "${path_to_toml}"
+                --target-dir "${cargo_target_dir}"
+                ${cargo_profile}
+                ${flags_genex}
+                # Any arguments to cargo must be placed before this line
+                ${local_rustflags_delimiter}
+                ${local_rustflags_genex}
+
+        # Note: Adding `build_byproducts` (the byproducts in the cargo target directory) here
+        # causes CMake to fail during the Generate stage, because the target `target_name` was not
+        # found. I don't know why this happens, so we just don't specify byproducts here and
+        # only specify the actual byproducts in the `POST_BUILD` custom command that copies the
+        # byproducts to the final destination.
+        # BYPRODUCTS  ${build_byproducts}
+        # 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
+        COMMAND_EXPAND_LISTS
+        VERBATIM
+    )
+
+    # User exposed custom target, that depends on the internal target.
+    # Corrosion post build steps are added on the internal target, which
+    # ensures that they run before any user defined post build steps on this
+    # target.
+    add_custom_target(
+        cargo-build_${target_name}
+        ALL
+    )
+    add_dependencies(cargo-build_${target_name} _cargo-build_${target_name})
+
+    # Add custom target before actual build that user defined custom commands (e.g. code generators) can
+    # use as a hook to do something before the build. This mainly exists to not expose the `_cargo-build` targets.
+    add_custom_target(cargo-prebuild_${target_name})
+    add_dependencies(_cargo-build_${target_name} cargo-prebuild_${target_name})
+    if(NOT TARGET cargo-prebuild)
+        add_custom_target(cargo-prebuild)
+    endif()
+    add_dependencies(cargo-prebuild cargo-prebuild_${target_name})
+
+    add_custom_target(
+        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
+    )
+
+    if (NOT TARGET cargo-clean)
+        add_custom_target(cargo-clean)
+    endif()
+    add_dependencies(cargo-clean cargo-clean_${target_name})
+endfunction()
+
+#[=======================================================================[.md:
+ANCHOR: corrosion-import-crate
+```cmake
+corrosion_import_crate(
+        MANIFEST_PATH <path/to/cargo.toml>
+        [ALL_FEATURES]
+        [NO_DEFAULT_FEATURES]
+        [NO_STD]
+        [NO_LINKER_OVERRIDE]
+        [LOCKED]
+        [FROZEN]
+        [PROFILE <cargo-profile>]
+        [IMPORTED_CRATES <variable-name>]
+        [CRATE_TYPES <crate_type1> ... <crate_typeN>]
+        [CRATES <crate1> ... <crateN>]
+        [FEATURES <feature1> ... <featureN>]
+        [FLAGS <flag1> ... <flagN>]
+)
+```
+* **MANIFEST_PATH**: Path to a [Cargo.toml Manifest] file.
+* **ALL_FEATURES**: Equivalent to [--all-features] passed to cargo build
+* **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)
+* **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)
+* **IMPORTED_CRATES**: Save the list of imported crates into the variable with the provided name in the current scope.
+* **CRATE_TYPES**: Only import the specified crate types. Valid values: `staticlib`, `cdylib`, `bin`.
+* **CRATES**: Only import the specified crates from a workspace. Values: Crate names.
+* **FEATURES**: Enable the specified features. Equivalent to [--features] passed to `cargo build`.
+* **FLAGS**:  Arbitrary flags to `cargo build`.
+
+[custom profile]: https://doc.rust-lang.org/cargo/reference/profiles.html#custom-profiles
+[--all-features]: https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options
+[--no-default-features]: https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options
+[--features]: https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options
+[`--locked`]: https://doc.rust-lang.org/cargo/commands/cargo.html#manifest-options
+[`--frozen`]: https://doc.rust-lang.org/cargo/commands/cargo.html#manifest-options
+[Cargo.toml Manifest]: https://doc.rust-lang.org/cargo/appendix/glossary.html#manifest
+
+ANCHOR_END: corrosion-import-crate
+#]=======================================================================]
+function(corrosion_import_crate)
+    set(OPTIONS ALL_FEATURES NO_DEFAULT_FEATURES NO_STD NO_LINKER_OVERRIDE LOCKED FROZEN)
+    set(ONE_VALUE_KEYWORDS MANIFEST_PATH PROFILE IMPORTED_CRATES)
+    set(MULTI_VALUE_KEYWORDS CRATE_TYPES CRATES FEATURES FLAGS)
+    cmake_parse_arguments(COR "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}" ${ARGN})
+    list(APPEND CMAKE_MESSAGE_CONTEXT "corrosion_import_crate")
+
+    if(DEFINED COR_UNPARSED_ARGUMENTS)
+        message(AUTHOR_WARNING "Unexpected arguments: " ${COR_UNPARSED_ARGUMENTS}
+            "\nCorrosion will ignore these unexpected arguments."
+            )
+    endif()
+    if(DEFINED COR_KEYWORDS_MISSING_VALUES)
+        message(DEBUG "Note: the following keywords passed to corrosion_import_crate had no associated value(s): "
+            ${COR_KEYWORDS_MISSING_VALUES}
+        )
+    endif()
+    if (NOT DEFINED COR_MANIFEST_PATH)
+        message(FATAL_ERROR "MANIFEST_PATH is a required keyword to corrosion_add_crate")
+    endif()
+    _corrosion_option_passthrough_helper(NO_LINKER_OVERRIDE COR no_linker_override)
+    _corrosion_option_passthrough_helper(LOCKED COR locked)
+    _corrosion_option_passthrough_helper(FROZEN COR frozen)
+    _corrosion_arg_passthrough_helper(CRATES COR crate_allowlist)
+    _corrosion_arg_passthrough_helper(CRATE_TYPES COR crate_types)
+
+    if(COR_PROFILE)
+        if(Rust_VERSION VERSION_LESS 1.57.0)
+            message(FATAL_ERROR "Selecting custom profiles via `PROFILE` requires at least rust 1.57.0, but you "
+                        "have ${Rust_VERSION}."
+        )
+        # The profile name could be part of a Generator expression, so this won't catch all occurences.
+        # Since it is hard to add an error message for genex, we don't do that here.
+        elseif("${COR_PROFILE}" STREQUAL "test" OR "${COR_PROFILE}" STREQUAL "bench")
+            message(FATAL_ERROR "Corrosion does not support building Rust crates with the cargo profiles"
+                    " `test` or `bench`. These profiles add a hash to the output artifact name that we"
+                    " cannot predict. Please consider using a custom cargo profile which inherits from the"
+                    " built-in profile instead."
+            )
+        endif()
+    endif()
+
+    if (NOT IS_ABSOLUTE "${COR_MANIFEST_PATH}")
+        set(COR_MANIFEST_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${COR_MANIFEST_PATH})
+    endif()
+
+    set(additional_cargo_flags ${COR_FLAGS})
+
+    if(COR_LOCKED AND NOT "--locked" IN_LIST additional_cargo_flags)
+        list(APPEND additional_cargo_flags  "--locked")
+    endif()
+    if(COR_FROZEN AND NOT "--frozen" IN_LIST additional_cargo_flags)
+        list(APPEND additional_cargo_flags  "--frozen")
+    endif()
+
+    set(imported_crates "")
+    if (CORROSION_NATIVE_TOOLING)
+        get_filename_component(manifest_directory "${COR_MANIFEST_PATH}" DIRECTORY)
+        get_filename_component(toml_dir_name ${manifest_directory} NAME)
+
+        set(
+            generated_cmake
+            "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/corrosion/${toml_dir_name}.dir/cargo-build.cmake"
+        )
+
+        if (CMAKE_VS_PLATFORM_NAME)
+            set (_CORROSION_CONFIGURATION_ROOT --configuration-root ${CMAKE_VS_PLATFORM_NAME})
+        endif()
+
+        set(crates_args)
+        foreach(crate ${COR_CRATES})
+            list(APPEND crates_args --crates ${crate})
+        endforeach()
+        if(DEFINED COR_CRATE_TYPES)
+            set(crate_types "--crate-type=${COR_CRATE_TYPES}")
+        endif()
+
+        list(APPEND passthrough_to_acb_args ${no_linker_override})
+        if(passthrough_to_acb_args)
+            # 31 == 0x1f
+            string(ASCII 31 unit_seperator)
+            list(JOIN passthrough_to_acb_args "${unit_seperator}" joined_args)
+            set(passthrough_to_acb "--passthrough-acb=${joined_args}")
+        endif()
+
+        execute_process(
+            COMMAND
+                ${_CORROSION_GENERATOR}
+                    --manifest-path ${COR_MANIFEST_PATH}
+                    gen-cmake
+                        ${_CORROSION_CONFIGURATION_ROOT}
+                        ${crates_args}
+                        ${crate_types}
+                        --imported-crates=imported_crates
+                        ${passthrough_to_acb}
+                        -o ${generated_cmake}
+            WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+            RESULT_VARIABLE ret)
+
+        if (NOT ret EQUAL "0")
+            message(FATAL_ERROR "corrosion-generator failed")
+        endif()
+
+        include(${generated_cmake})
+    else()
+        _generator_add_cargo_targets(
+            MANIFEST_PATH
+                "${COR_MANIFEST_PATH}"
+            IMPORTED_CRATES
+                imported_crates
+            ${crate_allowlist}
+            ${crate_types}
+            ${no_linker_override}
+        )
+    endif()
+
+    # Not target props yet:
+    # NO_STD
+    # NO_LINKER_OVERRIDE # We could simply zero INTERFACE_CORROSION_LINKER if this is set.
+    # LOCKED / FROZEN get merged into FLAGS after cargo metadata.
+
+    # Initialize the target properties with the arguments to corrosion_import_crate.
+    set_target_properties(
+            ${imported_crates}
+            PROPERTIES
+                "${_CORR_PROP_ALL_FEATURES}" "${COR_ALL_FEATURES}"
+                "${_CORR_PROP_NO_DEFAULT_FEATURES}" "${COR_NO_DEFAULT_FEATURES}"
+                "${_CORR_PROP_FEATURES}" "${COR_FEATURES}"
+                INTERFACE_CORROSION_CARGO_PROFILE "${COR_PROFILE}"
+                INTERFACE_CORROSION_CARGO_FLAGS "${additional_cargo_flags}"
+    )
+
+    # _CORR_PROP_ENV_VARS
+    if(DEFINED COR_IMPORTED_CRATES)
+        set(${COR_IMPORTED_CRATES} ${imported_crates} PARENT_SCOPE)
+    endif()
+endfunction()
+
+function(corrosion_set_linker_language target_name language)
+    message(FATAL_ERROR "corrosion_set_linker_language was deprecated and removed."
+            "Please use corrosion_set_linker and set a specific linker.")
+endfunction()
+
+function(corrosion_set_linker target_name linker)
+    if(NOT linker)
+        message(FATAL_ERROR "The linker passed to `corrosion_set_linker` may not be empty")
+    elseif(NOT TARGET "${target_name}")
+        message(FATAL_ERROR "The target `${target_name}` does not exist.")
+    endif()
+    if(MSVC)
+        message(WARNING "Explicitly setting the linker with the MSVC toolchain is currently not supported and ignored")
+    endif()
+
+    if(TARGET "${target_name}-static" AND NOT TARGET "${target_name}-shared")
+        message(WARNING "The target ${target_name} builds a static library."
+            "The linker is never invoked for a static library so specifying a linker has no effect."
+        )
+    endif()
+
+    set_property(
+        TARGET ${target_name}
+        PROPERTY INTERFACE_CORROSION_LINKER "${linker}"
+    )
+endfunction()
+
+function(corrosion_set_hostbuild target_name)
+    # Configure the target to be compiled for the Host target and ignore any cross-compile configuration.
+    set_property(
+            TARGET ${target_name}
+            PROPERTY ${_CORR_PROP_HOST_BUILD} 1
+    )
+endfunction()
+
+# Add flags for rustc (RUSTFLAGS) which affect the target and all of it's Rust dependencies
+#
+# Additional rustflags may be passed as optional parameters after rustflag.
+# Please note, that if you import multiple targets from a package or workspace, but set different
+# Rustflags via this function, the Rust dependencies will have to be rebuilt when changing targets.
+# Consider `corrosion_add_target_local_rustflags()` as an alternative to avoid this.
+function(corrosion_add_target_rustflags target_name rustflag)
+    # Additional rustflags may be passed as optional parameters after rustflag.
+    set_property(
+            TARGET ${target_name}
+            APPEND
+            PROPERTY INTERFACE_CORROSION_RUSTFLAGS ${rustflag} ${ARGN}
+    )
+endfunction()
+
+# Add flags for rustc (RUSTFLAGS) which only affect the target, but none of it's (Rust) dependencies
+#
+# Additional rustflags may be passed as optional parameters after rustc_flag.
+function(corrosion_add_target_local_rustflags target_name rustc_flag)
+    # Set Rustflags via `cargo rustc` which only affect the current crate, but not dependencies.
+    set_property(
+            TARGET ${target_name}
+            APPEND
+            PROPERTY INTERFACE_CORROSION_LOCAL_RUSTFLAGS ${rustc_flag} ${ARGN}
+    )
+endfunction()
+
+function(corrosion_set_env_vars target_name env_var)
+    # Additional environment variables may be passed as optional parameters after env_var.
+    set_property(
+        TARGET ${target_name}
+        APPEND
+        PROPERTY ${_CORR_PROP_ENV_VARS} ${env_var} ${ARGN}
+    )
+endfunction()
+
+function(corrosion_set_cargo_flags target_name)
+    # corrosion_set_cargo_flags(<target_name> [<flag1> ... ])
+
+    set_property(
+            TARGET ${target_name}
+            APPEND
+            PROPERTY INTERFACE_CORROSION_CARGO_FLAGS ${ARGN}
+    )
+endfunction()
+
+function(corrosion_set_features target_name)
+    # corrosion_set_features(<target_name> [ALL_FEATURES=Bool] [NO_DEFAULT_FEATURES] [FEATURES <feature1> ... ])
+    set(options NO_DEFAULT_FEATURES)
+    set(one_value_args ALL_FEATURES)
+    set(multi_value_args FEATURES)
+    cmake_parse_arguments(
+            PARSE_ARGV 1
+            SET
+            "${options}"
+            "${one_value_args}"
+            "${multi_value_args}"
+    )
+
+    if(DEFINED SET_ALL_FEATURES)
+        set_property(
+                TARGET ${target_name}
+                PROPERTY ${_CORR_PROP_ALL_FEATURES} ${SET_ALL_FEATURES}
+        )
+    endif()
+    if(SET_NO_DEFAULT_FEATURES)
+        set_property(
+                TARGET ${target_name}
+                PROPERTY ${_CORR_PROP_NO_DEFAULT_FEATURES} 1
+        )
+    endif()
+    if(SET_FEATURES)
+        set_property(
+                TARGET ${target_name}
+                APPEND
+                PROPERTY ${_CORR_PROP_FEATURES} ${SET_FEATURES}
+        )
+    endif()
+endfunction()
+
+function(corrosion_link_libraries target_name)
+    if(TARGET "${target_name}-static")
+        message(DEBUG "The target ${target_name} builds a static Rust library."
+                "Calling `target_link_libraries()` instead."
+        )
+        target_link_libraries("${target_name}-static" INTERFACE ${ARGN})
+        if(NOT TARGET "${target_name}-shared")
+            # Early return, since Rust won't invoke the linker for static libraries
+            return()
+        endif()
+    endif()
+    add_dependencies(_cargo-build_${target_name} ${ARGN})
+    foreach(library ${ARGN})
+        set_property(
+            TARGET _cargo-build_${target_name}
+            APPEND
+            PROPERTY CARGO_DEPS_LINKER_LANGUAGES
+            $<TARGET_PROPERTY:${library},LINKER_LANGUAGE>
+        )
+
+        corrosion_add_target_local_rustflags(${target_name} "-L$<TARGET_LINKER_FILE_DIR:${library}>")
+        corrosion_add_target_local_rustflags(${target_name} "-l$<TARGET_LINKER_FILE_BASE_NAME:${library}>")
+    endforeach()
+endfunction()
+
+function(corrosion_install)
+    # Default install dirs
+    include(GNUInstallDirs)
+
+    # Parse arguments to corrosion_install
+    list(GET ARGN 0 INSTALL_TYPE)
+    list(REMOVE_AT ARGN 0)
+
+    # The different install types that are supported. Some targets may have more than one of these
+    # types. For example, on Windows, a shared library will have both an ARCHIVE component and a
+    # RUNTIME component.
+    set(INSTALL_TARGET_TYPES ARCHIVE LIBRARY RUNTIME PRIVATE_HEADER PUBLIC_HEADER)
+
+    # Arguments to each install target type
+    set(OPTIONS)
+    set(ONE_VALUE_ARGS DESTINATION)
+    set(MULTI_VALUE_ARGS PERMISSIONS CONFIGURATIONS)
+    set(TARGET_ARGS ${OPTIONS} ${ONE_VALUE_ARGS} ${MULTI_VALUE_ARGS})
+
+    if (INSTALL_TYPE STREQUAL "TARGETS")
+        # corrosion_install(TARGETS ... [EXPORT <export-name>]
+        #                   [[ARCHIVE|LIBRARY|RUNTIME|PRIVATE_HEADER|PUBLIC_HEADER]
+        #                    [DESTINATION <dir>]
+        #                    [PERMISSIONS permissions...]
+        #                    [CONFIGURATIONS [Debug|Release|...]]
+        #                   ] [...])
+
+        # Extract targets
+        set(INSTALL_TARGETS)
+        list(LENGTH ARGN ARGN_LENGTH)
+        set(DELIMITERS EXPORT ${INSTALL_TARGET_TYPES} ${TARGET_ARGS})
+        while(ARGN_LENGTH)
+            # If we hit another keyword, stop - we've found all the targets
+            list(GET ARGN 0 FRONT)
+            if (FRONT IN_LIST DELIMITERS)
+                break()
+            endif()
+
+            list(APPEND INSTALL_TARGETS ${FRONT})
+            list(REMOVE_AT ARGN 0)
+
+            # Update ARGN_LENGTH
+            list(LENGTH ARGN ARGN_LENGTH)
+        endwhile()
+
+        # Check if there are any args left before proceeding
+        list(LENGTH ARGN ARGN_LENGTH)
+        if (ARGN_LENGTH)
+            list(GET ARGN 0 FRONT)
+            if (FRONT STREQUAL "EXPORT")
+                list(REMOVE_AT ARGN 0) # Pop "EXPORT"
+
+                list(GET ARGN 0 EXPORT_NAME)
+                list(REMOVE_AT ARGN 0) # Pop <export-name>
+                message(FATAL_ERROR "EXPORT keyword not yet implemented!")
+            endif()
+        endif()
+
+        # Loop over all arguments and get options for each install target type
+        list(LENGTH ARGN ARGN_LENGTH)
+        while(ARGN_LENGTH)
+            # Check if we're dealing with arguments for a specific install target type, or with
+            # default options for all target types.
+            list(GET ARGN 0 FRONT)
+            if (FRONT IN_LIST INSTALL_TARGET_TYPES)
+                set(INSTALL_TARGET_TYPE ${FRONT})
+                list(REMOVE_AT ARGN 0)
+            else()
+                set(INSTALL_TARGET_TYPE DEFAULT)
+            endif()
+
+            # Gather the arguments to this install type
+            set(ARGS)
+            while(ARGN_LENGTH)
+                # If the next keyword is an install target type, then break - arguments have been
+                # gathered.
+                list(GET ARGN 0 FRONT)
+                if (FRONT IN_LIST INSTALL_TARGET_TYPES)
+                    break()
+                endif()
+
+                list(APPEND ARGS ${FRONT})
+                list(REMOVE_AT ARGN 0)
+
+                list(LENGTH ARGN ARGN_LENGTH)
+            endwhile()
+
+            # Parse the arguments and register the file install
+            cmake_parse_arguments(
+                COR "${OPTIONS}" "${ONE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGS})
+
+            if (COR_DESTINATION)
+                set(COR_INSTALL_${INSTALL_TARGET_TYPE}_DESTINATION ${COR_DESTINATION})
+            endif()
+
+            if (COR_PERMISSIONS)
+                set(COR_INSTALL_${INSTALL_TARGET_TYPE}_PERMISSIONS ${COR_PERMISSIONS})
+            endif()
+
+            if (COR_CONFIGURATIONS)
+                set(COR_INSTALL_${INSTALL_TARGET_TYPE}_CONFIGURATIONS ${COR_CONFIGURATIONS})
+            endif()
+
+            # Update ARG_LENGTH
+            list(LENGTH ARGN ARGN_LENGTH)
+        endwhile()
+
+        # Default permissions for all files
+        set(DEFAULT_PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
+
+        # Loop through each install target and register file installations
+        foreach(INSTALL_TARGET ${INSTALL_TARGETS})
+            # Don't both implementing target type differentiation using generator expressions since
+            # TYPE cannot change after target creation
+            get_property(
+                TARGET_TYPE
+                TARGET ${INSTALL_TARGET} PROPERTY TYPE
+            )
+
+            # Install executable files first
+            if (TARGET_TYPE STREQUAL "EXECUTABLE")
+                if (DEFINED COR_INSTALL_RUNTIME_DESTINATION)
+                    set(DESTINATION ${COR_INSTALL_RUNTIME_DESTINATION})
+                elseif (DEFINED COR_INSTALL_DEFAULT_DESTINATION)
+                    set(DESTINATION ${COR_INSTALL_DEFAULT_DESTINATION})
+                else()
+                    set(DESTINATION ${CMAKE_INSTALL_BINDIR})
+                endif()
+
+                if (DEFINED COR_INSTALL_RUNTIME_PERMISSIONS)
+                    set(PERMISSIONS ${COR_INSTALL_RUNTIME_PERMISSIONS})
+                elseif (DEFINED COR_INSTALL_DEFAULT_PERMISSIONS)
+                    set(PERMISSIONS ${COR_INSTALL_DEFAULT_PERMISSIONS})
+                else()
+                    set(
+                        PERMISSIONS
+                        ${DEFAULT_PERMISSIONS} OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE)
+                endif()
+
+                if (DEFINED COR_INSTALL_RUNTIME_CONFIGURATIONS)
+                    set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_RUNTIME_CONFIGURATIONS})
+                elseif (DEFINED COR_INSTALL_DEFAULT_CONFIGURATIONS)
+                    set(CONFIGURATIONS CONFIGURATIONS ${COR_INSTALL_DEFAULT_CONFIGURATIONS})
+                else()
+                    set(CONFIGURATIONS)
+                endif()
+
+                install(
+                    FILES $<TARGET_FILE:${INSTALL_TARGET}>
+                    DESTINATION ${DESTINATION}
+                    PERMISSIONS ${PERMISSIONS}
+                    ${CONFIGURATIONS}
+                )
+            endif()
+        endforeach()
+
+    elseif(INSTALL_TYPE STREQUAL "EXPORT")
+        message(FATAL_ERROR "install(EXPORT ...) not yet implemented")
+    endif()
+endfunction()
+
+#[=======================================================================[.md:
+** EXPERIMENTAL **: This function is currently still considered experimental
+  and is not officially released yet. Feedback and Suggestions are welcome.
+
+ANCHOR: corrosion_add_cxxbridge
+
+```cmake
+corrosion_add_cxxbridge(cxx_target
+        CRATE <imported_target_name>
+        [FILES <file1.rs> <file2.rs>]
+)
+```
+
+Adds build-rules to create C++ bindings using the [cxx] crate.
+
+### Arguments:
+* `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
+
+#### Currently missing arguments
+
+The following arguments to cxxbridge **currently** have no way to be passed by the user:
+- `--cfg`
+- `--cxx-impl-annotations`
+- `--include`
+
+The created rules approximately do the following:
+- Check which version of `cxx` the Rust crate specified by the `CRATE` argument depends on.
+- Check if the exact same version of `cxxbridge-cmd` is installed (available in `PATH`)
+- If not, create a rule to build the exact same version of `cxxbridge-cmd`.
+- Create rules to run `cxxbridge` and generate
+  - The `rust/cxx.h` header
+  - A header and source file for each of the files specified in `FILES`
+- The generated sources (and header include directories) are added to the `cxxtarget` CMake
+  library target.
+
+### Limitations
+
+We currently require the `CRATE` argument to be a target imported by Corrosion, however,
+Corrosion does not import `rlib` only libraries. As a workaround users can add
+`staticlib` to their list of crate kinds. In the future this may be solved more properly,
+by either adding an option to also import Rlib targets (without build rules) or by
+adding a `MANIFEST_PATH` argument to this function, specifying where the crate is.
+
+### Contributing
+
+Specifically some more realistic test / demo projects and feedback about limitations would be
+welcome.
+
+[cxx]: https://github.com/dtolnay/cxx
+
+ANCHOR_END: corrosion_add_cxxbridge
+#]=======================================================================]
+function(corrosion_add_cxxbridge cxx_target)
+    set(OPTIONS)
+    set(ONE_VALUE_KEYWORDS CRATE)
+    set(MULTI_VALUE_KEYWORDS FILES)
+    cmake_parse_arguments(PARSE_ARGV 1 _arg "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}")
+
+    set(required_keywords CRATE FILES)
+    foreach(keyword ${required_keywords})
+        if(NOT DEFINED "_arg_${keyword}")
+            message(FATAL_ERROR "Missing required parameter `${keyword}`.")
+        elseif("${_arg_${keyword}}" STREQUAL "")
+            message(FATAL_ERROR "Required parameter `${keyword}` may not be set to an empty string.")
+        endif()
+    endforeach()
+
+    get_target_property(manifest_path "${_arg_CRATE}" INTERFACE_COR_PACKAGE_MANIFEST_PATH)
+
+    if(NOT EXISTS "${manifest_path}")
+        message(FATAL_ERROR "Internal error: No package manifest found at ${manifest_path}")
+    endif()
+
+    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`")
+    endif()
+
+    # First check if a suitable version of cxxbridge is installed
+    find_program(INSTALLED_CXXBRIDGE cxxbridge PATHS "$ENV{HOME}/.cargo/bin/")
+    mark_as_advanced(INSTALLED_CXXBRIDGE)
+    if(INSTALLED_CXXBRIDGE)
+        execute_process(COMMAND ${INSTALLED_CXXBRIDGE} --version OUTPUT_VARIABLE cxxbridge_version_output)
+        if(cxxbridge_version_output MATCHES "cxxbridge ([0-9]+.[0-9]+.[0-9]+)")
+            set(cxxbridge_version "${CMAKE_MATCH_1}")
+        else()
+            set(cxxbridge_version "")
+        endif()
+    endif()
+
+    set(cxxbridge "")
+    if(cxxbridge_version)
+        if(cxxbridge_version VERSION_EQUAL cxx_required_version)
+            set(cxxbridge "${INSTALLED_CXXBRIDGE}")
+            if(NOT TARGET "cxxbridge_v${cxx_required_version}")
+                # Add an empty target.
+                add_custom_target("cxxbridge_v${cxx_required_version}"
+                    )
+            endif()
+        endif()
+    endif()
+
+    # No suitable version of cxxbridge was installed, so use custom target to build correct version.
+    if(NOT cxxbridge)
+        if(NOT TARGET "cxxbridge_v${cxx_required_version}")
+            add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}/bin/cxxbridge"
+                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
+                    cxxbridge-cmd
+                    --version "${cxx_required_version}"
+                    --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})"
+                )
+            add_custom_target("cxxbridge_v${cxx_required_version}"
+                DEPENDS "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}/bin/cxxbridge"
+                )
+        endif()
+        set(cxxbridge "${CMAKE_BINARY_DIR}/corrosion/cxxbridge_v${cxx_required_version}/bin/cxxbridge")
+    endif()
+
+
+    # The generated folder structure will be of the following form
+    #
+    #    CMAKE_CURRENT_BINARY_DIR
+    #        corrosion_generated
+    #            cxxbridge
+    #                <cxx_target>
+    #                    include
+    #                        <cxx_target>
+    #                            <headers>
+    #                        rust
+    #                            cxx.h
+    #                    src
+    #                        <sourcefiles>
+    #            cbindgen
+    #                ...
+    #            other
+    #                ...
+
+    set(corrosion_generated_dir "${CMAKE_CURRENT_BINARY_DIR}/corrosion_generated")
+    set(generated_dir "${corrosion_generated_dir}/cxxbridge/${cxx_target}")
+    set(header_placement_dir "${generated_dir}/include/${cxx_target}")
+    set(source_placement_dir "${generated_dir}/src")
+
+    add_library(${cxx_target} STATIC)
+    target_include_directories(${cxx_target}
+        PUBLIC
+            $<BUILD_INTERFACE:${generated_dir}/include>
+            $<INSTALL_INTERFACE:include>
+    )
+
+    # 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})
+    endif()
+
+    file(MAKE_DIRECTORY "${generated_dir}/include/rust")
+    add_custom_command(
+            OUTPUT "${generated_dir}/include/rust/cxx.h"
+            COMMAND
+            ${cxxbridge} --header --output "${generated_dir}/include/rust/cxx.h"
+            DEPENDS "cxxbridge_v${cxx_required_version}"
+            COMMENT "Generating rust/cxx.h header"
+    )
+
+    foreach(filepath ${_arg_FILES})
+        get_filename_component(filename ${filepath} NAME_WE)
+        get_filename_component(directory ${filepath} DIRECTORY)
+        set(directory_component "")
+        if(directory)
+            set(directory_component "${directory}/")
+        endif()
+        # todo: convert potentially absolute paths to relative paths..
+        set(cxx_header ${directory_component}${filename}.h)
+        set(cxx_source ${directory_component}${filename}.cpp)
+
+        # todo: not all projects may use the `src` directory.
+        set(rust_source_path "${manifest_dir}/src/${filepath}")
+
+        file(MAKE_DIRECTORY "${header_placement_dir}/${directory}" "${source_placement_dir}/${directory}")
+
+        add_custom_command(
+            OUTPUT
+            "${header_placement_dir}/${cxx_header}"
+            "${source_placement_dir}/${cxx_source}"
+            COMMAND
+                ${cxxbridge} ${rust_source_path} --header --output "${header_placement_dir}/${cxx_header}"
+            COMMAND
+                ${cxxbridge} ${rust_source_path}
+                    --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}"
+        )
+
+        target_sources(${cxx_target}
+            PRIVATE
+                "${header_placement_dir}/${cxx_header}"
+                "${generated_dir}/include/rust/cxx.h"
+                "${source_placement_dir}/${cxx_source}"
+        )
+    endforeach()
+endfunction()
+
+#[=======================================================================[.md:
+ANCHOR: corrosion_cbindgen
+```cmake
+corrosion_cbindgen(
+        TARGET <imported_target_name>
+        HEADER_NAME <output_header_name>
+        [MANIFEST_DIRECTORY <package_manifest_directory>]
+        [CBINDGEN_VERSION <version>]
+        [FLAGS <flag1> ... <flagN>]
+)
+```
+
+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.
+
+
+* **TARGET**: The name of an imported Rust library target (crate), 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`.
+
+* **MANIFEST_DIRECTORY**: 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.
+
+* **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
+
+ANCHOR_END: corrosion_cbindgen
+#]=======================================================================]
+function(corrosion_experimental_cbindgen)
+    set(OPTIONS "")
+    set(ONE_VALUE_KEYWORDS TARGET MANIFEST_DIRECTORY HEADER_NAME 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)
+    foreach(keyword ${required_keywords})
+        if(NOT DEFINED "CCN_${keyword}")
+            message(FATAL_ERROR "Missing required parameter `${keyword}`.")
+        elseif("${CCN_${keyword}}" STREQUAL "")
+            message(FATAL_ERROR "Required parameter `${keyword}` may not be set to an empty string.")
+        endif()
+    endforeach()
+    set(rust_target "${CCN_TARGET}")
+    unset(package_manifest_dir)
+
+
+    set(hostbuild_override "$<BOOL:$<TARGET_PROPERTY:${rust_target},${_CORR_PROP_HOST_BUILD}>>")
+    set(cbindgen_target_triple "$<IF:${hostbuild_override},${_CORROSION_RUST_CARGO_HOST_TARGET},${_CORROSION_RUST_CARGO_TARGET}>")
+
+    if(TARGET "${rust_target}")
+        get_target_property(package_manifest_path "${rust_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)
+        # 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."
+            )
+        else()
+            set(package_manifest_dir "${CCN_MANIFEST_DIRECTORY}")
+        endif()
+    endif()
+
+    unset(rust_cargo_package)
+    if(NOT DEFINED CCN_CARGO_PACKAGE)
+        get_target_property(rust_cargo_package "${rust_target}" INTERFACE_COR_CARGO_PACKAGE_NAME )
+        if(NOT rust_cargo_package)
+            message(FATAL_ERROR "Could not determine cargo package name for cbindgen!")
+        endif()
+    else()
+        set(rust_cargo_package "${CCN_CARGO_PACKAGE}")
+    endif()
+    message(STATUS "Using package ${rust_cargo_package} as crate for cbindgen")
+
+
+    set(output_header_name "${CCN_HEADER_NAME}")
+
+    find_program(installed_cbindgen cbindgen)
+
+    # Install the newest cbindgen version into our build tree.
+    if(installed_cbindgen)
+        set(cbindgen "${installed_cbindgen}")
+    else()
+        set(local_cbindgen_install_dir "${CMAKE_BINARY_DIR}/corrosion/cbindgen")
+        unset(executable_postfix)
+        if(Rust_CARGO_HOST_OS STREQUAL "windows")
+            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
+                    cbindgen
+                    --root "${local_cbindgen_install_dir}"
+                    ${_CORROSION_QUIET_OUTPUT_FLAG}
+                COMMENT "Building cbindgen"
+                )
+            add_custom_target("_corrosion_cbindgen"
+                DEPENDS "${cbindgen}"
+                )
+        endif()
+    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(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}
+            INTERFACE
+            FILE_SET HEADERS
+            BASE_DIRS "${header_placement_dir}"
+            FILES "${header_placement_dir}/${output_header_name}"
+        )
+    else()
+        # Note: not clear to me how install would best work before CMake 3.23
+        target_include_directories(${rust_target}
+            INTERFACE
+            $<BUILD_INTERFACE:${header_placement_dir}>
+            $<INSTALL_INTERFACE:include>
+            )
+    endif()
+
+    # 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}")
+
+    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}")
+
+    # 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 )
+    if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.22")
+        add_custom_command(
+            OUTPUT
+            "${generated_header}"
+            COMMAND
+            "${CMAKE_COMMAND}" -E env
+                TARGET="${cbindgen_target_triple}"
+                "${cbindgen}"
+                        --output "${generated_header}"
+                        --crate "${rust_cargo_package}"
+                        ${depfile_cbindgen_arg}
+                        ${CCN_FLAGS}
+            COMMENT "Generate cbindgen bindings for package ${rust_cargo_package} and output header ${generated_header}"
+            DEPFILE "${generated_depfile}"
+            COMMAND_EXPAND_LISTS
+            WORKING_DIRECTORY "${package_manifest_dir}"
+        )
+        add_custom_target("_corrosion_cbindgen_${rust_target}_bindings_${header_identifier}"
+                          DEPENDS "${generated_header}"
+                          COMMENT "Generate ${generated_header} for ${rust_target}"
+        )
+    else()
+        add_custom_target("_corrosion_cbindgen_${rust_target}_bindings_${header_identifier}"
+                          "${CMAKE_COMMAND}" -E env
+                              TARGET="${cbindgen_target_triple}"
+                              "${cbindgen}"
+                              --output "${generated_header}"
+                              --crate "${rust_cargo_package}"
+                              ${depfile_cbindgen_arg}
+                              ${CCN_FLAGS}
+                          COMMENT "Generate ${generated_header} for ${rust_target}"
+                          COMMAND_EXPAND_LISTS
+                          WORKING_DIRECTORY "${package_manifest_dir}"
+        )
+    endif()
+
+    if(NOT installed_cbindgen)
+        add_custom_command(
+            OUTPUT "${generated_header}"
+            APPEND
+            DEPENDS _corrosion_cbindgen
+        )
+    endif()
+
+    if(NOT TARGET "_corrosion_cbindgen_${rust_target}_bindings")
+        add_custom_target(_corrosion_cbindgen_${rust_target}_bindings
+                COMMENT "Generate cbindgen bindings for package ${rust_cargo_package}"
+        )
+    endif()
+
+    add_dependencies("_corrosion_cbindgen_${rust_target}_bindings" "_corrosion_cbindgen_${rust_target}_bindings_${header_identifier}")
+    add_dependencies(${rust_target} "_corrosion_cbindgen_${rust_target}_bindings")
+endfunction()
+
+# Parse the version of a Rust package from it's package manifest (Cargo.toml)
+function(corrosion_parse_package_version package_manifest_path out_package_version)
+    if(NOT EXISTS "${package_manifest_path}")
+        message(FATAL_ERROR "Package manifest `${package_manifest_path}` does not exist.")
+    endif()
+
+    file(READ "${package_manifest_path}" package_manifest)
+
+    # Find the package table. It may contain arrays, so match until \n\[, which should mark the next
+    # table. Note: backslashes must be doubled to escape the backslash for the bracket. LF is single
+    # backslash however. On windows the line also ends in \n, so matching against \n\[ is sufficient
+    # to detect an opening bracket on a new line.
+    set(package_table_regex "\\[package\\](.*)\n\\[")
+
+    string(REGEX MATCH "${package_table_regex}" _package_table "${package_manifest}")
+
+    if(CMAKE_MATCH_COUNT EQUAL "1")
+        set(package_table "${CMAKE_MATCH_1}")
+    else()
+        message(DEBUG
+                "Failed to find `[package]` table in package manifest `${package_manifest_path}`.\n"
+                "Matches: ${CMAKE_MATCH_COUNT}\n"
+        )
+        set(${out_package_version}
+            "NOTFOUND"
+            PARENT_SCOPE
+        )
+    endif()
+    # Match `version = "0.3.2"`, `"version" = "0.3.2" Contains one matching group for the version
+    set(version_regex "[\r]?\n[\"']?version[\"']?[ \t]*=[ \t]*[\"']([0-9\.]+)[\"']")
+
+    string(REGEX MATCH "${version_regex}" _version "${package_table}")
+
+    if("${package_table}" MATCHES "${version_regex}")
+        set(${out_package_version}
+            "${CMAKE_MATCH_1}"
+            PARENT_SCOPE
+        )
+    else()
+        message(DEBUG "Failed to extract package version from manifest `${package_manifest_path}`.")
+        set(${out_package_version}
+            "NOTFOUND"
+            PARENT_SCOPE
+        )
+    endif()
+endfunction()
+
+# Helper macro to pass through an optional `OPTION` argument parsed via `cmake_parse_arguments`
+# to another function that takes the same OPTION.
+# If the option was set, then the variable <var_name> will be set to the same option name again,
+# otherwise <var_name> will be unset.
+macro(_corrosion_option_passthrough_helper option_name prefix var_name)
+    if(${${prefix}_${option_name}})
+        set("${var_name}" "${option_name}")
+    else()
+        unset("${var_name}")
+    endif()
+endmacro()
+
+# Helper macro to pass through an optional argument with value(s), parsed via `cmake_parse_arguments`,
+# to another function that takes the same keyword + associated values.
+# If the argument was given, then the variable <var_name> will be a list of the argument name and the values,
+# which will be expanded, when calling the function (assuming no quotes).
+macro(_corrosion_arg_passthrough_helper arg_name prefix var_name)
+    if(DEFINED "${prefix}_${arg_name}")
+        set("${var_name}" "${arg_name}" "${${prefix}_${arg_name}}")
+    else()
+        unset("${var_name}")
+    endif()
+endmacro()
+
+list(POP_BACK CMAKE_MESSAGE_CONTEXT)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/cmake/CorrosionConfig.cmake.in	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,18 @@
+@PACKAGE_INIT@
+
+if (Corrosion_FOUND)
+    return()
+endif()
+
+list(APPEND CMAKE_MODULE_PATH "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_DATADIR@/cmake")
+
+set(CORROSION_NATIVE_TOOLING_INSTALLED @CORROSION_NATIVE_TOOLING@)
+if(CORROSION_NATIVE_TOOLING_INSTALLED AND NOT TARGET Corrosion::Generator)
+    add_executable(Corrosion::Generator IMPORTED GLOBAL)
+
+    set_property(
+        TARGET Corrosion::Generator
+        PROPERTY IMPORTED_LOCATION "@CMAKE_INSTALL_FULL_LIBEXECDIR@/corrosion-generator")
+endif()
+
+include(Corrosion)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/cmake/CorrosionGenerator.cmake	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,326 @@
+function(_cargo_metadata out manifest)
+    set(OPTIONS LOCKED FROZEN)
+    set(ONE_VALUE_KEYWORDS "")
+    set(MULTI_VALUE_KEYWORDS "")
+    cmake_parse_arguments(PARSE_ARGV 2 CM "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}")
+
+    list(APPEND CMAKE_MESSAGE_CONTEXT "_cargo_metadata")
+
+    if(DEFINED CM_UNPARSED_ARGUMENTS)
+        message(FATAL_ERROR "Internal error - unexpected arguments: ${CM_UNPARSED_ARGUMENTS}")
+    elseif(DEFINED CM_KEYWORDS_MISSING_VALUES)
+        message(FATAL_ERROR "Internal error - the following keywords had no associated value(s):"
+            "${CM_KEYWORDS_MISSING_VALUES}")
+    endif()
+
+    set(cargo_locked "")
+    set(cargo_frozen "")
+    if(LOCKED)
+        set(cargo_locked "--locked")
+    endif()
+    if(FROZEN)
+        set(cargo_frozen "--frozen")
+    endif()
+    execute_process(
+        COMMAND
+            ${CMAKE_COMMAND} -E env
+                "CARGO_BUILD_RUSTC=${_CORROSION_RUSTC}"
+                "${_CORROSION_CARGO}"
+                    metadata
+                        --manifest-path "${manifest}"
+                        --format-version 1
+                        # We don't care about non-workspace dependencies
+                        --no-deps
+                        ${cargo_locked}
+                        ${cargo_frozen}
+
+        OUTPUT_VARIABLE json
+        COMMAND_ERROR_IS_FATAL ANY
+    )
+
+    set(${out} "${json}" PARENT_SCOPE)
+endfunction()
+
+# Add targets (crates) of one package
+function(_generator_add_package_targets)
+    set(OPTIONS NO_LINKER_OVERRIDE)
+    set(ONE_VALUE_KEYWORDS WORKSPACE_MANIFEST_PATH PACKAGE_MANIFEST_PATH PACKAGE_NAME PACKAGE_VERSION TARGETS_JSON OUT_CREATED_TARGETS)
+    set(MULTI_VALUE_KEYWORDS CRATE_TYPES)
+    cmake_parse_arguments(PARSE_ARGV 0 GAPT "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}")
+
+    if(DEFINED GAPT_UNPARSED_ARGUMENTS)
+        message(FATAL_ERROR "Internal error - unexpected arguments: ${GAPT_UNPARSED_ARGUMENTS}")
+    elseif(DEFINED GAPT_KEYWORDS_MISSING_VALUES)
+        message(FATAL_ERROR "Internal error - the following keywords had no associated value(s):"
+                    "${GAPT_KEYWORDS_MISSING_VALUES}")
+    endif()
+
+    _corrosion_option_passthrough_helper(NO_LINKER_OVERRIDE GAPT no_linker_override)
+
+    set(workspace_manifest_path "${GAPT_WORKSPACE_MANIFEST_PATH}")
+    set(package_manifest_path "${GAPT_PACKAGE_MANIFEST_PATH}")
+    set(package_name "${GAPT_PACKAGE_NAME}")
+    set(package_version "${GAPT_PACKAGE_VERSION}")
+    set(targets "${GAPT_TARGETS_JSON}")
+    set(out_created_targets "${GAPT_OUT_CREATED_TARGETS}")
+    set(crate_types "${GAPT_CRATE_TYPES}")
+
+    set(corrosion_targets "")
+
+    file(TO_CMAKE_PATH "${package_manifest_path}" manifest_path)
+
+    string(JSON targets_len LENGTH "${targets}")
+    math(EXPR targets_len-1 "${targets_len} - 1")
+
+    message(DEBUG "Found ${targets_len} targets in package ${package_name}")
+
+    foreach(ix RANGE ${targets_len-1})
+        string(JSON target GET "${targets}" ${ix})
+        string(JSON target_name GET "${target}" "name")
+        string(JSON target_kind GET "${target}" "kind")
+        string(JSON target_kind_len LENGTH "${target_kind}")
+
+        math(EXPR target_kind_len-1 "${target_kind_len} - 1")
+        set(kinds)
+        foreach(ix RANGE ${target_kind_len-1})
+            string(JSON kind GET "${target_kind}" ${ix})
+            if(NOT crate_types OR ${kind} IN_LIST crate_types)
+                list(APPEND kinds ${kind})
+            endif()
+        endforeach()
+
+        if(TARGET "${target_name}"
+            AND ("staticlib" IN_LIST kinds OR "cdylib" IN_LIST kinds OR "bin" IN_LIST kinds)
+            )
+            message(WARNING "Failed to import Rust crate ${target_name} (kind: `${target_kind}`) because a target "
+                "with the same name already exists. Skipping this target.\n"
+                "Help: If you are importing a package which exposes both a `lib` and "
+                "a `bin` target, please consider explicitly naming the targets in your `Cargo.toml` manifest.\n"
+                "Note: If you have multiple different packages which have targets with the same name, please note that "
+                "this is currently not supported by Corrosion. Feel free to open an issue on Github to request "
+                "supporting this scenario."
+                )
+            # Skip this target to prevent a hard error.
+            continue()
+        endif()
+
+        if("staticlib" IN_LIST kinds OR "cdylib" IN_LIST kinds)
+            # Explicitly set library names have always been forbidden from using dashes (by cargo).
+            # Starting with Rust 1.79, names inherited from the package name will have dashes replaced
+            # by underscores too. Corrosion will thus replace dashes with underscores, to make the target
+            # name consistent independent of the Rust version. `bin` target names are not affected.
+            # See https://github.com/corrosion-rs/corrosion/issues/501 for more details.
+            string(REPLACE "\-" "_" target_name "${target_name}")
+
+            set(archive_byproducts "")
+            set(shared_lib_byproduct "")
+            set(pdb_byproduct "")
+
+            _corrosion_add_library_target(
+                WORKSPACE_MANIFEST_PATH "${workspace_manifest_path}"
+                TARGET_NAME "${target_name}"
+                LIB_KINDS ${kinds}
+                OUT_ARCHIVE_OUTPUT_BYPRODUCTS archive_byproducts
+                OUT_SHARED_LIB_BYPRODUCTS shared_lib_byproduct
+                OUT_PDB_BYPRODUCT pdb_byproduct
+            )
+
+            set(byproducts "")
+            list(APPEND byproducts "${archive_byproducts}" "${shared_lib_byproduct}" "${pdb_byproduct}")
+
+            set(cargo_build_out_dir "")
+            _add_cargo_build(
+                cargo_build_out_dir
+                PACKAGE ${package_name}
+                TARGET ${target_name}
+                MANIFEST_PATH "${manifest_path}"
+                WORKSPACE_MANIFEST_PATH "${workspace_manifest_path}"
+                TARGET_KINDS "${kinds}"
+                BYPRODUCTS "${byproducts}"
+                # Optional
+                ${no_linker_override}
+            )
+            if(archive_byproducts)
+                _corrosion_copy_byproducts(
+                    ${target_name} ARCHIVE_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${archive_byproducts}"
+                )
+            endif()
+            if(shared_lib_byproduct)
+                _corrosion_copy_byproducts(
+                    ${target_name} LIBRARY_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${shared_lib_byproduct}"
+                )
+            endif()
+            if(pdb_byproduct)
+                _corrosion_copy_byproducts(
+                    ${target_name} PDB_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${pdb_byproduct}"
+                )
+            endif()
+            list(APPEND corrosion_targets ${target_name})
+            set_property(TARGET "${target_name}" PROPERTY INTERFACE_COR_CARGO_PACKAGE_NAME "${package_name}" )
+        # Note: "bin" is mutually exclusive with "staticlib/cdylib", since `bin`s are seperate crates from libraries.
+        elseif("bin" IN_LIST kinds)
+            set(bin_byproduct "")
+            set(pdb_byproduct "")
+            _corrosion_add_bin_target("${workspace_manifest_path}" "${target_name}"
+                "bin_byproduct" "pdb_byproduct"
+            )
+
+            set(byproducts "")
+            list(APPEND byproducts "${bin_byproduct}" "${pdb_byproduct}")
+
+            set(cargo_build_out_dir "")
+            _add_cargo_build(
+                cargo_build_out_dir
+                PACKAGE "${package_name}"
+                TARGET "${target_name}"
+                MANIFEST_PATH "${manifest_path}"
+                WORKSPACE_MANIFEST_PATH "${workspace_manifest_path}"
+                TARGET_KINDS "bin"
+                BYPRODUCTS "${byproducts}"
+                # Optional
+                ${no_linker_override}
+            )
+            _corrosion_copy_byproducts(
+                    ${target_name} RUNTIME_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${bin_byproduct}"
+            )
+            if(pdb_byproduct)
+                _corrosion_copy_byproducts(
+                        ${target_name} PDB_OUTPUT_DIRECTORY "${cargo_build_out_dir}" "${pdb_byproduct}"
+                )
+            endif()
+            list(APPEND corrosion_targets ${target_name})
+            set_property(TARGET "${target_name}" PROPERTY INTERFACE_COR_CARGO_PACKAGE_NAME "${package_name}" )
+        else()
+            # ignore other kinds (like examples, tests, build scripts, ...)
+        endif()
+    endforeach()
+
+    if(NOT corrosion_targets)
+        message(DEBUG "No relevant targets found in package ${package_name} - Ignoring")
+    else()
+        set_target_properties(${corrosion_targets} PROPERTIES INTERFACE_COR_PACKAGE_MANIFEST_PATH "${package_manifest_path}")
+    endif()
+    set(${out_created_targets} "${corrosion_targets}" PARENT_SCOPE)
+
+endfunction()
+
+# Add all cargo targets defined in the packages defined in the Cargo.toml manifest at
+# `MANIFEST_PATH`.
+function(_generator_add_cargo_targets)
+    set(options NO_LINKER_OVERRIDE)
+    set(one_value_args MANIFEST_PATH IMPORTED_CRATES)
+    set(multi_value_args CRATES CRATE_TYPES)
+    cmake_parse_arguments(
+        GGC
+        "${options}"
+        "${one_value_args}"
+        "${multi_value_args}"
+        ${ARGN}
+    )
+    list(APPEND CMAKE_MESSAGE_CONTEXT "_add_cargo_targets")
+
+    _corrosion_option_passthrough_helper(NO_LINKER_OVERRIDE GGC no_linker_override)
+    _corrosion_arg_passthrough_helper(CRATE_TYPES GGC crate_types)
+
+    _cargo_metadata(json "${GGC_MANIFEST_PATH}")
+    string(JSON packages GET "${json}" "packages")
+    string(JSON workspace_members GET "${json}" "workspace_members")
+
+    string(JSON pkgs_len LENGTH "${packages}")
+    math(EXPR pkgs_len-1 "${pkgs_len} - 1")
+
+    string(JSON ws_mems_len LENGTH ${workspace_members})
+    math(EXPR ws_mems_len-1 "${ws_mems_len} - 1")
+
+    set(created_targets "")
+    set(available_package_names "")
+    foreach(ix RANGE ${pkgs_len-1})
+        string(JSON pkg GET "${packages}" ${ix})
+        string(JSON pkg_id GET "${pkg}" "id")
+        string(JSON pkg_name GET "${pkg}" "name")
+        string(JSON pkg_manifest_path GET "${pkg}" "manifest_path")
+        string(JSON pkg_version GET "${pkg}" "version")
+        list(APPEND available_package_names "${pkg_name}")
+
+        if(DEFINED GGC_CRATES)
+            if(NOT pkg_name IN_LIST GGC_CRATES)
+                continue()
+            endif()
+        endif()
+
+        # probably this loop is not necessary at all, since when using --no-deps, the
+        # contents of packages should already be only workspace members!
+        unset(pkg_is_ws_member)
+        foreach(ix RANGE ${ws_mems_len-1})
+            string(JSON ws_mem GET "${workspace_members}" ${ix})
+            if(ws_mem STREQUAL pkg_id)
+                set(pkg_is_ws_member YES)
+                break()
+            endif()
+        endforeach()
+
+        if(NOT DEFINED pkg_is_ws_member)
+            # Since we pass `--no-deps` to cargo metadata now,  I think this situation can't happen, but lets check for
+            # it anyway, just to discover any potential issues.
+            # If nobody complains for a while, it should be safe to remove this check and the previous loop, which
+            # should speed up the configuration process.
+            message(WARNING "The package `${pkg_name}` unexpectedly is not part of the workspace."
+                "Please open an issue at corrosion with some background information on the package"
+            )
+        endif()
+
+        string(JSON targets GET "${pkg}" "targets")
+
+        _generator_add_package_targets(
+            WORKSPACE_MANIFEST_PATH "${GGC_MANIFEST_PATH}"
+            PACKAGE_MANIFEST_PATH "${pkg_manifest_path}"
+            PACKAGE_NAME "${pkg_name}"
+            PACKAGE_VERSION "${pkg_version}"
+            TARGETS_JSON "${targets}"
+            OUT_CREATED_TARGETS curr_created_targets
+            ${no_linker_override}
+            ${crate_types}
+        )
+        list(APPEND created_targets "${curr_created_targets}")
+    endforeach()
+
+    if(NOT created_targets)
+        set(crates_error_message "")
+        if(DEFINED GGC_CRATES)
+            set(crates_error_message "\n`corrosion_import_crate()` was called with the `CRATES` "
+                "parameter set to `${GGC_CRATES}`. Corrosion will only attempt to import packages matching "
+                    "names from this list."
+            )
+        endif()
+        message(FATAL_ERROR
+                "Found no targets in ${pkgs_len} packages."
+                ${crates_error_message}.
+                "\nPlease keep in mind that corrosion will only import Rust `bin` targets or"
+                "`staticlib` or `cdylib` library targets."
+                "The following packages were found in the Manifest: ${available_package_names}"
+        )
+    else()
+        message(DEBUG "Corrosion created the following CMake targets: ${created_targets}")
+    endif()
+
+    if(GGC_IMPORTED_CRATES)
+        set(${GGC_IMPORTED_CRATES} "${created_targets}" PARENT_SCOPE)
+    endif()
+
+    foreach(target_name ${created_targets})
+        foreach(output_var RUNTIME_OUTPUT_DIRECTORY ARCHIVE_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY PDB_OUTPUT_DIRECTORY)
+            get_target_property(output_dir ${target_name} "${output_var}")
+            if (NOT output_dir AND DEFINED "CMAKE_${output_var}")
+                set_property(TARGET ${target_name} PROPERTY ${output_var} "${CMAKE_${output_var}}")
+            endif()
+
+            foreach(config_type ${CMAKE_CONFIGURATION_TYPES})
+                string(TOUPPER "${config_type}" config_type_upper)
+                get_target_property(output_dir ${target_name} "${output_var}_${config_type_upper}")
+                if (NOT output_dir AND DEFINED "CMAKE_${output_var}_${config_type_upper}")
+                    set_property(TARGET ${target_name} PROPERTY "${output_var}_${config_type_upper}" "${CMAKE_${output_var}_${config_type_upper}}")
+                endif()
+            endforeach()
+        endforeach()
+    endforeach()
+endfunction()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/cmake/FindRust.cmake	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,834 @@
+#[=======================================================================[.rst:
+FindRust
+--------
+
+Find Rust
+
+This module finds an installed rustc compiler and the cargo build tool. If Rust
+is managed by rustup it determines the available toolchains and returns a
+concrete Rust version, not a rustup proxy.
+
+#]=======================================================================]
+
+cmake_minimum_required(VERSION 3.12)
+
+# search for Cargo here and set up a bunch of cool flags and stuff
+include(FindPackageHandleStandardArgs)
+
+list(APPEND CMAKE_MESSAGE_CONTEXT "FindRust")
+
+# Print error message and return.
+macro(_findrust_failed)
+    if("${Rust_FIND_REQUIRED}")
+        message(FATAL_ERROR ${ARGN})
+    elseif(NOT "${Rust_FIND_QUIETLY}")
+        message(WARNING ${ARGN})
+    endif()
+    # Note: PARENT_SCOPE is the scope of the caller of the caller of this macro.
+    set(Rust_FOUND "" PARENT_SCOPE)
+    return()
+endmacro()
+
+# Checks if the actual version of a Rust toolchain matches the VERSION requirements specified in find_package.
+function(_findrust_version_ok ACTUAL_VERSION OUT_IS_OK)
+    if(DEFINED Rust_FIND_VERSION_RANGE)
+        if(Rust_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE")
+            set(COMPARSION_OPERATOR "VERSION_LESS_EQUAL")
+        elseif(Rust_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE")
+            set(COMPARSION_OPERATOR "VERSION_LESS")
+        else()
+            message(FATAL_ERROR "Unexpected value in `<PackageName>_FIND_VERSION_RANGE_MAX`: "
+                    "`${Rust_FIND_VERSION_RANGE_MAX}`.")
+        endif()
+        if(("${ACTUAL_VERSION}" VERSION_GREATER_EQUAL "${Rust_FIND_VERSION_RANGE_MIN}")
+                AND
+            ( "${ACTUAL_VERSION}" ${COMPARSION_OPERATOR} "${Rust_FIND_VERSION_RANGE_MAX}" )
+        )
+            set("${OUT_IS_OK}" TRUE PARENT_SCOPE)
+        else()
+            set("${OUT_IS_OK}" FALSE PARENT_SCOPE)
+        endif()
+    elseif(DEFINED Rust_FIND_VERSION)
+        if(Rust_VERSION_EXACT)
+            set(COMPARISON_OPERATOR VERSION_EQUAL)
+        else()
+            set(COMPARISON_OPERATOR VERSION_GREATER_EQUAL)
+        endif()
+        if(_TOOLCHAIN_${_TOOLCHAIN_SELECTED}_VERSION "${COMPARISON_OPERATOR}" Rust_FIND_VERSION)
+            set("${OUT_IS_OK}" TRUE PARENT_SCOPE)
+        else()
+            set("${OUT_IS_OK}" FALSE PARENT_SCOPE)
+        endif()
+    else()
+        # if no VERSION requirement was specified, the version is always okay.
+        set("${OUT_IS_OK}" TRUE PARENT_SCOPE)
+    endif()
+endfunction()
+
+function(_corrosion_strip_target_triple input_triple_or_path output_triple)
+    # If the target_triple is a path to a custom target specification file, then strip everything
+    # except the filename from `target_triple`.
+    get_filename_component(target_triple_ext "${input_triple_or_path}" EXT)
+    set(target_triple "${input_triple_or_path}")
+    if(target_triple_ext)
+        if(target_triple_ext STREQUAL ".json")
+            get_filename_component(target_triple "${input_triple_or_path}"  NAME_WE)
+        endif()
+    endif()
+    set(${output_triple} "${target_triple}" PARENT_SCOPE)
+endfunction()
+
+function(_corrosion_parse_target_triple target_triple out_arch out_vendor out_os out_env)
+    _corrosion_strip_target_triple(${target_triple} target_triple)
+
+    # The vendor part may be left out from the target triple, and since `env` is also optional,
+    # we determine if vendor is present by matching against a list of known vendors.
+    set(known_vendors
+        "apple"
+        "esp[a-z0-9]*" # espressif, e.g. riscv32imc-esp-espidf or xtensa-esp32s3-none-elf
+        "fortanix"
+        "kmc"
+        "pc"
+        "nintendo"
+        "nvidia"
+        "openwrt"
+        "alpine"
+        "chimera"
+        "unikraft"
+        "unknown"
+        "uwp" # aarch64-uwp-windows-msvc
+        "wrs" # e.g. aarch64-wrs-vxworks
+        "sony"
+        "sun"
+        )
+    # todo: allow users to add additional vendors to the list via a cmake variable.
+    list(JOIN known_vendors "|" known_vendors_joined)
+    # vendor is optional - We detect if vendor is present by matching against a known list of
+    # vendors. The next field is the OS, which we assume to always be present, while the last field
+    # is again optional and contains the environment.
+    string(REGEX MATCH
+        "^([a-z0-9_\.]+)-((${known_vendors_joined})-)?([a-z0-9_]+)(-([a-z0-9_]+))?$"
+        whole_match
+        "${target_triple}"
+        )
+    if((NOT whole_match) AND (NOT CORROSION_NO_WARN_PARSE_TARGET_TRIPLE_FAILED))
+        message(WARNING "Failed to parse target-triple `${target_triple}`."
+            "Corrosion determines some information about the output artifacts based on OS "
+            "specified in the Rust target-triple.\n"
+            "Currently this is relevant for windows and darwin (mac) targets, since file "
+            "extensions differ.\n"
+            "Note: If you are targeting a different OS you can suppress this warning by"
+            " setting the CMake cache variable "
+            "`CORROSION_NO_WARN_PARSE_TARGET_TRIPLE_FAILED`."
+            "Please consider opening an issue on github if you you need to add a new vendor to the list."
+            )
+    endif()
+
+    message(DEBUG "Parsed Target triple: arch: ${CMAKE_MATCH_1}, vendor: ${CMAKE_MATCH_3}, "
+        "OS: ${CMAKE_MATCH_4}, env: ${CMAKE_MATCH_6}")
+
+    set("${out_arch}" "${CMAKE_MATCH_1}" PARENT_SCOPE)
+    set("${out_vendor}" "${CMAKE_MATCH_3}" PARENT_SCOPE)
+    set("${out_os}" "${CMAKE_MATCH_4}" PARENT_SCOPE)
+    set("${out_env}" "${CMAKE_MATCH_6}" PARENT_SCOPE)
+endfunction()
+
+function(_corrosion_determine_libs_new target_triple out_libs out_flags)
+    set(package_dir "${CMAKE_BINARY_DIR}/corrosion/required_libs")
+    # Cleanup on reconfigure to get a cleans state (in case we change something in the future)
+    file(REMOVE_RECURSE "${package_dir}")
+    file(MAKE_DIRECTORY "${package_dir}")
+    set(manifest "[package]\nname = \"required_libs\"\nedition = \"2018\"\nversion = \"0.1.0\"\n")
+    string(APPEND manifest "\n[lib]\ncrate-type=[\"staticlib\"]\npath = \"lib.rs\"\n")
+    string(APPEND manifest "\n[workspace]\n")
+    file(WRITE "${package_dir}/Cargo.toml" "${manifest}")
+    file(WRITE "${package_dir}/lib.rs" "pub fn add(left: usize, right: usize) -> usize {left + right}\n")
+
+    execute_process(
+        COMMAND ${CMAKE_COMMAND} -E env
+            "CARGO_BUILD_RUSTC=${Rust_COMPILER_CACHED}"
+        ${Rust_CARGO_CACHED} rustc --verbose --color never --target=${target_triple} -- --print=native-static-libs
+        WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/corrosion/required_libs"
+        RESULT_VARIABLE cargo_build_result
+        ERROR_VARIABLE cargo_build_error_message
+    )
+    if(cargo_build_result)
+        message(DEBUG "Determining required native libraries - failed: ${cargo_build_result}.")
+        message(TRACE "The cargo build error was: ${cargo_build_error_message}")
+        message(DEBUG "Note: This is expected for Rust targets without std support")
+        return()
+    else()
+        # The pattern starts with `native-static-libs:` and goes to the end of the line.
+        if(cargo_build_error_message MATCHES "native-static-libs: ([^\r\n]+)\r?\n")
+            string(REPLACE " " ";" "libs_list" "${CMAKE_MATCH_1}")
+            set(stripped_lib_list "")
+            set(flag_list "")
+
+            set(was_last_framework OFF)
+            foreach(lib ${libs_list})
+                # merge -framework;lib -> "-framework lib" as CMake does de-duplication of link libraries, and -framework prefix is required
+                if (lib STREQUAL "-framework")
+                    set(was_last_framework ON)
+                    continue()
+                endif()
+                if (was_last_framework)
+                    list(APPEND stripped_lib_list "-framework ${lib}")
+                    set(was_last_framework OFF)
+                    continue()
+                endif()
+                
+                # Flags start with / for MSVC
+                if (lib MATCHES "^/" AND ${target_triple} MATCHES "msvc$")
+                    list(APPEND flag_list "${lib}")
+                else()
+                    # Strip leading `-l` (unix) and potential .lib suffix (windows)
+                    string(REGEX REPLACE "^-l" "" "stripped_lib" "${lib}")
+                    string(REGEX REPLACE "\.lib$" "" "stripped_lib" "${stripped_lib}")
+                    list(APPEND stripped_lib_list "${stripped_lib}")
+                endif()
+            endforeach()
+            set(libs_list "${stripped_lib_list}")
+            # Special case `msvcrt` to link with the debug version in Debug mode.
+            list(TRANSFORM libs_list REPLACE "^msvcrt$" "\$<\$<CONFIG:Debug>:msvcrtd>")
+        else()
+            message(DEBUG "Determining required native libraries - failed: Regex match failure.")
+            message(DEBUG "`native-static-libs` not found in: `${cargo_build_error_message}`")
+            return()
+        endif()
+    endif()
+    set("${out_libs}" "${libs_list}" PARENT_SCOPE)
+    set("${out_flags}" "${flag_list}" PARENT_SCOPE)
+endfunction()
+
+if (NOT "${Rust_TOOLCHAIN}" STREQUAL "$CACHE{Rust_TOOLCHAIN}")
+    # Promote Rust_TOOLCHAIN to a cache variable if it is not already a cache variable
+    set(Rust_TOOLCHAIN ${Rust_TOOLCHAIN} CACHE STRING "Requested rustup toolchain" FORCE)
+endif()
+
+set(_RESOLVE_RUSTUP_TOOLCHAINS_DESC "Indicates whether to descend into the toolchain pointed to by rustup")
+set(Rust_RESOLVE_RUSTUP_TOOLCHAINS ON CACHE BOOL ${_RESOLVE_RUSTUP_TOOLCHAINS_DESC})
+
+# This block checks to see if we're prioritizing a rustup-managed toolchain.
+if (DEFINED Rust_TOOLCHAIN)
+    # If the user specifies `Rust_TOOLCHAIN`, then look for `rustup` first, rather than `rustc`.
+    find_program(Rust_RUSTUP rustup PATHS "$ENV{HOME}/.cargo/bin")
+    if(NOT Rust_RUSTUP)
+        if(NOT "${Rust_FIND_QUIETLY}")
+            message(
+                WARNING "CMake variable `Rust_TOOLCHAIN` specified, but `rustup` was not found. "
+                "Ignoring toolchain and looking for a Rust toolchain not managed by rustup.")
+        endif()
+    endif()
+else()
+    # If we aren't definitely using a rustup toolchain, look for rustc first - the user may have
+    # a toolchain installed via a method other than rustup higher in the PATH, which should be
+    # preferred. However, if the first-found rustc is a rustup proxy, then we'll revert to
+    # finding the preferred toolchain via rustup.
+
+    # Uses `Rust_COMPILER` to let user-specified `rustc` win. But we will still "override" the
+    # user's setting if it is pointing to `rustup`. Default rustup install path is provided as a
+    # backup if a toolchain cannot be found in the user's PATH.
+
+    if (DEFINED Rust_COMPILER)
+        set(_Rust_COMPILER_TEST "${Rust_COMPILER}")
+        set(_USER_SPECIFIED_RUSTC ON)
+        if(NOT (EXISTS "${_Rust_COMPILER_TEST}" AND NOT IS_DIRECTORY "${_Rust_COMPILER_TEST}"))
+            set(_ERROR_MESSAGE "Rust_COMPILER was set to `${Rust_COMPILER}`, but this file does "
+                "not exist."
+            )
+            _findrust_failed(${_ERROR_MESSAGE})
+            return()
+        endif()
+    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"
+                    "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})
+        endif()
+    endif()
+
+    # Check if the discovered rustc is actually a "rustup" proxy.
+    execute_process(
+        COMMAND
+            ${CMAKE_COMMAND} -E env
+                RUSTUP_FORCE_ARG0=rustup
+            "${_Rust_COMPILER_TEST}" --version
+        OUTPUT_VARIABLE _RUSTC_VERSION_RAW
+        ERROR_VARIABLE _RUSTC_VERSION_STDERR
+        RESULT_VARIABLE _RUSTC_VERSION_RESULT
+    )
+
+    if(NOT (_RUSTC_VERSION_RESULT EQUAL "0"))
+        _findrust_failed("`${_Rust_COMPILER_TEST} --version` failed with ${_RUSTC_VERSION_RESULT}\n"
+            "rustc stderr:\n${_RUSTC_VERSION_STDERR}"
+            )
+    endif()
+
+    if (_RUSTC_VERSION_RAW MATCHES "rustup [0-9\\.]+")
+        if (_USER_SPECIFIED_RUSTC)
+            message(
+                WARNING "User-specified Rust_COMPILER pointed to rustup's rustc proxy. Corrosion's "
+                "FindRust will always try to evaluate to an actual Rust toolchain, and so the "
+                "user-specified Rust_COMPILER will be discarded in favor of the default "
+                "rustup-managed toolchain."
+            )
+
+            unset(Rust_COMPILER)
+            unset(Rust_COMPILER CACHE)
+        endif()
+
+        # Get `rustup` next to the `rustc` proxy
+        get_filename_component(_RUST_PROXIES_PATH "${_Rust_COMPILER_TEST}" DIRECTORY)
+        find_program(Rust_RUSTUP rustup HINTS "${_RUST_PROXIES_PATH}" NO_DEFAULT_PATH)
+    endif()
+
+    unset(_Rust_COMPILER_TEST CACHE)
+endif()
+
+# At this point, the only thing we should have evaluated is a path to `rustup` _if that's what the
+# best source for a Rust toolchain was determined to be_.
+if (NOT Rust_RUSTUP)
+    set(Rust_RESOLVE_RUSTUP_TOOLCHAINS OFF CACHE BOOL ${_RESOLVE_RUSTUP_TOOLCHAINS_DESC} FORCE)
+endif()
+
+# List of user variables that will override any toolchain-provided setting
+set(_Rust_USER_VARS Rust_COMPILER Rust_CARGO Rust_CARGO_TARGET Rust_CARGO_HOST_TARGET)
+foreach(_VAR ${_Rust_USER_VARS})
+    if (DEFINED "${_VAR}")
+        set(${_VAR}_CACHED "${${_VAR}}" CACHE INTERNAL "Internal cache of ${_VAR}")
+    else()
+        unset(${_VAR}_CACHED CACHE)
+    endif()
+endforeach()
+
+# Discover what toolchains are installed by rustup, if the discovered `rustc` is a proxy from
+# `rustup` and the user hasn't explicitly requested to override this behavior, then select either
+# the default toolchain, or the requested toolchain Rust_TOOLCHAIN
+if (Rust_RESOLVE_RUSTUP_TOOLCHAINS)
+    execute_process(
+        COMMAND
+            "${Rust_RUSTUP}" toolchain list --verbose
+        OUTPUT_VARIABLE _TOOLCHAINS_RAW
+    )
+
+    string(REPLACE "\n" ";" _TOOLCHAINS_RAW "${_TOOLCHAINS_RAW}")
+    set(_DISCOVERED_TOOLCHAINS "")
+    set(_DISCOVERED_TOOLCHAINS_RUSTC_PATH "")
+    set(_DISCOVERED_TOOLCHAINS_CARGO_PATH "")
+    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]+(.+)")
+            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\\).*")
+                set(_TOOLCHAIN_DEFAULT "${_TOOLCHAIN}")
+            endif()
+
+            if (_TOOLCHAIN_TYPE MATCHES ".*\\(override\\).*")
+                set(_TOOLCHAIN_OVERRIDE "${_TOOLCHAIN}")
+            endif()
+
+            execute_process(
+                    COMMAND
+                    "${_TOOLCHAIN_PATH}/bin/rustc" --version
+                    OUTPUT_VARIABLE _TOOLCHAIN_RAW_VERSION
+            )
+            if (_TOOLCHAIN_RAW_VERSION MATCHES "rustc ([0-9]+)\\.([0-9]+)\\.([0-9]+)(-nightly)?")
+                list(APPEND _DISCOVERED_TOOLCHAINS "${_TOOLCHAIN}")
+                list(APPEND _DISCOVERED_TOOLCHAINS_RUSTC_PATH "${_TOOLCHAIN_PATH}/bin/rustc")
+                list(APPEND _DISCOVERED_TOOLCHAINS_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
+
+                # We need this variable to determine the default toolchain, since `foreach(... IN ZIP_LISTS ...)`
+                # requires CMake 3.17. As a workaround we define this variable to lookup the version when iterating
+                # through the `_DISCOVERED_TOOLCHAINS` lists.
+                set(_TOOLCHAIN_${_TOOLCHAIN}_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
+                if(CMAKE_MATCH_4)
+                    set(_TOOLCHAIN_${_TOOLCHAIN}_IS_NIGHTLY "TRUE")
+                else()
+                    set(_TOOLCHAIN_${_TOOLCHAIN}_IS_NIGHTLY "FALSE")
+                endif()
+                if(EXISTS "${_TOOLCHAIN_PATH}/bin/cargo")
+                    list(APPEND _DISCOVERED_TOOLCHAINS_CARGO_PATH "${_TOOLCHAIN_PATH}/bin/cargo")
+                else()
+                    list(APPEND _DISCOVERED_TOOLCHAINS_CARGO_PATH "NOTFOUND")
+                endif()
+            else()
+                message(AUTHOR_WARNING "Unexpected output from `rustc --version` for Toolchain `${_TOOLCHAIN}`: "
+                        "`${_TOOLCHAIN_RAW_VERSION}`.\n"
+                        "Ignoring this toolchain."
+                )
+            endif()
+        else()
+            message(AUTHOR_WARNING "Didn't recognize toolchain: ${_TOOLCHAIN_RAW}. Ignoring this toolchain.\n"
+                "Rustup toolchain list output( `${Rust_RUSTUP} toolchain list --verbose`):\n"
+                "${_TOOLCHAINS_RAW}"
+            )
+        endif()
+    endforeach()
+
+    # Expose a list of available rustup toolchains.
+    list(LENGTH _DISCOVERED_TOOLCHAINS _toolchain_len)
+    list(LENGTH _DISCOVERED_TOOLCHAINS_RUSTC_PATH _toolchain_rustc_len)
+    list(LENGTH _DISCOVERED_TOOLCHAINS_CARGO_PATH _toolchain_cargo_len)
+    list(LENGTH _DISCOVERED_TOOLCHAINS_VERSION _toolchain_version_len)
+    if(NOT
+        (_toolchain_len EQUAL _toolchain_rustc_len
+            AND _toolchain_cargo_len EQUAL _toolchain_version_len
+            AND _toolchain_len EQUAL _toolchain_cargo_len)
+        )
+        message(FATAL_ERROR "Internal error - list length mismatch."
+            "List lengths: ${_toolchain_len} toolchains, ${_toolchain_rustc_len} rustc, ${_toolchain_cargo_len} cargo,"
+            " ${_toolchain_version_len} version. The lengths should be the same."
+        )
+    endif()
+
+    set(Rust_RUSTUP_TOOLCHAINS CACHE INTERNAL "List of available Rustup toolchains" "${_DISCOVERED_TOOLCHAINS}")
+    set(Rust_RUSTUP_TOOLCHAINS_RUSTC_PATH
+        CACHE INTERNAL
+        "List of the rustc paths corresponding to the toolchain at the same index in `Rust_RUSTUP_TOOLCHAINS`."
+        "${_DISCOVERED_TOOLCHAINS_RUSTC_PATH}"
+    )
+    set(Rust_RUSTUP_TOOLCHAINS_CARGO_PATH
+        CACHE INTERNAL
+        "List of the cargo paths corresponding to the toolchain at the same index in `Rust_RUSTUP_TOOLCHAINS`. \
+        May also be `NOTFOUND` if the toolchain does not have a cargo executable."
+        "${_DISCOVERED_TOOLCHAINS_CARGO_PATH}"
+    )
+    set(Rust_RUSTUP_TOOLCHAINS_VERSION
+        CACHE INTERNAL
+        "List of the rust toolchain version corresponding to the toolchain at the same index in \
+        `Rust_RUSTUP_TOOLCHAINS`."
+        "${_DISCOVERED_TOOLCHAINS_VERSION}"
+    )
+
+    # Rust_TOOLCHAIN is preferred over a requested version if it is set.
+    if (NOT DEFINED Rust_TOOLCHAIN)
+        if (NOT DEFINED _TOOLCHAIN_OVERRIDE)
+            set(_TOOLCHAIN_SELECTED "${_TOOLCHAIN_DEFAULT}")
+        else()
+            set(_TOOLCHAIN_SELECTED "${_TOOLCHAIN_OVERRIDE}")
+        endif()
+        # Check default toolchain first.
+        _findrust_version_ok("_TOOLCHAIN_${_TOOLCHAIN_SELECTED}_VERSION" _VERSION_OK)
+        if(NOT "${_VERSION_OK}")
+            foreach(_TOOLCHAIN "${_DISCOVERED_TOOLCHAINS}")
+                _findrust_version_ok("_TOOLCHAIN_${_TOOLCHAIN}_VERSION" _VERSION_OK)
+                if("${_VERSION_OK}")
+                    set(_TOOLCHAIN_SELECTED "${_TOOLCHAIN}")
+                    break()
+                endif()
+            endforeach()
+            # Check if we found a suitable version in the for loop.
+            if(NOT "${_VERSION_OK}")
+                string(REPLACE ";" "\n" _DISCOVERED_TOOLCHAINS "${_DISCOVERED_TOOLCHAINS}")
+                _findrust_failed("Failed to find a Rust toolchain matching the version requirements of "
+                        "${Rust_FIND_VERSION}. Available toolchains: ${_DISCOVERED_TOOLCHAINS}")
+            endif()
+        endif()
+    endif()
+
+    set(Rust_TOOLCHAIN "${_TOOLCHAIN_SELECTED}" CACHE STRING "The rustup toolchain to use")
+    set_property(CACHE Rust_TOOLCHAIN PROPERTY STRINGS "${_DISCOVERED_TOOLCHAINS}")
+
+    if(NOT Rust_FIND_QUIETLY)
+        message(STATUS "Rust Toolchain: ${Rust_TOOLCHAIN}")
+    endif()
+
+    if (NOT Rust_TOOLCHAIN IN_LIST _DISCOVERED_TOOLCHAINS)
+        # If the precise toolchain wasn't found, try appending the default host
+        execute_process(
+            COMMAND
+                "${Rust_RUSTUP}" show
+            RESULT_VARIABLE _SHOW_RESULT
+            OUTPUT_VARIABLE _SHOW_RAW
+        )
+        if(NOT "${_SHOW_RESULT}" EQUAL "0")
+            _findrust_failed("Command `${Rust_RUSTUP} show` failed")
+        endif()
+
+        if (_SHOW_RAW MATCHES "Default host: ([a-zA-Z0-9_\\-]*)\n")
+            set(_DEFAULT_HOST "${CMAKE_MATCH_1}")
+        else()
+            _findrust_failed("Failed to parse \"Default host\" from `${Rust_RUSTUP} show`. Got: ${_SHOW_RAW}")
+        endif()
+
+        if (NOT "${Rust_TOOLCHAIN}-${_DEFAULT_HOST}" IN_LIST _DISCOVERED_TOOLCHAINS)
+            set(_NOT_FOUND_MESSAGE "Could not find toolchain '${Rust_TOOLCHAIN}'\n"
+                "Available toolchains:\n"
+            )
+            foreach(_TOOLCHAIN ${_DISCOVERED_TOOLCHAINS})
+                list(APPEND _NOT_FOUND_MESSAGE "  `${_TOOLCHAIN}`\n")
+            endforeach()
+            _findrust_failed(${_NOT_FOUND_MESSAGE})
+        endif()
+
+        set(_RUSTUP_TOOLCHAIN_FULL "${Rust_TOOLCHAIN}-${_DEFAULT_HOST}")
+    else()
+        set(_RUSTUP_TOOLCHAIN_FULL "${Rust_TOOLCHAIN}")
+    endif()
+
+    set(_RUST_TOOLCHAIN_PATH "${_TOOLCHAIN_${_RUSTUP_TOOLCHAIN_FULL}_PATH}")
+    if(NOT "${Rust_FIND_QUIETLY}")
+        message(VERBOSE "Rust toolchain ${_RUSTUP_TOOLCHAIN_FULL}")
+        message(VERBOSE "Rust toolchain path ${_RUST_TOOLCHAIN_PATH}")
+    endif()
+
+    # Is overridden if the user specifies `Rust_COMPILER` explicitly.
+    find_program(
+        Rust_COMPILER_CACHED
+        rustc
+            HINTS "${_RUST_TOOLCHAIN_PATH}/bin"
+            NO_DEFAULT_PATH)
+elseif (Rust_RUSTUP)
+    get_filename_component(_RUST_TOOLCHAIN_PATH "${Rust_RUSTUP}" DIRECTORY)
+    get_filename_component(_RUST_TOOLCHAIN_PATH "${_RUST_TOOLCHAIN_PATH}" DIRECTORY)
+    find_program(
+        Rust_COMPILER_CACHED
+        rustc
+            HINTS "${_RUST_TOOLCHAIN_PATH}/bin"
+            NO_DEFAULT_PATH)
+else()
+    find_program(Rust_COMPILER_CACHED rustc)
+    if (EXISTS "${Rust_COMPILER_CACHED}")
+        # rustc is expected to be at `<toolchain_path>/bin/rustc`.
+        get_filename_component(_RUST_TOOLCHAIN_PATH "${Rust_COMPILER_CACHED}" DIRECTORY)
+        get_filename_component(_RUST_TOOLCHAIN_PATH "${_RUST_TOOLCHAIN_PATH}" DIRECTORY)
+    endif()
+endif()
+
+if (NOT EXISTS "${Rust_COMPILER_CACHED}")
+    set(_NOT_FOUND_MESSAGE "The rustc executable was not found. "
+        "Rust not installed or ~/.cargo/bin not added to path?\n"
+        "Hint: Consider setting `Rust_COMPILER` to the absolute path of `rustc`."
+    )
+    _findrust_failed(${_NOT_FOUND_MESSAGE})
+endif()
+
+if (Rust_RESOLVE_RUSTUP_TOOLCHAINS)
+    set(_NOT_FOUND_MESSAGE "Rust was detected to be managed by rustup, but failed to find `cargo` "
+        "next to `rustc` in `${_RUST_TOOLCHAIN_PATH}/bin`. This can happen for custom toolchains, "
+        "if cargo was not built. "
+        "Please manually specify the path to a compatible `cargo` by setting `Rust_CARGO`."
+    )
+    find_program(
+        Rust_CARGO_CACHED
+        cargo
+            HINTS "${_RUST_TOOLCHAIN_PATH}/bin"
+            NO_DEFAULT_PATH
+    )
+    # note: maybe can use find_package_handle_standard_args here, if we remove the _CACHED postfix.
+    # not sure why that is here...
+    if(NOT EXISTS "${Rust_CARGO_CACHED}")
+        _findrust_failed(${_NOT_FOUND_MESSAGE})
+    endif()
+    set(Rust_TOOLCHAIN_IS_RUSTUP_MANAGED TRUE CACHE INTERNAL "" FORCE)
+else()
+    set(_NOT_FOUND_MESSAGE "Failed to find `cargo` in PATH and `${_RUST_TOOLCHAIN_PATH}/bin`.\n"
+        "Please ensure cargo is in PATH or manually specify the path to a compatible `cargo` by "
+        "setting `Rust_CARGO`."
+    )
+    # On some systems (e.g. NixOS) cargo is not managed by rustup and also not next to rustc.
+    find_program(
+            Rust_CARGO_CACHED
+            cargo
+                HINTS "${_RUST_TOOLCHAIN_PATH}/bin"
+    )
+    # note: maybe can use find_package_handle_standard_args here, if we remove the _CACHED postfix.
+    # not sure why that is here...
+    if(NOT EXISTS "${Rust_CARGO_CACHED}")
+        _findrust_failed(${_NOT_FOUND_MESSAGE})
+    endif()
+endif()
+
+execute_process(
+    COMMAND "${Rust_CARGO_CACHED}" --version --verbose
+    OUTPUT_VARIABLE _CARGO_VERSION_RAW
+    RESULT_VARIABLE _CARGO_VERSION_RESULT
+)
+# todo: check if cargo is a required component!
+if(NOT ( "${_CARGO_VERSION_RESULT}" EQUAL "0" ))
+    _findrust_failed("Failed to get cargo version.\n"
+        "`${Rust_CARGO_CACHED} --version` failed with error: `${_CARGO_VERSION_RESULT}"
+)
+endif()
+
+# todo: don't set cache variables here, but let find_package_handle_standard_args do the promotion
+# later.
+if (_CARGO_VERSION_RAW MATCHES "cargo ([0-9]+)\\.([0-9]+)\\.([0-9]+)")
+    set(Rust_CARGO_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE)
+    set(Rust_CARGO_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE)
+    set(Rust_CARGO_VERSION_PATCH "${CMAKE_MATCH_3}" CACHE INTERNAL "" FORCE)
+    set(Rust_CARGO_VERSION "${Rust_CARGO_VERSION_MAJOR}.${Rust_CARGO_VERSION_MINOR}.${Rust_CARGO_VERSION_PATCH}" CACHE INTERNAL "" FORCE)
+# Workaround for the version strings where the `cargo ` prefix is missing.
+elseif(_CARGO_VERSION_RAW MATCHES "([0-9]+)\\.([0-9]+)\\.([0-9]+)")
+    set(Rust_CARGO_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE)
+    set(Rust_CARGO_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE)
+    set(Rust_CARGO_VERSION_PATCH "${CMAKE_MATCH_3}" CACHE INTERNAL "" FORCE)
+    set(Rust_CARGO_VERSION "${Rust_CARGO_VERSION_MAJOR}.${Rust_CARGO_VERSION_MINOR}.${Rust_CARGO_VERSION_PATCH}" CACHE INTERNAL "" FORCE)
+else()
+    _findrust_failed(
+        "Failed to parse cargo version. `cargo --version` evaluated to (${_CARGO_VERSION_RAW}). "
+        "Expected a <Major>.<Minor>.<Patch> version triple."
+    )
+endif()
+
+execute_process(
+    COMMAND "${Rust_COMPILER_CACHED}" --version --verbose
+    OUTPUT_VARIABLE _RUSTC_VERSION_RAW
+    RESULT_VARIABLE _RUSTC_VERSION_RESULT
+)
+
+if(NOT ( "${_RUSTC_VERSION_RESULT}" EQUAL "0" ))
+    _findrust_failed("Failed to get rustc version.\n"
+        "${Rust_COMPILER_CACHED} --version failed with error: `${_RUSTC_VERSION_RESULT}`")
+endif()
+
+if (_RUSTC_VERSION_RAW MATCHES "rustc ([0-9]+)\\.([0-9]+)\\.([0-9]+)(-nightly)?")
+    set(Rust_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE)
+    set(Rust_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE)
+    set(Rust_VERSION_PATCH "${CMAKE_MATCH_3}" CACHE INTERNAL "" FORCE)
+    set(Rust_VERSION "${Rust_VERSION_MAJOR}.${Rust_VERSION_MINOR}.${Rust_VERSION_PATCH}" CACHE INTERNAL "" FORCE)
+    if(CMAKE_MATCH_4)
+        set(Rust_IS_NIGHTLY 1 CACHE INTERNAL "" FORCE)
+    else()
+        set(Rust_IS_NIGHTLY 0 CACHE INTERNAL "" FORCE)
+    endif()
+else()
+    _findrust_failed("Failed to parse rustc version. `${Rust_COMPILER_CACHED} --version --verbose` "
+        "evaluated to:\n`${_RUSTC_VERSION_RAW}`"
+    )
+endif()
+
+if (_RUSTC_VERSION_RAW MATCHES "host: ([a-zA-Z0-9_\\-]*)\n")
+    set(Rust_DEFAULT_HOST_TARGET "${CMAKE_MATCH_1}")
+    set(Rust_CARGO_HOST_TARGET_CACHED "${Rust_DEFAULT_HOST_TARGET}" CACHE STRING "Host triple")
+else()
+    _findrust_failed(
+        "Failed to parse rustc host target. `rustc --version --verbose` evaluated to:\n${_RUSTC_VERSION_RAW}"
+    )
+endif()
+
+if (_RUSTC_VERSION_RAW MATCHES "LLVM version: ([0-9]+)\\.([0-9]+)(\\.([0-9]+))?")
+    set(Rust_LLVM_VERSION_MAJOR "${CMAKE_MATCH_1}" CACHE INTERNAL "" FORCE)
+    set(Rust_LLVM_VERSION_MINOR "${CMAKE_MATCH_2}" CACHE INTERNAL "" FORCE)
+    # With the Rust toolchain 1.44.1 the reported LLVM version is 9.0, i.e. without a patch version.
+    # Since cmake regex does not support non-capturing groups, just ignore Match 3.
+    set(Rust_LLVM_VERSION_PATCH "${CMAKE_MATCH_4}" CACHE INTERNAL "" FORCE)
+    set(Rust_LLVM_VERSION "${Rust_LLVM_VERSION_MAJOR}.${Rust_LLVM_VERSION_MINOR}.${Rust_LLVM_VERSION_PATCH}" CACHE INTERNAL "" FORCE)
+elseif(NOT Rust_FIND_QUIETLY)
+    message(
+            WARNING
+            "Failed to parse rustc LLVM version. `rustc --version --verbose` evaluated to:\n${_RUSTC_VERSION_RAW}"
+    )
+endif()
+
+if (NOT Rust_CARGO_TARGET_CACHED)
+    unset(_CARGO_ARCH)
+    unset(_CARGO_ABI)
+    if (WIN32)
+        if (CMAKE_VS_PLATFORM_NAME)
+            string(TOLOWER "${CMAKE_VS_PLATFORM_NAME}" LOWER_VS_PLATFORM_NAME)
+            if ("${LOWER_VS_PLATFORM_NAME}" STREQUAL "win32")
+                set(_CARGO_ARCH i686)
+            elseif("${LOWER_VS_PLATFORM_NAME}" STREQUAL "x64")
+                set(_CARGO_ARCH x86_64)
+            elseif("${LOWER_VS_PLATFORM_NAME}" STREQUAL "arm64")
+                set(_CARGO_ARCH aarch64)
+            else()
+                message(WARNING "VS Platform '${CMAKE_VS_PLATFORM_NAME}' not recognized")
+            endif()
+        endif()
+        # Fallback path
+        if(NOT DEFINED _CARGO_ARCH)
+            # Possible values for windows when not cross-compiling taken from here:
+            # https://learn.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details
+            # When cross-compiling the user is expected to supply the value, so we match more variants.
+            if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(AMD64|amd64|x86_64)$")
+                set(_CARGO_ARCH x86_64)
+            elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(ARM64|arm64|aarch64)$")
+                set(_CARGO_ARCH aarch64)
+            elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(X86|x86|i686)$")
+                set(_CARGO_ARCH i686)
+            elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "i586")
+                set(_CARGO_ARCH i586)
+            elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "IA64")
+                message(FATAL_ERROR "No rust target for Intel Itanium.")
+            elseif(NOT "${CMAKE_SYSTEM_PROCESSOR}")
+                message(WARNING "Failed to detect target architecture. Please set `CMAKE_SYSTEM_PROCESSOR`"
+                    " to your target architecture or set `Rust_CARGO_TARGET` to your cargo target triple."
+                )
+            else()
+                message(WARNING "Failed to detect target architecture. Please set "
+                    "`Rust_CARGO_TARGET` to your cargo target triple."
+                )
+            endif()
+        endif()
+
+        set(_CARGO_VENDOR "pc-windows")
+
+        # The MSVC Generators will always target the msvc ABI.
+        # For other generators we check the compiler ID and compiler target (if present)
+        # If no compiler is set and we are not cross-compiling then we just choose the
+        # default rust host target.
+        if(DEFINED MSVC
+            OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"
+            OR "${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC"
+            OR "${CMAKE_CXX_COMPILER_TARGET}" MATCHES "-msvc$"
+            OR "${CMAKE_C_COMPILER_TARGET}" MATCHES "-msvc$"
+        )
+            set(_CARGO_ABI msvc)
+        elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU"
+            OR "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU"
+            OR "${CMAKE_CXX_COMPILER_TARGET}" MATCHES "-gnu$"
+            OR "${CMAKE_C_COMPILER_TARGET}" MATCHES "-gnu$"
+            OR (NOT CMAKE_CROSSCOMPILING AND "${Rust_DEFAULT_HOST_TARGET}" MATCHES "-gnu$")
+            )
+            set(_CARGO_ABI gnu)
+        elseif(NOT "${CMAKE_CROSSCOMPILING}" AND "${Rust_DEFAULT_HOST_TARGET}" MATCHES "-msvc$")
+            # We first check if the gnu branch matches to ensure this fallback is only used
+            # if no compiler is enabled.
+            set(_CARGO_ABI msvc)
+        else()
+            message(WARNING "Could not determine the target ABI. Please specify `Rust_CARGO_TARGET` manually.")
+        endif()
+
+        if(DEFINED _CARGO_ARCH AND DEFINED _CARGO_VENDOR AND DEFINED _CARGO_ABI)
+            set(Rust_CARGO_TARGET_CACHED "${_CARGO_ARCH}-${_CARGO_VENDOR}-${_CARGO_ABI}"
+                CACHE STRING "Target triple")
+        endif()
+    elseif (ANDROID)
+        if (CMAKE_ANDROID_ARCH_ABI STREQUAL armeabi-v7a)
+            if (CMAKE_ANDROID_ARM_MODE)
+                set(_Rust_ANDROID_TARGET armv7-linux-androideabi)
+            else ()
+                set(_Rust_ANDROID_TARGET thumbv7neon-linux-androideabi)
+            endif()
+        elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL arm64-v8a)
+            set(_Rust_ANDROID_TARGET aarch64-linux-android)
+        elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL x86)
+            set(_Rust_ANDROID_TARGET i686-linux-android)
+        elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL x86_64)
+            set(_Rust_ANDROID_TARGET x86_64-linux-android)
+        endif()
+
+        if (_Rust_ANDROID_TARGET)
+            set(Rust_CARGO_TARGET_CACHED "${_Rust_ANDROID_TARGET}" CACHE STRING "Target triple")
+        endif()
+    elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "OHOS")
+        if(CMAKE_OHOS_ARCH_ABI STREQUAL arm64-v8a)
+            set(_RUST_OHOS_TARGET aarch64-unknown-linux-ohos)
+        elseif(CMAKE_OHOS_ARCH_ABI STREQUAL armeabi-v7a)
+            set(_RUST_OHOS_TARGET armv7-unknown-linux-ohos)
+        elseif(CMAKE_OHOS_ARCH_ABI STREQUAL x86_64)
+            set(_RUST_OHOS_TARGET x86_64-unknown-linux-ohos)
+        else()
+            message(WARNING "unrecognized OHOS architecture: ${OHOS_ARCH}")
+        endif()
+        if(_RUST_OHOS_TARGET)
+            set(Rust_CARGO_TARGET_CACHED "${_RUST_OHOS_TARGET}" CACHE STRING "Target triple")
+        endif()
+    endif()
+    # Fallback to the default host target
+    if(NOT Rust_CARGO_TARGET_CACHED)
+        if(CMAKE_CROSSCOMPILING)
+            message(WARNING "CMake is in cross-compiling mode, but the cargo target-triple could not be inferred."
+                "Falling back to the default host target. Please consider manually setting `Rust_CARGO_TARGET`."
+            )
+        endif()
+        set(Rust_CARGO_TARGET_CACHED "${Rust_DEFAULT_HOST_TARGET}" CACHE STRING "Target triple")
+    endif()
+
+    message(STATUS "Rust Target: ${Rust_CARGO_TARGET_CACHED}")
+endif()
+
+if(Rust_CARGO_TARGET_CACHED STREQUAL Rust_DEFAULT_HOST_TARGET)
+    set(Rust_CROSSCOMPILING FALSE CACHE INTERNAL "Rust is configured for cross-compiling")
+else()
+    set(Rust_CROSSCOMPILING TRUE  CACHE INTERNAL "Rust is configured for cross-compiling")
+endif()
+
+_corrosion_parse_target_triple("${Rust_CARGO_TARGET_CACHED}" rust_arch rust_vendor rust_os rust_env)
+_corrosion_parse_target_triple("${Rust_CARGO_HOST_TARGET_CACHED}" rust_host_arch rust_host_vendor rust_host_os rust_host_env)
+
+set(Rust_CARGO_TARGET_ARCH "${rust_arch}" CACHE INTERNAL "Target architecture")
+set(Rust_CARGO_TARGET_VENDOR "${rust_vendor}" CACHE INTERNAL "Target vendor")
+set(Rust_CARGO_TARGET_OS "${rust_os}" CACHE INTERNAL "Target Operating System")
+set(Rust_CARGO_TARGET_ENV "${rust_env}" CACHE INTERNAL "Target environment")
+
+set(Rust_CARGO_HOST_ARCH "${rust_host_arch}" CACHE INTERNAL "Host architecture")
+set(Rust_CARGO_HOST_VENDOR "${rust_host_vendor}" CACHE INTERNAL "Host vendor")
+set(Rust_CARGO_HOST_OS "${rust_host_os}" CACHE INTERNAL "Host Operating System")
+set(Rust_CARGO_HOST_ENV "${rust_host_env}" CACHE INTERNAL "Host environment")
+
+if(NOT DEFINED CACHE{Rust_CARGO_TARGET_LINK_NATIVE_LIBS})
+    message(STATUS "Determining required link libraries for target ${Rust_CARGO_TARGET_CACHED}")
+    unset(required_native_libs)
+    _corrosion_determine_libs_new("${Rust_CARGO_TARGET_CACHED}" required_native_libs required_link_flags)
+    if(DEFINED required_native_libs)
+        message(STATUS "Required static libs for target ${Rust_CARGO_TARGET_CACHED}: ${required_native_libs}" )
+    endif()
+    if(DEFINED required_link_flags)
+        message(STATUS "Required link flags for target ${Rust_CARGO_TARGET_CACHED}: ${required_link_flags}" )
+    endif()
+    # In very recent corrosion versions it is possible to override the rust compiler version
+    # per target, so to be totally correct we would need to determine the libraries for
+    # every installed Rust version, that the user could choose from.
+    # In practice there aren't likely going to be any major differences, so we just do it once
+    # for the target and once for the host target (if cross-compiling).
+    set(Rust_CARGO_TARGET_LINK_NATIVE_LIBS "${required_native_libs}" CACHE INTERNAL
+            "Required native libraries when linking Rust static libraries")
+    set(Rust_CARGO_TARGET_LINK_OPTIONS "${required_link_flags}" CACHE INTERNAL
+            "Required link flags when linking Rust static libraries")
+endif()
+
+if(Rust_CROSSCOMPILING AND NOT DEFINED CACHE{Rust_CARGO_HOST_TARGET_LINK_NATIVE_LIBS})
+    message(STATUS "Determining required link libraries for target ${Rust_CARGO_HOST_TARGET_CACHED}")
+    unset(host_libs)
+    _corrosion_determine_libs_new("${Rust_CARGO_HOST_TARGET_CACHED}" host_libs host_flags)
+    if(DEFINED host_libs)
+        message(STATUS "Required static libs for host target ${Rust_CARGO_HOST_TARGET_CACHED}: ${host_libs}" )
+    endif()
+    set(Rust_CARGO_HOST_TARGET_LINK_NATIVE_LIBS "${host_libs}" CACHE INTERNAL
+        "Required native libraries when linking Rust static libraries for the host target")
+    set(Rust_CARGO_HOST_TARGET_LINK_OPTIONS "${host_flags}" CACHE INTERNAL
+        "Required linker flags when linking Rust static libraries for the host target")
+endif()
+
+# Set the input variables as non-cache variables so that the variables are available after
+# `find_package`, even if the values were evaluated to defaults.
+foreach(_VAR ${_Rust_USER_VARS})
+    set(${_VAR} "${${_VAR}_CACHED}")
+    # Ensure cached variables have type INTERNAL
+    set(${_VAR}_CACHED "${${_VAR}_CACHED}" CACHE INTERNAL "Internal cache of ${_VAR}")
+endforeach()
+
+find_package_handle_standard_args(
+    Rust
+    REQUIRED_VARS Rust_COMPILER Rust_VERSION Rust_CARGO Rust_CARGO_VERSION Rust_CARGO_TARGET Rust_CARGO_HOST_TARGET
+    VERSION_VAR Rust_VERSION
+)
+
+
+if(NOT TARGET Rust::Rustc)
+    add_executable(Rust::Rustc IMPORTED GLOBAL)
+    set_property(
+        TARGET Rust::Rustc
+        PROPERTY IMPORTED_LOCATION "${Rust_COMPILER_CACHED}"
+    )
+
+    add_executable(Rust::Cargo IMPORTED GLOBAL)
+    set_property(
+        TARGET Rust::Cargo
+        PROPERTY IMPORTED_LOCATION "${Rust_CARGO_CACHED}"
+    )
+    set(Rust_FOUND true)
+endif()
+
+list(POP_BACK CMAKE_MESSAGE_CONTEXT)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/doc/.gitignore	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,1 @@
+book
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/doc/book.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,5 @@
+[book]
+language = "en"
+multilingual = false
+src = "src"
+title = "Corrosion v0.5 documentation"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/doc/src/SUMMARY.md	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,9 @@
+# Summary
+
+- [Introduction](./introduction.md)
+- [Quick Start](./quick_start.md)
+- [Setup Corrosion](./setup_corrosion.md)
+- [Usage](./usage.md)
+- [Advanced](./advanced.md)
+- [FFI binding integrations](./ffi_bindings.md)
+- [Common Issues](./common_issues.md)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/doc/src/advanced.md	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,100 @@
+## What does corrosion do?
+
+The specifics of what corrosion does should be regarded as an implementation detail and not relied on
+when writing user code. However, a basic understanding of what corrosion does may be helpful when investigating
+issues.
+
+### FindRust
+
+Corrosion maintains a CMake module `FindRust` which is executed when Corrosion is loaded, i.e. at the time
+of `find_package(corrosion)`, `FetchContent_MakeAvailable(corrosion)` or `add_subdirectory(corrosion)` depending
+on the method used to include Corrosion.
+
+`FindRust` will search for installed rust toolchains, respecting the options prefixed with `Rust_` documented in
+the [Usage](usage.md#corrosion-options) chapter.
+It will select _one_ Rust toolchain to be used for the compilation of Rust code. Toolchains managed by `rustup`
+will be resolved and corrosion will always select a specific toolchain, not a `rustup` proxy.
+
+
+### Importing Rust crates
+
+Corrosion's main function is `corrosion_import_crate`, which internally will call `cargo metadata` to provide
+structured information based on the `Cargo.toml` manifest.
+Corrosion will then iterate over all workspace and/or package members and find all rust crates that are either
+a static (`staticlib`) or shared (`cdylib`) library or a `bin` target and create CMake targets matching the
+crate name. Additionally, a build target is created for each imported target, containing the required build
+command to create the imported artifact. This build command can be influenced by various arguments to 
+`corrosion_import_crate` as well as corrosion specific target properties which are documented int the  
+[Usage](usage.md) chapter.
+Corrosion adds the necessary dependencies and also copies the target artifacts out of the cargo build tree
+to standard CMake locations, even respecting `OUTPUT_DIRECTORY` target properties if set.
+
+### Linking
+
+Depending on the type of the crate the linker will either be invoked by CMake or by `rustc`.
+Rust `staticlib`s are linked into C/C++ code via `target_link_libraries()` and the linker is
+invoked by CMake.
+For rust `cdylib`s and `bin`s, the linker is invoked via `rustc` and CMake just gets the final artifact.
+
+#### CMake invokes the linker
+
+When CMake invokes the linker, everything is as usual. CMake will call the linker with
+the compiler as the linker driver and users can just use the regular CMake functions to
+modify linking behaviour. `corrosion_set_linker()` has **no effect**.
+As a convenience, `corrosion_link_libraries()` will forward its arguments to `target_link_libraries()`.
+
+#### Rustc invokes the linker 
+
+Rust `cdylib`s and `bin`s are linked via `rustc`. Corrosion provides several helper functions
+to influence the linker invocation for such targets. 
+
+`corrosion_link_libraries()` is a limited version of `target_link_libraries()` 
+for rust `cdylib` or `bin` targets.
+Under the hood this function passes `-l` and `-L` flags to the linker invocation and
+ensures the linked libraries are built first.
+Much of the advanced functionality available in `target_link_libraries()` is not implemented yet,
+but pull-requests are welcome! In the meantime, users may want to use 
+`corrosion_add_target_local_rustflags()` to pass customized linking flags.
+
+`corrosion_set_linker()` can be used to specify a custom linker, in case the default one
+chosen by corrosion is not what you want.
+Corrosion currently instructs `rustc` to use the C/C++ compiler as the linker driver.
+This is done because:
+- For C++ code we must link with `libstdc++` or `libc++` (depending on the compiler), so we must
+  either specify the library on the link line or use a `c++` compiler as the linker driver.
+- `Rustc`s default linker selection currently is not so great. For a number of platforms
+  `rustc` will fallback to `cc` as the linker driver. When cross-compiling, this leads
+  to linking failures, since the linker driver is for the host architecture.
+  Corrosion avoids this by specifying the C/C++ compiler as the linker driver.
+
+
+In some cases, especially in older rust versions (pre 1.68), the linker flavor detection 
+of `rustc` is also not correct, so when setting a custom linker you may want to pass the
+[`-C linker-flavor`](https://doc.rust-lang.org/rustc/codegen-options/index.html#linker-flavor)
+rustflag via `corrosion_add_target_local_rustflags()`.
+
+## FFI bindings
+
+For interaction between Rust and other languages there need to be some FFI bindings of some sort.
+For simple cases manually defining the interfaces may be sufficient, but in many cases users
+wish to use tools like [bindgen], [cbindgen], [cxx] or [autocxx] to automate the generating of
+bindings.
+
+In principle there are two different ways to generate the bindings:
+- use a `build.rs` script to generate the bindings when cargo is invoked, using
+  library versions of the tools to generate the bindings.
+- use the cli versions of the tools and setup custom CMake targets/commands to
+  generate the bindings. This approach should be preferred if the bindings are needed
+  by the C/C++ side.
+
+Corrosion currently provides 2 experimental functions to integrate cbindgen and cxx into
+the build process. They are not 100% production ready yet, but should work well as a 
+template on how to integrate generating bindings into your build process.
+
+Todo: expand this documentation and link to other resources.
+
+[bindgen]: https://rust-lang.github.io/rust-bindgen/
+[cbindgen]: https://github.com/eqrion/cbindgen
+[cxx]: https://cxx.rs/
+[autocxx]: https://google.github.io/autocxx/index.html
+  
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/doc/src/common_issues.md	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,88 @@
+# Commonly encountered (Non-Corrosion) Issues
+
+## Table of Contents
+
+- [Linking Debug C/C++ libraries into Rust fails on Windows MSVC targets](#linking-debug-cc-libraries-into-rust-fails-on-windows-msvc-targets)
+- [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)
+
+## Linking Debug C/C++ libraries into Rust fails on Windows MSVC targets
+
+`rustc` always links against the non-debug Windows runtime on `*-msvc` targets.
+This is tracked [in this issue](https://github.com/rust-lang/rust/issues/39016)
+and could be fixed upstream.
+
+A typical error message for this issue is:
+
+```
+   Compiling rust_bin v0.1.0 (D:\a\corrosion\corrosion\test\cxxbridge\cxxbridge_cpp2rust\rust)
+error: linking with `link.exe` failed: exit code: 1319
+[ redacted ]
+  = note: cxxbridge-cpp.lib(lib.cpp.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '2' doesn't match value '0' in libcxx-bafec361a1a30317.rlib(cxx.o)
+
+          cxxbridge-cpp.lib(lib.cpp.obj) : error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MDd_DynamicDebug' doesn't match value 'MD_DynamicRelease' in libcxx-bafec361a1a30317.rlib(cxx.o)
+
+          cpp_lib.lib(cpplib.cpp.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '2' doesn't match value '0' in libcxx-bafec361a1a30317.rlib(cxx.o)
+
+          cpp_lib.lib(cpplib.cpp.obj) : error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MDd_DynamicDebug' doesn't match value 'MD_DynamicRelease' in libcxx-bafec361a1a30317.rlib(cxx.o)
+
+          msvcrt.lib(initializers.obj) : warning LNK4098: defaultlib 'msvcrtd.lib' conflicts with use of other libs; use /NODEFAULTLIB:library
+```
+
+### Solutions
+
+One solution is to also use the non-debug version when building the C/C++ libraries. 
+You can set the [MSVC_RUNTIME_LIBRARY] target properties of your C/C++ libraries to the non-debug variants.
+By default you will probably want to select the `MultiThreadedDLL` variant, unless you specified
+[`-Ctarget-feature=+crt-static`](https://rust-lang.github.io/rfcs/1721-crt-static.html) in your
+`RUSTFLAGS`.
+
+
+[MSVC_RUNTIME_LIBRARY]: https://cmake.org/cmake/help/latest/prop_tgt/MSVC_RUNTIME_LIBRARY.html#prop_tgt:MSVC_RUNTIME_LIBRARY
+
+## Linking Rust static libraries into Debug C/C++ binaries fails on Windows MSVC targets
+
+This issue is quite similar to the previous one, except that this time it's a Rust library being linked
+into a C/C++ target. If it's 100% only Rust code you likely won't even have any issues.
+However, if somewhere in the dependency graph C/C++ code is built and linked into your Rust library,
+you will likely encounter this issue. Please note, that using [cxx] counts as using C++ code and will
+lead to this issue.
+
+The previous solution should also work for this case, but additionally you [may also
+have success](https://github.com/rust-lang/rust/issues/39016#issuecomment-853964918) by using 
+`corrosion_set_env_vars(your_rust_lib "CFLAGS=-MDd" "CXXFLAGS=-MDd")` (or `-MTd` for a statically linked
+runtime).
+For debug builds, this is likely to be the preferable solution. It assumes that downstream C/C++ code
+is built by the `cc` crate, which respects the `CFLAGS` and `CXXFLAGS` environment variables.
+
+[cxx]: https://github.com/dtolnay/cxx
+
+
+## Missing `soname` on Linux for `cdylibs`
+
+Cargo doesn't support setting the `soname` field for cdylib, which may cause issues.
+You can set the soname manually by passing a linker-flag such as `-Clink-arg=-Wl,-soname,libyour_crate.so`
+to the linker via `corrosion_add_target_local_rustflags()` and additionally seting the `IMPORTED_SONAME`
+property on the import CMake target:  
+```
+set_target_properties(your_crate-shared PROPERTIES IMPORTED_SONAME libyour_crate.so)
+```
+Replace `your_crate` with the name of your shared library as defined in the `[lib]` section of your Cargo.toml
+Manifest file.
+
+Attention: The Linux section may not be entirely correct, maybe `$ORIGIN` needs to be added to the linker arguments.
+Feel free to open a pull-request with corrections.
+
+## Missing `install_name` on MacOS for `ccdylibs` / Hardcoded references to the build-directory
+
+The solution here is essentially the same as in the previous section.
+```
+corrosion_add_target_local_rustflags(your_crate -Clink-arg=-Wl,-install_name,@rpath/libyour_crate.dylib,-current_version,${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR},-compatibility_version,${PROJECT_VERSION_MAJOR}.0)
+set_target_properties(your_crate-shared PROPERTIES IMPORTED_NO_SONAME 0)
+set_target_properties(your_crate-shared PROPERTIES IMPORTED_SONAME libyour_crate.dylib)
+```
+When building binaries using this shared library, you should set the build rpath to the output directory of
+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).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/doc/src/ffi_bindings.md	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,43 @@
+# Integrating Automatically Generated FFI Bindings
+
+There are a number of tools to automatically generate bindings between Rust and different
+foreign languages.
+
+1. [bindgen](#bindgen)
+2. [cbindgen](#cbindgen-integration)
+3. [cxx](#cxx-integration)
+
+## bindgen
+
+[bindgen] is a tool to automatically generate Rust bindings from C headers.
+As such, integrating bindgen [via a build-script](https://rust-lang.github.io/rust-bindgen/library-usage.html)
+works well and their doesn't seem to be a need to create CMake rules for 
+generating the bindings.
+
+[bindgen]: https://github.com/rust-lang/rust-bindgen
+
+## cbindgen integration
+
+⚠️⚠️⚠️ **EXPERIMENTAL** ⚠️⚠️⚠️
+
+[cbindgen] is a tool that generates C/C++ headers from Rust code. When compiling C/C++
+code that `#include`s such generated headers the buildsystem must be aware of the dependencies.
+Generating the headers via a build-script is possible, but Corrosion offers no guidance here.
+
+Instead, Corrosion offers an experimental function to add CMake rules using cbindgen to generate
+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** ⚠️⚠️⚠️
+
+[cxx] is a tool which generates bindings for C++/Rust interop.
+
+{{#include ../../cmake/Corrosion.cmake:corrosion_add_cxxbridge}}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/doc/src/introduction.md	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,19 @@
+## About Corrosion
+
+Corrosion, formerly known as cmake-cargo, is a tool for integrating Rust into an existing CMake
+project. Corrosion is capable of automatically importing executables, static libraries, and
+dynamic libraries from a Rust package or workspace as CMake targets.
+
+The imported static and dynamic library types can be linked into C/C++ CMake targets using the usual
+CMake functions such as [`target_link_libraries()`].
+For rust executables and dynamic libraries corrosion provides a `corrosion_link_libraries`
+helper function to conveniently add the necessary flags to link C/C++ libraries into
+the rust target.
+
+You are currently viewing the documentation of the stable v0.5 release branch.
+
+[`target_link_libraries()`]: https://cmake.org/cmake/help/latest/command/target_link_libraries.html
+
+## Requirements
+
+Corrosion v0.5 requires at least CMake 3.15 and at least Rust 1.46 or newer.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/doc/src/quick_start.md	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,36 @@
+# Quick Start
+
+You can add corrosion to your project via the `FetchContent` CMake module or one of the other methods
+described in the [Setup chapter](setup_corrosion.md).
+Afterwards you can import Rust targets defined in a `Cargo.toml` manifest file by using
+`corrosion_import_crate`. This will add CMake targets with names matching the crate names defined
+in the Cargo.toml manifest. These targets can then subsequently be used, e.g. to link the imported
+target into a regular C/C++ target.
+
+The example below shows how to add Corrosion to your project via `FetchContent`
+and how to import a rust library and link it into a regular C/C++ CMake target.
+
+```cmake
+include(FetchContent)
+
+FetchContent_Declare(
+    Corrosion
+    GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
+    GIT_TAG v0.5 # Optionally specify a commit hash, version tag or branch here
+)
+# Set any global configuration variables such as `Rust_TOOLCHAIN` before this line!
+FetchContent_MakeAvailable(Corrosion)
+
+# Import targets defined in a package or workspace manifest `Cargo.toml` file
+corrosion_import_crate(MANIFEST_PATH rust-lib/Cargo.toml)
+
+add_executable(your_cool_cpp_bin main.cpp)
+
+# In this example the the `Cargo.toml` file passed to `corrosion_import_crate` is assumed to have
+# defined a static (`staticlib`) or shared (`cdylib`) rust library with the name "rust-lib".
+# A target with the same name is now available in CMake and you can use it to link the rust library into
+# your C/C++ CMake target(s).
+target_link_libraries(your_cool_cpp_bin PUBLIC rust-lib)
+```
+
+Please see the [Usage chapter](usage.md) for a complete discussion of possible configuration options.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/doc/src/setup_corrosion.md	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,89 @@
+# Adding Corrosion to your project
+
+There are two fundamental installation methods that are supported by Corrosion - installation as a
+CMake package or using it as a subdirectory in an existing CMake project. For CMake versions below
+3.19 Corrosion strongly recommends installing the package, either via a package manager or manually
+using CMake's installation facilities.
+If you have CMake 3.19 or newer, we recommend to use either the [FetchContent](#fetchcontent) or the 
+[Subdirectory](#subdirectory) method to integrate Corrosion.
+
+## FetchContent
+If you are using CMake >= 3.19 or installation is difficult or not feasible in
+your environment, you can use the
+[FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) module to include
+Corrosion. This will download Corrosion and use it as if it were a subdirectory at configure time.
+
+In your CMakeLists.txt:
+```cmake
+include(FetchContent)
+
+FetchContent_Declare(
+    Corrosion
+    GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
+    GIT_TAG v0.5 # Optionally specify a commit hash, version tag or branch here
+)
+# Set any global configuration variables such as `Rust_TOOLCHAIN` before this line!
+FetchContent_MakeAvailable(Corrosion)
+```
+
+## Subdirectory
+Corrosion can also be used directly as a subdirectory. This solution may work well for small
+projects, but it's discouraged for large projects with many dependencies, especially those which may
+themselves use Corrosion. Either copy the Corrosion library into your source tree, being sure to
+preserve the `LICENSE` file, or add this repository as a git submodule:
+```bash
+git submodule add https://github.com/corrosion-rs/corrosion.git
+```
+
+From there, using Corrosion is easy. In your CMakeLists.txt:
+```cmake
+add_subdirectory(path/to/corrosion)
+```
+
+## Installation
+
+
+Installation will pre-build all of Corrosion's native tooling (required only for CMake versions
+below 3.19) and install it together with Corrosions CMake files into a standard location.
+On CMake >= 3.19 installing Corrosion does not offer any speed advantages, unless the native
+tooling option is explicitly enabled.
+
+### Install from source
+
+First, download and install Corrosion:
+```bash
+git clone https://github.com/corrosion-rs/corrosion.git
+# Optionally, specify -DCMAKE_INSTALL_PREFIX=<target-install-path> to specify a 
+# custom installation directory
+cmake -Scorrosion -Bbuild -DCMAKE_BUILD_TYPE=Release
+cmake --build build --config Release
+# This next step may require sudo or admin privileges if you're installing to a system location,
+# which is the default.
+cmake --install build --config Release
+```
+
+You'll want to ensure that the install directory is available in your `PATH` or `CMAKE_PREFIX_PATH`
+environment variable. This is likely to already be the case by default on a Unix system, but on
+Windows it will install to `C:\Program Files (x86)\Corrosion` by default, which will not be in your
+`PATH` or `CMAKE_PREFIX_PATH` by default.
+
+Once Corrosion is installed, and you've ensured the package is available in your `PATH`, you
+can use it from your own project like any other package from your CMakeLists.txt:
+```cmake
+find_package(Corrosion REQUIRED)
+```
+
+### Package Manager
+
+#### Homebrew (unofficial)
+
+Corrosion is available via Homebrew and can be installed via
+
+```bash
+brew install corrosion
+```
+
+Please note that this package is community maintained. Please also keep in mind that Corrosion follows
+semantic versioning and minor version bumps (i.e. `0.3` -> `0.4`) may contain breaking changes, while 
+Corrosion is still pre `1.0`.
+Please read the release notes when upgrading Corrosion.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/doc/src/usage.md	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,320 @@
+## Usage
+
+### Automatically import crate targets with `corrosion_import_crate`
+
+In order to integrate a Rust crate into CMake, you first need to import Rust crates from
+a [package] or [workspace]. Corrosion provides `corrosion_import_crate()` to automatically import
+crates defined in a Cargo.toml Manifest file:
+
+{{#include ../../cmake/Corrosion.cmake:corrosion-import-crate}}
+
+Corrosion will use `cargo metadata` to add a cmake target for each crate defined in the Manifest file
+and add the necessary rules to build the targets.
+For Rust executables an [`IMPORTED`] executable target is created with the same name as defined in the `[[bin]]`
+section of the Manifest corresponding to this target.
+If no such name was defined the target name defaults to the Rust package name.
+For Rust library targets an [`INTERFACE`] library target is created with the same name as defined in the `[lib]`
+section of the Manifest. This `INTERFACE` library links an internal corrosion target, which is either a
+`SHARED` or `STATIC` `IMPORTED` library, depending on the Rust crate type (`cdylib` vs `staticlib`).
+
+The created library targets can be linked into other CMake targets by simply using [target_link_libraries].
+
+Corrosion will by default copy the produced Rust artifacts into `${CMAKE_CURRENT_BINARY_DIR}`. The target location
+can be changed by setting the CMake `OUTPUT_DIRECTORY` target properties on the imported Rust targets.
+See the [OUTPUT_DIRECTORY](#cmake-output_directory-target-properties-and-imported_location) section for more details.
+
+Many of the options available for `corrosion_import_crate` can also be individually set per
+target, see [Per Target options](#per-target-options) for details.
+
+[package]: https://doc.rust-lang.org/book/ch07-01-packages-and-crates.html
+[workspace]: https://doc.rust-lang.org/cargo/reference/workspaces.html
+[`IMPORTED`]: https://cmake.org/cmake/help/latest/prop_tgt/IMPORTED.html
+[`INTERFACE`]: https://cmake.org/cmake/help/latest/command/add_library.html#interface-libraries
+[target_link_libraries]: https://cmake.org/cmake/help/latest/command/target_link_libraries.html
+
+### Per Target options
+
+Some configuration options can be specified individually for each target. You can set them via the
+`corrosion_set_xxx()` functions specified below:
+
+- `corrosion_set_env_vars(<target_name> <key1=value1> [... <keyN=valueN>])`: Define environment variables
+  that should be set during the invocation of `cargo build` for the specified target. Please note that
+  the environment variable will only be set for direct builds of the target via cmake, and not for any
+  build where cargo built the crate in question as a dependency for another target.
+  The environment variables may contain generator expressions.
+- `corrosion_add_target_rustflags(<target_name> <rustflag> [... <rustflagN>])`: When building the target,
+  the `RUSTFLAGS` environment variable will contain the flags added via this function. Please note that any
+  dependencies (built by cargo) will also see these flags. See also: `corrosion_add_target_local_rustflags`.
+- `corrosion_add_target_local_rustflags(target_name rustc_flag [more_flags ...])`: Support setting
+  rustflags for only the main target (crate) and none of its dependencies.
+  This is useful in cases where you only need rustflags on the main-crate, but need to set different
+  flags for different targets. Without "local" Rustflags this would require rebuilds of the
+  dependencies when switching targets.
+- `corrosion_set_hostbuild(<target_name>)`: The target should be compiled for the Host target and ignore any
+  cross-compile configuration.
+- `corrosion_set_features(<target_name> [ALL_FEATURES <Bool>] [NO_DEFAULT_FEATURES] [FEATURES <feature1> ... ])`:
+  For a given target, enable specific features via `FEATURES`, toggle `ALL_FEATURES` on or off or disable all features
+  via `NO_DEFAULT_FEATURES`. For more information on features, please see also the
+  [cargo reference](https://doc.rust-lang.org/cargo/reference/features.html).
+- `corrosion_set_cargo_flags(<target_name> <flag1> ...])`:
+  For a given target, add options and flags at the end of `cargo build` invocation. This will be appended after any
+  arguments passed through the `FLAGS` during the crate import.
+- `corrosion_set_linker(target_name linker)`: Use `linker` to link the target.
+  Please note that this only has an effect for targets where the final linker invocation is done
+  by cargo, i.e. targets where foreign code is linked into rust code and not the other way around.
+  Please also note that if you are cross-compiling and specify a linker such as `clang`, you are
+  responsible for also adding a rustflag which adds the necessary `--target=` argument for the
+  linker.
+
+
+### Global Corrosion Options
+All of 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`.
+- `Rust_RESOLVE_RUSTUP_TOOLCHAINS:BOOL` - If the found `rustc` is a `rustup` proxy, resolve a
+  concrete path to a specific toolchain managed by `rustup`, according to the `rustup` toolchain
+  selection rules and other options detailed here. If this option is turned off, the found `rustc`
+  will be used as-is to compile, even if it is a `rustup` proxy, which might increase compilation
+  time. Default: `ON` if the found `rustc` is a rustup proxy or a `rustup` managed toolchain was
+  requested, `OFF` otherwise. Forced `OFF` if `rustup` was not found.
+- `Rust_CARGO:STRING` - Path to `cargo`. Default: the `cargo` installed next to `${Rust_COMPILER}`.
+- `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_NATIVE_TOOLING:BOOL` - Use a native tool (written in Rust) as part of Corrosion. This
+  option increases the configure-time significantly unless Corrosion is installed.
+  Default: `OFF` if CMake >= 3.19.0. Forced `ON` for CMake < 3.19.
+
+
+#### Developer/Maintainer Options
+These options are not used in the course of normal Corrosion usage, but are used to configure how
+Corrosion is built and installed. Only applies to Corrosion builds and subdirectory uses.
+
+- `CORROSION_DEV_MODE:BOOL` - Indicates that Corrosion is being actively developed. Default: `OFF`
+  if Corrosion is a subdirectory, `ON` if it is the top-level project
+- `CORROSION_BUILD_TESTS:BOOL` - Build the Corrosion tests. Default: `Off` if Corrosion is a
+  subdirectory, `ON` if it is the top-level project
+- `CORROSION_GENERATOR_EXECUTABLE:STRING` - Specify a path to the corrosion-generator executable.
+  This is to support scenarios where it's easier to build corrosion-generator outside of the normal
+  bootstrap path, such as in the case of package managers that make it very easy to import Rust
+  crates for fully reproducible, offline builds.
+- `CORROSION_INSTALL_EXECUTABLE:BOOL` - Controls whether corrosion-generator is installed with the
+  package. Default: `ON` with `CORROSION_GENERATOR_EXECUTABLE` unset, otherwise `OFF`
+
+
+### Information provided by Corrosion
+
+For your convenience, Corrosion sets a number of variables which contain information about the version of the rust
+toolchain. You can use the CMake version comparison operators
+(e.g. [`VERSION_GREATER_EQUAL`](https://cmake.org/cmake/help/latest/command/if.html#version-comparisons)) on the main
+variable (e.g. `if(Rust_VERSION VERSION_GREATER_EQUAL "1.57.0")`), or you can inspect the major, minor and patch
+versions individually.
+- `Rust_VERSION<_MAJOR|_MINOR|_PATCH>` - The version of rustc.
+- `Rust_CARGO_VERSION<_MAJOR|_MINOR|_PATCH>` - The cargo version.
+- `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.
+- 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`
+  - `Rust_CARGO_TARGET_VENDOR`, `Rust_CARGO_HOST_VENDOR`: e.g. `apple`, `pc`, `unknown` etc.
+  - `Rust_CARGO_TARGET_OS`, `Rust_CARGO_HOST_OS`:  e.g. `darwin`, `linux`, `windows`, `none`
+  - `Rust_CARGO_TARGET_ENV`, `Rust_CARGO_HOST_ENV`: e.g. `gnu`, `musl`
+
+
+
+
+### Selecting a custom cargo profile
+
+[Rust 1.57](https://blog.rust-lang.org/2021/12/02/Rust-1.57.0.html) stabilized the support for custom
+[profiles](https://doc.rust-lang.org/cargo/reference/profiles.html). If you are using a sufficiently new rust toolchain,
+you may select a custom profile by adding the optional argument `PROFILE <profile_name>` to
+`corrosion_import_crate()`. If you do not specify a profile, or you use an older toolchain, corrosion will select
+the standard `dev` profile if the CMake config is either `Debug` or unspecified. In all other cases the `release`
+profile is chosen for cargo.
+
+### Importing C-Style Libraries Written in Rust
+Corrosion makes it completely trivial to import a crate into an existing CMake project. Consider
+a project called [rust2cpp](test/rust2cpp/rust2cpp) with the following file structure:
+```
+rust2cpp/
+    rust/
+        src/
+            lib.rs
+        Cargo.lock
+        Cargo.toml
+    CMakeLists.txt
+    main.cpp
+```
+
+This project defines a simple Rust lib crate, like so, in [`rust2cpp/rust/Cargo.toml`](test/rust2cpp/rust2cpp/rust/Cargo.toml):
+```toml
+[package]
+name = "rust-lib"
+version = "0.1.0"
+authors = ["Andrew Gaspar <andrew.gaspar@outlook.com>"]
+license = "MIT"
+edition = "2018"
+
+[dependencies]
+
+[lib]
+crate-type=["staticlib"]
+```
+
+In addition to `"staticlib"`, you can also use `"cdylib"`. In fact, you can define both with a
+single crate and switch between which is used using the standard
+[`BUILD_SHARED_LIBS`](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html) variable.
+
+This crate defines a simple crate called `rust-lib`. Importing this crate into your
+[CMakeLists.txt](test/rust2cpp/CMakeLists.txt) is trivial:
+```cmake
+# Note: you must have already included Corrosion for `corrosion_import_crate` to be available. See # the `Installation` section above.
+
+corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml)
+```
+
+Now that you've imported the crate into CMake, all of the executables, static libraries, and dynamic
+libraries defined in the Rust can be directly referenced. So, merely define your C++ executable as
+normal in CMake and add your crate's library using target_link_libraries:
+```cmake
+add_executable(cpp-exe main.cpp)
+target_link_libraries(cpp-exe PUBLIC rust-lib)
+```
+
+That's it! You're now linking your Rust library to your C++ library.
+
+#### Generate Bindings to Rust Library Automatically
+
+Currently, you must manually declare bindings in your C or C++ program to the exported routines and
+types in your Rust project. You can see boths sides of this in
+[the Rust code](test/rust2cpp/rust2cpp/rust/src/lib.rs) and in [the C++ code](test/rust2cpp/rust2cpp/main.cpp).
+
+Integration with [cbindgen](https://github.com/eqrion/cbindgen) is
+planned for the future.
+
+### Importing Libraries Written in C and C++ Into Rust
+
+The rust targets can be imported with `corrosion_import_crate()` into CMake.
+For targets where the linker should be invoked by Rust corrosion provides
+`corrosion_link_libraries()` to link your C/C++ libraries with the Rust target.
+For additional linker flags you may use `corrosion_add_target_local_rustflags()`
+and pass linker arguments via the `-Clink-args` flag to rustc. These flags will
+only be passed to the final rustc invocation and not affect any rust dependencies.
+
+C bindings can be generated via [bindgen](https://github.com/rust-lang/rust-bindgen).
+Corrosion does not offer any direct integration yet, but you can either generate the
+bindings in the build-script of your crate, or generate the bindings as a CMake build step
+(e.g. a custom target) and add a dependency from `cargo-prebuild_<rust_target>` to your
+custom target for generating the bindings.
+
+Example:
+
+```cmake
+# Import your Rust targets
+corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml)
+# Link C/C++ libraries with your Rust target
+corrosion_link_libraries(target_name c_library)
+# Optionally explicitly define which linker to use.
+corrosion_set_linker(target_name your_custom_linker)
+# Optionally set linker arguments
+corrosion_add_target_local_rustflags(target_name "-Clink-args=<linker arguments>")
+# Optionally tell CMake that the rust crate depends on another target (e.g. a code generator)
+add_dependencies(cargo-prebuild_<target_name> custom_bindings_target)
+```
+
+### Cross Compiling
+Corrosion attempts to support cross-compiling as generally as possible, though not all
+configurations are tested. Cross-compiling is explicitly supported in the following scenarios.
+
+In all cases, you will need to install the standard library for the Rust target triple. When using
+Rustup, you can use it to install the target standard library:
+
+```bash
+rustup target add <target-rust-triple>
+```
+
+If the target triple is automatically derived, Corrosion will print the target during configuration.
+For example:
+
+```
+-- Rust Target: aarch64-linux-android
+```
+
+#### Windows-to-Windows
+Corrosion supports cross-compiling between arbitrary Windows architectures using the Visual Studio
+Generator. For example, to cross-compile for ARM64 from any platform, simply set the `-A`
+architecture flag:
+
+```bash
+cmake -S. -Bbuild-arm64 -A ARM64
+cmake --build build-arm64
+```
+
+Please note that for projects containing a build-script at least Rust 1.54 is required due to a bug
+in previous cargo versions, which causes the build-script to incorrectly be built for the target
+platform.
+
+#### Linux-to-Linux
+In order to cross-compile on Linux, you will need to install a cross-compiler. For example, on
+Ubuntu, to cross compile for 64-bit Little-Endian PowerPC Little-Endian, install
+`g++-powerpc64le-linux-gnu` from apt-get:
+
+```bash
+sudo apt install g++-powerpc64le-linux-gnu
+```
+
+Currently, Corrosion does not automatically determine the target triple while cross-compiling on
+Linux, so you'll need to specify a matching `Rust_CARGO_TARGET`.
+
+```bash
+cmake -S. -Bbuild-ppc64le -DRust_CARGO_TARGET=powerpc64le-unknown-linux-gnu -DCMAKE_CXX_COMPILER=powerpc64le-linux-gnu-g++
+cmake --build build-ppc64le
+```
+
+#### Android
+
+Cross-compiling for Android is supported on all platforms with the Makefile and Ninja generators,
+and the Rust target triple will automatically be selected. The CMake
+[cross-compiling instructions for Android](https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-android)
+apply here. For example, to build for ARM64:
+
+```bash
+cmake -S. -Bbuild-android-arm64 -GNinja -DCMAKE_SYSTEM_NAME=Android \
+      -DCMAKE_ANDROID_NDK=/path/to/android-ndk-rxxd -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a
+```
+
+**Important note:** The Android SDK ships with CMake 3.10 at newest, which Android Studio will
+prefer over any CMake you've installed locally. CMake 3.10 is insufficient for using Corrosion,
+which requires a minimum of CMake 3.15. If you're using Android Studio to build your project,
+follow the instructions in the Android Studio documentation for
+[using a specific version of CMake](https://developer.android.com/studio/projects/install-ndk#vanilla_cmake).
+
+
+### CMake `OUTPUT_DIRECTORY` target properties and `IMPORTED_LOCATION`
+
+Corrosion respects the following `OUTPUT_DIRECTORY` target properties on CMake >= 3.19:
+-   [ARCHIVE_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.html)
+-   [LIBRARY_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/LIBRARY_OUTPUT_DIRECTORY.html)
+-   [RUNTIME_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/RUNTIME_OUTPUT_DIRECTORY.html)
+-   [PDB_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/prop_tgt/PDB_OUTPUT_DIRECTORY.html)
+
+If the target property is set (e.g. by defining the `CMAKE_XYZ_OUTPUT_DIRECTORY` variable before calling
+`corrosion_import_crate()`), corrosion will copy the built rust artifacts to the location defined in the
+target property.
+Due to limitations in CMake these target properties are evaluated in a deferred manner, to
+support the user setting the target properties after the call to `corrosion_import_crate()`.
+This has the side effect that the `IMPORTED_LOCATION` property will be set late, and users should not
+use `get_property` to read `IMPORTED_LOCATION` at configure time. Instead, generator expressions
+should be used to get the location of the target artifact.
+If `IMPORTED_LOCATION` is needed at configure time users may use `cmake_language(DEFER CALL ...)` to defer
+evaluation to after the `IMPORTED_LOCATION` property is set.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/generator/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,64 @@
+message(STATUS "Building CMake Generator for Corrosion - This may take a while")
+
+set(generator_src "${CMAKE_CURRENT_BINARY_DIR}/legacy_generator_src")
+set(generator_destination "${CMAKE_CURRENT_BINARY_DIR}/legacy_generator")
+set(generator_build_quiet "")
+
+file(MAKE_DIRECTORY "${generator_src}")
+file(COPY src DESTINATION "${generator_src}")
+if(Rust_VERSION VERSION_LESS "1.56")
+    message(STATUS "Corrosion Generator: Using Compatibility lock file, due to rust version less than 1.56")
+    file(COPY Compat.Cargo.lock Compat.Cargo.toml DESTINATION "${generator_src}")
+    file(RENAME "${generator_src}/Compat.Cargo.lock" "${generator_src}/Cargo.lock")
+    file(RENAME "${generator_src}/Compat.Cargo.toml" "${generator_src}/Cargo.toml")
+else()
+    file(COPY Cargo.lock Cargo.toml DESTINATION "${generator_src}")
+endif()
+
+# Using cargo install has the advantage of caching the build in the user .cargo directory,
+# so likely the rebuild will be very cheap even after deleting the build directory.
+execute_process(
+        COMMAND ${CMAKE_COMMAND}
+        -E env
+        # If the Generator is built at configure of a project (instead of being pre-installed)
+        # We don't want environment variables like `RUSTFLAGS` affecting the Generator build.
+        --unset=RUSTFLAGS
+        "CARGO_BUILD_RUSTC=${RUSTC_EXECUTABLE}"
+        "${CARGO_EXECUTABLE}" install
+        --path "."
+        --root "${generator_destination}"
+        --locked
+        ${_CORROSION_QUIET_OUTPUT_FLAG}
+        WORKING_DIRECTORY "${generator_src}"
+        RESULT_VARIABLE generator_build_failed
+)
+if(generator_build_failed)
+    message(FATAL_ERROR "Building CMake Generator for Corrosion - failed")
+else()
+    message(STATUS "Building CMake Generator for Corrosion - done")
+endif()
+set(host_executable_suffix "")
+if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+    set(host_executable_suffix ".exe")
+endif()
+
+set(_CORROSION_GENERATOR_EXE
+        "${generator_destination}/bin/corrosion-generator${host_executable_suffix}"
+)
+
+add_executable(Corrosion::Generator IMPORTED GLOBAL)
+set_property(
+        TARGET Corrosion::Generator
+        PROPERTY IMPORTED_LOCATION "${_CORROSION_GENERATOR_EXE}")
+
+if (CORROSION_DEV_MODE)
+    # If you're developing Corrosion, you want to make sure to re-configure whenever the
+    # generator changes.
+    file(GLOB_RECURSE _RUST_FILES CONFIGURE_DEPENDS generator/src/*.rs)
+    file(GLOB _CARGO_FILES CONFIGURE_DEPENDS generator/Cargo.*)
+    set_property(
+            DIRECTORY APPEND
+            PROPERTY CMAKE_CONFIGURE_DEPENDS
+            ${_RUST_FILES} ${_CARGO_FILES})
+endif()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/generator/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,17 @@
+[package]
+name = "corrosion-generator"
+version = "0.1.0"
+authors = ["Andrew Gaspar <andrew.gaspar@outlook.com>"]
+license = "MIT"
+edition = "2018"
+
+[dependencies]
+cargo_metadata = "0.17"
+serde = { version = " 1.0.186", features = ["derive"] }
+
+[dependencies.clap]
+version = "2.34"
+default-features = false
+# Make sure this crate still compiles while it is checked out
+# in a sub-directory of a repository that has a Cargo.toml.
+[workspace]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/generator/Compat.Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,23 @@
+[package]
+name = "corrosion-generator"
+version = "0.1.0"
+authors = ["Andrew Gaspar <andrew.gaspar@outlook.com>"]
+license = "MIT"
+edition = "2018"
+
+[dependencies]
+cargo_metadata = "0.15"
+# The crates below are indirect dependencies of cargo metadata,
+# We explicitly specify maximum versions to allow building the generator
+# with older toolchains.
+# Version 1.0.157 upgrades to syn 2.0 and raises MSRV to 1.56
+serde = { version = ">=1, <1.0.157", default-features=false }
+# Version 1.0.40 upgrades to syn 2.0 and raises MSRV to 1.56
+thiserror = { version = ">=1, <1.0.40", default-features=false }
+
+[dependencies.clap]
+version = "2.34"
+default-features = false
+# Make sure this crate still compiles while it is checked out
+# in a sub-directory of a repository that has a Cargo.toml.
+[workspace]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/generator/src/main.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,95 @@
+use std::path::PathBuf;
+
+use cargo_metadata::Metadata;
+use clap::{App, Arg};
+
+mod subcommands {
+    pub mod gen_cmake;
+}
+
+use subcommands::*;
+
+// common options
+const MANIFEST_PATH: &str = "manifest-path";
+const CARGO_EXECUTABLE: &str = "cargo-executable";
+const VERBOSE: &str = "verbose";
+const LOCKED: &str = "locked";
+const FROZEN: &str = "frozen";
+
+pub struct GeneratorSharedArgs {
+    pub manifest_path: PathBuf,
+    pub cargo_executable: PathBuf,
+    pub metadata: Metadata,
+    pub verbose: bool,
+}
+
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+    let matches = App::new("CMake Generator for Cargo")
+        .version("0.1")
+        .author("Andrew Gaspar <andrew.gaspar@outlook.com>")
+        .about("Generates CMake files for Cargo projects")
+        .arg(
+            Arg::with_name(MANIFEST_PATH)
+                .long("manifest-path")
+                .value_name("Cargo.toml")
+                .help("Specifies the target Cargo project")
+                .required(true)
+                .takes_value(true),
+        )
+        .arg(
+            Arg::with_name(CARGO_EXECUTABLE)
+                .long("cargo")
+                .value_name("EXECUTABLE")
+                .required(true)
+                .help("Path to the cargo executable to use"),
+        )
+        .arg(
+            Arg::with_name(VERBOSE)
+                .long("verbose")
+                .help("Request verbose output"),
+        )
+        .arg(
+            Arg::with_name(LOCKED)
+                .long("locked")
+                .help("Pass --locked to cargo invocations"),
+        )
+        .arg(
+            Arg::with_name(FROZEN)
+                .long("frozen")
+                .help("Pass --frozen to cargo invocations"),
+        )
+        .subcommand(gen_cmake::subcommand())
+        .get_matches();
+
+    let mut cmd = cargo_metadata::MetadataCommand::new();
+    cmd.no_deps();
+    if matches.is_present(LOCKED) {
+        cmd.other_options(["--locked".into()]);
+    }
+    if matches.is_present(FROZEN) {
+        cmd.other_options(["--frozen".into()]);
+    }
+
+    let manifest_path = matches.value_of(MANIFEST_PATH).unwrap();
+    let cargo_executable = matches.value_of(CARGO_EXECUTABLE).unwrap();
+
+    cmd.manifest_path(manifest_path);
+    cmd.cargo_path(cargo_executable);
+
+    let metadata = cmd.exec()?;
+
+    let shared_args = GeneratorSharedArgs {
+        manifest_path: manifest_path.into(),
+        cargo_executable: cargo_executable.into(),
+        metadata,
+        verbose: matches.is_present(VERBOSE),
+    };
+
+    match matches.subcommand() {
+        (gen_cmake::GEN_CMAKE, Some(matches)) => gen_cmake::invoke(&shared_args, matches)?,
+        _ => unreachable!(),
+    };
+
+    // We should never reach this statement
+    std::process::exit(1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/generator/src/subcommands/gen_cmake.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,160 @@
+use std::{
+    fs::{create_dir_all, File},
+    io::{stdout, Write},
+    path::Path,
+    rc::Rc,
+};
+
+use clap::{App, Arg, ArgMatches, SubCommand};
+
+mod target;
+
+// Command name
+pub const GEN_CMAKE: &str = "gen-cmake";
+
+// Options
+const OUT_FILE: &str = "out-file";
+const CONFIGURATION_ROOT: &str = "configuration-root";
+const CRATES: &str = "crates";
+const IMPORTED_CRATES: &str = "imported-crates";
+const CRATE_TYPE: &str = "crate-type";
+const PASSTHROUGH_ADD_CARGO_BUILD: &str = "passthrough-acb";
+
+pub fn subcommand() -> App<'static, 'static> {
+    SubCommand::with_name(GEN_CMAKE)
+        .arg(
+            Arg::with_name(CONFIGURATION_ROOT)
+                .long("configuration-root")
+                .value_name("DIRECTORY")
+                .takes_value(true)
+                .help(
+                    "Specifies a root directory for configuration folders. E.g. Win32 \
+                 in VS Generator.",
+                ),
+        )
+        .arg(
+            Arg::with_name(CRATES)
+                .long("crates")
+                .value_name("crates")
+                .takes_value(true)
+                .multiple(true)
+                .require_delimiter(true)
+                .help("Specifies which crates of the workspace to import"),
+        )
+        .arg(
+            Arg::with_name(CRATE_TYPE)
+                .long(CRATE_TYPE)
+                .value_name("kind")
+                .possible_values(&["staticlib", "cdylib", "bin"])
+                .multiple(true)
+                .value_delimiter(";")
+                .help("Only import the specified crate types")
+        )
+        .arg(
+            Arg::with_name(OUT_FILE)
+                .short("o")
+                .long("out-file")
+                .value_name("FILE")
+                .help("Output CMake file name. Defaults to stdout."),
+        )
+        .arg(
+            Arg::with_name(IMPORTED_CRATES)
+                .long(IMPORTED_CRATES)
+                .value_name("variable_name")
+                .takes_value(true)
+                .help("Save a list of the imported target names into c CMake variable with the given name"),
+        )
+        .arg(
+            Arg::with_name(PASSTHROUGH_ADD_CARGO_BUILD)
+                .long(PASSTHROUGH_ADD_CARGO_BUILD)
+                .takes_value(true)
+                .multiple(true)
+                .value_delimiter(std::char::from_u32(0x1f).unwrap().to_string().as_str())
+                .help("Passthrough arguments to the _add_cargo_build invocation(s) in CMake")
+        )
+}
+
+pub fn invoke(
+    args: &crate::GeneratorSharedArgs,
+    matches: &ArgMatches,
+) -> Result<(), Box<dyn std::error::Error>> {
+    let mut out_file: Box<dyn Write> = if let Some(path) = matches.value_of(OUT_FILE) {
+        let path = Path::new(path);
+        if let Some(parent) = path.parent() {
+            create_dir_all(parent).expect("Failed to create directory!");
+        }
+        let file = File::create(path).expect("Unable to open out-file!");
+        Box::new(file)
+    } else {
+        Box::new(stdout())
+    };
+
+    writeln!(
+        out_file,
+        "\
+cmake_minimum_required(VERSION 3.15)
+"
+    )?;
+
+    let crates = matches
+        .values_of(CRATES)
+        .map_or(Vec::new(), |c| c.collect());
+    let crate_kinds: Option<Vec<&str>> = matches.values_of(CRATE_TYPE).map(|c| c.collect());
+    let workspace_manifest_path = Rc::new(args.manifest_path.clone());
+    let targets: Vec<_> = args
+        .metadata
+        .packages
+        .iter()
+        .filter(|p| {
+            args.metadata.workspace_members.contains(&p.id)
+                && (crates.is_empty() || crates.contains(&p.name.as_str()))
+        })
+        .cloned()
+        .map(Rc::new)
+        .flat_map(|package| {
+            package
+                .targets
+                .iter()
+                .filter_map(|t| {
+                    target::CargoTarget::from_metadata(
+                        package.clone(),
+                        t.clone(),
+                        workspace_manifest_path.clone(),
+                        &crate_kinds,
+                    )
+                })
+                .collect::<Vec<_>>()
+        })
+        .collect();
+
+    let passthrough_args: Vec<String> = matches
+        .values_of(PASSTHROUGH_ADD_CARGO_BUILD)
+        .map(|values| {
+            // Add quotes around each argument for CMake to preserve which arguments belong together.
+            values
+                .filter(|val| !val.is_empty())
+                .map(|val| format!("\"{}\"", val))
+                .collect()
+        })
+        .unwrap_or_default();
+    let passthrough_str = passthrough_args.join(" ");
+
+    for target in &targets {
+        target
+            .emit_cmake_target(&mut out_file, &passthrough_str)
+            .unwrap();
+    }
+    if let Some(imported_crate_list_name) = matches.value_of(IMPORTED_CRATES) {
+        let imported_targets: Vec<_> = targets.iter().map(|target| target.target_name()).collect();
+        let imported_targets_list = imported_targets.join(";");
+        writeln!(
+            out_file,
+            "set({} \"{}\")",
+            imported_crate_list_name, imported_targets_list
+        )?;
+    }
+
+    writeln!(out_file)?;
+
+    std::process::exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/generator/src/subcommands/gen_cmake/target.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,222 @@
+use std::error::Error;
+use std::path::PathBuf;
+use std::rc::Rc;
+
+#[derive(Clone)]
+pub enum CargoTargetType {
+    Executable,
+    Library {
+        has_staticlib: bool,
+        has_cdylib: bool,
+    },
+}
+
+#[derive(Clone)]
+pub struct CargoTarget {
+    cargo_package: Rc<cargo_metadata::Package>,
+    cargo_target: cargo_metadata::Target,
+    target_type: CargoTargetType,
+    workspace_manifest_path: Rc<PathBuf>,
+}
+
+impl CargoTargetType {
+    fn to_string(&self) -> String {
+        let mut s = String::new();
+        match self {
+            Self::Executable => {
+                s.push_str("bin");
+            }
+            Self::Library {
+                has_staticlib,
+                has_cdylib,
+            } => {
+                if *has_staticlib {
+                    s.push_str("staticlib")
+                }
+                if *has_cdylib {
+                    s.push_str(" cdylib")
+                }
+            }
+        }
+        s
+    }
+}
+
+impl CargoTarget {
+    pub fn from_metadata(
+        cargo_package: Rc<cargo_metadata::Package>,
+        cargo_target: cargo_metadata::Target,
+        workspace_manifest_path: Rc<PathBuf>,
+        // If Some, only import crates if the kind variant is given in crate_kinds.
+        crate_kinds: &Option<Vec<&str>>,
+    ) -> Option<Self> {
+        let filtered_kinds: Vec<String> = cargo_target
+            .kind
+            .clone()
+            .into_iter()
+            .filter(|kind| match crate_kinds {
+                None => true,
+                Some(allowed_kinds_subset) => allowed_kinds_subset.contains(&&**kind),
+            })
+            .collect();
+
+        let target_type = if filtered_kinds
+            .iter()
+            .any(|k| k.as_str() == "staticlib" || k.as_str() == "cdylib")
+        {
+            CargoTargetType::Library {
+                has_staticlib: filtered_kinds.iter().any(|k| k == "staticlib"),
+                has_cdylib: filtered_kinds.iter().any(|k| k == "cdylib"),
+            }
+        } else if filtered_kinds.iter().any(|k| k == "bin") {
+            CargoTargetType::Executable
+        } else {
+            return None;
+        };
+
+        Some(Self {
+            cargo_package,
+            cargo_target,
+            target_type,
+            workspace_manifest_path,
+        })
+    }
+
+    /// Cargo / Rust 1.78 and newer replace dashes with underscores in libraries
+    /// To make the names consistent across versions we also do the replacement here.
+    pub(crate) fn target_name(&self) -> String {
+        match self.target_type {
+            CargoTargetType::Library { .. } => self.cargo_target.name.replace("-", "_"),
+            _ => self.cargo_target.name.to_string(),
+        }
+    }
+
+    pub fn emit_cmake_target(
+        &self,
+        out_file: &mut dyn std::io::Write,
+        passthrough_add_cargo_build: &str,
+    ) -> Result<(), Box<dyn Error>> {
+        writeln!(
+            out_file,
+            "set(byproducts \"\")
+                            set(cargo_build_out_dir \"\")
+                            set(archive_byproducts \"\")
+                            set(shared_lib_byproduct \"\")
+                            set(pdb_byproduct \"\")
+                            set(bin_byproduct \"\")
+        "
+        )?;
+        let ws_manifest = self
+            .workspace_manifest_path
+            .to_str()
+            .expect("Non-utf8 path encountered")
+            .replace("\\", "/");
+
+        match self.target_type {
+            CargoTargetType::Library {
+                has_staticlib,
+                has_cdylib,
+            } => {
+                assert!(has_staticlib || has_cdylib);
+                let ws_manifest = self
+                    .workspace_manifest_path
+                    .to_str()
+                    .expect("Non-utf8 path encountered")
+                    .replace("\\", "/");
+                let mut lib_kinds = if has_staticlib { "staticlib" } else { "" }.to_string();
+                if has_cdylib {
+                    if has_staticlib {
+                        lib_kinds.push(' ');
+                    }
+                    lib_kinds.push_str("cdylib")
+                }
+
+                writeln!(
+                    out_file,
+                    "
+                    _corrosion_add_library_target(
+                            WORKSPACE_MANIFEST_PATH \"{workspace_manifest_path}\"
+                            TARGET_NAME \"{target_name}\"
+                            LIB_KINDS {lib_kinds}
+                            OUT_ARCHIVE_OUTPUT_BYPRODUCTS archive_byproducts
+                            OUT_SHARED_LIB_BYPRODUCTS shared_lib_byproduct
+                            OUT_PDB_BYPRODUCT pdb_byproduct
+                    )
+                    list(APPEND byproducts
+                            \"${{archive_byproducts}}\"
+                            \"${{shared_lib_byproduct}}\"
+                            \"${{pdb_byproduct}}\"
+                    )
+                    ",
+                    workspace_manifest_path = ws_manifest,
+                    target_name = self.target_name(),
+                    lib_kinds = lib_kinds,
+                )?;
+            }
+            CargoTargetType::Executable => {
+                writeln!(
+                    out_file,
+                    "
+                    _corrosion_add_bin_target(\"{workspace_manifest_path}\" \"{target_name}\"
+                        bin_byproduct pdb_byproduct
+                    )
+                    set(byproducts \"\")
+                    list(APPEND byproducts \"${{bin_byproduct}}\" \"${{pdb_byproduct}}\")
+                    ",
+                    workspace_manifest_path = ws_manifest,
+                    target_name = self.target_name(),
+                )?;
+            }
+        };
+        let target_kinds = self.target_type.to_string();
+        writeln!(out_file,
+            "
+            set(cargo_build_out_dir \"\")
+            _add_cargo_build(
+                cargo_build_out_dir
+                PACKAGE \"{package_name}\"
+                TARGET \"{target_name}\"
+                MANIFEST_PATH \"{package_manifest_path}\"
+                WORKSPACE_MANIFEST_PATH \"{workspace_manifest_path}\"
+                TARGET_KINDS {target_kinds}
+                BYPRODUCTS \"${{byproducts}}\"
+                {passthrough_add_cargo_build}
+            )
+
+            set_target_properties({target_name} PROPERTIES
+                INTERFACE_COR_PACKAGE_MANIFEST_PATH \"{package_manifest_path}\"
+            )
+
+            if(archive_byproducts)
+                _corrosion_copy_byproducts(
+                    {target_name} ARCHIVE_OUTPUT_DIRECTORY \"${{cargo_build_out_dir}}\" \"${{archive_byproducts}}\" FALSE
+                )
+            endif()
+            if(shared_lib_byproduct)
+                _corrosion_copy_byproducts(
+                    {target_name} LIBRARY_OUTPUT_DIRECTORY \"${{cargo_build_out_dir}}\" \"${{shared_lib_byproduct}}\" FALSE
+                )
+            endif()
+            if(pdb_byproduct)
+                _corrosion_copy_byproducts(
+                    {target_name} PDB_OUTPUT_DIRECTORY \"${{cargo_build_out_dir}}\" \"${{pdb_byproduct}}\" FALSE
+                )
+            endif()
+            if(bin_byproduct)
+                _corrosion_copy_byproducts(
+                    {target_name} RUNTIME_OUTPUT_DIRECTORY \"${{cargo_build_out_dir}}\" \"${{bin_byproduct}}\" TRUE
+                )
+            endif()
+            set_property(TARGET {target_name} PROPERTY INTERFACE_COR_CARGO_PACKAGE_NAME {package_name} )
+            ",
+            package_name = self.cargo_package.name,
+            target_name = self.target_name(),
+            package_manifest_path = self.cargo_package.manifest_path.as_str().replace("\\", "/"),
+            workspace_manifest_path = ws_manifest,
+            target_kinds = target_kinds,
+            passthrough_add_cargo_build = passthrough_add_cargo_build,
+
+        )?;
+        Ok(())
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,183 @@
+# This option is currently used to prevent recursion
+option(CORROSION_TESTS "Enable Corrosion tests" ON)
+mark_as_advanced(CORROSION_TESTS)
+if(NOT CORROSION_TESTS)
+    return()
+endif()
+
+option(CORROSION_TESTS_CXXBRIDGE
+        "Build cxxbridge tests which requires cxxbridge executable being available"
+        OFF)
+option(CORROSION_TESTS_KEEP_BUILDDIRS
+    "By default corrosion tests will cleanup after themselves. This option limits the cleaning up to the
+     target directories and will keep the build directories, which may be useful for caching."
+    OFF)
+mark_as_advanced(CORROSION_TESTS_NO_CLEANUP)
+
+set(test_install_path "${CMAKE_CURRENT_BINARY_DIR}/test-install-corrosion")
+
+set(test_header_contents
+        "option(CORROSION_TESTS_FIND_CORROSION \"Use Corrosion as a subdirectory\" OFF)"
+        "if (CORROSION_TESTS_FIND_CORROSION)"
+        "    set(CMAKE_PREFIX_PATH \"${test_install_path}\" CACHE INTERNAL \"\" FORCE)"
+        "    find_package(Corrosion REQUIRED PATHS \"${test_install_path}\" NO_CMAKE_SYSTEM_PATH)"
+        "else()"
+        "    add_subdirectory(\"${CMAKE_CURRENT_SOURCE_DIR}/..\" corrosion)"
+        "endif()"
+)
+
+string(REPLACE ";" "\n" test_header_contents "${test_header_contents}")
+
+file(WRITE test_header.cmake "${test_header_contents}")
+
+option(CORROSION_TESTS_INSTALL_CORROSION
+        "Install Corrosion to a test directory and let tests use the installed Corrosion"
+        OFF)
+if(CORROSION_TESTS_INSTALL_CORROSION)
+    add_test(NAME "install_corrosion_configure"
+        COMMAND
+            ${CMAKE_COMMAND}
+            -S "${CMAKE_CURRENT_SOURCE_DIR}/.."
+            -B "${CMAKE_CURRENT_BINARY_DIR}/build-corrosion"
+            -DCORROSION_VERBOSE_OUTPUT=ON
+            -DCORROSION_TESTS=OFF
+            -DCMAKE_BUILD_TYPE=Release
+            -G${CMAKE_GENERATOR}
+            "-DCMAKE_INSTALL_PREFIX=${test_install_path}"
+    )
+    add_test(NAME "install_corrosion_build"
+            COMMAND
+            ${CMAKE_COMMAND} --build "${CMAKE_CURRENT_BINARY_DIR}/build-corrosion" --config Release
+            )
+    add_test(NAME "install_corrosion_install"
+            COMMAND
+            ${CMAKE_COMMAND} --install "${CMAKE_CURRENT_BINARY_DIR}/build-corrosion" --config Release
+        )
+    set_tests_properties("install_corrosion_configure" PROPERTIES FIXTURES_SETUP "fixture_corrosion_configure")
+    set_tests_properties("install_corrosion_build"     PROPERTIES FIXTURES_SETUP "fixture_corrosion_build")
+    set_tests_properties("install_corrosion_build"     PROPERTIES FIXTURES_REQUIRED "fixture_corrosion_configure")
+    set_tests_properties("install_corrosion_install"   PROPERTIES FIXTURES_REQUIRED "fixture_corrosion_build")
+    set_tests_properties("install_corrosion_install"   PROPERTIES FIXTURES_SETUP "fixture_corrosion_install")
+
+    add_test(NAME "install_corrosion_build_cleanup" COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/build-corrosion")
+    set_tests_properties("install_corrosion_build_cleanup" PROPERTIES
+        FIXTURES_CLEANUP
+        "fixture_corrosion_configure;fixture_corrosion_build"
+    )
+
+    add_test(NAME "install_corrosion_cleanup" COMMAND "${CMAKE_COMMAND}" -E remove_directory "${test_install_path}")
+    set_tests_properties("install_corrosion_cleanup" PROPERTIES
+        FIXTURES_CLEANUP
+        "fixture_corrosion_configure;fixture_corrosion_build;fixture_corrosion_install"
+    )
+endif()
+
+function(corrosion_tests_add_test test_name bin_names)
+    set(options "")
+    set(one_value_kewords "TEST_SRC_DIR")
+    set(multi_value_keywords "")
+    cmake_parse_arguments(PARSE_ARGV 2 TST "${options}" "${one_value_kewords}" "${multi_value_keywords}")
+    set(pass_through_arguments "${TST_UNPARSED_ARGUMENTS}")
+
+# In the future we could add multiple tests here for different configurations (generator, build mode, rust version ...)
+# which would allow us to simplify the github job matrix
+    if(TST_TEST_SRC_DIR)
+        set(test_dir "${TST_TEST_SRC_DIR}")
+    else()
+        set(test_dir "${test_name}")
+    endif()
+
+
+    if(CMAKE_C_COMPILER)
+        set(TEST_C_COMPILER "C_COMPILER" "${CMAKE_C_COMPILER}")
+    endif()
+    if(CMAKE_CXX_COMPILER)
+        set(TEST_CXX_COMPILER "CXX_COMPILER" "${CMAKE_CXX_COMPILER}")
+    endif()
+    if(CMAKE_GENERATOR_PLATFORM)
+        set(TEST_GENERATOR_PLATFORM "GENERATOR_PLATFORM" "${CMAKE_GENERATOR_PLATFORM}")
+    endif()
+    if(CORROSION_GENERATOR_EXECUTABLE)
+        # Mainly used in CI to build the native generator once and then reuse it for all tests
+        set(TEST_GENERATOR_BIN EXTERNAL_CORROSION_GENERATOR "${CORROSION_GENERATOR_EXECUTABLE}")
+    endif()
+    if(CMAKE_CROSSCOMPILING)
+        set(TEST_SYSTEM_NAME SYSTEM_NAME "${CMAKE_SYSTEM_NAME}")
+    endif()
+
+    add_test(NAME "${test_name}_build"
+            COMMAND
+            ${CMAKE_COMMAND}
+            -P "${CMAKE_SOURCE_DIR}/test/ConfigureAndBuild.cmake"
+            SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/${test_dir}"
+            BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build-${test_name}"
+            GENERATOR "${CMAKE_GENERATOR}"
+            RUST_TOOLCHAIN "${Rust_TOOLCHAIN}"
+            CARGO_TARGET "${Rust_CARGO_TARGET}"
+            "${TEST_SYSTEM_NAME}"
+            "${TEST_C_COMPILER}"
+            "${TEST_CXX_COMPILER}"
+            "${TEST_GENERATOR_PLATFORM}"
+            "${TEST_GENERATOR_BIN}"
+            ${pass_through_arguments}
+
+            COMMAND_EXPAND_LISTS
+            )
+    set_tests_properties("${test_name}_build" PROPERTIES FIXTURES_SETUP "build_fixture_${test_name}")
+    if(CORROSION_TESTS_INSTALL_CORROSION)
+        set_tests_properties("${test_name}_build" PROPERTIES FIXTURES_REQUIRED "fixture_corrosion_install")
+    endif()
+    foreach(bin ${bin_names})
+        if(WIN32)
+            set(bin_filename "${bin}.exe")
+        else()
+            set(bin_filename "${bin}")
+        endif()
+        add_test(NAME "${test_name}_run_${bin}" COMMAND "${CMAKE_CURRENT_BINARY_DIR}/build-${test_name}/${bin_filename}")
+        set_tests_properties("${test_name}_run_${bin}" PROPERTIES FIXTURES_REQUIRED "build_fixture_${test_name}")
+        # CMAKE_CROSSCOMPILING is not set when cross-compiling with VS (via -A flag).
+        # Todo: We could run x86 binaries on x64 hosts.
+        if(CMAKE_CROSSCOMPILING OR CMAKE_VS_PLATFORM_NAME)
+            # Todo: In the future we could potentially run some tests with qemu.
+            set_tests_properties("${test_name}_run_${bin}" PROPERTIES DISABLED TRUE)
+        endif()
+    endforeach()
+
+    if(CORROSION_TESTS_KEEP_BUILDDIRS)
+        add_test(NAME "${test_name}_cleanup_artifacts"
+            COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_CURRENT_BINARY_DIR}/build-${test_name}" --target clean
+        )
+        add_test(NAME "${test_name}_cleanup_cargo"
+            COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/build-${test_name}/cargo"
+            )
+        set_tests_properties("${test_name}_cleanup_artifacts" PROPERTIES FIXTURES_CLEANUP "build_fixture_${test_name}")
+        set_tests_properties("${test_name}_cleanup_cargo" PROPERTIES FIXTURES_CLEANUP "build_fixture_${test_name}")
+    else()
+        add_test(NAME "${test_name}_cleanup" COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/build-${test_name}")
+        set_tests_properties("${test_name}_cleanup" PROPERTIES FIXTURES_CLEANUP "build_fixture_${test_name}")
+    endif()
+endfunction()
+
+# Please keep this in alphabetical order.
+add_subdirectory(cargo_flags)
+add_subdirectory(cpp2rust)
+if(Rust_VERSION VERSION_GREATER_EQUAL "1.64.0")
+    # Flag `--crate-type` is only supported since Rust 1.64.0
+    add_subdirectory(crate_type)
+endif()
+add_subdirectory(custom_profiles)
+add_subdirectory(cbindgen)
+add_subdirectory(cxxbridge)
+add_subdirectory(envvar)
+add_subdirectory(external_corrosion_generator)
+add_subdirectory(features)
+add_subdirectory(find_rust)
+add_subdirectory(gensource)
+add_subdirectory(hostbuild)
+add_subdirectory(multitarget)
+add_subdirectory(nostd)
+add_subdirectory("output directory")
+add_subdirectory(parse_target_triple)
+add_subdirectory(rust2cpp)
+add_subdirectory(rustflags)
+add_subdirectory(workspace)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/ConfigureAndBuild.cmake	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,114 @@
+# CMake script to configure and build a test project
+
+set(TEST_ARG_LIST)
+
+# Expect actual arguments to start at index 3 (cmake -P <script_name>)
+foreach(ARG_INDEX RANGE 3 ${CMAKE_ARGC})
+    list(APPEND TEST_ARG_LIST "${CMAKE_ARGV${ARG_INDEX}}")
+endforeach()
+
+set(options "USE_INSTALLED_CORROSION")
+set(oneValueArgs
+    SOURCE_DIR
+    BINARY_DIR
+    GENERATOR
+    GENERATOR_PLATFORM
+    RUST_TOOLCHAIN
+    CARGO_TARGET
+    C_COMPILER
+    CXX_COMPILER
+    SYSTEM_NAME
+    EXTERNAL_CORROSION_GENERATOR
+    CARGO_PROFILE
+)
+set(multiValueArgs "PASS_THROUGH_ARGS")
+cmake_parse_arguments(TEST "${options}" "${oneValueArgs}"
+                      "${multiValueArgs}" ${TEST_ARG_LIST} )
+
+if(TEST_CARGO_TARGET)
+    set(TEST_Rust_CARGO_TARGET "-DRust_CARGO_TARGET=${TEST_CARGO_TARGET}")
+endif()
+if(TEST_USE_INSTALLED_CORROSION)
+    set(TEST_CORROSION_INSTALL "-DCORROSION_TESTS_FIND_CORROSION=ON")
+endif()
+if(TEST_GENERATOR_PLATFORM)
+    set(TEST_GENERATOR_PLATFORM "-A${TEST_GENERATOR_PLATFORM}")
+endif()
+if(TEST_C_COMPILER)
+    set(TEST_C_COMPILER "-DCMAKE_C_COMPILER=${TEST_C_COMPILER}")
+endif()
+if(TEST_CXX_COMPILER)
+    set(TEST_CXX_COMPILER "-DCMAKE_CXX_COMPILER=${TEST_CXX_COMPILER}")
+endif()
+if(TEST_SYSTEM_NAME)
+    set(TEST_SYSTEM_NAME "-DCMAKE_SYSTEM_NAME=${TEST_SYSTEM_NAME}")
+endif()
+if(TEST_EXTERNAL_CORROSION_GENERATOR)
+    set(TEST_EXTERNAL_CORROSION_GENERATOR
+        "-DCORROSION_GENERATOR_EXECUTABLE=${TEST_EXTERNAL_CORROSION_GENERATOR}"
+    )
+endif()
+if(TEST_CARGO_PROFILE)
+    set(TEST_CARGO_PROFILE "-DCARGO_PROFILE=${TEST_CARGO_PROFILE}")
+endif()
+
+# Remove old binary directory
+file(REMOVE_RECURSE "${TEST_BINARY_DIR}")
+
+file(MAKE_DIRECTORY "${TEST_BINARY_DIR}")
+
+message(STATUS "TEST_BINARY_DIRECTORY: ${TEST_BINARY_DIR}")
+
+execute_process(
+    COMMAND
+        "${CMAKE_COMMAND}"
+            "-G${TEST_GENERATOR}"
+            "-DRust_TOOLCHAIN=${TEST_RUST_TOOLCHAIN}"
+            --log-level Debug
+            ${TEST_Rust_CARGO_TARGET}
+            ${TEST_CORROSION_INSTALL}
+            ${TEST_GENERATOR_PLATFORM}
+            ${TEST_C_COMPILER}
+            ${TEST_CXX_COMPILER}
+            ${TEST_SYSTEM_NAME}
+            ${TEST_EXTERNAL_CORROSION_GENERATOR}
+            ${TEST_CARGO_PROFILE}
+            ${TEST_PASS_THROUGH_ARGS}
+            -S "${TEST_SOURCE_DIR}"
+            -B "${TEST_BINARY_DIR}"
+        COMMAND_ECHO STDOUT
+        RESULT_VARIABLE EXIT_CODE
+)
+
+if (NOT "${EXIT_CODE}" EQUAL 0)
+    message(FATAL_ERROR "Configure step failed. Exit code: `${EXIT_CODE}`")
+endif()
+
+if ("${TEST_GENERATOR}" STREQUAL "Ninja Multi-Config"
+        OR "${TEST_GENERATOR}" MATCHES "Visual Studio"
+    )
+    foreach(config Debug Release RelWithDebInfo)
+        execute_process(
+                COMMAND "${CMAKE_COMMAND}"
+                    --build "${TEST_BINARY_DIR}"
+                    --config "${config}"
+                COMMAND_ECHO STDOUT
+                RESULT_VARIABLE EXIT_CODE
+        )
+        if (NOT "${EXIT_CODE}" EQUAL 0)
+            message(FATAL_ERROR "Build step failed for config `${config}`. "
+                    "Exit code: `${EXIT_CODE}`")
+        endif()
+    endforeach()
+else()
+    execute_process(
+            COMMAND "${CMAKE_COMMAND}" --build "${TEST_BINARY_DIR}"
+            COMMAND_ECHO STDOUT
+            RESULT_VARIABLE EXIT_CODE
+    )
+    if (NOT "${EXIT_CODE}" EQUAL 0)
+        message(FATAL_ERROR "Build step failed. Exit code: `${EXIT_CODE}`")
+    endif()
+endif()
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/README.md	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,7 @@
+# Corrosion Tests
+
+Corrosions tests are run via ctest. The tests themselves utilize CMake script mode
+to configure and build a test project, which allows for great flexibility.
+Using ctest properties such as `PASS_REGULAR_EXPRESSION` or `FAIL_REGULAR_EXPRESSION`
+can be used to confirm that built executable targets run as expected, but can also
+be used to fail tests if Corrosion warnings appear in the configure output.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cargo_flags/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,3 @@
+corrosion_tests_add_test(cargo_flags "flags-exe")
+
+set_tests_properties("cargo_flags_run_flags-exe" PROPERTIES PASS_REGULAR_EXPRESSION [[Hello, Cxx! I am Rust!]])
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cargo_flags/cargo_flags/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml FLAGS --features one)
+
+add_executable(flags-exe main.cpp)
+target_link_libraries(flags-exe PUBLIC flags_lib)
+corrosion_set_cargo_flags(flags_lib --features two)
+corrosion_set_cargo_flags(flags_lib $<TARGET_PROPERTY:flags_lib,more_flags>)
+
+set_property(
+    TARGET flags_lib
+    APPEND
+    PROPERTY more_flags --features three
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cargo_flags/cargo_flags/main.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,6 @@
+extern "C" void rust_function(char const *name);
+
+
+int main(int argc, char **argv) {
+        rust_function("Cxx");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cargo_flags/cargo_flags/rust/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,13 @@
+[package]
+name = "flags-lib"
+version = "0.1.0"
+edition = "2018"
+
+[lib]
+crate-type=["staticlib"]
+
+[features]
+
+one = []
+two = []
+three = []
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cargo_flags/cargo_flags/rust/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,14 @@
+use std::os::raw::c_char;
+
+#[no_mangle]
+pub extern "C" fn rust_function(name: *const c_char) {
+    let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() };
+    println!("Hello, {}! I am Rust!", name);
+
+    #[cfg(not(feature = "one"))]
+    compile_error!("Feature one is not enabled");
+    #[cfg(not(feature = "two"))]
+    compile_error!("Feature two is not enabled");
+    #[cfg(not(feature = "three"))]
+    compile_error!("Feature three is not enabled");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cbindgen/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,9 @@
+corrosion_tests_add_test(cbindgen_rust2cpp "cpp-exe" TEST_SRC_DIR rust2cpp)
+
+set_tests_properties(cbindgen_rust2cpp_run_cpp-exe PROPERTIES PASS_REGULAR_EXPRESSION
+        "^add_point Result: Point { x: 100, y: 100 }\r?\n$"
+)
+# 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
+# - c++ code uses the rust lib and is used in turn by the rust bin.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cbindgen/rust2cpp/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml)
+corrosion_experimental_cbindgen(TARGET rust_lib 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)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cbindgen/rust2cpp/main.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,19 @@
+#include "rust-lib.h"
+#include <cassert>
+
+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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cbindgen/rust2cpp/rust/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,10 @@
+[package]
+name = "rust-lib"
+version = "0.1.0"
+license = "MIT"
+edition = "2018"
+
+[dependencies]
+
+[lib]
+crate-type=["staticlib"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cbindgen/rust2cpp/rust/cbindgen.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,2 @@
+language = "C++"
+include_version = true
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cbindgen/rust2cpp/rust/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,30 @@
+pub const MAGIC_NUMBER: u64 = 0xABCD_EFAB;
+
+#[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
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cpp2rust/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,5 @@
+corrosion_tests_add_test(cpp2rust "rust-exe")
+
+set_tests_properties("cpp2rust_run_rust-exe" PROPERTIES PASS_REGULAR_EXPRESSION
+        "Hello, Rust! I am Cpp!\r?\nHello, Rust! I am Cpp library Number 2!\r?\nHello, Rust! I am Cpp library Number 3!"
+        )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cpp2rust/cpp2rust/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml)
+
+add_library(cpp-lib lib.cpp)
+target_compile_features(cpp-lib PRIVATE cxx_std_14)
+set_target_properties(
+    cpp-lib
+    PROPERTIES
+        POSITION_INDEPENDENT_CODE ON
+)
+
+add_library(cpp-lib2 lib2.cpp)
+target_compile_features(cpp-lib2 PRIVATE cxx_std_14)
+set_target_properties(
+        cpp-lib2
+        PROPERTIES
+        POSITION_INDEPENDENT_CODE ON
+        OUTPUT_NAME cpp-lib-renamed
+)
+
+add_library(cpp-lib3 "path with space/lib3.cpp" )
+target_compile_features(cpp-lib3 PRIVATE cxx_std_14)
+set_target_properties(
+        cpp-lib3
+        PROPERTIES
+        POSITION_INDEPENDENT_CODE ON
+)
+
+corrosion_link_libraries(rust-exe cpp-lib cpp-lib2 cpp-lib3)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cpp2rust/cpp2rust/lib.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,5 @@
+#include <iostream>
+
+extern "C" void cpp_function(char const *name) {
+    std::cout << "Hello, " << name << "! I am Cpp!\n";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cpp2rust/cpp2rust/lib2.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,11 @@
+#include <iostream>
+#include <stdint.h>
+
+extern "C" void cpp_function2(char const *name) {
+    std::cout << "Hello, " << name << "! I am Cpp library Number 2!\n";
+}
+
+extern "C" uint32_t get_42() {
+    uint32_t v = 42;
+    return v;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cpp2rust/cpp2rust/path with space/lib3.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,8 @@
+// Check that libraries located at a path containing a space can also be linked.
+
+#include <iostream>
+
+extern "C" void cpp_function3(char const *name) {
+    std::cout << "Hello, " << name << "! I am Cpp library Number 3!\n";
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cpp2rust/cpp2rust/rust/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,9 @@
+[package]
+name = "rust-exe"
+version = "0.1.0"
+authors = ["Andrew Gaspar <andrew.gaspar@outlook.com>"]
+license = "MIT"
+edition = "2018"
+
+[dependencies]
+rust-dependency = { path = "rust_dependency" }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cpp2rust/cpp2rust/rust/build.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,4 @@
+// Build-scripts also need to be linked, so just add a dummy buildscript ensuring this works.
+fn main() {
+    println!("Build-script is running.")
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cpp2rust/cpp2rust/rust/rust_dependency/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,8 @@
+[package]
+name = "rust-dependency"
+version = "0.1.0"
+license = "MIT"
+edition = "2018"
+
+[dependencies]
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cpp2rust/cpp2rust/rust/rust_dependency/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,8 @@
+
+extern "C" {
+    fn get_42() -> u32;
+}
+pub fn calls_ffi() {
+    let res = unsafe { get_42()};
+    assert_eq!(res, 42);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/crate_type/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,6 @@
+corrosion_tests_add_test(crate_type "cpp-exe")
+
+
+set_tests_properties("crate_type_run_cpp-exe" PROPERTIES PASS_REGULAR_EXPRESSION
+        "Hello from lib 1!\r?\nHello from lib 2!"
+        )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/crate_type/crate_type/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+# Add --crate-type to ensure that only the specified type of library is built and no error is thrown
+corrosion_import_crate(MANIFEST_PATH proj1/Cargo.toml CRATE_TYPES staticlib FLAGS --crate-type=staticlib)
+corrosion_import_crate(MANIFEST_PATH proj2/Cargo.toml CRATE_TYPES cdylib FLAGS --crate-type=cdylib)
+
+add_executable(cpp-exe main.cpp)
+target_link_libraries(cpp-exe proj1)
+target_link_libraries(cpp-exe proj2)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/crate_type/crate_type/main.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,8 @@
+extern "C" void rust_function1();
+extern "C" void rust_function2();
+
+int main() {
+    rust_function1();
+    rust_function2();
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/crate_type/crate_type/proj1/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,10 @@
+[package]
+name = "proj1"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+
+[lib]
+crate-type=["staticlib", "cdylib"]
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/crate_type/crate_type/proj1/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,4 @@
+#[no_mangle]
+pub extern "C" fn rust_function1() {
+    println!("Hello from lib 1!");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/crate_type/crate_type/proj2/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,9 @@
+[package]
+name = "proj2"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+
+[lib]
+crate-type=["staticlib", "cdylib"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/crate_type/crate_type/proj2/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,4 @@
+#[no_mangle]
+pub extern "C" fn rust_function2() {
+    println!("Hello from lib 2!");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/custom_profiles/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,27 @@
+# The tests in this folder test specifying the cargo profile name via the --profile option.
+# The built-in `test` and `bench` profiles are _not_ supported, because they output
+# artifacts to a different location and add a hash to the artifact name.
+if(Rust_VERSION VERSION_GREATER_EQUAL 1.57.0)
+
+    corrosion_tests_add_test(custom_profiles_global "custom-profile-exe" TEST_SRC_DIR custom_profiles)
+    corrosion_tests_add_test(custom_profiles_target_specific "custom-profile-exe"
+        TEST_SRC_DIR custom_profiles
+        PASS_THROUGH_ARGS -DCORROSION_TEST_USE_TARGET_SPECIFIC_OVERRIDE=ON
+    )
+    corrosion_tests_add_test(dev_profile "dev_bin" TEST_SRC_DIR basic_profiles CARGO_PROFILE dev)
+    corrosion_tests_add_test(release_profile "release_bin" TEST_SRC_DIR basic_profiles CARGO_PROFILE release)
+
+    set_tests_properties("custom_profiles_global_run_custom-profile-exe" PROPERTIES PASS_REGULAR_EXPRESSION
+        "^Hello, Cpp! I'm Rust!\r?\n$"
+        )
+    set_tests_properties("custom_profiles_target_specific_run_custom-profile-exe" PROPERTIES PASS_REGULAR_EXPRESSION
+        "^Hello, Cpp! I'm Rust!\r?\n$"
+        )
+    set_tests_properties("dev_profile_run_dev_bin" PROPERTIES PASS_REGULAR_EXPRESSION
+        "^Hello, Cpp! I'm Rust!\r?\n$"
+        )
+    set_tests_properties("release_profile_run_release_bin" PROPERTIES PASS_REGULAR_EXPRESSION
+        "^Hello, Cpp! I'm Rust!\r?\n$"
+        )
+
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/custom_profiles/basic_profiles/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+if(NOT DEFINED CARGO_PROFILE)
+    message(FATAL_ERROR "Test internal error. The test should be called with the CARGO_PROFILE parameter.")
+endif()
+
+corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml PROFILE ${CARGO_PROFILE})
+
+add_executable(${CARGO_PROFILE}_bin main.cpp)
+target_link_libraries(${CARGO_PROFILE}_bin PUBLIC cargo_profiles_lib)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/custom_profiles/basic_profiles/main.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,6 @@
+extern "C" void rust_function(char const *name);
+
+
+int main(int argc, char **argv) {
+        rust_function("Cpp");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/custom_profiles/basic_profiles/rust/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,7 @@
+[package]
+name = "cargo-profiles-lib"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type=["staticlib"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/custom_profiles/basic_profiles/rust/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,8 @@
+use std::os::raw::c_char;
+
+#[no_mangle]
+pub extern "C" fn rust_function(name: *const c_char) {
+    let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() };
+    println!("Hello, {}! I'm Rust!", name);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/custom_profiles/custom_profiles/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+set(_release_profile $<IF:$<CONFIG:Release>,release-without-dbg,custom-without-dbg>)
+set(custom_profile $<IF:$<CONFIG:Debug>,dev-without-dbg,${_release_profile}>)
+
+if(CORROSION_TEST_USE_TARGET_SPECIFIC_OVERRIDE)
+    # Select "wrong" profile here on purpose.
+    corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml PROFILE dev)
+    set_target_properties(custom_profiles_lib
+        PROPERTIES
+        INTERFACE_CORROSION_CARGO_PROFILE "${custom_profile}"
+    )
+else()
+    corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml PROFILE ${custom_profile})
+endif()
+
+add_executable(custom-profile-exe main.cpp)
+target_link_libraries(custom-profile-exe PUBLIC custom_profiles_lib)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/custom_profiles/custom_profiles/main.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,6 @@
+extern "C" void rust_function(char const *name);
+
+
+int main(int argc, char **argv) {
+        rust_function("Cpp");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/custom_profiles/custom_profiles/rust/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,24 @@
+[package]
+name = "custom-profiles-lib"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type=["staticlib"]
+
+# Test if neither release or debug where selected by only disabling debug-assertions in the inherited profile.
+[profile.release]
+debug-assertions = true
+
+[profile.dev-without-dbg]
+inherits = "dev"
+debug-assertions = false
+
+[profile.release-without-dbg]
+inherits = "release"
+debug-assertions = false
+
+[profile.custom-without-dbg]
+inherits = "release"
+opt-level = 1
+debug-assertions = false
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/custom_profiles/custom_profiles/rust/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,11 @@
+use std::os::raw::c_char;
+
+#[no_mangle]
+pub extern "C" fn rust_function(name: *const c_char) {
+    let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() };
+    println!("Hello, {}! I'm Rust!", name);
+}
+
+
+#[cfg(debug_assertions)]
+const _: () = assert!(false, "Debug assertions where not disabled via custom profile!");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cxxbridge/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,20 @@
+if(CORROSION_TESTS_CXXBRIDGE)
+    corrosion_tests_add_test(cxxbridge_cpp2rust_1 "rust_bin"
+        TEST_SRC_DIR cxxbridge_cpp2rust
+        PASS_THROUGH_ARGS -DTEST_CXXBRIDGE_VARIANT1=ON
+    )
+    corrosion_tests_add_test(cxxbridge_cpp2rust_2 "rust_bin"
+            TEST_SRC_DIR cxxbridge_cpp2rust
+            PASS_THROUGH_ARGS -DTEST_CXXBRIDGE_VARIANT2=ON
+    )
+    corrosion_tests_add_test(cxxbridge_rust2cpp "cxxbridge-exe")
+
+    set_tests_properties("cxxbridge_cpp2rust_1_run_rust_bin"
+        PROPERTIES PASS_REGULAR_EXPRESSION
+            "main function"
+    )
+    set_tests_properties("cxxbridge_rust2cpp_run_cxxbridge-exe"
+        PROPERTIES PASS_REGULAR_EXPRESSION
+            "Hello cxxbridge from lib.rs! \\[4, 5, 6\\]\r?\nHello cxxbridge from foo/mod.rs! \\[4, 5, 6\\]"
+    )
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,39 @@
+cmake_minimum_required(VERSION 3.15)
+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-cpp CRATE rust_bin FILES lib.rs)
+target_include_directories(cxxbridge-cpp PRIVATE "include")
+
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux"
+    OR (CMAKE_SYSTEM_NAME STREQUAL "Windows"
+        AND (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+    )
+)
+    corrosion_add_target_local_rustflags(rust_bin "-Clink-arg=-fuse-ld=lld")
+endif()
+
+if(MSVC)
+    set_target_properties(cxxbridge-cpp PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
+endif()
+
+if(TEST_CXXBRIDGE_VARIANT1)
+    # Variant 1: Merge the C++ User sources into the generated library target.
+    target_sources(cxxbridge-cpp PRIVATE cpplib.cpp)
+    corrosion_link_libraries(rust_bin cxxbridge-cpp)
+elseif(TEST_CXXBRIDGE_VARIANT2)
+    # Variant 2: Create a separate C++ library and link both the User library and
+    # the generated library into rust
+    add_library(cpp_lib STATIC cpplib.cpp)
+    target_include_directories(cpp_lib PUBLIC "${CMAKE_CURRENT_LIST_DIR}/include")
+    target_link_libraries(cpp_lib PUBLIC cxxbridge-cpp)
+    corrosion_link_libraries(rust_bin cpp_lib cxxbridge-cpp)
+    if(MSVC)
+        set_target_properties(cpp_lib PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
+    endif()
+else()
+    message(FATAL_ERROR "Internal test error - required option not defined")
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/cpplib.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,15 @@
+#include <iostream>
+#include "cpplib.h"
+#include "cxxbridge-cpp/lib.h"
+#include "rust/cxx.h"
+
+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 write_image(::rust::Str path, ::RsImage const & image) {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/include/cpplib.h	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,5 @@
+#pragma once
+#include "cxxbridge-cpp/lib.h"
+
+::RsImage read_image(::rust::Str path);
+void write_image(::rust::Str path, ::RsImage const & image);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/rust/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,10 @@
+[package]
+name = "rust_bin"
+version = "0.1.0"
+edition = "2018"
+
+[lib]
+name = "cxxbridge_lib"
+
+[dependencies]
+cxx = "1.0"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/rust/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,26 @@
+#[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;
+        fn write_image(path: &str, image: &RsImage);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cxxbridge/cxxbridge_cpp2rust/rust/src/main.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,14 @@
+use cxxbridge_lib::ffi::{RsImage,Rgba,read_image};
+
+fn main() {
+    println!("main function");
+    let expected = RsImage { width: 1, height: 1, raster: Rgba {
+        r: 1.0,
+        g: 2.0,
+        b: 3.0,
+        a: 4.0,
+    }};
+    let actual = read_image("dummy path");
+    println!("returned from C++");
+    assert_eq!(actual, expected)
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+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-cpp CRATE cxxbridge_crate MANIFEST_PATH rust FILES lib.rs foo/mod.rs)
+
+add_executable(cxxbridge-exe main.cpp)
+target_link_libraries(cxxbridge-exe PUBLIC cxxbridge-cpp)
+
+if(MSVC)
+    # Note: This is required because we use `cxx` which uses `cc` to compile and link C++ code.
+    corrosion_set_env_vars(cxxbridge_crate "CFLAGS=-MDd" "CXXFLAGS=-MDd")
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/main.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,11 @@
+#include <cxxbridge-cpp/foo/mod.h>
+#include <cxxbridge-cpp/lib.h>
+#include <vector>
+
+int main(int argc, char **argv)
+{
+    std::vector<uint64_t> input = { 4, 5, 6};
+    rust::Slice<const ::std::uint64_t> slice{input.data(), input.size()};
+    lib::print(slice);
+    foo::print(slice);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/rust/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,10 @@
+[package]
+name = "cxxbridge-crate"
+version = "0.1.0"
+edition = "2018"
+
+[lib]
+crate-type = ["staticlib"]
+
+[dependencies]
+cxx = "1.0"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/rust/src/foo/mod.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,10 @@
+#[cxx::bridge(namespace = "foo")]
+mod bridge {
+    extern "Rust" {
+        fn print(slice: &[u64]);
+    }
+}
+
+fn print(slice: &[u64]) {
+    println!("Hello cxxbridge from foo/mod.rs! {:?}", slice);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/cxxbridge/cxxbridge_rust2cpp/rust/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,12 @@
+mod foo;
+
+#[cxx::bridge(namespace = "lib")]
+mod bridge {
+    extern "Rust" {
+        fn print(slice: &[u64]);
+    }
+}
+
+fn print(slice: &[u64]) {
+    println!("Hello cxxbridge from lib.rs! {:?}", slice);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/envvar/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,5 @@
+corrosion_tests_add_test(envvar "program_requiring_rust_lib_with_envvar")
+
+set_tests_properties("envvar_run_program_requiring_rust_lib_with_envvar" PROPERTIES PASS_REGULAR_EXPRESSION
+        "Ok"
+        )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/envvar/envvar/.cargo/config.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,2 @@
+[env]
+COR_CONFIG_TOML_ENV_VAR = "EnvVariableSetViaConfig.toml"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/envvar/envvar/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+corrosion_import_crate(MANIFEST_PATH Cargo.toml)
+
+corrosion_set_env_vars(rust_lib_requiring_envvar
+        "ANOTHER_VARIABLE=ANOTHER_VALUE"
+        "$<TARGET_PROPERTY:program_requiring_rust_lib_with_envvar,INDIRECT_VAR_TEST>"
+        "COR_CARGO_VERSION_MAJOR=${Rust_CARGO_VERSION_MAJOR}"
+        "COR_CARGO_VERSION_MINOR=${Rust_CARGO_VERSION_MINOR}"
+)
+
+add_executable(program_requiring_rust_lib_with_envvar main.cpp)
+
+set_property(
+    TARGET program_requiring_rust_lib_with_envvar
+    APPEND
+    PROPERTY INDIRECT_VAR_TEST
+    "REQUIRED_VARIABLE=EXPECTED_VALUE"
+)
+
+target_link_libraries(program_requiring_rust_lib_with_envvar PUBLIC rust_lib_requiring_envvar)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/envvar/envvar/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,9 @@
+[package]
+name = "rust-lib-requiring-envvar"
+version = "0.1.0"
+authors = ["Olivier Goffart <ogoffart@sixtyfps.io>"]
+edition = "2018"
+build = "build.rs"
+
+[lib]
+crate-type = [ "lib", "cdylib" ]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/envvar/envvar/build.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,18 @@
+fn main() {
+    assert_eq!(env!("REQUIRED_VARIABLE"), "EXPECTED_VALUE");
+    assert_eq!(std::env::var("ANOTHER_VARIABLE").unwrap(), "ANOTHER_VALUE");
+    let cargo_major = env!("COR_CARGO_VERSION_MAJOR")
+        .parse::<u32>()
+        .expect("Invalid Major version");
+    let cargo_minor = env!("COR_CARGO_VERSION_MINOR")
+        .parse::<u32>()
+        .expect("Invalid Minor version");
+
+    // The `[env]` section in `.cargo/config.toml` was added in version 1.56.
+    if cargo_major > 1 || (cargo_major == 1 && cargo_minor >= 56) {
+        // Check if cargo picks up the config.toml, which sets this additional env variable.
+        let env_value = option_env!("COR_CONFIG_TOML_ENV_VAR")
+            .expect("Test failure! Cargo >= 1.56.0 should set this environment variable");
+        assert_eq!(env_value, "EnvVariableSetViaConfig.toml");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/envvar/envvar/main.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,5 @@
+#include <iostream>
+
+int main() {
+    std::cout << "Ok";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/envvar/envvar/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,7 @@
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn it_works() {
+        assert_eq!(2 + 2, 4);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/external_corrosion_generator/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,14 @@
+if(CORROSION_TESTS_INSTALL_CORROSION)
+    add_test(NAME "ExternalCorrosionGenerator"
+            COMMAND
+            ${CMAKE_COMMAND}
+            -P "${CMAKE_SOURCE_DIR}/test/ConfigureAndBuild.cmake"
+            SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/ExternalCorrosionGenerator"
+            BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build"
+            GENERATOR "${CMAKE_GENERATOR}"
+            RUST_TOOLCHAIN "${Rust_TOOLCHAIN}"
+            EXTERNAL_CORROSION_GENERATOR "${test_install_path}/libexec/corrosion-generator"
+            COMMAND_EXPAND_LISTS
+            )
+    set_tests_properties("ExternalCorrosionGenerator" PROPERTIES FIXTURES_REQUIRED "fixture_corrosion_install")
+endif()
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/external_corrosion_generator/ExternalCorrosionGenerator/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.15)
+project(ExternalCorrosionGenerator LANGUAGES C)
+
+add_subdirectory(../../.. corrosion)
+
+get_property(
+    GENERATOR_EXE_LOCATION
+    TARGET Corrosion::Generator PROPERTY IMPORTED_LOCATION
+)
+
+if (NOT GENERATOR_EXE_LOCATION STREQUAL CORROSION_GENERATOR_EXECUTABLE)
+    message(
+        FATAL_ERROR
+        "\
+Corrosion Generator not Imported Correctly:
+    Corrosion::Generator IMPORTED_LOCATION: ${GENERATOR_EXE_LOCATION}
+    CORROSION_GENERATOR_EXECUTABLE:         ${CORROSION_GENERATOR_EXECUTABLE}")
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/external_corrosion_generator/ExternalCorrosionGenerator/Test.cmake	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,14 @@
+set(CORROSION_DIR ${CMAKE_ARGV3})
+set(CORROSION_INSTALL ${CMAKE_ARGV4})
+
+execute_process(
+    COMMAND
+        ${CMAKE_COMMAND} .
+            -DCORROSION_GENERATOR_EXECUTABLE=${CORROSION_INSTALL}/libexec/corrosion-generator
+    COMMAND_ECHO STDOUT
+    RESULT_VARIABLE SUCCESS
+)
+
+if (NOT SUCCESS EQUAL 0)
+    message(FATAL_ERROR)
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/features/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,5 @@
+corrosion_tests_add_test(features "features-cpp-exe")
+
+set_tests_properties("features_run_features-cpp-exe" PROPERTIES PASS_REGULAR_EXPRESSION
+        "Hello, Cpp! I'm Rust!\r?\nHello, Cpp again! I'm Rust again!\r?\nHello, Cpp again! I'm Rust again, third time the charm!"
+        )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/features/features/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,27 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml FEATURES thirdfeature ALL_FEATURES)
+
+add_executable(features-cpp-exe main.cpp)
+target_link_libraries(features-cpp-exe PUBLIC rust_feature_lib)
+
+corrosion_set_features(rust_feature_lib
+        ALL_FEATURES OFF
+        NO_DEFAULT_FEATURES
+        FEATURES
+            $<TARGET_PROPERTY:features-cpp-exe,app_features>
+)
+
+set_property(
+    TARGET features-cpp-exe
+    APPEND
+    PROPERTY app_features myfeature
+)
+set_property(
+    TARGET features-cpp-exe
+    APPEND
+    PROPERTY app_features secondfeature
+)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/features/features/main.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,13 @@
+extern "C" void rust_function(char const *name);
+extern "C" void rust_second_function(char const *name);
+extern "C" void rust_third_function(char const *name);
+
+int main(int argc, char **argv) {
+    if (argc < 2) {
+        rust_function("Cpp");
+        rust_second_function("Cpp again");
+        rust_third_function("Cpp again");
+    } else {
+        rust_function(argv[1]);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/features/features/rust/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,18 @@
+[package]
+name = "rust-feature-lib"
+version = "0.1.0"
+authors = ["Andrew Gaspar <andrew.gaspar@outlook.com>"]
+license = "MIT"
+edition = "2018"
+
+[dependencies]
+
+[lib]
+crate-type=["staticlib"]
+
+[features]
+default = ["compile-breakage"]
+myfeature = []
+secondfeature = []
+thirdfeature = []
+compile-breakage = []
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/features/features/rust/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,26 @@
+#[cfg(feature = "myfeature")]
+use std::os::raw::c_char;
+
+#[no_mangle]
+#[cfg(feature = "myfeature")]
+pub extern "C" fn rust_function(name: *const c_char) {
+    let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() };
+    println!("Hello, {}! I'm Rust!", name);
+}
+
+#[no_mangle]
+#[cfg(feature = "secondfeature")]
+pub extern "C" fn rust_second_function(name: *const c_char) {
+    let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() };
+    println!("Hello, {}! I'm Rust again!", name);
+}
+
+#[no_mangle]
+#[cfg(feature = "thirdfeature")]
+pub extern "C" fn rust_third_function(name: *const c_char) {
+    let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() };
+    println!("Hello, {}! I'm Rust again, third time the charm!", name);
+}
+
+#[cfg(feature = "compile-breakage")]
+const _: [(); 1] = [(); 2]; // Trigger a compile error to make sure that we succeeded in de-activating this feature
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/find_rust/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,3 @@
+corrosion_tests_add_test(find_rust "")
+corrosion_tests_add_test(rustup_proxy "")
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/find_rust/find_rust/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,9 @@
+
+cmake_minimum_required(VERSION 3.15)
+project(FindRust LANGUAGES CXX)
+
+set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../../../cmake" ${CMAKE_MODULE_PATH})
+
+# make sure find_package(Rust) can be used more than once
+find_package(Rust REQUIRED)
+find_package(Rust REQUIRED)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/find_rust/rustup_proxy/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,47 @@
+cmake_minimum_required(VERSION 3.15)
+project(RustupProxy LANGUAGES CXX)
+
+set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../../../cmake" ${CMAKE_MODULE_PATH})
+
+function(_assert_is_rustup_proxy executable_path)
+    execute_process(
+        COMMAND
+            ${CMAKE_COMMAND} -E env
+                RUSTUP_FORCE_ARG0=rustup
+            "${executable_path}" --version
+        OUTPUT_VARIABLE _VERSION_RAW
+        ERROR_VARIABLE _VERSION_STDERR
+        RESULT_VARIABLE _VERSION_RESULT
+    )
+
+    if(NOT _VERSION_RESULT EQUAL "0")
+        message(FATAL_ERROR "`${executable_path} --version` failed with ${_VERSION_RESULT}\n"
+            "stderr:\n${_VERSION_STDERR}"
+        )
+    endif()
+
+    if (NOT _VERSION_RAW MATCHES "rustup [0-9\\.]+")
+        message(FATAL_ERROR "`${executable_path} --version` output does not match rustup: ${_VERSION_RAW}\n")
+    endif()
+endfunction()
+
+set(Rust_RESOLVE_RUSTUP_TOOLCHAINS OFF CACHE BOOL "" FORCE)
+find_package(Rust REQUIRED)
+
+if (NOT Rust_FOUND)
+    message(FATAL_ERROR "Rustup not found")
+endif()
+
+get_property(
+    RUSTC_EXECUTABLE
+    TARGET Rust::Rustc PROPERTY IMPORTED_LOCATION
+)
+
+_assert_is_rustup_proxy(${RUSTC_EXECUTABLE})
+
+get_property(
+    CARGO_EXECUTABLE
+    TARGET Rust::Cargo PROPERTY IMPORTED_LOCATION
+)
+
+_assert_is_rustup_proxy(${CARGO_EXECUTABLE})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/gensource/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,5 @@
+corrosion_tests_add_test(gensource "")
+
+#set_tests_properties("features_run_features-cpp-exe" PROPERTIES PASS_REGULAR_EXPRESSION
+#        "Hello, Cpp! I'm Rust!\r?\nHello, Cpp again! I'm Rust again!\r?\nHello, Cpp again! I'm Rust again, third time the charm!"
+#        )
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/gensource/gensource/.gitignore	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,1 @@
+src/foo.rs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/gensource/gensource/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+add_subdirectory(generator)
+
+add_custom_command(
+	OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/foo.rs"
+	DEPENDS $<TARGET_FILE:srcgen>
+	COMMAND $<TARGET_FILE:srcgen> "${CMAKE_CURRENT_SOURCE_DIR}/src/foo.rs"
+)
+
+add_custom_target(after_generation DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/foo.rs")
+add_custom_target(genexdebug COMMAND ${CMAKE_COMMAND} -E echo "Config DEBUG: $<TARGET_PROPERTY:srcgen,IMPORTED_LOCATION_DEBUG> Config Release: $<TARGET_PROPERTY:srcgen,IMPORTED_LOCATION_RELEASE> IMPORTED_LOCATION: $<TARGET_PROPERTY:srcgen,IMPORTED_LOCATION>")
+
+corrosion_import_crate(MANIFEST_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml)
+add_dependencies(cargo-prebuild_generated after_generation)
+
+# Simple test for corrosion_parse_package_version
+corrosion_parse_package_version("${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml" srcgen_version)
+if (NOT "${srcgen_version}" VERSION_EQUAL "0.1.0")
+	message(FATAL_ERROR "Test failed to parse expected version")
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/gensource/gensource/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,10 @@
+[package]
+name = "generated"
+version = "0.1.0"
+edition = "2018"
+
+[lib]
+crate-type = ["lib", "cdylib"]
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/gensource/gensource/generator/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,2 @@
+corrosion_import_crate(MANIFEST_PATH Cargo.toml)
+corrosion_set_hostbuild(srcgen)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/gensource/gensource/generator/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,8 @@
+[package]
+name = "srcgen"
+version = "0.1.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/gensource/gensource/generator/src/main.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,6 @@
+use std::io::Write;
+fn main() -> Result<(), std::io::Error> {
+    let out_name = std::env::args().skip(1).next().unwrap();
+    let mut out_file = std::fs::File::create(out_name)?;
+    Ok(write!(out_file, "const _: () = ();")?)
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/gensource/gensource/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,10 @@
+mod foo;
+
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn it_works() {
+        let result = 2 + 2;
+        assert_eq!(result, 4);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/hostbuild/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,11 @@
+# FIXME: ONly test this when cross-compiling?
+corrosion_tests_add_test(hostbuild "rust-host-program")
+
+set_tests_properties("hostbuild_run_rust-host-program" PROPERTIES PASS_REGULAR_EXPRESSION
+        "^ok\r?\nHello Rust Hostbuild, I am an external C function"
+        )
+# Run tests are disabled by default when cross-compiling, however we still want to test hostbuild!
+# So we manually re-enable the test here.
+if(CMAKE_CROSSCOMPILING)
+    set_tests_properties("hostbuild_run_rust-host-program" PROPERTIES DISABLED FALSE)
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/hostbuild/hostbuild/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,7 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+corrosion_import_crate(MANIFEST_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml)
+
+corrosion_set_hostbuild(rust-host-program)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/hostbuild/hostbuild/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,8 @@
+[package]
+name = "rust-host-program"
+version = "0.1.0"
+authors = ["Olivier Goffart <ogoffart@sixtyfps.io>"]
+edition = "2018"
+
+[build-dependencies]
+cc = "1.0"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/hostbuild/hostbuild/build.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,10 @@
+fn main() {
+    let out_dir = std::env::var("OUT_DIR").unwrap();
+    cc::Build::new()
+        .file("src/lib.c")
+        .compile("hello");
+
+    println!("cargo:rustc-link-search=native={}", out_dir);
+    println!("cargo:rustc-link-lib=hello");
+    println!("cargo:rerun-if-changed=src/lib.c");
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/hostbuild/hostbuild/src/lib.c	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,5 @@
+#include <stdio.h>
+
+void c_function(char const *name) {
+   printf("Hello %s, I am an external C function\n", name);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/hostbuild/hostbuild/src/main.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,13 @@
+use std::os::raw::c_char;
+
+extern "C" {
+    fn c_function(name: *const c_char);
+}
+
+fn main() {
+    println!("ok");
+    let name = b"Rust Hostbuild\0";
+    unsafe {
+        c_function(name.as_ptr() as _);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/multitarget/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,22 @@
+corrosion_tests_add_test(multitarget "bin1;bin2;bin3")
+
+# Don't run this test in parallel with others, since the target directory size may cause issues.
+set_tests_properties("multitarget_build" PROPERTIES RUN_SERIAL TRUE)
+
+set_tests_properties("multitarget_run_bin1" PROPERTIES PASS_REGULAR_EXPRESSION
+        "Hello, world!\r?\nHello, bin1! I'm Cpp!"
+        RUN_SERIAL
+        TRUE
+        )
+
+set_tests_properties("multitarget_run_bin2" PROPERTIES PASS_REGULAR_EXPRESSION
+        "Hello, world!\r?\nHello, bin2! I'm Cpp!"
+        RUN_SERIAL
+        TRUE
+        )
+
+set_tests_properties("multitarget_run_bin3" PROPERTIES PASS_REGULAR_EXPRESSION
+        "Hello, world!\r?\nHello, bin3! I'm Cpp!"
+        RUN_SERIAL
+        TRUE
+        )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/multitarget/multitarget/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+corrosion_import_crate(MANIFEST_PATH Cargo.toml)
+
+add_library(cpp-lib4 lib.cpp)
+target_compile_features(cpp-lib4 PRIVATE cxx_std_14)
+set_property(TARGET cpp-lib4 PROPERTY POSITION_INDEPENDENT_CODE ON)
+corrosion_link_libraries(bin1 cpp-lib4)
+corrosion_link_libraries(bin2 cpp-lib4)
+corrosion_link_libraries(bin3 cpp-lib4)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/multitarget/multitarget/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,19 @@
+[package]
+name = "multitarget-crate"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+
+[lib]
+name = "multitarget_lib"
+crate-type=["lib", "staticlib", "cdylib"]
+
+[[bin]]
+name = "bin1"
+
+[[bin]]
+name = "bin2"
+
+[[bin]]
+name = "bin3"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/multitarget/multitarget/lib.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,5 @@
+#include <iostream>
+
+extern "C" void cpp_function(char const *name) {
+    std::cout << "Hello, " << name << "! I'm Cpp!\n";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/multitarget/multitarget/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,9 @@
+use std::os::raw::c_char;
+
+pub fn hello_world() {
+    println!("Hello, world!");
+}
+
+extern "C" {
+    pub fn cpp_function(name: *const c_char);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/nostd/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,1 @@
+corrosion_tests_add_test(nostd "")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/nostd/nostd/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml NO_STD)
+
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -nostdlib")
+list(REMOVE_ITEM CMAKE_CXX_IMPLICIT_LINK_LIBRARIES stdc++)
+
+add_library(nostd-cpp-lib STATIC main.cpp)
+target_link_libraries(nostd-cpp-lib PUBLIC rust-nostd-lib)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/nostd/nostd/main.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,6 @@
+extern "C" void rust_function();
+
+extern "C" void cpp_function() {
+    // Fail on linking issues
+    rust_function();
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/nostd/nostd/rust/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,17 @@
+[package]
+name = "rust-nostd-lib"
+version = "0.1.0"
+edition = "2015"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+
+[lib]
+crate-type=["staticlib"]
+
+[profile.release]
+panic = "abort"
+
+[profile.dev]
+panic = "abort"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/nostd/nostd/rust/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,10 @@
+#![no_std]
+use core::panic::PanicInfo;
+
+#[no_mangle]
+pub extern "C" fn rust_function() {}
+
+#[panic_handler]
+fn panic(_panic: &PanicInfo<'_>) -> ! {
+    loop {}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/output directory/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,140 @@
+if(CMAKE_VERSION VERSION_LESS 3.19.0)
+    return()
+endif()
+
+if(CMAKE_C_COMPILER)
+    set(TEST_C_COMPILER "C_COMPILER" "${CMAKE_C_COMPILER}")
+endif()
+if(CMAKE_CXX_COMPILER)
+    set(TEST_CXX_COMPILER "CXX_COMPILER" "${CMAKE_CXX_COMPILER}")
+endif()
+if(CMAKE_GENERATOR_PLATFORM)
+    set(TEST_GENERATOR_PLATFORM "GENERATOR_PLATFORM" "${CMAKE_GENERATOR_PLATFORM}")
+endif()
+
+add_test(NAME "output_directory_build"
+    COMMAND
+        ${CMAKE_COMMAND}
+        -P "${CMAKE_SOURCE_DIR}/test/ConfigureAndBuild.cmake"
+        SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/output directory"
+        BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build"
+        GENERATOR "${CMAKE_GENERATOR}"
+        RUST_TOOLCHAIN "${Rust_TOOLCHAIN}"
+        CARGO_TARGET "${Rust_CARGO_TARGET}"
+        SYSTEM_NAME "${CMAKE_SYSTEM_NAME}"
+        "${TEST_C_COMPILER}"
+        "${TEST_CXX_COMPILER}"
+        "${TEST_GENERATOR_PLATFORM}"
+
+        COMMAND_EXPAND_LISTS
+)
+set_tests_properties("output_directory_build" PROPERTIES FIXTURES_SETUP "build_fixture_output_directory")
+if(CORROSION_TESTS_INSTALL_CORROSION)
+    set_tests_properties("output_directory_build" PROPERTIES FIXTURES_REQUIRED "fixture_corrosion_install")
+endif()
+
+foreach(output_approach targetprop var)
+    if(output_approach STREQUAL "targetprop")
+       set(rust_proj_suffix "1")
+    elseif(output_approach STREQUAL "var")
+        set(rust_proj_suffix "2")
+    else()
+        message(FATAL_ERROR "specify rust project suffix for new output approach ${output_approach}")
+    endif()
+
+    set(bin_name "rust_bin${rust_proj_suffix}${CMAKE_EXECUTABLE_SUFFIX}")
+
+    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}"
+    )
+    set_tests_properties("output_directory_bin_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory")
+
+    set(lib_name "rust_lib${rust_proj_suffix}")
+
+    set(static_lib_name "${CMAKE_STATIC_LIBRARY_PREFIX}${lib_name}${CMAKE_STATIC_LIBRARY_SUFFIX}")
+
+    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}"
+    )
+    set_tests_properties("output_directory_staticlib_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory")
+
+    if(MINGW)
+        # Windows-GNU defines "lib" as prefix for DLLs, but cargo creates foo.dll instead of libfoo.dll
+        set(dynamic_lib_prefix "")
+    else()
+        set(dynamic_lib_prefix "${CMAKE_SHARED_LIBRARY_PREFIX}")
+    endif()
+    set(dynamic_lib_name "${dynamic_lib_prefix}${lib_name}${CMAKE_SHARED_LIBRARY_SUFFIX}")
+
+    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}"
+    )
+    set_tests_properties("output_directory_cdylib_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory")
+
+    if(WIN32)
+        set(implib_name ${CMAKE_IMPORT_LIBRARY_PREFIX}${lib_name}${CMAKE_IMPORT_LIBRARY_SUFFIX})
+
+        add_test(NAME output_directory_implib_${output_approach}
+            COMMAND
+            "${CMAKE_COMMAND}"
+            -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}"
+            )
+        set_tests_properties("output_directory_implib_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory")
+
+        if(MSVC)
+            if(output_approach STREQUAL "targetprop")
+                set(expected_lib_pdb_path "custom_lib_pdb_targetprop")
+                set(expected_bin_pdb_path "custom_bin_pdb_targetprop")
+            elseif(output_approach STREQUAL "var")
+                # When using a CMAKE_ variable instead of a target property, both targets
+                # end up in the same directory.
+                set(expected_lib_pdb_path "custom_binlib_pdb_var")
+                set(expected_bin_pdb_path "custom_binlib_pdb_var")
+            else()
+                message(FATAL_ERROR "specify rust project suffix for new output approach ${output_approach}")
+            endif()
+
+            set(lib_pdb_name "${lib_name}.pdb")
+            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}"
+                )
+            set_tests_properties("output_directory_cdylib_pdb_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory")
+
+            set(bin_pdb_name "rust_bin${rust_proj_suffix}.pdb")
+            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}"
+                )
+            set_tests_properties("output_directory_bin_pdb_${output_approach}" PROPERTIES FIXTURES_REQUIRED "build_fixture_output_directory")
+        endif()
+    endif()
+
+endforeach()
+
+add_test(NAME postbuild_custom_command
+    COMMAND
+    "${CMAKE_COMMAND}"
+    -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")
+
+add_test(NAME "output_directory_cleanup" COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/build")
+set_tests_properties("output_directory_cleanup" PROPERTIES FIXTURES_CLEANUP "build_fixture_output_directory")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/output directory/TestFileExists.cmake	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,13 @@
+# 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 <script_name>)
+
+# 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()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/output directory/output directory/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,44 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+corrosion_import_crate(MANIFEST_PATH proj1/Cargo.toml)
+
+# Note: The output directories defined here must be manually kept in sync with the expected test location.
+set_target_properties(rust_bin1
+    PROPERTIES
+        RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_bin_targetprop"
+        PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_bin_pdb_targetprop"
+
+)
+set_target_properties(rust_lib1 PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_archive_targetprop")
+set_target_properties(rust_lib1
+    PROPERTIES
+        LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_lib_targetprop"
+        PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_lib_pdb_targetprop"
+)
+
+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 "$<TARGET_PROPERTY:rust_bin1,LOCATION>" "${CMAKE_CURRENT_BINARY_DIR}/another_dir/moved_bin"
+    )
+
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_bin_var")
+set(CMAKE_PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_binlib_pdb_var")
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_archive_var")
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/custom_lib_var")
+
+corrosion_import_crate(MANIFEST_PATH proj2/Cargo.toml)
+
+unset(CMAKE_RUNTIME_OUTPUT_DIRECTORY)
+unset(CMAKE_PDB_OUTPUT_DIRECTORY)
+unset(CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
+unset(CMAKE_LIBRARY_OUTPUT_DIRECTORY)
+unset(CMAKE_PDB_OUTPUT_DIRECTORY)
+
+add_executable(consumer consumer.cpp)
+add_dependencies(consumer cargo-build_rust_lib1 cargo-build_rust_lib2)
+
+target_link_libraries(consumer rust_lib1 rust_lib2)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/output directory/output directory/consumer.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,16 @@
+#include <iostream>
+#include <cstdlib>
+
+extern "C" unsigned int ret_12();
+
+
+int main(int argc, char *argv[])
+{
+    std::cout << "HI\n";
+    unsigned int a = ret_12();
+    if (a != 12) {
+        return -1;
+    }
+
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/output directory/output directory/proj1/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,11 @@
+[package]
+name = "rust_package1"
+version = "0.1.0"
+edition = "2018"
+
+[lib]
+name = "rust_lib1"
+crate-type=["staticlib", "cdylib"]
+
+[[bin]]
+name = "rust_bin1"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/output directory/output directory/proj1/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,4 @@
+#[no_mangle]
+pub extern "C" fn ret_12() -> u32 {
+    12
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/output directory/output directory/proj2/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,11 @@
+[package]
+name = "rust_package2"
+version = "0.1.0"
+edition = "2018"
+
+[lib]
+name = "rust_lib2"
+crate-type=["staticlib", "cdylib"]
+
+[[bin]]
+name = "rust_bin2"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/output directory/output directory/proj2/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,4 @@
+#[no_mangle]
+pub extern "C" fn ret_12() -> u32 {
+    12
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/parse_target_triple/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,10 @@
+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"
+        )
+
+set_tests_properties("parse_target_triple_should_fail_build" PROPERTIES PASS_REGULAR_EXPRESSION
+    "CMake Warning"
+    )
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/parse_target_triple/parse_target_triple/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,106 @@
+# This test is supposed to ensure that the regex in _corrosion_parse_platform works as expected.
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+# Todo: Test if the output matches expectations.
+_corrosion_parse_target_triple("../../blah/x86_64-unknown-custom-gnu.json" arch vendor os env)
+_corrosion_parse_target_triple("x86_64-unknown-custom-gnu.json" arch vendor os env)
+_corrosion_parse_target_triple("/path/to/x86_64-unknown-custom-musl.json" arch vendor os env)
+_corrosion_parse_target_triple("../../blah/x86_64-custom_os.json" arch vendor os env)
+
+# List of builtin targets aquired via `rustup target list` with rust 1.64 on Linux.
+set(rustup_shipped_targets
+        "aarch64-apple-darwin"
+        "aarch64-apple-ios"
+        "aarch64-apple-ios-sim"
+        "aarch64-fuchsia"
+        "aarch64-linux-android"
+        "aarch64-pc-windows-msvc"
+        "aarch64-unknown-linux-gnu"
+        "aarch64-unknown-linux-musl"
+        "aarch64-unknown-none"
+        "aarch64-unknown-none-softfloat"
+        "arm-linux-androideabi"
+        "arm-unknown-linux-gnueabi"
+        "arm-unknown-linux-gnueabihf"
+        "arm-unknown-linux-musleabi"
+        "arm-unknown-linux-musleabihf"
+        "armebv7r-none-eabi"
+        "armebv7r-none-eabihf"
+        "armv5te-unknown-linux-gnueabi"
+        "armv5te-unknown-linux-musleabi"
+        "armv7-linux-androideabi"
+        "armv7-unknown-linux-gnueabi"
+        "armv7-unknown-linux-gnueabihf"
+        "armv7-unknown-linux-musleabi"
+        "armv7-unknown-linux-musleabihf"
+        "armv7a-none-eabi"
+        "armv7r-none-eabi"
+        "armv7r-none-eabihf"
+        "asmjs-unknown-emscripten"
+        "i586-pc-windows-msvc"
+        "i586-unknown-linux-gnu"
+        "i586-unknown-linux-musl"
+        "i686-linux-android"
+        "i686-pc-windows-gnu"
+        "i686-pc-windows-msvc"
+        "i686-unknown-freebsd"
+        "i686-unknown-linux-gnu"
+        "i686-unknown-linux-musl"
+        "mips-unknown-linux-gnu"
+        "mips-unknown-linux-musl"
+        "mips64-unknown-linux-gnuabi64"
+        "mips64-unknown-linux-muslabi64"
+        "mips64el-unknown-linux-gnuabi64"
+        "mips64el-unknown-linux-muslabi64"
+        "mipsel-unknown-linux-gnu"
+        "mipsel-unknown-linux-musl"
+        "nvptx64-nvidia-cuda"
+        "powerpc-unknown-linux-gnu"
+        "powerpc64-unknown-linux-gnu"
+        "powerpc64le-unknown-linux-gnu"
+        "riscv32i-unknown-none-elf"
+        "riscv32imac-unknown-none-elf"
+        "riscv32imc-unknown-none-elf"
+        "riscv64gc-unknown-linux-gnu"
+        "riscv64gc-unknown-none-elf"
+        "riscv64imac-unknown-none-elf"
+        "s390x-unknown-linux-gnu"
+        "sparc64-unknown-linux-gnu"
+        "sparcv9-sun-solaris"
+        "thumbv6m-none-eabi"
+        "thumbv7em-none-eabi"
+        "thumbv7em-none-eabihf"
+        "thumbv7m-none-eabi"
+        "thumbv7neon-linux-androideabi"
+        "thumbv7neon-unknown-linux-gnueabihf"
+        "thumbv8m.base-none-eabi"
+        "thumbv8m.main-none-eabi"
+        "thumbv8m.main-none-eabihf"
+        "wasm32-unknown-emscripten"
+        "wasm32-unknown-unknown"
+        "wasm32-wasi"
+        "x86_64-apple-darwin"
+        "x86_64-apple-ios"
+        "x86_64-fortanix-unknown-sgx"
+        "x86_64-fuchsia"
+        "x86_64-linux-android"
+        "x86_64-pc-solaris"
+        "x86_64-pc-windows-gnu"
+        "x86_64-pc-windows-msvc"
+        "x86_64-sun-solaris"
+        "x86_64-unknown-freebsd"
+        "x86_64-unknown-illumos"
+        "x86_64-unknown-linux-gnu"
+        "x86_64-unknown-linux-gnux32"
+        "x86_64-unknown-linux-musl"
+        "x86_64-unknown-netbsd"
+        "x86_64-unknown-none"
+        "x86_64-unknown-redox"
+)
+set(other_targets riscv32imc-esp-espidf xtensa-esp32s3-none-elf)
+
+foreach(target ${rustup_shipped_targets} ${other_targets})
+    _corrosion_parse_target_triple("${target}"  arch vendor os env)
+endforeach()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/parse_target_triple/parse_target_triple_should_fail/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,6 @@
+# This test is supposed to ensure that the regex in _corrosion_parse_platform works as expected.
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+_corrosion_parse_target_triple("x86_64-unknown-linux-gnu-toomuch" arch vendor os env)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rust2cpp/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,9 @@
+corrosion_tests_add_test(rust2cpp "cpp-exe;cpp-exe-shared")
+
+set_tests_properties("rust2cpp_run_cpp-exe" PROPERTIES PASS_REGULAR_EXPRESSION
+        "^Hello, Cpp! I'm Rust!\r?\n$"
+        )
+
+set_tests_properties("rust2cpp_run_cpp-exe-shared" PROPERTIES PASS_REGULAR_EXPRESSION
+        "^Hello, Cpp! I'm Rust!\r?\n$"
+        )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rust2cpp/rust2cpp/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml)
+
+add_executable(cpp-exe main.cpp)
+target_link_libraries(cpp-exe PUBLIC rust_lib)
+
+add_executable(cpp-exe-shared main.cpp)
+target_link_libraries(cpp-exe-shared
+        PUBLIC rust_lib-shared)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rust2cpp/rust2cpp/main.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,9 @@
+extern "C" void rust_function(char const *name);
+
+int main(int argc, char **argv) {
+    if (argc < 2) {
+        rust_function("Cpp");
+    } else {
+        rust_function(argv[1]);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rust2cpp/rust2cpp/rust/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,11 @@
+[package]
+name = "rust-lib"
+version = "0.1.0"
+authors = ["Andrew Gaspar <andrew.gaspar@outlook.com>"]
+license = "MIT"
+edition = "2018"
+
+[dependencies]
+
+[lib]
+crate-type=["staticlib", "cdylib"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rust2cpp/rust2cpp/rust/build.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,4 @@
+// Build-scripts also need to be linked, so just add a dummy buildscript ensuring this works.
+fn main() {
+    println!("Build-script is running.")
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rust2cpp/rust2cpp/rust/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,7 @@
+use std::os::raw::c_char;
+
+#[no_mangle]
+pub extern "C" fn rust_function(name: *const c_char) {
+    let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() };
+    println!("Hello, {}! I'm Rust!", name);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rustflags/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,7 @@
+corrosion_tests_add_test(rustflags "rustflags-cpp-exe")
+
+set_tests_properties("rustflags_run_rustflags-cpp-exe" PROPERTIES PASS_REGULAR_EXPRESSION
+        "Hello, Cpp! I'm Rust!\r?\nHello, Cpp again! I'm Rust in (Debug|Release) mode again!\r?\nHello, Cpp again! I'm Rust again, third time the charm!\r?\n$"
+        )
+
+corrosion_tests_add_test(cargo_config_rustflags "cargo_config_rustflags")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rustflags/cargo_config_rustflags/.cargo/config.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,2 @@
+[build]
+rustflags = ["--cfg=some_cargo_config_rustflag"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rustflags/cargo_config_rustflags/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+corrosion_import_crate(MANIFEST_PATH Cargo.toml)
+
+# Do not use `corrosion_add_target_rustflags()` here, since we want to test if the rustflag from `.cargo/config.toml`
+# is picked up.
+
+# Local rustflags should not interfere with `.cargo/config.toml`, so enable one.
+corrosion_add_target_local_rustflags(cargo_config_rustflags "--cfg=local_rustflag")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rustflags/cargo_config_rustflags/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,4 @@
+[package]
+name = "cargo_config_rustflags"
+version = "0.1.0"
+edition = "2018"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rustflags/cargo_config_rustflags/src/main.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,16 @@
+
+#[cfg(some_cargo_config_rustflag)]
+fn print_line() {
+    println!("Rustflag is enabled");
+}
+
+// test that local rustflags don't override global rustflags set via `.cargo/config`
+#[cfg(local_rustflag)]
+fn test_local_rustflag() {
+    println!("local_rustflag was enabled");
+}
+
+fn main() {
+    print_line();
+    test_local_rustflag();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rustflags/rustflags/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml)
+
+add_executable(rustflags-cpp-exe main.cpp)
+target_link_libraries(rustflags-cpp-exe PUBLIC rustflag_test_lib)
+
+# Test --cfg=key="value" rustflag.
+corrosion_add_target_rustflags(rustflag_test_lib --cfg=test_rustflag_cfg1="test_rustflag_cfg1_value")
+
+# Test using a generator expression to produce a rustflag and passing multiple rustflags.
+corrosion_add_target_rustflags(rustflag_test_lib
+        --cfg=test_rustflag_cfg2="$<IF:$<OR:$<CONFIG:Debug>,$<CONFIG:>>,debug,release>"
+        "--cfg=test_rustflag_cfg3"
+)
+
+corrosion_add_target_local_rustflags(rustflag_test_lib "--cfg=test_local_rustflag1")
+corrosion_add_target_local_rustflags(rustflag_test_lib --cfg=test_local_rustflag2="value")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rustflags/rustflags/main.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,13 @@
+extern "C" void rust_function(char const *name);
+extern "C" void rust_second_function(char const *name);
+extern "C" void rust_third_function(char const *name);
+
+int main(int argc, char **argv) {
+    if (argc < 2) {
+        rust_function("Cpp");
+        rust_second_function("Cpp again");
+        rust_third_function("Cpp again");
+    } else {
+        rust_function(argv[1]);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rustflags/rustflags/rust/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,11 @@
+[package]
+name = "rustflag-test-lib"
+version = "0.1.0"
+license = "MIT"
+edition = "2018"
+
+[dependencies]
+some_dependency = { path = "some_dependency" }
+
+[lib]
+crate-type=["staticlib"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rustflags/rustflags/rust/some_dependency/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,6 @@
+[package]
+name = "some_dependency"
+version = "0.1.0"
+license = "MIT"
+edition = "2018"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rustflags/rustflags/rust/some_dependency/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,10 @@
+//! Test that the local rustflags are only passed to the main crate and not to dependencies.
+#[cfg(test_local_rustflag1)]
+const _: [(); 1] = [(); 2];
+
+#[cfg(test_local_rustflag2 = "value")]
+const _: [(); 1] = [(); 2];
+
+pub fn some_function() -> u32 {
+    42
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/rustflags/rustflags/rust/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,40 @@
+#[cfg(test_rustflag_cfg1 = "test_rustflag_cfg1_value")]
+use std::os::raw::c_char;
+
+#[no_mangle]
+#[cfg(test_rustflag_cfg1 = "test_rustflag_cfg1_value")]
+pub extern "C" fn rust_function(name: *const c_char) {
+    let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() };
+    println!("Hello, {}! I'm Rust!", name);
+}
+
+#[no_mangle]
+#[cfg(all(debug_assertions, test_rustflag_cfg2 = "debug"))]
+pub extern "C" fn rust_second_function(name: *const c_char) {
+    let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() };
+    println!("Hello, {}! I'm Rust in Debug mode again!", name);
+}
+
+#[no_mangle]
+#[cfg(all(not(debug_assertions), test_rustflag_cfg2 = "release"))]
+pub extern "C" fn rust_second_function(name: *const c_char) {
+    let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() };
+    println!("Hello, {}! I'm Rust in Release mode again!", name);
+}
+
+#[no_mangle]
+#[cfg(test_rustflag_cfg3)]
+pub extern "C" fn rust_third_function(name: *const c_char) {
+    let name = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() };
+    println!("Hello, {}! I'm Rust again, third time the charm!", name);
+    assert_eq!(some_dependency::some_function(), 42);
+}
+
+#[cfg(not(test_rustflag_cfg3))]
+const _: [(); 1] = [(); 2];
+
+#[cfg(not(test_local_rustflag1))]
+const _: [(); 1] = [(); 2];
+
+#[cfg(not(test_local_rustflag2 = "value"))]
+const _: [(); 1] = [(); 2];
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/workspace/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,6 @@
+corrosion_tests_add_test(workspace "my_program")
+
+set_tests_properties("workspace_run_my_program" PROPERTIES PASS_REGULAR_EXPRESSION
+        "^Ok\r?\n$"
+        )
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/workspace/workspace/CMakeLists.txt	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.15)
+project(test_project VERSION 0.1.0)
+include(../../test_header.cmake)
+
+corrosion_import_crate(
+    MANIFEST_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml
+    CRATES member1 member2
+    IMPORTED_CRATES imported_crate_list
+)
+
+#NOTE: member3 also contains a binary called my_program, but that shouldn't be a problem since it is not imported
+add_executable(my_program main.cpp)
+target_link_libraries(my_program PUBLIC member1 member2)
+
+# Test that the list of imported crates matches our expectations.
+if(NOT DEFINED imported_crate_list)
+    message(FATAL_ERROR "Corrosion failed to set the variable passed via IMPORTED_CRATES.")
+endif()
+set(expected_crates member1 member2)
+foreach(crate ${expected_crates})
+    if(NOT "${crate}" IN_LIST imported_crate_list)
+        message(FATAL_ERROR "Expected ${crate} to be imported, but it wasn't. Imported crate list:\n"
+            "${imported_crate_list}"
+        )
+    endif()
+endforeach()
+set(additional_crates ${imported_crate_list})
+list(REMOVE_ITEM additional_crates ${expected_crates})
+if(additional_crates)
+    message(FATAL_ERROR "Corrosion unexpectedly imported the following crates: ${additional_crates}")
+endif()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/workspace/workspace/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,5 @@
+[workspace]
+members=["member1", "member2", "member3"]
+
+[workspace.package]
+version = "0.1.0"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/workspace/workspace/main.cpp	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,4 @@
+#include <iostream>
+int main() {
+        std::cout << "Ok";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/workspace/workspace/member1/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,8 @@
+[package]
+name = "member1"
+version = "0.1.0"
+edition = "2018"
+description = "descr;\"hello\\"
+
+[lib]
+crate-type = [ "lib", "cdylib" ]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/workspace/workspace/member1/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,7 @@
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn it_works() {
+        assert_eq!(2 + 2, 4);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/workspace/workspace/member2/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,8 @@
+[package]
+name = "member2"
+version = "0.1.0"
+authors = ["Olivier Goffart <ogoffart@sixtyfps.io>"]
+edition = "2018"
+
+[lib]
+crate-type = ["staticlib"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/workspace/workspace/member2/src/lib.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,7 @@
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn it_works() {
+        assert_eq!(2 + 2, 4);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/workspace/workspace/member3/Cargo.toml	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,14 @@
+[package]
+name = "member3"
+version = "0.1.0"
+authors = ["Olivier Goffart <ogoffart@sixtyfps.io>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[[bin]]
+name = "my_program"
+path = "src/main.rs"
+
+[dependencies]
+member1 = { path = "../member1" }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/corrosion/test/workspace/workspace/member3/src/main.rs	Wed Aug 28 15:31:51 2024 +0200
@@ -0,0 +1,3 @@
+fn main() {
+    println!("Hello, world!");
+}