diff --git a/.gitlab/ci/configure_fedora44_makefiles.cmake b/.gitlab/ci/configure_fedora44_makefiles.cmake index 2cd93f40bf..49ed52fc05 100644 --- a/.gitlab/ci/configure_fedora44_makefiles.cmake +++ b/.gitlab/ci/configure_fedora44_makefiles.cmake @@ -112,6 +112,7 @@ set(CMake_TEST_LOCALE_CHARSET "ISO-8859-1" CACHE STRING "") set(CMake_TEST_LOCALE_C_UTF8 ON CACHE BOOL "") set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "") set(CMake_TEST_Qt5 "ON" CACHE BOOL "") +set(CMake_TEST_Rust "ON" CACHE STRING "") set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "") set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "") diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst index fb93e2496a..9fd95e83e5 100644 --- a/Help/dev/experimental.rst +++ b/Help/dev/experimental.rst @@ -127,7 +127,7 @@ Rust Support In order to activate support for Rust, set * variable ``CMAKE_EXPERIMENTAL_RUST`` to -* value ``efaed83b-d73a-48af-999a-bd0a6172c313``. +* value ``b6fdddce-bf66-41a5-bc5f-077f6fa4d2a1``. This UUID may change in future versions of CMake. Be sure to use the value documented here by the source tree of the version of CMake with which you are diff --git a/Modules/CMakeDetermineRustCompiler.cmake b/Modules/CMakeDetermineRustCompiler.cmake index 971354fabc..4272e1bf26 100644 --- a/Modules/CMakeDetermineRustCompiler.cmake +++ b/Modules/CMakeDetermineRustCompiler.cmake @@ -3,7 +3,7 @@ include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake) -if(NOT "${CMAKE_GENERATOR}" MATCHES "^Ninja") +if(NOT "${CMAKE_GENERATOR}" MATCHES "^(Ninja)|(Unix Makefiles)") message(FATAL_ERROR "Rust language not supported by \"${CMAKE_GENERATOR}\" generator") endif() diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx index 6db67678ca..f873769cc9 100644 --- a/Source/cmCommonTargetGenerator.cxx +++ b/Source/cmCommonTargetGenerator.cxx @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -653,3 +654,34 @@ bool cmCommonTargetGenerator::HaveRequiredLanguages( }; return std::all_of(languagesNeeded.cbegin(), languagesNeeded.cend(), unary); } + +void cmCommonTargetGenerator::ComputeRustFlagsForObjects( + std::string& linkCrates, std::string& nativeObjects, + std::vector const& objects) +{ + std::stringstream rlibsArgs; + std::stringstream objectsArgs; + auto const processObject = [&](std::string const& obj) { + cm::filesystem::path const objPath(obj); + if (objPath.extension() == ".rlib") { + // Drop the "lib..." prefix and the ".rs" suffix. The prefix is required + // by Rust on the crate rlib file, but is hidden from the user when using + // the crate from Rust source code, so we drop it to be consistent with + // common usage in Rust. + std::string objStem = objPath.stem().string(); + objStem = objStem.substr(3, objStem.length() - 6); + rlibsArgs << " --extern=" << objStem << "=" + << this->LocalCommonGenerator->ConvertToOutputFormat( + obj, cmOutputConverter::SHELL); + } else { + objectsArgs << " -Clink-arg=" + << this->LocalCommonGenerator->ConvertToOutputFormat( + obj, cmOutputConverter::SHELL); + } + }; + for (auto const& obj : objects) { + processObject(obj); + } + linkCrates += rlibsArgs.str(); + nativeObjects += objectsArgs.str(); +} diff --git a/Source/cmCommonTargetGenerator.h b/Source/cmCommonTargetGenerator.h index b97b947c47..d8fd69499d 100644 --- a/Source/cmCommonTargetGenerator.h +++ b/Source/cmCommonTargetGenerator.h @@ -90,6 +90,10 @@ protected: bool HaveRequiredLanguages(std::vector const& sources, std::set& languagesNeeded) const; + void ComputeRustFlagsForObjects(std::string& linkCrates, + std::string& nativeObjects, + std::vector const& objects); + private: using ByLanguageMap = std::map; struct ByConfig diff --git a/Source/cmExperimental.cxx b/Source/cmExperimental.cxx index 9c1f2f1f55..fa565315fa 100644 --- a/Source/cmExperimental.cxx +++ b/Source/cmExperimental.cxx @@ -64,7 +64,7 @@ cmExperimental::FeatureData const LookupTable[] = { cmExperimental::TryCompileCondition::Never }, // Rust support { "Rust", - "efaed83b-d73a-48af-999a-bd0a6172c313", + "b6fdddce-bf66-41a5-bc5f-077f6fa4d2a1", "CMAKE_EXPERIMENTAL_RUST", "CMake's support for the Rust programming language is experimental. " "It is meant only for experimentation and feedback to CMake developers.", diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index d3d589aaa7..0312da44dc 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -1013,6 +1013,18 @@ void cmGeneratorTarget::GetRustMainCrateRoot( IMPLEMENT_VISIT(SourceKindRustMainCrateRoot); } +cmSourceFile const* cmGeneratorTarget::GetRustMainCrateRoot( + std::string const& config) const +{ + std::vector files; + GetRustMainCrateRoot(files, config); + if (files.empty()) { + return nullptr; + } + assert(files.size() == 1); + return files[0]; +} + std::set const& cmGeneratorTarget::GetUtilityItems() const { if (!this->UtilityItemsDone) { diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 23f59bc015..2889b39c46 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -228,6 +228,8 @@ public: void GetRustMainCrateRoot(std::vector&, std::string const& config) const; + cmSourceFile const* GetRustMainCrateRoot(std::string const& config) const; + std::set const& GetUtilityItems() const; void ComputeObjectMapping(); diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index 453fbe2847..77c040fef5 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -598,6 +598,16 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) vars.Launcher = linkerLauncher.c_str(); } + std::string rustMainCrateRootPath; + std::string rustLinkCrates; + std::string rustNativeObjects; + if (CreateRustLinkArguments(linkLanguage, rustMainCrateRootPath, + rustLinkCrates, rustNativeObjects)) { + vars.RustMainCrateRoot = rustMainCrateRootPath.c_str(); + vars.RustLinkCrates = rustLinkCrates.c_str(); + vars.RustNativeObjects = rustNativeObjects.c_str(); + } + if (this->UseLWYU) { cmValue lwyuCheck = this->Makefile->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK"); diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index c046e11381..d04051c632 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -837,6 +837,16 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( vars.Manifests = manifests.c_str(); vars.Config = this->GetConfigName().c_str(); + std::string rustMainCrateRootPath; + std::string rustLinkCrates; + std::string rustNativeObjects; + if (CreateRustLinkArguments(linkLanguage, rustMainCrateRootPath, + rustLinkCrates, rustNativeObjects)) { + vars.RustMainCrateRoot = rustMainCrateRootPath.c_str(); + vars.RustLinkCrates = rustLinkCrates.c_str(); + vars.RustNativeObjects = rustNativeObjects.c_str(); + } + // Compute the directory portion of the install_name setting. std::string install_name_dir; if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY) { diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 59c0519201..22600419bb 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -976,6 +976,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( vars.Flags = flags.c_str(); vars.ISPCHeader = ispcHeaderForShell.c_str(); vars.Config = this->GetConfigName().c_str(); + vars.RustEmit = source.GetRustEmitProperty()->c_str(); std::string definesString = cmStrCat("$(", lang, "_DEFINES)"); @@ -2053,6 +2054,12 @@ void cmMakefileTargetGenerator::AppendObjectDepends( // Add dependencies on the external object files. cm::append(depends, this->ExternalObjects); + // Add dependency on the Rust main crate root file. + if (cmSourceFile const* mainCrateRoot = + this->GeneratorTarget->GetRustMainCrateRoot(this->GetConfigName())) { + depends.push_back(mainCrateRoot->GetFullPath()); + } + // Add a dependency on the rule file itself. this->LocalGenerator->AppendRuleDepend(depends, this->BuildFileNameFull.c_str()); @@ -2326,6 +2333,33 @@ void cmMakefileTargetGenerator::CreateObjectLists( } } +bool cmMakefileTargetGenerator::CreateRustLinkArguments( + std::string const& linkLanguage, std::string& rustMainCrateRootPath, + std::string& rustLinkCrates, std::string& rustNativeObjects) +{ + if (linkLanguage == "Rust") { + this->ComputeRustFlagsForObjects(rustLinkCrates, rustNativeObjects, + this->Objects); + this->ComputeRustFlagsForObjects(rustLinkCrates, rustNativeObjects, + this->ExternalObjects); + + cmSourceFile const* mainCrateRoot = + this->GeneratorTarget->GetRustMainCrateRoot(this->GetConfigName()); + if (!mainCrateRoot) { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, + "Target " + + this->GeneratorTarget->GetName() + + " has no main crate root."); + return false; + } + rustMainCrateRootPath = mainCrateRoot->GetFullPath(); + rustMainCrateRootPath = this->LocalGenerator->ConvertToOutputFormat( + rustMainCrateRootPath, cmOutputConverter::SHELL); + return true; + } + return false; +} + void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags, std::string const& lang, std::string const& /*config*/) diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h index bcb5f26d8f..ce78230344 100644 --- a/Source/cmMakefileTargetGenerator.h +++ b/Source/cmMakefileTargetGenerator.h @@ -193,6 +193,11 @@ protected: bool useWatcomQuote, std::string const& linkLanguage, ResponseFlagFor responseMode = ResponseFlagFor::Link); + bool CreateRustLinkArguments(std::string const& linkLanguage, + std::string& rustMainCrateRootPath, + std::string& rustLinkCrates, + std::string& rustNativeObjects); + /** Add commands for generate def files */ void GenDefFile(std::vector& real_link_commands); diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 7ff9bb2304..3fbc1786c3 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -1299,40 +1298,21 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( linkBuild.ExplicitDeps = this->GetObjects(config); // First we handle Rust rlib and normal native objects. - std::stringstream rlibs; - std::stringstream objects; - for (auto const& obj : linkBuild.ExplicitDeps) { - cm::filesystem::path const objPath(obj); - if (objPath.extension() == ".rlib") { - // Drop the "lib..." prefix and the ".rs" suffix. The prefix is - // required by Rust on the crate rlib file, but is hidden from the user - // when using the crate from Rust source code, so we drop it to be - // consistent with common usage in Rust. - std::string objStem = objPath.stem().string(); - objStem = objStem.substr(3, objStem.length() - 6); - rlibs << " --extern=" << objStem << "=" - << lg->ConvertToOutputFormat(obj, cmOutputConverter::SHELL); - } else { - objects << " -Clink-arg=" - << lg->ConvertToOutputFormat(obj, cmOutputConverter::SHELL); - } - } - vars["RUST_LINK_CRATES"] = rlibs.str(); - vars["RUST_NATIVE_OBJECTS"] = objects.str(); + this->ComputeRustFlagsForObjects(vars["RUST_LINK_CRATES"], + vars["RUST_NATIVE_OBJECTS"], + linkBuild.ExplicitDeps); // Then, we handle the main crate root that is build as part of the link // step. - std::vector mainCrateRoot; - gt->GetRustMainCrateRoot(mainCrateRoot, config); - if (mainCrateRoot.size() != 1) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "Target " + gt->GetName() + - " has none or more than one main crate root."); + cmSourceFile const* mainCrateRoot = gt->GetRustMainCrateRoot(config); + if (!mainCrateRoot) { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, + "Target " + gt->GetName() + + " has no main crate root."); return; } std::string mainCrateRootPath = - this->GetCompiledSourceNinjaPath(mainCrateRoot[0]); + this->GetCompiledSourceNinjaPath(mainCrateRoot); linkBuild.ExplicitDeps.emplace_back(mainCrateRootPath); mainCrateRootPath = lg->ConvertToOutputFormat(mainCrateRootPath, cmOutputConverter::SHELL); diff --git a/Tests/RunCMake/Rust/Editions-build-stdout.txt b/Tests/RunCMake/Rust/Editions-build-Ninja-stdout.txt similarity index 100% rename from Tests/RunCMake/Rust/Editions-build-stdout.txt rename to Tests/RunCMake/Rust/Editions-build-Ninja-stdout.txt diff --git a/Tests/RunCMake/Rust/Editions-build-UnixMakefiles-stdout.txt b/Tests/RunCMake/Rust/Editions-build-UnixMakefiles-stdout.txt new file mode 100644 index 0000000000..a946b1e8c0 --- /dev/null +++ b/Tests/RunCMake/Rust/Editions-build-UnixMakefiles-stdout.txt @@ -0,0 +1,10 @@ +\[ 20%\] Building Rust object CMakeFiles[\\/]Edition.20[0-9][0-9]\.dir[\\/]libedition_20[0-9][0-9]\.rs\.rlib +\[ 20%\] Built target Edition\.20[0-9][0-9] +\[ 40%\] Building Rust object CMakeFiles[\\/]Edition.20[0-9][0-9]\.dir[\\/]libedition_20[0-9][0-9]\.rs\.rlib +\[ 40%\] Built target Edition\.20[0-9][0-9] +\[ 60%\] Building Rust object CMakeFiles[\\/]Edition.20[0-9][0-9]\.dir[\\/]libedition_20[0-9][0-9]\.rs\.rlib +\[ 60%\] Built target Edition\.20[0-9][0-9] +\[ 80%\] Building Rust object CMakeFiles[\\/]Edition.20[0-9][0-9]\.dir[\\/]libedition_20[0-9][0-9]\.rs\.rlib +\[ 80%\] Built target Edition\.20[0-9][0-9] +\[100%\] Linking Rust executable Editions +\[100%\] Built target Editions diff --git a/Tests/RunCMake/Rust/Editions.cmake b/Tests/RunCMake/Rust/Editions.cmake index 0520e06b3b..95b408ffbe 100644 --- a/Tests/RunCMake/Rust/Editions.cmake +++ b/Tests/RunCMake/Rust/Editions.cmake @@ -1,4 +1,4 @@ -set(CMAKE_EXPERIMENTAL_RUST "efaed83b-d73a-48af-999a-bd0a6172c313") +set(CMAKE_EXPERIMENTAL_RUST "b6fdddce-bf66-41a5-bc5f-077f6fa4d2a1") enable_language(Rust) # No Edition set, rustc defaults to 2015. diff --git a/Tests/RunCMake/Rust/Enable.cmake b/Tests/RunCMake/Rust/Enable.cmake index ec06ab645c..9bdff73128 100644 --- a/Tests/RunCMake/Rust/Enable.cmake +++ b/Tests/RunCMake/Rust/Enable.cmake @@ -1,3 +1,3 @@ -set(CMAKE_EXPERIMENTAL_RUST "efaed83b-d73a-48af-999a-bd0a6172c313") +set(CMAKE_EXPERIMENTAL_RUST "b6fdddce-bf66-41a5-bc5f-077f6fa4d2a1") enable_language(Rust) message(STATUS "CMAKE_Rust_COMPILER='${CMAKE_Rust_COMPILER}'") diff --git a/Tests/RunCMake/Rust/RunCMakeTest.cmake b/Tests/RunCMake/Rust/RunCMakeTest.cmake index c3d0207b04..ae6f60206c 100644 --- a/Tests/RunCMake/Rust/RunCMakeTest.cmake +++ b/Tests/RunCMake/Rust/RunCMakeTest.cmake @@ -7,5 +7,11 @@ 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) + if (RunCMake_GENERATOR MATCHES "Ninja") + run_cmake_command(Editions-build-Ninja ${CMAKE_COMMAND} --build . --config Debug) + elseif(RunCMake_GENERATOR MATCHES "Makefiles") + run_cmake_command(Editions-build-UnixMakefiles ${CMAKE_COMMAND} --build . --config Debug) + else() + message(FATAL_ERROR "Unsupported ${RunCMake_GENERATOR} generator!") + endif() endblock() diff --git a/Tests/RustMix/CMakeLists.txt b/Tests/RustMix/CMakeLists.txt index bf2f2c88aa..029f534913 100644 --- a/Tests/RustMix/CMakeLists.txt +++ b/Tests/RustMix/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 4.2) -set(CMAKE_EXPERIMENTAL_RUST "efaed83b-d73a-48af-999a-bd0a6172c313") +set(CMAKE_EXPERIMENTAL_RUST "b6fdddce-bf66-41a5-bc5f-077f6fa4d2a1") project(RustMix LANGUAGES C CXX Rust) diff --git a/Tests/RustOnly/CMakeLists.txt b/Tests/RustOnly/CMakeLists.txt index 29b4a5b68e..72b6efae79 100644 --- a/Tests/RustOnly/CMakeLists.txt +++ b/Tests/RustOnly/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 4.2) -set(CMAKE_EXPERIMENTAL_RUST "efaed83b-d73a-48af-999a-bd0a6172c313") +set(CMAKE_EXPERIMENTAL_RUST "b6fdddce-bf66-41a5-bc5f-077f6fa4d2a1") project(RustOnly LANGUAGES Rust) diff --git a/Tests/RustPie/CMakeLists.txt b/Tests/RustPie/CMakeLists.txt index 0c3b657fcf..9adc23c019 100644 --- a/Tests/RustPie/CMakeLists.txt +++ b/Tests/RustPie/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 4.2) -set(CMAKE_EXPERIMENTAL_RUST "efaed83b-d73a-48af-999a-bd0a6172c313") +set(CMAKE_EXPERIMENTAL_RUST "b6fdddce-bf66-41a5-bc5f-077f6fa4d2a1") function(setup PREFIX) add_library(${PREFIX}_static STATIC ../static.rs)