Allow Xcode project Info.plist to be moved (#47993)
This commit is contained in:
parent
c3406914a8
commit
a6c3ffe10c
@ -354,8 +354,8 @@ class IosProject implements XcodeBasedProject {
|
||||
@override
|
||||
File get podManifestLock => hostAppRoot.childDirectory('Pods').childFile('Manifest.lock');
|
||||
|
||||
/// The 'Info.plist' file of the host app.
|
||||
File get hostInfoPlist => hostAppRoot.childDirectory(_hostAppBundleName).childFile('Info.plist');
|
||||
/// 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');
|
||||
|
||||
@override
|
||||
Directory get symlinks => _flutterLibRoot.childDirectory('.symlinks');
|
||||
@ -384,27 +384,43 @@ class IosProject implements XcodeBasedProject {
|
||||
/// iOS tooling needed to read it is not installed.
|
||||
Future<String> get productBundleIdentifier async {
|
||||
String fromPlist;
|
||||
try {
|
||||
fromPlist = PlistParser.instance.getValueFromFile(
|
||||
hostInfoPlist.path,
|
||||
PlistParser.kCFBundleIdentifierKey,
|
||||
);
|
||||
} on FileNotFoundException {
|
||||
// iOS tooling not found; likely not running OSX; let [fromPlist] be null
|
||||
final File defaultInfoPlist = defaultHostInfoPlist;
|
||||
// Users can change the location of the Info.plist.
|
||||
// Try parsing the default, first.
|
||||
if (defaultInfoPlist.existsSync()) {
|
||||
try {
|
||||
fromPlist = PlistParser.instance.getValueFromFile(
|
||||
defaultHostInfoPlist.path,
|
||||
PlistParser.kCFBundleIdentifierKey,
|
||||
);
|
||||
} on FileNotFoundException {
|
||||
// iOS tooling not found; likely not running OSX; let [fromPlist] be null
|
||||
}
|
||||
if (fromPlist != null && !fromPlist.contains('\$')) {
|
||||
// Info.plist has no build variables in product bundle ID.
|
||||
return fromPlist;
|
||||
}
|
||||
}
|
||||
if (fromPlist != null && !fromPlist.contains('\$')) {
|
||||
// Info.plist has no build variables in product bundle ID.
|
||||
return fromPlist;
|
||||
final Map<String, String> allBuildSettings = await buildSettings;
|
||||
if (allBuildSettings != null) {
|
||||
if (fromPlist != null) {
|
||||
// Perform variable substitution using build settings.
|
||||
return xcode.substituteXcodeVariables(fromPlist, allBuildSettings);
|
||||
}
|
||||
return allBuildSettings['PRODUCT_BUNDLE_IDENTIFIER'];
|
||||
}
|
||||
|
||||
// On non-macOS platforms, parse the first PRODUCT_BUNDLE_IDENTIFIER from
|
||||
// the project file. This can return the wrong bundle identifier if additional
|
||||
// bundles have been added to the project and are found first, like frameworks
|
||||
// or companion watchOS projects. However, on non-macOS platforms this is
|
||||
// only used for display purposes and to regenerate organization names, so
|
||||
// best-effort is probably fine.
|
||||
final String fromPbxproj = _firstMatchInFile(xcodeProjectInfoFile, _productBundleIdPattern)?.group(2);
|
||||
if (fromPbxproj != null && (fromPlist == null || fromPlist == _productBundleIdVariable)) {
|
||||
// Common case. Avoids parsing build settings.
|
||||
return fromPbxproj;
|
||||
}
|
||||
if (fromPlist != null && xcode.xcodeProjectInterpreter.isInstalled) {
|
||||
// General case: perform variable substitution using build settings.
|
||||
return xcode.substituteXcodeVariables(fromPlist, await buildSettings);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -415,11 +431,17 @@ class IosProject implements XcodeBasedProject {
|
||||
if (!xcode.xcodeProjectInterpreter.isInstalled) {
|
||||
return null;
|
||||
}
|
||||
_buildSettings ??= await xcode.xcodeProjectInterpreter.getBuildSettings(
|
||||
Map<String, String> buildSettings = _buildSettings;
|
||||
buildSettings ??= await xcode.xcodeProjectInterpreter.getBuildSettings(
|
||||
xcodeProject.path,
|
||||
_hostAppBundleName,
|
||||
);
|
||||
return _buildSettings;
|
||||
if (buildSettings != null && buildSettings.isNotEmpty) {
|
||||
// No timeouts, flakes, or errors.
|
||||
_buildSettings = buildSettings;
|
||||
return buildSettings;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, String> _buildSettings;
|
||||
|
@ -348,32 +348,54 @@ apply plugin: 'kotlin-android'
|
||||
});
|
||||
}
|
||||
|
||||
testWithMocks('null, if no pbxproj or plist entries', () async {
|
||||
testWithMocks('null, if no build settings or plist entries', () async {
|
||||
final FlutterProject project = await someProject();
|
||||
expect(await project.ios.productBundleIdentifier, isNull);
|
||||
});
|
||||
testWithMocks('from pbxproj file, if no plist', () async {
|
||||
|
||||
testWithMocks('from build settings, if no plist', () async {
|
||||
final FlutterProject project = await someProject();
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, any)).thenAnswer(
|
||||
(_) {
|
||||
return Future<Map<String,String>>.value(<String, String>{
|
||||
'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject',
|
||||
});
|
||||
}
|
||||
);
|
||||
expect(await project.ios.productBundleIdentifier, 'io.flutter.someProject');
|
||||
});
|
||||
|
||||
testWithMocks('from project file, if no plist or build settings', () async {
|
||||
final FlutterProject project = await someProject();
|
||||
addIosProjectFile(project.directory, projectFileContent: () {
|
||||
return projectFileWithBundleId('io.flutter.someProject');
|
||||
});
|
||||
expect(await project.ios.productBundleIdentifier, 'io.flutter.someProject');
|
||||
});
|
||||
|
||||
testWithMocks('from plist, if no variables', () async {
|
||||
final FlutterProject project = await someProject();
|
||||
project.ios.defaultHostInfoPlist.createSync(recursive: true);
|
||||
when(mockPlistUtils.getValueFromFile(any, any)).thenReturn('io.flutter.someProject');
|
||||
expect(await project.ios.productBundleIdentifier, 'io.flutter.someProject');
|
||||
});
|
||||
testWithMocks('from pbxproj and plist, if default variable', () async {
|
||||
|
||||
testWithMocks('from build settings and plist, if default variable', () async {
|
||||
final FlutterProject project = await someProject();
|
||||
addIosProjectFile(project.directory, projectFileContent: () {
|
||||
return projectFileWithBundleId('io.flutter.someProject');
|
||||
});
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, any)).thenAnswer(
|
||||
(_) {
|
||||
return Future<Map<String,String>>.value(<String, String>{
|
||||
'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject',
|
||||
});
|
||||
}
|
||||
);
|
||||
when(mockPlistUtils.getValueFromFile(any, any)).thenReturn('\$(PRODUCT_BUNDLE_IDENTIFIER)');
|
||||
expect(await project.ios.productBundleIdentifier, 'io.flutter.someProject');
|
||||
});
|
||||
testWithMocks('from pbxproj and plist, by substitution', () async {
|
||||
|
||||
testWithMocks('from build settings and plist, by substitution', () async {
|
||||
final FlutterProject project = await someProject();
|
||||
project.ios.defaultHostInfoPlist.createSync(recursive: true);
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, any)).thenAnswer(
|
||||
(_) {
|
||||
return Future<Map<String,String>>.value(<String, String>{
|
||||
|
Loading…
x
Reference in New Issue
Block a user