Remove libc dependency for the generated code
This commit is contained in:
		| @@ -116,12 +116,10 @@ namespace elna::riscv | ||||
|     static void generate_intrinsics(std::shared_ptr<source::writer<std::byte>> writer, | ||||
|             std::vector<reference>& references) | ||||
|     { | ||||
|         writer->sink("printf"); | ||||
|         { | ||||
|             std::vector<instruction> instructions; | ||||
|             auto format_string = writer->sink(reinterpret_cast<const std::byte *>("%c\n\0"), 4); | ||||
|  | ||||
|             prologue(instructions); | ||||
|  | ||||
|             instructions.push_back(instruction(base_opcode::opImm) | ||||
|                     .i(x_register::a1, funct3_t::addi, x_register::zero, 't')); | ||||
|             instructions.push_back(instruction(base_opcode::branch) | ||||
| @@ -129,42 +127,68 @@ namespace elna::riscv | ||||
|             instructions.push_back(instruction(base_opcode::opImm) | ||||
|                     .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) | ||||
|                     .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::auipc).u(x_register::ra, 0)); | ||||
|             instructions.push_back(instruction(base_opcode::jalr) | ||||
|                     .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); | ||||
|             instructions.push_back(instruction(base_opcode::opImm) | ||||
|                     .i(x_register::t1, funct3_t::addi, x_register::zero, '\n')); | ||||
|             instructions.push_back(instruction(base_opcode::store) | ||||
|                     .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()), | ||||
|                     instructions.size() * sizeof(instruction)); | ||||
|         } | ||||
|         { | ||||
|             std::vector<instruction> instructions; | ||||
|             auto format_string = writer->sink(reinterpret_cast<const std::byte *>("%d\n\0"), 4); | ||||
|  | ||||
|             prologue(instructions); | ||||
|  | ||||
|             instructions.push_back(instruction(base_opcode::opImm) | ||||
|                     .i(x_register::a1, 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); | ||||
|                     .i(x_register::t0, funct3_t::addi, x_register::a0, 0)); | ||||
|             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::auipc).u(x_register::ra, 0)); | ||||
|             instructions.push_back(instruction(base_opcode::jalr) | ||||
|                     .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); | ||||
|             instructions.push_back(instruction(base_opcode::opImm) | ||||
|                     .i(x_register::t1, funct3_t::addi, x_register::zero, '\n')); | ||||
|             instructions.push_back(instruction(base_opcode::store) | ||||
|                     .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()), | ||||
|                     instructions.size() * sizeof(instruction)); | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <cstdint> | ||||
| #include <string> | ||||
| #include <filesystem> | ||||
|  | ||||
| #define BOOST_PROCESS_USE_STD_FS | ||||
| @@ -39,7 +38,6 @@ namespace elna | ||||
|             boost::asio::readable_pipe& read_pipe); | ||||
|     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); | ||||
|     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); | ||||
|     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); | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
|   "main": "tools/index.js", | ||||
|   "type": "module", | ||||
|   "scripts": { | ||||
|     "start": "node tools/index.js" | ||||
|     "cross": "node tools/cross.js" | ||||
|   }, | ||||
|   "author": "Eugen Wissner <belka@caraus.de>", | ||||
|   "license": "MPL-2.0" | ||||
|   | ||||
| @@ -165,7 +165,7 @@ namespace elna::source | ||||
|         this->current.emplace_back(quadruple_operator::start); | ||||
|         program->body().accept(this); | ||||
|         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) | ||||
|   | ||||
| @@ -44,7 +44,7 @@ namespace elna::source | ||||
|     void name_analysis_visitor::visit(program *program) | ||||
|     { | ||||
|         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); | ||||
|     } | ||||
|  | ||||
| @@ -105,7 +105,7 @@ namespace elna::source | ||||
|         this->argument_offset = 0; | ||||
|  | ||||
|         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); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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 <iostream> | ||||
| #include <fstream> | ||||
| #include <algorithm> | ||||
| #include <boost/process/env.hpp> | ||||
|  | ||||
| namespace elna | ||||
| @@ -105,22 +104,6 @@ namespace elna | ||||
|         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) | ||||
|     { | ||||
|         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::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.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"), | ||||
|                     linker_arguments | ||||
| @@ -248,7 +221,7 @@ namespace elna | ||||
|             "-nographic", | ||||
|             "-M", "virt", | ||||
|             "-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", | ||||
|             "-initrd", "build/root.cpio" | ||||
|         }; | ||||
|   | ||||
| @@ -7,45 +7,57 @@ 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 kernelVersion = '5.15.159' | ||||
| const gccVersion = '14.1.0' | ||||
| const binutilsVersion = '2.42' | ||||
| const glibcVersion = '2.39' | ||||
| 
 | ||||
