Check Xcode build setting FULL_PRODUCT_NAME for bundle name (#55799)
This commit is contained in:
parent
741ef91961
commit
c8c55b4098
@ -73,15 +73,15 @@ elif [[ "$OS" == "darwin" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ! -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets" ]]; then
|
if [[ ! -d "build/ios/iphoneos/Flutter Gallery.app/Frameworks/App.framework/flutter_assets" ]]; then
|
||||||
echo "Error: flutter_assets not assembled"
|
echo "Error: flutter_assets not assembled"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[
|
if [[
|
||||||
-d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/isolate_snapshot_data" ||
|
-d "build/ios/iphoneos/Flutter Gallery.app/Frameworks/App.framework/flutter_assets/isolate_snapshot_data" ||
|
||||||
-d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/kernel_blob.bin" ||
|
-d "build/ios/iphoneos/Flutter Gallery.app/Frameworks/App.framework/flutter_assets/kernel_blob.bin" ||
|
||||||
-d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/vm_snapshot_data"
|
-d "build/ios/iphoneos/Flutter Gallery.app/Frameworks/App.framework/flutter_assets/vm_snapshot_data"
|
||||||
]]; then
|
]]; then
|
||||||
echo "Error: compiled debug version of app with --release flag"
|
echo "Error: compiled debug version of app with --release flag"
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -451,7 +451,15 @@ class CompileTest {
|
|||||||
watch.start();
|
watch.start();
|
||||||
await flutter('build', options: options);
|
await flutter('build', options: options);
|
||||||
watch.stop();
|
watch.stop();
|
||||||
final String appPath = '$cwd/build/ios/Release-iphoneos/Runner.app/';
|
final Directory appBuildDirectory = dir(path.join(cwd, 'build/ios/Release-iphoneos'));
|
||||||
|
final Directory appBundle = appBuildDirectory
|
||||||
|
.listSync()
|
||||||
|
.whereType<Directory>()
|
||||||
|
.singleWhere((Directory directory) => path.extension(directory.path) == '.app', orElse: () => null);
|
||||||
|
if (appBundle == null) {
|
||||||
|
throw 'Failed to find app bundle in ${appBuildDirectory.path}';
|
||||||
|
}
|
||||||
|
final String appPath = appBundle.path;
|
||||||
// IPAs are created manually, https://flutter.dev/ios-release/
|
// IPAs are created manually, https://flutter.dev/ios-release/
|
||||||
await exec('tar', <String>['-zcf', 'build/app.ipa', appPath]);
|
await exec('tar', <String>['-zcf', 'build/app.ipa', appPath]);
|
||||||
releaseSizeInBytes = await file('$cwd/build/app.ipa').length();
|
releaseSizeInBytes = await file('$cwd/build/app.ipa').length();
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
97C146EE1CF9000F007C117D /* Flutter Gallery.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Flutter Gallery.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||||
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
@ -88,7 +88,7 @@
|
|||||||
97C146EF1CF9000F007C117D /* Products */ = {
|
97C146EF1CF9000F007C117D /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
97C146EE1CF9000F007C117D /* Runner.app */,
|
97C146EE1CF9000F007C117D /* Flutter Gallery.app */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -157,7 +157,7 @@
|
|||||||
);
|
);
|
||||||
name = Runner;
|
name = Runner;
|
||||||
productName = Runner;
|
productName = Runner;
|
||||||
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
productReference = 97C146EE1CF9000F007C117D /* Flutter Gallery.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
@ -369,7 +369,7 @@
|
|||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.examples.gallery;
|
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.examples.gallery;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "Flutter Gallery";
|
||||||
};
|
};
|
||||||
name = Profile;
|
name = Profile;
|
||||||
};
|
};
|
||||||
@ -494,7 +494,7 @@
|
|||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.examples.gallery;
|
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.examples.gallery;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "Flutter Gallery";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
@ -515,7 +515,7 @@
|
|||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.examples.gallery;
|
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.examples.gallery;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "Flutter Gallery";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
BuildableName = "Runner.app"
|
BuildableName = "Flutter Gallery.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
@ -27,19 +27,17 @@
|
|||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
BuildableName = "Runner.app"
|
BuildableName = "Flutter Gallery.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
<Testables>
|
||||||
</AdditionalOptions>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
@ -56,13 +54,11 @@
|
|||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
BuildableName = "Runner.app"
|
BuildableName = "Flutter Gallery.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Profile"
|
buildConfiguration = "Profile"
|
||||||
@ -75,7 +71,7 @@
|
|||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
BuildableName = "Runner.app"
|
BuildableName = "Flutter Gallery.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>Flutter Gallery</string>
|
<string>$(PRODUCT_NAME)</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
|
@ -357,18 +357,22 @@ abstract class IOSApp extends ApplicationPackage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class BuildableIOSApp extends IOSApp {
|
class BuildableIOSApp extends IOSApp {
|
||||||
BuildableIOSApp(this.project, String projectBundleId)
|
BuildableIOSApp(this.project, String projectBundleId, String hostAppBundleName)
|
||||||
: super(projectBundleId: projectBundleId);
|
: _hostAppBundleName = hostAppBundleName,
|
||||||
|
super(projectBundleId: projectBundleId);
|
||||||
|
|
||||||
static Future<BuildableIOSApp> fromProject(IosProject project) async {
|
static Future<BuildableIOSApp> fromProject(IosProject project) async {
|
||||||
final String projectBundleId = await project.productBundleIdentifier;
|
final String projectBundleId = await project.productBundleIdentifier;
|
||||||
return BuildableIOSApp(project, projectBundleId);
|
final String hostAppBundleName = await project.hostAppBundleName;
|
||||||
|
return BuildableIOSApp(project, projectBundleId, hostAppBundleName);
|
||||||
}
|
}
|
||||||
|
|
||||||
final IosProject project;
|
final IosProject project;
|
||||||
|
|
||||||
|
final String _hostAppBundleName;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get name => project.hostAppBundleName;
|
String get name => _hostAppBundleName;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get simulatorBundlePath => _buildAppPath('iphonesimulator');
|
String get simulatorBundlePath => _buildAppPath('iphonesimulator');
|
||||||
@ -377,7 +381,7 @@ class BuildableIOSApp extends IOSApp {
|
|||||||
String get deviceBundlePath => _buildAppPath('iphoneos');
|
String get deviceBundlePath => _buildAppPath('iphoneos');
|
||||||
|
|
||||||
String _buildAppPath(String type) {
|
String _buildAppPath(String type) {
|
||||||
return globals.fs.path.join(getIosBuildDirectory(), type, name);
|
return globals.fs.path.join(getIosBuildDirectory(), type, _hostAppBundleName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,7 +632,7 @@ bool upgradePbxProjWithFlutterAssets(IosProject project, Logger logger) {
|
|||||||
final Match match = oldAssets.firstMatch(line);
|
final Match match = oldAssets.firstMatch(line);
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
if (printedStatuses.add(match.group(1))) {
|
if (printedStatuses.add(match.group(1))) {
|
||||||
logger.printStatus('Removing obsolete reference to ${match.group(1)} from ${project.hostAppBundleName}');
|
logger.printStatus('Removing obsolete reference to ${match.group(1)} from ${project.xcodeProject?.basename}');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
buffer.writeln(line);
|
buffer.writeln(line);
|
||||||
|
@ -685,7 +685,7 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
|
|||||||
// Match the log prefix (in order to shorten it):
|
// Match the log prefix (in order to shorten it):
|
||||||
// * Xcode 8: Sep 13 15:28:51 cbracken-macpro localhost Runner[37195]: (Flutter) Observatory listening on http://127.0.0.1:57701/
|
// * Xcode 8: Sep 13 15:28:51 cbracken-macpro localhost Runner[37195]: (Flutter) Observatory listening on http://127.0.0.1:57701/
|
||||||
// * Xcode 9: 2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) Observatory listening on http://127.0.0.1:57701/
|
// * Xcode 9: 2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) Observatory listening on http://127.0.0.1:57701/
|
||||||
static final RegExp _mapRegex = RegExp(r'\S+ +\S+ +\S+ +(\S+ +)?(\S+)\[\d+\]\)?: (\(.*?\))? *(.*)$');
|
static final RegExp _mapRegex = RegExp(r'\S+ +\S+ +(?:\S+) (.+?(?=\[))\[\d+\]\)?: (\(.*?\))? *(.*)$');
|
||||||
|
|
||||||
// Jan 31 19:23:28 --- last message repeated 1 time ---
|
// Jan 31 19:23:28 --- last message repeated 1 time ---
|
||||||
static final RegExp _lastMessageSingleRegex = RegExp(r'\S+ +\S+ +\S+ --- last message repeated 1 time ---$');
|
static final RegExp _lastMessageSingleRegex = RegExp(r'\S+ +\S+ +\S+ --- last message repeated 1 time ---$');
|
||||||
@ -700,12 +700,16 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
|
|||||||
String _filterDeviceLine(String string) {
|
String _filterDeviceLine(String string) {
|
||||||
final Match match = _mapRegex.matchAsPrefix(string);
|
final Match match = _mapRegex.matchAsPrefix(string);
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
final String category = match.group(2);
|
|
||||||
final String tag = match.group(3);
|
|
||||||
final String content = match.group(4);
|
|
||||||
|
|
||||||
// Filter out non-Flutter originated noise from the engine.
|
// The category contains the text between the date and the PID. Depending on which version of iOS being run,
|
||||||
if (_appName != null && category != _appName) {
|
// it can contain "hostname App Name" or just "App Name".
|
||||||
|
final String category = match.group(1);
|
||||||
|
final String tag = match.group(2);
|
||||||
|
final String content = match.group(3);
|
||||||
|
|
||||||
|
// Filter out log lines from an app other than this one (category doesn't match the app name).
|
||||||
|
// If the hostname is included in the category, check that it doesn't end with the app name.
|
||||||
|
if (_appName != null && !category.endsWith(_appName)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -725,7 +729,7 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
|
|||||||
|
|
||||||
if (_appName == null) {
|
if (_appName == null) {
|
||||||
return '$category: $content';
|
return '$category: $content';
|
||||||
} else if (category == _appName) {
|
} else if (category == _appName || category.endsWith(' $_appName')) {
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,7 +341,7 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
|
|||||||
|
|
||||||
static final RegExp _productBundleIdPattern = RegExp(r'''^\s*PRODUCT_BUNDLE_IDENTIFIER\s*=\s*(["']?)(.*?)\1;\s*$''');
|
static final RegExp _productBundleIdPattern = RegExp(r'''^\s*PRODUCT_BUNDLE_IDENTIFIER\s*=\s*(["']?)(.*?)\1;\s*$''');
|
||||||
static const String _productBundleIdVariable = r'$(PRODUCT_BUNDLE_IDENTIFIER)';
|
static const String _productBundleIdVariable = r'$(PRODUCT_BUNDLE_IDENTIFIER)';
|
||||||
static const String _hostAppBundleName = 'Runner';
|
static const String _hostAppProjectName = 'Runner';
|
||||||
|
|
||||||
Directory get ephemeralDirectory => parent.directory.childDirectory('.ios');
|
Directory get ephemeralDirectory => parent.directory.childDirectory('.ios');
|
||||||
Directory get _editableDirectory => parent.directory.childDirectory('ios');
|
Directory get _editableDirectory => parent.directory.childDirectory('ios');
|
||||||
@ -362,9 +362,6 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
|
|||||||
/// a Flutter module with an editable host app.
|
/// a Flutter module with an editable host app.
|
||||||
Directory get _flutterLibRoot => isModule ? ephemeralDirectory : _editableDirectory;
|
Directory get _flutterLibRoot => isModule ? 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 project.
|
/// True, if the parent Flutter project is a module project.
|
||||||
bool get isModule => parent.isModule;
|
bool get isModule => parent.isModule;
|
||||||
|
|
||||||
@ -387,19 +384,19 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
|
|||||||
File get podManifestLock => hostAppRoot.childDirectory('Pods').childFile('Manifest.lock');
|
File get podManifestLock => hostAppRoot.childDirectory('Pods').childFile('Manifest.lock');
|
||||||
|
|
||||||
/// The default 'Info.plist' file of the host app. The developer can change this location in Xcode.
|
/// The default 'Info.plist' file of the host app. The developer can change this location in Xcode.
|
||||||
File get defaultHostInfoPlist => hostAppRoot.childDirectory(_hostAppBundleName).childFile('Info.plist');
|
File get defaultHostInfoPlist => hostAppRoot.childDirectory(_hostAppProjectName).childFile('Info.plist');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Directory get symlinks => _flutterLibRoot.childDirectory('.symlinks');
|
Directory get symlinks => _flutterLibRoot.childDirectory('.symlinks');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Directory get xcodeProject => hostAppRoot.childDirectory('$_hostAppBundleName.xcodeproj');
|
Directory get xcodeProject => hostAppRoot.childDirectory('$_hostAppProjectName.xcodeproj');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
File get xcodeProjectInfoFile => xcodeProject.childFile('project.pbxproj');
|
File get xcodeProjectInfoFile => xcodeProject.childFile('project.pbxproj');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Directory get xcodeWorkspace => hostAppRoot.childDirectory('$_hostAppBundleName.xcworkspace');
|
Directory get xcodeWorkspace => hostAppRoot.childDirectory('$_hostAppProjectName.xcworkspace');
|
||||||
|
|
||||||
/// Xcode workspace shared data directory for the host app.
|
/// Xcode workspace shared data directory for the host app.
|
||||||
Directory get xcodeWorkspaceSharedData => xcodeWorkspace.childDirectory('xcshareddata');
|
Directory get xcodeWorkspaceSharedData => xcodeWorkspace.childDirectory('xcshareddata');
|
||||||
@ -414,7 +411,11 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
|
|||||||
|
|
||||||
/// The product bundle identifier of the host app, or null if not set or if
|
/// The product bundle identifier of the host app, or null if not set or if
|
||||||
/// iOS tooling needed to read it is not installed.
|
/// iOS tooling needed to read it is not installed.
|
||||||
Future<String> get productBundleIdentifier async {
|
Future<String> get productBundleIdentifier async =>
|
||||||
|
_productBundleIdentifier ??= await _parseProductBundleIdentifier();
|
||||||
|
String _productBundleIdentifier;
|
||||||
|
|
||||||
|
Future<String> _parseProductBundleIdentifier() async {
|
||||||
String fromPlist;
|
String fromPlist;
|
||||||
final File defaultInfoPlist = defaultHostInfoPlist;
|
final File defaultInfoPlist = defaultHostInfoPlist;
|
||||||
// Users can change the location of the Info.plist.
|
// Users can change the location of the Info.plist.
|
||||||
@ -456,28 +457,51 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The bundle name of the host app, `My App.app`.
|
||||||
|
Future<String> get hostAppBundleName async =>
|
||||||
|
_hostAppBundleName ??= await _parseHostAppBundleName();
|
||||||
|
String _hostAppBundleName;
|
||||||
|
|
||||||
|
Future<String> _parseHostAppBundleName() async {
|
||||||
|
// The product name and bundle name are derived from the display name, which the user
|
||||||
|
// is instructed to change in Xcode as part of deploying to the App Store.
|
||||||
|
// https://flutter.dev/docs/deployment/ios#review-xcode-project-settings
|
||||||
|
// The only source of truth for the name is Xcode's interpretation of the build settings.
|
||||||
|
String productName;
|
||||||
|
if (globals.xcodeProjectInterpreter.isInstalled) {
|
||||||
|
final Map<String, String> xcodeBuildSettings = await buildSettings;
|
||||||
|
if (xcodeBuildSettings != null) {
|
||||||
|
productName = xcodeBuildSettings['FULL_PRODUCT_NAME'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (productName == null) {
|
||||||
|
globals.printTrace('FULL_PRODUCT_NAME not present, defaulting to $_hostAppProjectName');
|
||||||
|
}
|
||||||
|
return productName ?? '$_hostAppProjectName.app';
|
||||||
|
}
|
||||||
|
|
||||||
/// The build settings for the host app of this project, as a detached map.
|
/// The build settings for the host app of this project, as a detached map.
|
||||||
///
|
///
|
||||||
/// Returns null, if iOS tooling is unavailable.
|
/// Returns null, if iOS tooling is unavailable.
|
||||||
Future<Map<String, String>> get buildSettings async {
|
Future<Map<String, String>> get buildSettings async =>
|
||||||
|
_buildSettings ??= await _xcodeProjectBuildSettings();
|
||||||
|
Map<String, String> _buildSettings;
|
||||||
|
|
||||||
|
Future<Map<String, String>> _xcodeProjectBuildSettings() async {
|
||||||
if (!globals.xcodeProjectInterpreter.isInstalled) {
|
if (!globals.xcodeProjectInterpreter.isInstalled) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Map<String, String> buildSettings = _buildSettings;
|
final Map<String, String> buildSettings = await globals.xcodeProjectInterpreter.getBuildSettings(
|
||||||
buildSettings ??= await globals.xcodeProjectInterpreter.getBuildSettings(
|
|
||||||
xcodeProject.path,
|
xcodeProject.path,
|
||||||
_hostAppBundleName,
|
_hostAppProjectName,
|
||||||
);
|
);
|
||||||
if (buildSettings != null && buildSettings.isNotEmpty) {
|
if (buildSettings != null && buildSettings.isNotEmpty) {
|
||||||
// No timeouts, flakes, or errors.
|
// No timeouts, flakes, or errors.
|
||||||
_buildSettings = buildSettings;
|
|
||||||
return buildSettings;
|
return buildSettings;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, String> _buildSettings;
|
|
||||||
|
|
||||||
Future<void> ensureReadyForPlatformSpecificTooling() async {
|
Future<void> ensureReadyForPlatformSpecificTooling() async {
|
||||||
await _regenerateFromTemplateIfNeeded();
|
await _regenerateFromTemplateIfNeeded();
|
||||||
if (!_flutterLibRoot.existsSync()) {
|
if (!_flutterLibRoot.existsSync()) {
|
||||||
@ -614,7 +638,7 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
|
|||||||
? _flutterLibRoot
|
? _flutterLibRoot
|
||||||
.childDirectory('Flutter')
|
.childDirectory('Flutter')
|
||||||
.childDirectory('FlutterPluginRegistrant')
|
.childDirectory('FlutterPluginRegistrant')
|
||||||
: hostAppRoot.childDirectory(_hostAppBundleName);
|
: hostAppRoot.childDirectory(_hostAppProjectName);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _overwriteFromTemplate(String path, Directory target) async {
|
Future<void> _overwriteFromTemplate(String path, Directory target) async {
|
||||||
@ -888,7 +912,7 @@ class MacOSProject extends FlutterProjectPlatform implements XcodeBasedProject {
|
|||||||
@override
|
@override
|
||||||
String get pluginConfigKey => MacOSPlugin.kConfigKey;
|
String get pluginConfigKey => MacOSPlugin.kConfigKey;
|
||||||
|
|
||||||
static const String _hostAppBundleName = 'Runner';
|
static const String _hostAppProjectName = 'Runner';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool existsSync() => _macOSDirectory.existsSync();
|
bool existsSync() => _macOSDirectory.existsSync();
|
||||||
@ -932,13 +956,13 @@ class MacOSProject extends FlutterProjectPlatform implements XcodeBasedProject {
|
|||||||
File get podManifestLock => _macOSDirectory.childDirectory('Pods').childFile('Manifest.lock');
|
File get podManifestLock => _macOSDirectory.childDirectory('Pods').childFile('Manifest.lock');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Directory get xcodeProject => _macOSDirectory.childDirectory('$_hostAppBundleName.xcodeproj');
|
Directory get xcodeProject => _macOSDirectory.childDirectory('$_hostAppProjectName.xcodeproj');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
File get xcodeProjectInfoFile => xcodeProject.childFile('project.pbxproj');
|
File get xcodeProjectInfoFile => xcodeProject.childFile('project.pbxproj');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Directory get xcodeWorkspace => _macOSDirectory.childDirectory('$_hostAppBundleName.xcworkspace');
|
Directory get xcodeWorkspace => _macOSDirectory.childDirectory('$_hostAppProjectName.xcworkspace');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Directory get symlinks => ephemeralDirectory.childDirectory('.symlinks');
|
Directory get symlinks => ephemeralDirectory.childDirectory('.symlinks');
|
||||||
|
@ -13,6 +13,8 @@ import 'package:flutter_tools/src/device.dart';
|
|||||||
import 'package:flutter_tools/src/ios/devices.dart';
|
import 'package:flutter_tools/src/ios/devices.dart';
|
||||||
import 'package:flutter_tools/src/ios/ios_deploy.dart';
|
import 'package:flutter_tools/src/ios/ios_deploy.dart';
|
||||||
import 'package:flutter_tools/src/ios/mac.dart';
|
import 'package:flutter_tools/src/ios/mac.dart';
|
||||||
|
import 'package:flutter_tools/src/ios/xcodeproj.dart';
|
||||||
|
import 'package:flutter_tools/src/macos/xcode.dart';
|
||||||
import 'package:flutter_tools/src/project.dart';
|
import 'package:flutter_tools/src/project.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
import 'package:platform/platform.dart';
|
import 'package:platform/platform.dart';
|
||||||
@ -64,67 +66,34 @@ final FakePlatform macPlatform = FakePlatform(
|
|||||||
);
|
);
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
FileSystem fileSystem;
|
group('IOSDevice.startApp succeeds in release mode', () {
|
||||||
FakeProcessManager processManager;
|
FileSystem fileSystem;
|
||||||
BufferLogger logger;
|
FakeProcessManager processManager;
|
||||||
|
BufferLogger logger;
|
||||||
|
MockXcode mockXcode;
|
||||||
|
MockXcodeProjectInterpreter mockXcodeProjectInterpreter;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
logger = BufferLogger.test();
|
logger = BufferLogger.test();
|
||||||
fileSystem = MemoryFileSystem.test();
|
fileSystem = MemoryFileSystem.test();
|
||||||
processManager = FakeProcessManager.list(<FakeCommand>[]);
|
processManager = FakeProcessManager.list(<FakeCommand>[]);
|
||||||
});
|
|
||||||
|
|
||||||
testUsingContext('IOSDevice.startApp succeeds in release mode with buildable app', () async {
|
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
|
||||||
final IOSDevice iosDevice = setUpIOSDevice(
|
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
|
||||||
fileSystem: fileSystem,
|
when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer(
|
||||||
processManager: processManager,
|
(_) {
|
||||||
logger: logger,
|
return Future<XcodeProjectInfo>.value(XcodeProjectInfo(
|
||||||
);
|
<String>['Runner'],
|
||||||
setUpIOSProject(fileSystem);
|
<String>['Debug', 'Release'],
|
||||||
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
|
<String>['Runner'],
|
||||||
final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter');
|
));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
mockXcode = MockXcode();
|
||||||
|
when(mockXcode.isVersionSatisfactory).thenReturn(true);
|
||||||
|
});
|
||||||
|
|
||||||
processManager.addCommand(FakeCommand(command: _xattrArgs(flutterProject)));
|
testUsingContext('with buildable app', () async {
|
||||||
processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
|
|
||||||
processManager.addCommand(const FakeCommand(command: <String>[...kRunReleaseArgs, '-showBuildSettings']));
|
|
||||||
processManager.addCommand(FakeCommand(
|
|
||||||
command: <String>[
|
|
||||||
'ios-deploy',
|
|
||||||
'--id',
|
|
||||||
'123',
|
|
||||||
'--bundle',
|
|
||||||
'build/ios/iphoneos/Runner.app',
|
|
||||||
'--no-wifi',
|
|
||||||
'--justlaunch',
|
|
||||||
'--args',
|
|
||||||
const <String>[
|
|
||||||
'--enable-dart-profiling',
|
|
||||||
'--enable-service-port-fallback',
|
|
||||||
'--disable-service-auth-codes',
|
|
||||||
'--observatory-port=53781',
|
|
||||||
].join(' ')
|
|
||||||
])
|
|
||||||
);
|
|
||||||
|
|
||||||
final LaunchResult launchResult = await iosDevice.startApp(
|
|
||||||
buildableIOSApp,
|
|
||||||
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
|
|
||||||
platformArgs: <String, Object>{},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(launchResult.started, true);
|
|
||||||
expect(processManager.hasRemainingExpectations, false);
|
|
||||||
}, overrides: <Type, Generator>{
|
|
||||||
ProcessManager: () => processManager,
|
|
||||||
FileSystem: () => fileSystem,
|
|
||||||
Logger: () => logger,
|
|
||||||
Platform: () => macPlatform,
|
|
||||||
});
|
|
||||||
|
|
||||||
testUsingContext('IOSDevice.startApp succeeds in release mode with buildable '
|
|
||||||
'app with flaky buildSettings call', () async {
|
|
||||||
LaunchResult launchResult;
|
|
||||||
FakeAsync().run((FakeAsync time) {
|
|
||||||
final IOSDevice iosDevice = setUpIOSDevice(
|
final IOSDevice iosDevice = setUpIOSDevice(
|
||||||
fileSystem: fileSystem,
|
fileSystem: fileSystem,
|
||||||
processManager: processManager,
|
processManager: processManager,
|
||||||
@ -132,29 +101,18 @@ void main() {
|
|||||||
);
|
);
|
||||||
setUpIOSProject(fileSystem);
|
setUpIOSProject(fileSystem);
|
||||||
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
|
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
|
||||||
final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter');
|
final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter', 'My Super Awesome App.app');
|
||||||
|
|
||||||
processManager.addCommand(FakeCommand(command: _xattrArgs(flutterProject)));
|
processManager.addCommand(FakeCommand(command: _xattrArgs(flutterProject)));
|
||||||
processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
|
processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
|
||||||
// The first showBuildSettings call should timeout.
|
processManager.addCommand(const FakeCommand(command: <String>[...kRunReleaseArgs, '-showBuildSettings']));
|
||||||
processManager.addCommand(
|
|
||||||
const FakeCommand(
|
|
||||||
command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
|
|
||||||
duration: Duration(minutes: 5), // this is longer than the timeout of 1 minute.
|
|
||||||
));
|
|
||||||
// The second call succeedes and is made after the first times out.
|
|
||||||
processManager.addCommand(
|
|
||||||
const FakeCommand(
|
|
||||||
command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
|
|
||||||
exitCode: 0,
|
|
||||||
));
|
|
||||||
processManager.addCommand(FakeCommand(
|
processManager.addCommand(FakeCommand(
|
||||||
command: <String>[
|
command: <String>[
|
||||||
'ios-deploy',
|
'ios-deploy',
|
||||||
'--id',
|
'--id',
|
||||||
'123',
|
'123',
|
||||||
'--bundle',
|
'--bundle',
|
||||||
'build/ios/iphoneos/Runner.app',
|
'build/ios/iphoneos/My Super Awesome App.app',
|
||||||
'--no-wifi',
|
'--no-wifi',
|
||||||
'--justlaunch',
|
'--justlaunch',
|
||||||
'--args',
|
'--args',
|
||||||
@ -167,94 +125,160 @@ void main() {
|
|||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
iosDevice.startApp(
|
final LaunchResult launchResult = await iosDevice.startApp(
|
||||||
buildableIOSApp,
|
buildableIOSApp,
|
||||||
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
|
||||||
platformArgs: <String, Object>{},
|
platformArgs: <String, Object>{},
|
||||||
).then((LaunchResult result) {
|
);
|
||||||
launchResult = result;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Elapse duration for process timeout.
|
expect(launchResult.started, true);
|
||||||
time.flushMicrotasks();
|
expect(processManager.hasRemainingExpectations, false);
|
||||||
time.elapse(const Duration(minutes: 1));
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => processManager,
|
||||||
// Elapse duration for overall process timer.
|
FileSystem: () => fileSystem,
|
||||||
time.flushMicrotasks();
|
Logger: () => logger,
|
||||||
time.elapse(const Duration(minutes: 5));
|
Platform: () => macPlatform,
|
||||||
|
XcodeProjectInterpreter: () => mockXcodeProjectInterpreter,
|
||||||
time.flushTimers();
|
Xcode: () => mockXcode,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(launchResult?.started, true);
|
testUsingContext('with flaky buildSettings call', () async {
|
||||||
expect(processManager.hasRemainingExpectations, false);
|
LaunchResult launchResult;
|
||||||
}, overrides: <Type, Generator>{
|
FakeAsync().run((FakeAsync time) {
|
||||||
ProcessManager: () => processManager,
|
final IOSDevice iosDevice = setUpIOSDevice(
|
||||||
FileSystem: () => fileSystem,
|
fileSystem: fileSystem,
|
||||||
Logger: () => logger,
|
processManager: processManager,
|
||||||
Platform: () => macPlatform,
|
logger: logger,
|
||||||
});
|
);
|
||||||
|
setUpIOSProject(fileSystem);
|
||||||
|
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
|
||||||
|
final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter', 'My Super Awesome App.app');
|
||||||
|
|
||||||
testUsingContext('IOSDevice.startApp succeeds in release mode with buildable '
|
processManager.addCommand(FakeCommand(command: _xattrArgs(flutterProject)));
|
||||||
'app with concurrent build failure', () async {
|
processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
|
||||||
final IOSDevice iosDevice = setUpIOSDevice(
|
// The first showBuildSettings call should timeout.
|
||||||
fileSystem: fileSystem,
|
processManager.addCommand(
|
||||||
processManager: processManager,
|
const FakeCommand(
|
||||||
logger: logger,
|
command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
|
||||||
);
|
duration: Duration(minutes: 5), // this is longer than the timeout of 1 minute.
|
||||||
setUpIOSProject(fileSystem);
|
));
|
||||||
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
|
// The second call succeedes and is made after the first times out.
|
||||||
final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter');
|
processManager.addCommand(
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
|
||||||
|
exitCode: 0,
|
||||||
|
));
|
||||||
|
processManager.addCommand(FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
'ios-deploy',
|
||||||
|
'--id',
|
||||||
|
'123',
|
||||||
|
'--bundle',
|
||||||
|
'build/ios/iphoneos/My Super Awesome App.app',
|
||||||
|
'--no-wifi',
|
||||||
|
'--justlaunch',
|
||||||
|
'--args',
|
||||||
|
const <String>[
|
||||||
|
'--enable-dart-profiling',
|
||||||
|
'--enable-service-port-fallback',
|
||||||
|
'--disable-service-auth-codes',
|
||||||
|
'--observatory-port=53781',
|
||||||
|
].join(' ')
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
processManager.addCommand(FakeCommand(command: _xattrArgs(flutterProject)));
|
iosDevice.startApp(
|
||||||
// The first xcrun call should fail with a
|
buildableIOSApp,
|
||||||
// concurrent build exception.
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
|
||||||
processManager.addCommand(
|
platformArgs: <String, Object>{},
|
||||||
const FakeCommand(
|
).then((LaunchResult result) {
|
||||||
command: kRunReleaseArgs,
|
launchResult = result;
|
||||||
exitCode: 1,
|
});
|
||||||
stdout: kConcurrentBuildErrorMessage,
|
|
||||||
));
|
|
||||||
processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
|
|
||||||
processManager.addCommand(
|
|
||||||
const FakeCommand(
|
|
||||||
command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
|
|
||||||
exitCode: 0,
|
|
||||||
));
|
|
||||||
processManager.addCommand(FakeCommand(
|
|
||||||
command: <String>[
|
|
||||||
'ios-deploy',
|
|
||||||
'--id',
|
|
||||||
'123',
|
|
||||||
'--bundle',
|
|
||||||
'build/ios/iphoneos/Runner.app',
|
|
||||||
'--no-wifi',
|
|
||||||
'--justlaunch',
|
|
||||||
'--args',
|
|
||||||
const <String>[
|
|
||||||
'--enable-dart-profiling',
|
|
||||||
'--enable-service-port-fallback',
|
|
||||||
'--disable-service-auth-codes',
|
|
||||||
'--observatory-port=53781',
|
|
||||||
].join(' ')
|
|
||||||
])
|
|
||||||
);
|
|
||||||
|
|
||||||
final LaunchResult launchResult = await iosDevice.startApp(
|
// Elapse duration for process timeout.
|
||||||
buildableIOSApp,
|
time.flushMicrotasks();
|
||||||
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
|
time.elapse(const Duration(minutes: 1));
|
||||||
platformArgs: <String, Object>{},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(logger.statusText,
|
// Elapse duration for overall process timer.
|
||||||
contains('Xcode build failed due to concurrent builds, will retry in 2 seconds'));
|
time.flushMicrotasks();
|
||||||
expect(launchResult.started, true);
|
time.elapse(const Duration(minutes: 5));
|
||||||
expect(processManager.hasRemainingExpectations, false);
|
|
||||||
}, overrides: <Type, Generator>{
|
time.flushTimers();
|
||||||
ProcessManager: () => processManager,
|
});
|
||||||
FileSystem: () => fileSystem,
|
|
||||||
Logger: () => logger,
|
expect(launchResult?.started, true);
|
||||||
Platform: () => macPlatform,
|
expect(processManager.hasRemainingExpectations, false);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => processManager,
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
Logger: () => logger,
|
||||||
|
Platform: () => macPlatform,
|
||||||
|
XcodeProjectInterpreter: () => mockXcodeProjectInterpreter,
|
||||||
|
Xcode: () => mockXcode,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('with concurrent build failures', () async {
|
||||||
|
final IOSDevice iosDevice = setUpIOSDevice(
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
processManager: processManager,
|
||||||
|
logger: logger,
|
||||||
|
);
|
||||||
|
setUpIOSProject(fileSystem);
|
||||||
|
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
|
||||||
|
final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter', 'My Super Awesome App.app');
|
||||||
|
|
||||||
|
processManager.addCommand(FakeCommand(command: _xattrArgs(flutterProject)));
|
||||||
|
// The first xcrun call should fail with a
|
||||||
|
// concurrent build exception.
|
||||||
|
processManager.addCommand(
|
||||||
|
const FakeCommand(
|
||||||
|
command: kRunReleaseArgs,
|
||||||
|
exitCode: 1,
|
||||||
|
stdout: kConcurrentBuildErrorMessage,
|
||||||
|
));
|
||||||
|
processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
|
||||||
|
processManager.addCommand(
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
|
||||||
|
exitCode: 0,
|
||||||
|
));
|
||||||
|
processManager.addCommand(FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
'ios-deploy',
|
||||||
|
'--id',
|
||||||
|
'123',
|
||||||
|
'--bundle',
|
||||||
|
'build/ios/iphoneos/My Super Awesome App.app',
|
||||||
|
'--no-wifi',
|
||||||
|
'--justlaunch',
|
||||||
|
'--args',
|
||||||
|
const <String>[
|
||||||
|
'--enable-dart-profiling',
|
||||||
|
'--enable-service-port-fallback',
|
||||||
|
'--disable-service-auth-codes',
|
||||||
|
'--observatory-port=53781',
|
||||||
|
].join(' ')
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
final LaunchResult launchResult = await iosDevice.startApp(
|
||||||
|
buildableIOSApp,
|
||||||
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
|
||||||
|
platformArgs: <String, Object>{},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(logger.statusText,
|
||||||
|
contains('Xcode build failed due to concurrent builds, will retry in 2 seconds'));
|
||||||
|
expect(launchResult.started, true);
|
||||||
|
expect(processManager.hasRemainingExpectations, false);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => processManager,
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
Logger: () => logger,
|
||||||
|
Platform: () => macPlatform,
|
||||||
|
XcodeProjectInterpreter: () => mockXcodeProjectInterpreter,
|
||||||
|
Xcode: () => mockXcode,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,10 +287,9 @@ void setUpIOSProject(FileSystem fileSystem) {
|
|||||||
fileSystem.file('.packages').writeAsStringSync('\n');
|
fileSystem.file('.packages').writeAsStringSync('\n');
|
||||||
fileSystem.directory('ios').createSync();
|
fileSystem.directory('ios').createSync();
|
||||||
fileSystem.directory('ios/Runner.xcworkspace').createSync();
|
fileSystem.directory('ios/Runner.xcworkspace').createSync();
|
||||||
fileSystem.directory('ios/Runner.xcodeproj').createSync();
|
fileSystem.file('ios/Runner.xcodeproj/project.pbxproj').createSync(recursive: true);
|
||||||
fileSystem.file('ios/Runner.xcodeproj/project.pbxproj').createSync();
|
|
||||||
// This is the expected output directory.
|
// This is the expected output directory.
|
||||||
fileSystem.directory('build/ios/iphoneos/Runner.app').createSync(recursive: true);
|
fileSystem.directory('build/ios/iphoneos/My Super Awesome App.app').createSync(recursive: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
IOSDevice setUpIOSDevice({
|
IOSDevice setUpIOSDevice({
|
||||||
@ -311,3 +334,5 @@ IOSDevice setUpIOSDevice({
|
|||||||
|
|
||||||
class MockArtifacts extends Mock implements Artifacts {}
|
class MockArtifacts extends Mock implements Artifacts {}
|
||||||
class MockCache extends Mock implements Cache {}
|
class MockCache extends Mock implements Cache {}
|
||||||
|
class MockXcode extends Mock implements Xcode {}
|
||||||
|
class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {}
|
||||||
|
@ -396,7 +396,7 @@ Exited (sigterm)''',
|
|||||||
final MockFile pbxprojFile = MockFile();
|
final MockFile pbxprojFile = MockFile();
|
||||||
|
|
||||||
when(project.xcodeProjectInfoFile).thenReturn(pbxprojFile);
|
when(project.xcodeProjectInfoFile).thenReturn(pbxprojFile);
|
||||||
when(project.hostAppBundleName).thenReturn('UnitTestRunner.app');
|
when(project.hostAppBundleName).thenAnswer((_) => Future<String>.value('UnitTestRunner.app'));
|
||||||
when(pbxprojFile.readAsLinesSync())
|
when(pbxprojFile.readAsLinesSync())
|
||||||
.thenAnswer((_) => flutterAssetPbxProjLines);
|
.thenAnswer((_) => flutterAssetPbxProjLines);
|
||||||
when(pbxprojFile.existsSync())
|
when(pbxprojFile.existsSync())
|
||||||
|
@ -452,10 +452,10 @@ void main() {
|
|||||||
simControl: mockSimControl,
|
simControl: mockSimControl,
|
||||||
xcode: mockXcode,
|
xcode: mockXcode,
|
||||||
);
|
);
|
||||||
await launchDeviceUnifiedLogging(device, 'Runner');
|
await launchDeviceUnifiedLogging(device, 'My Super Awesome App');
|
||||||
|
|
||||||
const String expectedPredicate = 'eventType = logEvent AND '
|
const String expectedPredicate = 'eventType = logEvent AND '
|
||||||
'processImagePath ENDSWITH "Runner" AND '
|
'processImagePath ENDSWITH "My Super Awesome App" AND '
|
||||||
'(senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" OR processImageUUID == senderImageUUID) AND '
|
'(senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" OR processImageUUID == senderImageUUID) AND '
|
||||||
'NOT(eventMessage CONTAINS ": could not find icon for representation -> com.apple.") AND '
|
'NOT(eventMessage CONTAINS ": could not find icon for representation -> com.apple.") AND '
|
||||||
'NOT(eventMessage BEGINSWITH "assertion failed: ") AND '
|
'NOT(eventMessage BEGINSWITH "assertion failed: ") AND '
|
||||||
@ -539,7 +539,9 @@ void main() {
|
|||||||
fakeProcessManager
|
fakeProcessManager
|
||||||
..addCommand(const FakeCommand(
|
..addCommand(const FakeCommand(
|
||||||
command: <String>['tail', '-n', '0', '-F', 'system.log'],
|
command: <String>['tail', '-n', '0', '-F', 'system.log'],
|
||||||
stdout: 'Dec 20 17:04:32 md32-11-vm1 Runner[88374]: flutter: Observatory listening on http://127.0.0.1:64213/1Uoeu523990=/',
|
stdout: '''
|
||||||
|
Dec 20 17:04:32 md32-11-vm1 My Super Awesome App[88374]: flutter: Observatory listening on http://127.0.0.1:64213/1Uoeu523990=/
|
||||||
|
Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
|
||||||
))
|
))
|
||||||
..addCommand(const FakeCommand(
|
..addCommand(const FakeCommand(
|
||||||
command: <String>['tail', '-n', '0', '-F', '/private/var/log/system.log']
|
command: <String>['tail', '-n', '0', '-F', '/private/var/log/system.log']
|
||||||
@ -571,9 +573,9 @@ void main() {
|
|||||||
..addCommand(const FakeCommand(
|
..addCommand(const FakeCommand(
|
||||||
command: <String>['tail', '-n', '0', '-F', 'system.log'],
|
command: <String>['tail', '-n', '0', '-F', 'system.log'],
|
||||||
stdout: '''
|
stdout: '''
|
||||||
2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) Observatory listening on http://127.0.0.1:57701/
|
2017-09-13 15:26:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) Observatory listening on http://127.0.0.1:57701/
|
||||||
2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) ))))))))))
|
2017-09-13 15:26:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) ))))))))))
|
||||||
2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) #0 Object.noSuchMethod (dart:core-patch/dart:core/object_patch.dart:46)'''
|
2017-09-13 15:26:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) #0 Object.noSuchMethod (dart:core-patch/dart:core/object_patch.dart:46)'''
|
||||||
))
|
))
|
||||||
..addCommand(const FakeCommand(
|
..addCommand(const FakeCommand(
|
||||||
command: <String>['tail', '-n', '0', '-F', '/private/var/log/system.log']
|
command: <String>['tail', '-n', '0', '-F', '/private/var/log/system.log']
|
||||||
@ -607,19 +609,19 @@ void main() {
|
|||||||
..addCommand(const FakeCommand(
|
..addCommand(const FakeCommand(
|
||||||
command: <String>['tail', '-n', '0', '-F', 'system.log'],
|
command: <String>['tail', '-n', '0', '-F', 'system.log'],
|
||||||
stdout: '''
|
stdout: '''
|
||||||
2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) Single line message
|
2017-09-13 15:26:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) Single line message
|
||||||
2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) Multi line message
|
2017-09-13 15:26:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) Multi line message
|
||||||
continues...
|
continues...
|
||||||
continues...
|
continues...
|
||||||
2020-03-11 15:58:28.207175-0700 localhost Runner[72166]: (libnetwork.dylib) [com.apple.network:] [28 www.googleapis.com:443 stream, pid: 72166, tls] cancelled
|
2020-03-11 15:58:28.207175-0700 localhost My Super Awesome App[72166]: (libnetwork.dylib) [com.apple.network:] [28 www.googleapis.com:443 stream, pid: 72166, tls] cancelled
|
||||||
[28.1 64A98447-EABF-4983-A387-7DB9D0C1785F 10.0.1.200.57912<->172.217.6.74:443]
|
[28.1 64A98447-EABF-4983-A387-7DB9D0C1785F 10.0.1.200.57912<->172.217.6.74:443]
|
||||||
Connected Path: satisfied (Path is satisfied), interface: en18
|
Connected Path: satisfied (Path is satisfied), interface: en18
|
||||||
Duration: 0.271s, DNS @0.000s took 0.001s, TCP @0.002s took 0.019s, TLS took 0.046s
|
Duration: 0.271s, DNS @0.000s took 0.001s, TCP @0.002s took 0.019s, TLS took 0.046s
|
||||||
bytes in/out: 4468/1933, packets in/out: 11/10, rtt: 0.016s, retransmitted packets: 0, out-of-order packets: 0
|
bytes in/out: 4468/1933, packets in/out: 11/10, rtt: 0.016s, retransmitted packets: 0, out-of-order packets: 0
|
||||||
2017-09-13 15:36:57.228948-0700 localhost Runner[37195]: (Flutter) Multi line message again
|
2017-09-13 15:36:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) Multi line message again
|
||||||
and it goes...
|
and it goes...
|
||||||
and goes...
|
and goes...
|
||||||
2017-09-13 15:36:57.228948-0700 localhost Runner[37195]: (Flutter) Single line message, not the part of the above
|
2017-09-13 15:36:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) Single line message, not the part of the above
|
||||||
'''
|
'''
|
||||||
))
|
))
|
||||||
..addCommand(const FakeCommand(
|
..addCommand(const FakeCommand(
|
||||||
@ -657,7 +659,7 @@ void main() {
|
|||||||
|
|
||||||
group('unified logging', () {
|
group('unified logging', () {
|
||||||
testUsingContext('log reader handles escaped multiline messages', () async {
|
testUsingContext('log reader handles escaped multiline messages', () async {
|
||||||
const String logPredicate = 'eventType = logEvent AND processImagePath ENDSWITH "Runner" '
|
const String logPredicate = 'eventType = logEvent AND processImagePath ENDSWITH "My Super Awesome App" '
|
||||||
'AND (senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" '
|
'AND (senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" '
|
||||||
'OR processImageUUID == senderImageUUID) AND NOT(eventMessage CONTAINS ": could not find icon '
|
'OR processImageUUID == senderImageUUID) AND NOT(eventMessage CONTAINS ": could not find icon '
|
||||||
'for representation -> com.apple.") AND NOT(eventMessage BEGINSWITH "assertion failed: ") '
|
'for representation -> com.apple.") AND NOT(eventMessage BEGINSWITH "assertion failed: ") '
|
||||||
|
@ -466,6 +466,39 @@ apply plugin: 'kotlin-android'
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('application bundle name', () {
|
||||||
|
MemoryFileSystem fs;
|
||||||
|
MockXcodeProjectInterpreter mockXcodeProjectInterpreter;
|
||||||
|
setUp(() {
|
||||||
|
fs = MemoryFileSystem();
|
||||||
|
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('app product name defaults to Runner.app', () async {
|
||||||
|
final FlutterProject project = await someProject();
|
||||||
|
expect(await project.ios.hostAppBundleName, 'Runner.app');
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fs,
|
||||||
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
|
XcodeProjectInterpreter: () => mockXcodeProjectInterpreter
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('app product name xcodebuild settings', () async {
|
||||||
|
final FlutterProject project = await someProject();
|
||||||
|
when(mockXcodeProjectInterpreter.getBuildSettings(any, any)).thenAnswer((_) {
|
||||||
|
return Future<Map<String,String>>.value(<String, String>{
|
||||||
|
'FULL_PRODUCT_NAME': 'My App.app'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await project.ios.hostAppBundleName, 'My App.app');
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fs,
|
||||||
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
|
XcodeProjectInterpreter: () => mockXcodeProjectInterpreter
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
group('organization names set', () {
|
group('organization names set', () {
|
||||||
testInMemory('is empty, if project not created', () async {
|
testInMemory('is empty, if project not created', () async {
|
||||||
final FlutterProject project = await someProject();
|
final FlutterProject project = await someProject();
|
||||||
|
@ -45,7 +45,7 @@ class MockApplicationPackageStore extends ApplicationPackageStore {
|
|||||||
versionCode: 1,
|
versionCode: 1,
|
||||||
launchActivity: 'io.flutter.android.mock.MockActivity',
|
launchActivity: 'io.flutter.android.mock.MockActivity',
|
||||||
),
|
),
|
||||||
iOS: BuildableIOSApp(MockIosProject(), MockIosProject.bundleId),
|
iOS: BuildableIOSApp(MockIosProject(), MockIosProject.bundleId, MockIosProject.appBundleName),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,12 +529,13 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery {
|
|||||||
|
|
||||||
class MockIosProject extends Mock implements IosProject {
|
class MockIosProject extends Mock implements IosProject {
|
||||||
static const String bundleId = 'com.example.test';
|
static const String bundleId = 'com.example.test';
|
||||||
|
static const String appBundleName = 'My Super Awesome App.app';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> get productBundleIdentifier async => bundleId;
|
Future<String> get productBundleIdentifier async => bundleId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get hostAppBundleName => 'Runner.app';
|
Future<String> get hostAppBundleName async => appBundleName;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockAndroidDevice extends Mock implements AndroidDevice {
|
class MockAndroidDevice extends Mock implements AndroidDevice {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user