From 8087f7ea78df6e92f9419e42b5766cfe6db3b9f2 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 20 Dec 2024 13:22:28 +0000 Subject: [PATCH 01/14] Add start of nbuild tool. --- .cargo/config.toml | 2 + .gitignore | 1 + .vscode/settings.json | 5 +- Cargo.lock | 2 +- Cargo.toml | 3 + README.md | 59 +++-------- nbuild/Cargo.lock | 237 ++++++++++++++++++++++++++++++++++++++++++ nbuild/Cargo.toml | 15 +++ nbuild/README.md | 12 +++ nbuild/src/lib.rs | 69 ++++++++++++ nbuild/src/main.rs | 103 ++++++++++++++++++ 11 files changed, 463 insertions(+), 45 deletions(-) create mode 100644 .cargo/config.toml create mode 100644 nbuild/Cargo.lock create mode 100644 nbuild/Cargo.toml create mode 100644 nbuild/README.md create mode 100644 nbuild/src/lib.rs create mode 100644 nbuild/src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..75f3c33 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +nbuild = "run --manifest-path nbuild/Cargo.toml --" diff --git a/.gitignore b/.gitignore index dd9db28..1726119 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/nbuild/target /target **/*.rs.bk /release diff --git a/.vscode/settings.json b/.vscode/settings.json index d07faa6..b342773 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,7 @@ { "rust-analyzer.checkOnSave.allTargets": false, - "rust-analyzer.cargo.target": "thumbv6m-none-eabi" + "rust-analyzer.linkedProjects": [ + "./Cargo.toml", + "./nbuild/Cargo.toml" + ] } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 29b287c..f9116c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "arrayvec" diff --git a/Cargo.toml b/Cargo.toml index 9fca006..2b7a204 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,9 @@ members = [ "utilities/flames", ] resolver = "2" +exclude = [ + "nbuild" +] [workspace.dependencies] neotron-sdk = "0.2.0" diff --git a/README.md b/README.md index 176cdde..2937c2f 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ This OS is a work in progress. We intend to support: * [x] Executing applications from RAM * [x] Applications can print to stdout * [x] Applications can read from stdin - * [ ] Applications can open/close/read/write files + * [x] Applications can open/close/read files + * [ ] Applications can write to files * [x] MBR/FAT32 formatted block devices * [x] Read blocks * [x] Directory listing of / @@ -22,6 +23,7 @@ This OS is a work in progress. We intend to support: * [ ] Delete files * [ ] Change directory * [x] Load ELF binaries from disk +* [x] Load ELF binaries from ROM * [x] Changing text modes * [ ] Basic networking * [x] Music playback @@ -35,50 +37,13 @@ Your board will need an appropriate Neotron BIOS installed, and you need to have OpenOCD or some other programming tool running for your particular board. See your BIOS instructions for more details. -We compile one version of Neotron OS, but we link it three times to produce -three different binaries: +Building Neotron OS is handled by the `nbuild` tool, in this repository. -* `flash0002` - is linked to run from address `0x0002_0000` -* `flash1002` - is linked to run from address `0x1002_0000` -* `flash0802` - is linked to run from address `0x0802_0000` +Run `cargo nbuild help` for more information. -```console -$ git clone https://github.com/neotron-compute/Neotron-OS.git -$ cd Neotron-OS -$ cargo build --target thumbv6m-none-eabi --release --bins -$ ls ./target/thumbv6m-none-eabi/release/flash*02 -./target/thumbv6m-none-eabi/release/flash0002 ./target/thumbv6m-none-eabi/release/flash0802 ./target/thumbv6m-none-eabi/release/flash1002 -``` - -Your BIOS should tell you which one you want and how to load it onto your system. +Your BIOS should tell you which options to pass, and how to load the resulting image onto your system. -You can also build a *shared object* to load into a Windows/Linux/macOS application. - -```console -$ cargo build --lib -$ ls ./target/debug/*.so -./target/debug/libneotron_os.so -``` - -If you want to include a ROMFS, you need to: - -```bash -cargo install neotron-romfs-lsfs -cargo install neotron-romfs-mkfs -cargo install cargo-binutils -``` - -A bunch of utilities are supplied in the [`utilities`](./utilities/) folder. Build them all, and make a ROMFS image, then build the OS with the `ROMFS_PATH` environment variable set. - -```bash -TGT=$(pwd)/target/thumbv6m-none-eabi/release -cargo build --bin flames --target thumbv6m-none-eabi --release -rust-strip ${TGT}/flames -o ${TGT}/flames.elf -neotron-romfs-mkfs ${TGT}/flames.elf > ${TGT}/romfs.img -ROMFS_PATH=${TGT}/romfs.img cargo build --bin flash1002 --target thumbv6m-none-eabi --release -``` - -The OS will then include the ROMFS image, which you can access with the `rom` command. +Programs in the ROMFS can be loaded with: ```text > rom @@ -93,6 +58,14 @@ Loading 4908 bytes to 0x200022b4 A better UI for loading files from ROM is being planned (maybe we should have drive letters, and the ROM can be `R:`). +You can also build a *shared object* to load into a Windows/Linux/macOS application, like [Neotron Desktop BIOS](https://github.com/neotron-compute/neotron-desktop-bios): + +```console +$ cargo nbuild library +$ ls ./target/debug/*.so +./target/debug/libneotron_os.so +``` + ## Changelog See [`CHANGELOG.md`](./CHANGELOG.md) @@ -100,7 +73,7 @@ See [`CHANGELOG.md`](./CHANGELOG.md) ## Licence ```text -Neotron-OS Copyright (c) Jonathan 'theJPster' Pallant and The Neotron Developers, 2023 +Copyright (c) 2019-2024 Jonathan 'theJPster' Pallant and The Neotron Developers This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/nbuild/Cargo.lock b/nbuild/Cargo.lock new file mode 100644 index 0000000..611d957 --- /dev/null +++ b/nbuild/Cargo.lock @@ -0,0 +1,237 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "clap" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "nbuild" +version = "0.1.0" +dependencies = [ + "clap", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/nbuild/Cargo.toml b/nbuild/Cargo.toml new file mode 100644 index 0000000..3de7b85 --- /dev/null +++ b/nbuild/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "nbuild" +version = "0.1.0" +edition = "2021" +authors = [ + "Jonathan 'theJPster' Pallant ", + "The Neotron Developers" +] +description = "The Neotron Operating System Build System" +license = "GPL-3.0-or-later" +readme = "README.md" +repository = "https://github.com/neotron-compute/Neotron-OS" + +[dependencies] +clap = { version = "4.5.23", features = ["derive"] } diff --git a/nbuild/README.md b/nbuild/README.md new file mode 100644 index 0000000..3f99f91 --- /dev/null +++ b/nbuild/README.md @@ -0,0 +1,12 @@ +# nbuild - the Neotron OS Build System + +Building Neotron OS involves: + +* Compiling the Neotron OS source code +* Compiling and linking multiple Neotron OS utilities +* Assembling the utilities into a ROMFS image +* Linking the Neotron OS object code, including the ROMFS image + +This utility automates that process. + +Run `cargo nbuild --help` from the top level of the checkout for more information. diff --git a/nbuild/src/lib.rs b/nbuild/src/lib.rs new file mode 100644 index 0000000..22119b0 --- /dev/null +++ b/nbuild/src/lib.rs @@ -0,0 +1,69 @@ +//! Utility functions + +/// The ways that spawning `cargo` can fail +#[derive(Debug)] +pub enum CargoError { + SpawnError(std::io::Error), + RunError(std::process::ExitStatus), +} + +impl std::fmt::Display for CargoError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CargoError::SpawnError(error) => write!(f, "Failed to spawn `cargo`: {}", error), + CargoError::RunError(exit_status) => write!( + f, + "Failed to complete `cargo` command ({}). There should be an error above", + exit_status + ), + } + } +} + +/// Parse an integer, with an optional `0x` prefix. +/// +/// Underscores are ignored. +/// +/// ```rust +/// # use nbuild::parse_int; +/// assert_eq!(parse_int("0x0000_000A"), Ok(10)); +/// assert_eq!(parse_int("000_10"), Ok(10)); +/// ``` +pub fn parse_int(input: S) -> Result +where + S: AsRef, +{ + let input = input.as_ref().replace('_', ""); + if let Some(suffix) = input.strip_prefix("0x") { + u32::from_str_radix(suffix, 16) + } else { + u32::from_str_radix(&input, 10) + } +} + +/// Runs cargo +pub fn cargo

(commands: &[&str], target: Option<&str>, manifest_path: P) -> Result<(), CargoError> +where + P: AsRef, +{ + let mut command_line = std::process::Command::new("cargo"); + command_line.stdout(std::process::Stdio::inherit()); + command_line.stderr(std::process::Stdio::inherit()); + command_line.args(commands); + if let Some(target) = target { + command_line.arg("--target"); + command_line.arg(target); + } + command_line.arg("--manifest-path"); + command_line.arg(manifest_path.as_ref()); + + println!("Running: {:?}", command_line); + + let output = command_line.output().map_err(CargoError::SpawnError)?; + + if output.status.success() { + Ok(()) + } else { + Err(CargoError::RunError(output.status)) + } +} diff --git a/nbuild/src/main.rs b/nbuild/src/main.rs new file mode 100644 index 0000000..85a10c9 --- /dev/null +++ b/nbuild/src/main.rs @@ -0,0 +1,103 @@ +//! A series of utilities for building Neotron OS + +use clap::{Parser, Subcommand}; + +#[derive(Debug, Subcommand)] +enum Commands { + /// Builds the OS and the ROMFS + Binary { + /// The start address in Flash where Neotron OS should live + #[clap(long, default_value = "0x1000_0000")] + start_address: String, + /// The target we're building Neotron OS for + #[clap(long, default_value = "thumbv6m-none-eabi")] + target: String, + }, + /// Builds the OS as a library, for the native machine + Library { + /// The target we're building Neotron OS for + #[clap(long)] + target: Option, + }, + /// Handles formatting of the Neotron OS source code + Format { + /// Whether to just check the formatting + #[clap(long)] + check: bool, + }, +} + +/// A simple utility for building Neotron OS and a suitable ROMFS image +#[derive(Debug, Parser)] +#[clap(name = "nbuild", version = "0.1.0", author = "The Neotron Developers")] +pub struct NBuildApp { + /// The task to perform + #[command(subcommand)] + command: Option, +} + +fn main() { + println!("Neotron OS nbuild tool"); + let args = NBuildApp::parse(); + match args.command { + None => { + // No command given + println!("No command given. Try `cargo nbuild help`."); + std::process::exit(1); + } + Some(Commands::Binary { + start_address, + target, + }) => { + let Ok(start_address) = nbuild::parse_int(&start_address) else { + eprintln!("{:?} was not a valid integer", start_address); + std::process::exit(1); + }; + println!( + "Cross-compiling Neotron OS binaries, using start address 0x{:08x} and target {:?}", + start_address, target + ); + if let Err(e) = nbuild::cargo( + &["build", "--bins", "--release"], + Some(&target), + "./neotron-os/Cargo.toml", + ) { + eprintln!("Build failed: {}", e); + std::process::exit(1); + } + } + Some(Commands::Library { target }) => { + let target = target.as_deref(); + println!( + "Compiling Neotron OS library, using target {:?}", + target.unwrap_or("native") + ); + if let Err(e) = nbuild::cargo(&["build", "--lib"], target, "./neotron-os/Cargo.toml") { + eprintln!("Build failed: {}", e); + std::process::exit(1); + } + } + Some(Commands::Format { check }) => { + let mut is_error = false; + let commands = if check { + vec!["fmt", "--check"] + } else { + vec!["fmt"] + }; + println!("Formatting Neotron OS"); + if let Err(e) = nbuild::cargo(&commands, None, "./neotron-os/Cargo.toml") { + eprintln!("Format failed: {}", e); + is_error = true; + } + if let Err(e) = nbuild::cargo(&commands, None, "./nbuild/Cargo.toml") { + eprintln!("Format failed: {}", e); + is_error = true; + } + if is_error { + std::process::exit(1); + } + } + } +} + +// End of file From 2587e64fde1ed44573d9681a88b291ada478a457 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 20 Dec 2024 17:27:28 +0000 Subject: [PATCH 02/14] Use nbuild format in CI --- .github/workflows/format.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index ac616f8..5621c55 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -14,4 +14,5 @@ jobs: - name: Add Tool run: rustup component add rustfmt - name: Check Format - run: cargo fmt -- --check + run: cargo nbuild format --check + From 4a18fd65eeb8de7adf494f5d74ad7774bb628663 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 20 Dec 2024 17:27:18 +0000 Subject: [PATCH 03/14] Let nbuild set the start address to anything --- .github/workflows/rust.yml | 118 +++++------ nbuild/src/lib.rs | 35 +++- nbuild/src/main.rs | 185 +++++++++++++----- neotron-os/Cargo.toml | 12 +- neotron-os/build.rs | 24 +-- neotron-os/neotron-flash-0802.ld | 159 --------------- neotron-os/neotron-flash-1002.ld | 163 --------------- ...eotron-flash-0002.ld => neotron-os-arm.ld} | 4 +- neotron-os/src/bin/flash0002.rs | 18 -- neotron-os/src/bin/flash0802.rs | 18 -- neotron-os/src/{bin/flash1002.rs => main.rs} | 0 11 files changed, 249 insertions(+), 487 deletions(-) delete mode 100644 neotron-os/neotron-flash-0802.ld delete mode 100644 neotron-os/neotron-flash-1002.ld rename neotron-os/{neotron-flash-0002.ld => neotron-os-arm.ld} (96%) delete mode 100644 neotron-os/src/bin/flash0002.rs delete mode 100644 neotron-os/src/bin/flash0802.rs rename neotron-os/src/{bin/flash1002.rs => main.rs} (100%) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4ecb7e5..ea8d7a2 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -3,78 +3,82 @@ name: Build on: [push, pull_request] jobs: - build: - name: Build (and Release) + binaries: + name: Build Binaries + strategy: + matrix: + target: [thumbv6m-none-eabi, thumbv7em-none-eabi, thumbv8m.main-none-eabi] + start_address: [0x0802_0000, 0x1002_0000] + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + - run: | + rustup target add ${{ matrix.target }} + cargo nbuild binaries --target=${{ matrix.target }} --start-address=${{ matrix.start_address }} + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + if: ${{success()}} + with: + name: ${{ matrix.target }}-${{ matrix.start_address }}-binaries + if-no-files-found: error + path: | + ./target/${{ matrix.target }}/release/neotron-os + linux-libraries: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - - - name: Check Syntax - run: | - cargo check - - - name: Test - run: | - cargo test --lib - - - name: Install Targets and Tools - run: | - rustup toolchain install stable --profile minimal --no-self-update - rustup default stable - rustup target add thumbv7em-none-eabi - rustup target add thumbv7m-none-eabi - rustup target add thumbv6m-none-eabi - rustup component add llvm-tools-preview - echo CARGO_INCREMENTAL=0 >> $GITHUB_ENV - echo CARGO_TERM_COLOR=always >> $GITHUB_ENV - - - name: Install tools - uses: taiki-e/install-action@v2 + - name: Build + run: cargo nbuild libraries + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + if: ${{success()}} with: - tool: cargo-binutils@0.3.6 - - - name: Install ROMFS tools - run: | - cargo install neotron-romfs-lsfs - cargo install neotron-romfs-mkfs - + name: linux-libraries + if-no-files-found: error + path: | + ./target/release/libneotron_os.so + windows-libraries: + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true - name: Build - run: | - ./build.sh --verbose - + run: cargo nbuild libraries - name: Upload Artifacts uses: actions/upload-artifact@v4 if: ${{success()}} with: - name: Artifacts + name: windows-libraries if-no-files-found: error path: | - ./release/ - + ./target/release/neotron_os.dll + run-tests: + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - run: cargo nbuild test + release: + name: Upload Release + needs: [binaries, linux-libraries, windows-libraries] + if: github.event_name == 'push' && startswith(github.ref, 'refs/tags/') + steps: + - name: Make release area + run: | + mkdir ./release + - name: Download Artifacts + uses: actions/download-artifact@v4 + with: + path: ./release - name: Upload files to Release - if: github.event_name == 'push' && startswith(github.ref, 'refs/tags/') uses: softprops/action-gh-release@v1 with: files: | - ./release/thumbv6m-none-eabi-flash0002-libneotron_os.bin - ./release/thumbv6m-none-eabi-flash0002-libneotron_os.elf - ./release/thumbv6m-none-eabi-flash0802-libneotron_os.bin - ./release/thumbv6m-none-eabi-flash0802-libneotron_os.elf - ./release/thumbv6m-none-eabi-flash1002-libneotron_os.bin - ./release/thumbv6m-none-eabi-flash1002-libneotron_os.elf - ./release/thumbv7em-none-eabi-flash0002-libneotron_os.bin - ./release/thumbv7em-none-eabi-flash0002-libneotron_os.elf - ./release/thumbv7em-none-eabi-flash0802-libneotron_os.bin - ./release/thumbv7em-none-eabi-flash0802-libneotron_os.elf - ./release/thumbv7em-none-eabi-flash1002-libneotron_os.bin - ./release/thumbv7em-none-eabi-flash1002-libneotron_os.elf - ./release/thumbv7m-none-eabi-flash0002-libneotron_os.bin - ./release/thumbv7m-none-eabi-flash0002-libneotron_os.elf - ./release/thumbv7m-none-eabi-flash0802-libneotron_os.bin - ./release/thumbv7m-none-eabi-flash0802-libneotron_os.elf - ./release/thumbv7m-none-eabi-flash1002-libneotron_os.bin - ./release/thumbv7m-none-eabi-flash1002-libneotron_os.elf - ./release/x86_64-unknown-linux-gnu-libneotron_os.so + ./release/* diff --git a/nbuild/src/lib.rs b/nbuild/src/lib.rs index 22119b0..da5e2ee 100644 --- a/nbuild/src/lib.rs +++ b/nbuild/src/lib.rs @@ -20,6 +20,23 @@ impl std::fmt::Display for CargoError { } } +/// The kinds of package we have +#[derive(Debug, PartialEq, Eq)] +pub enum PackageKind { + Os, + Utility, + NBuild, +} + +/// Describes a package in this repository +#[derive(Debug)] +pub struct Package { + pub name: &'static str, + pub path: &'static std::path::Path, + pub output: &'static std::path::Path, + pub kind: PackageKind, +} + /// Parse an integer, with an optional `0x` prefix. /// /// Underscores are ignored. @@ -37,12 +54,25 @@ where if let Some(suffix) = input.strip_prefix("0x") { u32::from_str_radix(suffix, 16) } else { - u32::from_str_radix(&input, 10) + input.parse() } } /// Runs cargo pub fn cargo

(commands: &[&str], target: Option<&str>, manifest_path: P) -> Result<(), CargoError> +where + P: AsRef, +{ + cargo_with_env(commands, target, manifest_path, &[]) +} + +/// Runs cargo with extra environment variables +pub fn cargo_with_env

( + commands: &[&str], + target: Option<&str>, + manifest_path: P, + environment: &[(&'static str, String)], +) -> Result<(), CargoError> where P: AsRef, { @@ -56,6 +86,9 @@ where } command_line.arg("--manifest-path"); command_line.arg(manifest_path.as_ref()); + for (k, v) in environment.into_iter() { + command_line.env(k, v); + } println!("Running: {:?}", command_line); diff --git a/nbuild/src/main.rs b/nbuild/src/main.rs index 85a10c9..31a88c4 100644 --- a/nbuild/src/main.rs +++ b/nbuild/src/main.rs @@ -7,14 +7,14 @@ enum Commands { /// Builds the OS and the ROMFS Binary { /// The start address in Flash where Neotron OS should live - #[clap(long, default_value = "0x1000_0000")] + #[clap(long, default_value = "0x1002_0000")] start_address: String, /// The target we're building Neotron OS for #[clap(long, default_value = "thumbv6m-none-eabi")] target: String, }, /// Builds the OS as a library, for the native machine - Library { + Libraries { /// The target we're building Neotron OS for #[clap(long)] target: Option, @@ -25,6 +25,8 @@ enum Commands { #[clap(long)] check: bool, }, + /// Checks the Neotron OS source code using clippy + Clippy, } /// A simple utility for building Neotron OS and a suitable ROMFS image @@ -36,9 +38,33 @@ pub struct NBuildApp { command: Option, } +fn packages() -> Vec { + vec![ + nbuild::Package { + name: "nbuild", + path: std::path::Path::new("./nbuild/Cargo.toml"), + output: std::path::Path::new("./nbuild/target/debug/nbuild{exe}"), + kind: nbuild::PackageKind::NBuild, + }, + nbuild::Package { + name: "flames utility", + path: std::path::Path::new("./utilities/flames/Cargo.toml"), + output: std::path::Path::new("./target/{target}/{profile}/flames"), + kind: nbuild::PackageKind::Utility, + }, + nbuild::Package { + name: "Neotron OS", + path: std::path::Path::new("./neotron-os/Cargo.toml"), + output: std::path::Path::new("./target/{target}/{profile}/neotron-os"), + kind: nbuild::PackageKind::Os, + }, + ] +} + fn main() { println!("Neotron OS nbuild tool"); let args = NBuildApp::parse(); + let packages = packages(); match args.command { None => { // No command given @@ -49,55 +75,120 @@ fn main() { start_address, target, }) => { - let Ok(start_address) = nbuild::parse_int(&start_address) else { - eprintln!("{:?} was not a valid integer", start_address); - std::process::exit(1); - }; - println!( - "Cross-compiling Neotron OS binaries, using start address 0x{:08x} and target {:?}", - start_address, target - ); - if let Err(e) = nbuild::cargo( - &["build", "--bins", "--release"], - Some(&target), - "./neotron-os/Cargo.toml", - ) { - eprintln!("Build failed: {}", e); - std::process::exit(1); - } + binary(&packages, &start_address, &target); + } + Some(Commands::Libraries { target }) => library(&packages, target.as_deref()), + Some(Commands::Format { check }) => format(&packages, check), + Some(Commands::Clippy) => clippy(&packages), + } +} + +/// Builds the utility and OS packages as binaries +fn binary(packages: &[nbuild::Package], start_address: &str, target: &str) { + let mut is_error = false; + let Ok(start_address) = nbuild::parse_int(start_address) else { + eprintln!("{:?} was not a valid integer", start_address); + std::process::exit(1); + }; + for package in packages + .iter() + .filter(|p| p.kind == nbuild::PackageKind::Utility) + { + println!( + "Cross-compiling {}, using target {:?}", + package.name, target + ); + if let Err(e) = nbuild::cargo(&["build", "--release"], Some(target), package.path) { + eprintln!("Build of {} failed: {}", package.name, e); + is_error = true; + } + } + for package in packages + .iter() + .filter(|p| p.kind == nbuild::PackageKind::Os) + { + println!( + "Cross-compiling {}, using start address 0x{:08x} and target {:?}", + package.name, start_address, target + ); + let environment = [( + "NEOTRON_OS_START_ADDRESS", + format!("0x{:08x}", start_address), + )]; + if let Err(e) = nbuild::cargo_with_env( + &["build", "--release"], + Some(target), + package.path, + &environment, + ) { + eprintln!("Build of {} failed: {}", package.name, e); + is_error = true; + } + } + if is_error { + std::process::exit(1); + } +} + +/// Builds the OS packages as a library +fn library(packages: &[nbuild::Package], target: Option<&str>) { + let mut is_error = false; + println!( + "Compiling Neotron OS library, using target {:?}", + target.unwrap_or("native") + ); + for package in packages + .iter() + .filter(|p| p.kind == nbuild::PackageKind::Os) + { + println!( + "Compiling {}, target {:?}", + package.name, + target.unwrap_or("native") + ); + if let Err(e) = nbuild::cargo(&["build", "--lib"], target, package.path) { + eprintln!("Build of {} failed: {}", package.name, e); + is_error = true; } - Some(Commands::Library { target }) => { - let target = target.as_deref(); - println!( - "Compiling Neotron OS library, using target {:?}", - target.unwrap_or("native") - ); - if let Err(e) = nbuild::cargo(&["build", "--lib"], target, "./neotron-os/Cargo.toml") { - eprintln!("Build failed: {}", e); - std::process::exit(1); - } + } + if is_error { + std::process::exit(1); + } +} + +/// Runs `cargo fmt` over all the packages +fn format(packages: &[nbuild::Package], check: bool) { + let mut is_error = false; + let commands = if check { + vec!["fmt", "--check"] + } else { + vec!["fmt"] + }; + for package in packages.iter() { + println!("Formatting {}", package.name); + if let Err(e) = nbuild::cargo(&commands, None, package.path) { + eprintln!("Format failed: {}", e); + is_error = true; } - Some(Commands::Format { check }) => { - let mut is_error = false; - let commands = if check { - vec!["fmt", "--check"] - } else { - vec!["fmt"] - }; - println!("Formatting Neotron OS"); - if let Err(e) = nbuild::cargo(&commands, None, "./neotron-os/Cargo.toml") { - eprintln!("Format failed: {}", e); - is_error = true; - } - if let Err(e) = nbuild::cargo(&commands, None, "./nbuild/Cargo.toml") { - eprintln!("Format failed: {}", e); - is_error = true; - } - if is_error { - std::process::exit(1); - } + } + if is_error { + std::process::exit(1); + } +} + +/// Runs `cargo clippy` over all the packages +fn clippy(packages: &[nbuild::Package]) { + let mut is_error = false; + for package in packages.iter() { + println!("Linting {} with clippy", package.name); + if let Err(e) = nbuild::cargo(&["clippy"], None, package.path) { + eprintln!("Lint failed: {}", e); + is_error = true; } } + if is_error { + std::process::exit(1); + } } // End of file diff --git a/neotron-os/Cargo.toml b/neotron-os/Cargo.toml index ab7dc16..77c373f 100644 --- a/neotron-os/Cargo.toml +++ b/neotron-os/Cargo.toml @@ -12,17 +12,7 @@ readme = "README.md" repository = "https://github.com/neotron-compute/Neotron-OS" [[bin]] -name = "flash1002" -test = false -bench = false - -[[bin]] -name = "flash0802" -test = false -bench = false - -[[bin]] -name = "flash0002" +name = "neotron-os" test = false bench = false diff --git a/neotron-os/build.rs b/neotron-os/build.rs index a91e548..c685dab 100644 --- a/neotron-os/build.rs +++ b/neotron-os/build.rs @@ -1,13 +1,14 @@ use std::io::prelude::*; +const LINKER_SCRIPT: &str = "neotron-os-arm.ld"; + fn main() { - if let Ok("none") = std::env::var("CARGO_CFG_TARGET_OS").as_deref() { - copy_linker_script("neotron-flash-1002.ld"); - println!("cargo::rustc-link-arg-bin=flash1002=-Tneotron-flash-1002.ld"); - copy_linker_script("neotron-flash-0802.ld"); - println!("cargo::rustc-link-arg-bin=flash0802=-Tneotron-flash-0802.ld"); - copy_linker_script("neotron-flash-0002.ld"); - println!("cargo::rustc-link-arg-bin=flash0002=-Tneotron-flash-0002.ld"); + if Ok("none") == std::env::var("CARGO_CFG_TARGET_OS").as_deref() { + let start_address = std::env::var("NEOTRON_OS_START_ADDRESS"); + let start_address = start_address.as_deref().unwrap_or("0x10020000"); + copy_linker_script(start_address); + println!("cargo::rustc-link-arg-bin=neotron-os=-T{}", LINKER_SCRIPT); + println!("cargo::rerun-if-env-changed=NEOTRON_OS_START_ADDRESS"); } if let Ok(cmd_output) = std::process::Command::new("git") @@ -44,12 +45,13 @@ fn main() { /// Put the given script in our output directory and ensure it's on the linker /// search path. -fn copy_linker_script(path: &str) { +fn copy_linker_script(start_address: &str) { let out = &std::path::PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); - let contents = std::fs::read_to_string(path).expect("loading ld script"); - std::fs::File::create(out.join(path)) + let contents = std::fs::read_to_string(LINKER_SCRIPT).expect("loading ld script"); + let patched = contents.replace("${{start_address}}", start_address); + std::fs::File::create(out.join(LINKER_SCRIPT)) .unwrap() - .write_all(contents.as_bytes()) + .write_all(patched.as_bytes()) .unwrap(); println!("cargo::rustc-link-search={}", out.display()); } diff --git a/neotron-os/neotron-flash-0802.ld b/neotron-os/neotron-flash-0802.ld deleted file mode 100644 index 3a961ab..0000000 --- a/neotron-os/neotron-flash-0802.ld +++ /dev/null @@ -1,159 +0,0 @@ -/* # Developer notes - -- Symbols that start with a double underscore (__) are considered "private" - -- Symbols that start with a single underscore (_) are considered "semi-public"; they can be - overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { - static mut __sbss }`). - -- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a - symbol if not dropped if it appears in or near the front of the linker arguments and "it's not - needed" by any of the preceding objects (linker arguments) - -- `PROVIDE` is used to provide default values that can be overridden by a user linker script - -- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* - the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization - routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see - "Address (..) is out of bounds" in the disassembly produced by `objdump`. -*/ - -/* Provides information about the memory layout of the device */ -MEMORY -{ - /* The first 128 KiB is for the BIOS. We get the rest. */ - FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 256K - /* - * We get the bottom 4KB of RAM. Anything above that is for applications - * (up to wherever the BIOS tells us we can use.) - */ - RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 4K -} - -/* # Entry point = what the BIOS calls to start the OS */ -ENTRY(os_main); - -/* -Where the Transient Program Area starts. -*/ -_tpa_start = ORIGIN(RAM) + LENGTH(RAM); - -/* # Sections */ -SECTIONS -{ - - /* ## Sections in FLASH */ - .entry_point ORIGIN(FLASH) : - { - KEEP(*(.entry_point)) - } > FLASH - - PROVIDE(_stext = ADDR(.entry_point) + SIZEOF(.entry_point)); - - /* ### .text */ - .text _stext : - { - *(.text .text.*); - *(.HardFaultTrampoline); - *(.HardFault.*); - } > FLASH - - /* ### .rodata */ - .rodata : ALIGN(4) - { - *(.rodata .rodata.*); - - /* 4-byte align the end (VMA) of this section. - This is required by LLD to ensure the LMA of the following .data - section will have the correct alignment. */ - . = ALIGN(4); - } > FLASH - - /* ## Sections in RAM */ - /* ### .data */ - .data : ALIGN(4) - { - . = ALIGN(4); - __sdata = .; - *(.data .data.*); - . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ - __edata = .; - } > RAM AT > FLASH - - /* LMA of .data */ - __sidata = LOADADDR(.data); - - /* ### .bss */ - .bss : ALIGN(4) - { - . = ALIGN(4); - __sbss = .; - *(.bss .bss.*); - . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ - __ebss = .; - } > RAM - - /* ### .uninit */ - .uninit (NOLOAD) : ALIGN(4) - { - . = ALIGN(4); - *(.uninit .uninit.*); - . = ALIGN(4); - } > RAM - - /* Place the heap right after `.uninit` */ - . = ALIGN(4); - __sheap = .; - - /* ## .got */ - /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in - the input files and raise an error if relocatable code is found */ - .got (NOLOAD) : - { - KEEP(*(.got .got.*)); - } - - /* ## Discarded sections */ - /DISCARD/ : - { - /* Unused exception related info that only wastes space */ - *(.ARM.exidx); - *(.ARM.exidx.*); - *(.ARM.extab.*); - } -} - -/* Do not exceed this mark in the error messages below | */ -/* # Alignment checks */ -ASSERT(ORIGIN(FLASH) % 4 == 0, " -ERROR(cortex-m-rt): the start of the FLASH region must be 4-byte aligned"); - -ASSERT(ORIGIN(RAM) % 4 == 0, " -ERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned"); - -ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, " -BUG(cortex-m-rt): .data is not 4-byte aligned"); - -ASSERT(__sidata % 4 == 0, " -BUG(cortex-m-rt): the LMA of .data is not 4-byte aligned"); - -ASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, " -BUG(cortex-m-rt): .bss is not 4-byte aligned"); - -ASSERT(__sheap % 4 == 0, " -BUG(cortex-m-rt): start of .heap is not 4-byte aligned"); - -/* # Position checks */ - -/* ## .text */ -ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), " -ERROR(cortex-m-rt): The .text section must be placed inside the FLASH memory. -Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'"); - -/* # Other checks */ -ASSERT(SIZEOF(.got) == 0, " -ERROR(cortex-m-rt): .got section detected in the input object files -Dynamic relocations are not supported. If you are linking to C code compiled using -the 'cc' crate then modify your build script to compile the C code _without_ -the -fPIC flag. See the documentation of the `cc::Build.pic` method for details."); -/* Do not exceed this mark in the error messages above | */ diff --git a/neotron-os/neotron-flash-1002.ld b/neotron-os/neotron-flash-1002.ld deleted file mode 100644 index 0ba14c0..0000000 --- a/neotron-os/neotron-flash-1002.ld +++ /dev/null @@ -1,163 +0,0 @@ -/* # Developer notes - -- Symbols that start with a double underscore (__) are considered "private" - -- Symbols that start with a single underscore (_) are considered "semi-public"; they can be - overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { - static mut __sbss }`). - -- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a - symbol if not dropped if it appears in or near the front of the linker arguments and "it's not - needed" by any of the preceding objects (linker arguments) - -- `PROVIDE` is used to provide default values that can be overridden by a user linker script - -- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* - the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization - routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see - "Address (..) is out of bounds" in the disassembly produced by `objdump`. -*/ - -/* Provides information about the memory layout of the device */ -MEMORY -{ - /* The first 128 KiB is for the BIOS. We get the rest. */ - FLASH (rx) : ORIGIN = 0x10020000, LENGTH = 256K - - /* - * The RAM reserved for the OS. Above this is the Transient Program Area. - * - * This is defined by the Neotron specification for a given platform. On this - * Cortex-M based platform, it's the start of Cortex-M SRAM, plus 4 KiB, or - * 0x2000_1000. - */ - RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 4K -} - -/* # Entry point = what the BIOS calls to start the OS */ -ENTRY(os_main); - -/* -Where the Transient Program Area starts. -*/ -_tpa_start = ORIGIN(RAM) + LENGTH(RAM); - -/* # Sections */ -SECTIONS -{ - - /* ## Sections in FLASH */ - .entry_point ORIGIN(FLASH) : - { - KEEP(*(.entry_point)) - } > FLASH - - PROVIDE(_stext = ADDR(.entry_point) + SIZEOF(.entry_point)); - - /* ### .text */ - .text _stext : - { - *(.text .text.*); - *(.HardFaultTrampoline); - *(.HardFault.*); - } > FLASH - - /* ### .rodata */ - .rodata : ALIGN(4) - { - *(.rodata .rodata.*); - - /* 4-byte align the end (VMA) of this section. - This is required by LLD to ensure the LMA of the following .data - section will have the correct alignment. */ - . = ALIGN(4); - } > FLASH - - /* ## Sections in RAM */ - /* ### .data */ - .data : ALIGN(4) - { - . = ALIGN(4); - __sdata = .; - *(.data .data.*); - . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ - __edata = .; - } > RAM AT > FLASH - - /* LMA of .data */ - __sidata = LOADADDR(.data); - - /* ### .bss */ - .bss : ALIGN(4) - { - . = ALIGN(4); - __sbss = .; - *(.bss .bss.*); - . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ - __ebss = .; - } > RAM - - /* ### .uninit */ - .uninit (NOLOAD) : ALIGN(4) - { - . = ALIGN(4); - *(.uninit .uninit.*); - . = ALIGN(4); - } > RAM - - /* Place the heap right after `.uninit` */ - . = ALIGN(4); - __sheap = .; - - /* ## .got */ - /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in - the input files and raise an error if relocatable code is found */ - .got (NOLOAD) : - { - KEEP(*(.got .got.*)); - } - - /* ## Discarded sections */ - /DISCARD/ : - { - /* Unused exception related info that only wastes space */ - *(.ARM.exidx); - *(.ARM.exidx.*); - *(.ARM.extab.*); - } -} - -/* Do not exceed this mark in the error messages below | */ -/* # Alignment checks */ -ASSERT(ORIGIN(FLASH) % 4 == 0, " -ERROR(cortex-m-rt): the start of the FLASH region must be 4-byte aligned"); - -ASSERT(ORIGIN(RAM) % 4 == 0, " -ERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned"); - -ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, " -BUG(cortex-m-rt): .data is not 4-byte aligned"); - -ASSERT(__sidata % 4 == 0, " -BUG(cortex-m-rt): the LMA of .data is not 4-byte aligned"); - -ASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, " -BUG(cortex-m-rt): .bss is not 4-byte aligned"); - -ASSERT(__sheap % 4 == 0, " -BUG(cortex-m-rt): start of .heap is not 4-byte aligned"); - -/* # Position checks */ - -/* ## .text */ -ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), " -ERROR(cortex-m-rt): The .text section must be placed inside the FLASH memory. -Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'"); - -/* # Other checks */ -ASSERT(SIZEOF(.got) == 0, " -ERROR(cortex-m-rt): .got section detected in the input object files -Dynamic relocations are not supported. If you are linking to C code compiled using -the 'cc' crate then modify your build script to compile the C code _without_ -the -fPIC flag. See the documentation of the `cc::Build.pic` method for details."); -/* Do not exceed this mark in the error messages above | */ diff --git a/neotron-os/neotron-flash-0002.ld b/neotron-os/neotron-os-arm.ld similarity index 96% rename from neotron-os/neotron-flash-0002.ld rename to neotron-os/neotron-os-arm.ld index 59626d9..9f31cbe 100644 --- a/neotron-os/neotron-flash-0002.ld +++ b/neotron-os/neotron-os-arm.ld @@ -21,8 +21,8 @@ /* Provides information about the memory layout of the device */ MEMORY { - /* The first 128 KiB is for the BIOS. We get the rest. */ - FLASH (rx) : ORIGIN = 0x00020000, LENGTH = 256K + /* The BIOS is before the OS, we get the rest. We place a large upper bound on the length. */ + FLASH (rx) : ORIGIN = ${{start_address}}, LENGTH = 4096K /* * We get the bottom 4KB of RAM. Anything above that is for applications * (up to wherever the BIOS tells us we can use.) diff --git a/neotron-os/src/bin/flash0002.rs b/neotron-os/src/bin/flash0002.rs deleted file mode 100644 index 8de5d7b..0000000 --- a/neotron-os/src/bin/flash0002.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! Binary Neotron OS Image -//! -//! This is for Flash Addresses that start at `0x0002_0000`. -//! -//! Copyright (c) The Neotron Developers, 2022 -//! -//! Licence: GPL v3 or higher (see ../LICENCE.md) - -#![no_std] -#![no_main] - -/// This tells the BIOS how to start the OS. This must be the first four bytes -/// of our portion of Flash. -#[link_section = ".entry_point"] -#[used] -pub static ENTRY_POINT_ADDR: extern "C" fn(&neotron_common_bios::Api) -> ! = neotron_os::os_main; - -// End of file diff --git a/neotron-os/src/bin/flash0802.rs b/neotron-os/src/bin/flash0802.rs deleted file mode 100644 index 3e3a09e..0000000 --- a/neotron-os/src/bin/flash0802.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! Binary Neotron OS Image -//! -//! This is for Flash Addresses that start at `0x0802_0000`. -//! -//! Copyright (c) The Neotron Developers, 2022 -//! -//! Licence: GPL v3 or higher (see ../LICENCE.md) - -#![no_std] -#![no_main] - -/// This tells the BIOS how to start the OS. This must be the first four bytes -/// of our portion of Flash. -#[link_section = ".entry_point"] -#[used] -pub static ENTRY_POINT_ADDR: extern "C" fn(&neotron_common_bios::Api) -> ! = neotron_os::os_main; - -// End of file diff --git a/neotron-os/src/bin/flash1002.rs b/neotron-os/src/main.rs similarity index 100% rename from neotron-os/src/bin/flash1002.rs rename to neotron-os/src/main.rs From 34f0f33080b7fad3c00e7ac2efb9c15cebbc4f88 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 20 Dec 2024 17:32:10 +0000 Subject: [PATCH 04/14] Add more runs-on --- .github/workflows/rust.yml | 40 ++++++++++++++------------------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ea8d7a2..ccbc485 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -4,21 +4,19 @@ on: [push, pull_request] jobs: binaries: - name: Build Binaries + runs-on: ubuntu-latest strategy: matrix: target: [thumbv6m-none-eabi, thumbv7em-none-eabi, thumbv8m.main-none-eabi] start_address: [0x0802_0000, 0x1002_0000] steps: - - name: Checkout - uses: actions/checkout@v4 + - uses: actions/checkout@v4 with: submodules: true - run: | rustup target add ${{ matrix.target }} cargo nbuild binaries --target=${{ matrix.target }} --start-address=${{ matrix.start_address }} - - name: Upload Artifacts - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v4 if: ${{success()}} with: name: ${{ matrix.target }}-${{ matrix.start_address }}-binaries @@ -28,14 +26,11 @@ jobs: linux-libraries: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 + - uses: actions/checkout@v4 with: submodules: true - - name: Build - run: cargo nbuild libraries - - name: Upload Artifacts - uses: actions/upload-artifact@v4 + - run: cargo nbuild libraries + - uses: actions/upload-artifact@v4 if: ${{success()}} with: name: linux-libraries @@ -45,14 +40,11 @@ jobs: windows-libraries: runs-on: windows-latest steps: - - name: Checkout - uses: actions/checkout@v4 + - uses: actions/checkout@v4 with: submodules: true - - name: Build - run: cargo nbuild libraries - - name: Upload Artifacts - uses: actions/upload-artifact@v4 + - run: cargo nbuild libraries + - uses: actions/upload-artifact@v4 if: ${{success()}} with: name: windows-libraries @@ -60,25 +52,23 @@ jobs: path: | ./target/release/neotron_os.dll run-tests: + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: submodules: true - run: cargo nbuild test release: - name: Upload Release - needs: [binaries, linux-libraries, windows-libraries] + runs-on: ubuntu-latest + needs: [binaries, linux-libraries, windows-libraries, run-tests] if: github.event_name == 'push' && startswith(github.ref, 'refs/tags/') steps: - - name: Make release area - run: | + - run: | mkdir ./release - - name: Download Artifacts - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v4 with: path: ./release - - name: Upload files to Release - uses: softprops/action-gh-release@v1 + - uses: softprops/action-gh-release@v1 with: files: | ./release/* From 1f745bae0fc9faa39f327b5b8b2296d106770474 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 20 Dec 2024 17:46:44 +0000 Subject: [PATCH 05/14] Add test subcommand --- nbuild/src/lib.rs | 1 + nbuild/src/main.rs | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/nbuild/src/lib.rs b/nbuild/src/lib.rs index da5e2ee..b843ce6 100644 --- a/nbuild/src/lib.rs +++ b/nbuild/src/lib.rs @@ -35,6 +35,7 @@ pub struct Package { pub path: &'static std::path::Path, pub output: &'static std::path::Path, pub kind: PackageKind, + pub testable: bool, } /// Parse an integer, with an optional `0x` prefix. diff --git a/nbuild/src/main.rs b/nbuild/src/main.rs index 31a88c4..a9edc78 100644 --- a/nbuild/src/main.rs +++ b/nbuild/src/main.rs @@ -27,6 +27,8 @@ enum Commands { }, /// Checks the Neotron OS source code using clippy Clippy, + /// Runs any tests + Test, } /// A simple utility for building Neotron OS and a suitable ROMFS image @@ -45,18 +47,21 @@ fn packages() -> Vec { path: std::path::Path::new("./nbuild/Cargo.toml"), output: std::path::Path::new("./nbuild/target/debug/nbuild{exe}"), kind: nbuild::PackageKind::NBuild, + testable: true, }, nbuild::Package { name: "flames utility", path: std::path::Path::new("./utilities/flames/Cargo.toml"), output: std::path::Path::new("./target/{target}/{profile}/flames"), kind: nbuild::PackageKind::Utility, + testable: false, }, nbuild::Package { name: "Neotron OS", path: std::path::Path::new("./neotron-os/Cargo.toml"), output: std::path::Path::new("./target/{target}/{profile}/neotron-os"), kind: nbuild::PackageKind::Os, + testable: false, }, ] } @@ -80,6 +85,7 @@ fn main() { Some(Commands::Libraries { target }) => library(&packages, target.as_deref()), Some(Commands::Format { check }) => format(&packages, check), Some(Commands::Clippy) => clippy(&packages), + Some(Commands::Test) => test(&packages), } } @@ -191,4 +197,19 @@ fn clippy(packages: &[nbuild::Package]) { } } +/// Runs `cargo test` over all the packages +fn test(packages: &[nbuild::Package]) { + let mut is_error = false; + for package in packages.iter().filter(|p| p.testable) { + println!("Testing {}", package.name); + if let Err(e) = nbuild::cargo(&["test"], None, package.path) { + eprintln!("Test failed: {}", e); + is_error = true; + } + } + if is_error { + std::process::exit(1); + } +} + // End of file From bfe20c1b7276b1136628a4eae1bb2b772f70d14e Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 20 Dec 2024 17:46:52 +0000 Subject: [PATCH 06/14] Fix subcommand name in CI script --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ccbc485..0fd4a6d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -15,7 +15,7 @@ jobs: submodules: true - run: | rustup target add ${{ matrix.target }} - cargo nbuild binaries --target=${{ matrix.target }} --start-address=${{ matrix.start_address }} + cargo nbuild binary --target=${{ matrix.target }} --start-address=${{ matrix.start_address }} - uses: actions/upload-artifact@v4 if: ${{success()}} with: From e8d45997b5fe216462c09b665811aa74b865ccc5 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 20 Dec 2024 18:51:55 +0000 Subject: [PATCH 07/14] Build library as release --- nbuild/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nbuild/src/main.rs b/nbuild/src/main.rs index a9edc78..2be6c6a 100644 --- a/nbuild/src/main.rs +++ b/nbuild/src/main.rs @@ -152,7 +152,7 @@ fn library(packages: &[nbuild::Package], target: Option<&str>) { package.name, target.unwrap_or("native") ); - if let Err(e) = nbuild::cargo(&["build", "--lib"], target, package.path) { + if let Err(e) = nbuild::cargo(&["build", "--release", "--lib"], target, package.path) { eprintln!("Build of {} failed: {}", package.name, e); is_error = true; } From 5c07e3058ebcfdc66020ec7717da9c04b830b1a0 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 20 Dec 2024 18:55:09 +0000 Subject: [PATCH 08/14] Single-line run steps --- .github/workflows/rust.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0fd4a6d..d607cfd 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -13,9 +13,8 @@ jobs: - uses: actions/checkout@v4 with: submodules: true - - run: | - rustup target add ${{ matrix.target }} - cargo nbuild binary --target=${{ matrix.target }} --start-address=${{ matrix.start_address }} + - run: rustup target add ${{ matrix.target }} + - run: cargo nbuild binary --target=${{ matrix.target }} --start-address=${{ matrix.start_address }} - uses: actions/upload-artifact@v4 if: ${{success()}} with: @@ -63,8 +62,7 @@ jobs: needs: [binaries, linux-libraries, windows-libraries, run-tests] if: github.event_name == 'push' && startswith(github.ref, 'refs/tags/') steps: - - run: | - mkdir ./release + - run: mkdir ./release - uses: actions/download-artifact@v4 with: path: ./release From 25264620265e7da0f0fc7d18ad6ca1ad9f7103e1 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 20 Dec 2024 19:55:27 +0000 Subject: [PATCH 09/14] Make ROMFS files and bin files. --- .github/workflows/rust.yml | 3 ++ nbuild/Cargo.lock | 66 +++++++++++++++++++++++++++++++ nbuild/Cargo.toml | 4 ++ nbuild/src/lib.rs | 65 +++++++++++++++++++++++++----- nbuild/src/main.rs | 81 ++++++++++++++++++++++++++++++++++---- neotron-os/build.rs | 3 +- 6 files changed, 203 insertions(+), 19 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d607cfd..2037502 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -14,6 +14,7 @@ jobs: with: submodules: true - run: rustup target add ${{ matrix.target }} + - run: rustup component add llvm-tools-preview - run: cargo nbuild binary --target=${{ matrix.target }} --start-address=${{ matrix.start_address }} - uses: actions/upload-artifact@v4 if: ${{success()}} @@ -22,6 +23,8 @@ jobs: if-no-files-found: error path: | ./target/${{ matrix.target }}/release/neotron-os + ./target/${{ matrix.target }}/release/neotron-os.bin + ./target/${{ matrix.target }}/release/romfs.bin linux-libraries: runs-on: ubuntu-latest steps: diff --git a/nbuild/Cargo.lock b/nbuild/Cargo.lock index 611d957..7787e26 100644 --- a/nbuild/Cargo.lock +++ b/nbuild/Cargo.lock @@ -51,6 +51,27 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "num-traits", +] + [[package]] name = "clap" version = "4.5.23" @@ -97,6 +118,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "heck" version = "0.5.0" @@ -113,7 +140,46 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" name = "nbuild" version = "0.1.0" dependencies = [ + "chrono", "clap", + "embedded-io", + "neotron-api", + "neotron-romfs", +] + +[[package]] +name = "neotron-api" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67d6c96706b6f3ec069abfb042cadfd2d701980fa4940f407c0bc28ee1e1c493" +dependencies = [ + "bitflags", + "neotron-ffi", +] + +[[package]] +name = "neotron-ffi" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d37886e73d87732421aaf5da617eead9d69a7daf6b0d059780f76157d9ce5372" + +[[package]] +name = "neotron-romfs" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f7987e34f25f780b5dd5a22b5da7ce9a566d93b8f608f78293a170f35f024c7" +dependencies = [ + "embedded-io", + "neotron-api", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", ] [[package]] diff --git a/nbuild/Cargo.toml b/nbuild/Cargo.toml index 3de7b85..99fc2d8 100644 --- a/nbuild/Cargo.toml +++ b/nbuild/Cargo.toml @@ -12,4 +12,8 @@ readme = "README.md" repository = "https://github.com/neotron-compute/Neotron-OS" [dependencies] +chrono = { version = "0.4.39", default-features = false, features = ["std"] } clap = { version = "4.5.23", features = ["derive"] } +embedded-io = { version = "0.6.1", features = ["std"] } +neotron-api = "0.2.0" +neotron-romfs = "2.0" diff --git a/nbuild/src/lib.rs b/nbuild/src/lib.rs index b843ce6..9416b5f 100644 --- a/nbuild/src/lib.rs +++ b/nbuild/src/lib.rs @@ -2,18 +2,18 @@ /// The ways that spawning `cargo` can fail #[derive(Debug)] -pub enum CargoError { +pub enum ProcessError { SpawnError(std::io::Error), RunError(std::process::ExitStatus), } -impl std::fmt::Display for CargoError { +impl std::fmt::Display for ProcessError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - CargoError::SpawnError(error) => write!(f, "Failed to spawn `cargo`: {}", error), - CargoError::RunError(exit_status) => write!( + ProcessError::SpawnError(error) => write!(f, "Failed to spawn command: {}", error), + ProcessError::RunError(exit_status) => write!( f, - "Failed to complete `cargo` command ({}). There should be an error above", + "Failed to complete command ({}). There should be an error above", exit_status ), } @@ -33,9 +33,16 @@ pub enum PackageKind { pub struct Package { pub name: &'static str, pub path: &'static std::path::Path, - pub output: &'static std::path::Path, pub kind: PackageKind, pub testable: bool, + pub output_template: Option<&'static str>, +} + +impl Package { + pub fn output(&self, target: &str, profile: &str) -> Option { + self.output_template + .map(|s| s.replace("{target}", target).replace("{profile}", profile)) + } } /// Parse an integer, with an optional `0x` prefix. @@ -60,7 +67,11 @@ where } /// Runs cargo -pub fn cargo

(commands: &[&str], target: Option<&str>, manifest_path: P) -> Result<(), CargoError> +pub fn cargo

( + commands: &[&str], + target: Option<&str>, + manifest_path: P, +) -> Result<(), ProcessError> where P: AsRef, { @@ -73,7 +84,7 @@ pub fn cargo_with_env

( target: Option<&str>, manifest_path: P, environment: &[(&'static str, String)], -) -> Result<(), CargoError> +) -> Result<(), ProcessError> where P: AsRef, { @@ -93,11 +104,45 @@ where println!("Running: {:?}", command_line); - let output = command_line.output().map_err(CargoError::SpawnError)?; + let output = command_line.output().map_err(ProcessError::SpawnError)?; if output.status.success() { Ok(()) } else { - Err(CargoError::RunError(output.status)) + Err(ProcessError::RunError(output.status)) + } +} + +/// Make a binary version of an ELF file +pub fn make_bin

(path: P) -> Result +where + P: AsRef, +{ + let path = path.as_ref(); + println!("Making binary of: {}", path.display()); + let output = std::process::Command::new("rustc") + .arg("--print") + .arg("target-libdir") + .output() + .expect("Failed to run rustc --print target-libdir"); + let sysroot = String::from_utf8(output.stdout).expect("sysroot path isn't UTF-8"); + let sysroot: std::path::PathBuf = sysroot.trim().into(); + let mut objcopy = sysroot.clone(); + objcopy.pop(); + objcopy.push("bin"); + objcopy.push("llvm-objcopy"); + let mut command_line = std::process::Command::new(objcopy); + command_line.args(["-O", "binary"]); + command_line.arg(path); + let output_file = path.with_extension("bin"); + command_line.arg(&output_file); + println!("Running: {:?}", command_line); + let output = command_line.output().map_err(ProcessError::SpawnError)?; + if output.status.success() { + Ok(output_file) + } else { + Err(ProcessError::RunError(output.status)) } } + +// End of file diff --git a/nbuild/src/main.rs b/nbuild/src/main.rs index 2be6c6a..4022d6d 100644 --- a/nbuild/src/main.rs +++ b/nbuild/src/main.rs @@ -45,21 +45,21 @@ fn packages() -> Vec { nbuild::Package { name: "nbuild", path: std::path::Path::new("./nbuild/Cargo.toml"), - output: std::path::Path::new("./nbuild/target/debug/nbuild{exe}"), + output_template: None, kind: nbuild::PackageKind::NBuild, testable: true, }, nbuild::Package { - name: "flames utility", + name: "flames", path: std::path::Path::new("./utilities/flames/Cargo.toml"), - output: std::path::Path::new("./target/{target}/{profile}/flames"), + output_template: Some("./target/{target}/{profile}/flames"), kind: nbuild::PackageKind::Utility, testable: false, }, nbuild::Package { name: "Neotron OS", path: std::path::Path::new("./neotron-os/Cargo.toml"), - output: std::path::Path::new("./target/{target}/{profile}/neotron-os"), + output_template: Some("./target/{target}/{profile}/neotron-os"), kind: nbuild::PackageKind::Os, testable: false, }, @@ -91,11 +91,16 @@ fn main() { /// Builds the utility and OS packages as binaries fn binary(packages: &[nbuild::Package], start_address: &str, target: &str) { + use chrono::{Datelike, Timelike}; + let mut is_error = false; let Ok(start_address) = nbuild::parse_int(start_address) else { eprintln!("{:?} was not a valid integer", start_address); std::process::exit(1); }; + + let mut romfs_entries = Vec::new(); + // Build utilities for package in packages .iter() .filter(|p| p.kind == nbuild::PackageKind::Utility) @@ -108,7 +113,57 @@ fn binary(packages: &[nbuild::Package], start_address: &str, target: &str) { eprintln!("Build of {} failed: {}", package.name, e); is_error = true; } + let package_output = package + .output(target, "release") + .expect("utilties should have an output"); + let contents = match std::fs::read(&package_output) { + Ok(contents) => contents, + Err(e) => { + eprintln!("Reading of {} failed: {}", package_output, e); + continue; + } + }; + let ctime = std::time::SystemTime::now(); + let ctime = chrono::DateTime::::from(ctime); + romfs_entries.push(neotron_romfs::Entry { + metadata: neotron_romfs::EntryMetadata { + file_name: package.name, + ctime: neotron_api::file::Time { + year_since_1970: (ctime.year() - 1970) as u8, + zero_indexed_month: ctime.month0() as u8, + zero_indexed_day: ctime.day0() as u8, + hours: ctime.hour() as u8, + minutes: ctime.minute() as u8, + seconds: ctime.second() as u8, + }, + file_size: contents.len() as u32, + }, + contents, + }); } + + // Build ROMFS + let mut buffer = Vec::new(); + let _size = match neotron_romfs::RomFs::construct_into(&mut buffer, &romfs_entries) { + Ok(size) => size, + Err(e) => { + eprintln!("Making ROMFS failed: {:?}", e); + std::process::exit(1); + } + }; + let mut romfs_path = std::path::PathBuf::new(); + romfs_path.push(std::env::current_dir().expect("We have no CWD?")); + romfs_path.push("target"); + romfs_path.push(target); + romfs_path.push("release"); + romfs_path.push("romfs.bin"); + if let Err(e) = std::fs::write(&romfs_path, &buffer) { + eprintln!("Writing ROMFS to {} failed: {:?}", romfs_path.display(), e); + std::process::exit(1); + } + println!("Built ROMFS at {}", romfs_path.display()); + + // Build OS for package in packages .iter() .filter(|p| p.kind == nbuild::PackageKind::Os) @@ -117,10 +172,13 @@ fn binary(packages: &[nbuild::Package], start_address: &str, target: &str) { "Cross-compiling {}, using start address 0x{:08x} and target {:?}", package.name, start_address, target ); - let environment = [( - "NEOTRON_OS_START_ADDRESS", - format!("0x{:08x}", start_address), - )]; + let environment = [ + ( + "NEOTRON_OS_START_ADDRESS", + format!("0x{:08x}", start_address), + ), + ("ROMFS_PATH", romfs_path.to_string_lossy().to_string()), + ]; if let Err(e) = nbuild::cargo_with_env( &["build", "--release"], Some(target), @@ -130,6 +188,13 @@ fn binary(packages: &[nbuild::Package], start_address: &str, target: &str) { eprintln!("Build of {} failed: {}", package.name, e); is_error = true; } + let package_output = package + .output(target, "release") + .expect("PackageKind::Os should always have output"); + if let Err(e) = nbuild::make_bin(&package_output) { + eprintln!("objcopy of {} failed: {}", package_output, e); + is_error = true; + } } if is_error { std::process::exit(1); diff --git a/neotron-os/build.rs b/neotron-os/build.rs index c685dab..f56cbe0 100644 --- a/neotron-os/build.rs +++ b/neotron-os/build.rs @@ -36,9 +36,10 @@ fn main() { println!("cargo::rustc-link-lib=dylib=msvcrt"); } - if option_env!("ROMFS_PATH").is_some() { + if let Some(path) = option_env!("ROMFS_PATH") { println!("cargo::rustc-cfg=romfs_enabled=\"yes\""); println!("cargo::rerun-if-env-changed=ROMFS_PATH"); + println!("cargo::rerun-if-changed={}", path); } println!("cargo::rustc-check-cfg=cfg(romfs_enabled, values(\"yes\"))"); } From fe16ced1a5f34e6d8cb5ea2ab086b6b49a03aa07 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 20 Dec 2024 20:36:42 +0000 Subject: [PATCH 10/14] Update instructions --- .github/workflows/rust.yml | 16 ++++++++-------- README.md | 25 +++++++++++++++++++------ nbuild/src/main.rs | 4 ++-- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2037502..27ee7cf 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -19,37 +19,37 @@ jobs: - uses: actions/upload-artifact@v4 if: ${{success()}} with: - name: ${{ matrix.target }}-${{ matrix.start_address }}-binaries + name: ${{ matrix.target }}-${{ matrix.start_address }}-binary if-no-files-found: error path: | ./target/${{ matrix.target }}/release/neotron-os ./target/${{ matrix.target }}/release/neotron-os.bin ./target/${{ matrix.target }}/release/romfs.bin - linux-libraries: + linux-library: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: submodules: true - - run: cargo nbuild libraries + - run: cargo nbuild library - uses: actions/upload-artifact@v4 if: ${{success()}} with: - name: linux-libraries + name: linux-library if-no-files-found: error path: | ./target/release/libneotron_os.so - windows-libraries: + windows-library: runs-on: windows-latest steps: - uses: actions/checkout@v4 with: submodules: true - - run: cargo nbuild libraries + - run: cargo nbuild library - uses: actions/upload-artifact@v4 if: ${{success()}} with: - name: windows-libraries + name: windows-library if-no-files-found: error path: | ./target/release/neotron_os.dll @@ -62,7 +62,7 @@ jobs: - run: cargo nbuild test release: runs-on: ubuntu-latest - needs: [binaries, linux-libraries, windows-libraries, run-tests] + needs: [binaries, linux-library, windows-library, run-tests] if: github.event_name == 'push' && startswith(github.ref, 'refs/tags/') steps: - run: mkdir ./release diff --git a/README.md b/README.md index 2937c2f..b96272d 100644 --- a/README.md +++ b/README.md @@ -37,18 +37,30 @@ Your board will need an appropriate Neotron BIOS installed, and you need to have OpenOCD or some other programming tool running for your particular board. See your BIOS instructions for more details. -Building Neotron OS is handled by the `nbuild` tool, in this repository. +Building Neotron OS is handled by the `nbuild` tool, in this repository. Run `cargo nbuild help` for more information. -Run `cargo nbuild help` for more information. +To make an image for a board like the Neotron Pico, you want to run `cargo nbuild binary`. By default this will produce a `thumbv6m-none-eabi` image linked to run at address `0x1002_0000`, with a ROMFS containing various utilities, which is what you need on a Neotron Pico. Your BIOS should tell you if you need to change these options, and how to load the resulting image onto your system. -Your BIOS should tell you which options to pass, and how to load the resulting image onto your system. +```console +$ cargo nbuild binary +... +$ ls ./target/thumbv6m-none-eabi/release +build/ examples/ flames.d libflames.d libneotron_os.d neotron-os neotron-os.d +deps/ flames incremental/ libflames.rlib libneotron_os.rlib neotron-os.bin romfs.bin +``` + +Here: + +* `romfs.bin` is the raw ROMFS image +* `neotron-os` is an ELF file containing the OS and the ROMFS image +* `neotron-os.bin` is an raw binary copy of the contents of the ELF file -Programs in the ROMFS can be loaded with: +When the OS is running, programs in the ROMFS can be loaded with: ```text > rom -flames.elf (14212 bytes) -> rom flames.elf +flames (14212 bytes) +> rom flames Loading 4256 bytes to 0x20001000 Loading 532 bytes to 0x200020a0 Loading 4908 bytes to 0x200022b4 @@ -62,6 +74,7 @@ You can also build a *shared object* to load into a Windows/Linux/macOS applicat ```console $ cargo nbuild library +... $ ls ./target/debug/*.so ./target/debug/libneotron_os.so ``` diff --git a/nbuild/src/main.rs b/nbuild/src/main.rs index 4022d6d..90dac30 100644 --- a/nbuild/src/main.rs +++ b/nbuild/src/main.rs @@ -14,7 +14,7 @@ enum Commands { target: String, }, /// Builds the OS as a library, for the native machine - Libraries { + Library { /// The target we're building Neotron OS for #[clap(long)] target: Option, @@ -82,7 +82,7 @@ fn main() { }) => { binary(&packages, &start_address, &target); } - Some(Commands::Libraries { target }) => library(&packages, target.as_deref()), + Some(Commands::Library { target }) => library(&packages, target.as_deref()), Some(Commands::Format { check }) => format(&packages, check), Some(Commands::Clippy) => clippy(&packages), Some(Commands::Test) => test(&packages), From f793c63158bf80b4bde8cbf194c29cd8884d091f Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 20 Dec 2024 20:36:48 +0000 Subject: [PATCH 11/14] Clean up release --- .github/workflows/clippy.yml | 14 ++++++++++ .github/workflows/format.yml | 13 +++------ .github/workflows/rust.yml | 52 +++++++++++++++++++++++++++++------- 3 files changed, 61 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/clippy.yml diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml new file mode 100644 index 0000000..7aba910 --- /dev/null +++ b/.github/workflows/clippy.yml @@ -0,0 +1,14 @@ +name: Format + +on: [push, pull_request] + +jobs: + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - run: rustup component add clippy + - run: cargo nbuild clippy + diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 5621c55..57ae7a3 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -3,16 +3,11 @@ name: Format on: [push, pull_request] jobs: - run_cargo_fmt: - name: Run cargo fmt + format: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 + - uses: actions/checkout@v4 with: submodules: true - - name: Add Tool - run: rustup component add rustfmt - - name: Check Format - run: cargo nbuild format --check - + - run: rustup component add rustfmt + - run: cargo nbuild format --check diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 27ee7cf..41541fc 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -3,7 +3,7 @@ name: Build on: [push, pull_request] jobs: - binaries: + embedded-binaries: runs-on: ubuntu-latest strategy: matrix: @@ -25,7 +25,7 @@ jobs: ./target/${{ matrix.target }}/release/neotron-os ./target/${{ matrix.target }}/release/neotron-os.bin ./target/${{ matrix.target }}/release/romfs.bin - linux-library: + x64_64-unknown-linux-gnu-library: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -35,11 +35,11 @@ jobs: - uses: actions/upload-artifact@v4 if: ${{success()}} with: - name: linux-library + name: x64_64-unknown-linux-gnu-library if-no-files-found: error path: | ./target/release/libneotron_os.so - windows-library: + x86_64-pc-windows-msvc-library: runs-on: windows-latest steps: - uses: actions/checkout@v4 @@ -49,10 +49,27 @@ jobs: - uses: actions/upload-artifact@v4 if: ${{success()}} with: - name: windows-library + name: x86_64-pc-windows-msvc-library if-no-files-found: error path: | ./target/release/neotron_os.dll + ./target/release/neotron_os.dll.exp + ./target/release/neotron_os.dll.lib + ./target/release/neotron_os.pdb + aarch64-apple-darwin-library: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - run: cargo nbuild library + - uses: actions/upload-artifact@v4 + if: ${{success()}} + with: + name: aarch64-apple-darwin-library + if-no-files-found: error + path: | + ./target/release/libneotron_os.dylib run-tests: runs-on: ubuntu-latest steps: @@ -60,16 +77,33 @@ jobs: with: submodules: true - run: cargo nbuild test + preview-release: + runs-on: ubuntu-latest + needs: [embedded-binaries, x64_64-unknown-linux-gnu-library, x86_64-pc-windows-msvc-library, aarch64-apple-darwin-library, run-tests] + steps: + - run: mkdir ./release + - uses: actions/download-artifact@v4 + with: + path: ./release-${{ github.ref_name }} + - run: ls -lR ./release-${{ github.ref_name }} + - run: zip -r ./release-${{ github.ref_name }}.zip ./release-${{ github.ref_name }} + - uses: actions/upload-artifact@v4 + if: ${{success()}} + with: + name: release + if-no-files-found: error + path: | + ./release-${{ github.ref_name }}.zip release: runs-on: ubuntu-latest - needs: [binaries, linux-library, windows-library, run-tests] + needs: [preview-release] if: github.event_name == 'push' && startswith(github.ref, 'refs/tags/') steps: - - run: mkdir ./release - uses: actions/download-artifact@v4 with: - path: ./release + name: release + path: . - uses: softprops/action-gh-release@v1 with: files: | - ./release/* + ./release-${{ github.ref_name }}.zip From 19a29331f0aa7c7213f46b11c6325159413a8e1e Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 20 Dec 2024 22:15:32 +0000 Subject: [PATCH 12/14] clippy fixes --- neotron-os/src/refcell.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/neotron-os/src/refcell.rs b/neotron-os/src/refcell.rs index fd85cf1..af7d630 100644 --- a/neotron-os/src/refcell.rs +++ b/neotron-os/src/refcell.rs @@ -92,7 +92,7 @@ pub struct CsRefCellGuard<'a, T> { parent: &'a CsRefCell, } -impl<'a, T> Deref for CsRefCellGuard<'a, T> { +impl Deref for CsRefCellGuard<'_, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -101,14 +101,14 @@ impl<'a, T> Deref for CsRefCellGuard<'a, T> { } } -impl<'a, T> DerefMut for CsRefCellGuard<'a, T> { +impl DerefMut for CsRefCellGuard<'_, T> { fn deref_mut(&mut self) -> &mut Self::Target { let ptr = self.parent.inner.get(); unsafe { &mut *ptr } } } -impl<'a, T> Drop for CsRefCellGuard<'a, T> { +impl Drop for CsRefCellGuard<'_, T> { fn drop(&mut self) { // We hold this refcell guard exclusively, so this can't race self.parent.locked.store(false, Ordering::Release); From 024bb8e1756f2ebfc9b85e83b111b79a52683e8b Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 20 Dec 2024 22:22:20 +0000 Subject: [PATCH 13/14] Rename --- .github/workflows/clippy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 7aba910..6175d51 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -1,4 +1,4 @@ -name: Format +name: Clippy on: [push, pull_request] From de5e13ba9ffbb4bb06c331c53644b03d14aa3584 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 20 Dec 2024 22:26:39 +0000 Subject: [PATCH 14/14] Remove old build script --- build.sh | 39 --------------------------------------- 1 file changed, 39 deletions(-) delete mode 100755 build.sh diff --git a/build.sh b/build.sh deleted file mode 100755 index 2ac6939..0000000 --- a/build.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) - -RELEASE_DIR=${SCRIPT_DIR}/release - -mkdir -p ${RELEASE_DIR} - -# Build the embedded binaries for each core type and each flash layout -for TARGET_ARCH in thumbv6m-none-eabi thumbv7m-none-eabi thumbv7em-none-eabi; do - echo "TARGET is ${TARGET_ARCH}" - # Rename our utilities to have an ELF extension - for utility in flames; do - ( cd ${SCRIPT_DIR} && cargo build $* --release --target=${TARGET_ARCH} --bin ${utility} ) - rust-strip ${SCRIPT_DIR}/target/${TARGET_ARCH}/release/${utility} -o ${SCRIPT_DIR}/target/${TARGET_ARCH}/release/${utility}.elf - done - # Make a ROMFS - export ROMFS_PATH=${SCRIPT_DIR}/target/${TARGET_ARCH}/release/romfs.img - neotron-romfs-mkfs \ - ${SCRIPT_DIR}/target/${TARGET_ARCH}/release/flames.elf \ - > ${ROMFS_PATH} - neotron-romfs-lsfs ${ROMFS_PATH} - # Build the OS again, with the new ROMFS - for BINARY in flash0002 flash0802 flash1002; do - echo "BINARY is ${BINARY}" - ( cd ${SCRIPT_DIR}/neotron-os && cargo build $* --release --target=${TARGET_ARCH} --bin ${BINARY} ) - # objcopy would do the build for us first, but it doesn't have good build output - rust-objcopy -O binary ${SCRIPT_DIR}/target/${TARGET_ARCH}/release/${BINARY} ${RELEASE_DIR}/${TARGET_ARCH}-${BINARY}-libneotron_os.bin - # Keep the ELF file too (for debugging) - cp ${SCRIPT_DIR}/target/${TARGET_ARCH}/release/${BINARY} ${RELEASE_DIR}/${TARGET_ARCH}-${BINARY}-libneotron_os.elf - done -done - -# Build the host version -echo "Building HOST" -( cd ${SCRIPT_DIR} && cargo build --verbose --lib --release --target=x86_64-unknown-linux-gnu ) -cp ${SCRIPT_DIR}/target/x86_64-unknown-linux-gnu/release/libneotron_os.so ${RELEASE_DIR}/x86_64-unknown-linux-gnu-libneotron_os.so