Unverified Commit 00195994 authored by Emmanuel Garcia's avatar Emmanuel Garcia Committed by GitHub

Build AAR for all build variants by default (#44797)

parent 66821916
...@@ -26,9 +26,9 @@ abstract class AndroidBuilder { ...@@ -26,9 +26,9 @@ abstract class AndroidBuilder {
/// Builds an AAR artifact. /// Builds an AAR artifact.
Future<void> buildAar({ Future<void> buildAar({
@required FlutterProject project, @required FlutterProject project,
@required AndroidBuildInfo androidBuildInfo, @required Set<AndroidBuildInfo> androidBuildInfo,
@required String target, @required String target,
@required String outputDir, @required String outputDirectoryPath,
}); });
/// Builds an APK artifact. /// Builds an APK artifact.
...@@ -54,23 +54,32 @@ class _AndroidBuilderImpl extends AndroidBuilder { ...@@ -54,23 +54,32 @@ class _AndroidBuilderImpl extends AndroidBuilder {
@override @override
Future<void> buildAar({ Future<void> buildAar({
@required FlutterProject project, @required FlutterProject project,
@required AndroidBuildInfo androidBuildInfo, @required Set<AndroidBuildInfo> androidBuildInfo,
@required String target, @required String target,
@required String outputDir, @required String outputDirectoryPath,
}) async { }) async {
try { try {
Directory outputDirectory = Directory outputDirectory =
fs.directory(outputDir ?? project.android.buildDirectory); fs.directory(outputDirectoryPath ?? project.android.buildDirectory);
if (project.isModule) { if (project.isModule) {
// Module projects artifacts are located in `build/host`. // Module projects artifacts are located in `build/host`.
outputDirectory = outputDirectory.childDirectory('host'); outputDirectory = outputDirectory.childDirectory('host');
} }
await buildGradleAar( for (AndroidBuildInfo androidBuildInfo in androidBuildInfo) {
project: project, await buildGradleAar(
androidBuildInfo: androidBuildInfo, project: project,
target: target, androidBuildInfo: androidBuildInfo,
outputDir: outputDirectory, target: target,
printHowToConsumeAaar: true, outputDirectory: outputDirectory,
);
}
printHowToConsumeAar(
buildModes: androidBuildInfo
.map<String>((AndroidBuildInfo androidBuildInfo) {
return androidBuildInfo.buildInfo.modeName;
}).toSet(),
androidPackage: project.manifest.androidPackage,
repoDirectory: getRepoDirectory(outputDirectory),
); );
} finally { } finally {
androidSdk.reinitialize(); androidSdk.reinitialize();
......
...@@ -57,7 +57,6 @@ Directory getBundleDirectory(FlutterProject project) { ...@@ -57,7 +57,6 @@ Directory getBundleDirectory(FlutterProject project) {
/// The directory where the repo is generated. /// The directory where the repo is generated.
/// Only applicable to AARs. /// Only applicable to AARs.
@visibleForTesting
Directory getRepoDirectory(Directory buildDirectory) { Directory getRepoDirectory(Directory buildDirectory) {
return buildDirectory return buildDirectory
.childDirectory('outputs') .childDirectory('outputs')
...@@ -474,20 +473,17 @@ Future<void> buildGradleApp({ ...@@ -474,20 +473,17 @@ Future<void> buildGradleApp({
/// ///
/// * [project] is typically [FlutterProject.current()]. /// * [project] is typically [FlutterProject.current()].
/// * [androidBuildInfo] is the build configuration. /// * [androidBuildInfo] is the build configuration.
/// * [target] is the target dart entrypoint. Typically, `lib/main.dart`.
/// * [outputDir] is the destination of the artifacts, /// * [outputDir] is the destination of the artifacts,
Future<void> buildGradleAar({ Future<void> buildGradleAar({
@required FlutterProject project, @required FlutterProject project,
@required AndroidBuildInfo androidBuildInfo, @required AndroidBuildInfo androidBuildInfo,
@required String target, @required String target,
@required Directory outputDir, @required Directory outputDirectory,
@required bool printHowToConsumeAaar,
}) async { }) async {
assert(project != null); assert(project != null);
assert(androidBuildInfo != null);
assert(target != null); assert(target != null);
assert(outputDir != null); assert(androidBuildInfo != null);
assert(printHowToConsumeAaar != null); assert(outputDirectory != null);
if (androidSdk == null) { if (androidSdk == null) {
exitWithNoSdkMessage(); exitWithNoSdkMessage();
...@@ -516,13 +512,14 @@ Future<void> buildGradleAar({ ...@@ -516,13 +512,14 @@ Future<void> buildGradleAar({
gradleUtils.getExecutable(project), gradleUtils.getExecutable(project),
'-I=$initScript', '-I=$initScript',
'-Pflutter-root=$flutterRoot', '-Pflutter-root=$flutterRoot',
'-Poutput-dir=${outputDir.path}', '-Poutput-dir=${outputDirectory.path}',
'-Pis-plugin=${manifest.isPlugin}', '-Pis-plugin=${manifest.isPlugin}',
]; ];
if (target != null && target.isNotEmpty) { if (target != null && target.isNotEmpty) {
command.add('-Ptarget=$target'); command.add('-Ptarget=$target');
} }
if (androidBuildInfo.targetArchs.isNotEmpty) { if (androidBuildInfo.targetArchs.isNotEmpty) {
final String targetPlatforms = androidBuildInfo.targetArchs final String targetPlatforms = androidBuildInfo.targetArchs
.map(getPlatformNameForAndroidArch).join(','); .map(getPlatformNameForAndroidArch).join(',');
...@@ -567,7 +564,7 @@ Future<void> buildGradleAar({ ...@@ -567,7 +564,7 @@ Future<void> buildGradleAar({
exitCode: exitCode, exitCode: exitCode,
); );
} }
final Directory repoDirectory = getRepoDirectory(outputDir); final Directory repoDirectory = getRepoDirectory(outputDirectory);
if (!repoDirectory.existsSync()) { if (!repoDirectory.existsSync()) {
printStatus(result.stdout, wrap: false); printStatus(result.stdout, wrap: false);
printError(result.stderr, wrap: false); printError(result.stderr, wrap: false);
...@@ -580,24 +577,17 @@ Future<void> buildGradleAar({ ...@@ -580,24 +577,17 @@ Future<void> buildGradleAar({
'$successMark Built ${fs.path.relative(repoDirectory.path)}.', '$successMark Built ${fs.path.relative(repoDirectory.path)}.',
color: TerminalColor.green, 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. /// Prints how to consume the AAR from a host app.
void _printHowToConsumeAar({ void printHowToConsumeAar({
@required String buildMode, @required Set<String> buildModes,
@required String androidPackage, @required String androidPackage,
@required String repoPath, @required Directory repoDirectory,
}) { }) {
assert(buildMode != null); assert(buildModes != null && buildModes.isNotEmpty);
assert(androidPackage != null); assert(androidPackage != null);
assert(repoPath != null); assert(repoDirectory != null);
printStatus(''' printStatus('''
...@@ -607,20 +597,42 @@ ${terminal.bolden('Consuming the Module')} ...@@ -607,20 +597,42 @@ ${terminal.bolden('Consuming the Module')}
repositories { repositories {
maven { maven {
url '$repoPath' url '${repoDirectory.path}'
} }
maven { maven {
url 'http://download.flutter.io' 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 { dependencies {''');
${buildMode}Implementation '$androidPackage:flutter_$buildMode:1.0'
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<int> bytes) { String _hex(List<int> bytes) {
...@@ -705,8 +717,7 @@ Future<void> buildPluginsAsAar( ...@@ -705,8 +717,7 @@ Future<void> buildPluginsAsAar(
), ),
), ),
target: '', target: '',
outputDir: buildDirectory, outputDirectory: buildDirectory,
printHowToConsumeAaar: false,
); );
} on ToolExit { } on ToolExit {
// Log the entire plugin entry in `.flutter-plugins` since it // Log the entire plugin entry in `.flutter-plugins` since it
......
...@@ -21,7 +21,7 @@ import 'build_web.dart'; ...@@ -21,7 +21,7 @@ import 'build_web.dart';
class BuildCommand extends FlutterCommand { class BuildCommand extends FlutterCommand {
BuildCommand({bool verboseHelp = false}) { BuildCommand({bool verboseHelp = false}) {
addSubcommand(BuildAarCommand(verboseHelp: verboseHelp)); addSubcommand(BuildAarCommand());
addSubcommand(BuildApkCommand(verboseHelp: verboseHelp)); addSubcommand(BuildApkCommand(verboseHelp: verboseHelp));
addSubcommand(BuildAppBundleCommand(verboseHelp: verboseHelp)); addSubcommand(BuildAppBundleCommand(verboseHelp: verboseHelp));
addSubcommand(BuildAotCommand(verboseHelp: verboseHelp)); addSubcommand(BuildAotCommand(verboseHelp: verboseHelp));
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'dart:async'; import 'dart:async';
import '../android/android_builder.dart'; import '../android/android_builder.dart';
import '../base/common.dart';
import '../base/os.dart'; import '../base/os.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../cache.dart'; import '../cache.dart';
...@@ -14,18 +15,35 @@ import '../runner/flutter_command.dart' show FlutterCommandResult; ...@@ -14,18 +15,35 @@ import '../runner/flutter_command.dart' show FlutterCommandResult;
import 'build.dart'; import 'build.dart';
class BuildAarCommand extends BuildSubCommand { class BuildAarCommand extends BuildSubCommand {
BuildAarCommand({bool verboseHelp = false}) { BuildAarCommand() {
addBuildModeFlags(verboseHelp: verboseHelp); 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(); usesFlavorOption();
usesPubOption(); usesPubOption();
argParser argParser
..addMultiOption('target-platform', ..addMultiOption(
'target-platform',
splitCommas: true, splitCommas: true,
defaultsTo: <String>['android-arm', 'android-arm64', 'android-x64'], defaultsTo: <String>['android-arm', 'android-arm64', 'android-x64'],
allowed: <String>['android-arm', 'android-arm64', 'android-x86', 'android-x64'], allowed: <String>['android-arm', 'android-arm64', 'android-x86', 'android-x64'],
help: 'The target platform for which the project is compiled.', 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.' help: 'The absolute path to the directory where the repository is generated.'
'By default, this is \'<current-directory>android/build\'. ', 'By default, this is \'<current-directory>android/build\'. ',
); );
...@@ -61,21 +79,35 @@ class BuildAarCommand extends BuildSubCommand { ...@@ -61,21 +79,35 @@ class BuildAarCommand extends BuildSubCommand {
@override @override
final String description = 'Build a repository containing an AAR and a POM file.\n\n' 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 ' '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 @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
final BuildInfo buildInfo = getBuildInfo(); final Set<AndroidBuildInfo> androidBuildInfo = <AndroidBuildInfo>{};
final AndroidBuildInfo androidBuildInfo = AndroidBuildInfo(buildInfo, final Iterable<AndroidArch> targetArchitectures = argResults['target-platform']
targetArchs: argResults['target-platform'].map<AndroidArch>(getAndroidArchForName)); .map<AndroidArch>(getAndroidArchForName);
for (String buildMode in const <String>['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( await androidBuilder.buildAar(
project: _getProject(), project: _getProject(),
target: '', // Not needed because this command only builds Android's code. target: '', // Not needed because this command only builds Android's code.
androidBuildInfo: androidBuildInfo, androidBuildInfo: androidBuildInfo,
outputDir: argResults['output-dir'], outputDirectoryPath: argResults['output-dir'],
); );
return null; return null;
} }
......
...@@ -1618,79 +1618,6 @@ plugin2=${plugin2.path} ...@@ -1618,79 +1618,6 @@ plugin2=${plugin2.path}
ProcessManager: () => mockProcessManager, 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<Logger>();
expect(
logger.statusText,
contains('Built build/outputs/repo'),
);
expect(
logger.statusText,
contains('''
Consuming the Module
1. Open <host>/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: <Type, Generator>{
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 { testUsingContext('doesn\'t indicate how to consume an AAR when printHowToConsumeAaar is false', () async {
final File manifestFile = fs.file('pubspec.yaml'); final File manifestFile = fs.file('pubspec.yaml');
manifestFile.createSync(recursive: true); manifestFile.createSync(recursive: true);
...@@ -1721,9 +1648,8 @@ To learn more, visit https://flutter.dev/go/build-aar''')); ...@@ -1721,9 +1648,8 @@ To learn more, visit https://flutter.dev/go/build-aar'''));
await buildGradleAar( await buildGradleAar(
androidBuildInfo: const AndroidBuildInfo(BuildInfo(BuildMode.release, null)), androidBuildInfo: const AndroidBuildInfo(BuildInfo(BuildMode.release, null)),
project: FlutterProject.current(), project: FlutterProject.current(),
outputDir: fs.directory('build/'), outputDirectory: fs.directory('build/'),
target: '', target: '',
printHowToConsumeAaar: false,
); );
final BufferLogger logger = context.get<Logger>(); final BufferLogger logger = context.get<Logger>();
...@@ -1885,9 +1811,8 @@ To learn more, visit https://flutter.dev/go/build-aar''')); ...@@ -1885,9 +1811,8 @@ To learn more, visit https://flutter.dev/go/build-aar'''));
await buildGradleAar( await buildGradleAar(
androidBuildInfo: const AndroidBuildInfo(BuildInfo(BuildMode.release, null)), androidBuildInfo: const AndroidBuildInfo(BuildInfo(BuildMode.release, null)),
project: FlutterProject.current(), project: FlutterProject.current(),
outputDir: fs.directory('build/'), outputDirectory: fs.directory('build/'),
target: '', target: '',
printHowToConsumeAaar: false,
); );
final List<String> actualGradlewCall = verify( final List<String> actualGradlewCall = verify(
...@@ -1913,6 +1838,192 @@ To learn more, visit https://flutter.dev/go/build-aar''')); ...@@ -1913,6 +1838,192 @@ To learn more, visit https://flutter.dev/go/build-aar'''));
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
}); });
group('printHowToConsumeAar', () {
testUsingContext('stdout contains release, debug and profile', () async {
printHowToConsumeAar(
buildModes: const <String>{'release', 'debug', 'profile'},
androidPackage: 'com.mycompany',
repoDirectory: fs.directory('build/'),
);
final BufferLogger logger = context.get<Logger>();
expect(
logger.statusText,
contains(
'\n'
'Consuming the Module\n'
' 1. Open <host>/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: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
Platform: () => fakePlatform('android'),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('stdout contains release', () async {
printHowToConsumeAar(
buildModes: const <String>{'release'},
androidPackage: 'com.mycompany',
repoDirectory: fs.directory('build/'),
);
final BufferLogger logger = context.get<Logger>();
expect(
logger.statusText,
contains(
'\n'
'Consuming the Module\n'
' 1. Open <host>/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: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
Platform: () => fakePlatform('android'),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('stdout contains debug', () async {
printHowToConsumeAar(
buildModes: const <String>{'debug'},
androidPackage: 'com.mycompany',
repoDirectory: fs.directory('build/'),
);
final BufferLogger logger = context.get<Logger>();
expect(
logger.statusText,
contains(
'\n'
'Consuming the Module\n'
' 1. Open <host>/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: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
Platform: () => fakePlatform('android'),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('stdout contains profile', () async {
printHowToConsumeAar(
buildModes: const <String>{'profile'},
androidPackage: 'com.mycompany',
repoDirectory: fs.directory('build/'),
);
final BufferLogger logger = context.get<Logger>();
expect(
logger.statusText,
contains(
'\n'
'Consuming the Module\n'
' 1. Open <host>/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: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
Platform: () => fakePlatform('android'),
ProcessManager: () => FakeProcessManager.any(),
});
});
} }
/// Generates a fake app bundle at the location [directoryName]/[fileName]. /// Generates a fake app bundle at the location [directoryName]/[fileName].
......
...@@ -13,9 +13,9 @@ class FakeAndroidBuilder implements AndroidBuilder { ...@@ -13,9 +13,9 @@ class FakeAndroidBuilder implements AndroidBuilder {
@override @override
Future<void> buildAar({ Future<void> buildAar({
@required FlutterProject project, @required FlutterProject project,
@required AndroidBuildInfo androidBuildInfo, @required Set<AndroidBuildInfo> androidBuildInfo,
@required String target, @required String target,
@required String outputDir, @required String outputDirectoryPath,
}) async {} }) async {}
@override @override
......
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