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

Revert "Add flutter build aar (#35217)" (#36731)

This reverts commit 11460b83.
parent 11460b83
......@@ -961,14 +961,9 @@ Future<void> _androidGradleTests(String subShard) async {
if (subShard == 'gradle1') {
await _runDevicelabTest('gradle_plugin_light_apk_test', env: env);
await _runDevicelabTest('gradle_plugin_fat_apk_test', env: env);
await _runDevicelabTest('gradle_jetifier_test', env: env);
await _runDevicelabTest('gradle_plugin_dependencies_test', env: env);
await _runDevicelabTest('gradle_migrate_settings_test', env: env);
}
if (subShard == 'gradle2') {
await _runDevicelabTest('gradle_plugin_bundle_test', env: env);
await _runDevicelabTest('module_test', env: env);
await _runDevicelabTest('build_aar_plugin_test', env: env);
await _runDevicelabTest('build_aar_module_test', env: env);
}
}
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
final String gradlew = Platform.isWindows ? 'gradlew.bat' : 'gradlew';
final String gradlewExecutable = Platform.isWindows ? gradlew : './$gradlew';
/// Tests that AARs can be built on module projects.
Future<void> main() async {
await task(() async {
section('Find Java');
final String javaHome = await findJavaHome();
if (javaHome == null)
return TaskResult.failure('Could not find Java');
print('\nUsing JAVA_HOME=$javaHome');
section('Create module project');
final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.');
final Directory projectDir = Directory(path.join(tempDir.path, 'hello'));
try {
await inDirectory(tempDir, () async {
await flutter(
'create',
options: <String>['--org', 'io.flutter.devicelab', '--template', 'module', 'hello'],
);
});
section('Add plugins');
final File pubspec = File(path.join(projectDir.path, 'pubspec.yaml'));
String content = pubspec.readAsStringSync();
content = content.replaceFirst(
'\ndependencies:\n',
'\ndependencies:\n device_info:\n package_info:\n',
);
pubspec.writeAsStringSync(content, flush: true);
await inDirectory(projectDir, () async {
await flutter(
'packages',
options: <String>['get'],
);
});
section('Build release AAR');
await inDirectory(projectDir, () async {
await flutter(
'build',
options: <String>['aar', '--verbose'],
);
});
final String repoPath = path.join(
projectDir.path,
'build',
'host',
'outputs',
'repo',
);
checkFileExists(path.join(
repoPath,
'io',
'flutter',
'devicelab',
'hello',
'flutter_release',
'1.0',
'flutter_release-1.0.aar',
));
checkFileExists(path.join(
repoPath,
'io',
'flutter',
'devicelab',
'hello',
'flutter_release',
'1.0',
'flutter_release-1.0.pom',
));
checkFileExists(path.join(
repoPath,
'io',
'flutter',
'plugins',
'deviceinfo',
'device_info_release',
'1.0',
'device_info_release-1.0.aar',
));
checkFileExists(path.join(
repoPath,
'io',
'flutter',
'plugins',
'deviceinfo',
'device_info_release',
'1.0',
'device_info_release-1.0.pom',
));
checkFileExists(path.join(
repoPath,
'io',
'flutter',
'plugins',
'packageinfo',
'package_info_release',
'1.0',
'package_info_release-1.0.aar',
));
checkFileExists(path.join(
repoPath,
'io',
'flutter',
'plugins',
'packageinfo',
'package_info_release',
'1.0',
'package_info_release-1.0.pom',
));
section('Build debug AAR');
await inDirectory(projectDir, () async {
await flutter(
'build',
options: <String>['aar', '--verbose', '--debug'],
);
});
checkFileExists(path.join(
repoPath,
'io',
'flutter',
'devicelab',
'hello',
'flutter_release',
'1.0',
'flutter_release-1.0.aar',
));
checkFileExists(path.join(
repoPath,
'io',
'flutter',
'devicelab',
'hello',
'flutter_debug',
'1.0',
'flutter_debug-1.0.pom',
));
checkFileExists(path.join(
repoPath,
'io',
'flutter',
'plugins',
'deviceinfo',
'device_info_debug',
'1.0',
'device_info_debug-1.0.aar',
));
checkFileExists(path.join(
repoPath,
'io',
'flutter',
'plugins',
'deviceinfo',
'device_info_debug',
'1.0',
'device_info_debug-1.0.pom',
));
checkFileExists(path.join(
repoPath,
'io',
'flutter',
'plugins',
'packageinfo',
'package_info_debug',
'1.0',
'package_info_debug-1.0.aar',
));
checkFileExists(path.join(
repoPath,
'io',
'flutter',
'plugins',
'packageinfo',
'package_info_debug',
'1.0',
'package_info_debug-1.0.pom',
));
return TaskResult.success(null);
} catch (e) {
return TaskResult.failure(e.toString());
} finally {
rmTree(tempDir);
}
});
}
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
final String gradlew = Platform.isWindows ? 'gradlew.bat' : 'gradlew';
final String gradlewExecutable = Platform.isWindows ? gradlew : './$gradlew';
/// Tests that AARs can be built on plugin projects.
Future<void> main() async {
await task(() async {
section('Find Java');
final String javaHome = await findJavaHome();
if (javaHome == null)
return TaskResult.failure('Could not find Java');
print('\nUsing JAVA_HOME=$javaHome');
section('Create plugin project');
final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.');
final Directory projectDir = Directory(path.join(tempDir.path, 'hello'));
try {
await inDirectory(tempDir, () async {
await flutter(
'create',
options: <String>[
'--org', 'io.flutter.devicelab',
'--template', 'plugin',
'hello',
],
);
});
section('Build release AAR');
await inDirectory(projectDir, () async {
await flutter(
'build',
options: <String>['aar', '--verbose'],
);
});
final String repoPath = path.join(
projectDir.path,
'build',
'outputs',
'repo',
);
final File releaseAar = File(path.join(
repoPath,
'io',
'flutter',
'devicelab',
'hello',
'hello_release',
'1.0',
'hello_release-1.0.aar',
));
if (!exists(releaseAar)) {
return TaskResult.failure('Failed to build the release AAR file.');
}
final File releasePom = File(path.join(
repoPath,
'io',
'flutter',
'devicelab',
'hello',
'hello_release',
'1.0',
'hello_release-1.0.pom',
));
if (!exists(releasePom)) {
return TaskResult.failure('Failed to build the release POM file.');
}
section('Build debug AAR');
await inDirectory(projectDir, () async {
await flutter(
'build',
options: <String>[
'aar',
'--verbose',
'--debug',
],
);
});
final File debugAar = File(path.join(
repoPath,
'io',
'flutter',
'devicelab',
'hello',
'hello_debug',
'1.0',
'hello_debug-1.0.aar',
));
if (!exists(debugAar)) {
return TaskResult.failure('Failed to build the debug AAR file.');
}
final File debugPom = File(path.join(
repoPath,
'io',
'flutter',
'devicelab',
'hello',
'hello_debug',
'1.0',
'hello_debug-1.0.pom',
));
if (!exists(debugPom)) {
return TaskResult.failure('Failed to build the debug POM file.');
}
return TaskResult.success(null);
} catch (e) {
return TaskResult.failure(e.toString());
} finally {
rmTree(tempDir);
}
});
}
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
final String gradlew = Platform.isWindows ? 'gradlew.bat' : 'gradlew';
final String gradlewExecutable = Platform.isWindows ? gradlew : './$gradlew';
/// Tests that Jetifier can translate plugins that use support libraries.
Future<void> main() async {
await task(() async {
section('Find Java');
final String javaHome = await findJavaHome();
if (javaHome == null)
return TaskResult.failure('Could not find Java');
print('\nUsing JAVA_HOME=$javaHome');
section('Create Flutter AndroidX app project');
final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.');
final Directory projectDir = Directory(path.join(tempDir.path, 'hello'));
try {
await inDirectory(tempDir, () async {
await flutter(
'create',
options: <String>[
'--org', 'io.flutter.devicelab',
'--androidx',
'hello',
],
);
});
section('Add plugin that uses support libraries');
final File pubspec = File(path.join(projectDir.path, 'pubspec.yaml'));
String content = pubspec.readAsStringSync();
content = content.replaceFirst(
'\ndependencies:\n',
'\ndependencies:\n firebase_auth: 0.7.0\n',
);
pubspec.writeAsStringSync(content, flush: true);
await inDirectory(projectDir, () async {
await flutter(
'packages',
options: <String>['get'],
);
});
section('Build release APK');
await inDirectory(projectDir, () async {
await flutter(
'build',
options: <String>[
'apk',
'--target-platform', 'android-arm',
'--verbose',
],
);
});
final File releaseApk = File(path.join(
projectDir.path,
'build',
'app',
'outputs',
'apk',
'release',
'app-release.apk',
));
if (!exists(releaseApk)) {
return TaskResult.failure('Failed to build release APK.');
}
checkApkContainsClasses(releaseApk, <String>[
// The plugin class defined by `firebase_auth`.
'io.flutter.plugins.firebaseauth.FirebaseAuthPlugin',
// Used by `firebase_auth`.
'com.google.firebase.FirebaseApp',
// Base class for activities that enables composition of higher level components.
'androidx.core.app.ComponentActivity',
]);
section('Build debug APK');
await inDirectory(projectDir, () async {
await flutter(
'build',
options: <String>[
'apk',
'--target-platform', 'android-arm',
'--debug', '--verbose',
],
);
});
final File debugApk = File(path.join(
projectDir.path,
'build',
'app',
'outputs',
'apk',
'debug',
'app-debug.apk',
));
if (!exists(debugApk)) {
return TaskResult.failure('Failed to build debug APK.');
}
checkApkContainsClasses(debugApk, <String>[
// The plugin class defined by `firebase_auth`.
'io.flutter.plugins.firebaseauth.FirebaseAuthPlugin',
// Used by `firebase_auth`.
'com.google.firebase.FirebaseApp',
// Base class for activities that enables composition of higher level components.
'androidx.core.app.ComponentActivity',
]);
return TaskResult.success(null);
} catch (e) {
return TaskResult.failure(e.toString());
} finally {
rmTree(tempDir);
}
});
}
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
final String gradlew = Platform.isWindows ? 'gradlew.bat' : 'gradlew';
final String gradlewExecutable = Platform.isWindows ? gradlew : './$gradlew';
/// Tests that [settings_aar.gradle] is created when possible.
Future<void> main() async {
await task(() async {
section('Find Java');
final String javaHome = await findJavaHome();
if (javaHome == null)
return TaskResult.failure('Could not find Java');
print('\nUsing JAVA_HOME=$javaHome');
section('Create app project');
final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.');
final Directory projectDir = Directory(path.join(tempDir.path, 'hello'));
try {
await inDirectory(tempDir, () async {
await flutter(
'create',
options: <String>['hello'],
);
});
section('Override settings.gradle V1');
final String relativeNewSettingsGradle = path.join('android', 'settings_aar.gradle');
section('Build APK');
String stdout;
await inDirectory(projectDir, () async {
stdout = await evalFlutter(
'build',
options: <String>[
'apk',
'--flavor', 'does-not-exist',
],
canFail: true, // The flavor doesn't exist.
);
});
const String newFileContent = 'include \':app\'';
final File settingsGradle = File(path.join(projectDir.path, 'android', 'settings.gradle'));
final File newSettingsGradle = File(path.join(projectDir.path, 'android', 'settings_aar.gradle'));
if (!newSettingsGradle.existsSync()) {
return TaskResult.failure('Expected file: `${newSettingsGradle.path}`.');
}
if (newSettingsGradle.readAsStringSync().trim() != newFileContent) {
return TaskResult.failure('Expected to create `${newSettingsGradle.path}` V1.');
}
if (!stdout.contains('Creating `$relativeNewSettingsGradle`') ||
!stdout.contains('`$relativeNewSettingsGradle` created successfully')) {
return TaskResult.failure('Expected update message in stdout.');
}
section('Override settings.gradle V2');
const String deprecatedFileContentV2 = '''
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withInputStream { stream -> plugins.load(stream) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":\$name"
project(":\$name").projectDir = pluginDirectory
}
''';
settingsGradle.writeAsStringSync(deprecatedFileContentV2, flush: true);
newSettingsGradle.deleteSync();
section('Build APK');
await inDirectory(projectDir, () async {
stdout = await evalFlutter(
'build',
options: <String>[
'apk',
'--flavor', 'does-not-exist',
],
canFail: true, // The flavor doesn't exist.
);
});
if (newSettingsGradle.readAsStringSync().trim() != newFileContent) {
return TaskResult.failure('Expected to create `${newSettingsGradle.path}` V2.');
}
if (!stdout.contains('Creating `$relativeNewSettingsGradle`') ||
!stdout.contains('`$relativeNewSettingsGradle` created successfully')) {
return TaskResult.failure('Expected update message in stdout.');
}
section('Override settings.gradle with custom logic');
const String customDeprecatedFileContent = '''
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withInputStream { stream -> plugins.load(stream) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":\$name"
project(":\$name").projectDir = pluginDirectory
}
// some custom logic
''';
settingsGradle.writeAsStringSync(customDeprecatedFileContent, flush: true);
newSettingsGradle.deleteSync();
section('Build APK');
final StringBuffer stderr = StringBuffer();
await inDirectory(projectDir, () async {
stdout = await evalFlutter(
'build',
options: <String>[
'apk',
'--flavor', 'does-not-exist',
],
canFail: true, // The flavor doesn't exist.
stderr: stderr,
);
});
if (newSettingsGradle.existsSync()) {
return TaskResult.failure('Unexpected file: `${newSettingsGradle.path}`.');
}
if (!stdout.contains('Creating `$relativeNewSettingsGradle`')) {
return TaskResult.failure('Expected update message in stdout.');
}
if (stdout.contains('`$relativeNewSettingsGradle` created successfully')) {
return TaskResult.failure('Unexpected message in stdout.');
}
if (!stderr.toString().contains('Flutter tried to create the file '
'`$relativeNewSettingsGradle`, but failed.')) {
return TaskResult.failure('Expected failure message in stdout.');
}
return TaskResult.success(null);
} catch (e) {
return TaskResult.failure(e.toString());
} finally {
rmTree(tempDir);
}
});
}
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
final String gradlew = Platform.isWindows ? 'gradlew.bat' : 'gradlew';
final String gradlewExecutable = Platform.isWindows ? gradlew : './$gradlew';
/// Tests that projects can include plugins that have a transtive dependency in common.
/// For more info see: https://github.com/flutter/flutter/issues/27254.
Future<void> main() async {
await task(() async {
section('Find Java');
final String javaHome = await findJavaHome();
if (javaHome == null)
return TaskResult.failure('Could not find Java');
print('\nUsing JAVA_HOME=$javaHome');
section('Create Flutter AndroidX app project');
final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.');
final Directory projectDir = Directory(path.join(tempDir.path, 'hello'));
try {
await inDirectory(tempDir, () async {
await flutter(
'create',
options: <String>[
'--org', 'io.flutter.devicelab',
'--androidx',
'hello',
],
);
});
section('Add plugin that have conflicting dependencies');
final File pubspec = File(path.join(projectDir.path, 'pubspec.yaml'));
String content = pubspec.readAsStringSync();
// `flutter_local_notifications` uses `androidx.core:core:1.0.1`
// `firebase_core` and `firebase_messaging` use `androidx.core:core:1.0.0`.
content = content.replaceFirst(
'\ndependencies:\n',
'\ndependencies:\n flutter_local_notifications: 0.7.1+3\n firebase_core:\n firebase_messaging:\n',
);
pubspec.writeAsStringSync(content, flush: true);
await inDirectory(projectDir, () async {
await flutter(
'packages',
options: <String>['get'],
);
});
section('Build release APK');
await inDirectory(projectDir, () async {
await flutter(
'build',
options: <String>[
'apk',
'--target-platform', 'android-arm',
'--verbose',
],
);
});
final File releaseApk = File(path.join(
projectDir.path,
'build',
'app',
'outputs',
'apk',
'release',
'app-release.apk',
));
if (!exists(releaseApk)) {
return TaskResult.failure('Failed to build release APK.');
}
checkApkContainsClasses(releaseApk, <String>[
// Used by `flutter_local_notifications`.
'com.google.gson.Gson',
// Used by `firebase_core` and `firebase_messaging`.
'com.google.firebase.FirebaseApp',
// Used by `firebase_core`.
'com.google.firebase.FirebaseOptions',
// Used by `firebase_messaging`.
'com.google.firebase.messaging.FirebaseMessaging',
]);
section('Build debug APK');
await inDirectory(projectDir, () async {
await flutter(
'build',
options: <String>[
'apk',
'--target-platform', 'android-arm',
'--debug',
'--verbose',
],
);
});
final File debugApk = File(path.join(
projectDir.path,
'build',
'app',
'outputs',
'apk',
'debug',
'app-debug.apk',
));
if (!exists(debugApk)) {
return TaskResult.failure('Failed to build debug APK.');
}
checkApkContainsClasses(debugApk, <String>[
// Used by `flutter_local_notifications`.
'com.google.gson.Gson',
// Used by `firebase_core` and `firebase_messaging`.
'com.google.firebase.FirebaseApp',
// Used by `firebase_core`.
'com.google.firebase.FirebaseOptions',
// Used by `firebase_messaging`.
'com.google.firebase.messaging.FirebaseMessaging',
]);
return TaskResult.success(null);
} catch (e) {
return TaskResult.failure(e.toString());
} finally {
rmTree(tempDir);
}
});
}
......@@ -42,7 +42,7 @@ Future<void> main() async {
String content = await pubspec.readAsString();
content = content.replaceFirst(
'\ndependencies:\n',
'\ndependencies:\n device_info:\n package_info:\n',
'\ndependencies:\n battery:\n package_info:\n',
);
await pubspec.writeAsString(content, flush: true);
await inDirectory(projectDir, () async {
......
......@@ -143,7 +143,7 @@ Future<void> main() async {
String content = await pubspec.readAsString();
content = content.replaceFirst(
'\ndependencies:\n',
'\ndependencies:\n device_info:\n package_info:\n',
'\ndependencies:\n battery:\n package_info:\n',
);
await pubspec.writeAsString(content, flush: true);
await inDirectory(projectDir, () async {
......
......@@ -83,93 +83,6 @@ bool hasMultipleOccurrences(String text, Pattern pattern) {
return text.indexOf(pattern) != text.lastIndexOf(pattern);
}
/// Utility class to analyze the content inside an APK using dexdump,
/// which is provided by the Android SDK.
/// https://android.googlesource.com/platform/art/+/master/dexdump/dexdump.cc
class ApkExtractor {
ApkExtractor(this.apkFile);
/// The APK.
final File apkFile;
bool _extracted = false;
Directory _outputDir;
Future<void> _extractApk() async {
if (_extracted) {
return;
}
_outputDir = apkFile.parent.createTempSync('apk');
if (Platform.isWindows) {
await eval('7za', <String>['x', apkFile.path], workingDirectory: _outputDir.path);
} else {
await eval('unzip', <String>[apkFile.path], workingDirectory: _outputDir.path);
}
_extracted = true;
}
/// Returns the full path to the [dexdump] tool.
Future<String> _findDexDump() async {
final String androidHome = Platform.environment['ANDROID_HOME'] ??
Platform.environment['ANDROID_SDK_ROOT'];
if (androidHome == null || androidHome.isEmpty) {
throw Exception('Unset env flag: `ANDROID_HOME` or `ANDROID_SDK_ROOT`.');
}
String dexdumps;
if (Platform.isWindows) {
dexdumps = await eval('dir', <String>['/s/b', 'dexdump.exe'],
workingDirectory: androidHome);
} else {
dexdumps = await eval('find', <String>[androidHome, '-name', 'dexdump']);
}
if (dexdumps.isEmpty) {
throw Exception('Couldn\'t find a dexdump executable.');
}
return dexdumps.split('\n').first;
}
// Removes any temporary directory.
void dispose() {
if (!_extracted) {
return;
}
rmTree(_outputDir);
_extracted = true;
}
/// Returns true if the APK contains a given class.
Future<bool> containsClass(String className) async {
await _extractApk();
final String dexDump = await _findDexDump();
final String classesDex = path.join(_outputDir.path, 'classes.dex');
if (!File(classesDex).existsSync()) {
throw Exception('Couldn\'t find classes.dex in the APK.');
}
final String classDescriptors = await eval(dexDump,
<String>[classesDex], printStdout: false);
if (classDescriptors.isEmpty) {
throw Exception('No descriptors found in classes.dex.');
}
return classDescriptors.contains(className.replaceAll('.', '/'));
}
}
/// Checks that the classes are contained in the APK, throws otherwise.
Future<void> checkApkContainsClasses(File apk, List<String> classes) async {
final ApkExtractor extractor = ApkExtractor(apk);
for (String className in classes) {
if (!(await extractor.containsClass(className))) {
throw Exception('APK doesn\'t contain class `$className`.');
}
}
extractor.dispose();
}
class FlutterProject {
FlutterProject(this.parent, this.name);
......
......@@ -303,7 +303,7 @@ Future<int> exec(
/// Executes a command and returns its standard output as a String.
///
/// For logging purposes, the command's output is also printed out by default.
/// For logging purposes, the command's output is also printed out.
Future<String> eval(
String executable,
List<String> arguments, {
......@@ -311,8 +311,6 @@ Future<String> eval(
bool canFail = false, // as in, whether failures are ok. False means that they are fatal.
String workingDirectory,
StringBuffer stderr, // if not null, the stderr will be written here
bool printStdout = true,
bool printStderr = true,
}) async {
final Process process = await startProcess(executable, arguments, environment: environment, workingDirectory: workingDirectory);
......@@ -323,18 +321,14 @@ Future<String> eval(
.transform<String>(utf8.decoder)
.transform<String>(const LineSplitter())
.listen((String line) {
if (printStdout) {
print('stdout: $line');
}
print('stdout: $line');
output.writeln(line);
}, onDone: () { stdoutDone.complete(); });
process.stderr
.transform<String>(utf8.decoder)
.transform<String>(const LineSplitter())
.listen((String line) {
if (printStderr) {
print('stderr: $line');
}
print('stderr: $line');
stderr?.writeln(line);
}, onDone: () { stderrDone.complete(); });
......@@ -625,10 +619,3 @@ void setLocalEngineOptionIfNecessary(List<String> options, [String flavor]) {
options.add('--local-engine=${osNames[deviceOperatingSystem]}_$flavor');
}
}
/// Checks that the file exists, otherwise throws a [FileSystemException].
void checkFileExists(String file) {
if (!exists(File(file))) {
throw FileSystemException('Expected file to exit.', file);
}
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This script is used to initialize the build in a module or plugin project.
// During this phase, the script applies the Maven plugin and configures the
// destination of the local repository.
// The local repository will contain the AAR and POM files.
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.maven.MavenDeployer
import org.gradle.api.plugins.MavenPlugin
import org.gradle.api.tasks.Upload
void configureProject(Project project, File outputDir) {
if (!project.hasProperty("android")) {
throw new GradleException("Android property not found.")
}
if (!project.android.hasProperty("libraryVariants")) {
throw new GradleException("Can't generate AAR on a non Android library project.");
}
project.apply plugin: "maven"
project.android.libraryVariants.all { variant ->
addAarTask(project, variant)
}
// Snapshot versions include the timestamp in the artifact name.
// Therefore, remove the snapshot part, so new runs of `flutter build aar` overrides existing artifacts.
// This version isn't relevant in Flutter since the pub version is used
// to resolve dependencies.
project.version = project.version.replace("-SNAPSHOT", "")
project.uploadArchives {
repositories {
mavenDeployer {
repository(url: "file://${outputDir}/outputs/repo")
}
}
}
// Check if the project uses the Flutter plugin (defined in flutter.gradle).
Boolean usesFlutterPlugin = project.plugins.find { it.class.name == "FlutterPlugin" } != null
if (!usesFlutterPlugin) {
// Plugins don't include their dependencies under the assumption that the parent project adds them.
if (project.properties['android.useAndroidX']) {
project.dependencies {
compileOnly "androidx.annotation:annotation:+"
}
} else {
project.dependencies {
compileOnly "com.android.support:support-annotations:+"
}
}
project.dependencies {
// The Flutter plugin already adds `flutter.jar`.
compileOnly project.files("${getFlutterRoot(project)}/bin/cache/artifacts/engine/android-arm-release/flutter.jar")
}
}
}
String getFlutterRoot(Project project) {
if (!project.hasProperty("flutter-root")) {
throw new GradleException("The `-Pflutter-root` flag must be specified.")
}
return project.property("flutter-root")
}
void addAarTask(Project project, variant) {
String variantName = variant.name.capitalize()
String taskName = "assembleAar${variantName}"
project.tasks.create(name: taskName) {
// This check is required to be able to configure the archives before `uploadArchives` runs.
if (!project.gradle.startParameter.taskNames.contains(taskName)) {
return
}
// NOTE(blasten): `android.defaultPublishConfig` must equal the variant name to build.
// Where variant name is `<product-flavor><Build-Type>`. However, it's too late to configure
// `defaultPublishConfig` at this point. Therefore, the code below ensures that the
// default build config uses the artifacts produced for the specific build variant.
Task bundle = project.tasks.findByName("bundle${variantName}Aar") // gradle:3.2.0
if (bundle == null) {
bundle = project.tasks.findByName("bundle${variantName}") // gradle:3.1.0
}
if (bundle == null) {
throw new GradleException("Can't generate AAR for variant ${variantName}.");
}
project.uploadArchives.repositories.mavenDeployer {
pom {
artifactId = "${project.name}_${variant.name.toLowerCase()}"
}
}
// Clear the current archives since the current one is assigned based on
// `android.defaultPublishConfig` which defaults to `release`.
project.configurations["archives"].artifacts.clear()
// Add the artifact that will be published.
project.artifacts.add("archives", bundle)
// Generate the Maven artifacts.
finalizedBy "uploadArchives"
}
}
projectsEvaluated {
if (rootProject.property("is-plugin").toBoolean()) {
if (rootProject.hasProperty("output-dir")) {
rootProject.buildDir = rootProject.property("output-dir")
} else {
rootProject.buildDir = "../build";
}
// In plugin projects, the Android library is the root project.
configureProject(rootProject, rootProject.buildDir)
return
}
// In module projects, the Android library project is the `:flutter` subproject.
Project androidLibrarySubproject = rootProject.subprojects.find { it.name == "flutter" }
// In module projects, the `buildDir` is defined in the `:app` subproject.
Project appSubproject = rootProject.subprojects.find { it.name == "app" }
assert appSubproject != null
assert androidLibrarySubproject != null
if (appSubproject.hasProperty("output-dir")) {
appSubproject.buildDir = appSubproject.property("output-dir")
} else {
appSubproject.buildDir = "../build/host"
}
configureProject(androidLibrarySubproject, appSubproject.buildDir)
}
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}
;EOF
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withInputStream { stream -> plugins.load(stream) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}
To manually update `settings.gradle`, follow these steps:
1. Copy `settings.gradle` as `settings_aar.gradle`
2. Remove the following code from `settings_aar.gradle`:
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:meta/meta.dart';
import '../base/common.dart';
import '../build_info.dart';
import '../project.dart';
import 'android_sdk.dart';
import 'gradle.dart';
/// Provides a method to build a module or plugin as AAR.
abstract class AarBuilder {
/// Builds the AAR artifacts.
Future<void> build({
@required FlutterProject project,
@required AndroidBuildInfo androidBuildInfo,
@required String target,
@required String outputDir,
});
}
/// Default implementation of [AarBuilder].
class AarBuilderImpl extends AarBuilder {
AarBuilderImpl();
/// Builds the AAR and POM files for the current Flutter module or plugin.
@override
Future<void> build({
@required FlutterProject project,
@required AndroidBuildInfo androidBuildInfo,
@required String target,
@required String outputDir,
}) async {
if (!project.android.isUsingGradle) {
throwToolExit(
'The build process for Android has changed, and the current project configuration\n'
'is no longer valid. Please consult\n\n'
' https://github.com/flutter/flutter/wiki/Upgrading-Flutter-projects-to-build-with-gradle\n\n'
'for details on how to upgrade the project.'
);
}
if (!project.manifest.isModule && !project.manifest.isPlugin) {
throwToolExit('AARs can only be built for plugin or module projects.');
}
// Validate that we can find an Android SDK.
if (androidSdk == null) {
throwToolExit('No Android SDK found. Try setting the `ANDROID_SDK_ROOT` environment variable.');
}
await buildGradleAar(
project: project,
androidBuildInfo: androidBuildInfo,
target: target,
outputDir: outputDir,
);
androidSdk.reinitialize();
}
}
......@@ -9,7 +9,6 @@ import '../commands/build_macos.dart';
import '../commands/build_windows.dart';
import '../runner/flutter_command.dart';
import 'build_aar.dart';
import 'build_aot.dart';
import 'build_apk.dart';
import 'build_appbundle.dart';
......@@ -20,7 +19,6 @@ import 'build_web.dart';
class BuildCommand extends FlutterCommand {
BuildCommand({bool verboseHelp = false}) {
addSubcommand(BuildAarCommand(verboseHelp: verboseHelp));
addSubcommand(BuildApkCommand(verboseHelp: verboseHelp));
addSubcommand(BuildAppBundleCommand(verboseHelp: verboseHelp));
addSubcommand(BuildAotCommand());
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import '../android/aar.dart';
import '../base/context.dart';
import '../base/os.dart';
import '../build_info.dart';
import '../project.dart';
import '../reporting/usage.dart';
import '../runner/flutter_command.dart' show DevelopmentArtifact, FlutterCommandResult;
import 'build.dart';
/// The AAR builder in the current context.
AarBuilder get aarBuilder => context.get<AarBuilder>() ?? AarBuilderImpl();
class BuildAarCommand extends BuildSubCommand {
BuildAarCommand({bool verboseHelp = false}) {
addBuildModeFlags(verboseHelp: verboseHelp);
usesFlavorOption();
usesPubOption();
argParser
..addMultiOption('target-platform',
splitCommas: true,
defaultsTo: <String>['android-arm', 'android-arm64'],
allowed: <String>['android-arm', 'android-arm64', 'android-x86', 'android-x64'],
help: 'The target platform for which the project is compiled.',
)
..addOption('output-dir',
help: 'The absolute path to the directory where the repository is generated.'
'By default, this is \'<current-directory>android/build\'. ',
);
}
@override
final String name = 'aar';
@override
Future<Map<String, String>> get usageValues async {
final Map<String, String> usage = <String, String>{};
final FlutterProject futterProject = _getProject();
if (futterProject == null) {
return usage;
}
if (futterProject.manifest.isModule) {
usage[kCommandBuildAarProjectType] = 'module';
} else if (futterProject.manifest.isPlugin) {
usage[kCommandBuildAarProjectType] = 'plugin';
} else {
usage[kCommandBuildAarProjectType] = 'app';
}
usage[kCommandBuildAarTargetPlatform] =
(argResults['target-platform'] as List<String>).join(',');
return usage;
}
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{
DevelopmentArtifact.universal,
DevelopmentArtifact.android,
};
@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'
'To learn more about how to use these artifacts, see '
'https://docs.gradle.org/current/userguide/repository_types.html#sub:maven_local';
@override
Future<FlutterCommandResult> runCommand() async {
final BuildInfo buildInfo = getBuildInfo();
final AndroidBuildInfo androidBuildInfo = AndroidBuildInfo(buildInfo,
targetArchs: argResults['target-platform'].map<AndroidArch>(getAndroidArchForName));
await aarBuilder.build(
project: _getProject(),
target: '', // Not needed because this command only builds Android's code.
androidBuildInfo: androidBuildInfo,
outputDir: argResults['output-dir'],
);
return null;
}
/// Returns the [FlutterProject] which is determinated from the remaining command-line
/// argument if any or the current working directory.
FlutterProject _getProject() {
if (argResults.rest.isEmpty) {
return FlutterProject.current();
}
return FlutterProject.fromPath(findProjectRoot(argResults.rest.first));
}
}
......@@ -36,9 +36,6 @@ class FeatureFlags {
/// Whether flutter desktop for Windows is enabled.
bool get isWindowsEnabled => _isEnabled(flutterWindowsDesktopFeature);
/// Whether plugins are built as AARs in app projects.
bool get isPluginAsAarEnabled => _isEnabled(flutterBuildPluginAsAarFeature);
// Calculate whether a particular feature is enabled for the current channel.
static bool _isEnabled(Feature feature) {
final String currentChannel = FlutterVersion.instance.channel;
......@@ -68,7 +65,6 @@ const List<Feature> allFeatures = <Feature>[
flutterLinuxDesktopFeature,
flutterMacOSDesktopFeature,
flutterWindowsDesktopFeature,
flutterBuildPluginAsAarFeature,
];
/// The [Feature] for flutter web.
......@@ -119,20 +115,6 @@ const Feature flutterWindowsDesktopFeature = Feature(
),
);
/// The [Feature] for building plugins as AARs in an app project.
const Feature flutterBuildPluginAsAarFeature = Feature(
name: 'Build plugins independently as AARs in app projects',
configSetting: 'enable-build-plugin-as-aar',
master: FeatureChannelSetting(
available: true,
enabledByDefault: true,
),
dev: FeatureChannelSetting(
available: true,
enabledByDefault: false,
),
);
/// A [Feature] is a process for conditionally enabling tool features.
///
/// All settings are optional, and if not provided will generally default to
......
......@@ -510,6 +510,10 @@ class AndroidProject {
return fs.directory(fs.path.join(hostAppGradleRoot.path, 'app', 'build', 'outputs', 'apk'));
}
Directory get gradleAppBundleOutV1Directory {
return fs.directory(fs.path.join(hostAppGradleRoot.path, 'app', 'build', 'outputs', 'bundle'));
}
/// Whether the current flutter project has an Android sub-project.
bool existsSync() {
return parent.isModule || _editableHostAppDirectory.existsSync();
......
......@@ -57,16 +57,13 @@ const String kCommandBuildBundleIsModule = 'cd25';
const String kCommandResult = 'cd26';
const String kCommandHasTerminal = 'cd31';
const String kCommandBuildAarTargetPlatform = 'cd34';
const String kCommandBuildAarProjectType = 'cd35';
const String reloadExceptionTargetPlatform = 'cd27';
const String reloadExceptionSdkName = 'cd28';
const String reloadExceptionEmulator = 'cd29';
const String reloadExceptionFullRestart = 'cd30';
const String enabledFlutterFeatures = 'cd32';
// Next ID: cd36
// Next ID: cd34
Usage get flutterUsage => Usage.instance;
......
......@@ -26,9 +26,6 @@ if (flutterVersionName == null) {
apply plugin: 'com.android.library'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
group '{{androidIdentifier}}'
version '1.0'
android {
compileSdkVersion 28
......
......@@ -6,37 +6,24 @@ def flutterProjectRoot = new File(scriptFile).parentFile.parentFile
gradle.include ':flutter'
gradle.project(':flutter').projectDir = new File(flutterProjectRoot, '.android/Flutter')
if (System.getProperty('build-plugins-as-aars') != 'true') {
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot, '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot, '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.toPath().resolve(path).resolve('android').toFile()
gradle.include ":$name"
gradle.project(":$name").projectDir = pluginDirectory
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.toPath().resolve(path).resolve('android').toFile()
gradle.include ":$name"
gradle.project(":$name").projectDir = pluginDirectory
}
gradle.getGradle().projectsLoaded { g ->
g.rootProject.beforeEvaluate { p ->
_mainModuleName = binding.variables['mainModuleName']
if (_mainModuleName != null && !_mainModuleName.empty) {
p.ext.mainModuleName = _mainModuleName
}
def subprojects = []
def flutterProject
p.subprojects { sp ->
if (sp.name == 'flutter') {
flutterProject = sp
} else {
subprojects.add(sp)
}
}
assert flutterProject != null
flutterProject.ext.hostProjects = subprojects
flutterProject.ext.pluginBuildDir = new File(flutterProjectRoot, 'build/host')
}
g.rootProject.afterEvaluate { p ->
p.subprojects { sp ->
......
group '{{androidIdentifier}}'
version '1.0'
version '1.0-SNAPSHOT'
buildscript {
repositories {
......
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:args/command_runner.dart';
import 'package:flutter_tools/src/android/aar.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build_aar.dart';
import 'package:flutter_tools/src/reporting/usage.dart';
import 'package:mockito/mockito.dart';
import '../../src/common.dart';
import '../../src/context.dart';
void main() {
Cache.disableLocking();
group('getUsage', () {
Directory tempDir;
AarBuilder mockAarBuilder;
setUp(() {
tempDir = fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
mockAarBuilder = MockAarBuilder();
when(mockAarBuilder.build(
project: anyNamed('project'),
androidBuildInfo: anyNamed('androidBuildInfo'),
target: anyNamed('target'),
outputDir: anyNamed('outputDir'))).thenAnswer((_) => Future<void>.value());
});
tearDown(() {
tryToDelete(tempDir);
});
Future<BuildAarCommand> runCommandIn(String target, { List<String> arguments }) async {
final BuildAarCommand command = BuildAarCommand();
final CommandRunner<void> runner = createTestCommandRunner(command);
await runner.run(<String>[
'aar',
...?arguments,
target,
]);
return command;
}
testUsingContext('indicate that project is a module', () async {
final String projectPath = await createProject(tempDir,
arguments: <String>['--no-pub', '--template=module']);
final BuildAarCommand command = await runCommandIn(projectPath);
expect(await command.usageValues,
containsPair(kCommandBuildAarProjectType, 'module'));
}, overrides: <Type, Generator>{
AarBuilder: () => mockAarBuilder,
}, timeout: allowForCreateFlutterProject);
testUsingContext('indicate that project is a plugin', () async {
final String projectPath = await createProject(tempDir,
arguments: <String>['--no-pub', '--template=plugin', '--project-name=aar_test']);
final BuildAarCommand command = await runCommandIn(projectPath);
expect(await command.usageValues,
containsPair(kCommandBuildAarProjectType, 'plugin'));
}, overrides: <Type, Generator>{
AarBuilder: () => mockAarBuilder,
}, timeout: allowForCreateFlutterProject);
testUsingContext('indicate the target platform', () async {
final String projectPath = await createProject(tempDir,
arguments: <String>['--no-pub', '--template=module']);
final BuildAarCommand command = await runCommandIn(projectPath,
arguments: <String>['--target-platform=android-arm']);
expect(await command.usageValues,
containsPair(kCommandBuildAarTargetPlatform, 'android-arm'));
}, overrides: <Type, Generator>{
AarBuilder: () => mockAarBuilder,
}, timeout: allowForCreateFlutterProject);
});
}
class MockAarBuilder extends Mock implements AarBuilder {}
......@@ -418,21 +418,6 @@ void main() {
expect(featureFlags.isWindowsEnabled, false);
}));
/// Plugins as AARS
test('plugins built as AARs with config on master', () => testbed.run(() {
when(mockFlutterVerion.channel).thenReturn('master');
when<bool>(mockFlutterConfig.getValue('enable-build-plugin-as-aar')).thenReturn(true);
expect(featureFlags.isPluginAsAarEnabled, true);
}));
test('plugins built as AARs with config on dev', () => testbed.run(() {
when(mockFlutterVerion.channel).thenReturn('dev');
when<bool>(mockFlutterConfig.getValue('enable-build-plugin-as-aar')).thenReturn(true);
expect(featureFlags.isPluginAsAarEnabled, true);
}));
});
}
......
......@@ -697,7 +697,6 @@ class TestFeatureFlags implements FeatureFlags {
this.isMacOSEnabled = false,
this.isWebEnabled = false,
this.isWindowsEnabled = false,
this.isPluginAsAarEnabled = false,
});
@override
......@@ -711,7 +710,4 @@ class TestFeatureFlags implements FeatureFlags {
@override
final bool isWindowsEnabled;
@override
final bool isPluginAsAarEnabled;
}
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