1# Rust Module Configuration Rules and Guide 2 3## Introduction 4 5Rust is a static, strongly typed programming language. It has advantages such as secure memory management, high running performance, and native support for multi-thread development. Rust uses Cargo to create projects and compile and build Rust code.<br> 6To integrate C/C++ code and improve the build speed, OpenHarmony uses Generate Ninja (GN) and Ninja as its build system. GN has simple and easy-to-use build language, and Ninja provides direct and efficient assembly-level build rules. 7To integrate Rust code and maximize the interaction between the C/C++ code used in OpenHarmony and Rust, OpenHarmony uses GN as a unified build tool to build Rust source code files (xxx.rs) and is added with features such as interoperability with C/C++, compile time lints, test, IDL conversion, third-party library integration, and IDE. In addition, the GN framework is extended to support automatic interface conversion, which greatly simplifying development. 8 9### Basic Concepts 10 11| Term | Description | 12| ----- | ------------------------------------------------------------ | 13| Cargo | Cargo is an official build tool used by Rust. It allows Rust projects to declare dependencies and ensures reproducible builds.| 14| crate | Crate is a unit that can be independently compiled. | 15| Lint | Lint is a code analysis tool used to flag programming errors, bugs, stylistic errors, and suspicious constructs. It performs extensive error analysis on programs.| 16 17 18 19## Configuration Rules 20OpenHarmony provides a variety of GN templates for compiling Rust executables, dynamic libraries, and static libraries. The following table describes the templates. 21 22| GN Template | Description | Output | 23|--------------------------|-----------------|-----------------------------------| 24| ohos_rust_executable | Rust executable file. | Rust executable file, without the file name extension. | 25| ohos_rust_shared_library | Rust dynamic library. | Rust dylib dynamic library, with the default file name extension **.dylib.so**. | 26| ohos_rust_static_library | Rust static library. | Rust rlib static library, with the default file name extension **.rlib**. | 27| ohos_rust_proc_macro | Rust proc_macro library. | Rust proc_macro library, with the default file name extension **.so**. | 28| ohos_rust_shared_ffi | Rust Foreign Function Interface (FFI) dynamic library. | Rust cdylib dynamic library, which is called by the C/C++ module. The default file name extension is **.so**.| 29| ohos_rust_static_ffi | Rust FFI static library. | Rust staticlib library, which is called by the C/C++ module. The default file name extension is **.a**.| 30| ohos_rust_cargo_crate | Third-party Cargo crate. | Third-party Rust crates, which support rlib, dylib, and bin. | 31| ohos_rust_systemtest | Rust system test cases. | Executable system test cases for Rust, without the file name extension. | 32| ohos_rust_unittest | Rust unit test cases. | Executable unit test cases for Rust, without the file name extension. | 33| ohos_rust_fuzztest | Rust fuzzing test cases. | Executable fuzzing test cases for Rust, without the file name extension. | 34 35 36## Configuration Guide 37The configuration of the Rust module is similar to that of the C/C++ module. For details, see [Module Configuration Rules](subsys-build-module.md). The following provides examples of using different Rust templates. 38### Configuring a Rust Static Library 39The following example shows how to use the **ohos_rust_executable** and **ohos_rust_static_library** templates to build a binary executable and a static rlib library, respectively. The executable depends on the static library. 40The procedure is as follows: 41 421. Create **build/rust/tests/test_rlib_crate/src/simple_printer.rs**. 43 44 ```rust 45 //! simple_printer 46 47 /// struct RustLogMessage 48 49 pub struct RustLogMessage { 50 /// i32: id 51 pub id: i32, 52 /// String: msg 53 pub msg: String, 54 } 55 56 /// function rust_log_rlib 57 pub fn rust_log_rlib(msg: RustLogMessage) { 58 println!("id:{} message:{:?}", msg.id, msg.msg) 59 } 60 ``` 61 622. Create **build/rust/tests/test_rlib_crate/src/main.rs**. 63 64 ```rust 65 //! rlib_crate example for Rust. 66 67 extern crate simple_printer_rlib; 68 69 use simple_printer_rlib::rust_log_rlib; 70 use simple_printer_rlib::RustLogMessage; 71 72 fn main() { 73 let msg: RustLogMessage = RustLogMessage { 74 id: 0, 75 msg: "string in rlib crate".to_string(), 76 }; 77 rust_log_rlib(msg); 78 } 79 ``` 80 813. Configure the GN build script **build/rust/tests/test_rlib_crate/BUILD.gn**. 82 83 ``` 84 import("//build/ohos.gni") 85 86 ohos_rust_executable("test_rlib_crate") { 87 sources = [ "src/main.rs" ] 88 deps = [ ":simple_printer_rlib" ] 89 } 90 91 ohos_rust_static_library("simple_printer_rlib") { 92 sources = [ "src/simple_printer.rs" ] 93 crate_name = "simple_printer_rlib" 94 crate_type = "rlib" 95 features = [ "std" ] 96 } 97 ``` 98 994. Run **BUILD.gn** to generate the build targets. 100 101  102 103### Configuring a Third-Party Library 104 105The **BUILD.gn** file of the rust third-party library can be automatically generated using the cargo2gn tool. For details, see [Using Cargo2gn](subsys-build-cargo2gn-guide.md). 106 107The following example shows how to use the **ohos_rust_executable** and **ohos_rust_cargo_crate** templates to compile a third-party static library rlib file that contains a prebuilt file **build.rs**. 108The procedure is as follows: 109 1101. Create **build/rust/tests/test_rlib_cargo_crate/crate/src/lib.rs**. 111 112 ```rust 113 include!(concat!(env!("OUT_DIR"), "/generated/generated.rs")); 114 115 pub fn say_hello_from_crate() { 116 assert_eq!(run_some_generated_code(), 45); 117 #[cfg(is_new_rustc)] 118 println!("Is new rustc"); 119 #[cfg(is_old_rustc)] 120 println!("Is old rustc"); 121 #[cfg(is_ohos)] 122 println!("Is ohos"); 123 #[cfg(is_mac)] 124 println!("Is darwin"); 125 #[cfg(has_feature_a)] 126 println!("Has feature_a"); 127 #[cfg(not(has_feature_a))] 128 panic!("Wasn't passed feature_a"); 129 #[cfg(not(has_feature_b))] 130 #[cfg(test_a_and_b)] 131 panic!("feature_b wasn't passed"); 132 #[cfg(has_feature_b)] 133 #[cfg(not(test_a_and_b))] 134 panic!("feature_b was passed"); 135 } 136 137 #[cfg(test)] 138 mod tests { 139 /// Test features are passed through from BUILD.gn correctly. This test is the target configuration. 140 #[test] 141 #[cfg(test_a_and_b)] 142 fn test_features_passed_target1() { 143 #[cfg(not(has_feature_a))] 144 panic!("feature a was not passed"); 145 #[cfg(not(has_feature_b))] 146 panic!("feature b was not passed"); 147 } 148 149 #[test] 150 fn test_generated_code_works() { 151 assert_eq!(crate::run_some_generated_code(), 45); 152 } 153 } 154 ``` 155 1562. Create **build/rust/tests/test_rlib_cargo_crate/crate/src/main.rs**. 157 158 ```rust 159 pub fn main() { 160 test_rlib_crate::say_hello_from_crate(); 161 } 162 ``` 163 1643. Create **build/rust/tests/test_rlib_cargo_crate/crate/build.rs**. 165 166 ```rust 167 use std::env; 168 use std::path::Path; 169 use std::io::Write; 170 use std::process::Command; 171 use std::str::{self, FromStr}; 172 173 fn main() { 174 println!("cargo:rustc-cfg=build_script_ran"); 175 let my_minor = match rustc_minor_version() { 176 Some(my_minor) => my_minor, 177 None => return, 178 }; 179 180 if my_minor >= 34 { 181 println!("cargo:rustc-cfg=is_new_rustc"); 182 } else { 183 println!("cargo:rustc-cfg=is_old_rustc"); 184 } 185 186 let target = env::var("TARGET").unwrap(); 187 188 if target.contains("ohos") { 189 println!("cargo:rustc-cfg=is_ohos"); 190 } 191 if target.contains("darwin") { 192 println!("cargo:rustc-cfg=is_mac"); 193 } 194 195 let feature_a = env::var_os("CARGO_FEATURE_MY_FEATURE_A").is_some(); 196 if feature_a { 197 println!("cargo:rustc-cfg=has_feature_a"); 198 } 199 let feature_b = env::var_os("CARGO_FEATURE_MY_FEATURE_B").is_some(); 200 if feature_b { 201 println!("cargo:rustc-cfg=has_feature_b"); 202 } 203 204 // Tests used to verify whether Cargo features are enabled. 205 assert!(Path::new("build.rs").exists()); 206 assert!(Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("build.rs").exists()); 207 assert!(Path::new(&env::var_os("OUT_DIR").unwrap()).exists()); 208 209 // Ensure that the following env var is set. 210 env::var_os("CARGO_CFG_TARGET_ARCH").unwrap(); 211 212 generate_some_code().unwrap(); 213 } 214 215 fn generate_some_code() -> std::io::Result<()> { 216 let test_output_dir = Path::new(&env::var_os("OUT_DIR").unwrap()).join("generated"); 217 let _ = std::fs::create_dir_all(&test_output_dir); 218 // Test that environment variables from .gn files are passed to build scripts. 219 let preferred_number = env::var("ENV_VAR_FOR_BUILD_SCRIPT").unwrap(); 220 let mut file = std::fs::File::create(test_output_dir.join("generated.rs"))?; 221 write!(file, "fn run_some_generated_code() -> u32 {{ {} }}", preferred_number)?; 222 Ok(()) 223 } 224 225 fn rustc_minor_version() -> Option<u32> { 226 let rustc_bin = match env::var_os("RUSTC") { 227 Some(rustc_bin) => rustc_bin, 228 None => return None, 229 }; 230 231 let output = match Command::new(rustc_bin).arg("--version").output() { 232 Ok(output) => output, 233 Err(_) => return None, 234 }; 235 236 let rustc_version = match str::from_utf8(&output.stdout) { 237 Ok(rustc_version) => rustc_version, 238 Err(_) => return None, 239 }; 240 241 let mut pieces = rustc_version.split('.'); 242 if pieces.next() != Some("rustc 1") { 243 return None; 244 } 245 246 let next_var = match pieces.next() { 247 Some(next_var) => next_var, 248 None => return None, 249 }; 250 251 u32::from_str(next_var).ok() 252 } 253 ``` 254 2554. Configure the GN build script **build/rust/tests/test_rlib_cargo_crate/BUILD.gn**. 256 257 ``` 258 import("//build/templates/rust/ohos_cargo_crate.gni") 259 260 ohos_cargo_crate("target") { 261 crate_name = "test_rlib_crate" 262 crate_root = "crate/src/lib.rs" 263 sources = [ "crate/src/lib.rs" ] 264 265 #To generate the build_script binary 266 build_root = "crate/build.rs" 267 build_sources = [ "crate/build.rs" ] 268 build_script_outputs = [ "generated/generated.rs" ] 269 270 features = [ 271 "my-feature_a", 272 "my-feature_b", 273 "std", 274 ] 275 rustflags = [ 276 "--cfg", 277 "test_a_and_b", 278 ] 279 rustenv = [ "ENV_VAR_FOR_BUILD_SCRIPT=45" ] 280 } 281 282 # Exists to test the case that a single crate has both a library and a binary 283 ohos_cargo_crate("test_rlib_crate_associated_bin") { 284 crate_root = "crate/src/main.rs" 285 crate_type = "bin" 286 sources = [ "crate/src/main.rs" ] 287 288 #To generate the build_script binary 289 build_root = "crate/build.rs" 290 build_sources = [ "crate/build.rs" ] 291 features = [ 292 "my-feature_a", 293 "my-feature_b", 294 "std", 295 ] 296 rustenv = [ "ENV_VAR_FOR_BUILD_SCRIPT=45" ] 297 deps = [ ":target" ] 298 } 299 ``` 300 3015. Run **BUILD.gn** to generate the build target. 302 303  304 305### Other Configuration Examples 306You can find the Rust module configuration examples in the **build/rust/tests** directory. 307 308| Directory | Description | 309|----------------------------------------------|----------------------------------------------------------| 310| build/rust/tests/test_bin_crate | Tests the build of an executable file on the host platform and running of the executable file on the target platform.| 311| build/rust/tests/test_static_link | Tests the static linking of an executable file to a standard library. | 312| build/rust/tests/test_dylib_crate | Tests the build of a dynamic library and dynamic linking. | 313| build/rust/tests/test_rlib_crate | Tests the build of a static library and static linking. | 314| build/rust/tests/test_proc_macro_crate | Tests the build of Rust process macros and the linking function. Test cases are provided for different types of macros. | 315| build/rust/tests/test_cdylib_crate | Tests the generation of Rust FFI bindings to a C/C++ dynamic library. | 316| build/rust/tests/test_staticlib_crate | Tests the generation of Rust FFI bindings to a C/C++ static library. | 317| build/rust/tests/rust_test_ut | Tests the Rust code unit test template. | 318| build/rust/tests/rust_test_st | Tests the Rust code system test template. | 319| build/rust/tests/test_bin_cargo_crate | Tests the build and running of a Rust third-party executable file. The third-party source code contains **build.rs**. | 320| build/rust/tests/test_rlib_cargo_crate | Tests the build of a Rust third-party static library and static linking. The third-party source code contains **build.rs**. | 321| build/rust/tests/test_proc_macro_cargo_crate | Tests the build of Rust third-party process macros and linking. The third-party source code contains **build.rs**. | 322| build/rust/tests/rust_test_fuzzb | Tests the Rust code fuzzing test template. | 323## Reference 324 325### Feature Examples 326 327#### Linking a C/C++ library in Rust Source Code 328By default, the dynamic library of the OpenHarmony C/C++ module is in the **.z.so** format. However, when the Rust **-l** command is executed, only the dynamic library in the **.so** format is linked by default. If a C/C++ dynamic library is used as the dependency, you need to add **output_extension = "so"** to the GN build script of the dynamic library to make the generated dynamic library be named with **.so** instead of **.z.so**. 329If a dynamic library is directly linked in the Rust source code, the dynamic library must be in **.so** format. In this case, use the dynamic library name without "lib". The following is an example of linking **libhilog.so** in the Rust source code. 330 331```rust 332#[link(name = "hilog")] 333``` 334#### Using externs 335If a module depends on the binary rlib library, you can use the **externs** attribute. 336``` 337executable("foo") { 338 sources = [ "main.rs" ] 339 externs = [{ # Convert it to `--extern bar=path/to/bar.rlib` during the compilation. 340 crate_name = "bar" 341 path = "path/to/bar.rlib" 342 }] 343} 344``` 345### Lint Rules 346The OpenHarmony framework supports two types of lints: rustc lints and Clippy lints. Each type of lint has three levels: openharmony (highest), vendor, and none (lowest). 347When configuring the Rust module, you can specify the lint level in **rustc_lints** or **clippy_lints**. 348If **rustc_lints** or **clippy_lints** is not configured in the module, the lint level is matched based on the module path. Different restrictions apply to the syntax specifications of Rust code in different directories. Therefore, you need to pay attention to the path of the module when configuring the Rust module to build in OpenHarmony. 349 350#### Levels of Rustc Lints and Clippy Lints 351| **Lint Type**| **Module Attribute**| **Lint Level**| **Lint Level Flag**| **Lint Content** | 352| ------------- | ------------ | ------------- | ----------------- | ------------------------------------------------------------ | 353| rustc lints | rustc_lints | openharmony | RustOhosLints | "-A deprecated", "-D missing-docs", "-D warnings" | 354| rustc lints | rustc_lints | vendor | RustcVendorLints | "-A deprecated", "-D warnings" | 355| rustc lints | rustc_lints | none | allowAllLints | "-cap-lints allow" | 356| Clippy lints | clippy_lints| openharmony | ClippyOhosLints | "-A clippy::type-complexity", "-A clippy::unnecessary-wraps", "-A clippy::unusual-byte-groupings", "-A clippy::upper-case-acronyms" | 357| Clippy lints | clippy_lints | vendor | ClippyVendorLints | "-A clippy::complexity", "-A Clippy::perf", "-A clippy::style" | 358| Clippy lints | clippy_lints | none | allowAllLints | "--cap-lints allow" | 359 360#### Mapping Between Code Paths and Lint Levels 361| Path | Lint Level | 362| ---------- | ----------- | 363| thirdparty | none | 364| prebuilts | none | 365| vendor | vendor | 366| device | vendor | 367| others | openharmony | 368 369### [Interactive Tool User Guide](subsys-build-bindgen-cxx-guide.md) 370### [Using Cargo2gn](subsys-build-cargo2gn-guide.md) 371