Skip to content

Commit

Permalink
Add DIDKit FFI
Browse files Browse the repository at this point in the history
Close #1
  • Loading branch information
clehner committed Oct 20, 2020
1 parent 7516bb7 commit d522834
Show file tree
Hide file tree
Showing 115 changed files with 2,321 additions and 1 deletion.
11 changes: 11 additions & 0 deletions .cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[target.i686-linux-android]
linker = "i686-linux-android16-clang"

[target.armv7-linux-androideabi]
linker = "armv7a-linux-androideabi16-clang"

[target.aarch64-linux-android]
linker = "aarch64-linux-android21-clang"

[target.x86_64-linux-android]
linker = "x86_64-linux-android21-clang"
44 changes: 43 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml', '**.rs') }}
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml', 'lib/Makefile', '**.rs') }}
restore-keys: |
${{ runner.os }}-cargo-
Expand All @@ -47,3 +47,45 @@ jobs:

- name: Test
run: cargo test --verbose --workspace

- name: Install Rust Android targets
run: make -C lib install-rustup-android

- name: Install JDK
uses: actions/setup-java@v1
with:
java-version: 1.8

- name: Install Flutter
uses: subosito/flutter-action@v1

- name: Opt out of Dart/Flutter analytics
run: |
dart --disable-analytics
flutter --suppress-analytics config --no-analytics
- name: Install Android SDK
uses: android-actions/setup-android@v2

- name: Cache Android NDK
id: ndk-cache
uses: actions/cache@v2
with:
path: ${{ env.ANDROID_SDK_ROOT }}/ndk-bundle
key: ${{ runner.os }}-ndk-bundle

- name: Install Android NDK
if: steps.ndk-cache.outputs.cache-hit != 'true'
run: $ANDROID_SDK_ROOT/tools/bin/sdkmanager ndk-bundle

- name: Test C FFI
run: make -C lib ../target/test/c.stamp

- name: Test JNI
run: make -C lib ../target/test/java.stamp

- name: Test Dart/Flutter plugin
run: make -C lib ../target/test/flutter.stamp

- name: Build Android Archive
run: make -C lib ../target/test/aar.stamp
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ members = [
"http",
"cli",
"lib",
"lib/cbindings"
]
1 change: 1 addition & 0 deletions lib/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/tmp
4 changes: 4 additions & 0 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ edition = "2018"

[dependencies]
ssi = { path = "../../ssi" }
jni = "0.17"

[lib]
crate-type = ["lib", "cdylib"]
8 changes: 8 additions & 0 deletions lib/FFI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## FFI

The DIDKit library is written in Rust, but has bindings for other languages and environments:

- [C](c/)
- [Java](java/)
- [Android](android/)
- [Flutter](flutter/)
102 changes: 102 additions & 0 deletions lib/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# didkit/lib/Makefile

TARGET=../target

.PHONY: test
test: $(TARGET)/test/c.stamp \
$(TARGET)/test/java.stamp \
$(TARGET)/test/aar.stamp \
$(TARGET)/test/flutter.stamp

## Setup

android/res $(TARGET)/test $(TARGET)/jvm:
mkdir -p $@

## Rust

RUST_SRC=Cargo.toml $(wildcard src/*.rs src/*/*.rs src/*/*/*.rs)

$(TARGET)/didkit.h: cbindgen.toml cbindings/build.rs cbindings/Cargo.toml $(RUST_SRC)
cargo build -p didkit-cbindings
test -s $@ && touch $@

$(TARGET)/release/libdidkit.so: $(RUST_SRC)
cargo build --lib --release
strip $@

## C

$(TARGET)/test/c.stamp: $(TARGET)/cabi-test $(TARGET)/release/libdidkit.so | $(TARGET)/test
$(TARGET)/cabi-test
touch $@

$(TARGET)/cabi-test: c/test.c $(TARGET)/didkit.h
$(CC) -I$(TARGET) $< -ldl -o $@

## Java