| function findBuildTarget () { | ||||
| async function gccVerbose (gccBinary) { | ||||
|   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 = [] | ||||
| 
 | ||||
|   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) | ||||
|       resolve(Buffer.concat(buffers).toString()) | ||||
|     }) | ||||
|   }) | ||||
| } | ||||
| 
 | ||||
| function createImage (rootfs) { | ||||
|   const rootExt4 = path.join(tmp, 'rootfs.ext4') | ||||
| async function findBuildTarget (gccVersion) { | ||||
|   let gccBinary = 'gcc' | ||||
|   let output = await gccVerbose(gccBinary) | ||||
| 
 | ||||
|   childProcess.execFileSync('dd', ['if=/dev/zero', `of=${rootExt4}`, 'bs=1M', 'count=1024'], { stdio: 'inherit' }) | ||||
|   childProcess.execFileSync('/sbin/mkfs.ext4', ['-d', rootfs, rootExt4], { stdio: 'inherit' }) | ||||
|   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 | ||||
|       } | ||||
|     }, {}) | ||||
| } | ||||
| 
 | ||||
| async function downloadAndUnarchive (url) { | ||||
| @@ -89,70 +101,70 @@ async function downloadAndUnarchive (url) { | ||||
|   }) | ||||
| } | ||||
| 
 | ||||
| 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 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 | ||||
| } | ||||
| 
 | ||||
