@fails-components/webtransport-transport-http3-quiche
Version:
A component to add webtransport support (server and client) to node.js, transport using libquiche
316 lines (299 loc) • 8.46 kB
JavaScript
import { arch, argv, env, platform } from 'node:process'
import { spawn } from 'node:child_process'
import { cp, rename, mkdtemp, rm, access } from 'node:fs/promises'
import { constants } from 'node:fs'
import path from 'node:path'
import pkg from './package.json' with { type: 'json' }
import { tmpdir } from 'node:os'
const binplatform = platform + '_' + arch
console.log(
'Webtransport binary handler: We are working on platform ',
binplatform
)
console.log(
'Webtransport executed in dir:', process.cwd()
)
const callGit = (args, opts) => {
// console.log('callgit', args)
return new Promise((resolve, reject) => {
const git = spawn('git', args, {
cwd: (opts && opts.cwd) || process.cwd(),
shell: true
})
const output = []
git.stdout.on('data', (data) => {
output.push(data)
})
git.stderr.on('data', (data) => {
console.error(data.toString())
})
git.on('close', (code) => {
if (code === 0) resolve(output.join(''))
else {
reject(output.join(''))
}
})
})
}
const extractthirdparty = async () => {
let tmppath
let fatal = false
console.error('Now extracting third party lib code from git....')
const destdir = process.cwd() + '/third_party'
let exists = false
try {
await access(destdir, constants.F_OK)
exists = true
} catch (error) {
// ok this is ok
}
if (exists) {
console.error(
'Destination dir already exists: ' + destdir + ', skip downloading.'
)
return
}
try {
console.log('tmpdir', tmpdir())
tmppath = await mkdtemp(path.join(tmpdir(), 'wtbuild-'))
} catch (error) {
console.error('mktmp dir', error)
throw new Error('Cannot generate tmp dir')
}
try {
await callGit(
[
'clone',
'--branch',
`v${pkg.version}`,
'--depth',
'1',
pkg.repository.url.replace('git+https', 'https')
],
{ cwd: tmppath }
)
const copath = tmppath + '/webtransport'
const submodules = [
'transports/http3-quiche/third_party/boringssl/src',
'transports/http3-quiche/third_party/abseil-cpp',
'transports/http3-quiche/third_party/quiche',
'transports/http3-quiche/third_party/zlib',
'transports/http3-quiche/third_party/googleurl',
'transports/http3-quiche/third_party/protobuf'
]
for (const mod in submodules) {
const sub = submodules[mod]
await callGit(
[
'config',
'-f',
'.gitmodules',
'submodule.' + sub + '.shallow',
'true'
],
{ cwd: copath }
)
await callGit(['submodule', 'update', '--init', '--recursive', sub], {
cwd: copath
})
}
try {
await rm(destdir, { recursive: true, maxRetries: 10 })
} catch (err) {
console.log('destdir does not exist (only warning, ignore): ', err)
}
try {
await rename(path.join(copath, '/transports/http3-quiche/third_party'), destdir)
} catch (error) {
console.log('rename tmp dir failed:', error)
console.log('copy instead start... (can take a while)...')
await cp(path.join(copath, '/transports/http3-quiche/third_party'), destdir, { recursive: true })
console.log('copy instead finished')
}
} catch (error) {
console.error('failed to get third party code from git', error)
fatal = true
}
try {
await rm(tmppath, { recursive: true, maxRetries: 10 })
} catch (error) {
console.error('failed to remove temp dir ', error)
}
if (fatal) throw new Error('Cannot get thirdparty code')
console.error('Extracting third party lib code from git finished.')
}
const prebuild = (args) => {
return new Promise((resolve, reject) => {
const pb = 'npx'
// if (platform === 'win32') cmakejs = 'cmake-js.exe'
const proc = spawn(pb, ['prebuild', ...args], {
cwd: process.cwd(),
stdio: [null, 'inherit', 'inherit'],
shell: true
})
proc.on('close', (code) => {
console.log(`child process exited with code ${code}`)
if (code === 0) resolve()
else reject(new Error(`child process exited with code ${code}`))
})
})
}
const prebuildInstall = async (args) => {
return new Promise((resolve, reject) => {
const pb = 'prebuild-install'
// if (platform === 'win32') cmakejs = 'cmake-js.exe'
const proc = spawn(pb, args, {
cwd: process.cwd(),
stdio: [null, 'inherit', 'inherit'],
shell: true
})
proc.on('close', (code) => {
if (code === 0) resolve(code)
else reject(code)
console.log(`child process exited with code ${code}`)
})
})
}
const buildTypes = async () => {
return new Promise((resolve, reject) => {
const tsc = 'tsc'
const proc = spawn(tsc, ['--build'], {
cwd: process.cwd(),
stdio: [null, 'inherit', 'inherit'],
shell: true
})
proc.on('close', (code) => {
if (code === 0) resolve(code)
else reject(code)
console.log(`child process exited with code ${code}`)
})
})
}
const execbuild = async (args) => {
return new Promise((resolve, reject) => {
const cmakejs = 'cmake-js'
// if (platform === 'win32') cmakejs = 'cmake-js.exe'
const proc = spawn(cmakejs, args, {
cwd: process.cwd(),
stdio: [null, 'inherit', 'inherit'],
shell: true
})
proc.on('close', (code) => {
if (code === 0) resolve(code)
else reject(code)
console.log(`child process exited with code ${code}`)
})
})
}
if (argv.length > 2) {
const target = pkg.binary.napi_versions
const platformargs = [
'--CDnapi_build_version=' + target,
'-O',
'build_' + binplatform
]
if (platform === 'win32') platformargs.push('-t', 'ClangCL')
const pbargs = []
const pbargspre = []
if (platform === 'win32') pbargs.push('-t', 'ClangCL')
if (env.BUILDARCH) {
pbargspre.push('--arch', env.BUILDARCH)
platformargs.push('--arch', env.BUILDARCH)
}
if (env.GH_TOKEN) pbargspre.push('--u', env.GH_TOKEN)
switch (argv[2]) {
case 'prebuild':
try {
await prebuild([
'-t',
'6',
'-r',
'napi',
'--strip',
...pbargspre,
'--backend',
'cmake-js',
'--',
...pbargs
])
} catch (error) {
console.log('prebuild failed')
process.exit(1)
}
break
case 'install':
try {
const pbres = await prebuildInstall([
'-r',
'napi',
'-d',
'-t',
'6',
'--verbose'
])
if (pbres === 0) break
} catch (error) {
console.error(
'No prebuild available, building binary, this may take more than 20 minutes'
)
}
try {
// if we do not succeed, we have to build it ourselves
await extractthirdparty()
await execbuild(['build', ...platformargs])
} catch (error) {
console.error('Building binary failed: ', error)
process.exit(1)
}
break
case 'build':
try {
await execbuild(['build', ...platformargs])
await buildTypes()
} catch (error) {
console.error('Building binary failed: ', error)
process.exit(1)
}
break
case 'rebuild':
try {
await execbuild(['rebuild', ...platformargs])
await buildTypes()
} catch (error) {
console.error('ReBuilding binary failed: ', error)
process.exit(1)
}
break
case 'build-debug':
try {
await execbuild(['build', '-D', ...platformargs])
await buildTypes()
} catch (error) {
console.error('Building binary failed: ', error)
process.exit(1)
}
break
case 'rebuild-debug':
try {
await execbuild(['rebuild', '-D', ...platformargs])
await buildTypes()
} catch (error) {
console.error('ReBuilding binary failed: ', error)
process.exit(1)
}
break
case 'extract-thirdparty':
try {
// if we do not succeed, we have to build it ourselves
await extractthirdparty()
} catch (error) {
console.error('Extract third party failed: ', error)
}
break
default:
console.log('unsupported argument ', argv[2])
break
}
} else {
console.log('Please supply a task as argument')
}