tools/corrosion/cmake/CorrosionGenerator.cmake
branchtransitional_engine
changeset 16050 6a3dc15b78b9
--- /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()