Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
b00f1c45
Unverified
Commit
b00f1c45
authored
May 04, 2023
by
chunhtai
Committed by
GitHub
May 04, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Adding vmservice to get iOS app settings (#123156)
fixes
https://github.com/flutter/flutter/issues/120405
parent
529b919f
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
462 additions
and
189 deletions
+462
-189
build_ios.dart
packages/flutter_tools/lib/src/commands/build_ios.dart
+5
-5
intellij_validator.dart
...es/flutter_tools/lib/src/intellij/intellij_validator.dart
+2
-2
application_package.dart
packages/flutter_tools/lib/src/ios/application_package.dart
+1
-1
devices.dart
packages/flutter_tools/lib/src/ios/devices.dart
+1
-1
plist_parser.dart
packages/flutter_tools/lib/src/ios/plist_parser.dart
+8
-4
simulators.dart
packages/flutter_tools/lib/src/ios/simulators.dart
+1
-1
xcodeproj.dart
packages/flutter_tools/lib/src/ios/xcodeproj.dart
+23
-2
flutter_application_migration.dart
...b/src/macos/migrations/flutter_application_migration.dart
+1
-1
vmservice.dart
packages/flutter_tools/lib/src/vmservice.dart
+20
-0
xcode_project.dart
packages/flutter_tools/lib/src/xcode_project.dart
+128
-24
build_ipa_test.dart
...er_tools/test/commands.shard/hermetic/build_ipa_test.dart
+2
-2
macos_project_migration_test.dart
...est/general.shard/macos/macos_project_migration_test.dart
+4
-4
project_test.dart
packages/flutter_tools/test/general.shard/project_test.dart
+235
-115
resident_web_runner_test.dart
...er_tools/test/general.shard/resident_web_runner_test.dart
+4
-0
plist_parser_test.dart
...utter_tools/test/integration.shard/plist_parser_test.dart
+25
-25
fakes.dart
packages/flutter_tools/test/src/fakes.dart
+2
-2
No files found.
packages/flutter_tools/lib/src/commands/build_ios.dart
View file @
b00f1c45
...
...
@@ -376,11 +376,11 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
final
Map
<
String
,
String
?>
xcodeProjectSettingsMap
=
<
String
,
String
?>{};
xcodeProjectSettingsMap
[
'Version Number'
]
=
globals
.
plistParser
.
get
StringValueFromFile
(
plistPath
,
PlistParser
.
kCFBundleShortVersionStringKey
);
xcodeProjectSettingsMap
[
'Build Number'
]
=
globals
.
plistParser
.
get
StringValueFromFile
(
plistPath
,
PlistParser
.
kCFBundleVersionKey
);
xcodeProjectSettingsMap
[
'Display Name'
]
=
globals
.
plistParser
.
get
StringValueFromFile
(
plistPath
,
PlistParser
.
kCFBundleDisplayNameKey
);
xcodeProjectSettingsMap
[
'Deployment Target'
]
=
globals
.
plistParser
.
get
StringValueFromFile
(
plistPath
,
PlistParser
.
kMinimumOSVersionKey
);
xcodeProjectSettingsMap
[
'Bundle Identifier'
]
=
globals
.
plistParser
.
get
StringValueFromFile
(
plistPath
,
PlistParser
.
kCFBundleIdentifierKey
);
xcodeProjectSettingsMap
[
'Version Number'
]
=
globals
.
plistParser
.
get
ValueFromFile
<
String
>
(
plistPath
,
PlistParser
.
kCFBundleShortVersionStringKey
);
xcodeProjectSettingsMap
[
'Build Number'
]
=
globals
.
plistParser
.
get
ValueFromFile
<
String
>
(
plistPath
,
PlistParser
.
kCFBundleVersionKey
);
xcodeProjectSettingsMap
[
'Display Name'
]
=
globals
.
plistParser
.
get
ValueFromFile
<
String
>
(
plistPath
,
PlistParser
.
kCFBundleDisplayNameKey
);
xcodeProjectSettingsMap
[
'Deployment Target'
]
=
globals
.
plistParser
.
get
ValueFromFile
<
String
>
(
plistPath
,
PlistParser
.
kMinimumOSVersionKey
);
xcodeProjectSettingsMap
[
'Bundle Identifier'
]
=
globals
.
plistParser
.
get
ValueFromFile
<
String
>
(
plistPath
,
PlistParser
.
kCFBundleIdentifierKey
);
final
List
<
ValidationMessage
>
validationMessages
=
xcodeProjectSettingsMap
.
entries
.
map
((
MapEntry
<
String
,
String
?>
entry
)
{
final
String
title
=
entry
.
key
;
...
...
packages/flutter_tools/lib/src/intellij/intellij_validator.dart
View file @
b00f1c45
...
...
@@ -494,7 +494,7 @@ class IntelliJValidatorOnMac extends IntelliJValidator {
@override
String
get
version
{
return
_version
??=
_plistParser
.
get
StringValueFromFile
(
return
_version
??=
_plistParser
.
get
ValueFromFile
<
String
>
(
plistFile
,
PlistParser
.
kCFBundleShortVersionStringKey
,
)
??
'unknown'
;
...
...
@@ -508,7 +508,7 @@ class IntelliJValidatorOnMac extends IntelliJValidator {
}
final
String
?
altLocation
=
_plistParser
.
get
StringValueFromFile
(
plistFile
,
'JetBrainsToolboxApp'
);
.
get
ValueFromFile
<
String
>
(
plistFile
,
'JetBrainsToolboxApp'
);
if
(
altLocation
!=
null
)
{
_pluginsPath
=
'
$altLocation
.plugins'
;
...
...
packages/flutter_tools/lib/src/ios/application_package.dart
View file @
b00f1c45
...
...
@@ -58,7 +58,7 @@ abstract class IOSApp extends ApplicationPackage {
globals
.
printError
(
'Invalid prebuilt iOS app. Does not contain Info.plist.'
);
return
null
;
}
final
String
?
id
=
globals
.
plistParser
.
get
StringValueFromFile
(
final
String
?
id
=
globals
.
plistParser
.
get
ValueFromFile
<
String
>
(
plistPath
,
PlistParser
.
kCFBundleIdentifierKey
,
);
...
...
packages/flutter_tools/lib/src/ios/devices.dart
View file @
b00f1c45
...
...
@@ -451,7 +451,7 @@ class IOSDevice extends Device {
_logger
.
printError
(
''
);
return
LaunchResult
.
failed
();
}
packageId
=
buildResult
.
xcodeBuildExecution
?.
buildSettings
[
'PRODUCT_BUNDLE_IDENTIFIER'
];
packageId
=
buildResult
.
xcodeBuildExecution
?.
buildSettings
[
IosProject
.
kProductBundleIdKey
];
}
packageId
??=
package
.
id
;
...
...
packages/flutter_tools/lib/src/ios/plist_parser.dart
View file @
b00f1c45
...
...
@@ -24,6 +24,7 @@ class PlistParser {
final
Logger
_logger
;
final
ProcessUtils
_processUtils
;
// info.pList keys
static
const
String
kCFBundleIdentifierKey
=
'CFBundleIdentifier'
;
static
const
String
kCFBundleShortVersionStringKey
=
'CFBundleShortVersionString'
;
static
const
String
kCFBundleExecutableKey
=
'CFBundleExecutable'
;
...
...
@@ -32,6 +33,9 @@ class PlistParser {
static
const
String
kMinimumOSVersionKey
=
'MinimumOSVersion'
;
static
const
String
kNSPrincipalClassKey
=
'NSPrincipalClass'
;
// entitlement file keys
static
const
String
kAssociatedDomainsKey
=
'com.apple.developer.associated-domains'
;
static
const
String
_plutilExecutable
=
'/usr/bin/plutil'
;
/// Returns the content, converted to XML, of the plist file located at
...
...
@@ -164,8 +168,8 @@ class PlistParser {
return
null
;
}
/// Parses the Plist file located at [plistFilePath] and returns the
string
///
value
that's associated with the specified [key] within the property list.
/// Parses the Plist file located at [plistFilePath] and returns the
value
/// that's associated with the specified [key] within the property list.
///
/// If [plistFilePath] points to a non-existent file or a file that's not a
/// valid property list file, this will return null.
...
...
@@ -173,8 +177,8 @@ class PlistParser {
/// If [key] is not found in the property list, this will return null.
///
/// The [plistFilePath] and [key] arguments must not be null.
String
?
getStringValueFromFile
(
String
plistFilePath
,
String
key
)
{
T
?
getValueFromFile
<
T
>
(
String
plistFilePath
,
String
key
)
{
final
Map
<
String
,
dynamic
>
parsed
=
parseFile
(
plistFilePath
);
return
parsed
[
key
]
as
String
?;
return
parsed
[
key
]
as
T
?;
}
}
packages/flutter_tools/lib/src/ios/simulators.dart
View file @
b00f1c45
...
...
@@ -468,7 +468,7 @@ class IOSSimulator extends Device {
// parsing the xcodeproj or configuration files.
// See https://github.com/flutter/flutter/issues/31037 for more information.
final
String
plistPath
=
globals
.
fs
.
path
.
join
(
package
.
simulatorBundlePath
,
'Info.plist'
);
final
String
?
bundleIdentifier
=
globals
.
plistParser
.
get
StringValueFromFile
(
plistPath
,
PlistParser
.
kCFBundleIdentifierKey
);
final
String
?
bundleIdentifier
=
globals
.
plistParser
.
get
ValueFromFile
<
String
>
(
plistPath
,
PlistParser
.
kCFBundleIdentifierKey
);
if
(
bundleIdentifier
==
null
)
{
globals
.
printError
(
'Invalid prebuilt iOS app. Info.plist does not contain bundle identifier'
);
return
LaunchResult
.
failed
();
...
...
packages/flutter_tools/lib/src/ios/xcodeproj.dart
View file @
b00f1c45
...
...
@@ -185,6 +185,7 @@ class XcodeProjectInterpreter {
final
Status
status
=
_logger
.
startSpinner
();
final
String
?
scheme
=
buildContext
.
scheme
;
final
String
?
configuration
=
buildContext
.
configuration
;
final
String
?
target
=
buildContext
.
target
;
final
String
?
deviceId
=
buildContext
.
deviceId
;
final
List
<
String
>
showBuildSettingsCommand
=
<
String
>[
...
xcrunCommand
(),
...
...
@@ -195,6 +196,8 @@ class XcodeProjectInterpreter {
...<
String
>[
'-scheme'
,
scheme
],
if
(
configuration
!=
null
)
...<
String
>[
'-configuration'
,
configuration
],
if
(
target
!=
null
)
...<
String
>[
'-target'
,
target
],
if
(
buildContext
.
environmentType
==
EnvironmentType
.
simulator
)
...<
String
>[
'-sdk'
,
'iphonesimulator'
],
'-destination'
,
...
...
@@ -380,6 +383,7 @@ class XcodeProjectBuildContext {
this
.
configuration
,
this
.
environmentType
=
EnvironmentType
.
physical
,
this
.
deviceId
,
this
.
target
,
this
.
isWatch
=
false
,
});
...
...
@@ -387,10 +391,11 @@ class XcodeProjectBuildContext {
final
String
?
configuration
;
final
EnvironmentType
environmentType
;
final
String
?
deviceId
;
final
String
?
target
;
final
bool
isWatch
;
@override
int
get
hashCode
=>
Object
.
hash
(
scheme
,
configuration
,
environmentType
,
deviceId
);
int
get
hashCode
=>
Object
.
hash
(
scheme
,
configuration
,
environmentType
,
deviceId
,
target
);
@override
bool
operator
==(
Object
other
)
{
...
...
@@ -402,10 +407,26 @@ class XcodeProjectBuildContext {
other
.
configuration
==
configuration
&&
other
.
deviceId
==
deviceId
&&
other
.
environmentType
==
environmentType
&&
other
.
isWatch
==
isWatch
;
other
.
isWatch
==
isWatch
&&
other
.
target
==
target
;
}
}
/// The settings that are relevant for setting up universal links
@immutable
class
XcodeUniversalLinkSettings
{
const
XcodeUniversalLinkSettings
({
this
.
bundleIdentifier
,
this
.
teamIdentifier
,
this
.
associatedDomains
=
const
<
String
>[],
});
final
String
?
bundleIdentifier
;
final
String
?
teamIdentifier
;
final
List
<
String
>
associatedDomains
;
}
/// Information about an Xcode project.
///
/// Represents the output of `xcodebuild -list`.
...
...
packages/flutter_tools/lib/src/macos/migrations/flutter_application_migration.dart
View file @
b00f1c45
...
...
@@ -27,7 +27,7 @@ class FlutterApplicationMigration extends ProjectMigrator {
void
migrate
()
{
if
(
_infoPlistFile
.
existsSync
())
{
final
String
?
principalClass
=
globals
.
plistParser
.
get
StringValueFromFile
(
_infoPlistFile
.
path
,
PlistParser
.
kNSPrincipalClassKey
);
globals
.
plistParser
.
get
ValueFromFile
<
String
>
(
_infoPlistFile
.
path
,
PlistParser
.
kNSPrincipalClassKey
);
if
(
principalClass
==
null
||
principalClass
==
'NSApplication'
)
{
// No NSPrincipalClass defined, or already converted. No migration
// needed.
...
...
packages/flutter_tools/lib/src/vmservice.dart
View file @
b00f1c45
...
...
@@ -41,6 +41,7 @@ const String kFlutterMemoryInfoServiceName = 'flutterMemoryInfo';
const
String
kFlutterGetSkSLServiceName
=
'flutterGetSkSL'
;
const
String
kFlutterGetIOSBuildOptionsServiceName
=
'flutterGetIOSBuildOptions'
;
const
String
kFlutterGetAndroidBuildVariantsServiceName
=
'flutterGetAndroidBuildVariants'
;
const
String
kFlutterGetIOSDeeplinkSettingsServiceName
=
'flutterGetIOSDeeplinkSettings'
;
/// The error response code from an unrecoverable compilation failure.
const
int
kIsolateReloadBarred
=
1005
;
...
...
@@ -337,6 +338,25 @@ Future<vm_service.VmService> setUpVmService({
registrationRequests
.
add
(
vmService
.
registerService
(
kFlutterGetAndroidBuildVariantsServiceName
,
kFlutterToolAlias
),
);
vmService
.
registerServiceCallback
(
kFlutterGetIOSDeeplinkSettingsServiceName
,
(
Map
<
String
,
Object
?>
params
)
async
{
final
XcodeUniversalLinkSettings
settings
=
await
flutterProject
.
ios
.
universalLinkSettings
(
configuration:
params
[
'configuration'
]!
as
String
,
scheme:
params
[
'scheme'
]!
as
String
,
target:
params
[
'target'
]!
as
String
,
);
return
<
String
,
Object
>{
'result'
:
<
String
,
Object
>{
kResultType:
kResultTypeSuccess
,
'bundleIdentifier'
:
settings
.
bundleIdentifier
??
''
,
'teamIdentifier'
:
settings
.
teamIdentifier
??
''
,
'associatedDomains'
:
settings
.
associatedDomains
,
},
};
});
registrationRequests
.
add
(
vmService
.
registerService
(
kFlutterGetIOSDeeplinkSettingsServiceName
,
'Flutter Tools'
),
);
}
if
(
printStructuredErrorLogMethod
!=
null
)
{
...
...
packages/flutter_tools/lib/src/xcode_project.dart
View file @
b00f1c45
...
...
@@ -124,8 +124,16 @@ class IosProject extends XcodeBasedProject {
@override
String
get
pluginConfigKey
=>
IOSPlugin
.
kConfigKey
;
static
final
RegExp
_productBundleIdPattern
=
RegExp
(
r''
'^
\
s*PRODUCT_BUNDLE_IDENTIFIER
\
s*=
\
s*(["'
]?)(.*?)
\
1
;
\
s
*
$
''');
static const String _productBundleIdVariable = r'
$
(
PRODUCT_BUNDLE_IDENTIFIER
)
';
// build setting keys
static
const
String
kProductBundleIdKey
=
'PRODUCT_BUNDLE_IDENTIFIER'
;
static
const
String
kTeamIdKey
=
'DEVELOPMENT_TEAM'
;
static
const
String
kEntitlementFilePathKey
=
'CODE_SIGN_ENTITLEMENTS'
;
static
const
String
kHostAppBundleNameKey
=
'FULL_PRODUCT_NAME'
;
static
final
RegExp
_productBundleIdPattern
=
RegExp
(
'^
\\
s*
$kProductBundleIdKey
\\
s*=
\\
s*(["
\'
]?)(.*?)
\\
1;
\\
s*
\$
'
);
static
const
String
_kProductBundleIdVariable
=
'
\$
(
$kProductBundleIdKey
)'
;
static
final
RegExp
_associatedDomainPattern
=
RegExp
(
r'^applinks:(.*)'
);
Directory
get
ephemeralModuleDirectory
=>
parent
.
directory
.
childDirectory
(
'.ios'
);
Directory
get
_editableDirectory
=>
parent
.
directory
.
childDirectory
(
'ios'
);
...
...
@@ -203,24 +211,71 @@ class IosProject extends XcodeBasedProject {
return
parent
.
isModule
||
_editableDirectory
.
existsSync
();
}
Future
<
XcodeUniversalLinkSettings
>
universalLinkSettings
({
required
String
configuration
,
required
String
scheme
,
required
String
target
,
})
async
{
final
XcodeProjectBuildContext
context
=
XcodeProjectBuildContext
(
configuration:
configuration
,
scheme:
scheme
,
target:
target
,
);
return
XcodeUniversalLinkSettings
(
bundleIdentifier:
await
_productBundleIdentifierWithBuildContext
(
context
),
teamIdentifier:
await
_getTeamIdentifier
(
context
),
associatedDomains:
await
_getAssociatedDomains
(
context
),
);
}
/// The product bundle identifier of the host app, or null if not set or if
/// iOS tooling needed to read it is not installed.
Future
<
String
?>
productBundleIdentifier
(
BuildInfo
?
buildInfo
)
async
{
if
(!
existsSync
())
{
return
null
;
}
return _productBundleIdentifier ??= await _parseProductBundleIdentifier(buildInfo);
XcodeProjectBuildContext
?
buildContext
;
final
XcodeProjectInfo
?
info
=
await
projectInfo
();
if
(
info
!=
null
)
{
final
String
?
scheme
=
info
.
schemeFor
(
buildInfo
);
if
(
scheme
==
null
)
{
info
.
reportFlavorNotFoundAndExit
();
}
final
String
?
configuration
=
info
.
buildConfigurationFor
(
buildInfo
,
scheme
,
);
buildContext
=
XcodeProjectBuildContext
(
configuration:
configuration
,
scheme:
scheme
,
);
}
return
_productBundleIdentifierWithBuildContext
(
buildContext
);
}
Future
<
String
?>
_productBundleIdentifierWithBuildContext
(
XcodeProjectBuildContext
?
buildContext
)
async
{
if
(!
existsSync
())
{
return
null
;
}
if
(
_productBundleIdentifiers
.
containsKey
(
buildContext
))
{
return
_productBundleIdentifiers
[
buildContext
];
}
return
_productBundleIdentifiers
[
buildContext
]
=
await
_parseProductBundleIdentifier
(
buildContext
);
}
String? _productBundleIdentifier;
Future<String?> _parseProductBundleIdentifier(BuildInfo? buildInfo) async {
final
Map
<
XcodeProjectBuildContext
?,
String
?>
_productBundleIdentifiers
=
<
XcodeProjectBuildContext
?,
String
?>{};
Future
<
String
?>
_parseProductBundleIdentifier
(
XcodeProjectBuildContext
?
buildContext
)
async
{
String
?
fromPlist
;
final
File
defaultInfoPlist
=
defaultHostInfoPlist
;
// Users can change the location of the Info.plist.
// Try parsing the default, first.
if
(
defaultInfoPlist
.
existsSync
())
{
try
{
fromPlist = globals.plistParser.get
StringValueFromFile
(
fromPlist
=
globals
.
plistParser
.
get
ValueFromFile
<
String
>
(
defaultHostInfoPlist
.
path
,
PlistParser
.
kCFBundleIdentifierKey
,
);
...
...
@@ -232,13 +287,18 @@ class IosProject extends XcodeBasedProject {
return
fromPlist
;
}
}
final Map<String, String>? allBuildSettings = await buildSettingsForBuildInfo(buildInfo);
if
(
buildContext
==
null
)
{
// Getting build settings to evaluate info.Plist requires a context.
return
null
;
}
final
Map
<
String
,
String
>?
allBuildSettings
=
await
_buildSettingsForXcodeProjectBuildContext
(
buildContext
);
if
(
allBuildSettings
!=
null
)
{
if
(
fromPlist
!=
null
)
{
// Perform variable substitution using build settings.
return
substituteXcodeVariables
(
fromPlist
,
allBuildSettings
);
}
return allBuildSettings[
'
PRODUCT_BUNDLE_IDENTIFIER
'
];
return
allBuildSettings
[
kProductBundleIdKey
];
}
// On non-macOS platforms, parse the first PRODUCT_BUNDLE_IDENTIFIER from
...
...
@@ -248,13 +308,48 @@ class IosProject extends XcodeBasedProject {
// 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 == _
p
roductBundleIdVariable)) {
if
(
fromPbxproj
!=
null
&&
(
fromPlist
==
null
||
fromPlist
==
_
kP
roductBundleIdVariable
))
{
return
fromPbxproj
;
}
return
null
;
}
Future
<
String
?>
_getTeamIdentifier
(
XcodeProjectBuildContext
buildContext
)
async
{
final
Map
<
String
,
String
>?
buildSettings
=
await
_buildSettingsForXcodeProjectBuildContext
(
buildContext
);
if
(
buildSettings
!=
null
)
{
return
buildSettings
[
kTeamIdKey
];
}
return
null
;
}
Future
<
List
<
String
>>
_getAssociatedDomains
(
XcodeProjectBuildContext
buildContext
)
async
{
final
Map
<
String
,
String
>?
buildSettings
=
await
_buildSettingsForXcodeProjectBuildContext
(
buildContext
);
if
(
buildSettings
!=
null
)
{
final
String
?
entitlementPath
=
buildSettings
[
kEntitlementFilePathKey
];
if
(
entitlementPath
!=
null
)
{
final
File
entitlement
=
hostAppRoot
.
childFile
(
entitlementPath
);
if
(
entitlement
.
existsSync
())
{
final
List
<
String
>?
domains
=
globals
.
plistParser
.
getValueFromFile
<
List
<
Object
>>(
entitlement
.
path
,
PlistParser
.
kAssociatedDomainsKey
,
)?.
cast
<
String
>();
if
(
domains
!=
null
)
{
final
List
<
String
>
result
=
<
String
>[];
for
(
final
String
domain
in
domains
)
{
final
RegExpMatch
?
match
=
_associatedDomainPattern
.
firstMatch
(
domain
);
if
(
match
!=
null
)
{
result
.
add
(
match
.
group
(
1
)!);
}
}
return
result
;
}
}
}
}
return
const
<
String
>[];
}
/// The bundle name of the host app, `My App.app`.
Future
<
String
?>
hostAppBundleName
(
BuildInfo
?
buildInfo
)
async
{
if
(!
existsSync
())
{
...
...
@@ -273,11 +368,11 @@ class IosProject extends XcodeBasedProject {
if
(
globals
.
xcodeProjectInterpreter
?.
isInstalled
??
false
)
{
final
Map
<
String
,
String
>?
xcodeBuildSettings
=
await
buildSettingsForBuildInfo
(
buildInfo
);
if
(
xcodeBuildSettings
!=
null
)
{
productName
=
xcodeBuildSettings
[
'FULL_PRODUCT_NAME'
];
productName
=
xcodeBuildSettings
[
kHostAppBundleNameKey
];
}
}
if
(
productName
==
null
)
{
globals
.
printTrace
(
'
FULL_PRODUCT_NAME
not present, defaulting to
$hostAppProjectName
'
);
globals
.
printTrace
(
'
$kHostAppBundleNameKey
not present, defaulting to
$hostAppProjectName
'
);
}
return
productName
??
'
${XcodeBasedProject._defaultHostAppName}
.app'
;
}
...
...
@@ -287,9 +382,11 @@ class IosProject extends XcodeBasedProject {
/// Returns null, if iOS tooling is unavailable.
Future
<
Map
<
String
,
String
>?>
buildSettingsForBuildInfo
(
BuildInfo
?
buildInfo
,
{
String
?
scheme
,
String
?
configuration
,
String
?
target
,
EnvironmentType
environmentType
=
EnvironmentType
.
physical
,
String
?
deviceId
,
String
?
scheme
,
bool
isWatch
=
false
,
})
async
{
if
(!
existsSync
())
{
...
...
@@ -300,24 +397,31 @@ class IosProject extends XcodeBasedProject {
return
null
;
}
scheme
??=
info
.
schemeFor
(
buildInfo
);
if
(
scheme
==
null
)
{
scheme
=
info
.
schemeFor
(
buildInfo
);
if
(
scheme
==
null
)
{
info
.
reportFlavorNotFoundAndExit
();
}
info
.
reportFlavorNotFoundAndExit
();
}
final
String
?
configuration
=
(
await
projectInfo
())?.
buildConfigurationFor
(
configuration
??
=
(
await
projectInfo
())?.
buildConfigurationFor
(
buildInfo
,
scheme
,
);
final
XcodeProjectBuildContext
buildContext
=
XcodeProjectBuildContext
(
environmentType:
environmentType
,
scheme:
scheme
,
configuration:
configuration
,
deviceId:
deviceId
,
isWatch:
isWatch
,
return
_buildSettingsForXcodeProjectBuildContext
(
XcodeProjectBuildContext
(
environmentType:
environmentType
,
scheme:
scheme
,
configuration:
configuration
,
target:
target
,
deviceId:
deviceId
,
isWatch:
isWatch
,
),
);
}
Future
<
Map
<
String
,
String
>?>
_buildSettingsForXcodeProjectBuildContext
(
XcodeProjectBuildContext
buildContext
)
async
{
if
(!
existsSync
())
{
return
null
;
}
final
Map
<
String
,
String
>?
currentBuildSettings
=
_buildSettingsByBuildContext
[
buildContext
];
if
(
currentBuildSettings
==
null
)
{
final
Map
<
String
,
String
>?
calculatedBuildSettings
=
await
_xcodeProjectBuildSettings
(
buildContext
);
...
...
@@ -381,7 +485,7 @@ class IosProject extends XcodeBasedProject {
// In older versions of Xcode, if the target was a watchOS companion app,
// the Info.plist file of the target contained the key WKCompanionAppBundleIdentifier.
if
(
infoFile
.
existsSync
())
{
final
String
?
fromPlist
=
globals
.
plistParser
.
get
StringValueFromFile
(
infoFile
.
path
,
'WKCompanionAppBundleIdentifier'
);
final
String
?
fromPlist
=
globals
.
plistParser
.
get
ValueFromFile
<
String
>
(
infoFile
.
path
,
'WKCompanionAppBundleIdentifier'
);
if
(
bundleIdentifier
==
fromPlist
)
{
return
true
;
}
...
...
packages/flutter_tools/test/commands.shard/hermetic/build_ipa_test.dart
View file @
b00f1c45
...
...
@@ -62,8 +62,8 @@ class FakePlistUtils extends Fake implements PlistParser {
final
Map
<
String
,
Map
<
String
,
Object
>>
fileContents
=
<
String
,
Map
<
String
,
Object
>>{};
@override
String
?
getStringValueFromFile
(
String
plistFilePath
,
String
key
)
{
return
fileContents
[
plistFilePath
]![
key
]
as
String
?;
T
?
getValueFromFile
<
T
>
(
String
plistFilePath
,
String
key
)
{
return
fileContents
[
plistFilePath
]![
key
]
as
T
?;
}
}
...
...
packages/flutter_tools/test/general.shard/macos/macos_project_migration_test.dart
View file @
b00f1c45
...
...
@@ -329,7 +329,7 @@ platform :osx, '10.14'
);
infoPlistFile
.
writeAsStringSync
(
'contents'
);
// Just so it exists: parser is a fake.
macOSProjectMigration
.
migrate
();
expect
(
fakePlistParser
.
get
StringValueFromFile
(
infoPlistFile
.
path
,
PlistParser
.
kNSPrincipalClassKey
),
isNull
);
expect
(
fakePlistParser
.
get
ValueFromFile
<
String
>
(
infoPlistFile
.
path
,
PlistParser
.
kNSPrincipalClassKey
),
isNull
);
expect
(
testLogger
.
statusText
,
isEmpty
);
});
...
...
@@ -341,7 +341,7 @@ platform :osx, '10.14'
);
infoPlistFile
.
writeAsStringSync
(
'contents'
);
// Just so it exists: parser is a fake.
macOSProjectMigration
.
migrate
();
expect
(
fakePlistParser
.
get
StringValueFromFile
(
infoPlistFile
.
path
,
PlistParser
.
kNSPrincipalClassKey
),
'NSApplication'
);
expect
(
fakePlistParser
.
get
ValueFromFile
<
String
>
(
infoPlistFile
.
path
,
PlistParser
.
kNSPrincipalClassKey
),
'NSApplication'
);
expect
(
testLogger
.
statusText
,
isEmpty
);
});
...
...
@@ -353,7 +353,7 @@ platform :osx, '10.14'
);
infoPlistFile
.
writeAsStringSync
(
'contents'
);
// Just so it exists: parser is a fake.
macOSProjectMigration
.
migrate
();
expect
(
fakePlistParser
.
get
StringValueFromFile
(
infoPlistFile
.
path
,
PlistParser
.
kNSPrincipalClassKey
),
'NSApplication'
);
expect
(
fakePlistParser
.
get
ValueFromFile
<
String
>
(
infoPlistFile
.
path
,
PlistParser
.
kNSPrincipalClassKey
),
'NSApplication'
);
// Only print once.
expect
(
'Updating
${infoPlistFile.basename}
to use NSApplication instead of FlutterApplication.'
.
allMatches
(
testLogger
.
statusText
).
length
,
1
);
});
...
...
@@ -367,7 +367,7 @@ platform :osx, '10.14'
);
infoPlistFile
.
writeAsStringSync
(
'contents'
);
// Just so it exists: parser is a fake.
macOSProjectMigration
.
migrate
();
expect
(
fakePlistParser
.
get
StringValueFromFile
(
infoPlistFile
.
path
,
PlistParser
.
kNSPrincipalClassKey
),
differentApp
);
expect
(
fakePlistParser
.
get
ValueFromFile
<
String
>
(
infoPlistFile
.
path
,
PlistParser
.
kNSPrincipalClassKey
),
differentApp
);
expect
(
testLogger
.
traceText
,
isEmpty
);
});
});
...
...
packages/flutter_tools/test/general.shard/project_test.dart
View file @
b00f1c45
...
...
@@ -693,7 +693,7 @@ apply plugin: 'kotlin-android'
});
});
group
(
'
product bundle identifier
'
,
()
{
group
(
'
With mocked context
'
,
()
{
late
MemoryFileSystem
fs
;
late
FakePlistParser
testPlistUtils
;
late
FakeXcodeProjectInterpreter
xcodeProjectInterpreter
;
...
...
@@ -718,140 +718,260 @@ apply plugin: 'kotlin-android'
});
}
testWithMocks
(
'null, if no build settings or plist entries'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
isNull
);
});
group
(
'universal link'
,
()
{
testWithMocks
(
'build with flavor'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
xcodeProject
.
createSync
();
project
.
ios
.
defaultHostInfoPlist
.
createSync
(
recursive:
true
);
const
String
entitlementFilePath
=
'myEntitlement.Entitlement'
;
project
.
ios
.
hostAppRoot
.
childFile
(
entitlementFilePath
).
createSync
(
recursive:
true
);
const
XcodeProjectBuildContext
buildContext
=
XcodeProjectBuildContext
(
target:
'Runner'
,
scheme:
'Debug'
,
configuration:
'config'
,
);
xcodeProjectInterpreter
.
buildSettingsByBuildContext
[
buildContext
]
=
<
String
,
String
>{
IosProject
.
kProductBundleIdKey
:
'io.flutter.someProject'
,
IosProject
.
kTeamIdKey
:
'ABC'
,
IosProject
.
kEntitlementFilePathKey
:
entitlementFilePath
,
'SUFFIX'
:
'suffix'
,
};
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
testPlistUtils
.
setProperty
(
PlistParser
.
kCFBundleIdentifierKey
,
r'$(PRODUCT_BUNDLE_IDENTIFIER).$(SUFFIX)'
);
testPlistUtils
.
setProperty
(
PlistParser
.
kAssociatedDomainsKey
,
<
String
>[
'applinks:example.com'
,
'applinks:example2.com'
,
],
);
final
XcodeUniversalLinkSettings
settings
=
await
project
.
ios
.
universalLinkSettings
(
target:
'Runner'
,
scheme:
'Debug'
,
configuration:
'config'
,
);
expect
(
settings
.
associatedDomains
,
unorderedEquals
(
<
String
>[
'example.com'
,
'example2.com'
,
],
),
);
expect
(
settings
.
teamIdentifier
,
'ABC'
);
expect
(
settings
.
bundleIdentifier
,
'io.flutter.someProject.suffix'
);
});
testWithMocks
(
'from build settings, if no plist'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
xcodeProject
.
createSync
();
const
XcodeProjectBuildContext
buildContext
=
XcodeProjectBuildContext
(
scheme:
'Runner'
);
xcodeProjectInterpreter
.
buildSettingsByBuildContext
[
buildContext
]
=
<
String
,
String
>{
'PRODUCT_BUNDLE_IDENTIFIER'
:
'io.flutter.someProject'
,
};
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
testWithMocks
(
'can handle entitlement file in nested directory structure.'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
xcodeProject
.
createSync
();
project
.
ios
.
defaultHostInfoPlist
.
createSync
(
recursive:
true
);
const
String
entitlementFilePath
=
'nested/somewhere/myEntitlement.Entitlement'
;
project
.
ios
.
hostAppRoot
.
childFile
(
entitlementFilePath
).
createSync
(
recursive:
true
);
const
XcodeProjectBuildContext
buildContext
=
XcodeProjectBuildContext
(
target:
'Runner'
,
scheme:
'Debug'
,
configuration:
'config'
,
);
xcodeProjectInterpreter
.
buildSettingsByBuildContext
[
buildContext
]
=
<
String
,
String
>{
IosProject
.
kProductBundleIdKey
:
'io.flutter.someProject'
,
IosProject
.
kTeamIdKey
:
'ABC'
,
IosProject
.
kEntitlementFilePathKey
:
entitlementFilePath
,
'SUFFIX'
:
'suffix'
,
};
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
testPlistUtils
.
setProperty
(
PlistParser
.
kCFBundleIdentifierKey
,
r'$(PRODUCT_BUNDLE_IDENTIFIER).$(SUFFIX)'
);
testPlistUtils
.
setProperty
(
PlistParser
.
kAssociatedDomainsKey
,
<
String
>[
'applinks:example.com'
,
'applinks:example2.com'
,
],
);
final
XcodeUniversalLinkSettings
settings
=
await
project
.
ios
.
universalLinkSettings
(
target:
'Runner'
,
scheme:
'Debug'
,
configuration:
'config'
,
);
expect
(
settings
.
associatedDomains
,
unorderedEquals
(
<
String
>[
'example.com'
,
'example2.com'
,
],
),
);
expect
(
settings
.
teamIdentifier
,
'ABC'
);
expect
(
settings
.
bundleIdentifier
,
'io.flutter.someProject.suffix'
);
});
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
'io.flutter.someProject'
);
testWithMocks
(
'return empty when no entitlement'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
xcodeProject
.
createSync
();
project
.
ios
.
defaultHostInfoPlist
.
createSync
(
recursive:
true
);
const
XcodeProjectBuildContext
buildContext
=
XcodeProjectBuildContext
(
target:
'Runner'
,
scheme:
'Debug'
,
configuration:
'config'
,
);
xcodeProjectInterpreter
.
buildSettingsByBuildContext
[
buildContext
]
=
<
String
,
String
>{
IosProject
.
kProductBundleIdKey
:
'io.flutter.someProject'
,
IosProject
.
kTeamIdKey
:
'ABC'
,
};
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
testPlistUtils
.
setProperty
(
PlistParser
.
kCFBundleIdentifierKey
,
r'$(PRODUCT_BUNDLE_IDENTIFIER)'
);
final
XcodeUniversalLinkSettings
settings
=
await
project
.
ios
.
universalLinkSettings
(
target:
'Runner'
,
scheme:
'Debug'
,
configuration:
'config'
,
);
expect
(
settings
.
teamIdentifier
,
'ABC'
);
expect
(
settings
.
bundleIdentifier
,
'io.flutter.someProject'
);
});
});
testWithMocks
(
'from project file, if no plist or build settings'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
addIosProjectFile
(
project
.
directory
,
projectFileContent:
()
{
return
projectFileWithBundleId
(
'io.flutter.someProject'
);
group
(
'product bundle identifier'
,
()
{
testWithMocks
(
'null, if no build settings or plist entries'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
isNull
);
});
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
'io.flutter.someProject'
);
});
testWithMocks
(
'from plist, if no variables'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
defaultHostInfoPlist
.
createSync
(
recursive:
true
);
testPlistUtils
.
setProperty
(
'CFBundleIdentifier'
,
'io.flutter.someProject'
);
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
'io.flutter.someProject'
);
});
testWithMocks
(
'from build settings, if no plist'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
xcodeProject
.
createSync
();
const
XcodeProjectBuildContext
buildContext
=
XcodeProjectBuildContext
(
scheme:
'Runner'
);
xcodeProjectInterpreter
.
buildSettingsByBuildContext
[
buildContext
]
=
<
String
,
String
>{
IosProject
.
kProductBundleIdKey
:
'io.flutter.someProject'
,
};
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
'io.flutter.someProject'
);
});
testWithMocks
(
'from build settings and plist, if default variable'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
xcodeProject
.
createSync
();
const
XcodeProjectBuildContext
buildContext
=
XcodeProjectBuildContext
(
scheme:
'Runner'
);
xcodeProjectInterpreter
.
buildSettingsByBuildContext
[
buildContext
]
=
<
String
,
String
>{
'PRODUCT_BUNDLE_IDENTIFIER'
:
'io.flutter.someProject'
,
};
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
testPlistUtils
.
setProperty
(
'CFBundleIdentifier'
,
r'$(PRODUCT_BUNDLE_IDENTIFIER)'
);
testWithMocks
(
'from project file, if no plist or build settings'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
'io.flutter.someProject'
);
});
addIosProjectFile
(
project
.
directory
,
projectFileContent:
()
{
return
projectFileWithBundleId
(
'io.flutter.someProject'
);
});
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
'io.flutter.someProject'
);
});
testWithMocks
(
'from build settings and plist, by substitution'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
xcodeProject
.
createSync
();
project
.
ios
.
defaultHostInfoPlist
.
createSync
(
recursive:
true
);
const
XcodeProjectBuildContext
buildContext
=
XcodeProjectBuildContext
(
scheme:
'Runner'
);
xcodeProjectInterpreter
.
buildSettingsByBuildContext
[
buildContext
]
=
<
String
,
String
>{
'PRODUCT_BUNDLE_IDENTIFIER'
:
'io.flutter.someProject'
,
'SUFFIX'
:
'suffix'
,
};
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
testPlistUtils
.
setProperty
(
'CFBundleIdentifier'
,
r'$(PRODUCT_BUNDLE_IDENTIFIER).$(SUFFIX)'
);
testWithMocks
(
'from plist, if no variables'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
defaultHostInfoPlist
.
createSync
(
recursive:
true
);
testPlistUtils
.
setProperty
(
'CFBundleIdentifier'
,
'io.flutter.someProject'
);
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
'io.flutter.someProject'
);
});
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
'io.flutter.someProject.suffix'
);
});
testWithMocks
(
'from build settings and plist, if default variable'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
xcodeProject
.
createSync
();
const
XcodeProjectBuildContext
buildContext
=
XcodeProjectBuildContext
(
scheme:
'Runner'
);
xcodeProjectInterpreter
.
buildSettingsByBuildContext
[
buildContext
]
=
<
String
,
String
>{
IosProject
.
kProductBundleIdKey
:
'io.flutter.someProject'
,
};
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
testPlistUtils
.
setProperty
(
'CFBundleIdentifier'
,
r'$(PRODUCT_BUNDLE_IDENTIFIER)'
);
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
'io.flutter.someProject'
);
});
testWithMocks
(
'Always pass parsing org on ios project with flavors'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
addIosProjectFile
(
project
.
directory
,
projectFileContent:
()
{
return
projectFileWithBundleId
(
'io.flutter.someProject'
,
qualifier:
"'"
);
testWithMocks
(
'from build settings and plist, by substitution'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
xcodeProject
.
createSync
();
project
.
ios
.
defaultHostInfoPlist
.
createSync
(
recursive:
true
);
const
XcodeProjectBuildContext
buildContext
=
XcodeProjectBuildContext
(
scheme:
'Runner'
);
xcodeProjectInterpreter
.
buildSettingsByBuildContext
[
buildContext
]
=
<
String
,
String
>{
IosProject
.
kProductBundleIdKey
:
'io.flutter.someProject'
,
'SUFFIX'
:
'suffix'
,
};
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
testPlistUtils
.
setProperty
(
'CFBundleIdentifier'
,
r'$(PRODUCT_BUNDLE_IDENTIFIER).$(SUFFIX)'
);
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
'io.flutter.someProject.suffix'
);
});
project
.
ios
.
xcodeProject
.
createSync
();
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'free'
,
'paid'
],
logger
);
expect
(
await
project
.
organizationNames
,
<
String
>[]);
});
testWithMocks
(
'Always pass parsing org on ios project with flavors'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
addIosProjectFile
(
project
.
directory
,
projectFileContent:
()
{
return
projectFileWithBundleId
(
'io.flutter.someProject'
,
qualifier:
"'"
);
});
project
.
ios
.
xcodeProject
.
createSync
();
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'free'
,
'paid'
],
logger
);
testWithMocks
(
'fails with no flavor and defined schemes'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
xcodeProject
.
createSync
();
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'free'
,
'paid'
],
logger
);
expect
(
await
project
.
organizationNames
,
<
String
>[]);
});
await
expectToolExitLater
(
project
.
ios
.
productBundleIdentifier
(
null
),
contains
(
'You must specify a --flavor option to select one of the available schemes.'
)
);
});
testWithMocks
(
'fails with no flavor and defined schemes'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
xcodeProject
.
createSync
();
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'free'
,
'paid'
],
logger
);
testWithMocks
(
'handles case insensitive flavor'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
xcodeProject
.
createSync
();
const
XcodeProjectBuildContext
buildContext
=
XcodeProjectBuildContext
(
scheme:
'Free'
);
xcodeProjectInterpreter
.
buildSettingsByBuildContext
[
buildContext
]
=
<
String
,
String
>{
'PRODUCT_BUNDLE_IDENTIFIER'
:
'io.flutter.someProject'
,
};
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Free'
],
logger
);
const
BuildInfo
buildInfo
=
BuildInfo
(
BuildMode
.
debug
,
'free'
,
treeShakeIcons:
false
);
await
expectToolExitLater
(
project
.
ios
.
productBundleIdentifier
(
null
),
contains
(
'You must specify a --flavor option to select one of the available schemes.'
),
);
});
expect
(
await
project
.
ios
.
productBundleIdentifier
(
buildInfo
),
'io.flutter.someProject'
);
});
testWithMocks
(
'handles case insensitive flavor'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
xcodeProject
.
createSync
();
const
XcodeProjectBuildContext
buildContext
=
XcodeProjectBuildContext
(
scheme:
'Free'
);
xcodeProjectInterpreter
.
buildSettingsByBuildContext
[
buildContext
]
=
<
String
,
String
>{
IosProject
.
kProductBundleIdKey
:
'io.flutter.someProject'
,
};
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Free'
],
logger
);
const
BuildInfo
buildInfo
=
BuildInfo
(
BuildMode
.
debug
,
'free'
,
treeShakeIcons:
false
);
expect
(
await
project
.
ios
.
productBundleIdentifier
(
buildInfo
),
'io.flutter.someProject'
);
});
testWithMocks
(
'fails with flavor and default schemes'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
xcodeProject
.
createSync
();
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
const
BuildInfo
buildInfo
=
BuildInfo
(
BuildMode
.
debug
,
'free'
,
treeShakeIcons:
false
);
testWithMocks
(
'fails with flavor and default schemes'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
project
.
ios
.
xcodeProject
.
createSync
();
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
const
BuildInfo
buildInfo
=
BuildInfo
(
BuildMode
.
debug
,
'free'
,
treeShakeIcons:
false
);
await
expectToolExitLater
(
project
.
ios
.
productBundleIdentifier
(
buildInfo
),
contains
(
'The Xcode project does not define custom schemes. You cannot use the --flavor option.'
)
);
});
await
expectToolExitLater
(
project
.
ios
.
productBundleIdentifier
(
buildInfo
),
contains
(
'The Xcode project does not define custom schemes. You cannot use the --flavor option.'
),
);
});
testWithMocks
(
'empty surrounded by quotes'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
addIosProjectFile
(
project
.
directory
,
projectFileContent:
()
{
return
projectFileWithBundleId
(
''
,
qualifier:
'"'
);
testWithMocks
(
'empty surrounded by quotes'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
addIosProjectFile
(
project
.
directory
,
projectFileContent:
()
{
return
projectFileWithBundleId
(
''
,
qualifier:
'"'
);
});
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
''
);
});
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
''
);
});
testWithMocks
(
'surrounded by double quotes'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
addIosProjectFile
(
project
.
directory
,
projectFileContent:
()
{
return
projectFileWithBundleId
(
'io.flutter.someProject'
,
qualifier:
'"'
);
testWithMocks
(
'surrounded by double quotes'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
addIosProjectFile
(
project
.
directory
,
projectFileContent:
()
{
return
projectFileWithBundleId
(
'io.flutter.someProject'
,
qualifier:
'"'
);
});
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
'io.flutter.someProject'
);
});
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
'io.flutter.someProject'
);
});
testWithMocks
(
'surrounded by single quotes'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
addIosProjectFile
(
project
.
directory
,
projectFileContent:
()
{
return
projectFileWithBundleId
(
'io.flutter.someProject'
,
qualifier:
"'"
);
testWithMocks
(
'surrounded by single quotes'
,
()
async
{
final
FlutterProject
project
=
await
someProject
();
xcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[],
<
String
>[],
<
String
>[
'Runner'
],
logger
);
addIosProjectFile
(
project
.
directory
,
projectFileContent:
()
{
return
projectFileWithBundleId
(
'io.flutter.someProject'
,
qualifier:
"'"
);
});
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
'io.flutter.someProject'
);
});
expect
(
await
project
.
ios
.
productBundleIdentifier
(
null
),
'io.flutter.someProject'
);
});
});
...
...
@@ -999,7 +1119,7 @@ apply plugin: 'kotlin-android'
setUp
(()
{
const
XcodeProjectBuildContext
buildContext
=
XcodeProjectBuildContext
(
scheme:
'Runner'
);
mockXcodeProjectInterpreter
.
buildSettingsByBuildContext
[
buildContext
]
=
<
String
,
String
>{
'PRODUCT_BUNDLE_IDENTIFIER'
:
'io.flutter.someProject'
,
IosProject
.
kProductBundleIdKey
:
'io.flutter.someProject'
,
};
mockXcodeProjectInterpreter
.
xcodeProjectInfo
=
XcodeProjectInfo
(<
String
>[
'Runner'
,
'WatchTarget'
],
<
String
>[],
<
String
>[
'Runner'
,
'WatchScheme'
],
logger
);
});
...
...
@@ -1093,7 +1213,7 @@ apply plugin: 'kotlin-android'
deviceId:
'123'
,
);
mockXcodeProjectInterpreter
.
buildSettingsByBuildContext
[
buildContext
]
=
<
String
,
String
>{
'PRODUCT_BUNDLE_IDENTIFIER'
:
'io.flutter.someProject'
,
IosProject
.
kProductBundleIdKey
:
'io.flutter.someProject'
,
};
project
.
ios
.
hostAppRoot
.
childDirectory
(
'WatchTarget'
).
childFile
(
'Info.plist'
).
createSync
(
recursive:
true
);
testPlistParser
.
setProperty
(
'WKCompanionAppBundleIdentifier'
,
r'$(PRODUCT_BUNDLE_IDENTIFIER)'
);
...
...
@@ -1127,7 +1247,7 @@ apply plugin: 'kotlin-android'
deviceId:
'123'
,
);
mockXcodeProjectInterpreter
.
buildSettingsByBuildContext
[
buildContext
]
=
<
String
,
String
>{
'PRODUCT_BUNDLE_IDENTIFIER'
:
'io.flutter.someProject'
,
IosProject
.
kProductBundleIdKey
:
'io.flutter.someProject'
,
};
const
XcodeProjectBuildContext
watchBuildContext
=
XcodeProjectBuildContext
(
...
...
@@ -1167,7 +1287,7 @@ apply plugin: 'kotlin-android'
deviceId:
'123'
);
mockXcodeProjectInterpreter
.
buildSettingsByBuildContext
[
buildContext
]
=
<
String
,
String
>{
'PRODUCT_BUNDLE_IDENTIFIER'
:
'io.flutter.someProject'
,
IosProject
.
kProductBundleIdKey
:
'io.flutter.someProject'
,
};
const
XcodeProjectBuildContext
watchBuildContext
=
XcodeProjectBuildContext
(
...
...
@@ -1176,7 +1296,7 @@ apply plugin: 'kotlin-android'
isWatch:
true
,
);
mockXcodeProjectInterpreter
.
buildSettingsByBuildContext
[
watchBuildContext
]
=
<
String
,
String
>{
'PRODUCT_BUNDLE_IDENTIFIER'
:
'io.flutter.someProject'
,
IosProject
.
kProductBundleIdKey
:
'io.flutter.someProject'
,
'INFOPLIST_KEY_WKCompanionAppBundleIdentifier'
:
r'$(PRODUCT_BUNDLE_IDENTIFIER)'
,
};
...
...
packages/flutter_tools/test/general.shard/resident_web_runner_test.dart
View file @
b00f1c45
...
...
@@ -82,6 +82,10 @@ const List<VmServiceExpectation> kAttachIsolateExpectations =
'service'
:
kFlutterGetAndroidBuildVariantsServiceName
,
'alias'
:
kFlutterToolAlias
,
}),
FakeVmServiceRequest
(
method:
'registerService'
,
args:
<
String
,
Object
>{
'service'
:
kFlutterGetIOSDeeplinkSettingsServiceName
,
'alias'
:
'Flutter Tools'
,
}),
FakeVmServiceRequest
(
method:
'streamListen'
,
args:
<
String
,
Object
>{
...
...
packages/flutter_tools/test/integration.shard/plist_parser_test.dart
View file @
b00f1c45
...
...
@@ -74,52 +74,52 @@ void main() {
file
.
deleteSync
();
});
testWithoutContext
(
'PlistParser.get
StringValueFromFile
works with an XML file'
,
()
{
testWithoutContext
(
'PlistParser.get
ValueFromFile<String>
works with an XML file'
,
()
{
file
.
writeAsBytesSync
(
base64
.
decode
(
base64PlistXml
));
expect
(
parser
.
get
StringValueFromFile
(
file
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
parser
.
get
StringValueFromFile
(
file
.
absolute
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
absolute
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
logger
.
statusText
,
isEmpty
);
expect
(
logger
.
errorText
,
isEmpty
);
},
skip:
!
platform
.
isMacOS
);
// [intended] requires macos tool chain.
testWithoutContext
(
'PlistParser.get
StringValueFromFile
works with a binary file'
,
()
{
testWithoutContext
(
'PlistParser.get
ValueFromFile<String>
works with a binary file'
,
()
{
file
.
writeAsBytesSync
(
base64
.
decode
(
base64PlistBinary
));
expect
(
parser
.
get
StringValueFromFile
(
file
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
parser
.
get
StringValueFromFile
(
file
.
absolute
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
absolute
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
logger
.
statusText
,
isEmpty
);
expect
(
logger
.
errorText
,
isEmpty
);
},
skip:
!
platform
.
isMacOS
);
// [intended] requires macos tool chain.
testWithoutContext
(
'PlistParser.get
StringValueFromFile
works with a JSON file'
,
()
{
testWithoutContext
(
'PlistParser.get
ValueFromFile<String>
works with a JSON file'
,
()
{
file
.
writeAsBytesSync
(
base64
.
decode
(
base64PlistJson
));
expect
(
parser
.
get
StringValueFromFile
(
file
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
parser
.
get
StringValueFromFile
(
file
.
absolute
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
absolute
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
logger
.
statusText
,
isEmpty
);
expect
(
logger
.
errorText
,
isEmpty
);
},
skip:
!
platform
.
isMacOS
);
// [intended] requires macos tool chain.
testWithoutContext
(
'PlistParser.get
StringValueFromFile
returns null for a non-existent plist file'
,
()
{
expect
(
parser
.
get
StringValueFromFile
(
'missing.plist'
,
'CFBundleIdentifier'
),
null
);
testWithoutContext
(
'PlistParser.get
ValueFromFile<String>
returns null for a non-existent plist file'
,
()
{
expect
(
parser
.
get
ValueFromFile
<
String
>
(
'missing.plist'
,
'CFBundleIdentifier'
),
null
);
expect
(
logger
.
statusText
,
isEmpty
);
expect
(
logger
.
errorText
,
isEmpty
);
},
skip:
!
platform
.
isMacOS
);
// [intended] requires macos tool chain.
testWithoutContext
(
'PlistParser.get
StringValueFromFile
returns null for a non-existent key within a plist'
,
()
{
testWithoutContext
(
'PlistParser.get
ValueFromFile<String>
returns null for a non-existent key within a plist'
,
()
{
file
.
writeAsBytesSync
(
base64
.
decode
(
base64PlistXml
));
expect
(
parser
.
get
StringValueFromFile
(
file
.
path
,
'BadKey'
),
null
);
expect
(
parser
.
get
StringValueFromFile
(
file
.
absolute
.
path
,
'BadKey'
),
null
);
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
path
,
'BadKey'
),
null
);
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
absolute
.
path
,
'BadKey'
),
null
);
expect
(
logger
.
statusText
,
isEmpty
);
expect
(
logger
.
errorText
,
isEmpty
);
},
skip:
!
platform
.
isMacOS
);
// [intended] requires macos tool chain.
testWithoutContext
(
'PlistParser.get
StringValueFromFile
returns null for a malformed plist file'
,
()
{
testWithoutContext
(
'PlistParser.get
ValueFromFile<String>
returns null for a malformed plist file'
,
()
{
file
.
writeAsBytesSync
(
const
<
int
>[
1
,
2
,
3
,
4
,
5
,
6
]);
expect
(
parser
.
get
StringValueFromFile
(
file
.
path
,
'CFBundleIdentifier'
),
null
);
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
path
,
'CFBundleIdentifier'
),
null
);
expect
(
logger
.
statusText
,
contains
(
'Property List error: Unexpected character
\
x01 at line 1 / '
'JSON error: JSON text did not start with array or object and option to allow fragments not '
'set. around line 1, column 0.
\n
'
));
...
...
@@ -127,11 +127,11 @@ void main() {
' Command: /usr/bin/plutil -convert xml1 -o -
${file.absolute.path}
\n
'
);
},
skip:
!
platform
.
isMacOS
);
// [intended] requires macos tool chain.
testWithoutContext
(
'PlistParser.get
StringValueFromFile
throws when /usr/bin/plutil is not found'
,
()
async
{
testWithoutContext
(
'PlistParser.get
ValueFromFile<String>
throws when /usr/bin/plutil is not found'
,
()
async
{
file
.
writeAsBytesSync
(
base64
.
decode
(
base64PlistXml
));
expect
(
()
=>
parser
.
get
StringValueFromFile
(
file
.
path
,
'unused'
),
()
=>
parser
.
get
ValueFromFile
<
String
>
(
file
.
path
,
'unused'
),
throwsA
(
isA
<
FileNotFoundException
>()),
);
expect
(
logger
.
statusText
,
isEmpty
);
...
...
@@ -141,21 +141,21 @@ void main() {
testWithoutContext
(
'PlistParser.replaceKey can replace a key'
,
()
async
{
file
.
writeAsBytesSync
(
base64
.
decode
(
base64PlistXml
));
expect
(
parser
.
get
StringValueFromFile
(
file
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
parser
.
replaceKey
(
file
.
path
,
key:
'CFBundleIdentifier'
,
value:
'dev.flutter.fake'
),
isTrue
);
expect
(
logger
.
statusText
,
isEmpty
);
expect
(
logger
.
errorText
,
isEmpty
);
expect
(
parser
.
get
StringValueFromFile
(
file
.
path
,
'CFBundleIdentifier'
),
equals
(
'dev.flutter.fake'
));
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
path
,
'CFBundleIdentifier'
),
equals
(
'dev.flutter.fake'
));
},
skip:
!
platform
.
isMacOS
);
// [intended] requires macos tool chain.
testWithoutContext
(
'PlistParser.replaceKey can create a new key'
,
()
async
{
file
.
writeAsBytesSync
(
base64
.
decode
(
base64PlistXml
));
expect
(
parser
.
get
StringValueFromFile
(
file
.
path
,
'CFNewKey'
),
isNull
);
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
path
,
'CFNewKey'
),
isNull
);
expect
(
parser
.
replaceKey
(
file
.
path
,
key:
'CFNewKey'
,
value:
'dev.flutter.fake'
),
isTrue
);
expect
(
logger
.
statusText
,
isEmpty
);
expect
(
logger
.
errorText
,
isEmpty
);
expect
(
parser
.
get
StringValueFromFile
(
file
.
path
,
'CFNewKey'
),
equals
(
'dev.flutter.fake'
));
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
path
,
'CFNewKey'
),
equals
(
'dev.flutter.fake'
));
},
skip:
!
platform
.
isMacOS
);
// [intended] requires macos tool chain.
testWithoutContext
(
'PlistParser.replaceKey can delete a key'
,
()
async
{
...
...
@@ -164,7 +164,7 @@ void main() {
expect
(
parser
.
replaceKey
(
file
.
path
,
key:
'CFBundleIdentifier'
),
isTrue
);
expect
(
logger
.
statusText
,
isEmpty
);
expect
(
logger
.
errorText
,
isEmpty
);
expect
(
parser
.
get
StringValueFromFile
(
file
.
path
,
'CFBundleIdentifier'
),
isNull
);
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
path
,
'CFBundleIdentifier'
),
isNull
);
},
skip:
!
platform
.
isMacOS
);
// [intended] requires macos tool chain.
testWithoutContext
(
'PlistParser.replaceKey throws when /usr/bin/plutil is not found'
,
()
async
{
...
...
@@ -192,9 +192,9 @@ void main() {
testWithoutContext
(
'PlistParser.replaceKey works with a JSON file'
,
()
{
file
.
writeAsBytesSync
(
base64
.
decode
(
base64PlistJson
));
expect
(
parser
.
get
StringValueFromFile
(
file
.
absolute
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
absolute
.
path
,
'CFBundleIdentifier'
),
'io.flutter.flutter.app'
);
expect
(
parser
.
replaceKey
(
file
.
path
,
key:
'CFBundleIdentifier'
,
value:
'dev.flutter.fake'
),
isTrue
);
expect
(
parser
.
get
StringValueFromFile
(
file
.
absolute
.
path
,
'CFBundleIdentifier'
),
'dev.flutter.fake'
);
expect
(
parser
.
get
ValueFromFile
<
String
>
(
file
.
absolute
.
path
,
'CFBundleIdentifier'
),
'dev.flutter.fake'
);
expect
(
logger
.
statusText
,
isEmpty
);
expect
(
logger
.
errorText
,
isEmpty
);
},
skip:
!
platform
.
isMacOS
);
// [intended] requires macos tool chain.
...
...
packages/flutter_tools/test/src/fakes.dart
View file @
b00f1c45
...
...
@@ -296,8 +296,8 @@ class FakePlistParser implements PlistParser {
}
@override
String
?
getStringValueFromFile
(
String
plistFilePath
,
String
key
)
{
return
_underlyingValues
[
key
]
as
String
?;
T
?
getValueFromFile
<
T
>
(
String
plistFilePath
,
String
key
)
{
return
_underlyingValues
[
key
]
as
T
?;
}
@override
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment