このページには、 Androidロギングに関する情報、 Rust AIDLの例、 CからRustを呼び出す方法、 CXXを使用したRust / C ++相互運用の手順が記載されています。
Androidのログ
次の例は、メッセージをlogcat
(デバイス上)またはstdout
(ホスト上)に記録する方法を示しています。
Android.bp
モジュールで、依存関係としてliblogger
とliblog_rust
を追加します。
rust_binary {
name: "logging_test",
srcs: ["src/main.rs"],
rustlibs: [
"liblogger",
"liblog_rust",
],
}
次に、Rustソースに次のコードを追加します。
use log::{debug, error, Level};
fn main() {
let init_success = logger::init(
logger::Config::default()
.with_tag_on_device("mytag")
.with_min_level(Level::Trace),
);
debug!("This is a debug message.");
error!("Something went wrong!");
}
つまり、上記の2つの依存関係( liblogger
とliblog_rust
)を追加し、 init
メソッドを1回呼び出し(必要に応じて複数回呼び出すことができます)、提供されたマクロを使用してメッセージをログに記録します。可能な構成オプションのリストについては、ロガークレートを参照してください。
ロガークレートは、ログに記録する内容を定義するためのAPIを提供します。コードがデバイス上で実行されているかホスト上で実行されているか(ホスト側のテストの一部など)に応じて、メッセージはandroid_loggerまたはenv_loggerのいずれかを使用してログに記録されます。
RustAIDLの例
このセクションでは、RustでAIDLを使用するHelloWorldスタイルの例を示します。
Android開発者ガイドAIDLの概要セクションを開始点として使用して、 IRemoteService.aidl
ファイルに次の内容をexternal/rust/binder_example/aidl/com/example/android/IRemoteService.aidl
を作成します。
// IRemoteService.aidl
package com.example.android;
// Declare any non-default types here with import statements
/** Example service interface */
interface IRemoteService {
/** Request the process ID of this service, to do evil things with it. */
int getPid();
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
次に、 external/rust/binder_example/aidl/Android.bp
ファイル内で、 aidl_interface
モジュールを定義します。デフォルトでは有効になっていないため、Rustバックエンドを明示的に有効にする必要があります。
aidl_interface {
name: "com.example.android.remoteservice",
srcs: [ "aidl/com/example/android/*.aidl", ],
unstable: true, // Add during development until the interface is stabilized.
backend: {
rust: {
// By default, the Rust backend is not enabled
enabled: true,
},
},
}
AIDLバックエンドはRustソースジェネレーターであるため、他のRustソースジェネレーターと同様に動作し、Rustライブラリを生成します。生成されたRustライブラリモジュールは、依存関係として他のRustモジュールで使用できます。生成されたライブラリを依存関係として使用する例として、 rust_library
はexternal/rust/binder_example/Android.bp
で次のように定義できます。
rust_library {
name: "libmyservice",
srcs: ["src/lib.rs"],
crate_name: "myservice",
rustlibs: [
"com.example.android.remoteservice-rust",
"libbinder_rs",
],
}
rustlibsで使用されるrustlibs
生成ライブラリのモジュール名の形式は、 aidl_interface
モジュール名の後に-rust
が続くことに注意してください。この場合、 com.example.android.remoteservice-rust
。
AIDLインターフェースは、次のようにsrc/lib.rs
で参照できます。
// Note carefully the AIDL crates structure:
// * the AIDL module name: "com_example_android_remoteservice"
// * next "::aidl"
// * next the AIDL package name "::com::example::android"
// * the interface: "::IRemoteService"
// * finally, the 'BnRemoteService' and 'IRemoteService' submodules
//! This module implements the IRemoteService AIDL interface
use com_example_android_remoteservice::aidl::com::example::android::{
IRemoteService::{BnRemoteService, IRemoteService}
};
use com_example_android_remoteservice::binder::{
BinderFeatures, Interface, Result as BinderResult, Strong,
};
/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyService;
impl Interface for MyService {}
impl IRemoteService for MyService {
fn getPid(&self) -> BinderResult<i32> {
Ok(42)
}
fn basicTypes(&self, _: i32, _: i64, _: bool, _: f32, _: f64, _: &str) -> BinderResult<()> {
// Do something interesting...
Ok(())
}
}
最後に、以下に示すように、Rustバイナリでサービスを開始します。
use myservice::MyService;
fn main() {
// [...]
let my_service = MyService;
let my_service_binder = BnRemoteService::new_binder(
my_service,
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Does not return - spawn or perform any work you mean to do before this call.
binder::ProcessState::join_thread_pool()
}
CからRustを呼び出す
この例は、CからRustを呼び出す方法を示しています。
ラストライブラリの例
次のように、 external/rust/simple_printer/libsimple_printer.rs
にあるlibsimple_printer
ファイルを定義します。
//! A simple hello world example that can be called from C
#[no_mangle]
/// Print "Hello Rust!"
pub extern fn print_c_hello_rust() {
println!("Hello Rust!");
}
Rustライブラリは、依存するCモジュールがプルできるヘッダーを定義する必要があるため、 external/rust/simple_printer/simple_printer.h
ヘッダーを次のように定義します。
#ifndef SIMPLE_PRINTER_H
#define SIMPLE_PRINTER_H
void print_c_hello_rust();
#endif
ここに表示されているように、 external/rust/simple_printer/Android.bp
を定義します。
rust_ffi {
name: "libsimple_c_printer",
crate_name: "simple_c_printer",
srcs: ["libsimple_c_printer.rs"],
// Define include_dirs so cc_binary knows where the headers are.
include_dirs: ["."],
}
例Cバイナリ
external/rust/c_hello_rust/main.c
を次のように定義します。
#include "simple_printer.h"
int main() {
print_c_hello_rust();
return 0;
}
external/rust/c_hello_rust/Android.bp
を次のように定義します。
cc_binary {
name: "c_hello_rust",
srcs: ["main.c"],
shared_libs: ["libsimple_c_printer"],
}
最後に、 m c_hello_rust
を呼び出してビルドします。
RustとJavaの相互運用
jni
は、Java Native Interface(JNI)を介してRustとJavaの相互運用性を提供します。これは、JavaのJNI( JNIEnv
、 JClass
、 JString
など)に直接プラグインするcdylib
ライブラリを生成するためにRustに必要な型定義を定義します。 cxx
を介してcodegenを実行するC ++バインディングとは異なり、JNIを介したJavaの相互運用性では、ビルド中にコード生成ステップを実行する必要はありません。したがって、特別なビルドシステムのサポートは必要ありません。 Javaコードは、他のネイティブライブラリと同様にRustが提供するcdylib
をロードします。
使用法
RustコードとJavaコードの両方での使用法は、 jni
のドキュメントで説明されています。そこにある入門例に従ってください。 src/lib.rs
を作成したら、このページに戻って、Androidのビルドシステムでライブラリをビルドする方法を学びます。
ビルド定義
Javaでは、動的にロードできるように、Rustライブラリをcdylib
として提供する必要があります。 SoongのRustライブラリの定義は次のとおりです。
rust_ffi_shared {
name: "libhello_jni",
crate_name: "hello_jni",
srcs: ["src/lib.rs"],
// The jni crate is required
rustlibs: ["libjni"],
}
Javaライブラリには、 required
依存関係としてRustライブラリがリストされています。これにより、ビルド時の依存関係ではなくても、Javaライブラリと一緒にデバイスにインストールされます。
java_library {
name: "libhelloworld",
[...]
required: ["libhellorust"]
[...]
}
または、RustライブラリをAndroidManifest.xml
ファイルに含める必要がある場合は、次のようにライブラリをuses_libs
に追加します。
java_library {
name: "libhelloworld",
[...]
uses_libs: ["libhellorust"]
[...]
}
CXXを使用したRust-C ++相互運用
CXXクレートは、RustとC ++のサブセットの間で安全なFFIを提供します。 CXXのドキュメントには、一般的にどのように機能するかについての良い例が示されています。次の例は、Androidでの使用方法を示しています。
Rustが呼び出すC ++コードをCXXに生成させるには、 genrule
を呼び出すgenruleと、それをライブラリにバンドルするcc_library_static
を定義します。 C ++でRustコードを呼び出す場合、またはC ++とRustの間で共有される型を使用する場合は、2番目のgenruleを定義します(Rustバインディングを含むC ++ヘッダーを生成するため)。
cc_library_static {
name: "libcxx_test_cpp",
srcs: ["cxx_test.cpp"],
generated_headers: [
"cxx-bridge-header",
"libcxx_test_bridge_header"
],
generated_sources: ["libcxx_test_bridge_code"],
}
genrule {
name: "libcxx_test_bridge_code",
tools: ["cxxbridge"],
cmd: "$(location cxxbridge) $(in) >> $(out)",
srcs: ["lib.rs"],
out: ["libcxx_test_cxx_generated.cc"],
}
// This defines a second genrule to generate a C++ header.
// * The generated header contains the C++ bindings to the Rust exported
// * functions in lib.rs.
genrule {
name: "libcxx_test_bridge_header",
tools: ["cxxbridge"],
cmd: "$(location cxxbridge) $(in) --header >> $(out)",
srcs: ["lib.rs"],
out: ["lib.rs.h"],
}
次に、これをRustライブラリまたは実行可能ファイルにリンクします。
rust_binary {
name: "cxx_test",
srcs: ["lib.rs"],
rustlibs: ["libcxx"],
static_libs: ["libcxx_test_cpp"],
}
.cpp
ファイルと.hpp
ファイルで、必要に応じてCXXラッパータイプを使用して、必要に応じてC ++関数を定義します。たとえば、 cxx_test.hpp
定義には次のものが含まれます。
#pragma once
#include "rust/cxx.h"
int greet(rust::Str greetee);
cxx_test.cpp
には
#include "cxx_test.hpp"
#include "lib.rs.h"
#include <iostream>
int greet(rust::Str greetee) {
std::cout << "Hello, " << greetee << std::endl;
return get_num();
}
Rustからこれを使用するには、lib.rsで以下のようにlib.rs
ブリッジを定義します。
#[cxx::bridge]
mod ffi {
unsafe extern "C++" {
include!("cxx_test.hpp");
fn greet(greetee: &str) -> i32;
}
extern "Rust" {
fn get_num() -> i32;
}
}
fn main() {
let result = ffi::greet("world");
println!("C++ returned {}", result);
}
fn get_num() -> i32 {
return 42;
}