pj-nodegit
Version:
Node.js libgit2 asynchronous native bindings
224 lines (186 loc) • 7.43 kB
JavaScript
const crypto = require("crypto");
const execPromise = require("./execPromise");
// for fs.remove. replace with fs.rm after dropping v12 support
const fse = require("fs-extra");
const fsNonPromise = require("fs");
const { promises: fs } = fsNonPromise;
const path = require("path");
const got = require("got");
const { performance } = require("perf_hooks");
const { promisify } = require("util");
const stream = require("stream");
const tar = require("tar-fs");
const zlib = require("zlib");
const pipeline = promisify(stream.pipeline);
const win32BatPath = path.join(__dirname, "build-openssl.bat");
const vendorPath = path.resolve(__dirname, "..", "vendor");
const extractPath = path.join(vendorPath, "openssl");
const getOpenSSLSourceUrl = (version) => `https://www.openssl.org/source/openssl-${version}.tar.gz`;
const getOpenSSLSourceSha256Url = (version) => `${getOpenSSLSourceUrl(version)}.sha256`;
class HashVerify extends stream.Transform {
constructor(algorithm, expected) {
super();
this.expected = expected;
this.hash = crypto.createHash(algorithm);
}
_transform(chunk, encoding, callback) {
this.hash.update(chunk, encoding);
callback(null, chunk);
}
_final(callback) {
const digest = this.hash.digest("hex");
const digestOk = digest === this.expected;
callback(digestOk ? null : new Error(`Digest not OK: ${digest} !== ${this.expected}`));
}
}
const buildDarwin = async (buildCwd, macOsDeploymentTarget) => {
await execPromise(`./Configure darwin64-x86_64-cc shared enable-ec_nistp_64_gcc_128 no-ssl2 no-ssl3 no-comp --prefix="${
extractPath
}" --openssldir="${extractPath}" -mmacosx-version-min=${macOsDeploymentTarget}`, {
cwd: buildCwd
}, { pipeOutput: true });
await execPromise("make", {
cwd: buildCwd
}, { pipeOutput: true });
await execPromise("make test", {
cwd: buildCwd
}, { pipeOutput: true });
await execPromise("make install", {
cwd: buildCwd,
maxBuffer: 10 * 1024 * 1024 // we should really just use spawn
}, { pipeOutput: true });
};
const buildWin32 = async (buildCwd) => {
const vcvarsallArch = process.arch === "x64" ? "x64" : "x86";
const programFilesPath = (vcvarsallArch === "x64"
? process.env["ProgramFiles(x86)"]
: process.env.ProgramFiles) || "C:\\Program Files";
const vcvarsallPath = process.env.npm_config_vcvarsall_path || `${
programFilesPath
}\\Microsoft Visual Studio\\2017\\BuildTools\\VC\\Auxiliary\\Build\\vcvarsall.bat`;
try {
await fs.stat(vcvarsallPath);
} catch {
throw new Error(`vcvarsall.bat not found at ${vcvarsallPath}`);
}
const vcTarget = vcvarsallArch === "x64" ? "VC-WIN64A" : "VC-WIN32";
await execPromise(`"${win32BatPath}" "${vcvarsallPath}" ${vcvarsallArch} ${vcTarget}`, {
cwd: buildCwd,
maxBuffer: 10 * 1024 * 1024 // we should really just use spawn
}, { pipeOutput: true });
};
const removeOpenSSLIfOudated = async (openSSLVersion) => {
try {
let openSSLResult;
try {
const openSSLPath = path.join(extractPath, "bin", "openssl");
openSSLResult = await execPromise(`${openSSLPath} version`);
} catch {
/* if we fail to get the version, assume removal not required */
}
if (!openSSLResult) {
return;
}
const versionMatch = openSSLResult.match(/^OpenSSL (\d\.\d\.\d[a-z]*)/);
const installedVersion = versionMatch && versionMatch[1];
if (!installedVersion || installedVersion === openSSLVersion) {
return;
}
console.log("Removing outdated OpenSSL at: ", extractPath);
await fse.remove(extractPath);
console.log("Outdated OpenSSL removed.");
} catch (err) {
console.log("Remove outdated OpenSSL failed: ", err);
}
};
const makeOnStreamDownloadProgress = () => {
let lastReport = performance.now();
return ({ percent, transferred, total }) => {
const currentTime = performance.now();
if (currentTime - lastReport > 1 * 1000) {
lastReport = currentTime;
console.log(`progress: ${transferred}/${total} (${(percent * 100).toFixed(2)}%)`)
}
};
};
const buildOpenSSLIfNecessary = async (openSSLVersion, macOsDeploymentTarget) => {
if (process.platform !== "darwin" && process.platform !== "win32") {
console.log(`Skipping OpenSSL build, not required on ${process.platform}`);
return;
}
await removeOpenSSLIfOudated(openSSLVersion);
try {
await fs.stat(extractPath);
console.log("Skipping OpenSSL build, dir exists:"+extractPath);
// return;
} catch {
console.log("OpenSSL build, dir not exists:"+extractPath);
}
const openSSLUrl = getOpenSSLSourceUrl(openSSLVersion);
const openSSLSha256Url = getOpenSSLSourceSha256Url(openSSLVersion);
const openSSLSha256 = (await got(openSSLSha256Url)).body.trim();
const downloadStream = got.stream(openSSLUrl);
downloadStream.on("downloadProgress", makeOnStreamDownloadProgress());
await pipeline(
downloadStream,
new HashVerify("sha256", openSSLSha256),
zlib.createGunzip(),
tar.extract(extractPath)
);
console.log(`OpenSSL ${openSSLVersion} download + extract complete: SHA256 OK.`);
const buildCwd = path.join(extractPath, `openssl-${openSSLVersion}`);
if (process.platform === "darwin") {
await buildDarwin(buildCwd, macOsDeploymentTarget);
} else if (process.platform === "win32") {
await buildWin32(buildCwd);
} else {
throw new Error(`Unknown platform: ${process.platform}`);
}
console.log("Build finished.");
}
const downloadOpenSSLIfNecessary = async (downloadBinUrl, maybeDownloadSha256) => {
if (process.platform !== "darwin" && process.platform !== "win32") {
console.log(`Skipping OpenSSL download, not required on ${process.platform}`);
return;
}
try {
await fs.stat(extractPath);
console.log("Skipping OpenSSL download, dir exists");
return;
} catch {}
const downloadStream = got.stream(downloadBinUrl);
downloadStream.on("downloadProgress", makeOnStreamDownloadProgress());
const pipelineSteps = [
downloadStream,
maybeDownloadSha256 ? new HashVerify("sha256", maybeDownloadSha256) : null,
zlib.createGunzip(),
tar.extract(extractPath)
].filter(step => step !== null);
await pipeline(
...pipelineSteps
);
console.log(`OpenSSL download + extract complete${maybeDownloadSha256 ? ": SHA256 OK." : "."}`);
console.log("Download finished.");
}
const acquireOpenSSL = async () => {
try {
const maybeDownloadBinUrl = process.env.npm_config_openssl_bin_url;
if (maybeDownloadBinUrl) {
const maybeDownloadSha256 = process.env.npm_config_openssl_bin_sha256;
await downloadOpenSSLIfNecessary(maybeDownloadBinUrl, maybeDownloadSha256);
return;
}
let macOsDeploymentTarget;
if (process.platform === "darwin") {
macOsDeploymentTarget = process.argv[2];
if (!macOsDeploymentTarget || !macOsDeploymentTarget.match(/\d+\.\d+/)) {
throw new Error(`Invalid macOsDeploymentTarget: ${macOsDeploymentTarget}`);
}
}
await buildOpenSSLIfNecessary("1.1.1l", macOsDeploymentTarget);
} catch (err) {
console.error("Acquire failed: ", err);
process.exit(1);
}
};
acquireOpenSSL();