Change default organization name to example.com in project templates (#14116)
diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart
index 142ec29..b4e3283 100644
--- a/packages/flutter_tools/lib/src/commands/create.dart
+++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -22,6 +22,7 @@
import '../globals.dart';
import '../ios/xcodeproj.dart';
import '../plugins.dart';
+import '../project.dart';
import '../runner/flutter_command.dart';
import '../template.dart';
import '../version.dart';
@@ -65,7 +66,7 @@
);
argParser.addOption(
'org',
- defaultsTo: 'com.yourcompany',
+ defaultsTo: 'com.example',
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.'
);
@@ -135,7 +136,18 @@
// 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'];
+ String organization = argResults['org'];
+ if (!argResults.wasParsed('org')) {
+ final Set<String> existingOrganizations = await new FlutterProject(projectDir).organizationNames();
+ if (existingOrganizations.length == 1) {
+ organization = existingOrganizations.first;
+ } else if (1 < existingOrganizations.length) {
+ throwToolExit(
+ 'Ambiguous organization in existing files: $existingOrganizations.\n'
+ 'The --org command line argument must be specified to recreate project.'
+ );
+ }
+ }
final String projectName = fs.path.basename(dirPath);
String error =_validateProjectDir(dirPath, flutterRoot: flutterRoot);
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index 14fa5a3..a5639f7 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -393,10 +393,10 @@
}
if (result.xcodeBuildExecution != null &&
result.xcodeBuildExecution.buildForPhysicalDevice &&
- app.id?.contains('com.yourcompany') ?? false) {
+ app.id?.contains('com.example') ?? false) {
printError('');
printError('It appears that your application still contains the default signing identifier.');
- printError("Try replacing 'com.yourcompany' with your signing id in Xcode:");
+ printError("Try replacing 'com.example' with your signing id in Xcode:");
printError(' open ios/Runner.xcworkspace');
return;
}
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart
new file mode 100644
index 0000000..1fd1b4f
--- /dev/null
+++ b/packages/flutter_tools/lib/src/project.dart
@@ -0,0 +1,96 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'base/file_system.dart';
+
+/// Represents the contents of a Flutter project at the specified [directory].
+class FlutterProject {
+ FlutterProject(this.directory);
+
+ /// The location of this project.
+ final Directory directory;
+
+ /// Asynchronously returns the organization names found in this project as
+ /// part of iOS product bundle identifier, Android application ID, or
+ /// Gradle group ID.
+ Future<Set<String>> organizationNames() async {
+ final List<String> candidates = await Future.wait(<Future<String>>[
+ ios.productBundleIdentifier(),
+ android.applicationId(),
+ android.group(),
+ example.android.applicationId(),
+ example.ios.productBundleIdentifier(),
+ ]);
+ return new Set<String>.from(
+ candidates.map(_organizationNameFromPackageName)
+ .where((String name) => name != null)
+ );
+ }
+
+ String _organizationNameFromPackageName(String packageName) {
+ if (packageName != null && 0 <= packageName.lastIndexOf('.'))
+ return packageName.substring(0, packageName.lastIndexOf('.'));
+ else
+ return null;
+ }
+
+ /// The iOS sub project of this project.
+ IosProject get ios => new IosProject(directory.childDirectory('ios'));
+
+ /// The Android sub project of this project.
+ AndroidProject get android => new AndroidProject(directory.childDirectory('android'));
+
+ /// The example sub project of this (plugin) project.
+ FlutterProject get example => new FlutterProject(directory.childDirectory('example'));
+}
+
+/// Represents the contents of the ios/ folder of a Flutter project.
+class IosProject {
+ static final RegExp _productBundleIdPattern = new RegExp(r'^\s*PRODUCT_BUNDLE_IDENTIFIER\s*=\s*(.*);\s*$');
+ IosProject(this.directory);
+
+ final Directory directory;
+
+ Future<String> productBundleIdentifier() {
+ final File projectFile = directory.childDirectory('Runner.xcodeproj').childFile('project.pbxproj');
+ return _firstMatchInFile(projectFile, _productBundleIdPattern).then((Match match) => match?.group(1));
+ }
+}
+
+/// Represents the contents of the android/ folder of a Flutter project.
+class AndroidProject {
+ static final RegExp _applicationIdPattern = new RegExp('^\\s*applicationId\\s+[\'\"](.*)[\'\"]\\s*\$');
+ static final RegExp _groupPattern = new RegExp('^\\s*group\\s+[\'\"](.*)[\'\"]\\s*\$');
+
+ AndroidProject(this.directory);
+
+ final Directory directory;
+
+ Future<String> applicationId() {
+ final File gradleFile = directory.childDirectory('app').childFile('build.gradle');
+ return _firstMatchInFile(gradleFile, _applicationIdPattern).then((Match match) => match?.group(1));
+ }
+
+ Future<String> group() {
+ final File gradleFile = directory.childFile('build.gradle');
+ return _firstMatchInFile(gradleFile, _groupPattern).then((Match match) => match?.group(1));
+ }
+}
+
+/// Asynchronously returns the first line-based match for [regExp] in [file].
+///
+/// Assumes UTF8 encoding.
+Future<Match> _firstMatchInFile(File file, RegExp regExp) async {
+ if (!await file.exists()) {
+ return null;
+ }
+ return file
+ .openRead()
+ .transform(UTF8.decoder)
+ .transform(const LineSplitter())
+ .map(regExp.firstMatch)
+ .firstWhere((Match match) => match != null, defaultValue: () => null);
+}