From 00195994117057c9d3ea9aec76da1fa5652aab11 Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Wed, 13 Nov 2019 13:27:10 -0800 Subject: [PATCH] Build AAR for all build variants by default (#44797) --- .../lib/src/android/android_builder.dart | 31 +- .../flutter_tools/lib/src/android/gradle.dart | 67 +++-- .../flutter_tools/lib/src/commands/build.dart | 2 +- .../lib/src/commands/build_aar.dart | 52 +++- .../general.shard/android/gradle_test.dart | 265 +++++++++++++----- .../test/src/android_common.dart | 4 +- 6 files changed, 292 insertions(+), 129 deletions(-) diff --git a/packages/flutter_tools/lib/src/android/android_builder.dart b/packages/flutter_tools/lib/src/android/android_builder.dart index b362591920f..a8c9db03c83 100644 --- a/packages/flutter_tools/lib/src/android/android_builder.dart +++ b/packages/flutter_tools/lib/src/android/android_builder.dart @@ -26,9 +26,9 @@ abstract class AndroidBuilder { /// Builds an AAR artifact. Future buildAar({ @required FlutterProject project, - @required AndroidBuildInfo androidBuildInfo, + @required Set androidBuildInfo, @required String target, - @required String outputDir, + @required String outputDirectoryPath, }); /// Builds an APK artifact. @@ -54,23 +54,32 @@ class _AndroidBuilderImpl extends AndroidBuilder { @override Future buildAar({ @required FlutterProject project, - @required AndroidBuildInfo androidBuildInfo, + @required Set androidBuildInfo, @required String target, - @required String outputDir, + @required String outputDirectoryPath, }) async { try { Directory outputDirectory = - fs.directory(outputDir ?? project.android.buildDirectory); + fs.directory(outputDirectoryPath ?? project.android.buildDirectory); if (project.isModule) { // Module projects artifacts are located in `build/host`. outputDirectory = outputDirectory.childDirectory('host'); } - await buildGradleAar( - project: project, - androidBuildInfo: androidBuildInfo, - target: target, - outputDir: outputDirectory, - printHowToConsumeAaar: true, + for (AndroidBuildInfo androidBuildInfo in androidBuildInfo) { + await buildGradleAar( + project: project, + androidBuildInfo: androidBuildInfo, + target: target, + outputDirectory: outputDirectory, + ); + } + printHowToConsumeAar( + buildModes: androidBuildInfo + .map((AndroidBuildInfo androidBuildInfo) { + return androidBuildInfo.buildInfo.modeName; + }).toSet(), + androidPackage: project.manifest.androidPackage, + repoDirectory: getRepoDirectory(outputDirectory), ); } finally { androidSdk.reinitialize(); diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart index a4b55947080..aab8c9c21a7 100644 --- a/packages/flutter_tools/lib/src/android/gradle.dart +++ b/packages/flutter_tools/lib/src/android/gradle.dart @@ -57,7 +57,6 @@ Directory getBundleDirectory(FlutterProject project) { /// The directory where the repo is generated. /// Only applicable to AARs. -@visibleForTesting Directory getRepoDirectory(Directory buildDirectory) { return buildDirectory .childDirectory('outputs') @@ -474,20 +473,17 @@ Future buildGradleApp({ /// /// * [project] is typically [FlutterProject.current()]. /// * [androidBuildInfo] is the build configuration. -/// * [target] is the target dart entrypoint. Typically, `lib/main.dart`. /// * [outputDir] is the destination of the artifacts, Future buildGradleAar({ @required FlutterProject project, @required AndroidBuildInfo androidBuildInfo, @required String target, - @required Directory outputDir, - @required bool printHowToConsumeAaar, + @required Directory outputDirectory, }) async { assert(project != null); - assert(androidBuildInfo != null); assert(target != null); - assert(outputDir != null); - assert(printHowToConsumeAaar != null); + assert(androidBuildInfo != null); + assert(outputDirectory != null); if (androidSdk == null) { exitWithNoSdkMessage(); @@ -516,13 +512,14 @@ Future buildGradleAar({ gradleUtils.getExecutable(project), '-I=$initScript', '-Pflutter-root=$flutterRoot', - '-Poutput-dir=${outputDir.path}', + '-Poutput-dir=${outputDirectory.path}', '-Pis-plugin=${manifest.isPlugin}', ]; if (target != null && target.isNotEmpty) { command.add('-Ptarget=$target'); } + if (androidBuildInfo.targetArchs.isNotEmpty) { final String targetPlatforms = androidBuildInfo.targetArchs .map(getPlatformNameForAndroidArch).join(','); @@ -567,7 +564,7 @@ Future buildGradleAar({ exitCode: exitCode, ); } - final Directory repoDirectory = getRepoDirectory(outputDir); + final Directory repoDirectory = getRepoDirectory(outputDirectory); if (!repoDirectory.existsSync()) { printStatus(result.stdout, wrap: false); printError(result.stderr, wrap: false); @@ -580,24 +577,17 @@ Future buildGradleAar({ '$successMark Built ${fs.path.relative(repoDirectory.path)}.', color: TerminalColor.green, ); - if (printHowToConsumeAaar) { - _printHowToConsumeAar( - buildMode: androidBuildInfo.buildInfo.modeName, - androidPackage: project.manifest.androidPackage, - repoPath: repoDirectory.path, - ); - } } /// Prints how to consume the AAR from a host app. -void _printHowToConsumeAar({ - @required String buildMode, +void printHowToConsumeAar({ + @required Set buildModes, @required String androidPackage, - @required String repoPath, + @required Directory repoDirectory, }) { - assert(buildMode != null); + assert(buildModes != null && buildModes.isNotEmpty); assert(androidPackage != null); - assert(repoPath != null); + assert(repoDirectory != null); printStatus(''' @@ -607,20 +597,42 @@ ${terminal.bolden('Consuming the Module')} repositories { maven { - url '$repoPath' + url '${repoDirectory.path}' } maven { url 'http://download.flutter.io' } } - 3. Make the host app depend on the $buildMode module: + 3. Make the host app depend on the Flutter module: - dependencies { - ${buildMode}Implementation '$androidPackage:flutter_$buildMode:1.0' + dependencies {'''); + + for (String buildMode in buildModes) { + printStatus(''' + ${buildMode}Implementation '$androidPackage:flutter_$buildMode:1.0'''); + } + +printStatus(''' + } +'''); + + if (buildModes.contains('profile')) { + printStatus(''' + + 4. Add the `profile` build type: + + android { + buildTypes { + profile { + initWith debug + } } + } +'''); + } -To learn more, visit https://flutter.dev/go/build-aar'''); +printStatus('To learn more, visit https://flutter.dev/go/build-aar'''); } String _hex(List bytes) { @@ -705,8 +717,7 @@ Future buildPluginsAsAar( ), ), target: '', - outputDir: buildDirectory, - printHowToConsumeAaar: false, + outputDirectory: buildDirectory, ); } on ToolExit { // Log the entire plugin entry in `.flutter-plugins` since it diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index 22401621075..55f52ef7d6d 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -21,7 +21,7 @@ import 'build_web.dart'; class BuildCommand extends FlutterCommand { BuildCommand({bool verboseHelp = false}) { - addSubcommand(BuildAarCommand(verboseHelp: verboseHelp)); + addSubcommand(BuildAarCommand()); addSubcommand(BuildApkCommand(verboseHelp: verboseHelp)); addSubcommand(BuildAppBundleCommand(verboseHelp: verboseHelp)); addSubcommand(BuildAotCommand(verboseHelp: verboseHelp)); diff --git a/packages/flutter_tools/lib/src/commands/build_aar.dart b/packages/flutter_tools/lib/src/commands/build_aar.dart index 73bbb17b8f3..f35f6656b6f 100644 --- a/packages/flutter_tools/lib/src/commands/build_aar.dart +++ b/packages/flutter_tools/lib/src/commands/build_aar.dart @@ -5,6 +5,7 @@ import 'dart:async'; import '../android/android_builder.dart'; +import '../base/common.dart'; import '../base/os.dart'; import '../build_info.dart'; import '../cache.dart'; @@ -14,18 +15,35 @@ import '../runner/flutter_command.dart' show FlutterCommandResult; import 'build.dart'; class BuildAarCommand extends BuildSubCommand { - BuildAarCommand({bool verboseHelp = false}) { - addBuildModeFlags(verboseHelp: verboseHelp); + BuildAarCommand() { + argParser + ..addFlag( + 'debug', + defaultsTo: true, + help: 'Build a debug version of the current project.', + ) + ..addFlag( + 'profile', + defaultsTo: true, + help: 'Build a version of the current project specialized for performance profiling.', + ) + ..addFlag( + 'release', + defaultsTo: true, + help: 'Build a release version of the current project.', + ); usesFlavorOption(); usesPubOption(); argParser - ..addMultiOption('target-platform', + ..addMultiOption( + 'target-platform', splitCommas: true, defaultsTo: ['android-arm', 'android-arm64', 'android-x64'], allowed: ['android-arm', 'android-arm64', 'android-x86', 'android-x64'], help: 'The target platform for which the project is compiled.', ) - ..addOption('output-dir', + ..addOption( + 'output-dir', help: 'The absolute path to the directory where the repository is generated.' 'By default, this is \'android/build\'. ', ); @@ -61,21 +79,35 @@ class BuildAarCommand extends BuildSubCommand { @override final String description = 'Build a repository containing an AAR and a POM file.\n\n' - 'The POM file is used to include the dependencies that the AAR was compiled against.\n\n' + 'By default, AARs are built for `release`, `debug` and `profile`.\n' + 'The POM file is used to include the dependencies that the AAR was compiled against.\n' 'To learn more about how to use these artifacts, see ' - 'https://docs.gradle.org/current/userguide/repository_types.html#sub:maven_local'; + 'https://flutter.dev/go/build-aar'; @override Future runCommand() async { - final BuildInfo buildInfo = getBuildInfo(); - final AndroidBuildInfo androidBuildInfo = AndroidBuildInfo(buildInfo, - targetArchs: argResults['target-platform'].map(getAndroidArchForName)); + final Set androidBuildInfo = {}; + final Iterable targetArchitectures = argResults['target-platform'] + .map(getAndroidArchForName); + for (String buildMode in const ['debug', 'profile', 'release']) { + if (argResults[buildMode]) { + androidBuildInfo.add( + AndroidBuildInfo( + BuildInfo(BuildMode.fromName(buildMode), argResults['flavor']), + targetArchs: targetArchitectures, + ) + ); + } + } + if (androidBuildInfo.isEmpty) { + throwToolExit('Please specify a build mode and try again.'); + } await androidBuilder.buildAar( project: _getProject(), target: '', // Not needed because this command only builds Android's code. androidBuildInfo: androidBuildInfo, - outputDir: argResults['output-dir'], + outputDirectoryPath: argResults['output-dir'], ); return null; } diff --git a/packages/flutter_tools/test/general.shard/android/gradle_test.dart b/packages/flutter_tools/test/general.shard/android/gradle_test.dart index 8f1309864bd..3e18716bc4a 100644 --- a/packages/flutter_tools/test/general.shard/android/gradle_test.dart +++ b/packages/flutter_tools/test/general.shard/android/gradle_test.dart @@ -1618,79 +1618,6 @@ plugin2=${plugin2.path} ProcessManager: () => mockProcessManager, }); - testUsingContext('indicates how to consume an AAR when printHowToConsumeAaar is true', () async { - final File manifestFile = fs.file('pubspec.yaml'); - manifestFile.createSync(recursive: true); - manifestFile.writeAsStringSync(''' - flutter: - module: - androidPackage: com.example.test - ''' - ); - - fs.file('.android/gradlew').createSync(recursive: true); - - fs.file('.android/gradle.properties') - .writeAsStringSync('irrelevant'); - - fs.file('.android/build.gradle') - .createSync(recursive: true); - - // Let any process start. Assert after. - when(mockProcessManager.run( - any, - environment: anyNamed('environment'), - workingDirectory: anyNamed('workingDirectory'), - )).thenAnswer((_) async => ProcessResult(1, 0, '', '')); - - fs.directory('build/outputs/repo').createSync(recursive: true); - - await buildGradleAar( - androidBuildInfo: const AndroidBuildInfo(BuildInfo(BuildMode.release, null)), - project: FlutterProject.current(), - outputDir: fs.directory('build/'), - target: '', - printHowToConsumeAaar: true, - ); - - final BufferLogger logger = context.get(); - expect( - logger.statusText, - contains('Built build/outputs/repo'), - ); - expect( - logger.statusText, - contains(''' -Consuming the Module - 1. Open /app/build.gradle - 2. Ensure you have the repositories configured, otherwise add them: - - repositories { - maven { - url 'build/outputs/repo' - } - maven { - url 'http://download.flutter.io' - } - } - - 3. Make the host app depend on the release module: - - dependencies { - releaseImplementation 'com.example.test:flutter_release:1.0' - } - -To learn more, visit https://flutter.dev/go/build-aar''')); - - }, overrides: { - AndroidSdk: () => mockAndroidSdk, - AndroidStudio: () => mockAndroidStudio, - Cache: () => cache, - Platform: () => android, - FileSystem: () => fs, - ProcessManager: () => mockProcessManager, - }); - testUsingContext('doesn\'t indicate how to consume an AAR when printHowToConsumeAaar is false', () async { final File manifestFile = fs.file('pubspec.yaml'); manifestFile.createSync(recursive: true); @@ -1721,9 +1648,8 @@ To learn more, visit https://flutter.dev/go/build-aar''')); await buildGradleAar( androidBuildInfo: const AndroidBuildInfo(BuildInfo(BuildMode.release, null)), project: FlutterProject.current(), - outputDir: fs.directory('build/'), + outputDirectory: fs.directory('build/'), target: '', - printHowToConsumeAaar: false, ); final BufferLogger logger = context.get(); @@ -1885,9 +1811,8 @@ To learn more, visit https://flutter.dev/go/build-aar''')); await buildGradleAar( androidBuildInfo: const AndroidBuildInfo(BuildInfo(BuildMode.release, null)), project: FlutterProject.current(), - outputDir: fs.directory('build/'), + outputDirectory: fs.directory('build/'), target: '', - printHowToConsumeAaar: false, ); final List actualGradlewCall = verify( @@ -1913,6 +1838,192 @@ To learn more, visit https://flutter.dev/go/build-aar''')); ProcessManager: () => mockProcessManager, }); }); + + group('printHowToConsumeAar', () { + testUsingContext('stdout contains release, debug and profile', () async { + printHowToConsumeAar( + buildModes: const {'release', 'debug', 'profile'}, + androidPackage: 'com.mycompany', + repoDirectory: fs.directory('build/'), + ); + + final BufferLogger logger = context.get(); + expect( + logger.statusText, + contains( + '\n' + 'Consuming the Module\n' + ' 1. Open /app/build.gradle\n' + ' 2. Ensure you have the repositories configured, otherwise add them:\n' + '\n' + ' repositories {\n' + ' maven {\n' + ' url \'build/\'\n' + ' }\n' + ' maven {\n' + ' url \'http://download.flutter.io\'\n' + ' }\n' + ' }\n' + '\n' + ' 3. Make the host app depend on the Flutter module:\n' + '\n' + ' dependencies {\n' + ' releaseImplementation \'com.mycompany:flutter_release:1.0\n' + ' debugImplementation \'com.mycompany:flutter_debug:1.0\n' + ' profileImplementation \'com.mycompany:flutter_profile:1.0\n' + ' }\n' + '\n' + '\n' + ' 4. Add the `profile` build type:\n' + '\n' + ' android {\n' + ' buildTypes {\n' + ' profile {\n' + ' initWith debug\n' + ' }\n' + ' }\n' + ' }\n' + '\n' + 'To learn more, visit https://flutter.dev/go/build-aar\n' + ) + ); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + Platform: () => fakePlatform('android'), + ProcessManager: () => FakeProcessManager.any(), + }); + + testUsingContext('stdout contains release', () async { + printHowToConsumeAar( + buildModes: const {'release'}, + androidPackage: 'com.mycompany', + repoDirectory: fs.directory('build/'), + ); + + final BufferLogger logger = context.get(); + expect( + logger.statusText, + contains( + '\n' + 'Consuming the Module\n' + ' 1. Open /app/build.gradle\n' + ' 2. Ensure you have the repositories configured, otherwise add them:\n' + '\n' + ' repositories {\n' + ' maven {\n' + ' url \'build/\'\n' + ' }\n' + ' maven {\n' + ' url \'http://download.flutter.io\'\n' + ' }\n' + ' }\n' + '\n' + ' 3. Make the host app depend on the Flutter module:\n' + '\n' + ' dependencies {\n' + ' releaseImplementation \'com.mycompany:flutter_release:1.0\n' + ' }\n' + '\n' + 'To learn more, visit https://flutter.dev/go/build-aar\n' + ) + ); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + Platform: () => fakePlatform('android'), + ProcessManager: () => FakeProcessManager.any(), + }); + + testUsingContext('stdout contains debug', () async { + printHowToConsumeAar( + buildModes: const {'debug'}, + androidPackage: 'com.mycompany', + repoDirectory: fs.directory('build/'), + ); + + final BufferLogger logger = context.get(); + expect( + logger.statusText, + contains( + '\n' + 'Consuming the Module\n' + ' 1. Open /app/build.gradle\n' + ' 2. Ensure you have the repositories configured, otherwise add them:\n' + '\n' + ' repositories {\n' + ' maven {\n' + ' url \'build/\'\n' + ' }\n' + ' maven {\n' + ' url \'http://download.flutter.io\'\n' + ' }\n' + ' }\n' + '\n' + ' 3. Make the host app depend on the Flutter module:\n' + '\n' + ' dependencies {\n' + ' debugImplementation \'com.mycompany:flutter_debug:1.0\n' + ' }\n' + '\n' + 'To learn more, visit https://flutter.dev/go/build-aar\n' + ) + ); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + Platform: () => fakePlatform('android'), + ProcessManager: () => FakeProcessManager.any(), + }); + + testUsingContext('stdout contains profile', () async { + printHowToConsumeAar( + buildModes: const {'profile'}, + androidPackage: 'com.mycompany', + repoDirectory: fs.directory('build/'), + ); + + final BufferLogger logger = context.get(); + expect( + logger.statusText, + contains( + '\n' + 'Consuming the Module\n' + ' 1. Open /app/build.gradle\n' + ' 2. Ensure you have the repositories configured, otherwise add them:\n' + '\n' + ' repositories {\n' + ' maven {\n' + ' url \'build/\'\n' + ' }\n' + ' maven {\n' + ' url \'http://download.flutter.io\'\n' + ' }\n' + ' }\n' + '\n' + ' 3. Make the host app depend on the Flutter module:\n' + '\n' + ' dependencies {\n' + ' profileImplementation \'com.mycompany:flutter_profile:1.0\n' + ' }\n' + '\n' + '\n' + ' 4. Add the `profile` build type:\n' + '\n' + ' android {\n' + ' buildTypes {\n' + ' profile {\n' + ' initWith debug\n' + ' }\n' + ' }\n' + ' }\n' + '\n' + 'To learn more, visit https://flutter.dev/go/build-aar\n' + ) + ); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + Platform: () => fakePlatform('android'), + ProcessManager: () => FakeProcessManager.any(), + }); + }); } /// Generates a fake app bundle at the location [directoryName]/[fileName]. diff --git a/packages/flutter_tools/test/src/android_common.dart b/packages/flutter_tools/test/src/android_common.dart index dda0afb75fd..1754d2d2177 100644 --- a/packages/flutter_tools/test/src/android_common.dart +++ b/packages/flutter_tools/test/src/android_common.dart @@ -13,9 +13,9 @@ class FakeAndroidBuilder implements AndroidBuilder { @override Future buildAar({ @required FlutterProject project, - @required AndroidBuildInfo androidBuildInfo, + @required Set androidBuildInfo, @required String target, - @required String outputDir, + @required String outputDirectoryPath, }) async {} @override