mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-05 07:10:26 +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)
|
||||
|
||||
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()
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include <cm/filesystem>
|
||||
#include <cm/string_view>
|
||||
#include <cmext/string_view>
|
||||
|
||||
@@ -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<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,
|
||||
std::set<std::string>& languagesNeeded) const;
|
||||
|
||||
void ComputeRustFlagsForObjects(std::string& linkCrates,
|
||||
std::string& nativeObjects,
|
||||
std::vector<std::string> const& objects);
|
||||
|
||||
private:
|
||||
using ByLanguageMap = std::map<std::string, std::string>;
|
||||
struct ByConfig
|
||||
|
||||
@@ -1013,6 +1013,18 @@ void cmGeneratorTarget::GetRustMainCrateRoot(
|
||||
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
|
||||
{
|
||||
if (!this->UtilityItemsDone) {
|
||||
|
||||
@@ -228,6 +228,8 @@ public:
|
||||
void GetRustMainCrateRoot(std::vector<cmSourceFile const*>&,
|
||||
std::string const& config) const;
|
||||
|
||||
cmSourceFile const* GetRustMainCrateRoot(std::string const& config) const;
|
||||
|
||||
std::set<cmLinkItem> const& GetUtilityItems() const;
|
||||
|
||||
void ComputeObjectMapping();
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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*/)
|
||||
|
||||
@@ -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<std::string>& real_link_commands);
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include <cm/filesystem>
|
||||
#include <cm/memory>
|
||||
#include <cm/optional>
|
||||
#include <cm/vector>
|
||||
@@ -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<cmSourceFile const*> 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);
|
||||
|
||||
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)
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user