Skip to content

Commit 59215e6

Browse files
authored
Macos binary + retrieve binary from .pub-cache (#72)
1 parent 4299a3c commit 59215e6

File tree

8 files changed

+145
-103
lines changed

8 files changed

+145
-103
lines changed

.github/workflows/publish.yml

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,16 @@ jobs:
2020
- run: echo "PUB_VER=$(grep -oP 'version:\s\K[0-9]+\.[0-9]+\.[0-9]+' dart/pubspec.yaml)" >> $GITHUB_ENV
2121
# Compare the versions in env to github.ref_name
2222
- run: |
23-
if [ "${{ env.CMAKE_VER }}" != "${{ env.PUB_VER }}" || "${{ env.CMAKE_VER }}" != "${{ github.ref_name }}" || "${{ github.ref_name }}" != "${{ env.PUB_VER }}" ]; then
24-
echo "Version mismatch: CMakeLists.txt: ${{ env.CMAKE_VER }} != dart/pubspec.yaml: ${{ env.PUB_VER }} != github.ref_name: ${{ github.ref_name }}"
25-
exit 1
23+
if [[ "${{ env.CMAKE_VER }}" == "${{ env.PUB_VER }}" ]] && \
24+
[[ "${{ env.CMAKE_VER }}" == "${{ github.ref_name }}" ]]; then
25+
echo "All variables are equal."
26+
else
27+
echo "Variables differ:"
28+
echo "CMAKE_VER: ${{ env.CMAKE_VER }}"
29+
echo "PUB_VER: ${{ env.PUB_VER }}"
30+
echo "GITHUB_REF_NAME: ${{ github.ref_name }}"
2631
fi
27-
compile:
32+
binary:
2833
name: Binary
2934
needs: version
3035
strategy:
@@ -42,6 +47,10 @@ jobs:
4247
- os: android
4348
arch: arm64-v8a
4449
runner: ubuntu-latest
50+
- os: macos
51+
arch: x64
52+
runner: macos-latest
53+
4554
runs-on: ${{ matrix.dist.runner }}
4655
container:
4756
image: ghcr.io/jeffmur/fhel:builder
@@ -68,7 +77,7 @@ jobs:
6877

6978
publish:
7079
name: pub.dev
71-
needs: version
80+
needs: [version, binary]
7281
runs-on: ubuntu-latest
7382
container:
7483
image: ghcr.io/jeffmur/fhel:builder

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,6 @@ apk-ci: build-cmake
112112

113113
# Publish dart package from github workflows
114114
.PHONY: publish-ci
115-
publish-ci: build-cmake
115+
publish-ci:
116116
@git config --global --add safe.directory /tmp/flutter
117117
@cd $(DART_SRC); $(MAKE) publish-force

dart/CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
## 0.0.4-linux
1+
## 0.0.6
2+
3+
Added setup script to automate the process of setting up the library for the first time.
4+
5+
```bash
6+
dart run fhel:setup <platform> --arch <architecture>
7+
```
8+
9+
## 0.0.5
210

311
Testing linux release
412

dart/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ dart run fhel:setup <platform> --arch <architecture>
3232
|----------|--------------|
3333
| Android | `x86_64` `arm64-v8a` `armeabi-v7a` |
3434
| Linux | `x64` |
35+
| MacOS | `x64` |
3536

3637
```dart
3738
import 'package:fhel/seal.dart';

dart/bin/setup_commands.dart

Lines changed: 21 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ import 'package:stack_trace/stack_trace.dart';
99

1010
const setupPkgName = "fhel";
1111
const baseUrl = "https://github.com/jeffmur/fhel/releases/download";
12+
const ARCH_OPT_MAP = {
13+
"macos": ["arm64"],
14+
"windows": ["x64"],
15+
"linux": ["x64"],
16+
"android": ["x86_64", "arm64-v8a", "armeabi-v7a"],
17+
"ios": ["os64"],
18+
};
1219

1320
abstract class BaseSetupCommand extends Command {
1421
@override
@@ -18,8 +25,18 @@ abstract class BaseSetupCommand extends Command {
1825
String get name => "base";
1926

2027
String get arch {
21-
final arch_ = argResults?["arch"] as String;
22-
return ARCH_MAP[os]?[arch_] as String? ?? arch_;
28+
return argResults?["arch"] as String;
29+
}
30+
31+
BaseSetupCommand() {
32+
argParser.addOption(
33+
"arch",
34+
abbr: "a",
35+
allowed: ARCH_OPT_MAP[name],
36+
defaultsTo: ARCH_OPT_MAP[name]!.first, // default to first arch
37+
mandatory: false,
38+
);
39+
argParser.addFlag("force", abbr: "f", help: "Force download and extract");
2340
}
2441

2542
String get pkgRoot => Frame.caller().uri.resolve("..").toFilePath();
@@ -59,8 +76,9 @@ abstract class BaseSetupCommand extends Command {
5976
final saveFile = File(cacheTarPath);
6077

6178
if (force || !saveFile.existsSync()) {
62-
if (!saveFile.parent.existsSync())
79+
if (!saveFile.parent.existsSync()) {
6380
saveFile.parent.createSync(recursive: true);
81+
}
6482

6583
final String url = "$baseUrl/v$version/$libTarName";
6684
print(asInfo("Downloading $url"));
@@ -118,16 +136,6 @@ class MacOsSetupCommand extends BaseSetupCommand {
118136

119137
@override
120138
String get name => "macos";
121-
122-
MacOsSetupCommand() {
123-
argParser.addOption(
124-
"arch",
125-
abbr: "a",
126-
allowed: ["x86_64", "x64", "arm64"],
127-
mandatory: true,
128-
);
129-
argParser.addFlag("force", abbr: "f", help: "Force download and extract");
130-
}
131139
}
132140

133141
class WindowsSetupCommand extends BaseSetupCommand {
@@ -136,12 +144,6 @@ class WindowsSetupCommand extends BaseSetupCommand {
136144

137145
@override
138146
String get name => "windows";
139-
140-
WindowsSetupCommand() {
141-
argParser.addOption("arch",
142-
abbr: "a", allowed: ["x86_64", "x64"], mandatory: true);
143-
argParser.addFlag("force", abbr: "f", help: "Force download and extract");
144-
}
145147
}
146148

147149
class LinuxSetupCommand extends BaseSetupCommand {
@@ -150,12 +152,6 @@ class LinuxSetupCommand extends BaseSetupCommand {
150152

151153
@override
152154
String get name => "linux";
153-
154-
LinuxSetupCommand() {
155-
argParser.addOption("arch",
156-
abbr: "a", allowed: ["x86_64", "x64"], mandatory: true);
157-
argParser.addFlag("force", abbr: "f", help: "Force download and extract");
158-
}
159155
}
160156

161157
class AndroidSetupCommand extends BaseSetupCommand {
@@ -164,16 +160,6 @@ class AndroidSetupCommand extends BaseSetupCommand {
164160

165161
@override
166162
String get name => "android";
167-
168-
AndroidSetupCommand() {
169-
argParser.addOption(
170-
"arch",
171-
abbr: "a",
172-
allowed: ["x86_64", "arm64-v8a", "armeabi-v7a"],
173-
mandatory: true,
174-
);
175-
argParser.addFlag("force", abbr: "f", help: "Force download and extract");
176-
}
177163
}
178164

179165
class IosSetupCommand extends BaseSetupCommand {
@@ -182,35 +168,8 @@ class IosSetupCommand extends BaseSetupCommand {
182168

183169
@override
184170
String get name => "ios";
185-
186-
IosSetupCommand() {
187-
argParser.addOption(
188-
"arch",
189-
abbr: "a",
190-
allowed: ["os64"],
191-
mandatory: false,
192-
defaultsTo: "os64",
193-
);
194-
argParser.addFlag("force", abbr: "f", help: "Force download and extract");
195-
}
196171
}
197172

198-
const ARCH_MAP = {
199-
OS.windows: {
200-
"x86_64": "x64",
201-
},
202-
OS.linux: {
203-
"x86_64": "x64",
204-
},
205-
OS.macos: {
206-
"x86_64": "x64",
207-
},
208-
OS.ios: {
209-
"x86_64": "x64",
210-
},
211-
OS.android: {},
212-
};
213-
214173
class OS {
215174
static const windows = "windows";
216175
static const linux = "linux";

dart/lib/afhe.dart

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class Afhe {
5151
}
5252

5353
/// Initializes the [Backend] without a [Scheme].
54-
///
54+
///
5555
/// Typically used for loading parameters from a shared session.
5656
Afhe.noScheme(String backendName) {
5757
backend = Backend.set(backendName);
@@ -121,16 +121,17 @@ class Afhe {
121121
///
122122
/// Used for generating a shared session.
123123
String genContextFromParameters(Map parameters) {
124-
final ptr = _c_gen_context_from_str(library, parameters['header'], parameters['size']);
124+
final ptr = _c_gen_context_from_str(
125+
library, parameters['header'], parameters['size']);
125126
raiseForStatus();
126127
return ptr.toDartString();
127128
}
128-
129+
129130
/// Returns the number of slots based on parameters.
130131
int get slotCount => _c_slot_count(library);
131132

132133
/// Returns the string representation of FHE parameters.
133-
///
134+
///
134135
/// Useful for saving to disk or sending over the network.
135136
/// The `header` is the string representation of the parameters.
136137
/// The `size` is the length of the string.
@@ -194,7 +195,7 @@ class Afhe {
194195
}
195196

196197
/// Loads a [Ciphertext] from a non-human-readable format.
197-
///
198+
///
198199
/// Useful for loading from disk or receiving over the network.
199200
/// The [size] is the length of the data.
200201
/// The [data] is a pointer to the memory address of the data.
@@ -206,7 +207,8 @@ class Afhe {
206207

207208
/// Encodes a list of integers into a [Plaintext].
208209
Plaintext encodeVecInt(List<int> vec) {
209-
Pointer ptr = _c_encode_vector_int(library, intListToUint64Array(vec), vec.length);
210+
Pointer ptr =
211+
_c_encode_vector_int(library, intListToUint64Array(vec), vec.length);
210212
raiseForStatus();
211213
return Plaintext.fromPointer(backend, ptr);
212214
}
@@ -228,7 +230,8 @@ class Afhe {
228230

229231
/// Encodes a list of doubles into a [Plaintext].
230232
Plaintext encodeVecDouble(List<double> vec) {
231-
Pointer ptr = _c_encode_vector_double(library, doubleListToArray(vec), vec.length);
233+
Pointer ptr =
234+
_c_encode_vector_double(library, doubleListToArray(vec), vec.length);
232235
raiseForStatus();
233236
// String cannot be extracted from C object for CKKS
234237
return Plaintext.fromPointer(backend, ptr, extractStr: false);
@@ -312,7 +315,7 @@ class Afhe {
312315
}
313316

314317
/// Raises the [Ciphertext] to a [power].
315-
///
318+
///
316319
/// Only supported for BFV/BGV [Scheme].
317320
/// The [power] must be a positive integer.
318321
/// Applies relinearization after each multiplication step.

dart/lib/ffi.dart

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,71 @@ part of '../afhe.dart';
22

33
const String _libraryName = 'fhel';
44

5+
// pub-cache directory
6+
String get desktopPubCachePath {
7+
final homeDir =
8+
Platform.environment['HOME'] ?? Platform.environment['USERPROFILE'];
9+
final pubCachePath =
10+
Platform.environment['PUB_CACHE'] ?? '$homeDir/.pub-cache';
11+
final pubHostedUrl = Platform.environment['PUB_HOSTED_URL'] ?? 'pub.dev';
12+
return "$pubCachePath/hosted/$pubHostedUrl";
13+
}
14+
15+
// dynamic libfhel
16+
final libfhel = switch (Platform.operatingSystem) {
17+
"windows" => "$_libraryName.dll",
18+
"linux" || "android" => "lib$_libraryName.so",
19+
"macos" => "lib$_libraryName.dylib",
20+
_ => throw UnsupportedError(
21+
"Platform ${Platform.operatingSystem} not supported",
22+
)
23+
};
24+
25+
// Latest .pub-cache version
26+
final version = () {
27+
final pubCachePath = desktopPubCachePath;
28+
final directory = Directory(pubCachePath);
29+
30+
// Filter for relevant directories and extract versions
31+
final versions = directory
32+
.listSync()
33+
.whereType<Directory>() // Filter out non-directory entries
34+
.map((directory) {
35+
final name = path.basename(directory.path);
36+
final match = RegExp('$_libraryName-(.*)').firstMatch(name);
37+
return match != null
38+
? match.group(1)!
39+
: '-1'; // Use null-aware operator and assert non-null group
40+
})
41+
.where((version) => version != '-1') // Filter out non-matching versions
42+
.toList();
43+
44+
versions.sort(); // Sort versions in ascending order
45+
46+
return versions.isEmpty
47+
? throw StateError('Package $_libraryName not found in $pubCachePath. Have you run `dart pub install $_libraryName`?')
48+
: versions.last; // Handle the case where no matching versions are found
49+
}();
50+
551
// load native library
652
final DynamicLibrary dylib = () {
7-
if (Platform.isIOS) return DynamicLibrary.process();
8-
final defaultLibPath = switch (Platform.operatingSystem) {
9-
"windows" => "$_libraryName.dll",
10-
"linux" || "android" => "lib$_libraryName.so",
11-
"macos" => "lib$_libraryName.dylib",
12-
_ => throw UnsupportedError(
13-
"Platform ${Platform.operatingSystem} not supported",
14-
)
15-
};
16-
final libPath = path.absolute(
17-
Platform.environment["FHEL_C_LIB_PREFIX"] ?? '', defaultLibPath);
18-
return DynamicLibrary.open(libPath);
53+
// Continuous integration
54+
if (Platform.environment.containsKey('FHEL_C_LIB_PREFIX')) {
55+
return DynamicLibrary.open("${Platform.environment['FHEL_C_LIB_PREFIX']}/$libfhel");
56+
}
57+
// Development environment
58+
if (Platform.environment.containsKey('FHEL_C_LIB_PATH')) {
59+
return DynamicLibrary.open(Platform.environment['FHEL_C_LIB_PATH']!);
60+
}
61+
// Android: packaged at runtime, see dart/android/build.gradle
62+
if (Platform.isAndroid) {
63+
return DynamicLibrary.open(libfhel);
64+
}
65+
// Production environment: .pub-cache
66+
try {
67+
return DynamicLibrary.open(
68+
'$desktopPubCachePath/$_libraryName-$version/${Platform.operatingSystem}/$libfhel');
69+
} catch (e) {
70+
throw StateError('$e. Have you run `dart run fhel:setup --${Platform.operatingSystem}`?');
71+
}
1972
}();

0 commit comments

Comments
 (0)