Commit 251d83a4 authored by Mikkel Nygaard Ravn's avatar Mikkel Nygaard Ravn Committed by GitHub

Add org option to flutter create (#10290)

parent 6762e869
...@@ -39,12 +39,18 @@ class CreateCommand extends FlutterCommand { ...@@ -39,12 +39,18 @@ class CreateCommand extends FlutterCommand {
'plugin', 'plugin',
negatable: true, negatable: true,
defaultsTo: false, defaultsTo: false,
help: 'Generate a new Flutter Plugin project.' help: 'Generate a Flutter plugin project.'
); );
argParser.addOption( argParser.addOption(
'description', 'description',
defaultsTo: 'A new flutter project.', defaultsTo: 'A new Flutter project.',
help: 'The description to use for your new flutter project. This string ends up in the pubspec.yaml file.' help: 'The description to use for your new Flutter project. This string ends up in the pubspec.yaml file.'
);
argParser.addOption(
'org',
defaultsTo: 'com.yourcompany',
help: 'The organization responsible for your new Flutter project, in reverse domain name notation.\n'
'This string is used in Java package names and as prefix in the iOS bundle identifier.'
); );
argParser.addOption( argParser.addOption(
'ios-language', 'ios-language',
...@@ -110,6 +116,7 @@ class CreateCommand extends FlutterCommand { ...@@ -110,6 +116,7 @@ class CreateCommand extends FlutterCommand {
// TODO(goderbauer): Work-around for: https://github.com/dart-lang/path/issues/24 // TODO(goderbauer): Work-around for: https://github.com/dart-lang/path/issues/24
if (fs.path.basename(dirPath) == '.') if (fs.path.basename(dirPath) == '.')
dirPath = fs.path.dirname(dirPath); dirPath = fs.path.dirname(dirPath);
final String organization = argResults['org'];
final String projectName = fs.path.basename(dirPath); final String projectName = fs.path.basename(dirPath);
String error =_validateProjectDir(dirPath, flutterRoot: flutterRoot); String error =_validateProjectDir(dirPath, flutterRoot: flutterRoot);
...@@ -121,6 +128,7 @@ class CreateCommand extends FlutterCommand { ...@@ -121,6 +128,7 @@ class CreateCommand extends FlutterCommand {
throwToolExit(error); throwToolExit(error);
final Map<String, dynamic> templateContext = _templateContext( final Map<String, dynamic> templateContext = _templateContext(
organization: organization,
projectName: projectName, projectName: projectName,
projectDescription: argResults['description'], projectDescription: argResults['description'],
dirPath: dirPath, dirPath: dirPath,
...@@ -151,8 +159,8 @@ class CreateCommand extends FlutterCommand { ...@@ -151,8 +159,8 @@ class CreateCommand extends FlutterCommand {
final String androidPluginIdentifier = templateContext['androidIdentifier']; final String androidPluginIdentifier = templateContext['androidIdentifier'];
final String exampleProjectName = projectName + '_example'; final String exampleProjectName = projectName + '_example';
templateContext['projectName'] = exampleProjectName; templateContext['projectName'] = exampleProjectName;
templateContext['androidIdentifier'] = _createAndroidIdentifier(exampleProjectName); templateContext['androidIdentifier'] = _createAndroidIdentifier(organization, exampleProjectName);
templateContext['iosIdentifier'] = _createUTIIdentifier(exampleProjectName); templateContext['iosIdentifier'] = _createUTIIdentifier(organization, exampleProjectName);
templateContext['description'] = 'Demonstrates how to use the $projectName plugin.'; templateContext['description'] = 'Demonstrates how to use the $projectName plugin.';
templateContext['pluginProjectName'] = projectName; templateContext['pluginProjectName'] = projectName;
templateContext['androidPluginIdentifier'] = androidPluginIdentifier; templateContext['androidPluginIdentifier'] = androidPluginIdentifier;
...@@ -224,6 +232,7 @@ To edit platform code in an IDE see https://flutter.io/platform-plugins/#edit-co ...@@ -224,6 +232,7 @@ To edit platform code in an IDE see https://flutter.io/platform-plugins/#edit-co
} }
Map<String, dynamic> _templateContext({ Map<String, dynamic> _templateContext({
String organization,
String projectName, String projectName,
String projectDescription, String projectDescription,
String androidLanguage, String androidLanguage,
...@@ -241,9 +250,10 @@ To edit platform code in an IDE see https://flutter.io/platform-plugins/#edit-co ...@@ -241,9 +250,10 @@ To edit platform code in an IDE see https://flutter.io/platform-plugins/#edit-co
: pluginDartClass + 'Plugin'; : pluginDartClass + 'Plugin';
return <String, dynamic>{ return <String, dynamic>{
'organization': organization,
'projectName': projectName, 'projectName': projectName,
'androidIdentifier': _createAndroidIdentifier(projectName), 'androidIdentifier': _createAndroidIdentifier(organization, projectName),
'iosIdentifier': _createUTIIdentifier(projectName), 'iosIdentifier': _createUTIIdentifier(organization, projectName),
'description': projectDescription, 'description': projectDescription,
'dartSdk': '$flutterRoot/bin/cache/dart-sdk', 'dartSdk': '$flutterRoot/bin/cache/dart-sdk',
'androidMinApiLevel': android.minApiLevel, 'androidMinApiLevel': android.minApiLevel,
...@@ -264,8 +274,8 @@ To edit platform code in an IDE see https://flutter.io/platform-plugins/#edit-co ...@@ -264,8 +274,8 @@ To edit platform code in an IDE see https://flutter.io/platform-plugins/#edit-co
} }
} }
String _createAndroidIdentifier(String name) { String _createAndroidIdentifier(String organization, String name) {
return 'com.yourcompany.$name'; return '$organization.$name';
} }
String _createPluginClassName(String name) { String _createPluginClassName(String name) {
...@@ -273,12 +283,12 @@ String _createPluginClassName(String name) { ...@@ -273,12 +283,12 @@ String _createPluginClassName(String name) {
return camelizedName[0].toUpperCase() + camelizedName.substring(1); return camelizedName[0].toUpperCase() + camelizedName.substring(1);
} }
String _createUTIIdentifier(String name) { String _createUTIIdentifier(String organization, String name) {
// Create a UTI (https://en.wikipedia.org/wiki/Uniform_Type_Identifier) from a base name // Create a UTI (https://en.wikipedia.org/wiki/Uniform_Type_Identifier) from a base name
final RegExp disallowed = new RegExp(r"[^a-zA-Z0-9\-\.\u0080-\uffff]+"); final RegExp disallowed = new RegExp(r"[^a-zA-Z0-9\-\.\u0080-\uffff]+");
name = camelCase(name).replaceAll(disallowed, ''); name = camelCase(name).replaceAll(disallowed, '');
name = name.isEmpty ? 'untitled' : name; name = name.isEmpty ? 'untitled' : name;
return 'com.yourcompany.$name'; return '$organization.$name';
} }
final Set<String> _packageDependencies = new Set<String>.from(<String>[ final Set<String> _packageDependencies = new Set<String>.from(<String>[
......
...@@ -84,13 +84,20 @@ class Template { ...@@ -84,13 +84,20 @@ class Template {
return null; return null;
relativeDestinationPath = relativeDestinationPath.replaceAll('$platform-$language.tmpl', platform); relativeDestinationPath = relativeDestinationPath.replaceAll('$platform-$language.tmpl', platform);
} }
final String organization = context['organization'];
final String projectName = context['projectName']; final String projectName = context['projectName'];
final String pluginClass = context['pluginClass']; final String pluginClass = context['pluginClass'];
final String destinationDirPath = destination.absolute.path; final String destinationDirPath = destination.absolute.path;
final String pathSeparator = fs.path.separator;
String finalDestinationPath = fs.path String finalDestinationPath = fs.path
.join(destinationDirPath, relativeDestinationPath) .join(destinationDirPath, relativeDestinationPath)
.replaceAll(_kCopyTemplateExtension, '') .replaceAll(_kCopyTemplateExtension, '')
.replaceAll(_kTemplateExtension, ''); .replaceAll(_kTemplateExtension, '')
.replaceAll(
'${pathSeparator}organization$pathSeparator',
'$pathSeparator${organization.replaceAll('.', pathSeparator)}$pathSeparator',
);
if (projectName != null) if (projectName != null)
finalDestinationPath = finalDestinationPath.replaceAll('projectName', projectName); finalDestinationPath = finalDestinationPath.replaceAll('projectName', projectName);
if (pluginClass != null) if (pluginClass != null)
......
...@@ -52,7 +52,7 @@ void main() { ...@@ -52,7 +52,7 @@ void main() {
testUsingContext('kotlin/swift project', () async { testUsingContext('kotlin/swift project', () async {
return _createAndAnalyzeProject( return _createAndAnalyzeProject(
projectDir, projectDir,
<String>['--android-language', 'kotlin', '--ios-language', 'swift'], <String>['--android-language', 'kotlin', '-i', 'swift'],
<String>[ <String>[
'android/app/src/main/kotlin/com/yourcompany/flutter_project/MainActivity.kt', 'android/app/src/main/kotlin/com/yourcompany/flutter_project/MainActivity.kt',
'ios/Runner/AppDelegate.swift', 'ios/Runner/AppDelegate.swift',
...@@ -89,7 +89,7 @@ void main() { ...@@ -89,7 +89,7 @@ void main() {
testUsingContext('kotlin/swift plugin project', () async { testUsingContext('kotlin/swift plugin project', () async {
return _createAndAnalyzeProject( return _createAndAnalyzeProject(
projectDir, projectDir,
<String>['--plugin', '--android-language', 'kotlin', '--ios-language', 'swift'], <String>['--plugin', '-a', 'kotlin', '--ios-language', 'swift'],
<String>[ <String>[
'android/src/main/kotlin/com/yourcompany/flutter_project/FlutterProjectPlugin.kt', 'android/src/main/kotlin/com/yourcompany/flutter_project/FlutterProjectPlugin.kt',
'ios/Classes/FlutterProjectPlugin.h', 'ios/Classes/FlutterProjectPlugin.h',
...@@ -111,6 +111,21 @@ void main() { ...@@ -111,6 +111,21 @@ void main() {
); );
}); });
testUsingContext('plugin project with custom org', () async {
return _createAndAnalyzeProject(
projectDir,
<String>['--plugin', '--org', 'com.bar.foo'],
<String>[
'android/src/main/java/com/bar/foo/flutter_project/FlutterProjectPlugin.java',
'example/android/app/src/main/java/com/bar/foo/flutter_project_example/MainActivity.java',
],
<String>[
'android/src/main/java/com/yourcompany/flutter_project/FlutterProjectPlugin.java',
'example/android/app/src/main/java/com/yourcompany/flutter_project_example/MainActivity.java',
],
);
});
testUsingContext('project with-driver-test', () async { testUsingContext('project with-driver-test', () async {
return _createAndAnalyzeProject( return _createAndAnalyzeProject(
projectDir, projectDir,
...@@ -126,7 +141,7 @@ void main() { ...@@ -126,7 +141,7 @@ void main() {
final CreateCommand command = new CreateCommand(); final CreateCommand command = new CreateCommand();
final CommandRunner<Null> runner = createTestCommandRunner(command); final CommandRunner<Null> runner = createTestCommandRunner(command);
await runner.run(<String>['create', '--no-pub', projectDir.path]); await runner.run(<String>['create', '--no-pub', '--org', 'com.foo.bar', projectDir.path]);
void expectExists(String relPath) { void expectExists(String relPath) {
expect(fs.isFileSync('${projectDir.path}/$relPath'), true); expect(fs.isFileSync('${projectDir.path}/$relPath'), true);
...@@ -156,6 +171,12 @@ void main() { ...@@ -156,6 +171,12 @@ void main() {
expect(xcodeConfig, contains('FLUTTER_ROOT=')); expect(xcodeConfig, contains('FLUTTER_ROOT='));
expect(xcodeConfig, contains('FLUTTER_APPLICATION_PATH=')); expect(xcodeConfig, contains('FLUTTER_APPLICATION_PATH='));
expect(xcodeConfig, contains('FLUTTER_FRAMEWORK_DIR=')); expect(xcodeConfig, contains('FLUTTER_FRAMEWORK_DIR='));
// App identification
final String xcodeProjectPath = fs.path.join('ios', 'Runner.xcodeproj', 'project.pbxproj');
expectExists(xcodeProjectPath);
final File xcodeProjectFile = fs.file(fs.path.join(projectDir.path, xcodeProjectPath));
final String xcodeProject = xcodeProjectFile.readAsStringSync();
expect(xcodeProject, contains('PRODUCT_BUNDLE_IDENTIFIER = com.foo.bar.flutterProject'));
}); });
// Verify that we can regenerate over an existing project. // Verify that we can regenerate over an existing project.
......
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