1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-06-17 14:47:35 +02:00

Publish Comprehensive Rust 🦀

This commit is contained in:
Martin Geisler
2022-12-21 16:36:30 +01:00
commit c212a473ba
252 changed files with 8047 additions and 0 deletions

7
src/android/aidl.md Normal file
View File

@ -0,0 +1,7 @@
# AIDL
The [Android Interface Definition Language
(AIDL)](https://developer.android.com/guide/components/aidl) is support in Rust:
* Rust code can call existing AIDL servers,
* You can create new AIDL servers in Rust.

View File

@ -0,0 +1,38 @@
// ANCHOR: libbirthdayservice
rust_library {
name: "libbirthdayservice",
srcs: ["src/lib.rs"],
crate_name: "birthdayservice",
rustlibs: [
"com.example.birthdayservice-rust",
"libbinder_rs",
],
}
// ANCHOR_END: libbirthdayservice
// ANCHOR: birthday_server
rust_binary {
name: "birthday_server",
crate_name: "birthday_server",
srcs: ["src/server.rs"],
rustlibs: [
"com.example.birthdayservice-rust",
"libbinder_rs",
"libbirthdayservice",
],
prefer_rlib: true,
}
// ANCHOR_END: birthday_server
// ANCHOR: birthday_client
rust_binary {
name: "birthday_client",
crate_name: "birthday_client",
srcs: ["src/client.rs"],
rustlibs: [
"com.example.birthdayservice-rust",
"libbinder_rs",
],
prefer_rlib: true,
}
// ANCHOR_END: birthday_client

View File

@ -0,0 +1,10 @@
aidl_interface {
name: "com.example.birthdayservice",
srcs: ["com/example/birthdayservice/*.aidl"],
unstable: true,
backend: {
rust: { // Rust is not enabled by default
enabled: true,
},
},
}

View File

@ -0,0 +1,22 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: IBirthdayService
package com.example.birthdayservice;
/** Birthday service interface. */
interface IBirthdayService {
/** Generate a Happy Birthday message. */
String wishHappyBirthday(String name, int years);
}

View File

@ -0,0 +1,42 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: main
//! Birthday service.
use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::IBirthdayService;
use com_example_birthdayservice::binder;
const SERVICE_IDENTIFIER: &str = "birthdayservice";
/// Connect to the BirthdayService.
pub fn connect() -> Result<binder::Strong<dyn IBirthdayService>, binder::StatusCode> {
binder::get_interface(SERVICE_IDENTIFIER)
}
/// Call the birthday service.
fn main() -> Result<(), binder::Status> {
let name = std::env::args()
.nth(1)
.unwrap_or_else(|| String::from("Bob"));
let years = std::env::args()
.nth(2)
.and_then(|arg| arg.parse::<i32>().ok())
.unwrap_or(42);
binder::ProcessState::start_thread_pool();
let service = connect().expect("Failed to connect to BirthdayService");
let msg = service.wishHappyBirthday(&name, years)?;
println!("{msg}");
Ok(())
}

View File

@ -0,0 +1,31 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: IBirthdayService
//! Implementation of the `IBirthdayService` AIDL interface.
use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::IBirthdayService;
use com_example_birthdayservice::binder;
/// The `IBirthdayService` implementation.
pub struct BirthdayService;
impl binder::Interface for BirthdayService {}
impl IBirthdayService for BirthdayService {
fn wishHappyBirthday(&self, name: &str, years: i32) -> binder::Result<String> {
Ok(format!(
"Happy Birthday {name}, congratulations with the {years} years!"
))
}
}

View File

@ -0,0 +1,33 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: main
//! Birthday service.
use birthdayservice::BirthdayService;
use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::BnBirthdayService;
use com_example_birthdayservice::binder;
const SERVICE_IDENTIFIER: &str = "birthdayservice";
/// Entry point for birthday service.
fn main() {
let birthday_service = BirthdayService;
let birthday_service_binder = BnBirthdayService::new_binder(
birthday_service,
binder::BinderFeatures::default(),
);
binder::add_service(SERVICE_IDENTIFIER, birthday_service_binder.as_binder())
.expect("Failed to register service");
binder::ProcessState::join_thread_pool()
}

View File

@ -0,0 +1,14 @@
# Changing API
Let us extend the API with more functionality: we want to let clients specify a
list of lines for the birthday card:
```java
package com.example.birthdayservice;
/** Birthday service interface. */
interface IBirthdayService {
/** Generate a Happy Birthday message. */
String wishHappyBirthday(String name, int years, in String[] text);
}
```

View File

@ -0,0 +1,24 @@
# AIDL Client
Finally, we can create a Rust client for our new service.
*birthday_service/src/client.rs*:
```rust,ignore
{{#include birthday_service/src/client.rs:main}}
```
*birthday_service/Android.bp*:
```javascript
{{#include birthday_service/Android.bp:birthday_client}}
```
Notice that the client does not depend on `libbirthdayservice`.
Build, push, and run the client on your device:
```shell
{{#include ../build_all.sh:birthday_client}}
Happy Birthday Charlie, congratulations with the 60 years!
```

View File

@ -0,0 +1,29 @@
# Deploy
We can now build, push, and start the service:
```shell
{{#include ../build_all.sh:birthday_server}}
```
In another terminal, check that the service runs:
```shell
{{#include ../build_all.sh:service_check_birthday_server}}
Service birthdayservice: found
```
You can also call the service with `service call`:
```shell
$ {{#include ../build_all.sh:service_call_birthday_server}}
Result: Parcel(
0x00000000: 00000000 00000036 00610048 00700070 '....6...H.a.p.p.'
0x00000010: 00200079 00690042 00740072 00640068 'y. .B.i.r.t.h.d.'
0x00000020: 00790061 00420020 0062006f 0020002c 'a.y. .B.o.b.,. .'
0x00000030: 006f0063 0067006e 00610072 00750074 'c.o.n.g.r.a.t.u.'
0x00000040: 0061006c 00690074 006e006f 00200073 'l.a.t.i.o.n.s. .'
0x00000050: 00690077 00680074 00740020 00650068 'w.i.t.h. .t.h.e.'
0x00000060: 00320020 00200034 00650079 00720061 ' .2.4. .y.e.a.r.'
0x00000070: 00210073 00000000 's.!..... ')
```

View File

@ -0,0 +1,15 @@
# Service Implementation
We can now implement the AIDL service:
*birthday_service/src/lib.rs*:
```rust,ignore
{{#include birthday_service/src/lib.rs:IBirthdayService}}
```
*birthday_service/Android.bp*:
```javascript
{{#include birthday_service/Android.bp:libbirthdayservice}}
```

View File

@ -0,0 +1,18 @@
# AIDL Interfaces
You declare the API of your service using an AIDL interface:
*birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl*:
```java
{{#include birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:IBirthdayService}}
```
*birthday_service/aidl/Android.bp*:
```javascript
{{#include birthday_service/aidl/Android.bp}}
```
Add `vendor_available: true` if your AIDL file is used by a binary in the vendor
partition.

View File

@ -0,0 +1,15 @@
# AIDL Server
Finally, we can create a server which exposes the service:
*birthday_service/src/server.rs*:
```rust,ignore
{{#include birthday_service/src/server.rs:main}}
```
*birthday_service/Android.bp*:
```javascript
{{#include birthday_service/Android.bp:birthday_server}}
```

27
src/android/bpfmt.sh Executable file
View File

@ -0,0 +1,27 @@
#!/bin/zsh
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Simple wrapper for bpfmt which will remove unnecessary newlines before the
# mdbook anchors.
if ! type bpfmt > /dev/null; then
echo 'Can not find bpfmt, do you need to run "m bpfmt"?'
exit 1
fi
for f in comprehensive_rust/**/Android.bp; do
bpfmt -s -w $f
sed -zi 's|\n// ANCHOR_END|// ANCHOR_END|g' $f
done

View File

@ -0,0 +1,16 @@
# Build Rules
The Android build system (Soong) supports Rust via a number of modules:
| Module Type | Description |
|-------------------|----------------------------------------------------------------------------------------------------|
| `rust_binary` | Produces a Rust binary. |
| `rust_library` | Produces a Rust library, and provides both `rlib` and `dylib` variants. |
| `rust_ffi` | Produces a Rust C library usable by `cc` modules, and provides both static and shared variants. |
| `rust_proc_macro` | Produces a `proc-macro` Rust library. These are analogous to compiler plugins. |
| `rust_test` | Produces a Rust test binary that uses the standard Rust test harness. |
| `rust_fuzz` | Produces a Rust fuzz binary leveraging `libfuzzer`. |
| `rust_protobuf` | Generates source and produces a Rust library that provides an interface for a particular protobuf. |
| `rust_bindgen` | Generates source and produces a Rust library containing Rust bindings to C libraries. |
We will look at `rust_binary` and `rust_library` next.

View File

@ -0,0 +1,23 @@
# Rust Binaries
Let us start with a simple application. At the root of an AOSP checkout, create
the following files:
_hello_rust/Android.bp_:
```javascript
{{#include binary/Android.bp}}
```
_hello_rust/src/main.rs_:
```rust
{{#include binary/src/main.rs:main}}
```
You can now build, push, and run the binary:
```shell
{{#include ../build_all.sh:hello_rust}}
Hello from Rust!
```

View File

@ -0,0 +1,5 @@
rust_binary {
name: "hello_rust",
crate_name: "hello_rust",
srcs: ["src/main.rs"],
}

View File

@ -0,0 +1,21 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: main
//! Rust demo.
/// Prints a greeting to standard output.
fn main() {
println!("Hello from Rust!");
}

View File

@ -0,0 +1,37 @@
# Rust Libraries
You use `rust_library` to create a new Rust library for Android.
Here we declare a dependency on two libraries:
* `libgreeting`, which we define below,
* `libtextwrap`, which is a crate already vendored in
[`external/rust/crates/`][crates].
[crates]: https://cs.android.com/android/platform/superproject/+/master:external/rust/crates/
_hello_rust/Android.bp_:
```javascript
{{#include library/Android.bp}}
```
_hello_rust/src/main.rs_:
```rust,ignore
{{#include library/src/main.rs:main}}
```
_hello_rust/src/lib.rs_:
```rust,ignore
{{#include library/src/lib.rs:greeting}}
```
You build, push, and run the binary like before:
```shell
{{#include ../build_all.sh:hello_rust_with_dep}}
Hello Bob, it is very
nice to meet you!
```

View File

@ -0,0 +1,16 @@
rust_binary {
name: "hello_rust_with_dep",
crate_name: "hello_rust_with_dep",
srcs: ["src/main.rs"],
rustlibs: [
"libgreetings",
"libtextwrap",
],
prefer_rlib: true,
}
rust_library {
name: "libgreetings",
crate_name: "greetings",
srcs: ["src/lib.rs"],
}

View File

@ -0,0 +1,21 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: greeting
//! Greeting library.
/// Greet `name`.
pub fn greeting(name: &str) -> String {
format!("Hello {name}, it is very nice to meet you!")
}

View File

@ -0,0 +1,24 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: main
//! Rust demo.
use greetings::greeting;
use textwrap::fill;
/// Prints a greeting to standard output.
fn main() {
println!("{}", fill(&greeting("Bob"), 24));
}

130
src/android/build_all.sh Executable file
View File

@ -0,0 +1,130 @@
#!/bin/bash
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -e
function run_example() {
while read -r line; do
if [[ "$line" != \#* ]]; then
echo "$line"
eval "${line#$ }"
fi
done
}
cd $ANDROID_BUILD_TOP
source build/envsetup.sh
lunch aosp_cf_x86_64_phone-userdebug
#acloud reconnect --autoconnect adb
adb root
adb shell rm -rf '/data/local/tmp/*'
run_example <<EOF
# ANCHOR: hello_rust
$ m hello_rust
$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust /data/local/tmp
$ adb shell /data/local/tmp/hello_rust
# ANCHOR_END: hello_rust
EOF
run_example <<EOF
# ANCHOR: hello_rust_with_dep
$ m hello_rust_with_dep
$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust_with_dep /data/local/tmp
$ adb shell /data/local/tmp/hello_rust_with_dep
# ANCHOR_END: hello_rust_with_dep
EOF
function birthday_server() {
run_example <<EOF
# ANCHOR: birthday_server
$ m birthday_server
$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_server /data/local/tmp
$ adb shell /data/local/tmp/birthday_server
# ANCHOR_END: birthday_server
EOF
}
pkill -f birthday_server || true
birthday_server &
BIRTHDAY_SERVER_PID=$!
while adb shell service check birthdayservice | grep -q 'not found'; do
echo "Waiting on birthdayservice..."
sleep 3
done
echo "Found birthdayservice..."
run_example <<EOF
# ANCHOR: service_check_birthday_server
$ adb shell service check birthdayservice
# ANCHOR_END: service_check_birthday_server
EOF
run_example <<EOF
# ANCHOR: service_call_birthday_server
$ adb shell service call birthdayservice 1 s16 Bob i32 24
# ANCHOR_END: service_call_birthday_server
EOF
run_example <<EOF
# ANCHOR: birthday_client
$ m birthday_client
$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_client /data/local/tmp
$ adb shell /data/local/tmp/birthday_client Charlie 60
# ANCHOR_END: birthday_client
EOF
pkill -f birthday_server
run_example <<EOF
# ANCHOR: hello_rust_logs
$ m hello_rust_logs
$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust_logs /data/local/tmp
$ adb shell /data/local/tmp/hello_rust_logs
# ANCHOR_END: hello_rust_logs
EOF
run_example <<EOF
# ANCHOR: print_birthday_card
$ m print_birthday_card
$ adb push $ANDROID_PRODUCT_OUT/system/bin/print_birthday_card /data/local/tmp
$ adb shell /data/local/tmp/print_birthday_card
# ANCHOR_END: print_birthday_card
EOF
run_example <<EOF
# ANCHOR: libbirthday_bindgen_test
$ atest libbirthday_bindgen_test
# ANCHOR_END: libbirthday_bindgen_test
EOF
run_example <<EOF
# ANCHOR: analyze_numbers
$ m analyze_numbers
$ adb push $ANDROID_PRODUCT_OUT/system/bin/analyze_numbers /data/local/tmp
$ adb shell /data/local/tmp/analyze_numbers
# ANCHOR_END: analyze_numbers
EOF
run_example <<EOF
# ANCHOR: helloworld_jni
$ m helloworld_jni
$ adb sync # requires adb root && adb remount
$ adb shell /system/bin/helloworld_jni
# ANCHOR_END: helloworld_jni
EOF

View File

@ -0,0 +1,10 @@
# Interoperability
Rust has excellent support for interoperability with other languages. This means
that you can
* Call Rust functions from other languages
* Call functions written in other languages from Rust
When you call functions in a foreign language we say that you're using a
_foreign function interface_, also known as FFI.

View File

@ -0,0 +1,13 @@
# With C++
The [CXX crate][1] makes it possible to do safe interoperability between Rust
and C++.
The overall approach looks like this:
<img src="cpp/overview.svg">
See the [CXX tutorial][2] for an full example of using this.
[1]: https://cxx.rs/
[2]: https://cxx.rs/tutorial.html

View File

@ -0,0 +1 @@
../../../../third_party/cxx/overview.svg

View File

@ -0,0 +1,39 @@
# Interoperability with Java
Java can load shared objects via [Java Native Interface
(JNI)](https://en.wikipedia.org/wiki/Java_Native_Interface). The [`jni`
crate](https://docs.rs/jni/) allows you to create a compatible library.
First, we create a Rust function to export to Java:
_interoperability/java/src/lib.rs_:
```rust,compile_fail
{{#include java/src/lib.rs:hello}}
```
_interoperability/java/Android.bp_:
```javascript
{{#include java/Android.bp:libhello_jni}}
```
Finally, we can call this function from Java:
_interoperability/java/HelloWorld.java_:
```java
{{#include java/HelloWorld.java:HelloWorld}}
```
_interoperability/java/Android.bp_:
```javascript
{{#include java/Android.bp:helloworld_jni}}
```
Finally, you can build, sync, and run the binary:
```shell
{{#include ../build_all.sh:helloworld_jni}}
```

View File

@ -0,0 +1,17 @@
// ANCHOR: libhello_jni
rust_ffi_shared {
name: "libhello_jni",
crate_name: "hello_jni",
srcs: ["src/lib.rs"],
rustlibs: ["libjni"],
}
// ANCHOR_END: libhello_jni
// ANCHOR: helloworld_jni
java_binary {
name: "helloworld_jni",
srcs: ["HelloWorld.java"],
main_class: "HelloWorld",
required: ["libhello_jni"],
}
// ANCHOR_END: helloworld_jni

View File

@ -0,0 +1,29 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ANCHOR: HelloWorld
class HelloWorld {
private static native String hello(String name);
static {
System.loadLibrary("hello_jni");
}
public static void main(String[] args) {
String output = HelloWorld.hello("Alice");
System.out.println(output);
}
}

View File

@ -0,0 +1,33 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: hello
//! Rust <-> Java FFI demo.
use jni::objects::{JClass, JString};
use jni::sys::jstring;
use jni::JNIEnv;
/// HelloWorld::hello method implementation.
#[no_mangle]
pub extern "system" fn Java_HelloWorld_hello(
env: JNIEnv,
_class: JClass,
name: JString,
) -> jstring {
let input: String = env.get_string(name).unwrap().into();
let greeting = format!("Hello, {input}!");
let output = env.new_string(greeting).unwrap();
output.into_inner()
}

View File

@ -0,0 +1,26 @@
# Interoperability with C
Rust has full support for linking object files with a C calling convention.
Similarly, you can export Rust functions and call them from C.
You can do it by hand if you want:
```rust
extern "C" {
fn abs(x: i32) -> i32;
}
fn main() {
let x = -42;
let abs_x = unsafe { abs(x) };
println!("{x}, {abs_x}");
}
```
We already saw this in the [Safe FFI Wrapper
exercise](../../exercises/day-3/safe-ffi-wrapper.md).
> This assumes full knowledge of the target platform. Not recommended for
> production.
We will look at better options next.

View File

@ -0,0 +1,75 @@
# Using Bindgen
The [bindgen](https://rust-lang.github.io/rust-bindgen/introduction.html) tool
can auto-generate bindings from a C header file.
First create a small C library:
_interoperability/bindgen/libbirthday.h_:
```c
{{#include bindgen/libbirthday.h:card}}
```
_interoperability/bindgen/libbirthday.c_:
```c
{{#include bindgen/libbirthday.c:print_card}}
```
Add this to your `Android.bp` file:
_interoperability/bindgen/Android.bp_:
```javascript
{{#include bindgen/Android.bp:libbirthday}}
```
Create a wrapper header file for the library (not strictly needed in this
example):
_interoperability/bindgen/libbirthday_wrapper.h_:
```c
{{#include bindgen/libbirthday_wrapper.h:include}}
```
You can now auto-generate the bindings:
_interoperability/bindgen/Android.bp_:
```javascript
{{#include bindgen/Android.bp:libbirthday_bindgen}}
```
Finally, we can use the bindings in our Rust program:
_interoperability/bindgen/Android.bp_:
```javascript
{{#include bindgen/Android.bp:print_birthday_card}}
```
_interoperability/bindgen/main.rs_:
```rust,compile_fail
{{#include bindgen/main.rs:main}}
```
Build, push, and run the binary on your device:
```shell
{{#include ../../build_all.sh:print_birthday_card}}
```
Finally, we can run auto-generated tests to ensure the bindings work:
_interoperability/bindgen/Android.bp_:
```javascript
{{#include bindgen/Android.bp:libbirthday_bindgen_test}}
```
```shell
{{#include ../../build_all.sh:libbirthday_bindgen_test}}
```

View File

@ -0,0 +1,36 @@
// ANCHOR: libbirthday
cc_library {
name: "libbirthday",
srcs: ["libbirthday.c"],
}
// ANCHOR_END: libbirthday
// ANCHOR: libbirthday_bindgen
rust_bindgen {
name: "libbirthday_bindgen",
crate_name: "birthday_bindgen",
wrapper_src: "libbirthday_wrapper.h",
source_stem: "bindings",
static_libs: ["libbirthday"],
}
// ANCHOR_END: libbirthday_bindgen
// ANCHOR: libbirthday_bindgen_test
rust_test {
name: "libbirthday_bindgen_test",
srcs: [":libbirthday_bindgen"],
crate_name: "libbirthday_bindgen_test",
test_suites: ["general-tests"],
auto_gen_config: true,
clippy_lints: "none", // Generated file, skip linting
lints: "none",
}
// ANCHOR_END: libbirthday_bindgen_test
// ANCHOR: print_birthday_card
rust_binary {
name: "print_birthday_card",
srcs: ["main.rs"],
rustlibs: ["libbirthday_bindgen"],
}
// ANCHOR_END: print_birthday_card

View File

@ -0,0 +1,20 @@
# Create a C library
_interoperability/c/libbirthday/Android.bp_:
```javascript
{{#include c/libbirthday/Android.bp:libbirthday}}
```
_interoperability/c/libbirthday/libbirthday.h_:
```c
{{#include c/libbirthday/libbirthday.h}}
```
_interoperability/c/libbirthday/libbirthday.c_:
```c
{{#include c/libbirthday/libbirthday.c}}
```

View File

@ -0,0 +1,26 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ANCHOR: print_card
#include <stdio.h>
#include "libbirthday.h"
void print_card(const card* card) {
printf("+--------------\n");
printf("| Happy Birthday %s!\n", card->name);
printf("| Congratulations with the %i years!\n", card->years);
printf("+--------------\n");
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ANCHOR: card
typedef struct card {
const char* name;
int years;
} card;
void print_card(const card* card);

View File

@ -0,0 +1,18 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ANCHOR: include
#include "libbirthday.h"

View File

@ -0,0 +1,29 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: main
//! Bindgen demo.
use birthday_bindgen::{card, print_card};
fn main() {
let name = std::ffi::CString::new("Peter").unwrap();
let card = card {
name: name.as_ptr(),
years: 42,
};
unsafe {
print_card(&card as *const card);
}
}

View File

@ -0,0 +1 @@
# Calling Rust from C

View File

@ -0,0 +1,21 @@
# Handwritten FFI
We can declare external functions by hand:
```rust
extern "C" {
fn abs(x: i32) -> i32;
}
fn main() {
let x = -42;
let abs_x = unsafe { abs(x) };
println!("{x}, {abs_x}");
}
```
We already saw this in the [Safe FFI Wrapper
exercise](../../exercises/day-3/safe-ffi-wrapper.md).
> This assumes full knowledge of the target platform. Not recommended for
> production.

View File

@ -0,0 +1,42 @@
# Calling Rust
Exporting Rust functions and types to C is easy:
_interoperability/rust/libanalyze/analyze.rs_
```rust,editable
{{#include rust/libanalyze/analyze.rs:analyze_numbers}}
```
_interoperability/rust/libanalyze/analyze.h_
```c
{{#include rust/libanalyze/analyze.h:analyze_numbers}}
```
_interoperability/rust/libanalyze/Android.bp_
```javascript
{{#include rust/libanalyze/Android.bp}}
```
We can now call this from a C binary:
_interoperability/rust/analyze/main.c_
```c
{{#include rust/analyze/main.c:main}}
```
_interoperability/rust/analyze/Android.bp_
```javascript
{{#include rust/analyze/Android.bp}}
```
Build, push, and run the binary on your device:
```shell
{{#include ../../build_all.sh:analyze_numbers}}
```

View File

@ -0,0 +1,5 @@
cc_binary {
name: "analyze_numbers",
srcs: ["main.c"],
static_libs: ["libanalyze_ffi"],
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ANCHOR: main
#include "analyze.h"
int main() {
analyze_numbers(10, 20);
analyze_numbers(123, 123);
return 0;
}

View File

@ -0,0 +1,6 @@
rust_ffi {
name: "libanalyze_ffi",
crate_name: "analyze_ffi",
srcs: ["analyze.rs"],
include_dirs: ["."],
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ANCHOR: analyze_numbers
#ifndef ANALYSE_H
#define ANALYSE_H
extern "C" {
void analyze_numbers(int x, int y);
}
#endif

View File

@ -0,0 +1,29 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: analyze_numbers
//! Rust FFI demo.
#![deny(improper_ctypes_definitions)]
use std::os::raw::c_int;
/// Analyze the numbers.
#[no_mangle]
pub extern "C" fn analyze_numbers(x: c_int, y: c_int) {
if x < y {
println!("x ({x}) is smallest!");
} else {
println!("y ({y}) is probably larger than x ({x})");
}
}

31
src/android/logging.md Normal file
View File

@ -0,0 +1,31 @@
# Logging
You should use the `log` crate to automatically log to `logcat` (on-device) or
`stdout` (on-host):
_hello_rust_logs/Android.bp_:
```javascript
{{#include logging/Android.bp}}
```
_hello_rust_logs/src/main.rs_:
```rust,ignore
{{#include logging/src/main.rs:main}}
```
Build, push, and run the binary on your device:
```shell
{{#include build_all.sh:hello_rust_logs}}
```
The logs show up in `adb logcat`:
```shell
$ adb logcat -s rust
09-08 08:38:32.454 2420 2420 D rust: hello_rust_logs: Starting program.
09-08 08:38:32.454 2420 2420 I rust: hello_rust_logs: Things are going fine.
09-08 08:38:32.454 2420 2420 E rust: hello_rust_logs: Something went wrong!
```

View File

@ -0,0 +1,11 @@
rust_binary {
name: "hello_rust_logs",
crate_name: "hello_rust_logs",
srcs: ["src/main.rs"],
rustlibs: [
"liblog_rust",
"liblogger",
],
prefer_rlib: true,
host_supported: true,
}

View File

@ -0,0 +1,29 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: main
//! Rust logging demo.
use log::{debug, error};
/// Logs a greeting.
fn main() {
logger::init(
logger::Config::default()
.with_tag_on_device("rust")
.with_min_level(log::Level::Trace),
);
debug!("Starting program.");
error!("Something went wrong!");
}

13
src/android/setup.md Normal file
View File

@ -0,0 +1,13 @@
# Setup
We will be using an Android Virtual Device to test our code. Make sure you have
access to one or create a new one with:
```shell
$ source build/envsetup.sh
$ lunch aosp_cf_x86_64_phone-userdebug
$ acloud create
```
Please see the [Android Developer
Codelab](https://source.android.com/docs/setup/start) for details.