Rust跨平台与条件编译总结

栏目: 编程语言 · Rust · 发布时间: 6年前

内容简介:build.rs可实现正常项目编译前的额外操作,比如代码生成、编译所依赖的C/C++库等行为。为了让编程过程更可控,通常需要输出状态表示通过了某一阶段,或遇到什么错误,Cargo支持build.rs编译时输出不同类型的语句,比如info、warning、error等,以下为示例,参考自目前我看到比较完整的参考是官方的libstd/build.rs,编译我们业务所需的第三方库的命令几乎都可以从那找到“灵感”,下面贴出完整代码镇宅,关键操作是在Cargo.toml中添加

build.rs可实现正常项目编译前的额外操作,比如代码生成、编译所依赖的C/C++库等行为。为了让编程过程更可控,通常需要输出状态表示通过了某一阶段,或遇到什么错误,Cargo支持build.rs编译时输出不同类型的语句,比如info、warning、error等,以下为示例,参考自 rustdroid-native

println!("cargo:warning=Error executing process command <{:?}>: {}", cmd, e);
复制代码

调用C/C++编译器编译所依赖的第三方C/C++库

目前我看到比较完整的参考是官方的libstd/build.rs,编译我们业务所需的第三方库的命令几乎都可以从那找到“灵感”,下面贴出完整代码镇宅,关键操作是 build_libbacktrace() ,通过 cc::Build 实例把需要编译的C/C++代码声明起来, 理论上支持正则匹配

#![deny(warnings)]

extern crate build_helper;
extern crate cc;

use build_helper::native_lib_boilerplate;
use std::env;
use std::fs::File;

fn main() {
    let target = env::var("TARGET").expect("TARGET was not set");
    if cfg!(feature = "backtrace") &&
        !target.contains("cloudabi") &&
        !target.contains("emscripten") &&
        !target.contains("msvc") &&
        !target.contains("wasm32")
    {
        let _ = build_libbacktrace(&target);
    }

    if target.contains("linux") {
        if target.contains("android") {
            println!("cargo:rustc-link-lib=dl");
            println!("cargo:rustc-link-lib=log");
            println!("cargo:rustc-link-lib=gcc");
        } else if !target.contains("musl") {
            println!("cargo:rustc-link-lib=dl");
            println!("cargo:rustc-link-lib=rt");
            println!("cargo:rustc-link-lib=pthread");
        }
    } else if target.contains("freebsd") {
        println!("cargo:rustc-link-lib=execinfo");
        println!("cargo:rustc-link-lib=pthread");
    } else if target.contains("dragonfly") || target.contains("bitrig") ||
              target.contains("netbsd") || target.contains("openbsd") {
        println!("cargo:rustc-link-lib=pthread");
    } else if target.contains("solaris") {
        println!("cargo:rustc-link-lib=socket");
        println!("cargo:rustc-link-lib=posix4");
        println!("cargo:rustc-link-lib=pthread");
        println!("cargo:rustc-link-lib=resolv");
    } else if target.contains("apple-darwin") {
        println!("cargo:rustc-link-lib=System");

        // res_init and friends require -lresolv on macOS/iOS.
        // See #41582 and http://blog.achernya.com/2013/03/os-x-has-silly-libsystem.html
        println!("cargo:rustc-link-lib=resolv");
    } else if target.contains("apple-ios") {
        println!("cargo:rustc-link-lib=System");
        println!("cargo:rustc-link-lib=objc");
        println!("cargo:rustc-link-lib=framework=Security");
        println!("cargo:rustc-link-lib=framework=Foundation");
        println!("cargo:rustc-link-lib=resolv");
    } else if target.contains("windows") {
        println!("cargo:rustc-link-lib=advapi32");
        println!("cargo:rustc-link-lib=ws2_32");
        println!("cargo:rustc-link-lib=userenv");
        println!("cargo:rustc-link-lib=shell32");
    } else if target.contains("fuchsia") {
        println!("cargo:rustc-link-lib=zircon");
        println!("cargo:rustc-link-lib=fdio");
    } else if target.contains("cloudabi") {
        if cfg!(feature = "backtrace") {
            println!("cargo:rustc-link-lib=unwind");
        }
        println!("cargo:rustc-link-lib=c");
        println!("cargo:rustc-link-lib=compiler_rt");
    }
}

fn build_libbacktrace(target: &str) -> Result<(), ()> {
    let native = native_lib_boilerplate("libbacktrace", "libbacktrace", "backtrace", "")?;

    let mut build = cc::Build::new();
    build
        .flag("-fvisibility=hidden")
        .include("../libbacktrace")
        .include(&native.out_dir)
        .out_dir(&native.out_dir)
        .warnings(false)
        .file("../libbacktrace/alloc.c")
        .file("../libbacktrace/backtrace.c")
        .file("../libbacktrace/dwarf.c")
        .file("../libbacktrace/fileline.c")
        .file("../libbacktrace/posix.c")
        .file("../libbacktrace/read.c")
        .file("../libbacktrace/sort.c")
        .file("../libbacktrace/state.c");

    let any_debug = env::var("RUSTC_DEBUGINFO").unwrap_or_default() == "true" ||
        env::var("RUSTC_DEBUGINFO_LINES").unwrap_or_default() == "true";
    build.debug(any_debug);

    if target.contains("darwin") {
        build.file("../libbacktrace/macho.c");
    } else if target.contains("windows") {
        build.file("../libbacktrace/pecoff.c");
    } else {
        build.file("../libbacktrace/elf.c");

        let pointer_width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap();
        if pointer_width == "64" {
            build.define("BACKTRACE_ELF_SIZE", "64");
        } else {
            build.define("BACKTRACE_ELF_SIZE", "32");
        }
    }

    File::create(native.out_dir.join("backtrace-supported.h")).unwrap();
    build.define("BACKTRACE_SUPPORTED", "1");
    build.define("BACKTRACE_USES_MALLOC", "1");
    build.define("BACKTRACE_SUPPORTS_THREADS", "0");
    build.define("BACKTRACE_SUPPORTS_DATA", "0");

    File::create(native.out_dir.join("config.h")).unwrap();
    if !target.contains("apple-ios") &&
       !target.contains("solaris") &&
       !target.contains("redox") &&
       !target.contains("android") &&
       !target.contains("haiku") {
        build.define("HAVE_DL_ITERATE_PHDR", "1");
    }
    build.define("_GNU_SOURCE", "1");
    build.define("_LARGE_FILES", "1");

    build.compile("backtrace");
    Ok(())
}
复制代码

