- Create a static library in Rust
- Define a function that prints to screen
- Test it from Rust
Create a new library project and give it a name. In these notes we will use the meaningless name anvil
, so the resulting static library will called libanvil.a
.
$ cd ~/code $ cargo new --lib anvil Created library `anvil` package $
Open Cargo.toml
and add a [lib]
section to select the output formats:
[package] name = "anvil" version = "0.1.0" authors = ["Thomas Karpiniec <tom.karpiniec@outlook.com>"] edition = "2018" [lib] crate-type = ["lib", "staticlib"] [dependencies]
Open src/lib.rs
and delete the generated test. Add a function like this one:
#[no_mangle]
pub extern "C" fn hello_devworld() {
println!("Hello, /dev/world 2019!");
}
Inside the anvil
project directory, make sure it compiles:
$ cargo build Compiling anvil v0.1.0 (/Users/tk/code/anvil) Finished dev [unoptimized + debuginfo] target(s) in 0.36s
Check that you can see both the rust library and the static library in the target
subdirectory:
$ ls -l target/debug/lib* -rw-r--r-- 2 tk staff 4416376 24 Aug 11:05 target/debug/libanvil.a -rw-r--r-- 1 tk staff 78 24 Aug 11:05 target/debug/libanvil.d -rw-r--r-- 2 tk staff 18520 24 Aug 11:05 target/debug/libanvil.rlib
Check that your function is being exported from the static library. This means it can be used from C (and Swift)!
$ nm target/debug/libanvil.a | grep hello 0000000000000000 T _hello_devworld
Next, let's provide a way to test the library function by calling it from a normal Rust application. Inside the anvil
directory, manually create a new directory and a file inside it: examples/testanvil.rs
.
$ mkdir examples $ cd examples $ touch testanvil.rs
Your project folder should now be laid out like this:
├── Cargo.lock ├── Cargo.toml ├── examples │ └── testanvil.rs ├── src │ └── lib.rs ...
Open testanvil.rs
and enter the following Rust program:
use anvil::*;
fn main() {
hello_devworld();
}
You can now use this test program to call into your anvil library.
$ cargo run --example testanvil Finished dev [unoptimized + debuginfo] target(s) in 0.00s Running `target/debug/examples/testanvil` Hello, /dev/world 2019!
Searching on https://crates.io for something interesting we might spot the crate ferris-says
.
Let's make our function more exciting. Open Cargo.toml
and add a reference to this crate underneath [dependencies]
.
[dependencies]
ferris-says = "0.1"
Change src/lib.rs
to use this new crate:
use std::io::{stdout, BufWriter};
#[no_mangle]
pub extern "C" fn hello_devworld() {
let phrase = b"Hello, /dev/world/2019!";
let stdout = stdout();
let mut writer = BufWriter::new(stdout.lock());
ferris_says::say(phrase, 30, &mut writer).unwrap();
}
Now we get a more interesting printout.
$ cargo run --example testanvil Finished dev [unoptimized + debuginfo] target(s) in 0.01s Running `target/debug/examples/testanvil` ---------------------------------- | Hello, /dev/world/2019! | ---------------------------------- \ \ _~^~^~_ \) / o o \ (/ '_ - _' / '-----' \
If you don't feel like going all the way to Swift yet, you can try out the library from an ordinary C program.
We need to make sure we link in all the required system libraries to support our library and its use of the Rust standard library. Run this command to see what flags you will need - note that this may vary from system to system.
$ cargo rustc -- --print native-static-libs Compiling anvil v0.1.0 (/Users/tk/code/anvil) note: Link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms. note: native-static-libs: -lSystem -lresolv -lc -lm Finished dev [unoptimized + debuginfo] target(s) in 0.71s
Create a new folder out of the way, and copy over your compiled static library.
$ mkdir ~/code/hello_c $ cd ~/code/hello_c $ cp ~/code/anvil/target/debug/libanvil.a . $ touch hello.c
Edit hello.c
and fill in a program that calls the library function. We don't have an anvil.h
header yet (that comes later!) so we'll need to declare the function prototype ourselves.
extern void hello_devworld();
int main(int argc, char *argv[]) {
hello_devworld();
return 0;
}
Compile and run it:
$ cc hello.c libanvil.a -lSystem -lresolv -lc -lm -o hello $ ./hello ---------------------------------- | Hello, /dev/world/2019! | ---------------------------------- \ \ _~^~^~_ \) / o o \ (/ '_ - _' / '-----' \