You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在全平台构建无需额外目标的最小#[no_std]二进制用于测试构建依赖

如何在无外部目标的情况下构建仅需编译的#[no_std]二进制用于测试构建依赖

问题背景

我需要在#[no_std]环境中测试一个构建依赖。Cargo为构建依赖创建的环境是宿主环境而非目标环境,但待编译crate为#[no_std]时存在细微差异。作为集成测试的一部分,我会在磁盘上创建依赖于待测试构建依赖的最小虚拟crate,启动新Cargo进程构建该crate,以Cargo的退出状态作为测试依据。

当前核心问题:如何使用Cargo构建#[no_std]二进制,无需当前rustc安装包含外部目标(如arm...、thumb...),也不依赖执行测试的平台?

当前尝试的配置与报错

我尝试了最基础的仅需编译无需运行的#[no_std] crate配置:

Cargo.toml 配置

[package]
name = "nostd"
version = "0.1"
build = true

[build-dependencies]
to_be_tested = ...

[profile.dev]
panic = "abort" # 无需 panic 处理逻辑

src/main.rs 代码

#![no_main]
#![no_std]

use core::panic::PanicInfo;

#[panic_handler]
fn panic(_panic: &PanicInfo<'_>) -> ! {
    // 永远不会被执行
    loop {}
}

但构建时失败,报错如下:

error: linking with `cc` failed: exit status: 1
  |
  = note: [...]
  = note: ld: warning: no platform load command found in '[...]/symbols.o', assuming: macOS
          ld: Undefined symbols:
            _main, referenced from:
                <initial-undefines>
          clang: error: linker command failed with exit code 1 (use -v to see invocation)

问题本质是链接器要求存在入口函数(如main),且入口函数的签名随目标平台变化,而我需要在任意平台上完成编译,无需适配不同平台的入口签名——毕竟这个crate只需要编译成功,不需要实际运行。

解决方案

方案1:改用库类型crate跳过链接

如果测试仅需验证构建依赖逻辑,不需要验证二进制链接环节,可以将虚拟crate改为库类型:

修改Cargo.toml:

[package]
name = "nostd_test"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["rlib"] # 静态库,无需链接std

[build-dependencies]
to_be_tested = { path = "../path/to/your/crate" }

[profile.dev]
panic = "abort"

对应的src/lib.rs:

#![no_std]

use core::panic::PanicInfo;

#[panic_handler]
fn panic(_: &PanicInfo<'_>) -> ! {
    loop {}
}

运行cargo build --lib即可,只会编译库代码,不会触发链接操作,自然不会出现main缺失的问题。

方案2:跳过链接阶段,仅生成目标文件

如果必须使用二进制目标,可以通过Rust编译选项跳过链接,只生成目标文件:

在虚拟crate的.cargo/config.toml中添加:

[build]
rustflags = ["--emit=obj"]

或者直接在命令行传递参数:

cargo build --target=$(rustc -vV | grep host | cut -d' ' -f2) --rustflags="--emit=obj"

此方式下Cargo仅完成代码编译生成目标文件,不执行链接,也就不会要求入口函数存在。

方案3:添加跨平台兼容的入口函数存根

如果必须完成链接,可以通过条件编译为不同平台提供适配的入口函数:

修改src/main.rs:

#![no_main]
#![no_std]

use core::panic::PanicInfo;

#[panic_handler]
fn panic(_panic: &PanicInfo<'_>) -> ! {
    loop {}
}

// Linux 平台入口
#[cfg(target_os = "linux")]
#[link_section = ".text.startup"]
#[no_mangle]
pub extern "C" fn _start() -> ! {
    loop {}
}

// macOS 平台入口
#[cfg(target_os = "macos")]
#[no_mangle]
pub extern "C" fn main(_argc: isize, _argv: *const *const u8) -> ! {
    loop {}
}

// Windows 平台入口
#[cfg(target_os = "windows")]
#[no_mangle]
pub extern "system" fn WinMain(_hInstance: isize, _hPrevInstance: isize, _lpCmdLine: *const u8, _nShowCmd: u32) -> ! {
    loop {}
}

// 其他通用平台入口
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
#[no_mangle]
pub extern "C" fn main() -> ! {
    loop {}
}

该方案通过条件编译覆盖主流平台的入口要求,入口函数仅做无限循环(不会实际执行逻辑),满足链接器要求的同时,完全适配仅需编译的测试场景。


内容的提问来源于stack exchange,提问作者user2722968

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 17:27:47