条件编译

所有的条件编译都由通过cfg配置实现,cfg支持any、all、not等逻辑谓词组合。

基本用法

在Cargo.toml中添加 [features] 段,然后列举需要组合的feature名,大体上相当于 gcc -条件1 -条件2 -条件3 ...

[features]
default = []
metal = ["gfx-backend-metal"]
vulkan = ["gfx-backend-vulkan"]
dx12 = ["gfx-backend-dx12"]
复制代码

mod级别条件编译

实现示例,参考 gl_generator.rs

#[cfg(feature = "unstable_generator_utils")]
pub mod generators;
#[cfg(not(feature = "unstable_generator_utils"))]
mod generators;
复制代码

编译特定CPU架构

指定target_arch + CPU架构名称字符串,如 #[cfg(target_arch= "x86")]#[cfg(any(target_arch = "arm", target_arch = "x86"))]

参考 libstd/os/android/raw.rs

#[cfg(any(target_arch = "arm", target_arch = "x86"))]
mod arch {
    use os::raw::{c_uint, c_uchar, c_ulonglong, c_longlong, c_ulong};
    use os::unix::raw::{uid_t, gid_t};

    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub type dev_t = u64;
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub type mode_t = u32;

    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub type blkcnt_t = u64;
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub type blksize_t = u64;
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub type ino_t = u64;
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub type nlink_t = u64;
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub type off_t = u64;
    #[stable(feature = "raw_ext", since = "1.1.0")]
    pub type time_t = i64;
复制代码
#[doc(include = "os/raw/char.md")]
#[cfg(any(all(target_os = "linux", any(target_arch = "aarch64",
                                       target_arch = "arm",
                                       target_arch = "powerpc",
                                       target_arch = "powerpc64",
                                       target_arch = "s390x")),
复制代码
[target.'cfg(any(target_os = "macos", all(target_os = "ios", target_arch = "aarch64")))'.dependencies.gfx-backend-metal]
git = "https://github.com/gfx-rs/gfx"
version = "0.1"
optional = true

[target.'cfg(target_os = "android")'.dependencies.gfx-backend-vulkan]
git = "https://github.com/gfx-rs/gfx"
version = "0.1"
optional = true

#[target.'cfg(windows)'.dependencies.gfx-backend-dx12]
#git = "https://github.com/gfx-rs/gfx"
#version = "0.1"
#optional = true
复制代码

编译成指定类型二进制包(.a/.so/.r)

目前还没找到支持编译出macOS/iOS支持的 .framework 办法。

在Cargo.toml中添加 [lib] 段,

  • name 表示输出的库名, 最终输出文件名为lib+name.a或lib+name.so比如libportability.so
  • crate-type 表示输出的二进制包类型,比如
    • staticlib = .a iOS只认Rust输出.a,Android可以.a和.so,配置成 ["staticlib", "cdylib"] 在用cargo-lipo时会出警告不支持 cdylib ,忽略即可。
    • cdylib = .so
    • rlib = 给Rust用的静态库
    • dylib = 给Rust用的动态库
  • path 表示库项目的入口文件,通常是src/lib.rs,如果改动了这一位置,可通过path = 新位置实现,比如:
[lib]
name = "portability"
crate-type = ["staticlib", "cdylib"]
path = "src/ios/lib.rs"
复制代码

SDK开发的“售后服务”

提供.a/.so给业务团队,这一过程可能会有人为失误导致大家对接失败,下面介绍些我们使用的小技巧。

读取.a静态库的iOS版本

在macOS terminal执行如下命令,用 / 查找 VERSION

otool -lv xyz.a | less
复制代码

参考: check-ios-deployment-target-of-a-static-library

nm查看导出符号

有时编码疏忽导致没给需要导出的C接口添加 #[no_mangle]extern 等修饰,或者使用了不合理的优化attribute导致符号被优化掉,此时业务链接我们的库就会失败,因此,交付二进制包前用nm确认符号表是合格的工程师习惯。以下为示例代码。

nm -D ./target/release/libportability.so  | grep fun_call_exported_to_c
0000000000003190 T fun_call_exported_to_c
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

代码之美

代码之美

Grey Wilson / 聂雪军 / 机械工业出版社 / 2008年09月 / 99.00元

《代码之美》介绍了人类在一个奋斗领域中的创造性和灵活性:计算机系统的开发领域。在每章中的漂亮代码都是来自独特解决方案的发现,而这种发现是来源于作者超越既定边界的远见卓识,并且识别出被多数人忽视的需求以及找出令人叹为观止的问题解决方案。 《代码之美》33章,有38位作者,每位作者贡献一章。每位作者都将自己心目中对于“美丽的代码”的认识浓缩在一章当中,张力十足。38位大牛,每个人对代码之美都有自......一起来看看 《代码之美》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具