Skip to content

Commit 464af38

Browse files
authored
fix: add docs.rs stub ASIO bindings (#1075)
* Generate minimal ASIO bindings when DOCS_RS is set so docs.rs can build without the ASIO SDK or MSVC toolchain. * Add package.metadata.docs.rs default-target and empty targets to asio-sys/Cargo.toml. * Provide a c_long alias on non-Windows platforms to match Windows i32.
1 parent 9ec3241 commit 464af38

File tree

5 files changed

+234
-1
lines changed

5 files changed

+234
-1
lines changed

asio-sys/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
### Fixed
11+
- Fixed docs.rs documentation build by generating stub bindings when building for docs.rs
12+
813
## [0.2.3] - 2025-12-12
914

1015
### Added

asio-sys/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@ parse_cfg = "4.1"
2020
[dependencies]
2121
num-derive = "0.4"
2222
num-traits = "0.2"
23+
24+
[package.metadata.docs.rs]
25+
default-target = "x86_64-pc-windows-msvc"
26+
targets = []

asio-sys/asio_stub_bindings.rs

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// Stub bindings for docs.rs
2+
// These are minimal type and function definitions to allow documentation generation
3+
// without requiring the actual ASIO SDK.
4+
5+
use std::os::raw::{c_char, c_double, c_void};
6+
7+
// On Windows (the only platform where ASIO actually runs), c_long is i32.
8+
// On non-Windows platforms (for docs.rs and local testing), redefine c_long as i32 to match.
9+
#[cfg(target_os = "windows")]
10+
use std::os::raw::c_long;
11+
#[cfg(not(target_os = "windows"))]
12+
type c_long = i32;
13+
14+
pub type ASIOBool = c_long;
15+
pub type ASIOError = c_long;
16+
pub type ASIOSampleRate = c_double;
17+
18+
#[repr(C)]
19+
#[derive(Debug, Copy, Clone)]
20+
pub struct ASIOSamples {
21+
pub hi: u32,
22+
pub lo: u32,
23+
}
24+
25+
#[repr(C)]
26+
#[derive(Debug, Copy, Clone)]
27+
pub struct ASIOTimeStamp {
28+
pub hi: u32,
29+
pub lo: u32,
30+
}
31+
32+
#[repr(C)]
33+
#[derive(Debug, Copy, Clone)]
34+
pub struct ASIODriverInfo {
35+
pub asioVersion: c_long,
36+
pub driverVersion: c_long,
37+
pub name: [c_char; 32],
38+
pub errorMessage: [c_char; 124],
39+
pub sysRef: *mut c_void,
40+
}
41+
42+
#[repr(C)]
43+
#[derive(Debug, Copy, Clone)]
44+
pub struct ASIOChannelInfo {
45+
pub channel: c_long,
46+
pub isInput: ASIOBool,
47+
pub isActive: ASIOBool,
48+
pub channelGroup: c_long,
49+
pub type_: c_long,
50+
pub name: [c_char; 32],
51+
}
52+
53+
#[repr(C)]
54+
#[derive(Debug, Copy, Clone)]
55+
pub struct ASIOBufferInfo {
56+
pub isInput: ASIOBool,
57+
pub channelNum: c_long,
58+
pub buffers: [*mut c_void; 2],
59+
}
60+
61+
#[repr(C)]
62+
#[derive(Debug, Copy, Clone)]
63+
pub struct ASIOCallbacks {
64+
pub bufferSwitch: *const c_void,
65+
pub sampleRateDidChange: *const c_void,
66+
pub asioMessage: *const c_void,
67+
pub bufferSwitchTimeInfo: *const c_void,
68+
}
69+
70+
#[repr(C)]
71+
#[derive(Debug, Copy, Clone)]
72+
pub struct AsioTimeInfo {
73+
pub speed: c_double,
74+
pub systemTime: ASIOTimeStamp,
75+
pub samplePosition: ASIOSamples,
76+
pub sampleRate: ASIOSampleRate,
77+
pub flags: c_long,
78+
pub reserved: [c_char; 12],
79+
}
80+
81+
#[repr(C)]
82+
#[derive(Debug, Copy, Clone)]
83+
pub struct ASIOTimeCode {
84+
pub speed: c_double,
85+
pub timeCodeSamples: ASIOSamples,
86+
pub flags: c_long,
87+
pub future: [c_char; 64],
88+
}
89+
90+
#[repr(C)]
91+
#[derive(Debug, Copy, Clone)]
92+
pub struct ASIOTime {
93+
pub reserved: [c_long; 4],
94+
pub timeInfo: AsioTimeInfo,
95+
pub timeCode: ASIOTimeCode,
96+
}
97+
98+
#[repr(transparent)]
99+
#[derive(Debug, Copy, Clone)]
100+
pub struct AsioTimeInfoFlags(pub u32);
101+
102+
impl AsioTimeInfoFlags {
103+
pub const kSystemTimeValid: Self = Self(1);
104+
pub const kSamplePositionValid: Self = Self(1 << 1);
105+
}
106+
107+
impl std::ops::BitOr for AsioTimeInfoFlags {
108+
type Output = Self;
109+
fn bitor(self, rhs: Self) -> Self {
110+
Self(self.0 | rhs.0)
111+
}
112+
}
113+
114+
#[repr(transparent)]
115+
#[derive(Debug, Copy, Clone)]
116+
pub struct ASIOTimeCodeFlags(pub u32);
117+
118+
// Stub functions (will never be called on docs.rs)
119+
#[no_mangle]
120+
pub unsafe extern "C" fn ASIOInit(_info: *mut ASIODriverInfo) -> ASIOError {
121+
0
122+
}
123+
#[no_mangle]
124+
pub unsafe extern "C" fn ASIOExit() -> ASIOError {
125+
0
126+
}
127+
#[no_mangle]
128+
pub unsafe extern "C" fn ASIOStart() -> ASIOError {
129+
0
130+
}
131+
#[no_mangle]
132+
pub unsafe extern "C" fn ASIOStop() -> ASIOError {
133+
0
134+
}
135+
#[no_mangle]
136+
pub unsafe extern "C" fn ASIOGetChannels(_ins: *mut c_long, _outs: *mut c_long) -> ASIOError {
137+
0
138+
}
139+
#[no_mangle]
140+
pub unsafe extern "C" fn ASIOGetChannelInfo(_info: *mut ASIOChannelInfo) -> ASIOError {
141+
0
142+
}
143+
#[no_mangle]
144+
pub unsafe extern "C" fn ASIOCreateBuffers(
145+
_infos: *mut ASIOBufferInfo,
146+
_num: c_long,
147+
_size: c_long,
148+
_callbacks: *mut ASIOCallbacks,
149+
) -> ASIOError {
150+
0
151+
}
152+
#[no_mangle]
153+
pub unsafe extern "C" fn ASIODisposeBuffers() -> ASIOError {
154+
0
155+
}
156+
#[no_mangle]
157+
pub unsafe extern "C" fn ASIOGetBufferSize(
158+
_min: *mut c_long,
159+
_max: *mut c_long,
160+
_pref: *mut c_long,
161+
_gran: *mut c_long,
162+
) -> ASIOError {
163+
0
164+
}
165+
#[no_mangle]
166+
pub unsafe extern "C" fn ASIOGetSamplePosition(
167+
_pos: *mut ASIOSamples,
168+
_stamp: *mut ASIOTimeStamp,
169+
) -> ASIOError {
170+
0
171+
}
172+
#[no_mangle]
173+
pub unsafe extern "C" fn ASIOOutputReady() -> ASIOError {
174+
0
175+
}
176+
177+
#[no_mangle]
178+
pub unsafe extern "C" fn get_driver_names(_names: *mut *mut c_char, _max: c_long) -> c_long {
179+
0
180+
}
181+
#[no_mangle]
182+
pub unsafe extern "C" fn load_asio_driver(_name: *mut c_char) -> bool {
183+
false
184+
}
185+
#[no_mangle]
186+
pub unsafe extern "C" fn remove_current_driver() {}
187+
#[no_mangle]
188+
pub unsafe extern "C" fn get_sample_rate(_rate: *mut c_double) -> ASIOError {
189+
0
190+
}
191+
#[no_mangle]
192+
pub unsafe extern "C" fn set_sample_rate(_rate: c_double) -> ASIOError {
193+
0
194+
}
195+
#[no_mangle]
196+
pub unsafe extern "C" fn can_sample_rate(_rate: c_double) -> ASIOError {
197+
0
198+
}

asio-sys/build.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ fn is_msvc() -> bool {
4141
}
4242

4343
fn main() {
44+
// When building on docs.rs, skip the actual build and generate stub bindings
45+
if std::env::var("DOCS_RS").is_ok() {
46+
println!("cargo:warning=Building for docs.rs - generating stub bindings");
47+
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("bad path"));
48+
create_stub_bindings(&out_dir);
49+
return;
50+
}
51+
4452
println!("cargo:rerun-if-env-changed={}", CPAL_ASIO_DIR);
4553

4654
// ASIO SDK directory
@@ -130,6 +138,17 @@ fn create_lib(cpal_asio_dir: &Path) {
130138
.compile("libasio.a");
131139
}
132140

141+
/// Creates stub bindings for docs.rs
142+
///
143+
/// Since docs.rs builds in a sandboxed environment without network access
144+
/// and cannot cross-compile Windows MSVC targets with C++ dependencies,
145+
/// we generate minimal stub bindings that allow documentation to be built.
146+
fn create_stub_bindings(out_dir: &Path) {
147+
let stub_content = include_str!("asio_stub_bindings.rs");
148+
let binding_path = out_dir.join("asio_bindings.rs");
149+
std::fs::write(&binding_path, stub_content).expect("Failed to write stub bindings");
150+
}
151+
133152
fn create_bindings(cpal_asio_dir: &PathBuf) {
134153
let mut asio_header = None;
135154
let mut asio_sys_header = None;

asio-sys/src/bindings/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,20 @@ use self::errors::{AsioError, AsioErrorWrapper, LoadDriverError};
66
use num_traits::FromPrimitive;
77

88
use std::ffi::{CStr, CString};
9-
use std::os::raw::{c_char, c_double, c_long, c_void};
9+
use std::os::raw::{c_char, c_double, c_void};
1010
use std::ptr::null_mut;
1111
use std::sync::{
1212
atomic::{AtomicBool, Ordering},
1313
Arc, Mutex, MutexGuard, Weak,
1414
};
1515

16+
// On Windows (where ASIO actually runs), c_long is i32.
17+
// On non-Windows platforms (for docs.rs and local testing), redefine c_long as i32 to match.
18+
#[cfg(target_os = "windows")]
19+
use std::os::raw::c_long;
20+
#[cfg(not(target_os = "windows"))]
21+
type c_long = i32;
22+
1623
// Bindings import
1724
use self::asio_import as ai;
1825

0 commit comments

Comments
 (0)