diff --git a/RELEASES.md b/RELEASES.md index dd6e0fa0..a1588a7c 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -10,7 +10,10 @@ ### New features - Support using the `$` generator expression in `OUTPUT_DIRECTORY`. [#459] - +- Add `OVERRIDE_CRATE_TYPE` option to corrosion_import_crate, allowing users to override + the crate-types of Rust libraries (e.g. force building as a staticlib instead of an rlib). +- Support *-windows-gnullvm targets. +- experimental support in corrosion_install for installing libraries and header files [#459]: https://github.com/corrosion-rs/corrosion/pull/459 diff --git a/cmake/Corrosion.cmake b/cmake/Corrosion.cmake index 429176b0..37f72725 100644 --- a/cmake/Corrosion.cmake +++ b/cmake/Corrosion.cmake @@ -579,11 +579,20 @@ function(_add_cargo_build out_cargo_build_out_dir) set(workspace_manifest_path "${ACB_WORKSPACE_MANIFEST_PATH}") set(build_byproducts "${ACB_BYPRODUCTS}") - + unset(cargo_rustc_crate_types) 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") + if("${Rust_VERSION}" VERSION_GREATER_EQUAL "1.64") + # https://doc.rust-lang.org/1.64.0/cargo/commands/cargo-rustc.html + # `--crate-type` is documented since Rust 1.64 for `cargo rustc`. + # We just unconditionally set it when available, to support overriding the crate type. + # Due to https://github.com/rust-lang/cargo/issues/14498 we can't use one argument and pass a + # comma seperated list. Instead we use multiple arguments. + set(cargo_rustc_crate_types "${target_kinds}") + list(TRANSFORM cargo_rustc_crate_types PREPEND "--crate-type=") + endif() elseif("bin" IN_LIST target_kinds) set(cargo_rustc_filter "--bin=${target_name}") else() @@ -777,6 +786,7 @@ function(_add_cargo_build out_cargo_build_out_dir) ${no_default_features_arg} ${features_genex} --package ${package_name} + ${cargo_rustc_crate_types} --manifest-path "${path_to_toml}" --target-dir "${cargo_target_dir}" ${cargo_profile} @@ -846,6 +856,7 @@ corrosion_import_crate( [PROFILE ] [IMPORTED_CRATES ] [CRATE_TYPES ... ] + [OVERRIDE_CRATE_TYPE = ...] [CRATES ... ] [FEATURES ... ] [FLAGS ... ] @@ -861,6 +872,9 @@ corrosion_import_crate( * **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`. +* **OVERRIDE_CRATE_TYPE**: Override the crate-types of a cargo crate with the given comma-separated values. + Internally uses the `rustc` flag [`--crate-type`] to override the crate-type. + Valid values for the crate types are the library types `staticlib` and `cdylib`. * **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`. @@ -871,6 +885,7 @@ corrosion_import_crate( [--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 +[`--crate-type`]: https://doc.rust-lang.org/rustc/command-line-arguments.html#--crate-type-a-list-of-types-of-crates-for-the-compiler-to-emit [Cargo.toml Manifest]: https://doc.rust-lang.org/cargo/appendix/glossary.html#manifest ANCHOR_END: corrosion-import-crate @@ -878,7 +893,7 @@ 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) + set(MULTI_VALUE_KEYWORDS CRATE_TYPES CRATES FEATURES FLAGS OVERRIDE_CRATE_TYPE) cmake_parse_arguments(COR "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}" ${ARGN}) list(APPEND CMAKE_MESSAGE_CONTEXT "corrosion_import_crate") @@ -917,6 +932,46 @@ function(corrosion_import_crate) endif() endif() + # intended to be used with foreach(... ZIP_LISTS ...), meaning + # that the crate_types at index i of `override_crate_type_types_list` are + # for the package_name at index i of `override_crate_type_package_name_list`. + # It would really be nice if CMake had structs or dicts. + unset(override_crate_type_package_name_list) + unset(override_crate_type_types_list) + unset(OVERRIDE_CRATE_TYPE_ARGS) + if(DEFINED COR_OVERRIDE_CRATE_TYPE) + string(JOIN " " usage_help + "Each argument to OVERRIDE_CRATE_TYPE must be of the form `=." + "The package_name must be a valid cargo package name and the crate_type must be " + "a comma-seperated list with valid values being `staticlib`, `cdylib` and `bin`" + ) + foreach(entry IN LISTS COR_OVERRIDE_CRATE_TYPE) + string(REPLACE "=" ";" key_val_list ${entry}) + list(LENGTH key_val_list key_val_list_len) + if(NOT key_val_list_len EQUAL "2") + message(FATAL_ERROR "Invalid argument: `${entry}` for parameter OVERRIDE_CRATE_TYPE!\n" + "${usage_help}" + ) + endif() + list(GET key_val_list "0" package_name) + list(GET key_val_list "1" crate_types) + list(APPEND override_crate_type_package_name_list "${package_name}") + list(APPEND override_crate_type_types_list "${crate_types}") + endforeach() + list(LENGTH override_crate_type_package_name_list num_override_packages) + list(LENGTH override_crate_type_types_list num_override_packages2) + if("${Rust_VERSION}" VERSION_LESS "1.64") + message(WARNING "OVERRIDE_CRATE_TYPE requires at Rust 1.64 or newer. Ignoring the option") + elseif(NOT num_override_packages EQUAL num_override_packages2) + message(WARNING "Internal error while parsing OVERRIDE_CRATE_TYPE arguments.\n" + "Corrosion will ignore this argument and continue." + ) + else() + # Pass by ref: we intentionally pass the list names here! + set(override_crate_types_arg "OVERRIDE_CRATE_TYPE_ARGS" "override_crate_type_package_name_list" "override_crate_type_types_list") + endif() + endif() + if (NOT IS_ABSOLUTE "${COR_MANIFEST_PATH}") set(COR_MANIFEST_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${COR_MANIFEST_PATH}) endif() @@ -940,6 +995,7 @@ function(corrosion_import_crate) ${crate_allowlist} ${crate_types} ${no_linker_override} + ${override_crate_types_arg} ) # Not target props yet: diff --git a/cmake/CorrosionGenerator.cmake b/cmake/CorrosionGenerator.cmake index b883ebd3..b1a1d84a 100644 --- a/cmake/CorrosionGenerator.cmake +++ b/cmake/CorrosionGenerator.cmake @@ -44,8 +44,14 @@ 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) + 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 OVERRIDE_CRATE_TYPE_ARGS) cmake_parse_arguments(PARSE_ARGV 0 GAPT "${OPTIONS}" "${ONE_VALUE_KEYWORDS}" "${MULTI_VALUE_KEYWORDS}") if(DEFINED GAPT_UNPARSED_ARGUMENTS) @@ -64,6 +70,10 @@ function(_generator_add_package_targets) set(targets "${GAPT_TARGETS_JSON}") set(out_created_targets "${GAPT_OUT_CREATED_TARGETS}") set(crate_types "${GAPT_CRATE_TYPES}") + if(DEFINED GAPT_OVERRIDE_CRATE_TYPE_ARGS) + list(GET GAPT_OVERRIDE_CRATE_TYPE_ARGS 0 override_crate_name_list_ref) + list(GET GAPT_OVERRIDE_CRATE_TYPE_ARGS 1 override_crate_types_list_ref) + endif() set(corrosion_targets "") @@ -82,13 +92,26 @@ function(_generator_add_package_targets) 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() - + unset(override_package_crate_type) + # OVERRIDE_CRATE_TYPE is more specific than the CRATE_TYPES argument to corrosion_import_crate, and thus takes + # priority. + if(DEFINED GAPT_OVERRIDE_CRATE_TYPE_ARGS) + foreach(override_crate_name override_crate_types IN ZIP_LISTS ${override_crate_name_list_ref} ${override_crate_types_list_ref}) + if("${override_crate_name}" STREQUAL "${target_name}") + message(DEBUG "Forcing crate ${target_name} to crate-type(s): ${override_crate_types}.") + # Convert to CMake list + string(REPLACE "," ";" kinds "${override_crate_types}") + break() + endif() + endforeach() + else() + 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() + endif() if(TARGET "${target_name}" AND ("staticlib" IN_LIST kinds OR "cdylib" IN_LIST kinds OR "bin" IN_LIST kinds) ) @@ -213,7 +236,7 @@ endfunction() 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) + set(multi_value_args CRATES CRATE_TYPES OVERRIDE_CRATE_TYPE_ARGS) cmake_parse_arguments( GGC "${options}" @@ -225,6 +248,7 @@ function(_generator_add_cargo_targets) _corrosion_option_passthrough_helper(NO_LINKER_OVERRIDE GGC no_linker_override) _corrosion_arg_passthrough_helper(CRATE_TYPES GGC crate_types) + _corrosion_arg_passthrough_helper(OVERRIDE_CRATE_TYPE_ARGS GGC override_crate_types) _cargo_metadata(json "${GGC_MANIFEST_PATH}") string(JSON packages GET "${json}" "packages") @@ -284,6 +308,7 @@ function(_generator_add_cargo_targets) OUT_CREATED_TARGETS curr_created_targets ${no_linker_override} ${crate_types} + ${override_crate_types} ) list(APPEND created_targets "${curr_created_targets}") endforeach() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 17e9059c..20f4e77d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -168,6 +168,7 @@ 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) + add_subdirectory(override_crate_type) endif() add_subdirectory(custom_profiles) add_subdirectory(cbindgen) diff --git a/test/override_crate_type/CMakeLists.txt b/test/override_crate_type/CMakeLists.txt new file mode 100644 index 00000000..c246f77e --- /dev/null +++ b/test/override_crate_type/CMakeLists.txt @@ -0,0 +1,9 @@ +corrosion_tests_add_test(override_crate_type "cpp-exe;cpp-exe-shared") + +set_tests_properties("override_crate_type_run_cpp-exe" PROPERTIES PASS_REGULAR_EXPRESSION + "^Hello, Cpp! I'm Rust!\r?\n$" + ) + +set_tests_properties("override_crate_type_run_cpp-exe-shared" PROPERTIES PASS_REGULAR_EXPRESSION + "^Hello, Cpp! I'm Rust!\r?\n$" + ) diff --git a/test/override_crate_type/override_crate_type/CMakeLists.txt b/test/override_crate_type/override_crate_type/CMakeLists.txt new file mode 100644 index 00000000..8a465ab9 --- /dev/null +++ b/test/override_crate_type/override_crate_type/CMakeLists.txt @@ -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 OVERRIDE_CRATE_TYPE my_rust_lib=staticlib,cdylib) + +add_executable(cpp-exe main.cpp) +target_link_libraries(cpp-exe PUBLIC my_rust_lib) + +add_executable(cpp-exe-shared main.cpp) +target_link_libraries(cpp-exe-shared + PUBLIC my_rust_lib-shared) diff --git a/test/override_crate_type/override_crate_type/main.cpp b/test/override_crate_type/override_crate_type/main.cpp new file mode 100644 index 00000000..785c0bc3 --- /dev/null +++ b/test/override_crate_type/override_crate_type/main.cpp @@ -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]); + } +} diff --git a/test/override_crate_type/override_crate_type/rust/Cargo.lock b/test/override_crate_type/override_crate_type/rust/Cargo.lock new file mode 100644 index 00000000..5dc1732e --- /dev/null +++ b/test/override_crate_type/override_crate_type/rust/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "rust-lib" +version = "0.1.0" diff --git a/test/override_crate_type/override_crate_type/rust/Cargo.toml b/test/override_crate_type/override_crate_type/rust/Cargo.toml new file mode 100644 index 00000000..d53defba --- /dev/null +++ b/test/override_crate_type/override_crate_type/rust/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rust-lib" +version = "0.1.0" +authors = ["Andrew Gaspar "] +license = "MIT" +edition = "2018" + + +[lib] +name = "my_rust_lib" diff --git a/test/override_crate_type/override_crate_type/rust/build.rs b/test/override_crate_type/override_crate_type/rust/build.rs new file mode 100644 index 00000000..9dfeaa0b --- /dev/null +++ b/test/override_crate_type/override_crate_type/rust/build.rs @@ -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.") +} diff --git a/test/override_crate_type/override_crate_type/rust/src/lib.rs b/test/override_crate_type/override_crate_type/rust/src/lib.rs new file mode 100644 index 00000000..194e5650 --- /dev/null +++ b/test/override_crate_type/override_crate_type/rust/src/lib.rs @@ -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); +}