Add scripts to build a toolchain for running VM tests
This commit is contained in:
373
tools/index.js
Normal file
373
tools/index.js
Normal file
@ -0,0 +1,373 @@
|
||||
import fs from 'fs/promises'
|
||||
import path from 'node:path'
|
||||
import childProcess from 'node:child_process'
|
||||
import process from 'process'
|
||||
import os from 'os'
|
||||
|
||||
// Define constants.
|
||||
const tmp = path.resolve('./build/tools')
|
||||
const target = 'riscv32-unknown-linux-gnu'
|
||||
const build = 'x86_64-slackware-linux'
|
||||
const baseDirectory = path.resolve('./tools')
|
||||
|
||||
const busyboxVersion = '1.36.1'
|
||||
const kernelVersion = '5.15.158'
|
||||
const gccVersion = '13.2.0'
|
||||
const binutilsVersion = '2.42'
|
||||
const glibcVersion = '2.39'
|
||||
|
||||
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) {
|
||||
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=${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) {
|
||||
const sourceDirectory = await downloadAndUnarchive(
|
||||
new URL(`https://download.dlackware.com/slackware/slackware64-current/source/d/gcc/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=${build}`,
|
||||
`--host=${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) {
|
||||
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=${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) {
|
||||
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=${build}`,
|
||||
`--host=${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) {
|
||||
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=${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')
|
||||
|
||||
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)
|
||||
|
||||
const kernelImage = await buildKernel(sysroot, rootfs)
|
||||
await buildGlibc(sysroot, rootfs)
|
||||
await buildBinutils(binutilsSource, sysroot, rootfs)
|
||||
await buildGCC2(gccSource, sysroot, rootfs)
|
||||
buildInit (sysroot)
|
||||
|
||||
await buildBusyBox(sysroot, rootfs)
|
||||
await createRoot(rootfs)
|
||||
|
||||
await buildGCC3(gccSource, sysroot, rootfs)
|
||||
createImage(rootfs)
|
||||
console.log(kernelImage)
|
Reference in New Issue
Block a user