mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-26 05:29:38 +01:00
Make builds reproducible
This is done by using `BTreeMap`s and `BTreeSet`s to get deterministic ordering. Also updated the CI job to check reproducibility of all examples.
This commit is contained in:
parent
fdba26525c
commit
2f89688ca9
5 changed files with 66 additions and 38 deletions
|
@ -23,7 +23,10 @@ matrix:
|
||||||
rust: nightly
|
rust: nightly
|
||||||
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
|
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
|
||||||
|
|
||||||
before_install: set -e
|
before_install:
|
||||||
|
- set -e
|
||||||
|
- sudo apt-get update
|
||||||
|
- sudo apt-get install -y binutils-arm-none-eabi
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- bash ci/install.sh
|
- bash ci/install.sh
|
||||||
|
|
45
ci/script.sh
45
ci/script.sh
|
@ -81,20 +81,45 @@ main() {
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $ex != types ]; then
|
test_arm_example() {
|
||||||
cargo run --example $ex --target $T | \
|
local EXAMPLE=$1
|
||||||
diff -u ci/expected/$ex.run -
|
local TARGET=$2
|
||||||
|
local BUILD_MODE=$3
|
||||||
|
local FEATURES=$4
|
||||||
|
|
||||||
cargo run --example $ex --target $T --release | \
|
if [ $BUILD_MODE = "release" ]; then
|
||||||
diff -u ci/expected/$ex.run -
|
local RELEASE_FLAG="--release"
|
||||||
|
else
|
||||||
|
local RELEASE_FLAG=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$FEATURES" ]; then
|
||||||
|
local FEATURES_FLAG="--features $FEATURES"
|
||||||
|
else
|
||||||
|
local FEATURES_FLAG=""
|
||||||
|
fi
|
||||||
|
local CARGO_FLAGS="--example $EXAMPLE --target $TARGET $RELEASE_FLAG $FEATURES_FLAG"
|
||||||
|
|
||||||
|
cargo run $CARGO_FLAGS | diff -u ci/expected/$EXAMPLE.run -
|
||||||
|
arm-none-eabi-objcopy -O ihex target/$TARGET/$BUILD_MODE/examples/$EXAMPLE ${EXAMPLE}_1.hex
|
||||||
|
|
||||||
|
# build again to ensure that the build is reproducable
|
||||||
|
cargo clean
|
||||||
|
cargo build $CARGO_FLAGS
|
||||||
|
arm-none-eabi-objcopy -O ihex target/$TARGET/$BUILD_MODE/examples/$EXAMPLE ${EXAMPLE}_2.hex
|
||||||
|
|
||||||
|
# compare results of both builds
|
||||||
|
cmp ${EXAMPLE}_1.hex ${EXAMPLE}_2.hex
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ $ex != types ]; then
|
||||||
|
test_arm_example $ex $T "debug" ""
|
||||||
|
test_arm_example $ex $T "release" ""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $TARGET != thumbv6m-none-eabi ]; then
|
if [ $TARGET != thumbv6m-none-eabi ]; then
|
||||||
cargo run --features timer-queue --example $ex --target $T | \
|
test_arm_example $ex $T "debug" "timer-queue"
|
||||||
diff -u ci/expected/$ex.run -
|
test_arm_example $ex $T "release" "timer-queue"
|
||||||
|
|
||||||
cargo run --features timer-queue --example $ex --target $T --release | \
|
|
||||||
diff -u ci/expected/$ex.run -
|
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
esac
|
esac
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
cmp,
|
cmp,
|
||||||
collections::{HashMap, HashSet},
|
collections::{BTreeMap, HashMap, HashSet},
|
||||||
};
|
};
|
||||||
|
|
||||||
use syn::{Attribute, Ident, Type};
|
use syn::{Attribute, Ident, Type};
|
||||||
|
@ -65,7 +65,7 @@ pub struct Dispatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Priority -> Dispatcher
|
/// Priority -> Dispatcher
|
||||||
pub type Dispatchers = HashMap<u8, Dispatcher>;
|
pub type Dispatchers = BTreeMap<u8, Dispatcher>;
|
||||||
|
|
||||||
pub type Capacities = HashMap<Ident, u8>;
|
pub type Capacities = HashMap<Ident, u8>;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::{BTreeMap, HashMap},
|
||||||
sync::atomic::{AtomicUsize, Ordering},
|
sync::atomic::{AtomicUsize, Ordering},
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
@ -20,13 +20,13 @@ use crate::{
|
||||||
// NOTE to avoid polluting the user namespaces we map some identifiers to pseudo-hygienic names.
|
// NOTE to avoid polluting the user namespaces we map some identifiers to pseudo-hygienic names.
|
||||||
// In some instances we also use the pseudo-hygienic names for safety, for example the user should
|
// In some instances we also use the pseudo-hygienic names for safety, for example the user should
|
||||||
// not modify the priority field of resources.
|
// not modify the priority field of resources.
|
||||||
type Aliases = HashMap<Ident, Ident>;
|
type Aliases = BTreeMap<Ident, Ident>;
|
||||||
|
|
||||||
struct Context {
|
struct Context {
|
||||||
// Alias
|
// Alias
|
||||||
#[cfg(feature = "timer-queue")]
|
#[cfg(feature = "timer-queue")]
|
||||||
baseline: Ident,
|
baseline: Ident,
|
||||||
dispatchers: HashMap<u8, Dispatcher>,
|
dispatchers: BTreeMap<u8, Dispatcher>,
|
||||||
// Alias (`fn`)
|
// Alias (`fn`)
|
||||||
idle: Ident,
|
idle: Ident,
|
||||||
// Alias (`fn`)
|
// Alias (`fn`)
|
||||||
|
@ -41,7 +41,7 @@ struct Context {
|
||||||
schedule_enum: Ident,
|
schedule_enum: Ident,
|
||||||
// Task -> Alias (`fn`)
|
// Task -> Alias (`fn`)
|
||||||
schedule_fn: Aliases,
|
schedule_fn: Aliases,
|
||||||
tasks: HashMap<Ident, Task>,
|
tasks: BTreeMap<Ident, Task>,
|
||||||
// Alias (`struct` / `static mut`)
|
// Alias (`struct` / `static mut`)
|
||||||
timer_queue: Ident,
|
timer_queue: Ident,
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ impl Default for Context {
|
||||||
Context {
|
Context {
|
||||||
#[cfg(feature = "timer-queue")]
|
#[cfg(feature = "timer-queue")]
|
||||||
baseline: mk_ident(None),
|
baseline: mk_ident(None),
|
||||||
dispatchers: HashMap::new(),
|
dispatchers: BTreeMap::new(),
|
||||||
idle: mk_ident(Some("idle")),
|
idle: mk_ident(Some("idle")),
|
||||||
init: mk_ident(Some("init")),
|
init: mk_ident(Some("init")),
|
||||||
priority: mk_ident(None),
|
priority: mk_ident(None),
|
||||||
|
@ -74,7 +74,7 @@ impl Default for Context {
|
||||||
resources: HashMap::new(),
|
resources: HashMap::new(),
|
||||||
schedule_enum: mk_ident(None),
|
schedule_enum: mk_ident(None),
|
||||||
schedule_fn: Aliases::new(),
|
schedule_fn: Aliases::new(),
|
||||||
tasks: HashMap::new(),
|
tasks: BTreeMap::new(),
|
||||||
timer_queue: mk_ident(None),
|
timer_queue: mk_ident(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2034,7 +2034,7 @@ fn mk_ident(name: Option<&str>) -> Ident {
|
||||||
}
|
}
|
||||||
|
|
||||||
// `once = true` means that these locals will be called from a function that will run *once*
|
// `once = true` means that these locals will be called from a function that will run *once*
|
||||||
fn mk_locals(locals: &HashMap<Ident, Static>, once: bool) -> proc_macro2::TokenStream {
|
fn mk_locals(locals: &BTreeMap<Ident, Static>, once: bool) -> proc_macro2::TokenStream {
|
||||||
let lt = if once { Some(quote!('static)) } else { None };
|
let lt = if once { Some(quote!('static)) } else { None };
|
||||||
|
|
||||||
let locals = locals
|
let locals = locals
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{BTreeMap, BTreeSet},
|
||||||
iter, u8,
|
iter, u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -120,10 +120,10 @@ impl App {
|
||||||
pub fn parse(items: Vec<Item>, args: AppArgs) -> parse::Result<Self> {
|
pub fn parse(items: Vec<Item>, args: AppArgs) -> parse::Result<Self> {
|
||||||
let mut idle = None;
|
let mut idle = None;
|
||||||
let mut init = None;
|
let mut init = None;
|
||||||
let mut exceptions = HashMap::new();
|
let mut exceptions = BTreeMap::new();
|
||||||
let mut interrupts = HashMap::new();
|
let mut interrupts = BTreeMap::new();
|
||||||
let mut resources = HashMap::new();
|
let mut resources = BTreeMap::new();
|
||||||
let mut tasks = HashMap::new();
|
let mut tasks = BTreeMap::new();
|
||||||
let mut free_interrupts = None;
|
let mut free_interrupts = None;
|
||||||
|
|
||||||
for item in items {
|
for item in items {
|
||||||
|
@ -418,25 +418,25 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Idents = HashSet<Ident>;
|
pub type Idents = BTreeSet<Ident>;
|
||||||
|
|
||||||
pub type Exceptions = HashMap<Ident, Exception>;
|
pub type Exceptions = BTreeMap<Ident, Exception>;
|
||||||
|
|
||||||
pub type Interrupts = HashMap<Ident, Interrupt>;
|
pub type Interrupts = BTreeMap<Ident, Interrupt>;
|
||||||
|
|
||||||
pub type Resources = HashMap<Ident, Resource>;
|
pub type Resources = BTreeMap<Ident, Resource>;
|
||||||
|
|
||||||
pub type Statics = Vec<ItemStatic>;
|
pub type Statics = Vec<ItemStatic>;
|
||||||
|
|
||||||
pub type Tasks = HashMap<Ident, Task>;
|
pub type Tasks = BTreeMap<Ident, Task>;
|
||||||
|
|
||||||
pub type FreeInterrupts = HashMap<Ident, FreeInterrupt>;
|
pub type FreeInterrupts = BTreeMap<Ident, FreeInterrupt>;
|
||||||
|
|
||||||
pub struct Idle {
|
pub struct Idle {
|
||||||
pub args: IdleArgs,
|
pub args: IdleArgs,
|
||||||
pub attrs: Vec<Attribute>,
|
pub attrs: Vec<Attribute>,
|
||||||
pub unsafety: Option<Token![unsafe]>,
|
pub unsafety: Option<Token![unsafe]>,
|
||||||
pub statics: HashMap<Ident, Static>,
|
pub statics: BTreeMap<Ident, Static>,
|
||||||
pub stmts: Vec<Stmt>,
|
pub stmts: Vec<Stmt>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -607,7 +607,7 @@ pub struct Init {
|
||||||
pub args: InitArgs,
|
pub args: InitArgs,
|
||||||
pub attrs: Vec<Attribute>,
|
pub attrs: Vec<Attribute>,
|
||||||
pub unsafety: Option<Token![unsafe]>,
|
pub unsafety: Option<Token![unsafe]>,
|
||||||
pub statics: HashMap<Ident, Static>,
|
pub statics: BTreeMap<Ident, Static>,
|
||||||
pub stmts: Vec<Stmt>,
|
pub stmts: Vec<Stmt>,
|
||||||
// TODO remove in v0.5.x
|
// TODO remove in v0.5.x
|
||||||
pub assigns: Vec<Assign>,
|
pub assigns: Vec<Assign>,
|
||||||
|
@ -703,7 +703,7 @@ pub struct Exception {
|
||||||
pub args: ExceptionArgs,
|
pub args: ExceptionArgs,
|
||||||
pub attrs: Vec<Attribute>,
|
pub attrs: Vec<Attribute>,
|
||||||
pub unsafety: Option<Token![unsafe]>,
|
pub unsafety: Option<Token![unsafe]>,
|
||||||
pub statics: HashMap<Ident, Static>,
|
pub statics: BTreeMap<Ident, Static>,
|
||||||
pub stmts: Vec<Stmt>,
|
pub stmts: Vec<Stmt>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -791,7 +791,7 @@ pub struct Interrupt {
|
||||||
pub args: InterruptArgs,
|
pub args: InterruptArgs,
|
||||||
pub attrs: Vec<Attribute>,
|
pub attrs: Vec<Attribute>,
|
||||||
pub unsafety: Option<Token![unsafe]>,
|
pub unsafety: Option<Token![unsafe]>,
|
||||||
pub statics: HashMap<Ident, Static>,
|
pub statics: BTreeMap<Ident, Static>,
|
||||||
pub stmts: Vec<Stmt>,
|
pub stmts: Vec<Stmt>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1070,8 +1070,8 @@ pub struct Static {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Static {
|
impl Static {
|
||||||
fn parse(items: Vec<ItemStatic>) -> parse::Result<HashMap<Ident, Static>> {
|
fn parse(items: Vec<ItemStatic>) -> parse::Result<BTreeMap<Ident, Static>> {
|
||||||
let mut statics = HashMap::new();
|
let mut statics = BTreeMap::new();
|
||||||
|
|
||||||
for item in items {
|
for item in items {
|
||||||
if statics.contains_key(&item.ident) {
|
if statics.contains_key(&item.ident) {
|
||||||
|
@ -1104,7 +1104,7 @@ pub struct Task {
|
||||||
pub attrs: Vec<Attribute>,
|
pub attrs: Vec<Attribute>,
|
||||||
pub unsafety: Option<Token![unsafe]>,
|
pub unsafety: Option<Token![unsafe]>,
|
||||||
pub inputs: Vec<ArgCaptured>,
|
pub inputs: Vec<ArgCaptured>,
|
||||||
pub statics: HashMap<Ident, Static>,
|
pub statics: BTreeMap<Ident, Static>,
|
||||||
pub stmts: Vec<Stmt>,
|
pub stmts: Vec<Stmt>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue