Unverified Commit 8b8d368d authored by Chris Bracken's avatar Chris Bracken Committed by GitHub

Replace --prefer-shared-library with --build-shared-library (#17394)

This replaces the --prefer-shared-library flag, which falls back to
regular (non-shared-lib) compile if the NDK is not found, with the
--build-shared-library flag, which exits with an error message if the
NDK is not found.

This simplifies the set of allowed code paths through AOT compile,
resulting in better testability and easier-to-follow logic. It also
results in more predictable behaviour for continuous integration and
other scenarios.
parent d1e49bc6
...@@ -137,27 +137,35 @@ class AOTSnapshotter { ...@@ -137,27 +137,35 @@ class AOTSnapshotter {
@required String packagesPath, @required String packagesPath,
@required String outputPath, @required String outputPath,
@required bool previewDart2, @required bool previewDart2,
@required bool preferSharedLibrary, @required bool buildSharedLibrary,
IOSArch iosArch, IOSArch iosArch,
List<String> extraGenSnapshotOptions: const <String>[], List<String> extraGenSnapshotOptions: const <String>[],
}) async { }) async {
if (!_isValidAotPlatform(platform, buildMode)) { if (!_isValidAotPlatform(platform, buildMode)) {
printError('${getNameForTargetPlatform(platform)} does not support AOT compilation.'); printError('${getNameForTargetPlatform(platform)} does not support AOT compilation.');
return -1; return 1;
} }
// TODO(cbracken): replace IOSArch with TargetPlatform.ios_{armv7,arm64}. // TODO(cbracken): replace IOSArch with TargetPlatform.ios_{armv7,arm64}.
assert(platform != TargetPlatform.ios || iosArch != null); assert(platform != TargetPlatform.ios || iosArch != null);
final bool compileToSharedLibrary = preferSharedLibrary && androidSdk.ndkCompiler != null; // buildSharedLibrary is ignored for iOS builds.
if (preferSharedLibrary && !compileToSharedLibrary) { if (platform == TargetPlatform.ios)
printStatus('Could not find NDK compiler. Not building in shared library mode.'); buildSharedLibrary = false;
if (buildSharedLibrary && androidSdk.ndkCompiler == null) {
printError(
'Could not find NDK in Android SDK at ${androidSdk.directory}.\n'
'Unable to build with --build-shared-library\n'
'To install the NDK, see instructions at https://developer.android.com/ndk/guides/'
);
return 1;
} }
final PackageMap packageMap = new PackageMap(packagesPath); final PackageMap packageMap = new PackageMap(packagesPath);
final String packageMapError = packageMap.checkValid(); final String packageMapError = packageMap.checkValid();
if (packageMapError != null) { if (packageMapError != null) {
printError(packageMapError); printError(packageMapError);
return -2; return 1;
} }
final Directory outputDir = fs.directory(outputPath); final Directory outputDir = fs.directory(outputPath);
...@@ -198,7 +206,7 @@ class AOTSnapshotter { ...@@ -198,7 +206,7 @@ class AOTSnapshotter {
} }
final String assembly = fs.path.join(outputDir.path, 'snapshot_assembly.S'); final String assembly = fs.path.join(outputDir.path, 'snapshot_assembly.S');
if (compileToSharedLibrary || platform == TargetPlatform.ios) { if (buildSharedLibrary || platform == TargetPlatform.ios) {
// Assembly AOT snapshot. // Assembly AOT snapshot.
outputPaths.add(assembly); outputPaths.add(assembly);
genSnapshotArgs.add('--snapshot_kind=app-aot-assembly'); genSnapshotArgs.add('--snapshot_kind=app-aot-assembly');
...@@ -230,7 +238,7 @@ class AOTSnapshotter { ...@@ -230,7 +238,7 @@ class AOTSnapshotter {
final Iterable<String> missingInputs = inputPaths.where((String p) => !fs.isFileSync(p)); final Iterable<String> missingInputs = inputPaths.where((String p) => !fs.isFileSync(p));
if (missingInputs.isNotEmpty) { if (missingInputs.isNotEmpty) {
printError('Missing input files: $missingInputs from $inputPaths'); printError('Missing input files: $missingInputs from $inputPaths');
return -3; return 1;
} }
// If inputs and outputs have not changed since last run, skip the build. // If inputs and outputs have not changed since last run, skip the build.
...@@ -242,7 +250,7 @@ class AOTSnapshotter { ...@@ -242,7 +250,7 @@ class AOTSnapshotter {
'targetPlatform': platform.toString(), 'targetPlatform': platform.toString(),
'entryPoint': mainPath, 'entryPoint': mainPath,
'dart2': previewDart2.toString(), 'dart2': previewDart2.toString(),
'sharedLib': compileToSharedLibrary.toString(), 'sharedLib': buildSharedLibrary.toString(),
'extraGenSnapshotOptions': extraGenSnapshotOptions.join(' '), 'extraGenSnapshotOptions': extraGenSnapshotOptions.join(' '),
}, },
depfilePaths: <String>[depfilePath], depfilePaths: <String>[depfilePath],
...@@ -261,7 +269,7 @@ class AOTSnapshotter { ...@@ -261,7 +269,7 @@ class AOTSnapshotter {
); );
if (genSnapshotExitCode != 0) { if (genSnapshotExitCode != 0) {
printError('Dart snapshot generator failed with exit code $genSnapshotExitCode'); printError('Dart snapshot generator failed with exit code $genSnapshotExitCode');
return -4; return genSnapshotExitCode;
} }
// Write path to gen_snapshot, since snapshots have to be re-generated when we roll // Write path to gen_snapshot, since snapshots have to be re-generated when we roll
...@@ -274,10 +282,12 @@ class AOTSnapshotter { ...@@ -274,10 +282,12 @@ class AOTSnapshotter {
final RunResult result = await _buildIosFramework(iosArch: iosArch, assemblyPath: assembly, outputPath: outputDir.path); final RunResult result = await _buildIosFramework(iosArch: iosArch, assemblyPath: assembly, outputPath: outputDir.path);
if (result.exitCode != 0) if (result.exitCode != 0)
return result.exitCode; return result.exitCode;
} else if (compileToSharedLibrary) { } else if (buildSharedLibrary) {
final RunResult result = await _buildAndroidSharedLibrary(assemblyPath: assembly, outputPath: outputDir.path); final RunResult result = await _buildAndroidSharedLibrary(assemblyPath: assembly, outputPath: outputDir.path);
if (result.exitCode != 0) if (result.exitCode != 0) {
printError('Failed to build AOT snapshot. Compiler terminated with exit code ${result.exitCode}');
return result.exitCode; return result.exitCode;
}
} }
// Compute and record build fingerprint. // Compute and record build fingerprint.
...@@ -298,8 +308,10 @@ class AOTSnapshotter { ...@@ -298,8 +308,10 @@ class AOTSnapshotter {
final String assemblyO = fs.path.join(outputPath, 'snapshot_assembly.o'); final String assemblyO = fs.path.join(outputPath, 'snapshot_assembly.o');
final RunResult compileResult = await xcode.cc(commonBuildOptions.toList()..addAll(<String>['-c', assemblyPath, '-o', assemblyO])); final RunResult compileResult = await xcode.cc(commonBuildOptions.toList()..addAll(<String>['-c', assemblyPath, '-o', assemblyO]));
if (compileResult.exitCode != 0) if (compileResult.exitCode != 0) {
printError('Failed to compile AOT snapshot. Compiler terminated with exit code ${compileResult.exitCode}');
return compileResult; return compileResult;
}
final String frameworkDir = fs.path.join(outputPath, 'App.framework'); final String frameworkDir = fs.path.join(outputPath, 'App.framework');
fs.directory(frameworkDir).createSync(recursive: true); fs.directory(frameworkDir).createSync(recursive: true);
...@@ -313,6 +325,9 @@ class AOTSnapshotter { ...@@ -313,6 +325,9 @@ class AOTSnapshotter {
assemblyO, assemblyO,
]); ]);
final RunResult linkResult = await xcode.clang(linkArgs); final RunResult linkResult = await xcode.clang(linkArgs);
if (linkResult.exitCode != 0) {
printError('Failed to link AOT snapshot. Linker terminated with exit code ${compileResult.exitCode}');
}
return linkResult; return linkResult;
} }
......
...@@ -33,11 +33,16 @@ class BuildAotCommand extends BuildSubCommand { ...@@ -33,11 +33,16 @@ class BuildAotCommand extends BuildSubCommand {
hide: !verboseHelp, hide: !verboseHelp,
help: 'Preview Dart 2.0 functionality.', help: 'Preview Dart 2.0 functionality.',
) )
..addFlag('build-shared-library',
negatable: false,
defaultsTo: false,
help: 'Compile to a *.so file (requires NDK when building for Android).'
)
..addMultiOption('ios-arch', ..addMultiOption('ios-arch',
splitCommas: true, splitCommas: true,
defaultsTo: defaultIOSArchs.map(getNameForIOSArch), defaultsTo: defaultIOSArchs.map(getNameForIOSArch),
allowed: IOSArch.values.map(getNameForIOSArch), allowed: IOSArch.values.map(getNameForIOSArch),
help: 'iOS architectures to build', help: 'iOS architectures to build.',
) )
..addMultiOption(FlutterOptions.kExtraFrontEndOptions, ..addMultiOption(FlutterOptions.kExtraFrontEndOptions,
splitCommas: true, splitCommas: true,
...@@ -46,10 +51,7 @@ class BuildAotCommand extends BuildSubCommand { ...@@ -46,10 +51,7 @@ class BuildAotCommand extends BuildSubCommand {
..addMultiOption(FlutterOptions.kExtraGenSnapshotOptions, ..addMultiOption(FlutterOptions.kExtraGenSnapshotOptions,
splitCommas: true, splitCommas: true,
hide: true, hide: true,
) );
..addFlag('prefer-shared-library',
negatable: false,
help: 'Whether to prefer compiling to a *.so file (android only).');
} }
@override @override
...@@ -115,7 +117,7 @@ class BuildAotCommand extends BuildSubCommand { ...@@ -115,7 +117,7 @@ class BuildAotCommand extends BuildSubCommand {
packagesPath: PackageMap.globalPackagesPath, packagesPath: PackageMap.globalPackagesPath,
outputPath: outputPath, outputPath: outputPath,
previewDart2: previewDart2, previewDart2: previewDart2,
preferSharedLibrary: false, buildSharedLibrary: false,
extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions], extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions],
).then((int buildExitCode) { ).then((int buildExitCode) {
if (buildExitCode != 0) if (buildExitCode != 0)
...@@ -142,16 +144,18 @@ class BuildAotCommand extends BuildSubCommand { ...@@ -142,16 +144,18 @@ class BuildAotCommand extends BuildSubCommand {
packagesPath: PackageMap.globalPackagesPath, packagesPath: PackageMap.globalPackagesPath,
outputPath: outputPath, outputPath: outputPath,
previewDart2: previewDart2, previewDart2: previewDart2,
preferSharedLibrary: argResults['prefer-shared-library'], buildSharedLibrary: argResults['build-shared-library'],
extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions], extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions],
); );
if (snapshotExitCode != 0) { if (snapshotExitCode != 0) {
printError('Snapshotting exited with non-zero exit code: $snapshotExitCode'); printError('Snapshotting exited with non-zero exit code: $snapshotExitCode');
return;
} }
} }
} on String catch (error) { } on String catch (error) {
// Catch the String exceptions thrown from the `runCheckedSync` methods below. // Catch the String exceptions thrown from the `runCheckedSync` methods below.
printError(error); printError(error);
return;
} }
status?.stop(); status?.stop();
......
...@@ -6,6 +6,7 @@ import 'dart:async'; ...@@ -6,6 +6,7 @@ import 'dart:async';
import 'dart:convert' show json; import 'dart:convert' show json;
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/base/build.dart'; import 'package:flutter_tools/src/base/build.dart';
...@@ -21,6 +22,7 @@ import 'package:test/test.dart'; ...@@ -21,6 +22,7 @@ import 'package:test/test.dart';
import '../src/context.dart'; import '../src/context.dart';
class MockFlutterVersion extends Mock implements FlutterVersion {} class MockFlutterVersion extends Mock implements FlutterVersion {}
class MockAndroidSdk extends Mock implements AndroidSdk {}
class MockArtifacts extends Mock implements Artifacts {} class MockArtifacts extends Mock implements Artifacts {}
class MockXcode extends Mock implements Xcode {} class MockXcode extends Mock implements Xcode {}
...@@ -287,6 +289,7 @@ void main() { ...@@ -287,6 +289,7 @@ void main() {
_FakeGenSnapshot genSnapshot; _FakeGenSnapshot genSnapshot;
MemoryFileSystem fs; MemoryFileSystem fs;
AOTSnapshotter snapshotter; AOTSnapshotter snapshotter;
MockAndroidSdk mockAndroidSdk;
MockArtifacts mockArtifacts; MockArtifacts mockArtifacts;
MockXcode mockXcode; MockXcode mockXcode;
...@@ -308,6 +311,7 @@ void main() { ...@@ -308,6 +311,7 @@ void main() {
genSnapshot = new _FakeGenSnapshot(); genSnapshot = new _FakeGenSnapshot();
snapshotter = new AOTSnapshotter(); snapshotter = new AOTSnapshotter();
mockAndroidSdk = new MockAndroidSdk();
mockArtifacts = new MockArtifacts(); mockArtifacts = new MockArtifacts();
mockXcode = new MockXcode(); mockXcode = new MockXcode();
for (BuildMode mode in BuildMode.values) { for (BuildMode mode in BuildMode.values) {
...@@ -320,6 +324,7 @@ void main() { ...@@ -320,6 +324,7 @@ void main() {
}); });
final Map<Type, Generator> contextOverrides = <Type, Generator>{ final Map<Type, Generator> contextOverrides = <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
Artifacts: () => mockArtifacts, Artifacts: () => mockArtifacts,
FileSystem: () => fs, FileSystem: () => fs,
GenSnapshot: () => genSnapshot, GenSnapshot: () => genSnapshot,
...@@ -334,7 +339,7 @@ void main() { ...@@ -334,7 +339,7 @@ void main() {
mainPath: 'main.dill', mainPath: 'main.dill',
packagesPath: '.packages', packagesPath: '.packages',
outputPath: outputPath, outputPath: outputPath,
preferSharedLibrary: false, buildSharedLibrary: false,
previewDart2: true, previewDart2: true,
), isNot(equals(0))); ), isNot(equals(0)));
}, overrides: contextOverrides); }, overrides: contextOverrides);
...@@ -347,7 +352,7 @@ void main() { ...@@ -347,7 +352,7 @@ void main() {
mainPath: 'main.dill', mainPath: 'main.dill',
packagesPath: '.packages', packagesPath: '.packages',
outputPath: outputPath, outputPath: outputPath,
preferSharedLibrary: false, buildSharedLibrary: false,
previewDart2: true, previewDart2: true,
), isNot(0)); ), isNot(0));
}, overrides: contextOverrides); }, overrides: contextOverrides);
...@@ -373,7 +378,7 @@ void main() { ...@@ -373,7 +378,7 @@ void main() {
mainPath: 'main.dill', mainPath: 'main.dill',
packagesPath: '.packages', packagesPath: '.packages',
outputPath: outputPath, outputPath: outputPath,
preferSharedLibrary: false, buildSharedLibrary: false,
previewDart2: true, previewDart2: true,
iosArch: IOSArch.arm64, iosArch: IOSArch.arm64,
); );
...@@ -420,7 +425,7 @@ void main() { ...@@ -420,7 +425,7 @@ void main() {
mainPath: 'main.dill', mainPath: 'main.dill',
packagesPath: '.packages', packagesPath: '.packages',
outputPath: outputPath, outputPath: outputPath,
preferSharedLibrary: false, buildSharedLibrary: false,
previewDart2: true, previewDart2: true,
iosArch: IOSArch.arm64, iosArch: IOSArch.arm64,
); );
...@@ -443,5 +448,24 @@ void main() { ...@@ -443,5 +448,24 @@ void main() {
'main.dill', 'main.dill',
]); ]);
}, overrides: contextOverrides); }, overrides: contextOverrides);
testUsingContext('returns failure if buildSharedLibrary is true but no NDK is found', () async {
final String outputPath = fs.path.join('build', 'foo');
when(mockAndroidSdk.ndkCompiler).thenReturn(null);
final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.android_arm,
buildMode: BuildMode.release,
mainPath: 'main.dill',
packagesPath: '.packages',
outputPath: outputPath,
buildSharedLibrary: true,
previewDart2: true,
);
expect(genSnapshotExitCode, isNot(0));
expect(genSnapshot.callCount, 0);
}, overrides: contextOverrides);
}); });
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment