未验证 提交 6bb8405b 编写于 作者: O openharmony_ci 提交者: Gitee

!18633 [翻译完成】#I6YHMX

Merge pull request !18633 from Annie_wang/PR16810
......@@ -18,6 +18,10 @@
- [Configuring Parameters for Accelerating Local Build](subsys-build-reference.md)
- [Viewing Ninja Build Information](subsys-build-reference.md)
- [HAP Build Guide](subsys-build-gn-hap-compilation-guide.md)
- Rust Build Guide
- [Rust Module Configuration Rules and Guide](subsys-build-rust-compilation.md)
- [Using Interactive Tools](subsys-build-bindgen-cxx-guide.md)
- [Using Cargo2gn](subsys-build-cargo2gn-guide.md)
- [FAQs](subsys-build-FAQ.md)
- [ArkCompiler Development](subsys-arkcompiler-guide.md)
- [Distributed Remote Startup](subsys-remote-start.md)
......
# Interactive Tool User Guide
## Introduction
You can use Bindgen and CXX to implement the interaction between Rust and C/C++. Bindgen enables Rust to call C by converting C interfaces into Rust interfaces. CXX implement interaction between C++ and Rust by generating bindings between C interfaces and Rust interfaces.
![bindgen_and_cxx_tools](./figures/bindgen_and_cxx_tools.png)
## Using Bindgen
### Procedure
The following example shows how to use Bindgen to implement invocation of C by Rust.
1. In the header file **lib.h**, define two interfaces **FuncAAddB** and **SayHello** in C. The **FuncAAddB** interface calculates the sum of two numbers, and the **SayHello** interface prints strings.
```c
#ifndef BUILD_RUST_TESTS_BINDGEN_TEST_LIB_H_
#define BUILD_RUST_TESTS_BINDGEN_TEST_LIB_H_
#include <stdint.h>
#include "build/rust/tests/test_bindgen_test/test_for_hello_world/lib2.h"
uint32_t FuncAAddB(uint32_t a, uint32_t b);
void SayHello(const char *message);
#endif // BUILD_RUST_TESTS_BINDGEN_TEST_LIB_H_
```
2. Add the implementation of the two interfaces to **lib.c**.
```c
#include "build/rust/tests/test_bindgen_test/test_for_hello_world/lib.h"
#include <stdint.h>
#include <stdio.h>
void SayHello(const char *message)
{
printf("This is a test for bindgen hello world:\n");
printf("%s\n", message);
}
uint32_t FuncAAddB(uint32_t a, uint32_t b)
{
printf("This is a test for bindgen of a + b:\n");
return a + b;
}
```
3. Create the **main.rs** file to call C interfaces through c_ffi using Rust. Note that insecure interfaces called by Rust must be encapsulated by using **unsafe**.
```rust
//! bindgen test for hello world
#![allow(clippy::approx_constant)]
mod c_ffi {
#![allow(dead_code)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
include!(env!("BINDGEN_RS_FILE"));
}
/// pub fn add_two_numbers_in_c
pub fn add_two_numbers_in_c(a: u32, b: u32) -> u32 {
unsafe { c_ffi::FuncAAddB(a, b) }
}
use std::ffi::c_char;
use std::ffi::CString;
/// fn main()
fn main() {
println!("{} + {} = {}", 3, 7, add_two_numbers_in_c(3, 7));
let c_str = CString::new("This is a message from C").unwrap();
let c_world: *const c_char = c_str.as_ptr() as *const c_char;
unsafe {
c_ffi::SayHello(c_world);
}
}
```
4. Create the **BUILD.gn** file to define the dependency of the Rust module on the C module.
```GN
import("//build/ohos.gni")
ohos_shared_library("c_lib") {
sources = [ "lib.c" ]
defines = [ "COMPONENT_IMPLEMENTATION" ]
}
rust_bindgen("c_lib_bindgen") {
header = "lib.h"
}
ohos_rust_executable("bindgen_test") {
deps = [ ":c_lib" ]
deps += [ ":c_lib_bindgen" ]
sources = [ "main.rs" ]
bindgen_output = get_target_outputs(":c_lib_bindgen")
inputs = bindgen_output
rustenv = [ "BINDGEN_RS_FILE=" + rebase_path(bindgen_output[0]) ]
crate_root = "main.rs"
}
```
**Verification**
![bindgen_test](./figures/bindgen_test.png)
## Using CXX
### Calling Rust Interfaces by C++
1. In the Rust **lib.rs** file, add the C++ interfaces to be called in **mod ffi**, and add the interfaces in **extern "Rust"** to expose them to C++.
```rust
//! #[cxx::bridge]
#[cxx::bridge]
mod ffi{
#![allow(dead_code)]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Shared {
z: usize,
}
extern "Rust"{
fn print_message_in_rust();
fn r_return_primitive() -> usize;
fn r_return_shared() -> Shared;
fn r_return_rust_string() -> String;
fn r_return_sum(_: usize, _: usize) -> usize;
}
}
fn print_message_in_rust(){
println!("Here is a test for cpp call Rust.");
}
fn r_return_shared() -> ffi::Shared {
println!("Here is a message from Rust,test for ffi::Shared:");
ffi::Shared { z: 1996 }
}
fn r_return_primitive() -> usize {
println!("Here is a message from Rust,test for usize:");
1997
}
fn r_return_rust_string() -> String {
println!("Here is a message from Rust,test for String");
"Hello World!".to_owned()
}
fn r_return_sum(n1: usize, n2: usize) -> usize {
println!("Here is a message from Rust,test for {} + {} is:",n1 ,n2);
n1 + n2
}
```
2. Include **lib.rs.h** (converted from **lib.rs** by the CXX tool) in C++ code.
```c++
#include <iostream>
#include "build/rust/tests/test_cxx/src/lib.rs.h"
int main(int argc, const char* argv[])
{
int a = 2021;
int b = 4;
print_message_in_rust();
std::cout << r_return_primitive() << std::endl;
std::cout << r_return_shared().z << std::endl;
std::cout << std::string(r_return_rust_string()) << std::endl;
std::cout << r_return_sum(a, b) << std::endl;
return 0;
}
```
3. Create the **BUILD.gn** file. The underlying rust_cxx calls the CXX tool to convert the **lib.rs** file into **lib.rs.h** and **lib.rs.cc**. **ohos_rust_static_ffi** implements compilation of the Rust source code, and **ohos_executable** implements compilation of the C++ code.
```
import("//build/ohos.gni")
import("//build/templates/rust/rust_cxx.gni")
rust_cxx("test_cxx_exe_gen") {
sources = [ "src/lib.rs" ]
}
ohos_rust_static_ffi("test_cxx_examp_rust") {
sources = [ "src/lib.rs" ]
deps = [ "//build/rust:cxx_rustdeps" ]
}
ohos_executable("test_cxx_exe") {
sources = [ "main.cpp" ]
sources += get_target_outputs(":test_cxx_exe_gen")
include_dirs = [ "${target_gen_dir}" ]
deps = [
":test_cxx_examp_rust",
":test_cxx_exe_gen",
"//build/rust:cxx_cppdeps",
]
}
```
**Verification**
![cpp_call_rust](./figures/cpp_call_rust.png)
### Calling C++ by Rust
1. Create the header file **client_blobstore.h**.
```c++
#ifndef BUILD_RUST_TESTS_CLIENT_BLOBSTORE_H
#define BUILD_RUST_TESTS_CLIENT_BLOBSTORE_H
#include <memory>
#include "third_party/rust/cxx/include/cxx.h"
namespace nsp_org {
namespace nsp_blobstore {
struct MultiBufs;
struct Metadata_Blob;
class client_blobstore {
public:
client_blobstore();
uint64_t put_buf(MultiBufs &buf) const;
void add_tag(uint64_t blobid, rust::Str add_tag) const;
Metadata_Blob get_metadata(uint64_t blobid) const;
private:
class impl;
std::shared_ptr<impl> impl;
};
std::unique_ptr<client_blobstore> blobstore_client_new();
} // namespace nsp_blobstore
} // namespace nsp_org
#endif
```
2. Create the **client_blobstore.cpp** file.
```c++
#include <algorithm>
#include <functional>
#include <set>
#include <string>
#include <unordered_map>
#include "src/main.rs.h"
#include "build/rust/tests/test_cxx_rust/include/client_blobstore.h"
namespace nsp_org {
namespace nsp_blobstore {
// Toy implementation of an in-memory nsp_blobstore.
//
// The real implementation of client_blobstore could be a large complex C++
// library.
class client_blobstore::impl {
friend client_blobstore;
using Blob = struct {
std::string data;
std::set<std::string> tags;
};
std::unordered_map<uint64_t, Blob> blobs;
};
client_blobstore::client_blobstore() : impl(new class client_blobstore::impl) {}
// Upload a new blob and return a blobid that serves as a handle to the blob.
uint64_t client_blobstore::put_buf(MultiBufs &buf) const
{
std::string contents;
// Traverse the caller's res_chunk iterator.
//
// In reality there might be sophisticated batching of chunks and/or parallel
// upload implemented by the nsp_blobstore's C++ client.
while (true) {
auto res_chunk = next_chunk(buf);
if (res_chunk.size() == 0) {
break;
}
contents.append(reinterpret_cast<const char *>(res_chunk.data()), res_chunk.size());
}
// Insert into map and provide caller the handle.
auto res = std::hash<std::string> {} (contents);
impl->blobs[res] = {std::move(contents), {}};
return res;
}
// Add add_tag to an existing blob.
void client_blobstore::add_tag(uint64_t blobid, rust::Str add_tag) const
{
impl->blobs[blobid].tags.emplace(add_tag);
}
// Retrieve get_metadata about a blob.
Metadata_Blob client_blobstore::get_metadata(uint64_t blobid) const
{
Metadata_Blob get_metadata {};
auto blob = impl->blobs.find(blobid);
if (blob != impl->blobs.end()) {
get_metadata.size = blob->second.data.size();
std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(),
[&](auto &t) { get_metadata.tags.emplace_back(t); });
}
return get_metadata;
}
std::unique_ptr<client_blobstore> blobstore_client_new()
{
return std::make_unique<client_blobstore>();
}
} // namespace nsp_blobstore
} // namespace nsp_org
```
3. In **ffi** of the **main.rs** file, use the macro **includes!** to import the header file **client_blobstore.h**. Then, the **main()** function of Rust can call the C++ interfaces in ffi mode.
```rust
//! test_cxx_rust
#[cxx::bridge(namespace = "nsp_org::nsp_blobstore")]
mod ffi {
// Shared structs with fields visible to both languages.
struct Metadata_Blob {
size: usize,
tags: Vec<String>,
}
// Rust types and signatures exposed to C++.
extern "Rust" {
type MultiBufs;
fn next_chunk(buf: &mut MultiBufs) -> &[u8];
}
// C++ types and signatures exposed to Rust.
unsafe extern "C++" {
include!("build/rust/tests/test_cxx_rust/include/client_blobstore.h");
type client_blobstore;
fn blobstore_client_new() -> UniquePtr<client_blobstore>;
fn put_buf(&self, parts: &mut MultiBufs) -> u64;
fn add_tag(&self, blobid: u64, add_tag: &str);
fn get_metadata(&self, blobid: u64) -> Metadata_Blob;
}
}
// An iterator over contiguous chunks of a discontiguous file object.
//
// Toy implementation uses a Vec<Vec<u8>> but in reality this might be iterating
// over some more complex Rust data structure like a rope, or maybe loading
// chunks lazily from somewhere.
/// pub struct MultiBufs
pub struct MultiBufs {
chunks: Vec<Vec<u8>>,
pos: usize,
}
/// pub fn next_chunk
pub fn next_chunk(buf: &mut MultiBufs) -> &[u8] {
let next = buf.chunks.get(buf.pos);
buf.pos += 1;
next.map_or(&[], Vec::as_slice)
}
/// fn main()
fn main() {
let client = ffi::blobstore_client_new();
// Upload a blob.
let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()];
let mut buf = MultiBufs { chunks, pos: 0 };
let blobid = client.put_buf(&mut buf);
println!("This is a test for Rust call cpp:");
println!("blobid = {}", blobid);
// Add a add_tag.
client.add_tag(blobid, "rust");
// Read back the tags.
let get_metadata = client.get_metadata(blobid);
println!("tags = {:?}", get_metadata.tags);
}
```
4. Create the **BUILD.gn** file. Use CXX to convert **main.rs** into **lib.rs.h** and **lib.rs.cc**, which are used as the source code of **test_cxx_rust_staticlib**. Compile Rust **main.rs**, and add the dependency **test_cxx_rust_staticlib**.
```
import("//build/ohos.gni")
rust_cxx("test_cxx_rust_gen") {
sources = [ "src/main.rs" ]
}
ohos_static_library("test_cxx_rust_staticlib") {
sources = [ "src/client_blobstore.cpp" ]
sources += get_target_outputs(":test_cxx_rust_gen")
include_dirs = [
"${target_gen_dir}",
"//third_party/rust/cxx/v1/crate/include",
"include",
]
deps = [
":test_cxx_rust_gen",
"//build/rust:cxx_cppdeps",
]
}
ohos_rust_executable("test_cxx_rust") {
sources = [ "src/main.rs" ]
deps = [
":test_cxx_rust_staticlib",
"//build/rust:cxx_rustdeps",
]
}
```
**Verification**
![rust_call_cpp](./figures/rust_call_cpp.png)
# Using Cargo2gn
## Overview
Rust uses Cargo as its build system and package manager. **Cargo.toml** is the manifest file of Cargo. It contains metadata such as the name, version, and dependencies for crates (packages) in Rust.
When used in OpenHarmony, **Cargo.toml** needs to be converted into **BUILD.gn** rules. cargo2gn is the tool that can achieve this purpose. It can convert one or more Rust libraries.
## Converting a Single Library
1. Go to the directory of the rust library to be converted, for example, **bindgen**.
```
cd openharmony/third_party/rust/bindgen
```
2. Create the configuration file **cargo2gn.json**.
```
{
"copy-out": true,
"run": true,
"add-workspace": true,
"cargo-bin": "/mnt/xxx/openharmony/prebuilts/rustc/linux-x86_64/current/bin"
}
```
3. Run the following command to perform the conversion:
```
python /mnt/xxx/openharmony/build/scripts/cargo2gn.py --config cargo2gn.json
```
The conversion result is as follows:
```
import("//build/templates/rust/ohos_cargo_crate.gni")
ohos_cargo_crate("lib") {
crate_name = "bindgen"
crate_type = "rlib"
crate_root = "./lib.rs"
sources = ["./lib.rs"]
edition = "2018"
cargo_pkg_version = "0.64.0"
cargo_pkg_authors = "Jyun-Yan You <jyyou.tw@gmail.com>, Emilio Cobos Álvarez <emilio@crisal.io>, Nick Fitzgerald <fitzgen@gmail.com>, The Servo project developers"
cargo_pkg_name = "bindgen"
cargo_pkg_description = "Automatically generates Rust FFI bindings to C and C++ libraries."
deps = [
"//third_party/rust/bitflags:lib",
"//third_party/rust/cexpr:lib",
"//third_party/rust/clang-sys:lib",
"//third_party/rust/lazy_static:lib",
"//third_party/rust/lazycell:lib",
"//third_party/rust/log:lib",
"//third_party/rust/peeking_take_while:lib",
"//third_party/rust/proc-macro2:lib",
"//third_party/rust/quote:lib",
"//third_party/rust/regex:lib",
"//third_party/rust/rustc-hash:lib",
"//third_party/rust/shlex:lib",
"//third_party/rust/syn:lib",
"//third_party/rust/which:lib",
]
features = [
"default",
"log",
"logging",
"static",
"which",
"which-rustfmt",
]
build_root = "build.rs"
build_sources = ["build.rs"]
build_script_outputs = ["host-target.txt"]
}
```
## Converting Multiple Libraries in Batches
1. Go to the **rust** directory.
```
cd openharmony/third_party/rust
```
2. Add all the rust libraries to be converted to [workspace] in **Cargo.toml** in the **rust** directory.
```
[workspace]
members = [
"aho-corasick",
"memchr",
]
```
3. Perform steps 2 and 3 in section "Converting a Single Library".
# Rust Module Configuration Rules and Guide
## Introduction
Rust 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.
To 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.
To 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.
### Basic Concepts
| Term | Description |
| ----- | ------------------------------------------------------------ |
| Cargo | Cargo is an official build tool used by Rust. It allows Rust projects to declare dependencies and ensures reproducible builds.|
| crate | Crate is a unit that can be independently compiled. |
| 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.|
## Configure Rules
OpenHarmony provides a variety of GN templates for compiling Rust executables, dynamic libraries, and static libraries. The following table describes the templates.
| GN Template | Description | Output |
| ------------------------ | ----------------- | ----------------------------------------------- |
| ohos_rust_executable | Rust executable file. | Rust executable file, without the file name extension. |
| ohos_rust_shared_library | Rust dynamic library. | Rust dylib dynamic library, with the default file name extension **.dylib.so**. |
| ohos_rust_static_library | Rust static library. | Rust rlib static library, with the default file name extension **.rlib**. |
| ohos_rust_proc_macro | Rust proc_macro library. | Rust proc_macro library, with the default file name extension **.so**. |
| 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**.|
| 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**. |
| ohos_rust_cargo_crate | Third-party Cargo crate.| Third-party Rust crates, which support rlib, dylib, and bin. |
| ohos_rust_systemtest | Rust system test cases. | Executable system test cases for Rust, without the file name extension. |
| ohos_rust_unittest | Rust unit test cases. | Executable unit test cases for Rust, without the file name extension. |
## Configuration Guide
The 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.
### Configuring a Rust Static Library
The 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.
The procedure is as follows:
1. Create **build/rust/tests/test_rlib_crate/src/simple_printer.rs**.
```rust
//! simple_printer
/// struct RustLogMessage
pub struct RustLogMessage {
/// i32: id
pub id: i32,
/// String: msg
pub msg: String,
}
/// function rust_log_rlib
pub fn rust_log_rlib(msg: RustLogMessage) {
println!("id:{} message:{:?}", msg.id, msg.msg)
}
```
2. Create **build/rust/tests/test_rlib_crate/src/main.rs**.
```rust
//! rlib_crate example for Rust.
extern crate simple_printer_rlib;
use simple_printer_rlib::rust_log_rlib;
use simple_printer_rlib::RustLogMessage;
fn main() {
let msg: RustLogMessage = RustLogMessage {
id: 0,
msg: "string in rlib crate".to_string(),
};
rust_log_rlib(msg);
}
```
3. Configure the GN build script **build/rust/tests/test_rlib_crate/BUILD.gn**.
```
import("//build/ohos.gni")
ohos_rust_executable("test_rlib_crate") {
sources = [ "src/main.rs" ]
deps = [ ":simple_printer_rlib" ]
}
ohos_rust_static_library("simple_printer_rlib") {
sources = [ "src/simple_printer.rs" ]
crate_name = "simple_printer_rlib"
crate_type = "rlib"
features = [ "std" ]
}
```
4. Run the **BUILD.gn** to generate the build targets.
![test_rlib_crate](./figures/test_rlib_crate.png)
### Configuring a Third-Party Library
The following example shows how to use the **ohos_rust_executable** and **ohos_rust_cargo_crate** templates to build a third-party static rlib library whose code contains a prebuilt file **build.rs**.
The procedure is as follows:
1. Create **build/rust/tests/test_rlib_cargo_crate/crate/src/lib.rs**.
```rust
include!(concat!(env!("OUT_DIR"), "/generated/generated.rs"));
pub fn say_hello_from_crate() {
assert_eq!(run_some_generated_code(), 45);
#[cfg(is_new_rustc)]
println!("Is new rustc");
#[cfg(is_old_rustc)]
println!("Is old rustc");
#[cfg(is_ohos)]
println!("Is ohos");
#[cfg(is_mac)]
println!("Is darwin");
#[cfg(has_feature_a)]
println!("Has feature_a");
#[cfg(not(has_feature_a))]
panic!("Wasn't passed feature_a");
#[cfg(not(has_feature_b))]
#[cfg(test_a_and_b)]
panic!("feature_b wasn't passed");
#[cfg(has_feature_b)]
#[cfg(not(test_a_and_b))]
panic!("feature_b was passed");
}
#[cfg(test)]
mod tests {
/// Test features are passed through from BUILD.gn correctly. This test is the target configuration.
#[test]
#[cfg(test_a_and_b)]
fn test_features_passed_target1() {
#[cfg(not(has_feature_a))]
panic!("feature a was not passed");
#[cfg(not(has_feature_b))]
panic!("feature b was not passed");
}
#[test]
fn test_generated_code_works() {
assert_eq!(crate::run_some_generated_code(), 45);
}
}
```
2. Create **build/rust/tests/test_rlib_cargo_crate/crate/src/main.rs**.
```rust
pub fn main() {
test_rlib_crate::say_hello_from_crate();
}
```
3. Create **build/rust/tests/test_rlib_cargo_crate/crate/build.rs**.
```rust
use std::env;
use std::path::Path;
use std::io::Write;
use std::process::Command;
use std::str::{self, FromStr};
fn main() {
println!("cargo:rustc-cfg=build_script_ran");
let my_minor = match rustc_minor_version() {
Some(my_minor) => my_minor,
None => return,
};
if my_minor >= 34 {
println!("cargo:rustc-cfg=is_new_rustc");
} else {
println!("cargo:rustc-cfg=is_old_rustc");
}
let target = env::var("TARGET").unwrap();
if target.contains("ohos") {
println!("cargo:rustc-cfg=is_ohos");
}
if target.contains("darwin") {
println!("cargo:rustc-cfg=is_mac");
}
let feature_a = env::var_os("CARGO_FEATURE_MY_FEATURE_A").is_some();
if feature_a {
println!("cargo:rustc-cfg=has_feature_a");
}
let feature_b = env::var_os("CARGO_FEATURE_MY_FEATURE_B").is_some();
if feature_b {
println!("cargo:rustc-cfg=has_feature_b");
}
// Tests used to verify whether Cargo features are enabled.
assert!(Path::new("build.rs").exists());
assert!(Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("build.rs").exists());
assert!(Path::new(&env::var_os("OUT_DIR").unwrap()).exists());
// Ensure that the following env var is set.
env::var_os("CARGO_CFG_TARGET_ARCH").unwrap();
generate_some_code().unwrap();
}
fn generate_some_code() -> std::io::Result<()> {
let test_output_dir = Path::new(&env::var_os("OUT_DIR").unwrap()).join("generated");
let _ = std::fs::create_dir_all(&test_output_dir);
// Test that environment variables from .gn files are passed to build scripts.
let preferred_number = env::var("ENV_VAR_FOR_BUILD_SCRIPT").unwrap();
let mut file = std::fs::File::create(test_output_dir.join("generated.rs"))?;
write!(file, "fn run_some_generated_code() -> u32 {{ {} }}", preferred_number)?;
Ok(())
}
fn rustc_minor_version() -> Option<u32> {
let rustc_bin = match env::var_os("RUSTC") {
Some(rustc_bin) => rustc_bin,
None => return None,
};
let output = match Command::new(rustc_bin).arg("--version").output() {
Ok(output) => output,
Err(_) => return None,
};
let rustc_version = match str::from_utf8(&output.stdout) {
Ok(rustc_version) => rustc_version,
Err(_) => return None,
};
let mut pieces = rustc_version.split('.');
if pieces.next() != Some("rustc 1") {
return None;
}
let next_var = match pieces.next() {
Some(next_var) => next_var,
None => return None,
};
u32::from_str(next_var).ok()
}
```
4. Configure the GN build script **build/rust/tests/test_rlib_cargo_crate/BUILD.gn**.
```
import("//build/templates/rust/ohos_cargo_crate.gni")
ohos_cargo_crate("target") {
crate_name = "test_rlib_crate"
crate_root = "crate/src/lib.rs"
sources = [ "crate/src/lib.rs" ]
#To generate the build_script binary
build_root = "crate/build.rs"
build_sources = [ "crate/build.rs" ]
build_script_outputs = [ "generated/generated.rs" ]
features = [
"my-feature_a",
"my-feature_b",
"std",
]
rustflags = [
"--cfg",
"test_a_and_b",
]
rustenv = [ "ENV_VAR_FOR_BUILD_SCRIPT=45" ]
}
# Exists to test the case that a single crate has both a library and a binary
ohos_cargo_crate("test_rlib_crate_associated_bin") {
crate_root = "crate/src/main.rs"
crate_type = "bin"
sources = [ "crate/src/main.rs" ]
#To generate the build_script binary
build_root = "crate/build.rs"
build_sources = [ "crate/build.rs" ]
features = [
"my-feature_a",
"my-feature_b",
"std",
]
rustenv = [ "ENV_VAR_FOR_BUILD_SCRIPT=45" ]
deps = [ ":target" ]
}
```
5. Run the **BUILD.gn** to generate the build target.
![test_rlib_cargo_crate](./figures/test_rlib_cargo_crate.png)
### Other Configuration Examples
You can find the Rust module configuration examples in the **build/rust/tests** directory.
| Directory | Description |
| -------------------------------------------- | ------------------------------------------------------------ |
| 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. |
| build/rust/tests/test_static_link | Tests the static linking of an executable file to a standard library. |
| build/rust/tests/test_dylib_crate | Tests the build of a dynamic library and dynamic linking. |
| build/rust/tests/test_rlib_crate | Tests the build of a static library and static linking. |
| 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. |
| build/rust/tests/test_cdylib_crate | Tests the generation of Rust FFI bindings to a C/C++ dynamic library. |
| build/rust/tests/test_staticlib_crate | Tests the generation of Rust FFI bindings to a C/C++ static library. |
| build/rust/tests/test_rust_ut | Tests the Rust code unit test template. |
| build/rust/tests/test_rust_st | Tests the Rust code system test template. |
| build/rust/tests/test_bin_cargo_crate | Tests the build and running of a Rust third-party executable file. The third-party code contains the **build.rs**. |
| build/rust/tests/test_rlib_cargo_crate | Tests the build of a Rust third-party static library and static linking. The third-party code contains the **build.rs**. |
| build/rust/tests/test_proc_macro_cargo_crate | Tests the build of Rust third-party process macros and linking. The third-party code contains the **build.rs**. |
## Reference
### Feature Examples
#### Linking a C/C++ library in Rust Source Code
By 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**.
If 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.
```rust
#[link(name = "hilog")]
```
#### Using externs
If a module depends on the binary rlib library, you can use the **externs** attribute.
```
executable("foo") {
sources = [ "main.rs" ]
externs = [{ # Convert it to `--extern bar=path/to/bar.rlib` during the compilation.
crate_name = "bar"
path = "path/to/bar.rlib"
}]
}
```
### Lint Rules
The 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).
When configuring the Rust module, you can specify the lint level in **rustc_lints** or **clippy_lints**.
If **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.
#### Levels of rustc Lints and Clippy Lints
| **Lint Type**| **Module Attribute**| **Lint Level**| **Lint Level Flag**| **Lint Content** |
| ------------- | ------------ | ------------- | ----------------- | ------------------------------------------------------------ |
| rustc lints | rustc_lints | openharmony | RustOhosLints | "-A deprecated", "-D missing-docs", "-D warnings" |
| rustc lints | rustc_lints | vendor | RustcVendorLints | "-A deprecated", "-D warnings" |
| rustc lints | rustc_lints | none | allowAllLints | "-cap-lints allow" |
| Clippy lints | clippy_lints | openharmony | ClippyOhosLints | "-A clippy::type-complexity", "-A clippy::unnecessary-wraps", "-A clippy::unusual-byte-groupings", "-A clippy::upper-case-acronyms" |
| Clippy lints | clippy_lints | vendor | ClippyVendorLints | "-A clippy::complexity", "-A Clippy::perf", "-A clippy::style" |
| Clippy lints | clippy_lints | none | allowAllLints | "--cap-lints allow" |
#### Mapping Between Code Paths and Lint Levels
| Path | Lint Level |
| ---------- | ----------- |
| thirdparty | none |
| prebuilts | none |
| vendor | vendor |
| device | vendor |
| others | openharmony |
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册