board_info/board_info.rs
1//! Prints what this binary is running on: the board, and the Bela
2//! version.
3//!
4//! The first thing to ask for from anyone reporting a problem. It
5//! brings no audio system up and touches no audio hardware, so it can
6//! be run on a board that is already doing something else, and it
7//! answers even when nothing else here works — an image whose libbela
8//! is not the one the binary was built against says so in the first two
9//! lines rather than through whatever failure that mismatch causes
10//! later.
11//!
12//! ```text
13//! board: GemStereo
14//! version: 1.18.0
15//! ```
16//!
17//! The version line names both versions when the library and the
18//! vendored headers disagree, and only the one when they do not.
19//!
20//! With `--all-modes` it asks each detect mode in turn instead, which
21//! is how the difference between them was measured. `scan` is in that
22//! list and is the one mode with a side effect: it goes out over the
23//! buses and writes `/run/bela/belaconfig`, so it needs permission to
24//! write that file and is not what a program should call routinely.
25//! It is therefore asked last, after the modes that read that file,
26//! and its line is a fresh answer to compare with what they found
27//! rather than a value they have just been handed.
28//!
29//! Cross-compile and run on the board (see docs/cross-compile.md):
30//!
31//! ```sh
32//! cargo build -p bela --release --target aarch64-unknown-linux-gnu --example board_info
33//! ```
34
35use std::process::ExitCode;
36
37#[cfg(bela_device)]
38fn main() -> ExitCode {
39 use core::iter::once;
40 use std::env::args;
41
42 use bela::{Board, DetectMode, Version};
43
44 let arguments: Vec<String> = args().skip(1).collect();
45 let all_modes = match arguments.as_slice() {
46 [] => false,
47 [flag] if flag == "--all-modes" => true,
48 _ => {
49 eprintln!(
50 "usage: board_info [--all-modes]\n\
51 \x20 --all-modes ask every detect mode, including the scan that writes\n\
52 \x20 /run/bela/belaconfig"
53 );
54 return ExitCode::FAILURE;
55 }
56 };
57
58 if all_modes {
59 // Named per line, because the interesting result is the one
60 // that disagrees with the others.
61 //
62 // `Scan` goes last however `DetectMode::ALL` is ordered: it
63 // writes `/run/bela/belaconfig`, which `Cache`, `CacheOnly` and
64 // `User` read. Asking it first would leave those three
65 // reporting what this same run had just written, and four
66 // modes agreeing would say nothing about the board. Last, they
67 // report what was already on the board and the scan is a fresh
68 // answer to compare with it.
69 let scan_last = DetectMode::ALL
70 .iter()
71 .filter(|mode| **mode != DetectMode::Scan)
72 .chain(once(&DetectMode::Scan));
73 for mode in scan_last {
74 println!("board[{mode}]: {}", Board::detect(*mode));
75 }
76 } else {
77 // `Cache` rather than `Scan`: on a running board the daemon has
78 // already written the file, so this is a file read. It is not
79 // free of side effects — with no file to read it falls back to
80 // scanning, which writes one — but it is the mode that leaves
81 // a working board alone, and `CacheOnly` would answer `NoHw`
82 // on a board that simply had not been scanned yet.
83 println!("board: {}", Board::detect(DetectMode::Cache));
84 }
85
86 // Both versions on one line. They agree on a board whose image is
87 // the one the bindings were vendored from, and the whole point of
88 // printing them together is the run where they do not.
89 let running = Version::running();
90 if running == Version::HEADERS {
91 println!("version: {running}");
92 } else {
93 println!(
94 "version: {running} (this binary was built against {headers})",
95 headers = Version::HEADERS
96 );
97 }
98
99 ExitCode::SUCCESS
100}
101
102#[cfg(not(bela_device))]
103fn main() -> ExitCode {
104 eprintln!("This example must be cross-compiled for Bela Gem (aarch64-unknown-linux-gnu).");
105 ExitCode::FAILURE
106}