diff --git a/.github/workflows/publish-agb-entry.yml b/.github/workflows/publish-agb-entry.yml new file mode 100644 index 00000000..c132ca60 --- /dev/null +++ b/.github/workflows/publish-agb-entry.yml @@ -0,0 +1,20 @@ +name: Publish agb-entry + +on: + push: + tags: + - agb-entry/v* + +jobs: + build: + runs-on: ubuntu-20.04 + steps: + - name: Install build tools + run: sudo apt-get update && sudo apt-get install build-essential binutils-arm-none-eabi -y + - name: Check out repository + uses: actions/checkout@v2 + - name: Login to crates.io + run: cargo login ${{ secrets.CRATE_API }} + - name: Publish agb-entry + run: cargo publish + working-directory: ./agb-entry \ No newline at end of file diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f3e11737..8f32d62b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -38,6 +38,9 @@ jobs: - name: Run Clippy on agb image converter working-directory: agb-image-converter run: cargo clippy --verbose + - name: Run Clippy on agb entry + working-directory: agb-entry + run: cargo clippy --verbose - name: Run Tests for agb in debug mode working-directory: agb run: cargo test --verbose diff --git a/agb-entry/Cargo.lock b/agb-entry/Cargo.lock new file mode 100644 index 00000000..304e5599 --- /dev/null +++ b/agb-entry/Cargo.lock @@ -0,0 +1,123 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "agb_entry" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "rand", + "syn", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "syn" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" diff --git a/agb-entry/Cargo.toml b/agb-entry/Cargo.toml new file mode 100644 index 00000000..f88f91a6 --- /dev/null +++ b/agb-entry/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "agb_entry" +version = "0.1.0" +authors = ["Gwilym Kuiper "] +edition = "2018" +license = "MPL-2.0" +description = "Macro for declaring the entry point for a game using the agb library" + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "1.0.73", features = ["full", "extra-traits"] } +proc-macro2 = "1.0.27" +quote = "1.0.9" +rand = "0.8.4" \ No newline at end of file diff --git a/agb-entry/src/lib.rs b/agb-entry/src/lib.rs new file mode 100644 index 00000000..40cc44aa --- /dev/null +++ b/agb-entry/src/lib.rs @@ -0,0 +1,62 @@ +// This macro is based very heavily on the entry one in rust-embedded +use proc_macro::TokenStream; + +use proc_macro2::Span; +use quote::quote; +use rand::Rng; +use syn::{Ident, ItemFn, ReturnType, Type, Visibility}; + +#[proc_macro_attribute] +pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { + let f: ItemFn = syn::parse(input).expect("#[agb::entry] must be applied to a function"); + + // Check that the function signature is correct + assert!( + f.sig.constness.is_none() + && f.vis == Visibility::Inherited + && f.sig.abi.is_none() + && f.sig.inputs.is_empty() + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && match f.sig.output { + ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)), + _ => false, + }, + "#[agb::entry] must have signature [unsafe] fn () -> !" + ); + + assert!( + args.to_string() == "", + "Must pass no args to #[agb::entry] macro" + ); + + let fn_name = random_ident(); + + let attrs = f.attrs; + let stmts = f.block.stmts; + + quote!( + #[export_name = "main"] + #(#attrs)* + pub fn #fn_name() -> ! { + #(#stmts)* + } + ) + .into() +} + +fn random_ident() -> Ident { + let mut rng = rand::thread_rng(); + Ident::new( + &(0..16) + .map(|i| { + if i == 0 || rng.gen() { + (b'a' + rng.gen::() % 25) as char + } else { + (b'0' + rng.gen::() % 10) as char + } + }) + .collect::(), + Span::call_site(), + ) +} diff --git a/agb/Cargo.lock b/agb/Cargo.lock index 8003559d..fa875b72 100644 --- a/agb/Cargo.lock +++ b/agb/Cargo.lock @@ -12,10 +12,21 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" name = "agb" version = "0.6.0" dependencies = [ + "agb_entry", "agb_image_converter", "bitflags", ] +[[package]] +name = "agb_entry" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "rand", + "syn", +] + [[package]] name = "agb_image_converter" version = "0.6.0" @@ -83,6 +94,17 @@ dependencies = [ "byteorder", ] +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "image" version = "0.23.14" @@ -98,6 +120,12 @@ dependencies = [ "png", ] +[[package]] +name = "libc" +version = "0.2.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" + [[package]] name = "miniz_oxide" version = "0.3.7" @@ -160,6 +188,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + [[package]] name = "proc-macro2" version = "1.0.28" @@ -178,6 +212,46 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + [[package]] name = "serde" version = "1.0.127" @@ -223,3 +297,9 @@ name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" diff --git a/agb/Cargo.toml b/agb/Cargo.toml index 01164dce..675ce260 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -20,6 +20,7 @@ debug = true [dependencies] bitflags = "1.2" agb_image_converter = { version = "0.6.0", path = "../agb-image-converter" } +agb_entry = { version = "0.1.0", path = "../agb-entry" } [package.metadata.docs.rs] default-target = "thumbv6m-none-eabi" diff --git a/agb/examples/beep.rs b/agb/examples/beep.rs index e6f6cefa..ec377000 100644 --- a/agb/examples/beep.rs +++ b/agb/examples/beep.rs @@ -5,8 +5,8 @@ extern crate agb; use agb::sound; -#[no_mangle] -pub fn main() -> ! { +#[agb::entry] +fn main() -> ! { let gba = agb::Gba::new(); gba.sound.enable(); diff --git a/agb/examples/bitmap3.rs b/agb/examples/bitmap3.rs index 06fb89c6..702dcb82 100644 --- a/agb/examples/bitmap3.rs +++ b/agb/examples/bitmap3.rs @@ -10,8 +10,8 @@ struct Vector2D { y: i32, } -#[no_mangle] -pub fn main() -> ! { +#[agb::entry] +fn main() -> ! { let mut gba = agb::Gba::new(); let mut bitmap = gba.display.video.bitmap3(); let vblank = agb::interrupt::VBlank::get(); diff --git a/agb/examples/bitmap4.rs b/agb/examples/bitmap4.rs index b097e688..09af1688 100644 --- a/agb/examples/bitmap4.rs +++ b/agb/examples/bitmap4.rs @@ -5,8 +5,8 @@ extern crate agb; use agb::display; -#[no_mangle] -pub fn main() -> ! { +#[agb::entry] +fn main() -> ! { let mut gba = agb::Gba::new(); let mut bitmap = gba.display.video.bitmap4(); let vblank = agb::interrupt::VBlank::get(); diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index 8650730d..6cabaccf 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -35,8 +35,8 @@ fn frame_ranger(count: u32, start: u32, end: u32, delay: u32) -> u16 { (((count / delay) % (end + 1 - start)) + start) as u16 } -#[no_mangle] -pub fn main() -> ! { +#[agb::entry] +fn main() -> ! { let map_as_grid: &[[u16; 32]; 32] = unsafe { (&MAP_MAP as *const [u16; 1024] as *const [[u16; 32]; 32]) .as_ref() diff --git a/agb/examples/mixer_basic.rs b/agb/examples/mixer_basic.rs index b26deda6..dd1bafb3 100644 --- a/agb/examples/mixer_basic.rs +++ b/agb/examples/mixer_basic.rs @@ -11,8 +11,8 @@ use agb::Gba; // Music - "I will not let you let me down" by Josh Woodward, free download at http://joshwoodward.com const I_WILL_NOT_LET_YOU_LET_ME_DOWN: &[u8] = include_bytes!("i-will-not-let-you-let-me-down.raw"); -#[no_mangle] -pub fn main() -> ! { +#[agb::entry] +fn main() -> ! { let mut gba = Gba::new(); let mut input = ButtonController::new(); let vblank_provider = agb::interrupt::VBlank::get(); diff --git a/agb/examples/multiple_video.rs b/agb/examples/multiple_video.rs index 174241e1..4b1ffd50 100644 --- a/agb/examples/multiple_video.rs +++ b/agb/examples/multiple_video.rs @@ -10,8 +10,8 @@ struct Vector2D { y: i32, } -#[no_mangle] -pub fn main() -> ! { +#[agb::entry] +fn main() -> ! { let mut gba = agb::Gba::new(); let vblank = agb::interrupt::VBlank::get(); let mut input = agb::input::ButtonController::new(); diff --git a/agb/examples/output.rs b/agb/examples/output.rs index 747c806e..b7d17e96 100644 --- a/agb/examples/output.rs +++ b/agb/examples/output.rs @@ -2,8 +2,9 @@ #![no_main] extern crate agb; -#[no_mangle] -pub fn main() -> ! { + +#[agb::entry] +fn main() -> ! { let count = agb::interrupt::Mutex::new(0); agb::add_interrupt_handler!(agb::interrupt::Interrupt::VBlank, |key| { let mut count = count.lock_with_key(&key); diff --git a/agb/examples/panic.rs b/agb/examples/panic.rs index cc45c782..80fd6d7b 100644 --- a/agb/examples/panic.rs +++ b/agb/examples/panic.rs @@ -5,8 +5,8 @@ extern crate agb; use agb::display; -#[no_mangle] -pub fn main() -> ! { +#[agb::entry] +fn main() -> ! { let mut gba = agb::Gba::new(); let mut bitmap = gba.display.video.bitmap3(); diff --git a/agb/examples/syscall.rs b/agb/examples/syscall.rs index 4b99a11d..6bd8c2fe 100644 --- a/agb/examples/syscall.rs +++ b/agb/examples/syscall.rs @@ -4,8 +4,8 @@ extern crate agb; use agb::{display, syscall}; -#[no_mangle] -pub fn main() -> ! { +#[agb::entry] +fn main() -> ! { let mut gba = agb::Gba::new(); let mut bitmap = gba.display.video.bitmap3(); diff --git a/agb/examples/test_logo.rs b/agb/examples/test_logo.rs index 6eddc02b..dad1d98e 100644 --- a/agb/examples/test_logo.rs +++ b/agb/examples/test_logo.rs @@ -5,8 +5,8 @@ extern crate agb; use agb::display::example_logo; -#[no_mangle] -pub fn main() -> ! { +#[agb::entry] +fn main() -> ! { let mut gba = agb::Gba::new(); let mut gfx = gba.display.video.tiled0(); diff --git a/agb/examples/wave.rs b/agb/examples/wave.rs index c25f3260..fdf59877 100644 --- a/agb/examples/wave.rs +++ b/agb/examples/wave.rs @@ -14,8 +14,8 @@ struct BackCosines { row: usize, } -#[no_mangle] -pub fn main() -> ! { +#[agb::entry] +fn main() -> ! { let mut gba = agb::Gba::new(); let mut gfx = gba.display.video.tiled0(); diff --git a/agb/src/lib.rs b/agb/src/lib.rs index ae534632..70f7ef6e 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -22,6 +22,8 @@ pub mod sound; pub use agb_image_converter::include_gfx; +pub use agb_entry::entry; + mod bitarray; pub mod interrupt; mod memory_mapped; diff --git a/release.sh b/release.sh index 7f9a64f8..bd45f28f 100755 --- a/release.sh +++ b/release.sh @@ -28,6 +28,10 @@ case "$PROJECT" in DIRECTORY="agb-image-converter" TAGNAME="agb-image-converter/v$VERSION" ;; + agb-entry) + DIRECTORY="agb-entry" + TAGNAME="agb-entry/v$VERSION" + ;; mgba-test-runner) DIRECTORY="mgba-test-runner" TAGNAME="mgba-test-runner/v$VERSION"