Skip to content

Commit 609a642

Browse files
committed
Compare linked plugin frameworks as (id, frameworkName) pairs
1 parent ef71d3d commit 609a642

2 files changed

Lines changed: 102 additions & 11 deletions

File tree

‎packages/react-native/scripts/spm/__tests__/plugin-framework-mismatch-test.js‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,75 @@ describe('assertPluginFrameworksLinked, linked frameworks from the marker', () =
281281

282282
expect(check(app).lines).toEqual([EXPO_NOT_PROVIDED]);
283283
});
284+
285+
it('fails when plugins swap the ids of two linked frameworks', () => {
286+
const app = scaffoldApp();
287+
inject(app, [
288+
REACT,
289+
frameworkEntry('a', 'Foo', 'plugins/a.xcframework'),
290+
frameworkEntry('b', 'Bar', 'plugins/b.xcframework'),
291+
]);
292+
writeSidecar(app.appRoot, [
293+
{id: 'a', frameworkName: 'Bar'},
294+
{id: 'b', frameworkName: 'Foo'},
295+
]);
296+
297+
const {error, lines} = check(app);
298+
299+
expect(error).toBeInstanceOf(PluginFrameworkMismatchError);
300+
expect(lines).toEqual([
301+
'error: The Xcode project links Foo.framework (a), but the autolinking plugin that provides Foo gives it a different id. Run `npx react-native spm update` to relink Foo in the committed project for the whole team.',
302+
'error: The Xcode project links Bar.framework (b), but the autolinking plugin that provides Bar gives it a different id. Run `npx react-native spm update` to relink Bar in the committed project for the whole team.',
303+
]);
304+
});
305+
306+
it('fails once when a plugin changes the id of a linked framework', () => {
307+
const app = scaffoldApp();
308+
inject(app, [REACT, frameworkEntry('a', 'Foo', 'plugins/a.xcframework')]);
309+
writeSidecar(app.appRoot, [{id: 'c', frameworkName: 'Foo'}]);
310+
311+
expect(check(app).lines).toEqual([
312+
'error: The Xcode project links Foo.framework (a), but the autolinking plugin that provides Foo gives it a different id. Run `npx react-native spm update` to relink Foo in the committed project for the whole team.',
313+
]);
314+
});
315+
316+
it('reports a new framework and swapped ids once each', () => {
317+
const app = scaffoldApp();
318+
inject(app, [
319+
REACT,
320+
frameworkEntry('a', 'Foo', 'plugins/a.xcframework'),
321+
frameworkEntry('b', 'Bar', 'plugins/b.xcframework'),
322+
]);
323+
writeSidecar(app.appRoot, [
324+
{id: 'a', frameworkName: 'Bar'},
325+
{id: 'b', frameworkName: 'Foo'},
326+
{id: 'c', frameworkName: 'Baz'},
327+
]);
328+
329+
expect(check(app).lines).toEqual([
330+
'error: Baz (c) is a precompiled framework from an autolinking plugin, but the Xcode project does not link it. Run `npx react-native spm update` to add Baz to the committed project for the whole team.',
331+
'error: The Xcode project links Foo.framework (a), but the autolinking plugin that provides Foo gives it a different id. Run `npx react-native spm update` to relink Foo in the committed project for the whole team.',
332+
'error: The Xcode project links Bar.framework (b), but the autolinking plugin that provides Bar gives it a different id. Run `npx react-native spm update` to relink Bar in the committed project for the whole team.',
333+
]);
334+
});
284335
});
285336

