-
Notifications
You must be signed in to change notification settings - Fork 28.9k
SPM migrator should exit when unmigrated custom targets are found #165115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
SPM migrator should exit when unmigrated custom targets are found #165115
Conversation
final int packageProductDependenciesIndex = lines.indexWhere( | ||
(String line) => line.trim().contains('packageProductDependencies'), | ||
runnerNativeTargetStartIndex, | ||
lines.insertAll(nativeTargetStartIndex + 1, newContent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used an early return to avoid the else here, hence the odd diff.
|
||
if (applicationNativeTargets.isEmpty) { | ||
throw Exception( | ||
'Unable to find parsed PBXNativeTargets for ${_xcodeProject.hostAppProjectName} project.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old code had target.
at the end, but that is a bit misleading. It's the Runner
project that has N Targets.
Because the migrator now handles multiple Targets, it gets more confusing so I updated it here to rectify that.
if (packageProductDependenciesIndex == -1 || | ||
packageProductDependenciesIndex > endSectionIndex) { | ||
throw Exception( | ||
'Unable to find packageProductDependencies for ${_xcodeProject.hostAppProjectName} PBXNativeTarget.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use the name of the affected target? I also saw that there isn't an existing test for this throw statement. I guess this cannot really happen since the nativeTarget.packageProductDependencies
are not null at this point?
if (packageProductDependenciesIndex == -1 || | ||
packageProductDependenciesIndex > endSectionIndex) { | ||
throw Exception( | ||
'Unable to find packageProductDependencies for $pbxTargetName in ${_xcodeProject.hostAppProjectName} project.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added the name of the affected target + clarifying that hostAppProjectName is the project
/// - The migrator is rerun through the flutter CLI upon executing `flutter run` | ||
/// - The migrator should skip sections like 'PBXBuildFile' or 'XCSwiftPackageProductDependency', | ||
/// since they are already migrated | ||
void _ensureNewIdentifiersNotUsed(List<String> lines) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When adding the missing check for already migrated targets back, I encountered an issue:
The _ensureNewIdentifiersNotUsed
is too conservative with its checks.
So I had to fix this by allowing the identifiers in the sections where these might occur after the migrator ran once. This enables the migrator to run again when additional application targets are added.
I'm not sure if there is a better solution to this issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it too conservative? I think it's probably best to avoid duplicate ids whether they are in the same section or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, if we were to not modify the check, and we run the migrator on a pbxproj file which has more than one native target, then this check fails. This is due to encountering the identifiers in the custom targets that have already been migrated manually, as the old check only expects the identifiers once in the entire file.
For example:
Case 1: (the migration should be skipped)
- migrated Runner target
- manually migrated custom target
Case 2: (the migrator should throw for the newly added custom target)
- migrated Runner target
- manually migrated custom target
- newly added, but not yet manually migrated custom target
When I run the tests after I revert that change, I see that the following tests start to fail:
skips PBXNativeTarget migration if all targets already migrated
skips Runner PBXNativeTarget migration if already migrated
fails if unmigrated custom target is detected
/// Returns false if the [identifier] does not occur in the given [lines]. | ||
/// Returns false if the [identifier] only occurs within the sections denoted by the [sectionNames]. | ||
/// Returns true otherwise. | ||
bool _isIdentifierOutsideAllowedSections( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is used by the new duplicate identifier check
@@ -671,6 +744,57 @@ void main() { | |||
expect(testLogger.traceText, contains('project.pbxproj already migrated. Skipping...')); | |||
}); | |||
|
|||
testWithoutContext('skips PBXNativeTarget migration if all targets are migrated', () async { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was no "skip" test for when the Runner target was already migrated. This is one of several new tests to cover that.
@@ -2980,15 +3272,16 @@ String migratedNativeTargetSection( | |||
SupportedPlatform platform, { | |||
bool missingPackageProductDependencies = false, | |||
bool withOtherDependency = false, | |||
String? otherNativeTarget, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated this method to allow for inserting an additional PBXNativeTarget. To keep the readability for the Runner target, I went with a StringBuffer, to allow for splitting it up, while preserving the sections.
/// A variant of [migratedNativeTargetSection] | ||
/// that includes an additional PBXNativeTarget, "OtherTarget", that is not migrated. | ||
String migratedRunnerWithUnmigratedOtherTargetSection(SupportedPlatform platform) { | ||
final String otherTarget = <String>[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only added this target for an iOS specific test. Not sure if we need a MacOS variant also? The only real difference is the identifiers being slightly different.
Done! |
Edit: It seems we only need some extra handling for the There is one failing set of tests, but that is because the Flutter tool itself also provides custom targets & schemes.
|
); | ||
} | ||
|
||
bool _isNotATestingNativeTarget(ParsedNativeTarget target) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check should help with the failures for the RunnerTests target.
IIRC the migrator previously didn't do anything for that type of target, so I think we can exclude testing targets from the migrator?
@@ -294,7 +331,7 @@ class SwiftPackageManagerIntegrationMigration extends ProjectMigrator { | |||
<EnvironmentBuildable> | |||
<BuildableReference | |||
BuildableIdentifier = "primary" | |||
BlueprintIdentifier = "$_runnerNativeTargetIdentifier" | |||
BlueprintIdentifier = "$blueprintIdentifier" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Undo, we only will ever insert the $_runnerNativeTargetIdentifier
.
index = lineIndex; | ||
break; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may match on the wrong Blueprint Identifier.
For example, when I change the target of my scheme, $_runnerNativeTargetIdentifier
is the first BlueprintIdentifier
found in the scheme file, but now buildForRunning
equals NO
.
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "788C022D2DA97110009DD71A"
BuildableName = "NEW_TARGET.app"
BlueprintName = "NEW_TARGET"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the scheme file is XML, I think it would be better to use XmlDocument.parse
to try and figure this out.
So steps would be
- Parse the XML
- Check if the LaunchAction > BuildableProductRunnable > BuildableReference matches
_runnerNativeTargetIdentifier
, if not throw an error. Otherwise, get the BuildableName, BlueprintName, ReferencedContainer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't all changes below this point be removed since we're not migrating any custom targets?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right about that, we're only supposed to detect the custom targets in the pbxproj, not the active xcscheme file.
And good point about using the XML parser, no need to reinvent the wheel.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
$buildableName | ||
$blueprintName | ||
$referencedContainer | ||
BuildableName = "$buildableName" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the BlueprintIdentifier
attribute also keeps the XML attribute in the template, I opted for doing that for the others also.
/// - The migrator is rerun through the flutter CLI upon executing `flutter run` | ||
/// - The migrator should skip sections like 'PBXBuildFile' or 'XCSwiftPackageProductDependency', | ||
/// since they are already migrated | ||
void _ensureNewIdentifiersNotUsed(List<String> lines) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it too conservative? I think it's probably best to avoid duplicate ids whether they are in the same section or not.
} | ||
|
||
List<String> _migrateNativeTargets(List<String> lines, ParsedProjectInfo projectInfo) { | ||
if (projectInfo.nativeTargets.isNotEmpty && _areNativeTargetsMigrated(projectInfo)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we want to be more conservative since there can be more types of targets than just unit testing which Flutter may or may not support. For example, if they add a watch extension, which is not supported by Flutter currently and we wouldn't want them to migrate it, this will fail.
Here's roughly what I would do:
// get BlueprintIdentifier from scheme for the current flavor
final (
:String blueprintIdentifier,
:String buildableName,
:String blueprintName,
:String referencedContainer,
) = _parseSchemeFile(schemeFile.basename, schemeContent, throwOnCustomTarget = false);
...
// Check if the selected flavor specifically is migrated
final bool migrated =
projectInfo.nativeTargets
.where(
(ParsedNativeTarget target) =>
target.identifier == blueprintIdentifier &&
target.packageProductDependencies != null &&
target.packageProductDependencies!.contains(
_flutterPluginsSwiftPackageProductDependencyIdentifier,
),
)
.toList()
.isNotEmpty;
// If not migrated, show error
Hey @vashworth and @navaronbracke, greetings from stale PR triage. 👋 |
This PR updates the Swift Package Manager project migrator to detect when custom targets are used and exits with a message pointing to the docs, if any of the custom targets are not migrated yet.
I also found some discrepancies in the wording around when a reference in the pbxproj is for the project or a native target.
That has been corrected in this PR as well.
Fixes #164099
If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].
Pre-launch Checklist
///
).