| async function buildKernel (sysroot, rootfs) { | ||||
| async function buildKernel (rootfs, options) { | ||||
|   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}-`, | ||||
|     CROSS_COMPILE: `${options.target}-`, | ||||
|     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', ['-j', os.availableParallelism()], { 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 }) | ||||
| 
 | ||||
|   return path.join(cwd, 'arch/riscv/boot/Image') | ||||
| } | ||||
| 
 | ||||
| async function buildInit (sysroot) { | ||||
| async function buildInit (rootfs, options) { | ||||
|   const env = { | ||||
|     ...process.env, | ||||
|     PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}` | ||||
|     PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}` | ||||
|   } | ||||
|   const compilerArguments = [ | ||||
|     '-ffreestanding', '-static', | ||||
|     '-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) { | ||||
|   const sourceDirectory = await downloadAndUnarchive( | ||||
|     new URL(`https://ftp.gnu.org/gnu/glibc/glibc-${glibcVersion}.tar.xz`) | ||||
|   ) | ||||
| async function buildGlibc (sourceDirectory, rootfs, options) { | ||||
|   const configureOptions = [ | ||||
|     '--prefix=/usr', | ||||
|     '--libdir=/usr/lib', | ||||
|     `--host=${target}`, | ||||
|     `--host=${options.target}`, | ||||
|     `--target=${options.target}`, | ||||
|     `--build=${options.build}`, | ||||
|     `--enable-kernel=${kernelVersion}`, | ||||
|     `--with-headers=${path.join(rootfs, 'usr/include')}`, | ||||
|     '--disable-nscd' | ||||
|     `--with-headers=${path.join(options.sysroot, 'usr/include')}`, | ||||
|     '--disable-nscd', | ||||
|     '--disable-libquadmath', | ||||
|     '--disable-libitm', | ||||
|     '--disable-werror', | ||||
|     'libc_cv_forced_unwind=yes' | ||||
|   ] | ||||
|   const bin = path.join(rootfs, 'bin') | ||||
|   const 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'); | ||||
|   await fs.mkdir(cwd) | ||||
| @@ -163,35 +175,42 @@ async function buildGlibc (sysroot, rootfs, options) { | ||||
|     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( | ||||
|     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 env = { | ||||
|     ...process.env, | ||||
|     CC: options.gcc, | ||||
|     CXX: options.gxx | ||||
|   } | ||||
|   const configureOptions = [ | ||||
|     `--prefix=${sysroot}`, | ||||
|     `--target=${target}`, | ||||
|     `--prefix=${rootfs}`, | ||||
|     `--target=${options.target}`, | ||||
|     '--disable-nls', | ||||
|     '--enable-gprofng=no', | ||||
|     '--disable-werror', | ||||
|     '--enable-default-hash-style=gnu' | ||||
|     '--enable-default-hash-style=gnu', | ||||
|     '--disable-libquadmath' | ||||
|   ] | ||||
|   childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, { | ||||
|     stdio: 'inherit', | ||||
|     cwd | ||||
|     cwd, | ||||
|     env | ||||
|   }) | ||||
|   childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', cwd }) | ||||
|   childProcess.execFileSync('make', ['install'], { stdio: 'inherit', cwd }) | ||||
|   childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', cwd, env }) | ||||
|   childProcess.execFileSync('make', ['install'], { stdio: 'inherit', cwd, env }) | ||||
| 
 | ||||
|   return sourceDirectory | ||||
| } | ||||
| 
 | ||||
| async function buildGCC1 (sysroot, rootfs, options) { | ||||
| async function buildGCC1 (rootfs, options) { | ||||
|   const sourceDirectory = await downloadAndUnarchive( | ||||
|     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 | ||||
|   }) | ||||
|   const configureOptions = [ | ||||
|     `--prefix=${sysroot}`, | ||||
|     `--with-sysroot=${rootfs}`, | ||||
|     `--prefix=${rootfs}`, | ||||
|     `--with-sysroot=${options.sysroot}`, | ||||
|     '--enable-languages=c,c++', | ||||
|     '--disable-shared', | ||||
|     '--with-arch=rv32imafdc', | ||||
| @@ -225,16 +244,18 @@ async function buildGCC1 (sysroot, rootfs, options) { | ||||
|     '--disable-nls', | ||||
|     '--with-newlib', | ||||
|     '--without-headers', | ||||
|     `--target=${target}`, | ||||
|     `--target=${options.target}`, | ||||
|     `--build=${options.build}`, | ||||
|     `--host=${options.build}` | ||||
|   ] | ||||
|   const flags = '-O2 -fPIC' | ||||
|   const env = { | ||||
|     ...process.env, | ||||
|     CC: options.gcc, | ||||
|     CXX: options.gxx, | ||||
|     CFLAGS: 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, { | ||||
|     stdio: 'inherit', | ||||
| @@ -247,45 +268,14 @@ async function buildGCC1 (sysroot, rootfs, options) { | ||||
|   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) { | ||||
| async function buildGCC2 (sourceDirectory, 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}`, | ||||
|     `--prefix=${rootfs}`, | ||||
|     `--with-sysroot=${options.sysroot}`, | ||||
|     '--enable-languages=c,c++,lto', | ||||
|     '--enable-lto', | ||||
|     '--enable-shared', | ||||
| @@ -297,10 +287,11 @@ async function buildGCC2 (sourceDirectory, sysroot, rootfs, options) { | ||||
|     '--disable-multilib', | ||||
|     '--enable-checking=release', | ||||
|     '--disable-libssp', | ||||
|     '--disable-libquadmath', | ||||
|     '--enable-threads=posix', | ||||
|     '--with-default-libstdcxx-abi=new', | ||||
|     '--disable-nls', | ||||
|     `--target=${target}`, | ||||
|     `--target=${options.target}`, | ||||
|     `--build=${options.build}`, | ||||
|     `--host=${options.build}` | ||||
|   ] | ||||
| @@ -309,7 +300,7 @@ async function buildGCC2 (sourceDirectory, sysroot, rootfs, options) { | ||||
|     ...process.env, | ||||
|     CFLAGS: 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, { | ||||
|     stdio: 'inherit', | ||||
| @@ -320,83 +311,25 @@ async function buildGCC2 (sourceDirectory, sysroot, rootfs, options) { | ||||
|   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() | ||||
| } | ||||
| const options = await findBuildTarget(gccVersion) | ||||
| 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.mkdir(targetDirectory) | ||||
| } | ||||
| 
 | ||||
| const binutilsSource = await buildCrossBinutils(sysroot) | ||||
| const gccSource = await buildGCC1(sysroot, rootfs, options) | ||||
| const binutilsSource = await buildCrossBinutils(rootfs, options) | ||||
| const gccSource = await buildGCC1(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) | ||||
| const glibcSource = await copyGlibcHeaders() | ||||
| const kernelImage = await buildKernel(rootfs, options) | ||||
| await buildGlibc(glibcSource, rootfs, options) | ||||
| await buildGCC2(gccSource, rootfs, options) | ||||
| buildInit (rootfs, options) | ||||
| 
 | ||||
| await buildBusyBox(sysroot, rootfs) | ||||
| await createRoot(rootfs) | ||||
| 
 | ||||
| await buildGCC3(gccSource, sysroot, rootfs, options) | ||||
| createImage(rootfs) | ||||
| 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; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user