From 2f89688ca974944781878a74873801597c0b1f11 Mon Sep 17 00:00:00 2001 From: Hugo van der Wijst Date: Tue, 15 Jan 2019 22:42:50 -0800 Subject: [PATCH] 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. --- .travis.yml | 5 ++++- ci/script.sh | 45 +++++++++++++++++++++++++++++++++---------- macros/src/analyze.rs | 4 ++-- macros/src/codegen.rs | 14 +++++++------- macros/src/syntax.rs | 36 +++++++++++++++++----------------- 5 files changed, 66 insertions(+), 38 deletions(-) diff --git a/.travis.yml b/.travis.yml index 481f8fea88..31d10e84ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,10 @@ matrix: rust: nightly 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: - bash ci/install.sh diff --git a/ci/script.sh b/ci/script.sh index 645db3aacd..ab7f34b24b 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -81,20 +81,45 @@ main() { continue fi - if [ $ex != types ]; then - cargo run --example $ex --target $T | \ - diff -u ci/expected/$ex.run - + test_arm_example() { + local EXAMPLE=$1 + local TARGET=$2 + local BUILD_MODE=$3 + local FEATURES=$4 - cargo run --example $ex --target $T --release | \ - diff -u ci/expected/$ex.run - + if [ $BUILD_MODE = "release" ]; then + 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 if [ $TARGET != thumbv6m-none-eabi ]; then - cargo run --features timer-queue --example $ex --target $T | \ - diff -u ci/expected/$ex.run - - - cargo run --features timer-queue --example $ex --target $T --release | \ - diff -u ci/expected/$ex.run - + test_arm_example $ex $T "debug" "timer-queue" + test_arm_example $ex $T "release" "timer-queue" fi done esac diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs index 19575b7728..cfd8ebc94a 100644 --- a/macros/src/analyze.rs +++ b/macros/src/analyze.rs @@ -1,6 +1,6 @@ use std::{ cmp, - collections::{HashMap, HashSet}, + collections::{BTreeMap, HashMap, HashSet}, }; use syn::{Attribute, Ident, Type}; @@ -65,7 +65,7 @@ pub struct Dispatcher { } /// Priority -> Dispatcher -pub type Dispatchers = HashMap; +pub type Dispatchers = BTreeMap; pub type Capacities = HashMap; diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index af3def6476..a96eaef99d 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -2,7 +2,7 @@ use proc_macro::TokenStream; use std::{ - collections::HashMap, + collections::{BTreeMap, HashMap}, sync::atomic::{AtomicUsize, Ordering}, 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. // In some instances we also use the pseudo-hygienic names for safety, for example the user should // not modify the priority field of resources. -type Aliases = HashMap; +type Aliases = BTreeMap; struct Context { // Alias #[cfg(feature = "timer-queue")] baseline: Ident, - dispatchers: HashMap, + dispatchers: BTreeMap, // Alias (`fn`) idle: Ident, // Alias (`fn`) @@ -41,7 +41,7 @@ struct Context { schedule_enum: Ident, // Task -> Alias (`fn`) schedule_fn: Aliases, - tasks: HashMap, + tasks: BTreeMap, // Alias (`struct` / `static mut`) timer_queue: Ident, } @@ -66,7 +66,7 @@ impl Default for Context { Context { #[cfg(feature = "timer-queue")] baseline: mk_ident(None), - dispatchers: HashMap::new(), + dispatchers: BTreeMap::new(), idle: mk_ident(Some("idle")), init: mk_ident(Some("init")), priority: mk_ident(None), @@ -74,7 +74,7 @@ impl Default for Context { resources: HashMap::new(), schedule_enum: mk_ident(None), schedule_fn: Aliases::new(), - tasks: HashMap::new(), + tasks: BTreeMap::new(), 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* -fn mk_locals(locals: &HashMap, once: bool) -> proc_macro2::TokenStream { +fn mk_locals(locals: &BTreeMap, once: bool) -> proc_macro2::TokenStream { let lt = if once { Some(quote!('static)) } else { None }; let locals = locals diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs index ad7d8bde34..581eb8319b 100644 --- a/macros/src/syntax.rs +++ b/macros/src/syntax.rs @@ -1,5 +1,5 @@ use std::{ - collections::{HashMap, HashSet}, + collections::{BTreeMap, BTreeSet}, iter, u8, }; @@ -120,10 +120,10 @@ impl App { pub fn parse(items: Vec, args: AppArgs) -> parse::Result { let mut idle = None; let mut init = None; - let mut exceptions = HashMap::new(); - let mut interrupts = HashMap::new(); - let mut resources = HashMap::new(); - let mut tasks = HashMap::new(); + let mut exceptions = BTreeMap::new(); + let mut interrupts = BTreeMap::new(); + let mut resources = BTreeMap::new(); + let mut tasks = BTreeMap::new(); let mut free_interrupts = None; for item in items { @@ -418,25 +418,25 @@ impl App { } } -pub type Idents = HashSet; +pub type Idents = BTreeSet; -pub type Exceptions = HashMap; +pub type Exceptions = BTreeMap; -pub type Interrupts = HashMap; +pub type Interrupts = BTreeMap; -pub type Resources = HashMap; +pub type Resources = BTreeMap; pub type Statics = Vec; -pub type Tasks = HashMap; +pub type Tasks = BTreeMap; -pub type FreeInterrupts = HashMap; +pub type FreeInterrupts = BTreeMap; pub struct Idle { pub args: IdleArgs, pub attrs: Vec, pub unsafety: Option, - pub statics: HashMap, + pub statics: BTreeMap, pub stmts: Vec, } @@ -607,7 +607,7 @@ pub struct Init { pub args: InitArgs, pub attrs: Vec, pub unsafety: Option, - pub statics: HashMap, + pub statics: BTreeMap, pub stmts: Vec, // TODO remove in v0.5.x pub assigns: Vec, @@ -703,7 +703,7 @@ pub struct Exception { pub args: ExceptionArgs, pub attrs: Vec, pub unsafety: Option, - pub statics: HashMap, + pub statics: BTreeMap, pub stmts: Vec, } @@ -791,7 +791,7 @@ pub struct Interrupt { pub args: InterruptArgs, pub attrs: Vec, pub unsafety: Option, - pub statics: HashMap, + pub statics: BTreeMap, pub stmts: Vec, } @@ -1070,8 +1070,8 @@ pub struct Static { } impl Static { - fn parse(items: Vec) -> parse::Result> { - let mut statics = HashMap::new(); + fn parse(items: Vec) -> parse::Result> { + let mut statics = BTreeMap::new(); for item in items { if statics.contains_key(&item.ident) { @@ -1104,7 +1104,7 @@ pub struct Task { pub attrs: Vec, pub unsafety: Option, pub inputs: Vec, - pub statics: HashMap, + pub statics: BTreeMap, pub stmts: Vec, }