diff --git a/packages/platform-android/src/commands/runAndroid/index.js b/packages/platform-android/src/commands/runAndroid/index.js index 096da3991..a687ff69b 100644 --- a/packages/platform-android/src/commands/runAndroid/index.js +++ b/packages/platform-android/src/commands/runAndroid/index.js @@ -24,6 +24,7 @@ import { getDefaultUserTerminal, CLIError, } from '@react-native-community/cli-tools'; +import warnAboutManuallyLinkedLibs from '../../link/warnAboutManuallyLinkedLibs'; // Verifies this is an Android project function checkAndroid(root) { @@ -55,6 +56,8 @@ function runAndroid(argv: Array, config: ConfigT, args: FlagsT) { return; } + warnAboutManuallyLinkedLibs(config); + if (!args.packager) { return buildAndRun(args); } diff --git a/packages/platform-android/src/link/warnAboutManuallyLinkedLibs.js b/packages/platform-android/src/link/warnAboutManuallyLinkedLibs.js new file mode 100644 index 000000000..78ceb9a29 --- /dev/null +++ b/packages/platform-android/src/link/warnAboutManuallyLinkedLibs.js @@ -0,0 +1,51 @@ +// @flow + +import chalk from 'chalk'; +import {logger} from '@react-native-community/cli-tools'; +import type {ConfigT} from 'types'; +import getLinkConfig from './index'; + +// TODO: move to cli-tools once platform-ios and platform-android are migrated +// to TS and unify with iOS implementation +export default function warnAboutManuallyLinkedLibs( + config: ConfigT, + platform: string = 'android', + linkConfig: $Call = getLinkConfig(), +) { + let deps = []; + + for (let key in config.dependencies) { + const dependency = config.dependencies[key]; + try { + const projectConfig = config.project[platform]; + const dependencyConfig = dependency.platforms[platform]; + if (projectConfig && dependencyConfig) { + const x = linkConfig.isInstalled( + projectConfig, + dependency.name, + dependencyConfig, + ); + deps = deps.concat(x ? dependency.name : []); + } + } catch (error) { + logger.debug('Checking manually linked modules failed.', error); + } + } + + const installedModules = [...new Set(deps)]; + + if (installedModules.length) { + logger.error( + `React Native CLI uses autolinking for native dependencies, but following modules are linked manually: \n${installedModules + .map( + x => + ` - ${chalk.bold(x)} ${chalk.dim( + `(to unlink run: "react-native unlink ${x}")`, + )}`, + ) + .join( + '\n', + )}\nThis is likely to happen when upgrading React Native from version lower than 0.60 to 0.60 or later. Please unlink them as they are likely to cause build failures. You can do so with "react-native unlink" command as shown above. If a library is not compatible with autolinking yet, please ignore this warning and notify the library maintainers.`, + ); + } +} diff --git a/packages/platform-ios/src/commands/runIOS/index.js b/packages/platform-ios/src/commands/runIOS/index.js index 239334d07..1726d2ea4 100644 --- a/packages/platform-ios/src/commands/runIOS/index.js +++ b/packages/platform-ios/src/commands/runIOS/index.js @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. * * @flow - * @format */ import child_process from 'child_process'; @@ -16,6 +15,7 @@ import type {ConfigT} from 'types'; import findXcodeProject from './findXcodeProject'; import parseIOSDevicesList from './parseIOSDevicesList'; import findMatchingSimulator from './findMatchingSimulator'; +import warnAboutManuallyLinkedLibs from '../../link/warnAboutManuallyLinkedLibs'; import { logger, CLIError, @@ -42,6 +42,8 @@ function runIOS(_: Array, ctx: ConfigT, args: FlagsT) { ); } + warnAboutManuallyLinkedLibs(ctx); + process.chdir(args.projectPath); const xcodeProject = findXcodeProject(fs.readdirSync('.')); diff --git a/packages/platform-ios/src/config/index.js b/packages/platform-ios/src/config/index.js index a6f7b4855..5bf40f7b8 100644 --- a/packages/platform-ios/src/config/index.js +++ b/packages/platform-ios/src/config/index.js @@ -9,11 +9,14 @@ */ import path from 'path'; +import {memoize} from 'lodash'; import findProject from './findProject'; import findPodfilePath from './findPodfilePath'; import findPodspec from './findPodspec'; import type {UserConfigT} from 'types'; +const memoizedFindProject = memoize(findProject); + /** * For libraries specified without an extension, add '.tbd' for those that * start with 'lib' and '.framework' to the rest. @@ -37,7 +40,7 @@ export function projectConfig( if (!userConfig) { return; } - const project = userConfig.project || findProject(folder); + const project = userConfig.project || memoizedFindProject(folder); /** * No iOS config found here diff --git a/packages/platform-ios/src/link-pods/isInstalled.js b/packages/platform-ios/src/link-pods/isInstalled.js index d925a5592..caf97eb71 100644 --- a/packages/platform-ios/src/link-pods/isInstalled.js +++ b/packages/platform-ios/src/link-pods/isInstalled.js @@ -4,14 +4,18 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @flow */ import readPodfile from './readPodfile'; import getPodspecName from '../config/getPodspecName'; +import type {ProjectConfigIOST, DependencyConfigIOST} from 'types'; -export default function isInstalled(iOSProject, dependencyConfig) { - if (!iOSProject.podfile) { +export default function isInstalled( + iOSProject: ProjectConfigIOST, + dependencyConfig: DependencyConfigIOST, +) { + if (!iOSProject.podfile || !dependencyConfig.podspecPath) { return false; } // match line with pod declaration: pod 'dependencyPodName' (other possible parameters of pod are ignored) diff --git a/packages/platform-ios/src/link/common/isInstalled.js b/packages/platform-ios/src/link/common/isInstalled.js index acbe38838..fcb5fab00 100644 --- a/packages/platform-ios/src/link/common/isInstalled.js +++ b/packages/platform-ios/src/link/common/isInstalled.js @@ -4,13 +4,18 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @flow */ import isInstalledIOS from '../isInstalled'; import isInstalledPods from '../../link-pods/isInstalled'; +import type {ProjectConfigIOST, DependencyConfigIOST} from 'types'; -export default function isInstalled(projectConfig, name, dependencyConfig) { +export default function isInstalled( + projectConfig: ProjectConfigIOST, + name?: string, + dependencyConfig: DependencyConfigIOST, +) { return ( isInstalledIOS(projectConfig, dependencyConfig) || isInstalledPods(projectConfig, dependencyConfig) diff --git a/packages/platform-ios/src/link/index.js b/packages/platform-ios/src/link/index.js index cb7d49142..687e922f4 100644 --- a/packages/platform-ios/src/link/index.js +++ b/packages/platform-ios/src/link/index.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @flow */ import isInstalled from './common/isInstalled'; diff --git a/packages/platform-ios/src/link/isInstalled.js b/packages/platform-ios/src/link/isInstalled.js index cf88883bc..13c74cdc1 100644 --- a/packages/platform-ios/src/link/isInstalled.js +++ b/packages/platform-ios/src/link/isInstalled.js @@ -4,19 +4,33 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format + * @flow */ import xcode from 'xcode'; import getGroup from './getGroup'; import hasLibraryImported from './hasLibraryImported'; +import type {ProjectConfigIOST, DependencyConfigIOST} from 'types'; + +const memo = new Map(); /** * Returns true if `xcodeproj` specified by dependencyConfig is present * in a top level `libraryFolder` */ -export default function isInstalled(projectConfig, dependencyConfig) { - const project = xcode.project(projectConfig.pbxprojPath).parseSync(); +export default function isInstalled( + projectConfig: ProjectConfigIOST, + dependencyConfig: DependencyConfigIOST, +) { + let project; + + if (memo.has(projectConfig.pbxprojPath)) { + project = memo.get(projectConfig.pbxprojPath); + } else { + project = xcode.project(projectConfig.pbxprojPath).parseSync(); + memo.set(projectConfig.pbxprojPath, project); + } + const libraries = getGroup(project, projectConfig.libraryFolder); if (!libraries) { diff --git a/packages/platform-ios/src/link/warnAboutManuallyLinkedLibs.js b/packages/platform-ios/src/link/warnAboutManuallyLinkedLibs.js new file mode 100644 index 000000000..fc5a67d6b --- /dev/null +++ b/packages/platform-ios/src/link/warnAboutManuallyLinkedLibs.js @@ -0,0 +1,51 @@ +// @flow + +import chalk from 'chalk'; +import {logger} from '@react-native-community/cli-tools'; +import type {ConfigT} from 'types'; +import getLinkConfig from './index'; + +// TODO: move to cli-tools once platform-ios and platform-android are migrated +// to TS and unify with Android implementation +export default function warnAboutManuallyLinkedLibs( + config: ConfigT, + platform?: string = 'ios', + linkConfig: $Call = getLinkConfig(), +) { + let deps = []; + + for (let key in config.dependencies) { + const dependency = config.dependencies[key]; + try { + const projectConfig = config.project[platform]; + const dependencyConfig = dependency.platforms[platform]; + if (projectConfig && dependencyConfig) { + const x = linkConfig.isInstalled( + projectConfig, + dependency.name, + dependencyConfig, + ); + deps = deps.concat(x ? dependency.name : []); + } + } catch (error) { + logger.debug('Checking manually linked modules failed.', error); + } + } + + const installedModules = [...new Set(deps)]; + + if (installedModules.length) { + logger.error( + `React Native CLI uses autolinking for native dependencies, but following modules are linked manually: \n${installedModules + .map( + x => + ` - ${chalk.bold(x)} ${chalk.dim( + `(to unlink run: "react-native unlink ${x}")`, + )}`, + ) + .join( + '\n', + )}\nThis is likely to happen when upgrading React Native from version lower than 0.60 to 0.60 or later. Please unlink them as they are likely to cause build failures. You can do so with "react-native unlink" command as shown above. If a library is not compatible with autolinking yet, please ignore this warning and notify the library maintainers.`, + ); + } +}