提交 03c40788 编写于 作者: L lubinglun

Optimize the description of rust module configuration

Issue:I6JNH

Test:None
Signed-off-by: Nlubinglun <lubinglun@huawei.com>
Change-Id: I02acbc058785ac47065f1946229cfe939893210f
上级 3f3bba19
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
- [HAP编译构建指导](subsys-build-gn-hap-compilation-guide.md) - [HAP编译构建指导](subsys-build-gn-hap-compilation-guide.md)
- Rust编译构建指导 - Rust编译构建指导
- [Rust模块配置规则和指导](subsys-build-rust-compilation.md) - [Rust模块配置规则和指导](subsys-build-rust-compilation.md)
- [Bindgen、CXX工具使用指导](subsys-build-bindgen-cxx-guide.md) - [交互工具使用指导](subsys-build-bindgen-cxx-guide.md)
- [Cargo2gn工具操作指导](subsys-build-cargo2gn-guide.md) - [Cargo2gn工具操作指导](subsys-build-cargo2gn-guide.md)
- [ 常见问题](subsys-build-FAQ.md) - [ 常见问题](subsys-build-FAQ.md)
- [ArkCompiler](subsys-arkcompiler-guide.md) - [ArkCompiler](subsys-arkcompiler-guide.md)
......
...@@ -2,13 +2,13 @@ ...@@ -2,13 +2,13 @@
## 概述 ## 概述
Bindgen和CXX工具的主要功能是为了实现Rust和C/C++之间的交互,其中,Bindgen通过将C接口转换为Rust接口来实现Rust对C的调用,CXX可以通过建立C接口和Rust接口的映射关系来实现C++和Rust的相互调用。 Bindgen和CXX工具的主要功能是实现Rust和C/C++之间的交互。其中,Bindgen通过将C接口转换为Rust接口来实现Rust对C的调用,CXX可以通过建立C接口和Rust接口的映射关系来实现C++和Rust的相互调用。
![bindgen_and_cxx_tools](./figures/bindgen_and_cxx_tools.png) ![bindgen_and_cxx_tools](./figures/bindgen_and_cxx_tools.png)
## Bindgen工具使用指导 ## Bindgen工具使用指导
### Rust调用C示例 ### 操作步骤
下面是一个使用bindgen实现Rust调用C的示例。 下面是一个使用bindgen实现Rust调用C的示例。
1. 在C代码侧,使用头文件lib.h定义两个接口,接口FuncAAddB用来实现两数求和,接口SayHello用来打印字符串。 1. 在C代码侧,使用头文件lib.h定义两个接口,接口FuncAAddB用来实现两数求和,接口SayHello用来打印字符串。
...@@ -28,95 +28,84 @@ Bindgen和CXX工具的主要功能是为了实现Rust和C/C++之间的交互, ...@@ -28,95 +28,84 @@ Bindgen和CXX工具的主要功能是为了实现Rust和C/C++之间的交互,
2. 在lib.c中添加对两个接口的对应实现。 2. 在lib.c中添加对两个接口的对应实现。
```c ```c
#include "build/rust/tests/test_bindgen_test/test_for_hello_world/lib.h" #include "build/rust/tests/test_bindgen_test/test_for_hello_world/lib.h"
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
void SayHello(const char *message) void SayHello(const char *message)
{ {
printf("This is a test for bindgen hello world:\n"); printf("This is a test for bindgen hello world:\n");
printf("%s\n", message); printf("%s\n", message);
} }
uint32_t FuncAAddB(uint32_t a, uint32_t b) uint32_t FuncAAddB(uint32_t a, uint32_t b)
{ {
printf("This is a test for bindgen of a + b:\n"); printf("This is a test for bindgen of a + b:\n");
return a + b; return a + b;
} }
``` ```
3. 在Rust侧文件main.rs的c_ffi中添加"include!(env!("BINDGEN_RS_FILE"))"",将bindgen转换出来的.rs文件导入到Rust侧,通过c_ffi的方式就能使用C的接口,注意Rust侧调用的不安全接口需要实现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. 添加构建文件BUILD.gn,建立Rust模块对C模块的依赖。
```GN 3. 添加文件main.rs,就可以在Rust侧通过c_ffi实现对C侧的接口调用。注意Rust侧调用的不安全接口需要使用unsafe封装。
import("//build/ohos.gni")
```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);
}
}
ohos_shared_library("c_lib") { ```
sources = [ "lib.c" ]
defines = [ "COMPONENT_IMPLEMENTATION" ]
}
rust_bindgen("c_lib_bindgen") { 4. 添加构建文件BUILD.gn,建立Rust模块对C模块的依赖。
header = "lib.h"
}
ohos_rust_executable("bindgen_test") { ```GN
deps = [ ":c_lib" ] import("//build/ohos.gni")
deps += [ ":c_lib_bindgen" ]
sources = [ "main.rs" ] ohos_shared_library("c_lib") {
bindgen_output = get_target_outputs(":c_lib_bindgen") sources = [ "lib.c" ]
inputs = bindgen_output defines = [ "COMPONENT_IMPLEMENTATION" ]
rustenv = [ "BINDGEN_RS_FILE=" + rebase_path(bindgen_output[0]) ] }
crate_root = "main.rs"
} 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"
}
```
调测验证 **调测验证**
![bindgen_test](./figures/bindgen_test.png) ![bindgen_test](./figures/bindgen_test.png)
### 参考文档
更多用例参考:
[rust-bindgen/bindgen-tests/tests/headers at main · rust-lang/rust-bindgen · GitHub](https://github.com/rust-lang/rust-bindgen/tree/main/bindgen-tests/tests/headers)
[rust-bindgen/bindgen-tests/tests/expectations/tests at main · rust-lang/rust-bindgen · GitHub](https://github.com/rust-lang/rust-bindgen/tree/main/bindgen-tests/tests/expectations/tests)
[Generating Bindings to C++ - The `bindgen` User Guide (rust-lang.github.io)](https://rust-lang.github.io/rust-bindgen/cpp.html)
## CXX工具使用指导 ## CXX工具使用指导
...@@ -124,94 +113,94 @@ ohos_rust_executable("bindgen_test") { ...@@ -124,94 +113,94 @@ ohos_rust_executable("bindgen_test") {
1. 在Rust侧文件lib.rs里mod ffi写清楚需要调用的C++接口,并将接口包含在extern "Rust"里面,暴露给C++侧使用。 1. 在Rust侧文件lib.rs里mod ffi写清楚需要调用的C++接口,并将接口包含在extern "Rust"里面,暴露给C++侧使用。
```rust ```rust
//! #[cxx::bridge] //! #[cxx::bridge]
#[cxx::bridge] #[cxx::bridge]
mod ffi{ mod ffi{
#![allow(dead_code)] #![allow(dead_code)]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Shared { struct Shared {
z: usize, z: usize,
} }
extern "Rust"{ extern "Rust"{
fn print_message_in_rust(); fn print_message_in_rust();
fn r_return_primitive() -> usize; fn r_return_primitive() -> usize;
fn r_return_shared() -> Shared; fn r_return_shared() -> Shared;
fn r_return_rust_string() -> String; fn r_return_rust_string() -> String;
fn r_return_sum(_: usize, _: usize) -> usize; fn r_return_sum(_: usize, _: usize) -> usize;
} }
} }
fn print_message_in_rust(){ fn print_message_in_rust(){
println!("Here is a test for cpp call Rust."); println!("Here is a test for cpp call Rust.");
} }
fn r_return_shared() -> ffi::Shared { fn r_return_shared() -> ffi::Shared {
println!("Here is a message from Rust,test for ffi::Shared:"); println!("Here is a message from Rust,test for ffi::Shared:");
ffi::Shared { z: 1996 } ffi::Shared { z: 1996 }
} }
fn r_return_primitive() -> usize { fn r_return_primitive() -> usize {
println!("Here is a message from Rust,test for usize:"); println!("Here is a message from Rust,test for usize:");
1997 1997
} }
fn r_return_rust_string() -> String { fn r_return_rust_string() -> String {
println!("Here is a message from Rust,test for String"); println!("Here is a message from Rust,test for String");
"Hello World!".to_owned() "Hello World!".to_owned()
} }
fn r_return_sum(n1: usize, n2: usize) -> usize { fn r_return_sum(n1: usize, n2: usize) -> usize {
println!("Here is a message from Rust,test for {} + {} is:",n1 ,n2); println!("Here is a message from Rust,test for {} + {} is:",n1 ,n2);
n1 + n2 n1 + n2
} }
``` ```
2. C++侧将cxx工具转换出来的lib.rs.h包含进来,就可以使用C++侧的接口。 2. C++侧将cxx工具转换出来的lib.rs.h包含进来,就可以使用C++侧的接口。
```c++ ```c++
#include <iostream> #include <iostream>
#include "build/rust/tests/test_cxx/src/lib.rs.h" #include "build/rust/tests/test_cxx/src/lib.rs.h"
int main(int argc, const char* argv[]) int main(int argc, const char* argv[])
{ {
int a = 2021; int a = 2021;
int b = 4; int b = 4;
print_message_in_rust(); print_message_in_rust();
std::cout << r_return_primitive() << std::endl; std::cout << r_return_primitive() << std::endl;
std::cout << r_return_shared().z << std::endl; std::cout << r_return_shared().z << std::endl;
std::cout << std::string(r_return_rust_string()) << std::endl; std::cout << std::string(r_return_rust_string()) << std::endl;
std::cout << r_return_sum(a, b) << std::endl; std::cout << r_return_sum(a, b) << std::endl;
return 0; return 0;
} }
``` ```
3. 添加构建文件BUILD.gn。rust_cxx底层调用CXX工具将lib.rs文件转换成lib.rs.h和lib.rs.cc文件,ohos_rust_static_ffi实现Rust侧源码的编译,ohos_executable实现C++侧代码的编译。 3. 添加构建文件BUILD.gn。rust_cxx底层调用CXX工具将lib.rs文件转换成lib.rs.h和lib.rs.cc文件,ohos_rust_static_ffi实现Rust侧源码的编译,ohos_executable实现C++侧代码的编译。
``` ```
import("//build/ohos.gni") import("//build/ohos.gni")
import("//build/templates/rust/rust_cxx.gni") import("//build/templates/rust/rust_cxx.gni")
rust_cxx("test_cxx_exe_gen") { rust_cxx("test_cxx_exe_gen") {
sources = [ "src/lib.rs" ] sources = [ "src/lib.rs" ]
} }
ohos_rust_static_ffi("test_cxx_examp_rust") { ohos_rust_static_ffi("test_cxx_examp_rust") {
sources = [ "src/lib.rs" ] sources = [ "src/lib.rs" ]
deps = [ "//build/rust:cxx_rustdeps" ] deps = [ "//build/rust:cxx_rustdeps" ]
} }
ohos_executable("test_cxx_exe") { ohos_executable("test_cxx_exe") {
sources = [ "main.cpp" ] sources = [ "main.cpp" ]
sources += get_target_outputs(":test_cxx_exe_gen") sources += get_target_outputs(":test_cxx_exe_gen")
include_dirs = [ "${target_gen_dir}" ] include_dirs = [ "${target_gen_dir}" ]
deps = [ deps = [
":test_cxx_examp_rust", ":test_cxx_examp_rust",
":test_cxx_exe_gen", ":test_cxx_exe_gen",
"//build/rust:cxx_cppdeps", "//build/rust:cxx_cppdeps",
] ]
} }
``` ```
编译验证 **调测验证**
![cpp_call_rust](./figures/cpp_call_rust.png) ![cpp_call_rust](./figures/cpp_call_rust.png)
...@@ -219,214 +208,214 @@ ohos_executable("test_cxx_exe") { ...@@ -219,214 +208,214 @@ ohos_executable("test_cxx_exe") {
1. 添加头文件client_blobstore.h。 1. 添加头文件client_blobstore.h。
```c++ ```c++
#ifndef BUILD_RUST_TESTS_CLIENT_BLOBSTORE_H #ifndef BUILD_RUST_TESTS_CLIENT_BLOBSTORE_H
#define BUILD_RUST_TESTS_CLIENT_BLOBSTORE_H #define BUILD_RUST_TESTS_CLIENT_BLOBSTORE_H
#include <memory> #include <memory>
#include "third_party/rust/cxx/include/cxx.h" #include "third_party/rust/cxx/include/cxx.h"
namespace nsp_org { namespace nsp_org {
namespace nsp_blobstore { namespace nsp_blobstore {
struct MultiBufs; struct MultiBufs;
struct Metadata_Blob; struct Metadata_Blob;
class client_blobstore { class client_blobstore {
public: public:
client_blobstore(); client_blobstore();
uint64_t put_buf(MultiBufs &buf) const; uint64_t put_buf(MultiBufs &buf) const;
void add_tag(uint64_t blobid, rust::Str add_tag) const; void add_tag(uint64_t blobid, rust::Str add_tag) const;
Metadata_Blob get_metadata(uint64_t blobid) const; Metadata_Blob get_metadata(uint64_t blobid) const;
private: private:
class impl; class impl;
std::shared_ptr<impl> impl; std::shared_ptr<impl> impl;
}; };
std::unique_ptr<client_blobstore> blobstore_client_new(); std::unique_ptr<client_blobstore> blobstore_client_new();
} // namespace nsp_blobstore } // namespace nsp_blobstore
} // namespace nsp_org } // namespace nsp_org
#endif #endif
``` ```
2. 添加cpp文件client_blobstore.cpp。 2. 添加cpp文件client_blobstore.cpp。
```c++ ```c++
#include <algorithm> #include <algorithm>
#include <functional> #include <functional>
#include <set> #include <set>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include "src/main.rs.h" #include "src/main.rs.h"
#include "build/rust/tests/test_cxx_rust/include/client_blobstore.h" #include "build/rust/tests/test_cxx_rust/include/client_blobstore.h"
namespace nsp_org { namespace nsp_org {
namespace nsp_blobstore { namespace nsp_blobstore {
// Toy implementation of an in-memory nsp_blobstore. // Toy implementation of an in-memory nsp_blobstore.
// //
// In reality the implementation of client_blobstore could be a large complex C++ // In reality the implementation of client_blobstore could be a large complex C++
// library. // library.
class client_blobstore::impl { class client_blobstore::impl {
friend client_blobstore; friend client_blobstore;
using Blob = struct { using Blob = struct {
std::string data; std::string data;
std::set<std::string> tags; std::set<std::string> tags;
}; };
std::unordered_map<uint64_t, Blob> blobs; std::unordered_map<uint64_t, Blob> blobs;
}; };
client_blobstore::client_blobstore() : impl(new class client_blobstore::impl) {} 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. // 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 uint64_t client_blobstore::put_buf(MultiBufs &buf) const
{ {
std::string contents; std::string contents;
// Traverse the caller's res_chunk iterator. // Traverse the caller's res_chunk iterator.
// //
// In reality there might be sophisticated batching of chunks and/or parallel // In reality there might be sophisticated batching of chunks and/or parallel
// upload implemented by the nsp_blobstore's C++ client. // upload implemented by the nsp_blobstore's C++ client.
while (true) { while (true) {
auto res_chunk = next_chunk(buf); auto res_chunk = next_chunk(buf);
if (res_chunk.size() == 0) { if (res_chunk.size() == 0) {
break; break;
} }
contents.append(reinterpret_cast<const char *>(res_chunk.data()), res_chunk.size()); contents.append(reinterpret_cast<const char *>(res_chunk.data()), res_chunk.size());
} }
// Insert into map and provide caller the handle. // Insert into map and provide caller the handle.
auto res = std::hash<std::string> {} (contents); auto res = std::hash<std::string> {} (contents);
impl->blobs[res] = {std::move(contents), {}}; impl->blobs[res] = {std::move(contents), {}};
return res; return res;
} }
// Add add_tag to an existing blob. // Add add_tag to an existing blob.
void client_blobstore::add_tag(uint64_t blobid, rust::Str add_tag) const void client_blobstore::add_tag(uint64_t blobid, rust::Str add_tag) const
{ {
impl->blobs[blobid].tags.emplace(add_tag); impl->blobs[blobid].tags.emplace(add_tag);
} }
// Retrieve get_metadata about a blob. // Retrieve get_metadata about a blob.
Metadata_Blob client_blobstore::get_metadata(uint64_t blobid) const Metadata_Blob client_blobstore::get_metadata(uint64_t blobid) const
{ {
Metadata_Blob get_metadata {}; Metadata_Blob get_metadata {};
auto blob = impl->blobs.find(blobid); auto blob = impl->blobs.find(blobid);
if (blob != impl->blobs.end()) { if (blob != impl->blobs.end()) {
get_metadata.size = blob->second.data.size(); get_metadata.size = blob->second.data.size();
std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(), std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(),
[&](auto &t) { get_metadata.tags.emplace_back(t); }); [&](auto &t) { get_metadata.tags.emplace_back(t); });
} }
return get_metadata; return get_metadata;
} }
std::unique_ptr<client_blobstore> blobstore_client_new() std::unique_ptr<client_blobstore> blobstore_client_new()
{ {
return std::make_unique<client_blobstore>(); return std::make_unique<client_blobstore>();
} }
} // namespace nsp_blobstore } // namespace nsp_blobstore
} // namespace nsp_org } // namespace nsp_org
``` ```
3. main.rs文件,在main.rs文件的ffi里面,通过宏include!将头文件client_blobstore.h引入进来,从而在Rust的main函数里面就可以通过ffi的方式调用C++的接口。 3. main.rs文件,在main.rs文件的ffi里面,通过宏include!将头文件client_blobstore.h引入进来,从而在Rust的main函数里面就可以通过ffi的方式调用C++的接口。
```rust ```rust
//! test_cxx_rust //! test_cxx_rust
#[cxx::bridge(namespace = "nsp_org::nsp_blobstore")] #[cxx::bridge(namespace = "nsp_org::nsp_blobstore")]
mod ffi { mod ffi {
// Shared structs with fields visible to both languages. // Shared structs with fields visible to both languages.
struct Metadata_Blob { struct Metadata_Blob {
size: usize, size: usize,
tags: Vec<String>, tags: Vec<String>,
} }
// Rust types and signatures exposed to C++. // Rust types and signatures exposed to C++.
extern "Rust" { extern "Rust" {
type MultiBufs; type MultiBufs;
fn next_chunk(buf: &mut MultiBufs) -> &[u8]; fn next_chunk(buf: &mut MultiBufs) -> &[u8];
} }
// C++ types and signatures exposed to Rust. // C++ types and signatures exposed to Rust.
unsafe extern "C++" { unsafe extern "C++" {
include!("build/rust/tests/test_cxx_rust/include/client_blobstore.h"); include!("build/rust/tests/test_cxx_rust/include/client_blobstore.h");
type client_blobstore; type client_blobstore;
fn blobstore_client_new() -> UniquePtr<client_blobstore>; fn blobstore_client_new() -> UniquePtr<client_blobstore>;
fn put_buf(&self, parts: &mut MultiBufs) -> u64; fn put_buf(&self, parts: &mut MultiBufs) -> u64;
fn add_tag(&self, blobid: u64, add_tag: &str); fn add_tag(&self, blobid: u64, add_tag: &str);
fn get_metadata(&self, blobid: u64) -> Metadata_Blob; fn get_metadata(&self, blobid: u64) -> Metadata_Blob;
} }
} }
// An iterator over contiguous chunks of a discontiguous file object. // An iterator over contiguous chunks of a discontiguous file object.
// //
// Toy implementation uses a Vec<Vec<u8>> but in reality this might be iterating // 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 // over some more complex Rust data structure like a rope, or maybe loading
// chunks lazily from somewhere. // chunks lazily from somewhere.
/// pub struct MultiBufs /// pub struct MultiBufs
pub struct MultiBufs { pub struct MultiBufs {
chunks: Vec<Vec<u8>>, chunks: Vec<Vec<u8>>,
pos: usize, pos: usize,
} }
/// pub fn next_chunk /// pub fn next_chunk
pub fn next_chunk(buf: &mut MultiBufs) -> &[u8] { pub fn next_chunk(buf: &mut MultiBufs) -> &[u8] {
let next = buf.chunks.get(buf.pos); let next = buf.chunks.get(buf.pos);
buf.pos += 1; buf.pos += 1;
next.map_or(&[], Vec::as_slice) next.map_or(&[], Vec::as_slice)
} }
/// fn main() /// fn main()
fn main() { fn main() {
let client = ffi::blobstore_client_new(); let client = ffi::blobstore_client_new();
// Upload a blob. // Upload a blob.
let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()]; let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()];
let mut buf = MultiBufs { chunks, pos: 0 }; let mut buf = MultiBufs { chunks, pos: 0 };
let blobid = client.put_buf(&mut buf); let blobid = client.put_buf(&mut buf);
println!("This is a test for Rust call cpp:"); println!("This is a test for Rust call cpp:");
println!("blobid = {}", blobid); println!("blobid = {}", blobid);
// Add a add_tag. // Add a add_tag.
client.add_tag(blobid, "rust"); client.add_tag(blobid, "rust");
// Read back the tags. // Read back the tags.
let get_metadata = client.get_metadata(blobid); let get_metadata = client.get_metadata(blobid);
println!("tags = {:?}", get_metadata.tags); println!("tags = {:?}", get_metadata.tags);
} }
``` ```
4. 添加构建文件BUILD.gn。使用CXX将main.rs转换成lib.rs.h和lib.rs.cc,同时将产物作为test_cxx_rust_staticlib的源码,编译Rust源码main.rs并将test_cxx_rust_staticlib依赖进来。 4. 添加构建文件BUILD.gn。使用CXX将main.rs转换成lib.rs.h和lib.rs.cc,同时将产物作为test_cxx_rust_staticlib的源码,编译Rust源码main.rs并将test_cxx_rust_staticlib依赖进来。
``` ```
import("//build/ohos.gni") import("//build/ohos.gni")
rust_cxx("test_cxx_rust_gen") { rust_cxx("test_cxx_rust_gen") {
sources = [ "src/main.rs" ] sources = [ "src/main.rs" ]
} }
ohos_static_library("test_cxx_rust_staticlib") { ohos_static_library("test_cxx_rust_staticlib") {
sources = [ "src/client_blobstore.cpp" ] sources = [ "src/client_blobstore.cpp" ]
sources += get_target_outputs(":test_cxx_rust_gen") sources += get_target_outputs(":test_cxx_rust_gen")
include_dirs = [ include_dirs = [
"${target_gen_dir}", "${target_gen_dir}",
"//third_party/rust/cxx/v1/crate/include", "//third_party/rust/cxx/v1/crate/include",
"include", "include",
] ]
deps = [ deps = [
":test_cxx_rust_gen", ":test_cxx_rust_gen",
"//build/rust:cxx_cppdeps", "//build/rust:cxx_cppdeps",
] ]
} }
ohos_rust_executable("test_cxx_rust") { ohos_rust_executable("test_cxx_rust") {
sources = [ "src/main.rs" ] sources = [ "src/main.rs" ]
deps = [ deps = [
":test_cxx_rust_staticlib", ":test_cxx_rust_staticlib",
"//build/rust:cxx_rustdeps", "//build/rust:cxx_rustdeps",
] ]
} }
``` ```
编译验证 **调测验证**
![rust_call_cpp](./figures/rust_call_cpp.png) ![rust_call_cpp](./figures/rust_call_cpp.png)
\ No newline at end of file
...@@ -326,11 +326,11 @@ ohos_prebuilt_static_library("helloworld") { ...@@ -326,11 +326,11 @@ ohos_prebuilt_static_library("helloworld") {
### Hap模板 ### Hap模板
hap模板详见:[ HAP编译构建指导](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/subsystems/subsys-build-gn-hap-compilation-guide.md) hap模板详见:[ HAP编译构建指导](subsys-build-gn-hap-compilation-guide.md)
### Rust模板 ### Rust模板
rust模板详见:[ Rust模块配置规则和指导](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/subsystems/subsys-build-rust-compilation.md) rust模板详见:[ Rust模块配置规则和指导](subsys-build-rust-compilation.md)
### 其他常用模板 ### 其他常用模板
......
...@@ -2,26 +2,24 @@ ...@@ -2,26 +2,24 @@
## 概述 ## 概述
Rust是一门静态和强类型语言,具有更安全的内存管理、更好的运行性能、原生支持多线程开发等优势。 Rust是一门静态强类型语言,具有更安全的内存管理、更好的运行性能、原生支持多线程开发等优势。Rust官方也使用Cargo工具来专门为Rust代码创建工程和构建编译。
OpenHarmony为了集成C/C++代码和提升编译速度,使用了GN + Ninja的编译构建系统。GN的构建语言简洁易读,Ninja的汇编级编译规则直接高效。
Rust官方使用了cargo工具创建工程和构建编译,在OpenHarmony中希望通过gn构建Rust源码文件(xxx.rs),增加与C/C++互操作、编译时lint、测试、IDL转换、三方库集成、IDE等功能,扩展gn框架,并增加接口自动化转换,最大程度简化开发。 为了在OpenHarmony中集成Rust代码,并最大程度发挥Rust和OpenHarmony中原有C/C++代码的交互性,采用GN作为统一构建工具,即通过GN构建Rust源码文件(xxx.rs),并增加与C/C++互操作、编译时lint、测试、IDL转换、三方库集成、IDE等功能。同时扩展gn框架,支持接口自动化转换,最大程度简化开发。
OpenHarmony拥有集成Rust的先决条件:为了集成OpenHarmony中的C/C++代码,提升编译速度使用了gn+ninja的编译构建系统。gn的构建语言简洁易读,ninja的汇编级编译规则直接高效。
### 基本概念 ### 基本概念
| 术语 | 描述 | | 术语 | 描述 |
| ----- | ------------------------------------------------------------ | | ----- | ------------------------------------------------------------ |
| Cargo | Cargo是一个工具,允许Rust 项目声明其各种依赖项,并确保您始终获得可重复的构建。 | | Cargo | Cargo是Rust官方使用的构建工具,允许Rust项目声明其各种依赖项,并确保您始终获得可重复的构建。 |
| crate | crate是一个独立的可编译单元。 | | crate | crate是一个独立的可编译单元。 |
| lints | lints 是指出常见编程错误、错误、样式错误和可疑结构的工具。可以对程序进行更加广泛的错误分析。 | | lints | lints 是指出常见编程错误、错误、样式错误和可疑结构的工具。可以对程序进行更加广泛的错误分析。 |
## 配置规则 ## 配置规则
当前OpenHarmony提供了用于Rust代码编译构建的各类型gn模板。包含如下模板 OpenHarmony提供了用于Rust代码编译构建的各类型GN模板,可以用于编译Rust可执行文件,动态库和静态库等。各类型模板说明如下
| gn模板 | 功能 | 输出 | | GN模板 | 功能 | 输出 |
| ------------------------ | ----------------- | ----------------------------------------------- | | ------------------------ | ----------------- | ----------------------------------------------- |
| ohos_rust_executable | rust可执行文件 | rust可执行文件,不带后缀 | | ohos_rust_executable | rust可执行文件 | rust可执行文件,不带后缀 |
| ohos_rust_shared_library | rust动态库 | rust dylib动态库,默认后缀.dylib.so | | ohos_rust_shared_library | rust动态库 | rust dylib动态库,默认后缀.dylib.so |
...@@ -36,11 +34,11 @@ OpenHarmony拥有集成Rust的先决条件:为了集成OpenHarmony中的C/C++ ...@@ -36,11 +34,11 @@ OpenHarmony拥有集成Rust的先决条件:为了集成OpenHarmony中的C/C++
## 配置指导 ## 配置指导
### test_rlib_crate示例 配置Rust模块与C/C++模块类似,参考[模块配置规则](subsys-build-module.md)。下面是使用不同模板的示例。
### 配置Rust静态库示例
test_rlib_crate是测试对Rust可执行bin文件和静态库rlib文件的编译,以及可执行文件对静态库的依赖。 该示例用于测试Rust可执行bin文件和静态库rlib文件的编译,以及可执行文件对静态库的依赖,使用模板ohos_rust_executable和ohos_rust_static_library。操作步骤如下:
1. 创建build/rust/tests/test_rlib_crate/src/simple_printer.rs,示例如下 1. 创建build/rust/tests/test_rlib_crate/src/simple_printer.rs,如下所示
```rust ```rust
//! simple_printer //! simple_printer
...@@ -60,7 +58,7 @@ test_rlib_crate是测试对Rust可执行bin文件和静态库rlib文件的编译 ...@@ -60,7 +58,7 @@ test_rlib_crate是测试对Rust可执行bin文件和静态库rlib文件的编译
} }
``` ```
2. 创建build/rust/tests/test_rlib_crate/src/main.rs,示例如下 2. 创建build/rust/tests/test_rlib_crate/src/main.rs,如下所示
```rust ```rust
//! rlib_crate example for Rust. //! rlib_crate example for Rust.
...@@ -79,7 +77,7 @@ test_rlib_crate是测试对Rust可执行bin文件和静态库rlib文件的编译 ...@@ -79,7 +77,7 @@ test_rlib_crate是测试对Rust可执行bin文件和静态库rlib文件的编译
} }
``` ```
3. 配置gn脚本build/rust/tests/test_rlib_crate/BUILD.gn,示例如下 3. 配置gn脚本build/rust/tests/test_rlib_crate/BUILD.gn,如下所示
``` ```
import("//build/ohos.gni") import("//build/ohos.gni")
...@@ -97,15 +95,14 @@ test_rlib_crate是测试对Rust可执行bin文件和静态库rlib文件的编译 ...@@ -97,15 +95,14 @@ test_rlib_crate是测试对Rust可执行bin文件和静态库rlib文件的编译
} }
``` ```
4. 运行结果如下: 4. 执行编译得到的可执行文件,运行结果如下:
![test_rlib_crate](./figures/test_rlib_crate.png) ![test_rlib_crate](./figures/test_rlib_crate.png)
### test_rlib_cargo_crate示例 ### 配置三方库示例
该示例用于测试包含预编译文件build.rs的三方静态库rlib文件的编译,使用了模板ohos_rust_executable和ohos_rust_cargo_crate。操作步骤如下:
test_rlib_cargo_crate是测试包含预编译文件build.rs的静态库rlib文件的编译。 1. 创建build/rust/tests/test_rlib_cargo_crate/crate/src/lib.rs,如下所示:
1. 创建build/rust/tests/test_rlib_cargo_crate/crate/src/lib.rs,示例如下:
```rust ```rust
include!(concat!(env!("OUT_DIR"), "/generated/generated.rs")); include!(concat!(env!("OUT_DIR"), "/generated/generated.rs"));
...@@ -151,7 +148,7 @@ test_rlib_cargo_crate是测试包含预编译文件build.rs的静态库rlib文 ...@@ -151,7 +148,7 @@ test_rlib_cargo_crate是测试包含预编译文件build.rs的静态库rlib文
} }
``` ```
2. 创建build/rust/tests/test_rlib_cargo_crate/crate/src/main.rs,示例如下 2. 创建build/rust/tests/test_rlib_cargo_crate/crate/src/main.rs,如下所示
```rust ```rust
pub fn main() { pub fn main() {
...@@ -159,7 +156,7 @@ test_rlib_cargo_crate是测试包含预编译文件build.rs的静态库rlib文 ...@@ -159,7 +156,7 @@ test_rlib_cargo_crate是测试包含预编译文件build.rs的静态库rlib文
} }
``` ```
3. 创建build/rust/tests/test_rlib_cargo_crate/crate/build.rs,示例如下 3. 创建build/rust/tests/test_rlib_cargo_crate/crate/build.rs,如下所示
```rust ```rust
use std::env; use std::env;
...@@ -250,7 +247,7 @@ test_rlib_cargo_crate是测试包含预编译文件build.rs的静态库rlib文 ...@@ -250,7 +247,7 @@ test_rlib_cargo_crate是测试包含预编译文件build.rs的静态库rlib文
} }
``` ```
4. 配置gn脚本build/rust/tests/test_rlib_cargo_crate/BUILD.gn,示例如下 4. 配置gn脚本build/rust/tests/test_rlib_cargo_crate/BUILD.gn,如下所示
``` ```
import("//build/templates/rust/ohos_cargo_crate.gni") import("//build/templates/rust/ohos_cargo_crate.gni")
...@@ -296,11 +293,11 @@ test_rlib_cargo_crate是测试包含预编译文件build.rs的静态库rlib文 ...@@ -296,11 +293,11 @@ test_rlib_cargo_crate是测试包含预编译文件build.rs的静态库rlib文
} }
``` ```
5. 运行结果如下: 5. 执行编译得到的可执行文件,运行结果如下:
![test_rlib_cargo_crate](./figures/test_rlib_cargo_crate.png) ![test_rlib_cargo_crate](./figures/test_rlib_cargo_crate.png)
### 源码实例 ### 其他源码实例
在build/rust/tests目录下有Rust各类型模块的配置实例可供参考: 在build/rust/tests目录下有Rust各类型模块的配置实例可供参考:
| 用例目录 | 测试功能 | | 用例目录 | 测试功能 |
| -------------------------------------------- | ------------------------------------------------------------ | | -------------------------------------------- | ------------------------------------------------------------ |
...@@ -316,23 +313,14 @@ test_rlib_cargo_crate是测试包含预编译文件build.rs的静态库rlib文 ...@@ -316,23 +313,14 @@ test_rlib_cargo_crate是测试包含预编译文件build.rs的静态库rlib文
| build/rust/tests/test_bin_cargo_crate | 测试ohos_cargo_crate对拥有build.rs预编译的可执行文件编译链接和运行,适用于rust三方crate编译依赖 | | build/rust/tests/test_bin_cargo_crate | 测试ohos_cargo_crate对拥有build.rs预编译的可执行文件编译链接和运行,适用于rust三方crate编译依赖 |
| build/rust/tests/test_rlib_cargo_crate | 测试ohos_cargo_crate对拥有build.rs预编译的静态库文件编译链接和运行,适用于rust三方crate编译依赖 | | build/rust/tests/test_rlib_cargo_crate | 测试ohos_cargo_crate对拥有build.rs预编译的静态库文件编译链接和运行,适用于rust三方crate编译依赖 |
| build/rust/tests/test_proc_macro_cargo_crate | 测试ohos_cargo_crate对拥有build.rs预编译的过程宏编译链接和运行,适用于rust三方crate编译依赖 | | build/rust/tests/test_proc_macro_cargo_crate | 测试ohos_cargo_crate对拥有build.rs预编译的过程宏编译链接和运行,适用于rust三方crate编译依赖 |
### 运行验证
当前支持target ohos arm和ohos arm64架构。arm64模拟器编译启动方法:
```shell
./build.sh --product-name qemu-arm64-linux-min
./vendor/ohemu/qemu_arm64_linux_min/qemu_run.sh -e out/qemu-arm-linux/packages/phone/images/
```
## 参考
## 注意事项
### 特性点实例 ### 特性点实例
#### Rust源码依赖调用C/C++库 #### Rust源码依赖调用C/C++库
OpenHarmony上C/C++模块动态库默认用.z.so后缀,但是被Rust依赖部分会转成-l链接,默认只会链接.so后缀的动态库。因此被依赖的C/C++动态库要加上output_externsion = "so"。同理,在Rust源码中如果直接链接动态库,后缀也需要使用".so",使用动态库的中间名,不需要添加lib前缀,例如链接libhilog.so: OpenHarmony上C/C++模块动态库默认用.z.so后缀,但是Rust的编译命令通过-l链接时,默认只会链接.so后缀的动态库。因此如果要依赖一个C/C++动态库编译模块,需要在该动态库的GN构建文件中添加output_externsion = "so"的声明,这样编译得到的动态库将会以".so"作为后缀,而不是".z.so"。
在Rust源码中如果直接链接动态库,后缀也需要使用".so",这时使用动态库的中间名,不需要添加lib前缀。例如Rust源码中链接libhilog.so:
```rust ```rust
#[link(name = "hilog")] #[link(name = "hilog")]
``` ```
...@@ -347,12 +335,10 @@ executable("foo") { ...@@ -347,12 +335,10 @@ executable("foo") {
}] }]
} }
``` ```
#### IDE的Rust-project.json编译
```shell
./build.sh --product-name rk3568 --build-target=build/rust:default --export-rust-project
```
### lints规则 ### lints规则
OpenHarmony框架支持rustc lints和clippy lints两级lints,分别由模块属性rustc_lints和clippy_lints标识;同时支持三个等级的标准:"openharmony"、"vendor"和"none","openharmony"是系统配置,最为严格。模块无配置时按照路径匹配等级。 OpenHarmony框架支持rustc lints和clippy lints两种lints,每种lints划为三个等级的标准:"openharmony"、"vendor"和"none",严格程度按照"openharmony" -> "vendor" -> "none"逐级递减。
配置Rust模块时可以通过rustc_lints和clippy_lints来指定使用lints的等级。
模块中没有配置rustc_lints或者clippy_lints时会根据模块所在路径来匹配lints等级。不同路径下的Rust代码的语法规范会有不同程度地约束,因此用户在OpenHarmony配置Rust代码编译模块时还应关注模块所在路径。
#### rustc lints和clippy lints的各等级标志 #### rustc lints和clippy lints的各等级标志
| **lints类型** | **模块属性** | **lints等级** | **lints等级标志** | **lints内容** | | **lints类型** | **模块属性** | **lints等级** | **lints等级标志** | **lints内容** |
| ------------- | ------------ | ------------- | ----------------- | ------------------------------------------------------------ | | ------------- | ------------ | ------------- | ----------------- | ------------------------------------------------------------ |
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册