tutanota/buildSrc/nativeLibraryProvider.js

95 lines
No EOL
2.9 KiB
JavaScript

import path from "path"
import fs from "fs"
import stream from "stream"
import {execFileSync, spawn} from "child_process"
/**
* Rebuild native lib for either current node version or for electron version and
* cache in the "native-cache" directory.
* ABI for nodejs and for electron differs and we need to build it differently for each. We do caching
* to avoid rebuilding between different invocations (e.g. running desktop and running tests).
* @param environment {"electron"|"node"}
* @param rootDir path to the root of the project
* @param nodeModule name of the npm module to rebuild
* @param builtPath relative path in the npm module to the output of node-gyp
* @param log
* @returns string path to .node for built better-sqlite3
*/
export async function getNativeLibModulePath({environment, rootDir, nodeModule, builtPath, log}) {
const dir = path.join(rootDir, "native-cache", environment)
await fs.promises.mkdir(dir, {recursive: true})
const electronVersion = getVersion("electron")
const libraryVersion = getVersion(nodeModule)
let filePath
if (environment === "electron") {
filePath = `${nodeModule}-${libraryVersion}-electron-${electronVersion}.node`
} else {
filePath = `${nodeModule}-${libraryVersion}.node`
}
const libPath = path.resolve(path.join(dir, filePath))
try {
// Check if the file is there
await fs.promises.access(libPath)
log(`Using cached ${nodeModule} at`, libPath)
} catch {
log(`Compiling ${nodeModule}...`)
await rebuild({environment, rootDir, electronVersion, log, nodeModule})
await fs.promises.copyFile(path.join(rootDir, `node_modules/${nodeModule}/${builtPath}`), libPath)
}
return libPath
}
async function rebuild({environment, rootDir, electronVersion, log, nodeModule}) {
const libDir = path.join(rootDir, `./node_modules/${nodeModule}`)
const logStream = new stream.Writable({
autoDestroy: true,
write(chunk, encoding, callback) {
log(chunk.toString())
callback()
},
})
const gypForSqlite = spawn(
"npm exec",
[
"--",
"node-gyp",
"rebuild",
"--release",
"--build-from-source",
`--arch=${process.arch}`,
...(
environment === "electron"
? [
"--runtime=electron",
'--dist-url=https://www.electronjs.org/headers',
`--target=${electronVersion}`,
]
: []
)
],
{
stdio: [null, "pipe", "pipe"],
shell: true,
cwd: libDir,
}
)
gypForSqlite.stdout.pipe(logStream)
gypForSqlite.stderr.pipe(logStream)
return new Promise((resolve, reject) => {
gypForSqlite.on('exit', (code) => {
if (code === 0) {
log(`Compiled ${nodeModule} successfully` + "\n")
resolve()
} else {
log(`Compiling ${nodeModule} failed` + "\n")
reject(new Error(`Compiling ${nodeModule} failed: ${code}`))
}
})
})
}
function getVersion(nodeModule) {
return JSON.parse(execFileSync("npm", ["list", nodeModule, "--json"]).toString().trim()).dependencies[nodeModule].version
}