2024-05-14 22:09:05 +02:00
|
|
|
import fs from 'fs/promises'
|
|
|
|
import path from 'node:path'
|
|
|
|
import childProcess from 'node:child_process'
|
|
|
|
import process from 'process'
|
|
|
|
import os from 'os'
|
2024-05-20 01:42:56 +02:00
|
|
|
import { Buffer } from 'node:buffer'
|
2024-05-14 22:09:05 +02:00
|
|
|
|
|
|
|
// Define constants.
|
|
|
|
const tmp = path.resolve('./build/tools')
|
|
|
|
const baseDirectory = path.resolve('./tools')
|
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
const kernelVersion = '5.15.159'
|
2024-05-20 01:42:56 +02:00
|
|
|
const gccVersion = '14.1.0'
|
2024-05-14 22:09:05 +02:00
|
|
|
const binutilsVersion = '2.42'
|
|
|
|
const glibcVersion = '2.39'
|
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
async function gccVerbose (gccBinary) {
|
2024-05-20 01:42:56 +02:00
|
|
|
const env = {
|
2024-05-23 01:13:16 +02:00
|
|
|
...process.env,
|
|
|
|
LANG: 'C'
|
2024-05-20 01:42:56 +02:00
|
|
|
}
|
2024-05-23 01:13:16 +02:00
|
|
|
const gccV = childProcess.spawn(gccBinary, ['--verbose'], { stdio: ['ignore', 'ignore', 'pipe'], env })
|
2024-05-20 01:42:56 +02:00
|
|
|
const buffers = []
|
|
|
|
|
|
|
|
gccV.stderr.on('data', function (data) {
|
|
|
|
buffers.push(data)
|
|
|
|
})
|
|
|
|
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
|
|
gccV.on('exit', function () {
|
2024-05-23 01:13:16 +02:00
|
|
|
resolve(Buffer.concat(buffers).toString())
|
2024-05-20 01:42:56 +02:00
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
async function findBuildTarget (gccVersion) {
|
|
|
|
let gccBinary = 'gcc'
|
|
|
|
let output = await gccVerbose(gccBinary)
|
2024-05-14 22:09:05 +02:00
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
if (output.startsWith('Apple clang')) {
|
|
|
|
gccBinary = `gcc-${gccVersion.split('.')[0]}`
|
|
|
|
output = await gccVerbose(gccBinary)
|
|
|
|
}
|
|
|
|
return output
|
|
|
|
.split('\n')
|
|
|
|
.reduce(function (accumulator, line) {
|
|
|
|
if (line.startsWith('Target: ')) {
|
|
|
|
return {
|
|
|
|
...accumulator,
|
|
|
|
build: line.split(' ')[1]
|
|
|
|
}
|
|
|
|
} else if (line.startsWith('COLLECT_GCC')) {
|
|
|
|
return {
|
|
|
|
...accumulator,
|
|
|
|
gcc: line.split('=')[1]
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return accumulator
|
|
|
|
}
|
|
|
|
}, {})
|
2024-05-14 22:09:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
async function copyGlibcHeaders () {
|
|
|
|
const sourceDirectory = await downloadAndUnarchive(
|
|
|
|
new URL(`https://ftp.gnu.org/gnu/glibc/glibc-${glibcVersion}.tar.xz`)
|
|
|
|
)
|
|
|
|
const includeDirectory = path.join(tmp, 'include')
|
|
|
|
|
|
|
|
await fs.mkdir(includeDirectory)
|
|
|
|
await fs.cp(path.join(sourceDirectory, 'elf/elf.h'), path.join(includeDirectory, 'elf.h'))
|
|
|
|
|
|
|
|
return sourceDirectory
|
2024-05-14 22:09:05 +02:00
|
|
|
}
|
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
async function buildKernel (rootfs, options) {
|
2024-05-14 22:09:05 +02:00
|
|
|
const cwd = await downloadAndUnarchive(
|
|
|
|
new URL(`https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${kernelVersion}.tar.xz`)
|
|
|
|
)
|
|
|
|
const env = {
|
|
|
|
...process.env,
|
2024-05-23 01:13:16 +02:00
|
|
|
CROSS_COMPILE: `${options.target}-`,
|
2024-05-14 22:09:05 +02:00
|
|
|
ARCH: 'riscv',
|
2024-05-23 01:13:16 +02:00
|
|
|
PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`,
|
|
|
|
HOSTCFLAGS: `-D_UUID_T -D__GETHOSTUUID_H -I${path.join(tmp, 'include')}`
|
2024-05-14 22:09:05 +02:00
|
|
|
}
|
|
|
|
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 })
|
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
const userDirectory = path.join(options.sysroot, 'usr')
|
2024-05-14 22:09:05 +02:00
|
|
|
await fs.cp(path.join(cwd, 'usr/include'), path.join(userDirectory, 'include'), { recursive: true })
|
|
|
|
|
|
|
|
return path.join(cwd, 'arch/riscv/boot/Image')
|
|
|
|
}
|
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
async function buildInit (rootfs, options) {
|
2024-05-14 22:09:05 +02:00
|
|
|
const env = {
|
|
|
|
...process.env,
|
2024-05-23 01:13:16 +02:00
|
|
|
PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`
|
2024-05-14 22:09:05 +02:00
|
|
|
}
|
|
|
|
const compilerArguments = [
|
|
|
|
'-ffreestanding', '-static',
|
|
|
|
'-o', path.join(tmp, 'init'),
|
2024-05-23 01:13:16 +02:00
|
|
|
path.join(baseDirectory, 'init.c')
|
2024-05-14 22:09:05 +02:00
|
|
|
]
|
2024-05-23 01:13:16 +02:00
|
|
|
childProcess.execFileSync('riscv32-unknown-linux-gnu-gcc', compilerArguments, { stdio: 'inherit', env })
|
2024-05-14 22:09:05 +02:00
|
|
|
}
|
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
async function buildGlibc (sourceDirectory, rootfs, options) {
|
2024-05-14 22:09:05 +02:00
|
|
|
const configureOptions = [
|
|
|
|
'--prefix=/usr',
|
2024-05-23 01:13:16 +02:00
|
|
|
`--host=${options.target}`,
|
|
|
|
`--target=${options.target}`,
|
2024-05-20 01:42:56 +02:00
|
|
|
`--build=${options.build}`,
|
2024-05-14 22:09:05 +02:00
|
|
|
`--enable-kernel=${kernelVersion}`,
|
2024-05-23 01:13:16 +02:00
|
|
|
`--with-headers=${path.join(options.sysroot, 'usr/include')}`,
|
|
|
|
'--disable-nscd',
|
|
|
|
'--disable-libquadmath',
|
|
|
|
'--disable-libitm',
|
|
|
|
'--disable-werror',
|
|
|
|
'libc_cv_forced_unwind=yes'
|
2024-05-14 22:09:05 +02:00
|
|
|
]
|
2024-05-23 01:13:16 +02:00
|
|
|
const bin = path.join(rootfs, 'bin')
|
2024-05-14 22:09:05 +02:00
|
|
|
const env = {
|
|
|
|
...process.env,
|
2024-05-23 01:13:16 +02:00
|
|
|
PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`
|
2024-05-14 22:09:05 +02:00
|
|
|
}
|
|
|
|
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 })
|
2024-05-23 01:13:16 +02:00
|
|
|
childProcess.execFileSync('make', [`install_root=${options.sysroot}`, 'install'], { stdio: 'inherit', env, cwd })
|
2024-05-14 22:09:05 +02:00
|
|
|
}
|
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
async function buildCrossBinutils (rootfs, options) {
|
2024-05-14 22:09:05 +02:00
|
|
|
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)
|
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
const env = {
|
|
|
|
...process.env,
|
|
|
|
CC: options.gcc,
|
|
|
|
CXX: options.gxx
|
|
|
|
}
|
2024-05-14 22:09:05 +02:00
|
|
|
const configureOptions = [
|
2024-05-23 01:13:16 +02:00
|
|
|
`--prefix=${rootfs}`,
|
|
|
|
`--target=${options.target}`,
|
2024-05-14 22:09:05 +02:00
|
|
|
'--disable-nls',
|
|
|
|
'--enable-gprofng=no',
|
|
|
|
'--disable-werror',
|
2024-05-23 01:13:16 +02:00
|
|
|
'--enable-default-hash-style=gnu',
|
|
|
|
'--disable-libquadmath'
|
2024-05-14 22:09:05 +02:00
|
|
|
]
|
|
|
|
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, {
|
|
|
|
stdio: 'inherit',
|
2024-05-23 01:13:16 +02:00
|
|
|
cwd,
|
|
|
|
env
|
2024-05-14 22:09:05 +02:00
|
|
|
})
|
2024-05-23 01:13:16 +02:00
|
|
|
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', cwd, env })
|
|
|
|
childProcess.execFileSync('make', ['install'], { stdio: 'inherit', cwd, env })
|
2024-05-14 22:09:05 +02:00
|
|
|
|
|
|
|
return sourceDirectory
|
|
|
|
}
|
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
async function buildGCC1 (rootfs, options) {
|
2024-05-14 22:09:05 +02:00
|
|
|
const sourceDirectory = await downloadAndUnarchive(
|
2024-05-20 01:42:56 +02:00
|
|
|
new URL(`https://gcc.gnu.org/pub/gcc/releases/gcc-${gccVersion}/gcc-${gccVersion}.tar.xz`)
|
2024-05-14 22:09:05 +02:00
|
|
|
)
|
|
|
|
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 = [
|
2024-05-23 01:13:16 +02:00
|
|
|
`--prefix=${rootfs}`,
|
|
|
|
`--with-sysroot=${options.sysroot}`,
|
2024-05-14 22:09:05 +02:00
|
|
|
'--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',
|
2024-05-23 01:13:16 +02:00
|
|
|
`--target=${options.target}`,
|
2024-05-20 01:42:56 +02:00
|
|
|
`--build=${options.build}`,
|
|
|
|
`--host=${options.build}`
|
2024-05-14 22:09:05 +02:00
|
|
|
]
|
|
|
|
const flags = '-O2 -fPIC'
|
|
|
|
const env = {
|
|
|
|
...process.env,
|
2024-05-23 01:13:16 +02:00
|
|
|
CC: options.gcc,
|
|
|
|
CXX: options.gxx,
|
2024-05-14 22:09:05 +02:00
|
|
|
CFLAGS: flags,
|
|
|
|
CXXFLAGS: flags,
|
2024-05-23 01:13:16 +02:00
|
|
|
PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`
|
2024-05-14 22:09:05 +02:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
async function buildGCC2 (sourceDirectory, rootfs, options) {
|
2024-05-14 22:09:05 +02:00
|
|
|
const cwd = path.join(path.dirname(sourceDirectory), 'build-gcc');
|
|
|
|
await fs.rm(cwd, { recursive: true, force: true })
|
|
|
|
await fs.mkdir(cwd)
|
|
|
|
|
|
|
|
const configureOptions = [
|
2024-05-23 01:13:16 +02:00
|
|
|
`--prefix=${rootfs}`,
|
|
|
|
`--with-sysroot=${options.sysroot}`,
|
2024-05-14 22:09:05 +02:00
|
|
|
'--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',
|
2024-05-23 01:13:16 +02:00
|
|
|
'--disable-libquadmath',
|
2024-05-14 22:09:05 +02:00
|
|
|
'--enable-threads=posix',
|
|
|
|
'--with-default-libstdcxx-abi=new',
|
|
|
|
'--disable-nls',
|
2024-05-23 01:13:16 +02:00
|
|
|
`--target=${options.target}`,
|
2024-05-20 01:42:56 +02:00
|
|
|
`--build=${options.build}`,
|
|
|
|
`--host=${options.build}`
|
2024-05-14 22:09:05 +02:00
|
|
|
]
|
|
|
|
const flags = '-O2 -fPIC'
|
|
|
|
const env = {
|
|
|
|
...process.env,
|
|
|
|
CFLAGS: flags,
|
|
|
|
CXXFLAGS: flags,
|
2024-05-23 01:13:16 +02:00
|
|
|
PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`
|
2024-05-14 22:09:05 +02:00
|
|
|
}
|
|
|
|
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 })
|
|
|
|
}
|
|
|
|
|
|
|
|
const rootfs = path.join(tmp, 'rootfs')
|
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
const options = await findBuildTarget(gccVersion)
|
|
|
|
options.gxx = options.gcc.replaceAll('c', '+')
|
|
|
|
options.sysroot = path.join(tmp, 'sysroot')
|
|
|
|
options.target = 'riscv32-unknown-linux-gnu'
|
2024-05-20 01:42:56 +02:00
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
for (const targetDirectory of [tmp, rootfs]) {
|
2024-05-14 22:09:05 +02:00
|
|
|
await fs.rm(targetDirectory, { recursive: true, force: true })
|
|
|
|
await fs.mkdir(targetDirectory)
|
|
|
|
}
|
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
const binutilsSource = await buildCrossBinutils(rootfs, options)
|
|
|
|
const gccSource = await buildGCC1(rootfs, options)
|
2024-05-14 22:09:05 +02:00
|
|
|
|
2024-05-23 01:13:16 +02:00
|
|
|
const glibcSource = await copyGlibcHeaders()
|
|
|
|
const kernelImage = await buildKernel(rootfs, options)
|
|
|
|
await buildGlibc(glibcSource, rootfs, options)
|
|
|
|
await buildGCC2(gccSource, rootfs, options)
|
|
|
|
buildInit (rootfs, options)
|
2024-05-14 22:09:05 +02:00
|
|
|
|
|
|
|
console.log(kernelImage)
|