Skip to content

Commit

Permalink
Add minimal example project (including CI) - take 2 (#122)
Browse files Browse the repository at this point in the history
* Add minimal example project (including CI)
  • Loading branch information
mgeier authored Oct 16, 2020
1 parent 9ca75ad commit b37d3e9
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 0 deletions.
84 changes: 84 additions & 0 deletions .github/workflows/example-project.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: Build example project

on: [push, pull_request]

env:
CARGO_TERM_COLOR: always

jobs:

example-project:

strategy:
matrix:
include:
- os: ubuntu-latest
- os: macos-latest
- os: windows-latest
toolchain-suffix: -gnu
- os: windows-latest
toolchain-suffix: -msvc

runs-on: ${{ matrix.os }}

steps:
- name: Clone Git repository
uses: actions/checkout@v2

- name: Install Rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable${{ matrix.toolchain-suffix }}
override: true

- name: Install cargo-c applet
run: |
cargo install --path .
- name: Set MSVC binaries path
if: matrix.toolchain-suffix == '-msvc'
run: |
$BinGlob = "VC\Tools\MSVC\*\bin\Hostx64\x64"
$BinPath = vswhere -latest -products * -find "$BinGlob" | Select-Object -Last 1
echo "$BinPath" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Test example project
working-directory: example-project
run: |
cargo test --verbose
- name: Build C API for example project
working-directory: example-project
run: |
cargo cbuild --verbose --release
- name: Install into temporary location
if: startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'ubuntu')
working-directory: example-project
run: |
cargo cinstall --verbose --release --destdir=temp
- name: Copy installed files to /usr/local
if: startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'ubuntu')
working-directory: example-project
run: |
sudo cp -r temp/usr/local/* /usr/local/
- name: Test pkg-config
if: startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'ubuntu')
run: |
set -x
test "$(pkg-config --cflags example_project)" = "-I/usr/local/include/example_project"
test "$(pkg-config --libs example_project)" = "-L/usr/local/lib -lexample_project"
- name: Update dynamic linker cache
if: startsWith(matrix.os, 'ubuntu')
run: |
sudo ldconfig
- name: Test usage from C (using Makefile)
if: startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'ubuntu')
working-directory: example-project/usage-from-c
run: |
make
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/target
**/*.rs.bk
Cargo.lock
/example-project/target/
/example-project/usage-from-c/run_tests
11 changes: 11 additions & 0 deletions example-project/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "example-project"
version = "0.1.0"
edition = "2018"

[features]
default = []
capi = ["libc"]

[dependencies]
libc = { version = "0.2", optional = true }
19 changes: 19 additions & 0 deletions example-project/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Example project using `cargo-c`
===============================

For detailed usage instructions, have a look at the
[Github workflow configuration](../.github/workflows/example-project.yml).

Note that `cargo install --path .` is used to install `cargo-c`
from the locally cloned Git repository.
If you want to install the latest release from
[crates.io](https://crates.io/crates/cargo-c),
you should use this instead:

cargo install cargo-c

Running `cargo cbuild --release` will create the C header file
`example_project.h` in the `target/release` directory.
This file will contain the comments from the file [`capi.rs`](src/capi.rs).

Run `cargo doc --open` to view the documentation of the Rust code.
7 changes: 7 additions & 0 deletions example-project/cbindgen.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
include_guard = "EXAMPLE_PROJECT_H"
include_version = true
language = "C"
cpp_compat = true

[export.rename]
"OddCounter" = "ExampleProjectOddCounter"
31 changes: 31 additions & 0 deletions example-project/src/capi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use crate::OddCounter;

// NB: The documentation comments from this file will be available
// in the auto-generated header file example_project.h

/// Create new counter object given a start value.
///
/// On error (if an even start value is used), NULL is returned.
/// The returned object must be eventually discarded with example_project_oddcounter_free().
#[no_mangle]
pub extern "C" fn example_project_oddcounter_new(start: u32) -> Option<Box<OddCounter>> {
OddCounter::new(start).ok().map(Box::new)
}

/// Discard a counter object.
///
/// Passing NULL is allowed.
#[no_mangle]
pub extern "C" fn example_project_oddcounter_free(_: Option<Box<OddCounter>>) {}

/// Increment a counter object.
#[no_mangle]
pub extern "C" fn example_project_oddcounter_increment(counter: &mut OddCounter) {
counter.increment()
}

/// Obtain the current value of a counter object.
#[no_mangle]
pub extern "C" fn example_project_oddcounter_get_current(counter: &OddCounter) -> u32 {
counter.current()
}
68 changes: 68 additions & 0 deletions example-project/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*!
Example library for [cargo-c].
[cargo-c]: https://crates.io/crates/cargo-c
*/

#![warn(rust_2018_idioms)]
#![deny(missing_docs)]

#[cfg(cargo_c)]
mod capi;

/// A counter for odd numbers.
///
/// Note that this `struct` does *not* use `#[repr(C)]`.
/// It can therefore contain arbitrary Rust types.
/// In the C API, it will be available as an *opaque pointer*.
#[derive(Debug)]
pub struct OddCounter {
number: u32,
}

impl OddCounter {
/// Create a new counter, given an odd number to start.
pub fn new(start: u32) -> Result<OddCounter, OddCounterError> {
if start % 2 == 0 {
Err(OddCounterError::Even)
} else {
Ok(OddCounter { number: start })
}
}

/// Increment by 2.
pub fn increment(&mut self) {
self.number += 2;
}

/// Obtain the current (odd) number.
pub fn current(&self) -> u32 {
self.number
}
}

/// Error type for [OddCounter::new].
///
/// In a "real" library, there would probably be more error variants.
#[derive(Debug)]
pub enum OddCounterError {
/// An even number was specified as `start` value.
Even,
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn create42() {
assert!(OddCounter::new(42).is_err());
}

#[test]
fn increment43() {
let mut counter = OddCounter::new(43).unwrap();
counter.increment();
assert_eq!(counter.current(), 45);
}
}
7 changes: 7 additions & 0 deletions example-project/usage-from-c/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
LDLIBS = -lexample_project

test: run_tests
./run_tests

clean:
$(RM) run_tests
24 changes: 24 additions & 0 deletions example-project/usage-from-c/run_tests.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include <example_project/example_project.h>
#include <stdio.h>

int main() {
ExampleProjectOddCounter *counter = example_project_oddcounter_new(4);
if (counter) {
printf("Unexpected success\n");
return 1;
}
counter = example_project_oddcounter_new(5);
if (!counter) {
printf("Error creating ExampleProjectOddCounter\n");
return 1;
}
example_project_oddcounter_increment(counter);
uint32_t result = example_project_oddcounter_get_current(counter);
example_project_oddcounter_free(counter);
if (result == 7) {
return 0;
} else {
printf("Error: unexpected result: %d\n", result);
return 1;
}
}

0 comments on commit b37d3e9

Please sign in to comment.