Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native assets support for MacOS and iOS #130494

Merged
merged 150 commits into from
Sep 10, 2023
Merged
Show file tree
Hide file tree
Changes from 148 commits
Commits
Show all changes
150 commits
Select commit Hold shift + click to select a range
e5fb66f
Native assets support for MacOS and iOS
dcharkes Jul 13, 2023
135e38a
Merge branch 'master' into native-assets-macos-ios
dcharkes Jul 14, 2023
10f83bb
Updated pubspecs
dcharkes Jul 14, 2023
8654ec5
Fix analyzer warnings
dcharkes Jul 14, 2023
cbbcc83
fix comment
dcharkes Jul 14, 2023
bf0bdeb
Fix printing native assets as null
dcharkes Jul 14, 2023
83b9371
Don't pass empty native asset paths on
dcharkes Jul 14, 2023
56062b4
Check native assets folder exists before rsync
dcharkes Jul 14, 2023
7f21013
fix whitespace
dcharkes Jul 14, 2023
376a620
Move shared test infra to separate file
dcharkes Jul 14, 2023
07bcd34
Use mocked FileSystem in BuildIOS commands
dcharkes Jul 14, 2023
257eb92
fix test mock file system
dcharkes Jul 14, 2023
f48fc79
Fix another test mock file system
dcharkes Jul 14, 2023
038735c
Skip test on Linux and Windows
dcharkes Jul 14, 2023
f4f268e
fix analysis
dcharkes Jul 14, 2023
30df706
Don't try to build native assets if there is no package config + fix …
dcharkes Jul 14, 2023
a8c85d2
remove unused imports
dcharkes Jul 14, 2023
361fa9c
fix test
dcharkes Jul 14, 2023
eb28e4c
print stdout and stderr in test on failure
dcharkes Jul 14, 2023
8de8782
fix test
dcharkes Jul 14, 2023
3279d40
Merge commit 'f212b1012de72d5a8e041f768e0a9026b919060d' into native-a…
dcharkes Jul 20, 2023
c36824f
Run `flutter update-packages --force-upgrade`.
dcharkes Jul 20, 2023
24d6716
Merge branch 'main' into native-assets-macos-ios
dcharkes Jul 24, 2023
b71ebc7
Run `flutter update-packages --force-upgrade`
dcharkes Jul 24, 2023
07b4b4a
Add ios devicelab tests to ci
dcharkes Jul 24, 2023
5857b58
address small comments
dcharkes Jul 24, 2023
bb8ba84
Try fixing CI
dcharkes Jul 24, 2023
471ed3e
more formatting
dcharkes Jul 24, 2023
fcea77c
replace `assert(` with `throwToolExit(`
dcharkes Jul 24, 2023
159c061
formatting
dcharkes Jul 24, 2023
294744b
Don't rsync if no native assets dir
dcharkes Jul 24, 2023
380a33c
test owners
dcharkes Jul 24, 2023
ecefcab
Use `--no-codesign` for non-devicelab test
dcharkes Jul 24, 2023
8103cba
Remove mock checking hack
dcharkes Jul 25, 2023
67919f2
Follow multi-step ci.yaml plan
dcharkes Jul 25, 2023
b9491ef
Merge template create logic
dcharkes Jul 25, 2023
3f70e7b
Fix template readme
dcharkes Jul 25, 2023
30edbef
Don't generate OS-files for ffi packages
dcharkes Jul 25, 2023
015b7aa
Use configurable build directory
dcharkes Jul 25, 2023
f44bace
Fix FFI plugin create
dcharkes Jul 25, 2023
c391665
Support `flutter run -d all`
dcharkes Jul 25, 2023
3e5320f
Fix formatting
dcharkes Jul 25, 2023
747b7d8
Set required environment variable for backend test
dcharkes Jul 25, 2023
6147b40
Remove copyright from template
dcharkes Jul 25, 2023
0305c7c
Remove timeout override
dcharkes Jul 25, 2023
24dcc6d
Add create tests.
dcharkes Jul 25, 2023
265c9c8
compile test
dcharkes Jul 25, 2023
4c30314
features test
dcharkes Jul 25, 2023
aef678a
Fix formatting
dcharkes Jul 26, 2023
afb48a2
fix formatting
dcharkes Jul 26, 2023
1a64144
Merge branch 'main' into native-assets-macos-ios
dcharkes Jul 26, 2023
fb3ea8f
native asset target unit tests
dcharkes Jul 26, 2023
072e248
native asset dry run and build unit tests
dcharkes Jul 26, 2023
dbd63d9
skip justification comments
dcharkes Jul 26, 2023
3bb4a8f
remove skipped tests
dcharkes Jul 26, 2023
d55b550
Make module_test_ios work locally
dcharkes Jul 26, 2023
7fa8d19
Introduce a wrapper class for `NativeAssetsBuildRunner`
dcharkes Jul 27, 2023
86c9fa7
Unit test xcconfig native assets
dcharkes Jul 27, 2023
4d9902d
Add `NativeAssetsBuildRunner` to `Command`s and add tests
dcharkes Jul 27, 2023
f44ad01
Run hot native assets test
dcharkes Jul 27, 2023
dd37e5f
Fix analysis
dcharkes Jul 27, 2023
1334dcf
implement `packagesWithNativeAssets`
dcharkes Jul 27, 2023
84a513e
fix formatting and imports
dcharkes Jul 27, 2023
f4c963c
Mock xcrun clang --version process calls in tests
dcharkes Jul 27, 2023
b836799
Print file contents in test to see why Windows fails
dcharkes Jul 27, 2023
c765865
Try fixing test on Window
dcharkes Jul 27, 2023
fc8f5a8
Merge branch 'main' into native-assets-macos-ios
dcharkes Jul 27, 2023
30a215f
Fix another path in tests on Windows
dcharkes Jul 27, 2023
4a5a1a9
Run hot test
dcharkes Jul 27, 2023
d65f843
Stop reusing dep object for now
dcharkes Jul 27, 2023
eb22b3f
Merge branch 'main' into native-assets-macos-ios
dcharkes Jul 28, 2023
205abc6
Add native assets to module_test_ios
dcharkes Jul 28, 2023
3c0d78a
Enable native assets in module_test_ios
dcharkes Jul 28, 2023
0579167
Fix bundling in add2app scenario
dcharkes Jul 28, 2023
220a98d
Merge branch 'main' into native-assets-macos-ios
dcharkes Jul 31, 2023
6518778
Explore passing `native_assets.yaml` between targets
dcharkes Jul 31, 2023
d02dc7c
Fix analysis issues
dcharkes Jul 31, 2023
1e51dbd
Try using dependencies for native_assets_builder
dcharkes Aug 2, 2023
fbb104f
Merge branch 'main' into native-assets-macos-ios
dcharkes Aug 3, 2023
44d53c6
Run `flutter update-packages --force-upgrade`
dcharkes Aug 3, 2023
2523075
Fix analysis
dcharkes Aug 3, 2023
0b5cc4d
bump dependency to published version
dcharkes Aug 8, 2023
98cec9d
Merge branch 'main' into native-assets-macos-ios
dcharkes Aug 8, 2023
dc00450
Run `flutter update-packages --force-upgrade`
dcharkes Aug 8, 2023
3ef0202
Undo `flutter update-packages --force-upgrade`
dcharkes Aug 8, 2023
8f85fbc
Run regen hash
dcharkes Aug 8, 2023
b6e907e
Bump template dependencies
dcharkes Aug 9, 2023
576476f
Merge branch 'main' into native-assets-macos-ios
dcharkes Aug 9, 2023
279033d
Fix template
dcharkes Aug 9, 2023
ebb3178
Stop passing a `native_assets.yaml` path to flutter assemble
dcharkes Aug 9, 2023
f0833da
Add logging around `native_assets.yaml` existing
dcharkes Aug 9, 2023
8bc18e9
Ensure `native_assets.yaml` exists in unit test
dcharkes Aug 9, 2023
2d49e8b
Try figuring out why `native_assets.yaml` is not being created in `Na…
dcharkes Aug 9, 2023
1f31279
Fix analysis
dcharkes Aug 9, 2023
6615cbe
Ensure writing a `native_assets.yaml` in MacOS if native assets is no…
dcharkes Aug 9, 2023
c6eff93
Test add2app bundles dylibs
dcharkes Aug 9, 2023
68a7e08
Call FFI in add2app
dcharkes Aug 9, 2023
bf79b41
Remove FFI call from add2app UI
dcharkes Aug 9, 2023
ed5e6ef
Merge commit 'bb6b72d6c66f62e2a13569241f4c7c711b5c5e9c' into native-a…
dcharkes Aug 10, 2023
eb2328d
Mock CCompilerConfig so tests can be randomized
dcharkes Aug 10, 2023
439e49e
Unit test NativeAssets target
dcharkes Aug 10, 2023
b32c6ab
Fix analysis
dcharkes Aug 10, 2023
efcb4c1
Late initialize `cCompilerConfig` to avoid eagerly running
dcharkes Aug 10, 2023
bf01584
Merge branch 'main' into native-assets-macos-ios
dcharkes Aug 17, 2023
c67a797
Fix imports after merge
dcharkes Aug 17, 2023
407e4b1
Merge branch 'main' into native-assets-macos-ios
dcharkes Aug 30, 2023
0108ff4
address comments
dcharkes Aug 30, 2023
4881a22
Move FakeNativeAssetsBuildRunner to test dir
dcharkes Aug 30, 2023
8f5b9fb
Move cCompilerConfig for macOS to macOS file
dcharkes Aug 30, 2023
c05da58
fix xcode_backend.dart
dcharkes Aug 30, 2023
96bdfa5
address comments
dcharkes Aug 30, 2023
92d48df
rename var
dcharkes Aug 30, 2023
4e264ca
Move shared logic out of macos/native_assets.dart
dcharkes Aug 30, 2023
927f62b
split generate FFI plugin and package
dcharkes Aug 30, 2023
79ddeb7
address comment
dcharkes Aug 30, 2023
b28fea9
Remove platforms argument from create commands
dcharkes Aug 30, 2023
db0126d
Stop generating os-specific folders
dcharkes Aug 30, 2023
8927a83
Remove inDirectory
dcharkes Aug 30, 2023
0136067
Fix formatting
dcharkes Aug 30, 2023
7af5f2f
Gate test behind isMacOS
dcharkes Aug 30, 2023
430c287
address comments
dcharkes Aug 30, 2023
381d764
expect process calls in test
dcharkes Aug 30, 2023
e0023a9
Expect no process invocations in test
dcharkes Aug 30, 2023
f7014af
Static libraries error test
dcharkes Aug 30, 2023
712cae1
Add test expectations
dcharkes Aug 30, 2023
fae782f
hot runner test exception
dcharkes Aug 30, 2023
523b3dd
Skip tests with mac process invocations on windows
dcharkes Aug 31, 2023
9120cbb
Gate flutter create --template=package_ffi behind experimental flag
dcharkes Aug 31, 2023
1a28977
Remove `// UNCOMMENT: ` stuff from module test
dcharkes Aug 31, 2023
25c8c38
Don't cache allowed templates for create command
dcharkes Sep 1, 2023
e483d95
Formatting assignment no newlines
dcharkes Sep 1, 2023
6e5528c
Hand format `=>`s
dcharkes Sep 1, 2023
908e79a
`MacOS` and `iOS` spelling in method names
dcharkes Sep 1, 2023
d8f52c0
Lint on same line
dcharkes Sep 1, 2023
db6e954
Fix formatting
dcharkes Sep 1, 2023
010424b
Merge branch 'main' into native-assets-macos-ios
dcharkes Sep 1, 2023
80c23af
Remove if
dcharkes Sep 1, 2023
abdb553
Revert "Remove if"
dcharkes Sep 1, 2023
f36293d
Add comment
dcharkes Sep 1, 2023
5591c1f
Merge branch 'main' into native-assets-macos-ios
dcharkes Sep 4, 2023
a3b9885
Address comments
dcharkes Sep 6, 2023
17c4f9d
Merge branch 'main' into native-assets-macos-ios
dcharkes Sep 6, 2023
75e033b
Address comment
dcharkes Sep 6, 2023
39b63f1
Merge branch 'main' into native-assets-macos-ios
dcharkes Sep 6, 2023
e546a67
Merge branch 'master' into native-assets-macos-ios
christopherfujino Sep 6, 2023
1124075
Merge branch 'main' into native-assets-macos-ios
dcharkes Sep 7, 2023
27bf22d
Merge branch 'main' into native-assets-macos-ios
dcharkes Sep 7, 2023
6d68824
Merge branch 'main' into native-assets-macos-ios
dcharkes Sep 8, 2023
5504335
Move OS branching to native_assets.dart to run_hot.dart
dcharkes Sep 8, 2023
67a4338
Merge branch 'main' into native-assets-macos-ios
dcharkes Sep 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 20 additions & 0 deletions .ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4070,6 +4070,26 @@ targets:
["devicelab", "ios", "mac"]
task_name: microbenchmarks_ios

