mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 15:17:07 +00:00
[vm/ffi] Fix prepare_flutter_bundle.dart
It now includes /ffi_test_functions_generated.cc and prints instructions on how to use the generated bundle. This CL adds some TODOs and deletes the non-migrated copy that was in the NNBD folder. Finally, this CL disable a check that fails because of static linking on iOS. Issue https://github.com/dart-lang/sdk/issues/39570. Change-Id: Ie85d92568bb95eaafd2991725a1a31cdecd246be Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/169402 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
parent
31c065ef4c
commit
312a572ade
4 changed files with 43 additions and 214 deletions
|
@ -7,6 +7,7 @@
|
||||||
// SharedObjects=ffi_test_functions
|
// SharedObjects=ffi_test_functions
|
||||||
|
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
|
import 'dart:io';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
|
||||||
import "package:expect/expect.dart";
|
import "package:expect/expect.dart";
|
||||||
|
@ -29,7 +30,9 @@ void main() async {
|
||||||
final receivePort = ReceivePort();
|
final receivePort = ReceivePort();
|
||||||
Isolate.spawn(secondIsolateMain, receivePort.sendPort);
|
Isolate.spawn(secondIsolateMain, receivePort.sendPort);
|
||||||
await receivePort.first;
|
await receivePort.first;
|
||||||
Expect.equals(42, getGlobalVar());
|
if (!Platform.isIOS /* Static linking causes different behavior. */) {
|
||||||
|
Expect.equals(42, getGlobalVar());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void secondIsolateMain(SendPort sendPort) {
|
void secondIsolateMain(SendPort sendPort) {
|
||||||
|
|
|
@ -1,204 +0,0 @@
|
||||||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. 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:io';
|
|
||||||
|
|
||||||
import 'package:path/path.dart' as path;
|
|
||||||
import 'package:args/args.dart';
|
|
||||||
|
|
||||||
main(List<String> args) async {
|
|
||||||
if (args.length != 1) {
|
|
||||||
print('Usage ${Platform.executable} ${Platform.script} <output-dir>');
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
final sdkRoot =
|
|
||||||
path.canonicalize(path.join(Platform.script.path, '../../..'));
|
|
||||||
final flutterTestsDir = args.single;
|
|
||||||
|
|
||||||
print('Using SDK root: $sdkRoot');
|
|
||||||
final testFiles = <String>[];
|
|
||||||
final failedOrTimedOut = <String>[];
|
|
||||||
final filteredTests = <String>[];
|
|
||||||
await for (final testFile in listTestFiles(sdkRoot, filteredTests)) {
|
|
||||||
final duration = await run(sdkRoot, testFile);
|
|
||||||
if (duration != null && duration.inSeconds < 5) {
|
|
||||||
testFiles.add(testFile);
|
|
||||||
} else {
|
|
||||||
failedOrTimedOut.add(testFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
testFiles.sort();
|
|
||||||
failedOrTimedOut.sort();
|
|
||||||
filteredTests.sort();
|
|
||||||
|
|
||||||
dumpTestList(testFiles, 'The following tests will be included:');
|
|
||||||
dumpTestList(failedOrTimedOut,
|
|
||||||
'The following tests will be excluded due to timeout or test failure:');
|
|
||||||
dumpTestList(
|
|
||||||
filteredTests,
|
|
||||||
'The following tests were filtered due to using '
|
|
||||||
'dart_api.h/async/DynamicLibrary.{process,executable}/...');
|
|
||||||
|
|
||||||
final allFiles = <String>{};
|
|
||||||
allFiles.add(path.join(sdkRoot, 'pkg/expect/lib/expect.dart'));
|
|
||||||
for (final testFile in testFiles) {
|
|
||||||
allFiles.add(testFile);
|
|
||||||
await addImportedFilesTo(allFiles, testFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
await generateCleanDir(flutterTestsDir);
|
|
||||||
|
|
||||||
final dartTestsDir = path.join(flutterTestsDir, 'lib/src/generated');
|
|
||||||
await generateDartTests(dartTestsDir, allFiles, testFiles);
|
|
||||||
|
|
||||||
final ccDir = path.join(flutterTestsDir, 'ios/Classes');
|
|
||||||
await generateCLibs(sdkRoot, ccDir, allFiles, testFiles);
|
|
||||||
|
|
||||||
print('');
|
|
||||||
print('Please copy generated files into FFI flutter test application');
|
|
||||||
print(' * $dartTestsDir');
|
|
||||||
print(' * $ccDir');
|
|
||||||
}
|
|
||||||
|
|
||||||
void dumpTestList(List<String> testFiles, String message) {
|
|
||||||
if (testFiles.isEmpty) return;
|
|
||||||
|
|
||||||
print(message);
|
|
||||||
for (final testFile in testFiles) {
|
|
||||||
print(' ${path.basename(testFile)}');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final importRegExp = RegExp(r'''^import.*['"](.+)['"].*;''');
|
|
||||||
|
|
||||||
Future addImportedFilesTo(Set<String> allFiles, String testFile) async {
|
|
||||||
final content = await File(testFile).readAsString();
|
|
||||||
for (final line in content.split('\n')) {
|
|
||||||
final match = importRegExp.matchAsPrefix(line);
|
|
||||||
if (match != null) {
|
|
||||||
final filename = match.group(1);
|
|
||||||
if (!filename.contains('dart:') &&
|
|
||||||
!filename.contains('package:expect') &&
|
|
||||||
!filename.contains('package:ffi')) {
|
|
||||||
final importedFile = Uri.file(testFile).resolve(filename).toFilePath();
|
|
||||||
if (allFiles.add(importedFile)) {
|
|
||||||
addImportedFilesTo(allFiles, importedFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future generateCLibs(String sdkRoot, String destDir, Set<String> allFiles,
|
|
||||||
List<String> testFiles) async {
|
|
||||||
final dir = await generateCleanDir(destDir);
|
|
||||||
|
|
||||||
String destinationFile;
|
|
||||||
|
|
||||||
final lib1 =
|
|
||||||
path.join(sdkRoot, 'runtime/bin/ffi_test/ffi_test_dynamic_library.cc');
|
|
||||||
destinationFile =
|
|
||||||
path.join(dir.path, path.basename(lib1)).replaceAll('.cc', '.cpp');
|
|
||||||
File(destinationFile).writeAsStringSync(File(lib1).readAsStringSync());
|
|
||||||
|
|
||||||
final lib2 = path.join(sdkRoot, 'runtime/bin/ffi_test/ffi_test_functions.cc');
|
|
||||||
destinationFile =
|
|
||||||
path.join(dir.path, path.basename(lib2)).replaceAll('.cc', '.cpp');
|
|
||||||
File(destinationFile).writeAsStringSync(File(lib2).readAsStringSync());
|
|
||||||
}
|
|
||||||
|
|
||||||
String cleanDart(String content) {
|
|
||||||
return content.replaceAll('package:expect/expect.dart', 'expect.dart');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future generateDartTests(
|
|
||||||
String destDir, Set<String> allFiles, List<String> testFiles) async {
|
|
||||||
final dir = await generateCleanDir(destDir);
|
|
||||||
|
|
||||||
final sink = File(path.join(dir.path, 'all.dart')).openWrite();
|
|
||||||
sink.writeln('import "dart:async";');
|
|
||||||
sink.writeln('');
|
|
||||||
for (int i = 0; i < testFiles.length; ++i) {
|
|
||||||
sink.writeln('import "${path.basename(testFiles[i])}" as main$i;');
|
|
||||||
}
|
|
||||||
sink.writeln('');
|
|
||||||
sink.writeln('Future invoke(dynamic fun) async {');
|
|
||||||
sink.writeln(' if (fun is void Function() || fun is Future Function()) {');
|
|
||||||
sink.writeln(' return await fun();');
|
|
||||||
sink.writeln(' } else {');
|
|
||||||
sink.writeln(' return await fun(<String>[]);');
|
|
||||||
sink.writeln(' }');
|
|
||||||
sink.writeln('}');
|
|
||||||
sink.writeln('');
|
|
||||||
sink.writeln('dynamic main() async {');
|
|
||||||
for (int i = 0; i < testFiles.length; ++i) {
|
|
||||||
sink.writeln(' await invoke(main$i.main);');
|
|
||||||
}
|
|
||||||
sink.writeln('}');
|
|
||||||
await sink.close();
|
|
||||||
|
|
||||||
for (final file in allFiles) {
|
|
||||||
File(path.join(dir.path, path.basename(file)))
|
|
||||||
.writeAsStringSync(cleanDart(File(file).readAsStringSync()));
|
|
||||||
}
|
|
||||||
|
|
||||||
File(path.join(dir.path, 'dylib_utils.dart')).writeAsStringSync('''
|
|
||||||
import 'dart:ffi' as ffi;
|
|
||||||
import 'dart:io' show Platform;
|
|
||||||
|
|
||||||
ffi.DynamicLibrary dlopenPlatformSpecific(String name, {String path}) {
|
|
||||||
return Platform.isAndroid
|
|
||||||
? ffi.DynamicLibrary.open('libffi_tests.so')
|
|
||||||
: ffi.DynamicLibrary.process();
|
|
||||||
}
|
|
||||||
''');
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream<String> listTestFiles(
|
|
||||||
String sdkRoot, List<String> filteredTests) async* {
|
|
||||||
await for (final file in Directory(path.join(sdkRoot, 'tests/ffi')).list()) {
|
|
||||||
if (file is File && file.path.endsWith('_test.dart')) {
|
|
||||||
// These tests are VM specific and cannot necessarily be run on Flutter.
|
|
||||||
if (path.basename(file.path).startsWith('vmspecific_')) {
|
|
||||||
filteredTests.add(file.path);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// These tests use special features which are hard to test on Flutter.
|
|
||||||
final contents = file.readAsStringSync();
|
|
||||||
if (contents.contains(RegExp('//# .* compile-time error')) ||
|
|
||||||
contents.contains('DynamicLibrary.process') ||
|
|
||||||
contents.contains('DynamicLibrary.executable')) {
|
|
||||||
filteredTests.add(file.path);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
yield file.path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Duration> run(String sdkRoot, String testFile) async {
|
|
||||||
final env = Map<String, String>.from(Platform.environment);
|
|
||||||
env['LD_LIBRARY_PATH'] = path.join(sdkRoot, 'out/ReleaseX64');
|
|
||||||
final sw = Stopwatch()..start();
|
|
||||||
final Process process = await Process.start(
|
|
||||||
Platform.executable, <String>[testFile],
|
|
||||||
environment: env);
|
|
||||||
final timer = Timer(const Duration(seconds: 3), () => process.kill());
|
|
||||||
process.stdout.listen((_) {});
|
|
||||||
process.stderr.listen((_) {});
|
|
||||||
if (await process.exitCode != 0) return null;
|
|
||||||
timer.cancel();
|
|
||||||
return sw.elapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Directory> generateCleanDir(String dirname) async {
|
|
||||||
final directory = Directory(dirname);
|
|
||||||
if (await directory.exists()) {
|
|
||||||
await directory.delete(recursive: true);
|
|
||||||
}
|
|
||||||
await directory.create(recursive: true);
|
|
||||||
return directory;
|
|
||||||
}
|
|
|
@ -7,6 +7,7 @@
|
||||||
// SharedObjects=ffi_test_functions
|
// SharedObjects=ffi_test_functions
|
||||||
|
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
|
import 'dart:io';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
|
||||||
import "package:expect/expect.dart";
|
import "package:expect/expect.dart";
|
||||||
|
@ -29,7 +30,9 @@ void main() async {
|
||||||
final receivePort = ReceivePort();
|
final receivePort = ReceivePort();
|
||||||
Isolate.spawn(secondIsolateMain, receivePort.sendPort);
|
Isolate.spawn(secondIsolateMain, receivePort.sendPort);
|
||||||
await receivePort.first;
|
await receivePort.first;
|
||||||
Expect.equals(42, getGlobalVar());
|
if (!Platform.isIOS /* Static linking causes different behavior. */) {
|
||||||
|
Expect.equals(42, getGlobalVar());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void secondIsolateMain(SendPort sendPort) {
|
void secondIsolateMain(SendPort sendPort) {
|
||||||
|
|
|
@ -6,7 +6,9 @@ import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
import 'package:args/args.dart';
|
|
||||||
|
// TODO(dacoharkes): Migrate script to be nullsafe and generate nullsafe
|
||||||
|
// Flutter app.
|
||||||
|
|
||||||
main(List<String> args) async {
|
main(List<String> args) async {
|
||||||
if (args.length != 1) {
|
if (args.length != 1) {
|
||||||
|
@ -51,16 +53,34 @@ main(List<String> args) async {
|
||||||
|
|
||||||
await generateCleanDir(flutterTestsDir);
|
await generateCleanDir(flutterTestsDir);
|
||||||
|
|
||||||
final dartTestsDir = path.join(flutterTestsDir, 'lib/src/generated');
|
final dartTestsDirRelative = 'example/lib';
|
||||||
|
final dartTestsDir = path.join(flutterTestsDir, dartTestsDirRelative);
|
||||||
await generateDartTests(dartTestsDir, allFiles, testFiles);
|
await generateDartTests(dartTestsDir, allFiles, testFiles);
|
||||||
|
|
||||||
final ccDir = path.join(flutterTestsDir, 'ios/Classes');
|
final ccDirRelative = 'ios/Classes';
|
||||||
|
final ccDir = path.join(flutterTestsDir, ccDirRelative);
|
||||||
await generateCLibs(sdkRoot, ccDir, allFiles, testFiles);
|
await generateCLibs(sdkRoot, ccDir, allFiles, testFiles);
|
||||||
|
|
||||||
print('');
|
print('''
|
||||||
print('Please copy generated files into FFI flutter test application');
|
|
||||||
print(' * $dartTestsDir');
|
Files generated in:
|
||||||
print(' * $ccDir');
|
* $dartTestsDir
|
||||||
|
* $ccDir
|
||||||
|
|
||||||
|
Generate flutter test application with:
|
||||||
|
flutter create --platforms=android,ios --template=plugin <ffi_test_app_name>
|
||||||
|
|
||||||
|
Please copy generated files into FFI flutter test application:
|
||||||
|
cd <ffi_test_app_dir> && cp -r $flutterTestsDir ./
|
||||||
|
|
||||||
|
After copying modify the test application:
|
||||||
|
* Modify example/pubspec.yaml to depend on package:ffi.
|
||||||
|
* Modify example/lib/main.dart to invoke all.dart while rendering.
|
||||||
|
* Open example/ios/Runner.xcworkspace in Xcode.
|
||||||
|
* Add the cpp files to Pods/Development Pods/<deep nesting>/ios/Classes
|
||||||
|
to ensure they are statically linked to the app.
|
||||||
|
''');
|
||||||
|
// TODO(dacoharkes): Automate these steps. How to automate the XCode step?
|
||||||
}
|
}
|
||||||
|
|
||||||
void dumpTestList(List<String> testFiles, String message) {
|
void dumpTestList(List<String> testFiles, String message) {
|
||||||
|
@ -108,6 +128,12 @@ Future generateCLibs(String sdkRoot, String destDir, Set<String> allFiles,
|
||||||
destinationFile =
|
destinationFile =
|
||||||
path.join(dir.path, path.basename(lib2)).replaceAll('.cc', '.cpp');
|
path.join(dir.path, path.basename(lib2)).replaceAll('.cc', '.cpp');
|
||||||
File(destinationFile).writeAsStringSync(File(lib2).readAsStringSync());
|
File(destinationFile).writeAsStringSync(File(lib2).readAsStringSync());
|
||||||
|
|
||||||
|
final lib3 = path.join(
|
||||||
|
sdkRoot, 'runtime/bin/ffi_test/ffi_test_functions_generated.cc');
|
||||||
|
destinationFile =
|
||||||
|
path.join(dir.path, path.basename(lib3)).replaceAll('.cc', '.cpp');
|
||||||
|
File(destinationFile).writeAsStringSync(File(lib3).readAsStringSync());
|
||||||
}
|
}
|
||||||
|
|
||||||
String cleanDart(String content) {
|
String cleanDart(String content) {
|
||||||
|
@ -159,7 +185,8 @@ ffi.DynamicLibrary dlopenPlatformSpecific(String name, {String path}) {
|
||||||
|
|
||||||
Stream<String> listTestFiles(
|
Stream<String> listTestFiles(
|
||||||
String sdkRoot, List<String> filteredTests) async* {
|
String sdkRoot, List<String> filteredTests) async* {
|
||||||
await for (final file in Directory(path.join(sdkRoot, 'tests/ffi')).list()) {
|
await for (final file
|
||||||
|
in Directory(path.join(sdkRoot, 'tests/ffi_2')).list()) {
|
||||||
if (file is File && file.path.endsWith('_test.dart')) {
|
if (file is File && file.path.endsWith('_test.dart')) {
|
||||||
// These tests are VM specific and cannot necessarily be run on Flutter.
|
// These tests are VM specific and cannot necessarily be run on Flutter.
|
||||||
if (path.basename(file.path).startsWith('vmspecific_')) {
|
if (path.basename(file.path).startsWith('vmspecific_')) {
|
||||||
|
|
Loading…
Reference in a new issue