Skip to content

Commit 75addf8

Browse files
authored
Conan 👨🏼
* Dynamic Library distribution system compiles for each target os + architecture
1 parent 41880aa commit 75addf8

18 files changed

+414
-48
lines changed

.github/workflows/publish.yml

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -39,41 +39,75 @@ jobs:
3939
strategy:
4040
matrix:
4141
dist:
42-
- os: linux
43-
arch: x64
44-
runner: ubuntu-latest
45-
- os: android
46-
arch: x86_64
47-
runner: ubuntu-latest
48-
- os: android
49-
arch: armeabi-v7a
50-
runner: ubuntu-latest
51-
- os: android
52-
arch: arm64-v8a
53-
runner: ubuntu-latest
54-
- os: macos
55-
arch: arm64
56-
runner: macos-latest
42+
- profile: linux-x64
43+
runner: ubuntu-latest
44+
release: linux-x64
45+
- profile: macos-arm64
46+
runner: macos-latest
47+
release: macos-arm64
48+
- profile: android-x86_64
49+
runner: ubuntu-latest
50+
release: android-x86_64
51+
- profile: android-armv8
52+
runner: ubuntu-latest
53+
release: android-arm64-v8a
54+
fail-fast: false
5755

5856
runs-on: ${{ matrix.dist.runner }}
5957
steps:
6058
- uses: actions/checkout@v4
6159
with:
6260
submodules: "true"
63-
- id: dist
64-
env:
65-
OS: ${{ matrix.dist.os }}
66-
ARCH: ${{ matrix.dist.arch }}
67-
run: make dist-ci
68-
- uses: actions/upload-artifact@v4
69-
name: Upload Artifact
61+
62+
- uses: actions/setup-python@v5
63+
with:
64+
python-version: "3.x"
65+
66+
- uses: nttld/setup-ndk@v1
67+
if: startsWith(matrix.dist.profile, 'android')
68+
id: setup-ndk
7069
with:
71-
path: ${{ steps.dist.outputs.tar_gz_path }}
72-
name: ${{ steps.dist.outputs.tar_gz_name }}
70+
ndk-version: r26d
71+
72+
- name: Install Dependencies
73+
run: |
74+
python -m pip install --upgrade pip
75+
pip install poetry
76+
poetry config virtualenvs.in-project true
77+
poetry install -v
78+
source .venv/bin/activate
79+
conan profile detect
80+
81+
- name: Compile
82+
env:
83+
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
84+
id: compile
85+
run: |
86+
source .venv/bin/activate
87+
if ${{ startsWith(matrix.dist.profile, 'android') }};
88+
then
89+
conan create . \
90+
--profile:host profiles/${{ matrix.dist.profile }} \
91+
--conf:host tools.android:ndk_path=$ANDROID_NDK_HOME \
92+
--options:host '&:ci=True'
93+
else
94+
conan create . \
95+
--options:host ci=True
96+
fi
97+
98+
- name: Package
99+
id: package
100+
env:
101+
OUTPUT_TAR_NAME: libfhel-${{ matrix.dist.release }}.tar.gz
102+
run: |
103+
cd ${{ steps.compile.outputs.conan_package_path }}
104+
tar -czf $OUTPUT_TAR_NAME *
105+
echo "tar_gz_path=$(pwd)/$OUTPUT_TAR_NAME" >> $GITHUB_OUTPUT
106+
73107
- uses: softprops/action-gh-release@v2
74108
name: GitHub Release
75109
with:
76-
files: ${{ steps.dist.outputs.tar_gz_path }}
110+
files: ${{ steps.package.outputs.tar_gz_path }}
77111
draft: true
78112
prerelease: false
79113

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ Testing/
1515
# Dart
1616
pubspec.lock
1717

18+
# Python
19+
.venv
20+
pyvenv.cfg
21+
poetry.lock
22+
23+
# Conan
24+
CMakeFiles/
25+
CMakeCache.txt
26+
CMakePresets.json
27+
cmake_install.cmake
28+
*conan*
29+
!conanfile.py
30+
1831
# IntelliJ related
1932
*.iml
2033
*.ipr

CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ cmake_minimum_required(VERSION 3.10)
1212
set(CMAKE_CXX_STANDARD 14)
1313
set(CMAKE_CXX_STANDARD_REQUIRED ON)
1414

15-
project(fhel VERSION 0.0.8 LANGUAGES CXX)
15+
project(fhel VERSION 0.0.12 LANGUAGES CXX)
1616

1717
# Import Backend Library
18-
add_subdirectory(src/backend/seal)
18+
add_subdirectory(src/backend/seal seal_build)
1919

2020
# find_package(SEAL REQUIRED)
2121