- name: Mac_ios native_assets_ios_simulator
recipe: devicelab/devicelab_drone
presubmit: false
bringup: true # TODO(dacoharkes): Set to false in follow up PR and check that test works on CI.
timeout: 60
properties:
tags: >
["devicelab", "ios", "mac"]
task_name: native_assets_ios_simulator

- name: Mac_ios native_assets_ios
recipe: devicelab/devicelab_drone
presubmit: false
bringup: true # TODO(dacoharkes): Set to false in follow up PR and check that test works on CI.
timeout: 60
properties:
tags: >
["devicelab", "ios", "mac"]
task_name: native_assets_ios

dcharkes marked this conversation as resolved.
Show resolved Hide resolved
- name: Mac_ios native_platform_view_ui_tests_ios
recipe: devicelab/devicelab_drone
presubmit: false
Expand Down
2 changes: 2 additions & 0 deletions TESTOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@
/dev/devicelab/bin/tasks/large_image_changer_perf_ios.dart @zanderso @flutter/engine
/dev/devicelab/bin/tasks/macos_chrome_dev_mode.dart @zanderso @flutter/tool
/dev/devicelab/bin/tasks/microbenchmarks_ios.dart @cyanglaz @flutter/engine
/dev/devicelab/bin/tasks/native_assets_ios_simulator.dart @dacoharkes @flutter/ios
/dev/devicelab/bin/tasks/native_assets_ios.dart @dacoharkes @flutter/ios
/dev/devicelab/bin/tasks/native_platform_view_ui_tests_ios.dart @hellohuanlin @flutter/ios
/dev/devicelab/bin/tasks/new_gallery_ios__transition_perf.dart @zanderso @flutter/engine
/dev/devicelab/bin/tasks/new_gallery_skia_ios__transition_perf.dart @zanderso @flutter/engine
Expand Down
65 changes: 62 additions & 3 deletions dev/devicelab/bin/tasks/module_test_ios.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,36 @@ Future<void> main() async {
final File marquee = File(path.join(flutterModuleLibSource.path, 'marquee'));
marquee.copySync(path.join(flutterModuleLibDestination.path, 'marquee.dart'));

section('Create package with native assets');

await flutter(
'config',
options: <String>['--enable-native-assets'],
);

const String ffiPackageName = 'ffi_package';
await _createFfiPackage(ffiPackageName, tempDir);

section('Add FFI package');

final File pubspec = File(path.join(projectDir.path, 'pubspec.yaml'));
String content = await pubspec.readAsString();
content = content.replaceFirst(
'dependencies:\n',
'''
dependencies:
$ffiPackageName:
path: ../$ffiPackageName
''',
);
await pubspec.writeAsString(content, flush: true);
await inDirectory(projectDir, () async {
await flutter(
'packages',
options: <String>['get'],
);
});

section('Build ephemeral host app in release mode without CocoaPods');

await inDirectory(projectDir, () async {
Expand Down Expand Up @@ -162,10 +192,8 @@ Future<void> main() async {

section('Add plugins');

final File pubspec = File(path.join(projectDir.path, 'pubspec.yaml'));
String content = await pubspec.readAsString();
content = content.replaceFirst(
'\ndependencies:\n',
'dependencies:\n',
// One framework, one Dart-only, one that does not support iOS, and one with a resource bundle.
'''
dependencies:
Expand Down Expand Up @@ -221,6 +249,11 @@ dependencies:
// Dart-only, no embedded framework.
checkDirectoryNotExists(path.join(ephemeralIOSHostApp.path, 'Frameworks', '$dartPluginName.framework'));

// Native assets embedded, no embedded framework.
const String libFfiPackageDylib = 'lib$ffiPackageName.dylib';
checkFileExists(path.join(ephemeralIOSHostApp.path, 'Frameworks', libFfiPackageDylib));
checkDirectoryNotExists(path.join(ephemeralIOSHostApp.path, 'Frameworks', '$ffiPackageName.framework'));

section('Clean and pub get module');

await inDirectory(projectDir, () async {
Expand Down Expand Up @@ -350,6 +383,11 @@ end
'isolate_snapshot_data',
));

checkFileExists(path.join(
hostFrameworksDirectory,
libFfiPackageDylib,
));

section('Check the NOTICE file is correct');

final String licenseFilePath = path.join(
Expand Down Expand Up @@ -449,6 +487,13 @@ end
throw TaskResult.failure('Unexpected armv7 architecture slice in $builtAppBinary');
}

// Check native assets are bundled.
checkFileExists(path.join(
archivedAppPath,
'Frameworks',
libFfiPackageDylib,
));

// The host app example builds plugins statically, url_launcher_ios.framework
// should not exist.
checkDirectoryNotExists(path.join(
Expand Down Expand Up @@ -685,3 +730,17 @@ class $dartPluginClass {
// Remove the native plugin code.
await Directory(path.join(pluginDir, 'ios')).delete(recursive: true);
}

Future<void> _createFfiPackage(String name, Directory parent) async {
await inDirectory(parent, () async {
await flutter(
'create',
options: <String>[
'--org',
'io.flutter.devicelab',
'--template=package_ffi',
name,
],
);
});
}
14 changes: 14 additions & 0 deletions dev/devicelab/bin/tasks/native_assets_ios.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter_devicelab/framework/devices.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/tasks/native_assets_test.dart';

Future<void> main() async {
dcharkes marked this conversation as resolved.
Show resolved Hide resolved
await task(() async {
deviceOperatingSystem = DeviceOperatingSystem.ios;
return createNativeAssetsTest()();
});
}
31 changes: 31 additions & 0 deletions dev/devicelab/bin/tasks/native_assets_ios_simulator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter_devicelab/framework/devices.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/ios.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/tasks/native_assets_test.dart';

Future<void> main() async {
dcharkes marked this conversation as resolved.
Show resolved Hide resolved
await task(() async {
deviceOperatingSystem = DeviceOperatingSystem.ios;
String? simulatorDeviceId;
try {
await testWithNewIOSSimulator(
'TestNativeAssetsSim',
(String deviceId) async {
simulatorDeviceId = deviceId;
await createNativeAssetsTest(
deviceIdOverride: deviceId,
isIosSimulator: true,
)();
},
);
} finally {
await removeIOSimulator(simulatorDeviceId);
}
return TaskResult.success(null);
});
}
191 changes: 191 additions & 0 deletions dev/devicelab/lib/tasks/native_assets_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:path/path.dart' as path;

import '../framework/devices.dart';
import '../framework/framework.dart';
import '../framework/task_result.dart';
import '../framework/utils.dart';

const String _packageName = 'package_with_native_assets';

const List<String> _buildModes = <String>[
'debug',
'profile',
'release',
];

TaskFunction createNativeAssetsTest({
String? deviceIdOverride,
bool checkAppRunningOnLocalDevice = true,
bool isIosSimulator = false,
}) {
return () async {
if (deviceIdOverride == null) {
final Device device = await devices.workingDevice;
await device.unlock();
deviceIdOverride = device.deviceId;
}

await enableNativeAssets();
dcharkes marked this conversation as resolved.
Show resolved Hide resolved

for (final String buildMode in _buildModes) {
if (buildMode != 'debug' && isIosSimulator) {
continue;
}
final TaskResult buildModeResult = await inTempDir((Directory tempDirectory) async {
final Directory packageDirectory = await createTestProject(_packageName, tempDirectory);
final Directory exampleDirectory = dir(packageDirectory.uri.resolve('example/').toFilePath());

final List<String> options = <String>[
'-d',
deviceIdOverride!,
'--no-android-gradle-daemon',
'--no-publish-port',
'--verbose',
'--uninstall-first',
'--$buildMode',
];
int transitionCount = 0;
bool done = false;

await inDirectory<void>(exampleDirectory, () async {
final int runFlutterResult = await runFlutter(
options: options,
onLine: (String line, Process process) {
if (done) {
return;
}
switch (transitionCount) {
case 0:
if (!line.contains('Flutter run key commands.')) {
return;
}
if (buildMode == 'debug') {
// Do a hot reload diff on the initial dill file.
process.stdin.writeln('r');
} else {
done = true;
process.stdin.writeln('q');
}
case 1:
if (!line.contains('Reloaded')) {
return;
}
process.stdin.writeln('R');
case 2:
// Do a hot restart, pushing a new complete dill file.
if (!line.contains('Restarted application')) {
return;
}
// Do another hot reload, pushing a diff to the second dill file.
process.stdin.writeln('r');
case 3:
if (!line.contains('Reloaded')) {
return;
}
done = true;
process.stdin.writeln('q');
}
transitionCount += 1;
},
);
if (runFlutterResult != 0) {
print('Flutter run returned non-zero exit code: $runFlutterResult.');
}
});

final int expectedNumberOfTransitions = buildMode == 'debug' ? 4 : 1;
if (transitionCount != expectedNumberOfTransitions) {
return TaskResult.failure(
'Did not get expected number of transitions: $transitionCount '
'(expected $expectedNumberOfTransitions)',
);
}
return TaskResult.success(null);
});
if (buildModeResult.failed) {
return buildModeResult;
}
}
return TaskResult.success(null);
};
}

Future<int> runFlutter({
required List<String> options,
required void Function(String, Process) onLine,
}) async {
final Process process = await startFlutter(
'run',
options: options,
);

final Completer<void> stdoutDone = Completer<void>();
final Completer<void> stderrDone = Completer<void>();
process.stdout.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen((String line) {
onLine(line, process);
print('stdout: $line');
}, onDone: stdoutDone.complete);

process.stderr.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen(
(String line) => print('stderr: $line'),
onDone: stderrDone.complete,
);

await Future.wait<void>(<Future<void>>[stdoutDone.future, stderrDone.future]);
final int exitCode = await process.exitCode;
return exitCode;
}

final String _flutterBin = path.join(flutterDirectory.path, 'bin', 'flutter');

Future<void> enableNativeAssets() async {
print('Enabling configs for native assets...');
final int configResult = await exec(
_flutterBin,
<String>[
'config',
'-v',
'--enable-native-assets',
],
canFail: true);
if (configResult != 0) {
print('Failed to enable configuration, tasks may not run.');
}
}

Future<Directory> createTestProject(
String packageName,
Directory tempDirectory,
) async {
final int createResult = await exec(
_flutterBin,
<String>[
'create',
'--template=package_ffi',
packageName,
],
workingDirectory: tempDirectory.path,
canFail: true,
);
assert(createResult == 0);

final Directory packageDirectory = Directory.fromUri(tempDirectory.uri.resolve('$packageName/'));
return packageDirectory;
}

Future<T> inTempDir<T>(Future<T> Function(Directory tempDirectory) fun) async {
final Directory tempDirectory = dir(Directory.systemTemp.createTempSync().resolveSymbolicLinksSync());
try {
return await fun(tempDirectory);
} finally {
tempDirectory.deleteSync(recursive: true);
}
}