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
|
@override
|
||||||
File get podManifestLock => hostAppRoot.childDirectory('Pods').childFile('Manifest.lock');
|
File get podManifestLock => hostAppRoot.childDirectory('Pods').childFile('Manifest.lock');
|
||||||
|
|
||||||
/// The 'Info.plist' file of the host app.
|
/// The default 'Info.plist' file of the host app. The developer can change this location in Xcode.
|
||||||
File get hostInfoPlist => hostAppRoot.childDirectory(_hostAppBundleName).childFile('Info.plist');
|
File get defaultHostInfoPlist => hostAppRoot.childDirectory(_hostAppBundleName).childFile('Info.plist');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Directory get symlinks => _flutterLibRoot.childDirectory('.symlinks');
|
Directory get symlinks => _flutterLibRoot.childDirectory('.symlinks');
|
||||||
@ -384,27 +384,43 @@ class IosProject implements XcodeBasedProject {
|
|||||||
/// 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 {
|
||||||
String fromPlist;
|
String fromPlist;
|
||||||
try {
|
final File defaultInfoPlist = defaultHostInfoPlist;
|
||||||
fromPlist = PlistParser.instance.getValueFromFile(
|
// Users can change the location of the Info.plist.
|
||||||
hostInfoPlist.path,
|
// Try parsing the default, first.
|
||||||
PlistParser.kCFBundleIdentifierKey,
|
if (defaultInfoPlist.existsSync()) {
|
||||||
);
|
try {
|
||||||
} on FileNotFoundException {
|
fromPlist = PlistParser.instance.getValueFromFile(
|
||||||
// iOS tooling not found; likely not running OSX; let [fromPlist] be null
|
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('\$')) {
|
final Map<String, String> allBuildSettings = await buildSettings;
|
||||||
// Info.plist has no build variables in product bundle ID.
|
if (allBuildSettings != null) {
|
||||||
return fromPlist;
|
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);
|
final String fromPbxproj = _firstMatchInFile(xcodeProjectInfoFile, _productBundleIdPattern)?.group(2);
|
||||||
if (fromPbxproj != null && (fromPlist == null || fromPlist == _productBundleIdVariable)) {
|
if (fromPbxproj != null && (fromPlist == null || fromPlist == _productBundleIdVariable)) {
|
||||||
// Common case. Avoids parsing build settings.
|
|
||||||
return fromPbxproj;
|
return fromPbxproj;
|
||||||
}
|
}
|
||||||
if (fromPlist != null && xcode.xcodeProjectInterpreter.isInstalled) {
|
|
||||||
// General case: perform variable substitution using build settings.
|
|
||||||
return xcode.substituteXcodeVariables(fromPlist, await buildSettings);
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,11 +431,17 @@ class IosProject implements XcodeBasedProject {
|
|||||||
if (!xcode.xcodeProjectInterpreter.isInstalled) {
|
if (!xcode.xcodeProjectInterpreter.isInstalled) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
_buildSettings ??= await xcode.xcodeProjectInterpreter.getBuildSettings(
|
Map<String, String> buildSettings = _buildSettings;
|
||||||
|
buildSettings ??= await xcode.xcodeProjectInterpreter.getBuildSettings(
|
||||||
xcodeProject.path,
|
xcodeProject.path,
|
||||||
_hostAppBundleName,
|
_hostAppBundleName,
|
||||||
);
|
);
|
||||||
return _buildSettings;
|
if (buildSettings != null && buildSettings.isNotEmpty) {
|
||||||
|
// No timeouts, flakes, or errors.
|
||||||
|
_buildSettings = buildSettings;
|
||||||
|
return buildSettings;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, String> _buildSettings;
|
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();
|
final FlutterProject project = await someProject();
|
||||||
expect(await project.ios.productBundleIdentifier, isNull);
|
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();
|
final FlutterProject project = await someProject();
|
||||||
addIosProjectFile(project.directory, projectFileContent: () {
|
addIosProjectFile(project.directory, projectFileContent: () {
|
||||||
return projectFileWithBundleId('io.flutter.someProject');
|
return projectFileWithBundleId('io.flutter.someProject');
|
||||||
});
|
});
|
||||||
expect(await project.ios.productBundleIdentifier, 'io.flutter.someProject');
|
expect(await project.ios.productBundleIdentifier, 'io.flutter.someProject');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithMocks('from plist, if no variables', () async {
|
testWithMocks('from plist, if no variables', () async {
|
||||||
final FlutterProject project = await someProject();
|
final FlutterProject project = await someProject();
|
||||||
|
project.ios.defaultHostInfoPlist.createSync(recursive: true);
|
||||||
when(mockPlistUtils.getValueFromFile(any, any)).thenReturn('io.flutter.someProject');
|
when(mockPlistUtils.getValueFromFile(any, any)).thenReturn('io.flutter.someProject');
|
||||||
expect(await project.ios.productBundleIdentifier, '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();
|
final FlutterProject project = await someProject();
|
||||||
addIosProjectFile(project.directory, projectFileContent: () {
|
when(mockXcodeProjectInterpreter.getBuildSettings(any, any)).thenAnswer(
|
||||||
return projectFileWithBundleId('io.flutter.someProject');
|
(_) {
|
||||||
});
|
return Future<Map<String,String>>.value(<String, String>{
|
||||||
|
'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
when(mockPlistUtils.getValueFromFile(any, any)).thenReturn('\$(PRODUCT_BUNDLE_IDENTIFIER)');
|
when(mockPlistUtils.getValueFromFile(any, any)).thenReturn('\$(PRODUCT_BUNDLE_IDENTIFIER)');
|
||||||
expect(await project.ios.productBundleIdentifier, 'io.flutter.someProject');
|
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();
|
final FlutterProject project = await someProject();
|
||||||
|
project.ios.defaultHostInfoPlist.createSync(recursive: true);
|
||||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, any)).thenAnswer(
|
when(mockXcodeProjectInterpreter.getBuildSettings(any, any)).thenAnswer(
|
||||||
(_) {
|
(_) {
|
||||||
return Future<Map<String,String>>.value(<String, String>{
|
return Future<Map<String,String>>.value(<String, String>{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user