diff --git a/src/clang-fetcher.ts b/src/clang-fetcher.ts index 827966adc..4f9810cad 100644 --- a/src/clang-fetcher.ts +++ b/src/clang-fetcher.ts @@ -7,6 +7,7 @@ import * as zlib from 'zlib'; import { ELECTRON_GYP_DIR } from './constants'; import { fetch } from './fetcher'; import { downloadLinuxSysroot } from './sysroot-fetcher'; +import { downloadLibcxxHeaders, downloadLibcxxObjects } from './libcxx-fetcher'; const d = debug('electron-rebuild'); @@ -50,6 +51,46 @@ export async function getClangEnvironmentVars(electronVersion: string, targetArc if (process.platform === 'linux') { const sysrootPath = await downloadLinuxSysroot(electronVersion, targetArch); clangArgs.push('--sysroot', sysrootPath); + + const cc = `"${path.resolve(clangDir, 'clang')}" ${clangArgs.join(' ')}` + const cxx = `"${path.resolve(clangDir, 'clang++')}" ${clangArgs.join(' ')}` + + // on Electron 13 or higher, build native modules with Electron's libc++ libraries + if (parseInt(electronVersion.split('.')[0]) >= 13) { + const libcxxObjectsDownloadDir = await downloadLibcxxObjects(electronVersion, targetArch) + const libcxxHeadersDownloadDir = await downloadLibcxxHeaders(electronVersion, ['libc++', 'libc++abi']) + + const libcxxObjects = path.resolve(libcxxObjectsDownloadDir, 'libc++') + const libcxxHeaders = path.resolve(libcxxHeadersDownloadDir, 'libc++') + const libcxxabiHeaders = path.resolve(libcxxHeadersDownloadDir, 'libc++abi') + + const cxxflags = [ + '-std=c++14', + '-nostdinc++', + '-D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS', + `-isystem"${libcxxHeaders}"`, + `-isystem"${libcxxabiHeaders}"`, + '-fPIC' + ].join(' '); + + const ldflags = [ + '-stdlib=libc++', + '-fuse-ld=lld', + `-L"${libcxxObjects}"`, + '-lc++abi' + ].join(' '); + + return { + env: { + CC: cc, + CXX: cxx, + CFLAGS: cxxflags, + CXXFLAGS: cxxflags, + LDFLAGS: ldflags, + }, + args: gypArgs, + } + } } return { diff --git a/src/libcxx-fetcher.ts b/src/libcxx-fetcher.ts new file mode 100644 index 000000000..8b16684fa --- /dev/null +++ b/src/libcxx-fetcher.ts @@ -0,0 +1,71 @@ +import * as debug from 'debug'; +import * as fs from 'fs-extra'; +import * as path from 'path'; +import * as tar from 'tar'; +import * as zlib from 'zlib'; +import { ELECTRON_GYP_DIR } from './constants'; +import { fetch } from './fetcher'; + +const d = debug('electron-rebuild'); + +// Determine base where we download these headers +// `-isystem"${path.resolve(BASE, 'buildtools', 'third_party', 'libc++', 'trunk', 'include')}"`, +// `-isystem"${path.resolve(BASE, 'buildtools', 'third_party', 'libc++abi', 'trunk', 'include')}"`, +export async function downloadLibcxxHeaders(electronVersion: string, libs: string[]): Promise { + const headersDirPath = path.resolve(ELECTRON_GYP_DIR, `${electronVersion}-libcxx_headers`); + + for (const lib_name in libs) { + if (await fs.pathExists(path.resolve(headersDirPath, `${lib_name}`))) return headersDirPath; + if (!await fs.pathExists(ELECTRON_GYP_DIR)) await fs.mkdirp(ELECTRON_GYP_DIR); + + // download libcxxabi_headers.zip + const contents = await fetch(`https://raw.githubusercontent.com/electron/electron/v${electronVersion}/${lib_name}_headers.zip`, 'buffer') + d(`deflating ${lib_name}_headers`); + zlib.deflateSync(contents); + const tarPath = path.resolve(ELECTRON_GYP_DIR, `${electronVersion}-${lib_name}_headers.tar`); + if (await fs.pathExists(tarPath)) await fs.remove(tarPath) + await fs.writeFile(tarPath, Buffer.from(contents)); + await fs.mkdirp(headersDirPath); + + d(`tar running on ${lib_name}_headers`); + await tar.x({ + file: tarPath, + cwd: headersDirPath, + }); + + await fs.remove(tarPath); + d(`cleaning up ${lib_name}_headers tar file`); + } + return headersDirPath; + } + +// Determine base where we download these headers +// `-L"${path.resolve(BASE, 'out', `${utils.getOutDir({ shouldLog: true })}`, 'obj', 'buildtools', 'third_party', 'libc++abi')}"`, +// `-L"${path.resolve(BASE, 'out', `${utils.getOutDir({ shouldLog: true })}`, 'obj', 'buildtools', 'third_party', 'libc++')}"`, +export async function downloadLibcxxObjects(electronVersion: string, targetArch: string): Promise { + const platform = process.platform; + const libcxxObjectsDirPath = path.resolve(ELECTRON_GYP_DIR, 'libcxx-objects'); + + if (await fs.pathExists(path.resolve(libcxxObjectsDirPath, 'libcxx-objects'))) return libcxxObjectsDirPath; + if (!await fs.pathExists(ELECTRON_GYP_DIR)) await fs.mkdirp(ELECTRON_GYP_DIR); + + // download objects (e.g. libcxx-objects-v13.0.0-linux-x64.zip) + const contents = await fetch(`https://raw.githubusercontent.com/electron/electron/v${electronVersion}/libcxx-objects-v${electronVersion}-${platform}-${targetArch}.zip`, 'buffer') + d(`deflating libcxx-objects-${platform}-${targetArch}`); + zlib.deflateSync(contents); + const tarPath = path.resolve(ELECTRON_GYP_DIR, `libcxx-objects-v${electronVersion}-${platform}-${targetArch}.tar`); + if (await fs.pathExists(tarPath)) await fs.remove(tarPath) + await fs.writeFile(tarPath, Buffer.from(contents)); + await fs.mkdirp(libcxxObjectsDirPath); + + d(`tar running on libcxx-objects-${platform}-${targetArch}`); + await tar.x({ + file: tarPath, + cwd: libcxxObjectsDirPath, + }); + + await fs.remove(tarPath); + d(`cleaning up libcxx-objects-${platform}-${targetArch} tar file`); + return libcxxObjectsDirPath; + } + \ No newline at end of file