diff options
-rw-r--r-- | .travis.yml | 88 | ||||
-rw-r--r-- | ci/install.sh | 11 | ||||
-rw-r--r-- | ci/script.sh | 45 | ||||
-rw-r--r-- | xtask/Cargo.toml | 4 | ||||
-rw-r--r-- | xtask/src/lib.rs | 206 | ||||
-rw-r--r-- | xtask/src/main.rs | 204 | ||||
-rw-r--r-- | xtask/tests/ci.rs | 57 |
7 files changed, 284 insertions, 331 deletions
diff --git a/.travis.yml b/.travis.yml index 7f25eae..14e5602 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,80 +1,26 @@ language: rust -matrix: - allow_failures: - - rust: nightly - include: - - env: TARGET=x86_64-unknown-linux-gnu - rust: stable - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - - env: TARGET=thumbv6m-none-eabi - rust: stable - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - - env: TARGET=thumbv7m-none-eabi - rust: stable - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - - env: TARGET=thumbv7em-none-eabi - rust: stable - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - - env: TARGET=thumbv7em-none-eabihf - rust: stable - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - # MSRV - - env: TARGET=thumbv6m-none-eabi - rust: 1.31.0 - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - # MSRV - - env: TARGET=thumbv7m-none-eabi - rust: 1.31.0 - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - # MSRV - - env: TARGET=thumbv7em-none-eabi - rust: 1.31.0 - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - # MSRV - - env: TARGET=thumbv7em-none-eabihf - rust: 1.31.0 - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - - env: TARGET=thumbv6m-none-eabi - rust: nightly - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - - env: TARGET=thumbv7m-none-eabi - rust: nightly - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - - env: TARGET=thumbv7em-none-eabi - rust: nightly - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) +branches: + only: + - master + - staging + - trying - - env: TARGET=thumbv7em-none-eabihf - rust: nightly - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) +rust: + - 1.35.0 + - stable + - nightly - - env: TARGET=thumbv8m.main-none-eabi - 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) - - env: TARGET=thumbv8m.base-none-eabi - rust: nightly - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) +matrix: + allow_failures: + - rust: nightly before_install: set -e -install: - - bash ci/install.sh - script: - - bash ci/script.sh + - cargo test --all after_script: set +e @@ -83,12 +29,6 @@ cache: cargo before_cache: - chmod -R a+r $HOME/.cargo; -branches: - only: - - master - - staging - - trying - notifications: email: on_success: never diff --git a/ci/install.sh b/ci/install.sh deleted file mode 100644 index fb3e52d..0000000 --- a/ci/install.sh +++ /dev/null @@ -1,11 +0,0 @@ -set -euxo pipefail - -main() { - case $TARGET in - thumbv*-none-eabi*) - rustup target add $TARGET - ;; - esac -} - -main diff --git a/ci/script.sh b/ci/script.sh deleted file mode 100644 index fc1e754..0000000 --- a/ci/script.sh +++ /dev/null @@ -1,45 +0,0 @@ -set -euxo pipefail - -main() { - if [ $TRAVIS_RUST_VERSION = nightly ]; then - export RUSTFLAGS="-D warnings" - fi - - cargo check --target $TARGET - - if [ $TRAVIS_RUST_VERSION = nightly ]; then - cargo check --target $TARGET --features 'inline-asm' - fi - - case $TARGET in - thumbv7em-none-eabi*) - cargo check --target $TARGET --features cm7-r0p1 - - if [ $TRAVIS_RUST_VERSION = nightly ]; then - cargo check --target $TARGET --features 'cm7-r0p1 inline-asm' - fi - ;; - - thumbv*-none-eabi*) - ;; - - x86_64-unknown-linux-gnu) - cargo test --target $TARGET - ;; - esac - - if [ $TARGET = x86_64-unknown-linux-gnu ]; then - cargo xtask check-blobs - fi - - if [ $TRAVIS_RUST_VERSION = nightly ]; then - # Get the latest nightly with a working clippy - rustup toolchain uninstall nightly - rustup set profile default - rustup default nightly - rustup target add $TARGET - cargo clippy --target $TARGET -- -D warnings - fi -} - -main diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 9d35164..06c5143 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -5,5 +5,9 @@ authors = ["The Cortex-M Team <cortex-m@teams.rust-embedded.org>"] edition = "2018" publish = false +[[test]] +name = "ci" +harness = false + [dependencies] ar = "0.8.0" diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs new file mode 100644 index 0000000..4575f65 --- /dev/null +++ b/xtask/src/lib.rs @@ -0,0 +1,206 @@ +//! `cargo xtask` automation. +//! +//! Please refer to <https://github.com/matklad/cargo-xtask/> for an explanation of the concept. +//! +//! Also see the docs in `asm.rs`. + +use process::Stdio; +use std::env::current_dir; +use std::{ + collections::BTreeMap, + fs::{self, File}, + process::{self, Command}, +}; + +fn toolchain() -> String { + fs::read_to_string("asm-toolchain") + .unwrap() + .trim() + .to_string() +} + +fn rustc() -> Command { + let mut cmd = Command::new("rustc"); + cmd.arg(format!("+{}", toolchain())); + cmd +} + +fn assemble_really(target: &str, cfgs: &[&str], plugin_lto: bool) { + let mut cmd = rustc(); + + // Set the codegen target. + cmd.arg("--target").arg(target); + // Set all the `--cfg` directives for the target. + cmd.args(cfgs.iter().map(|cfg| format!("--cfg={}", cfg))); + + // We want some level of debuginfo to allow unwinding through the functions. + cmd.arg("-g"); + // We always optimize the assembly shims. There's not really any reason not to. + cmd.arg("-O"); + + // rustc will usually add frame pointers by default to aid with debugging, but that is a high + // overhead for the tiny assembly routines. + cmd.arg("-Cforce-frame-pointers=no"); + + // We don't want any system-specific paths to show up since we ship the result to other users. + // Add `--remap-path-prefix $(pwd)=.`. + let mut dir = current_dir().unwrap().as_os_str().to_os_string(); + dir.push("=."); + cmd.arg("--remap-path-prefix").arg(dir); + + // We let rustc build a single object file, not a staticlib, since the latter pulls in loads of + // code that will never be used (`compiler_builtins` and `core::fmt`, etc.). We build the static + // archive by hand after compiling. + cmd.arg("--emit=obj"); + + if plugin_lto { + // Make artifacts compatible with Linker-Plugin LTO (and incompatible with everything else). + cmd.arg("-Clinker-plugin-lto"); + } + + let file_stub = if plugin_lto { + format!("{}-lto", target) + } else { + target.to_string() + }; + + let obj_file = format!("bin/{}.o", file_stub); + + // Pass output and input file. + cmd.arg("-o").arg(&obj_file); + cmd.arg("asm.rs"); + + println!("{:?}", cmd); + let status = cmd.status().unwrap(); + assert!(status.success()); + + // Archive `target.o` -> `bin/target.a`. + let mut builder = ar::Builder::new(File::create(format!("bin/{}.a", file_stub)).unwrap()); + + // Use `append`, not `append_path`, to avoid adding any filesystem metadata (modification times, + // etc.). + let file = fs::read(&obj_file).unwrap(); + builder + .append( + &ar::Header::new(obj_file.as_bytes().to_vec(), file.len() as u64), + &*file, + ) + .unwrap(); + + fs::remove_file(&obj_file).unwrap(); +} + +fn assemble(target: &str, cfgs: &[&str]) { + assemble_really(target, cfgs, false); + assemble_really(target, cfgs, true); +} + +// `--target` -> `--cfg` list (mirrors what `build.rs` does). +static TARGETS: &[(&str, &[&str])] = &[ + ("thumbv6m-none-eabi", &[]), + ("thumbv7m-none-eabi", &["armv7m"]), + ("thumbv7em-none-eabi", &["armv7m", "armv7em"]), + ("thumbv7em-none-eabihf", &["armv7m", "armv7em", "has_fpu"]), + ("thumbv8m.base-none-eabi", &["armv8m", "armv8m_base"]), + ( + "thumbv8m.main-none-eabi", + &["armv7m", "armv8m", "armv8m_main"], + ), + ( + "thumbv8m.main-none-eabihf", + &["armv7m", "armv8m", "armv8m_main", "has_fpu"], + ), +]; + +pub fn install_targets(targets: &mut dyn Iterator<Item = &str>, toolchain: Option<&str>) { + let mut rustup = Command::new("rustup"); + rustup.arg("target").arg("add").args(targets); + + if let Some(toolchain) = toolchain { + rustup.arg("--toolchain").arg(toolchain); + } + + let status = rustup.status().unwrap(); + assert!(status.success(), "rustup command failed: {:?}", rustup); +} + +pub fn assemble_blobs() { + let mut cmd = rustc(); + cmd.arg("-V"); + cmd.stdout(Stdio::null()); + let status = cmd.status().unwrap(); + let toolchain = toolchain(); + + if !status.success() { + println!( + "asm toolchain {} does not seem to be installed. installing it now.", + toolchain + ); + + let mut rustup = Command::new("rustup"); + let status = rustup.arg("install").arg(&toolchain).status().unwrap(); + assert!(status.success(), "rustup command failed: {:?}", rustup); + } + + install_targets( + &mut TARGETS.iter().map(|(target, _)| *target), + Some(&*toolchain), + ); + + for (target, cfgs) in TARGETS { + println!("building artifacts for {}", target); + assemble(target, cfgs); + } +} + +pub fn check_blobs() { + // Load each `.a` file in `bin` into memory. + let mut files_before = BTreeMap::new(); + for entry in fs::read_dir("bin").unwrap() { + let entry = entry.unwrap(); + if entry.path().extension().unwrap() == "a" { + files_before.insert( + entry + .path() + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_string(), + fs::read(entry.path()).unwrap(), + ); + } + } + + assemble_blobs(); + + let mut files_after = BTreeMap::new(); + for entry in fs::read_dir("bin").unwrap() { + let entry = entry.unwrap(); + if entry.path().extension().unwrap() == "a" { + files_after.insert( + entry + .path() + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_string(), + fs::read(entry.path()).unwrap(), + ); + } + } + + // Ensure they contain the same files. + let before = files_before.keys().collect::<Vec<_>>(); + let after = files_after.keys().collect::<Vec<_>>(); + assert_eq!(before, after); + + for ((file, before), (_, after)) in files_before.iter().zip(files_after.iter()) { + if before != after { + panic!("{} differs between rebuilds", file); + } + } + + println!("Blobs identical."); +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index ab59f57..ec55bf8 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,207 +1,9 @@ -//! `cargo xtask` automation. -//! -//! Please refer to <https://github.com/matklad/cargo-xtask/> for an explanation of the concept. -//! -//! Also see the docs in `asm.rs`. - -use process::Stdio; -use std::env::{self, current_dir}; -use std::{ - collections::BTreeMap, - fs::{self, File}, - process::{self, Command}, -}; - -fn toolchain() -> String { - fs::read_to_string("asm-toolchain") - .unwrap() - .trim() - .to_string() -} - -fn rustc() -> Command { - let mut cmd = Command::new("rustc"); - cmd.arg(format!("+{}", toolchain())); - cmd -} - -fn assemble_really(target: &str, cfgs: &[&str], plugin_lto: bool) { - let mut cmd = rustc(); - - // Set the codegen target. - cmd.arg("--target").arg(target); - // Set all the `--cfg` directives for the target. - cmd.args(cfgs.iter().map(|cfg| format!("--cfg={}", cfg))); - - // We want some level of debuginfo to allow unwinding through the functions. - cmd.arg("-g"); - // We always optimize the assembly shims. There's not really any reason not to. - cmd.arg("-O"); - - // rustc will usually add frame pointers by default to aid with debugging, but that is a high - // overhead for the tiny assembly routines. - cmd.arg("-Cforce-frame-pointers=no"); - - // We don't want any system-specific paths to show up since we ship the result to other users. - // Add `--remap-path-prefix $(pwd)=.`. - let mut dir = current_dir().unwrap().as_os_str().to_os_string(); - dir.push("=."); - cmd.arg("--remap-path-prefix").arg(dir); - - // We let rustc build a single object file, not a staticlib, since the latter pulls in loads of - // code that will never be used (`compiler_builtins` and `core::fmt`, etc.). We build the static - // archive by hand after compiling. - cmd.arg("--emit=obj"); - - if plugin_lto { - // Make artifacts compatible with Linker-Plugin LTO (and incompatible with everything else). - cmd.arg("-Clinker-plugin-lto"); - } - - let file_stub = if plugin_lto { - format!("{}-lto", target) - } else { - target.to_string() - }; - - let obj_file = format!("bin/{}.o", file_stub); - - // Pass output and input file. - cmd.arg("-o").arg(&obj_file); - cmd.arg("asm.rs"); - - println!("{:?}", cmd); - let status = cmd.status().unwrap(); - assert!(status.success()); - - // Archive `target.o` -> `bin/target.a`. - let mut builder = ar::Builder::new(File::create(format!("bin/{}.a", file_stub)).unwrap()); - - // Use `append`, not `append_path`, to avoid adding any filesystem metadata (modification times, - // etc.). - let file = fs::read(&obj_file).unwrap(); - builder - .append( - &ar::Header::new(obj_file.as_bytes().to_vec(), file.len() as u64), - &*file, - ) - .unwrap(); - - fs::remove_file(&obj_file).unwrap(); -} - -fn assemble(target: &str, cfgs: &[&str]) { - assemble_really(target, cfgs, false); - assemble_really(target, cfgs, true); -} - -// `--target` -> `--cfg` list (mirrors what `build.rs` does). -static TARGETS: &[(&str, &[&str])] = &[ - ("thumbv6m-none-eabi", &[]), - ("thumbv7m-none-eabi", &["armv7m"]), - ("thumbv7em-none-eabi", &["armv7m", "armv7em"]), - ("thumbv7em-none-eabihf", &["armv7m", "armv7em", "has_fpu"]), - ("thumbv8m.base-none-eabi", &["armv8m", "armv8m_base"]), - ( - "thumbv8m.main-none-eabi", - &["armv7m", "armv8m", "armv8m_main"], - ), - ( - "thumbv8m.main-none-eabihf", - &["armv7m", "armv8m", "armv8m_main", "has_fpu"], - ), -]; - -fn assemble_blobs() { - let mut cmd = rustc(); - cmd.arg("-V"); - cmd.stdout(Stdio::null()); - let status = cmd.status().unwrap(); - - if !status.success() { - let toolchain = toolchain(); - println!( - "asm toolchain {} does not seem to be installed. installing it now.", - toolchain - ); - - let mut rustup = Command::new("rustup"); - let status = rustup.arg("install").arg(&toolchain).status().unwrap(); - assert!(status.success(), "rustup command failed: {:?}", rustup); - - let mut rustup = Command::new("rustup"); - let status = rustup - .arg("target") - .arg("add") - .args(TARGETS.iter().map(|(target, _)| *target)) - .arg("--toolchain") - .arg(toolchain) - .status() - .unwrap(); - assert!(status.success(), "rustup command failed: {:?}", rustup); - } - - for (target, cfgs) in TARGETS { - println!("building artifacts for {}", target); - assemble(target, cfgs); - } -} - -fn check_blobs() { - // Load each `.a` file in `bin` into memory. - let mut files_before = BTreeMap::new(); - for entry in fs::read_dir("bin").unwrap() { - let entry = entry.unwrap(); - if entry.path().extension().unwrap() == "a" { - files_before.insert( - entry - .path() - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_string(), - fs::read(entry.path()).unwrap(), - ); - } - } - - assemble_blobs(); - - let mut files_after = BTreeMap::new(); - for entry in fs::read_dir("bin").unwrap() { - let entry = entry.unwrap(); - if entry.path().extension().unwrap() == "a" { - files_after.insert( - entry - .path() - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_string(), - fs::read(entry.path()).unwrap(), - ); - } - } - - // Ensure they contain the same files. - let before = files_before.keys().collect::<Vec<_>>(); - let after = files_after.keys().collect::<Vec<_>>(); - assert_eq!(before, after); - - for ((file, before), (_, after)) in files_before.iter().zip(files_after.iter()) { - if before != after { - panic!("{} differs between rebuilds", file); - } - } - - println!("Blobs identical."); -} +use std::{env, process}; +use xtask::{assemble_blobs, check_blobs}; fn main() { let subcommand = env::args().skip(1).next(); - match subcommand.as_deref() { + match subcommand.as_ref().map(|s| &**s) { Some("assemble") => assemble_blobs(), Some("check-blobs") => check_blobs(), _ => { diff --git a/xtask/tests/ci.rs b/xtask/tests/ci.rs new file mode 100644 index 0000000..48356e4 --- /dev/null +++ b/xtask/tests/ci.rs @@ -0,0 +1,57 @@ +use std::process::Command; +use std::{env, str}; +use xtask::{check_blobs, install_targets}; + +static TARGETS: &[&str] = &[ + "thumbv6m-none-eabi", + "thumbv7m-none-eabi", + "thumbv7em-none-eabi", + "thumbv7em-none-eabihf", + "thumbv8m.base-none-eabi", + "thumbv8m.main-none-eabi", + "thumbv8m.main-none-eabihf", +]; + +fn build(target: &str, features: &[&str]) { + println!("building for {} {:?}", target, features); + let mut cargo = Command::new("cargo"); + cargo.args(&["build", "--target", target]); + for feat in features { + cargo.args(&["--features", *feat]); + } + + let status = cargo.status().unwrap(); + assert!(status.success()); +} + +fn main() { + // Tests execute in the containing crate's root dir, `cd ..` so that we find `asm` etc. + env::set_current_dir("..").unwrap(); + + install_targets(&mut TARGETS.iter().cloned(), None); + + // Check that the ASM blobs are up-to-date. + check_blobs(); + + let output = Command::new("rustc").arg("-V").output().unwrap(); + let is_nightly = str::from_utf8(&output.stdout).unwrap().contains("nightly"); + + // Build `cortex-m` for each supported target. + for target in TARGETS { + build(*target, &[]); + + if is_nightly { + // This may fail when nightly breaks. That's fine, the CI job isn't essential. + build(*target, &["inline-asm"]); + } + + if target.starts_with("thumbv7em") { + // These can target Cortex-M7s, which have an errata workaround. + build(*target, &["cm7-r0p1"]); + + if is_nightly { + build(*target, &["inline-asm", "cm7-r0p1"]); + } + } + } +} |