Remove libc dependency for the generated code
This commit is contained in:
parent
189b2e6054
commit
8ea09ff3b4
@ -116,12 +116,10 @@ namespace elna::riscv
|
|||||||
static void generate_intrinsics(std::shared_ptr<source::writer<std::byte>> writer,
|
static void generate_intrinsics(std::shared_ptr<source::writer<std::byte>> writer,
|
||||||
std::vector<reference>& references)
|
std::vector<reference>& references)
|
||||||
{
|
{
|
||||||
writer->sink("printf");
|
|
||||||
{
|
{
|
||||||
std::vector<instruction> instructions;
|
std::vector<instruction> instructions;
|
||||||
auto format_string = writer->sink(reinterpret_cast<const std::byte *>("%c\n\0"), 4);
|
|
||||||
|
|
||||||
prologue(instructions);
|
prologue(instructions);
|
||||||
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
.i(x_register::a1, funct3_t::addi, x_register::zero, 't'));
|
.i(x_register::a1, funct3_t::addi, x_register::zero, 't'));
|
||||||
instructions.push_back(instruction(base_opcode::branch)
|
instructions.push_back(instruction(base_opcode::branch)
|
||||||
@ -129,42 +127,68 @@ namespace elna::riscv
|
|||||||
instructions.push_back(instruction(base_opcode::opImm)
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
.i(x_register::a1, funct3_t::addi, x_register::zero, 'f'));
|
.i(x_register::a1, funct3_t::addi, x_register::zero, 'f'));
|
||||||
|
|
||||||
relocate(format_string, address_t::high20, references, instructions, writer);
|
|
||||||
instructions.push_back(instruction(base_opcode::lui).u(x_register::a5, 0));
|
|
||||||
relocate(format_string, address_t::lower12i, references, instructions, writer);
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
.i(x_register::a0, funct3_t::addi, x_register::a5, 0));
|
.i(x_register::t0, funct3_t::addi, x_register::a0, 0));
|
||||||
|
instructions.push_back(instruction(base_opcode::store)
|
||||||
|
.s(0, funct3_t::sw, x_register::s0, x_register::a1));
|
||||||
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
|
.i(x_register::a0, funct3_t::addi, x_register::zero, 1));
|
||||||
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
|
.i(x_register::a1, funct3_t::addi, x_register::s0, 0));
|
||||||
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
|
.i(x_register::a2, funct3_t::addi, x_register::zero, 1));
|
||||||
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
|
.i(x_register::a7, funct3_t::addi, x_register::zero, 64));
|
||||||
|
instructions.push_back(instruction(base_opcode::system)
|
||||||
|
.i(x_register::zero, funct3_t::priv, x_register::zero, 0));
|
||||||
|
|
||||||
relocate("printf", address_t::text, references, instructions ,writer);
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0));
|
.i(x_register::t1, funct3_t::addi, x_register::zero, '\n'));
|
||||||
instructions.push_back(instruction(base_opcode::jalr)
|
instructions.push_back(instruction(base_opcode::store)
|
||||||
.i(x_register::ra, funct3_t::jalr, x_register::ra, 0));
|
.s(0, funct3_t::sw, x_register::s0, x_register::t1));
|
||||||
|
instructions.push_back(instruction(base_opcode::system)
|
||||||
|
.i(x_register::zero, funct3_t::priv, x_register::zero, 0));
|
||||||
|
|
||||||
epilogue(8, instructions);
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
|
.i(x_register::a0, funct3_t::addi, x_register::t0, 0));
|
||||||
|
|
||||||
|
epilogue(12, instructions);
|
||||||
|
|
||||||
writer->sink("writeb", reinterpret_cast<const std::byte *>(instructions.data()),
|
writer->sink("writeb", reinterpret_cast<const std::byte *>(instructions.data()),
|
||||||
instructions.size() * sizeof(instruction));
|
instructions.size() * sizeof(instruction));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::vector<instruction> instructions;
|
std::vector<instruction> instructions;
|
||||||
auto format_string = writer->sink(reinterpret_cast<const std::byte *>("%d\n\0"), 4);
|
|
||||||
|
|
||||||
prologue(instructions);
|
prologue(instructions);
|
||||||
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
.i(x_register::a1, funct3_t::addi, x_register::a0, 0));
|
.i(x_register::t0, funct3_t::addi, x_register::a0, 0));
|
||||||
|
|
||||||
relocate(format_string, address_t::high20, references, instructions, writer);
|
|
||||||
instructions.push_back(instruction(base_opcode::lui).u(x_register::a5, 0));
|
|
||||||
relocate(format_string, address_t::lower12i, references, instructions, writer);
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
.i(x_register::a0, funct3_t::addi, x_register::a5, 0));
|
.i(x_register::a0, funct3_t::addi, x_register::a0, '0'));
|
||||||
|
instructions.push_back(instruction(base_opcode::store)
|
||||||
|
.s(0, funct3_t::sw, x_register::s0, x_register::a0));
|
||||||
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
|
.i(x_register::a0, funct3_t::addi, x_register::zero, 1));
|
||||||
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
|
.i(x_register::a1, funct3_t::addi, x_register::s0, 0));
|
||||||
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
|
.i(x_register::a2, funct3_t::addi, x_register::zero, 1));
|
||||||
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
|
.i(x_register::a7, funct3_t::addi, x_register::zero, 64));
|
||||||
|
instructions.push_back(instruction(base_opcode::system)
|
||||||
|
.i(x_register::zero, funct3_t::priv, x_register::zero, 0));
|
||||||
|
|
||||||
relocate("printf", address_t::text, references, instructions, writer);
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0));
|
.i(x_register::t1, funct3_t::addi, x_register::zero, '\n'));
|
||||||
instructions.push_back(instruction(base_opcode::jalr)
|
instructions.push_back(instruction(base_opcode::store)
|
||||||
.i(x_register::ra, funct3_t::jalr, x_register::ra, 0));
|
.s(0, funct3_t::sw, x_register::s0, x_register::t1));
|
||||||
|
instructions.push_back(instruction(base_opcode::system)
|
||||||
|
.i(x_register::zero, funct3_t::priv, x_register::zero, 0));
|
||||||
|
|
||||||
epilogue(8, instructions);
|
instructions.push_back(instruction(base_opcode::opImm)
|
||||||
|
.i(x_register::a0, funct3_t::addi, x_register::t0, 0));
|
||||||
|
|
||||||
|
epilogue(12, instructions);
|
||||||
|
|
||||||
writer->sink("writei", reinterpret_cast<const std::byte *>(instructions.data()),
|
writer->sink("writei", reinterpret_cast<const std::byte *>(instructions.data()),
|
||||||
instructions.size() * sizeof(instruction));
|
instructions.size() * sizeof(instruction));
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
#define BOOST_PROCESS_USE_STD_FS
|
#define BOOST_PROCESS_USE_STD_FS
|
||||||
@ -39,7 +38,6 @@ namespace elna
|
|||||||
boost::asio::readable_pipe& read_pipe);
|
boost::asio::readable_pipe& read_pipe);
|
||||||
int run_for_output(boost::asio::io_context& context, const std::uint8_t stream_number,
|
int run_for_output(boost::asio::io_context& context, const std::uint8_t stream_number,
|
||||||
const std::filesystem::path& binary, std::initializer_list<boost::string_view> arguments);
|
const std::filesystem::path& binary, std::initializer_list<boost::string_view> arguments);
|
||||||
std::string find_object(const std::vector<std::filesystem::path>& environment, const std::string& object);
|
|
||||||
test_status build_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry);
|
test_status build_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry);
|
||||||
void run_test(boost::asio::io_context& context, const std::filesystem::path& test_entry);
|
void run_test(boost::asio::io_context& context, const std::filesystem::path& test_entry);
|
||||||
void print_result(const std::filesystem::path& test_entry, const test_status& result);
|
void print_result(const std::filesystem::path& test_entry, const test_status& result);
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"main": "tools/index.js",
|
"main": "tools/index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node tools/index.js"
|
"cross": "node tools/cross.js"
|
||||||
},
|
},
|
||||||
"author": "Eugen Wissner <belka@caraus.de>",
|
"author": "Eugen Wissner <belka@caraus.de>",
|
||||||
"license": "MPL-2.0"
|
"license": "MPL-2.0"
|
||||||
|
@ -165,7 +165,7 @@ namespace elna::source
|
|||||||
this->current.emplace_back(quadruple_operator::start);
|
this->current.emplace_back(quadruple_operator::start);
|
||||||
program->body().accept(this);
|
program->body().accept(this);
|
||||||
this->current.emplace_back(quadruple_operator::stop);
|
this->current.emplace_back(quadruple_operator::stop);
|
||||||
code["main"] = std::move(this->current);
|
code["_start"] = std::move(this->current);
|
||||||
}
|
}
|
||||||
|
|
||||||
void intermediate_code_generator::visit(call_statement *statement)
|
void intermediate_code_generator::visit(call_statement *statement)
|
||||||
|
@ -44,7 +44,7 @@ namespace elna::source
|
|||||||
void name_analysis_visitor::visit(program *program)
|
void name_analysis_visitor::visit(program *program)
|
||||||
{
|
{
|
||||||
class procedure_type main_type{ std::vector<std::shared_ptr<const class type>>(), this->pointer_size };
|
class procedure_type main_type{ std::vector<std::shared_ptr<const class type>>(), this->pointer_size };
|
||||||
this->table->enter("main", std::make_shared<procedure_info>(main_type, this->table));
|
this->table->enter("_start", std::make_shared<procedure_info>(main_type, this->table));
|
||||||
empty_visitor::visit(program);
|
empty_visitor::visit(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ namespace elna::source
|
|||||||
this->argument_offset = 0;
|
this->argument_offset = 0;
|
||||||
|
|
||||||
empty_visitor::visit(program);
|
empty_visitor::visit(program);
|
||||||
std::dynamic_pointer_cast<procedure_info>(table->lookup("main"))->local_stack_size =
|
std::dynamic_pointer_cast<procedure_info>(table->lookup("_start"))->local_stack_size =
|
||||||
std::abs(this->local_offset);
|
std::abs(this->local_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
writei(3 + 4 + 5 + 1 + 2 + 4 + 3)
|
|
||||||
.
|
|
@ -1 +0,0 @@
|
|||||||
22
|
|
@ -1 +1 @@
|
|||||||
10
|
8
|
||||||
|
@ -1 +1 @@
|
|||||||
24
|
H
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
writei(5 * 2)
|
writei(4 * 2)
|
||||||
.
|
.
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <algorithm>
|
|
||||||
#include <boost/process/env.hpp>
|
#include <boost/process/env.hpp>
|
||||||
|
|
||||||
namespace elna
|
namespace elna
|
||||||
@ -105,22 +104,6 @@ namespace elna
|
|||||||
return elna_child.wait();
|
return elna_child.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string find_object(const std::vector<std::filesystem::path>& environment, const std::string& object)
|
|
||||||
{
|
|
||||||
auto variable = std::find(environment.cbegin(), environment.cend(), object);
|
|
||||||
|
|
||||||
for (const auto& variable : environment)
|
|
||||||
{
|
|
||||||
auto full_path = variable / object;
|
|
||||||
|
|
||||||
if (std::filesystem::exists(full_path))
|
|
||||||
{
|
|
||||||
return full_path.native();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
test_status build_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry)
|
test_status build_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry)
|
||||||
{
|
{
|
||||||
const std::filesystem::path test_filename = test_entry.path().filename();
|
const std::filesystem::path test_filename = test_entry.path().filename();
|
||||||
@ -145,17 +128,7 @@ namespace elna
|
|||||||
std::vector<std::filesystem::path> environment;
|
std::vector<std::filesystem::path> environment;
|
||||||
std::vector<std::string> linker_arguments = { "-o", test_binary.string() };
|
std::vector<std::string> linker_arguments = { "-o", test_binary.string() };
|
||||||
|
|
||||||
for (const auto segment : boost::this_process::environment()["LIBRARY_PATH"].to_vector())
|
|
||||||
{
|
|
||||||
linker_arguments.push_back("-L" + segment);
|
|
||||||
environment.push_back(std::filesystem::path(segment));
|
|
||||||
}
|
|
||||||
linker_arguments.push_back(find_object(environment, "crt0.o"));
|
|
||||||
linker_arguments.push_back(find_object(environment, "crtbegin.o"));
|
|
||||||
linker_arguments.push_back(test_object.string());
|
linker_arguments.push_back(test_object.string());
|
||||||
linker_arguments.insert(linker_arguments.cend(),
|
|
||||||
{ "--start-group", "-lgcc", "-lc", "-lgloss", "--end-group" });
|
|
||||||
linker_arguments.push_back(find_object(environment, "crtend.o"));
|
|
||||||
|
|
||||||
boost::process::v2::execute(boost::process::v2::process(context, boost::process::search_path("ld"),
|
boost::process::v2::execute(boost::process::v2::process(context, boost::process::search_path("ld"),
|
||||||
linker_arguments
|
linker_arguments
|
||||||
@ -248,7 +221,7 @@ namespace elna
|
|||||||
"-nographic",
|
"-nographic",
|
||||||
"-M", "virt",
|
"-M", "virt",
|
||||||
"-bios", "default",
|
"-bios", "default",
|
||||||
"-kernel", "build/tools/linux-5.15.158/arch/riscv/boot/Image",
|
"-kernel", "build/tools/linux-5.15.159/arch/riscv/boot/Image",
|
||||||
"-append", "quiet",
|
"-append", "quiet",
|
||||||
"-initrd", "build/root.cpio"
|
"-initrd", "build/root.cpio"
|
||||||
};
|
};
|
||||||
|
@ -7,45 +7,57 @@ import { Buffer } from 'node:buffer'
|
|||||||
|
|
||||||
// Define constants.
|
// Define constants.
|
||||||
const tmp = path.resolve('./build/tools')
|
const tmp = path.resolve('./build/tools')
|
||||||
const target = 'riscv32-unknown-linux-gnu'
|
|
||||||
const baseDirectory = path.resolve('./tools')
|
const baseDirectory = path.resolve('./tools')
|
||||||
|
|
||||||
const busyboxVersion = '1.36.1'
|
const kernelVersion = '5.15.159'
|
||||||
const kernelVersion = '5.15.158'
|
|
||||||
const gccVersion = '14.1.0'
|
const gccVersion = '14.1.0'
|
||||||
const binutilsVersion = '2.42'
|
const binutilsVersion = '2.42'
|
||||||
const glibcVersion = '2.39'
|
const glibcVersion = '2.39'
|
||||||
|
|
||||||
function findBuildTarget () {
|
async function gccVerbose (gccBinary) {
|
||||||
const env = {
|
const env = {
|
||||||
LANG: 'en_US.UTF-8'
|
...process.env,
|
||||||
|
LANG: 'C'
|
||||||
}
|
}
|
||||||
const gccV = childProcess.spawn('gcc', ['--verbose'], { stdio: ['ignore', 'ignore', 'pipe'], env })
|
const gccV = childProcess.spawn(gccBinary, ['--verbose'], { stdio: ['ignore', 'ignore', 'pipe'], env })
|
||||||
const buffers = []
|
const buffers = []
|
||||||
|
|
||||||
gccV.stderr.on('data', function (data) {
|
gccV.stderr.on('data', function (data) {
|
||||||
buffers.push(data)
|
buffers.push(data)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
gccV.on('exit', function () {
|
gccV.on('exit', function () {
|
||||||
const [_, target] = Buffer.concat(buffers)
|
resolve(Buffer.concat(buffers).toString())
|
||||||
.toString()
|
|
||||||
.split('\n')
|
|
||||||
.find(line => line.startsWith('Target: '))
|
|
||||||
.split(' ')
|
|
||||||
|
|
||||||
resolve(target)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function createImage (rootfs) {
|
async function findBuildTarget (gccVersion) {
|
||||||
const rootExt4 = path.join(tmp, 'rootfs.ext4')
|
let gccBinary = 'gcc'
|
||||||
|
let output = await gccVerbose(gccBinary)
|
||||||
|
|
||||||
childProcess.execFileSync('dd', ['if=/dev/zero', `of=${rootExt4}`, 'bs=1M', 'count=1024'], { stdio: 'inherit' })
|
if (output.startsWith('Apple clang')) {
|
||||||
childProcess.execFileSync('/sbin/mkfs.ext4', ['-d', rootfs, rootExt4], { stdio: 'inherit' })
|
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
|
||||||
|
}
|
||||||
|
}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function downloadAndUnarchive (url) {
|
async function downloadAndUnarchive (url) {
|
||||||
@ -89,70 +101,70 @@ async function downloadAndUnarchive (url) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildBusyBox (sysroot, rootfs) {
|
async function copyGlibcHeaders () {
|
||||||
const cwd = await downloadAndUnarchive(new URL(`https://busybox.net/downloads/busybox-${busyboxVersion}.tar.bz2`))
|
const sourceDirectory = await downloadAndUnarchive(
|
||||||
const env = {
|
new URL(`https://ftp.gnu.org/gnu/glibc/glibc-${glibcVersion}.tar.xz`)
|
||||||
...process.env,
|
)
|
||||||
CROSS_COMPILE: path.join(sysroot, 'bin', `${target}-`)
|
const includeDirectory = path.join(tmp, 'include')
|
||||||
}
|
|
||||||
const configuration = [
|
await fs.mkdir(includeDirectory)
|
||||||
`CONFIG_PREFIX=${rootfs}`,
|
await fs.cp(path.join(sourceDirectory, 'elf/elf.h'), path.join(includeDirectory, 'elf.h'))
|
||||||
`CONFIG_SYSROOT=${rootfs}`
|
|
||||||
]
|
return sourceDirectory
|
||||||
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) {
|
async function buildKernel (rootfs, options) {
|
||||||
const cwd = await downloadAndUnarchive(
|
const cwd = await downloadAndUnarchive(
|
||||||
new URL(`https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${kernelVersion}.tar.xz`)
|
new URL(`https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${kernelVersion}.tar.xz`)
|
||||||
)
|
)
|
||||||
const env = {
|
const env = {
|
||||||
...process.env,
|
...process.env,
|
||||||
CROSS_COMPILE: `${target}-`,
|
CROSS_COMPILE: `${options.target}-`,
|
||||||
ARCH: 'riscv',
|
ARCH: 'riscv',
|
||||||
PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}`
|
PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`,
|
||||||
|
HOSTCFLAGS: `-D_UUID_T -D__GETHOSTUUID_H -I${path.join(tmp, 'include')}`
|
||||||
}
|
}
|
||||||
childProcess.execFileSync('make', ['rv32_defconfig'], { stdio: 'inherit', env, cwd })
|
childProcess.execFileSync('make', ['rv32_defconfig'], { stdio: 'inherit', env, cwd })
|
||||||
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd })
|
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd })
|
||||||
childProcess.execFileSync('make', ['headers'], { stdio: 'inherit', env, cwd })
|
childProcess.execFileSync('make', ['headers'], { stdio: 'inherit', env, cwd })
|
||||||
|
|
||||||
const userDirectory = path.join(rootfs, 'usr')
|
const userDirectory = path.join(options.sysroot, 'usr')
|
||||||
await fs.cp(path.join(cwd, 'usr/include'), path.join(userDirectory, 'include'), { recursive: true })
|
await fs.cp(path.join(cwd, 'usr/include'), path.join(userDirectory, 'include'), { recursive: true })
|
||||||
|
|
||||||
return path.join(cwd, 'arch/riscv/boot/Image')
|
return path.join(cwd, 'arch/riscv/boot/Image')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildInit (sysroot) {
|
async function buildInit (rootfs, options) {
|
||||||
const env = {
|
const env = {
|
||||||
...process.env,
|
...process.env,
|
||||||
PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}`
|
PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`
|
||||||
}
|
}
|
||||||
const compilerArguments = [
|
const compilerArguments = [
|
||||||
'-ffreestanding', '-static',
|
'-ffreestanding', '-static',
|
||||||
'-o', path.join(tmp, 'init'),
|
'-o', path.join(tmp, 'init'),
|
||||||
path.join(baseDirectory, 'init.cpp')
|
path.join(baseDirectory, 'init.c')
|
||||||
]
|
]
|
||||||
childProcess.execFileSync('riscv32-unknown-linux-gnu-g++', compilerArguments, { stdio: 'inherit', env })
|
childProcess.execFileSync('riscv32-unknown-linux-gnu-gcc', compilerArguments, { stdio: 'inherit', env })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildGlibc (sysroot, rootfs, options) {
|
async function buildGlibc (sourceDirectory, rootfs, options) {
|
||||||
const sourceDirectory = await downloadAndUnarchive(
|
|
||||||
new URL(`https://ftp.gnu.org/gnu/glibc/glibc-${glibcVersion}.tar.xz`)
|
|
||||||
)
|
|
||||||
const configureOptions = [
|
const configureOptions = [
|
||||||
'--prefix=/usr',
|
'--prefix=/usr',
|
||||||
'--libdir=/usr/lib',
|
`--host=${options.target}`,
|
||||||
`--host=${target}`,
|
`--target=${options.target}`,
|
||||||
`--build=${options.build}`,
|
`--build=${options.build}`,
|
||||||
`--enable-kernel=${kernelVersion}`,
|
`--enable-kernel=${kernelVersion}`,
|
||||||
`--with-headers=${path.join(rootfs, 'usr/include')}`,
|
`--with-headers=${path.join(options.sysroot, 'usr/include')}`,
|
||||||
'--disable-nscd'
|
'--disable-nscd',
|
||||||
|
'--disable-libquadmath',
|
||||||
|
'--disable-libitm',
|
||||||
|
'--disable-werror',
|
||||||
|
'libc_cv_forced_unwind=yes'
|
||||||
]
|
]
|
||||||
|
const bin = path.join(rootfs, 'bin')
|
||||||
const env = {
|
const env = {
|
||||||
...process.env,
|
...process.env,
|
||||||
PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}`
|
PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`
|
||||||
}
|
}
|
||||||
const cwd = path.join(path.dirname(sourceDirectory), 'build-glibc');
|
const cwd = path.join(path.dirname(sourceDirectory), 'build-glibc');
|
||||||
await fs.mkdir(cwd)
|
await fs.mkdir(cwd)
|
||||||
@ -163,35 +175,42 @@ async function buildGlibc (sysroot, rootfs, options) {
|
|||||||
cwd
|
cwd
|
||||||
})
|
})
|
||||||
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd })
|
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd })
|
||||||
childProcess.execFileSync('make', [`DESTDIR=${rootfs}`, 'install'], { stdio: 'inherit', env, cwd })
|
childProcess.execFileSync('make', [`install_root=${options.sysroot}`, 'install'], { stdio: 'inherit', env, cwd })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildCrossBinutils (sysroot) {
|
async function buildCrossBinutils (rootfs, options) {
|
||||||
const sourceDirectory = await downloadAndUnarchive(
|
const sourceDirectory = await downloadAndUnarchive(
|
||||||
new URL(`https://ftp.gnu.org/gnu/binutils/binutils-${binutilsVersion}.tar.xz`)
|
new URL(`https://ftp.gnu.org/gnu/binutils/binutils-${binutilsVersion}.tar.xz`)
|
||||||
)
|
)
|
||||||
const cwd = path.join(path.dirname(sourceDirectory), 'build-binutils');
|
const cwd = path.join(path.dirname(sourceDirectory), 'build-binutils');
|
||||||
await fs.mkdir(cwd)
|
await fs.mkdir(cwd)
|
||||||
|
|
||||||
|
const env = {
|
||||||
|
...process.env,
|
||||||
|
CC: options.gcc,
|
||||||
|
CXX: options.gxx
|
||||||
|
}
|
||||||
const configureOptions = [
|
const configureOptions = [
|
||||||
`--prefix=${sysroot}`,
|
`--prefix=${rootfs}`,
|
||||||
`--target=${target}`,
|
`--target=${options.target}`,
|
||||||
'--disable-nls',
|
'--disable-nls',
|
||||||
'--enable-gprofng=no',
|
'--enable-gprofng=no',
|
||||||
'--disable-werror',
|
'--disable-werror',
|
||||||
'--enable-default-hash-style=gnu'
|
'--enable-default-hash-style=gnu',
|
||||||
|
'--disable-libquadmath'
|
||||||
]
|
]
|
||||||
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, {
|
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
cwd
|
cwd,
|
||||||
|
env
|
||||||
})
|
})
|
||||||
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', cwd })
|
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', cwd, env })
|
||||||
childProcess.execFileSync('make', ['install'], { stdio: 'inherit', cwd })
|
childProcess.execFileSync('make', ['install'], { stdio: 'inherit', cwd, env })
|
||||||
|
|
||||||
return sourceDirectory
|
return sourceDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildGCC1 (sysroot, rootfs, options) {
|
async function buildGCC1 (rootfs, options) {
|
||||||
const sourceDirectory = await downloadAndUnarchive(
|
const sourceDirectory = await downloadAndUnarchive(
|
||||||
new URL(`https://gcc.gnu.org/pub/gcc/releases/gcc-${gccVersion}/gcc-${gccVersion}.tar.xz`)
|
new URL(`https://gcc.gnu.org/pub/gcc/releases/gcc-${gccVersion}/gcc-${gccVersion}.tar.xz`)
|
||||||
)
|
)
|
||||||
@ -203,8 +222,8 @@ async function buildGCC1 (sysroot, rootfs, options) {
|
|||||||
cwd: sourceDirectory
|
cwd: sourceDirectory
|
||||||
})
|
})
|
||||||
const configureOptions = [
|
const configureOptions = [
|
||||||
`--prefix=${sysroot}`,
|
`--prefix=${rootfs}`,
|
||||||
`--with-sysroot=${rootfs}`,
|
`--with-sysroot=${options.sysroot}`,
|
||||||
'--enable-languages=c,c++',
|
'--enable-languages=c,c++',
|
||||||
'--disable-shared',
|
'--disable-shared',
|
||||||
'--with-arch=rv32imafdc',
|
'--with-arch=rv32imafdc',
|
||||||
@ -225,16 +244,18 @@ async function buildGCC1 (sysroot, rootfs, options) {
|
|||||||
'--disable-nls',
|
'--disable-nls',
|
||||||
'--with-newlib',
|
'--with-newlib',
|
||||||
'--without-headers',
|
'--without-headers',
|
||||||
`--target=${target}`,
|
`--target=${options.target}`,
|
||||||
`--build=${options.build}`,
|
`--build=${options.build}`,
|
||||||
`--host=${options.build}`
|
`--host=${options.build}`
|
||||||
]
|
]
|
||||||
const flags = '-O2 -fPIC'
|
const flags = '-O2 -fPIC'
|
||||||
const env = {
|
const env = {
|
||||||
...process.env,
|
...process.env,
|
||||||
|
CC: options.gcc,
|
||||||
|
CXX: options.gxx,
|
||||||
CFLAGS: flags,
|
CFLAGS: flags,
|
||||||
CXXFLAGS: flags,
|
CXXFLAGS: flags,
|
||||||
PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}`
|
PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`
|
||||||
}
|
}
|
||||||
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, {
|
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
@ -247,45 +268,14 @@ async function buildGCC1 (sysroot, rootfs, options) {
|
|||||||
return sourceDirectory
|
return sourceDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildBinutils (sourceDirectory, sysroot, rootfs, options) {
|
async function buildGCC2 (sourceDirectory, 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');
|
const cwd = path.join(path.dirname(sourceDirectory), 'build-gcc');
|
||||||
await fs.rm(cwd, { recursive: true, force: true })
|
await fs.rm(cwd, { recursive: true, force: true })
|
||||||
await fs.mkdir(cwd)
|
await fs.mkdir(cwd)
|
||||||
|
|
||||||
const configureOptions = [
|
const configureOptions = [
|
||||||
`--prefix=${sysroot}`,
|
`--prefix=${rootfs}`,
|
||||||
`--with-sysroot=${rootfs}`,
|
`--with-sysroot=${options.sysroot}`,
|
||||||
'--enable-languages=c,c++,lto',
|
'--enable-languages=c,c++,lto',
|
||||||
'--enable-lto',
|
'--enable-lto',
|
||||||
'--enable-shared',
|
'--enable-shared',
|
||||||
@ -297,10 +287,11 @@ async function buildGCC2 (sourceDirectory, sysroot, rootfs, options) {
|
|||||||
'--disable-multilib',
|
'--disable-multilib',
|
||||||
'--enable-checking=release',
|
'--enable-checking=release',
|
||||||
'--disable-libssp',
|
'--disable-libssp',
|
||||||
|
'--disable-libquadmath',
|
||||||
'--enable-threads=posix',
|
'--enable-threads=posix',
|
||||||
'--with-default-libstdcxx-abi=new',
|
'--with-default-libstdcxx-abi=new',
|
||||||
'--disable-nls',
|
'--disable-nls',
|
||||||
`--target=${target}`,
|
`--target=${options.target}`,
|
||||||
`--build=${options.build}`,
|
`--build=${options.build}`,
|
||||||
`--host=${options.build}`
|
`--host=${options.build}`
|
||||||
]
|
]
|
||||||
@ -309,7 +300,7 @@ async function buildGCC2 (sourceDirectory, sysroot, rootfs, options) {
|
|||||||
...process.env,
|
...process.env,
|
||||||
CFLAGS: flags,
|
CFLAGS: flags,
|
||||||
CXXFLAGS: flags,
|
CXXFLAGS: flags,
|
||||||
PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}`
|
PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`
|
||||||
}
|
}
|
||||||
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, {
|
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
@ -320,83 +311,25 @@ async function buildGCC2 (sourceDirectory, sysroot, rootfs, options) {
|
|||||||
childProcess.execFileSync('make', ['install'], { 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 rootfs = path.join(tmp, 'rootfs')
|
||||||
|
|
||||||
const options = {
|
const options = await findBuildTarget(gccVersion)
|
||||||
build: await findBuildTarget()
|
options.gxx = options.gcc.replaceAll('c', '+')
|
||||||
}
|
options.sysroot = path.join(tmp, 'sysroot')
|
||||||
|
options.target = 'riscv32-unknown-linux-gnu'
|
||||||
|
|
||||||
for (const targetDirectory of [tmp, sysroot, rootfs]) {
|
for (const targetDirectory of [tmp, rootfs]) {
|
||||||
await fs.rm(targetDirectory, { recursive: true, force: true })
|
await fs.rm(targetDirectory, { recursive: true, force: true })
|
||||||
await fs.mkdir(targetDirectory)
|
await fs.mkdir(targetDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
const binutilsSource = await buildCrossBinutils(sysroot)
|
const binutilsSource = await buildCrossBinutils(rootfs, options)
|
||||||
const gccSource = await buildGCC1(sysroot, rootfs, options)
|
const gccSource = await buildGCC1(rootfs, options)
|
||||||
|
|
||||||
const kernelImage = await buildKernel(sysroot, rootfs)
|
const glibcSource = await copyGlibcHeaders()
|
||||||
await buildGlibc(sysroot, rootfs, options)
|
const kernelImage = await buildKernel(rootfs, options)
|
||||||
await buildBinutils(binutilsSource, sysroot, rootfs, options)
|
await buildGlibc(glibcSource, rootfs, options)
|
||||||
await buildGCC2(gccSource, sysroot, rootfs, options)
|
await buildGCC2(gccSource, rootfs, options)
|
||||||
buildInit (sysroot)
|
buildInit (rootfs, options)
|
||||||
|
|
||||||
await buildBusyBox(sysroot, rootfs)
|
|
||||||
await createRoot(rootfs)
|
|
||||||
|
|
||||||
await buildGCC3(gccSource, sysroot, rootfs, options)
|
|
||||||
createImage(rootfs)
|
|
||||||
console.log(kernelImage)
|
console.log(kernelImage)
|
205
tools/init.c
Normal file
205
tools/init.c
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/reboot.h>
|
||||||
|
|
||||||
|
#define FILENAME_BUFFER_SIZE 256
|
||||||
|
|
||||||
|
size_t read_command(int descriptor, char *command_buffer)
|
||||||
|
{
|
||||||
|
ssize_t bytes_read = 0;
|
||||||
|
size_t read_so_far = 0;
|
||||||
|
|
||||||
|
while ((bytes_read = read(descriptor, command_buffer + read_so_far, FILENAME_BUFFER_SIZE - read_so_far - 1)) > 0)
|
||||||
|
{
|
||||||
|
read_so_far += bytes_read;
|
||||||
|
if (read_so_far >= FILENAME_BUFFER_SIZE - 1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command_buffer[read_so_far] = 0;
|
||||||
|
return read_so_far;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum status
|
||||||
|
{
|
||||||
|
status_success,
|
||||||
|
status_failure,
|
||||||
|
status_warning,
|
||||||
|
status_fatal
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned int make_path(char *destination, const char *directory, const char *filename, const char *extension)
|
||||||
|
{
|
||||||
|
unsigned int i = 0;
|
||||||
|
|
||||||
|
for (; i < FILENAME_BUFFER_SIZE; i++)
|
||||||
|
{
|
||||||
|
if (directory[i] == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
destination[i] = directory[i];
|
||||||
|
}
|
||||||
|
for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++)
|
||||||
|
{
|
||||||
|
if (filename[j] == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
destination[i] = filename[j];
|
||||||
|
}
|
||||||
|
if (extension == NULL)
|
||||||
|
{
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++)
|
||||||
|
{
|
||||||
|
if (extension[j] == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
destination[i] = extension[j];
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
destination[i] = 0;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum status run_test(const char *file_entry_name)
|
||||||
|
{
|
||||||
|
printf("Running %s. ", file_entry_name);
|
||||||
|
|
||||||
|
char filename[FILENAME_BUFFER_SIZE];
|
||||||
|
char command_buffer[FILENAME_BUFFER_SIZE];
|
||||||
|
char file_buffer[256];
|
||||||
|
int pipe_ends[2];
|
||||||
|
|
||||||
|
if (pipe(pipe_ends) == -1)
|
||||||
|
{
|
||||||
|
perror("pipe");
|
||||||
|
return status_fatal;
|
||||||
|
}
|
||||||
|
make_path(filename, "./tests/", file_entry_name, NULL);
|
||||||
|
|
||||||
|
int child_pid = fork();
|
||||||
|
if (child_pid == -1)
|
||||||
|
{
|
||||||
|
return status_fatal;
|
||||||
|
}
|
||||||
|
else if (child_pid == 0)
|
||||||
|
{
|
||||||
|
close(STDIN_FILENO);
|
||||||
|
close(STDERR_FILENO);
|
||||||
|
close(pipe_ends[0]); // Close the read end.
|
||||||
|
|
||||||
|
if (dup2(pipe_ends[1], STDOUT_FILENO) == -1)
|
||||||
|
{
|
||||||
|
perror("dup2");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
execl(filename, filename);
|
||||||
|
perror("execl");
|
||||||
|
}
|
||||||
|
close(STDOUT_FILENO);
|
||||||
|
close(pipe_ends[1]);
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
close(pipe_ends[1]); // Close the write end.
|
||||||
|
read_command(pipe_ends[0], command_buffer);
|
||||||
|
close(pipe_ends[0]);
|
||||||
|
|
||||||
|
int wait_status = 0;
|
||||||
|
wait(&wait_status);
|
||||||
|
|
||||||
|
make_path(filename, "./expectations/", file_entry_name, ".txt");
|
||||||
|
|
||||||
|
FILE *expectation_descriptor = fopen(filename, "r");
|
||||||
|
|
||||||
|
if (expectation_descriptor == NULL)
|
||||||
|
{
|
||||||
|
return status_warning;
|
||||||
|
}
|
||||||
|
size_t read_from_file = fread(file_buffer, 1, sizeof(file_buffer) - 1, expectation_descriptor);
|
||||||
|
fclose(expectation_descriptor);
|
||||||
|
|
||||||
|
file_buffer[read_from_file] = 0;
|
||||||
|
for (unsigned int i = 0; ; ++i)
|
||||||
|
{
|
||||||
|
if (command_buffer[i] == 0 && file_buffer[i] == 0)
|
||||||
|
{
|
||||||
|
fwrite("\n", 1, 1, stdout);
|
||||||
|
return status_success;
|
||||||
|
}
|
||||||
|
else if (command_buffer[i] != file_buffer[i])
|
||||||
|
{
|
||||||
|
printf("Failed. Got:\n%s", command_buffer);
|
||||||
|
return status_failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct summary
|
||||||
|
{
|
||||||
|
size_t total;
|
||||||
|
size_t failure;
|
||||||
|
size_t success;
|
||||||
|
};
|
||||||
|
|
||||||
|
void walk()
|
||||||
|
{
|
||||||
|
DIR *directory_stream = opendir("./tests");
|
||||||
|
struct dirent *file_entry;
|
||||||
|
|
||||||
|
struct summary test_summary = { .total = 0, .failure = 0, .success = 0 };
|
||||||
|
|
||||||
|
while ((file_entry = readdir(directory_stream)) != NULL)
|
||||||
|
{
|
||||||
|
if (file_entry->d_name[0] == '.')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++test_summary.total;
|
||||||
|
switch (run_test(file_entry->d_name))
|
||||||
|
{
|
||||||
|
case status_failure:
|
||||||
|
++test_summary.failure;
|
||||||
|
break;
|
||||||
|
case status_success:
|
||||||
|
++test_summary.success;
|
||||||
|
break;
|
||||||
|
case status_warning:
|
||||||
|
break;
|
||||||
|
case status_fatal:
|
||||||
|
goto end_walk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Successful: %lu, Failed: %lu, Total: %lu.\n",
|
||||||
|
test_summary.success, test_summary.failure, test_summary.total);
|
||||||
|
end_walk:
|
||||||
|
closedir(directory_stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int dev_console = open("/dev/console", O_WRONLY);
|
||||||
|
if (dev_console != -1)
|
||||||
|
{
|
||||||
|
dup2(dev_console, STDOUT_FILENO);
|
||||||
|
walk();
|
||||||
|
close(dev_console);
|
||||||
|
}
|
||||||
|
sync();
|
||||||
|
reboot(RB_POWER_OFF);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
185
tools/init.cpp
185
tools/init.cpp
@ -1,185 +0,0 @@
|
|||||||
#include <algorithm>
|
|
||||||
#include <memory>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <sys/reboot.h>
|
|
||||||
|
|
||||||
template<std::size_t size>
|
|
||||||
std::size_t read_command(int descriptor, char *command_buffer)
|
|
||||||
{
|
|
||||||
std::ptrdiff_t bytes_read{ 0 };
|
|
||||||
std::size_t read_so_far{ 0 };
|
|
||||||
|
|
||||||
while ((bytes_read = read(descriptor, command_buffer + read_so_far, size - read_so_far - 1)) > 0)
|
|
||||||
{
|
|
||||||
read_so_far += bytes_read;
|
|
||||||
if (read_so_far >= size - 1)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
command_buffer[read_so_far] = 0;
|
|
||||||
return read_so_far;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class status
|
|
||||||
{
|
|
||||||
success,
|
|
||||||
failure,
|
|
||||||
warning,
|
|
||||||
fatal
|
|
||||||
};
|
|
||||||
|
|
||||||
template<std::size_t size>
|
|
||||||
void make_path(char *destination, const char *directory, const char *filename, const char *extension)
|
|
||||||
{
|
|
||||||
memcpy(destination, directory, strlen(directory));
|
|
||||||
std::size_t remaining_space = size - strlen(directory);
|
|
||||||
|
|
||||||
if (extension != nullptr)
|
|
||||||
{
|
|
||||||
remaining_space -= strlen(extension) + 1;
|
|
||||||
}
|
|
||||||
strncpy(destination + strlen(directory), filename, remaining_space);
|
|
||||||
|
|
||||||
if (extension != nullptr)
|
|
||||||
{
|
|
||||||
strncpy(destination + strlen(destination), extension, strlen(extension));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
status run_test(const char *file_entry_name)
|
|
||||||
{
|
|
||||||
printf("Running %s. ", file_entry_name);
|
|
||||||
|
|
||||||
char filename[256];
|
|
||||||
char command_buffer[256];
|
|
||||||
char file_buffer[256];
|
|
||||||
int pipe_ends[2];
|
|
||||||
|
|
||||||
if (pipe(pipe_ends) == -1)
|
|
||||||
{
|
|
||||||
perror("pipe");
|
|
||||||
return status::fatal;
|
|
||||||
}
|
|
||||||
make_path<sizeof(filename)>(filename, "./tests/", file_entry_name, nullptr);
|
|
||||||
|
|
||||||
auto child_pid = fork();
|
|
||||||
if (child_pid == -1)
|
|
||||||
{
|
|
||||||
return status::fatal;
|
|
||||||
}
|
|
||||||
else if (child_pid == 0)
|
|
||||||
{
|
|
||||||
close(STDIN_FILENO);
|
|
||||||
close(STDERR_FILENO);
|
|
||||||
close(pipe_ends[0]); // Close the read end.
|
|
||||||
|
|
||||||
if (dup2(pipe_ends[1], STDOUT_FILENO) == -1)
|
|
||||||
{
|
|
||||||
perror("dup2");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
execl(filename, filename);
|
|
||||||
perror("execl");
|
|
||||||
}
|
|
||||||
close(STDOUT_FILENO);
|
|
||||||
close(pipe_ends[1]);
|
|
||||||
std::exit(1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
close(pipe_ends[1]); // Close the write end.
|
|
||||||
auto read_from_command = read_command<sizeof(command_buffer)>(pipe_ends[0], command_buffer);
|
|
||||||
close(pipe_ends[0]);
|
|
||||||
|
|
||||||
int wait_status{ 0 };
|
|
||||||
wait(&wait_status);
|
|
||||||
|
|
||||||
make_path<sizeof(filename)>(filename, "./expectations/", file_entry_name, ".txt");
|
|
||||||
|
|
||||||
std::unique_ptr<FILE, int(*)(FILE *)> expectation_descriptor(fopen(filename, "r"), &fclose);
|
|
||||||
|
|
||||||
if (expectation_descriptor == nullptr)
|
|
||||||
{
|
|
||||||
return status::warning;
|
|
||||||
}
|
|
||||||
auto read_from_file = fread(file_buffer, 1, sizeof(file_buffer) - 1, expectation_descriptor.get());
|
|
||||||
file_buffer[std::max<std::ptrdiff_t>(0, read_from_file)] = 0;
|
|
||||||
|
|
||||||
if (strcmp(command_buffer, file_buffer) != 0)
|
|
||||||
{
|
|
||||||
printf("Failed. Got:\n%s", command_buffer);
|
|
||||||
|
|
||||||
return status::failure;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fwrite("\n", 1, 1, stdout);
|
|
||||||
return status::success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct summary
|
|
||||||
{
|
|
||||||
std::size_t total{ 0 };
|
|
||||||
std::size_t failure{ 0 };
|
|
||||||
std::size_t success{ 0 };
|
|
||||||
};
|
|
||||||
|
|
||||||
void walk()
|
|
||||||
{
|
|
||||||
auto directory_stream = opendir("./tests");
|
|
||||||
dirent *file_entry;
|
|
||||||
|
|
||||||
summary test_summary;
|
|
||||||
|
|
||||||
while ((file_entry = readdir(directory_stream)) != nullptr)
|
|
||||||
{
|
|
||||||
if (file_entry->d_name[0] == '.')
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++test_summary.total;
|
|
||||||
switch (run_test(file_entry->d_name))
|
|
||||||
{
|
|
||||||
case status::failure:
|
|
||||||
++test_summary.failure;
|
|
||||||
break;
|
|
||||||
case status::success:
|
|
||||||
++test_summary.success;
|
|
||||||
break;
|
|
||||||
case status::warning:
|
|
||||||
break;
|
|
||||||
case status::fatal:
|
|
||||||
goto end_walk;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("Successful: %lu, Failed: %lu, Total: %lu.\n",
|
|
||||||
test_summary.success, test_summary.failure, test_summary.total);
|
|
||||||
end_walk:
|
|
||||||
closedir(directory_stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
auto dev_console = open("/dev/console", O_WRONLY);
|
|
||||||
if (dev_console != -1)
|
|
||||||
{
|
|
||||||
dup2(dev_console, STDOUT_FILENO);
|
|
||||||
walk();
|
|
||||||
close(dev_console);
|
|
||||||
}
|
|
||||||
sync();
|
|
||||||
reboot(RB_POWER_OFF);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user