内容简介: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 复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Haskell 中的条件编译
- .NET/C# 项目如何优雅地设置条件编译符号?
- .NET/C# 项目如何优雅地设置条件编译符号?
- SQL where条件和jion on条件的详解及区别
- Python 条件语句
- React 行内条件渲染
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。