diff --git a/.github/workflows/build-openssl-packages.yml b/.github/workflows/build-openssl-packages.yml index 426ca0652..bd584002d 100644 --- a/.github/workflows/build-openssl-packages.yml +++ b/.github/workflows/build-openssl-packages.yml @@ -9,8 +9,15 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-13, windows-2022] - arch: [x64, arm64] + include: + - os: windows-latest + arch: x64 + # - os: windows-latest + # arch: arm64 + - os: macos-15 + arch: x64 + - os: macos-15-intel + arch: arm64 fail-fast: false steps: @@ -21,17 +28,30 @@ jobs: uses: actions/setup-node@v4 with: node-version: 22 - architecture: ${{ matrix.arch }} + + - name: Install Toolchain + if: matrix.os == 'windows-latest' && matrix.arch == 'arm64' + uses: msys2/setup-msys2@v2 + with: + update: true + install: > + mingw-w64-aarch64-toolchain + mingw-w64-aarch64-cmake + mingw-w64-aarch64-ninja - name: Install dependencies run: npm install - name: Build OpenSSL packages - run: node utils/acquireOpenSSL.js + env: + TARGET_ARCH: ${{ matrix.arch }} + NODEGIT_OPENSSL_BUILD_PACKAGE: 1 + OPENSSL_MACOS_DEPLOYMENT_TARGET: "11.0" + run: node utils/acquireOpenSSL.mjs - name: Push OpenSSL package to S3 env: node_pre_gyp_bucket: ${{ secrets.node_pre_gyp_bucket }} AWS_ACCESS_KEY_ID: ${{ secrets.node_pre_gyp_accessKeyId }} AWS_SECRET_ACCESS_KEY: ${{ secrets.node_pre_gyp_secretAccessKey }} - run: node utils/uploadOpenSSL.js \ No newline at end of file + run: node utils/uploadOpenSSL.mjs diff --git a/generate/templates/templates/binding.gyp b/generate/templates/templates/binding.gyp index 61cad0cb5..b5e189c32 100644 --- a/generate/templates/templates/binding.gyp +++ b/generate/templates/templates/binding.gyp @@ -160,9 +160,8 @@ "<(electron_openssl_root)/include" ], "libraries": [ - # this order is significant on centos7 apparently... - "<(electron_openssl_root)/lib/libssl.a", - "<(electron_openssl_root)/lib/libcrypto.a" + "<(electron_openssl_root)/lib64/libssl.a", + "<(electron_openssl_root)/lib64/libcrypto.a" ] }], ["<(is_electron) == 1 and <(electron_openssl_static) != 1", { diff --git a/utils/acquireOpenSSL.mjs b/utils/acquireOpenSSL.mjs index 1d83f540b..047b95cb7 100644 --- a/utils/acquireOpenSSL.mjs +++ b/utils/acquireOpenSSL.mjs @@ -7,14 +7,13 @@ import tar from "tar-fs"; import zlib from "zlib"; import { createWriteStream, promises as fs } from "fs"; import { performance } from "perf_hooks"; -import { fileURLToPath } from 'url'; import { promisify } from "util"; const pipeline = promisify(stream.pipeline); import packageJson from '../package.json' with { type: "json" }; -const OPENSSL_VERSION = "1.1.1t"; +const OPENSSL_VERSION = "3.0.18"; const win32BatPath = path.join(import.meta.dirname, "build-openssl.bat"); const vendorPath = path.resolve(import.meta.dirname, "..", "vendor"); const opensslPatchPath = path.join(vendorPath, "patches", "openssl"); @@ -56,6 +55,8 @@ const makeHashVerifyOnFinal = (expected) => (digest) => { // currently this only needs to be done on linux const applyOpenSSLPatches = async (buildCwd, operatingSystem) => { try { + await fs.access(opensslPatchPath); + for (const patchFilename of await fs.readdir(opensslPatchPath)) { const patchTarget = patchFilename.split("-")[1]; if (patchFilename.split(".").pop() === "patch" && (patchTarget === operatingSystem || patchTarget === "all")) { @@ -66,6 +67,11 @@ const applyOpenSSLPatches = async (buildCwd, operatingSystem) => { } } } catch(e) { + if (e.code === "ENOENT") { + // no patches to apply + return; + } + console.log("Patch application failed: ", e); throw e; } @@ -86,6 +92,8 @@ const buildDarwin = async (buildCwd, macOsDeploymentTarget) => { "no-ssl2", "no-ssl3", "no-comp", + // disable tty ui since it fails a bunch of tests on GHA runners and we're just gonna link anyways + "no-ui-console", // set install directory `--prefix="${extractPath}"`, `--openssldir="${extractPath}"`, @@ -121,7 +129,7 @@ const buildLinux = async (buildCwd) => { // dependency on the system libssl/libcrypto which causes symbol conflicts and segfaults. // To fix this we need to hide all the openssl symbols to prevent them from being overridden // by the runtime linker. - "-fvisibility=hidden", + // "-fvisibility=hidden", // compile static libraries "no-shared", // disable ssl2, ssl3, and compression @@ -159,18 +167,60 @@ const buildWin32 = async (buildCwd, vsBuildArch) => { throw new Error("Expected vsBuildArch to be specified"); } - const programFilesPath = (process.arch === "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 exists = (filePath) => fs.stat(filePath).then(() => true).catch(() => false); + + let vcvarsallPath = undefined; + + if (process.env.npm_config_vcvarsall_path && await exists(process.env.npm_config_vcvarsall_path)) { + vcvarsallPath = process.env.npm_config_vcvarsall_path; + } else { + const potentialMsvsPaths = []; + + // GYP_MSVS_OVERRIDE_PATH is set by node-gyp so this should cover most cases + if (process.env.GYP_MSVS_OVERRIDE_PATH) { + potentialMsvsPaths.push(process.env.GYP_MSVS_OVERRIDE_PATH); + } + + const packageTypes = ["BuildTools", "Community", "Professional", "Enterprise"]; + const versions = ["2022", "2019"] + + const computePossiblePaths = (parentPath) => { + let possiblePaths = [] + for (const packageType of packageTypes) { + for (const version of versions) { + possiblePaths.push(path.join(parentPath, version, packageType)); + } + } + + return possiblePaths; + } + + if (process.env["ProgramFiles(x86)"]) { + const parentPath = path.join(process.env["ProgramFiles(x86)"], 'Microsoft Visual Studio'); + potentialMsvsPaths.push(...computePossiblePaths(parentPath)); + } + + if (process.env.ProgramFiles) { + const parentPath = path.join(process.env.ProgramFiles, 'Microsoft Visual Studio'); + potentialMsvsPaths.push(...computePossiblePaths(parentPath)); + } + + for (const potentialPath of potentialMsvsPaths) { + const wholePath = path.join(potentialPath, 'VC', 'Auxiliary', 'Build', 'vcvarsall.bat'); + console.log("checking", wholePath); + if (await exists(wholePath)) { + vcvarsallPath = wholePath; + break; + } + } + + if (!vcvarsallPath) { + throw new Error(`vcvarsall.bat not found`); + } } + console.log('using', vcvarsallPath); + let vcTarget; switch (vsBuildArch) { case "x64": { @@ -259,7 +309,7 @@ const buildOpenSSLIfNecessary = async ({ const openSSLUrl = getOpenSSLSourceUrl(openSSLVersion); const openSSLSha256Url = getOpenSSLSourceSha256Url(openSSLVersion); - const openSSLSha256 = (await got(openSSLSha256Url)).body.trim(); + const openSSLSha256 = (await got(openSSLSha256Url)).body.trim().split(' ')[0]; const downloadStream = got.stream(openSSLUrl); downloadStream.on("downloadProgress", makeOnStreamDownloadProgress()); @@ -332,7 +382,7 @@ const downloadOpenSSLIfNecessary = async ({ console.log("Download finished."); } -const getOpenSSLPackageName = () => { +export const getOpenSSLPackageName = () => { let arch = process.arch; if (process.platform === "win32" && ( process.arch === "ia32" || process.env.NODEGIT_VS_BUILD_ARCH === "x86" @@ -343,6 +393,8 @@ const getOpenSSLPackageName = () => { return `openssl-${OPENSSL_VERSION}-${process.platform}-${arch}.tar.gz`; } +export const getOpenSSLPackagePath = () => path.join(import.meta.dirname, getOpenSSLPackageName()); + const getOpenSSLPackageUrl = () => `${packageJson.binary.host}${getOpenSSLPackageName()}`; const buildPackage = async () => { @@ -366,7 +418,7 @@ const buildPackage = async () => { new HashVerify("sha256", (digest) => { resolve(digest); }), - createWriteStream(getOpenSSLPackageName()) + createWriteStream(getOpenSSLPackagePath()) ); const digest = await promise; await fs.writeFile(`${getOpenSSLPackageName()}.sha256`, digest); @@ -392,7 +444,7 @@ const acquireOpenSSL = async () => { let macOsDeploymentTarget; if (process.platform === "darwin") { - macOsDeploymentTarget = process.argv[2]; + macOsDeploymentTarget = process.argv[2] ?? process.env.OPENSSL_MACOS_DEPLOYMENT_TARGET if (!macOsDeploymentTarget || !macOsDeploymentTarget.match(/\d+\.\d+/)) { throw new Error(`Invalid macOsDeploymentTarget: ${macOsDeploymentTarget}`); } @@ -427,5 +479,5 @@ if (process.argv[1] === import.meta.filename) { catch(error) { console.error("Acquire OpenSSL failed: ", error); process.exit(1); - }; + } } diff --git a/utils/uploadOpenSSL.mjs b/utils/uploadOpenSSL.mjs index 784379cb6..86495e98e 100644 --- a/utils/uploadOpenSSL.mjs +++ b/utils/uploadOpenSSL.mjs @@ -2,26 +2,27 @@ import aws from 'aws-sdk'; import fs from "fs"; import path from "path"; -import pkgJson from './package.json' assert { type: "json" }; -import { getOpenSSLPackageName } from './acquireOpenSSL'; +import pkgJson from '../package.json' with { type: "json" }; +import { getOpenSSLPackagePath, getOpenSSLPackageName } from './acquireOpenSSL.mjs'; const s3 = new aws.S3(); -const uploadBinaryToS3 = (binaryName, bucketName, pathToFile) => +const uploadBinaryToS3 = (fileName, bucketName, pathToFile) => s3.upload({ Body: fs.createReadStream(pathToFile), Bucket: bucketName, - Key: binaryName, + Key: fileName, ACL: "public-read" }).promise(); export const uploadOpenSSL = async () => { - const binaryName = getOpenSSLPackageName(); - const pathToFile = path.join(import.meta.dirname, binaryName); - return uploadBinaryToS3(binaryName, pkgJson.binary.bucket_name, pathToFile); + const packageName = path.basename(getOpenSSLPackageName()); + const packagePath = getOpenSSLPackagePath(); + console.log(`Uploading ${packagePath} to s3://${pkgJson.binary.bucket_name}/${packageName}`); + return uploadBinaryToS3(packageName, pkgJson.binary.bucket_name, packagePath); }; -if (require.main === module) { +if (process.argv[1] === import.meta.filename) { uploadOpenSSL().catch((error) => { console.error('Push to S3 failed: ', error); process.exit(1); diff --git a/vendor/patches/openssl/001-linux-force_getentropy_dso_lookup.patch b/vendor/patches/openssl/001-linux-force_getentropy_dso_lookup.patch deleted file mode 100644 index 6802c7fa5..000000000 --- a/vendor/patches/openssl/001-linux-force_getentropy_dso_lookup.patch +++ /dev/null @@ -1,23 +0,0 @@ -openssl doesn't have any sort of guard around this section of code other than -checking if we're compiling an elf binary on gnu linux. the syscall wrapper -`getentropy` is only available on glibc >= 2.25 which is a problem if we want -to support platforms like centos7 which ships with glibc 2.17. Attempting to -load this code on centos7 causes a runtime "undefined symbol error since glibc -doesn't provide it. -luckily openssl provides a backup lookup method in form of a dlopen call but -theres no way to configure for it, hence this patch. -Note further that centos7 doesn't have this function or the syscall it wraps -so the symbol lookup will fail and it will fallback to reading from /dev/random. -hence this patch just fixes compilation. -author: JZA ---- crypto/rand/rand_unix.c -+++ crypto/rand/rand_unix.c -@@ -372,7 +372,7 @@ static ssize_t syscall_random(void *buf, size_t buflen) - * Note: Sometimes getentropy() can be provided but not implemented - * internally. So we need to check errno for ENOSYS - */ --# if defined(__GNUC__) && __GNUC__>=2 && defined(__ELF__) && !defined(__hpux) -+# if defined(__GNUC__) && __GNUC__>=2 && defined(__ELF__) && !defined(__hpux) && 0 - extern int getentropy(void *buffer, size_t length) __attribute__((weak)); - - if (getentropy != NULL) {