Until #228 #[cfg(any(target_arch = "x86_64", target_os = "macos"))] would always work in the dev container (which is our tier 1 development environment) because it was always linux/amd64. I initially thought more workflows would break and started working on this. Now that I know the impact is relatively low, I will shift my focus to other things I need to do that are more urgent. But I won't leave this without documenting the results of my Claude assisted archeology to boost future me or anyone else who wants to pick this up.
I think part of a future solution will be to have a bindings feature for the sys-crates and check automatically that the tracked bindings are up to date. Maybe have acap or acapx_y features in other to enable things that only work on targets with the ACAP framework. Maybe also have a no-acap feature because, if I remember correctly, we cannot enable conditional dependencies based on a feature not being enabled.
What the cfg statements achieve
All 36 occurrences of any(target_arch = "x86_64", target_os = "macos") express the same semantic: "is this a host build?" They exist because the ACAP SDK's C libraries and device services are only available when cross-compiling for Axis cameras (ARM Linux), but developers need host-side tooling (cargo check, cargo doc, IDE completions, cargo test) to work without the SDK installed.
The pattern is a heuristic that assumes host = x86_64 or macOS, target = everything else. It is acknowledged as imperfect — an ARM Linux desktop would be misclassified 1 — and multiple locations carry // TODO: Find a more robust configuration.
Group 1: Binding source selection (27 occurrences)
9 -sys crates × 3 locations each (src/lib.rs × 2 cfg branches + build.rs × 1 env var check)
axevent-sys, axoverlay-sys, axparameter-sys, axstorage-sys, bbox-sys, larod-sys, licensekey-sys, mdb-sys, vdo-sys
Why it's needed: Bindgen requires the SDK's C headers, which are only present in the cross-compilation sysroot. Without a fallback, cargo check on host fails immediately because build.rs can't find the headers. The pre-generated bindings.rs files were checked into the repo specifically to enable host-side cargo doc and cargo package 2. The cfg was then added to select between the two sources 3.
The build.rs files use CARGO_CFG_TARGET_ARCH / CARGO_CFG_TARGET_OS environment variables (the build-script equivalent of #[cfg]).
Group 2: Device-only test modules (6 occurrences)
apps/bounding_box_example/src/main.rs:130
apps/embedded_web_page/src/main.rs:11
apps/inspect_env/src/main.rs:22
apps/using_a_build_script/src/main.rs:12
apps/vapix_access/src/main.rs:31
crates/licensekey/src/flex.rs:176
Why it's needed: These tests exercise device-specific behavior: ACAP file layout, D-Bus services, VAPIX APIs, and FFI calls to device-only shared libraries. The original commit considered using conditional compilation everywhere but rejected it as too noisy for example apps where readability matters 3.
Five of the six would merely fail at runtime on host (wrong paths, missing services). The licensekey tests are the exception — they call FFI functions (licensekey_verify, etc.) that would cause a linker error on host because no liblicensekey.so exists.
The Makefile separately excludes these from check_tests via --exclude flags 3 4, so the cfg gates are a second layer of defense rather than the only one.
Group 3: Host/device behavior switching (2 occurrences)
crates/acap-vapix/src/lib.rs:85 — runtime cfg!() macro
apps/reverse_proxy/src/main.rs:100 — compile-time #[cfg] attribute
Why it's needed: These enable running apps on the developer's machine for faster iteration 5 6. On host, VAPIX credentials come from environment variables instead of D-Bus; the reverse proxy serves static files from a local directory instead of relying on the Axis web server.
The acap-vapix location deliberately uses the runtime cfg!() macro so both branches compile everywhere, avoiding conditional imports 5. The reverse_proxy location uses a compile-time #[cfg] because it gates a dependency (tower_http::services::ServeDir).
Group 4: Custom test runner (1 occurrence)
Why it's needed: remote-test.sh copies test binaries to a camera via SSH and executes them there 1. It must not activate for host targets (where tests run locally) or for Miri (which sets its own runner) 7. This is Cargo TOML cfg syntax, not Rust.
References
Until #228
#[cfg(any(target_arch = "x86_64", target_os = "macos"))]would always work in the dev container (which is our tier 1 development environment) because it was always linux/amd64. I initially thought more workflows would break and started working on this. Now that I know the impact is relatively low, I will shift my focus to other things I need to do that are more urgent. But I won't leave this without documenting the results of my Claude assisted archeology to boost future me or anyone else who wants to pick this up.I think part of a future solution will be to have a
bindingsfeature for the sys-crates and check automatically that the tracked bindings are up to date. Maybe haveacaporacapx_yfeatures in other to enable things that only work on targets with the ACAP framework. Maybe also have ano-acapfeature because, if I remember correctly, we cannot enable conditional dependencies based on a feature not being enabled.What the cfg statements achieve
All 36 occurrences of
any(target_arch = "x86_64", target_os = "macos")express the same semantic: "is this a host build?" They exist because the ACAP SDK's C libraries and device services are only available when cross-compiling for Axis cameras (ARM Linux), but developers need host-side tooling (cargo check,cargo doc, IDE completions,cargo test) to work without the SDK installed.The pattern is a heuristic that assumes host = x86_64 or macOS, target = everything else. It is acknowledged as imperfect — an ARM Linux desktop would be misclassified 1 — and multiple locations carry
// TODO: Find a more robust configuration.Group 1: Binding source selection (27 occurrences)
9
-syscrates × 3 locations each (src/lib.rs× 2 cfg branches +build.rs× 1 env var check)axevent-sys,axoverlay-sys,axparameter-sys,axstorage-sys,bbox-sys,larod-sys,licensekey-sys,mdb-sys,vdo-sysWhy it's needed: Bindgen requires the SDK's C headers, which are only present in the cross-compilation sysroot. Without a fallback,
cargo checkon host fails immediately becausebuild.rscan't find the headers. The pre-generatedbindings.rsfiles were checked into the repo specifically to enable host-sidecargo docandcargo package2. The cfg was then added to select between the two sources 3.The
build.rsfiles useCARGO_CFG_TARGET_ARCH/CARGO_CFG_TARGET_OSenvironment variables (the build-script equivalent of#[cfg]).Group 2: Device-only test modules (6 occurrences)
apps/bounding_box_example/src/main.rs:130apps/embedded_web_page/src/main.rs:11apps/inspect_env/src/main.rs:22apps/using_a_build_script/src/main.rs:12apps/vapix_access/src/main.rs:31crates/licensekey/src/flex.rs:176Why it's needed: These tests exercise device-specific behavior: ACAP file layout, D-Bus services, VAPIX APIs, and FFI calls to device-only shared libraries. The original commit considered using conditional compilation everywhere but rejected it as too noisy for example apps where readability matters 3.
Five of the six would merely fail at runtime on host (wrong paths, missing services). The
licensekeytests are the exception — they call FFI functions (licensekey_verify, etc.) that would cause a linker error on host because noliblicensekey.soexists.The Makefile separately excludes these from
check_testsvia--excludeflags 3 4, so the cfg gates are a second layer of defense rather than the only one.Group 3: Host/device behavior switching (2 occurrences)
crates/acap-vapix/src/lib.rs:85— runtimecfg!()macroapps/reverse_proxy/src/main.rs:100— compile-time#[cfg]attributeWhy it's needed: These enable running apps on the developer's machine for faster iteration 5 6. On host, VAPIX credentials come from environment variables instead of D-Bus; the reverse proxy serves static files from a local directory instead of relying on the Axis web server.
The
acap-vapixlocation deliberately uses the runtimecfg!()macro so both branches compile everywhere, avoiding conditional imports 5. Thereverse_proxylocation uses a compile-time#[cfg]because it gates a dependency (tower_http::services::ServeDir).Group 4: Custom test runner (1 occurrence)
.cargo/config.toml:1Why it's needed:
remote-test.shcopies test binaries to a camera via SSH and executes them there 1. It must not activate for host targets (where tests run locally) or for Miri (which sets its own runner) 7. This is Cargo TOML cfg syntax, not Rust.References
Footnotes
d9f6537— noted the x86_64 assumption is "an unnecessary restriction" ↩ ↩2ff4eb34— checked pre-generated bindings into the repo ↩3697564— introduced the cfg pattern and Makefile--exclude↩ ↩2 ↩3f513ffc— collapsed explicit exclude lists into glob patterns ↩6b5dd98— runtime behavior switching ↩ ↩2de16e5f— host-side static file serving ↩96daef9— Miri exclusion in config.toml ↩