Merge topic 'rust-rlib'

e6a544836b Rust: Update experimental UUID
881faf67e5 Rust: Switch to building .rs files to .rlib instead of native object files
942ddcdd36 Rust: Add support for position independent code

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !11686
This commit is contained in:
Brad King
2026-03-21 10:18:21 -04:00
committed by Kitware Robot
53 changed files with 559 additions and 102 deletions
+1 -1
View File
@@ -127,7 +127,7 @@ Rust Support
In order to activate support for Rust, set
* variable ``CMAKE_EXPERIMENTAL_RUST`` to
* value ``3cc9b32c-47d3-4056-8953-d74e69fc0d6c``.
* value ``6b6e613b-f6cb-402d-8aea-59034fa8c65b``.
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
+1
View File
@@ -608,6 +608,7 @@ Properties on Source Files
/prop_sf/OBJECT_DEPENDS
/prop_sf/OBJECT_NAME
/prop_sf/OBJECT_OUTPUTS
/prop_sf/Rust_EMIT
/prop_sf/SKIP_AUTOGEN
/prop_sf/SKIP_AUTOMOC
/prop_sf/SKIP_AUTORCC
+39
View File
@@ -0,0 +1,39 @@
Rust_EMIT
---------
.. versionadded:: 4.4
.. note::
Experimental. Gated by ``CMAKE_EXPERIMENTAL_RUST``.
This property controls the type of output generated by the Rust compiler. Can
be one of several values:
``link``
This is the default if the property is not set. The crate will be compiled
into an `rlib` file, e.g.: ``libfile.rs.rlib``
.. note::
CMake will automatically prefix the generated ``rlib`` file
with ``lib`` as the Rust compiler always requires such a
prefix for external crates.
``obj``
Generate a native object file, e.g.: ``file.rs.o``
``asm``
Generate an assembly file, e.g.: ``file.rs.s``
.. note::
The ``obj`` and ``asm`` output types are known to have the following
limitations:
* When enabled, the Rust compiler disables multiple codegen units and
ThinLTO. Depending on the situation, this can affect the generated code,
optimizations. It also reduce the internal parallelism inside and the
compiler and can reduce the effectiveness of incremental rebuilds.
* The generated ``obj`` files cannot be used as external crates, so other
Rust code can only use C-style API from them.
* The generated ``obj`` files cannot be linked as-is with native linkers,
additional crates from the Rust toolchain needs to linked too. The actual
crates to link depend on the code and compiler options.
+5
View File
@@ -3,3 +3,8 @@ set(CMAKE_Rust_COMPILER_ENV_VAR "RUSTC")
set(CMAKE_Rust_SOURCE_FILE_EXTENSIONS rs)
set(CMAKE_Rust_COMPILER_LOADED 1)
set(CMAKE_Rust_COMPILER_WORKS @CMAKE_Rust_COMPILER_WORKS@)
# Make sure CMake prefers to link with Rust when Rust source files are present
set(CMAKE_Rust_LINKER_PREFERENCE 60)
# Once compiled by Rust, static libraries can be linked by anyone.
set(CMAKE_Rust_LINKER_PREFERENCE_PROPAGATES 0)
+61 -13
View File
@@ -3,11 +3,20 @@
include(CMakeLanguageInformation)
if(UNIX)
set(CMAKE_Rust_OUTPUT_EXTENSION .o)
else()
set(CMAKE_Rust_OUTPUT_EXTENSION .obj)
endif()
set(CMAKE_Rust_OUTPUT_EXTENSION .rlib)
# Other values are supported to generate various outputs (LLVM bitcode, or IR,
# crate metadata, Rust MIR). However, CMake cannot do anything with those
# outputs, so we list output which can be reused in later stages of the build.
# See: https://doc.rust-lang.org/rustc/command-line-arguments.html#--emit-specifies-the-types-of-output-files-to-generate
set(CMAKE_Rust_EMIT_VALUES link obj asm)
# The output extension for each supported emit value.
set(CMAKE_Rust_EMIT_link_OUTPUT_EXTENSION .rlib)
set(CMAKE_Rust_EMIT_asm_OUTPUT_EXTENSION .s)
# Might be switched to .obj on Windows when using MSVC target triple, see:
# https://github.com/rust-lang/rust/issues/37207
set(CMAKE_Rust_EMIT_obj_OUTPUT_EXTENSION .o)
set(CMAKE_Rust_LIBRARY_PATH_FLAG "-L ")
set(CMAKE_Rust_LINK_LIBRARY_FILE_FLAG "-C link-arg=")
@@ -19,23 +28,62 @@ set(CMAKE_Rust_FLAGS_RELEASE_INIT "-O")
set(CMAKE_Rust_FLAGS_RELWITHDEBINFO_INIT "-O -g")
set(CMAKE_Rust_FLAGS_MINSIZEREL_INIT "-C opt-level=z")
block(
PROPAGATE
CMAKE_Rust_LINK_PIE_SUPPORTED
CMAKE_Rust_LINK_NO_PIE_SUPPORTED
CMAKE_Rust_COMPILE_OPTIONS_PIE
CMAKE_Rust_COMPILE_OPTIONS_PIC
CMAKE_Rust_LINK_OPTIONS_PIE
CMAKE_Rust_LINK_OPTIONS_NO_PIE
)
execute_process(
COMMAND "${CMAKE_Rust_COMPILER}" --print relocation-models
OUTPUT_VARIABLE RUSTC_OUTPUT
ERROR_VARIABLE RUSTC_ERROR
RESULT_VARIABLE RUSTC_EXITCODE
)
if(RUSTC_EXITCODE EQUAL "0")
string(REPLACE "\n" ";" RUSTC_OUTPUT_LINES "${RUSTC_OUTPUT}")
list(TRANSFORM RUSTC_OUTPUT_LINES STRIP)
if("pic" IN_LIST RUSTC_OUTPUT_LINES)
set(CMAKE_Rust_COMPILE_OPTIONS_PIC -C relocation-model=pic)
endif()
if("pie" IN_LIST RUSTC_OUTPUT_LINES)
set(CMAKE_Rust_LINK_PIE_SUPPORTED TRUE)
set(CMAKE_Rust_COMPILE_OPTIONS_PIE -C relocation-model=pie)
set(CMAKE_Rust_LINK_OPTIONS_PIE -C relocation-model=pie)
else()
set(CMAKE_Rust_LINK_PIE_SUPPORTED FALSE)
endif()
if("static" IN_LIST RUSTC_OUTPUT_LINES)
set(CMAKE_Rust_LINK_NO_PIE_SUPPORTED TRUE)
set(CMAKE_Rust_LINK_OPTIONS_NO_PIE -C relocation-model=static)
else()
set(CMAKE_Rust_LINK_NO_PIE_SUPPORTED FALSE)
endif()
else()
string(REPLACE "\n" "\n " RUSTC_ERROR " ${RUSTC_ERROR}")
message(FATAL_ERROR "Failed to check PIC/PIE support in rustc:\n${RUSTC_ERROR}")
endif()
endblock()
cmake_initialize_per_config_variable(CMAKE_Rust_FLAGS "Flags used by the Rust compiler")
if(NOT CMAKE_Rust_COMPILE_OBJECT)
set(CMAKE_Rust_COMPILE_OBJECT "<CMAKE_Rust_COMPILER> --crate-type=rlib <FLAGS> --emit=<RUST_EMIT>,dep-info=<DEP_FILE> -o <OBJECT> <SOURCE>")
endif()
if(NOT CMAKE_Rust_CREATE_STATIC_LIBRARY)
set(CMAKE_Rust_CREATE_STATIC_LIBRARY "${CMAKE_Rust_COMPILER} <LANGUAGE_COMPILE_FLAGS> --crate-type=staticlib <RUST_SOURCES> -o <TARGET> -C link-args=\"<RUST_OBJECT_DEPS>\"")
set(CMAKE_Rust_CREATE_STATIC_LIBRARY "${CMAKE_Rust_COMPILER} <LANGUAGE_COMPILE_FLAGS> --crate-type=staticlib --emit=link,dep-info=<DEP_FILE> <RUST_MAIN_CRATE_ROOT> -o <TARGET> <RUST_LINK_CRATES> <RUST_NATIVE_OBJECTS>")
endif()
if(NOT CMAKE_Rust_CREATE_SHARED_LIBRARY)
set(CMAKE_Rust_CREATE_SHARED_LIBRARY "${CMAKE_Rust_COMPILER} <LANGUAGE_COMPILE_FLAGS> --crate-type=cdylib <RUST_SOURCES> -o <TARGET> <LINK_FLAGS> <LINK_LIBRARIES> -C link-args=\"<RUST_OBJECT_DEPS>\"")
endif()
# Deadcode warnings are not useful when generating object files.
if(NOT CMAKE_Rust_COMPILE_OBJECT)
set(CMAKE_Rust_COMPILE_OBJECT "${CMAKE_Rust_COMPILER} <FLAGS> -A dead_code --crate-type=lib --emit=obj=<OBJECT>,dep-info=<DEP_FILE> <SOURCE>")
set(CMAKE_Rust_CREATE_SHARED_LIBRARY "${CMAKE_Rust_COMPILER} <LANGUAGE_COMPILE_FLAGS> --crate-type=cdylib --emit=link,dep-info=<DEP_FILE> <RUST_MAIN_CRATE_ROOT> -o <TARGET> <RUST_LINK_CRATES> <RUST_NATIVE_OBJECTS> <LINK_FLAGS> <LINK_LIBRARIES>")
endif()
if(NOT CMAKE_Rust_LINK_EXECUTABLE)
set(CMAKE_Rust_LINK_EXECUTABLE "${CMAKE_Rust_COMPILER} <FLAGS> --crate-type=bin <RUST_SOURCES> -o <TARGET> <LINK_FLAGS> <LINK_LIBRARIES> -C link-args=\"<RUST_OBJECT_DEPS>\"")
set(CMAKE_Rust_LINK_EXECUTABLE "${CMAKE_Rust_COMPILER} <FLAGS> --crate-type=bin --emit=link,dep-info=<DEP_FILE> <RUST_MAIN_CRATE_ROOT> -o <TARGET> <RUST_LINK_CRATES> <RUST_NATIVE_OBJECTS> <LINK_FLAGS> <LINK_LIBRARIES>")
endif()
set(CMAKE_Rust_INFORMATION_LOADED 1)
+1 -1
View File
@@ -64,7 +64,7 @@ cmExperimental::FeatureData const LookupTable[] = {
cmExperimental::TryCompileCondition::Never },
// Rust support
{ "Rust",
"3cc9b32c-47d3-4056-8953-d74e69fc0d6c",
"6b6e613b-f6cb-402d-8aea-59034fa8c65b",
"CMAKE_EXPERIMENTAL_RUST",
"CMake's support for the Rust programming language is experimental. "
"It is meant only for experimentation and feedback to CMake developers.",
+1
View File
@@ -1826,6 +1826,7 @@ Json::Value Target::DumpSource(cmGeneratorTarget::SourceAndKind const& sk,
case cmGeneratorTarget::SourceKindResx:
case cmGeneratorTarget::SourceKindXaml:
case cmGeneratorTarget::SourceKindUnityBatched:
case cmGeneratorTarget::SourceKindRustMainCrateRoot:
break;
}
+6
View File
@@ -1008,6 +1008,12 @@ void cmGeneratorTarget::GetManifests(std::vector<cmSourceFile const*>& data,
IMPLEMENT_VISIT(SourceKindManifest);
}
void cmGeneratorTarget::GetRustMainCrateRoot(
std::vector<cmSourceFile const*>& data, std::string const& config) const
{
IMPLEMENT_VISIT(SourceKindRustMainCrateRoot);
}
std::set<cmLinkItem> const& cmGeneratorTarget::GetUtilityItems() const
{
if (!this->UtilityItemsDone) {
+4
View File
@@ -145,6 +145,7 @@ public:
SourceKindCustomCommand,
SourceKindExternalObject,
SourceKindCxxModuleSource,
SourceKindRustMainCrateRoot,
SourceKindExtra,
SourceKindHeader,
SourceKindIDL,
@@ -224,6 +225,9 @@ public:
void GetManifests(std::vector<cmSourceFile const*>&,
std::string const& config) const;
void GetRustMainCrateRoot(std::vector<cmSourceFile const*>&,
std::string const& config) const;
std::set<cmLinkItem> const& GetUtilityItems() const;
void ComputeObjectMapping();
+28 -1
View File
@@ -347,6 +347,12 @@ void cmGeneratorTarget::ComputeKindedSources(KindedSources& files,
cmsys::RegularExpression header_regex(CM_HEADER_REGEX);
std::vector<cmSourceFile*> badObjLib;
cmValue const rustMainCrateRootProp =
this->GetProperty("Rust_MAIN_CRATE_ROOT");
cmSourceFile const* rustMainCrateRootSf = rustMainCrateRootProp
? this->Makefile->GetOrCreateSource(rustMainCrateRootProp)
: nullptr;
std::set<cmSourceFile*> emitted;
for (BT<std::string> const& s : srcs) {
// Create each source at most once.
@@ -379,7 +385,28 @@ void cmGeneratorTarget::ComputeKindedSources(KindedSources& files,
} else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
kind = SourceKindExternalObject;
} else if (!sf->GetOrDetermineLanguage().empty()) {
kind = SourceKindObjectSource;
if (sf->GetOrDetermineLanguage() == "Rust") {
// NOLINTNEXTLINE(bugprone-branch-clone)
if (this->Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
// There is no main crate root for object libraries.
kind = SourceKindObjectSource;
} else if (!rustMainCrateRootSf) {
// We do not have a main crate root source file, we use the first
// Rust source file for it.
rustMainCrateRootSf = sf;
kind = SourceKindRustMainCrateRoot;
} else if (rustMainCrateRootSf == sf) {
// Current source file is the main crate root defined in the target
// Rust_MAIN_CRATE_ROOT property.
kind = SourceKindRustMainCrateRoot;
} else {
// Any other Rust source file is treated as a normal object, but will
// be built into a .rlib. Maybe in the future this could be changed?
kind = SourceKindObjectSource;
}
} else {
kind = SourceKindObjectSource;
}
} else if (ext == "def") {
kind = SourceKindModuleDefinition;
if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) {
+30
View File
@@ -1086,6 +1086,13 @@ std::string cmGlobalGenerator::GetLanguageOutputExtension(
{
std::string const& lang = source.GetLanguage();
if (!lang.empty()) {
if (lang == "Rust") {
// Rust source file can be compiled into different type of outputs. So
// we need to change the extension based on the Rust_EMIT property.
if (cmValue const rustEmit = source.GetRustEmitProperty()) {
return this->GetRustEmitOutputExtension(rustEmit);
}
}
return this->GetLanguageOutputExtension(lang);
}
// if no language is found then check to see if it is already an
@@ -1110,6 +1117,16 @@ std::string cmGlobalGenerator::GetLanguageOutputExtension(
return "";
}
std::string cmGlobalGenerator::GetRustEmitOutputExtension(
std::string const& emitValue) const
{
auto const it = this->RustEmitToOutputExtension.find(emitValue);
if (it != this->RustEmitToOutputExtension.end()) {
return it->second;
}
return "";
}
cm::string_view cmGlobalGenerator::GetLanguageFromExtension(
cm::string_view ext) const
{
@@ -1210,6 +1227,19 @@ void cmGlobalGenerator::SetLanguageEnabledMaps(std::string const& l,
}
}
if (l == "Rust") {
std::string const emitValues =
mf->GetSafeDefinition("CMAKE_Rust_EMIT_VALUES");
cmList emitList{ emitValues };
for (std::string const& v : emitList) {
std::string emitOutputExtension =
cmStrCat("CMAKE_Rust_EMIT_", v, "_OUTPUT_EXTENSION");
if (cmValue outputExtension = mf->GetDefinition(emitOutputExtension)) {
this->RustEmitToOutputExtension[v] = outputExtension;
}
}
}
// The map was originally filled by SetLanguageEnabledFlag, but
// since then the compiler- and platform-specific files have been
// loaded which might have added more entries.
+3
View File
@@ -369,6 +369,8 @@ public:
std::string GetLanguageOutputExtension(cmSourceFile const&) const;
//! What is the object file extension for a given language?
std::string GetLanguageOutputExtension(std::string const& lang) const;
//! What is the object file extension for a given --emit option in Rust?
std::string GetRustEmitOutputExtension(std::string const& emitValue) const;
//! What is the configurations directory variable called?
virtual char const* GetCMakeCFGIntDir() const { return "."; }
@@ -875,6 +877,7 @@ private:
std::set<std::string> LanguagesInProgress;
std::map<std::string, std::string> OutputExtensions;
std::map<std::string, std::string> LanguageToOutputExtension;
std::map<std::string, std::string> RustEmitToOutputExtension;
#if __cplusplus >= 201402L || defined(_MSVC_LANG) && _MSVC_LANG >= 201402L
std::map<std::string, std::string, std::less<void>> ExtensionToLanguage;
#else
+20 -2
View File
@@ -3596,8 +3596,12 @@ void cmLocalGenerator::AppendPositionIndependentLinkerFlags(
}
char const* PICValue = target->GetLinkPIEProperty(config);
if (!PICValue) {
// POSITION_INDEPENDENT_CODE is not set
if (!PICValue && lang != "Rust") {
// POSITION_INDEPENDENT_CODE is not set, note that for Rust we do not
// return as the compiler tends to enable PIE all the time, which is the
// opposite of what C & C++ compilers do. So instead of letting the rust
// compiler decide on its own whether PIE should be enabled, we explicit
// set it.
return;
}
@@ -4674,6 +4678,20 @@ std::string cmLocalGenerator::GetObjectFileNameWithoutTarget(
*hasSourceExtension = keptSourceExtension;
}
if (source.GetLanguage() == "Rust") {
cmValue const rustEmit = source.GetRustEmitProperty();
// Rust requires any rlib to start with lib prefix on all platforms to
// allow linking to them as crate. So we enforce having lib prefix for rust
// "object" files.
if (rustEmit == "link") {
cmCMakePath objectPath(objectName);
std::string const objectFileName =
"lib" + objectPath.GetFileName().String();
objectPath.ReplaceFileName(objectFileName);
objectName = objectPath.String();
}
}
// Convert to a safe name.
return this->CreateSafeUniqueObjectFileName(objectName, dir_max);
}
+39 -25
View File
@@ -10,6 +10,7 @@
#include <unordered_set>
#include <utility>
#include <cm/filesystem>
#include <cm/memory>
#include <cm/optional>
#include <cm/vector>
@@ -479,8 +480,9 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(
}
if (this->TargetLinkLanguage(config) == "Rust") {
vars.RustSources = "$RUST_SOURCES";
vars.RustObjectDeps = "$RUST_OBJECT_DEPS";
vars.RustMainCrateRoot = "$RUST_MAIN_CRATE_ROOT";
vars.RustLinkCrates = "$RUST_LINK_CRATES";
vars.RustNativeObjects = "$RUST_NATIVE_OBJECTS";
}
std::string responseFlag;
@@ -1284,37 +1286,49 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
} else if (this->TargetLinkLanguage(config) == "Rust") {
// Use one-step build/link for Rust.
// Compute specific libraries to link with.
std::vector<cmSourceFile const*> sources;
gt->GetObjectSources(sources, config);
cmLocalGenerator const* lg = this->GetLocalGenerator();
std::string entry_obj;
for (auto const& source : sources) {
if (source->GetLanguage() == "Rust") {
if (vars.count("RUST_SOURCES") == 0) {
std::string const sourcePath =
this->GetCompiledSourceNinjaPath(source);
vars["RUST_SOURCES"] =
lg->ConvertToOutputFormat(sourcePath, cmOutputConverter::SHELL);
entry_obj = this->GetObjectFilePath(source, config);
} else {
assert(false && "Rust crate can only have 1 entry");
}
}
}
linkBuild.ExplicitDeps = this->GetObjects(config);
std::stringstream obj_deps;
// Do not try linking to object file created from the crate entry.
// First we handle Rust rlib and normal native objects.
std::stringstream rlibs;
std::stringstream objects;
for (auto const& obj : linkBuild.ExplicitDeps) {
if (obj != entry_obj) {
obj_deps << " "
<< lg->ConvertToOutputFormat(obj, cmOutputConverter::SHELL);
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();
vars["RUST_OBJECT_DEPS"] = obj_deps.str();
// 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.");
return;
}
std::string mainCrateRootPath =
this->GetCompiledSourceNinjaPath(mainCrateRoot[0]);
linkBuild.ExplicitDeps.emplace_back(mainCrateRootPath);
mainCrateRootPath =
lg->ConvertToOutputFormat(mainCrateRootPath, cmOutputConverter::SHELL);
vars["RUST_MAIN_CRATE_ROOT"] = mainCrateRootPath;
} else {
linkBuild.ExplicitDeps = this->GetObjects(config);
}
+6
View File
@@ -674,6 +674,7 @@ void cmNinjaTargetGenerator::WriteCompileRule(std::string const& lang,
vars.CudaCompileMode = "$CUDA_COMPILE_MODE";
vars.ISPCHeader = "$ISPC_HEADER_FILE";
vars.Config = "$CONFIG";
vars.RustEmit = "$RUST_EMIT";
cmMakefile* mf = this->GetMakefile();
@@ -1738,6 +1739,11 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
}
}
if (language == "Rust") {
cmValue const rustEmit = source->GetRustEmitProperty();
vars["RUST_EMIT"] = rustEmit;
}
if (language == "Swift") {
this->EmitSwiftDependencyInfo(source, config);
} else {
+16 -6
View File
@@ -156,14 +156,24 @@ std::string cmRulePlaceholderExpander::ExpandVariable(
return this->ReplaceValues->SwiftSources;
}
}
if (this->ReplaceValues->RustSources) {
if (variable == "RUST_SOURCES") {
return this->ReplaceValues->RustSources;
if (this->ReplaceValues->RustEmit) {
if (variable == "RUST_EMIT") {
return this->ReplaceValues->RustEmit;
}
}
if (this->ReplaceValues->RustObjectDeps) {
if (variable == "RUST_OBJECT_DEPS") {
return this->ReplaceValues->RustObjectDeps;
if (this->ReplaceValues->RustMainCrateRoot) {
if (variable == "RUST_MAIN_CRATE_ROOT") {
return this->ReplaceValues->RustMainCrateRoot;
}
}
if (this->ReplaceValues->RustLinkCrates) {
if (variable == "RUST_LINK_CRATES") {
return this->ReplaceValues->RustLinkCrates;
}
}
if (this->ReplaceValues->RustNativeObjects) {
if (variable == "RUST_NATIVE_OBJECTS") {
return this->ReplaceValues->RustNativeObjects;
}
}
if (this->ReplaceValues->TargetPDB) {
+4 -2
View File
@@ -79,8 +79,10 @@ public:
char const* SwiftModuleName = nullptr;
char const* SwiftOutputFileMapOption = nullptr;
char const* SwiftSources = nullptr;
char const* RustSources = nullptr;
char const* RustObjectDeps = nullptr;
char const* RustEmit = nullptr;
char const* RustMainCrateRoot = nullptr;
char const* RustLinkCrates = nullptr;
char const* RustNativeObjects = nullptr;
char const* ISPCHeader = nullptr;
char const* CudaCompileMode = nullptr;
char const* Fatbinary = nullptr;
+10
View File
@@ -503,3 +503,13 @@ void cmSourceFile::SetCustomCommand(std::unique_ptr<cmCustomCommand> cc)
{
this->CustomCommand = std::move(cc);
}
cmValue cmSourceFile::GetRustEmitProperty() const
{
static std::string const s_default = "link";
cmValue const value = this->GetProperty("Rust_EMIT");
if (!value || value->empty()) {
return cmValue(s_default);
}
return value;
}
+2
View File
@@ -184,6 +184,8 @@ public:
void SetObjectLibrary(std::string const& objlib);
std::string GetObjectLibrary() const;
cmValue GetRustEmitProperty() const;
private:
cmSourceFileLocation Location;
cmPropertyMap Properties;
+2
View File
@@ -380,6 +380,8 @@ TargetProperty const StaticTargetProperties[] = {
{ "Swift_LANGUAGE_VERSION"_s, IC::CanCompileSources },
{ "Swift_MODULE_DIRECTORY"_s, IC::CanCompileSources },
{ "Swift_COMPILATION_MODE"_s, IC::CanCompileSources },
// ---- Rust
{ "Rust_MAIN_CRATE_ROOT"_s, IC::CanCompileSources },
// ---- moc
{ "AUTOMOC"_s, IC::CanCompileSources },
{ "AUTOMOC_COMPILER_PREDEFINES"_s, IC::CanCompileSources },
@@ -2629,6 +2629,9 @@ void cmVisualStudio10TargetGenerator::WriteAllSources(Elem& e0)
case cmGeneratorTarget::SourceKindResx:
this->ResxObjs.push_back(si.Source);
break;
case cmGeneratorTarget::SourceKindRustMainCrateRoot:
tool = "None";
break;
case cmGeneratorTarget::SourceKindXaml:
this->XamlObjs.push_back(si.Source);
break;
+1
View File
@@ -468,6 +468,7 @@ if(BUILD_TESTING)
if(CMake_TEST_Rust)
ADD_TEST_MACRO(RustOnly RustOnly)
ADD_TEST_MACRO(RustMix RustMix)
ADD_TEST_MACRO(RustPie RustPie)
endif()
if(CMAKE_Fortran_COMPILER)
ADD_TEST_MACRO(FortranOnly FortranOnly)
+1 -1
View File
@@ -1,3 +1,3 @@
set(CMAKE_EXPERIMENTAL_RUST "3cc9b32c-47d3-4056-8953-d74e69fc0d6c")
set(CMAKE_EXPERIMENTAL_RUST "6b6e613b-f6cb-402d-8aea-59034fa8c65b")
enable_language(Rust)
message(STATUS "CMAKE_Rust_COMPILER='${CMAKE_Rust_COMPILER}'")
+25 -9
View File
@@ -1,14 +1,30 @@
cmake_minimum_required(VERSION 4.2)
set(CMAKE_EXPERIMENTAL_RUST "3cc9b32c-47d3-4056-8953-d74e69fc0d6c")
set(CMAKE_EXPERIMENTAL_RUST "6b6e613b-f6cb-402d-8aea-59034fa8c65b")
project(RustMix LANGUAGES C Rust)
project(RustMix LANGUAGES C CXX Rust)
add_library(liba STATIC liba.rs)
add_library(libb SHARED libb.rs)
add_library(libc OBJECT libc.rs)
add_library(rs_staticlib STATIC rs_staticlib.rs)
add_library(rs_cdylib SHARED rs_cdylib.rs)
add_library(rs_rlib OBJECT rs_rlib.rs)
add_executable(RustMix main.c)
target_link_libraries(RustMix liba)
target_link_libraries(RustMix libb)
target_link_libraries(RustMix libc)
add_library(c_static STATIC c_static.c)
add_library(c_shared SHARED c_shared.c)
add_library(c_obj OBJECT c_obj.c)
add_library(cpp_shared SHARED cpp_shared.cpp)
# Note: trying to link a C++ object file or static library into the Rust
# executable currently fails with errors related to missing symbols from
# libstdc++, while libstdc++ is present on the linker command line.
add_executable(RustMix main.rs)
target_link_libraries(
RustMix
rs_staticlib
rs_cdylib
rs_rlib
c_static
c_shared
c_obj
cpp_shared
)
+6
View File
@@ -0,0 +1,6 @@
#include <stdio.h>
void c_obj_greet()
{
printf("Hello from a C object file!");
}
+6
View File
@@ -0,0 +1,6 @@
#include <stdio.h>
void c_shared_greet()
{
printf("Hello from a C shared library!");
}
+6
View File
@@ -0,0 +1,6 @@
#include <stdio.h>
void c_static_greet()
{
printf("Hello from a C static library!");
}
+6
View File
@@ -0,0 +1,6 @@
#include <iostream>
extern "C" void cpp_shared_greet()
{
std::cout << "Hello from a C++ shader library" << std::endl;
}
-4
View File
@@ -1,4 +0,0 @@
#[no_mangle]
pub extern "C" fn liba_greet() {
println!("Hello from Rust liba");
}
-4
View File
@@ -1,4 +0,0 @@
#[no_mangle]
pub extern "C" fn libb_greet() {
println!("Hello from Rust libb");
}
-4
View File
@@ -1,4 +0,0 @@
#[no_mangle]
pub extern "C" fn libc_greet() {
println!("Hello from Rust libc");
}
-15
View File
@@ -1,15 +0,0 @@
#include <stdio.h>
extern void liba_greet();
extern void libb_greet();
extern void libc_greet();
int main()
{
printf("Hello from C main\n");
liba_greet();
libb_greet();
libc_greet();
return 0;
}
+30
View File
@@ -0,0 +1,30 @@
extern "C" {
// Functions defined in C
fn c_obj_greet();
fn c_static_greet();
fn c_shared_greet();
// Functions defined in C++
fn cpp_shared_greet();
// Functions defined in Rust through a C-style ABI
fn rs_staticlib_greet();
fn rs_cdylib_greet();
}
// Functions defined in Rust, using native Rust ABI
extern crate rs_rlib;
use rs_rlib::rs_rlib_greet;
fn main() {
println!("Hello from main.rs");
unsafe {
c_obj_greet();
c_static_greet();
c_shared_greet();
cpp_shared_greet();
rs_staticlib_greet();
rs_cdylib_greet();
}
rs_rlib_greet();
}
+4
View File
@@ -0,0 +1,4 @@
#[no_mangle]
pub extern "C" fn rs_cdylib_greet() {
println!("Hello from a Rust cdylib!");
}
+3
View File
@@ -0,0 +1,3 @@
pub fn rs_rlib_greet() {
println!("Hello from a Rust rlib!");
}
+4
View File
@@ -0,0 +1,4 @@
#[no_mangle]
pub extern "C" fn rs_staticlib_greet() {
println!("Hello from a Rust staticlib!");
}
+12 -8
View File
@@ -1,14 +1,18 @@
cmake_minimum_required(VERSION 4.2)
set(CMAKE_EXPERIMENTAL_RUST "3cc9b32c-47d3-4056-8953-d74e69fc0d6c")
set(CMAKE_EXPERIMENTAL_RUST "6b6e613b-f6cb-402d-8aea-59034fa8c65b")
project(RustOnly LANGUAGES Rust)
add_library(liba STATIC liba.rs)
add_library(libb SHARED libb.rs)
add_library(libc OBJECT libc.rs)
add_library(a STATIC a.rs)
add_library(b SHARED b.rs)
add_library(c OBJECT c.rs)
add_executable(RustOnly main.rs)
target_link_libraries(RustOnly liba)
target_link_libraries(RustOnly libb)
target_link_libraries(RustOnly libc)
add_executable(RustOnly d.rs main.rs e.rs)
# Ensure that Rust_MAIN_CRATE_ROOT property works.
set_target_properties(RustOnly PROPERTIES Rust_MAIN_CRATE_ROOT main.rs)
# Emit an object file instead of an rlib.
set_source_files_properties(e.rs PROPERTIES Rust_EMIT "obj")
target_link_libraries(RustOnly a)
target_link_libraries(RustOnly b)
target_link_libraries(RustOnly c)
+4
View File
@@ -0,0 +1,4 @@
// We can expose a Rust API, as this is compiled into a rlib.
pub fn libc_greet() {
println!("Hello from libc");
}
+4
View File
@@ -0,0 +1,4 @@
// We can expose a Rust API, as this is compiled into a rlib.
pub fn libd_greet() {
println!("Hello from libd");
}
+4
View File
@@ -0,0 +1,4 @@
#[no_mangle]
pub extern "C" fn libe_greet() {
println!("Hello from libe");
}
-4
View File
@@ -1,4 +0,0 @@
#[no_mangle]
pub extern "C" fn libc_greet() {
println!("Hello from libc");
}
+8 -2
View File
@@ -3,13 +3,19 @@ mod moda;
extern "C" {
fn liba_greet();
fn libb_greet();
fn libc_greet();
fn libe_greet();
}
extern crate c;
extern crate d;
use c::libc_greet;
use d::libd_greet;
fn main() {
println!("Hello from main.rs");
moda::moda_greet();
unsafe { liba_greet() };
unsafe { libb_greet() };
unsafe { libc_greet() };
libc_greet();
unsafe { libe_greet() };
}
+31
View File
@@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 4.2)
set(CMAKE_EXPERIMENTAL_RUST "6b6e613b-f6cb-402d-8aea-59034fa8c65b")
function(setup PREFIX)
add_library(${PREFIX}_static STATIC ../static.rs)
add_library(${PREFIX}_shared SHARED ../shared.rs)
add_library(${PREFIX}_object OBJECT ../object.rs)
add_executable(${PREFIX}_RustPie ../main.rs)
target_link_libraries(
${PREFIX}_RustPie PRIVATE
${PREFIX}_static
${PREFIX}_shared
${PREFIX}_object
)
endfunction()
project(RustPie LANGUAGES Rust)
add_subdirectory(Default)
add_subdirectory(Disabled)
add_subdirectory(Enabled)
file(
GENERATE OUTPUT ${CMAKE_BINARY_DIR}/runner_$<CONFIG>.rs
INPUT runner.rs
)
add_executable(RustPie ${CMAKE_BINARY_DIR}/runner_$<CONFIG>.rs)
add_dependencies(RustPie default_RustPie disabled_RustPie enabled_RustPie)
+3
View File
@@ -0,0 +1,3 @@
project(DefaultRustPie LANGUAGES Rust)
setup(default)
+12
View File
@@ -0,0 +1,12 @@
project(DisabledRustPie LANGUAGES Rust)
setup(disabled)
set_property(
TARGET
disabled_static
disabled_shared
disabled_object
disabled_RustPie
PROPERTY POSITION_INDEPENDENT_CODE OFF
)
+12
View File
@@ -0,0 +1,12 @@
project(EnabledRustPie LANGUAGES Rust)
setup(enabled)
set_property(
TARGET
enabled_static
enabled_shared
enabled_object
enabled_RustPie
PROPERTY POSITION_INDEPENDENT_CODE TRUE
)
+18
View File
@@ -0,0 +1,18 @@
extern "C" {
fn static_foo();
fn shared_foo();
}
extern crate object;
use object::object_foo;
fn main() {
unsafe {
static_foo();
shared_foo();
}
object_foo();
}
+3
View File
@@ -0,0 +1,3 @@
pub fn object_foo() {
println!("object_foo");
}
+66
View File
@@ -0,0 +1,66 @@
use std::process::Command;
#[cfg(target_os="macos")]
fn is_pie_executable(executable: &str) -> bool {
let output = Command::new("otool")
.args(["-hv", executable])
.output()
.expect("Failed to check executable");
if !output.status.success() {
panic!("otool exited with non-zero exit code");
}
output.stdout.windows(5).any(|w| {
(w[0] == b' ' || w[0] == b'\t')
&& (w[1..=3] == b"PIE")
&& (w[4] == b' ' || w[4] == b'\n')
})
}
#[cfg(all(target_family="unix", not(target_os="macos")))]
fn is_pie_executable(executable: &str) -> bool {
let output = Command::new("readelf")
.args(["-lW", executable])
.env("LANG", "C")
.env("LC_ALL", "C")
.output()
.expect("Failed to check executable");
if !output.status.success() {
panic!("readelf exited with non-zero exit code");
}
let is_pie = output.stdout.windows(20).any(|w| w == b"Elf file type is DYN");
let is_not_pie = output.stdout.windows(21).any(|w| w == b"Elf file type is EXEC");
assert!(is_pie ^ is_not_pie, "Cannot determine type of ELF file");
is_pie
}
fn main() {
let commands = [
// The actual path to the commands will be filled in by CMake.
r#"$<TARGET_FILE:default_RustPie>"#,
r#"$<TARGET_FILE:disabled_RustPie>"#,
r#"$<TARGET_FILE:enabled_RustPie>"#,
];
for command in commands {
println!("Start command: {command:?}");
let result = Command::new(command)
.spawn()
.expect("Failed to launch command")
.wait()
.expect("Failure while waiting for process to finish")
.success();
if !result {
panic!("Process exited with non-zero exit code.");
}
}
assert!(
!is_pie_executable(commands[1]),
"ERROR: disabled_RustPie must not be a PIE executable, but it is not."
);
assert!(
is_pie_executable(commands[2]),
"ERROR: enabled_RustPie must be a PIE executable, but it is not."
);
}
+4
View File
@@ -0,0 +1,4 @@
#[no_mangle]
pub extern "C" fn shared_foo() {
println!("shared_foo");
}
+4
View File
@@ -0,0 +1,4 @@
#[no_mangle]
pub extern "C" fn static_foo() {
println!("static_foo");
}