mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-07-30 17:36:08 +08:00
Rust: Switch to building .rs files to .rlib instead of native object files
Each Rust source file given to a target is considered a separate crate root. There's a concept of a "main crate root", which is the `.rs` file that is given to the final linker phase in CMake. Other Rust source file in a target are built as rlib separately. The project can still build `.rs` files into object files by setting the Rust_EMIT property on the desired source file.
This commit is contained in:
parent
942ddcdd36
commit
881faf67e5
@ -608,6 +608,7 @@ Properties on Source Files
|
|||||||
/prop_sf/OBJECT_DEPENDS
|
/prop_sf/OBJECT_DEPENDS
|
||||||
/prop_sf/OBJECT_NAME
|
/prop_sf/OBJECT_NAME
|
||||||
/prop_sf/OBJECT_OUTPUTS
|
/prop_sf/OBJECT_OUTPUTS
|
||||||
|
/prop_sf/Rust_EMIT
|
||||||
/prop_sf/SKIP_AUTOGEN
|
/prop_sf/SKIP_AUTOGEN
|
||||||
/prop_sf/SKIP_AUTOMOC
|
/prop_sf/SKIP_AUTOMOC
|
||||||
/prop_sf/SKIP_AUTORCC
|
/prop_sf/SKIP_AUTORCC
|
||||||
|
|||||||
39
Help/prop_sf/Rust_EMIT.rst
Normal file
39
Help/prop_sf/Rust_EMIT.rst
Normal 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.
|
||||||
@ -3,3 +3,8 @@ set(CMAKE_Rust_COMPILER_ENV_VAR "RUSTC")
|
|||||||
set(CMAKE_Rust_SOURCE_FILE_EXTENSIONS rs)
|
set(CMAKE_Rust_SOURCE_FILE_EXTENSIONS rs)
|
||||||
set(CMAKE_Rust_COMPILER_LOADED 1)
|
set(CMAKE_Rust_COMPILER_LOADED 1)
|
||||||
set(CMAKE_Rust_COMPILER_WORKS @CMAKE_Rust_COMPILER_WORKS@)
|
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)
|
||||||
|
|||||||
@ -3,11 +3,20 @@
|
|||||||
|
|
||||||
include(CMakeLanguageInformation)
|
include(CMakeLanguageInformation)
|
||||||
|
|
||||||
if(UNIX)
|
set(CMAKE_Rust_OUTPUT_EXTENSION .rlib)
|
||||||
set(CMAKE_Rust_OUTPUT_EXTENSION .o)
|
|
||||||
else()
|
# Other values are supported to generate various outputs (LLVM bitcode, or IR,
|
||||||
set(CMAKE_Rust_OUTPUT_EXTENSION .obj)
|
# crate metadata, Rust MIR). However, CMake cannot do anything with those
|
||||||
endif()
|
# 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_LIBRARY_PATH_FLAG "-L ")
|
||||||
set(CMAKE_Rust_LINK_LIBRARY_FILE_FLAG "-C link-arg=")
|
set(CMAKE_Rust_LINK_LIBRARY_FILE_FLAG "-C link-arg=")
|
||||||
@ -61,21 +70,20 @@ endblock()
|
|||||||
|
|
||||||
cmake_initialize_per_config_variable(CMAKE_Rust_FLAGS "Flags used by the Rust compiler")
|
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)
|
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()
|
endif()
|
||||||
|
|
||||||
if(NOT CMAKE_Rust_CREATE_SHARED_LIBRARY)
|
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>\"")
|
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()
|
|
||||||
|
|
||||||
# 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>")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT CMAKE_Rust_LINK_EXECUTABLE)
|
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()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_Rust_INFORMATION_LOADED 1)
|
set(CMAKE_Rust_INFORMATION_LOADED 1)
|
||||||
|
|||||||
@ -1826,6 +1826,7 @@ Json::Value Target::DumpSource(cmGeneratorTarget::SourceAndKind const& sk,
|
|||||||
case cmGeneratorTarget::SourceKindResx:
|
case cmGeneratorTarget::SourceKindResx:
|
||||||
case cmGeneratorTarget::SourceKindXaml:
|
case cmGeneratorTarget::SourceKindXaml:
|
||||||
case cmGeneratorTarget::SourceKindUnityBatched:
|
case cmGeneratorTarget::SourceKindUnityBatched:
|
||||||
|
case cmGeneratorTarget::SourceKindRustMainCrateRoot:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1009,6 +1009,12 @@ void cmGeneratorTarget::GetManifests(std::vector<cmSourceFile const*>& data,
|
|||||||
IMPLEMENT_VISIT(SourceKindManifest);
|
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
|
std::set<cmLinkItem> const& cmGeneratorTarget::GetUtilityItems() const
|
||||||
{
|
{
|
||||||
if (!this->UtilityItemsDone) {
|
if (!this->UtilityItemsDone) {
|
||||||
|
|||||||
@ -145,6 +145,7 @@ public:
|
|||||||
SourceKindCustomCommand,
|
SourceKindCustomCommand,
|
||||||
SourceKindExternalObject,
|
SourceKindExternalObject,
|
||||||
SourceKindCxxModuleSource,
|
SourceKindCxxModuleSource,
|
||||||
|
SourceKindRustMainCrateRoot,
|
||||||
SourceKindExtra,
|
SourceKindExtra,
|
||||||
SourceKindHeader,
|
SourceKindHeader,
|
||||||
SourceKindIDL,
|
SourceKindIDL,
|
||||||
@ -224,6 +225,9 @@ public:
|
|||||||
void GetManifests(std::vector<cmSourceFile const*>&,
|
void GetManifests(std::vector<cmSourceFile const*>&,
|
||||||
std::string const& config) const;
|
std::string const& config) const;
|
||||||
|
|
||||||
|
void GetRustMainCrateRoot(std::vector<cmSourceFile const*>&,
|
||||||
|
std::string const& config) const;
|
||||||
|
|
||||||
std::set<cmLinkItem> const& GetUtilityItems() const;
|
std::set<cmLinkItem> const& GetUtilityItems() const;
|
||||||
|
|
||||||
void ComputeObjectMapping();
|
void ComputeObjectMapping();
|
||||||
|
|||||||
@ -347,6 +347,12 @@ void cmGeneratorTarget::ComputeKindedSources(KindedSources& files,
|
|||||||
cmsys::RegularExpression header_regex(CM_HEADER_REGEX);
|
cmsys::RegularExpression header_regex(CM_HEADER_REGEX);
|
||||||
std::vector<cmSourceFile*> badObjLib;
|
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;
|
std::set<cmSourceFile*> emitted;
|
||||||
for (BT<std::string> const& s : srcs) {
|
for (BT<std::string> const& s : srcs) {
|
||||||
// Create each source at most once.
|
// Create each source at most once.
|
||||||
@ -379,7 +385,28 @@ void cmGeneratorTarget::ComputeKindedSources(KindedSources& files,
|
|||||||
} else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
|
} else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
|
||||||
kind = SourceKindExternalObject;
|
kind = SourceKindExternalObject;
|
||||||
} else if (!sf->GetOrDetermineLanguage().empty()) {
|
} 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") {
|
} else if (ext == "def") {
|
||||||
kind = SourceKindModuleDefinition;
|
kind = SourceKindModuleDefinition;
|
||||||
if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) {
|
if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) {
|
||||||
|
|||||||
@ -1086,6 +1086,13 @@ std::string cmGlobalGenerator::GetLanguageOutputExtension(
|
|||||||
{
|
{
|
||||||
std::string const& lang = source.GetLanguage();
|
std::string const& lang = source.GetLanguage();
|
||||||
if (!lang.empty()) {
|
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);
|
return this->GetLanguageOutputExtension(lang);
|
||||||
}
|
}
|
||||||
// if no language is found then check to see if it is already an
|
// if no language is found then check to see if it is already an
|
||||||
@ -1110,6 +1117,16 @@ std::string cmGlobalGenerator::GetLanguageOutputExtension(
|
|||||||
return "";
|
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 cmGlobalGenerator::GetLanguageFromExtension(
|
||||||
cm::string_view ext) const
|
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
|
// The map was originally filled by SetLanguageEnabledFlag, but
|
||||||
// since then the compiler- and platform-specific files have been
|
// since then the compiler- and platform-specific files have been
|
||||||
// loaded which might have added more entries.
|
// loaded which might have added more entries.
|
||||||
|
|||||||
@ -369,6 +369,8 @@ public:
|
|||||||
std::string GetLanguageOutputExtension(cmSourceFile const&) const;
|
std::string GetLanguageOutputExtension(cmSourceFile const&) const;
|
||||||
//! What is the object file extension for a given language?
|
//! What is the object file extension for a given language?
|
||||||
std::string GetLanguageOutputExtension(std::string const& lang) const;
|
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?
|
//! What is the configurations directory variable called?
|
||||||
virtual char const* GetCMakeCFGIntDir() const { return "."; }
|
virtual char const* GetCMakeCFGIntDir() const { return "."; }
|
||||||
@ -875,6 +877,7 @@ private:
|
|||||||
std::set<std::string> LanguagesInProgress;
|
std::set<std::string> LanguagesInProgress;
|
||||||
std::map<std::string, std::string> OutputExtensions;
|
std::map<std::string, std::string> OutputExtensions;
|
||||||
std::map<std::string, std::string> LanguageToOutputExtension;
|
std::map<std::string, std::string> LanguageToOutputExtension;
|
||||||
|
std::map<std::string, std::string> RustEmitToOutputExtension;
|
||||||
#if __cplusplus >= 201402L || defined(_MSVC_LANG) && _MSVC_LANG >= 201402L
|
#if __cplusplus >= 201402L || defined(_MSVC_LANG) && _MSVC_LANG >= 201402L
|
||||||
std::map<std::string, std::string, std::less<void>> ExtensionToLanguage;
|
std::map<std::string, std::string, std::less<void>> ExtensionToLanguage;
|
||||||
#else
|
#else
|
||||||
|
|||||||
@ -4678,6 +4678,20 @@ std::string cmLocalGenerator::GetObjectFileNameWithoutTarget(
|
|||||||
*hasSourceExtension = keptSourceExtension;
|
*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.
|
// Convert to a safe name.
|
||||||
return this->CreateSafeUniqueObjectFileName(objectName, dir_max);
|
return this->CreateSafeUniqueObjectFileName(objectName, dir_max);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
#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>
|
||||||
@ -479,8 +480,9 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this->TargetLinkLanguage(config) == "Rust") {
|
if (this->TargetLinkLanguage(config) == "Rust") {
|
||||||
vars.RustSources = "$RUST_SOURCES";
|
vars.RustMainCrateRoot = "$RUST_MAIN_CRATE_ROOT";
|
||||||
vars.RustObjectDeps = "$RUST_OBJECT_DEPS";
|
vars.RustLinkCrates = "$RUST_LINK_CRATES";
|
||||||
|
vars.RustNativeObjects = "$RUST_NATIVE_OBJECTS";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string responseFlag;
|
std::string responseFlag;
|
||||||
@ -1284,37 +1286,49 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
|
|||||||
} else if (this->TargetLinkLanguage(config) == "Rust") {
|
} else if (this->TargetLinkLanguage(config) == "Rust") {
|
||||||
// Use one-step build/link for Rust.
|
// Use one-step build/link for Rust.
|
||||||
// Compute specific libraries to link with.
|
// Compute specific libraries to link with.
|
||||||
std::vector<cmSourceFile const*> sources;
|
|
||||||
gt->GetObjectSources(sources, config);
|
|
||||||
cmLocalGenerator const* lg = this->GetLocalGenerator();
|
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);
|
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) {
|
for (auto const& obj : linkBuild.ExplicitDeps) {
|
||||||
if (obj != entry_obj) {
|
cm::filesystem::path const objPath(obj);
|
||||||
obj_deps << " "
|
if (objPath.extension() == ".rlib") {
|
||||||
<< lg->ConvertToOutputFormat(obj, cmOutputConverter::SHELL);
|
// 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 {
|
} else {
|
||||||
linkBuild.ExplicitDeps = this->GetObjects(config);
|
linkBuild.ExplicitDeps = this->GetObjects(config);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -674,6 +674,7 @@ void cmNinjaTargetGenerator::WriteCompileRule(std::string const& lang,
|
|||||||
vars.CudaCompileMode = "$CUDA_COMPILE_MODE";
|
vars.CudaCompileMode = "$CUDA_COMPILE_MODE";
|
||||||
vars.ISPCHeader = "$ISPC_HEADER_FILE";
|
vars.ISPCHeader = "$ISPC_HEADER_FILE";
|
||||||
vars.Config = "$CONFIG";
|
vars.Config = "$CONFIG";
|
||||||
|
vars.RustEmit = "$RUST_EMIT";
|
||||||
|
|
||||||
cmMakefile* mf = this->GetMakefile();
|
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") {
|
if (language == "Swift") {
|
||||||
this->EmitSwiftDependencyInfo(source, config);
|
this->EmitSwiftDependencyInfo(source, config);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -156,14 +156,24 @@ std::string cmRulePlaceholderExpander::ExpandVariable(
|
|||||||
return this->ReplaceValues->SwiftSources;
|
return this->ReplaceValues->SwiftSources;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this->ReplaceValues->RustSources) {
|
if (this->ReplaceValues->RustEmit) {
|
||||||
if (variable == "RUST_SOURCES") {
|
if (variable == "RUST_EMIT") {
|
||||||
return this->ReplaceValues->RustSources;
|
return this->ReplaceValues->RustEmit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this->ReplaceValues->RustObjectDeps) {
|
if (this->ReplaceValues->RustMainCrateRoot) {
|
||||||
if (variable == "RUST_OBJECT_DEPS") {
|
if (variable == "RUST_MAIN_CRATE_ROOT") {
|
||||||
return this->ReplaceValues->RustObjectDeps;
|
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) {
|
if (this->ReplaceValues->TargetPDB) {
|
||||||
|
|||||||
@ -79,8 +79,10 @@ public:
|
|||||||
char const* SwiftModuleName = nullptr;
|
char const* SwiftModuleName = nullptr;
|
||||||
char const* SwiftOutputFileMapOption = nullptr;
|
char const* SwiftOutputFileMapOption = nullptr;
|
||||||
char const* SwiftSources = nullptr;
|
char const* SwiftSources = nullptr;
|
||||||
char const* RustSources = nullptr;
|
char const* RustEmit = nullptr;
|
||||||
char const* RustObjectDeps = nullptr;
|
char const* RustMainCrateRoot = nullptr;
|
||||||
|
char const* RustLinkCrates = nullptr;
|
||||||
|
char const* RustNativeObjects = nullptr;
|
||||||
char const* ISPCHeader = nullptr;
|
char const* ISPCHeader = nullptr;
|
||||||
char const* CudaCompileMode = nullptr;
|
char const* CudaCompileMode = nullptr;
|
||||||
char const* Fatbinary = nullptr;
|
char const* Fatbinary = nullptr;
|
||||||
|
|||||||
@ -503,3 +503,13 @@ void cmSourceFile::SetCustomCommand(std::unique_ptr<cmCustomCommand> cc)
|
|||||||
{
|
{
|
||||||
this->CustomCommand = std::move(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;
|
||||||
|
}
|
||||||
|
|||||||
@ -184,6 +184,8 @@ public:
|
|||||||
void SetObjectLibrary(std::string const& objlib);
|
void SetObjectLibrary(std::string const& objlib);
|
||||||
std::string GetObjectLibrary() const;
|
std::string GetObjectLibrary() const;
|
||||||
|
|
||||||
|
cmValue GetRustEmitProperty() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
cmSourceFileLocation Location;
|
cmSourceFileLocation Location;
|
||||||
cmPropertyMap Properties;
|
cmPropertyMap Properties;
|
||||||
|
|||||||
@ -380,6 +380,8 @@ TargetProperty const StaticTargetProperties[] = {
|
|||||||
{ "Swift_LANGUAGE_VERSION"_s, IC::CanCompileSources },
|
{ "Swift_LANGUAGE_VERSION"_s, IC::CanCompileSources },
|
||||||
{ "Swift_MODULE_DIRECTORY"_s, IC::CanCompileSources },
|
{ "Swift_MODULE_DIRECTORY"_s, IC::CanCompileSources },
|
||||||
{ "Swift_COMPILATION_MODE"_s, IC::CanCompileSources },
|
{ "Swift_COMPILATION_MODE"_s, IC::CanCompileSources },
|
||||||
|
// ---- Rust
|
||||||
|
{ "Rust_MAIN_CRATE_ROOT"_s, IC::CanCompileSources },
|
||||||
// ---- moc
|
// ---- moc
|
||||||
{ "AUTOMOC"_s, IC::CanCompileSources },
|
{ "AUTOMOC"_s, IC::CanCompileSources },
|
||||||
{ "AUTOMOC_COMPILER_PREDEFINES"_s, IC::CanCompileSources },
|
{ "AUTOMOC_COMPILER_PREDEFINES"_s, IC::CanCompileSources },
|
||||||
|
|||||||
@ -2629,6 +2629,9 @@ void cmVisualStudio10TargetGenerator::WriteAllSources(Elem& e0)
|
|||||||
case cmGeneratorTarget::SourceKindResx:
|
case cmGeneratorTarget::SourceKindResx:
|
||||||
this->ResxObjs.push_back(si.Source);
|
this->ResxObjs.push_back(si.Source);
|
||||||
break;
|
break;
|
||||||
|
case cmGeneratorTarget::SourceKindRustMainCrateRoot:
|
||||||
|
tool = "None";
|
||||||
|
break;
|
||||||
case cmGeneratorTarget::SourceKindXaml:
|
case cmGeneratorTarget::SourceKindXaml:
|
||||||
this->XamlObjs.push_back(si.Source);
|
this->XamlObjs.push_back(si.Source);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -464,6 +464,7 @@ if(BUILD_TESTING)
|
|||||||
if(CMake_TEST_Rust)
|
if(CMake_TEST_Rust)
|
||||||
ADD_TEST_MACRO(RustOnly RustOnly)
|
ADD_TEST_MACRO(RustOnly RustOnly)
|
||||||
ADD_TEST_MACRO(RustMix RustMix)
|
ADD_TEST_MACRO(RustMix RustMix)
|
||||||
|
ADD_TEST_MACRO(RustPie RustPie)
|
||||||
endif()
|
endif()
|
||||||
if(CMAKE_Fortran_COMPILER)
|
if(CMAKE_Fortran_COMPILER)
|
||||||
ADD_TEST_MACRO(FortranOnly FortranOnly)
|
ADD_TEST_MACRO(FortranOnly FortranOnly)
|
||||||
|
|||||||
@ -2,13 +2,29 @@ cmake_minimum_required(VERSION 4.2)
|
|||||||
|
|
||||||
set(CMAKE_EXPERIMENTAL_RUST "3cc9b32c-47d3-4056-8953-d74e69fc0d6c")
|
set(CMAKE_EXPERIMENTAL_RUST "3cc9b32c-47d3-4056-8953-d74e69fc0d6c")
|
||||||
|
|
||||||
project(RustMix LANGUAGES C Rust)
|
project(RustMix LANGUAGES C CXX Rust)
|
||||||
|
|
||||||
add_library(liba STATIC liba.rs)
|
add_library(rs_staticlib STATIC rs_staticlib.rs)
|
||||||
add_library(libb SHARED libb.rs)
|
add_library(rs_cdylib SHARED rs_cdylib.rs)
|
||||||
add_library(libc OBJECT libc.rs)
|
add_library(rs_rlib OBJECT rs_rlib.rs)
|
||||||
|
|
||||||
add_executable(RustMix main.c)
|
add_library(c_static STATIC c_static.c)
|
||||||
target_link_libraries(RustMix liba)
|
add_library(c_shared SHARED c_shared.c)
|
||||||
target_link_libraries(RustMix libb)
|
add_library(c_obj OBJECT c_obj.c)
|
||||||
target_link_libraries(RustMix libc)
|
|
||||||
|
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
Tests/RustMix/c_obj.c
Normal file
6
Tests/RustMix/c_obj.c
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void c_obj_greet()
|
||||||
|
{
|
||||||
|
printf("Hello from a C object file!");
|
||||||
|
}
|
||||||
6
Tests/RustMix/c_shared.c
Normal file
6
Tests/RustMix/c_shared.c
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void c_shared_greet()
|
||||||
|
{
|
||||||
|
printf("Hello from a C shared library!");
|
||||||
|
}
|
||||||
6
Tests/RustMix/c_static.c
Normal file
6
Tests/RustMix/c_static.c
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void c_static_greet()
|
||||||
|
{
|
||||||
|
printf("Hello from a C static library!");
|
||||||
|
}
|
||||||
6
Tests/RustMix/cpp_shared.cpp
Normal file
6
Tests/RustMix/cpp_shared.cpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
extern "C" void cpp_shared_greet()
|
||||||
|
{
|
||||||
|
std::cout << "Hello from a C++ shader library" << std::endl;
|
||||||
|
}
|
||||||
@ -1,4 +0,0 @@
|
|||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn liba_greet() {
|
|
||||||
println!("Hello from Rust liba");
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn libb_greet() {
|
|
||||||
println!("Hello from Rust libb");
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn libc_greet() {
|
|
||||||
println!("Hello from Rust libc");
|
|
||||||
}
|
|
||||||
@ -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
Tests/RustMix/main.rs
Normal file
30
Tests/RustMix/main.rs
Normal 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
Tests/RustMix/rs_cdylib.rs
Normal file
4
Tests/RustMix/rs_cdylib.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn rs_cdylib_greet() {
|
||||||
|
println!("Hello from a Rust cdylib!");
|
||||||
|
}
|
||||||
3
Tests/RustMix/rs_rlib.rs
Normal file
3
Tests/RustMix/rs_rlib.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub fn rs_rlib_greet() {
|
||||||
|
println!("Hello from a Rust rlib!");
|
||||||
|
}
|
||||||
4
Tests/RustMix/rs_staticlib.rs
Normal file
4
Tests/RustMix/rs_staticlib.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn rs_staticlib_greet() {
|
||||||
|
println!("Hello from a Rust staticlib!");
|
||||||
|
}
|
||||||
@ -4,11 +4,15 @@ set(CMAKE_EXPERIMENTAL_RUST "3cc9b32c-47d3-4056-8953-d74e69fc0d6c")
|
|||||||
|
|
||||||
project(RustOnly LANGUAGES Rust)
|
project(RustOnly LANGUAGES Rust)
|
||||||
|
|
||||||
add_library(liba STATIC liba.rs)
|
add_library(a STATIC a.rs)
|
||||||
add_library(libb SHARED libb.rs)
|
add_library(b SHARED b.rs)
|
||||||
add_library(libc OBJECT libc.rs)
|
add_library(c OBJECT c.rs)
|
||||||
|
|
||||||
add_executable(RustOnly main.rs)
|
add_executable(RustOnly d.rs main.rs e.rs)
|
||||||
target_link_libraries(RustOnly liba)
|
# Ensure that Rust_MAIN_CRATE_ROOT property works.
|
||||||
target_link_libraries(RustOnly libb)
|
set_target_properties(RustOnly PROPERTIES Rust_MAIN_CRATE_ROOT main.rs)
|
||||||
target_link_libraries(RustOnly libc)
|
# 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
Tests/RustOnly/c.rs
Normal file
4
Tests/RustOnly/c.rs
Normal 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
Tests/RustOnly/d.rs
Normal file
4
Tests/RustOnly/d.rs
Normal 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
Tests/RustOnly/e.rs
Normal file
4
Tests/RustOnly/e.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn libe_greet() {
|
||||||
|
println!("Hello from libe");
|
||||||
|
}
|
||||||
@ -1,4 +0,0 @@
|
|||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn libc_greet() {
|
|
||||||
println!("Hello from libc");
|
|
||||||
}
|
|
||||||
@ -3,13 +3,19 @@ mod moda;
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
fn liba_greet();
|
fn liba_greet();
|
||||||
fn libb_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() {
|
fn main() {
|
||||||
println!("Hello from main.rs");
|
println!("Hello from main.rs");
|
||||||
moda::moda_greet();
|
moda::moda_greet();
|
||||||
unsafe { liba_greet() };
|
unsafe { liba_greet() };
|
||||||
unsafe { libb_greet() };
|
unsafe { libb_greet() };
|
||||||
unsafe { libc_greet() };
|
libc_greet();
|
||||||
|
unsafe { libe_greet() };
|
||||||
}
|
}
|
||||||
|
|||||||
31
Tests/RustPie/CMakeLists.txt
Normal file
31
Tests/RustPie/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
cmake_minimum_required(VERSION 4.2)
|
||||||
|
|
||||||
|
set(CMAKE_EXPERIMENTAL_RUST "3cc9b32c-47d3-4056-8953-d74e69fc0d6c")
|
||||||
|
|
||||||
|
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
Tests/RustPie/Default/CMakeLists.txt
Normal file
3
Tests/RustPie/Default/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
project(DefaultRustPie LANGUAGES Rust)
|
||||||
|
setup(default)
|
||||||
12
Tests/RustPie/Disabled/CMakeLists.txt
Normal file
12
Tests/RustPie/Disabled/CMakeLists.txt
Normal 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
Tests/RustPie/Enabled/CMakeLists.txt
Normal file
12
Tests/RustPie/Enabled/CMakeLists.txt
Normal 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
Tests/RustPie/main.rs
Normal file
18
Tests/RustPie/main.rs
Normal 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
Tests/RustPie/object.rs
Normal file
3
Tests/RustPie/object.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub fn object_foo() {
|
||||||
|
println!("object_foo");
|
||||||
|
}
|
||||||
66
Tests/RustPie/runner.rs
Normal file
66
Tests/RustPie/runner.rs
Normal 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
Tests/RustPie/shared.rs
Normal file
4
Tests/RustPie/shared.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn shared_foo() {
|
||||||
|
println!("shared_foo");
|
||||||
|
}
|
||||||
4
Tests/RustPie/static.rs
Normal file
4
Tests/RustPie/static.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn static_foo() {
|
||||||
|
println!("static_foo");
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user