Unverified Commit 9cb9bfbd authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] use new output location for the apk (#54328)

parent 650592b3
......@@ -39,7 +39,7 @@ Directory getApkDirectory(FlutterProject project) {
: project.android.buildDirectory
.childDirectory('app')
.childDirectory('outputs')
.childDirectory('apk');
.childDirectory('flutter-apk');
}
/// The directory where the app bundle artifact is generated.
......@@ -469,25 +469,33 @@ Future<void> buildGradleApp({
return;
}
// Gradle produced an APK.
final Iterable<File> apkFiles = findApkFiles(project, androidBuildInfo);
final Iterable<String> apkFilesPaths = project.isModule
? findApkFilesModule(project, androidBuildInfo)
: listApkPaths(androidBuildInfo);
final Directory apkDirectory = getApkDirectory(project);
final File apkFile = apkDirectory.childFile(apkFilesPaths.first);
if (!apkFile.existsSync()) {
_exitWithExpectedFileNotFound(
project: project,
fileExtension: '.apk',
);
}
// Copy the first APK to app.apk, so `flutter run` can find it.
// TODO(egarciad): Handle multiple APKs.
apkFiles.first.copySync(apkDirectory.childFile('app.apk').path);
apkFile.copySync(apkDirectory.childFile('app.apk').path);
globals.printTrace('calculateSha: $apkDirectory/app.apk');
final File apkShaFile = apkDirectory.childFile('app.apk.sha1');
apkShaFile.writeAsStringSync(_calculateSha(apkFiles.first));
apkShaFile.writeAsStringSync(_calculateSha(apkFile));
for (final File apkFile in apkFiles) {
final String appSize = (buildInfo.mode == BuildMode.debug)
? '' // Don't display the size when building a debug variant.
: ' (${getSizeAsMB(apkFile.lengthSync())})';
globals.printStatus(
'$successMark Built ${globals.fs.path.relative(apkFile.path)}$appSize.',
color: TerminalColor.green,
);
}
final String appSize = (buildInfo.mode == BuildMode.debug)
? '' // Don't display the size when building a debug variant.
: ' (${getSizeAsMB(apkFile.lengthSync())})';
globals.printStatus(
'$successMark Built ${globals.fs.path.relative(apkFile.path)}$appSize.',
color: TerminalColor.green,
);
}
/// Builds AAR and POM files.
......@@ -778,7 +786,7 @@ Future<void> buildPluginsAsAar(
/// Returns the APK files for a given [FlutterProject] and [AndroidBuildInfo].
@visibleForTesting
Iterable<File> findApkFiles(
Iterable<String> findApkFilesModule(
FlutterProject project,
AndroidBuildInfo androidBuildInfo,
) {
......@@ -815,7 +823,39 @@ Iterable<File> findApkFiles(
fileExtension: '.apk',
);
}
return apks;
return apks.map((File file) => file.path);
}
/// Returns the APK files for a given [FlutterProject] and [AndroidBuildInfo].
///
/// The flutter.gradle plugin will copy APK outputs into:
/// $buildDir/app/outputs/flutter-apk/app-<abi>-<flavor-flag>-<build-mode-flag>.apk
@visibleForTesting
Iterable<String> listApkPaths(
AndroidBuildInfo androidBuildInfo,
) {
final String buildType = camelCase(androidBuildInfo.buildInfo.modeName);
final List<String> apkPartialName = <String>[
if (androidBuildInfo.buildInfo.flavor?.isNotEmpty ?? false)
androidBuildInfo.buildInfo.flavor,
'$buildType.apk',
];
if (androidBuildInfo.splitPerAbi) {
return <String>[
for (AndroidArch androidArch in androidBuildInfo.targetArchs)
<String>[
'app',
getNameForAndroidArch(androidArch),
...apkPartialName
].join('-')
];
}
return <String>[
<String>[
'app',
...apkPartialName,
].join('-')
];
}
@visibleForTesting
......
......@@ -44,7 +44,7 @@ void main() {
expect(
getApkDirectory(project).path,
equals(globals.fs.path.join('foo', 'app', 'outputs', 'apk')),
equals(globals.fs.path.join('foo', 'app', 'outputs', 'flutter-apk')),
);
});
......@@ -312,107 +312,41 @@ void main() {
});
});
group('findApkFiles', () {
final Usage mockUsage = MockUsage();
testUsingContext('Finds APK without flavor in release', () {
final FlutterProject project = MockFlutterProject();
final AndroidProject androidProject = MockAndroidProject();
when(project.android).thenReturn(androidProject);
when(project.isModule).thenReturn(false);
when(androidProject.buildDirectory).thenReturn(globals.fs.directory('irrelevant'));
final Directory apkDirectory = globals.fs.directory(globals.fs.path.join('irrelevant', 'app', 'outputs', 'apk', 'release'));
apkDirectory.createSync(recursive: true);
apkDirectory.childFile('app-release.apk').createSync();
final Iterable<File> apks = findApkFiles(
project,
group('listApkPaths', () {
testWithoutContext('Finds APK without flavor in release', () {
final Iterable<String> apks = listApkPaths(
const AndroidBuildInfo(BuildInfo(BuildMode.release, '', treeShakeIcons: false)),
);
expect(apks.isNotEmpty, isTrue);
expect(apks.first.path, equals(globals.fs.path.join('irrelevant', 'app', 'outputs', 'apk', 'release', 'app-release.apk')));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('Finds APK with flavor in release mode', () {
final FlutterProject project = MockFlutterProject();
final AndroidProject androidProject = MockAndroidProject();
when(project.android).thenReturn(androidProject);
when(project.isModule).thenReturn(false);
when(androidProject.buildDirectory).thenReturn(globals.fs.directory('irrelevant'));
final Directory apkDirectory = globals.fs.directory(globals.fs.path.join('irrelevant', 'app', 'outputs', 'apk', 'release'));
apkDirectory.createSync(recursive: true);
apkDirectory.childFile('app-flavor1-release.apk').createSync();
expect(apks, <String>['app-release.apk']);
});
final Iterable<File> apks = findApkFiles(
project,
testWithoutContext('Finds APK with flavor in release mode', () {
final Iterable<String> apks = listApkPaths(
const AndroidBuildInfo(BuildInfo(BuildMode.release, 'flavor1', treeShakeIcons: false)),
);
expect(apks.isNotEmpty, isTrue);
expect(apks.first.path, equals(globals.fs.path.join('irrelevant', 'app', 'outputs', 'apk', 'release', 'app-flavor1-release.apk')));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('Finds APK with flavor in release mode - AGP v3', () {
final FlutterProject project = MockFlutterProject();
final AndroidProject androidProject = MockAndroidProject();
when(project.android).thenReturn(androidProject);
when(project.isModule).thenReturn(false);
when(androidProject.buildDirectory).thenReturn(globals.fs.directory('irrelevant'));
final Directory apkDirectory = globals.fs.directory(globals.fs.path.join('irrelevant', 'app', 'outputs', 'apk', 'flavor1', 'release'));
apkDirectory.createSync(recursive: true);
apkDirectory.childFile('app-flavor1-release.apk').createSync();
expect(apks, <String>['app-flavor1-release.apk']);
});
final Iterable<File> apks = findApkFiles(
project,
testWithoutContext('Finds APK with flavor in release mode - AGP v3', () {
final Iterable<String> apks = listApkPaths(
const AndroidBuildInfo(BuildInfo(BuildMode.release, 'flavor1', treeShakeIcons: false)),
);
expect(apks.isNotEmpty, isTrue);
expect(apks.first.path, equals(globals.fs.path.join('irrelevant', 'app', 'outputs', 'apk', 'flavor1', 'release', 'app-flavor1-release.apk')));
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
expect(apks, <String>['app-flavor1-release.apk']);
});
testUsingContext('apk not found', () {
final FlutterProject project = FlutterProject.current();
expect(
() {
findApkFiles(
project,
const AndroidBuildInfo(BuildInfo(BuildMode.debug, 'foo_bar', treeShakeIcons: false)),
);
},
throwsToolExit(
message:
"Gradle build failed to produce an .apk file. It's likely that this file "
"was generated under ${project.android.buildDirectory.path}, but the tool couldn't find it."
)
testWithoutContext('Finds APK with split-per-abi', () {
final Iterable<String> apks = listApkPaths(
const AndroidBuildInfo(BuildInfo(BuildMode.release, 'flavor1', treeShakeIcons: false), splitPerAbi: true),
);
verify(
mockUsage.sendEvent(
any,
any,
label: 'gradle-expected-file-not-found',
parameters: const <String, String> {
'cd37': 'androidGradlePluginVersion: 5.6.2, fileExtension: .apk',
},
),
).called(1);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
Usage: () => mockUsage,
expect(apks, unorderedEquals(<String>[
'app-armeabi-v7a-flavor1-release.apk',
'app-arm64-v8a-flavor1-release.apk',
'app-x86_64-flavor1-release.apk',
]));
});
});
......@@ -1415,8 +1349,7 @@ plugin1=${plugin1.path}
fileSystem.directory('build')
.childDirectory('app')
.childDirectory('outputs')
.childDirectory('apk')
.childDirectory('release')
.childDirectory('flutter-apk')
.childFile('app-release.apk')
.createSync(recursive: true);
......@@ -1455,7 +1388,6 @@ plugin1=${plugin1.path}
label: 'gradle-random-event-label-success',
parameters: anyNamed('parameters'),
)).called(1);
}, overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
Cache: () => cache,
......@@ -1553,17 +1485,6 @@ plugin1=${plugin1.path}
});
testUsingContext('indicates that an APK has been built successfully', () async {
when(mockProcessManager.start(any,
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment')))
.thenAnswer((_) {
return Future<Process>.value(
createMockProcess(
exitCode: 0,
stdout: '',
));
});
fileSystem.directory('android')
.childFile('build.gradle')
.createSync(recursive: true);
......@@ -1581,8 +1502,7 @@ plugin1=${plugin1.path}
fileSystem.directory('build')
.childDirectory('app')
.childDirectory('outputs')
.childDirectory('apk')
.childDirectory('release')
.childDirectory('flutter-apk')
.childFile('app-release.apk')
.createSync(recursive: true);
......@@ -1602,7 +1522,7 @@ plugin1=${plugin1.path}
expect(
testLogger.statusText,
contains('Built build/app/outputs/apk/release/app-release.apk (0.0MB)'),
contains('Built build/app/outputs/flutter-apk/app-release.apk (0.0MB)'),
);
}, overrides: <Type, Generator>{
......@@ -1610,7 +1530,7 @@ plugin1=${plugin1.path}
Cache: () => cache,
FileSystem: () => fileSystem,
Platform: () => android,
ProcessManager: () => mockProcessManager,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext("doesn't indicate how to consume an AAR when printHowToConsumeAaar is false", () async {
......
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