diff --git a/packages/react-native/scripts/spm/__tests__/generate-spm-package-test.js b/packages/react-native/scripts/spm/__tests__/generate-spm-package-test.js index f32559e234e..2a6e0293727 100644 --- a/packages/react-native/scripts/spm/__tests__/generate-spm-package-test.js +++ b/packages/react-native/scripts/spm/__tests__/generate-spm-package-test.js @@ -23,6 +23,59 @@ const path = require('node:path'); // generateXCFrameworksPackageSwift // --------------------------------------------------------------------------- +// Adding a product must be one edit to REACT_NATIVE_PRODUCTS (plus the static +// codegen template): the manifest's products AND targets both derive from it. +// ReactHeaders is the Clang umbrella target; every other product is served by +// an xcframework of the same name. +describe('generateXCFrameworksPackageSwift derives from the shared name constants', () => { + // jest.doMock registers in the module registry beyond the isolateModules + // scope, so the mocked constants must be dropped before the next test. + afterEach(() => { + jest.dontMock('../spm-utils'); + jest.resetModules(); + }); + + it('emits a product and a binary target for a newly reserved product', () => { + jest.isolateModules(() => { + // Declared by KIND, the way a real edit adds one — spm-utils derives the + // flat list from the kind lists, so the mock mirrors that derivation. + jest.doMock('../spm-utils', () => { + const actual = jest.requireActual('../spm-utils'); + const xcframeworkProducts = Object.freeze([ + ...actual.REACT_NATIVE_XCFRAMEWORK_PRODUCTS, + 'ReactBrandNewHeaders', + ]); + return { + ...actual, + REACT_NATIVE_XCFRAMEWORK_PRODUCTS: xcframeworkProducts, + REACT_NATIVE_PRODUCTS: Object.freeze([ + actual.REACT_NATIVE_UMBRELLA_PRODUCT, + ...xcframeworkProducts, + ]), + }; + }); + const { + generateXCFrameworksPackageSwift: generate, + } = require('../generate-spm-package'); + const out = generate(); + expect(out).toContain( + '.library(name: "ReactBrandNewHeaders", targets: ["ReactBrandNewHeaders"])', + ); + expect(out).toContain('path: "ReactBrandNewHeaders.xcframework"'); + }); + }); + + it('emits one library product per REACT_NATIVE_PRODUCTS entry, in order', () => { + const {REACT_NATIVE_PRODUCTS} = require('../spm-utils'); + const libraries = [ + ...generateXCFrameworksPackageSwift().matchAll( + /\.library\(name: "([^"]+)"/g, + ), + ].map(m => m[1]); + expect(libraries).toEqual([...REACT_NATIVE_PRODUCTS]); + }); +}); + describe('generateXCFrameworksPackageSwift', () => { it('exposes only invariant compile-time products', () => { const result = generateXCFrameworksPackageSwift(); diff --git a/packages/react-native/scripts/spm/__tests__/generate-spm-xcodeproj-test.js b/packages/react-native/scripts/spm/__tests__/generate-spm-xcodeproj-test.js index 34af3d71548..47c1326a2cf 100644 --- a/packages/react-native/scripts/spm/__tests__/generate-spm-xcodeproj-test.js +++ b/packages/react-native/scripts/spm/__tests__/generate-spm-xcodeproj-test.js @@ -61,6 +61,55 @@ const FRAMEWORK = { ], }; +// The pbxproj product references must follow the shared name constants: a +// product added there has to reach the app target, or the app links against a +// package product Xcode never references. +describe('SPM product references derive from the shared name constants', () => { + // jest.doMock registers in the module registry beyond the isolateModules + // scope, so the mocked constants must be dropped before the next test. + afterEach(() => { + jest.dontMock('../spm-utils'); + jest.resetModules(); + }); + + it('includes a newly reserved React Native product', () => { + jest.isolateModules(() => { + jest.doMock('../spm-utils', () => { + const actual = jest.requireActual('../spm-utils'); + return { + ...actual, + REACT_NATIVE_PRODUCTS: Object.freeze([ + ...actual.REACT_NATIVE_PRODUCTS, + 'ReactBrandNewHeaders', + ]), + }; + }); + const {buildSpmDependencyGraph} = require('../generate-spm-xcodeproj'); + const graph = buildSpmDependencyGraph( + (section, id) => `${section}:${id}`, + ); + expect(graph.products.map(p => p.product)).toContain( + 'ReactBrandNewHeaders', + ); + }); + }); + + it('references every React Native, aggregator and codegen product exactly once', () => { + const { + AUTOLINKED_PACKAGE_NAME, + REACT_CODEGEN_APP_PRODUCTS, + REACT_NATIVE_PRODUCTS, + } = require('../spm-utils'); + const {buildSpmDependencyGraph} = require('../generate-spm-xcodeproj'); + const graph = buildSpmDependencyGraph((section, id) => `${section}:${id}`); + expect(graph.products.map(p => p.product)).toEqual([ + ...REACT_NATIVE_PRODUCTS, + AUTOLINKED_PACKAGE_NAME, + ...REACT_CODEGEN_APP_PRODUCTS, + ]); + }); +}); + describe('scheme pre-action', () => { it('contains the sync script and target-scoped build environment', () => { const result = generateXcscheme( diff --git a/packages/react-native/scripts/spm/__tests__/spm-utils-test.js b/packages/react-native/scripts/spm/__tests__/spm-utils-test.js index f378a1d6ca1..f50bf4c2f9d 100644 --- a/packages/react-native/scripts/spm/__tests__/spm-utils-test.js +++ b/packages/react-native/scripts/spm/__tests__/spm-utils-test.js @@ -11,6 +11,16 @@ 'use strict'; const { + AUTOLINKED_PACKAGE_NAME, + REACT_CODEGEN_APP_PRODUCTS, + REACT_CODEGEN_PACKAGE_NAME, + REACT_CODEGEN_PRODUCTS, + REACT_HEADERS_TARGET_DIR, + REACT_NATIVE_HEADERS_PRODUCT, + REACT_NATIVE_PACKAGE_NAME, + REACT_NATIVE_PRODUCTS, + REACT_NATIVE_UMBRELLA_PRODUCT, + REACT_NATIVE_XCFRAMEWORK_PRODUCTS, RemoteVersionError, buildPerAppHeaderTree, defaultCacheDir, @@ -46,6 +56,59 @@ describe('toSwiftName', () => { }); }); +// --------------------------------------------------------------------------- +// Name constants — the single list every generated manifest derives its +// package and product names from +// --------------------------------------------------------------------------- + +describe('name constants', () => { + it('names the React Native package and the per-app codegen package', () => { + expect(REACT_NATIVE_PACKAGE_NAME).toBe('ReactNative'); + expect(REACT_CODEGEN_PACKAGE_NAME).toBe('React-GeneratedCode'); + }); + + it('pins each product list to its literal names', () => { + expect(REACT_NATIVE_PRODUCTS).toEqual([ + 'ReactHeaders', + 'ReactNativeHeaders', + 'ReactNativeDependenciesHeaders', + ]); + expect(REACT_CODEGEN_PRODUCTS).toEqual(['ReactAppHeaders']); + expect(REACT_CODEGEN_APP_PRODUCTS).toEqual([ + 'ReactCodegen', + 'ReactAppDependencyProvider', + ]); + }); + + it('tags each React Native product by kind, so no consumer has to infer it from position', () => { + expect(REACT_NATIVE_UMBRELLA_PRODUCT).toBe('ReactHeaders'); + expect(REACT_NATIVE_HEADERS_PRODUCT).toBe('ReactNativeHeaders'); + expect(REACT_NATIVE_XCFRAMEWORK_PRODUCTS).toEqual([ + 'ReactNativeHeaders', + 'ReactNativeDependenciesHeaders', + ]); + }); + + it('names the autolinking aggregator package (which shares its name with its product)', () => { + expect(AUTOLINKED_PACKAGE_NAME).toBe('Autolinked'); + }); + + it('names the invariant React headers target directory', () => { + expect(REACT_HEADERS_TARGET_DIR).toBe('ReactHeadersTarget'); + }); + + it('freezes the lists so no caller can mutate the shared source of truth', () => { + for (const list of [ + REACT_NATIVE_PRODUCTS, + REACT_CODEGEN_PRODUCTS, + REACT_CODEGEN_APP_PRODUCTS, + ]) { + expect(Array.isArray(list)).toBe(true); + expect(Object.isFrozen(list)).toBe(true); + } + }); +}); + // --------------------------------------------------------------------------- // defaultCacheDir // --------------------------------------------------------------------------- diff --git a/packages/react-native/scripts/spm/generate-spm-autolinking.js b/packages/react-native/scripts/spm/generate-spm-autolinking.js index 5e933af0144..cf2932731a4 100644 --- a/packages/react-native/scripts/spm/generate-spm-autolinking.js +++ b/packages/react-native/scripts/spm/generate-spm-autolinking.js @@ -65,6 +65,11 @@ const { } = require('./expand-spm-dependencies'); const {readPodspec} = require('./read-podspec'); const { + AUTOLINKED_PACKAGE_NAME, + REACT_CODEGEN_PACKAGE_NAME, + REACT_CODEGEN_PRODUCTS, + REACT_NATIVE_PACKAGE_NAME, + REACT_NATIVE_PRODUCTS, RemoteVersionError, findProjectRoot, makeLogger, @@ -90,7 +95,7 @@ const {log, warn} = makeLogger('generate-spm-autolinking'); let remoteCfg /*: ?{url: string, version: string, identity: string} */ = null; function reactNativePackageLabel() /*: string */ { - return remoteCfg != null ? remoteCfg.identity : 'ReactNative'; + return remoteCfg != null ? remoteCfg.identity : REACT_NATIVE_PACKAGE_NAME; } function reactNativePackageDecl(localDecl /*: string */) /*: string */ { return remoteCfg != null @@ -105,10 +110,11 @@ function reactNativePackageDecl(localDecl /*: string */) /*: string */ { function reactProducts() /*: Array<{name: string, package: string}> */ { const rn = reactNativePackageLabel(); return [ - {name: 'ReactHeaders', package: rn}, - {name: 'ReactNativeHeaders', package: rn}, - {name: 'ReactNativeDependenciesHeaders', package: rn}, - {name: 'ReactAppHeaders', package: 'React-GeneratedCode'}, + ...REACT_NATIVE_PRODUCTS.map(name => ({name, package: rn})), + ...REACT_CODEGEN_PRODUCTS.map(name => ({ + name, + package: REACT_CODEGEN_PACKAGE_NAME, + })), ]; } function reactProductDeps() /*: string */ { @@ -147,7 +153,7 @@ function reactDescriptor( }; } else if (absXcframeworks != null) { packageRef = { - name: 'ReactNative', + name: REACT_NATIVE_PACKAGE_NAME, path: toPosix(absXcframeworks), relPath: xcframeworksRelPath != null ? toPosix(xcframeworksRelPath) : undefined, @@ -156,7 +162,7 @@ function reactDescriptor( return null; } const products = reactProducts().filter( - p => p.package !== 'React-GeneratedCode' || codegenPackageExists, + p => p.package !== REACT_CODEGEN_PACKAGE_NAME || codegenPackageExists, ); return {packageRef, products}; } @@ -900,12 +906,14 @@ function generateAutolinkedPackageSwift( ) { packageDeps.push( reactNativePackageDecl( - `.package(name: "ReactNative", path: "${xcframeworksRelPath}")`, + `.package(name: "${REACT_NATIVE_PACKAGE_NAME}", path: "${xcframeworksRelPath}")`, ), ); // Per-app generated headers come from the ReactAppHeaders product in // the codegen package (sibling of the autolinking dir). - packageDeps.push(`.package(name: "React-GeneratedCode", path: "../ios")`); + packageDeps.push( + `.package(name: "${REACT_CODEGEN_PACKAGE_NAME}", path: "../ios")`, + ); } // AutolinkedAggregate's target dependencies: .product(...) for npm sub-package @@ -1008,10 +1016,10 @@ import PackageDescription import Foundation ${guardBlock}let package = Package( - name: "Autolinked", + name: "${AUTOLINKED_PACKAGE_NAME}", platforms: [.iOS(.v15)], products: [ - .library(name: "Autolinked", targets: ["AutolinkedAggregate"]), + .library(name: "${AUTOLINKED_PACKAGE_NAME}", targets: ["AutolinkedAggregate"]), ], ${packageDepsBlock} targets: [ .target( @@ -1075,13 +1083,13 @@ function generateSynthPackageSwift(spec /*: SynthPackageSpec */) /*: string */ { spec.codegenPackagePath ?? '../../../ios'; packageDeps.push( reactNativePackageDecl( - `.package(name: "ReactNative", path: "${reactNativePackagePath}")`, + `.package(name: "${REACT_NATIVE_PACKAGE_NAME}", path: "${reactNativePackagePath}")`, ), ); // Per-app generated headers come from the ReactAppHeaders product in // the codegen package. packageDeps.push( - `.package(name: "React-GeneratedCode", path: "${codegenPackagePath}")`, + `.package(name: "${REACT_CODEGEN_PACKAGE_NAME}", path: "${codegenPackagePath}")`, ); } for (const dep of spmDependencies) { diff --git a/packages/react-native/scripts/spm/generate-spm-package.js b/packages/react-native/scripts/spm/generate-spm-package.js index 058d3ecf0db..46a12fdb173 100644 --- a/packages/react-native/scripts/spm/generate-spm-package.js +++ b/packages/react-native/scripts/spm/generate-spm-package.js @@ -37,6 +37,12 @@ const {prepareFlavoredFrameworks} = require('./flavored-frameworks'); const { + REACT_HEADERS_TARGET_DIR, + REACT_NATIVE_HEADERS_PRODUCT, + REACT_NATIVE_PACKAGE_NAME, + REACT_NATIVE_PRODUCTS, + REACT_NATIVE_UMBRELLA_PRODUCT, + REACT_NATIVE_XCFRAMEWORK_PRODUCTS, deriveAppName, displayPath, findProjectRoot, @@ -166,32 +172,38 @@ function findSourcePath( * Package.swift also imports it as a named package dependency. */ function generateXCFrameworksPackageSwift() /*: string */ { + // Each product's target follows from its KIND, not from its position in the + // list: the umbrella is a Clang target over the staged headers, and every + // xcframework-backed product gets a binaryTarget of the same name. + const products = REACT_NATIVE_PRODUCTS.map( + product => ` .library(name: "${product}", targets: ["${product}"]),`, + ); + const targets = [ + ` .target( + name: "${REACT_NATIVE_UMBRELLA_PRODUCT}", + dependencies: ["${REACT_NATIVE_HEADERS_PRODUCT}"], + path: "${REACT_HEADERS_TARGET_DIR}", + publicHeadersPath: "include" + ),`, + ...REACT_NATIVE_XCFRAMEWORK_PRODUCTS.map( + product => ` .binaryTarget( + name: "${product}", + path: "${product}.xcframework" + ),`, + ), + ]; + return `// swift-tools-version: 6.0 // AUTO-GENERATED by scripts/generate-spm-package.js – do not edit manually. import PackageDescription let package = Package( - name: "ReactNative", + name: "${REACT_NATIVE_PACKAGE_NAME}", products: [ - .library(name: "ReactHeaders", targets: ["ReactHeaders"]), - .library(name: "ReactNativeHeaders", targets: ["ReactNativeHeaders"]), - .library(name: "ReactNativeDependenciesHeaders", targets: ["ReactNativeDependenciesHeaders"]), +${products.join('\n')} ], targets: [ - .target( - name: "ReactHeaders", - dependencies: ["ReactNativeHeaders"], - path: "ReactHeadersTarget", - publicHeadersPath: "include" - ), - .binaryTarget( - name: "ReactNativeHeaders", - path: "ReactNativeHeaders.xcframework" - ), - .binaryTarget( - name: "ReactNativeDependenciesHeaders", - path: "ReactNativeDependenciesHeaders.xcframework" - ), +${targets.join('\n')} ] ) `; diff --git a/packages/react-native/scripts/spm/generate-spm-xcodeproj.js b/packages/react-native/scripts/spm/generate-spm-xcodeproj.js index a32b57650a8..5c0c5d35ce3 100644 --- a/packages/react-native/scripts/spm/generate-spm-xcodeproj.js +++ b/packages/react-native/scripts/spm/generate-spm-xcodeproj.js @@ -46,6 +46,11 @@ const { uuidComment, } = require('./spm-pbxproj'); const { + AUTOLINKED_PACKAGE_NAME, + REACT_CODEGEN_APP_PRODUCTS, + REACT_CODEGEN_PACKAGE_NAME, + REACT_NATIVE_PACKAGE_NAME, + REACT_NATIVE_PRODUCTS, isValidScriptPhaseId, isValidScriptPhaseName, makeLogger, @@ -111,36 +116,21 @@ const GENERATED_SOURCE_FILE_TYPES /*: {[string]: string} */ = { // resolve the product dependencies — SPM doesn't expose transitive products. const SPM_PRODUCT_PACKAGES /*: Array<{product: string, packagePath: string, packageName: string}> */ = [ - { - product: 'ReactHeaders', - packagePath: 'build/xcframeworks', - packageName: 'ReactNative', - }, - { - product: 'ReactNativeHeaders', + ...REACT_NATIVE_PRODUCTS.map(product => ({ + product, packagePath: 'build/xcframeworks', - packageName: 'ReactNative', - }, + packageName: REACT_NATIVE_PACKAGE_NAME, + })), { - product: 'ReactNativeDependenciesHeaders', - packagePath: 'build/xcframeworks', - packageName: 'ReactNative', - }, - { - product: 'Autolinked', + product: AUTOLINKED_PACKAGE_NAME, packagePath: 'build/generated/autolinking', - packageName: 'Autolinked', - }, - { - product: 'ReactCodegen', - packagePath: 'build/generated/ios', - packageName: 'React-GeneratedCode', + packageName: AUTOLINKED_PACKAGE_NAME, }, - { - product: 'ReactAppDependencyProvider', + ...REACT_CODEGEN_APP_PRODUCTS.map(product => ({ + product, packagePath: 'build/generated/ios', - packageName: 'React-GeneratedCode', - }, + packageName: REACT_CODEGEN_PACKAGE_NAME, + })), ]; /*:: diff --git a/packages/react-native/scripts/spm/scaffold-package-swift.js b/packages/react-native/scripts/spm/scaffold-package-swift.js index 6d52f60760b..ce3103ebedd 100644 --- a/packages/react-native/scripts/spm/scaffold-package-swift.js +++ b/packages/react-native/scripts/spm/scaffold-package-swift.js @@ -43,6 +43,10 @@ const { const {expandSpmSourceGlobs} = require('./generate-spm-autolinking'); const {readPodspec} = require('./read-podspec'); const { + REACT_CODEGEN_PACKAGE_NAME, + REACT_CODEGEN_PRODUCTS, + REACT_NATIVE_PACKAGE_NAME, + REACT_NATIVE_PRODUCTS, SCAFFOLDER_MARKER, makeLogger, remotePackageConfig, @@ -652,7 +656,8 @@ function emitScaffoldedPackageSwift( // the app by definition, so it stays a path reference — relative, // computed at scaffold time. const remote = ctx.remote; - const rnLabel = remote != null ? remote.identity : 'ReactNative'; + const rnLabel = + remote != null ? remote.identity : REACT_NATIVE_PACKAGE_NAME; const codegenDir = ctx.codegenPackageDir; if (codegenDir == null) { throw new Error( @@ -670,21 +675,21 @@ function emitScaffoldedPackageSwift( 'emitScaffoldedPackageSwift: localXcfwPackageDir is required when no remote package is configured.', ); } - packageDeps.push(`.package(name: "ReactNative", path: "${xcfwDir}")`); + packageDeps.push( + `.package(name: "${REACT_NATIVE_PACKAGE_NAME}", path: "${xcfwDir}")`, + ); } packageDeps.push( - `.package(name: "React-GeneratedCode", path: "${codegenDir}")`, - ); - targetDeps.push(`.product(name: "ReactHeaders", package: "${rnLabel}")`); - targetDeps.push( - `.product(name: "ReactNativeHeaders", package: "${rnLabel}")`, - ); - targetDeps.push( - `.product(name: "ReactNativeDependenciesHeaders", package: "${rnLabel}")`, - ); - targetDeps.push( - '.product(name: "ReactAppHeaders", package: "React-GeneratedCode")', + `.package(name: "${REACT_CODEGEN_PACKAGE_NAME}", path: "${codegenDir}")`, ); + for (const product of REACT_NATIVE_PRODUCTS) { + targetDeps.push(`.product(name: "${product}", package: "${rnLabel}")`); + } + for (const product of REACT_CODEGEN_PRODUCTS) { + targetDeps.push( + `.product(name: "${product}", package: "${REACT_CODEGEN_PACKAGE_NAME}")`, + ); + } } for (const siblingName of spec.siblingNames) { const swiftSibling = toSwiftName(siblingName); diff --git a/packages/react-native/scripts/spm/spm-utils.js b/packages/react-native/scripts/spm/spm-utils.js index 68a70b20133..c974b12b01f 100644 --- a/packages/react-native/scripts/spm/spm-utils.js +++ b/packages/react-native/scripts/spm/spm-utils.js @@ -14,6 +14,55 @@ const fs = require('node:fs'); const os = require('node:os'); const path = require('node:path'); +// The package and product names React Native's own generated manifests use, +// in one place so the emitters in this directory cannot drift apart from each +// other. +// +// Adding a React Native SPM product also touches, depending on what the +// product is: +// - scripts/codegen/templates/Package.swift.spm-template — if the per-app +// codegen package must depend on it (a static Swift file, patched by +// string replacement, not generated from these constants) +// - flavored-frameworks.js INVARIANT_BINARY_TARGETS — if it is +// xcframework-backed +// - download-spm-artifacts.js REQUIRED_ARTIFACTS — if it ships as its own +// downloadable artifact (that list names ARTIFACTS, which overlap with +// product names without being the same set) +const REACT_NATIVE_PACKAGE_NAME /*: string */ = 'ReactNative'; +const REACT_CODEGEN_PACKAGE_NAME /*: string */ = 'React-GeneratedCode'; +// The autolinking aggregator package, whose single product shares its name. +const AUTOLINKED_PACKAGE_NAME /*: string */ = 'Autolinked'; +// React Native's products by KIND, so no consumer has to infer the kind from a +// position in a flat list. The umbrella is a Clang target over the staged +// headers that re-exports the pure-RN namespaces; the rest are each backed by +// an xcframework of the same name. +const REACT_NATIVE_UMBRELLA_PRODUCT /*: string */ = 'ReactHeaders'; +const REACT_NATIVE_HEADERS_PRODUCT /*: string */ = 'ReactNativeHeaders'; +const REACT_NATIVE_DEPENDENCIES_HEADERS_PRODUCT /*: string */ = + 'ReactNativeDependenciesHeaders'; +const REACT_NATIVE_XCFRAMEWORK_PRODUCTS /*: ReadonlyArray */ = + Object.freeze([ + REACT_NATIVE_HEADERS_PRODUCT, + REACT_NATIVE_DEPENDENCIES_HEADERS_PRODUCT, + ]); +// Derived, so a new product cannot be added without choosing a kind above. +const REACT_NATIVE_PRODUCTS /*: ReadonlyArray */ = Object.freeze([ + REACT_NATIVE_UMBRELLA_PRODUCT, + ...REACT_NATIVE_XCFRAMEWORK_PRODUCTS, +]); +// The codegen package product every autolinked target depends on for the app's +// generated headers. +const REACT_CODEGEN_PRODUCTS /*: ReadonlyArray */ = Object.freeze([ + 'ReactAppHeaders', +]); +// The codegen package products the APP target links, declared by the static +// template rather than by any emitter here. +const REACT_CODEGEN_APP_PRODUCTS /*: ReadonlyArray */ = Object.freeze([ + 'ReactCodegen', + 'ReactAppDependencyProvider', +]); +const REACT_HEADERS_TARGET_DIR /*: string */ = 'ReactHeadersTarget'; + /** * Creates a logger trio {log, warn, die} that prefixes messages with [name]. * log – green prefix, writes to stdout @@ -582,10 +631,10 @@ function installSpmCodegenTemplate( if (remote != null) { content = content .replace( - '.package(name: "ReactNative", path: "../../xcframeworks"),', + `.package(name: "${REACT_NATIVE_PACKAGE_NAME}", path: "../../xcframeworks"),`, `.package(url: "${remote.url}", exact: "${remote.version}"),`, ) - .split('package: "ReactNative")') + .split(`package: "${REACT_NATIVE_PACKAGE_NAME}")`) .join(`package: "${remote.identity}")`); } fs.writeFileSync(codegenPkgSwift, content, 'utf8'); @@ -666,6 +715,16 @@ function isValidScriptPhaseName(value /*: unknown */) /*: boolean */ { } module.exports = { + REACT_NATIVE_PACKAGE_NAME, + REACT_CODEGEN_PACKAGE_NAME, + AUTOLINKED_PACKAGE_NAME, + REACT_NATIVE_UMBRELLA_PRODUCT, + REACT_NATIVE_HEADERS_PRODUCT, + REACT_NATIVE_XCFRAMEWORK_PRODUCTS, + REACT_NATIVE_PRODUCTS, + REACT_CODEGEN_PRODUCTS, + REACT_CODEGEN_APP_PRODUCTS, + REACT_HEADERS_TARGET_DIR, makeLogger, displayPath, sharedCacheDir,