From 985fc7463c96a31673c12d3d5d40f466aa30f9f5 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Fri, 19 Mar 2021 15:36:42 -0700 Subject: [PATCH] [flutter_tools] add skeleton for build uwp (#78624) Adds the rest of the scaffolding for building a UWP application. The actual build functionality needs to be implemented, but could use buildWindows as an example (if it is going through cmake) #14967 --- .../flutter_tools/lib/src/commands/build.dart | 2 + .../lib/src/commands/build_winuwp.dart | 63 +++++++++++++++++++ packages/flutter_tools/lib/src/project.dart | 2 +- .../lib/src/windows/build_windows.dart | 26 ++++++++ .../hermetic/build_windows_test.dart | 63 ++++++++++++++++++- 5 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 packages/flutter_tools/lib/src/commands/build_winuwp.dart diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index cbe2393bcc..d3ceccd858 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -20,6 +20,7 @@ import 'build_fuchsia.dart'; import 'build_ios.dart'; import 'build_ios_framework.dart'; import 'build_web.dart'; +import 'build_winuwp.dart'; class BuildCommand extends FlutterCommand { BuildCommand({ bool verboseHelp = false }) { @@ -40,6 +41,7 @@ class BuildCommand extends FlutterCommand { verboseHelp: verboseHelp )); addSubcommand(BuildWindowsCommand(verboseHelp: verboseHelp)); + addSubcommand(BuildWindowsUwpCommand(verboseHelp: verboseHelp)); addSubcommand(BuildFuchsiaCommand(verboseHelp: verboseHelp)); } diff --git a/packages/flutter_tools/lib/src/commands/build_winuwp.dart b/packages/flutter_tools/lib/src/commands/build_winuwp.dart new file mode 100644 index 0000000000..a06987269b --- /dev/null +++ b/packages/flutter_tools/lib/src/commands/build_winuwp.dart @@ -0,0 +1,63 @@ +// 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. + +// @dart = 2.8 + +import 'package:meta/meta.dart'; + +import '../base/common.dart'; +import '../build_info.dart'; +import '../cache.dart'; +import '../features.dart'; +import '../globals.dart' as globals; +import '../project.dart'; +import '../runner/flutter_command.dart' show FlutterCommandResult; +import '../windows/build_windows.dart'; +import '../windows/visual_studio.dart'; +import 'build.dart'; + +/// A command to build a Windows UWP desktop target. +class BuildWindowsUwpCommand extends BuildSubCommand { + BuildWindowsUwpCommand({ bool verboseHelp = false }) { + addCommonDesktopBuildOptions(verboseHelp: verboseHelp); + } + + @override + final String name = 'winuwp'; + + @override + bool get hidden => !featureFlags.isWindowsUwpEnabled || !globals.platform.isWindows; + + @override + Future> get requiredArtifacts async => { + // TODO(flutter): add a windows_uwp artifact here once that is updated. + // https://github.com/flutter/flutter/issues/78627 + }; + + @override + String get description => 'Build a Windows UWP desktop application.'; + + @visibleForTesting + VisualStudio visualStudioOverride; + + @override + Future runCommand() async { + final FlutterProject flutterProject = FlutterProject.current(); + final BuildInfo buildInfo = await getBuildInfo(); + if (!featureFlags.isWindowsUwpEnabled) { + throwToolExit('"build windows" is not currently supported.'); + } + if (!globals.platform.isWindows) { + throwToolExit('"build windows" only supported on Windows hosts.'); + } + displayNullSafetyMode(buildInfo); + await buildWindowsUwp( + flutterProject.windowsUwp, + buildInfo, + target: targetFile, + visualStudioOverride: visualStudioOverride, + ); + return FlutterCommandResult.success(); + } +} diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart index 7f86463aec..425a27dfdc 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart @@ -1211,7 +1211,7 @@ class WindowsUwpProject extends WindowsProject { WindowsUwpProject._(FlutterProject parent) : super._(parent); @override - String get _childDirectory => 'windows-uwp'; + String get _childDirectory => 'winuwp'; /// Eventually this will be used to check if the user's unstable project needs to be regenerated. int get projectVersion => int.tryParse(_editableDirectory.childFile('project_version').readAsStringSync()); diff --git a/packages/flutter_tools/lib/src/windows/build_windows.dart b/packages/flutter_tools/lib/src/windows/build_windows.dart index 057dee0f36..ae9edaa5b2 100644 --- a/packages/flutter_tools/lib/src/windows/build_windows.dart +++ b/packages/flutter_tools/lib/src/windows/build_windows.dart @@ -26,6 +26,9 @@ import 'visual_studio.dart'; // future major versions of Visual Studio. const String _cmakeVisualStudioGeneratorIdentifier = 'Visual Studio 16 2019'; +/// Update the string when non-backwards compatible changes are made to the UWP template. +const int kCurrentUwpTemplateVersion = 0; + /// Builds the Windows project using msbuild. Future buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, { String target, @@ -110,6 +113,29 @@ Future buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, { } } +/// Build the Windows UWP project. +/// +/// Note that this feature is currently unfinished. +Future buildWindowsUwp(WindowsUwpProject windowsProject, BuildInfo buildInfo, { + String target, + VisualStudio visualStudioOverride, +}) async { + if (!windowsProject.existsSync()) { + throwToolExit( + 'No Windows UWP desktop project configured. See ' + 'https://flutter.dev/desktop#add-desktop-support-to-an-existing-flutter-app ' + 'to learn about adding Windows support to a project.', + ); + } + if (windowsProject.projectVersion != kCurrentUwpTemplateVersion) { + throwToolExit( + 'The Windows UWP project template and build process has changed. In order to build ' + 'you must delete the winuwp directory and re-create the project.', + ); + } + throwToolExit('Windows UWP builds are not implemented.'); +} + Future _runCmakeGeneration(String cmakePath, Directory buildDir, Directory sourceDir) async { final Stopwatch sw = Stopwatch()..start(); diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart index ffd1d87b5b..900f8b0543 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart @@ -10,6 +10,7 @@ import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/build_windows.dart'; +import 'package:flutter_tools/src/commands/build_winuwp.dart'; import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/reporting/reporting.dart'; import 'package:flutter_tools/src/windows/visual_studio.dart'; @@ -23,6 +24,7 @@ import '../../src/testbed.dart'; const String flutterRoot = r'C:\flutter'; const String buildFilePath = r'C:\windows\CMakeLists.txt'; +const String buildUwpFilePath = r'C:\winuwp\CMakeLists.txt'; const String visualStudioPath = r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community'; const String cmakePath = visualStudioPath + r'\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe'; @@ -67,11 +69,19 @@ void main() { } // Creates the mock files necessary to run a build. - void setUpMockProjectFilesForBuild({int templateVersion}) { + void setUpMockProjectFilesForBuild() { fileSystem.file(buildFilePath).createSync(recursive: true); setUpMockCoreProjectFiles(); } + void setUpMockUwpFilesForBuild(int version) { + final Directory projectDirectory = (fileSystem.file(buildUwpFilePath) + ..createSync(recursive: true)) + .parent; + projectDirectory.childFile('project_version').writeAsString(version.toString()); + setUpMockCoreProjectFiles(); + } + // Returns the command matching the build_windows call to generate CMake // files. FakeCommand cmakeGenerationCommand({void Function() onRun}) { @@ -421,6 +431,57 @@ C:\foo\windows\runner\main.cpp(17,1): error C2065: 'Baz': undeclared identifier FileSystemUtils: () => FileSystemUtils(fileSystem: fileSystem, platform: windowsPlatform), Usage: () => usage, }); + + testUsingContext('Windows build fails when there is no windows project', () async { + final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath); + final BuildWindowsUwpCommand command = BuildWindowsUwpCommand() + ..visualStudioOverride = fakeVisualStudio; + setUpMockCoreProjectFiles(); + + expect(createTestCommandRunner(command).run( + const ['winuwp', '--no-pub'] + ), throwsToolExit(message: 'No Windows UWP desktop project configured. See ' + 'https://flutter.dev/desktop#add-desktop-support-to-an-existing-flutter-app ' + 'to learn about adding Windows support to a project.')); + }, overrides: { + Platform: () => windowsPlatform, + FileSystem: () => fileSystem, + ProcessManager: () => FakeProcessManager.any(), + FeatureFlags: () => TestFeatureFlags(isWindowsUwpEnabled: true), + }); + + testUsingContext('Windows build fails on non windows platform', () async { + final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath); + final BuildWindowsUwpCommand command = BuildWindowsUwpCommand() + ..visualStudioOverride = fakeVisualStudio; + setUpMockUwpFilesForBuild(0); + + expect(createTestCommandRunner(command).run( + const ['winuwp', '--no-pub'] + ), throwsToolExit()); + }, overrides: { + Platform: () => notWindowsPlatform, + FileSystem: () => fileSystem, + ProcessManager: () => FakeProcessManager.any(), + FeatureFlags: () => TestFeatureFlags(isWindowsUwpEnabled: true), + }); + + testUsingContext('Windows build fails when the project version is out of date', () async { + final FakeVisualStudio fakeVisualStudio = FakeVisualStudio(cmakePath); + final BuildWindowsUwpCommand command = BuildWindowsUwpCommand() + ..visualStudioOverride = fakeVisualStudio; + setUpMockUwpFilesForBuild(-1); + + expect(createTestCommandRunner(command).run( + const ['winuwp', '--no-pub'] + ), throwsToolExit(message: 'The Windows UWP project template and build process has changed. ' + 'In order to build you must delete the winuwp directory and re-create the project')); + }, overrides: { + Platform: () => windowsPlatform, + FileSystem: () => fileSystem, + ProcessManager: () => FakeProcessManager.any(), + FeatureFlags: () => TestFeatureFlags(isWindowsUwpEnabled: true), + }); } class FakeVisualStudio extends Fake implements VisualStudio {