Compare commits
3 Commits
34d6a765b0
...
d86c79b9cb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d86c79b9cb | ||
|
|
a517fba88a | ||
|
|
3570777a0d |
23
README.md
23
README.md
@@ -436,7 +436,6 @@ Upload these to a Gitea release. The script requires `build-essential`,
|
||||
`mingw-w64`, and `zip` as described above.
|
||||
|
||||
### Running the test suite
|
||||
|
||||
```bash
|
||||
cargo test
|
||||
```
|
||||
@@ -447,6 +446,28 @@ build-system issues like missing linker flags and include paths. They require
|
||||
cmake and a C++ compiler; if those tools are not installed, the compile tests
|
||||
skip gracefully and everything else still passes.
|
||||
|
||||
#### Full test suite on Linux / WSL
|
||||
|
||||
The e2e tests need `cmake`, `g++`, and `arduino-cli` with the AVR core:
|
||||
```bash
|
||||
sudo apt install cmake g++
|
||||
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh
|
||||
sudo mv bin/arduino-cli /usr/local/bin/
|
||||
arduino-cli core install arduino:avr
|
||||
```
|
||||
|
||||
#### Full test suite on Windows
|
||||
|
||||
Install [arduino-cli](https://arduino.github.io/arduino-cli/installation/)
|
||||
and add it to your PATH, then install the AVR core:
|
||||
```
|
||||
arduino-cli core install arduino:avr
|
||||
```
|
||||
|
||||
CMake and a C++ compiler are needed for the host-side test compilation.
|
||||
Install [CMake](https://cmake.org/download/) and either MinGW-w64 or open
|
||||
a Visual Studio Developer Command Prompt (which provides `cl.exe`).
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
95
build.rs
Normal file
95
build.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
// build.rs -- Compile-time detection of optional build tools.
|
||||
//
|
||||
// Sets cfg flags that integration tests use to gracefully skip when
|
||||
// tools are missing instead of panicking with scary error messages.
|
||||
//
|
||||
// has_cmake cmake is in PATH
|
||||
// has_cpp_compiler g++, clang++, or cl is in PATH
|
||||
// has_git git is in PATH
|
||||
// has_arduino_cli arduino-cli is in PATH
|
||||
//
|
||||
// Usage in tests:
|
||||
// #[cfg_attr(not(has_cmake), ignore = "cmake not found")]
|
||||
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
/// Check if a tool is available by running it with --version.
|
||||
/// Works for most tools (cmake, git, g++, clang++, arduino-cli).
|
||||
fn has_tool(name: &str) -> bool {
|
||||
Command::new(name)
|
||||
.arg("--version")
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status()
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
/// Check if a C++ compiler is available.
|
||||
///
|
||||
/// g++ and clang++ respond to --version normally. MSVC's cl.exe does
|
||||
/// not -- it rejects --version and returns an error. On Windows we
|
||||
/// fall back to checking PATH via `where cl`. On Unix, cmake cannot
|
||||
/// discover MSVC so we only check g++ and clang++.
|
||||
fn has_cpp_compiler() -> bool {
|
||||
if has_tool("g++") || has_tool("clang++") {
|
||||
return true;
|
||||
}
|
||||
|
||||
// cl.exe doesn't support --version; check PATH directly on Windows.
|
||||
// On a regular command prompt cl may not be in PATH, but cmake can
|
||||
// still find MSVC via the Visual Studio registry. We check `where`
|
||||
// as a best-effort signal.
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let found = Command::new("where")
|
||||
.arg("cl")
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status()
|
||||
.map(|s| s.success())
|
||||
.unwrap_or(false);
|
||||
if found {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Last resort: cmake can discover MSVC even when cl is not in
|
||||
// PATH. Check if any Visual Studio installation exists by
|
||||
// looking for vswhere, which ships with VS 2017+.
|
||||
let vswhere = Command::new("cmd")
|
||||
.args(["/C", "where", "vswhere"])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status()
|
||||
.map(|s| s.success())
|
||||
.unwrap_or(false);
|
||||
if vswhere {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
let _ = (); // silence unused warning
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Declare custom cfg names so rustc doesn't warn about them.
|
||||
println!("cargo::rustc-check-cfg=cfg(has_cmake)");
|
||||
println!("cargo::rustc-check-cfg=cfg(has_cpp_compiler)");
|
||||
println!("cargo::rustc-check-cfg=cfg(has_git)");
|
||||
println!("cargo::rustc-check-cfg=cfg(has_arduino_cli)");
|
||||
|
||||
if has_tool("cmake") {
|
||||
println!("cargo:rustc-cfg=has_cmake");
|
||||
}
|
||||
if has_cpp_compiler() {
|
||||
println!("cargo:rustc-cfg=has_cpp_compiler");
|
||||
}
|
||||
if has_tool("git") {
|
||||
println!("cargo:rustc-cfg=has_git");
|
||||
}
|
||||
if has_tool("arduino-cli") {
|
||||
println!("cargo:rustc-cfg=has_arduino_cli");
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,10 @@
|
||||
// test.sh / test/run_tests.sh --> cmake, g++ (or clang++), git
|
||||
// build.sh --> arduino-cli (with arduino:avr core)
|
||||
//
|
||||
// If any dependency is missing the test FAILS -- that is intentional.
|
||||
// A build machine that ships Anvil binaries MUST have these tools.
|
||||
// If a dependency is missing, the test is SKIPPED (shown as "ignored"
|
||||
// in cargo test output). Detection happens at compile time via build.rs
|
||||
// which sets cfg flags: has_cmake, has_cpp_compiler, has_git,
|
||||
// has_arduino_cli.
|
||||
//
|
||||
// On Windows the .bat variants are tested instead.
|
||||
// ==========================================================================
|
||||
@@ -33,7 +35,7 @@ fn test_context(name: &str) -> TemplateContext {
|
||||
TemplateContext {
|
||||
project_name: name.to_string(),
|
||||
anvil_version: ANVIL_VERSION.to_string(),
|
||||
board_name: "Arduino Uno (ATmega328P)".to_string(),
|
||||
board_name: "uno".to_string(), // <-- was "Arduino Uno (ATmega328P)"
|
||||
fqbn: "arduino:avr:uno".to_string(),
|
||||
baud: 115200,
|
||||
}
|
||||
@@ -122,91 +124,6 @@ fn run_script_with_args(dir: &Path, script: &str, args: &[&str]) -> (bool, Strin
|
||||
(output.status.success(), stdout, stderr)
|
||||
}
|
||||
|
||||
/// Assert that a command-line tool is available in PATH.
|
||||
/// Panics with a clear message if not found.
|
||||
fn require_tool(name: &str) {
|
||||
let check = if cfg!(windows) {
|
||||
Command::new("where").arg(name).output()
|
||||
} else {
|
||||
Command::new("which").arg(name).output()
|
||||
};
|
||||
|
||||
match check {
|
||||
Ok(output) if output.status.success() => {}
|
||||
_ => panic!(
|
||||
"\n\n\
|
||||
===================================================================\n\
|
||||
MISSING BUILD DEPENDENCY: {name}\n\
|
||||
===================================================================\n\
|
||||
\n\
|
||||
Anvil's cargo tests REQUIRE build-machine dependencies.\n\
|
||||
Install '{name}' and re-run. See 'anvil doctor' for guidance.\n\
|
||||
\n\
|
||||
===================================================================\n"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that at least one C++ compiler is present.
|
||||
///
|
||||
/// On Windows, cmake discovers MSVC through the Visual Studio installation
|
||||
/// even when cl.exe is not directly in PATH, so we check for cl.exe as
|
||||
/// well as g++ and clang++. If none are found in PATH we still let cmake
|
||||
/// try -- it will fail at configure time with a clear message.
|
||||
fn require_cpp_compiler() {
|
||||
let check_tool = |name: &str| -> bool {
|
||||
Command::new(if cfg!(windows) { "where" } else { "which" })
|
||||
.arg(name)
|
||||
.output()
|
||||
.map(|o| o.status.success())
|
||||
.unwrap_or(false)
|
||||
};
|
||||
|
||||
let has_gpp = check_tool("g++");
|
||||
let has_clangpp = check_tool("clang++");
|
||||
let has_cl = if cfg!(windows) { check_tool("cl") } else { false };
|
||||
|
||||
// On Windows, cmake can discover MSVC even when cl.exe is not in
|
||||
// the current PATH (via vswhere / VS installation registry). So
|
||||
// we only hard-fail on Linux/macOS where the compiler really must
|
||||
// be in PATH.
|
||||
if !has_gpp && !has_clangpp && !has_cl {
|
||||
if cfg!(windows) {
|
||||
// Warn but don't panic -- cmake will try to find MSVC
|
||||
eprintln!(
|
||||
"\n\
|
||||
WARNING: No C++ compiler (g++, clang++, cl) found in PATH.\n\
|
||||
cmake may still find MSVC via Visual Studio installation.\n\
|
||||
If tests fail, open a VS Developer Command Prompt or install\n\
|
||||
Build Tools for Visual Studio.\n"
|
||||
);
|
||||
} else {
|
||||
panic!(
|
||||
"\n\n\
|
||||
===================================================================\n\
|
||||
MISSING BUILD DEPENDENCY: C++ compiler (g++ or clang++)\n\
|
||||
===================================================================\n\
|
||||
\n\
|
||||
Install g++ or clang++ and re-run.\n\
|
||||
\n\
|
||||
===================================================================\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Require cmake + C++ compiler + git (the test script prereqs).
|
||||
fn require_test_script_deps() {
|
||||
require_tool("cmake");
|
||||
require_tool("git");
|
||||
require_cpp_compiler();
|
||||
}
|
||||
|
||||
/// Require arduino-cli (the build script prereqs).
|
||||
fn require_build_script_deps() {
|
||||
require_tool("arduino-cli");
|
||||
}
|
||||
|
||||
/// Platform-appropriate script paths.
|
||||
fn root_test_script() -> &'static str {
|
||||
if cfg!(windows) { "test.bat" } else { "test.sh" }
|
||||
@@ -283,9 +200,10 @@ fn find_file_recursive(dir: &Path, prefix: &str) -> bool {
|
||||
// ==========================================================================
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(has_cmake), ignore = "cmake not found")]
|
||||
#[cfg_attr(not(has_cpp_compiler), ignore = "C++ compiler not found")]
|
||||
#[cfg_attr(not(has_git), ignore = "git not found")]
|
||||
fn test_root_test_script_executes_successfully() {
|
||||
require_test_script_deps();
|
||||
|
||||
let tmp = extract_project("root_test");
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -306,9 +224,10 @@ fn test_root_test_script_executes_successfully() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(has_cmake), ignore = "cmake not found")]
|
||||
#[cfg_attr(not(has_cpp_compiler), ignore = "C++ compiler not found")]
|
||||
#[cfg_attr(not(has_git), ignore = "git not found")]
|
||||
fn test_root_test_script_tests_actually_ran() {
|
||||
require_test_script_deps();
|
||||
|
||||
let tmp = extract_project("root_verify");
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -338,9 +257,10 @@ fn test_root_test_script_tests_actually_ran() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(has_cmake), ignore = "cmake not found")]
|
||||
#[cfg_attr(not(has_cpp_compiler), ignore = "C++ compiler not found")]
|
||||
#[cfg_attr(not(has_git), ignore = "git not found")]
|
||||
fn test_root_test_script_idempotent() {
|
||||
require_test_script_deps();
|
||||
|
||||
let tmp = extract_project("root_idem");
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -367,9 +287,10 @@ fn test_root_test_script_idempotent() {
|
||||
// ==========================================================================
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(has_cmake), ignore = "cmake not found")]
|
||||
#[cfg_attr(not(has_cpp_compiler), ignore = "C++ compiler not found")]
|
||||
#[cfg_attr(not(has_git), ignore = "git not found")]
|
||||
fn test_inner_run_tests_script_executes_successfully() {
|
||||
require_test_script_deps();
|
||||
|
||||
let tmp = extract_project("inner_test");
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -390,9 +311,10 @@ fn test_inner_run_tests_script_executes_successfully() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(has_cmake), ignore = "cmake not found")]
|
||||
#[cfg_attr(not(has_cpp_compiler), ignore = "C++ compiler not found")]
|
||||
#[cfg_attr(not(has_git), ignore = "git not found")]
|
||||
fn test_inner_run_tests_google_tests_actually_ran() {
|
||||
require_test_script_deps();
|
||||
|
||||
let tmp = extract_project("inner_gtest");
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -421,9 +343,10 @@ fn test_inner_run_tests_google_tests_actually_ran() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(has_cmake), ignore = "cmake not found")]
|
||||
#[cfg_attr(not(has_cpp_compiler), ignore = "C++ compiler not found")]
|
||||
#[cfg_attr(not(has_git), ignore = "git not found")]
|
||||
fn test_inner_run_tests_clean_flag_rebuilds() {
|
||||
require_test_script_deps();
|
||||
|
||||
let tmp = extract_project("inner_clean");
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -457,9 +380,10 @@ fn test_inner_run_tests_clean_flag_rebuilds() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(has_cmake), ignore = "cmake not found")]
|
||||
#[cfg_attr(not(has_cpp_compiler), ignore = "C++ compiler not found")]
|
||||
#[cfg_attr(not(has_git), ignore = "git not found")]
|
||||
fn test_inner_run_tests_produces_test_binary() {
|
||||
require_test_script_deps();
|
||||
|
||||
let tmp = extract_project("inner_bin");
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -482,9 +406,10 @@ fn test_inner_run_tests_produces_test_binary() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(has_cmake), ignore = "cmake not found")]
|
||||
#[cfg_attr(not(has_cpp_compiler), ignore = "C++ compiler not found")]
|
||||
#[cfg_attr(not(has_git), ignore = "git not found")]
|
||||
fn test_inner_run_tests_idempotent() {
|
||||
require_test_script_deps();
|
||||
|
||||
let tmp = extract_project("inner_idem");
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -513,9 +438,8 @@ fn test_inner_run_tests_idempotent() {
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
#[cfg_attr(not(has_arduino_cli), ignore = "arduino-cli not found")]
|
||||
fn test_build_script_compiles_sketch() {
|
||||
require_build_script_deps();
|
||||
|
||||
let tmp = extract_project("build_test");
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -536,9 +460,9 @@ fn test_build_script_compiles_sketch() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
#[cfg_attr(not(has_arduino_cli), ignore = "arduino-cli not found")]
|
||||
fn test_build_script_produces_compilation_output() {
|
||||
require_build_script_deps();
|
||||
|
||||
let tmp = extract_project("compile_out");
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -568,9 +492,9 @@ fn test_build_script_produces_compilation_output() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
#[cfg_attr(not(has_arduino_cli), ignore = "arduino-cli not found")]
|
||||
fn test_build_script_idempotent() {
|
||||
require_build_script_deps();
|
||||
|
||||
let tmp = extract_project("build_idem");
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -594,10 +518,12 @@ fn test_build_script_idempotent() {
|
||||
// ==========================================================================
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
#[cfg_attr(not(has_cmake), ignore = "cmake not found")]
|
||||
#[cfg_attr(not(has_cpp_compiler), ignore = "C++ compiler not found")]
|
||||
#[cfg_attr(not(has_git), ignore = "git not found")]
|
||||
#[cfg_attr(not(has_arduino_cli), ignore = "arduino-cli not found")]
|
||||
fn test_full_project_all_scripts_pass() {
|
||||
require_test_script_deps();
|
||||
require_build_script_deps();
|
||||
|
||||
let tmp = extract_project("full_e2e");
|
||||
|
||||
#[cfg(unix)]
|
||||
|
||||
Reference in New Issue
Block a user