Makefile

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -36,24 +36,6 @@ build-cmake:
3636
.PHONY: build
3737
build: trust-project build-cmake
3838

39-
# Release helper
40-
.PHONY: dist-cmake
41-
dist-cmake: export UNIT_TEST ?= OFF
42-
dist-cmake: export TARGET_DIR ?= $(FHE_RELEASE_DIR)
43-
dist-cmake:
44-
@make build-cmake
45-
46-
.PHONY: dist
47-
dist: dist-cmake
48-
49-
.PHONY: dist-ci
50-
dist-ci: OUTPUT_FILE ?= libfhel-$(OS)-$(ARCH).tar.gz
51-
dist-ci: OUTPUT_PATH ?= $(FHE_RELEASE_DIR)/$(OUTPUT_FILE)
52-
dist-ci: dist-cmake
53-
@ls -d $(FHE_RELEASE_DIR)/libfhel* | grep -o '[^/]*$$' | tar -czvf $(OUTPUT_PATH) -C $(FHE_RELEASE_DIR) --files-from=-
54-
@echo "tar_gz_name=$(OUTPUT_FILE)" >> $(GITHUB_OUTPUT)
55-
@echo "tar_gz_path=$(OUTPUT_PATH)" >> $(GITHUB_OUTPUT)
56-
5739
# Generate html dart api docs
5840
.PHONY: docs
5941
docs:

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,15 @@ dart pub publish
5050
```
5151

5252
In the future, we may configure an [automated](https://dart.dev/tools/pub/automated-publishing) publishing.
53+
54+
## Distribution 📦
55+
56+
For distribution, we use [Conan](https://conan.io/) to manage C++ dependencies, wrapped with [Poetry](https://python-poetry.org/) to be run in a virtual environment.
57+
58+
From the root of the project:
59+
```bash
60+
pip install poetry
61+
poetry config virtualenvs.create true
62+
poetry install
63+
conan create .
64+
```

conanfile.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
from conan import ConanFile
2+
from conan.tools.files import load, copy
3+
from conan.errors import ConanInvalidConfiguration
4+
from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps
5+
import conan.tools.files as cfiles
6+
from pathlib import Path
7+
import tarfile, os
8+
9+
# for compatibility
10+
release_arch = {
11+
# "windows": {
12+
# "x86_64": "x64",
13+
# },
14+
"linux": {
15+
"x86_64": "x64",
16+
},
17+
"android": {
18+
"x86_64": "x86_64",
19+
"armv8": "arm64-v8a",
20+
"armv7": "armeabi-v7a",
21+
},
22+
"macos": {
23+
"x86_64": "x64",
24+
"armv8": "arm64",
25+
},
26+
# "ios": {
27+
# "x86_64": "x64",
28+
# "armv8": "arm64",
29+
# },
30+
}
31+
32+
class fhel(ConanFile):
33+
name = "fhel"
34+
settings = "os", "arch", "compiler", "build_type"
35+
exports_sources = "src/*", "include/*", "test/*", "CMakeLists.txt"
36+
options = {
37+
"ci": [True, False],
38+
}
39+
40+
default_options = {
41+
"ci": False,
42+
}
43+
44+
def set_version(self):
45+
if not self.recipe_folder:
46+
raise ConanInvalidConfiguration("recipe_folder not set")
47+
48+
if not self.version:
49+
version_file = Path(
50+
os.path.join(self.recipe_folder, "dart" , "binary.version")
51+
)
52+
if not version_file.exists():
53+
raise ConanInvalidConfiguration("binary.version file not found")
54+
55+
self.version = load(self, version_file).strip();
56+
57+
def generate(self):
58+
"Generate the cmake toolchain file"
59+
tc = CMakeToolchain(self)
60+
tc.generate()
61+
62+
def seal_negative_args(self) -> list[str]:
63+
"""
64+
Append negative options to the cmake command,
65+
e.g. -DSEAL_USE_MSGZL=OFF (default is ON)
66+
"""
67+
args = []
68+
if self.settings.os == "Android":
69+
if self.settings.arch != "armv7":
70+
args.append("-DSEAL_BUILD_SEAL_C=1") # 64-bit only
71+
args.append("-DSEAL_USE_INTRIN=1")
72+
args.append("-DSEAL_ARM64_EXITCODE=0")
73+
args.append("-DSEAL_ARM64_EXITCODE__TRYRUN_OUTPUT=''")
74+
args.append("-DSEAL___BUILTIN_CLZLL_FOUND_EXITCODE=0")
75+
args.append("-DSEAL___BUILTIN_CLZLL_FOUND_EXITCODE__TRYRUN_OUTPUT=''")
76+
args.append("-DSEAL__ADDCARRY_U64_FOUND_EXITCODE=0")
77+
args.append("-DSEAL__ADDCARRY_U64_FOUND_EXITCODE__TRYRUN_OUTPUT=''")
78+
args.append("-DSEAL__SUBBORROW_U64_FOUND_EXITCODE=0")
79+
args.append("-DSEAL__SUBBORROW_U64_FOUND_EXITCODE__TRYRUN_OUTPUT=''")
80+
return args
81+
82+
def build(self):
83+
"Compile libfhel dynamic library"
84+
cmake = CMake(self)
85+
cmake.configure(cli_args=self.seal_negative_args())
86+
cmake.build()
87+
88+
def copy_files(self):
89+
"Copy the library to the package folder"
90+
if not self.source_folder:
91+
raise ConanInvalidConfiguration("source_folder not set")
92+
if not self.package_folder:
93+
raise ConanInvalidConfiguration("package_folder not set")
94+
95+
copy(self,
96+
pattern=f"libfhel*",
97+
src=self.source_folder,
98+
dst=self.package_folder,
99+
)
100+
101+
def package(self):
102+
"Package the compiled library"
103+
self.copy_files()
104+
if self.options.ci:
105+
with open(os.environ['GITHUB_OUTPUT'], "a+") as f:
106+
print(f'conan_package_path={self.package_folder}', file=f)

dart/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## 0.0.12
2+
3+
- Added Ciphertext.fromBytes(Uint8List) Constructor
4+
- Added Ciphertext.toBytes() returns Uint8List
5+
- Added afhe.decodeDouble returns first element of decoded vector
6+
7+
## 0.0.11
8+
9+
Conan Release Management
10+
111
## 0.0.8
212

313
Auto-build Android binary in github releases

dart/binary.version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.0.8
1+
0.0.12

dart/lib/afhe.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
library afhe;
77

88
import 'dart:ffi';
9+
import 'dart:typed_data'; // For Uint8List
910
import 'package:ffi/ffi.dart'; // for Utf8
1011
import 'dart:io' show Directory, Platform;
1112
import 'package:path/path.dart' as path;
@@ -237,6 +238,13 @@ class Afhe {
237238
return Plaintext.fromPointer(backend, ptr, extractStr: false);
238239
}
239240

241+
/// Decodes a [Plaintext] into a double.
242+
double decodeDouble(Plaintext plaintext) {
243+
Pointer<Double> ptr = _c_decode_vector_double(library, plaintext.obj);
244+
raiseForStatus();
245+
return ptr[0];
246+
}
247+
240248
/// Decodes a [Plaintext] into a list of doubles.
241249
List<double> decodeVecDouble(Plaintext plaintext, int arrayLength) {
242250
Pointer<Double> ptr = _c_decode_vector_double(library, plaintext.obj);

dart/lib/afhe/ciphertext.dart

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ final _LoadCiphertext _c_load_ciphertext = dylib
3737
.lookup<NativeFunction<_LoadCiphertextC>>('load_ciphertext')
3838
.asFunction();
3939

40-
4140
/// Represents an underlying C ciphertext object.
4241
///
4342
/// A ciphertext is an encrypted message. It is the output of the encryption function,
@@ -48,6 +47,7 @@ class Ciphertext {
4847
// String text = "";
4948
/// Selects the [Backend] used for the encryption scheme.
5049
Backend backend = Backend();
50+
5151
/// A pointer to the memory address of the underlying C++ object.
5252
Pointer obj = nullptr;
5353

@@ -68,6 +68,18 @@ class Ciphertext {
6868
/// Saves the [Ciphertext] to a non-human-readable format.
6969
/// Useful for saving to disk or sending over the network.
7070
Pointer<Uint8> save() => _c_save_ciphertext(obj);
71+
72+
/// Converts a [Ciphertext] into a serialized binary format.
73+
Uint8List toBytes() => save().asTypedList(saveSize);
74+
75+
/// Loads a [Ciphertext] from a serialized binary format.
76+
Ciphertext.fromBytes(Afhe fhe, Uint8List bytes) {
77+
final Pointer<Uint8> pointer = malloc.allocate<Uint8>(bytes.length);
78+
// Copy the bytes to the allocated memory
79+
final Uint8List pointerContent = pointer.asTypedList(bytes.length);
80+
pointerContent.setAll(0, bytes);
81+
obj = _c_load_ciphertext(fhe.library, pointer, bytes.length);
82+
}
7183
}
7284

7385
// --- noise budget ---

dart/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: fhel
22
description: |-
33
Natively supports Fully Homomorphic Encryption operations on sensitive data without revealing it.
44
Supports Key Derivation, Encryption, Decryption, and Mathematical operations.
5-
version: 0.0.8
5+
version: 0.0.12
66
repository: https://github.com/jeffmur/fhel
77

88
environment:

0 commit comments

Comments
 (0)