elna/tools/index.js

403 lines
12 KiB
JavaScript
Raw Normal View History

import fs from 'fs/promises'
import path from 'node:path'
import childProcess from 'node:child_process'
import process from 'process'
import os from 'os'
import { Buffer } from 'node:buffer'
// Define constants.
const tmp = path.resolve('./build/tools')
const target = 'riscv32-unknown-linux-gnu'
const baseDirectory = path.resolve('./tools')
const busyboxVersion = '1.36.1'
const kernelVersion = '5.15.158'
const gccVersion = '14.1.0'
const binutilsVersion = '2.42'
const glibcVersion = '2.39'
function findBuildTarget () {
const env = {
LANG: 'en_US.UTF-8'
}
const gccV = childProcess.spawn('gcc', ['--verbose'], { stdio: ['ignore', 'ignore', 'pipe'], env })
const buffers = []
gccV.stderr.on('data', function (data) {
buffers.push(data)
})
return new Promise(function (resolve, reject) {
gccV.on('exit', function () {
const [_, target] = Buffer.concat(buffers)
.toString()
.split('\n')
.find(line => line.startsWith('Target: '))
.split(' ')
resolve(target)
})
})
}
function createImage (rootfs) {
const rootExt4 = path.join(tmp, 'rootfs.ext4')
childProcess.execFileSync('dd', ['if=/dev/zero', `of=${rootExt4}`, 'bs=1M', 'count=1024'], { stdio: 'inherit' })
childProcess.execFileSync('/sbin/mkfs.ext4', ['-d', rootfs, rootExt4], { stdio: 'inherit' })
}
async function downloadAndUnarchive (url) {
const response = await fetch(url)
const bodyReader = response.body.getReader()
const basename = path.basename(url.pathname)
let archiveType = ''
let rootDirectory = ''
switch (path.extname(basename)) {
case '.bz2':
archiveType = '-j'
rootDirectory = path.basename(basename, '.tar.bz2')
break
case '.xz':
archiveType = '-J'
rootDirectory = path.basename(basename, '.tar.xz')
break
default:
break
}
return new Promise(async function (resolve, reject) {
const untar = childProcess.spawn('tar', ['-C', tmp, archiveType, '-xv'], { stdio: ['pipe', 'inherit', 'inherit'] })
let done = false
untar.on('exit', function () {
resolve(path.join(tmp, rootDirectory))
})
do {
const chunk = await bodyReader.read()
done = chunk.done
if (chunk.value !== undefined) {
untar.stdin.write(chunk.value)
}
} while (!done)
untar.stdin.end()
})
}
async function buildBusyBox (sysroot, rootfs) {
const cwd = await downloadAndUnarchive(new URL(`https://busybox.net/downloads/busybox-${busyboxVersion}.tar.bz2`))
const env = {
...process.env,
CROSS_COMPILE: path.join(sysroot, 'bin', `${target}-`)
}
const configuration = [
`CONFIG_PREFIX=${rootfs}`,
`CONFIG_SYSROOT=${rootfs}`
]
childProcess.execFileSync('make', ['defconfig'], { stdio: 'inherit', env, cwd })
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd })
childProcess.execFileSync('make', [...configuration, 'install'], { stdio: 'inherit', env, cwd })
}
async function buildKernel (sysroot, rootfs) {
const cwd = await downloadAndUnarchive(
new URL(`https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${kernelVersion}.tar.xz`)
)
const env = {
...process.env,
CROSS_COMPILE: `${target}-`,
ARCH: 'riscv',
PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}`
}
childProcess.execFileSync('make', ['rv32_defconfig'], { stdio: 'inherit', env, cwd })
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd })
childProcess.execFileSync('make', ['headers'], { stdio: 'inherit', env, cwd })
const userDirectory = path.join(rootfs, 'usr')
await fs.cp(path.join(cwd, 'usr/include'), path.join(userDirectory, 'include'), { recursive: true })
return path.join(cwd, 'arch/riscv/boot/Image')
}
async function buildInit (sysroot) {
const env = {
...process.env,
PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}`
}
const compilerArguments = [
'-ffreestanding', '-static',
'-o', path.join(tmp, 'init'),
path.join(baseDirectory, 'init.cpp')
]
childProcess.execFileSync('riscv32-unknown-linux-gnu-g++', compilerArguments, { stdio: 'inherit', env })
}
async function buildGlibc (sysroot, rootfs, options) {
const sourceDirectory = await downloadAndUnarchive(
new URL(`https://ftp.gnu.org/gnu/glibc/glibc-${glibcVersion}.tar.xz`)
)
const configureOptions = [
'--prefix=/usr',
'--libdir=/usr/lib',
`--host=${target}`,
`--build=${options.build}`,
`--enable-kernel=${kernelVersion}`,
`--with-headers=${path.join(rootfs, 'usr/include')}`,
'--disable-nscd'
]
const env = {
...process.env,
PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}`
}
const cwd = path.join(path.dirname(sourceDirectory), 'build-glibc');
await fs.mkdir(cwd)
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), './configure'), configureOptions, {
stdio: 'inherit',
env,
cwd
})
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd })
childProcess.execFileSync('make', [`DESTDIR=${rootfs}`, 'install'], { stdio: 'inherit', env, cwd })
}
async function buildCrossBinutils (sysroot) {
const sourceDirectory = await downloadAndUnarchive(
new URL(`https://ftp.gnu.org/gnu/binutils/binutils-${binutilsVersion}.tar.xz`)
)
const cwd = path.join(path.dirname(sourceDirectory), 'build-binutils');
await fs.mkdir(cwd)
const configureOptions = [
`--prefix=${sysroot}`,
`--target=${target}`,
'--disable-nls',
'--enable-gprofng=no',
'--disable-werror',
'--enable-default-hash-style=gnu'
]
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, {
stdio: 'inherit',
cwd
})
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', cwd })
childProcess.execFileSync('make', ['install'], { stdio: 'inherit', cwd })
return sourceDirectory
}
async function buildGCC1 (sysroot, rootfs, options) {
const sourceDirectory = await downloadAndUnarchive(
new URL(`https://gcc.gnu.org/pub/gcc/releases/gcc-${gccVersion}/gcc-${gccVersion}.tar.xz`)
)
const cwd = path.join(path.dirname(sourceDirectory), 'build-gcc');
await fs.mkdir(cwd)
childProcess.execFileSync(path.join(sourceDirectory, 'contrib/download_prerequisites'), {
stdio: 'inherit',
cwd: sourceDirectory
})
const configureOptions = [
`--prefix=${sysroot}`,
`--with-sysroot=${rootfs}`,
'--enable-languages=c,c++',
'--disable-shared',
'--with-arch=rv32imafdc',
'--with-abi=ilp32d',
'--with-tune=rocket',
'--with-isa-spec=20191213',
'--disable-bootstrap',
'--disable-multilib',
'--disable-libmudflap',
'--disable-libssp',
'--disable-libquadmath',
'--disable-libsanitizer',
'--disable-threads',
'--disable-libatomic',
'--disable-libgomp',
'--disable-libvtv',
'--disable-libstdcxx',
'--disable-nls',
'--with-newlib',
'--without-headers',
`--target=${target}`,
`--build=${options.build}`,
`--host=${options.build}`
]
const flags = '-O2 -fPIC'
const env = {
...process.env,
CFLAGS: flags,
CXXFLAGS: flags,
PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}`
}
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, {
stdio: 'inherit',
env,
cwd
})
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd })
childProcess.execFileSync('make', ['install'], { stdio: 'inherit', env, cwd })
return sourceDirectory
}
async function buildBinutils (sourceDirectory, sysroot, rootfs, options) {
const cwd = path.join(path.dirname(sourceDirectory), 'build-binutils');
await fs.rm(cwd, { recursive: true, force: true })
await fs.mkdir(cwd)
const configureOptions = [
'--prefix=/usr',
`--build=${options.build}`,
`--host=${target}`,
`--with-build-sysroot=${rootfs}`,
'--disable-nls',
'--enable-shared',
'--enable-gprofng=no',
'--disable-werror',
'--enable-default-hash-style=gnu'
]
const env = {
...process.env,
PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}`
}
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, {
stdio: 'inherit',
env,
cwd
})
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd })
childProcess.execFileSync('make', [`DESTDIR=${rootfs}`, 'install'], { stdio: 'inherit', env, cwd })
return sourceDirectory
}
async function buildGCC2 (sourceDirectory, sysroot, rootfs, options) {
const cwd = path.join(path.dirname(sourceDirectory), 'build-gcc');
await fs.rm(cwd, { recursive: true, force: true })
await fs.mkdir(cwd)
const configureOptions = [
`--prefix=${sysroot}`,
`--with-sysroot=${rootfs}`,
'--enable-languages=c,c++,lto',
'--enable-lto',
'--enable-shared',
'--with-arch=rv32imafdc',
'--with-abi=ilp32d',
'--with-tune=rocket',
'--with-isa-spec=20191213',
'--disable-bootstrap',
'--disable-multilib',
'--enable-checking=release',
'--disable-libssp',
'--enable-threads=posix',
'--with-default-libstdcxx-abi=new',
'--disable-nls',
`--target=${target}`,
`--build=${options.build}`,
`--host=${options.build}`
]
const flags = '-O2 -fPIC'
const env = {
...process.env,
CFLAGS: flags,
CXXFLAGS: flags,
PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}`
}
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, {
stdio: 'inherit',
env,
cwd
})
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd })
childProcess.execFileSync('make', ['install'], { stdio: 'inherit', env, cwd })
}
async function buildGCC3 (sourceDirectory, sysroot, rootfs, options) {
const cwd = path.join(path.dirname(sourceDirectory), 'build-gcc');
await fs.rm(cwd, { recursive: true, force: true })
await fs.mkdir(cwd)
const configureOptions = [
`--prefix=/usr`,
`--libdir=/usr/lib`,
'--enable-languages=c,c++,lto',
'--enable-lto',
'--enable-shared',
'--with-arch=rv32imafdc',
'--with-abi=ilp32d',
'--with-tune=rocket',
'--with-isa-spec=20191213',
'--disable-bootstrap',
'--disable-multilib',
'--enable-checking=release',
'--disable-libssp',
'--enable-threads=posix',
'--with-default-libstdcxx-abi=new',
'--disable-nls',
`--target=${target}`,
`--build=${options.build}`,
`--host=${target}`
]
const flags = '-O2 -fPIC'
const env = {
...process.env,
CFLAGS: flags,
CXXFLAGS: flags,
PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}`
}
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, {
stdio: 'inherit',
env,
cwd
})
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd })
childProcess.execFileSync('make', [`DESTDIR=${rootfs}`, 'install'], { stdio: 'inherit', env, cwd })
}
async function createRoot (rootfs) {
for (const directory of ['proc', 'sys', 'dev', 'etc/init.d', 'usr/local', 'usr/local/bin']) {
await fs.mkdir(path.join(rootfs, directory))
}
await fs.cp(path.join(baseDirectory, 'files/fstab'), path.join(rootfs, 'etc/fstab'))
await fs.cp(path.join(baseDirectory, 'files/rcS'), path.join(rootfs, 'etc/init.d/rcS'))
await fs.cp(path.join(baseDirectory, 'files/rcK'), path.join(rootfs, 'etc/init.d/rcK'))
await fs.cp(path.join(baseDirectory, 'files/inittab'), path.join(rootfs, 'etc/inittab'))
}
const sysroot = path.join(tmp, 'sysroot')
const rootfs = path.join(tmp, 'rootfs')
const options = {
build: await findBuildTarget()
}
for (const targetDirectory of [tmp, sysroot, rootfs]) {
await fs.rm(targetDirectory, { recursive: true, force: true })
await fs.mkdir(targetDirectory)
}
const binutilsSource = await buildCrossBinutils(sysroot)
const gccSource = await buildGCC1(sysroot, rootfs, options)
const kernelImage = await buildKernel(sysroot, rootfs)
await buildGlibc(sysroot, rootfs, options)
await buildBinutils(binutilsSource, sysroot, rootfs, options)
await buildGCC2(gccSource, sysroot, rootfs, options)
buildInit (sysroot)
await buildBusyBox(sysroot, rootfs)
await createRoot(rootfs)
await buildGCC3(gccSource, sysroot, rootfs, options)
createImage(rootfs)
console.log(kernelImage)