@red-mobile/nodejs-mobile-cordova
Version:
Node.js for Mobile Apps Cordova plugin
200 lines (187 loc) • 6.88 kB
JavaScript
const fs = require("fs");
const path = require("path");
const crypto = require("crypto");
const { spawnSync } = require("child_process");
function visitEveryFramework(projectPath) {
var foundFrameworks = [];
var countInvalidFrameworks = 0;
var countValidFrameworks = 0;
function recursivelyFindFrameworks(currentPath) {
let currentFiles = fs.readdirSync(currentPath);
for (let i = 0; i < currentFiles.length; i++) {
let currentFilename = path.normalize(
path.join(currentPath, currentFiles[i]),
);
if (fs.lstatSync(currentFilename).isDirectory()) {
if (currentFilename.endsWith(".node")) {
let frameworkContents = fs.readdirSync(currentFilename);
// Frameworks output by nodejs-mobile-gyp are expected to have only one file inside, corresponding to the proper shared library.
if (frameworkContents.length != 1) {
console.log(
'Skipping a ".node". Expected to find only one file inside this path: ' +
currentFilename,
);
countInvalidFrameworks++;
} else {
let currentBinaryName = frameworkContents[0];
let checkFileType = spawnSync("file", [
path.join(currentFilename, currentBinaryName),
]);
// File inside a .framework should be a dynamically linked shared library.
if (
checkFileType.stdout
.toString()
.indexOf("dynamically linked shared library") > -1
) {
let newFrameworkObject = {
originalFileName: currentFilename,
originalRelativePath: "",
originalBinaryName: currentBinaryName,
newFrameworkName: "",
newFrameworkFileName: "",
};
foundFrameworks.push(newFrameworkObject);
countValidFrameworks++;
} else {
console.log(
'Skipping a ".node". Couldn\'t find a dynamically linked shared library inside ' +
currentFilename,
);
countInvalidFrameworks++;
}
}
}
recursivelyFindFrameworks(currentFilename);
}
}
}
recursivelyFindFrameworks(projectPath);
console.log(
"Found " +
countValidFrameworks +
" valid frameworks and " +
countInvalidFrameworks +
" invalid frameworks after rebuilding the native modules for iOS.",
);
if (foundFrameworks.length < 1) {
console.log(
"No valid framework native modules were found. Skipping integrating them into the App.",
);
return;
}
for (let i = 0; i < foundFrameworks.length; i++) {
// Fill the helper fields for each framework.
let currentFramework = foundFrameworks[i];
currentFramework.originalRelativePath = path.relative(
projectPath,
currentFramework.originalFileName,
);
// To make each framework name unique while embedding, use a digest of the relative path.
let hash = crypto.createHash("sha1");
hash.update(currentFramework.originalRelativePath);
currentFramework.newFrameworkName = "node" + hash.digest("hex");
currentFramework.newFrameworkFileName = path.join(
path.dirname(currentFramework.originalFileName),
currentFramework.newFrameworkName + ".framework",
);
}
for (let i = 0; i < foundFrameworks.length; i++) {
// Rename the binaries to the new framework structure and add a .plist
let currentFramework = foundFrameworks[i];
fs.renameSync(
currentFramework.originalFileName,
currentFramework.newFrameworkFileName,
);
fs.renameSync(
path.join(
currentFramework.newFrameworkFileName,
currentFramework.originalBinaryName,
),
path.join(
currentFramework.newFrameworkFileName,
currentFramework.newFrameworkName,
),
);
// Read template Info.plist
let plistXmlContents = fs
.readFileSync(path.join(__dirname, "plisttemplate.xml"))
.toString();
// Replace values with the new bundle name and XCode environment variables.
plistXmlContents = plistXmlContents
.replace(
/\{ENV_MAC_OS_X_PRODUCT_BUILD_VERSION\}/g,
process.env.MAC_OS_X_PRODUCT_BUILD_VERSION,
)
.replace(/\{VAR_BINARY_NAME\}/g, currentFramework.newFrameworkName)
.replace(/\{ENV_DEFAULT_COMPILER\}/g, process.env.DEFAULT_COMPILER)
.replace(
/\{ENV_PLATFORM_PRODUCT_BUILD_VERSION\}/g,
process.env.PLATFORM_PRODUCT_BUILD_VERSION,
)
.replace(/\{ENV_SDK_VERSION\}/g, process.env.SDK_VERSION)
.replace(
/\{ENV_SDK_PRODUCT_BUILD_VERSION\}/g,
process.env.SDK_PRODUCT_BUILD_VERSION,
)
.replace(/\{ENV_SDK_NAME\}/g, process.env.SDK_NAME)
.replace(
/\{ENV_XCODE_VERSION_ACTUAL\}/g,
process.env.XCODE_VERSION_ACTUAL,
)
.replace(
/\{ENV_XCODE_PRODUCT_BUILD_VERSION\}/g,
process.env.XCODE_PRODUCT_BUILD_VERSION,
);
// Use plutil to generate the plist in the binary format.
let plistGeneration = spawnSync(
"plutil",
[
"-convert",
"binary1", // Will convert the xml plist to binary.
"-o",
path.join(currentFramework.newFrameworkFileName, "Info.plist"), // target Info.plist path.
"-", // read the input from the process stdin.
],
{
input: plistXmlContents,
},
);
}
var frameworkOverrideContents = [];
for (let i = 0; i < foundFrameworks.length; i++) {
// Generate the contents of a JSON file for overriding dlopen calls at runtime.
let currentFramework = foundFrameworks[i];
frameworkOverrideContents.push({
originalpath: currentFramework.originalRelativePath.split(path.sep),
newpath: [
"..",
"..",
"Frameworks",
currentFramework.newFrameworkName + ".framework",
currentFramework.newFrameworkName,
],
});
}
fs.writeFileSync(
path.join(projectPath, "override-dlopen-paths-data.json"),
JSON.stringify(frameworkOverrideContents),
);
// Copy runtime script that will override dlopen paths.
fs.copyFileSync(
path.join(__dirname, "override-dlopen-paths-preload.js"),
path.join(projectPath, "override-dlopen-paths-preload.js"),
);
for (let i = 0; i < foundFrameworks.length; i++) {
// Put an empty file in each of the .node original locations, since some modules check their existence.
fs.closeSync(fs.openSync(foundFrameworks[i].originalFileName, "w"));
}
}
if (process.argv.length >= 3) {
if (fs.existsSync(process.argv[2])) {
visitEveryFramework(path.normalize(process.argv[2]));
}
process.exit(0);
} else {
console.error("A path is expected as an argument.");
process.exit(1);
}