Compare commits
24 Commits
fdroid-v1.
...
rust_proce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a318977001 | ||
|
|
eff471c739 | ||
|
|
5454b262a4 | ||
|
|
e5e86fb41a | ||
|
|
1e9cc64a64 | ||
|
|
0205bec30a | ||
|
|
fad0c4559f | ||
|
|
2e4866d302 | ||
|
|
aadbe75c50 | ||
|
|
899bf79460 | ||
|
|
21af6d0070 | ||
|
|
1bad2b3555 | ||
|
|
ffa50df43e | ||
|
|
8fdc7dcd89 | ||
|
|
1ed26567a5 | ||
|
|
6101570c9d | ||
|
|
a33bbb22ae | ||
|
|
a2661ef6ed | ||
|
|
8daa22e423 | ||
|
|
aeb2235875 | ||
|
|
bf903562f6 | ||
|
|
9cb7c01481 | ||
|
|
233d1715e9 | ||
|
|
e3c019f7ed |
9
mobile/apps/photos/flutter_rust_bridge.yaml
Normal file
9
mobile/apps/photos/flutter_rust_bridge.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
rust_input: crate::api
|
||||
rust_root: rust/
|
||||
dart_output: lib/src/rust
|
||||
|
||||
dart_preamble: |
|
||||
// ignore_for_file: require_trailing_commas
|
||||
|
||||
full_dep: true
|
||||
web: false
|
||||
@@ -91,10 +91,9 @@ Future<void> dismissUpdateAppDialog(WidgetTester tester) async {
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
|
||||
///Use this widget as floating action buttom in HomeWidget so that frames
|
||||
///are built and rendered continuously so that timeline trace has continuous
|
||||
///data. Change the duraiton in `_startTimer()` to control the duraiton of
|
||||
///are built and rendered continuously so that timeline trace has continuous
|
||||
///data. Change the duraiton in `_startTimer()` to control the duraiton of
|
||||
///test on app init.
|
||||
|
||||
// class TempWidget extends StatefulWidget {
|
||||
@@ -127,4 +126,4 @@ Future<void> dismissUpdateAppDialog(WidgetTester tester) async {
|
||||
// ? const CircularProgressIndicator()
|
||||
// : const SizedBox.shrink();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import "dart:io";
|
||||
|
||||
import 'package:photos/core/cache/lru_map.dart';
|
||||
|
||||
@@ -5,7 +5,6 @@ class SetupSRPRequest {
|
||||
final String srpA;
|
||||
final bool isUpdate;
|
||||
|
||||
|
||||
SetupSRPRequest({
|
||||
required this.srpUserID,
|
||||
required this.srpSalt,
|
||||
@@ -82,6 +81,7 @@ class CompleteSRPSetupRequest {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SrpAttributes {
|
||||
final String srpUserID;
|
||||
final String srpSalt;
|
||||
|
||||
@@ -89,8 +89,8 @@ class FaceDetectionRelative extends Detection {
|
||||
// Calculate the scaling
|
||||
final double scaleX = originalSize.width / newSize.width;
|
||||
final double scaleY = originalSize.height / newSize.height;
|
||||
const double translateX = 0;
|
||||
const double translateY = 0;
|
||||
final double translateX = - ((originalSize.width - newSize.width) ~/ 2) / originalSize.width;
|
||||
final double translateY = - ((originalSize.height - newSize.height) ~/ 2) / originalSize.height;
|
||||
|
||||
// Transform Box
|
||||
_transformBox(box, scaleX, scaleY, translateX, translateY);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import "dart:async";
|
||||
import 'dart:typed_data' show Float32List, Uint8List;
|
||||
import 'dart:ui' as ui show Image;
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
import "package:onnx_dart/onnx_dart.dart";
|
||||
@@ -44,8 +43,9 @@ class FaceDetectionService extends MlModel {
|
||||
|
||||
/// Detects faces in the given image data.
|
||||
static Future<List<FaceDetectionRelative>> predict(
|
||||
ui.Image image,
|
||||
Uint8List rawRgbaBytes,
|
||||
Uint8List resizedBytes,
|
||||
int resizedHeight,
|
||||
int resizedWidth,
|
||||
int sessionAddress,
|
||||
) async {
|
||||
assert(
|
||||
@@ -57,9 +57,11 @@ class FaceDetectionService extends MlModel {
|
||||
|
||||
final startTime = DateTime.now();
|
||||
|
||||
final (inputImageList, scaledSize) = await preprocessImageYoloFace(
|
||||
image,
|
||||
rawRgbaBytes,
|
||||
final scaledSize = Dimensions(width: resizedWidth, height: resizedHeight);
|
||||
final inputImageList = await resizedToPreprocessedYoloFace(
|
||||
resizedBytes,
|
||||
scaledSize.width,
|
||||
scaledSize.height,
|
||||
);
|
||||
final preprocessingTime = DateTime.now();
|
||||
final preprocessingMs =
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import "dart:async" show unawaited;
|
||||
import "dart:typed_data" show Uint8List, Float32List;
|
||||
import "dart:ui" show Image;
|
||||
|
||||
import "package:logging/logging.dart";
|
||||
import "package:photos/core/event_bus.dart";
|
||||
import "package:photos/events/diff_sync_complete_event.dart";
|
||||
import "package:photos/events/people_changed_event.dart";
|
||||
import "package:photos/models/ml/face/dimension.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/face_detection/detection.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/face_detection/face_detection_service.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/face_embedding/face_embedding_service.dart";
|
||||
@@ -73,8 +73,11 @@ class FaceRecognitionService {
|
||||
|
||||
static Future<List<FaceResult>> runFacesPipeline(
|
||||
int enteFileID,
|
||||
Image image,
|
||||
Dimensions imageDimensions,
|
||||
Uint8List rawRgbaBytes,
|
||||
Uint8List resizedRgbBytes,
|
||||
int resizedHeight,
|
||||
int resizedWidth,
|
||||
int faceDetectionAddress,
|
||||
int faceEmbeddingAddress,
|
||||
) async {
|
||||
@@ -85,8 +88,9 @@ class FaceRecognitionService {
|
||||
final List<FaceDetectionRelative> faceDetectionResult =
|
||||
await _detectFacesSync(
|
||||
enteFileID,
|
||||
image,
|
||||
rawRgbaBytes,
|
||||
resizedRgbBytes,
|
||||
resizedHeight,
|
||||
resizedWidth,
|
||||
faceDetectionAddress,
|
||||
faceResults,
|
||||
);
|
||||
@@ -103,7 +107,7 @@ class FaceRecognitionService {
|
||||
|
||||
// Align the faces
|
||||
final Float32List faceAlignmentResult = await _alignFacesSync(
|
||||
image,
|
||||
imageDimensions,
|
||||
rawRgbaBytes,
|
||||
faceDetectionResult,
|
||||
faceResults,
|
||||
@@ -133,8 +137,9 @@ class FaceRecognitionService {
|
||||
/// Runs face recognition on the given image data.
|
||||
static Future<List<FaceDetectionRelative>> _detectFacesSync(
|
||||
int fileID,
|
||||
Image image,
|
||||
Uint8List rawRgbaBytes,
|
||||
Uint8List resizedBytes,
|
||||
int resizedHeight,
|
||||
int resizedWidth,
|
||||
int interpreterAddress,
|
||||
List<FaceResult> faceResults,
|
||||
) async {
|
||||
@@ -142,8 +147,9 @@ class FaceRecognitionService {
|
||||
// Get the bounding boxes of the faces
|
||||
final List<FaceDetectionRelative> faces =
|
||||
await FaceDetectionService.predict(
|
||||
image,
|
||||
rawRgbaBytes,
|
||||
resizedBytes,
|
||||
resizedHeight,
|
||||
resizedWidth,
|
||||
interpreterAddress,
|
||||
);
|
||||
|
||||
@@ -169,7 +175,7 @@ class FaceRecognitionService {
|
||||
/// Aligns multiple faces from the given image data.
|
||||
/// Returns a list of the aligned faces as image data.
|
||||
static Future<Float32List> _alignFacesSync(
|
||||
Image image,
|
||||
Dimensions imageDimensions,
|
||||
Uint8List rawRgbaBytes,
|
||||
List<FaceDetectionRelative> faces,
|
||||
List<FaceResult> faceResults,
|
||||
@@ -177,7 +183,7 @@ class FaceRecognitionService {
|
||||
try {
|
||||
final (alignedFaces, alignmentResults, _, blurValues, _) =
|
||||
await preprocessToMobileFaceNetFloat32List(
|
||||
image,
|
||||
imageDimensions,
|
||||
rawRgbaBytes,
|
||||
faces,
|
||||
);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
class GeneralFaceMlException implements Exception {
|
||||
final String message;
|
||||
|
||||
@@ -26,4 +25,4 @@ class CouldNotRunFaceDetector implements Exception {}
|
||||
|
||||
class CouldNotWarpAffine implements Exception {}
|
||||
|
||||
class CouldNotRunFaceEmbeddor implements Exception {}
|
||||
class CouldNotRunFaceEmbeddor implements Exception {}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import "dart:typed_data" show Uint8List, Float32List;
|
||||
import "dart:ui" show Image;
|
||||
|
||||
import "package:logging/logging.dart";
|
||||
import "package:onnx_dart/onnx_dart.dart";
|
||||
@@ -28,16 +27,23 @@ class ClipImageEncoder extends MlModel {
|
||||
factory ClipImageEncoder() => instance;
|
||||
|
||||
static Future<List<double>> predict(
|
||||
Image image,
|
||||
Uint8List rawRgbaBytes,
|
||||
Uint8List resizedBytes,
|
||||
int resizedHeight,
|
||||
int resizedWidth,
|
||||
int sessionAddress, [
|
||||
int? enteFileID,
|
||||
]) async {
|
||||
final startTime = DateTime.now();
|
||||
final inputList = await preprocessImageClip(image, rawRgbaBytes);
|
||||
|
||||
final inputList = await resizedToPreprocessedClip(
|
||||
resizedBytes,
|
||||
resizedWidth,
|
||||
resizedHeight,
|
||||
);
|
||||
final preprocessingTime = DateTime.now();
|
||||
final preprocessingMs =
|
||||
preprocessingTime.difference(startTime).inMilliseconds;
|
||||
|
||||
late List<double> result;
|
||||
try {
|
||||
if (MlModel.usePlatformPlugin) {
|
||||
@@ -57,7 +63,7 @@ class ClipImageEncoder extends MlModel {
|
||||
final inferenceMs = inferTime.difference(preprocessingTime).inMilliseconds;
|
||||
final totalMs = inferTime.difference(startTime).inMilliseconds;
|
||||
_logger.info(
|
||||
"Clip image predict took $totalMs ms${enteFileID != null ? " with fileID $enteFileID" : ""} (inference: $inferenceMs ms, preprocessing: $preprocessingMs ms)",
|
||||
"Clip image predict took $totalMs ms${enteFileID != null ? " with fileID $enteFileID" : ""} (preprocessing: $preprocessingMs ms, inference: $inferenceMs ms)",
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import "dart:async" show Timer, unawaited;
|
||||
import "dart:developer" as dev show log;
|
||||
import "dart:math" show min;
|
||||
import "dart:ui" show Image;
|
||||
|
||||
import "package:flutter/foundation.dart";
|
||||
import "package:logging/logging.dart";
|
||||
@@ -294,13 +293,15 @@ class SemanticSearchService {
|
||||
|
||||
static Future<ClipResult> runClipImage(
|
||||
int enteFileID,
|
||||
Image image,
|
||||
Uint8List rawRgbaBytes,
|
||||
Uint8List resizedBytes,
|
||||
int resizedHeight,
|
||||
int resizedWidth,
|
||||
int clipImageAddress,
|
||||
) async {
|
||||
final embedding = await ClipImageEncoder.predict(
|
||||
image,
|
||||
rawRgbaBytes,
|
||||
resizedBytes,
|
||||
resizedHeight,
|
||||
resizedWidth,
|
||||
clipImageAddress,
|
||||
enteFileID,
|
||||
);
|
||||
|
||||
45
mobile/apps/photos/lib/src/rust/api/image_processing.dart
Normal file
45
mobile/apps/photos/lib/src/rust/api/image_processing.dart
Normal file
@@ -0,0 +1,45 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: require_trailing_commas
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
import 'package:photos/src/rust/frb_generated.dart';
|
||||
|
||||
// These functions are ignored because they are not marked as `pub`: `process_image_ml`
|
||||
|
||||
Future<
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
)> processImageMlFromPath(
|
||||
{required String imagePath}) =>
|
||||
RustLib.instance.api
|
||||
.crateApiImageProcessingProcessImageMlFromPath(imagePath: imagePath);
|
||||
|
||||
Future<
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
)> processImageMlFromData(
|
||||
{required List<int> rgbaData,
|
||||
required int width,
|
||||
required int height}) =>
|
||||
RustLib.instance.api.crateApiImageProcessingProcessImageMlFromData(
|
||||
rgbaData: rgbaData, width: width, height: height);
|
||||
12
mobile/apps/photos/lib/src/rust/api/simple.dart
Normal file
12
mobile/apps/photos/lib/src/rust/api/simple.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: require_trailing_commas
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
import 'package:photos/src/rust/frb_generated.dart';
|
||||
|
||||
String greet({required String name}) =>
|
||||
RustLib.instance.api.crateApiSimpleGreet(name: name);
|
||||
9
mobile/apps/photos/lib/src/rust/custom/init_frb.dart
Normal file
9
mobile/apps/photos/lib/src/rust/custom/init_frb.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
import "package:photos/src/rust/frb_generated.dart";
|
||||
|
||||
bool _isInitFrb = false;
|
||||
|
||||
Future<void> initFrb() async {
|
||||
if (_isInitFrb) return;
|
||||
await RustLib.init();
|
||||
_isInitFrb = true;
|
||||
}
|
||||
525
mobile/apps/photos/lib/src/rust/frb_generated.dart
Normal file
525
mobile/apps/photos/lib/src/rust/frb_generated.dart
Normal file
@@ -0,0 +1,525 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: require_trailing_commas
|
||||
|
||||
// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
import 'package:photos/src/rust/api/image_processing.dart';
|
||||
import 'package:photos/src/rust/api/simple.dart';
|
||||
import 'package:photos/src/rust/frb_generated.dart';
|
||||
import 'package:photos/src/rust/frb_generated.io.dart'
|
||||
if (dart.library.js_interop) 'frb_generated.web.dart';
|
||||
|
||||
/// Main entrypoint of the Rust API
|
||||
class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
||||
@internal
|
||||
static final instance = RustLib._();
|
||||
|
||||
RustLib._();
|
||||
|
||||
/// Initialize flutter_rust_bridge
|
||||
static Future<void> init({
|
||||
RustLibApi? api,
|
||||
BaseHandler? handler,
|
||||
ExternalLibrary? externalLibrary,
|
||||
bool forceSameCodegenVersion = true,
|
||||
}) async {
|
||||
await instance.initImpl(
|
||||
api: api,
|
||||
handler: handler,
|
||||
externalLibrary: externalLibrary,
|
||||
forceSameCodegenVersion: forceSameCodegenVersion,
|
||||
);
|
||||
}
|
||||
|
||||
/// Initialize flutter_rust_bridge in mock mode.
|
||||
/// No libraries for FFI are loaded.
|
||||
static void initMock({
|
||||
required RustLibApi api,
|
||||
}) {
|
||||
instance.initMockImpl(
|
||||
api: api,
|
||||
);
|
||||
}
|
||||
|
||||
/// Dispose flutter_rust_bridge
|
||||
///
|
||||
/// The call to this function is optional, since flutter_rust_bridge (and everything else)
|
||||
/// is automatically disposed when the app stops.
|
||||
static void dispose() => instance.disposeImpl();
|
||||
|
||||
@override
|
||||
ApiImplConstructor<RustLibApiImpl, RustLibWire> get apiImplConstructor =>
|
||||
RustLibApiImpl.new;
|
||||
|
||||
@override
|
||||
WireConstructor<RustLibWire> get wireConstructor =>
|
||||
RustLibWire.fromExternalLibrary;
|
||||
|
||||
@override
|
||||
Future<void> executeRustInitializers() async {
|
||||
await api.crateApiSimpleInitApp();
|
||||
}
|
||||
|
||||
@override
|
||||
ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig =>
|
||||
kDefaultExternalLibraryLoaderConfig;
|
||||
|
||||
@override
|
||||
String get codegenVersion => '2.11.1';
|
||||
|
||||
@override
|
||||
int get rustContentHash => 1140226238;
|
||||
|
||||
static const kDefaultExternalLibraryLoaderConfig =
|
||||
ExternalLibraryLoaderConfig(
|
||||
stem: 'rust_lib_photos',
|
||||
ioDirectory: 'rust/target/release/',
|
||||
webPrefix: 'pkg/',
|
||||
);
|
||||
}
|
||||
|
||||
abstract class RustLibApi extends BaseApi {
|
||||
String crateApiSimpleGreet({required String name});
|
||||
|
||||
Future<void> crateApiSimpleInitApp();
|
||||
|
||||
Future<
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
)>
|
||||
crateApiImageProcessingProcessImageMlFromData(
|
||||
{required List<int> rgbaData,
|
||||
required int width,
|
||||
required int height});
|
||||
|
||||
Future<
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
)>
|
||||
crateApiImageProcessingProcessImageMlFromPath(
|
||||
{required String imagePath});
|
||||
}
|
||||
|
||||
class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
RustLibApiImpl({
|
||||
required super.handler,
|
||||
required super.wire,
|
||||
required super.generalizedFrbRustBinding,
|
||||
required super.portManager,
|
||||
});
|
||||
|
||||
@override
|
||||
String crateApiSimpleGreet({required String name}) {
|
||||
return handler.executeSync(SyncTask(
|
||||
callFfi: () {
|
||||
final arg0 = cst_encode_String(name);
|
||||
return wire.wire__crate__api__simple__greet(arg0);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_String,
|
||||
decodeErrorData: null,
|
||||
),
|
||||
constMeta: kCrateApiSimpleGreetConstMeta,
|
||||
argValues: [name],
|
||||
apiImpl: this,
|
||||
));
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiSimpleGreetConstMeta => const TaskConstMeta(
|
||||
debugName: "greet",
|
||||
argNames: ["name"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> crateApiSimpleInitApp() {
|
||||
return handler.executeNormal(NormalTask(
|
||||
callFfi: (port_) {
|
||||
return wire.wire__crate__api__simple__init_app(port_);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_unit,
|
||||
decodeErrorData: null,
|
||||
),
|
||||
constMeta: kCrateApiSimpleInitAppConstMeta,
|
||||
argValues: [],
|
||||
apiImpl: this,
|
||||
));
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiSimpleInitAppConstMeta => const TaskConstMeta(
|
||||
debugName: "init_app",
|
||||
argNames: [],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
)>
|
||||
crateApiImageProcessingProcessImageMlFromData(
|
||||
{required List<int> rgbaData,
|
||||
required int width,
|
||||
required int height}) {
|
||||
return handler.executeNormal(NormalTask(
|
||||
callFfi: (port_) {
|
||||
final arg0 = cst_encode_list_prim_u_8_loose(rgbaData);
|
||||
final arg1 = cst_encode_u_32(width);
|
||||
final arg2 = cst_encode_u_32(height);
|
||||
return wire
|
||||
.wire__crate__api__image_processing__process_image_ml_from_data(
|
||||
port_, arg0, arg1, arg2);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData:
|
||||
dco_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize,
|
||||
decodeErrorData: null,
|
||||
),
|
||||
constMeta: kCrateApiImageProcessingProcessImageMlFromDataConstMeta,
|
||||
argValues: [rgbaData, width, height],
|
||||
apiImpl: this,
|
||||
));
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiImageProcessingProcessImageMlFromDataConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "process_image_ml_from_data",
|
||||
argNames: ["rgbaData", "width", "height"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
)>
|
||||
crateApiImageProcessingProcessImageMlFromPath(
|
||||
{required String imagePath}) {
|
||||
return handler.executeNormal(NormalTask(
|
||||
callFfi: (port_) {
|
||||
final arg0 = cst_encode_String(imagePath);
|
||||
return wire
|
||||
.wire__crate__api__image_processing__process_image_ml_from_path(
|
||||
port_, arg0);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData:
|
||||
dco_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize,
|
||||
decodeErrorData: null,
|
||||
),
|
||||
constMeta: kCrateApiImageProcessingProcessImageMlFromPathConstMeta,
|
||||
argValues: [imagePath],
|
||||
apiImpl: this,
|
||||
));
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiImageProcessingProcessImageMlFromPathConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "process_image_ml_from_path",
|
||||
argNames: ["imagePath"],
|
||||
);
|
||||
|
||||
@protected
|
||||
String dco_decode_String(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return raw as String;
|
||||
}
|
||||
|
||||
@protected
|
||||
List<int> dco_decode_list_prim_u_8_loose(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return raw as List<int>;
|
||||
}
|
||||
|
||||
@protected
|
||||
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return raw as Uint8List;
|
||||
}
|
||||
|
||||
@protected
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
) dco_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
|
||||
dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
final arr = raw as List<dynamic>;
|
||||
if (arr.length != 9) {
|
||||
throw Exception('Expected 9 elements, got ${arr.length}');
|
||||
}
|
||||
return (
|
||||
dco_decode_list_prim_u_8_strict(arr[0]),
|
||||
dco_decode_usize(arr[1]),
|
||||
dco_decode_usize(arr[2]),
|
||||
dco_decode_list_prim_u_8_strict(arr[3]),
|
||||
dco_decode_usize(arr[4]),
|
||||
dco_decode_usize(arr[5]),
|
||||
dco_decode_list_prim_u_8_strict(arr[6]),
|
||||
dco_decode_usize(arr[7]),
|
||||
dco_decode_usize(arr[8]),
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
int dco_decode_u_32(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return raw as int;
|
||||
}
|
||||
|
||||
@protected
|
||||
int dco_decode_u_8(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return raw as int;
|
||||
}
|
||||
|
||||
@protected
|
||||
void dco_decode_unit(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return;
|
||||
}
|
||||
|
||||
@protected
|
||||
BigInt dco_decode_usize(dynamic raw) {
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
return dcoDecodeU64(raw);
|
||||
}
|
||||
|
||||
@protected
|
||||
String sse_decode_String(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
final inner = sse_decode_list_prim_u_8_strict(deserializer);
|
||||
return utf8.decoder.convert(inner);
|
||||
}
|
||||
|
||||
@protected
|
||||
List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
final len_ = sse_decode_i_32(deserializer);
|
||||
return deserializer.buffer.getUint8List(len_);
|
||||
}
|
||||
|
||||
@protected
|
||||
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
final len_ = sse_decode_i_32(deserializer);
|
||||
return deserializer.buffer.getUint8List(len_);
|
||||
}
|
||||
|
||||
@protected
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
) sse_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
|
||||
SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
final var_field0 = sse_decode_list_prim_u_8_strict(deserializer);
|
||||
final var_field1 = sse_decode_usize(deserializer);
|
||||
final var_field2 = sse_decode_usize(deserializer);
|
||||
final var_field3 = sse_decode_list_prim_u_8_strict(deserializer);
|
||||
final var_field4 = sse_decode_usize(deserializer);
|
||||
final var_field5 = sse_decode_usize(deserializer);
|
||||
final var_field6 = sse_decode_list_prim_u_8_strict(deserializer);
|
||||
final var_field7 = sse_decode_usize(deserializer);
|
||||
final var_field8 = sse_decode_usize(deserializer);
|
||||
return (
|
||||
var_field0,
|
||||
var_field1,
|
||||
var_field2,
|
||||
var_field3,
|
||||
var_field4,
|
||||
var_field5,
|
||||
var_field6,
|
||||
var_field7,
|
||||
var_field8
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
int sse_decode_u_32(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
return deserializer.buffer.getUint32();
|
||||
}
|
||||
|
||||
@protected
|
||||
int sse_decode_u_8(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
return deserializer.buffer.getUint8();
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_decode_unit(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
}
|
||||
|
||||
@protected
|
||||
BigInt sse_decode_usize(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
return deserializer.buffer.getBigUint64();
|
||||
}
|
||||
|
||||
@protected
|
||||
int sse_decode_i_32(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
return deserializer.buffer.getInt32();
|
||||
}
|
||||
|
||||
@protected
|
||||
bool sse_decode_bool(SseDeserializer deserializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
return deserializer.buffer.getUint8() != 0;
|
||||
}
|
||||
|
||||
@protected
|
||||
int cst_encode_u_32(int raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw;
|
||||
}
|
||||
|
||||
@protected
|
||||
int cst_encode_u_8(int raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw;
|
||||
}
|
||||
|
||||
@protected
|
||||
void cst_encode_unit(void raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw;
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_String(String self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_list_prim_u_8_strict(utf8.encoder.convert(self), serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_u_8_loose(
|
||||
List<int> self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_i_32(self.length, serializer);
|
||||
serializer.buffer
|
||||
.putUint8List(self is Uint8List ? self : Uint8List.fromList(self));
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_u_8_strict(
|
||||
Uint8List self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_i_32(self.length, serializer);
|
||||
serializer.buffer.putUint8List(self);
|
||||
}
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
) self,
|
||||
SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
sse_encode_list_prim_u_8_strict(self.$1, serializer);
|
||||
sse_encode_usize(self.$2, serializer);
|
||||
sse_encode_usize(self.$3, serializer);
|
||||
sse_encode_list_prim_u_8_strict(self.$4, serializer);
|
||||
sse_encode_usize(self.$5, serializer);
|
||||
sse_encode_usize(self.$6, serializer);
|
||||
sse_encode_list_prim_u_8_strict(self.$7, serializer);
|
||||
sse_encode_usize(self.$8, serializer);
|
||||
sse_encode_usize(self.$9, serializer);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_u_32(int self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
serializer.buffer.putUint32(self);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_u_8(int self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
serializer.buffer.putUint8(self);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_unit(void self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_usize(BigInt self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
serializer.buffer.putBigUint64(self);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_i_32(int self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
serializer.buffer.putInt32(self);
|
||||
}
|
||||
|
||||
@protected
|
||||
void sse_encode_bool(bool self, SseSerializer serializer) {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
serializer.buffer.putUint8(self ? 1 : 0);
|
||||
}
|
||||
}
|
||||
420
mobile/apps/photos/lib/src/rust/frb_generated.io.dart
Normal file
420
mobile/apps/photos/lib/src/rust/frb_generated.io.dart
Normal file
@@ -0,0 +1,420 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: require_trailing_commas
|
||||
|
||||
// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
|
||||
|
||||
import 'api/image_processing.dart';
|
||||
import 'api/simple.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi' as ffi;
|
||||
import 'frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart';
|
||||
|
||||
abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
RustLibApiImplPlatform({
|
||||
required super.handler,
|
||||
required super.wire,
|
||||
required super.generalizedFrbRustBinding,
|
||||
required super.portManager,
|
||||
});
|
||||
|
||||
@protected
|
||||
String dco_decode_String(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<int> dco_decode_list_prim_u_8_loose(dynamic raw);
|
||||
|
||||
@protected
|
||||
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
|
||||
|
||||
@protected
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
) dco_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
|
||||
dynamic raw);
|
||||
|
||||
@protected
|
||||
int dco_decode_u_32(dynamic raw);
|
||||
|
||||
@protected
|
||||
int dco_decode_u_8(dynamic raw);
|
||||
|
||||
@protected
|
||||
void dco_decode_unit(dynamic raw);
|
||||
|
||||
@protected
|
||||
BigInt dco_decode_usize(dynamic raw);
|
||||
|
||||
@protected
|
||||
String sse_decode_String(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
) sse_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
|
||||
SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
int sse_decode_u_32(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
int sse_decode_u_8(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
void sse_decode_unit(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
BigInt sse_decode_usize(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
int sse_decode_i_32(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
bool sse_decode_bool(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> cst_encode_String(String raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return cst_encode_list_prim_u_8_strict(utf8.encoder.convert(raw));
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose> cst_encode_list_prim_u_8_loose(
|
||||
List<int> raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
final ans = wire.cst_new_list_prim_u_8_loose(raw.length);
|
||||
ans.ref.ptr.asTypedList(raw.length).setAll(0, raw);
|
||||
return ans;
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> cst_encode_list_prim_u_8_strict(
|
||||
Uint8List raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
final ans = wire.cst_new_list_prim_u_8_strict(raw.length);
|
||||
ans.ref.ptr.asTypedList(raw.length).setAll(0, raw);
|
||||
return ans;
|
||||
}
|
||||
|
||||
@protected
|
||||
int cst_encode_usize(BigInt raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw.toSigned(64).toInt();
|
||||
}
|
||||
|
||||
@protected
|
||||
void cst_api_fill_to_wire_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
) apiObj,
|
||||
wire_cst_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize
|
||||
wireObj) {
|
||||
wireObj.field0 = cst_encode_list_prim_u_8_strict(apiObj.$1);
|
||||
wireObj.field1 = cst_encode_usize(apiObj.$2);
|
||||
wireObj.field2 = cst_encode_usize(apiObj.$3);
|
||||
wireObj.field3 = cst_encode_list_prim_u_8_strict(apiObj.$4);
|
||||
wireObj.field4 = cst_encode_usize(apiObj.$5);
|
||||
wireObj.field5 = cst_encode_usize(apiObj.$6);
|
||||
wireObj.field6 = cst_encode_list_prim_u_8_strict(apiObj.$7);
|
||||
wireObj.field7 = cst_encode_usize(apiObj.$8);
|
||||
wireObj.field8 = cst_encode_usize(apiObj.$9);
|
||||
}
|
||||
|
||||
@protected
|
||||
int cst_encode_u_32(int raw);
|
||||
|
||||
@protected
|
||||
int cst_encode_u_8(int raw);
|
||||
|
||||
@protected
|
||||
void cst_encode_unit(void raw);
|
||||
|
||||
@protected
|
||||
void sse_encode_String(String self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_u_8_loose(List<int> self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_u_8_strict(
|
||||
Uint8List self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
) self,
|
||||
SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_u_32(int self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_u_8(int self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_unit(void self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_usize(BigInt self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_i_32(int self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_bool(bool self, SseSerializer serializer);
|
||||
}
|
||||
|
||||
// Section: wire_class
|
||||
|
||||
// ignore_for_file: camel_case_types, non_constant_identifier_names, avoid_positional_boolean_parameters, annotate_overrides, constant_identifier_names
|
||||
// AUTO GENERATED FILE, DO NOT EDIT.
|
||||
//
|
||||
// Generated by `package:ffigen`.
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// generated by flutter_rust_bridge
|
||||
class RustLibWire implements BaseWire {
|
||||
factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) =>
|
||||
RustLibWire(lib.ffiDynamicLibrary);
|
||||
|
||||
/// Holds the symbol lookup function.
|
||||
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
|
||||
_lookup;
|
||||
|
||||
/// The symbols are looked up in [dynamicLibrary].
|
||||
RustLibWire(ffi.DynamicLibrary dynamicLibrary)
|
||||
: _lookup = dynamicLibrary.lookup;
|
||||
|
||||
/// The symbols are looked up with [lookup].
|
||||
RustLibWire.fromLookup(
|
||||
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName) lookup,
|
||||
) : _lookup = lookup;
|
||||
|
||||
void store_dart_post_cobject(DartPostCObjectFnType ptr) {
|
||||
return _store_dart_post_cobject(ptr);
|
||||
}
|
||||
|
||||
late final _store_dart_post_cobjectPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(DartPostCObjectFnType)>>(
|
||||
'store_dart_post_cobject',
|
||||
);
|
||||
late final _store_dart_post_cobject = _store_dart_post_cobjectPtr
|
||||
.asFunction<void Function(DartPostCObjectFnType)>();
|
||||
|
||||
WireSyncRust2DartDco wire__crate__api__simple__greet(
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> name,
|
||||
) {
|
||||
return _wire__crate__api__simple__greet(name);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__simple__greetPtr = _lookup<
|
||||
ffi.NativeFunction<
|
||||
WireSyncRust2DartDco Function(
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>)>>(
|
||||
'frbgen_photos_wire__crate__api__simple__greet');
|
||||
late final _wire__crate__api__simple__greet =
|
||||
_wire__crate__api__simple__greetPtr.asFunction<
|
||||
WireSyncRust2DartDco Function(
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)>();
|
||||
|
||||
void wire__crate__api__simple__init_app(int port_) {
|
||||
return _wire__crate__api__simple__init_app(port_);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__simple__init_appPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
|
||||
'frbgen_photos_wire__crate__api__simple__init_app',
|
||||
);
|
||||
late final _wire__crate__api__simple__init_app =
|
||||
_wire__crate__api__simple__init_appPtr.asFunction<void Function(int)>();
|
||||
|
||||
void wire__crate__api__image_processing__process_image_ml_from_data(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose> rgba_data,
|
||||
int width,
|
||||
int height,
|
||||
) {
|
||||
return _wire__crate__api__image_processing__process_image_ml_from_data(
|
||||
port_,
|
||||
rgba_data,
|
||||
width,
|
||||
height,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__image_processing__process_image_ml_from_dataPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose>,
|
||||
ffi.Uint32,
|
||||
ffi.Uint32,
|
||||
)>>(
|
||||
'frbgen_photos_wire__crate__api__image_processing__process_image_ml_from_data',
|
||||
);
|
||||
late final _wire__crate__api__image_processing__process_image_ml_from_data =
|
||||
_wire__crate__api__image_processing__process_image_ml_from_dataPtr
|
||||
.asFunction<
|
||||
void Function(
|
||||
int,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose>,
|
||||
int,
|
||||
int,
|
||||
)>();
|
||||
|
||||
void wire__crate__api__image_processing__process_image_ml_from_path(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> image_path,
|
||||
) {
|
||||
return _wire__crate__api__image_processing__process_image_ml_from_path(
|
||||
port_,
|
||||
image_path,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__image_processing__process_image_ml_from_pathPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)>>(
|
||||
'frbgen_photos_wire__crate__api__image_processing__process_image_ml_from_path',
|
||||
);
|
||||
late final _wire__crate__api__image_processing__process_image_ml_from_path =
|
||||
_wire__crate__api__image_processing__process_image_ml_from_pathPtr
|
||||
.asFunction<
|
||||
void Function(int, ffi.Pointer<wire_cst_list_prim_u_8_strict>)>();
|
||||
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose> cst_new_list_prim_u_8_loose(
|
||||
int len,
|
||||
) {
|
||||
return _cst_new_list_prim_u_8_loose(len);
|
||||
}
|
||||
|
||||
late final _cst_new_list_prim_u_8_loosePtr = _lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose> Function(
|
||||
ffi.Int32)>>('frbgen_photos_cst_new_list_prim_u_8_loose');
|
||||
late final _cst_new_list_prim_u_8_loose = _cst_new_list_prim_u_8_loosePtr
|
||||
.asFunction<ffi.Pointer<wire_cst_list_prim_u_8_loose> Function(int)>();
|
||||
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> cst_new_list_prim_u_8_strict(
|
||||
int len,
|
||||
) {
|
||||
return _cst_new_list_prim_u_8_strict(len);
|
||||
}
|
||||
|
||||
late final _cst_new_list_prim_u_8_strictPtr = _lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> Function(
|
||||
ffi.Int32)>>('frbgen_photos_cst_new_list_prim_u_8_strict');
|
||||
late final _cst_new_list_prim_u_8_strict = _cst_new_list_prim_u_8_strictPtr
|
||||
.asFunction<ffi.Pointer<wire_cst_list_prim_u_8_strict> Function(int)>();
|
||||
|
||||
int dummy_method_to_enforce_bundling() {
|
||||
return _dummy_method_to_enforce_bundling();
|
||||
}
|
||||
|
||||
late final _dummy_method_to_enforce_bundlingPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Int64 Function()>>(
|
||||
'dummy_method_to_enforce_bundling',
|
||||
);
|
||||
late final _dummy_method_to_enforce_bundling =
|
||||
_dummy_method_to_enforce_bundlingPtr.asFunction<int Function()>();
|
||||
}
|
||||
|
||||
typedef DartPostCObjectFnType
|
||||
= ffi.Pointer<ffi.NativeFunction<DartPostCObjectFnTypeFunction>>;
|
||||
typedef DartPostCObjectFnTypeFunction = ffi.Bool Function(
|
||||
DartPort port_id, ffi.Pointer<ffi.Void> message);
|
||||
typedef DartDartPostCObjectFnTypeFunction = bool Function(
|
||||
DartDartPort port_id, ffi.Pointer<ffi.Void> message);
|
||||
typedef DartPort = ffi.Int64;
|
||||
typedef DartDartPort = int;
|
||||
|
||||
final class wire_cst_list_prim_u_8_strict extends ffi.Struct {
|
||||
external ffi.Pointer<ffi.Uint8> ptr;
|
||||
|
||||
@ffi.Int32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_list_prim_u_8_loose extends ffi.Struct {
|
||||
external ffi.Pointer<ffi.Uint8> ptr;
|
||||
|
||||
@ffi.Int32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize
|
||||
extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> field0;
|
||||
|
||||
@ffi.UintPtr()
|
||||
external int field1;
|
||||
|
||||
@ffi.UintPtr()
|
||||
external int field2;
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> field3;
|
||||
|
||||
@ffi.UintPtr()
|
||||
external int field4;
|
||||
|
||||
@ffi.UintPtr()
|
||||
external int field5;
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> field6;
|
||||
|
||||
@ffi.UintPtr()
|
||||
external int field7;
|
||||
|
||||
@ffi.UintPtr()
|
||||
external int field8;
|
||||
}
|
||||
@@ -18,8 +18,9 @@ class BottomOfTitleBarWidget extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: showCloseButton ? MainAxisAlignment.spaceBetween :
|
||||
MainAxisAlignment.start,
|
||||
mainAxisAlignment: showCloseButton
|
||||
? MainAxisAlignment.spaceBetween
|
||||
: MainAxisAlignment.start,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Padding(
|
||||
|
||||
@@ -100,7 +100,7 @@ class ExtentsPageView extends StatefulWidget {
|
||||
int? itemCount,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
this.openDrawer,
|
||||
}) : childrenDelegate = SliverChildBuilderDelegate(
|
||||
}) : childrenDelegate = SliverChildBuilderDelegate(
|
||||
itemBuilder,
|
||||
childCount: itemCount,
|
||||
addAutomaticKeepAlives: false,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import "package:flutter/material.dart";
|
||||
import 'package:photos/models/file/file.dart';
|
||||
import "package:photos/ui/viewer/file/zoomable_image.dart";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import 'package:photos/models/file/file.dart';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import "dart:async";
|
||||
import "dart:io" show File, Platform;
|
||||
import "dart:math" show exp, max, min, pi;
|
||||
import "dart:math" show max, min;
|
||||
import "dart:typed_data" show Float32List, Uint8List;
|
||||
import "dart:ui";
|
||||
|
||||
@@ -24,14 +24,23 @@ final _logger = Logger("ImageMlUtil");
|
||||
/// These are 8 bit unsigned integers in range 0-255 for each RGB channel
|
||||
typedef RGB = (int, int, int);
|
||||
|
||||
const gaussianKernelSize = 5;
|
||||
const gaussianKernelRadius = gaussianKernelSize ~/ 2;
|
||||
const gaussianSigma = 10.0;
|
||||
final List<List<double>> gaussianKernel =
|
||||
create2DGaussianKernel(gaussianKernelSize, gaussianSigma);
|
||||
|
||||
const maxKernelSize = gaussianKernelSize;
|
||||
const maxKernelRadius = maxKernelSize ~/ 2;
|
||||
const List<String> supportedRustImageFormats = [
|
||||
'bmp',
|
||||
'dds',
|
||||
'farbfeld',
|
||||
'gif',
|
||||
'hdr',
|
||||
'ico',
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'exr',
|
||||
'png',
|
||||
'pnm',
|
||||
'qoi',
|
||||
'tga',
|
||||
'tiff',
|
||||
'webp',
|
||||
];
|
||||
|
||||
// Face thumbnail compression constants
|
||||
const int _faceThumbnailCompressionQuality = 90;
|
||||
@@ -102,6 +111,11 @@ Future<DecodedImage> decodeImageFromPath(
|
||||
return DecodedImage(image, rawRgbaBytes);
|
||||
}
|
||||
|
||||
bool canRustDecodeImage(String imagePath) {
|
||||
final format = imagePath.split('.').last;
|
||||
return supportedRustImageFormats.contains(format);
|
||||
}
|
||||
|
||||
/// Decodes [Uint8List] image data to an ui.[Image] object.
|
||||
Future<Image> decodeImageFromData(Uint8List imageData) async {
|
||||
// Decoding using flutter paint. This is the fastest and easiest method.
|
||||
@@ -218,18 +232,19 @@ Future<List<Uint8List>> generateFaceThumbnailsUsingCanvas(
|
||||
}
|
||||
}
|
||||
|
||||
Future<(Float32List, Dimensions)> preprocessImageYoloFace(
|
||||
Image image,
|
||||
Uint8List rawRgbaBytes,
|
||||
Future<Float32List> resizedToPreprocessedYoloFace(
|
||||
Uint8List rgbBytes,
|
||||
int rgbWidth,
|
||||
int rgbHeight,
|
||||
) async {
|
||||
const requiredWidth = 640;
|
||||
const requiredHeight = 640;
|
||||
final scale = min(requiredWidth / image.width, requiredHeight / image.height);
|
||||
final scaledWidth = (image.width * scale).round().clamp(0, requiredWidth);
|
||||
final scaledHeight = (image.height * scale).round().clamp(0, requiredHeight);
|
||||
final int letterboxWidth = requiredWidth - rgbWidth;
|
||||
final int letterboxHeight = requiredHeight - rgbHeight;
|
||||
final int letterboxWidthHalf = letterboxWidth ~/ 2;
|
||||
final int letterboxHeightHalf = letterboxHeight ~/ 2;
|
||||
|
||||
final processedBytes = Float32List(3 * requiredHeight * requiredWidth);
|
||||
|
||||
final buffer = Float32List.view(processedBytes.buffer);
|
||||
int pixelIndex = 0;
|
||||
const int channelOffsetGreen = requiredHeight * requiredWidth;
|
||||
@@ -237,14 +252,18 @@ Future<(Float32List, Dimensions)> preprocessImageYoloFace(
|
||||
for (var h = 0; h < requiredHeight; h++) {
|
||||
for (var w = 0; w < requiredWidth; w++) {
|
||||
late RGB pixel;
|
||||
if (w >= scaledWidth || h >= scaledHeight) {
|
||||
if (w < letterboxWidthHalf ||
|
||||
w >= rgbWidth + letterboxWidthHalf ||
|
||||
h < letterboxHeightHalf ||
|
||||
h >= rgbHeight + letterboxHeightHalf) {
|
||||
pixel = const (114, 114, 114);
|
||||
} else {
|
||||
pixel = _getPixelBilinear(
|
||||
w / scale,
|
||||
h / scale,
|
||||
image,
|
||||
rawRgbaBytes,
|
||||
final int byteIndex = 3 *
|
||||
(rgbWidth * (h - letterboxHeightHalf) + (w - letterboxWidthHalf));
|
||||
pixel = (
|
||||
rgbBytes[byteIndex],
|
||||
rgbBytes[byteIndex + 1],
|
||||
rgbBytes[byteIndex + 2]
|
||||
);
|
||||
}
|
||||
buffer[pixelIndex] = pixel.$1 / 255;
|
||||
@@ -254,40 +273,35 @@ Future<(Float32List, Dimensions)> preprocessImageYoloFace(
|
||||
}
|
||||
}
|
||||
|
||||
return (processedBytes, Dimensions(width: scaledWidth, height: scaledHeight));
|
||||
return processedBytes;
|
||||
}
|
||||
|
||||
Future<Float32List> preprocessImageClip(
|
||||
Image image,
|
||||
Uint8List rawRgbaBytes,
|
||||
Future<Float32List> resizedToPreprocessedClip(
|
||||
Uint8List rgbBytes,
|
||||
int rgbWidth,
|
||||
int rgbHeight,
|
||||
) async {
|
||||
const int requiredWidth = 256;
|
||||
const int requiredHeight = 256;
|
||||
const int requiredSize = 3 * requiredWidth * requiredHeight;
|
||||
final scale = max(requiredWidth / image.width, requiredHeight / image.height);
|
||||
final bool useAntiAlias = scale < 0.8;
|
||||
final scaledWidth = (image.width * scale).round();
|
||||
final scaledHeight = (image.height * scale).round();
|
||||
final widthOffset = max(0, scaledWidth - requiredWidth) / 2;
|
||||
final heightOffset = max(0, scaledHeight - requiredHeight) / 2;
|
||||
const requiredWidth = 256;
|
||||
const requiredHeight = 256;
|
||||
|
||||
final processedBytes = Float32List(requiredSize);
|
||||
final processedBytes = Float32List(3 * requiredHeight * requiredWidth);
|
||||
final buffer = Float32List.view(processedBytes.buffer);
|
||||
int pixelIndex = 0;
|
||||
const int greenOff = requiredHeight * requiredWidth;
|
||||
const int blueOff = 2 * requiredHeight * requiredWidth;
|
||||
for (var h = 0 + heightOffset; h < scaledHeight - heightOffset; h++) {
|
||||
for (var w = 0 + widthOffset; w < scaledWidth - widthOffset; w++) {
|
||||
final RGB pixel = _getPixelBilinear(
|
||||
w / scale,
|
||||
h / scale,
|
||||
image,
|
||||
rawRgbaBytes,
|
||||
antiAlias: useAntiAlias,
|
||||
const int channelOffsetGreen = requiredHeight * requiredWidth;
|
||||
const int channelOffsetBlue = 2 * requiredHeight * requiredWidth;
|
||||
final widthOffset = max(0, rgbWidth - requiredWidth) ~/ 2;
|
||||
final heightOffset = max(0, rgbHeight - requiredHeight) ~/ 2;
|
||||
for (var h = 0 + heightOffset; h < heightOffset + requiredHeight; h++) {
|
||||
for (var w = 0 + widthOffset; w < widthOffset + requiredWidth; w++) {
|
||||
final int byteIndex = 3 * (rgbWidth * h + w);
|
||||
final RGB pixel = (
|
||||
rgbBytes[byteIndex],
|
||||
rgbBytes[byteIndex + 1],
|
||||
rgbBytes[byteIndex + 2]
|
||||
);
|
||||
buffer[pixelIndex] = pixel.$1 / 255;
|
||||
buffer[pixelIndex + greenOff] = pixel.$2 / 255;
|
||||
buffer[pixelIndex + blueOff] = pixel.$3 / 255;
|
||||
buffer[pixelIndex + channelOffsetGreen] = pixel.$2 / 255;
|
||||
buffer[pixelIndex + channelOffsetBlue] = pixel.$3 / 255;
|
||||
pixelIndex++;
|
||||
}
|
||||
}
|
||||
@@ -297,20 +311,20 @@ Future<Float32List> preprocessImageClip(
|
||||
|
||||
Future<(Float32List, List<AlignmentResult>, List<bool>, List<double>, Size)>
|
||||
preprocessToMobileFaceNetFloat32List(
|
||||
Image image,
|
||||
Dimensions imageDimensions,
|
||||
Uint8List rawRgbaBytes,
|
||||
List<FaceDetectionRelative> relativeFaces, {
|
||||
int width = 112,
|
||||
int height = 112,
|
||||
}) async {
|
||||
final Size originalSize =
|
||||
Size(image.width.toDouble(), image.height.toDouble());
|
||||
Size(imageDimensions.width.toDouble(), imageDimensions.height.toDouble());
|
||||
|
||||
final List<FaceDetectionAbsolute> absoluteFaces =
|
||||
relativeToAbsoluteDetections(
|
||||
relativeDetections: relativeFaces,
|
||||
imageWidth: image.width,
|
||||
imageHeight: image.height,
|
||||
imageWidth: imageDimensions.width,
|
||||
imageHeight: imageDimensions.height,
|
||||
);
|
||||
|
||||
final alignedImagesFloat32List =
|
||||
@@ -334,7 +348,7 @@ Future<(Float32List, List<AlignmentResult>, List<bool>, List<double>, Size)>
|
||||
alignmentResults.add(alignmentResult);
|
||||
|
||||
_warpAffineFloat32List(
|
||||
image,
|
||||
imageDimensions,
|
||||
rawRgbaBytes,
|
||||
alignmentResult.affineMatrix,
|
||||
alignedImagesFloat32List,
|
||||
@@ -368,14 +382,11 @@ Future<(Float32List, List<AlignmentResult>, List<bool>, List<double>, Size)>
|
||||
RGB _readPixelColor(
|
||||
int x,
|
||||
int y,
|
||||
Image image,
|
||||
Dimensions image,
|
||||
Uint8List rgbaBytes,
|
||||
) {
|
||||
if (y < 0 || y >= image.height || x < 0 || x >= image.width) {
|
||||
if (y < -maxKernelRadius ||
|
||||
y >= image.height + maxKernelRadius ||
|
||||
x < -maxKernelRadius ||
|
||||
x >= image.width + maxKernelRadius) {
|
||||
if (y < -2 || y >= image.height + 2 || x < -2 || x >= image.width + 2) {
|
||||
_logger.severe(
|
||||
'`readPixelColor`: Invalid pixel coordinates, out of bounds. x: $x, y: $y',
|
||||
);
|
||||
@@ -393,29 +404,6 @@ RGB _readPixelColor(
|
||||
);
|
||||
}
|
||||
|
||||
RGB _getPixelBlurred(
|
||||
int x,
|
||||
int y,
|
||||
Image image,
|
||||
Uint8List rgbaBytes,
|
||||
) {
|
||||
double r = 0, g = 0, b = 0;
|
||||
for (int ky = 0; ky < gaussianKernelSize; ky++) {
|
||||
for (int kx = 0; kx < gaussianKernelSize; kx++) {
|
||||
final int px = (x - gaussianKernelRadius + kx);
|
||||
final int py = (y - gaussianKernelRadius + ky);
|
||||
|
||||
final RGB pixelRgbTuple = _readPixelColor(px, py, image, rgbaBytes);
|
||||
final double weight = gaussianKernel[ky][kx];
|
||||
|
||||
r += pixelRgbTuple.$1 * weight;
|
||||
g += pixelRgbTuple.$2 * weight;
|
||||
b += pixelRgbTuple.$3 * weight;
|
||||
}
|
||||
}
|
||||
return (r.round(), g.round(), b.round());
|
||||
}
|
||||
|
||||
List<List<int>> _createGrayscaleIntMatrixFromNormalized2List(
|
||||
Float32List imageList,
|
||||
int startIndex, {
|
||||
@@ -473,7 +461,7 @@ Future<Image> _cropImage(
|
||||
}
|
||||
|
||||
void _warpAffineFloat32List(
|
||||
Image inputImage,
|
||||
Dimensions inputImageDimensions,
|
||||
Uint8List rawRgbaBytes,
|
||||
List<List<double>> affineMatrix,
|
||||
Float32List outputList,
|
||||
@@ -528,8 +516,12 @@ void _warpAffineFloat32List(
|
||||
final num xOrigin = (xTrans - b00) * a00Prime + (yTrans - b10) * a01Prime;
|
||||
final num yOrigin = (xTrans - b00) * a10Prime + (yTrans - b10) * a11Prime;
|
||||
|
||||
final RGB pixel =
|
||||
_getPixelBicubic(xOrigin, yOrigin, inputImage, rawRgbaBytes);
|
||||
final RGB pixel = _getPixelBicubic(
|
||||
xOrigin,
|
||||
yOrigin,
|
||||
inputImageDimensions,
|
||||
rawRgbaBytes,
|
||||
);
|
||||
|
||||
// Set the new pixel
|
||||
outputList[startIndex + 3 * (yTrans * width + xTrans)] =
|
||||
@@ -584,56 +576,14 @@ Future<List<Uint8List>> compressFaceThumbnails(Map args) async {
|
||||
}
|
||||
}
|
||||
|
||||
RGB _getPixelBilinear(
|
||||
RGB _getPixelBicubic(
|
||||
num fx,
|
||||
num fy,
|
||||
Image image,
|
||||
Uint8List rawRgbaBytes, {
|
||||
bool antiAlias = false,
|
||||
}) {
|
||||
// Clamp to image boundaries
|
||||
fx = fx.clamp(0, image.width - 1);
|
||||
fy = fy.clamp(0, image.height - 1);
|
||||
|
||||
// Get the surrounding coordinates and their weights
|
||||
final int x0 = fx.floor();
|
||||
final int x1 = fx.ceil();
|
||||
final int y0 = fy.floor();
|
||||
final int y1 = fy.ceil();
|
||||
final dx = fx - x0;
|
||||
final dy = fy - y0;
|
||||
final dx1 = 1.0 - dx;
|
||||
final dy1 = 1.0 - dy;
|
||||
|
||||
// Get the original pixels (with gaussian blur if antialias)
|
||||
final RGB Function(int, int, Image, Uint8List) readPixel =
|
||||
antiAlias ? _getPixelBlurred : _readPixelColor;
|
||||
final RGB pixel1 = readPixel(x0, y0, image, rawRgbaBytes);
|
||||
final RGB pixel2 = readPixel(x1, y0, image, rawRgbaBytes);
|
||||
final RGB pixel3 = readPixel(x0, y1, image, rawRgbaBytes);
|
||||
final RGB pixel4 = readPixel(x1, y1, image, rawRgbaBytes);
|
||||
|
||||
int bilinear(
|
||||
num val1,
|
||||
num val2,
|
||||
num val3,
|
||||
num val4,
|
||||
) =>
|
||||
(val1 * dx1 * dy1 + val2 * dx * dy1 + val3 * dx1 * dy + val4 * dx * dy)
|
||||
.round();
|
||||
|
||||
// Calculate the weighted sum of pixels
|
||||
final int r = bilinear(pixel1.$1, pixel2.$1, pixel3.$1, pixel4.$1);
|
||||
final int g = bilinear(pixel1.$2, pixel2.$2, pixel3.$2, pixel4.$2);
|
||||
final int b = bilinear(pixel1.$3, pixel2.$3, pixel3.$3, pixel4.$3);
|
||||
|
||||
return (r, g, b);
|
||||
}
|
||||
|
||||
/// Get the pixel value using Bicubic Interpolation. Code taken mainly from https://github.com/brendan-duncan/image/blob/6e407612752ffdb90b28cd5863c7f65856349348/lib/src/image/image.dart#L697
|
||||
RGB _getPixelBicubic(num fx, num fy, Image image, Uint8List rawRgbaBytes) {
|
||||
fx = fx.clamp(0, image.width - 1);
|
||||
fy = fy.clamp(0, image.height - 1);
|
||||
Dimensions imageDimensions,
|
||||
Uint8List rawRgbaBytes,
|
||||
) {
|
||||
fx = fx.clamp(0, imageDimensions.width - 1);
|
||||
fy = fy.clamp(0, imageDimensions.height - 1);
|
||||
|
||||
final x = fx.toInt() - (fx >= 0.0 ? 0 : 1);
|
||||
final px = x - 1;
|
||||
@@ -652,62 +602,69 @@ RGB _getPixelBicubic(num fx, num fy, Image image, Uint8List rawRgbaBytes) {
|
||||
dx * dx * (2 * ipp - 5 * icp + 4 * inp - iap) +
|
||||
dx * dx * dx * (-ipp + 3 * icp - 3 * inp + iap));
|
||||
|
||||
final icc = _readPixelColor(x, y, image, rawRgbaBytes);
|
||||
final icc = _readPixelColor(x, y, imageDimensions, rawRgbaBytes);
|
||||
|
||||
final ipp =
|
||||
px < 0 || py < 0 ? icc : _readPixelColor(px, py, image, rawRgbaBytes);
|
||||
final icp = px < 0 ? icc : _readPixelColor(x, py, image, rawRgbaBytes);
|
||||
final inp = py < 0 || nx >= image.width
|
||||
final ipp = px < 0 || py < 0
|
||||
? icc
|
||||
: _readPixelColor(nx, py, image, rawRgbaBytes);
|
||||
final iap = ax >= image.width || py < 0
|
||||
: _readPixelColor(px, py, imageDimensions, rawRgbaBytes);
|
||||
final icp =
|
||||
px < 0 ? icc : _readPixelColor(x, py, imageDimensions, rawRgbaBytes);
|
||||
final inp = py < 0 || nx >= imageDimensions.width
|
||||
? icc
|
||||
: _readPixelColor(ax, py, image, rawRgbaBytes);
|
||||
: _readPixelColor(nx, py, imageDimensions, rawRgbaBytes);
|
||||
final iap = ax >= imageDimensions.width || py < 0
|
||||
? icc
|
||||
: _readPixelColor(ax, py, imageDimensions, rawRgbaBytes);
|
||||
|
||||
final ip0 = cubic(dx, ipp.$1, icp.$1, inp.$1, iap.$1);
|
||||
final ip1 = cubic(dx, ipp.$2, icp.$2, inp.$2, iap.$2);
|
||||
final ip2 = cubic(dx, ipp.$3, icp.$3, inp.$3, iap.$3);
|
||||
// final ip3 = cubic(dx, ipp.a, icp.a, inp.a, iap.a);
|
||||
|
||||
final ipc = px < 0 ? icc : _readPixelColor(px, y, image, rawRgbaBytes);
|
||||
final inc =
|
||||
nx >= image.width ? icc : _readPixelColor(nx, y, image, rawRgbaBytes);
|
||||
final iac =
|
||||
ax >= image.width ? icc : _readPixelColor(ax, y, image, rawRgbaBytes);
|
||||
final ipc =
|
||||
px < 0 ? icc : _readPixelColor(px, y, imageDimensions, rawRgbaBytes);
|
||||
final inc = nx >= imageDimensions.width
|
||||
? icc
|
||||
: _readPixelColor(nx, y, imageDimensions, rawRgbaBytes);
|
||||
final iac = ax >= imageDimensions.width
|
||||
? icc
|
||||
: _readPixelColor(ax, y, imageDimensions, rawRgbaBytes);
|
||||
|
||||
final ic0 = cubic(dx, ipc.$1, icc.$1, inc.$1, iac.$1);
|
||||
final ic1 = cubic(dx, ipc.$2, icc.$2, inc.$2, iac.$2);
|
||||
final ic2 = cubic(dx, ipc.$3, icc.$3, inc.$3, iac.$3);
|
||||
// final ic3 = cubic(dx, ipc.a, icc.a, inc.a, iac.a);
|
||||
|
||||
final ipn = px < 0 || ny >= image.height
|
||||
final ipn = px < 0 || ny >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(px, ny, image, rawRgbaBytes);
|
||||
final icn =
|
||||
ny >= image.height ? icc : _readPixelColor(x, ny, image, rawRgbaBytes);
|
||||
final inn = nx >= image.width || ny >= image.height
|
||||
: _readPixelColor(px, ny, imageDimensions, rawRgbaBytes);
|
||||
final icn = ny >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(nx, ny, image, rawRgbaBytes);
|
||||
final ian = ax >= image.width || ny >= image.height
|
||||
: _readPixelColor(x, ny, imageDimensions, rawRgbaBytes);
|
||||
final inn = nx >= imageDimensions.width || ny >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(ax, ny, image, rawRgbaBytes);
|
||||
: _readPixelColor(nx, ny, imageDimensions, rawRgbaBytes);
|
||||
final ian = ax >= imageDimensions.width || ny >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(ax, ny, imageDimensions, rawRgbaBytes);
|
||||
|
||||
final in0 = cubic(dx, ipn.$1, icn.$1, inn.$1, ian.$1);
|
||||
final in1 = cubic(dx, ipn.$2, icn.$2, inn.$2, ian.$2);
|
||||
final in2 = cubic(dx, ipn.$3, icn.$3, inn.$3, ian.$3);
|
||||
// final in3 = cubic(dx, ipn.a, icn.a, inn.a, ian.a);
|
||||
|
||||
final ipa = px < 0 || ay >= image.height
|
||||
final ipa = px < 0 || ay >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(px, ay, image, rawRgbaBytes);
|
||||
final ica =
|
||||
ay >= image.height ? icc : _readPixelColor(x, ay, image, rawRgbaBytes);
|
||||
final ina = nx >= image.width || ay >= image.height
|
||||
: _readPixelColor(px, ay, imageDimensions, rawRgbaBytes);
|
||||
final ica = ay >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(nx, ay, image, rawRgbaBytes);
|
||||
final iaa = ax >= image.width || ay >= image.height
|
||||
: _readPixelColor(x, ay, imageDimensions, rawRgbaBytes);
|
||||
final ina = nx >= imageDimensions.width || ay >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(ax, ay, image, rawRgbaBytes);
|
||||
: _readPixelColor(nx, ay, imageDimensions, rawRgbaBytes);
|
||||
final iaa = ax >= imageDimensions.width || ay >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(ax, ay, imageDimensions, rawRgbaBytes);
|
||||
|
||||
final ia0 = cubic(dx, ipa.$1, ica.$1, ina.$1, iaa.$1);
|
||||
final ia1 = cubic(dx, ipa.$2, ica.$2, ina.$2, iaa.$2);
|
||||
@@ -721,30 +678,3 @@ RGB _getPixelBicubic(num fx, num fy, Image image, Uint8List rawRgbaBytes) {
|
||||
|
||||
return (c0, c1, c2); // (red, green, blue)
|
||||
}
|
||||
|
||||
List<List<double>> create2DGaussianKernel(int size, double sigma) {
|
||||
final List<List<double>> kernel =
|
||||
List.generate(size, (_) => List<double>.filled(size, 0));
|
||||
double sum = 0.0;
|
||||
final int center = size ~/ 2;
|
||||
|
||||
for (int y = 0; y < size; y++) {
|
||||
for (int x = 0; x < size; x++) {
|
||||
final int dx = x - center;
|
||||
final int dy = y - center;
|
||||
final double g = (1 / (2 * pi * sigma * sigma)) *
|
||||
exp(-(dx * dx + dy * dy) / (2 * sigma * sigma));
|
||||
kernel[y][x] = g;
|
||||
sum += g;
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize the kernel
|
||||
for (int y = 0; y < size; y++) {
|
||||
for (int x = 0; x < size; x++) {
|
||||
kernel[y][x] /= sum;
|
||||
}
|
||||
}
|
||||
|
||||
return kernel;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "dart:io" show File, Platform;
|
||||
import "dart:math" as math show sqrt, min, max;
|
||||
import "dart:typed_data" show Uint8List;
|
||||
|
||||
import "package:flutter/services.dart" show PlatformException;
|
||||
import "package:logging/logging.dart";
|
||||
@@ -22,6 +23,8 @@ import "package:photos/services/machine_learning/ml_result.dart";
|
||||
import "package:photos/services/machine_learning/semantic_search/semantic_search_service.dart";
|
||||
import "package:photos/services/search_service.dart";
|
||||
import "package:photos/services/sync/local_sync_service.dart";
|
||||
import "package:photos/src/rust/api/image_processing.dart";
|
||||
import "package:photos/src/rust/custom/init_frb.dart";
|
||||
import "package:photos/utils/file_util.dart";
|
||||
import "package:photos/utils/image_ml_util.dart";
|
||||
import "package:photos/utils/network_util.dart";
|
||||
@@ -410,27 +413,60 @@ Future<MLResult> analyzeImageStatic(Map args) async {
|
||||
_logger.info(
|
||||
"Start analyzeImageStatic for fileID $enteFileID (runFaces: $runFaces, runClip: $runClip)",
|
||||
);
|
||||
await initFrb();
|
||||
final startTime = DateTime.now();
|
||||
|
||||
// Decode the image once to use for both face detection and alignment
|
||||
final decodedImage =
|
||||
await decodeImageFromPath(imagePath, includeRgbaBytes: true);
|
||||
final image = decodedImage.image;
|
||||
final rawRgbaBytes = decodedImage.rawRgbaBytes!;
|
||||
final bool decodeInRust = canRustDecodeImage(imagePath);
|
||||
late (
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
) rustResults;
|
||||
if (decodeInRust) {
|
||||
rustResults = await processImageMlFromPath(imagePath: imagePath);
|
||||
} else {
|
||||
final (image, decodedRgbaBytes) = await decodeImageFromPath(imagePath);
|
||||
rustResults = await processImageMlFromData(
|
||||
rgbaData: decodedRgbaBytes,
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
);
|
||||
}
|
||||
final (
|
||||
rawRgbaBytes,
|
||||
imageHeight,
|
||||
imageWidth,
|
||||
faceBytes,
|
||||
faceHeight,
|
||||
faceWidth,
|
||||
clipBytes,
|
||||
clipHeight,
|
||||
clipWidth
|
||||
) = rustResults;
|
||||
final decodedImageSize =
|
||||
Dimensions(height: image.height, width: image.width);
|
||||
final result = MLResult.fromEnteFileID(enteFileID);
|
||||
result.decodedImageSize = decodedImageSize;
|
||||
Dimensions(height: imageHeight.toInt(), width: imageWidth.toInt());
|
||||
final decodeTime = DateTime.now();
|
||||
final decodeMs = decodeTime.difference(startTime).inMilliseconds;
|
||||
|
||||
final result = MLResult.fromEnteFileID(enteFileID);
|
||||
result.decodedImageSize = decodedImageSize;
|
||||
String faceMsString = "", clipMsString = "";
|
||||
final pipelines = await Future.wait([
|
||||
runFaces
|
||||
? FaceRecognitionService.runFacesPipeline(
|
||||
enteFileID,
|
||||
image,
|
||||
decodedImageSize,
|
||||
rawRgbaBytes,
|
||||
faceBytes,
|
||||
faceHeight.toInt(),
|
||||
faceWidth.toInt(),
|
||||
faceDetectionAddress,
|
||||
faceEmbeddingAddress,
|
||||
).then((result) {
|
||||
@@ -442,8 +478,9 @@ Future<MLResult> analyzeImageStatic(Map args) async {
|
||||
runClip
|
||||
? SemanticSearchService.runClipImage(
|
||||
enteFileID,
|
||||
image,
|
||||
rawRgbaBytes,
|
||||
clipBytes,
|
||||
clipHeight.toInt(),
|
||||
clipWidth.toInt(),
|
||||
clipImageAddress,
|
||||
).then((result) {
|
||||
clipMsString =
|
||||
|
||||
@@ -175,6 +175,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
build_cli_annotations:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_cli_annotations
|
||||
sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -639,6 +647,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
ffigen:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: ffigen
|
||||
sha256: a0ca4853028c6a9e4d9a0a40bb744fceb898c89d75931d08e87b3987d0087060
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.0"
|
||||
ffmpeg_kit_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -774,7 +790,7 @@ packages:
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
flutter_driver:
|
||||
dependency: "direct dev"
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
@@ -906,14 +922,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_launcher_icons
|
||||
sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.1"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@@ -1007,6 +1015,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.24"
|
||||
flutter_rust_bridge:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_rust_bridge
|
||||
sha256: "37ef40bc6f863652e865f0b2563ea07f0d3c58d8efad803cc01933a4b2ee067e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.1"
|
||||
flutter_secure_storage:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -2048,7 +2064,7 @@ packages:
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
pool:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: pool
|
||||
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||
@@ -2081,7 +2097,7 @@ packages:
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
protobuf:
|
||||
dependency: "direct overridden"
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: protobuf
|
||||
sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
|
||||
@@ -2089,7 +2105,7 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
provider:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: provider
|
||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||
@@ -2113,7 +2129,7 @@ packages:
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: quiver
|
||||
sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2
|
||||
@@ -2137,6 +2153,13 @@ packages:
|
||||
url: "https://github.com/KasemJaffer/receive_sharing_intent.git"
|
||||
source: git
|
||||
version: "1.8.1"
|
||||
rust_lib_photos:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: rust_builder
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -2977,6 +3000,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
yaml_edit:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml_edit
|
||||
sha256: fb38626579fb345ad00e674e2af3a5c9b0cc4b9bfb8fd7f7ff322c7c9e62aef5
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
sdks:
|
||||
dart: ">=3.7.0-0 <4.0.0"
|
||||
flutter: ">=3.24.0"
|
||||
|
||||
@@ -95,6 +95,7 @@ dependencies:
|
||||
flutter_map: ^6.2.0
|
||||
flutter_map_marker_cluster: ^1.3.6
|
||||
flutter_password_strength: ^0.1.6
|
||||
flutter_rust_bridge: 2.11.1
|
||||
# Do not upgrade this package unless this issue is resolved:
|
||||
# https://github.com/juliansteenbakker/flutter_secure_storage/issues/870
|
||||
# On v9.2.4, keys related to lockscreen persist even after reintsall. For context see:
|
||||
@@ -169,14 +170,20 @@ dependencies:
|
||||
photo_view: ^0.14.0
|
||||
pinput: ^5.0.0
|
||||
pointycastle: ^3.7.3
|
||||
pool: ^1.5.1
|
||||
privacy_screen: # pub.dev is behind
|
||||
git:
|
||||
url: https://github.com/eddyuan/privacy_screen.git
|
||||
ref: 855418e
|
||||
protobuf: ^3.1.0
|
||||
provider: ^6.0.0
|
||||
quiver: ^3.0.1
|
||||
receive_sharing_intent: # pub.dev is behind
|
||||
git:
|
||||
url: https://github.com/KasemJaffer/receive_sharing_intent.git
|
||||
ref: 2cea396
|
||||
rust_lib_photos:
|
||||
path: rust_builder
|
||||
screenshot: ^3.0.0
|
||||
scrollable_positioned_list: ^0.3.5
|
||||
sentry: ^8.14.1
|
||||
@@ -257,9 +264,7 @@ flutter_intl:
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.7
|
||||
flutter_driver:
|
||||
sdk: flutter
|
||||
flutter_launcher_icons: ^0.13.1
|
||||
ffigen: ^15.0.0
|
||||
flutter_lints: ^3.0.1
|
||||
flutter_native_splash: ^2.4.4
|
||||
flutter_test:
|
||||
|
||||
1
mobile/apps/photos/rust/.gitignore
vendored
Normal file
1
mobile/apps/photos/rust/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
1656
mobile/apps/photos/rust/Cargo.lock
generated
Normal file
1656
mobile/apps/photos/rust/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
14
mobile/apps/photos/rust/Cargo.toml
Normal file
14
mobile/apps/photos/rust/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "rust_lib_photos"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "staticlib"]
|
||||
|
||||
[dependencies]
|
||||
flutter_rust_bridge = "=2.11.1"
|
||||
image = "0.25.4"
|
||||
resize = "0.8.7"
|
||||
rgb = "0.8.50"
|
||||
bytemuck = "1.16.0"
|
||||
151
mobile/apps/photos/rust/src/api/image_processing.rs
Normal file
151
mobile/apps/photos/rust/src/api/image_processing.rs
Normal file
@@ -0,0 +1,151 @@
|
||||
use image::ImageBuffer;
|
||||
use resize::{px::RGB, Pixel::RGB8, Type::Lanczos3, Type::Mitchell};
|
||||
use rgb::FromSlice;
|
||||
|
||||
pub fn process_image_ml_from_path(
|
||||
image_path: &str,
|
||||
) -> (
|
||||
Vec<u8>,
|
||||
usize,
|
||||
usize,
|
||||
Vec<u8>,
|
||||
usize,
|
||||
usize,
|
||||
Vec<u8>,
|
||||
usize,
|
||||
usize,
|
||||
) {
|
||||
// Load the image from the path (~200ms)
|
||||
let img: image::DynamicImage = image::open(image_path).expect("Failed to open image");
|
||||
|
||||
// Process the image
|
||||
let results = process_image_ml(img);
|
||||
|
||||
results
|
||||
}
|
||||
|
||||
pub fn process_image_ml_from_data(
|
||||
rgba_data: Vec<u8>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> (
|
||||
Vec<u8>,
|
||||
usize,
|
||||
usize,
|
||||
Vec<u8>,
|
||||
usize,
|
||||
usize,
|
||||
Vec<u8>,
|
||||
usize,
|
||||
usize,
|
||||
) {
|
||||
// Load the image from the data
|
||||
let img = image::DynamicImage::from(
|
||||
ImageBuffer::<image::Rgb<u8>, _>::from_raw(width, height, rgba_data)
|
||||
.expect("Failed to create image buffer"),
|
||||
);
|
||||
|
||||
// Process the image
|
||||
let results = process_image_ml(img);
|
||||
|
||||
results
|
||||
}
|
||||
|
||||
fn process_image_ml(
|
||||
img: image::DynamicImage,
|
||||
) -> (
|
||||
Vec<u8>,
|
||||
usize,
|
||||
usize,
|
||||
Vec<u8>,
|
||||
usize,
|
||||
usize,
|
||||
Vec<u8>,
|
||||
usize,
|
||||
usize,
|
||||
) {
|
||||
// Get dimensions for resized images (0ms)
|
||||
let (width, height) = (img.width() as usize, img.height() as usize);
|
||||
let scale_face = f32::min(640.0 / width as f32, 640.0 / height as f32);
|
||||
let scale_clip = f32::max(256.0 / width as f32, 256.0 / height as f32);
|
||||
let (new_width_face, new_height_face) = (
|
||||
f32::round(width as f32 * scale_face) as usize,
|
||||
f32::round(height as f32 * scale_face) as usize,
|
||||
);
|
||||
let (new_width_clip, new_height_clip) = (
|
||||
f32::round(width as f32 * scale_clip) as usize,
|
||||
f32::round(height as f32 * scale_clip) as usize,
|
||||
);
|
||||
let mut interpolation_face = Lanczos3;
|
||||
if scale_face > 1.0 {
|
||||
interpolation_face = Mitchell;
|
||||
}
|
||||
let mut interpolation_clip = Lanczos3;
|
||||
if scale_clip > 1.0 {
|
||||
interpolation_clip = Mitchell;
|
||||
}
|
||||
|
||||
// Convert image to RGB8 (~150ms)
|
||||
let rgba_decoded = img.to_rgba8().to_vec();
|
||||
let rgb_img = img.into_rgb8();
|
||||
|
||||
// Convert RGB8 to Vec<RGB> (~30ms)
|
||||
let rgb_vec = rgb_img.to_vec();
|
||||
|
||||
// Create resizer (~20ms)
|
||||
let mut resizer_face = resize::new(
|
||||
width,
|
||||
height,
|
||||
new_width_face,
|
||||
new_height_face,
|
||||
RGB8,
|
||||
interpolation_face,
|
||||
)
|
||||
.unwrap();
|
||||
let mut resizer_clip = resize::new(
|
||||
width,
|
||||
height,
|
||||
new_width_clip,
|
||||
new_height_clip,
|
||||
RGB8,
|
||||
interpolation_clip,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Create buffer for resized image (~120ms)
|
||||
let mut dst_face = vec![RGB::new(0, 0, 0); new_width_face * new_height_face];
|
||||
let mut dst_clip = vec![RGB::new(0, 0, 0); new_width_clip * new_height_clip];
|
||||
|
||||
// Resize the image (~120ms)
|
||||
resizer_face
|
||||
.resize(rgb_vec.as_rgb(), &mut dst_face)
|
||||
.unwrap();
|
||||
resizer_clip
|
||||
.resize(rgb_vec.as_rgb(), &mut dst_clip)
|
||||
.unwrap();
|
||||
|
||||
// Return resized images as Vec<u8> (~120ms)
|
||||
let mut result_face = Vec::with_capacity(new_width_face * new_height_face * 3);
|
||||
for pixel in dst_face {
|
||||
result_face.push(pixel.r);
|
||||
result_face.push(pixel.g);
|
||||
result_face.push(pixel.b);
|
||||
}
|
||||
let mut result_clip = Vec::with_capacity(new_width_clip * new_height_clip * 3);
|
||||
for pixel in dst_clip {
|
||||
result_clip.push(pixel.r);
|
||||
result_clip.push(pixel.g);
|
||||
result_clip.push(pixel.b);
|
||||
}
|
||||
(
|
||||
rgba_decoded,
|
||||
height,
|
||||
width,
|
||||
result_face,
|
||||
new_height_face,
|
||||
new_width_face,
|
||||
result_clip,
|
||||
new_height_clip,
|
||||
new_width_clip,
|
||||
)
|
||||
}
|
||||
2
mobile/apps/photos/rust/src/api/mod.rs
Normal file
2
mobile/apps/photos/rust/src/api/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod simple;
|
||||
pub mod image_processing;
|
||||
10
mobile/apps/photos/rust/src/api/simple.rs
Normal file
10
mobile/apps/photos/rust/src/api/simple.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
#[flutter_rust_bridge::frb(sync)] // Synchronous mode for simplicity of the demo
|
||||
pub fn greet(name: String) -> String {
|
||||
format!("Hello, {name}!")
|
||||
}
|
||||
|
||||
#[flutter_rust_bridge::frb(init)]
|
||||
pub fn init_app() {
|
||||
// Default utilities - feel free to customize
|
||||
flutter_rust_bridge::setup_default_user_utils();
|
||||
}
|
||||
517
mobile/apps/photos/rust/src/frb_generated.rs
Normal file
517
mobile/apps/photos/rust/src/frb_generated.rs
Normal file
@@ -0,0 +1,517 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
#![allow(
|
||||
non_camel_case_types,
|
||||
unused,
|
||||
non_snake_case,
|
||||
clippy::needless_return,
|
||||
clippy::redundant_closure_call,
|
||||
clippy::redundant_closure,
|
||||
clippy::useless_conversion,
|
||||
clippy::unit_arg,
|
||||
clippy::unused_unit,
|
||||
clippy::double_parens,
|
||||
clippy::let_and_return,
|
||||
clippy::too_many_arguments,
|
||||
clippy::match_single_binding,
|
||||
clippy::clone_on_copy,
|
||||
clippy::let_unit_value,
|
||||
clippy::deref_addrof,
|
||||
clippy::explicit_auto_deref,
|
||||
clippy::borrow_deref_ref,
|
||||
clippy::needless_borrow
|
||||
)]
|
||||
|
||||
// Section: imports
|
||||
|
||||
use flutter_rust_bridge::for_generated::byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt};
|
||||
use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable};
|
||||
use flutter_rust_bridge::{Handler, IntoIntoDart};
|
||||
|
||||
// Section: boilerplate
|
||||
|
||||
flutter_rust_bridge::frb_generated_boilerplate!(
|
||||
default_stream_sink_codec = DcoCodec,
|
||||
default_rust_opaque = RustOpaqueNom,
|
||||
default_rust_auto_opaque = RustAutoOpaqueNom,
|
||||
);
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.11.1";
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1140226238;
|
||||
|
||||
// Section: executor
|
||||
|
||||
flutter_rust_bridge::frb_generated_default_handler!();
|
||||
|
||||
// Section: wire_funcs
|
||||
|
||||
fn wire__crate__api__simple__greet_impl(
|
||||
name: impl CstDecode<String>,
|
||||
) -> flutter_rust_bridge::for_generated::WireSyncRust2DartDco {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::<flutter_rust_bridge::for_generated::DcoCodec, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "greet",
|
||||
port: None,
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync,
|
||||
},
|
||||
move || {
|
||||
let api_name = name.cst_decode();
|
||||
transform_result_dco::<_, _, ()>((move || {
|
||||
let output_ok = Result::<_, ()>::Ok(crate::api::simple::greet(api_name))?;
|
||||
Ok(output_ok)
|
||||
})())
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__simple__init_app_impl(port_: flutter_rust_bridge::for_generated::MessagePort) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::DcoCodec, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "init_app",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
move |context| {
|
||||
transform_result_dco::<_, _, ()>((move || {
|
||||
let output_ok = Result::<_, ()>::Ok({
|
||||
crate::api::simple::init_app();
|
||||
})?;
|
||||
Ok(output_ok)
|
||||
})())
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__image_processing__process_image_ml_from_data_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
rgba_data: impl CstDecode<Vec<u8>>,
|
||||
width: impl CstDecode<u32>,
|
||||
height: impl CstDecode<u32>,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::DcoCodec, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "process_image_ml_from_data",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let api_rgba_data = rgba_data.cst_decode();
|
||||
let api_width = width.cst_decode();
|
||||
let api_height = height.cst_decode();
|
||||
move |context| {
|
||||
transform_result_dco::<_, _, ()>((move || {
|
||||
let output_ok = Result::<_, ()>::Ok(
|
||||
crate::api::image_processing::process_image_ml_from_data(
|
||||
api_rgba_data,
|
||||
api_width,
|
||||
api_height,
|
||||
),
|
||||
)?;
|
||||
Ok(output_ok)
|
||||
})())
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__image_processing__process_image_ml_from_path_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
image_path: impl CstDecode<String>,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::DcoCodec, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "process_image_ml_from_path",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let api_image_path = image_path.cst_decode();
|
||||
move |context| {
|
||||
transform_result_dco::<_, _, ()>((move || {
|
||||
let output_ok = Result::<_, ()>::Ok(
|
||||
crate::api::image_processing::process_image_ml_from_path(&api_image_path),
|
||||
)?;
|
||||
Ok(output_ok)
|
||||
})())
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Section: dart2rust
|
||||
|
||||
impl CstDecode<u32> for u32 {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
fn cst_decode(self) -> u32 {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl CstDecode<u8> for u8 {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
fn cst_decode(self) -> u8 {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl CstDecode<usize> for usize {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
fn cst_decode(self) -> usize {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl SseDecode for String {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
let mut inner = <Vec<u8>>::sse_decode(deserializer);
|
||||
return String::from_utf8(inner).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for Vec<u8> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
let mut len_ = <i32>::sse_decode(deserializer);
|
||||
let mut ans_ = vec![];
|
||||
for idx_ in 0..len_ {
|
||||
ans_.push(<u8>::sse_decode(deserializer));
|
||||
}
|
||||
return ans_;
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode
|
||||
for (
|
||||
Vec<u8>,
|
||||
usize,
|
||||
usize,
|
||||
Vec<u8>,
|
||||
usize,
|
||||
usize,
|
||||
Vec<u8>,
|
||||
usize,
|
||||
usize,
|
||||
)
|
||||
{
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
let mut var_field0 = <Vec<u8>>::sse_decode(deserializer);
|
||||
let mut var_field1 = <usize>::sse_decode(deserializer);
|
||||
let mut var_field2 = <usize>::sse_decode(deserializer);
|
||||
let mut var_field3 = <Vec<u8>>::sse_decode(deserializer);
|
||||
let mut var_field4 = <usize>::sse_decode(deserializer);
|
||||
let mut var_field5 = <usize>::sse_decode(deserializer);
|
||||
let mut var_field6 = <Vec<u8>>::sse_decode(deserializer);
|
||||
let mut var_field7 = <usize>::sse_decode(deserializer);
|
||||
let mut var_field8 = <usize>::sse_decode(deserializer);
|
||||
return (
|
||||
var_field0, var_field1, var_field2, var_field3, var_field4, var_field5, var_field6,
|
||||
var_field7, var_field8,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for u32 {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
deserializer.cursor.read_u32::<NativeEndian>().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for u8 {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
deserializer.cursor.read_u8().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for () {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {}
|
||||
}
|
||||
|
||||
impl SseDecode for usize {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
deserializer.cursor.read_u64::<NativeEndian>().unwrap() as _
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for i32 {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
deserializer.cursor.read_i32::<NativeEndian>().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for bool {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
deserializer.cursor.read_u8().unwrap() != 0
|
||||
}
|
||||
}
|
||||
|
||||
fn pde_ffi_dispatcher_primary_impl(
|
||||
func_id: i32,
|
||||
port: flutter_rust_bridge::for_generated::MessagePort,
|
||||
ptr: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
rust_vec_len: i32,
|
||||
data_len: i32,
|
||||
) {
|
||||
// Codec=Pde (Serialization + dispatch), see doc to use other codecs
|
||||
match func_id {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn pde_ffi_dispatcher_sync_impl(
|
||||
func_id: i32,
|
||||
ptr: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
rust_vec_len: i32,
|
||||
data_len: i32,
|
||||
) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse {
|
||||
// Codec=Pde (Serialization + dispatch), see doc to use other codecs
|
||||
match func_id {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// Section: rust2dart
|
||||
|
||||
impl SseEncode for String {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
<Vec<u8>>::sse_encode(self.into_bytes(), serializer);
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for Vec<u8> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
<i32>::sse_encode(self.len() as _, serializer);
|
||||
for item in self {
|
||||
<u8>::sse_encode(item, serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode
|
||||
for (
|
||||
Vec<u8>,
|
||||
usize,
|
||||
usize,
|
||||
Vec<u8>,
|
||||
usize,
|
||||
usize,
|
||||
Vec<u8>,
|
||||
usize,
|
||||
usize,
|
||||
)
|
||||
{
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
<Vec<u8>>::sse_encode(self.0, serializer);
|
||||
<usize>::sse_encode(self.1, serializer);
|
||||
<usize>::sse_encode(self.2, serializer);
|
||||
<Vec<u8>>::sse_encode(self.3, serializer);
|
||||
<usize>::sse_encode(self.4, serializer);
|
||||
<usize>::sse_encode(self.5, serializer);
|
||||
<Vec<u8>>::sse_encode(self.6, serializer);
|
||||
<usize>::sse_encode(self.7, serializer);
|
||||
<usize>::sse_encode(self.8, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for u32 {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
serializer.cursor.write_u32::<NativeEndian>(self).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for u8 {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
serializer.cursor.write_u8(self).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for () {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {}
|
||||
}
|
||||
|
||||
impl SseEncode for usize {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
serializer
|
||||
.cursor
|
||||
.write_u64::<NativeEndian>(self as _)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for i32 {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
serializer.cursor.write_i32::<NativeEndian>(self).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for bool {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
serializer.cursor.write_u8(self as _).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
mod io {
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// Section: imports
|
||||
|
||||
use super::*;
|
||||
use flutter_rust_bridge::for_generated::byteorder::{
|
||||
NativeEndian, ReadBytesExt, WriteBytesExt,
|
||||
};
|
||||
use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable};
|
||||
use flutter_rust_bridge::{Handler, IntoIntoDart};
|
||||
|
||||
// Section: boilerplate
|
||||
|
||||
flutter_rust_bridge::frb_generated_boilerplate_io!();
|
||||
|
||||
// Section: dart2rust
|
||||
|
||||
impl CstDecode<String> for *mut wire_cst_list_prim_u_8_strict {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
fn cst_decode(self) -> String {
|
||||
let vec: Vec<u8> = self.cst_decode();
|
||||
String::from_utf8(vec).unwrap()
|
||||
}
|
||||
}
|
||||
impl CstDecode<Vec<u8>> for *mut wire_cst_list_prim_u_8_loose {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
fn cst_decode(self) -> Vec<u8> {
|
||||
unsafe {
|
||||
let wrap = flutter_rust_bridge::for_generated::box_from_leak_ptr(self);
|
||||
flutter_rust_bridge::for_generated::vec_from_leak_ptr(wrap.ptr, wrap.len)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl CstDecode<Vec<u8>> for *mut wire_cst_list_prim_u_8_strict {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
fn cst_decode(self) -> Vec<u8> {
|
||||
unsafe {
|
||||
let wrap = flutter_rust_bridge::for_generated::box_from_leak_ptr(self);
|
||||
flutter_rust_bridge::for_generated::vec_from_leak_ptr(wrap.ptr, wrap.len)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl CstDecode<(Vec<u8>,usize,usize,Vec<u8>,usize,usize,Vec<u8>,usize,usize,)> for wire_cst_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
fn cst_decode(self) -> (Vec<u8>,usize,usize,Vec<u8>,usize,usize,Vec<u8>,usize,usize,) {
|
||||
(self.field0.cst_decode(),self.field1.cst_decode(),self.field2.cst_decode(),self.field3.cst_decode(),self.field4.cst_decode(),self.field5.cst_decode(),self.field6.cst_decode(),self.field7.cst_decode(),self.field8.cst_decode(),)
|
||||
}
|
||||
}
|
||||
impl NewWithNullPtr for wire_cst_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize {
|
||||
fn new_with_null_ptr() -> Self {
|
||||
Self { field0: core::ptr::null_mut(),
|
||||
field1: Default::default(),
|
||||
field2: Default::default(),
|
||||
field3: core::ptr::null_mut(),
|
||||
field4: Default::default(),
|
||||
field5: Default::default(),
|
||||
field6: core::ptr::null_mut(),
|
||||
field7: Default::default(),
|
||||
field8: Default::default(), }
|
||||
}
|
||||
}
|
||||
impl Default for wire_cst_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize {
|
||||
fn default() -> Self {
|
||||
Self::new_with_null_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_photos_wire__crate__api__simple__greet(
|
||||
name: *mut wire_cst_list_prim_u_8_strict,
|
||||
) -> flutter_rust_bridge::for_generated::WireSyncRust2DartDco {
|
||||
wire__crate__api__simple__greet_impl(name)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_photos_wire__crate__api__simple__init_app(port_: i64) {
|
||||
wire__crate__api__simple__init_app_impl(port_)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_photos_wire__crate__api__image_processing__process_image_ml_from_data(
|
||||
port_: i64,
|
||||
rgba_data: *mut wire_cst_list_prim_u_8_loose,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) {
|
||||
wire__crate__api__image_processing__process_image_ml_from_data_impl(
|
||||
port_, rgba_data, width, height,
|
||||
)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_photos_wire__crate__api__image_processing__process_image_ml_from_path(
|
||||
port_: i64,
|
||||
image_path: *mut wire_cst_list_prim_u_8_strict,
|
||||
) {
|
||||
wire__crate__api__image_processing__process_image_ml_from_path_impl(port_, image_path)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_photos_cst_new_list_prim_u_8_loose(
|
||||
len: i32,
|
||||
) -> *mut wire_cst_list_prim_u_8_loose {
|
||||
let ans = wire_cst_list_prim_u_8_loose {
|
||||
ptr: flutter_rust_bridge::for_generated::new_leak_vec_ptr(Default::default(), len),
|
||||
len,
|
||||
};
|
||||
flutter_rust_bridge::for_generated::new_leak_box_ptr(ans)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_photos_cst_new_list_prim_u_8_strict(
|
||||
len: i32,
|
||||
) -> *mut wire_cst_list_prim_u_8_strict {
|
||||
let ans = wire_cst_list_prim_u_8_strict {
|
||||
ptr: flutter_rust_bridge::for_generated::new_leak_vec_ptr(Default::default(), len),
|
||||
len,
|
||||
};
|
||||
flutter_rust_bridge::for_generated::new_leak_box_ptr(ans)
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct wire_cst_list_prim_u_8_loose {
|
||||
ptr: *mut u8,
|
||||
len: i32,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct wire_cst_list_prim_u_8_strict {
|
||||
ptr: *mut u8,
|
||||
len: i32,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct wire_cst_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize
|
||||
{
|
||||
field0: *mut wire_cst_list_prim_u_8_strict,
|
||||
field1: usize,
|
||||
field2: usize,
|
||||
field3: *mut wire_cst_list_prim_u_8_strict,
|
||||
field4: usize,
|
||||
field5: usize,
|
||||
field6: *mut wire_cst_list_prim_u_8_strict,
|
||||
field7: usize,
|
||||
field8: usize,
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
pub use io::*;
|
||||
2
mobile/apps/photos/rust/src/lib.rs
Normal file
2
mobile/apps/photos/rust/src/lib.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod api;
|
||||
mod frb_generated;
|
||||
29
mobile/apps/photos/rust_builder/.gitignore
vendored
Normal file
29
mobile/apps/photos/rust_builder/.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
build/
|
||||
1
mobile/apps/photos/rust_builder/README.md
Normal file
1
mobile/apps/photos/rust_builder/README.md
Normal file
@@ -0,0 +1 @@
|
||||
Please ignore this folder, which is just glue to build Rust with Flutter.
|
||||
9
mobile/apps/photos/rust_builder/android/.gitignore
vendored
Normal file
9
mobile/apps/photos/rust_builder/android/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.cxx
|
||||
56
mobile/apps/photos/rust_builder/android/build.gradle
Normal file
56
mobile/apps/photos/rust_builder/android/build.gradle
Normal file
@@ -0,0 +1,56 @@
|
||||
// The Android Gradle Plugin builds the native code with the Android NDK.
|
||||
|
||||
group 'com.flutter_rust_bridge.rust_lib_photos'
|
||||
version '1.0'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// The Android Gradle Plugin knows how to build native code with the NDK.
|
||||
classpath 'com.android.tools.build:gradle:7.3.0'
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
if (project.android.hasProperty("namespace")) {
|
||||
namespace 'com.flutter_rust_bridge.rust_lib_photos'
|
||||
}
|
||||
|
||||
// Bumping the plugin compileSdkVersion requires all clients of this plugin
|
||||
// to bump the version in their app.
|
||||
compileSdkVersion 33
|
||||
|
||||
// Use the NDK version
|
||||
// declared in /android/app/build.gradle file of the Flutter project.
|
||||
// Replace it with a version number if this plugin requires a specfic NDK version.
|
||||
// (e.g. ndkVersion "23.1.7779620")
|
||||
ndkVersion android.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 19
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "../cargokit/gradle/plugin.gradle"
|
||||
cargokit {
|
||||
manifestDir = "../../rust"
|
||||
libname = "rust_lib_photos"
|
||||
}
|
||||
1
mobile/apps/photos/rust_builder/android/settings.gradle
Normal file
1
mobile/apps/photos/rust_builder/android/settings.gradle
Normal file
@@ -0,0 +1 @@
|
||||
rootProject.name = 'rust_lib_photos'
|
||||
@@ -0,0 +1,3 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.flutter_rust_bridge.rust_lib_photos">
|
||||
</manifest>
|
||||
4
mobile/apps/photos/rust_builder/cargokit/.gitignore
vendored
Normal file
4
mobile/apps/photos/rust_builder/cargokit/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
target
|
||||
.dart_tool
|
||||
*.iml
|
||||
!pubspec.lock
|
||||
11
mobile/apps/photos/rust_builder/cargokit/README
Normal file
11
mobile/apps/photos/rust_builder/cargokit/README
Normal file
@@ -0,0 +1,11 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
Experimental repository to provide glue for seamlessly integrating cargo build
|
||||
with flutter plugins and packages.
|
||||
|
||||
See https://matejknopp.com/post/flutter_plugin_in_rust_with_no_prebuilt_binaries/
|
||||
for a tutorial on how to use Cargokit.
|
||||
|
||||
Example plugin available at https://github.com/irondash/hello_rust_ffi_plugin.
|
||||
|
||||
58
mobile/apps/photos/rust_builder/cargokit/build_pod.sh
Executable file
58
mobile/apps/photos/rust_builder/cargokit/build_pod.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
BASEDIR=$(dirname "$0")
|
||||
|
||||
# Workaround for https://github.com/dart-lang/pub/issues/4010
|
||||
BASEDIR=$(cd "$BASEDIR" ; pwd -P)
|
||||
|
||||
# Remove XCode SDK from path. Otherwise this breaks tool compilation when building iOS project
|
||||
NEW_PATH=`echo $PATH | tr ":" "\n" | grep -v "Contents/Developer/" | tr "\n" ":"`
|
||||
|
||||
export PATH=${NEW_PATH%?} # remove trailing :
|
||||
|
||||
env
|
||||
|
||||
# Platform name (macosx, iphoneos, iphonesimulator)
|
||||
export CARGOKIT_DARWIN_PLATFORM_NAME=$PLATFORM_NAME
|
||||
|
||||
# Arctive architectures (arm64, armv7, x86_64), space separated.
|
||||
export CARGOKIT_DARWIN_ARCHS=$ARCHS
|
||||
|
||||
# Current build configuration (Debug, Release)
|
||||
export CARGOKIT_CONFIGURATION=$CONFIGURATION
|
||||
|
||||
# Path to directory containing Cargo.toml.
|
||||
export CARGOKIT_MANIFEST_DIR=$PODS_TARGET_SRCROOT/$1
|
||||
|
||||
# Temporary directory for build artifacts.
|
||||
export CARGOKIT_TARGET_TEMP_DIR=$TARGET_TEMP_DIR
|
||||
|
||||
# Output directory for final artifacts.
|
||||
export CARGOKIT_OUTPUT_DIR=$PODS_CONFIGURATION_BUILD_DIR/$PRODUCT_NAME
|
||||
|
||||
# Directory to store built tool artifacts.
|
||||
export CARGOKIT_TOOL_TEMP_DIR=$TARGET_TEMP_DIR/build_tool
|
||||
|
||||
# Directory inside root project. Not necessarily the top level directory of root project.
|
||||
export CARGOKIT_ROOT_PROJECT_DIR=$SRCROOT
|
||||
|
||||
FLUTTER_EXPORT_BUILD_ENVIRONMENT=(
|
||||
"$PODS_ROOT/../Flutter/ephemeral/flutter_export_environment.sh" # macOS
|
||||
"$PODS_ROOT/../Flutter/flutter_export_environment.sh" # iOS
|
||||
)
|
||||
|
||||
for path in "${FLUTTER_EXPORT_BUILD_ENVIRONMENT[@]}"
|
||||
do
|
||||
if [[ -f "$path" ]]; then
|
||||
source "$path"
|
||||
fi
|
||||
done
|
||||
|
||||
sh "$BASEDIR/run_build_tool.sh" build-pod "$@"
|
||||
|
||||
# Make a symlink from built framework to phony file, which will be used as input to
|
||||
# build script. This should force rebuild (podspec currently doesn't support alwaysOutOfDate
|
||||
# attribute on custom build phase)
|
||||
ln -fs "$OBJROOT/XCBuildData/build.db" "${BUILT_PRODUCTS_DIR}/cargokit_phony"
|
||||
ln -fs "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}" "${BUILT_PRODUCTS_DIR}/cargokit_phony_out"
|
||||
@@ -0,0 +1,5 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
A sample command-line application with an entrypoint in `bin/`, library code
|
||||
in `lib/`, and example unit test in `test/`.
|
||||
@@ -0,0 +1,34 @@
|
||||
# This is copied from Cargokit (which is the official way to use it currently)
|
||||
# Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
linter:
|
||||
rules:
|
||||
- prefer_relative_imports
|
||||
- directives_ordering
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
@@ -0,0 +1,8 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'package:build_tool/build_tool.dart' as build_tool;
|
||||
|
||||
void main(List<String> arguments) {
|
||||
build_tool.runMain(arguments);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'src/build_tool.dart' as build_tool;
|
||||
|
||||
Future<void> runMain(List<String> args) async {
|
||||
return build_tool.runMain(args);
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:version/version.dart';
|
||||
|
||||
import 'target.dart';
|
||||
import 'util.dart';
|
||||
|
||||
class AndroidEnvironment {
|
||||
AndroidEnvironment({
|
||||
required this.sdkPath,
|
||||
required this.ndkVersion,
|
||||
required this.minSdkVersion,
|
||||
required this.targetTempDir,
|
||||
required this.target,
|
||||
});
|
||||
|
||||
static void clangLinkerWrapper(List<String> args) {
|
||||
final clang = Platform.environment['_CARGOKIT_NDK_LINK_CLANG'];
|
||||
if (clang == null) {
|
||||
throw Exception(
|
||||
"cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_CLANG env var");
|
||||
}
|
||||
final target = Platform.environment['_CARGOKIT_NDK_LINK_TARGET'];
|
||||
if (target == null) {
|
||||
throw Exception(
|
||||
"cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_TARGET env var");
|
||||
}
|
||||
|
||||
runCommand(clang, [
|
||||
target,
|
||||
...args,
|
||||
]);
|
||||
}
|
||||
|
||||
/// Full path to Android SDK.
|
||||
final String sdkPath;
|
||||
|
||||
/// Full version of Android NDK.
|
||||
final String ndkVersion;
|
||||
|
||||
/// Minimum supported SDK version.
|
||||
final int minSdkVersion;
|
||||
|
||||
/// Target directory for build artifacts.
|
||||
final String targetTempDir;
|
||||
|
||||
/// Target being built.
|
||||
final Target target;
|
||||
|
||||
bool ndkIsInstalled() {
|
||||
final ndkPath = path.join(sdkPath, 'ndk', ndkVersion);
|
||||
final ndkPackageXml = File(path.join(ndkPath, 'package.xml'));
|
||||
return ndkPackageXml.existsSync();
|
||||
}
|
||||
|
||||
void installNdk({
|
||||
required String javaHome,
|
||||
}) {
|
||||
final sdkManagerExtension = Platform.isWindows ? '.bat' : '';
|
||||
final sdkManager = path.join(
|
||||
sdkPath,
|
||||
'cmdline-tools',
|
||||
'latest',
|
||||
'bin',
|
||||
'sdkmanager$sdkManagerExtension',
|
||||
);
|
||||
|
||||
log.info('Installing NDK $ndkVersion');
|
||||
runCommand(sdkManager, [
|
||||
'--install',
|
||||
'ndk;$ndkVersion',
|
||||
], environment: {
|
||||
'JAVA_HOME': javaHome,
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map<String, String>> buildEnvironment() async {
|
||||
final hostArch = Platform.isMacOS
|
||||
? "darwin-x86_64"
|
||||
: (Platform.isLinux ? "linux-x86_64" : "windows-x86_64");
|
||||
|
||||
final ndkPath = path.join(sdkPath, 'ndk', ndkVersion);
|
||||
final toolchainPath = path.join(
|
||||
ndkPath,
|
||||
'toolchains',
|
||||
'llvm',
|
||||
'prebuilt',
|
||||
hostArch,
|
||||
'bin',
|
||||
);
|
||||
|
||||
final minSdkVersion =
|
||||
math.max(target.androidMinSdkVersion!, this.minSdkVersion);
|
||||
|
||||
final exe = Platform.isWindows ? '.exe' : '';
|
||||
|
||||
final arKey = 'AR_${target.rust}';
|
||||
final arValue = ['${target.rust}-ar', 'llvm-ar', 'llvm-ar.exe']
|
||||
.map((e) => path.join(toolchainPath, e))
|
||||
.firstWhereOrNull((element) => File(element).existsSync());
|
||||
if (arValue == null) {
|
||||
throw Exception('Failed to find ar for $target in $toolchainPath');
|
||||
}
|
||||
|
||||
final targetArg = '--target=${target.rust}$minSdkVersion';
|
||||
|
||||
final ccKey = 'CC_${target.rust}';
|
||||
final ccValue = path.join(toolchainPath, 'clang$exe');
|
||||
final cfFlagsKey = 'CFLAGS_${target.rust}';
|
||||
final cFlagsValue = targetArg;
|
||||
|
||||
final cxxKey = 'CXX_${target.rust}';
|
||||
final cxxValue = path.join(toolchainPath, 'clang++$exe');
|
||||
final cxxFlagsKey = 'CXXFLAGS_${target.rust}';
|
||||
final cxxFlagsValue = targetArg;
|
||||
|
||||
final linkerKey =
|
||||
'cargo_target_${target.rust.replaceAll('-', '_')}_linker'.toUpperCase();
|
||||
|
||||
final ranlibKey = 'RANLIB_${target.rust}';
|
||||
final ranlibValue = path.join(toolchainPath, 'llvm-ranlib$exe');
|
||||
|
||||
final ndkVersionParsed = Version.parse(ndkVersion);
|
||||
final rustFlagsKey = 'CARGO_ENCODED_RUSTFLAGS';
|
||||
final rustFlagsValue = _libGccWorkaround(targetTempDir, ndkVersionParsed);
|
||||
|
||||
final runRustTool =
|
||||
Platform.isWindows ? 'run_build_tool.cmd' : 'run_build_tool.sh';
|
||||
|
||||
final packagePath = (await Isolate.resolvePackageUri(
|
||||
Uri.parse('package:build_tool/buildtool.dart')))!
|
||||
.toFilePath();
|
||||
final selfPath = path.canonicalize(path.join(
|
||||
packagePath,
|
||||
'..',
|
||||
'..',
|
||||
'..',
|
||||
runRustTool,
|
||||
));
|
||||
|
||||
// Make sure that run_build_tool is working properly even initially launched directly
|
||||
// through dart run.
|
||||
final toolTempDir =
|
||||
Platform.environment['CARGOKIT_TOOL_TEMP_DIR'] ?? targetTempDir;
|
||||
|
||||
return {
|
||||
arKey: arValue,
|
||||
ccKey: ccValue,
|
||||
cfFlagsKey: cFlagsValue,
|
||||
cxxKey: cxxValue,
|
||||
cxxFlagsKey: cxxFlagsValue,
|
||||
ranlibKey: ranlibValue,
|
||||
rustFlagsKey: rustFlagsValue,
|
||||
linkerKey: selfPath,
|
||||
// Recognized by main() so we know when we're acting as a wrapper
|
||||
'_CARGOKIT_NDK_LINK_TARGET': targetArg,
|
||||
'_CARGOKIT_NDK_LINK_CLANG': ccValue,
|
||||
'CARGOKIT_TOOL_TEMP_DIR': toolTempDir,
|
||||
};
|
||||
}
|
||||
|
||||
// Workaround for libgcc missing in NDK23, inspired by cargo-ndk
|
||||
String _libGccWorkaround(String buildDir, Version ndkVersion) {
|
||||
final workaroundDir = path.join(
|
||||
buildDir,
|
||||
'cargokit',
|
||||
'libgcc_workaround',
|
||||
'${ndkVersion.major}',
|
||||
);
|
||||
Directory(workaroundDir).createSync(recursive: true);
|
||||
if (ndkVersion.major >= 23) {
|
||||
File(path.join(workaroundDir, 'libgcc.a'))
|
||||
.writeAsStringSync('INPUT(-lunwind)');
|
||||
} else {
|
||||
// Other way around, untested, forward libgcc.a from libunwind once Rust
|
||||
// gets updated for NDK23+.
|
||||
File(path.join(workaroundDir, 'libunwind.a'))
|
||||
.writeAsStringSync('INPUT(-lgcc)');
|
||||
}
|
||||
|
||||
var rustFlags = Platform.environment['CARGO_ENCODED_RUSTFLAGS'] ?? '';
|
||||
if (rustFlags.isNotEmpty) {
|
||||
rustFlags = '$rustFlags\x1f';
|
||||
}
|
||||
rustFlags = '$rustFlags-L\x1f$workaroundDir';
|
||||
return rustFlags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:ed25519_edwards/ed25519_edwards.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'builder.dart';
|
||||
import 'crate_hash.dart';
|
||||
import 'options.dart';
|
||||
import 'precompile_binaries.dart';
|
||||
import 'rustup.dart';
|
||||
import 'target.dart';
|
||||
|
||||
class Artifact {
|
||||
/// File system location of the artifact.
|
||||
final String path;
|
||||
|
||||
/// Actual file name that the artifact should have in destination folder.
|
||||
final String finalFileName;
|
||||
|
||||
AritifactType get type {
|
||||
if (finalFileName.endsWith('.dll') ||
|
||||
finalFileName.endsWith('.dll.lib') ||
|
||||
finalFileName.endsWith('.pdb') ||
|
||||
finalFileName.endsWith('.so') ||
|
||||
finalFileName.endsWith('.dylib')) {
|
||||
return AritifactType.dylib;
|
||||
} else if (finalFileName.endsWith('.lib') || finalFileName.endsWith('.a')) {
|
||||
return AritifactType.staticlib;
|
||||
} else {
|
||||
throw Exception('Unknown artifact type for $finalFileName');
|
||||
}
|
||||
}
|
||||
|
||||
Artifact({
|
||||
required this.path,
|
||||
required this.finalFileName,
|
||||
});
|
||||
}
|
||||
|
||||
final _log = Logger('artifacts_provider');
|
||||
|
||||
class ArtifactProvider {
|
||||
ArtifactProvider({
|
||||
required this.environment,
|
||||
required this.userOptions,
|
||||
});
|
||||
|
||||
final BuildEnvironment environment;
|
||||
final CargokitUserOptions userOptions;
|
||||
|
||||
Future<Map<Target, List<Artifact>>> getArtifacts(List<Target> targets) async {
|
||||
final result = await _getPrecompiledArtifacts(targets);
|
||||
|
||||
final pendingTargets = List.of(targets);
|
||||
pendingTargets.removeWhere((element) => result.containsKey(element));
|
||||
|
||||
if (pendingTargets.isEmpty) {
|
||||
return result;
|
||||
}
|
||||
|
||||
final rustup = Rustup();
|
||||
for (final target in targets) {
|
||||
final builder = RustBuilder(target: target, environment: environment);
|
||||
builder.prepare(rustup);
|
||||
_log.info('Building ${environment.crateInfo.packageName} for $target');
|
||||
final targetDir = await builder.build();
|
||||
// For local build accept both static and dynamic libraries.
|
||||
final artifactNames = <String>{
|
||||
...getArtifactNames(
|
||||
target: target,
|
||||
libraryName: environment.crateInfo.packageName,
|
||||
aritifactType: AritifactType.dylib,
|
||||
remote: false,
|
||||
),
|
||||
...getArtifactNames(
|
||||
target: target,
|
||||
libraryName: environment.crateInfo.packageName,
|
||||
aritifactType: AritifactType.staticlib,
|
||||
remote: false,
|
||||
)
|
||||
};
|
||||
final artifacts = artifactNames
|
||||
.map((artifactName) => Artifact(
|
||||
path: path.join(targetDir, artifactName),
|
||||
finalFileName: artifactName,
|
||||
))
|
||||
.where((element) => File(element.path).existsSync())
|
||||
.toList();
|
||||
result[target] = artifacts;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<Map<Target, List<Artifact>>> _getPrecompiledArtifacts(
|
||||
List<Target> targets) async {
|
||||
if (userOptions.usePrecompiledBinaries == false) {
|
||||
_log.info('Precompiled binaries are disabled');
|
||||
return {};
|
||||
}
|
||||
if (environment.crateOptions.precompiledBinaries == null) {
|
||||
_log.fine('Precompiled binaries not enabled for this crate');
|
||||
return {};
|
||||
}
|
||||
|
||||
final start = Stopwatch()..start();
|
||||
final crateHash = CrateHash.compute(environment.manifestDir,
|
||||
tempStorage: environment.targetTempDir);
|
||||
_log.fine(
|
||||
'Computed crate hash $crateHash in ${start.elapsedMilliseconds}ms');
|
||||
|
||||
final downloadedArtifactsDir =
|
||||
path.join(environment.targetTempDir, 'precompiled', crateHash);
|
||||
Directory(downloadedArtifactsDir).createSync(recursive: true);
|
||||
|
||||
final res = <Target, List<Artifact>>{};
|
||||
|
||||
for (final target in targets) {
|
||||
final requiredArtifacts = getArtifactNames(
|
||||
target: target,
|
||||
libraryName: environment.crateInfo.packageName,
|
||||
remote: true,
|
||||
);
|
||||
final artifactsForTarget = <Artifact>[];
|
||||
|
||||
for (final artifact in requiredArtifacts) {
|
||||
final fileName = PrecompileBinaries.fileName(target, artifact);
|
||||
final downloadedPath = path.join(downloadedArtifactsDir, fileName);
|
||||
if (!File(downloadedPath).existsSync()) {
|
||||
final signatureFileName =
|
||||
PrecompileBinaries.signatureFileName(target, artifact);
|
||||
await _tryDownloadArtifacts(
|
||||
crateHash: crateHash,
|
||||
fileName: fileName,
|
||||
signatureFileName: signatureFileName,
|
||||
finalPath: downloadedPath,
|
||||
);
|
||||
}
|
||||
if (File(downloadedPath).existsSync()) {
|
||||
artifactsForTarget.add(Artifact(
|
||||
path: downloadedPath,
|
||||
finalFileName: artifact,
|
||||
));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Only provide complete set of artifacts.
|
||||
if (artifactsForTarget.length == requiredArtifacts.length) {
|
||||
_log.fine('Found precompiled artifacts for $target');
|
||||
res[target] = artifactsForTarget;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static Future<Response> _get(Uri url, {Map<String, String>? headers}) async {
|
||||
int attempt = 0;
|
||||
const maxAttempts = 10;
|
||||
while (true) {
|
||||
try {
|
||||
return await get(url, headers: headers);
|
||||
} on SocketException catch (e) {
|
||||
// Try to detect reset by peer error and retry.
|
||||
if (attempt++ < maxAttempts &&
|
||||
(e.osError?.errorCode == 54 || e.osError?.errorCode == 10054)) {
|
||||
_log.severe(
|
||||
'Failed to download $url: $e, attempt $attempt of $maxAttempts, will retry...');
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
continue;
|
||||
} else {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _tryDownloadArtifacts({
|
||||
required String crateHash,
|
||||
required String fileName,
|
||||
required String signatureFileName,
|
||||
required String finalPath,
|
||||
}) async {
|
||||
final precompiledBinaries = environment.crateOptions.precompiledBinaries!;
|
||||
final prefix = precompiledBinaries.uriPrefix;
|
||||
final url = Uri.parse('$prefix$crateHash/$fileName');
|
||||
final signatureUrl = Uri.parse('$prefix$crateHash/$signatureFileName');
|
||||
_log.fine('Downloading signature from $signatureUrl');
|
||||
final signature = await _get(signatureUrl);
|
||||
if (signature.statusCode == 404) {
|
||||
_log.warning(
|
||||
'Precompiled binaries not available for crate hash $crateHash ($fileName)');
|
||||
return;
|
||||
}
|
||||
if (signature.statusCode != 200) {
|
||||
_log.severe(
|
||||
'Failed to download signature $signatureUrl: status ${signature.statusCode}');
|
||||
return;
|
||||
}
|
||||
_log.fine('Downloading binary from $url');
|
||||
final res = await _get(url);
|
||||
if (res.statusCode != 200) {
|
||||
_log.severe('Failed to download binary $url: status ${res.statusCode}');
|
||||
return;
|
||||
}
|
||||
if (verify(
|
||||
precompiledBinaries.publicKey, res.bodyBytes, signature.bodyBytes)) {
|
||||
File(finalPath).writeAsBytesSync(res.bodyBytes);
|
||||
} else {
|
||||
_log.shout('Signature verification failed! Ignoring binary.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum AritifactType {
|
||||
staticlib,
|
||||
dylib,
|
||||
}
|
||||
|
||||
AritifactType artifactTypeForTarget(Target target) {
|
||||
if (target.darwinPlatform != null) {
|
||||
return AritifactType.staticlib;
|
||||
} else {
|
||||
return AritifactType.dylib;
|
||||
}
|
||||
}
|
||||
|
||||
List<String> getArtifactNames({
|
||||
required Target target,
|
||||
required String libraryName,
|
||||
required bool remote,
|
||||
AritifactType? aritifactType,
|
||||
}) {
|
||||
aritifactType ??= artifactTypeForTarget(target);
|
||||
if (target.darwinArch != null) {
|
||||
if (aritifactType == AritifactType.staticlib) {
|
||||
return ['lib$libraryName.a'];
|
||||
} else {
|
||||
return ['lib$libraryName.dylib'];
|
||||
}
|
||||
} else if (target.rust.contains('-windows-')) {
|
||||
if (aritifactType == AritifactType.staticlib) {
|
||||
return ['$libraryName.lib'];
|
||||
} else {
|
||||
return [
|
||||
'$libraryName.dll',
|
||||
'$libraryName.dll.lib',
|
||||
if (!remote) '$libraryName.pdb'
|
||||
];
|
||||
}
|
||||
} else if (target.rust.contains('-linux-')) {
|
||||
if (aritifactType == AritifactType.staticlib) {
|
||||
return ['lib$libraryName.a'];
|
||||
} else {
|
||||
return ['lib$libraryName.so'];
|
||||
}
|
||||
} else {
|
||||
throw Exception("Unsupported target: ${target.rust}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'artifacts_provider.dart';
|
||||
import 'builder.dart';
|
||||
import 'environment.dart';
|
||||
import 'options.dart';
|
||||
import 'target.dart';
|
||||
|
||||
class BuildCMake {
|
||||
final CargokitUserOptions userOptions;
|
||||
|
||||
BuildCMake({required this.userOptions});
|
||||
|
||||
Future<void> build() async {
|
||||
final targetPlatform = Environment.targetPlatform;
|
||||
final target = Target.forFlutterName(Environment.targetPlatform);
|
||||
if (target == null) {
|
||||
throw Exception("Unknown target platform: $targetPlatform");
|
||||
}
|
||||
|
||||
final environment = BuildEnvironment.fromEnvironment(isAndroid: false);
|
||||
final provider =
|
||||
ArtifactProvider(environment: environment, userOptions: userOptions);
|
||||
final artifacts = await provider.getArtifacts([target]);
|
||||
|
||||
final libs = artifacts[target]!;
|
||||
|
||||
for (final lib in libs) {
|
||||
if (lib.type == AritifactType.dylib) {
|
||||
File(lib.path)
|
||||
.copySync(path.join(Environment.outputDir, lib.finalFileName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'artifacts_provider.dart';
|
||||
import 'builder.dart';
|
||||
import 'environment.dart';
|
||||
import 'options.dart';
|
||||
import 'target.dart';
|
||||
|
||||
final log = Logger('build_gradle');
|
||||
|
||||
class BuildGradle {
|
||||
BuildGradle({required this.userOptions});
|
||||
|
||||
final CargokitUserOptions userOptions;
|
||||
|
||||
Future<void> build() async {
|
||||
final targets = Environment.targetPlatforms.map((arch) {
|
||||
final target = Target.forFlutterName(arch);
|
||||
if (target == null) {
|
||||
throw Exception(
|
||||
"Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}");
|
||||
}
|
||||
return target;
|
||||
}).toList();
|
||||
|
||||
final environment = BuildEnvironment.fromEnvironment(isAndroid: true);
|
||||
final provider =
|
||||
ArtifactProvider(environment: environment, userOptions: userOptions);
|
||||
final artifacts = await provider.getArtifacts(targets);
|
||||
|
||||
for (final target in targets) {
|
||||
final libs = artifacts[target]!;
|
||||
final outputDir = path.join(Environment.outputDir, target.android!);
|
||||
Directory(outputDir).createSync(recursive: true);
|
||||
|
||||
for (final lib in libs) {
|
||||
if (lib.type == AritifactType.dylib) {
|
||||
File(lib.path).copySync(path.join(outputDir, lib.finalFileName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'artifacts_provider.dart';
|
||||
import 'builder.dart';
|
||||
import 'environment.dart';
|
||||
import 'options.dart';
|
||||
import 'target.dart';
|
||||
import 'util.dart';
|
||||
|
||||
class BuildPod {
|
||||
BuildPod({required this.userOptions});
|
||||
|
||||
final CargokitUserOptions userOptions;
|
||||
|
||||
Future<void> build() async {
|
||||
final targets = Environment.darwinArchs.map((arch) {
|
||||
final target = Target.forDarwin(
|
||||
platformName: Environment.darwinPlatformName, darwinAarch: arch);
|
||||
if (target == null) {
|
||||
throw Exception(
|
||||
"Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}");
|
||||
}
|
||||
return target;
|
||||
}).toList();
|
||||
|
||||
final environment = BuildEnvironment.fromEnvironment(isAndroid: false);
|
||||
final provider =
|
||||
ArtifactProvider(environment: environment, userOptions: userOptions);
|
||||
final artifacts = await provider.getArtifacts(targets);
|
||||
|
||||
void performLipo(String targetFile, Iterable<String> sourceFiles) {
|
||||
runCommand("lipo", [
|
||||
'-create',
|
||||
...sourceFiles,
|
||||
'-output',
|
||||
targetFile,
|
||||
]);
|
||||
}
|
||||
|
||||
final outputDir = Environment.outputDir;
|
||||
|
||||
Directory(outputDir).createSync(recursive: true);
|
||||
|
||||
final staticLibs = artifacts.values
|
||||
.expand((element) => element)
|
||||
.where((element) => element.type == AritifactType.staticlib)
|
||||
.toList();
|
||||
final dynamicLibs = artifacts.values
|
||||
.expand((element) => element)
|
||||
.where((element) => element.type == AritifactType.dylib)
|
||||
.toList();
|
||||
|
||||
final libName = environment.crateInfo.packageName;
|
||||
|
||||
// If there is static lib, use it and link it with pod
|
||||
if (staticLibs.isNotEmpty) {
|
||||
final finalTargetFile = path.join(outputDir, "lib$libName.a");
|
||||
performLipo(finalTargetFile, staticLibs.map((e) => e.path));
|
||||
} else {
|
||||
// Otherwise try to replace bundle dylib with our dylib
|
||||
final bundlePaths = [
|
||||
'$libName.framework/Versions/A/$libName',
|
||||
'$libName.framework/$libName',
|
||||
];
|
||||
|
||||
for (final bundlePath in bundlePaths) {
|
||||
final targetFile = path.join(outputDir, bundlePath);
|
||||
if (File(targetFile).existsSync()) {
|
||||
performLipo(targetFile, dynamicLibs.map((e) => e.path));
|
||||
|
||||
// Replace absolute id with @rpath one so that it works properly
|
||||
// when moved to Frameworks.
|
||||
runCommand("install_name_tool", [
|
||||
'-id',
|
||||
'@rpath/$bundlePath',
|
||||
targetFile,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw Exception('Unable to find bundle for dynamic library');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:ed25519_edwards/ed25519_edwards.dart';
|
||||
import 'package:github/github.dart';
|
||||
import 'package:hex/hex.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import 'android_environment.dart';
|
||||
import 'build_cmake.dart';
|
||||
import 'build_gradle.dart';
|
||||
import 'build_pod.dart';
|
||||
import 'logging.dart';
|
||||
import 'options.dart';
|
||||
import 'precompile_binaries.dart';
|
||||
import 'target.dart';
|
||||
import 'util.dart';
|
||||
import 'verify_binaries.dart';
|
||||
|
||||
final log = Logger('build_tool');
|
||||
|
||||
abstract class BuildCommand extends Command {
|
||||
Future<void> runBuildCommand(CargokitUserOptions options);
|
||||
|
||||
@override
|
||||
Future<void> run() async {
|
||||
final options = CargokitUserOptions.load();
|
||||
|
||||
if (options.verboseLogging ||
|
||||
Platform.environment['CARGOKIT_VERBOSE'] == '1') {
|
||||
enableVerboseLogging();
|
||||
}
|
||||
|
||||
await runBuildCommand(options);
|
||||
}
|
||||
}
|
||||
|
||||
class BuildPodCommand extends BuildCommand {
|
||||
@override
|
||||
final name = 'build-pod';
|
||||
|
||||
@override
|
||||
final description = 'Build cocoa pod library';
|
||||
|
||||
@override
|
||||
Future<void> runBuildCommand(CargokitUserOptions options) async {
|
||||
final build = BuildPod(userOptions: options);
|
||||
await build.build();
|
||||
}
|
||||
}
|
||||
|
||||
class BuildGradleCommand extends BuildCommand {
|
||||
@override
|
||||
final name = 'build-gradle';
|
||||
|
||||
@override
|
||||
final description = 'Build android library';
|
||||
|
||||
@override
|
||||
Future<void> runBuildCommand(CargokitUserOptions options) async {
|
||||
final build = BuildGradle(userOptions: options);
|
||||
await build.build();
|
||||
}
|
||||
}
|
||||
|
||||
class BuildCMakeCommand extends BuildCommand {
|
||||
@override
|
||||
final name = 'build-cmake';
|
||||
|
||||
@override
|
||||
final description = 'Build CMake library';
|
||||
|
||||
@override
|
||||
Future<void> runBuildCommand(CargokitUserOptions options) async {
|
||||
final build = BuildCMake(userOptions: options);
|
||||
await build.build();
|
||||
}
|
||||
}
|
||||
|
||||
class GenKeyCommand extends Command {
|
||||
@override
|
||||
final name = 'gen-key';
|
||||
|
||||
@override
|
||||
final description = 'Generate key pair for signing precompiled binaries';
|
||||
|
||||
@override
|
||||
void run() {
|
||||
final kp = generateKey();
|
||||
final private = HEX.encode(kp.privateKey.bytes);
|
||||
final public = HEX.encode(kp.publicKey.bytes);
|
||||
print("Private Key: $private");
|
||||
print("Public Key: $public");
|
||||
}
|
||||
}
|
||||
|
||||
class PrecompileBinariesCommand extends Command {
|
||||
PrecompileBinariesCommand() {
|
||||
argParser
|
||||
..addOption(
|
||||
'repository',
|
||||
mandatory: true,
|
||||
help: 'Github repository slug in format owner/name',
|
||||
)
|
||||
..addOption(
|
||||
'manifest-dir',
|
||||
mandatory: true,
|
||||
help: 'Directory containing Cargo.toml',
|
||||
)
|
||||
..addMultiOption('target',
|
||||
help: 'Rust target triple of artifact to build.\n'
|
||||
'Can be specified multiple times or omitted in which case\n'
|
||||
'all targets for current platform will be built.')
|
||||
..addOption(
|
||||
'android-sdk-location',
|
||||
help: 'Location of Android SDK (if available)',
|
||||
)
|
||||
..addOption(
|
||||
'android-ndk-version',
|
||||
help: 'Android NDK version (if available)',
|
||||
)
|
||||
..addOption(
|
||||
'android-min-sdk-version',
|
||||
help: 'Android minimum rquired version (if available)',
|
||||
)
|
||||
..addOption(
|
||||
'temp-dir',
|
||||
help: 'Directory to store temporary build artifacts',
|
||||
)
|
||||
..addFlag(
|
||||
"verbose",
|
||||
abbr: "v",
|
||||
defaultsTo: false,
|
||||
help: "Enable verbose logging",
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
final name = 'precompile-binaries';
|
||||
|
||||
@override
|
||||
final description = 'Prebuild and upload binaries\n'
|
||||
'Private key must be passed through PRIVATE_KEY environment variable. '
|
||||
'Use gen_key through generate priave key.\n'
|
||||
'Github token must be passed as GITHUB_TOKEN environment variable.\n';
|
||||
|
||||
@override
|
||||
Future<void> run() async {
|
||||
final verbose = argResults!['verbose'] as bool;
|
||||
if (verbose) {
|
||||
enableVerboseLogging();
|
||||
}
|
||||
|
||||
final privateKeyString = Platform.environment['PRIVATE_KEY'];
|
||||
if (privateKeyString == null) {
|
||||
throw ArgumentError('Missing PRIVATE_KEY environment variable');
|
||||
}
|
||||
final githubToken = Platform.environment['GITHUB_TOKEN'];
|
||||
if (githubToken == null) {
|
||||
throw ArgumentError('Missing GITHUB_TOKEN environment variable');
|
||||
}
|
||||
final privateKey = HEX.decode(privateKeyString);
|
||||
if (privateKey.length != 64) {
|
||||
throw ArgumentError('Private key must be 64 bytes long');
|
||||
}
|
||||
final manifestDir = argResults!['manifest-dir'] as String;
|
||||
if (!Directory(manifestDir).existsSync()) {
|
||||
throw ArgumentError('Manifest directory does not exist: $manifestDir');
|
||||
}
|
||||
String? androidMinSdkVersionString =
|
||||
argResults!['android-min-sdk-version'] as String?;
|
||||
int? androidMinSdkVersion;
|
||||
if (androidMinSdkVersionString != null) {
|
||||
androidMinSdkVersion = int.tryParse(androidMinSdkVersionString);
|
||||
if (androidMinSdkVersion == null) {
|
||||
throw ArgumentError(
|
||||
'Invalid android-min-sdk-version: $androidMinSdkVersionString');
|
||||
}
|
||||
}
|
||||
final targetStrigns = argResults!['target'] as List<String>;
|
||||
final targets = targetStrigns.map((target) {
|
||||
final res = Target.forRustTriple(target);
|
||||
if (res == null) {
|
||||
throw ArgumentError('Invalid target: $target');
|
||||
}
|
||||
return res;
|
||||
}).toList(growable: false);
|
||||
final precompileBinaries = PrecompileBinaries(
|
||||
privateKey: PrivateKey(privateKey),
|
||||
githubToken: githubToken,
|
||||
manifestDir: manifestDir,
|
||||
repositorySlug: RepositorySlug.full(argResults!['repository'] as String),
|
||||
targets: targets,
|
||||
androidSdkLocation: argResults!['android-sdk-location'] as String?,
|
||||
androidNdkVersion: argResults!['android-ndk-version'] as String?,
|
||||
androidMinSdkVersion: androidMinSdkVersion,
|
||||
tempDir: argResults!['temp-dir'] as String?,
|
||||
);
|
||||
|
||||
await precompileBinaries.run();
|
||||
}
|
||||
}
|
||||
|
||||
class VerifyBinariesCommand extends Command {
|
||||
VerifyBinariesCommand() {
|
||||
argParser.addOption(
|
||||
'manifest-dir',
|
||||
mandatory: true,
|
||||
help: 'Directory containing Cargo.toml',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
final name = "verify-binaries";
|
||||
|
||||
@override
|
||||
final description = 'Verifies published binaries\n'
|
||||
'Checks whether there is a binary published for each targets\n'
|
||||
'and checks the signature.';
|
||||
|
||||
@override
|
||||
Future<void> run() async {
|
||||
final manifestDir = argResults!['manifest-dir'] as String;
|
||||
final verifyBinaries = VerifyBinaries(
|
||||
manifestDir: manifestDir,
|
||||
);
|
||||
await verifyBinaries.run();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> runMain(List<String> args) async {
|
||||
try {
|
||||
// Init logging before options are loaded
|
||||
initLogging();
|
||||
|
||||
if (Platform.environment['_CARGOKIT_NDK_LINK_TARGET'] != null) {
|
||||
return AndroidEnvironment.clangLinkerWrapper(args);
|
||||
}
|
||||
|
||||
final runner = CommandRunner('build_tool', 'Cargokit built_tool')
|
||||
..addCommand(BuildPodCommand())
|
||||
..addCommand(BuildGradleCommand())
|
||||
..addCommand(BuildCMakeCommand())
|
||||
..addCommand(GenKeyCommand())
|
||||
..addCommand(PrecompileBinariesCommand())
|
||||
..addCommand(VerifyBinariesCommand());
|
||||
|
||||
await runner.run(args);
|
||||
} on ArgumentError catch (e) {
|
||||
stderr.writeln(e.toString());
|
||||
exit(1);
|
||||
} catch (e, s) {
|
||||
log.severe(kDoubleSeparator);
|
||||
log.severe('Cargokit BuildTool failed with error:');
|
||||
log.severe(kSeparator);
|
||||
log.severe(e);
|
||||
// This tells user to install Rust, there's no need to pollute the log with
|
||||
// stack trace.
|
||||
if (e is! RustupNotFoundException) {
|
||||
log.severe(kSeparator);
|
||||
log.severe(s);
|
||||
log.severe(kSeparator);
|
||||
log.severe('BuildTool arguments: $args');
|
||||
}
|
||||
log.severe(kDoubleSeparator);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'android_environment.dart';
|
||||
import 'cargo.dart';
|
||||
import 'environment.dart';
|
||||
import 'options.dart';
|
||||
import 'rustup.dart';
|
||||
import 'target.dart';
|
||||
import 'util.dart';
|
||||
|
||||
final _log = Logger('builder');
|
||||
|
||||
enum BuildConfiguration {
|
||||
debug,
|
||||
release,
|
||||
profile,
|
||||
}
|
||||
|
||||
extension on BuildConfiguration {
|
||||
bool get isDebug => this == BuildConfiguration.debug;
|
||||
String get rustName => switch (this) {
|
||||
BuildConfiguration.debug => 'debug',
|
||||
BuildConfiguration.release => 'release',
|
||||
BuildConfiguration.profile => 'release',
|
||||
};
|
||||
}
|
||||
|
||||
class BuildException implements Exception {
|
||||
final String message;
|
||||
|
||||
BuildException(this.message);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'BuildException: $message';
|
||||
}
|
||||
}
|
||||
|
||||
class BuildEnvironment {
|
||||
final BuildConfiguration configuration;
|
||||
final CargokitCrateOptions crateOptions;
|
||||
final String targetTempDir;
|
||||
final String manifestDir;
|
||||
final CrateInfo crateInfo;
|
||||
|
||||
final bool isAndroid;
|
||||
final String? androidSdkPath;
|
||||
final String? androidNdkVersion;
|
||||
final int? androidMinSdkVersion;
|
||||
final String? javaHome;
|
||||
|
||||
BuildEnvironment({
|
||||
required this.configuration,
|
||||
required this.crateOptions,
|
||||
required this.targetTempDir,
|
||||
required this.manifestDir,
|
||||
required this.crateInfo,
|
||||
required this.isAndroid,
|
||||
this.androidSdkPath,
|
||||
this.androidNdkVersion,
|
||||
this.androidMinSdkVersion,
|
||||
this.javaHome,
|
||||
});
|
||||
|
||||
static BuildConfiguration parseBuildConfiguration(String value) {
|
||||
// XCode configuration adds the flavor to configuration name.
|
||||
final firstSegment = value.split('-').first;
|
||||
final buildConfiguration = BuildConfiguration.values.firstWhereOrNull(
|
||||
(e) => e.name == firstSegment,
|
||||
);
|
||||
if (buildConfiguration == null) {
|
||||
_log.warning('Unknown build configuraiton $value, will assume release');
|
||||
return BuildConfiguration.release;
|
||||
}
|
||||
return buildConfiguration;
|
||||
}
|
||||
|
||||
static BuildEnvironment fromEnvironment({
|
||||
required bool isAndroid,
|
||||
}) {
|
||||
final buildConfiguration =
|
||||
parseBuildConfiguration(Environment.configuration);
|
||||
final manifestDir = Environment.manifestDir;
|
||||
final crateOptions = CargokitCrateOptions.load(
|
||||
manifestDir: manifestDir,
|
||||
);
|
||||
final crateInfo = CrateInfo.load(manifestDir);
|
||||
return BuildEnvironment(
|
||||
configuration: buildConfiguration,
|
||||
crateOptions: crateOptions,
|
||||
targetTempDir: Environment.targetTempDir,
|
||||
manifestDir: manifestDir,
|
||||
crateInfo: crateInfo,
|
||||
isAndroid: isAndroid,
|
||||
androidSdkPath: isAndroid ? Environment.sdkPath : null,
|
||||
androidNdkVersion: isAndroid ? Environment.ndkVersion : null,
|
||||
androidMinSdkVersion:
|
||||
isAndroid ? int.parse(Environment.minSdkVersion) : null,
|
||||
javaHome: isAndroid ? Environment.javaHome : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RustBuilder {
|
||||
final Target target;
|
||||
final BuildEnvironment environment;
|
||||
|
||||
RustBuilder({
|
||||
required this.target,
|
||||
required this.environment,
|
||||
});
|
||||
|
||||
void prepare(
|
||||
Rustup rustup,
|
||||
) {
|
||||
final toolchain = _toolchain;
|
||||
if (rustup.installedTargets(toolchain) == null) {
|
||||
rustup.installToolchain(toolchain);
|
||||
}
|
||||
if (toolchain == 'nightly') {
|
||||
rustup.installRustSrcForNightly();
|
||||
}
|
||||
if (!rustup.installedTargets(toolchain)!.contains(target.rust)) {
|
||||
rustup.installTarget(target.rust, toolchain: toolchain);
|
||||
}
|
||||
}
|
||||
|
||||
CargoBuildOptions? get _buildOptions =>
|
||||
environment.crateOptions.cargo[environment.configuration];
|
||||
|
||||
String get _toolchain => _buildOptions?.toolchain.name ?? 'stable';
|
||||
|
||||
/// Returns the path of directory containing build artifacts.
|
||||
Future<String> build() async {
|
||||
final extraArgs = _buildOptions?.flags ?? [];
|
||||
final manifestPath = path.join(environment.manifestDir, 'Cargo.toml');
|
||||
runCommand(
|
||||
'rustup',
|
||||
[
|
||||
'run',
|
||||
_toolchain,
|
||||
'cargo',
|
||||
'build',
|
||||
...extraArgs,
|
||||
'--manifest-path',
|
||||
manifestPath,
|
||||
'-p',
|
||||
environment.crateInfo.packageName,
|
||||
if (!environment.configuration.isDebug) '--release',
|
||||
'--target',
|
||||
target.rust,
|
||||
'--target-dir',
|
||||
environment.targetTempDir,
|
||||
],
|
||||
environment: await _buildEnvironment(),
|
||||
);
|
||||
return path.join(
|
||||
environment.targetTempDir,
|
||||
target.rust,
|
||||
environment.configuration.rustName,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, String>> _buildEnvironment() async {
|
||||
if (target.android == null) {
|
||||
return {};
|
||||
} else {
|
||||
final sdkPath = environment.androidSdkPath;
|
||||
final ndkVersion = environment.androidNdkVersion;
|
||||
final minSdkVersion = environment.androidMinSdkVersion;
|
||||
if (sdkPath == null) {
|
||||
throw BuildException('androidSdkPath is not set');
|
||||
}
|
||||
if (ndkVersion == null) {
|
||||
throw BuildException('androidNdkVersion is not set');
|
||||
}
|
||||
if (minSdkVersion == null) {
|
||||
throw BuildException('androidMinSdkVersion is not set');
|
||||
}
|
||||
final env = AndroidEnvironment(
|
||||
sdkPath: sdkPath,
|
||||
ndkVersion: ndkVersion,
|
||||
minSdkVersion: minSdkVersion,
|
||||
targetTempDir: environment.targetTempDir,
|
||||
target: target,
|
||||
);
|
||||
if (!env.ndkIsInstalled() && environment.javaHome != null) {
|
||||
env.installNdk(javaHome: environment.javaHome!);
|
||||
}
|
||||
return env.buildEnvironment();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:toml/toml.dart';
|
||||
|
||||
class ManifestException {
|
||||
ManifestException(this.message, {required this.fileName});
|
||||
|
||||
final String? fileName;
|
||||
final String message;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (fileName != null) {
|
||||
return 'Failed to parse package manifest at $fileName: $message';
|
||||
} else {
|
||||
return 'Failed to parse package manifest: $message';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CrateInfo {
|
||||
CrateInfo({required this.packageName});
|
||||
|
||||
final String packageName;
|
||||
|
||||
static CrateInfo parseManifest(String manifest, {final String? fileName}) {
|
||||
final toml = TomlDocument.parse(manifest);
|
||||
final package = toml.toMap()['package'];
|
||||
if (package == null) {
|
||||
throw ManifestException('Missing package section', fileName: fileName);
|
||||
}
|
||||
final name = package['name'];
|
||||
if (name == null) {
|
||||
throw ManifestException('Missing package name', fileName: fileName);
|
||||
}
|
||||
return CrateInfo(packageName: name);
|
||||
}
|
||||
|
||||
static CrateInfo load(String manifestDir) {
|
||||
final manifestFile = File(path.join(manifestDir, 'Cargo.toml'));
|
||||
final manifest = manifestFile.readAsStringSync();
|
||||
return parseManifest(manifest, fileName: manifestFile.path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:convert/convert.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
class CrateHash {
|
||||
/// Computes a hash uniquely identifying crate content. This takes into account
|
||||
/// content all all .rs files inside the src directory, as well as Cargo.toml,
|
||||
/// Cargo.lock, build.rs and cargokit.yaml.
|
||||
///
|
||||
/// If [tempStorage] is provided, computed hash is stored in a file in that directory
|
||||
/// and reused on subsequent calls if the crate content hasn't changed.
|
||||
static String compute(String manifestDir, {String? tempStorage}) {
|
||||
return CrateHash._(
|
||||
manifestDir: manifestDir,
|
||||
tempStorage: tempStorage,
|
||||
)._compute();
|
||||
}
|
||||
|
||||
CrateHash._({
|
||||
required this.manifestDir,
|
||||
required this.tempStorage,
|
||||
});
|
||||
|
||||
String _compute() {
|
||||
final files = getFiles();
|
||||
final tempStorage = this.tempStorage;
|
||||
if (tempStorage != null) {
|
||||
final quickHash = _computeQuickHash(files);
|
||||
final quickHashFolder = Directory(path.join(tempStorage, 'crate_hash'));
|
||||
quickHashFolder.createSync(recursive: true);
|
||||
final quickHashFile = File(path.join(quickHashFolder.path, quickHash));
|
||||
if (quickHashFile.existsSync()) {
|
||||
return quickHashFile.readAsStringSync();
|
||||
}
|
||||
final hash = _computeHash(files);
|
||||
quickHashFile.writeAsStringSync(hash);
|
||||
return hash;
|
||||
} else {
|
||||
return _computeHash(files);
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes a quick hash based on files stat (without reading contents). This
|
||||
/// is used to cache the real hash, which is slower to compute since it involves
|
||||
/// reading every single file.
|
||||
String _computeQuickHash(List<File> files) {
|
||||
final output = AccumulatorSink<Digest>();
|
||||
final input = sha256.startChunkedConversion(output);
|
||||
|
||||
final data = ByteData(8);
|
||||
for (final file in files) {
|
||||
input.add(utf8.encode(file.path));
|
||||
final stat = file.statSync();
|
||||
data.setUint64(0, stat.size);
|
||||
input.add(data.buffer.asUint8List());
|
||||
data.setUint64(0, stat.modified.millisecondsSinceEpoch);
|
||||
input.add(data.buffer.asUint8List());
|
||||
}
|
||||
|
||||
input.close();
|
||||
return base64Url.encode(output.events.single.bytes);
|
||||
}
|
||||
|
||||
String _computeHash(List<File> files) {
|
||||
final output = AccumulatorSink<Digest>();
|
||||
final input = sha256.startChunkedConversion(output);
|
||||
|
||||
void addTextFile(File file) {
|
||||
// text Files are hashed by lines in case we're dealing with github checkout
|
||||
// that auto-converts line endings.
|
||||
final splitter = LineSplitter();
|
||||
if (file.existsSync()) {
|
||||
final data = file.readAsStringSync();
|
||||
final lines = splitter.convert(data);
|
||||
for (final line in lines) {
|
||||
input.add(utf8.encode(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (final file in files) {
|
||||
addTextFile(file);
|
||||
}
|
||||
|
||||
input.close();
|
||||
final res = output.events.single;
|
||||
|
||||
// Truncate to 128bits.
|
||||
final hash = res.bytes.sublist(0, 16);
|
||||
return hex.encode(hash);
|
||||
}
|
||||
|
||||
List<File> getFiles() {
|
||||
final src = Directory(path.join(manifestDir, 'src'));
|
||||
final files = src
|
||||
.listSync(recursive: true, followLinks: false)
|
||||
.whereType<File>()
|
||||
.toList();
|
||||
files.sortBy((element) => element.path);
|
||||
void addFile(String relative) {
|
||||
final file = File(path.join(manifestDir, relative));
|
||||
if (file.existsSync()) {
|
||||
files.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
addFile('Cargo.toml');
|
||||
addFile('Cargo.lock');
|
||||
addFile('build.rs');
|
||||
addFile('cargokit.yaml');
|
||||
return files;
|
||||
}
|
||||
|
||||
final String manifestDir;
|
||||
final String? tempStorage;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
extension on String {
|
||||
String resolveSymlink() => File(this).resolveSymbolicLinksSync();
|
||||
}
|
||||
|
||||
class Environment {
|
||||
/// Current build configuration (debug or release).
|
||||
static String get configuration =>
|
||||
_getEnv("CARGOKIT_CONFIGURATION").toLowerCase();
|
||||
|
||||
static bool get isDebug => configuration == 'debug';
|
||||
static bool get isRelease => configuration == 'release';
|
||||
|
||||
/// Temporary directory where Rust build artifacts are placed.
|
||||
static String get targetTempDir => _getEnv("CARGOKIT_TARGET_TEMP_DIR");
|
||||
|
||||
/// Final output directory where the build artifacts are placed.
|
||||
static String get outputDir => _getEnvPath('CARGOKIT_OUTPUT_DIR');
|
||||
|
||||
/// Path to the crate manifest (containing Cargo.toml).
|
||||
static String get manifestDir => _getEnvPath('CARGOKIT_MANIFEST_DIR');
|
||||
|
||||
/// Directory inside root project. Not necessarily root folder. Symlinks are
|
||||
/// not resolved on purpose.
|
||||
static String get rootProjectDir => _getEnv('CARGOKIT_ROOT_PROJECT_DIR');
|
||||
|
||||
// Pod
|
||||
|
||||
/// Platform name (macosx, iphoneos, iphonesimulator).
|
||||
static String get darwinPlatformName =>
|
||||
_getEnv("CARGOKIT_DARWIN_PLATFORM_NAME");
|
||||
|
||||
/// List of architectures to build for (arm64, armv7, x86_64).
|
||||
static List<String> get darwinArchs =>
|
||||
_getEnv("CARGOKIT_DARWIN_ARCHS").split(' ');
|
||||
|
||||
// Gradle
|
||||
static String get minSdkVersion => _getEnv("CARGOKIT_MIN_SDK_VERSION");
|
||||
static String get ndkVersion => _getEnv("CARGOKIT_NDK_VERSION");
|
||||
static String get sdkPath => _getEnvPath("CARGOKIT_SDK_DIR");
|
||||
static String get javaHome => _getEnvPath("CARGOKIT_JAVA_HOME");
|
||||
static List<String> get targetPlatforms =>
|
||||
_getEnv("CARGOKIT_TARGET_PLATFORMS").split(',');
|
||||
|
||||
// CMAKE
|
||||
static String get targetPlatform => _getEnv("CARGOKIT_TARGET_PLATFORM");
|
||||
|
||||
static String _getEnv(String key) {
|
||||
final res = Platform.environment[key];
|
||||
if (res == null) {
|
||||
throw Exception("Missing environment variable $key");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static String _getEnvPath(String key) {
|
||||
final res = _getEnv(key);
|
||||
if (Directory(res).existsSync()) {
|
||||
return res.resolveSymlink();
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
const String kSeparator = "--";
|
||||
const String kDoubleSeparator = "==";
|
||||
|
||||
bool _lastMessageWasSeparator = false;
|
||||
|
||||
void _log(LogRecord rec) {
|
||||
final prefix = '${rec.level.name}: ';
|
||||
final out = rec.level == Level.SEVERE ? stderr : stdout;
|
||||
if (rec.message == kSeparator) {
|
||||
if (!_lastMessageWasSeparator) {
|
||||
out.write(prefix);
|
||||
out.writeln('-' * 80);
|
||||
_lastMessageWasSeparator = true;
|
||||
}
|
||||
return;
|
||||
} else if (rec.message == kDoubleSeparator) {
|
||||
out.write(prefix);
|
||||
out.writeln('=' * 80);
|
||||
_lastMessageWasSeparator = true;
|
||||
return;
|
||||
}
|
||||
out.write(prefix);
|
||||
out.writeln(rec.message);
|
||||
_lastMessageWasSeparator = false;
|
||||
}
|
||||
|
||||
void initLogging() {
|
||||
Logger.root.level = Level.INFO;
|
||||
Logger.root.onRecord.listen((LogRecord rec) {
|
||||
final lines = rec.message.split('\n');
|
||||
for (final line in lines) {
|
||||
if (line.isNotEmpty || lines.length == 1 || line != lines.last) {
|
||||
_log(LogRecord(
|
||||
rec.level,
|
||||
line,
|
||||
rec.loggerName,
|
||||
));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void enableVerboseLogging() {
|
||||
Logger.root.level = Level.ALL;
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:ed25519_edwards/ed25519_edwards.dart';
|
||||
import 'package:hex/hex.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:source_span/source_span.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
import 'builder.dart';
|
||||
import 'environment.dart';
|
||||
import 'rustup.dart';
|
||||
|
||||
final _log = Logger('options');
|
||||
|
||||
/// A class for exceptions that have source span information attached.
|
||||
class SourceSpanException implements Exception {
|
||||
// This is a getter so that subclasses can override it.
|
||||
/// A message describing the exception.
|
||||
String get message => _message;
|
||||
final String _message;
|
||||
|
||||
// This is a getter so that subclasses can override it.
|
||||
/// The span associated with this exception.
|
||||
///
|
||||
/// This may be `null` if the source location can't be determined.
|
||||
SourceSpan? get span => _span;
|
||||
final SourceSpan? _span;
|
||||
|
||||
SourceSpanException(this._message, this._span);
|
||||
|
||||
/// Returns a string representation of `this`.
|
||||
///
|
||||
/// [color] may either be a [String], a [bool], or `null`. If it's a string,
|
||||
/// it indicates an ANSI terminal color escape that should be used to
|
||||
/// highlight the span's text. If it's `true`, it indicates that the text
|
||||
/// should be highlighted using the default color. If it's `false` or `null`,
|
||||
/// it indicates that the text shouldn't be highlighted.
|
||||
@override
|
||||
String toString({Object? color}) {
|
||||
if (span == null) return message;
|
||||
return 'Error on ${span!.message(message, color: color)}';
|
||||
}
|
||||
}
|
||||
|
||||
enum Toolchain {
|
||||
stable,
|
||||
beta,
|
||||
nightly,
|
||||
}
|
||||
|
||||
class CargoBuildOptions {
|
||||
final Toolchain toolchain;
|
||||
final List<String> flags;
|
||||
|
||||
CargoBuildOptions({
|
||||
required this.toolchain,
|
||||
required this.flags,
|
||||
});
|
||||
|
||||
static Toolchain _toolchainFromNode(YamlNode node) {
|
||||
if (node case YamlScalar(value: String name)) {
|
||||
final toolchain =
|
||||
Toolchain.values.firstWhereOrNull((element) => element.name == name);
|
||||
if (toolchain != null) {
|
||||
return toolchain;
|
||||
}
|
||||
}
|
||||
throw SourceSpanException(
|
||||
'Unknown toolchain. Must be one of ${Toolchain.values.map((e) => e.name)}.',
|
||||
node.span);
|
||||
}
|
||||
|
||||
static CargoBuildOptions parse(YamlNode node) {
|
||||
if (node is! YamlMap) {
|
||||
throw SourceSpanException('Cargo options must be a map', node.span);
|
||||
}
|
||||
Toolchain toolchain = Toolchain.stable;
|
||||
List<String> flags = [];
|
||||
for (final MapEntry(:key, :value) in node.nodes.entries) {
|
||||
if (key case YamlScalar(value: 'toolchain')) {
|
||||
toolchain = _toolchainFromNode(value);
|
||||
} else if (key case YamlScalar(value: 'extra_flags')) {
|
||||
if (value case YamlList(nodes: List<YamlNode> list)) {
|
||||
if (list.every((element) {
|
||||
if (element case YamlScalar(value: String _)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})) {
|
||||
flags = list.map((e) => e.value as String).toList();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
throw SourceSpanException(
|
||||
'Extra flags must be a list of strings', value.span);
|
||||
} else {
|
||||
throw SourceSpanException(
|
||||
'Unknown cargo option type. Must be "toolchain" or "extra_flags".',
|
||||
key.span);
|
||||
}
|
||||
}
|
||||
return CargoBuildOptions(toolchain: toolchain, flags: flags);
|
||||
}
|
||||
}
|
||||
|
||||
extension on YamlMap {
|
||||
/// Map that extracts keys so that we can do map case check on them.
|
||||
Map<dynamic, YamlNode> get valueMap =>
|
||||
nodes.map((key, value) => MapEntry(key.value, value));
|
||||
}
|
||||
|
||||
class PrecompiledBinaries {
|
||||
final String uriPrefix;
|
||||
final PublicKey publicKey;
|
||||
|
||||
PrecompiledBinaries({
|
||||
required this.uriPrefix,
|
||||
required this.publicKey,
|
||||
});
|
||||
|
||||
static PublicKey _publicKeyFromHex(String key, SourceSpan? span) {
|
||||
final bytes = HEX.decode(key);
|
||||
if (bytes.length != 32) {
|
||||
throw SourceSpanException(
|
||||
'Invalid public key. Must be 32 bytes long.', span);
|
||||
}
|
||||
return PublicKey(bytes);
|
||||
}
|
||||
|
||||
static PrecompiledBinaries parse(YamlNode node) {
|
||||
if (node case YamlMap(valueMap: Map<dynamic, YamlNode> map)) {
|
||||
if (map
|
||||
case {
|
||||
'url_prefix': YamlNode urlPrefixNode,
|
||||
'public_key': YamlNode publicKeyNode,
|
||||
}) {
|
||||
final urlPrefix = switch (urlPrefixNode) {
|
||||
YamlScalar(value: String urlPrefix) => urlPrefix,
|
||||
_ => throw SourceSpanException(
|
||||
'Invalid URL prefix value.', urlPrefixNode.span),
|
||||
};
|
||||
final publicKey = switch (publicKeyNode) {
|
||||
YamlScalar(value: String publicKey) =>
|
||||
_publicKeyFromHex(publicKey, publicKeyNode.span),
|
||||
_ => throw SourceSpanException(
|
||||
'Invalid public key value.', publicKeyNode.span),
|
||||
};
|
||||
return PrecompiledBinaries(
|
||||
uriPrefix: urlPrefix,
|
||||
publicKey: publicKey,
|
||||
);
|
||||
}
|
||||
}
|
||||
throw SourceSpanException(
|
||||
'Invalid precompiled binaries value. '
|
||||
'Expected Map with "url_prefix" and "public_key".',
|
||||
node.span);
|
||||
}
|
||||
}
|
||||
|
||||
/// Cargokit options specified for Rust crate.
|
||||
class CargokitCrateOptions {
|
||||
CargokitCrateOptions({
|
||||
this.cargo = const {},
|
||||
this.precompiledBinaries,
|
||||
});
|
||||
|
||||
final Map<BuildConfiguration, CargoBuildOptions> cargo;
|
||||
final PrecompiledBinaries? precompiledBinaries;
|
||||
|
||||
static CargokitCrateOptions parse(YamlNode node) {
|
||||
if (node is! YamlMap) {
|
||||
throw SourceSpanException('Cargokit options must be a map', node.span);
|
||||
}
|
||||
final options = <BuildConfiguration, CargoBuildOptions>{};
|
||||
PrecompiledBinaries? precompiledBinaries;
|
||||
|
||||
for (final entry in node.nodes.entries) {
|
||||
if (entry
|
||||
case MapEntry(
|
||||
key: YamlScalar(value: 'cargo'),
|
||||
value: YamlNode node,
|
||||
)) {
|
||||
if (node is! YamlMap) {
|
||||
throw SourceSpanException('Cargo options must be a map', node.span);
|
||||
}
|
||||
for (final MapEntry(:YamlNode key, :value) in node.nodes.entries) {
|
||||
if (key case YamlScalar(value: String name)) {
|
||||
final configuration = BuildConfiguration.values
|
||||
.firstWhereOrNull((element) => element.name == name);
|
||||
if (configuration != null) {
|
||||
options[configuration] = CargoBuildOptions.parse(value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
throw SourceSpanException(
|
||||
'Unknown build configuration. Must be one of ${BuildConfiguration.values.map((e) => e.name)}.',
|
||||
key.span);
|
||||
}
|
||||
} else if (entry.key case YamlScalar(value: 'precompiled_binaries')) {
|
||||
precompiledBinaries = PrecompiledBinaries.parse(entry.value);
|
||||
} else {
|
||||
throw SourceSpanException(
|
||||
'Unknown cargokit option type. Must be "cargo" or "precompiled_binaries".',
|
||||
entry.key.span);
|
||||
}
|
||||
}
|
||||
return CargokitCrateOptions(
|
||||
cargo: options,
|
||||
precompiledBinaries: precompiledBinaries,
|
||||
);
|
||||
}
|
||||
|
||||
static CargokitCrateOptions load({
|
||||
required String manifestDir,
|
||||
}) {
|
||||
final uri = Uri.file(path.join(manifestDir, "cargokit.yaml"));
|
||||
final file = File.fromUri(uri);
|
||||
if (file.existsSync()) {
|
||||
final contents = loadYamlNode(file.readAsStringSync(), sourceUrl: uri);
|
||||
return parse(contents);
|
||||
} else {
|
||||
return CargokitCrateOptions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CargokitUserOptions {
|
||||
// When Rustup is installed always build locally unless user opts into
|
||||
// using precompiled binaries.
|
||||
static bool defaultUsePrecompiledBinaries() {
|
||||
return Rustup.executablePath() == null;
|
||||
}
|
||||
|
||||
CargokitUserOptions({
|
||||
required this.usePrecompiledBinaries,
|
||||
required this.verboseLogging,
|
||||
});
|
||||
|
||||
CargokitUserOptions._()
|
||||
: usePrecompiledBinaries = defaultUsePrecompiledBinaries(),
|
||||
verboseLogging = false;
|
||||
|
||||
static CargokitUserOptions parse(YamlNode node) {
|
||||
if (node is! YamlMap) {
|
||||
throw SourceSpanException('Cargokit options must be a map', node.span);
|
||||
}
|
||||
bool usePrecompiledBinaries = defaultUsePrecompiledBinaries();
|
||||
bool verboseLogging = false;
|
||||
|
||||
for (final entry in node.nodes.entries) {
|
||||
if (entry.key case YamlScalar(value: 'use_precompiled_binaries')) {
|
||||
if (entry.value case YamlScalar(value: bool value)) {
|
||||
usePrecompiledBinaries = value;
|
||||
continue;
|
||||
}
|
||||
throw SourceSpanException(
|
||||
'Invalid value for "use_precompiled_binaries". Must be a boolean.',
|
||||
entry.value.span);
|
||||
} else if (entry.key case YamlScalar(value: 'verbose_logging')) {
|
||||
if (entry.value case YamlScalar(value: bool value)) {
|
||||
verboseLogging = value;
|
||||
continue;
|
||||
}
|
||||
throw SourceSpanException(
|
||||
'Invalid value for "verbose_logging". Must be a boolean.',
|
||||
entry.value.span);
|
||||
} else {
|
||||
throw SourceSpanException(
|
||||
'Unknown cargokit option type. Must be "use_precompiled_binaries" or "verbose_logging".',
|
||||
entry.key.span);
|
||||
}
|
||||
}
|
||||
return CargokitUserOptions(
|
||||
usePrecompiledBinaries: usePrecompiledBinaries,
|
||||
verboseLogging: verboseLogging,
|
||||
);
|
||||
}
|
||||
|
||||
static CargokitUserOptions load() {
|
||||
String fileName = "cargokit_options.yaml";
|
||||
var userProjectDir = Directory(Environment.rootProjectDir);
|
||||
|
||||
while (userProjectDir.parent.path != userProjectDir.path) {
|
||||
final configFile = File(path.join(userProjectDir.path, fileName));
|
||||
if (configFile.existsSync()) {
|
||||
final contents = loadYamlNode(
|
||||
configFile.readAsStringSync(),
|
||||
sourceUrl: configFile.uri,
|
||||
);
|
||||
final res = parse(contents);
|
||||
if (res.verboseLogging) {
|
||||
_log.info('Found user options file at ${configFile.path}');
|
||||
}
|
||||
return res;
|
||||
}
|
||||
userProjectDir = userProjectDir.parent;
|
||||
}
|
||||
return CargokitUserOptions._();
|
||||
}
|
||||
|
||||
final bool usePrecompiledBinaries;
|
||||
final bool verboseLogging;
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:ed25519_edwards/ed25519_edwards.dart';
|
||||
import 'package:github/github.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'artifacts_provider.dart';
|
||||
import 'builder.dart';
|
||||
import 'cargo.dart';
|
||||
import 'crate_hash.dart';
|
||||
import 'options.dart';
|
||||
import 'rustup.dart';
|
||||
import 'target.dart';
|
||||
|
||||
final _log = Logger('precompile_binaries');
|
||||
|
||||
class PrecompileBinaries {
|
||||
PrecompileBinaries({
|
||||
required this.privateKey,
|
||||
required this.githubToken,
|
||||
required this.repositorySlug,
|
||||
required this.manifestDir,
|
||||
required this.targets,
|
||||
this.androidSdkLocation,
|
||||
this.androidNdkVersion,
|
||||
this.androidMinSdkVersion,
|
||||
this.tempDir,
|
||||
});
|
||||
|
||||
final PrivateKey privateKey;
|
||||
final String githubToken;
|
||||
final RepositorySlug repositorySlug;
|
||||
final String manifestDir;
|
||||
final List<Target> targets;
|
||||
final String? androidSdkLocation;
|
||||
final String? androidNdkVersion;
|
||||
final int? androidMinSdkVersion;
|
||||
final String? tempDir;
|
||||
|
||||
static String fileName(Target target, String name) {
|
||||
return '${target.rust}_$name';
|
||||
}
|
||||
|
||||
static String signatureFileName(Target target, String name) {
|
||||
return '${target.rust}_$name.sig';
|
||||
}
|
||||
|
||||
Future<void> run() async {
|
||||
final crateInfo = CrateInfo.load(manifestDir);
|
||||
|
||||
final targets = List.of(this.targets);
|
||||
if (targets.isEmpty) {
|
||||
targets.addAll([
|
||||
...Target.buildableTargets(),
|
||||
if (androidSdkLocation != null) ...Target.androidTargets(),
|
||||
]);
|
||||
}
|
||||
|
||||
_log.info('Precompiling binaries for $targets');
|
||||
|
||||
final hash = CrateHash.compute(manifestDir);
|
||||
_log.info('Computed crate hash: $hash');
|
||||
|
||||
final String tagName = 'precompiled_$hash';
|
||||
|
||||
final github = GitHub(auth: Authentication.withToken(githubToken));
|
||||
final repo = github.repositories;
|
||||
final release = await _getOrCreateRelease(
|
||||
repo: repo,
|
||||
tagName: tagName,
|
||||
packageName: crateInfo.packageName,
|
||||
hash: hash,
|
||||
);
|
||||
|
||||
final tempDir = this.tempDir != null
|
||||
? Directory(this.tempDir!)
|
||||
: Directory.systemTemp.createTempSync('precompiled_');
|
||||
|
||||
tempDir.createSync(recursive: true);
|
||||
|
||||
final crateOptions = CargokitCrateOptions.load(
|
||||
manifestDir: manifestDir,
|
||||
);
|
||||
|
||||
final buildEnvironment = BuildEnvironment(
|
||||
configuration: BuildConfiguration.release,
|
||||
crateOptions: crateOptions,
|
||||
targetTempDir: tempDir.path,
|
||||
manifestDir: manifestDir,
|
||||
crateInfo: crateInfo,
|
||||
isAndroid: androidSdkLocation != null,
|
||||
androidSdkPath: androidSdkLocation,
|
||||
androidNdkVersion: androidNdkVersion,
|
||||
androidMinSdkVersion: androidMinSdkVersion,
|
||||
);
|
||||
|
||||
final rustup = Rustup();
|
||||
|
||||
for (final target in targets) {
|
||||
final artifactNames = getArtifactNames(
|
||||
target: target,
|
||||
libraryName: crateInfo.packageName,
|
||||
remote: true,
|
||||
);
|
||||
|
||||
if (artifactNames.every((name) {
|
||||
final fileName = PrecompileBinaries.fileName(target, name);
|
||||
return (release.assets ?? []).any((e) => e.name == fileName);
|
||||
})) {
|
||||
_log.info("All artifacts for $target already exist - skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
_log.info('Building for $target');
|
||||
|
||||
final builder =
|
||||
RustBuilder(target: target, environment: buildEnvironment);
|
||||
builder.prepare(rustup);
|
||||
final res = await builder.build();
|
||||
|
||||
final assets = <CreateReleaseAsset>[];
|
||||
for (final name in artifactNames) {
|
||||
final file = File(path.join(res, name));
|
||||
if (!file.existsSync()) {
|
||||
throw Exception('Missing artifact: ${file.path}');
|
||||
}
|
||||
|
||||
final data = file.readAsBytesSync();
|
||||
final create = CreateReleaseAsset(
|
||||
name: PrecompileBinaries.fileName(target, name),
|
||||
contentType: "application/octet-stream",
|
||||
assetData: data,
|
||||
);
|
||||
final signature = sign(privateKey, data);
|
||||
final signatureCreate = CreateReleaseAsset(
|
||||
name: signatureFileName(target, name),
|
||||
contentType: "application/octet-stream",
|
||||
assetData: signature,
|
||||
);
|
||||
bool verified = verify(public(privateKey), data, signature);
|
||||
if (!verified) {
|
||||
throw Exception('Signature verification failed');
|
||||
}
|
||||
assets.add(create);
|
||||
assets.add(signatureCreate);
|
||||
}
|
||||
_log.info('Uploading assets: ${assets.map((e) => e.name)}');
|
||||
for (final asset in assets) {
|
||||
// This seems to be failing on CI so do it one by one
|
||||
int retryCount = 0;
|
||||
while (true) {
|
||||
try {
|
||||
await repo.uploadReleaseAssets(release, [asset]);
|
||||
break;
|
||||
} on Exception catch (e) {
|
||||
if (retryCount == 10) {
|
||||
rethrow;
|
||||
}
|
||||
++retryCount;
|
||||
_log.shout(
|
||||
'Upload failed (attempt $retryCount, will retry): ${e.toString()}');
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_log.info('Cleaning up');
|
||||
tempDir.deleteSync(recursive: true);
|
||||
}
|
||||
|
||||
Future<Release> _getOrCreateRelease({
|
||||
required RepositoriesService repo,
|
||||
required String tagName,
|
||||
required String packageName,
|
||||
required String hash,
|
||||
}) async {
|
||||
Release release;
|
||||
try {
|
||||
_log.info('Fetching release $tagName');
|
||||
release = await repo.getReleaseByTagName(repositorySlug, tagName);
|
||||
} on ReleaseNotFound {
|
||||
_log.info('Release not found - creating release $tagName');
|
||||
release = await repo.createRelease(
|
||||
repositorySlug,
|
||||
CreateRelease.from(
|
||||
tagName: tagName,
|
||||
name: 'Precompiled binaries ${hash.substring(0, 8)}',
|
||||
targetCommitish: null,
|
||||
isDraft: false,
|
||||
isPrerelease: false,
|
||||
body: 'Precompiled binaries for crate $packageName, '
|
||||
'crate hash $hash.',
|
||||
));
|
||||
}
|
||||
return release;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'util.dart';
|
||||
|
||||
class _Toolchain {
|
||||
_Toolchain(
|
||||
this.name,
|
||||
this.targets,
|
||||
);
|
||||
|
||||
final String name;
|
||||
final List<String> targets;
|
||||
}
|
||||
|
||||
class Rustup {
|
||||
List<String>? installedTargets(String toolchain) {
|
||||
final targets = _installedTargets(toolchain);
|
||||
return targets != null ? List.unmodifiable(targets) : null;
|
||||
}
|
||||
|
||||
void installToolchain(String toolchain) {
|
||||
log.info("Installing Rust toolchain: $toolchain");
|
||||
runCommand("rustup", ['toolchain', 'install', toolchain]);
|
||||
_installedToolchains
|
||||
.add(_Toolchain(toolchain, _getInstalledTargets(toolchain)));
|
||||
}
|
||||
|
||||
void installTarget(
|
||||
String target, {
|
||||
required String toolchain,
|
||||
}) {
|
||||
log.info("Installing Rust target: $target");
|
||||
runCommand("rustup", [
|
||||
'target',
|
||||
'add',
|
||||
'--toolchain',
|
||||
toolchain,
|
||||
target,
|
||||
]);
|
||||
_installedTargets(toolchain)?.add(target);
|
||||
}
|
||||
|
||||
final List<_Toolchain> _installedToolchains;
|
||||
|
||||
Rustup() : _installedToolchains = _getInstalledToolchains();
|
||||
|
||||
List<String>? _installedTargets(String toolchain) => _installedToolchains
|
||||
.firstWhereOrNull(
|
||||
(e) => e.name == toolchain || e.name.startsWith('$toolchain-'))
|
||||
?.targets;
|
||||
|
||||
static List<_Toolchain> _getInstalledToolchains() {
|
||||
String extractToolchainName(String line) {
|
||||
// ignore (default) after toolchain name
|
||||
final parts = line.split(' ');
|
||||
return parts[0];
|
||||
}
|
||||
|
||||
final res = runCommand("rustup", ['toolchain', 'list']);
|
||||
|
||||
// To list all non-custom toolchains, we need to filter out lines that
|
||||
// don't start with "stable", "beta", or "nightly".
|
||||
Pattern nonCustom = RegExp(r"^(stable|beta|nightly)");
|
||||
final lines = res.stdout
|
||||
.toString()
|
||||
.split('\n')
|
||||
.where((e) => e.isNotEmpty && e.startsWith(nonCustom))
|
||||
.map(extractToolchainName)
|
||||
.toList(growable: true);
|
||||
|
||||
return lines
|
||||
.map(
|
||||
(name) => _Toolchain(
|
||||
name,
|
||||
_getInstalledTargets(name),
|
||||
),
|
||||
)
|
||||
.toList(growable: true);
|
||||
}
|
||||
|
||||
static List<String> _getInstalledTargets(String toolchain) {
|
||||
final res = runCommand("rustup", [
|
||||
'target',
|
||||
'list',
|
||||
'--toolchain',
|
||||
toolchain,
|
||||
'--installed',
|
||||
]);
|
||||
final lines = res.stdout
|
||||
.toString()
|
||||
.split('\n')
|
||||
.where((e) => e.isNotEmpty)
|
||||
.toList(growable: true);
|
||||
return lines;
|
||||
}
|
||||
|
||||
bool _didInstallRustSrcForNightly = false;
|
||||
|
||||
void installRustSrcForNightly() {
|
||||
if (_didInstallRustSrcForNightly) {
|
||||
return;
|
||||
}
|
||||
// Useful for -Z build-std
|
||||
runCommand(
|
||||
"rustup",
|
||||
['component', 'add', 'rust-src', '--toolchain', 'nightly'],
|
||||
);
|
||||
_didInstallRustSrcForNightly = true;
|
||||
}
|
||||
|
||||
static String? executablePath() {
|
||||
final envPath = Platform.environment['PATH'];
|
||||
final envPathSeparator = Platform.isWindows ? ';' : ':';
|
||||
final home = Platform.isWindows
|
||||
? Platform.environment['USERPROFILE']
|
||||
: Platform.environment['HOME'];
|
||||
final paths = [
|
||||
if (home != null) path.join(home, '.cargo', 'bin'),
|
||||
if (envPath != null) ...envPath.split(envPathSeparator),
|
||||
];
|
||||
for (final p in paths) {
|
||||
final rustup = Platform.isWindows ? 'rustup.exe' : 'rustup';
|
||||
final rustupPath = path.join(p, rustup);
|
||||
if (File(rustupPath).existsSync()) {
|
||||
return rustupPath;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import 'util.dart';
|
||||
|
||||
class Target {
|
||||
Target({
|
||||
required this.rust,
|
||||
this.flutter,
|
||||
this.android,
|
||||
this.androidMinSdkVersion,
|
||||
this.darwinPlatform,
|
||||
this.darwinArch,
|
||||
});
|
||||
|
||||
static final all = [
|
||||
Target(
|
||||
rust: 'armv7-linux-androideabi',
|
||||
flutter: 'android-arm',
|
||||
android: 'armeabi-v7a',
|
||||
androidMinSdkVersion: 16,
|
||||
),
|
||||
Target(
|
||||
rust: 'aarch64-linux-android',
|
||||
flutter: 'android-arm64',
|
||||
android: 'arm64-v8a',
|
||||
androidMinSdkVersion: 21,
|
||||
),
|
||||
Target(
|
||||
rust: 'i686-linux-android',
|
||||
flutter: 'android-x86',
|
||||
android: 'x86',
|
||||
androidMinSdkVersion: 16,
|
||||
),
|
||||
Target(
|
||||
rust: 'x86_64-linux-android',
|
||||
flutter: 'android-x64',
|
||||
android: 'x86_64',
|
||||
androidMinSdkVersion: 21,
|
||||
),
|
||||
Target(
|
||||
rust: 'x86_64-pc-windows-msvc',
|
||||
flutter: 'windows-x64',
|
||||
),
|
||||
Target(
|
||||
rust: 'x86_64-unknown-linux-gnu',
|
||||
flutter: 'linux-x64',
|
||||
),
|
||||
Target(
|
||||
rust: 'aarch64-unknown-linux-gnu',
|
||||
flutter: 'linux-arm64',
|
||||
),
|
||||
Target(
|
||||
rust: 'x86_64-apple-darwin',
|
||||
darwinPlatform: 'macosx',
|
||||
darwinArch: 'x86_64',
|
||||
),
|
||||
Target(
|
||||
rust: 'aarch64-apple-darwin',
|
||||
darwinPlatform: 'macosx',
|
||||
darwinArch: 'arm64',
|
||||
),
|
||||
Target(
|
||||
rust: 'aarch64-apple-ios',
|
||||
darwinPlatform: 'iphoneos',
|
||||
darwinArch: 'arm64',
|
||||
),
|
||||
Target(
|
||||
rust: 'aarch64-apple-ios-sim',
|
||||
darwinPlatform: 'iphonesimulator',
|
||||
darwinArch: 'arm64',
|
||||
),
|
||||
Target(
|
||||
rust: 'x86_64-apple-ios',
|
||||
darwinPlatform: 'iphonesimulator',
|
||||
darwinArch: 'x86_64',
|
||||
),
|
||||
];
|
||||
|
||||
static Target? forFlutterName(String flutterName) {
|
||||
return all.firstWhereOrNull((element) => element.flutter == flutterName);
|
||||
}
|
||||
|
||||
static Target? forDarwin({
|
||||
required String platformName,
|
||||
required String darwinAarch,
|
||||
}) {
|
||||
return all.firstWhereOrNull((element) => //
|
||||
element.darwinPlatform == platformName &&
|
||||
element.darwinArch == darwinAarch);
|
||||
}
|
||||
|
||||
static Target? forRustTriple(String triple) {
|
||||
return all.firstWhereOrNull((element) => element.rust == triple);
|
||||
}
|
||||
|
||||
static List<Target> androidTargets() {
|
||||
return all
|
||||
.where((element) => element.android != null)
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
/// Returns buildable targets on current host platform ignoring Android targets.
|
||||
static List<Target> buildableTargets() {
|
||||
if (Platform.isLinux) {
|
||||
// Right now we don't support cross-compiling on Linux. So we just return
|
||||
// the host target.
|
||||
final arch = runCommand('arch', []).stdout as String;
|
||||
if (arch.trim() == 'aarch64') {
|
||||
return [Target.forRustTriple('aarch64-unknown-linux-gnu')!];
|
||||
} else {
|
||||
return [Target.forRustTriple('x86_64-unknown-linux-gnu')!];
|
||||
}
|
||||
}
|
||||
return all.where((target) {
|
||||
if (Platform.isWindows) {
|
||||
return target.rust.contains('-windows-');
|
||||
} else if (Platform.isMacOS) {
|
||||
return target.darwinPlatform != null;
|
||||
}
|
||||
return false;
|
||||
}).toList(growable: false);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return rust;
|
||||
}
|
||||
|
||||
final String? flutter;
|
||||
final String rust;
|
||||
final String? android;
|
||||
final int? androidMinSdkVersion;
|
||||
final String? darwinPlatform;
|
||||
final String? darwinArch;
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'logging.dart';
|
||||
import 'rustup.dart';
|
||||
|
||||
final log = Logger("process");
|
||||
|
||||
class CommandFailedException implements Exception {
|
||||
final String executable;
|
||||
final List<String> arguments;
|
||||
final ProcessResult result;
|
||||
|
||||
CommandFailedException({
|
||||
required this.executable,
|
||||
required this.arguments,
|
||||
required this.result,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final stdout = result.stdout.toString().trim();
|
||||
final stderr = result.stderr.toString().trim();
|
||||
return [
|
||||
"External Command: $executable ${arguments.map((e) => '"$e"').join(' ')}",
|
||||
"Returned Exit Code: ${result.exitCode}",
|
||||
kSeparator,
|
||||
"STDOUT:",
|
||||
if (stdout.isNotEmpty) stdout,
|
||||
kSeparator,
|
||||
"STDERR:",
|
||||
if (stderr.isNotEmpty) stderr,
|
||||
].join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
class TestRunCommandArgs {
|
||||
final String executable;
|
||||
final List<String> arguments;
|
||||
final String? workingDirectory;
|
||||
final Map<String, String>? environment;
|
||||
final bool includeParentEnvironment;
|
||||
final bool runInShell;
|
||||
final Encoding? stdoutEncoding;
|
||||
final Encoding? stderrEncoding;
|
||||
|
||||
TestRunCommandArgs({
|
||||
required this.executable,
|
||||
required this.arguments,
|
||||
this.workingDirectory,
|
||||
this.environment,
|
||||
this.includeParentEnvironment = true,
|
||||
this.runInShell = false,
|
||||
this.stdoutEncoding,
|
||||
this.stderrEncoding,
|
||||
});
|
||||
}
|
||||
|
||||
class TestRunCommandResult {
|
||||
TestRunCommandResult({
|
||||
this.pid = 1,
|
||||
this.exitCode = 0,
|
||||
this.stdout = '',
|
||||
this.stderr = '',
|
||||
});
|
||||
|
||||
final int pid;
|
||||
final int exitCode;
|
||||
final String stdout;
|
||||
final String stderr;
|
||||
}
|
||||
|
||||
TestRunCommandResult Function(TestRunCommandArgs args)? testRunCommandOverride;
|
||||
|
||||
ProcessResult runCommand(
|
||||
String executable,
|
||||
List<String> arguments, {
|
||||
String? workingDirectory,
|
||||
Map<String, String>? environment,
|
||||
bool includeParentEnvironment = true,
|
||||
bool runInShell = false,
|
||||
Encoding? stdoutEncoding = systemEncoding,
|
||||
Encoding? stderrEncoding = systemEncoding,
|
||||
}) {
|
||||
if (testRunCommandOverride != null) {
|
||||
final result = testRunCommandOverride!(TestRunCommandArgs(
|
||||
executable: executable,
|
||||
arguments: arguments,
|
||||
workingDirectory: workingDirectory,
|
||||
environment: environment,
|
||||
includeParentEnvironment: includeParentEnvironment,
|
||||
runInShell: runInShell,
|
||||
stdoutEncoding: stdoutEncoding,
|
||||
stderrEncoding: stderrEncoding,
|
||||
));
|
||||
return ProcessResult(
|
||||
result.pid,
|
||||
result.exitCode,
|
||||
result.stdout,
|
||||
result.stderr,
|
||||
);
|
||||
}
|
||||
log.finer('Running command $executable ${arguments.join(' ')}');
|
||||
final res = Process.runSync(
|
||||
_resolveExecutable(executable),
|
||||
arguments,
|
||||
workingDirectory: workingDirectory,
|
||||
environment: environment,
|
||||
includeParentEnvironment: includeParentEnvironment,
|
||||
runInShell: runInShell,
|
||||
stderrEncoding: stderrEncoding,
|
||||
stdoutEncoding: stdoutEncoding,
|
||||
);
|
||||
if (res.exitCode != 0) {
|
||||
throw CommandFailedException(
|
||||
executable: executable,
|
||||
arguments: arguments,
|
||||
result: res,
|
||||
);
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
class RustupNotFoundException implements Exception {
|
||||
@override
|
||||
String toString() {
|
||||
return [
|
||||
' ',
|
||||
'rustup not found in PATH.',
|
||||
' ',
|
||||
'Maybe you need to install Rust? It only takes a minute:',
|
||||
' ',
|
||||
if (Platform.isWindows) 'https://www.rust-lang.org/tools/install',
|
||||
if (hasHomebrewRustInPath()) ...[
|
||||
'\$ brew unlink rust # Unlink homebrew Rust from PATH',
|
||||
],
|
||||
if (!Platform.isWindows)
|
||||
"\$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh",
|
||||
' ',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
static bool hasHomebrewRustInPath() {
|
||||
if (!Platform.isMacOS) {
|
||||
return false;
|
||||
}
|
||||
final envPath = Platform.environment['PATH'] ?? '';
|
||||
final paths = envPath.split(':');
|
||||
return paths.any((p) {
|
||||
return p.contains('homebrew') && File(path.join(p, 'rustc')).existsSync();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
String _resolveExecutable(String executable) {
|
||||
if (executable == 'rustup') {
|
||||
final resolved = Rustup.executablePath();
|
||||
if (resolved != null) {
|
||||
return resolved;
|
||||
}
|
||||
throw RustupNotFoundException();
|
||||
} else {
|
||||
return executable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:ed25519_edwards/ed25519_edwards.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import 'artifacts_provider.dart';
|
||||
import 'cargo.dart';
|
||||
import 'crate_hash.dart';
|
||||
import 'options.dart';
|
||||
import 'precompile_binaries.dart';
|
||||
import 'target.dart';
|
||||
|
||||
class VerifyBinaries {
|
||||
VerifyBinaries({
|
||||
required this.manifestDir,
|
||||
});
|
||||
|
||||
final String manifestDir;
|
||||
|
||||
Future<void> run() async {
|
||||
final crateInfo = CrateInfo.load(manifestDir);
|
||||
|
||||
final config = CargokitCrateOptions.load(manifestDir: manifestDir);
|
||||
final precompiledBinaries = config.precompiledBinaries;
|
||||
if (precompiledBinaries == null) {
|
||||
stdout.writeln('Crate does not support precompiled binaries.');
|
||||
} else {
|
||||
final crateHash = CrateHash.compute(manifestDir);
|
||||
stdout.writeln('Crate hash: $crateHash');
|
||||
|
||||
for (final target in Target.all) {
|
||||
final message = 'Checking ${target.rust}...';
|
||||
stdout.write(message.padRight(40));
|
||||
stdout.flush();
|
||||
|
||||
final artifacts = getArtifactNames(
|
||||
target: target,
|
||||
libraryName: crateInfo.packageName,
|
||||
remote: true,
|
||||
);
|
||||
|
||||
final prefix = precompiledBinaries.uriPrefix;
|
||||
|
||||
bool ok = true;
|
||||
|
||||
for (final artifact in artifacts) {
|
||||
final fileName = PrecompileBinaries.fileName(target, artifact);
|
||||
final signatureFileName =
|
||||
PrecompileBinaries.signatureFileName(target, artifact);
|
||||
|
||||
final url = Uri.parse('$prefix$crateHash/$fileName');
|
||||
final signatureUrl =
|
||||
Uri.parse('$prefix$crateHash/$signatureFileName');
|
||||
|
||||
final signature = await get(signatureUrl);
|
||||
if (signature.statusCode != 200) {
|
||||
stdout.writeln('MISSING');
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
final asset = await get(url);
|
||||
if (asset.statusCode != 200) {
|
||||
stdout.writeln('MISSING');
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!verify(precompiledBinaries.publicKey, asset.bodyBytes,
|
||||
signature.bodyBytes)) {
|
||||
stdout.writeln('INVALID SIGNATURE');
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
stdout.writeln('OK');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
453
mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.lock
Normal file
453
mobile/apps/photos/rust_builder/cargokit/build_tool/pubspec.lock
Normal file
@@ -0,0 +1,453 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_fe_analyzer_shared:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "64.0.0"
|
||||
adaptive_number:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: adaptive_number
|
||||
sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.0"
|
||||
args:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: args
|
||||
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
collection:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: collection
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
convert:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: convert
|
||||
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
coverage:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: coverage
|
||||
sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.3"
|
||||
crypto:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: crypto
|
||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
ed25519_edwards:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: ed25519_edwards
|
||||
sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.1"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.4"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
github:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: github
|
||||
sha256: "9966bc13bf612342e916b0a343e95e5f046c88f602a14476440e9b75d2295411"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.17.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
hex:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: hex
|
||||
sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_multi_server
|
||||
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.1"
|
||||
lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: lints
|
||||
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
logging:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: logging
|
||||
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
node_preamble:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: node_preamble
|
||||
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
path:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path
|
||||
sha256: "2ad4cddff7f5cc0e2d13069f2a3f7a73ca18f66abd6f5ecf215219cdb3638edb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.0"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.4.0"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
shelf_packages_handler:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_packages_handler
|
||||
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
shelf_static:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_static
|
||||
sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
source_map_stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_map_stack_trace
|
||||
sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
source_maps:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_maps
|
||||
sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.10.12"
|
||||
source_span:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
test:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: test
|
||||
sha256: "9b0dd8e36af4a5b1569029949d50a52cb2a2a2fdaa20cebb96e6603b9ae241f9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.24.6"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.1"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: "4bef837e56375537055fdbbbf6dd458b1859881f4c7e6da936158f77d61ab265"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.6"
|
||||
toml:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: toml
|
||||
sha256: "157c5dca5160fced243f3ce984117f729c788bb5e475504f3dbcda881accee44"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.14.0"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
version:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: version
|
||||
sha256: "2307e23a45b43f96469eeab946208ed63293e8afca9c28cd8b5241ff31c55f55"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "0fae432c85c4ea880b33b497d32824b97795b04cdaa74d270219572a1f50268d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.9.0"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
webkit_inspection_protocol:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webkit_inspection_protocol
|
||||
sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
yaml:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: yaml
|
||||
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.0.0 <4.0.0"
|
||||
@@ -0,0 +1,33 @@
|
||||
# This is copied from Cargokit (which is the official way to use it currently)
|
||||
# Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
name: build_tool
|
||||
description: Cargokit build_tool. Facilitates the build of Rust crate during Flutter application build.
|
||||
publish_to: none
|
||||
version: 1.0.0
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
|
||||
# Add regular dependencies here.
|
||||
dependencies:
|
||||
# these are pinned on purpose because the bundle_tool_runner doesn't have
|
||||
# pubspec.lock. See run_build_tool.sh
|
||||
logging: 1.2.0
|
||||
path: 1.8.0
|
||||
version: 3.0.0
|
||||
collection: 1.18.0
|
||||
ed25519_edwards: 0.3.1
|
||||
hex: 0.2.0
|
||||
yaml: 3.1.2
|
||||
source_span: 1.10.0
|
||||
github: 9.17.0
|
||||
args: 2.4.2
|
||||
crypto: 3.0.3
|
||||
convert: 3.1.1
|
||||
http: 1.1.0
|
||||
toml: 0.14.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^2.1.0
|
||||
test: ^1.24.0
|
||||
@@ -0,0 +1,99 @@
|
||||
SET(cargokit_cmake_root "${CMAKE_CURRENT_LIST_DIR}/..")
|
||||
|
||||
# Workaround for https://github.com/dart-lang/pub/issues/4010
|
||||
get_filename_component(cargokit_cmake_root "${cargokit_cmake_root}" REALPATH)
|
||||
|
||||
if(WIN32)
|
||||
# REALPATH does not properly resolve symlinks on windows :-/
|
||||
execute_process(COMMAND powershell -ExecutionPolicy Bypass -File "${CMAKE_CURRENT_LIST_DIR}/resolve_symlinks.ps1" "${cargokit_cmake_root}" OUTPUT_VARIABLE cargokit_cmake_root OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
|
||||
# Arguments
|
||||
# - target: CMAKE target to which rust library is linked
|
||||
# - manifest_dir: relative path from current folder to directory containing cargo manifest
|
||||
# - lib_name: cargo package name
|
||||
# - any_symbol_name: name of any exported symbol from the library.
|
||||
# used on windows to force linking with library.
|
||||
function(apply_cargokit target manifest_dir lib_name any_symbol_name)
|
||||
|
||||
set(CARGOKIT_LIB_NAME "${lib_name}")
|
||||
set(CARGOKIT_LIB_FULL_NAME "${CMAKE_SHARED_MODULE_PREFIX}${CARGOKIT_LIB_NAME}${CMAKE_SHARED_MODULE_SUFFIX}")
|
||||
if (CMAKE_CONFIGURATION_TYPES)
|
||||
set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>")
|
||||
set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/${CARGOKIT_LIB_FULL_NAME}")
|
||||
else()
|
||||
set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/${CARGOKIT_LIB_FULL_NAME}")
|
||||
endif()
|
||||
set(CARGOKIT_TEMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/cargokit_build")
|
||||
|
||||
if (FLUTTER_TARGET_PLATFORM)
|
||||
set(CARGOKIT_TARGET_PLATFORM "${FLUTTER_TARGET_PLATFORM}")
|
||||
else()
|
||||
set(CARGOKIT_TARGET_PLATFORM "windows-x64")
|
||||
endif()
|
||||
|
||||
set(CARGOKIT_ENV
|
||||
"CARGOKIT_CMAKE=${CMAKE_COMMAND}"
|
||||
"CARGOKIT_CONFIGURATION=$<CONFIG>"
|
||||
"CARGOKIT_MANIFEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/${manifest_dir}"
|
||||
"CARGOKIT_TARGET_TEMP_DIR=${CARGOKIT_TEMP_DIR}"
|
||||
"CARGOKIT_OUTPUT_DIR=${CARGOKIT_OUTPUT_DIR}"
|
||||
"CARGOKIT_TARGET_PLATFORM=${CARGOKIT_TARGET_PLATFORM}"
|
||||
"CARGOKIT_TOOL_TEMP_DIR=${CARGOKIT_TEMP_DIR}/tool"
|
||||
"CARGOKIT_ROOT_PROJECT_DIR=${CMAKE_SOURCE_DIR}"
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
set(SCRIPT_EXTENSION ".cmd")
|
||||
set(IMPORT_LIB_EXTENSION ".lib")
|
||||
else()
|
||||
set(SCRIPT_EXTENSION ".sh")
|
||||
set(IMPORT_LIB_EXTENSION "")
|
||||
execute_process(COMMAND chmod +x "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}")
|
||||
endif()
|
||||
|
||||
# Using generators in custom command is only supported in CMake 3.20+
|
||||
if (CMAKE_CONFIGURATION_TYPES AND ${CMAKE_VERSION} VERSION_LESS "3.20.0")
|
||||
foreach(CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES)
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/${CARGOKIT_LIB_FULL_NAME}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/_phony_"
|
||||
COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV}
|
||||
"${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake
|
||||
VERBATIM
|
||||
)
|
||||
endforeach()
|
||||
else()
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${OUTPUT_LIB}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/_phony_"
|
||||
COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV}
|
||||
"${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/_phony_" PROPERTIES SYMBOLIC TRUE)
|
||||
|
||||
if (TARGET ${target})
|
||||
# If we have actual cmake target provided create target and make existing
|
||||
# target depend on it
|
||||
add_custom_target("${target}_cargokit" DEPENDS ${OUTPUT_LIB})
|
||||
add_dependencies("${target}" "${target}_cargokit")
|
||||
target_link_libraries("${target}" PRIVATE "${OUTPUT_LIB}${IMPORT_LIB_EXTENSION}")
|
||||
if(WIN32)
|
||||
target_link_options(${target} PRIVATE "/INCLUDE:${any_symbol_name}")
|
||||
endif()
|
||||
else()
|
||||
# Otherwise (FFI) just use ALL to force building always
|
||||
add_custom_target("${target}_cargokit" ALL DEPENDS ${OUTPUT_LIB})
|
||||
endif()
|
||||
|
||||
# Allow adding the output library to plugin bundled libraries
|
||||
set("${target}_cargokit_lib" ${OUTPUT_LIB} PARENT_SCOPE)
|
||||
|
||||
endfunction()
|
||||
@@ -0,0 +1,27 @@
|
||||
function Resolve-Symlinks {
|
||||
[CmdletBinding()]
|
||||
[OutputType([string])]
|
||||
param(
|
||||
[Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
|
||||
[string] $Path
|
||||
)
|
||||
|
||||
[string] $separator = '/'
|
||||
[string[]] $parts = $Path.Split($separator)
|
||||
|
||||
[string] $realPath = ''
|
||||
foreach ($part in $parts) {
|
||||
if ($realPath -and !$realPath.EndsWith($separator)) {
|
||||
$realPath += $separator
|
||||
}
|
||||
$realPath += $part
|
||||
$item = Get-Item $realPath
|
||||
if ($item.Target) {
|
||||
$realPath = $item.Target.Replace('\', '/')
|
||||
}
|
||||
}
|
||||
$realPath
|
||||
}
|
||||
|
||||
$path=Resolve-Symlinks -Path $args[0]
|
||||
Write-Host $path
|
||||
179
mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle
Normal file
179
mobile/apps/photos/rust_builder/cargokit/gradle/plugin.gradle
Normal file
@@ -0,0 +1,179 @@
|
||||
/// This is copied from Cargokit (which is the official way to use it currently)
|
||||
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
|
||||
|
||||
import java.nio.file.Paths
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
CargoKitPlugin.file = buildscript.sourceFile
|
||||
|
||||
apply plugin: CargoKitPlugin
|
||||
|
||||
class CargoKitExtension {
|
||||
String manifestDir; // Relative path to folder containing Cargo.toml
|
||||
String libname; // Library name within Cargo.toml. Must be a cdylib
|
||||
}
|
||||
|
||||
abstract class CargoKitBuildTask extends DefaultTask {
|
||||
|
||||
@Input
|
||||
String buildMode
|
||||
|
||||
@Input
|
||||
String buildDir
|
||||
|
||||
@Input
|
||||
String outputDir
|
||||
|
||||
@Input
|
||||
String ndkVersion
|
||||
|
||||
@Input
|
||||
String sdkDirectory
|
||||
|
||||
@Input
|
||||
int compileSdkVersion;
|
||||
|
||||
@Input
|
||||
int minSdkVersion;
|
||||
|
||||
@Input
|
||||
String pluginFile
|
||||
|
||||
@Input
|
||||
List<String> targetPlatforms
|
||||
|
||||
@TaskAction
|
||||
def build() {
|
||||
if (project.cargokit.manifestDir == null) {
|
||||
throw new GradleException("Property 'manifestDir' must be set on cargokit extension");
|
||||
}
|
||||
|
||||
if (project.cargokit.libname == null) {
|
||||
throw new GradleException("Property 'libname' must be set on cargokit extension");
|
||||
}
|
||||
|
||||
def executableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "run_build_tool.cmd" : "run_build_tool.sh"
|
||||
def path = Paths.get(new File(pluginFile).parent, "..", executableName);
|
||||
|
||||
def manifestDir = Paths.get(project.buildscript.sourceFile.parent, project.cargokit.manifestDir)
|
||||
|
||||
def rootProjectDir = project.rootProject.projectDir
|
||||
|
||||
if (!Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
project.exec {
|
||||
commandLine 'chmod', '+x', path
|
||||
}
|
||||
}
|
||||
|
||||
project.exec {
|
||||
executable path
|
||||
args "build-gradle"
|
||||
environment "CARGOKIT_ROOT_PROJECT_DIR", rootProjectDir
|
||||
environment "CARGOKIT_TOOL_TEMP_DIR", "${buildDir}/build_tool"
|
||||
environment "CARGOKIT_MANIFEST_DIR", manifestDir
|
||||
environment "CARGOKIT_CONFIGURATION", buildMode
|
||||
environment "CARGOKIT_TARGET_TEMP_DIR", buildDir
|
||||
environment "CARGOKIT_OUTPUT_DIR", outputDir
|
||||
environment "CARGOKIT_NDK_VERSION", ndkVersion
|
||||
environment "CARGOKIT_SDK_DIR", sdkDirectory
|
||||
environment "CARGOKIT_COMPILE_SDK_VERSION", compileSdkVersion
|
||||
environment "CARGOKIT_MIN_SDK_VERSION", minSdkVersion
|
||||
environment "CARGOKIT_TARGET_PLATFORMS", targetPlatforms.join(",")
|
||||
environment "CARGOKIT_JAVA_HOME", System.properties['java.home']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CargoKitPlugin implements Plugin<Project> {
|
||||
|
||||
static String file;
|
||||
|
||||
private Plugin findFlutterPlugin(Project rootProject) {
|
||||
_findFlutterPlugin(rootProject.childProjects)
|
||||
}
|
||||
|
||||
private Plugin _findFlutterPlugin(Map projects) {
|
||||
for (project in projects) {
|
||||
for (plugin in project.value.getPlugins()) {
|
||||
if (plugin.class.name == "FlutterPlugin") {
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
def plugin = _findFlutterPlugin(project.value.childProjects);
|
||||
if (plugin != null) {
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
void apply(Project project) {
|
||||
def plugin = findFlutterPlugin(project.rootProject);
|
||||
|
||||
project.extensions.create("cargokit", CargoKitExtension)
|
||||
|
||||
if (plugin == null) {
|
||||
print("Flutter plugin not found, CargoKit plugin will not be applied.")
|
||||
return;
|
||||
}
|
||||
|
||||
def cargoBuildDir = "${project.buildDir}/build"
|
||||
|
||||
// Determine if the project is an application or library
|
||||
def isApplication = plugin.project.plugins.hasPlugin('com.android.application')
|
||||
def variants = isApplication ? plugin.project.android.applicationVariants : plugin.project.android.libraryVariants
|
||||
|
||||
variants.all { variant ->
|
||||
|
||||
final buildType = variant.buildType.name
|
||||
|
||||
def cargoOutputDir = "${project.buildDir}/jniLibs/${buildType}";
|
||||
def jniLibs = project.android.sourceSets.maybeCreate(buildType).jniLibs;
|
||||
jniLibs.srcDir(new File(cargoOutputDir))
|
||||
|
||||
def platforms = plugin.getTargetPlatforms().collect()
|
||||
|
||||
// Same thing addFlutterDependencies does in flutter.gradle
|
||||
if (buildType == "debug") {
|
||||
platforms.add("android-x86")
|
||||
platforms.add("android-x64")
|
||||
}
|
||||
|
||||
// The task name depends on plugin properties, which are not available
|
||||
// at this point
|
||||
project.getGradle().afterProject {
|
||||
def taskName = "cargokitCargoBuild${project.cargokit.libname.capitalize()}${buildType.capitalize()}";
|
||||
|
||||
if (project.tasks.findByName(taskName)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (plugin.project.android.ndkVersion == null) {
|
||||
throw new GradleException("Please set 'android.ndkVersion' in 'app/build.gradle'.")
|
||||
}
|
||||
|
||||
def task = project.tasks.create(taskName, CargoKitBuildTask.class) {
|
||||
buildMode = variant.buildType.name
|
||||
buildDir = cargoBuildDir
|
||||
outputDir = cargoOutputDir
|
||||
ndkVersion = plugin.project.android.ndkVersion
|
||||
sdkDirectory = plugin.project.android.sdkDirectory
|
||||
minSdkVersion = plugin.project.android.defaultConfig.minSdkVersion.apiLevel as int
|
||||
compileSdkVersion = plugin.project.android.compileSdkVersion.substring(8) as int
|
||||
targetPlatforms = platforms
|
||||
pluginFile = CargoKitPlugin.file
|
||||
}
|
||||
def onTask = { newTask ->
|
||||
if (newTask.name == "merge${buildType.capitalize()}NativeLibs") {
|
||||
newTask.dependsOn task
|
||||
// Fix gradle 7.4.2 not picking up JNI library changes
|
||||
newTask.outputs.upToDateWhen { false }
|
||||
}
|
||||
}
|
||||
project.tasks.each onTask
|
||||
project.tasks.whenTaskAdded onTask
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
91
mobile/apps/photos/rust_builder/cargokit/run_build_tool.cmd
Executable file
91
mobile/apps/photos/rust_builder/cargokit/run_build_tool.cmd
Executable file
@@ -0,0 +1,91 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
setlocal ENABLEDELAYEDEXPANSION
|
||||
|
||||
SET BASEDIR=%~dp0
|
||||
|
||||
if not exist "%CARGOKIT_TOOL_TEMP_DIR%" (
|
||||
mkdir "%CARGOKIT_TOOL_TEMP_DIR%"
|
||||
)
|
||||
cd /D "%CARGOKIT_TOOL_TEMP_DIR%"
|
||||
|
||||
SET BUILD_TOOL_PKG_DIR=%BASEDIR%build_tool
|
||||
SET DART=%FLUTTER_ROOT%\bin\cache\dart-sdk\bin\dart
|
||||
|
||||
set BUILD_TOOL_PKG_DIR_POSIX=%BUILD_TOOL_PKG_DIR:\=/%
|
||||
|
||||
(
|
||||
echo name: build_tool_runner
|
||||
echo version: 1.0.0
|
||||
echo publish_to: none
|
||||
echo.
|
||||
echo environment:
|
||||
echo sdk: '^>=3.0.0 ^<4.0.0'
|
||||
echo.
|
||||
echo dependencies:
|
||||
echo build_tool:
|
||||
echo path: %BUILD_TOOL_PKG_DIR_POSIX%
|
||||
) >pubspec.yaml
|
||||
|
||||
if not exist bin (
|
||||
mkdir bin
|
||||
)
|
||||
|
||||
(
|
||||
echo import 'package:build_tool/build_tool.dart' as build_tool;
|
||||
echo void main^(List^<String^> args^) ^{
|
||||
echo build_tool.runMain^(args^);
|
||||
echo ^}
|
||||
) >bin\build_tool_runner.dart
|
||||
|
||||
SET PRECOMPILED=bin\build_tool_runner.dill
|
||||
|
||||
REM To detect changes in package we compare output of DIR /s (recursive)
|
||||
set PREV_PACKAGE_INFO=.dart_tool\package_info.prev
|
||||
set CUR_PACKAGE_INFO=.dart_tool\package_info.cur
|
||||
|
||||
DIR "%BUILD_TOOL_PKG_DIR%" /s > "%CUR_PACKAGE_INFO%_orig"
|
||||
|
||||
REM Last line in dir output is free space on harddrive. That is bound to
|
||||
REM change between invocation so we need to remove it
|
||||
(
|
||||
Set "Line="
|
||||
For /F "UseBackQ Delims=" %%A In ("%CUR_PACKAGE_INFO%_orig") Do (
|
||||
SetLocal EnableDelayedExpansion
|
||||
If Defined Line Echo !Line!
|
||||
EndLocal
|
||||
Set "Line=%%A")
|
||||
) >"%CUR_PACKAGE_INFO%"
|
||||
DEL "%CUR_PACKAGE_INFO%_orig"
|
||||
|
||||
REM Compare current directory listing with previous
|
||||
FC /B "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" > nul 2>&1
|
||||
|
||||
If %ERRORLEVEL% neq 0 (
|
||||
REM Changed - copy current to previous and remove precompiled kernel
|
||||
if exist "%PREV_PACKAGE_INFO%" (
|
||||
DEL "%PREV_PACKAGE_INFO%"
|
||||
)
|
||||
MOVE /Y "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%"
|
||||
if exist "%PRECOMPILED%" (
|
||||
DEL "%PRECOMPILED%"
|
||||
)
|
||||
)
|
||||
|
||||
REM There is no CUR_PACKAGE_INFO it was renamed in previous step to %PREV_PACKAGE_INFO%
|
||||
REM which means we need to do pub get and precompile
|
||||
if not exist "%PRECOMPILED%" (
|
||||
echo Running pub get in "%cd%"
|
||||
"%DART%" pub get --no-precompile
|
||||
"%DART%" compile kernel bin/build_tool_runner.dart
|
||||
)
|
||||
|
||||
"%DART%" "%PRECOMPILED%" %*
|
||||
|
||||
REM 253 means invalid snapshot version.
|
||||
If %ERRORLEVEL% equ 253 (
|
||||
"%DART%" pub get --no-precompile
|
||||
"%DART%" compile kernel bin/build_tool_runner.dart
|
||||
"%DART%" "%PRECOMPILED%" %*
|
||||
)
|
||||
94
mobile/apps/photos/rust_builder/cargokit/run_build_tool.sh
Executable file
94
mobile/apps/photos/rust_builder/cargokit/run_build_tool.sh
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
BASEDIR=$(dirname "$0")
|
||||
|
||||
mkdir -p "$CARGOKIT_TOOL_TEMP_DIR"
|
||||
|
||||
cd "$CARGOKIT_TOOL_TEMP_DIR"
|
||||
|
||||
# Write a very simple bin package in temp folder that depends on build_tool package
|
||||
# from Cargokit. This is done to ensure that we don't pollute Cargokit folder
|
||||
# with .dart_tool contents.
|
||||
|
||||
BUILD_TOOL_PKG_DIR="$BASEDIR/build_tool"
|
||||
|
||||
if [[ -z $FLUTTER_ROOT ]]; then # not defined
|
||||
DART=dart
|
||||
else
|
||||
DART="$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dart"
|
||||
fi
|
||||
|
||||
cat << EOF > "pubspec.yaml"
|
||||
name: build_tool_runner
|
||||
version: 1.0.0
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
|
||||
dependencies:
|
||||
build_tool:
|
||||
path: "$BUILD_TOOL_PKG_DIR"
|
||||
EOF
|
||||
|
||||
mkdir -p "bin"
|
||||
|
||||
cat << EOF > "bin/build_tool_runner.dart"
|
||||
import 'package:build_tool/build_tool.dart' as build_tool;
|
||||
void main(List<String> args) {
|
||||
build_tool.runMain(args);
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create alias for `shasum` if it does not exist and `sha1sum` exists
|
||||
if ! [ -x "$(command -v shasum)" ] && [ -x "$(command -v sha1sum)" ]; then
|
||||
shopt -s expand_aliases
|
||||
alias shasum="sha1sum"
|
||||
fi
|
||||
|
||||
# Dart run will not cache any package that has a path dependency, which
|
||||
# is the case for our build_tool_runner. So instead we precompile the package
|
||||
# ourselves.
|
||||
# To invalidate the cached kernel we use the hash of ls -LR of the build_tool
|
||||
# package directory. This should be good enough, as the build_tool package
|
||||
# itself is not meant to have any path dependencies.
|
||||
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
PACKAGE_HASH=$(ls -lTR "$BUILD_TOOL_PKG_DIR" | shasum)
|
||||
else
|
||||
PACKAGE_HASH=$(ls -lR --full-time "$BUILD_TOOL_PKG_DIR" | shasum)
|
||||
fi
|
||||
|
||||
PACKAGE_HASH_FILE=".package_hash"
|
||||
|
||||
if [ -f "$PACKAGE_HASH_FILE" ]; then
|
||||
EXISTING_HASH=$(cat "$PACKAGE_HASH_FILE")
|
||||
if [ "$PACKAGE_HASH" != "$EXISTING_HASH" ]; then
|
||||
rm "$PACKAGE_HASH_FILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Run pub get if needed.
|
||||
if [ ! -f "$PACKAGE_HASH_FILE" ]; then
|
||||
"$DART" pub get --no-precompile
|
||||
"$DART" compile kernel bin/build_tool_runner.dart
|
||||
echo "$PACKAGE_HASH" > "$PACKAGE_HASH_FILE"
|
||||
fi
|
||||
|
||||
set +e
|
||||
|
||||
"$DART" bin/build_tool_runner.dill "$@"
|
||||
|
||||
exit_code=$?
|
||||
|
||||
# 253 means invalid snapshot version.
|
||||
if [ $exit_code == 253 ]; then
|
||||
"$DART" pub get --no-precompile
|
||||
"$DART" compile kernel bin/build_tool_runner.dart
|
||||
"$DART" bin/build_tool_runner.dill "$@"
|
||||
exit_code=$?
|
||||
fi
|
||||
|
||||
exit $exit_code
|
||||
1
mobile/apps/photos/rust_builder/ios/Classes/dummy_file.c
Normal file
1
mobile/apps/photos/rust_builder/ios/Classes/dummy_file.c
Normal file
@@ -0,0 +1 @@
|
||||
// This is an empty file to force CocoaPods to create a framework.
|
||||
45
mobile/apps/photos/rust_builder/ios/rust_lib_photos.podspec
Normal file
45
mobile/apps/photos/rust_builder/ios/rust_lib_photos.podspec
Normal file
@@ -0,0 +1,45 @@
|
||||
#
|
||||
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||
# Run `pod lib lint rust_lib_photos.podspec` to validate before publishing.
|
||||
#
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'rust_lib_photos'
|
||||
s.version = '0.0.1'
|
||||
s.summary = 'A new Flutter FFI plugin project.'
|
||||
s.description = <<-DESC
|
||||
A new Flutter FFI plugin project.
|
||||
DESC
|
||||
s.homepage = 'http://example.com'
|
||||
s.license = { :file => '../LICENSE' }
|
||||
s.author = { 'Your Company' => 'email@example.com' }
|
||||
|
||||
# This will ensure the source files in Classes/ are included in the native
|
||||
# builds of apps using this FFI plugin. Podspec does not support relative
|
||||
# paths, so Classes contains a forwarder C file that relatively imports
|
||||
# `../src/*` so that the C sources can be shared among all target platforms.
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'Classes/**/*'
|
||||
s.dependency 'Flutter'
|
||||
s.platform = :ios, '11.0'
|
||||
|
||||
# Flutter.framework does not contain a i386 slice.
|
||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
|
||||
s.swift_version = '5.0'
|
||||
|
||||
s.script_phase = {
|
||||
:name => 'Build Rust library',
|
||||
# First argument is relative path to the `rust` folder, second is name of rust library
|
||||
:script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../../rust rust_lib_photos',
|
||||
:execution_position => :before_compile,
|
||||
:input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'],
|
||||
# Let XCode know that the static library referenced in -force_load below is
|
||||
# created by this build step.
|
||||
:output_files => ["${BUILT_PRODUCTS_DIR}/librust_lib_photos.a"],
|
||||
}
|
||||
s.pod_target_xcconfig = {
|
||||
'DEFINES_MODULE' => 'YES',
|
||||
# Flutter.framework does not contain a i386 slice.
|
||||
'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386',
|
||||
'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib_photos.a',
|
||||
}
|
||||
end
|
||||
19
mobile/apps/photos/rust_builder/linux/CMakeLists.txt
Normal file
19
mobile/apps/photos/rust_builder/linux/CMakeLists.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
# The Flutter tooling requires that developers have CMake 3.10 or later
|
||||
# installed. You should not increase this version, as doing so will cause
|
||||
# the plugin to fail to compile for some customers of the plugin.
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
# Project-level configuration.
|
||||
set(PROJECT_NAME "rust_lib_photos")
|
||||
project(${PROJECT_NAME} LANGUAGES CXX)
|
||||
|
||||
include("../cargokit/cmake/cargokit.cmake")
|
||||
apply_cargokit(${PROJECT_NAME} ../../rust rust_lib_photos "")
|
||||
|
||||
# List of absolute paths to libraries that should be bundled with the plugin.
|
||||
# This list could contain prebuilt libraries, or libraries created by an
|
||||
# external build triggered from this build file.
|
||||
set(rust_lib_photos_bundled_libraries
|
||||
"${${PROJECT_NAME}_cargokit_lib}"
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
// This is an empty file to force CocoaPods to create a framework.
|
||||
@@ -0,0 +1,44 @@
|
||||
#
|
||||
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||
# Run `pod lib lint rust_lib_photos.podspec` to validate before publishing.
|
||||
#
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'rust_lib_photos'
|
||||
s.version = '0.0.1'
|
||||
s.summary = 'A new Flutter FFI plugin project.'
|
||||
s.description = <<-DESC
|
||||
A new Flutter FFI plugin project.
|
||||
DESC
|
||||
s.homepage = 'http://example.com'
|
||||
s.license = { :file => '../LICENSE' }
|
||||
s.author = { 'Your Company' => 'email@example.com' }
|
||||
|
||||
# This will ensure the source files in Classes/ are included in the native
|
||||
# builds of apps using this FFI plugin. Podspec does not support relative
|
||||
# paths, so Classes contains a forwarder C file that relatively imports
|
||||
# `../src/*` so that the C sources can be shared among all target platforms.
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'Classes/**/*'
|
||||
s.dependency 'FlutterMacOS'
|
||||
|
||||
s.platform = :osx, '10.11'
|
||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
||||
s.swift_version = '5.0'
|
||||
|
||||
s.script_phase = {
|
||||
:name => 'Build Rust library',
|
||||
# First argument is relative path to the `rust` folder, second is name of rust library
|
||||
:script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../../rust rust_lib_photos',
|
||||
:execution_position => :before_compile,
|
||||
:input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'],
|
||||
# Let XCode know that the static library referenced in -force_load below is
|
||||
# created by this build step.
|
||||
:output_files => ["${BUILT_PRODUCTS_DIR}/librust_lib_photos.a"],
|
||||
}
|
||||
s.pod_target_xcconfig = {
|
||||
'DEFINES_MODULE' => 'YES',
|
||||
# Flutter.framework does not contain a i386 slice.
|
||||
'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386',
|
||||
'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib_photos.a',
|
||||
}
|
||||
end
|
||||
34
mobile/apps/photos/rust_builder/pubspec.yaml
Normal file
34
mobile/apps/photos/rust_builder/pubspec.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
name: rust_lib_photos
|
||||
description: "Utility to build Rust code"
|
||||
version: 0.0.1
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
flutter: '>=3.3.0'
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
plugin_platform_interface: ^2.0.2
|
||||
|
||||
dev_dependencies:
|
||||
ffi: ^2.0.2
|
||||
ffigen: ^11.0.0
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.0
|
||||
|
||||
flutter:
|
||||
plugin:
|
||||
platforms:
|
||||
android:
|
||||
ffiPlugin: true
|
||||
ios:
|
||||
ffiPlugin: true
|
||||
linux:
|
||||
ffiPlugin: true
|
||||
macos:
|
||||
ffiPlugin: true
|
||||
windows:
|
||||
ffiPlugin: true
|
||||
17
mobile/apps/photos/rust_builder/windows/.gitignore
vendored
Normal file
17
mobile/apps/photos/rust_builder/windows/.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
flutter/
|
||||
|
||||
# Visual Studio user-specific files.
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Visual Studio build-related files.
|
||||
x64/
|
||||
x86/
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
20
mobile/apps/photos/rust_builder/windows/CMakeLists.txt
Normal file
20
mobile/apps/photos/rust_builder/windows/CMakeLists.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
# The Flutter tooling requires that developers have a version of Visual Studio
|
||||
# installed that includes CMake 3.14 or later. You should not increase this
|
||||
# version, as doing so will cause the plugin to fail to compile for some
|
||||
# customers of the plugin.
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
# Project-level configuration.
|
||||
set(PROJECT_NAME "rust_lib_photos")
|
||||
project(${PROJECT_NAME} LANGUAGES CXX)
|
||||
|
||||
include("../cargokit/cmake/cargokit.cmake")
|
||||
apply_cargokit(${PROJECT_NAME} ../../../../../../rust rust_lib_photos "")
|
||||
|
||||
# List of absolute paths to libraries that should be bundled with the plugin.
|
||||
# This list could contain prebuilt libraries, or libraries created by an
|
||||
# external build triggered from this build file.
|
||||
set(rust_lib_photos_bundled_libraries
|
||||
"${${PROJECT_NAME}_cargokit_lib}"
|
||||
PARENT_SCOPE
|
||||
)
|
||||
3
mobile/apps/photos/test_driver/integration_test.dart
Normal file
3
mobile/apps/photos/test_driver/integration_test.dart
Normal file
@@ -0,0 +1,3 @@
|
||||
import 'package:integration_test/integration_test_driver.dart';
|
||||
|
||||
Future<void> main() => integrationDriver();
|
||||
Reference in New Issue
Block a user