286337
describe('assertPluginFrameworksLinked, linked frameworks from the project, for an older marker', () => {
338+
it('compares setting prefixes and names separately', () => {
339+
const app = scaffoldApp();
340+
injectWithOlderMarker(app, [
341+
REACT,
342+
frameworkEntry('a', 'Foo', 'plugins/a.xcframework'),
343+
frameworkEntry('b', 'Bar', 'plugins/b.xcframework'),
344+
]);
345+
writeSidecar(app.appRoot, [
346+
{id: 'a', frameworkName: 'Bar'},
347+
{id: 'b', frameworkName: 'Foo'},
348+
]);
349+
350+
expect(check(app)).toEqual({error: null, lines: []});
351+
});
352+
287353
it('compares only setting prefixes when the frameworks manifest is missing', () => {
288354
const app = scaffoldApp();
289355
injectWithOlderMarker(app, [REACT, HERMES, EXPO]);

‎packages/react-native/scripts/spm/generate-spm-xcodeproj.js‎

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2293,26 +2293,33 @@ class PluginFrameworkMismatchError extends Error {
22932293

22942294
/**
22952295
* The frameworks the project links: the setting prefix of every one, and the
2296-
* names of the plugin ones. For an older marker they come from the embed
2297-
* phase: null without one, and null names when the frameworks manifest that
2298-
* tells plugin names from built-in ones is missing.
2296+
* names of the plugin ones. A marker that records them also pairs each plugin
2297+
* framework's id with its name. For an older marker they come from the embed
2298+
* phase, which cannot pair them: null without one, and null names when the
2299+
* frameworks manifest that tells plugin names from built-in ones is missing.
22992300
*/
23002301
function readLinkedFrameworks(
23012302
appRoot /*: string */,
23022303
xcodeprojPath /*: string */,
2303-
) /*: ?{settingPrefixes: Set<string>, pluginNames: ?Set<string>} */ {
2304+
) /*: ?{
2305+
settingPrefixes: Set<string>,
2306+
pluginNames: ?Set<string>,
2307+
pluginFrameworks?: Array<{id: string, frameworkName: string}>,
2308+
} */ {
23042309
const builtinIds = new Set(BUILTIN_FRAMEWORKS.map(({id}) => id));
23052310
const recorded = readMarker(xcodeprojPath)?.linkedFrameworks;
23062311
if (recorded != null) {
2312+
const pluginFrameworks = recorded.filter(
2313+
framework => !builtinIds.has(framework.id),
2314+
);
23072315
return {
23082316
settingPrefixes: new Set(
23092317
recorded.map(framework => frameworkSettingPrefix(framework.id)),
23102318
),
23112319
pluginNames: new Set(
2312-
recorded
2313-
.filter(framework => !builtinIds.has(framework.id))
2314-
.map(framework => framework.frameworkName),
2320+
pluginFrameworks.map(framework => framework.frameworkName),
23152321
),
2322+
pluginFrameworks,
23162323
};
23172324
}
23182325

@@ -2393,12 +2400,19 @@ function assertPluginFrameworksLinked(
23932400
const pairedNames = new Set(
23942401
pluginFrameworks.map(framework => framework.frameworkName),
23952402
);
2403+
const pairKey = (
2404+
{id, frameworkName} /*: {id: string, frameworkName: string, ...} */,
2405+
) => JSON.stringify([id, frameworkName]);
2406+
const pairedPairs = new Set(pluginFrameworks.map(pairKey));
23962407
const problems /*: Array<string> */ = [];
23972408
for (const {id, frameworkName} of pluginFrameworks) {
2398-
if (
2399-
!linked.settingPrefixes.has(frameworkSettingPrefix(id)) ||
2400-
linked.pluginNames?.has(frameworkName) === false
2401-
) {
2409+
// A name linked under another id is reported once, from the linked side.
2410+
const notLinked =
2411+
linked.pluginFrameworks != null
2412+
? linked.pluginNames?.has(frameworkName) === false
2413+
: !linked.settingPrefixes.has(frameworkSettingPrefix(id)) ||
2414+
linked.pluginNames?.has(frameworkName) === false;
2415+
if (notLinked) {
24022416
problems.push(
24032417
`error: ${frameworkName} (${id}) is a precompiled framework from an autolinking plugin, but the Xcode project does not link it. Run \`npx react-native spm update\` to add ${frameworkName} to the committed project for the whole team.`,
24042418
);
@@ -2418,6 +2432,17 @@ function assertPluginFrameworksLinked(
24182432
);
24192433
}
24202434
}
2435+
for (const framework of linked.pluginFrameworks ?? []) {
2436+
const {id, frameworkName} = framework;
2437+
if (
2438+
pairedNames.has(frameworkName) &&
2439+
!pairedPairs.has(pairKey(framework))
2440+
) {
2441+
problems.push(
2442+
`error: The Xcode project links ${frameworkName}.framework (${id}), but the autolinking plugin that provides ${frameworkName} gives it a different id. Run \`npx react-native spm update\` to relink ${frameworkName} in the committed project for the whole team.`,
2443+
);
2444+
}
2445+
}
24212446
if (problems.length > 0) {
24222447
problems.forEach(problem => console.error(problem));
24232448
throw new PluginFrameworkMismatchError(problems);

0 commit comments

Comments
 (0)