Skip to content

Commit

Permalink
fix(cedar_ffi): Windows bundling workarounds (#104)
Browse files Browse the repository at this point in the history
Workaround for: dart-lang/sdk#55207
  • Loading branch information
dnys1 authored Apr 5, 2024
1 parent 3c97655 commit 438aca4
Show file tree
Hide file tree
Showing 12 changed files with 359 additions and 136 deletions.
28 changes: 25 additions & 3 deletions packages/cedar_ffi/build.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,19 @@ final IOSink buildLogs = () {
Platform.script.resolve('.dart_tool/build.log'),
);
logsFile.createSync(recursive: true);
return logsFile.openWrite(mode: FileMode.write);
return logsFile.openWrite(mode: FileMode.write)
..writeln('Starting build: ${DateTime.now()}');
}();

void main(List<String> args) async {
try {
await build(args, (config, output) async {
buildLogs.writeln(config.toString());

output.addDependency(config.packageRoot.resolve('src/'));
output.addDependencies([
config.packageRoot.resolve('build.dart'),
config.packageRoot.resolve('src/'),
]);

// Build the Rust code in `src/` to `target/`.
//
Expand All @@ -39,9 +43,27 @@ void main(List<String> args) async {
if (!File.fromUri(binaryOut).existsSync()) {
throw Exception('$binaryOut does not exist');
}
if (config.targetOS == OS.windows) {
// Workaround for https://github.com/dart-lang/sdk/issues/55207
//
// Bundle a second asset which can resolve symbols from a previously
// loaded DLL. This allows having a fallback mechanism, since you
// cannot add duplicate assets.
//
// This only matters in release mode, but since `config.buildMode` is
// always set to `release` in Dart, it's no good.
final loadedAsset = NativeCodeAsset(
package: packageName,
name: 'src/ffi/cedar_bindings.loaded.ffi.dart',
linkMode: LookupInProcess(),
os: config.targetOS,
architecture: config.targetArchitecture,
);
output.addAsset(loadedAsset);
}
final nativeAsset = NativeCodeAsset(
package: packageName,
name: 'src/ffi/cedar_bindings.g.dart',
name: 'src/ffi/cedar_bindings.bundled.ffi.dart',
linkMode: DynamicLoadingBundled(),
os: config.targetOS,
architecture: config.targetArchitecture,
Expand Down
28 changes: 28 additions & 0 deletions packages/cedar_ffi/ffigen.bundled.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: CedarFfiBundled
description: C bindings to the Cedar policy engine (bundled native asset)
ffi-native:
language: c
headers:
entry-points:
- "src/include/bindings.h"
compiler-opts:
# Suppress nullability warnings on macOS
- "-Wno-nullability-completeness"
# Ignore warnings about availability macro
- "-Wno-availability"
output:
bindings: "lib/src/ffi/cedar_bindings.bundled.ffi.dart"
comments:
style: any
length: full
exclude-all-by-default: true
import:
symbol-files:
- 'package:cedar_ffi/src/ffi/symbols.yaml'
functions:
include:
- "cedar_.*"
leaf:
# All C APIs are leaf functions (e.g. they do not call into Dart)
include:
- ".*"
31 changes: 31 additions & 0 deletions packages/cedar_ffi/ffigen.loaded.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: CedarFfiLoaded
description: C bindings to the Cedar policy engine (process native asset)
ffi-native:
language: c
headers:
entry-points:
- "src/include/bindings.h"
compiler-opts:
# Suppress nullability warnings on macOS
- "-Wno-nullability-completeness"
# Ignore warnings about availability macro
- "-Wno-availability"
output:
bindings: "lib/src/ffi/cedar_bindings.loaded.ffi.dart"
comments:
style: any
length: full
exclude-all-by-default: true
import:
symbol-files:
- 'package:cedar_ffi/src/ffi/symbols.yaml'
functions:
include:
- "cedar_.*"
expose-typedefs:
include:
- "cedar_.*"
leaf:
# All C APIs are leaf functions (e.g. they do not call into Dart)
include:
- ".*"
27 changes: 27 additions & 0 deletions packages/cedar_ffi/ffigen.symbols.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: CedarFfiSymbols
description: C bindings to the Cedar policy engine (symbols-only)
ffi-native:
language: c
headers:
entry-points:
- "src/include/bindings.h"
compiler-opts:
# Suppress nullability warnings on macOS
- "-Wno-nullability-completeness"
# Ignore warnings about availability macro
- "-Wno-availability"
output:
bindings: "lib/src/ffi/cedar_bindings.symbols.ffi.dart"
symbol-file:
output: 'package:cedar_ffi/src/ffi/symbols.yaml'
import-path: 'package:cedar_ffi/src/ffi/cedar_bindings.symbols.ffi.dart'
comments:
style: any
length: full
exclude-all-by-default: true
structs:
include:
- "CCedar.*"
- "CedarStore"
- "CInitResult"
- "CAuthorizationDecision"
6 changes: 3 additions & 3 deletions packages/cedar_ffi/lib/src/cedar_policy_set_ffi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'dart:convert';
import 'dart:ffi';

import 'package:cedar/cedar.dart';
import 'package:cedar_ffi/src/ffi/cedar_bindings.g.dart' as bindings;
import 'package:cedar_ffi/src/ffi/cedar_bindings.dart';
import 'package:ffi/ffi.dart';

/// An FFI extension of [CedarPolicySet].
Expand All @@ -21,7 +21,7 @@ Map<String, Map<String, Object?>> parsePolicies(String policiesIdl) {
policiesIdl.toNativeUtf8(allocator: arena).cast(),
);
switch (cPolicies) {
case bindings.CCedarPolicySetResult(:final errors, :final errors_len)
case CCedarPolicySetResult(:final errors, :final errors_len)
when errors_len > 0:
final errorStrings = <String>[];
for (var i = 0; i < errors_len; i++) {
Expand All @@ -32,7 +32,7 @@ Map<String, Map<String, Object?>> parsePolicies(String policiesIdl) {
'${errorStrings.join(', ')}',
policiesIdl,
);
case bindings.CCedarPolicySetResult(
case CCedarPolicySetResult(
:final policies,
:final policy_ids,
:final policies_len,
Expand Down
16 changes: 8 additions & 8 deletions packages/cedar_ffi/lib/src/engine/cedar_engine.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'dart:convert';
import 'dart:ffi';

import 'package:cedar/cedar.dart';
import 'package:cedar_ffi/src/ffi/cedar_bindings.g.dart' as bindings;
import 'package:cedar_ffi/src/ffi/cedar_bindings.dart';
import 'package:ffi/ffi.dart';
import 'package:meta/meta.dart';

Expand All @@ -24,7 +24,7 @@ final class CedarEngine implements CedarAuthorizer, Finalizable {
@visibleForTesting bool validate = true,
}) {
final storeRef = using((arena) {
final config = arena<bindings.CCedarConfig>();
final config = arena<CCedarConfig>();
config.ref
..schema_json =
jsonEncode(schema.toJson()).toNativeUtf8(allocator: arena).cast()
Expand Down Expand Up @@ -61,16 +61,16 @@ final class CedarEngine implements CedarAuthorizer, Finalizable {
}

CedarEngine._({
required Pointer<bindings.CedarStore> ref,
required Pointer<CedarStore> ref,
}) : _ref = ref;

static final Finalizer<Pointer<bindings.CedarStore>> _finalizer = Finalizer(
static final Finalizer<Pointer<CedarStore>> _finalizer = Finalizer(
bindings.cedar_deinit,
);

var _closed = false;

final Pointer<bindings.CedarStore> _ref;
final Pointer<CedarStore> _ref;

@override
CedarAuthorizationResponse isAuthorized(
Expand All @@ -82,7 +82,7 @@ final class CedarEngine implements CedarAuthorizer, Finalizable {
throw StateError('Cedar engine is closed');
}
return using((arena) {
final query = arena<bindings.CCedarQuery>();
final query = arena<CCedarQuery>();
query.ref
..principal_str = switch (request.principal) {
final principal? => principal.normalized
Expand Down Expand Up @@ -122,13 +122,13 @@ final class CedarEngine implements CedarAuthorizer, Finalizable {
};
final cDecision = bindings.cedar_is_authorized(_ref, query);
return switch (cDecision) {
bindings.CAuthorizationDecision(:final completion_error)
CAuthorizationDecision(:final completion_error)
when completion_error != nullptr =>
throw Exception(
'Error performing authorization: '
'${completion_error.cast<Utf8>().toDartString()}',
),
bindings.CAuthorizationDecision(
CAuthorizationDecision(
:final is_authorized,
:final reasons,
:final reasons_len,
Expand Down
55 changes: 55 additions & 0 deletions packages/cedar_ffi/lib/src/ffi/cedar_bindings.bundled.ffi.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 47 additions & 0 deletions packages/cedar_ffi/lib/src/ffi/cedar_bindings.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// ignore_for_file: non_constant_identifier_names

import 'dart:ffi';

import 'package:cedar_ffi/src/ffi/cedar_bindings.bundled.ffi.dart' as bundled;
import 'package:cedar_ffi/src/ffi/cedar_bindings.loaded.ffi.dart' as loaded;

export 'package:cedar_ffi/src/ffi/cedar_bindings.symbols.ffi.dart';

final bindings = CedarBindings._();

enum _LinkMode { bundled, loaded }

final class CedarBindings {
CedarBindings._() {
try {
Native.addressOf<NativeFunction<loaded.NativeCedar_init>>(
loaded.cedar_init);
_linkMode = _LinkMode.loaded;
} on Object {
Native.addressOf<NativeFunction<loaded.NativeCedar_init>>(
bundled.cedar_init);
_linkMode = _LinkMode.bundled;
}
}

late final _LinkMode _linkMode;

late final cedar_init =
_linkMode == _LinkMode.loaded ? loaded.cedar_init : bundled.cedar_init;

late final cedar_deinit = _linkMode == _LinkMode.loaded
? loaded.cedar_deinit
: bundled.cedar_deinit;

late final cedar_is_authorized = _linkMode == _LinkMode.loaded
? loaded.cedar_is_authorized
: bundled.cedar_is_authorized;

late final cedar_link_policy_template = _linkMode == _LinkMode.loaded
? loaded.cedar_link_policy_template
: bundled.cedar_link_policy_template;

late final cedar_parse_policy_set = _linkMode == _LinkMode.loaded
? loaded.cedar_parse_policy_set
: bundled.cedar_parse_policy_set;
}
Loading

0 comments on commit 438aca4

Please sign in to comment.