Skip to content

Commit 25bb351

Browse files
committed
make bindgen optional
1 parent 0f0a055 commit 25bb351

File tree

5 files changed

+402
-4
lines changed

5 files changed

+402
-4
lines changed

Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ readme = "README.md"
99
license = "MIT"
1010
keywords = ["kcp", "bindings"]
1111

12+
[features]
13+
default = []
14+
# Enable this feature to use bindgen when pregenerated bindings are not available
15+
bindgen = ["dep:bindgen"]
16+
1217
[dependencies]
1318
bytes = "1"
1419
tokio = { version = "1", features = ["full"] }
@@ -25,5 +30,6 @@ tracing-subscriber = "0.3.19"
2530
rand = "0.8.5"
2631

2732
[build-dependencies]
28-
bindgen = { version="0.71.1", default-features=false, features=["runtime", "which-rustfmt"]}
33+
# Remove bindgen dependency, use manually written FFI bindings
34+
bindgen = { version="0.71.1", default-features=false, features=["runtime", "which-rustfmt"], optional = true }
2935
cc = "1.2.10"

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,36 @@ endpoint.set_kcp_config_factory(|conv| {
6767
KcpConfig::new_turbo(conv)
6868
});
6969
```
70+
71+
## Building
72+
73+
This crate uses pregenerated FFI bindings to avoid requiring LLVM/Clang at build time.
74+
75+
### For Users
76+
77+
Simply run:
78+
```bash
79+
cargo build
80+
```
81+
82+
No additional dependencies required! The pregenerated bindings are included in the source code.
83+
84+
### For Maintainers
85+
86+
When the KCP submodule is updated, you need to regenerate the bindings:
87+
88+
```bash
89+
# Generate new bindings for the current KCP version
90+
cargo build --features bindgen
91+
92+
# Commit the generated bindings file
93+
git add src/binding_*.rs
94+
git commit -m "Update bindings for KCP version"
95+
```
96+
97+
The generated binding file is named `binding_{commit_id}.rs` where `commit_id` is the current KCP submodule commit hash.
98+
99+
### Build Features
100+
101+
- **Default**: Uses pregenerated bindings, no LLVM dependency
102+
- **`bindgen`**: Enables bindgen to generate new bindings when needed

build.rs

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::env;
22
use std::path::{Path, PathBuf};
3+
use std::process::Command;
34

45
fn main() {
56
println!("cargo:rustc-link-lib=kcp");
@@ -18,8 +19,76 @@ fn main() {
1819
println!("cargo:rerun-if-changed=kcp/ikcp.c");
1920
println!("cargo:rerun-if-changed=wrapper.h");
2021

22+
// Get kcp submodule commit ID
23+
let kcp_commit = get_kcp_commit_id(&fulldir);
24+
let src_dir = Path::new(&dir).join("src");
25+
let binding_file = src_dir.join(format!("binding_{}.rs", kcp_commit));
26+
27+
println!("cargo:rerun-if-changed={}", binding_file.display());
28+
29+
// If pregenerated bindings file exists, use it
30+
if binding_file.exists() {
31+
println!("cargo:rustc-cfg=use_pregenerated_bindings");
32+
println!("Using pregenerated bindings: {}", binding_file.display());
33+
34+
// Copy pregenerated bindings file to OUT_DIR
35+
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
36+
let target_bindings = out_path.join("bindings.rs");
37+
std::fs::copy(&binding_file, &target_bindings)
38+
.expect("Failed to copy pregenerated bindings");
39+
} else {
40+
// Check if bindgen feature is enabled
41+
#[cfg(feature = "bindgen")]
42+
{
43+
println!("cargo:warning=Pregenerated bindings not found for commit {}, generating with bindgen", kcp_commit);
44+
println!("cargo:warning=Consider committing {} to avoid bindgen dependency", binding_file.display());
45+
generate_bindings(&dir, &binding_file);
46+
}
47+
48+
#[cfg(not(feature = "bindgen"))]
49+
{
50+
panic!(
51+
"Pregenerated bindings not found for kcp commit: {}\n\
52+
Expected file: {}\n\
53+
\n\
54+
To fix this issue, you have two options:\n\
55+
1. Enable bindgen feature: cargo build --features bindgen\n\
56+
2. Ask the maintainer to generate bindings for this kcp version\n\
57+
\n\
58+
If you are the maintainer, run: cargo build --features bindgen\n\
59+
Then commit the generated file: {}",
60+
kcp_commit,
61+
binding_file.display(),
62+
binding_file.display()
63+
);
64+
}
65+
}
66+
}
67+
68+
fn get_kcp_commit_id(kcp_dir: &Path) -> String {
69+
let output = Command::new("git")
70+
.args(&["rev-parse", "HEAD"])
71+
.current_dir(kcp_dir)
72+
.output()
73+
.expect("Failed to get kcp commit ID");
74+
75+
if !output.status.success() {
76+
panic!("Failed to get kcp commit ID: {}", String::from_utf8_lossy(&output.stderr));
77+
}
78+
79+
String::from_utf8(output.stdout)
80+
.expect("Invalid UTF-8 in git output")
81+
.trim()
82+
.to_string()
83+
}
84+
85+
#[cfg(feature = "bindgen")]
86+
fn generate_bindings(_manifest_dir: &str, binding_file: &Path) {
2187
let extra_header_path = std::env::var("KCP_SYS_EXTRA_HEADER_PATH").unwrap_or_default();
22-
let extra_header_paths = extra_header_path.split(":").filter(|s| !s.is_empty()).collect::<Vec<_>>();
88+
let extra_header_paths = extra_header_path
89+
.split(":")
90+
.filter(|s| !s.is_empty())
91+
.collect::<Vec<_>>();
2392

2493
let bindings = bindgen::Builder::default()
2594
.header("wrapper.h")
@@ -31,7 +100,18 @@ fn main() {
31100
.expect("Unable to generate bindings");
32101

33102
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
103+
let target_bindings = out_path.join("bindings.rs");
104+
105+
// Write to OUT_DIR (for build-time use)
106+
bindings
107+
.write_to_file(&target_bindings)
108+
.expect("Couldn't write bindings to OUT_DIR!");
109+
110+
// Also write to src directory (for committing)
34111
bindings
35-
.write_to_file(out_path.join("bindings.rs"))
36-
.expect("Couldn't write bindings!");
112+
.write_to_file(binding_file)
113+
.expect("Couldn't write bindings to src directory!");
114+
115+
println!("Generated bindings for current kcp commit: {}", binding_file.display());
116+
println!("Please commit this file to avoid bindgen dependency for other users");
37117
}
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
/* automatically generated by rust-bindgen 0.71.1 */
2+
3+
pub type ISTDUINT32 = ::core::ffi::c_uint;
4+
pub type ISTDINT32 = ::core::ffi::c_int;
5+
pub type IINT32 = ISTDINT32;
6+
pub type IUINT32 = ISTDUINT32;
7+
#[repr(C)]
8+
#[derive(Debug, Copy, Clone)]
9+
pub struct IQUEUEHEAD {
10+
pub next: *mut IQUEUEHEAD,
11+
pub prev: *mut IQUEUEHEAD,
12+
}
13+
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
14+
const _: () = {
15+
["Size of IQUEUEHEAD"][::core::mem::size_of::<IQUEUEHEAD>() - 16usize];
16+
["Alignment of IQUEUEHEAD"][::core::mem::align_of::<IQUEUEHEAD>() - 8usize];
17+
["Offset of field: IQUEUEHEAD::next"][::core::mem::offset_of!(IQUEUEHEAD, next) - 0usize];
18+
["Offset of field: IQUEUEHEAD::prev"][::core::mem::offset_of!(IQUEUEHEAD, prev) - 8usize];
19+
};
20+
#[repr(C)]
21+
#[derive(Debug, Copy, Clone)]
22+
pub struct IKCPCB {
23+
pub conv: IUINT32,
24+
pub mtu: IUINT32,
25+
pub mss: IUINT32,
26+
pub state: IUINT32,
27+
pub snd_una: IUINT32,
28+
pub snd_nxt: IUINT32,
29+
pub rcv_nxt: IUINT32,
30+
pub ts_recent: IUINT32,
31+
pub ts_lastack: IUINT32,
32+
pub ssthresh: IUINT32,
33+
pub rx_rttval: IINT32,
34+
pub rx_srtt: IINT32,
35+
pub rx_rto: IINT32,
36+
pub rx_minrto: IINT32,
37+
pub snd_wnd: IUINT32,
38+
pub rcv_wnd: IUINT32,
39+
pub rmt_wnd: IUINT32,
40+
pub cwnd: IUINT32,
41+
pub probe: IUINT32,
42+
pub current: IUINT32,
43+
pub interval: IUINT32,
44+
pub ts_flush: IUINT32,
45+
pub xmit: IUINT32,
46+
pub nrcv_buf: IUINT32,
47+
pub nsnd_buf: IUINT32,
48+
pub nrcv_que: IUINT32,
49+
pub nsnd_que: IUINT32,
50+
pub nodelay: IUINT32,
51+
pub updated: IUINT32,
52+
pub ts_probe: IUINT32,
53+
pub probe_wait: IUINT32,
54+
pub dead_link: IUINT32,
55+
pub incr: IUINT32,
56+
pub snd_queue: IQUEUEHEAD,
57+
pub rcv_queue: IQUEUEHEAD,
58+
pub snd_buf: IQUEUEHEAD,
59+
pub rcv_buf: IQUEUEHEAD,
60+
pub acklist: *mut IUINT32,
61+
pub ackcount: IUINT32,
62+
pub ackblock: IUINT32,
63+
pub user: *mut ::core::ffi::c_void,
64+
pub buffer: *mut ::core::ffi::c_char,
65+
pub fastresend: ::core::ffi::c_int,
66+
pub fastlimit: ::core::ffi::c_int,
67+
pub nocwnd: ::core::ffi::c_int,
68+
pub stream: ::core::ffi::c_int,
69+
pub logmask: ::core::ffi::c_int,
70+
pub output: ::core::option::Option<
71+
unsafe extern "C" fn(
72+
buf: *const ::core::ffi::c_char,
73+
len: ::core::ffi::c_int,
74+
kcp: *mut IKCPCB,
75+
user: *mut ::core::ffi::c_void,
76+
) -> ::core::ffi::c_int,
77+
>,
78+
pub writelog: ::core::option::Option<
79+
unsafe extern "C" fn(
80+
log: *const ::core::ffi::c_char,
81+
kcp: *mut IKCPCB,
82+
user: *mut ::core::ffi::c_void,
83+
),
84+
>,
85+
}
86+
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
87+
const _: () = {
88+
["Size of IKCPCB"][::core::mem::size_of::<IKCPCB>() - 272usize];
89+
["Alignment of IKCPCB"][::core::mem::align_of::<IKCPCB>() - 8usize];
90+
["Offset of field: IKCPCB::conv"][::core::mem::offset_of!(IKCPCB, conv) - 0usize];
91+
["Offset of field: IKCPCB::mtu"][::core::mem::offset_of!(IKCPCB, mtu) - 4usize];
92+
["Offset of field: IKCPCB::mss"][::core::mem::offset_of!(IKCPCB, mss) - 8usize];
93+
["Offset of field: IKCPCB::state"][::core::mem::offset_of!(IKCPCB, state) - 12usize];
94+
["Offset of field: IKCPCB::snd_una"][::core::mem::offset_of!(IKCPCB, snd_una) - 16usize];
95+
["Offset of field: IKCPCB::snd_nxt"][::core::mem::offset_of!(IKCPCB, snd_nxt) - 20usize];
96+
["Offset of field: IKCPCB::rcv_nxt"][::core::mem::offset_of!(IKCPCB, rcv_nxt) - 24usize];
97+
["Offset of field: IKCPCB::ts_recent"][::core::mem::offset_of!(IKCPCB, ts_recent) - 28usize];
98+
["Offset of field: IKCPCB::ts_lastack"][::core::mem::offset_of!(IKCPCB, ts_lastack) - 32usize];
99+
["Offset of field: IKCPCB::ssthresh"][::core::mem::offset_of!(IKCPCB, ssthresh) - 36usize];
100+
["Offset of field: IKCPCB::rx_rttval"][::core::mem::offset_of!(IKCPCB, rx_rttval) - 40usize];
101+
["Offset of field: IKCPCB::rx_srtt"][::core::mem::offset_of!(IKCPCB, rx_srtt) - 44usize];
102+
["Offset of field: IKCPCB::rx_rto"][::core::mem::offset_of!(IKCPCB, rx_rto) - 48usize];
103+
["Offset of field: IKCPCB::rx_minrto"][::core::mem::offset_of!(IKCPCB, rx_minrto) - 52usize];
104+
["Offset of field: IKCPCB::snd_wnd"][::core::mem::offset_of!(IKCPCB, snd_wnd) - 56usize];
105+
["Offset of field: IKCPCB::rcv_wnd"][::core::mem::offset_of!(IKCPCB, rcv_wnd) - 60usize];
106+
["Offset of field: IKCPCB::rmt_wnd"][::core::mem::offset_of!(IKCPCB, rmt_wnd) - 64usize];
107+
["Offset of field: IKCPCB::cwnd"][::core::mem::offset_of!(IKCPCB, cwnd) - 68usize];
108+
["Offset of field: IKCPCB::probe"][::core::mem::offset_of!(IKCPCB, probe) - 72usize];
109+
["Offset of field: IKCPCB::current"][::core::mem::offset_of!(IKCPCB, current) - 76usize];
110+
["Offset of field: IKCPCB::interval"][::core::mem::offset_of!(IKCPCB, interval) - 80usize];
111+
["Offset of field: IKCPCB::ts_flush"][::core::mem::offset_of!(IKCPCB, ts_flush) - 84usize];
112+
["Offset of field: IKCPCB::xmit"][::core::mem::offset_of!(IKCPCB, xmit) - 88usize];
113+
["Offset of field: IKCPCB::nrcv_buf"][::core::mem::offset_of!(IKCPCB, nrcv_buf) - 92usize];
114+
["Offset of field: IKCPCB::nsnd_buf"][::core::mem::offset_of!(IKCPCB, nsnd_buf) - 96usize];
115+
["Offset of field: IKCPCB::nrcv_que"][::core::mem::offset_of!(IKCPCB, nrcv_que) - 100usize];
116+
["Offset of field: IKCPCB::nsnd_que"][::core::mem::offset_of!(IKCPCB, nsnd_que) - 104usize];
117+
["Offset of field: IKCPCB::nodelay"][::core::mem::offset_of!(IKCPCB, nodelay) - 108usize];
118+
["Offset of field: IKCPCB::updated"][::core::mem::offset_of!(IKCPCB, updated) - 112usize];
119+
["Offset of field: IKCPCB::ts_probe"][::core::mem::offset_of!(IKCPCB, ts_probe) - 116usize];
120+
["Offset of field: IKCPCB::probe_wait"][::core::mem::offset_of!(IKCPCB, probe_wait) - 120usize];
121+
["Offset of field: IKCPCB::dead_link"][::core::mem::offset_of!(IKCPCB, dead_link) - 124usize];
122+
["Offset of field: IKCPCB::incr"][::core::mem::offset_of!(IKCPCB, incr) - 128usize];
123+
["Offset of field: IKCPCB::snd_queue"][::core::mem::offset_of!(IKCPCB, snd_queue) - 136usize];
124+
["Offset of field: IKCPCB::rcv_queue"][::core::mem::offset_of!(IKCPCB, rcv_queue) - 152usize];
125+
["Offset of field: IKCPCB::snd_buf"][::core::mem::offset_of!(IKCPCB, snd_buf) - 168usize];
126+
["Offset of field: IKCPCB::rcv_buf"][::core::mem::offset_of!(IKCPCB, rcv_buf) - 184usize];
127+
["Offset of field: IKCPCB::acklist"][::core::mem::offset_of!(IKCPCB, acklist) - 200usize];
128+
["Offset of field: IKCPCB::ackcount"][::core::mem::offset_of!(IKCPCB, ackcount) - 208usize];
129+
["Offset of field: IKCPCB::ackblock"][::core::mem::offset_of!(IKCPCB, ackblock) - 212usize];
130+
["Offset of field: IKCPCB::user"][::core::mem::offset_of!(IKCPCB, user) - 216usize];
131+
["Offset of field: IKCPCB::buffer"][::core::mem::offset_of!(IKCPCB, buffer) - 224usize];
132+
["Offset of field: IKCPCB::fastresend"][::core::mem::offset_of!(IKCPCB, fastresend) - 232usize];
133+
["Offset of field: IKCPCB::fastlimit"][::core::mem::offset_of!(IKCPCB, fastlimit) - 236usize];
134+
["Offset of field: IKCPCB::nocwnd"][::core::mem::offset_of!(IKCPCB, nocwnd) - 240usize];
135+
["Offset of field: IKCPCB::stream"][::core::mem::offset_of!(IKCPCB, stream) - 244usize];
136+
["Offset of field: IKCPCB::logmask"][::core::mem::offset_of!(IKCPCB, logmask) - 248usize];
137+
["Offset of field: IKCPCB::output"][::core::mem::offset_of!(IKCPCB, output) - 256usize];
138+
["Offset of field: IKCPCB::writelog"][::core::mem::offset_of!(IKCPCB, writelog) - 264usize];
139+
};
140+
pub type ikcpcb = IKCPCB;
141+
unsafe extern "C" {
142+
pub fn ikcp_create(conv: IUINT32, user: *mut ::core::ffi::c_void) -> *mut ikcpcb;
143+
}
144+
unsafe extern "C" {
145+
pub fn ikcp_release(kcp: *mut ikcpcb);
146+
}
147+
unsafe extern "C" {
148+
pub fn ikcp_setoutput(
149+
kcp: *mut ikcpcb,
150+
output: ::core::option::Option<
151+
unsafe extern "C" fn(
152+
buf: *const ::core::ffi::c_char,
153+
len: ::core::ffi::c_int,
154+
kcp: *mut ikcpcb,
155+
user: *mut ::core::ffi::c_void,
156+
) -> ::core::ffi::c_int,
157+
>,
158+
);
159+
}
160+
unsafe extern "C" {
161+
pub fn ikcp_recv(
162+
kcp: *mut ikcpcb,
163+
buffer: *mut ::core::ffi::c_char,
164+
len: ::core::ffi::c_int,
165+
) -> ::core::ffi::c_int;
166+
}
167+
unsafe extern "C" {
168+
pub fn ikcp_send(
169+
kcp: *mut ikcpcb,
170+
buffer: *const ::core::ffi::c_char,
171+
len: ::core::ffi::c_int,
172+
) -> ::core::ffi::c_int;
173+
}
174+
unsafe extern "C" {
175+
pub fn ikcp_update(kcp: *mut ikcpcb, current: IUINT32);
176+
}
177+
unsafe extern "C" {
178+
pub fn ikcp_check(kcp: *const ikcpcb, current: IUINT32) -> IUINT32;
179+
}
180+
unsafe extern "C" {
181+
pub fn ikcp_input(
182+
kcp: *mut ikcpcb,
183+
data: *const ::core::ffi::c_char,
184+
size: ::core::ffi::c_long,
185+
) -> ::core::ffi::c_int;
186+
}
187+
unsafe extern "C" {
188+
pub fn ikcp_flush(kcp: *mut ikcpcb);
189+
}
190+
unsafe extern "C" {
191+
pub fn ikcp_peeksize(kcp: *const ikcpcb) -> ::core::ffi::c_int;
192+
}
193+
unsafe extern "C" {
194+
pub fn ikcp_setmtu(kcp: *mut ikcpcb, mtu: ::core::ffi::c_int) -> ::core::ffi::c_int;
195+
}
196+
unsafe extern "C" {
197+
pub fn ikcp_wndsize(
198+
kcp: *mut ikcpcb,
199+
sndwnd: ::core::ffi::c_int,
200+
rcvwnd: ::core::ffi::c_int,
201+
) -> ::core::ffi::c_int;
202+
}
203+
unsafe extern "C" {
204+
pub fn ikcp_waitsnd(kcp: *const ikcpcb) -> ::core::ffi::c_int;
205+
}
206+
unsafe extern "C" {
207+
pub fn ikcp_nodelay(
208+
kcp: *mut ikcpcb,
209+
nodelay: ::core::ffi::c_int,
210+
interval: ::core::ffi::c_int,
211+
resend: ::core::ffi::c_int,
212+
nc: ::core::ffi::c_int,
213+
) -> ::core::ffi::c_int;
214+
}
215+
unsafe extern "C" {
216+
pub fn ikcp_log(
217+
kcp: *mut ikcpcb,
218+
mask: ::core::ffi::c_int,
219+
fmt: *const ::core::ffi::c_char,
220+
...
221+
);
222+
}
223+
unsafe extern "C" {
224+
pub fn ikcp_allocator(
225+
new_malloc: ::core::option::Option<
226+
unsafe extern "C" fn(arg1: usize) -> *mut ::core::ffi::c_void,
227+
>,
228+
new_free: ::core::option::Option<unsafe extern "C" fn(arg1: *mut ::core::ffi::c_void)>,
229+
);
230+
}
231+
unsafe extern "C" {
232+
pub fn ikcp_getconv(ptr: *const ::core::ffi::c_void) -> IUINT32;
233+
}

0 commit comments

Comments
 (0)