Add vmservice for android build options (#123034)
https://github.com/flutter/flutter/issues/120408
This commit is contained in:
parent
42fb0b2313
commit
55502fc36a
@ -695,6 +695,28 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
}
|
||||
}
|
||||
|
||||
// Add a task that can be called on flutter projects that prints the available build variants
|
||||
// in gradle.
|
||||
//
|
||||
// This task prints variants in this format:
|
||||
//
|
||||
// BuildVariant: debug
|
||||
// BuildVariant: release
|
||||
// BuildVariant: profile
|
||||
//
|
||||
// Format of the output of this task is used by `AndroidProject.getBuildVariants`.
|
||||
private static void addTaskForPrintBuildVariants(Project project) {
|
||||
// Warning: The name of this task is used by `AndroidProject.getBuildVariants`.
|
||||
project.tasks.register("printBuildVariants") {
|
||||
description "Prints out all build variants for this Android project"
|
||||
doLast {
|
||||
project.android.applicationVariants.all { variant ->
|
||||
println "BuildVariant: ${variant.name}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Flutter build mode suitable for the specified Android buildType.
|
||||
*
|
||||
@ -867,7 +889,9 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
if (project.hasProperty('validate-deferred-components')) {
|
||||
validateDeferredComponentsValue = project.property('validate-deferred-components').toBoolean()
|
||||
}
|
||||
|
||||
addTaskForJavaVersion(project)
|
||||
addTaskForPrintBuildVariants(project)
|
||||
def targetPlatforms = getTargetPlatforms()
|
||||
def addFlutterDeps = { variant ->
|
||||
if (shouldSplitPerAbi()) {
|
||||
|
@ -39,4 +39,7 @@ abstract class AndroidBuilder {
|
||||
bool deferredComponentsEnabled = false,
|
||||
bool configOnly = false,
|
||||
});
|
||||
|
||||
/// Returns a list of available build variant from the Android project.
|
||||
Future<List<String>> getBuildVariants({required FlutterProject project});
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ class AndroidSdk {
|
||||
reinitialize();
|
||||
}
|
||||
|
||||
static const String _javaHomeEnvironmentVariable = 'JAVA_HOME';
|
||||
static const String javaHomeEnvironmentVariable = 'JAVA_HOME';
|
||||
static const String _javaExecutable = 'java';
|
||||
|
||||
/// The Android SDK root directory.
|
||||
@ -462,10 +462,44 @@ class AndroidSdk {
|
||||
return versionString.split('_').first;
|
||||
}
|
||||
|
||||
/// A value that would be appropriate to use as JAVA_HOME.
|
||||
///
|
||||
/// This method considers jdk in the following order:
|
||||
/// * the JDK bundled with Android Studio, if one is found;
|
||||
/// * the JAVA_HOME in the ambient environment, if set;
|
||||
String? get javaHome {
|
||||
return findJavaHome(
|
||||
androidStudio: globals.androidStudio,
|
||||
fileSystem: globals.fs,
|
||||
operatingSystemUtils: globals.os,
|
||||
platform: globals.platform,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
static String? findJavaHome({
|
||||
required AndroidStudio? androidStudio,
|
||||
required FileSystem fileSystem,
|
||||
required OperatingSystemUtils operatingSystemUtils,
|
||||
required Platform platform,
|
||||
}) {
|
||||
if (androidStudio?.javaPath != null) {
|
||||
globals.printTrace("Using Android Studio's java.");
|
||||
return androidStudio!.javaPath!;
|
||||
}
|
||||
|
||||
final String? javaHomeEnv = platform.environment[javaHomeEnvironmentVariable];
|
||||
if (javaHomeEnv != null) {
|
||||
globals.printTrace('Using JAVA_HOME from environment valuables.');
|
||||
return javaHomeEnv;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Finds the java binary that is used for all operations across the tool.
|
||||
///
|
||||
/// First try Java bundled with Android Studio, then sniff JAVA_HOME, then fallback to PATH.
|
||||
///
|
||||
/// This comes from [findJavaHome] if that method returns non-null;
|
||||
/// otherwise, it gets from searching PATH.
|
||||
// TODO(andrewkolos): To prevent confusion when debugging Android-related
|
||||
// issues (see https://github.com/flutter/flutter/issues/122609 for an example),
|
||||
// this logic should be consistently followed by any Java-dependent operation
|
||||
@ -479,17 +513,15 @@ class AndroidSdk {
|
||||
required OperatingSystemUtils operatingSystemUtils,
|
||||
required Platform platform,
|
||||
}) {
|
||||
if (androidStudio?.javaPath != null) {
|
||||
globals.printTrace("Using Android Studio's java.");
|
||||
return fileSystem.path.join(androidStudio!.javaPath!, 'bin', 'java');
|
||||
}
|
||||
final String? javaHome = findJavaHome(
|
||||
androidStudio: androidStudio,
|
||||
fileSystem: fileSystem,
|
||||
operatingSystemUtils: operatingSystemUtils,
|
||||
platform: platform,
|
||||
);
|
||||
|
||||
final String? javaHomeEnv =
|
||||
platform.environment[_javaHomeEnvironmentVariable];
|
||||
if (javaHomeEnv != null) {
|
||||
// Trust JAVA_HOME.
|
||||
globals.printTrace('Using JAVA_HOME.');
|
||||
return fileSystem.path.join(javaHomeEnv, 'bin', 'java');
|
||||
if (javaHome != null) {
|
||||
return fileSystem.path.join(javaHome, 'bin', 'java');
|
||||
}
|
||||
|
||||
// Fallback to PATH based lookup.
|
||||
@ -528,8 +560,8 @@ class AndroidSdk {
|
||||
);
|
||||
if (javaBinary != null && globals.platform.environment['PATH'] != null) {
|
||||
_sdkManagerEnv!['PATH'] = globals.fs.path.dirname(javaBinary) +
|
||||
globals.os.pathVarSeparator +
|
||||
globals.platform.environment['PATH']!;
|
||||
globals.os.pathVarSeparator +
|
||||
globals.platform.environment['PATH']!;
|
||||
}
|
||||
}
|
||||
return _sdkManagerEnv!;
|
||||
|
@ -26,15 +26,28 @@ import '../build_info.dart';
|
||||
import '../cache.dart';
|
||||
import '../convert.dart';
|
||||
import '../flutter_manifest.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../project.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
import 'android_builder.dart';
|
||||
import 'android_studio.dart';
|
||||
import 'android_sdk.dart';
|
||||
import 'gradle_errors.dart';
|
||||
import 'gradle_utils.dart';
|
||||
import 'migrations/top_level_gradle_build_file_migration.dart';
|
||||
import 'multidex.dart';
|
||||
|
||||
/// The regex to grab variant names from printVariants gradle task
|
||||
///
|
||||
/// The task is defined in flutter/packages/flutter_tools/gradle/flutter.gradle.
|
||||
///
|
||||
/// The expected output from the task should be similar to:
|
||||
///
|
||||
/// BuildVariant: debug
|
||||
/// BuildVariant: release
|
||||
/// BuildVariant: profile
|
||||
final RegExp _kBuildVariantRegex = RegExp('^BuildVariant: (?<$_kBuildVariantRegexGroupName>.*)\$');
|
||||
const String _kBuildVariantRegexGroupName = 'variant';
|
||||
|
||||
/// The directory where the APK artifact is generated.
|
||||
Directory getApkDirectory(FlutterProject project) {
|
||||
return project.isModule
|
||||
@ -397,13 +410,14 @@ class AndroidGradleBuilder implements AndroidBuilder {
|
||||
..start();
|
||||
int exitCode = 1;
|
||||
try {
|
||||
final String? javaHome = globals.androidSdk?.javaHome;
|
||||
exitCode = await _processUtils.stream(
|
||||
command,
|
||||
workingDirectory: project.android.hostAppGradleRoot.path,
|
||||
allowReentrantFlutter: true,
|
||||
environment: <String, String>{
|
||||
if (javaPath != null)
|
||||
'JAVA_HOME': javaPath!,
|
||||
if (javaHome != null)
|
||||
AndroidSdk.javaHomeEnvironmentVariable: javaHome,
|
||||
},
|
||||
mapFunction: consumeLog,
|
||||
);
|
||||
@ -666,13 +680,14 @@ class AndroidGradleBuilder implements AndroidBuilder {
|
||||
..start();
|
||||
RunResult result;
|
||||
try {
|
||||
final String? javaHome = globals.androidSdk?.javaHome;
|
||||
result = await _processUtils.run(
|
||||
command,
|
||||
workingDirectory: project.android.hostAppGradleRoot.path,
|
||||
allowReentrantFlutter: true,
|
||||
environment: <String, String>{
|
||||
if (javaPath != null)
|
||||
'JAVA_HOME': javaPath!,
|
||||
if (javaHome != null)
|
||||
AndroidSdk.javaHomeEnvironmentVariable: javaHome,
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
@ -702,6 +717,51 @@ class AndroidGradleBuilder implements AndroidBuilder {
|
||||
color: TerminalColor.green,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<String>> getBuildVariants({required FlutterProject project}) async {
|
||||
final Status status = _logger.startProgress(
|
||||
"Running Gradle task 'printBuildVariants'...",
|
||||
);
|
||||
final List<String> command = <String>[
|
||||
_gradleUtils.getExecutable(project),
|
||||
'-q', // suppresses gradle output.
|
||||
'printBuildVariants',
|
||||
];
|
||||
|
||||
final Stopwatch sw = Stopwatch()
|
||||
..start();
|
||||
RunResult result;
|
||||
try {
|
||||
final String? javaHome = globals.androidSdk?.javaHome;
|
||||
result = await _processUtils.run(
|
||||
command,
|
||||
workingDirectory: project.android.hostAppGradleRoot.path,
|
||||
allowReentrantFlutter: true,
|
||||
environment: <String, String>{
|
||||
if (javaHome != null)
|
||||
AndroidSdk.javaHomeEnvironmentVariable: javaHome,
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
status.stop();
|
||||
}
|
||||
_usage.sendTiming('print', 'android build variants', sw.elapsed);
|
||||
|
||||
if (result.exitCode != 0) {
|
||||
_logger.printStatus(result.stdout, wrap: false);
|
||||
_logger.printError(result.stderr, wrap: false);
|
||||
return const <String>[];
|
||||
}
|
||||
final List<String> options = <String>[];
|
||||
for (final String line in LineSplitter.split(result.stdout)) {
|
||||
final RegExpMatch? match = _kBuildVariantRegex.firstMatch(line);
|
||||
if (match != null) {
|
||||
options.add(match.namedGroup(_kBuildVariantRegexGroupName)!);
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints how to consume the AAR from a host app.
|
||||
|
@ -11,6 +11,7 @@ import '../base/terminal.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../project.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
import 'android_sdk.dart';
|
||||
import 'android_studio.dart';
|
||||
import 'gradle_utils.dart';
|
||||
import 'multidex.dart';
|
||||
@ -379,7 +380,7 @@ final GradleHandledError flavorUndefinedHandler = GradleHandledError(
|
||||
workingDirectory: project.android.hostAppGradleRoot.path,
|
||||
environment: <String, String>{
|
||||
if (javaPath != null)
|
||||
'JAVA_HOME': javaPath!,
|
||||
AndroidSdk.javaHomeEnvironmentVariable: javaPath!,
|
||||
},
|
||||
);
|
||||
// Extract build types and product flavors.
|
||||
|
@ -7,6 +7,7 @@ import 'dart:async';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:package_config/package_config.dart';
|
||||
|
||||
import 'android/android_sdk.dart';
|
||||
import 'android/android_studio.dart';
|
||||
import 'base/common.dart';
|
||||
import 'base/error_handling_io.dart';
|
||||
@ -425,7 +426,7 @@ class AndroidMavenArtifacts extends ArtifactSet {
|
||||
],
|
||||
environment: <String, String>{
|
||||
if (javaPath != null)
|
||||
'JAVA_HOME': javaPath!,
|
||||
AndroidSdk.javaHomeEnvironmentVariable: javaPath!,
|
||||
},
|
||||
);
|
||||
if (processResult.exitCode != 0) {
|
||||
|
@ -7,6 +7,7 @@ import 'package:xml/xml.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
import '../src/convert.dart';
|
||||
import 'android/android_builder.dart';
|
||||
import 'android/gradle_utils.dart' as gradle;
|
||||
import 'base/common.dart';
|
||||
import 'base/error_handling_io.dart';
|
||||
@ -474,6 +475,13 @@ class AndroidProject extends FlutterProjectPlatform {
|
||||
/// Returns true if the current version of the Gradle plugin is supported.
|
||||
late final bool isSupportedVersion = _computeSupportedVersion();
|
||||
|
||||
Future<List<String>> getBuildVariants() async {
|
||||
if (!existsSync() || androidBuilder == null) {
|
||||
return const <String>[];
|
||||
}
|
||||
return androidBuilder!.getBuildVariants(project: parent);
|
||||
}
|
||||
|
||||
bool _computeSupportedVersion() {
|
||||
final FileSystem fileSystem = hostAppGradleRoot.fileSystem;
|
||||
final File plugin = hostAppGradleRoot.childFile(
|
||||
|
@ -31,6 +31,17 @@ const String kScreenshotMethod = '_flutter.screenshot';
|
||||
const String kRenderFrameWithRasterStatsMethod = '_flutter.renderFrameWithRasterStats';
|
||||
const String kReloadAssetFonts = '_flutter.reloadAssetFonts';
|
||||
|
||||
const String kFlutterToolAlias = 'Flutter Tools';
|
||||
|
||||
const String kReloadSourcesServiceName = 'reloadSources';
|
||||
const String kHotRestartServiceName = 'hotRestart';
|
||||
const String kFlutterVersionServiceName = 'flutterVersion';
|
||||
const String kCompileExpressionServiceName = 'compileExpression';
|
||||
const String kFlutterMemoryInfoServiceName = 'flutterMemoryInfo';
|
||||
const String kFlutterGetSkSLServiceName = 'flutterGetSkSL';
|
||||
const String kFlutterGetIOSBuildOptionsServiceName = 'flutterGetIOSBuildOptions';
|
||||
const String kFlutterGetAndroidBuildVariantsServiceName = 'flutterGetAndroidBuildVariants';
|
||||
|
||||
/// The error response code from an unrecoverable compilation failure.
|
||||
const int kIsolateReloadBarred = 1005;
|
||||
|
||||
@ -196,7 +207,7 @@ Future<vm_service.VmService> setUpVmService({
|
||||
// all at the end of this method.
|
||||
final List<Future<vm_service.Success?>> registrationRequests = <Future<vm_service.Success?>>[];
|
||||
if (reloadSources != null) {
|
||||
vmService.registerServiceCallback('reloadSources', (Map<String, Object?> params) async {
|
||||
vmService.registerServiceCallback(kReloadSourcesServiceName, (Map<String, Object?> params) async {
|
||||
final String isolateId = _validateRpcStringParam('reloadSources', params, 'isolateId');
|
||||
final bool force = _validateRpcBoolParam('reloadSources', params, 'force');
|
||||
final bool pause = _validateRpcBoolParam('reloadSources', params, 'pause');
|
||||
@ -209,11 +220,11 @@ Future<vm_service.VmService> setUpVmService({
|
||||
},
|
||||
};
|
||||
});
|
||||
registrationRequests.add(vmService.registerService('reloadSources', 'Flutter Tools'));
|
||||
registrationRequests.add(vmService.registerService(kReloadSourcesServiceName, kFlutterToolAlias));
|
||||
}
|
||||
|
||||
if (restart != null) {
|
||||
vmService.registerServiceCallback('hotRestart', (Map<String, Object?> params) async {
|
||||
vmService.registerServiceCallback(kHotRestartServiceName, (Map<String, Object?> params) async {
|
||||
final bool pause = _validateRpcBoolParam('compileExpression', params, 'pause');
|
||||
await restart(pause: pause);
|
||||
return <String, Object>{
|
||||
@ -222,10 +233,10 @@ Future<vm_service.VmService> setUpVmService({
|
||||
},
|
||||
};
|
||||
});
|
||||
registrationRequests.add(vmService.registerService('hotRestart', 'Flutter Tools'));
|
||||
registrationRequests.add(vmService.registerService(kHotRestartServiceName, kFlutterToolAlias));
|
||||
}
|
||||
|
||||
vmService.registerServiceCallback('flutterVersion', (Map<String, Object?> params) async {
|
||||
vmService.registerServiceCallback(kFlutterVersionServiceName, (Map<String, Object?> params) async {
|
||||
final FlutterVersion version = context.get<FlutterVersion>() ?? FlutterVersion();
|
||||
final Map<String, Object> versionJson = version.toJson();
|
||||
versionJson['frameworkRevisionShort'] = version.frameworkRevisionShort;
|
||||
@ -237,10 +248,10 @@ Future<vm_service.VmService> setUpVmService({
|
||||
},
|
||||
};
|
||||
});
|
||||
registrationRequests.add(vmService.registerService('flutterVersion', 'Flutter Tools'));
|
||||
registrationRequests.add(vmService.registerService(kFlutterVersionServiceName, kFlutterToolAlias));
|
||||
|
||||
if (compileExpression != null) {
|
||||
vmService.registerServiceCallback('compileExpression', (Map<String, Object?> params) async {
|
||||
vmService.registerServiceCallback(kCompileExpressionServiceName, (Map<String, Object?> params) async {
|
||||
final String isolateId = _validateRpcStringParam('compileExpression', params, 'isolateId');
|
||||
final String expression = _validateRpcStringParam('compileExpression', params, 'expression');
|
||||
final List<String> definitions = List<String>.from(params['definitions']! as List<Object?>);
|
||||
@ -257,10 +268,10 @@ Future<vm_service.VmService> setUpVmService({
|
||||
'result': <String, String>{'kernelBytes': kernelBytesBase64},
|
||||
};
|
||||
});
|
||||
registrationRequests.add(vmService.registerService('compileExpression', 'Flutter Tools'));
|
||||
registrationRequests.add(vmService.registerService(kCompileExpressionServiceName, kFlutterToolAlias));
|
||||
}
|
||||
if (device != null) {
|
||||
vmService.registerServiceCallback('flutterMemoryInfo', (Map<String, Object?> params) async {
|
||||
vmService.registerServiceCallback(kFlutterMemoryInfoServiceName, (Map<String, Object?> params) async {
|
||||
final MemoryInfo result = await device.queryMemoryInfo();
|
||||
return <String, Object>{
|
||||
'result': <String, Object>{
|
||||
@ -269,10 +280,10 @@ Future<vm_service.VmService> setUpVmService({
|
||||
},
|
||||
};
|
||||
});
|
||||
registrationRequests.add(vmService.registerService('flutterMemoryInfo', 'Flutter Tools'));
|
||||
registrationRequests.add(vmService.registerService(kFlutterMemoryInfoServiceName, kFlutterToolAlias));
|
||||
}
|
||||
if (skSLMethod != null) {
|
||||
vmService.registerServiceCallback('flutterGetSkSL', (Map<String, Object?> params) async {
|
||||
vmService.registerServiceCallback(kFlutterGetSkSLServiceName, (Map<String, Object?> params) async {
|
||||
final String? filename = await skSLMethod();
|
||||
if (filename == null) {
|
||||
return <String, Object>{
|
||||
@ -288,11 +299,11 @@ Future<vm_service.VmService> setUpVmService({
|
||||
},
|
||||
};
|
||||
});
|
||||
registrationRequests.add(vmService.registerService('flutterGetSkSL', 'Flutter Tools'));
|
||||
registrationRequests.add(vmService.registerService(kFlutterGetSkSLServiceName, kFlutterToolAlias));
|
||||
}
|
||||
|
||||
if (flutterProject != null) {
|
||||
vmService.registerServiceCallback('flutterGetIOSBuildOptions', (Map<String, Object?> params) async {
|
||||
vmService.registerServiceCallback(kFlutterGetIOSBuildOptionsServiceName, (Map<String, Object?> params) async {
|
||||
final XcodeProjectInfo? info = await flutterProject.ios.projectInfo();
|
||||
if (info == null) {
|
||||
return <String, Object>{
|
||||
@ -311,7 +322,20 @@ Future<vm_service.VmService> setUpVmService({
|
||||
};
|
||||
});
|
||||
registrationRequests.add(
|
||||
vmService.registerService('flutterGetIOSBuildOptions', 'Flutter Tools'),
|
||||
vmService.registerService(kFlutterGetIOSBuildOptionsServiceName, kFlutterToolAlias),
|
||||
);
|
||||
|
||||
vmService.registerServiceCallback(kFlutterGetAndroidBuildVariantsServiceName, (Map<String, Object?> params) async {
|
||||
final List<String> options = await flutterProject.android.getBuildVariants();
|
||||
return <String, Object>{
|
||||
'result': <String, Object>{
|
||||
kResultType: kResultTypeSuccess,
|
||||
'variants': options,
|
||||
},
|
||||
};
|
||||
});
|
||||
registrationRequests.add(
|
||||
vmService.registerService(kFlutterGetAndroidBuildVariantsServiceName, kFlutterToolAlias),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -446,6 +446,9 @@ class FakeAndroidSdk extends Fake implements AndroidSdk {
|
||||
|
||||
@override
|
||||
final Directory directory;
|
||||
|
||||
@override
|
||||
String? get javaHome => 'java';
|
||||
}
|
||||
|
||||
class FakeAndroidStudio extends Fake implements AndroidStudio {
|
||||
|
@ -230,4 +230,7 @@ class FakeAndroidSdk extends Fake implements AndroidSdk {
|
||||
|
||||
@override
|
||||
final Directory directory;
|
||||
|
||||
@override
|
||||
String? get javaHome => 'java';
|
||||
}
|
||||
|
@ -787,6 +787,68 @@ android {
|
||||
expect(androidApk?.id, 'com.example.foo');
|
||||
});
|
||||
|
||||
testUsingContext('can call custom gradle task getBuildOptions and parse the result', () async {
|
||||
final AndroidGradleBuilder builder = AndroidGradleBuilder(
|
||||
logger: logger,
|
||||
processManager: processManager,
|
||||
fileSystem: fileSystem,
|
||||
artifacts: Artifacts.test(),
|
||||
usage: testUsage,
|
||||
gradleUtils: FakeGradleUtils(),
|
||||
platform: FakePlatform(),
|
||||
);
|
||||
processManager.addCommand(const FakeCommand(
|
||||
command: <String>[
|
||||
'gradlew',
|
||||
'-q',
|
||||
'printBuildVariants',
|
||||
],
|
||||
stdout: '''
|
||||
BuildVariant: freeDebug
|
||||
BuildVariant: paidDebug
|
||||
BuildVariant: freeRelease
|
||||
BuildVariant: paidRelease
|
||||
BuildVariant: freeProfile
|
||||
BuildVariant: paidProfile
|
||||
''',
|
||||
));
|
||||
final List<String> actual = await builder.getBuildVariants(
|
||||
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
|
||||
);
|
||||
expect(actual, <String>['freeDebug', 'paidDebug', 'freeRelease', 'paidRelease', 'freeProfile', 'paidProfile']);
|
||||
}, overrides: <Type, Generator>{
|
||||
AndroidStudio: () => FakeAndroidStudio(),
|
||||
});
|
||||
|
||||
testUsingContext('getBuildOptions returns empty list if gradle returns error', () async {
|
||||
final AndroidGradleBuilder builder = AndroidGradleBuilder(
|
||||
logger: logger,
|
||||
processManager: processManager,
|
||||
fileSystem: fileSystem,
|
||||
artifacts: Artifacts.test(),
|
||||
usage: testUsage,
|
||||
gradleUtils: FakeGradleUtils(),
|
||||
platform: FakePlatform(),
|
||||
);
|
||||
processManager.addCommand(const FakeCommand(
|
||||
command: <String>[
|
||||
'gradlew',
|
||||
'-q',
|
||||
'printBuildVariants',
|
||||
],
|
||||
stderr: '''
|
||||
Gradle Crashed
|
||||
''',
|
||||
exitCode: 1,
|
||||
));
|
||||
final List<String> actual = await builder.getBuildVariants(
|
||||
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
|
||||
);
|
||||
expect(actual, const <String>[]);
|
||||
}, overrides: <Type, Generator>{
|
||||
AndroidStudio: () => FakeAndroidStudio(),
|
||||
});
|
||||
|
||||
testUsingContext("doesn't indicate how to consume an AAR when printHowToConsumeAar is false", () async {
|
||||
final AndroidGradleBuilder builder = AndroidGradleBuilder(
|
||||
logger: logger,
|
||||
|
@ -387,7 +387,7 @@ void main() {
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
OperatingSystemUtils: () => FakeOperatingSystemUtilsWithJava(),
|
||||
Platform: () => FakePlatform(environment: <String, String>{
|
||||
'JAVA_HOME': 'java-home-path',
|
||||
AndroidSdk.javaHomeEnvironmentVariable: 'java-home-path',
|
||||
}),
|
||||
Config: () => Config,
|
||||
AndroidStudio: () => FakeAndroidStudioWithoutJdk(),
|
||||
|
@ -553,7 +553,7 @@ Review licenses that have not been accepted (y/N)?
|
||||
androidStudio: null,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me', 'JAVA_HOME': 'home/java'},
|
||||
platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me', AndroidSdk.javaHomeEnvironmentVariable: 'home/java'},
|
||||
processManager: processManager,
|
||||
userMessages: UserMessages(),
|
||||
).validate();
|
||||
@ -576,7 +576,7 @@ Review licenses that have not been accepted (y/N)?
|
||||
androidStudio: null,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me', 'JAVA_HOME': 'home/java'},
|
||||
platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me', AndroidSdk.javaHomeEnvironmentVariable: 'home/java'},
|
||||
processManager: processManager,
|
||||
userMessages: UserMessages(),
|
||||
).validate();
|
||||
|
@ -589,7 +589,7 @@ void main() {
|
||||
})!.toJson(),
|
||||
),
|
||||
const FakeVmServiceRequest(
|
||||
method: 'reloadSources',
|
||||
method: kReloadSourcesServiceName,
|
||||
args: <String, Object>{
|
||||
'isolateId': '1',
|
||||
'pause': false,
|
||||
@ -649,7 +649,7 @@ void main() {
|
||||
})!.toJson(),
|
||||
),
|
||||
const FakeVmServiceRequest(
|
||||
method: 'reloadSources',
|
||||
method: kReloadSourcesServiceName,
|
||||
args: <String, Object>{
|
||||
'isolateId': '1',
|
||||
'pause': false,
|
||||
@ -713,7 +713,7 @@ void main() {
|
||||
})!.toJson(),
|
||||
),
|
||||
const FakeVmServiceRequest(
|
||||
method: 'reloadSources',
|
||||
method: kReloadSourcesServiceName,
|
||||
args: <String, Object>{
|
||||
'isolateId': '1',
|
||||
'pause': false,
|
||||
@ -776,7 +776,7 @@ void main() {
|
||||
jsonResponse: fakeVM.toJson(),
|
||||
),
|
||||
const FakeVmServiceRequest(
|
||||
method: 'reloadSources',
|
||||
method: kReloadSourcesServiceName,
|
||||
args: <String, Object>{
|
||||
'isolateId': '1',
|
||||
'pause': false,
|
||||
@ -866,7 +866,7 @@ void main() {
|
||||
jsonResponse: fakeVM.toJson(),
|
||||
),
|
||||
const FakeVmServiceRequest(
|
||||
method: 'reloadSources',
|
||||
method: kReloadSourcesServiceName,
|
||||
args: <String, Object>{
|
||||
'isolateId': '1',
|
||||
'pause': false,
|
||||
|
@ -63,20 +63,24 @@ const List<VmServiceExpectation> kAttachIsolateExpectations =
|
||||
'streamId': 'Isolate',
|
||||
}),
|
||||
FakeVmServiceRequest(method: 'registerService', args: <String, Object>{
|
||||
'service': 'reloadSources',
|
||||
'alias': 'Flutter Tools',
|
||||
'service': kReloadSourcesServiceName,
|
||||
'alias': kFlutterToolAlias,
|
||||
}),
|
||||
FakeVmServiceRequest(method: 'registerService', args: <String, Object>{
|
||||
'service': 'flutterVersion',
|
||||
'alias': 'Flutter Tools',
|
||||
'service': kFlutterVersionServiceName,
|
||||
'alias': kFlutterToolAlias,
|
||||
}),
|
||||
FakeVmServiceRequest(method: 'registerService', args: <String, Object>{
|
||||
'service': 'flutterMemoryInfo',
|
||||
'alias': 'Flutter Tools',
|
||||
'service': kFlutterMemoryInfoServiceName,
|
||||
'alias': kFlutterToolAlias,
|
||||
}),
|
||||
FakeVmServiceRequest(method: 'registerService', args: <String, Object>{
|
||||
'service': 'flutterGetIOSBuildOptions',
|
||||
'alias': 'Flutter Tools',
|
||||
'service': kFlutterGetIOSBuildOptionsServiceName,
|
||||
'alias': kFlutterToolAlias,
|
||||
}),
|
||||
FakeVmServiceRequest(method: 'registerService', args: <String, Object>{
|
||||
'service': kFlutterGetAndroidBuildVariantsServiceName,
|
||||
'alias': kFlutterToolAlias,
|
||||
}),
|
||||
FakeVmServiceRequest(
|
||||
method: 'streamListen',
|
||||
@ -611,7 +615,7 @@ void main() {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
...kAttachExpectations,
|
||||
const FakeVmServiceRequest(
|
||||
method: 'hotRestart',
|
||||
method: kHotRestartServiceName,
|
||||
jsonResponse: <String, Object>{
|
||||
'type': 'Success',
|
||||
}),
|
||||
@ -684,7 +688,7 @@ void main() {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
...kAttachExpectations,
|
||||
const FakeVmServiceRequest(
|
||||
method: 'hotRestart',
|
||||
method: kHotRestartServiceName,
|
||||
jsonResponse: <String, Object>{
|
||||
'type': 'Success',
|
||||
}),
|
||||
@ -883,7 +887,7 @@ void main() {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
...kAttachExpectations,
|
||||
const FakeVmServiceRequest(
|
||||
method: 'hotRestart',
|
||||
method: kHotRestartServiceName,
|
||||
jsonResponse: <String, Object>{
|
||||
'type': 'Failed',
|
||||
},
|
||||
@ -910,7 +914,7 @@ void main() {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
...kAttachExpectations,
|
||||
const FakeVmServiceRequest(
|
||||
method: 'hotRestart',
|
||||
method: kHotRestartServiceName,
|
||||
// Failed response,
|
||||
errorCode: RPCErrorCodes.kInternalError,
|
||||
),
|
||||
|
@ -70,7 +70,7 @@ void main() {
|
||||
vmService: mockVMService,
|
||||
);
|
||||
|
||||
expect(mockVMService.services, containsPair('reloadSources', 'Flutter Tools'));
|
||||
expect(mockVMService.services, containsPair(kReloadSourcesServiceName, kFlutterToolAlias));
|
||||
});
|
||||
|
||||
testWithoutContext('VM Service registers flutterMemoryInfo service', () async {
|
||||
@ -82,20 +82,29 @@ void main() {
|
||||
vmService: mockVMService,
|
||||
);
|
||||
|
||||
expect(mockVMService.services, containsPair('flutterMemoryInfo', 'Flutter Tools'));
|
||||
expect(mockVMService.services, containsPair(kFlutterMemoryInfoServiceName, kFlutterToolAlias));
|
||||
});
|
||||
|
||||
testWithoutContext('VmService registers flutterGetIOSBuildOptions service', () async {
|
||||
final MockVMService mockVMService = MockVMService();
|
||||
final FlutterProject mockedVMService = MockFlutterProject(
|
||||
mockedIos: MockIosProject(),
|
||||
);
|
||||
final FlutterProject mockedFlutterProject = MockFlutterProject();
|
||||
await setUpVmService(
|
||||
flutterProject: mockedVMService,
|
||||
flutterProject: mockedFlutterProject,
|
||||
vmService: mockVMService,
|
||||
);
|
||||
|
||||
expect(mockVMService.services, containsPair('flutterGetIOSBuildOptions', 'Flutter Tools'));
|
||||
expect(mockVMService.services, containsPair(kFlutterGetIOSBuildOptionsServiceName, kFlutterToolAlias));
|
||||
});
|
||||
|
||||
testWithoutContext('VmService registers flutterGetAndroidBuildVariants service', () async {
|
||||
final MockVMService mockVMService = MockVMService();
|
||||
final FlutterProject mockedFlutterProject = MockFlutterProject();
|
||||
await setUpVmService(
|
||||
flutterProject: mockedFlutterProject,
|
||||
vmService: mockVMService,
|
||||
);
|
||||
|
||||
expect(mockVMService.services, containsPair(kFlutterGetAndroidBuildVariantsServiceName, kFlutterToolAlias));
|
||||
});
|
||||
|
||||
testWithoutContext('VM Service registers flutterGetSkSL service', () async {
|
||||
@ -105,7 +114,7 @@ void main() {
|
||||
vmService: mockVMService,
|
||||
);
|
||||
|
||||
expect(mockVMService.services, containsPair('flutterGetSkSL', 'Flutter Tools'));
|
||||
expect(mockVMService.services, containsPair(kFlutterGetSkSLServiceName, kFlutterToolAlias));
|
||||
});
|
||||
|
||||
testWithoutContext('VM Service throws tool exit on service registration failure.', () async {
|
||||
@ -144,7 +153,7 @@ void main() {
|
||||
vmService: mockVMService,
|
||||
);
|
||||
|
||||
expect(mockVMService.services, containsPair('flutterVersion', 'Flutter Tools'));
|
||||
expect(mockVMService.services, containsPair(kFlutterVersionServiceName, kFlutterToolAlias));
|
||||
});
|
||||
|
||||
testUsingContext('VM Service prints messages for connection failures', () {
|
||||
@ -287,14 +296,14 @@ void main() {
|
||||
<String>['scheme1', 'scheme2'],
|
||||
MockLogger(),
|
||||
);
|
||||
final FlutterProject mockedVMService = MockFlutterProject(
|
||||
final FlutterProject mockedFlutterProject = MockFlutterProject(
|
||||
mockedIos: MockIosProject(mockedInfo: expectedProjectInfo),
|
||||
);
|
||||
await setUpVmService(
|
||||
flutterProject: mockedVMService,
|
||||
flutterProject: mockedFlutterProject,
|
||||
vmService: vmService
|
||||
);
|
||||
final vm_service.ServiceCallback cb = vmService.serviceCallBacks['flutterGetIOSBuildOptions']!;
|
||||
final vm_service.ServiceCallback cb = vmService.serviceCallBacks[kFlutterGetIOSBuildOptionsServiceName]!;
|
||||
|
||||
final Map<String, dynamic> response = await cb(<String, dynamic>{});
|
||||
final Map<String, dynamic> result = response['result']! as Map<String, dynamic>;
|
||||
@ -304,16 +313,34 @@ void main() {
|
||||
expect(result['schemes'], expectedProjectInfo.schemes);
|
||||
});
|
||||
|
||||
testWithoutContext('VmService forward flutterGetAndroidBuildVariants request and response correctly', () async {
|
||||
final MockVMService vmService = MockVMService();
|
||||
final List<String> expectedOptions = <String>['debug', 'release', 'profile'];
|
||||
final FlutterProject mockedFlutterProject = MockFlutterProject(
|
||||
mockedAndroid: MockAndroidProject(mockedOptions: expectedOptions),
|
||||
);
|
||||
await setUpVmService(
|
||||
flutterProject: mockedFlutterProject,
|
||||
vmService: vmService
|
||||
);
|
||||
final vm_service.ServiceCallback cb = vmService.serviceCallBacks[kFlutterGetAndroidBuildVariantsServiceName]!;
|
||||
|
||||
final Map<String, dynamic> response = await cb(<String, dynamic>{});
|
||||
final Map<String, dynamic> result = response['result']! as Map<String, dynamic>;
|
||||
expect(result[kResultType], kResultTypeSuccess);
|
||||
expect(result['variants'], expectedOptions);
|
||||
});
|
||||
|
||||
testWithoutContext('VmService forward flutterGetIOSBuildOptions request and response correctly when no iOS project', () async {
|
||||
final MockVMService vmService = MockVMService();
|
||||
final FlutterProject mockedVMService = MockFlutterProject(
|
||||
final FlutterProject mockedFlutterProject = MockFlutterProject(
|
||||
mockedIos: MockIosProject(),
|
||||
);
|
||||
await setUpVmService(
|
||||
flutterProject: mockedVMService,
|
||||
flutterProject: mockedFlutterProject,
|
||||
vmService: vmService
|
||||
);
|
||||
final vm_service.ServiceCallback cb = vmService.serviceCallBacks['flutterGetIOSBuildOptions']!;
|
||||
final vm_service.ServiceCallback cb = vmService.serviceCallBacks[kFlutterGetIOSBuildOptionsServiceName]!;
|
||||
|
||||
final Map<String, dynamic> response = await cb(<String, dynamic>{});
|
||||
final Map<String, dynamic> result = response['result']! as Map<String, dynamic>;
|
||||
@ -912,11 +939,16 @@ void main() {
|
||||
|
||||
class MockFlutterProject extends Fake implements FlutterProject {
|
||||
MockFlutterProject({
|
||||
required IosProject mockedIos
|
||||
}) : ios = mockedIos;
|
||||
IosProject? mockedIos,
|
||||
AndroidProject? mockedAndroid,
|
||||
}) : ios = mockedIos ?? MockIosProject(),
|
||||
android = mockedAndroid ?? MockAndroidProject();
|
||||
|
||||
@override
|
||||
final IosProject ios;
|
||||
|
||||
@override
|
||||
final AndroidProject android;
|
||||
}
|
||||
|
||||
class MockIosProject extends Fake implements IosProject {
|
||||
@ -928,6 +960,15 @@ class MockIosProject extends Fake implements IosProject {
|
||||
Future<XcodeProjectInfo?> projectInfo() async => mockedInfo;
|
||||
}
|
||||
|
||||
class MockAndroidProject extends Fake implements AndroidProject {
|
||||
MockAndroidProject({this.mockedOptions = const <String>[]});
|
||||
|
||||
final List<String> mockedOptions;
|
||||
|
||||
@override
|
||||
Future<List<String>> getBuildVariants() async => mockedOptions;
|
||||
}
|
||||
|
||||
class MockLogger extends Fake implements Logger { }
|
||||
|
||||
class MockVMService extends Fake implements vm_service.VmService {
|
||||
|
@ -0,0 +1,66 @@
|
||||
// Copyright 2014 The Flutter 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:convert';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:file/file.dart';
|
||||
import 'package:flutter_tools/src/android/gradle_utils.dart'
|
||||
show getGradlewFileName;
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
|
||||
import '../src/common.dart';
|
||||
import 'test_utils.dart';
|
||||
|
||||
void main() {
|
||||
late Directory tempDir;
|
||||
|
||||
setUp(() async {
|
||||
tempDir = createResolvedTempDirectorySync('run_test.');
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
tryToDelete(tempDir);
|
||||
});
|
||||
|
||||
testWithoutContext(
|
||||
'gradle task exists named printBuildVariants that prints build variants', () async {
|
||||
// Create a new flutter project.
|
||||
final String flutterBin =
|
||||
fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
|
||||
ProcessResult result = await processManager.run(<String>[
|
||||
flutterBin,
|
||||
'create',
|
||||
tempDir.path,
|
||||
'--project-name=testapp',
|
||||
], workingDirectory: tempDir.path);
|
||||
expect(result.exitCode, 0);
|
||||
// Ensure that gradle files exists from templates.
|
||||
result = await processManager.run(<String>[
|
||||
flutterBin,
|
||||
'build',
|
||||
'apk',
|
||||
'--config-only',
|
||||
], workingDirectory: tempDir.path);
|
||||
expect(result.exitCode, 0);
|
||||
|
||||
final Directory androidApp = tempDir.childDirectory('android');
|
||||
result = await processManager.run(<String>[
|
||||
'.${platform.pathSeparator}${getGradlewFileName(platform)}',
|
||||
...getLocalEngineArguments(),
|
||||
'-q', // quiet output.
|
||||
'printBuildVariants',
|
||||
], workingDirectory: androidApp.path);
|
||||
// Verify that gradlew has a javaVersion task.
|
||||
expect(result.exitCode, 0);
|
||||
// Verify the format is a number on its own line.
|
||||
const List<String> expectedLines = <String>[
|
||||
'BuildVariant: debug',
|
||||
'BuildVariant: release',
|
||||
'BuildVariant: profile',
|
||||
];
|
||||
final List<String> actualLines = LineSplitter.split(result.stdout.toString()).toList();
|
||||
expect(const ListEquality<String>().equals(actualLines, expectedLines), isTrue);
|
||||
});
|
||||
}
|
@ -36,6 +36,9 @@ class FakeAndroidBuilder implements AndroidBuilder {
|
||||
bool deferredComponentsEnabled = false,
|
||||
bool configOnly = false,
|
||||
}) async {}
|
||||
|
||||
@override
|
||||
Future<List<String>> getBuildVariants({required FlutterProject project}) async => const <String>[];
|
||||
}
|
||||
|
||||
/// Creates a [FlutterProject] in a directory named [flutter_project]
|
||||
|
@ -6,6 +6,7 @@ import 'dart:async';
|
||||
|
||||
import 'package:file/file.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/vmservice.dart';
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
import 'package:vm_service/vm_service_io.dart';
|
||||
|
||||
@ -96,7 +97,7 @@ Future<void> validateFlutterVersion(VmService client) async {
|
||||
client.onEvent('Service'),
|
||||
emitsThrough(predicate((Event e) {
|
||||
if (e.kind == EventKind.kServiceRegistered &&
|
||||
e.service == 'flutterVersion') {
|
||||
e.service == kFlutterVersionServiceName) {
|
||||
method = e.method;
|
||||
return true;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user