Skip to content

Commit

Permalink
Merge pull request atom#21927 from atom-community/incompatible-native…
Browse files Browse the repository at this point in the history
…-detection

fix: require .node files directly to detect incompatible native modules
  • Loading branch information
darangi authored Mar 13, 2021
2 parents c7bf8f1 + 0f04fb2 commit 7574784
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 15 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ node_modules
docs/output
docs/includes
spec/fixtures/evil-files/
!spec/fixtures/packages/package-with-incompatible-native-module-loaded-conditionally/node_modules/
out/
/electron/
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const condition = false;

if (condition) {
const { native } = require("./node_modules/native-module");
native(condition);
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "package-with-incompatible-native-module",
"version": "1.0.0",
"main": "./main.js"
}
14 changes: 14 additions & 0 deletions spec/package-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@ describe('Package', function() {
);
});

it('detects the package as incompatible even if .node file is loaded conditionally', function() {
const packagePath = atom.project
.getDirectories()[0]
.resolve(
'packages/package-with-incompatible-native-module-loaded-conditionally'
);
const pack = buildPackage(packagePath);
expect(pack.isCompatible()).toBe(false);
expect(pack.incompatibleModules[0].name).toBe('native-module');
expect(pack.incompatibleModules[0].path).toBe(
path.join(packagePath, 'node_modules', 'native-module')
);
});

it("utilizes _atomModuleCache if present to determine the package's native dependencies", function() {
let packagePath = atom.project
.getDirectories()[0]
Expand Down
53 changes: 38 additions & 15 deletions src/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -1126,46 +1126,60 @@ module.exports = class Package {
// Does the given module path contain native code?
isNativeModule(modulePath) {
try {
return (
fs.listSync(path.join(modulePath, 'build', 'Release'), ['.node'])
.length > 0
);
return this.getModulePathNodeFiles(modulePath).length > 0;
} catch (error) {
return false;
}
}

// Get an array of all the native modules that this package depends on.
// get the list of `.node` files for the given module path
getModulePathNodeFiles(modulePath) {
try {
const modulePathNodeFiles = fs.listSync(
path.join(modulePath, 'build', 'Release'),
['.node']
);
return modulePathNodeFiles;
} catch (error) {
return [];
}
}

// Get a Map of all the native modules => the `.node` files that this package depends on.
//
// First try to get this information from
// @metadata._atomModuleCache.extensions. If @metadata._atomModuleCache doesn't
// exist, recurse through all dependencies.
getNativeModuleDependencyPaths() {
const nativeModulePaths = [];
getNativeModuleDependencyPathsMap() {
const nativeModulePaths = new Map();

if (this.metadata._atomModuleCache) {
const nodeFilePaths = [];
const relativeNativeModuleBindingPaths =
(this.metadata._atomModuleCache.extensions &&
this.metadata._atomModuleCache.extensions['.node']) ||
[];
for (let relativeNativeModuleBindingPath of relativeNativeModuleBindingPaths) {
const nativeModulePath = path.join(
const nodeFilePath = path.join(
this.path,
relativeNativeModuleBindingPath,
'..',
'..',
'..'
);
nativeModulePaths.push(nativeModulePath);
nodeFilePaths.push(nodeFilePath);
}
nativeModulePaths.set(this.path, nodeFilePaths);
return nativeModulePaths;
}

var traversePath = nodeModulesPath => {
try {
for (let modulePath of fs.listSync(nodeModulesPath)) {
if (this.isNativeModule(modulePath))
nativeModulePaths.push(modulePath);
const modulePathNodeFiles = this.getModulePathNodeFiles(modulePath);
if (modulePathNodeFiles) {
nativeModulePaths.set(modulePath, modulePathNodeFiles);
}
traversePath(path.join(modulePath, 'node_modules'));
}
} catch (error) {}
Expand All @@ -1176,6 +1190,12 @@ module.exports = class Package {
return nativeModulePaths;
}

// Get an array of all the native modules that this package depends on.
// See `getNativeModuleDependencyPathsMap` for more information
getNativeModuleDependencyPaths() {
return [...this.getNativeModuleDependencyPathsMap().keys()];
}

/*
Section: Native Module Compatibility
*/
Expand Down Expand Up @@ -1277,8 +1297,7 @@ module.exports = class Package {
}

// Get the incompatible native modules that this package depends on.
// This recurses through all dependencies and requires all modules that
// contain a `.node` file.
// This recurses through all dependencies and requires all `.node` files.
//
// This information is cached in local storage on a per package/version basis
// to minimize the impact on startup time.
Expand All @@ -1293,9 +1312,13 @@ module.exports = class Package {
}

const incompatibleNativeModules = [];
for (let nativeModulePath of this.getNativeModuleDependencyPaths()) {
const nativeModulePaths = this.getNativeModuleDependencyPathsMap();
for (const [nativeModulePath, nodeFilesPaths] of nativeModulePaths) {
try {
require(nativeModulePath);
// require each .node file
for (const nodeFilePath of nodeFilesPaths) {
require(nodeFilePath);
}
} catch (error) {
let version;
try {
Expand Down

0 comments on commit 7574784

Please sign in to comment.