Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/platform-android/src/commands/runAndroid/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -55,6 +56,8 @@ function runAndroid(argv: Array<string>, config: ConfigT, args: FlagsT) {
return;
}

warnAboutManuallyLinkedLibs(config);

if (!args.packager) {
return buildAndRun(args);
}
Expand Down
51 changes: 51 additions & 0 deletions packages/platform-android/src/link/warnAboutManuallyLinkedLibs.js
Original file line number Diff line number Diff line change
@@ -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<typeof getLinkConfig> = 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
Copy link
Member

Choose a reason for hiding this comment

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

"but the following"

.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.`,
Copy link
Member

Choose a reason for hiding this comment

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

"This is likely happening when upgrading React Native from below 0.60 to 0.60 or above. Going forward, you can unlink this dependency via react-native unlink <dependency> and it will included in your app automatically. If a library isn't compatible with autolinking, disregard this message and notify the library maintainers.

Read more about autolinking: […]"

);
}
}
4 changes: 3 additions & 1 deletion packages/platform-ios/src/commands/runIOS/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/

import child_process from 'child_process';
Expand All @@ -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,
Expand All @@ -42,6 +42,8 @@ function runIOS(_: Array<string>, ctx: ConfigT, args: FlagsT) {
);
}

warnAboutManuallyLinkedLibs(ctx);

process.chdir(args.projectPath);

const xcodeProject = findXcodeProject(fs.readdirSync('.'));
Expand Down
5 changes: 4 additions & 1 deletion packages/platform-ios/src/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Member Author

Choose a reason for hiding this comment

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

findProject is expensive (runs FS operations through glob) and often called with the same folder argument, hence memoizing. Without this change, displaying the warning before builds would essentially be unusable, since it runs ~10s for as small as 4 native modules. With memoization the time is less than 800ms, yielding ~12x improvement that gets bigger with more deps.


/**
* For libraries specified without an extension, add '.tbd' for those that
* start with 'lib' and '.framework' to the rest.
Expand All @@ -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
Expand Down
10 changes: 7 additions & 3 deletions packages/platform-ios/src/link-pods/isInstalled.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 7 additions & 2 deletions packages/platform-ios/src/link/common/isInstalled.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion packages/platform-ios/src/link/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
20 changes: 17 additions & 3 deletions packages/platform-ios/src/link/isInstalled.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
51 changes: 51 additions & 0 deletions packages/platform-ios/src/link/warnAboutManuallyLinkedLibs.js
Original file line number Diff line number Diff line change
@@ -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<typeof getLinkConfig> = 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
Copy link
Member

Choose a reason for hiding this comment

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

"but the following"

.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.`,
Copy link
Member

Choose a reason for hiding this comment

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

Same as above.

);
}
}