Skip to content

Commit 0a4f6cd

Browse files
authored
Add split-debug and obfuscation to build aar (flutter#56342)
1 parent eadd30e commit 0a4f6cd

File tree

7 files changed

+269
-98
lines changed

7 files changed

+269
-98
lines changed

dev/devicelab/bin/tasks/android_obfuscate_test.dart

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import 'package:path/path.dart' as path;
1212
Future<void> main() async {
1313
await task(() async {
1414
try {
15-
bool foundProjectName = false;
15+
bool foundApkProjectName = false;
1616
await runProjectTest((FlutterProject flutterProject) async {
1717
section('APK content for task assembleRelease with --obfuscate');
1818
await inDirectory(flutterProject.rootPath, () async {
@@ -21,13 +21,14 @@ Future<void> main() async {
2121
'--target-platform=android-arm',
2222
'--obfuscate',
2323
'--split-debug-info=foo/',
24+
'--verbose',
2425
]);
2526
});
26-
final String outputDirectory = path.join(
27+
final String outputApkDirectory = path.join(
2728
flutterProject.rootPath,
2829
'build/app/outputs/apk/release/app-release.apk',
2930
);
30-
final Iterable<String> apkFiles = await getFilesInApk(outputDirectory);
31+
final Iterable<String> apkFiles = await getFilesInApk(outputApkDirectory);
3132

3233
checkCollectionContains<String>(<String>[
3334
...flutterAssets,
@@ -38,20 +39,69 @@ Future<void> main() async {
3839
// Verify that an identifier from the Dart project code is not present
3940
// in the compiled binary.
4041
await inDirectory(flutterProject.rootPath, () async {
41-
await exec('unzip', <String>[outputDirectory]);
42+
await exec('unzip', <String>[outputApkDirectory]);
43+
checkFileExists(path.join(flutterProject.rootPath, 'lib/armeabi-v7a/libapp.so'));
4244
final String response = await eval(
4345
'grep',
4446
<String>[flutterProject.name, 'lib/armeabi-v7a/libapp.so'],
4547
canFail: true,
4648
);
4749
if (response.trim().contains('matches')) {
48-
foundProjectName = true;
50+
foundApkProjectName = true;
4951
}
5052
});
5153
});
52-
if (foundProjectName) {
53-
return TaskResult.failure('Found project name in obfuscated dart library');
54+
55+
bool foundAarProjectName = false;
56+
await runModuleProjectTest((FlutterModuleProject flutterProject) async {
57+
section('AAR content with --obfuscate');
58+
59+
await inDirectory(flutterProject.rootPath, () async {
60+
await flutter('build', options: <String>[
61+
'aar',
62+
'--target-platform=android-arm',
63+
'--obfuscate',
64+
'--split-debug-info=foo/',
65+
'--no-debug',
66+
'--no-profile',
67+
'--verbose',
68+
]);
69+
});
70+
71+
final String outputAarDirectory = path.join(
72+
flutterProject.rootPath,
73+
'build/host/outputs/repo/com/example/${flutterProject.name}/flutter_release/1.0/flutter_release-1.0.aar',
74+
);
75+
final Iterable<String> aarFiles = await getFilesInAar(outputAarDirectory);
76+
77+
checkCollectionContains<String>(<String>[
78+
...flutterAssets,
79+
'jni/armeabi-v7a/libapp.so',
80+
], aarFiles);
81+
82+
// Verify that an identifier from the Dart project code is not present
83+
// in the compiled binary.
84+
await inDirectory(flutterProject.rootPath, () async {
85+
await exec('unzip', <String>[outputAarDirectory]);
86+
checkFileExists(path.join(flutterProject.rootPath, 'jni/armeabi-v7a/libapp.so'));
87+
final String response = await eval(
88+
'grep',
89+
<String>[flutterProject.name, 'jni/armeabi-v7a/libapp.so'],
90+
canFail: true,
91+
);
92+
if (response.trim().contains('matches')) {
93+
foundAarProjectName = true;
94+
}
95+
});
96+
});
97+
98+
if (foundApkProjectName) {
99+
return TaskResult.failure('Found project name in obfuscated APK dart library');
100+
}
101+
if (foundAarProjectName) {
102+
return TaskResult.failure('Found project name in obfuscated AAR dart library');
54103
}
104+
55105
return TaskResult.success(null);
56106
} on TaskResult catch (taskResult) {
57107
return taskResult;

dev/devicelab/lib/framework/apk_utils.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ Future<void> runPluginProjectTest(Future<void> testFunction(FlutterPluginProject
5151
}
5252
}
5353

54+
/// Runs the given [testFunction] on a freshly generated Flutter module project.
55+
Future<void> runModuleProjectTest(Future<void> testFunction(FlutterModuleProject moduleProject)) async {
56+
final Directory tempDir = Directory.systemTemp.createTempSync('flutter_devicelab_gradle_module_test.');
57+
final FlutterModuleProject moduleProject = await FlutterModuleProject.create(tempDir, 'hello_module');
58+
59+
try {
60+
await testFunction(moduleProject);
61+
} finally {
62+
rmTree(tempDir);
63+
}
64+
}
65+
5466
/// Returns the list of files inside an Android Package Kit.
5567
Future<Iterable<String>> getFilesInApk(String apk) async {
5668
if (!File(apk).existsSync()) {
@@ -357,6 +369,22 @@ class FlutterPluginProject {
357369
}
358370
}
359371

372+
class FlutterModuleProject {
373+
FlutterModuleProject(this.parent, this.name);
374+
375+
final Directory parent;
376+
final String name;
377+
378+
static Future<FlutterModuleProject> create(Directory directory, String name) async {
379+
await inDirectory(directory, () async {
380+
await flutter('create', options: <String>['--template=module', name]);
381+
});
382+
return FlutterModuleProject(directory, name);
383+
}
384+
385+
String get rootPath => path.join(parent.path, name);
386+
}
387+
360388
Future<void> _runGradleTask({String workingDirectory, String task, List<String> options}) async {
361389
final ProcessResult result = await _resultOfGradleTask(
362390
workingDirectory: workingDirectory,

packages/flutter_tools/lib/src/android/gradle.dart

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,8 @@ Future<void> buildGradleAar({
525525
throwToolExit('AARs can only be built for plugin or module projects.');
526526
}
527527

528-
final String aarTask = getAarTaskFor(androidBuildInfo.buildInfo);
528+
final BuildInfo buildInfo = androidBuildInfo.buildInfo;
529+
final String aarTask = getAarTaskFor(buildInfo);
529530
final Status status = globals.logger.startProgress(
530531
"Running Gradle task '$aarTask'...",
531532
timeout: timeoutConfiguration.slowOperation,
@@ -548,10 +549,28 @@ Future<void> buildGradleAar({
548549
'-Pis-plugin=${manifest.isPlugin}',
549550
'-PbuildNumber=$buildNumber'
550551
];
552+
if (globals.logger.isVerbose) {
553+
command.add('-Pverbose=true');
554+
} else {
555+
command.add('-q');
556+
}
551557

552558
if (target != null && target.isNotEmpty) {
553559
command.add('-Ptarget=$target');
554560
}
561+
if (buildInfo.splitDebugInfoPath != null) {
562+
command.add('-Psplit-debug-info=${buildInfo.splitDebugInfoPath}');
563+
}
564+
if (buildInfo.treeShakeIcons) {
565+
command.add('-Pfont-subset=true');
566+
}
567+
if (buildInfo.dartObfuscation) {
568+
if (buildInfo.mode == BuildMode.debug || buildInfo.mode == BuildMode.profile) {
569+
globals.printStatus('Dart obfuscation is not supported in ${toTitleCase(buildInfo.friendlyModeName)} mode, building as unobfuscated.');
570+
} else {
571+
command.add('-Pdart-obfuscation=true');
572+
}
573+
}
555574

556575
if (globals.artifacts is LocalEngineArtifacts) {
557576
final LocalEngineArtifacts localEngineArtifacts = globals.artifacts as LocalEngineArtifacts;
@@ -564,11 +583,8 @@ Future<void> buildGradleAar({
564583
'Local Maven repo: ${localEngineRepo.path}'
565584
);
566585
command.add('-Plocal-engine-repo=${localEngineRepo.path}');
567-
command.add('-Plocal-engine-build-mode=${androidBuildInfo.buildInfo.modeName}');
586+
command.add('-Plocal-engine-build-mode=${buildInfo.modeName}');
568587
command.add('-Plocal-engine-out=${localEngineArtifacts.engineOutPath}');
569-
if (androidBuildInfo.buildInfo.treeShakeIcons) {
570-
command.add('-Pfont-subset=true');
571-
}
572588

573589
// Copy the local engine repo in the output directory.
574590
try {

packages/flutter_tools/lib/src/commands/build_aar.dart

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class BuildAarCommand extends BuildSubCommand {
3838
usesFlavorOption();
3939
usesBuildNumberOption();
4040
usesPubOption();
41+
addSplitDebugInfoOption();
42+
addDartObfuscationOption();
4143
argParser
4244
..addMultiOption(
4345
'target-platform',
@@ -104,15 +106,18 @@ class BuildAarCommand extends BuildSubCommand {
104106

105107
for (final String buildMode in const <String>['debug', 'profile', 'release']) {
106108
if (boolArg(buildMode)) {
107-
androidBuildInfo.add(AndroidBuildInfo(
108-
BuildInfo(BuildMode.fromName(buildMode), stringArg('flavor'), treeShakeIcons: boolArg('tree-shake-icons')),
109-
targetArchs: targetArchitectures,
110-
));
109+
androidBuildInfo.add(
110+
AndroidBuildInfo(
111+
getBuildInfo(forcedBuildMode: BuildMode.fromName(buildMode)),
112+
targetArchs: targetArchitectures,
113+
)
114+
);
111115
}
112116
}
113117
if (androidBuildInfo.isEmpty) {
114118
throwToolExit('Please specify a build mode and try again.');
115119
}
120+
116121
await androidBuilder.buildAar(
117122
project: _getProject(),
118123
target: '', // Not needed because this command only builds Android's code.

packages/flutter_tools/lib/src/runner/flutter_command.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -558,10 +558,12 @@ abstract class FlutterCommand extends Command<void> {
558558
}
559559

560560
/// Compute the [BuildInfo] for the current flutter command.
561+
/// Commands that build multiple build modes can pass in a [forcedBuildMode]
562+
/// to be used instead of parsing flags.
561563
///
562564
/// Throws a [ToolExit] if the current set of options is not compatible with
563-
/// eachother.
564-
BuildInfo getBuildInfo() {
565+
/// each other.
566+
BuildInfo getBuildInfo({ BuildMode forcedBuildMode }) {
565567
final bool trackWidgetCreation = argParser.options.containsKey('track-widget-creation') &&
566568
boolArg('track-widget-creation');
567569

@@ -603,7 +605,7 @@ abstract class FlutterCommand extends Command<void> {
603605
'combination with "--${FlutterOptions.kSplitDebugInfoOption}"',
604606
);
605607
}
606-
final BuildMode buildMode = getBuildMode();
608+
final BuildMode buildMode = forcedBuildMode ?? getBuildMode();
607609
final bool treeShakeIcons = argParser.options.containsKey('tree-shake-icons')
608610
&& buildMode.isPrecompiled
609611
&& boolArg('tree-shake-icons');

0 commit comments

Comments
 (0)