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';
import '../android/android_sdk.dart';
import '../android/android_workflow.dart';
import '../android/apk.dart';
import '../application_package.dart';
import '../base/common.dart' show throwToolExit;
import '../base/file_system.dart';
......@@ -20,6 +19,7 @@ import '../build_info.dart';
import '../convert.dart';
import '../device.dart';
import '../globals.dart';
import '../platform_step.dart';
import '../project.dart';
import '../protocol_discovery.dart';
......@@ -374,7 +374,8 @@ class AndroidDevice extends Device {
if (!prebuiltApplication || androidSdk.licensesAvailable && androidSdk.latestVersion == null) {
printTrace('Building APK');
final FlutterProject project = FlutterProject.current();
await buildApk(
final PlatformBuildStep platformStep = platformBuilders.selectPlatform(buildInfo: buildInfo);
await platformStep.build(
project: project,
target: mainPath,
buildInfo: buildInfo,
......
......@@ -4,20 +4,25 @@
import 'dart:async';
import 'package:meta/meta.dart';
import '../base/common.dart';
import '../build_info.dart';
import '../cache.dart';
import '../platform_step.dart';
import '../project.dart';
import 'android_sdk.dart';
import 'gradle.dart';
Future<void> buildApk({
@required FlutterProject project,
@required String target,
BuildInfo buildInfo = BuildInfo.debug,
}) async {
/// The Android Gradle build step.
class AndroidPlatformBuildStep extends PlatformBuildStep {
const AndroidPlatformBuildStep();
@override
Future<void> build({
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'
......@@ -28,9 +33,9 @@ Future<void> buildApk({
}
// Validate that we can find an android sdk.
if (androidSdk == null)
if (androidSdk == null) {
throwToolExit('No Android SDK found. Try setting the ANDROID_SDK_ROOT environment variable.');
}
await buildGradleProject(
project: project,
buildInfo: buildInfo,
......@@ -38,4 +43,28 @@ Future<void> buildApk({
isBuildingBundle: false,
);
androidSdk.reinitialize();
}
@override
Set<TargetPlatform> get targetPlatforms => const <TargetPlatform>{
TargetPlatform.android_arm,
TargetPlatform.android_arm64,
TargetPlatform.android_x64,
TargetPlatform.android_x86,
};
@override
Set<BuildMode> get buildModes => const <BuildMode>{
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 @@
import 'dart:async';
import '../android/apk.dart';
import '../build_info.dart';
import '../platform_step.dart';
import '../project.dart';
import '../runner/flutter_command.dart' show DevelopmentArtifact, FlutterCommandResult;
import 'build.dart';
......@@ -47,10 +48,12 @@ class BuildApkCommand extends BuildSubCommand {
@override
Future<FlutterCommandResult> runCommand() async {
await buildApk(
final BuildInfo buildInfo = getBuildInfo();
final PlatformBuildStep platformStep = platformBuilders.selectPlatform(buildInfo: buildInfo);
await platformStep.build(
project: FlutterProject.current(),
target: targetFile,
buildInfo: getBuildInfo(),
buildInfo: buildInfo,
);
return null;
}
......
......@@ -36,6 +36,7 @@ import 'ios/simulators.dart';
import 'ios/xcodeproj.dart';
import 'linux/linux_workflow.dart';
import 'macos/macos_workflow.dart';
import 'platform_step.dart';
import 'run_hot.dart';
import 'usage.dart';
import 'version.dart';
......@@ -87,6 +88,7 @@ Future<T> runInContext<T>(
Logger: () => platform.isWindows ? WindowsStdoutLogger() : StdoutLogger(),
MacOSWorkflow: () => const MacOSWorkflow(),
OperatingSystemUtils: () => OperatingSystemUtils(),
PlatformBuilders: () => const PlatformBuilders(),
PlistBuddy: () => const PlistBuddy(),
SimControl: () => SimControl(),
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