Unverified Commit 43532970 authored by Mikkel Nygaard Ravn's avatar Mikkel Nygaard Ravn Committed by GitHub

Change default organization name to example.com in project templates (#14116)

parent 0efc06ca
......@@ -22,6 +22,7 @@ import '../flx.dart' as flx;
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 @@ class CreateCommand extends FlutterCommand {
);
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 @@ 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'];
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);
......
......@@ -393,10 +393,10 @@ Future<Null> diagnoseXcodeBuildFailure(
}
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;
}
......
// 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);
}
......@@ -62,7 +62,7 @@ final Map<String, String> _swiftBuildSettings = <String, String>{
'CLANG_ENABLE_MODULES': 'YES',
'ENABLE_BITCODE': 'NO',
'INFOPLIST_FILE': 'Runner/Info.plist',
'PRODUCT_BUNDLE_IDENTIFIER': 'com.yourcompany.test',
'PRODUCT_BUNDLE_IDENTIFIER': 'com.example.test',
'PRODUCT_NAME': 'blah',
'SWIFT_OBJC_BRIDGING_HEADER': 'Runner/Runner-Bridging-Header.h',
'SWIFT_OPTIMIZATION_LEVEL': '-Onone',
......
......@@ -11,6 +11,7 @@ import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/create.dart';
import 'package:flutter_tools/src/dart/sdk.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/version.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
......@@ -50,7 +51,7 @@ void main() {
projectDir,
<String>[],
<String>[
'android/app/src/main/java/com/yourcompany/flutterproject/MainActivity.java',
'android/app/src/main/java/com/example/flutterproject/MainActivity.java',
'ios/Runner/AppDelegate.h',
'ios/Runner/AppDelegate.m',
'ios/Runner/main.m',
......@@ -67,13 +68,13 @@ void main() {
projectDir,
<String>['--no-pub', '--android-language', 'kotlin', '-i', 'swift'],
<String>[
'android/app/src/main/kotlin/com/yourcompany/flutterproject/MainActivity.kt',
'android/app/src/main/kotlin/com/example/flutterproject/MainActivity.kt',
'ios/Runner/AppDelegate.swift',
'ios/Runner/Runner-Bridging-Header.h',
'lib/main.dart',
],
unexpectedPaths: <String>[
'android/app/src/main/java/com/yourcompany/flutterproject/MainActivity.java',
'android/app/src/main/java/com/example/flutterproject/MainActivity.java',
'ios/Runner/AppDelegate.h',
'ios/Runner/AppDelegate.m',
'ios/Runner/main.m',
......@@ -90,15 +91,15 @@ void main() {
'test/flutter_project_test.dart',
],
unexpectedPaths: <String>[
'android/app/src/main/java/com/yourcompany/flutterproject/MainActivity.java',
'android/src/main/java/com/yourcompany/flutterproject/FlutterProjectPlugin.java',
'android/app/src/main/java/com/example/flutterproject/MainActivity.java',
'android/src/main/java/com/example/flutterproject/FlutterProjectPlugin.java',
'ios/Classes/FlutterProjectPlugin.h',
'ios/Classes/FlutterProjectPlugin.m',
'ios/Runner/AppDelegate.h',
'ios/Runner/AppDelegate.m',
'ios/Runner/main.m',
'lib/main.dart',
'example/android/app/src/main/java/com/yourcompany/flutterprojectexample/MainActivity.java',
'example/android/app/src/main/java/com/example/flutterprojectexample/MainActivity.java',
'example/ios/Runner/AppDelegate.h',
'example/ios/Runner/AppDelegate.m',
'example/ios/Runner/main.m',
......@@ -114,11 +115,11 @@ void main() {
projectDir,
<String>['--template=plugin'],
<String>[
'android/src/main/java/com/yourcompany/flutterproject/FlutterProjectPlugin.java',
'android/src/main/java/com/example/flutterproject/FlutterProjectPlugin.java',
'ios/Classes/FlutterProjectPlugin.h',
'ios/Classes/FlutterProjectPlugin.m',
'lib/flutter_project.dart',
'example/android/app/src/main/java/com/yourcompany/flutterprojectexample/MainActivity.java',
'example/android/app/src/main/java/com/example/flutterprojectexample/MainActivity.java',
'example/ios/Runner/AppDelegate.h',
'example/ios/Runner/AppDelegate.m',
'example/ios/Runner/main.m',
......@@ -135,19 +136,19 @@ void main() {
projectDir,
<String>['--no-pub', '--template=plugin', '-a', 'kotlin', '--ios-language', 'swift'],
<String>[
'android/src/main/kotlin/com/yourcompany/flutterproject/FlutterProjectPlugin.kt',
'android/src/main/kotlin/com/example/flutterproject/FlutterProjectPlugin.kt',
'ios/Classes/FlutterProjectPlugin.h',
'ios/Classes/FlutterProjectPlugin.m',
'ios/Classes/SwiftFlutterProjectPlugin.swift',
'lib/flutter_project.dart',
'example/android/app/src/main/kotlin/com/yourcompany/flutterprojectexample/MainActivity.kt',
'example/android/app/src/main/kotlin/com/example/flutterprojectexample/MainActivity.kt',
'example/ios/Runner/AppDelegate.swift',
'example/ios/Runner/Runner-Bridging-Header.h',
'example/lib/main.dart',
],
unexpectedPaths: <String>[
'android/src/main/java/com/yourcompany/flutterproject/FlutterProjectPlugin.java',
'example/android/app/src/main/java/com/yourcompany/flutterprojectexample/MainActivity.java',
'android/src/main/java/com/example/flutterproject/FlutterProjectPlugin.java',
'example/android/app/src/main/java/com/example/flutterprojectexample/MainActivity.java',
'example/ios/Runner/AppDelegate.h',
'example/ios/Runner/AppDelegate.m',
'example/ios/Runner/main.m',
......@@ -158,17 +159,17 @@ void main() {
testUsingContext('plugin project with custom org', () async {
return _createProject(
projectDir,
<String>['--no-pub', '--template=plugin', '--org', 'com.bar.foo'],
<String>[
'android/src/main/java/com/bar/foo/flutterproject/FlutterProjectPlugin.java',
'example/android/app/src/main/java/com/bar/foo/flutterprojectexample/MainActivity.java',
],
unexpectedPaths: <String>[
'android/src/main/java/com/yourcompany/flutterproject/FlutterProjectPlugin.java',
'example/android/app/src/main/java/com/yourcompany/flutterprojectexample/MainActivity.java',
],
plugin: true,
projectDir,
<String>['--no-pub', '--template=plugin', '--org', 'com.bar.foo'],
<String>[
'android/src/main/java/com/bar/foo/flutterproject/FlutterProjectPlugin.java',
'example/android/app/src/main/java/com/bar/foo/flutterprojectexample/MainActivity.java',
],
unexpectedPaths: <String>[
'android/src/main/java/com/example/flutterproject/FlutterProjectPlugin.java',
'example/android/app/src/main/java/com/example/flutterprojectexample/MainActivity.java',
],
plugin: true,
);
}, timeout: allowForCreateFlutterProject);
......@@ -252,6 +253,83 @@ void main() {
await runner.run(<String>['create', '--no-pub', projectDir.path]);
}, timeout: allowForCreateFlutterProject);
testUsingContext('can re-gen android/ folder, reusing custom org', () async {
await _createProject(
projectDir,
<String>['--no-pub', '--org', 'com.bar.foo'],
<String>[],
);
projectDir.childDirectory('android').deleteSync(recursive: true);
return _createProject(
projectDir,
<String>['--no-pub'],
<String>[
'android/app/src/main/java/com/bar/foo/flutterproject/MainActivity.java',
],
unexpectedPaths: <String>[
'android/app/src/main/java/com/example/flutterproject/MainActivity.java',
],
);
}, timeout: allowForCreateFlutterProject);
testUsingContext('can re-gen ios/ folder, reusing custom org', () async {
await _createProject(
projectDir,
<String>['--no-pub', '--org', 'com.bar.foo'],
<String>[],
);
projectDir.childDirectory('ios').deleteSync(recursive: true);
await _createProject(projectDir, <String>['--no-pub'], <String>[]);
expect(
await new FlutterProject(projectDir).ios.productBundleIdentifier(),
'com.bar.foo.flutterProject',
);
}, timeout: allowForCreateFlutterProject);
testUsingContext('can re-gen plugin ios/ and example/ folders, reusing custom org', () async {
await _createProject(
projectDir,
<String>['--no-pub', '-t', 'plugin', '--org', 'com.bar.foo'],
<String>[],
);
projectDir.childDirectory('example').deleteSync(recursive: true);
projectDir.childDirectory('ios').deleteSync(recursive: true);
await _createProject(
projectDir,
<String>['--no-pub', '-t', 'plugin'],
<String>[
'example/android/app/src/main/java/com/bar/foo/flutterprojectexample/MainActivity.java',
'ios/Classes/FlutterProjectPlugin.h',
],
unexpectedPaths: <String>[
'example/android/app/src/main/java/com/example/flutterprojectexample/MainActivity.java',
'android/src/main/java/com/example/flutterproject/FlutterProjectPlugin.java',
],
);
expect(
await new FlutterProject(projectDir).example.ios.productBundleIdentifier(),
'com.bar.foo.flutterProjectExample',
);
}, timeout: allowForCreateFlutterProject);
testUsingContext('fails to re-gen without specified org when org is ambiguous', () async {
await _createProject(
projectDir,
<String>['--no-pub', '--org', 'com.bar.foo'],
<String>[],
);
fs.directory(fs.path.join(projectDir.path, 'ios')).deleteSync(recursive: true);
await _createProject(
projectDir,
<String>['--no-pub', '--org', 'com.bar.baz'],
<String>[],
);
expect(
() => _createProject(projectDir, <String>[], <String>[]),
throwsToolExit(message: 'Ambiguous organization'),
);
}, timeout: allowForCreateFlutterProject);
// Verify that we help the user correct an option ordering issue
testUsingContext('produces sensible error message', () async {
Cache.flutterRoot = '../..';
......
......@@ -282,7 +282,7 @@ Xcode's output:
=== CLEAN TARGET Runner OF PROJECT Runner WITH CONFIGURATION Release ===
Check dependencies
[BCEROR]No profiles for 'com.yourcompany.test' were found: Xcode couldn't find a provisioning profile matching 'com.yourcompany.test'.
[BCEROR]No profiles for 'com.example.test' were found: Xcode couldn't find a provisioning profile matching 'com.example.test'.
[BCEROR]Code signing is required for product type 'Application' in SDK 'iOS 10.3'
[BCEROR]Code signing is required for product type 'Application' in SDK 'iOS 10.3'
[BCEROR]Code signing is required for product type 'Application' in SDK 'iOS 10.3'
......@@ -304,7 +304,7 @@ Xcode's output:
=== BUILD TARGET Runner OF PROJECT Runner WITH CONFIGURATION Release ===
Check dependencies
No profiles for 'com.yourcompany.test' were found: Xcode couldn't find a provisioning profile matching 'com.yourcompany.test'.
No profiles for 'com.example.test' were found: Xcode couldn't find a provisioning profile matching 'com.example.test'.
Code signing is required for product type 'Application' in SDK 'iOS 10.3'
Code signing is required for product type 'Application' in SDK 'iOS 10.3'
Code signing is required for product type 'Application' in SDK 'iOS 10.3'
......
// 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 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:test/test.dart';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'src/context.dart';
void main() {
group('Project', () {
testInMemory('knows location', () {
final Directory directory = fs.directory('myproject');
expect(new FlutterProject(directory).directory, directory);
});
group('organization names set', () {
testInMemory('is empty, if project not created', () async {
final FlutterProject project = someProject();
expect(await project.organizationNames(), isEmpty);
});
testInMemory('is empty, if no platform folders exist', () async {
final FlutterProject project = someProject();
project.directory.createSync();
expect(await project.organizationNames(), isEmpty);
});
testInMemory('is populated from iOS bundle identifier', () async {
final FlutterProject project = someProject();
addIosWithBundleId(project.directory, 'io.flutter.someProject');
expect(await project.organizationNames(), <String>['io.flutter']);
});
testInMemory('is populated from Android application ID', () async {
final FlutterProject project = someProject();
addAndroidWithApplicationId(project.directory, 'io.flutter.someproject');
expect(await project.organizationNames(), <String>['io.flutter']);
});
testInMemory('is populated from iOS bundle identifier in plugin example', () async {
final FlutterProject project = someProject();
addIosWithBundleId(project.example.directory, 'io.flutter.someProject');
expect(await project.organizationNames(), <String>['io.flutter']);
});
testInMemory('is populated from Android application ID in plugin example', () async {
final FlutterProject project = someProject();
addAndroidWithApplicationId(project.example.directory, 'io.flutter.someproject');
expect(await project.organizationNames(), <String>['io.flutter']);
});
testInMemory('is populated from Android group in plugin', () async {
final FlutterProject project = someProject();
addAndroidWithGroup(project.directory, 'io.flutter.someproject');
expect(await project.organizationNames(), <String>['io.flutter']);
});
testInMemory('is singleton, if sources agree', () async {
final FlutterProject project = someProject();
addIosWithBundleId(project.directory, 'io.flutter.someProject');
addAndroidWithApplicationId(project.directory, 'io.flutter.someproject');
expect(await project.organizationNames(), <String>['io.flutter']);
});
testInMemory('is non-singleton, if sources disagree', () async {
final FlutterProject project = someProject();
addIosWithBundleId(project.directory, 'io.flutter.someProject');
addAndroidWithApplicationId(project.directory, 'io.clutter.someproject');
expect(
await project.organizationNames(),
<String>['io.flutter', 'io.clutter'],
);
});
});
});
}
FlutterProject someProject() =>
new FlutterProject(fs.directory('some_project'));
void testInMemory(String description, Future<Null> testMethod()) {
testUsingContext(
description,
testMethod,
overrides: <Type, Generator>{
FileSystem: () => new MemoryFileSystem(),
},
);
}
void addIosWithBundleId(Directory directory, String id) {
directory
.childDirectory('ios')
.childDirectory('Runner.xcodeproj')
.childFile('project.pbxproj')
..createSync(recursive: true)
..writeAsStringSync(projectFileWithBundleId(id));
}
void addAndroidWithApplicationId(Directory directory, String id) {
directory
.childDirectory('android')
.childDirectory('app')
.childFile('build.gradle')
..createSync(recursive: true)
..writeAsStringSync(gradleFileWithApplicationId(id));
}
void addAndroidWithGroup(Directory directory, String id) {
directory.childDirectory('android').childFile('build.gradle')
..createSync(recursive: true)
..writeAsStringSync(gradleFileWithGroupId(id));
}
String projectFileWithBundleId(String id) {
return '''
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
PRODUCT_BUNDLE_IDENTIFIER = $id;
PRODUCT_NAME = "\$(TARGET_NAME)";
};
name = Debug;
};
''';
}
String gradleFileWithApplicationId(String id) {
return '''
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
defaultConfig {
applicationId '$id'
}
}
''';
}
String gradleFileWithGroupId(String id) {
return '''
group '$id'
version '1.0-SNAPSHOT'
apply plugin: 'com.android.library'
android {
compileSdkVersion 27
}
''';
}
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