Unverified Commit 9f23866a authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Rename module --> application in flutter create command. (#22565)

This renames the "module" template to the "application" template, and makes "application" the default. The existing "app" template is now deprecated.

flutter create also now recognizes the type of project in an existing directory, and is able to recreate it without having the template type explicitly specified (although you can still do that). It does this now by first looking in the .metadata file for the new project_type field, and if it doesn't find that, then it looks at the directory structure. Also, the .metadata file is now overwritten even on an existing directory so that 1) the project_type can be added to legacy projects, and 2) the version of Flutter that updated the project last is updated.

I also cleaned up a bunch of things in create_test.dart, added many more tests, and added an example test to the test/ directory in the generated output of the application template.

Fixes #22530
Fixes #22344
parent 66f1c675
......@@ -143,7 +143,7 @@ Future<void> cleanupSystem() async {
final String gradlewBinaryName = Platform.isWindows ? 'gradlew.bat' : 'gradlew';
final Directory tempDir = Directory.systemTemp.createTempSync('flutter_devicelab_shutdown_gradle.');
recursiveCopy(Directory(path.join(flutterDirectory.path, 'bin', 'cache', 'artifacts', 'gradle_wrapper')), tempDir);
copy(File(path.join(path.join(flutterDirectory.path, 'packages', 'flutter_tools'), 'templates', 'create', 'android.tmpl', 'gradle', 'wrapper', 'gradle-wrapper.properties')), Directory(path.join(tempDir.path, 'gradle', 'wrapper')));
copy(File(path.join(path.join(flutterDirectory.path, 'packages', 'flutter_tools'), 'templates', 'app', 'android.tmpl', 'gradle', 'wrapper', 'gradle-wrapper.properties')), Directory(path.join(tempDir.path, 'gradle', 'wrapper')));
if (!Platform.isWindows) {
await exec(
'chmod',
......
......@@ -56,7 +56,7 @@ Future<Null> main(List<String> args) async {
ChannelCommand(verboseHelp: verboseHelp),
CleanCommand(),
ConfigCommand(verboseHelp: verboseHelp),
CreateCommand(),
CreateCommand(verboseHelp: verboseHelp),
DaemonCommand(hidden: !verboseHelp),
DevicesCommand(),
DoctorCommand(verbose: verbose),
......
......@@ -44,8 +44,8 @@ abstract class MakeHostAppEditableSubCommand extends FlutterCommand {
Future<Null> validateCommand() async {
await super.validateCommand();
_project = await FlutterProject.current();
if (!_project.isModule)
throw ToolExit("Only projects created using 'flutter create -t module' can have their host apps made editable.");
if (!_project.isApplication)
throw ToolExit("Only projects created using 'flutter create -t application' can have their host apps made editable.");
}
}
......
......@@ -110,14 +110,14 @@ class FlutterManifest {
return _flutterDescriptor['uses-material-design'] ?? false;
}
/// True if this manifest declares a Flutter module project.
/// True if this manifest declares a Flutter application project.
///
/// A Flutter project is considered a module when it has a `module:`
/// descriptor. A Flutter module project supports integration into an
/// A Flutter project is considered an application when it has a `application:`
/// descriptor. A Flutter application project supports integration into an
/// existing host app.
///
/// Such a project can be created using `flutter create -t module`.
bool get isModule => _flutterDescriptor.containsKey('module');
/// Such a project can be created using `flutter create -t application`.
bool get isApplication => _flutterDescriptor.containsKey('application');
/// True if this manifest declares a Flutter plugin project.
///
......@@ -130,21 +130,21 @@ class FlutterManifest {
bool get isPlugin => _flutterDescriptor.containsKey('plugin');
/// Returns the Android package declared by this manifest in its
/// module or plugin descriptor. Returns null, if there is no
/// application or plugin descriptor. Returns null, if there is no
/// such declaration.
String get androidPackage {
if (isModule)
return _flutterDescriptor['module']['androidPackage'];
if (isApplication)
return _flutterDescriptor['application']['androidPackage'];
if (isPlugin)
return _flutterDescriptor['plugin']['androidPackage'];
return null;
}
/// Returns the iOS bundle identifier declared by this manifest in its
/// module descriptor. Returns null, if there is no such declaration.
/// application descriptor. Returns null, if there is no such declaration.
String get iosBundleIdentifier {
if (isModule)
return _flutterDescriptor['module']['iosBundleIdentifier'];
if (isApplication)
return _flutterDescriptor['application']['iosBundleIdentifier'];
return null;
}
......
......@@ -335,7 +335,7 @@ Future<XcodeBuildResult> buildXcodeProject({
buildInfo: buildInfo,
);
refreshPluginsList(project);
if (hasPlugins(project) || (project.isModule && project.ios.podfile.existsSync())) {
if (hasPlugins(project) || (project.isApplication && project.ios.podfile.existsSync())) {
// If the Xcode project, Podfile, or Generated.xcconfig have changed since
// last run, pods should be updated.
final Fingerprinter fingerprinter = Fingerprinter(
......
......@@ -57,8 +57,8 @@ Future<void> updateGeneratedXcodeProperties({
localsBuffer.writeln('SYMROOT=\${SOURCE_ROOT}/../${getIosBuildDirectory()}');
if (!project.isModule) {
// For module projects we do not want to write the FLUTTER_FRAMEWORK_DIR
if (!project.isApplication) {
// For application projects we do not want to write the FLUTTER_FRAMEWORK_DIR
// explicitly. Rather we rely on the xcode backend script and the Podfile
// logic to derive it from FLUTTER_ROOT and FLUTTER_BUILD_MODE.
localsBuffer.writeln('FLUTTER_FRAMEWORK_DIR=${flutterFrameworkDir(buildInfo.mode)}');
......
......@@ -248,7 +248,7 @@ Future<void> _writeIOSPluginRegistrant(FlutterProject project, List<Plugin> plug
};
final String registryDirectory = project.ios.pluginRegistrantHost.path;
if (project.isModule) {
if (project.isApplication) {
final String registryClassesDirectory = fs.path.join(registryDirectory, 'Classes');
_renderTemplateToFile(
_iosPluginRegistrantPodspecTemplate,
......@@ -297,7 +297,7 @@ Future<void> injectPlugins(FlutterProject project) async {
final List<Plugin> plugins = findPlugins(project);
await _writeAndroidPluginRegistrant(project, plugins);
await _writeIOSPluginRegistrant(project, plugins);
if (!project.isModule && project.ios.hostAppRoot.existsSync()) {
if (!project.isApplication && project.ios.hostAppRoot.existsSync()) {
final CocoaPods cocoaPods = CocoaPods();
if (plugins.isNotEmpty)
cocoaPods.setupPodfile(project.ios);
......
......@@ -110,8 +110,8 @@ class FlutterProject {
FlutterManifest.empty(),
);
/// True, if this project is a Flutter module.
bool get isModule => manifest.isModule;
/// True, if this project is a Flutter application.
bool get isApplication => manifest.isApplication;
/// True, if this project has an example application.
bool get hasExampleApp => _exampleDirectory(directory).existsSync();
......@@ -132,7 +132,7 @@ class FlutterProject {
}
/// Generates project files necessary to make Gradle builds work on Android
/// and CocoaPods+Xcode work on iOS, for app and module projects only.
/// and CocoaPods+Xcode work on iOS, for app and application projects only.
Future<void> ensureReadyForPlatformSpecificTooling() async {
if (!directory.existsSync() || hasExampleApp)
return;
......@@ -146,7 +146,7 @@ class FlutterProject {
/// Represents the iOS sub-project of a Flutter project.
///
/// Instances will reflect the contents of the `ios/` sub-folder of
/// Flutter applications and the `.ios/` sub-folder of Flutter modules.
/// Flutter applications and the `.ios/` sub-folder of Flutter applications.
class IosProject {
IosProject._(this.parent);
......@@ -162,7 +162,7 @@ class IosProject {
/// This parent folder of `Runner.xcodeproj`.
Directory get hostAppRoot {
if (!isModule || _editableDirectory.existsSync())
if (!isApplication || _editableDirectory.existsSync())
return _editableDirectory;
return _ephemeralDirectory;
}
......@@ -172,14 +172,14 @@ class IosProject {
/// during build.
///
/// This is the same as [hostAppRoot] except when the project is
/// a Flutter module with an editable host app.
Directory get _flutterLibRoot => isModule ? _ephemeralDirectory : _editableDirectory;
/// a Flutter application with an editable host app.
Directory get _flutterLibRoot => isApplication ? _ephemeralDirectory : _editableDirectory;
/// The bundle name of the host app, `Runner.app`.
String get hostAppBundleName => '$_hostAppBundleName.app';
/// True, if the parent Flutter project is a module.
bool get isModule => parent.isModule;
/// True, if the parent Flutter project is an application.
bool get isApplication => parent.isApplication;
/// The xcode config file for [mode].
File xcodeConfigFor(String mode) => _flutterLibRoot.childDirectory('Flutter').childFile('$mode.xcconfig');
......@@ -264,32 +264,32 @@ class IosProject {
}
void _regenerateFromTemplateIfNeeded() {
if (!isModule)
if (!isApplication)
return;
final bool pubspecChanged = isOlderThanReference(entity: _ephemeralDirectory, referenceFile: parent.pubspecFile);
final bool toolingChanged = Cache.instance.isOlderThanToolsStamp(_ephemeralDirectory);
if (!pubspecChanged && !toolingChanged)
return;
_deleteIfExistsSync(_ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'ios', 'library'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'ios', 'library'), _ephemeralDirectory);
// Add ephemeral host app, if a editable host app does not already exist.
if (!_editableDirectory.existsSync()) {
_overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'ios', 'host_app_ephemeral'), _ephemeralDirectory);
if (hasPlugins(parent)) {
_overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral_cocoapods'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'ios', 'host_app_ephemeral_cocoapods'), _ephemeralDirectory);
}
}
}
Future<void> makeHostAppEditable() async {
assert(isModule);
assert(isApplication);
if (_editableDirectory.existsSync())
throwToolExit('iOS host app is already editable. To start fresh, delete the ios/ folder.');
_deleteIfExistsSync(_ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'ios', 'library'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral'), _editableDirectory);
_overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral_cocoapods'), _editableDirectory);
_overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_editable_cocoapods'), _editableDirectory);
_overwriteFromTemplate(fs.path.join('application', 'ios', 'library'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'ios', 'host_app_ephemeral'), _editableDirectory);
_overwriteFromTemplate(fs.path.join('application', 'ios', 'host_app_ephemeral_cocoapods'), _editableDirectory);
_overwriteFromTemplate(fs.path.join('application', 'ios', 'host_app_editable_cocoapods'), _editableDirectory);
await _updateGeneratedXcodeConfigIfNeeded();
await injectPlugins(parent);
}
......@@ -297,7 +297,7 @@ class IosProject {
File get generatedXcodePropertiesFile => _flutterLibRoot.childDirectory('Flutter').childFile('Generated.xcconfig');
Directory get pluginRegistrantHost {
return isModule
return isApplication
? _flutterLibRoot.childDirectory('Flutter').childDirectory('FlutterPluginRegistrant')
: hostAppRoot.childDirectory(_hostAppBundleName);
}
......@@ -319,7 +319,7 @@ class IosProject {
/// Represents the Android sub-project of a Flutter project.
///
/// Instances will reflect the contents of the `android/` sub-folder of
/// Flutter applications and the `.android/` sub-folder of Flutter modules.
/// Flutter applications and the `.android/` sub-folder of Flutter applications.
class AndroidProject {
AndroidProject._(this.parent);
......@@ -333,21 +333,21 @@ class AndroidProject {
/// containing the `app/` subdirectory and the `settings.gradle` file that
/// includes it in the overall Gradle project.
Directory get hostAppGradleRoot {
if (!isModule || _editableHostAppDirectory.existsSync())
if (!isApplication || _editableHostAppDirectory.existsSync())
return _editableHostAppDirectory;
return _ephemeralDirectory;
}
/// The Gradle root directory of the Android wrapping of Flutter and plugins.
/// This is the same as [hostAppGradleRoot] except when the project is
/// a Flutter module with an editable host app.
Directory get _flutterLibGradleRoot => isModule ? _ephemeralDirectory : _editableHostAppDirectory;
/// a Flutter application with an editable host app.
Directory get _flutterLibGradleRoot => isApplication ? _ephemeralDirectory : _editableHostAppDirectory;
Directory get _ephemeralDirectory => parent.directory.childDirectory('.android');
Directory get _editableHostAppDirectory => parent.directory.childDirectory('android');
/// True, if the parent Flutter project is a module.
bool get isModule => parent.isModule;
/// True, if the parent Flutter project is an application.
bool get isApplication => parent.isApplication;
File get appManifestFile {
return isUsingGradle
......@@ -376,12 +376,12 @@ class AndroidProject {
}
Future<void> ensureReadyForPlatformSpecificTooling() async {
if (isModule && _shouldRegenerateFromTemplate()) {
if (isApplication && _shouldRegenerateFromTemplate()) {
_regenerateLibrary();
// Add ephemeral host app, if an editable host app does not already exist.
if (!_editableHostAppDirectory.existsSync()) {
_overwriteFromTemplate(fs.path.join('module', 'android', 'host_app_common'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'host_app_ephemeral'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'android', 'host_app_common'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'android', 'host_app_ephemeral'), _ephemeralDirectory);
}
}
if (!hostAppGradleRoot.existsSync()) {
......@@ -396,13 +396,13 @@ class AndroidProject {
}
Future<void> makeHostAppEditable() async {
assert(isModule);
assert(isApplication);
if (_editableHostAppDirectory.existsSync())
throwToolExit('Android host app is already editable. To start fresh, delete the android/ folder.');
_regenerateLibrary();
_overwriteFromTemplate(fs.path.join('module', 'android', 'host_app_common'), _editableHostAppDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'host_app_editable'), _editableHostAppDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), _editableHostAppDirectory);
_overwriteFromTemplate(fs.path.join('application', 'android', 'host_app_common'), _editableHostAppDirectory);
_overwriteFromTemplate(fs.path.join('application', 'android', 'host_app_editable'), _editableHostAppDirectory);
_overwriteFromTemplate(fs.path.join('application', 'android', 'gradle'), _editableHostAppDirectory);
gradle.injectGradleWrapper(_editableHostAppDirectory);
gradle.writeLocalProperties(_editableHostAppDirectory.childFile('local.properties'));
await injectPlugins(parent);
......@@ -410,12 +410,12 @@ class AndroidProject {
File get localPropertiesFile => _flutterLibGradleRoot.childFile('local.properties');
Directory get pluginRegistrantHost => _flutterLibGradleRoot.childDirectory(isModule ? 'Flutter' : 'app');
Directory get pluginRegistrantHost => _flutterLibGradleRoot.childDirectory(isApplication ? 'Flutter' : 'app');
void _regenerateLibrary() {
_deleteIfExistsSync(_ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'library'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'android', 'library'), _ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('application', 'android', 'gradle'), _ephemeralDirectory);
gradle.injectGradleWrapper(_ephemeralDirectory);
}
......
......@@ -43,7 +43,7 @@
}
}
},
"module": {
"application": {
"type": "object",
"additionalProperties": false,
"properties": {
......
......@@ -6,3 +6,5 @@
version:
revision: {{flutterRevision}}
channel: {{flutterChannel}}
project_type: app
// This is a basic Flutter widget test.
// To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter
// provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to
// find child widgets in the widget tree, read text, and verify that the values of widget properties
// are correct.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
......
......@@ -2,7 +2,7 @@
## common
Written to root of Flutter module.
Written to root of Flutter application.
Adds Dart project files including `pubspec.yaml`.
......
......@@ -19,9 +19,8 @@ import io.flutter.plugins.GeneratedPluginRegistrant;
/**
* Main entry point for using Flutter in Android applications.
*
* <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling. Do not edit.
* It may be moved into flutter.jar or another library dependency of the Flutter module project
* at a later point.</p>
* <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling.
* DO NOT EDIT.</p>
*/
public final class Flutter {
private Flutter() {
......
......@@ -13,9 +13,8 @@ import io.flutter.view.FlutterView;
/**
* A {@link Fragment} managing a {@link FlutterView}.
*
* <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling. Do not edit.
* It may be moved into flutter.jar or another library dependency of the Flutter module project
* at a later point.</p>
* <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling.
* DO NOT EDIT.</p>
*/
public class FlutterFragment extends Fragment {
public static final String ARG_ROUTE = "route";
......
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: {{flutterRevision}}
channel: {{flutterChannel}}
project_type: application
This diff is collapsed.
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