JAVA_SRC=$(wildcard java/*/*.java java/*/*/*.java java/*/*/*/*.java)

$(TARGET)/test/java.stamp: $(TARGET)/jvm/com/spruceid/DIDKit.class $(TARGET)/release/libdidkit.so | $(TARGET)/test
java -Djava.class.path=$(TARGET)/jvm \
-Djava.library.path=$(TARGET)/release \
com.spruceid.DIDKit
touch $@

$(TARGET)/jvm/com/spruceid/DIDKit.class: java/com/spruceid/DIDKit.java | $(TARGET)/jvm
javac $^ -d $(TARGET)/jvm -source 1.7 -target 1.7

$(TARGET)/com_spruceid_DIDKit.h: java/com/spruceid/DIDkit.java
javac -h $(TARGET) $<

$(TARGET)/didkit.jar: $(TARGET)/jvm/com/spruceid/DIDKit.class
jar -cf $@ -C $(TARGET)/jvm $(patsubst $(TARGET)/jvm/%,%,$^)

## Android

.PHONY: install-rustup-android
install-rustup-android:
rustup target add i686-linux-android armv7-linux-androideabi aarch64-linux-android x86_64-linux-android

ANDROID_SDK_ROOT ?= ~/Android/Sdk
ANDROID_TOOLS ?= $(lastword $(wildcard $(ANDROID_SDK_ROOT)/build-tools/*))
ANDROID_NDK_HOME ?= $(lastword $(wildcard \
$(ANDROID_SDK_ROOT)/ndk/* \
$(ANDROID_SDK_ROOT)/ndk-bundle))
OS_NAME=$(shell uname | tr '[:upper:]' '[:lower:]')
TOOLCHAIN=$(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/$(OS_NAME)-x86_64
ANDROID_LIBS=\
$(TARGET)/i686-linux-android/release/libdidkit.so\
$(TARGET)/armv7-linux-androideabi/release/libdidkit.so\
$(TARGET)/aarch64-linux-android/release/libdidkit.so\
$(TARGET)/x86_64-linux-android/release/libdidkit.so

$(TARGET)/test/aar.stamp: $(TARGET)/didkit.aar | $(TARGET)/test
rm -rf tmp/test-aar
mkdir -p tmp/test-aar
cd tmp/test-aar && unzip -q ../../$< || unzip -l ../../$<
cd tmp/test-aar && unzip -qo classes.jar com/spruceid/DIDKit.class || unzip -l classes.jar
javap tmp/test-aar/com/spruceid/DIDKit.class | grep -q 'public class com.spruceid.DIDKit'
touch $@

$(TARGET)/didkit.aar: $(TARGET)/didkit.jar android/AndroidManifest.xml android/R.txt $(ANDROID_LIBS) | android/res
$(ANDROID_TOOLS)/aapt package -f -S android/res -F $@ --ignore-assets '.*:*~:README.md' android

$(TARGET)/%/release/libdidkit.so: $(RUST_SRC)
PATH=$(TOOLCHAIN)/bin:$(PATH) \
cargo build --lib --release --target $*
$(TOOLCHAIN)/bin/llvm-strip $@

## Flutter

$(TARGET)/test/flutter.stamp: flutter/lib/didkit.dart $(TARGET)/release/libdidkit.so | $(TARGET)/test
cd flutter && LD_LIBRARY_PATH=$$PWD \
flutter --suppress-analytics test
touch $@

## Cleanup

.PHONY: clean
clean:
cargo clean
3 changes: 3 additions & 0 deletions lib/android/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.spruceid.DIDKit">
</manifest>
Empty file added lib/android/R.txt
Empty file.
17 changes: 17 additions & 0 deletions lib/android/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# DIDKit - Android

[Android Library (AAR file)][AAR] for DIDKit. The AAR file includes Java class files using [JNI][], and binary shared libraries for Android's supported architectures (x86, armeabi-v7a, arm64-v8a, x86\_64). It can be added to existing Android projects using Android Studio or Gradle.

## Requires

Android SDK and NDK, for Linux x86\_64

## Build

In the parent directory, run:
```
make target/didkit.aar
```

[AAR]: https://developer.android.com/studio/projects/android-library.html#aar-contents
[JNI]: https://en.wikipedia.org/wiki/Java_Native_Interface
1 change: 1 addition & 0 deletions lib/android/classes.jar
1 change: 1 addition & 0 deletions lib/android/jni/arm64-v8a/libdidkit.so
1 change: 1 addition & 0 deletions lib/android/jni/armeabi-v7a/libdidkit.so
1 change: 1 addition & 0 deletions lib/android/jni/x86/libdidkit.so
1 change: 1 addition & 0 deletions lib/android/jni/x86_64/libdidkit.so
14 changes: 14 additions & 0 deletions lib/c/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# DIDKit - C

Shared Library for DIDKit. Intended to be compatible with C.

## Build

In the parent directory, run:
```
make ../target/release/libdidkit.so
```

## Android

To build the C shared library for Android targets, build the [DIDKit Android library](../android/).
15 changes: 15 additions & 0 deletions lib/c/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <dlfcn.h>
#include <err.h>
#include <stdio.h>
#include "didkit.h"

int main() {
void *lib = dlopen("../target/release/libdidkit.so", RTLD_NOW);
if (lib == NULL) errx(1, "dlopen: %s", dlerror());
const char *(*didkit_get_version)() = dlsym(lib, "didkit_get_version");
if (didkit_get_version == NULL) errx(1, "unable to find version function");
const char *version = didkit_get_version();
printf("C libdidkit version: %s\n", version);
int rc = dlclose(lib);
if (rc < 0) errx(1, "dlclose: %s", dlerror());
}
3 changes: 3 additions & 0 deletions lib/cbindgen.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
language = "C"
autogen_warning = "// didkit.h - autogenerated by cbindgen"
pragma_once = true
10 changes: 10 additions & 0 deletions lib/cbindings/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "didkit-cbindings"
version = "0.1.0"
authors = ["Charles E. Lehner <[email protected]>"]
edition = "2018"
publish = false

[build-dependencies]
#didkit = { path = "../" }
cbindgen = "0.14"
5 changes: 5 additions & 0 deletions lib/cbindings/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# cbindings for didkit

This crate builds a C header file for the didkit crate. It is a separate crate so that it runs after the didkit crate is built, rather than blocking compilation of the didkit crate. If there are syntax errors, the Rust compiler gives more useful output than would the failing build script using cbindgen.

Related issue: [Cargo post-build script execution](https://github.com/rust-lang/cargo/issues/545)
23 changes: 23 additions & 0 deletions lib/cbindings/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
extern crate cbindgen;

use std::env;
use std::path::Path;

fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let lib_dir = Path::new(&crate_dir).parent().unwrap();
let workspace_dir = lib_dir.parent().unwrap();

// Docs say to output into OUT_DIR, but then how do we use that?
// https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script
// https://stackoverflow.com/questions/63928113/is-there-a-way-to-change-out-dir-for-a-build-rs
// let out_dir = env::var("OUT_DIR").unwrap();
let out_dir = workspace_dir.join("target");
let out_file = out_dir.join("didkit.h");

cbindgen::generate(lib_dir)
.expect("Unable to generate bindings")
.write_to_file(&out_file);

println!("cargo:rerun-if-changed={:?}", &out_file);
}
1 change: 1 addition & 0 deletions lib/cbindings/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// placeholder for build-only crate
7 changes: 7 additions & 0 deletions lib/flutter/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.DS_Store
.dart_tool/

.packages
.pub/

build/
10 changes: 10 additions & 0 deletions lib/flutter/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: 6b39acdc53bad8b3d51d1618e3df4e299b344d7c
channel: master

project_type: plugin
3 changes: 3 additions & 0 deletions lib/flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 0.0.1

* TODO: Describe initial release.
1 change: 1 addition & 0 deletions lib/flutter/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TODO: Add your license here.
16 changes: 16 additions & 0 deletions lib/flutter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# DIDKit - Flutter

[Flutter plugin][packages-plugins] for the DIDKit library. Includes Dart bindings, and functionality for Android and iOS.

## Usage

You can depend on this plugin as a [path dependency][path-packages].

You will also need to build the DIDKit library for your target platforms.
To do that for Android, trigger building the AAR file - in the parent directory:
```
make ../target/didkit.aar
```

[path-packages]: https://dart.dev/tools/pub/dependencies#path-packages
[packages-plugins]: https://flutter.dev/developing-packages/
8 changes: 8 additions & 0 deletions lib/flutter/android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
33 changes: 33 additions & 0 deletions lib/flutter/android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
group 'com.spruceid.DIDKit'
version '1.0'

buildscript {
repositories {
google()
jcenter()
}

dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
}
}

rootProject.allprojects {
repositories {
google()
jcenter()
}
}

apply plugin: 'com.android.library'

android {
compileSdkVersion 29

defaultConfig {
minSdkVersion 16
}
lintOptions {
disable 'InvalidPackage'
}
}
3 changes: 3 additions & 0 deletions lib/flutter/android/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
Loading

0 comments on commit d522834

Please sign in to comment.