Thanks to visit codestin.com
Credit goes to github.com

Skip to content

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

Open
wants to merge 48 commits into
base: master
Choose a base branch
from

Conversation

navaronbracke
Copy link
Contributor

@navaronbracke navaronbracke commented Mar 13, 2025

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

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • I followed the [breaking change policy] and added [Data Driven Fixes] where supported.
  • All existing and new tests are passing.

@github-actions github-actions bot added the tool Affects the "flutter" command-line tool. See also t: labels. label Mar 13, 2025
final int packageProductDependenciesIndex = lines.indexWhere(
(String line) => line.trim().contains('packageProductDependencies'),
runnerNativeTargetStartIndex,
lines.insertAll(nativeTargetStartIndex + 1, newContent);
Copy link
Contributor Author

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.',
Copy link
Contributor Author

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.',
Copy link
Contributor Author

@navaronbracke navaronbracke Mar 13, 2025

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.',
Copy link
Contributor Author

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

@navaronbracke navaronbracke marked this pull request as ready for review March 13, 2025 15:22
@github-actions github-actions bot added a: desktop Running on desktop team-ios Owned by iOS platform team labels Mar 14, 2025
/// - 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) {
Copy link
Contributor Author

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?

/// 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(
Copy link
Contributor Author

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 {
Copy link
Contributor Author

@navaronbracke navaronbracke Mar 14, 2025

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,
Copy link
Contributor Author

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>[
Copy link
Contributor Author

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.

@@ -671,6 +755,183 @@ void main() {
expect(testLogger.traceText, contains('project.pbxproj already migrated. Skipping...'));
});

testWithoutContext('skips Runner PBXNativeTarget migration if already migrated', () async {
Copy link
Contributor Author

@navaronbracke navaronbracke Mar 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was no test that verified that the migrator could skip the PBXNativeTargets, so I added some for good measure.

expect(testLogger.traceText, contains('PBXNativeTargets already migrated. Skipping...'));
});

testWithoutContext(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a variant of the test above, but with an extra custom target

@navaronbracke navaronbracke changed the title handle multiple application product native targets in SPM migrator SPM migrator should exit when unmigrated custom targets are found Mar 29, 2025
@navaronbracke navaronbracke marked this pull request as ready for review March 29, 2025 13:29
@navaronbracke
Copy link
Contributor Author

Hi @navaronbracke! This is great work, but unfortunately I don't think this is something we want to adopt. When custom targets get involved it gets much more complicated and altering the pbxproj file is already a little precarious.

I think for a majority of Flutter users, the defaults are probably sufficient. I would be open to accepting a PR that detects a custom target is being used and is missing the migrations and throws a warning/error with a link to the documentation on how to add manually.

Done!

@navaronbracke navaronbracke marked this pull request as draft March 29, 2025 19:36
@navaronbracke
Copy link
Contributor Author

navaronbracke commented Mar 29, 2025

Edit: It seems we only need some extra handling for the RunnerTests target. The Flutter Assemble target is not in the PBXNativeTargets section (it is a PBXAggregateTarget) and the Flutter Assemble scheme is an autogenerated XCode scheme, so it does not exist in the xcschemes directory where we look for schemes.


There is one failing set of tests, but that is because the Flutter tool itself also provides custom targets & schemes.

I'll either update the templates to be SPM compliant for those or ignore them for now in the migrator.

1. Flutter Assemble target & scheme
2. RunnerTests target

I'm not sure if there are more that the tool provides? cc @vashworth

);
}

bool _isNotATestingNativeTarget(ParsedNativeTarget target) {
Copy link
Contributor Author

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?

@navaronbracke navaronbracke marked this pull request as ready for review March 31, 2025 14:55
@@ -294,7 +331,7 @@ class SwiftPackageManagerIntegrationMigration extends ProjectMigrator {
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "$_runnerNativeTargetIdentifier"
BlueprintIdentifier = "$blueprintIdentifier"
Copy link
Contributor

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;
}
}
Copy link
Contributor

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>
  ...

Copy link
Contributor

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

  1. Parse the XML
  2. Check if the LaunchAction > BuildableProductRunnable > BuildableReference matches _runnerNativeTargetIdentifier, if not throw an error. Otherwise, get the BuildableName, BlueprintName, ReferencedContainer

Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@navaronbracke navaronbracke requested a review from a team as a code owner April 26, 2025 12:19
$buildableName
$blueprintName
$referencedContainer
BuildableName = "$buildableName"
Copy link
Contributor Author

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.

@navaronbracke navaronbracke requested a review from vashworth April 26, 2025 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a: desktop Running on desktop team-ios Owned by iOS platform team tool Affects the "flutter" command-line tool. See also t: labels.
Projects
None yet
2 participants