mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
Rust: Add experimental support with Ninja
The TU for Rust is a crate, not per-source. `rustc` expects entry file to TU as input, and cannot work with object files. So we need to have one-step build/link in most cases, similar to what old Swift was doing. Issue: #25492 Signed-off-by: Ayush Singh <ayush@beagleboard.org>
This commit is contained in:
@@ -5,6 +5,7 @@ if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
|
||||
set(CMake_TEST_ISPC "ON" CACHE STRING "")
|
||||
endif()
|
||||
set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database" CACHE STRING "")
|
||||
set(CMake_TEST_Rust "ON" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "")
|
||||
|
||||
@@ -2,5 +2,6 @@ if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
|
||||
set(CMake_TEST_ISPC "ON" CACHE STRING "")
|
||||
endif()
|
||||
set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database" CACHE STRING "")
|
||||
set(CMake_TEST_Rust "ON" CACHE STRING "")
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
|
||||
|
||||
@@ -158,3 +158,15 @@ When activated, this experimental feature provides the following:
|
||||
* The experimental ``export(SBOM)`` and ``install(SBOM)`` commands are
|
||||
available to generate a Software Bill of Materials or "SBOM" for the current
|
||||
project. See :command:`install(SBOM)` for a complete overview of the command.
|
||||
|
||||
Rust Support
|
||||
============
|
||||
|
||||
In order to activate support for Rust, set
|
||||
|
||||
* variable ``CMAKE_EXPERIMENTAL_RUST`` to
|
||||
* value ``3cc9b32c-47d3-4056-8953-d74e69fc0d6c``.
|
||||
|
||||
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
|
||||
experimenting.
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file LICENSE.rst or https://cmake.org/licensing for details.
|
||||
|
||||
include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake)
|
||||
|
||||
if(NOT "${CMAKE_GENERATOR}" MATCHES "^Ninja")
|
||||
message(FATAL_ERROR "Rust language not supported by \"${CMAKE_GENERATOR}\" generator")
|
||||
endif()
|
||||
|
||||
set(CMAKE_Rust_COMPILER_INIT "rustc")
|
||||
set(CMAKE_Rust_COMPILER_HINTS "$ENV{HOME}/.cargo/bin")
|
||||
|
||||
_cmake_find_compiler(Rust)
|
||||
|
||||
get_filename_component(RUSTC_REAL "${CMAKE_Rust_COMPILER}" REALPATH)
|
||||
get_filename_component(RUSTC_FILENAME "${RUSTC_REAL}" NAME)
|
||||
|
||||
# When rustup is used for installing rust, rustc will just be a symlink to rustup. In such cases,
|
||||
# we need to query rustup for underlying rustc path.
|
||||
if(RUSTC_FILENAME STREQUAL "rustup")
|
||||
get_filename_component(RUSTC_DIR "${CMAKE_Rust_COMPILER}" DIRECTORY)
|
||||
set(RUSTUP_PATH "${RUSTC_DIR}/rustup")
|
||||
|
||||
# Fix RUSTUP_HOME in ctest.
|
||||
if(RUSTC_FILENAME STREQUAL "rustup" AND NOT "$ENV{CTEST_REAL_HOME}" STREQUAL "" AND "$ENV{RUSTUP_HOME}" STREQUAL "")
|
||||
set(ENV{RUSTUP_HOME} "$ENV{CTEST_REAL_HOME}/.rustup")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${RUSTUP_PATH} which rustc
|
||||
OUTPUT_VARIABLE REAL_RUSTC
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
if("${REAL_RUSTC}" STREQUAL "")
|
||||
message(FATAL_ERROR "Failed to find path to real rustc")
|
||||
endif()
|
||||
|
||||
set_property(CACHE CMAKE_Rust_COMPILER PROPERTY VALUE "${REAL_RUSTC}")
|
||||
endif()
|
||||
|
||||
if(CMAKE_Rust_COMPILER)
|
||||
set(CMAKE_Rust_COMPILER_WORKS TRUE)
|
||||
endif()
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_ROOT}/Modules/CMakeRustCompiler.cmake.in"
|
||||
"${CMAKE_PLATFORM_INFO_DIR}/CMakeRustCompiler.cmake"
|
||||
@ONLY)
|
||||
@@ -0,0 +1,5 @@
|
||||
set(CMAKE_Rust_COMPILER "@CMAKE_Rust_COMPILER@")
|
||||
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@)
|
||||
@@ -0,0 +1,41 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file LICENSE.rst or https://cmake.org/licensing for details.
|
||||
|
||||
include(CMakeLanguageInformation)
|
||||
|
||||
if(UNIX)
|
||||
set(CMAKE_Rust_OUTPUT_EXTENSION .o)
|
||||
else()
|
||||
set(CMAKE_Rust_OUTPUT_EXTENSION .obj)
|
||||
endif()
|
||||
|
||||
set(CMAKE_Rust_LIBRARY_PATH_FLAG "-L ")
|
||||
set(CMAKE_Rust_LINK_LIBRARY_FILE_FLAG "-C link-arg=")
|
||||
set(CMAKE_EXECUTABLE_RUNTIME_Rust_FLAG "-C link-arg=-Wl,-rpath,")
|
||||
set(CMAKE_EXECUTABLE_RUNTIME_Rust_FLAG_SEP ",")
|
||||
|
||||
set(CMAKE_Rust_FLAGS_DEBUG_INIT "-C opt-level=0 -g")
|
||||
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")
|
||||
|
||||
cmake_initialize_per_config_variable(CMAKE_Rust_FLAGS "Flags used by the Rust compiler")
|
||||
|
||||
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>\"")
|
||||
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>")
|
||||
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>\"")
|
||||
endif()
|
||||
|
||||
set(CMAKE_Rust_INFORMATION_LOADED 1)
|
||||
@@ -0,0 +1,9 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file LICENSE.rst or https://cmake.org/licensing for details.
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_ROOT}/Modules/CMakeRustCompiler.cmake.in"
|
||||
"${CMAKE_PLATFORM_INFO_DIR}/CMakeRustCompiler.cmake"
|
||||
@ONLY)
|
||||
|
||||
include(${CMAKE_PLATFORM_INFO_DIR}/CMakeRustCompiler.cmake)
|
||||
@@ -80,6 +80,14 @@ cmExperimental::FeatureData const LookupTable[] = {
|
||||
"experimentation and feedback to CMake developers.",
|
||||
{},
|
||||
cmExperimental::TryCompileCondition::Never },
|
||||
// Rust support
|
||||
{ "Rust",
|
||||
"3cc9b32c-47d3-4056-8953-d74e69fc0d6c",
|
||||
"CMAKE_EXPERIMENTAL_RUST",
|
||||
"CMake's support for the Rust programming language is experimental. "
|
||||
"It is meant only for experimentation and feedback to CMake developers.",
|
||||
{},
|
||||
cmExperimental::TryCompileCondition::Never },
|
||||
};
|
||||
static_assert(sizeof(LookupTable) / sizeof(LookupTable[0]) ==
|
||||
static_cast<size_t>(cmExperimental::Feature::Sentinel),
|
||||
|
||||
@@ -24,6 +24,7 @@ public:
|
||||
MappedPackageInfo,
|
||||
ExportBuildDatabase,
|
||||
GenerateSbom,
|
||||
Rust,
|
||||
|
||||
Sentinel,
|
||||
};
|
||||
|
||||
@@ -733,6 +733,16 @@ void cmGlobalGenerator::EnableLanguage(
|
||||
|
||||
for (std::string const& lang : languages) {
|
||||
needSetLanguageEnabledMaps[lang] = false;
|
||||
|
||||
if (lang == "Rust" &&
|
||||
!cmExperimental::HasSupportEnabled(*this->Makefiles[0].get(),
|
||||
cmExperimental::Feature::Rust)) {
|
||||
mf->IssueMessage(MessageType::FATAL_ERROR,
|
||||
"Experimental Rust support is not enabled.");
|
||||
cmSystemTools::SetFatalErrorOccurred();
|
||||
return;
|
||||
}
|
||||
|
||||
if (lang == "NONE") {
|
||||
this->SetLanguageEnabled("NONE", mf);
|
||||
continue;
|
||||
|
||||
@@ -478,6 +478,11 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(
|
||||
vars.Includes = "$INCLUDES";
|
||||
}
|
||||
|
||||
if (this->TargetLinkLanguage(config) == "Rust") {
|
||||
vars.RustSources = "$RUST_SOURCES";
|
||||
vars.RustObjectDeps = "$RUST_OBJECT_DEPS";
|
||||
}
|
||||
|
||||
std::string responseFlag;
|
||||
|
||||
std::string cmakeVarLang =
|
||||
@@ -1276,6 +1281,40 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
|
||||
gt->IsExecutableWithExports()) {
|
||||
linkBuild.Outputs.push_back(vars["SWIFT_MODULE"]);
|
||||
}
|
||||
} 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.
|
||||
for (auto const& obj : linkBuild.ExplicitDeps) {
|
||||
if (obj != entry_obj) {
|
||||
obj_deps << " "
|
||||
<< lg->ConvertToOutputFormat(obj, cmOutputConverter::SHELL);
|
||||
}
|
||||
}
|
||||
|
||||
vars["RUST_OBJECT_DEPS"] = obj_deps.str();
|
||||
} else {
|
||||
linkBuild.ExplicitDeps = this->GetObjects(config);
|
||||
}
|
||||
|
||||
@@ -143,6 +143,16 @@ std::string cmRulePlaceholderExpander::ExpandVariable(
|
||||
return this->ReplaceValues->SwiftSources;
|
||||
}
|
||||
}
|
||||
if (this->ReplaceValues->RustSources) {
|
||||
if (variable == "RUST_SOURCES") {
|
||||
return this->ReplaceValues->RustSources;
|
||||
}
|
||||
}
|
||||
if (this->ReplaceValues->RustObjectDeps) {
|
||||
if (variable == "RUST_OBJECT_DEPS") {
|
||||
return this->ReplaceValues->RustObjectDeps;
|
||||
}
|
||||
}
|
||||
if (this->ReplaceValues->TargetPDB) {
|
||||
if (variable == "TARGET_PDB") {
|
||||
return this->ReplaceValues->TargetPDB;
|
||||
|
||||
@@ -69,6 +69,8 @@ public:
|
||||
char const* SwiftModuleName = nullptr;
|
||||
char const* SwiftOutputFileMapOption = nullptr;
|
||||
char const* SwiftSources = nullptr;
|
||||
char const* RustSources = nullptr;
|
||||
char const* RustObjectDeps = nullptr;
|
||||
char const* ISPCHeader = nullptr;
|
||||
char const* CudaCompileMode = nullptr;
|
||||
char const* Fatbinary = nullptr;
|
||||
|
||||
@@ -461,6 +461,10 @@ if(BUILD_TESTING)
|
||||
ADD_TEST_MACRO(SwiftMixLib Swifty)
|
||||
endif()
|
||||
endif()
|
||||
if(CMake_TEST_Rust)
|
||||
ADD_TEST_MACRO(RustOnly RustOnly)
|
||||
ADD_TEST_MACRO(RustMix RustMix)
|
||||
endif()
|
||||
if(CMAKE_Fortran_COMPILER)
|
||||
ADD_TEST_MACRO(FortranOnly FortranOnly)
|
||||
set_property(TEST FortranOnly APPEND PROPERTY LABELS "Fortran")
|
||||
|
||||
@@ -600,6 +600,9 @@ if(UNIX AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG)
|
||||
-DCMAKE_EXECUTABLE_FORMAT=${CMAKE_EXECUTABLE_FORMAT}
|
||||
)
|
||||
endif()
|
||||
if(CMake_TEST_Rust)
|
||||
add_RunCMake_test(Rust)
|
||||
endif()
|
||||
add_RunCMake_test(ScriptMode)
|
||||
add_RunCMake_test(StandardLinkDirectories)
|
||||
add_RunCMake_test(Swift -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 4.2)
|
||||
project(${RunCMake_TEST} NONE)
|
||||
include(${RunCMake_TEST}.cmake)
|
||||
@@ -0,0 +1,6 @@
|
||||
^CMake Warning \(dev\) at Enable\.cmake:[0-9]+ \(enable_language\):
|
||||
CMake's support for the Rust programming language is experimental\. It is
|
||||
meant only for experimentation and feedback to CMake developers\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
||||
This warning is for project developers\. Use -Wno-dev to suppress it\.$
|
||||
@@ -0,0 +1 @@
|
||||
-- CMAKE_Rust_COMPILER='[^']*/rustc'
|
||||
@@ -0,0 +1,3 @@
|
||||
set(CMAKE_EXPERIMENTAL_RUST "3cc9b32c-47d3-4056-8953-d74e69fc0d6c")
|
||||
enable_language(Rust)
|
||||
message(STATUS "CMAKE_Rust_COMPILER='${CMAKE_Rust_COMPILER}'")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,4 @@
|
||||
^CMake Error at EnableExperimental\.cmake:[0-9]+ \(enable_language\):
|
||||
Experimental Rust support is not enabled\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
||||
@@ -0,0 +1 @@
|
||||
enable_language(Rust)
|
||||
@@ -0,0 +1,4 @@
|
||||
include(RunCMake)
|
||||
|
||||
run_cmake(Enable)
|
||||
run_cmake(EnableExperimental)
|
||||
@@ -0,0 +1,14 @@
|
||||
cmake_minimum_required(VERSION 4.2)
|
||||
|
||||
set(CMAKE_EXPERIMENTAL_RUST "3cc9b32c-47d3-4056-8953-d74e69fc0d6c")
|
||||
|
||||
project(RustMix LANGUAGES C Rust)
|
||||
|
||||
add_library(liba STATIC liba.rs)
|
||||
add_library(libb SHARED libb.rs)
|
||||
add_library(libc OBJECT libc.rs)
|
||||
|
||||
add_executable(RustMix main.c)
|
||||
target_link_libraries(RustMix liba)
|
||||
target_link_libraries(RustMix libb)
|
||||
target_link_libraries(RustMix libc)
|
||||
@@ -0,0 +1,4 @@
|
||||
#[no_mangle]
|
||||
pub extern "C" fn liba_greet() {
|
||||
println!("Hello from Rust liba");
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#[no_mangle]
|
||||
pub extern "C" fn libb_greet() {
|
||||
println!("Hello from Rust libb");
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#[no_mangle]
|
||||
pub extern "C" fn libc_greet() {
|
||||
println!("Hello from Rust libc");
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
cmake_minimum_required(VERSION 4.2)
|
||||
|
||||
set(CMAKE_EXPERIMENTAL_RUST "3cc9b32c-47d3-4056-8953-d74e69fc0d6c")
|
||||
|
||||
project(RustOnly LANGUAGES Rust)
|
||||
|
||||
add_library(liba STATIC liba.rs)
|
||||
add_library(libb SHARED libb.rs)
|
||||
add_library(libc OBJECT libc.rs)
|
||||
|
||||
add_executable(RustOnly main.rs)
|
||||
target_link_libraries(RustOnly liba)
|
||||
target_link_libraries(RustOnly libb)
|
||||
target_link_libraries(RustOnly libc)
|
||||
@@ -0,0 +1,4 @@
|
||||
#[no_mangle]
|
||||
pub extern "C" fn liba_greet() {
|
||||
println!("Hello from liba");
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#[no_mangle]
|
||||
pub extern "C" fn libb_greet() {
|
||||
println!("Hello from libb");
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#[no_mangle]
|
||||
pub extern "C" fn libc_greet() {
|
||||
println!("Hello from libc");
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
mod moda;
|
||||
|
||||
extern "C" {
|
||||
fn liba_greet();
|
||||
fn libb_greet();
|
||||
fn libc_greet();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("Hello from main.rs");
|
||||
moda::moda_greet();
|
||||
unsafe { liba_greet() };
|
||||
unsafe { libb_greet() };
|
||||
unsafe { libc_greet() };
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub fn moda_greet() {
|
||||
println!("Hello from moda");
|
||||
}
|
||||
Reference in New Issue
Block a user