diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index a530637c03..682d592e1c 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -405,6 +405,7 @@ Properties on Targets /prop_tgt/RUNTIME_OUTPUT_DIRECTORY_CONFIG /prop_tgt/RUNTIME_OUTPUT_NAME /prop_tgt/RUNTIME_OUTPUT_NAME_CONFIG + /prop_tgt/Rust_EDITION /prop_tgt/SKIP_BUILD_RPATH /prop_tgt/SKIP_LINTING /prop_tgt/SOURCE_DIR diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index d7987baf6d..fe285e423e 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -579,6 +579,7 @@ Variables that Control the Build /variable/CMAKE_POSITION_INDEPENDENT_CODE /variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY /variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY_CONFIG + /variable/CMAKE_Rust_EDITION /variable/CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS /variable/CMAKE_SHARED_LINKER_FLAGS /variable/CMAKE_SHARED_LINKER_FLAGS_CONFIG diff --git a/Help/prop_tgt/Rust_EDITION.rst b/Help/prop_tgt/Rust_EDITION.rst new file mode 100644 index 0000000000..454b1b1206 --- /dev/null +++ b/Help/prop_tgt/Rust_EDITION.rst @@ -0,0 +1,25 @@ +Rust_EDITION +------------ + +.. versionadded:: 4.4 + +.. note:: + Experimental. Gated by ``CMAKE_EXPERIMENTAL_RUST``. + +The Rust edition required to build this target. It is initialized from the +:variable:`CMAKE_Rust_EDITION` variable if defined. Setting this property +results in adding a flag such as ``--edition=2018`` to the compile line. + +Supported values are: + +``2015`` + Rust 2015, the default edition, started with Rust 1.0. + +``2018`` + Rust 2018, available since Rust 1.31. + +``2021`` + Rust 2021, available since Rust 1.56. + +``2024`` + Rust 2024, available since Rust 1.85. diff --git a/Help/variable/CMAKE_Rust_EDITION.rst b/Help/variable/CMAKE_Rust_EDITION.rst new file mode 100644 index 0000000000..273aea6205 --- /dev/null +++ b/Help/variable/CMAKE_Rust_EDITION.rst @@ -0,0 +1,10 @@ +CMAKE_Rust_EDITION +------------------ + +.. versionadded:: 4.4 + +.. note:: + Experimental. Gated by ``CMAKE_EXPERIMENTAL_RUST``. + +Default value for :prop_tgt:`Rust_EDITION` target property if set when a target +is created. diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 811d608b6f..d9df36eb46 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -2795,6 +2795,14 @@ void cmGeneratorTarget::AddHIPArchitectureFlags(cmBuildStep compileOrLink, } } +void cmGeneratorTarget::AddRustTargetFlags(std::string& flags) const +{ + cmValue const edition = this->GetProperty("Rust_EDITION"); + if (edition && !edition->empty()) { + flags += " --edition=" + *edition; + } +} + void cmGeneratorTarget::AddCUDAToolkitFlags(std::string& flags) const { std::string const& compiler = diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 388fa32ee2..c0fceea916 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -626,6 +626,8 @@ public: void AddISPCTargetFlags(std::string& flags) const; + void AddRustTargetFlags(std::string& flags) const; + std::string GetFeatureSpecificLinkRuleVariable( std::string const& var, std::string const& lang, std::string const& config) const; diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 258ce56477..d7a591c85b 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -2140,6 +2140,8 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, } } else if (lang == "HIP") { target->AddHIPArchitectureFlags(compileOrLink, config, flags); + } else if (lang == "Rust") { + target->AddRustTargetFlags(flags); } // Add VFS Overlay for Clang compilers diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 3172093f6b..2b3217e13e 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -381,6 +381,7 @@ TargetProperty const StaticTargetProperties[] = { { "Swift_MODULE_DIRECTORY"_s, IC::CanCompileSources }, { "Swift_COMPILATION_MODE"_s, IC::CanCompileSources }, // ---- Rust + { "Rust_EDITION"_s, IC::CanCompileSources }, { "Rust_MAIN_CRATE_ROOT"_s, IC::CanCompileSources }, // ---- moc { "AUTOMOC"_s, IC::CanCompileSources }, diff --git a/Tests/RunCMake/Rust/Editions-build-stdout.txt b/Tests/RunCMake/Rust/Editions-build-stdout.txt new file mode 100644 index 0000000000..21786cd077 --- /dev/null +++ b/Tests/RunCMake/Rust/Editions-build-stdout.txt @@ -0,0 +1,5 @@ +\[1/5\] Building Rust object CMakeFiles[\\/]Edition\.20[0-9][0-9]\.dir[\\/](Debug[\\/])?libedition_20[0-9][0-9]\.rs\.rlib +\[2/5\] Building Rust object CMakeFiles[\\/]Edition\.20[0-9][0-9]\.dir[\\/](Debug[\\/])?libedition_20[0-9][0-9]\.rs\.rlib +\[3/5\] Building Rust object CMakeFiles[\\/]Edition\.20[0-9][0-9]\.dir[\\/](Debug[\\/])?libedition_20[0-9][0-9]\.rs\.rlib +\[4/5\] Building Rust object CMakeFiles[\\/]Edition\.20[0-9][0-9]\.dir[\\/](Debug[\\/])?libedition_20[0-9][0-9]\.rs\.rlib +\[5/5\] Linking Rust executable (Debug[\\/])?Editions diff --git a/Tests/RunCMake/Rust/Editions-stderr.txt b/Tests/RunCMake/Rust/Editions-stderr.txt new file mode 100644 index 0000000000..c048dd6186 --- /dev/null +++ b/Tests/RunCMake/Rust/Editions-stderr.txt @@ -0,0 +1,6 @@ +^CMake Warning \(dev\) at Editions\.cmake:[0-9]+ \(enable_language\): + CMake's support for the Rust programming language is experimental\. It is + meant only for experimentation and feedback to CMake developers\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\.$ diff --git a/Tests/RunCMake/Rust/Editions-stdout.txt b/Tests/RunCMake/Rust/Editions-stdout.txt new file mode 100644 index 0000000000..e54269bf13 --- /dev/null +++ b/Tests/RunCMake/Rust/Editions-stdout.txt @@ -0,0 +1,4 @@ +Not searching for unused variables given on the command line. +-- Configuring done \([.0-9]+s\) +-- Generating done \([.0-9]+s\) +-- Build files have been written to: .*[\\/]Editions-build diff --git a/Tests/RunCMake/Rust/Editions.cmake b/Tests/RunCMake/Rust/Editions.cmake new file mode 100644 index 0000000000..cb4e48902c --- /dev/null +++ b/Tests/RunCMake/Rust/Editions.cmake @@ -0,0 +1,32 @@ +set(CMAKE_EXPERIMENTAL_RUST "6b6e613b-f6cb-402d-8aea-59034fa8c65b") +enable_language(Rust) + +# No Edition set, rustc defaults to 2015. +add_library(Edition.2015 OBJECT edition_2015.rs) +set_target_properties(Edition.2015 PROPERTIES Rust_EDITION 2015) + +# Explicitly set edition on target, without default value from variable. +add_library(Edition.2018 OBJECT edition_2018.rs) +set_target_properties(Edition.2018 PROPERTIES Rust_EDITION 2018) + +# Implicitly set edition on target from variable +set(CMAKE_Rust_EDITION 2021) +add_library(Edition.2021 OBJECT edition_2021.rs) + +# Explicitly override the edition. +add_library(Edition.2024 OBJECT edition_2024.rs) +set_target_properties(Edition.2024 PROPERTIES Rust_EDITION 2024) + +# Rust guarantees that we can link multiple crates with different editions +# together. We specify explicitly the edition to make sure CMake properly pass +# the edition in the linking step too. +unset(CMAKE_Rust_EDITION) +add_executable(Editions editions.rs) +set_target_properties(Editions PROPERTIES Rust_EDITION 2018) +target_link_libraries( + Editions PRIVATE + Edition.2015 + Edition.2018 + Edition.2021 + Edition.2024 +) diff --git a/Tests/RunCMake/Rust/RunCMakeTest.cmake b/Tests/RunCMake/Rust/RunCMakeTest.cmake index 5178b4f538..c3d0207b04 100644 --- a/Tests/RunCMake/Rust/RunCMakeTest.cmake +++ b/Tests/RunCMake/Rust/RunCMakeTest.cmake @@ -2,3 +2,10 @@ include(RunCMake) run_cmake(Enable) run_cmake(EnableExperimental) + +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Editions-build) + run_cmake(Editions) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(Editions-build ${CMAKE_COMMAND} --build . --config Debug) +endblock() diff --git a/Tests/RunCMake/Rust/edition_2015.rs b/Tests/RunCMake/Rust/edition_2015.rs new file mode 100644 index 0000000000..3c85b92784 --- /dev/null +++ b/Tests/RunCMake/Rust/edition_2015.rs @@ -0,0 +1,2 @@ +// dyn is as an identifier is only allowed in Rust 2015 +pub fn dyn() {} diff --git a/Tests/RunCMake/Rust/edition_2018.rs b/Tests/RunCMake/Rust/edition_2018.rs new file mode 100644 index 0000000000..2e68a3a652 --- /dev/null +++ b/Tests/RunCMake/Rust/edition_2018.rs @@ -0,0 +1,18 @@ +#![allow(rust_2021_compatibility)] + +pub trait Foo {} + +struct FooA {} +impl Foo for FooA {} + +struct FooB {} +impl Foo for FooB {} + +// dyn keyword is available in 2018+ +pub fn make_foo(value: i32) -> Box { + match value { + // Unsupported in 2021+, should be 0..=42 + 0...42 => Box::new(FooA{}), + _ => Box::new(FooB{}), + } +} diff --git a/Tests/RunCMake/Rust/edition_2021.rs b/Tests/RunCMake/Rust/edition_2021.rs new file mode 100644 index 0000000000..ad113c73cb --- /dev/null +++ b/Tests/RunCMake/Rust/edition_2021.rs @@ -0,0 +1,5 @@ +// gen is becoming a keyword in 2024 +pub fn gen() -> i32 { + // This does not compile before Rust 2021 + [1, 2, 3].into_iter().reduce(|acc, n| acc + n).unwrap_or(0) +} diff --git a/Tests/RunCMake/Rust/edition_2024.rs b/Tests/RunCMake/Rust/edition_2024.rs new file mode 100644 index 0000000000..4ba8093cde --- /dev/null +++ b/Tests/RunCMake/Rust/edition_2024.rs @@ -0,0 +1,8 @@ +pub fn add_options(a: Option, b: Option) -> Option { + // let chains only allowed in Rust 2024 + if let Some(a) = a && let Some(b) = b { + Some(a + b) + } else { + None + } +} diff --git a/Tests/RunCMake/Rust/editions.rs b/Tests/RunCMake/Rust/editions.rs new file mode 100644 index 0000000000..01aaa74d7d --- /dev/null +++ b/Tests/RunCMake/Rust/editions.rs @@ -0,0 +1,12 @@ + +use edition_2015; +use edition_2018; +use edition_2021; +use edition_2024; + +fn main() { + edition_2015::r#dyn(); + let _ = edition_2018::make_foo(1337); + let _ = edition_2021::r#gen(); + let _ = edition_2024::add_options(None, None); +}