Unverified Commit e5459942 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Start abstracting platform logic builds behind a shared interface (#31889)

parent caebdaf1
...@@ -8,7 +8,6 @@ import 'package:meta/meta.dart'; ...@@ -8,7 +8,6 @@ import 'package:meta/meta.dart';
import '../android/android_sdk.dart'; import '../android/android_sdk.dart';
import '../android/android_workflow.dart'; import '../android/android_workflow.dart';
import '../android/apk.dart';
import '../application_package.dart'; import '../application_package.dart';
import '../base/common.dart' show throwToolExit; import '../base/common.dart' show throwToolExit;
import '../base/file_system.dart'; import '../base/file_system.dart';
...@@ -20,6 +19,7 @@ import '../build_info.dart'; ...@@ -20,6 +19,7 @@ import '../build_info.dart';
import '../convert.dart'; import '../convert.dart';
import '../device.dart'; import '../device.dart';
import '../globals.dart'; import '../globals.dart';
import '../platform_step.dart';
import '../project.dart'; import '../project.dart';
import '../protocol_discovery.dart'; import '../protocol_discovery.dart';
...@@ -374,10 +374,11 @@ class AndroidDevice extends Device { ...@@ -374,10 +374,11 @@ class AndroidDevice extends Device {
if (!prebuiltApplication || androidSdk.licensesAvailable && androidSdk.latestVersion == null) { if (!prebuiltApplication || androidSdk.licensesAvailable && androidSdk.latestVersion == null) {
printTrace('Building APK'); printTrace('Building APK');
final FlutterProject project = FlutterProject.current(); final FlutterProject project = FlutterProject.current();
await buildApk( final PlatformBuildStep platformStep = platformBuilders.selectPlatform(buildInfo: buildInfo);
project: project, await platformStep.build(
target: mainPath, project: project,
buildInfo: buildInfo, target: mainPath,
buildInfo: buildInfo,
); );
// Package has been built, so we can get the updated application ID and // Package has been built, so we can get the updated application ID and
// activity name from the .apk. // activity name from the .apk.
......
...@@ -4,38 +4,67 @@ ...@@ -4,38 +4,67 @@
import 'dart:async'; import 'dart:async';
import 'package:meta/meta.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../cache.dart';
import '../platform_step.dart';
import '../project.dart'; import '../project.dart';
import 'android_sdk.dart'; import 'android_sdk.dart';
import 'gradle.dart'; import 'gradle.dart';
Future<void> buildApk({ /// The Android Gradle build step.
@required FlutterProject project, class AndroidPlatformBuildStep extends PlatformBuildStep {
@required String target, const AndroidPlatformBuildStep();
BuildInfo buildInfo = BuildInfo.debug,
}) async { @override
if (!project.android.isUsingGradle) { Future<void> build({
throwToolExit( FlutterProject project,
BuildInfo buildInfo,
String target,
}) async {
if (!project.android.isUsingGradle) {
throwToolExit(
'The build process for Android has changed, and the current project configuration\n' 'The build process for Android has changed, and the current project configuration\n'
'is no longer valid. Please consult\n\n' 'is no longer valid. Please consult\n\n'
' https://github.com/flutter/flutter/wiki/Upgrading-Flutter-projects-to-build-with-gradle\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.' 'for details on how to upgrade the project.'
);
}
// 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 buildGradleProject(
project: project,
buildInfo: buildInfo,
target: target,
isBuildingBundle: false,
); );
androidSdk.reinitialize();
} }
// Validate that we can find an android sdk. @override
if (androidSdk == null) Set<TargetPlatform> get targetPlatforms => const <TargetPlatform>{
throwToolExit('No Android SDK found. Try setting the ANDROID_SDK_ROOT environment variable.'); TargetPlatform.android_arm,
TargetPlatform.android_arm64,
await buildGradleProject( TargetPlatform.android_x64,
project: project, TargetPlatform.android_x86,
buildInfo: buildInfo, };
target: target,
isBuildingBundle: false, @override
); Set<BuildMode> get buildModes => const <BuildMode>{
androidSdk.reinitialize(); BuildMode.debug,
BuildMode.dynamicProfile,
BuildMode.dynamicRelease,
BuildMode.profile,
BuildMode.release,
};
@override
Set<DevelopmentArtifact> get developmentArtifacts => const <DevelopmentArtifact>{
DevelopmentArtifact.universal,
DevelopmentArtifact.android,
};
} }
...@@ -4,7 +4,8 @@ ...@@ -4,7 +4,8 @@
import 'dart:async'; import 'dart:async';
import '../android/apk.dart'; import '../build_info.dart';
import '../platform_step.dart';
import '../project.dart'; import '../project.dart';
import '../runner/flutter_command.dart' show DevelopmentArtifact, FlutterCommandResult; import '../runner/flutter_command.dart' show DevelopmentArtifact, FlutterCommandResult;
import 'build.dart'; import 'build.dart';
...@@ -47,10 +48,12 @@ class BuildApkCommand extends BuildSubCommand { ...@@ -47,10 +48,12 @@ class BuildApkCommand extends BuildSubCommand {
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
await buildApk( final BuildInfo buildInfo = getBuildInfo();
final PlatformBuildStep platformStep = platformBuilders.selectPlatform(buildInfo: buildInfo);
await platformStep.build(
project: FlutterProject.current(), project: FlutterProject.current(),
target: targetFile, target: targetFile,
buildInfo: getBuildInfo(), buildInfo: buildInfo,
); );
return null; return null;
} }
......
...@@ -36,6 +36,7 @@ import 'ios/simulators.dart'; ...@@ -36,6 +36,7 @@ import 'ios/simulators.dart';
import 'ios/xcodeproj.dart'; import 'ios/xcodeproj.dart';
import 'linux/linux_workflow.dart'; import 'linux/linux_workflow.dart';
import 'macos/macos_workflow.dart'; import 'macos/macos_workflow.dart';
import 'platform_step.dart';
import 'run_hot.dart'; import 'run_hot.dart';
import 'usage.dart'; import 'usage.dart';
import 'version.dart'; import 'version.dart';
...@@ -87,6 +88,7 @@ Future<T> runInContext<T>( ...@@ -87,6 +88,7 @@ Future<T> runInContext<T>(
Logger: () => platform.isWindows ? WindowsStdoutLogger() : StdoutLogger(), Logger: () => platform.isWindows ? WindowsStdoutLogger() : StdoutLogger(),
MacOSWorkflow: () => const MacOSWorkflow(), MacOSWorkflow: () => const MacOSWorkflow(),
OperatingSystemUtils: () => OperatingSystemUtils(), OperatingSystemUtils: () => OperatingSystemUtils(),
PlatformBuilders: () => const PlatformBuilders(),
PlistBuddy: () => const PlistBuddy(), PlistBuddy: () => const PlistBuddy(),
SimControl: () => SimControl(), SimControl: () => SimControl(),
SystemClock: () => const SystemClock(), SystemClock: () => const SystemClock(),
......
// 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 'android/apk.dart';
import 'base/common.dart';
import 'base/context.dart';
import 'build_info.dart';
import 'cache.dart';
import 'project.dart';
/// The [PlatformBuilders] instance.
PlatformBuilders get platformBuilders => context.get<PlatformBuilders>();
/// A registry that selects the correct [PlatformBuilder] based on the provided
/// build info.
class PlatformBuilders {
/// Create a new [PlatformBuilder] with `_platformSteps` as platform specific
/// implementations.
const PlatformBuilders([this._platformSteps = const <PlatformBuildStep>[
AndroidPlatformBuildStep(),
]]);
final List<PlatformBuildStep> _platformSteps;
/// Build the platform specific bundle for [flutterProject].
///
/// Selects the [PlatformBuildStep] that supports the required [TargetPlatform]
/// and [BuildMode] requested.
///
/// Throws a [ToolExit] if zero steps or more than one step match.
PlatformBuildStep selectPlatform({
BuildInfo buildInfo,
}) {
PlatformBuildStep selected;
for (PlatformBuildStep platformStep in _platformSteps) {
if (!platformStep.targetPlatforms.contains(buildInfo.targetPlatform)) {
continue;
}
if (!platformStep.buildModes.contains(buildInfo.mode)) {
continue;
}
if (selected != null) {
throwToolExit(
'Multiple platform steps registered for targetPlatform: ${buildInfo.targetPlatform} '
'and build mode: ${buildInfo.mode}.'
);
}
selected = platformStep;
}
if (selected == null) {
throwToolExit(
'No platform steps registered for targetPlatform: ${buildInfo.targetPlatform} '
'and build mode: ${buildInfo.mode}.'
);
}
return selected;
}
}
/// The workflow by which a flutter application in packaged into a platform
/// specific application.
abstract class PlatformBuildStep {
const PlatformBuildStep();
/// Build the platform specific bundle for [flutterProject].
Future<void> build({
FlutterProject project,
BuildInfo buildInfo,
String target,
});
/// The targets this platform step supports.
Set<TargetPlatform> get targetPlatforms;
/// The build modes this platform step supports.
Set<BuildMode> get buildModes;
/// Development artifacts required by this platform step.
Set<DevelopmentArtifact> get developmentArtifacts;
}
// 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:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/platform_step.dart';
import 'package:flutter_tools/src/project.dart';
import 'src/common.dart';
import 'src/testbed.dart';
void main() {
group(PlatformBuilders, () {
Testbed testbed;
setUp(() {
testbed = Testbed();
});
test('Fails if more than one builder matches', () => testbed.run(() {
const PlatformBuilders platformBuilders = PlatformBuilders(<PlatformBuildStep>[
FakePlatformStep(
buildModes: <BuildMode>{ BuildMode.debug, },
targetPlatforms: <TargetPlatform>{ TargetPlatform.android_arm }
),
FakePlatformStep(
buildModes: <BuildMode>{ BuildMode.debug, },
targetPlatforms: <TargetPlatform>{ TargetPlatform.android_arm }
),
]);
const BuildInfo buildInfo = BuildInfo(
BuildMode.debug,
'',
targetPlatform: TargetPlatform.android_arm,
);
expect(() => platformBuilders.selectPlatform(buildInfo: buildInfo), throwsA(isA<ToolExit>()));
}));
test('Fails if no builders match', () => testbed.run(() {
const PlatformBuilders platformBuilders = PlatformBuilders(<PlatformBuildStep>[
FakePlatformStep(
buildModes: <BuildMode>{ BuildMode.debug, },
targetPlatforms: <TargetPlatform>{ TargetPlatform.android_arm }
),
]);
const BuildInfo buildInfo = BuildInfo(
BuildMode.debug,
'',
targetPlatform: TargetPlatform.ios,
);
expect(() => platformBuilders.selectPlatform(buildInfo: buildInfo), throwsA(isA<ToolExit>()));
}));
test('Finds a matching step', () => testbed.run(() {
const PlatformBuildStep iosStep = FakePlatformStep(
buildModes: <BuildMode>{ BuildMode.debug, },
targetPlatforms: <TargetPlatform>{ TargetPlatform.ios }
);
const PlatformBuilders platformBuilders = PlatformBuilders(<PlatformBuildStep>[
FakePlatformStep(
buildModes: <BuildMode>{ BuildMode.debug, },
targetPlatforms: <TargetPlatform>{ TargetPlatform.android_arm }
),
iosStep,
]);
const BuildInfo buildInfo = BuildInfo(
BuildMode.debug,
'',
targetPlatform: TargetPlatform.ios,
);
expect(platformBuilders.selectPlatform(buildInfo: buildInfo), iosStep);
}));
});
}
class FakePlatformStep extends PlatformBuildStep {
const FakePlatformStep({
this.buildModes,
this.developmentArtifacts,
this.targetPlatforms,
});
@override
Future<void> build({FlutterProject project, BuildInfo buildInfo, String target}) {
return null;
}
@override
final Set<BuildMode> buildModes;
@override
final Set<DevelopmentArtifact> developmentArtifacts;
@override
final Set<TargetPlatform> targetPlatforms;
}
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