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 {
'plugin',
negatable: true,
defaultsTo: false,
help: 'Generate a new Flutter Plugin project.'
help: 'Generate a Flutter plugin project.'
);
argParser.addOption(
'description',
defaultsTo: 'A new flutter project.',
help: 'The description to use for your new flutter project. This string ends up in the pubspec.yaml file.'
defaultsTo: 'A new Flutter project.',
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(
'ios-language',
......@@ -110,6 +116,7 @@ class CreateCommand extends FlutterCommand {
// TODO(goderbauer): Work-around for: https://github.com/dart-lang/path/issues/24
if (fs.path.basename(dirPath) == '.')
dirPath = fs.path.dirname(dirPath);
final String organization = argResults['org'];
final String projectName = fs.path.basename(dirPath);
String error =_validateProjectDir(dirPath, flutterRoot: flutterRoot);
......@@ -121,6 +128,7 @@ class CreateCommand extends FlutterCommand {
throwToolExit(error);
final Map<String, dynamic> templateContext = _templateContext(
organization: organization,
projectName: projectName,
projectDescription: argResults['description'],
dirPath: dirPath,
......@@ -151,8 +159,8 @@ class CreateCommand extends FlutterCommand {
final String androidPluginIdentifier = templateContext['androidIdentifier'];
final String exampleProjectName = projectName + '_example';
templateContext['projectName'] = exampleProjectName;
templateContext['androidIdentifier'] = _createAndroidIdentifier(exampleProjectName);
templateContext['iosIdentifier'] = _createUTIIdentifier(exampleProjectName);
templateContext['androidIdentifier'] = _createAndroidIdentifier(organization, exampleProjectName);
templateContext['iosIdentifier'] = _createUTIIdentifier(organization, exampleProjectName);
templateContext['description'] = 'Demonstrates how to use the $projectName plugin.';
templateContext['pluginProjectName'] = projectName;
templateContext['androidPluginIdentifier'] = androidPluginIdentifier;
......@@ -224,6 +232,7 @@ To edit platform code in an IDE see https://flutter.io/platform-plugins/#edit-co
}
Map<String, dynamic> _templateContext({
String organization,
String projectName,
String projectDescription,
String androidLanguage,
......@@ -241,9 +250,10 @@ To edit platform code in an IDE see https://flutter.io/platform-plugins/#edit-co
: pluginDartClass + 'Plugin';
return <String, dynamic>{
'organization': organization,
'projectName': projectName,
'androidIdentifier': _createAndroidIdentifier(projectName),
'iosIdentifier': _createUTIIdentifier(projectName),
'androidIdentifier': _createAndroidIdentifier(organization, projectName),
'iosIdentifier': _createUTIIdentifier(organization, projectName),
'description': projectDescription,
'dartSdk': '$flutterRoot/bin/cache/dart-sdk',
'androidMinApiLevel': android.minApiLevel,
......@@ -264,8 +274,8 @@ To edit platform code in an IDE see https://flutter.io/platform-plugins/#edit-co
}
}
String _createAndroidIdentifier(String name) {
return 'com.yourcompany.$name';
String _createAndroidIdentifier(String organization, String name) {
return '$organization.$name';
}
String _createPluginClassName(String name) {
......@@ -273,12 +283,12 @@ String _createPluginClassName(String name) {
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
final RegExp disallowed = new RegExp(r"[^a-zA-Z0-9\-\.\u0080-\uffff]+");
name = camelCase(name).replaceAll(disallowed, '');
name = name.isEmpty ? 'untitled' : name;
return 'com.yourcompany.$name';
return '$organization.$name';
}
final Set<String> _packageDependencies = new Set<String>.from(<String>[
......
......@@ -84,13 +84,20 @@ class Template {
return null;
relativeDestinationPath = relativeDestinationPath.replaceAll('$platform-$language.tmpl', platform);
}
final String organization = context['organization'];
final String projectName = context['projectName'];
final String pluginClass = context['pluginClass'];
final String destinationDirPath = destination.absolute.path;
final String pathSeparator = fs.path.separator;
String finalDestinationPath = fs.path
.join(destinationDirPath, relativeDestinationPath)
.replaceAll(_kCopyTemplateExtension, '')
.replaceAll(_kTemplateExtension, '');
.replaceAll(_kTemplateExtension, '')
.replaceAll(
'${pathSeparator}organization$pathSeparator',
'$pathSeparator${organization.replaceAll('.', pathSeparator)}$pathSeparator',
);
if (projectName != null)
finalDestinationPath = finalDestinationPath.replaceAll('projectName', projectName);
if (pluginClass != null)
......
......@@ -52,7 +52,7 @@ void main() {
testUsingContext('kotlin/swift project', () async {
return _createAndAnalyzeProject(
projectDir,
<String>['--android-language', 'kotlin', '--ios-language', 'swift'],
<String>['--android-language', 'kotlin', '-i', 'swift'],
<String>[
'android/app/src/main/kotlin/com/yourcompany/flutter_project/MainActivity.kt',
'ios/Runner/AppDelegate.swift',
......@@ -89,7 +89,7 @@ void main() {
testUsingContext('kotlin/swift plugin project', () async {
return _createAndAnalyzeProject(
projectDir,
<String>['--plugin', '--android-language', 'kotlin', '--ios-language', 'swift'],
<String>['--plugin', '-a', 'kotlin', '--ios-language', 'swift'],
<String>[
'android/src/main/kotlin/com/yourcompany/flutter_project/FlutterProjectPlugin.kt',
'ios/Classes/FlutterProjectPlugin.h',
......@@ -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 {
return _createAndAnalyzeProject(
projectDir,
......@@ -126,7 +141,7 @@ void main() {
final CreateCommand command = new CreateCommand();
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) {
expect(fs.isFileSync('${projectDir.path}/$relPath'), true);
......@@ -156,6 +171,12 @@ void main() {
expect(xcodeConfig, contains('FLUTTER_ROOT='));
expect(xcodeConfig, contains('FLUTTER_APPLICATION_PATH='));
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.
......
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