mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-05 23:30:25 +00:00
Rust: Add support for compiling Rust with Makefile generator
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake)
|
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")
|
message(FATAL_ERROR "Rust language not supported by \"${CMAKE_GENERATOR}\" generator")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include <cm/filesystem>
|
||||||
#include <cm/string_view>
|
#include <cm/string_view>
|
||||||
#include <cmext/string_view>
|
#include <cmext/string_view>
|
||||||
|
|
||||||
@@ -653,3 +654,34 @@ bool cmCommonTargetGenerator::HaveRequiredLanguages(
|
|||||||
};
|
};
|
||||||
return std::all_of(languagesNeeded.cbegin(), languagesNeeded.cend(), unary);
|
return std::all_of(languagesNeeded.cbegin(), languagesNeeded.cend(), unary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cmCommonTargetGenerator::ComputeRustFlagsForObjects(
|
||||||
|
std::string& linkCrates, std::string& nativeObjects,
|
||||||
|
std::vector<std::string> 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();
|
||||||
|
}
|
||||||
|
|||||||
@@ -90,6 +90,10 @@ protected:
|
|||||||
bool HaveRequiredLanguages(std::vector<cmSourceFile const*> const& sources,
|
bool HaveRequiredLanguages(std::vector<cmSourceFile const*> const& sources,
|
||||||
std::set<std::string>& languagesNeeded) const;
|
std::set<std::string>& languagesNeeded) const;
|
||||||
|
|
||||||
|
void ComputeRustFlagsForObjects(std::string& linkCrates,
|
||||||
|
std::string& nativeObjects,
|
||||||
|
std::vector<std::string> const& objects);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using ByLanguageMap = std::map<std::string, std::string>;
|
using ByLanguageMap = std::map<std::string, std::string>;
|
||||||
struct ByConfig
|
struct ByConfig
|
||||||
|
|||||||
@@ -1013,6 +1013,18 @@ void cmGeneratorTarget::GetRustMainCrateRoot(
|
|||||||
IMPLEMENT_VISIT(SourceKindRustMainCrateRoot);
|
IMPLEMENT_VISIT(SourceKindRustMainCrateRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmSourceFile const* cmGeneratorTarget::GetRustMainCrateRoot(
|
||||||
|
std::string const& config) const
|
||||||
|
{
|
||||||
|
std::vector<cmSourceFile const*> files;
|
||||||
|
GetRustMainCrateRoot(files, config);
|
||||||
|
if (files.empty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
assert(files.size() == 1);
|
||||||
|
return files[0];
|
||||||
|
}
|
||||||
|
|
||||||
std::set<cmLinkItem> const& cmGeneratorTarget::GetUtilityItems() const
|
std::set<cmLinkItem> const& cmGeneratorTarget::GetUtilityItems() const
|
||||||
{
|
{
|
||||||
if (!this->UtilityItemsDone) {
|
if (!this->UtilityItemsDone) {
|
||||||
|
|||||||
@@ -228,6 +228,8 @@ public:
|
|||||||
void GetRustMainCrateRoot(std::vector<cmSourceFile const*>&,
|
void GetRustMainCrateRoot(std::vector<cmSourceFile const*>&,
|
||||||
std::string const& config) const;
|
std::string const& config) const;
|
||||||
|
|
||||||
|
cmSourceFile const* GetRustMainCrateRoot(std::string const& config) const;
|
||||||
|
|
||||||
std::set<cmLinkItem> const& GetUtilityItems() const;
|
std::set<cmLinkItem> const& GetUtilityItems() const;
|
||||||
|
|
||||||
void ComputeObjectMapping();
|
void ComputeObjectMapping();
|
||||||
|
|||||||
@@ -598,6 +598,16 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
|
|||||||
vars.Launcher = linkerLauncher.c_str();
|
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) {
|
if (this->UseLWYU) {
|
||||||
cmValue lwyuCheck =
|
cmValue lwyuCheck =
|
||||||
this->Makefile->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK");
|
this->Makefile->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK");
|
||||||
|
|||||||
@@ -837,6 +837,16 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
|
|||||||
vars.Manifests = manifests.c_str();
|
vars.Manifests = manifests.c_str();
|
||||||
vars.Config = this->GetConfigName().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.
|
// Compute the directory portion of the install_name setting.
|
||||||
std::string install_name_dir;
|
std::string install_name_dir;
|
||||||
if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY) {
|
if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY) {
|
||||||
|
|||||||
@@ -976,6 +976,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
|
|||||||
vars.Flags = flags.c_str();
|
vars.Flags = flags.c_str();
|
||||||
vars.ISPCHeader = ispcHeaderForShell.c_str();
|
vars.ISPCHeader = ispcHeaderForShell.c_str();
|
||||||
vars.Config = this->GetConfigName().c_str();
|
vars.Config = this->GetConfigName().c_str();
|
||||||
|
vars.RustEmit = source.GetRustEmitProperty()->c_str();
|
||||||
|
|
||||||
std::string definesString = cmStrCat("$(", lang, "_DEFINES)");
|
std::string definesString = cmStrCat("$(", lang, "_DEFINES)");
|
||||||
|
|
||||||
@@ -2053,6 +2054,12 @@ void cmMakefileTargetGenerator::AppendObjectDepends(
|
|||||||
// Add dependencies on the external object files.
|
// Add dependencies on the external object files.
|
||||||
cm::append(depends, this->ExternalObjects);
|
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.
|
// Add a dependency on the rule file itself.
|
||||||
this->LocalGenerator->AppendRuleDepend(depends,
|
this->LocalGenerator->AppendRuleDepend(depends,
|
||||||
this->BuildFileNameFull.c_str());
|
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,
|
void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags,
|
||||||
std::string const& lang,
|
std::string const& lang,
|
||||||
std::string const& /*config*/)
|
std::string const& /*config*/)
|
||||||
|
|||||||
@@ -193,6 +193,11 @@ protected:
|
|||||||
bool useWatcomQuote, std::string const& linkLanguage,
|
bool useWatcomQuote, std::string const& linkLanguage,
|
||||||
ResponseFlagFor responseMode = ResponseFlagFor::Link);
|
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 */
|
/** Add commands for generate def files */
|
||||||
void GenDefFile(std::vector<std::string>& real_link_commands);
|
void GenDefFile(std::vector<std::string>& real_link_commands);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <cm/filesystem>
|
|
||||||
#include <cm/memory>
|
#include <cm/memory>
|
||||||
#include <cm/optional>
|
#include <cm/optional>
|
||||||
#include <cm/vector>
|
#include <cm/vector>
|
||||||
@@ -1299,40 +1298,21 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
|
|||||||
linkBuild.ExplicitDeps = this->GetObjects(config);
|
linkBuild.ExplicitDeps = this->GetObjects(config);
|
||||||
|
|
||||||
// First we handle Rust rlib and normal native objects.
|
// First we handle Rust rlib and normal native objects.
|
||||||
std::stringstream rlibs;
|
this->ComputeRustFlagsForObjects(vars["RUST_LINK_CRATES"],
|
||||||
std::stringstream objects;
|
vars["RUST_NATIVE_OBJECTS"],
|
||||||
for (auto const& obj : linkBuild.ExplicitDeps) {
|
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();
|
|
||||||
|
|
||||||
// Then, we handle the main crate root that is build as part of the link
|
// Then, we handle the main crate root that is build as part of the link
|
||||||
// step.
|
// step.
|
||||||
std::vector<cmSourceFile const*> mainCrateRoot;
|
cmSourceFile const* mainCrateRoot = gt->GetRustMainCrateRoot(config);
|
||||||
gt->GetRustMainCrateRoot(mainCrateRoot, config);
|
if (!mainCrateRoot) {
|
||||||
if (mainCrateRoot.size() != 1) {
|
this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
|
||||||
this->Makefile->IssueMessage(
|
"Target " + gt->GetName() +
|
||||||
MessageType::FATAL_ERROR,
|
" has no main crate root.");
|
||||||
"Target " + gt->GetName() +
|
|
||||||
" has none or more than one main crate root.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::string mainCrateRootPath =
|
std::string mainCrateRootPath =
|
||||||
this->GetCompiledSourceNinjaPath(mainCrateRoot[0]);
|
this->GetCompiledSourceNinjaPath(mainCrateRoot);
|
||||||
linkBuild.ExplicitDeps.emplace_back(mainCrateRootPath);
|
linkBuild.ExplicitDeps.emplace_back(mainCrateRootPath);
|
||||||
mainCrateRootPath =
|
mainCrateRootPath =
|
||||||
lg->ConvertToOutputFormat(mainCrateRootPath, cmOutputConverter::SHELL);
|
lg->ConvertToOutputFormat(mainCrateRootPath, cmOutputConverter::SHELL);
|
||||||
|
|||||||
10
Tests/RunCMake/Rust/Editions-build-UnixMakefiles-stdout.txt
Normal file
10
Tests/RunCMake/Rust/Editions-build-UnixMakefiles-stdout.txt
Normal file
@@ -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
|
||||||
@@ -7,5 +7,11 @@ block()
|
|||||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Editions-build)
|
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Editions-build)
|
||||||
run_cmake(Editions)
|
run_cmake(Editions)
|
||||||
set(RunCMake_TEST_NO_CLEAN 1)
|
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()
|
endblock()
|
||||||
|
|||||||
Reference in New Issue
Block a user