[flutter_tools] scaffolding for Windows UWP template (#78067)
This commit is contained in:
parent
c30fdfbe82
commit
2584afd7ec
@ -22,15 +22,18 @@ import '../reporting/reporting.dart';
|
||||
import '../runner/flutter_command.dart';
|
||||
import 'create_base.dart';
|
||||
|
||||
const String kPlatformHelp =
|
||||
'The platforms supported by this project. '
|
||||
'Platform folders (e.g. android/) will be generated in the target project. '
|
||||
'This argument only works when "--template" is set to app or plugin. '
|
||||
'When adding platforms to a plugin project, the pubspec.yaml will be updated with the requested platform. '
|
||||
'Adding desktop platforms requires the corresponding desktop config setting to be enabled.';
|
||||
|
||||
class CreateCommand extends CreateBase {
|
||||
CreateCommand({
|
||||
bool verboseHelp = false,
|
||||
}) : super(verboseHelp: verboseHelp) {
|
||||
addPlatformsOptions(customHelp: 'The platforms supported by this project. '
|
||||
'Platform folders (e.g. android/) will be generated in the target project. '
|
||||
'This argument only works when "--template" is set to app or plugin. '
|
||||
'When adding platforms to a plugin project, the pubspec.yaml will be updated with the requested platform. '
|
||||
'Adding desktop platforms requires the corresponding desktop config setting to be enabled.');
|
||||
addPlatformsOptions(customHelp: kPlatformHelp);
|
||||
argParser.addOption(
|
||||
'template',
|
||||
abbr: 't',
|
||||
@ -39,8 +42,7 @@ class CreateCommand extends CreateBase {
|
||||
valueHelp: 'type',
|
||||
allowedHelp: <String, String>{
|
||||
flutterProjectTypeToString(FlutterProjectType.app): '(default) Generate a Flutter application.',
|
||||
flutterProjectTypeToString(FlutterProjectType.package): 'Generate a shareable Flutter project containing modular '
|
||||
'Dart code.',
|
||||
flutterProjectTypeToString(FlutterProjectType.package): 'Generate a shareable Flutter project containing only Dart code.',
|
||||
flutterProjectTypeToString(FlutterProjectType.plugin): 'Generate a shareable Flutter project containing an API '
|
||||
'in Dart code with a platform-specific implementation for Android, for iOS code, or '
|
||||
'for both.',
|
||||
@ -240,6 +242,7 @@ class CreateCommand extends CreateBase {
|
||||
linux: featureFlags.isLinuxEnabled && platforms.contains('linux'),
|
||||
macos: featureFlags.isMacOSEnabled && platforms.contains('macos'),
|
||||
windows: featureFlags.isWindowsEnabled && platforms.contains('windows'),
|
||||
windowsUwp: featureFlags.isWindowsUwpEnabled && platforms.contains('winuwp'),
|
||||
// Enable null-safety for sample code, which is - unlike our regular templates - already migrated.
|
||||
dartSdkVersionBounds: sampleCode != null ? '">=2.12.0-0 <3.0.0"' : '">=2.7.0 <3.0.0"'
|
||||
);
|
||||
@ -382,15 +385,11 @@ Your $application code is in $relativeAppMain.
|
||||
}
|
||||
|
||||
Future<int> _generatePlugin(Directory directory, Map<String, dynamic> templateContext, { bool overwrite = false }) async {
|
||||
// Plugin doesn't create any platform by default
|
||||
// Plugins only add a platform if it was requested explicitly by the user.
|
||||
if (!argResults.wasParsed('platforms')) {
|
||||
// If the user didn't explicitly declare the platforms, we don't generate any platforms.
|
||||
templateContext['ios'] = false;
|
||||
templateContext['android'] = false;
|
||||
templateContext['web'] = false;
|
||||
templateContext['linux'] = false;
|
||||
templateContext['macos'] = false;
|
||||
templateContext['windows'] = false;
|
||||
for (final String platform in kAllCreatePlatforms) {
|
||||
templateContext[platform] = false;
|
||||
}
|
||||
}
|
||||
final List<String> platformsToAdd = _getSupportedPlatformsFromTemplateContext(templateContext);
|
||||
|
||||
@ -457,18 +456,8 @@ Your $application code is in $relativeAppMain.
|
||||
|
||||
List<String> _getSupportedPlatformsFromTemplateContext(Map<String, dynamic> templateContext) {
|
||||
return <String>[
|
||||
if (templateContext['ios'] == true)
|
||||
'ios',
|
||||
if (templateContext['android'] == true)
|
||||
'android',
|
||||
if (templateContext['web'] == true)
|
||||
'web',
|
||||
if (templateContext['linux'] == true)
|
||||
'linux',
|
||||
if (templateContext['windows'] == true)
|
||||
'windows',
|
||||
if (templateContext['macos'] == true)
|
||||
'macos',
|
||||
for (String platform in kAllCreatePlatforms)
|
||||
if (templateContext[platform] == true) platform
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ import '../base/utils.dart';
|
||||
import '../cache.dart';
|
||||
import '../convert.dart';
|
||||
import '../dart/pub.dart';
|
||||
import '../features.dart';
|
||||
import '../flutter_project_metadata.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../project.dart';
|
||||
@ -31,6 +32,18 @@ const List<String> _kAvailablePlatforms = <String>[
|
||||
'web',
|
||||
];
|
||||
|
||||
/// A list of all possible create platforms, even those that may not be enabled
|
||||
/// with the current config.
|
||||
const List<String> kAllCreatePlatforms = <String>[
|
||||
'ios',
|
||||
'android',
|
||||
'windows',
|
||||
'linux',
|
||||
'macos',
|
||||
'web',
|
||||
'winuwp',
|
||||
];
|
||||
|
||||
const String _kDefaultPlatformArgumentHelp =
|
||||
'(required) The platforms supported by this project. '
|
||||
'Platform folders (e.g. android/) will be generated in the target project. '
|
||||
@ -130,17 +143,28 @@ abstract class CreateBase extends FlutterCommand {
|
||||
@protected
|
||||
void addPlatformsOptions({String customHelp}) {
|
||||
argParser.addMultiOption('platforms',
|
||||
help: customHelp ?? _kDefaultPlatformArgumentHelp,
|
||||
defaultsTo: _kAvailablePlatforms,
|
||||
allowed: _kAvailablePlatforms);
|
||||
help: customHelp ?? _kDefaultPlatformArgumentHelp,
|
||||
defaultsTo: <String>[
|
||||
..._kAvailablePlatforms,
|
||||
if (featureFlags.isWindowsUwpEnabled)
|
||||
'winuwp',
|
||||
],
|
||||
allowed: <String>[
|
||||
..._kAvailablePlatforms,
|
||||
if (featureFlags.isWindowsUwpEnabled)
|
||||
'winuwp',
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Throw with exit code 2 if the output directory is invalid.
|
||||
@protected
|
||||
void validateOutputDirectoryArg() {
|
||||
if (argResults.rest.isEmpty) {
|
||||
throwToolExit('No option specified for the output directory.\n$usage',
|
||||
exitCode: 2);
|
||||
throwToolExit(
|
||||
'No option specified for the output directory.\n$usage',
|
||||
exitCode: 2,
|
||||
);
|
||||
}
|
||||
|
||||
if (argResults.rest.length > 1) {
|
||||
@ -156,37 +180,8 @@ abstract class CreateBase extends FlutterCommand {
|
||||
}
|
||||
|
||||
/// Gets the flutter root directory.
|
||||
///
|
||||
/// Throw with exit code 2 if the flutter sdk installed is invalid.
|
||||
@protected
|
||||
String get flutterRoot {
|
||||
if (Cache.flutterRoot == null) {
|
||||
throwToolExit(
|
||||
'The FLUTTER_ROOT environment variable was not specified. Unable to find package:flutter.',
|
||||
exitCode: 2);
|
||||
}
|
||||
final String flutterRoot = globals.fs.path.absolute(Cache.flutterRoot);
|
||||
|
||||
final String flutterPackagesDirectory =
|
||||
globals.fs.path.join(flutterRoot, 'packages');
|
||||
final String flutterPackagePath =
|
||||
globals.fs.path.join(flutterPackagesDirectory, 'flutter');
|
||||
if (!globals.fs
|
||||
.isFileSync(globals.fs.path.join(flutterPackagePath, 'pubspec.yaml'))) {
|
||||
throwToolExit('Unable to find package:flutter in $flutterPackagePath',
|
||||
exitCode: 2);
|
||||
}
|
||||
|
||||
final String flutterDriverPackagePath =
|
||||
globals.fs.path.join(flutterRoot, 'packages', 'flutter_driver');
|
||||
if (!globals.fs.isFileSync(
|
||||
globals.fs.path.join(flutterDriverPackagePath, 'pubspec.yaml'))) {
|
||||
throwToolExit(
|
||||
'Unable to find package:flutter_driver in $flutterDriverPackagePath',
|
||||
exitCode: 2);
|
||||
}
|
||||
return flutterRoot;
|
||||
}
|
||||
String get flutterRoot => Cache.flutterRoot;
|
||||
|
||||
/// Determines the project type in an existing flutter project.
|
||||
///
|
||||
@ -332,9 +327,8 @@ abstract class CreateBase extends FlutterCommand {
|
||||
bool linux = false,
|
||||
bool macos = false,
|
||||
bool windows = false,
|
||||
bool windowsUwp = false,
|
||||
}) {
|
||||
flutterRoot = globals.fs.path.normalize(flutterRoot);
|
||||
|
||||
final String pluginDartClass = _createPluginClassName(projectName);
|
||||
final String pluginClass = pluginDartClass.endsWith('Plugin')
|
||||
? pluginDartClass
|
||||
@ -379,6 +373,7 @@ abstract class CreateBase extends FlutterCommand {
|
||||
'linux': linux,
|
||||
'macos': macos,
|
||||
'windows': windows,
|
||||
'winuwp': windowsUwp,
|
||||
'year': DateTime.now().year,
|
||||
'dartSdkVersionBounds': dartSdkVersionBounds,
|
||||
};
|
||||
@ -410,8 +405,12 @@ abstract class CreateBase extends FlutterCommand {
|
||||
Directory directory, Map<String, dynamic> templateContext,
|
||||
{bool overwrite = false, bool pluginExampleApp = false}) async {
|
||||
int generatedCount = 0;
|
||||
generatedCount += await renderTemplate('app', directory, templateContext,
|
||||
overwrite: overwrite);
|
||||
generatedCount += await renderTemplate(
|
||||
'app',
|
||||
directory,
|
||||
templateContext,
|
||||
overwrite: overwrite,
|
||||
);
|
||||
final FlutterProject project = FlutterProject.fromDirectory(directory);
|
||||
if (templateContext['android'] == true) {
|
||||
generatedCount += _injectGradleWrapper(project);
|
||||
@ -432,6 +431,7 @@ abstract class CreateBase extends FlutterCommand {
|
||||
macOSPlatform: templateContext['macos'] as bool ?? false,
|
||||
windowsPlatform: templateContext['windows'] as bool ?? false,
|
||||
webPlatform: templateContext['web'] as bool ?? false,
|
||||
windowsUwpPlatform: templateContext['winuwp'] as bool ?? false,
|
||||
);
|
||||
}
|
||||
if (templateContext['android'] == true) {
|
||||
@ -616,32 +616,10 @@ const Set<String> _keywords = <String>{
|
||||
};
|
||||
|
||||
const Set<String> _packageDependencies = <String>{
|
||||
'analyzer',
|
||||
'args',
|
||||
'async',
|
||||
'collection',
|
||||
'convert',
|
||||
'crypto',
|
||||
'flutter',
|
||||
'flutter_test',
|
||||
'front_end',
|
||||
'html',
|
||||
'http',
|
||||
'intl',
|
||||
'io',
|
||||
'isolate',
|
||||
'kernel',
|
||||
'logging',
|
||||
'matcher',
|
||||
'meta',
|
||||
'mime',
|
||||
'path',
|
||||
'plugin',
|
||||
'pool',
|
||||
'test',
|
||||
'utf',
|
||||
'watcher',
|
||||
'yaml',
|
||||
};
|
||||
|
||||
/// Whether [name] is a valid Pub package.
|
||||
|
@ -135,6 +135,7 @@ const List<Feature> allFeatures = <Feature>[
|
||||
flutterLinuxDesktopFeature,
|
||||
flutterMacOSDesktopFeature,
|
||||
flutterWindowsDesktopFeature,
|
||||
windowsUwpEmbedding,
|
||||
singleWidgetReload,
|
||||
flutterAndroidFeature,
|
||||
flutterIOSFeature,
|
||||
|
@ -170,6 +170,10 @@ class FlutterProject {
|
||||
WindowsProject _windows;
|
||||
WindowsProject get windows => _windows ??= WindowsProject._(this);
|
||||
|
||||
/// The Windows UWP sub project of this project.
|
||||
WindowsUwpProject _windowUwp;
|
||||
WindowsUwpProject get windowsUwp => _windowUwp ??= WindowsUwpProject._(this);
|
||||
|
||||
/// The Fuchsia sub project of this project.
|
||||
FuchsiaProject _fuchsia;
|
||||
FuchsiaProject get fuchsia => _fuchsia ??= FuchsiaProject._(this);
|
||||
@ -287,6 +291,7 @@ class FlutterProject {
|
||||
bool macOSPlatform = false,
|
||||
bool windowsPlatform = false,
|
||||
bool webPlatform = false,
|
||||
bool windowsUwpPlatform = false,
|
||||
}) async {
|
||||
if (!directory.existsSync() || hasExampleApp || isPlugin) {
|
||||
return;
|
||||
@ -310,6 +315,9 @@ class FlutterProject {
|
||||
if (webPlatform) {
|
||||
await web.ensureReadyForPlatformSpecificTooling();
|
||||
}
|
||||
if (windowsUwpPlatform) {
|
||||
await windowsUwp.ensureReadyForPlatformSpecificTooling();
|
||||
}
|
||||
await injectPlugins(
|
||||
this,
|
||||
androidPlatform: androidPlatform,
|
||||
@ -1163,6 +1171,8 @@ class WindowsProject extends FlutterProjectPlatform implements CmakeBasedProject
|
||||
@override
|
||||
String get pluginConfigKey => WindowsPlugin.kConfigKey;
|
||||
|
||||
String get _childDirectory => 'windows';
|
||||
|
||||
@override
|
||||
bool existsSync() => _editableDirectory.existsSync() && cmakeFile.existsSync();
|
||||
|
||||
@ -1181,7 +1191,7 @@ class WindowsProject extends FlutterProjectPlatform implements CmakeBasedProject
|
||||
@override
|
||||
Directory get pluginSymlinkDirectory => ephemeralDirectory.childDirectory('.plugin_symlinks');
|
||||
|
||||
Directory get _editableDirectory => parent.directory.childDirectory('windows');
|
||||
Directory get _editableDirectory => parent.directory.childDirectory(_childDirectory);
|
||||
|
||||
/// The directory in the project that is managed by Flutter. As much as
|
||||
/// possible, files that are edited by Flutter tooling after initial project
|
||||
@ -1196,6 +1206,17 @@ class WindowsProject extends FlutterProjectPlatform implements CmakeBasedProject
|
||||
Future<void> ensureReadyForPlatformSpecificTooling() async {}
|
||||
}
|
||||
|
||||
/// The Windows UWP version of the Windows project.
|
||||
class WindowsUwpProject extends WindowsProject {
|
||||
WindowsUwpProject._(FlutterProject parent) : super._(parent);
|
||||
|
||||
@override
|
||||
String get _childDirectory => 'windows-uwp';
|
||||
|
||||
/// 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());
|
||||
}
|
||||
|
||||
/// The Linux sub project.
|
||||
class LinuxProject extends FlutterProjectPlatform implements CmakeBasedProject {
|
||||
LinuxProject._(this.parent);
|
||||
|
@ -162,6 +162,11 @@ class Template {
|
||||
if (relativeDestinationPath.startsWith('windows.tmpl') && !windows) {
|
||||
return null;
|
||||
}
|
||||
// Only build a Windows UWP project if explicitly asked.
|
||||
final bool windowsUwp = context['winuwp'] as bool;
|
||||
if (relativeDestinationPath.startsWith('winuwp.tmpl') && !windowsUwp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String projectName = context['projectName'] as String;
|
||||
final String androidIdentifier = context['androidIdentifier'] as String;
|
||||
|
@ -0,0 +1 @@
|
||||
0
|
@ -137,6 +137,7 @@
|
||||
"templates/app/windows.tmpl/runner/utils.h",
|
||||
"templates/app/windows.tmpl/runner/win32_window.cpp",
|
||||
"templates/app/windows.tmpl/runner/win32_window.h",
|
||||
"templates/app/winuwp.tmpl/project_version",
|
||||
"templates/cocoapods/Podfile-ios-objc",
|
||||
"templates/cocoapods/Podfile-ios-swift",
|
||||
"templates/cocoapods/Podfile-macos",
|
||||
|
@ -38,11 +38,14 @@ const String _kNoPlatformsMessage = 'You\'ve created a plugin project that doesn
|
||||
const String frameworkRevision = '12345678';
|
||||
const String frameworkChannel = 'omega';
|
||||
const String _kDisabledPlatformRequestedMessage = 'currently not supported on your local environment.';
|
||||
// TODO(fujino): replace FakePlatform.fromPlatform() with FakePlatform()
|
||||
|
||||
// This needs to be created from the local platform due to re-entrant flutter calls made in this test.
|
||||
FakePlatform _kNoColorTerminalPlatform() => FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false;
|
||||
|
||||
final Map<Type, Generator> noColorTerminalOverride = <Type, Generator>{
|
||||
Platform: _kNoColorTerminalPlatform,
|
||||
};
|
||||
|
||||
const String samplesIndexJson = '''
|
||||
[
|
||||
{ "id": "sample1" },
|
||||
@ -702,6 +705,7 @@ void main() {
|
||||
expect(projectDir.childDirectory('macos'), isNot(exists));
|
||||
expect(projectDir.childDirectory('windows'), isNot(exists));
|
||||
expect(projectDir.childDirectory('web'), isNot(exists));
|
||||
expect(projectDir.childDirectory('winuwp'), isNot(exists));
|
||||
}, overrides: <Type, Generator>{
|
||||
FeatureFlags: () => TestFeatureFlags(),
|
||||
});
|
||||
@ -720,18 +724,42 @@ void main() {
|
||||
expect(projectDir.childDirectory('macos'), isNot(exists));
|
||||
expect(projectDir.childDirectory('windows'), isNot(exists));
|
||||
expect(projectDir.childDirectory('web'), isNot(exists));
|
||||
expect(projectDir.childDirectory('example').childDirectory('linux'),
|
||||
isNot(exists));
|
||||
expect(projectDir.childDirectory('example').childDirectory('macos'),
|
||||
isNot(exists));
|
||||
expect(projectDir.childDirectory('example').childDirectory('windows'),
|
||||
isNot(exists));
|
||||
expect(projectDir.childDirectory('example').childDirectory('web'),
|
||||
isNot(exists));
|
||||
expect(projectDir.childDirectory('winuwp'), isNot(exists));
|
||||
expect(projectDir.childDirectory('example').childDirectory('linux'), isNot(exists));
|
||||
expect(projectDir.childDirectory('example').childDirectory('macos'), isNot(exists));
|
||||
expect(projectDir.childDirectory('example').childDirectory('windows'), isNot(exists));
|
||||
expect(projectDir.childDirectory('example').childDirectory('web'), isNot(exists));
|
||||
expect(projectDir.childDirectory('example').childDirectory('winuwp'), isNot(exists));
|
||||
}, overrides: <Type, Generator>{
|
||||
FeatureFlags: () => TestFeatureFlags(),
|
||||
});
|
||||
|
||||
testUsingContext('app supports Windows UWP if requested', () async {
|
||||
Cache.flutterRoot = '../..';
|
||||
|
||||
final CreateCommand command = CreateCommand();
|
||||
final CommandRunner<void> runner = createTestCommandRunner(command);
|
||||
|
||||
await runner.run(<String>[
|
||||
'create',
|
||||
'--no-pub',
|
||||
'--platforms=winuwp',
|
||||
projectDir.path,
|
||||
]);
|
||||
|
||||
expect(projectDir.childDirectory('linux'), isNot(exists));
|
||||
expect(projectDir.childDirectory('android'), isNot(exists));
|
||||
expect(projectDir.childDirectory('ios'), isNot(exists));
|
||||
expect(projectDir.childDirectory('windows'), isNot(exists));
|
||||
expect(projectDir.childDirectory('macos'), isNot(exists));
|
||||
expect(projectDir.childDirectory('web'), isNot(exists));
|
||||
expect(projectDir.childDirectory('winuwp'), exists);
|
||||
expect(logger.errorText, isNot(contains(_kNoPlatformsMessage)));
|
||||
}, overrides: <Type, Generator>{
|
||||
FeatureFlags: () => TestFeatureFlags(isWindowsUwpEnabled: true),
|
||||
Logger: () => logger,
|
||||
});
|
||||
|
||||
testUsingContext('app supports Linux if requested', () async {
|
||||
Cache.flutterRoot = '../..';
|
||||
|
||||
@ -745,13 +773,13 @@ void main() {
|
||||
projectDir.path,
|
||||
]);
|
||||
|
||||
expect(
|
||||
projectDir.childDirectory('linux').childFile('CMakeLists.txt'), exists);
|
||||
expect(projectDir.childDirectory('linux').childFile('CMakeLists.txt'), exists);
|
||||
expect(projectDir.childDirectory('android'), isNot(exists));
|
||||
expect(projectDir.childDirectory('ios'), isNot(exists));
|
||||
expect(projectDir.childDirectory('windows'), isNot(exists));
|
||||
expect(projectDir.childDirectory('macos'), isNot(exists));
|
||||
expect(projectDir.childDirectory('web'), isNot(exists));
|
||||
expect(projectDir.childDirectory('winuwp'), isNot(exists));
|
||||
expect(logger.errorText, isNot(contains(_kNoPlatformsMessage)));
|
||||
}, overrides: <Type, Generator>{
|
||||
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
|
||||
|
Loading…
x
Reference in New Issue
Block a user