Skip to content
This repository was archived by the owner on Jan 13, 2024. It is now read-only.

Commit 165617a

Browse files
authored
bootstrap: better support of .node files (#1321)
When a `.node` file is required `pkg` copies it in a temporary folder like `/tmp/<hash>/addon.node` the problem is that for modules like `sharp` this isn't working as them are statically linked to other `.so` files using relative paths. This pr will copy the entire module folder inside the temporary directory, this allows also to remove the `tryImport` function as there is no reason to try again if that fails. I tested this on my end and it's working I also grouped all hash folders inside `pkg` folder to be sure that the copy will run when users will update pkg, otherwise the copy may not be done when the folder already exists but was created with a previous pkg version Fixes #1075
1 parent 59125d1 commit 165617a

File tree

1 file changed

+66
-70
lines changed

1 file changed

+66
-70
lines changed

prelude/bootstrap.js

+66-70
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,49 @@ function createMountpoint(interior, exterior) {
158158
mountpoints.push({ interior, exterior });
159159
}
160160

161+
function copyFileSync(source, target) {
162+
let targetFile = target;
163+
164+
// If target is a directory, a new file with the same name will be created
165+
if (fs.existsSync(target)) {
166+
if (fs.lstatSync(target).isDirectory()) {
167+
targetFile = path.join(target, path.basename(source));
168+
}
169+
}
170+
171+
fs.writeFileSync(targetFile, fs.readFileSync(source));
172+
}
173+
174+
function copyFolderRecursiveSync(source, target) {
175+
let files = [];
176+
177+
// Check if folder needs to be created or integrated
178+
const targetFolder = path.join(target, path.basename(source));
179+
if (!fs.existsSync(targetFolder)) {
180+
fs.mkdirSync(targetFolder);
181+
}
182+
183+
// Copy
184+
if (fs.lstatSync(source).isDirectory()) {
185+
files = fs.readdirSync(source);
186+
files.forEach((file) => {
187+
const curSource = path.join(source, file);
188+
if (fs.lstatSync(curSource).isDirectory()) {
189+
copyFolderRecursiveSync(curSource, targetFolder);
190+
} else {
191+
copyFileSync(curSource, targetFolder);
192+
}
193+
});
194+
}
195+
}
196+
197+
function createDirRecursively(dir) {
198+
if (!fs.existsSync(dir)) {
199+
createDirRecursively(path.join(dir, '..'));
200+
fs.mkdirSync(dir);
201+
}
202+
}
203+
161204
/*
162205
163206
// TODO move to some test
@@ -2020,88 +2063,41 @@ function payloadFileSync(pointer) {
20202063
const modulePath = revertMakingLong(args[1]);
20212064
const moduleBaseName = path.basename(modulePath);
20222065
const moduleFolder = path.dirname(modulePath);
2023-
const unknownModuleErrorRegex = /([^:]+): cannot open shared object file: No such file or directory/;
20242066

2025-
function tryImporting(_tmpFolder, previousErrorMessage) {
2026-
try {
2027-
const res = ancestor.dlopen.apply(process, args);
2028-
return res;
2029-
} catch (e) {
2030-
if (e.message === previousErrorMessage) {
2031-
// we already tried to fix this and it didn't work, give up
2032-
throw e;
2033-
}
2034-
if (e.message.match(unknownModuleErrorRegex)) {
2035-
// this case triggers on linux, the error message give us a clue on what dynamic linking library
2036-
// is missing.
2037-
// some modules are packaged with dynamic linking and needs to open other files that should be in
2038-
// the same directory, in this case, we write this file in the same /tmp directory and try to
2039-
// import the module again
2040-
2041-
const moduleName = e.message.match(unknownModuleErrorRegex)[1];
2042-
const importModulePath = path.join(moduleFolder, moduleName);
2043-
2044-
if (!fs.existsSync(importModulePath)) {
2045-
throw new Error(
2046-
`INTERNAL ERROR this file doesn't exist in the virtual file system :${importModulePath}`
2047-
);
2048-
}
2049-
const moduleContent1 = fs.readFileSync(importModulePath);
2050-
const tmpModulePath1 = path.join(_tmpFolder, moduleName);
2051-
2052-
try {
2053-
fs.statSync(tmpModulePath1);
2054-
} catch (err) {
2055-
fs.writeFileSync(tmpModulePath1, moduleContent1, { mode: 0o555 });
2056-
}
2057-
return tryImporting(_tmpFolder, e.message);
2058-
}
2067+
// Example: moduleFolder = /snapshot/appname/node_modules/sharp/build/Release
2068+
const modulePkgPathRegex = /.*?node_modules\/((.+?)\/.*)/;
2069+
// Example: modulePackagePath = sharp/build/Release
2070+
const modulePackagePath = moduleFolder.match(modulePkgPathRegex)[1];
2071+
// Example: modulePackageName = sharp
2072+
const modulePackageName = moduleFolder.match(modulePkgPathRegex)[2];
2073+
// Example: modulePkgFolder = /snapshot/appname/node_modules/sharp
2074+
const modulePkgFolder = moduleFolder.replace(
2075+
modulePackagePath,
2076+
modulePackageName
2077+
);
20592078

2060-
// this case triggers on windows mainly.
2061-
// we copy all stuff that exists in the folder of the .node module
2062-
// into the temporary folders...
2063-
const files = fs.readdirSync(moduleFolder);
2064-
for (const file of files) {
2065-
if (file === moduleBaseName) {
2066-
// ignore the current module
2067-
continue;
2068-
}
2069-
const filenameSrc = path.join(moduleFolder, file);
2070-
2071-
if (fs.statSync(filenameSrc).isDirectory()) {
2072-
continue;
2073-
}
2074-
const filenameDst = path.join(_tmpFolder, file);
2075-
const content = fs.readFileSync(filenameSrc);
2076-
2077-
fs.writeFileSync(filenameDst, content, { mode: 0o555 });
2078-
}
2079-
return tryImporting(_tmpFolder, e.message);
2080-
}
2081-
}
20822079
if (insideSnapshot(modulePath)) {
20832080
const moduleContent = fs.readFileSync(modulePath);
20842081

20852082
// Node addon files and .so cannot be read with fs directly, they are loaded with process.dlopen which needs a filesystem path
20862083
// we need to write the file somewhere on disk first and then load it
2084+
// the hash is needed to be sure we reload the module in case it changes
20872085
const hash = createHash('sha256').update(moduleContent).digest('hex');
20882086

2089-
const tmpFolder = path.join(tmpdir(), hash);
2087+
// Example: /tmp/pkg/<hash>
2088+
const tmpFolder = path.join(tmpdir(), 'pkg', hash);
20902089
if (!fs.existsSync(tmpFolder)) {
2091-
fs.mkdirSync(tmpFolder);
2090+
// here we copy all files from the snapshot module folder to temporary folder
2091+
// we keep the module folder structure to prevent issues with modules that are statically
2092+
// linked using relative paths (Fix #1075)
2093+
createDirRecursively(tmpFolder);
2094+
copyFolderRecursiveSync(modulePkgFolder, tmpFolder);
20922095
}
2093-
const tmpModulePath = path.join(tmpFolder, moduleBaseName);
20942096

2095-
try {
2096-
fs.statSync(tmpModulePath);
2097-
} catch (e) {
2098-
// Most likely this means the module is not on disk yet
2099-
fs.writeFileSync(tmpModulePath, moduleContent, { mode: 0o755 });
2100-
}
2101-
args[1] = tmpModulePath;
2102-
tryImporting(tmpFolder);
2103-
} else {
2104-
return ancestor.dlopen.apply(process, args);
2097+
// replace the path with the new module path
2098+
args[1] = path.join(tmpFolder, modulePackagePath, moduleBaseName);
21052099
}
2100+
2101+
return ancestor.dlopen.apply(process, args);
21062102
};
21072103
})();

0 commit comments

Comments
 (0)