Skip to content

Commit b6d57b4

Browse files
committed
feat: allow compiling on the ESP32
1 parent 5420c9a commit b6d57b4

File tree

7 files changed

+200
-70
lines changed

7 files changed

+200
-70
lines changed

libcoap-sys/Cargo.toml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,20 @@ mbedtls-sys-auto = {version = "^2.26", optional = true}
7070
libc = "^0.2.126"
7171
tinydtls-sys = {version = "^0.2.0", default-features = false, optional = true}
7272

73+
[target.'cfg(target_os="espidf")'.dependencies]
74+
esp-idf-sys = { version = "0.34.1"}
75+
7376
[build-dependencies]
74-
bindgen = "^0.69.4"
77+
bindgen = "^0.63"
7578
autotools = "^0.2.3"
7679
fs_extra = "^1.2"
7780
pkg-config = "^0.3.24"
81+
regex = "1.10.5"
82+
embuild = { version = "0.31.3", features = ["bindgen", "espidf", "cmake"]}
7883

7984
[package.metadata.docs.rs]
8085
features = ["dtls", "dtls_backend_openssl", "vendored"]
86+
87+
[[package.metadata.esp-idf-sys.extra_components]]
88+
remote_component = { name = "espressif/coap", version = "4.3.4~3"}
89+
bindings_header = "src/wrapper.h"

libcoap-sys/build.rs

Lines changed: 152 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,105 @@ impl ToString for DtlsBackend {
3737
}
3838
}
3939

40+
fn get_builder_espidf() -> bindgen::Builder {
41+
embuild::espidf::sysenv::output();
42+
let esp_idf_path = embuild::espidf::sysenv::idf_path().ok_or("missing IDF path").unwrap();
43+
let esp_idf_buildroot = env::var("DEP_ESP_IDF_ROOT").unwrap();
44+
let esp_include_path = embuild::espidf::sysenv::cincl_args().ok_or("missing IDF cincl args").unwrap();
45+
let embuild_env = embuild::espidf::sysenv::env_path().ok_or("missing IDF env path").unwrap();
46+
let esp_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
47+
let cfg_flags = embuild::espidf::sysenv::cfg_args().ok_or("missing cfg flags from IDF").unwrap();
48+
49+
// Determine compiler path
50+
env::set_var("PATH", &embuild_env);
51+
let cmake_info = embuild::cmake::Query::new(
52+
&Path::new(&esp_idf_buildroot).join("build"),
53+
"cargo",
54+
&[embuild::cmake::file_api::ObjKind::Codemodel, embuild::cmake::file_api::ObjKind::Toolchains, embuild::cmake::file_api::ObjKind::Cache],
55+
).unwrap().get_replies().unwrap();
56+
let compiler = cmake_info
57+
.get_toolchains().map_err(|_e| "Can't get toolchains")
58+
.and_then(|mut t| {
59+
t.take(embuild::cmake::file_api::codemodel::Language::C)
60+
.ok_or("No C toolchain")
61+
})
62+
.and_then(|t| {
63+
t.compiler
64+
.path
65+
.ok_or("No compiler path set")
66+
}).unwrap();
67+
68+
// Parse include arguments
69+
let arg_splitter = regex::Regex::new(r##"(?:[^\\]"[^"]*[^\\]")?(\s)"##).unwrap();
70+
let apostrophe_remover = regex::Regex::new(r##"^"(?<content>.*)"$"##).unwrap();
71+
let esp_clang_args = arg_splitter.split(
72+
esp_include_path.args.as_str()
73+
).map(|x| apostrophe_remover.replace(x.trim(), "$content").to_string()).collect::<Vec<String>>();
74+
let bindgen_builder = embuild::bindgen::Factory {
75+
clang_args: esp_clang_args.clone(),
76+
linker: Some(compiler),
77+
mcu: None,
78+
force_cpp: false,
79+
sysroot: None
80+
}.builder().unwrap();
81+
82+
let clang_target = if esp_arch.starts_with("riscv32") {"riscv32"} else {esp_arch.as_str()};
83+
let short_target = if esp_arch.starts_with("riscv32") {"riscv"} else {esp_arch.as_str()};
84+
let target_mcu = if cfg_flags.get("esp32").is_some() {"esp32"}
85+
else if cfg_flags.get("esp32s2").is_some() { "esp32s2" }
86+
else if cfg_flags.get("esp32s3").is_some() { "esp32s3" }
87+
else if cfg_flags.get("esp32c3").is_some() { "esp32c3" }
88+
else if cfg_flags.get("esp32c2").is_some() { "esp32c2" }
89+
else if cfg_flags.get("esp32h2").is_some() { "esp32h2" }
90+
else if cfg_flags.get("esp32c5").is_some() { "esp32c5" }
91+
else if cfg_flags.get("esp32c6").is_some() { "esp32c6" }
92+
else if cfg_flags.get("esp32p4").is_some() { "esp32p4" }
93+
else {panic!("unknown ESP target MCU, please add target to libcoap-sys build.rs file!")};
94+
95+
return bindgen_builder
96+
.clang_args(&esp_clang_args)
97+
.clang_arg("-target")
98+
.clang_arg(clang_target)
99+
.clang_arg("-DESP_PLATFORM")
100+
.clang_arg("-DLWIP_IPV4=1")
101+
.clang_arg("-DLWIP_IPV6=1")
102+
.clang_arg(format!("-I{}/components/lwip/lwip/src/include", esp_idf_path))
103+
.clang_arg(format!("-I{}/components/lwip/port/freertos/include", esp_idf_path))
104+
.clang_arg(format!("-I{}/components/esp_system/include", esp_idf_path))
105+
.clang_arg(format!("-I{}/components/freertos/esp_additions/include", esp_idf_path))
106+
.clang_arg(format!("-I{}/components/freertos/esp_additions/include/freertos", esp_idf_path))
107+
.clang_arg(format!("-I{}/components/freertos/esp_additions/arch/{}/include", esp_idf_path, short_target)) // for older espidf
108+
.clang_arg(format!("-I{}/components/freertos/config/{}/include", esp_idf_path, short_target)) // for newer espidf
109+
.clang_arg(format!("-I{}/components/{}/include", esp_idf_path, short_target))
110+
.clang_arg(format!("-I{}/components/freertos/FreeRTOS-Kernel-SMP/include", esp_idf_path))
111+
.clang_arg(format!("-I{}/components/freertos/FreeRTOS-Kernel-SMP/portable/{}/include/freertos", esp_idf_path, short_target))
112+
.clang_arg(format!("-I{}/components/soc/{}/include", esp_idf_path, target_mcu))
113+
.clang_arg(format!("-I{}/components/heap/include", esp_idf_path))
114+
.clang_arg(format!("-I{}/components/esp_rom/include", esp_idf_path))
115+
.clang_arg(format!("-I{}/managed_components/espressif__coap/libcoap/include", esp_idf_buildroot))
116+
.clang_arg(format!("-I{}/build/config/", esp_idf_buildroot))
117+
.allowlist_type("epoll_event");
118+
}
119+
120+
fn get_builder() -> bindgen::Builder {
121+
bindgen::Builder::default()
122+
.blocklist_type("epoll_event")
123+
}
124+
40125
fn main() {
41126
println!("cargo:rerun-if-changed=src/libcoap/");
42127
println!("cargo:rerun-if-changed=src/wrapper.h");
43-
let mut bindgen_builder = bindgen::Builder::default();
44128
// Read required environment variables.
45129
let orig_pkg_config = std::env::var_os("PKG_CONFIG_PATH").map(|v| String::from(v.to_str().unwrap()));
46130
let out_dir = env::var_os("OUT_DIR").unwrap();
131+
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
132+
133+
let mut bindgen_builder = match target_os.as_str() {
134+
"espidf" => get_builder_espidf(),
135+
_ => get_builder()
136+
};
137+
138+
47139

48140
let mut dtls_backend = Option::None;
49141
if cfg!(feature = "dtls") {
@@ -86,7 +178,7 @@ fn main() {
86178
}
87179

88180
// Build vendored library if feature was set.
89-
if cfg!(feature = "vendored") {
181+
if cfg!(feature = "vendored") && target_os.as_str() != "espidf" {
90182
let libcoap_src_dir = Path::new(&out_dir).join("libcoap");
91183
// Read Makeflags into vector of strings
92184
//let make_flags: Vec<String> = std::env::var_os("CARGO_MAKEFLAGS")
@@ -275,72 +367,74 @@ fn main() {
275367
env::set_current_dir(current_dir_backup).expect("unable to switch back to source dir");
276368
}
277369

278-
// Tell cargo to link libcoap.
279-
println!(
280-
"cargo:rustc-link-lib={}{}",
281-
cfg!(feature = "static").then(|| "static=").unwrap_or("dylib="),
282-
format!(
283-
"coap-3-{}",
284-
&dtls_backend
285-
.as_ref()
286-
.map(|v| v.to_string())
287-
.unwrap_or_else(|| "notls".to_string())
288-
)
289-
.as_str()
290-
);
370+
if target_os.as_str() != "espidf" {
371+
// Tell cargo to link libcoap.
372+
println!(
373+
"cargo:rustc-link-lib={}{}",
374+
cfg!(feature = "static").then(|| "static=").unwrap_or("dylib="),
375+
format!(
376+
"coap-3-{}",
377+
&dtls_backend
378+
.as_ref()
379+
.map(|v| v.to_string())
380+
.unwrap_or_else(|| "notls".to_string())
381+
)
382+
.as_str()
383+
);
291384

292-
// For the DTLS libraries, we need to tell cargo which external libraries to link.
293-
// Note that these linker instructions have to be added *after* the linker instruction
294-
// for libcoap itself, as some linkers require dependencies to be in reverse order.
295-
if let Some(dtls_backend) = dtls_backend {
296-
match dtls_backend {
297-
DtlsBackend::TinyDtls => {
298-
// Handled by tinydtls-sys
299-
},
300-
DtlsBackend::OpenSsl => {
301-
// Handled by openssl-sys
302-
},
303-
DtlsBackend::MbedTls => {
304-
// If mbedtls is vendored, mbedtls-sys-auto already takes care of linking.
305-
if env::var_os("DEP_MBEDTLS_INCLUDE").is_none() {
306-
// We aren't using mbedtls-sys-auto if we aren't vendoring (as it doesn't support
307-
// mbedtls >= 3.0.0), so we need to tell cargo to link to mbedtls ourselves.
385+
// For the DTLS libraries, we need to tell cargo which external libraries to link.
386+
// Note that these linker instructions have to be added *after* the linker instruction
387+
// for libcoap itself, as some linkers require dependencies to be in reverse order.
388+
if let Some(dtls_backend) = dtls_backend {
389+
match dtls_backend {
390+
DtlsBackend::TinyDtls => {
391+
// Handled by tinydtls-sys
392+
},
393+
DtlsBackend::OpenSsl => {
394+
// Handled by openssl-sys
395+
},
396+
DtlsBackend::MbedTls => {
397+
// If mbedtls is vendored, mbedtls-sys-auto already takes care of linking.
398+
if env::var_os("DEP_MBEDTLS_INCLUDE").is_none() {
399+
// We aren't using mbedtls-sys-auto if we aren't vendoring (as it doesn't support
400+
// mbedtls >= 3.0.0), so we need to tell cargo to link to mbedtls ourselves.
308401

309-
if let Some(mbedtls_lib_path) = env::var_os("MBEDTLS_LIBRARY_PATH") {
310-
println!("cargo:rustc-link-search=native={}", mbedtls_lib_path.to_str().unwrap())
311-
}
312-
// Try to find mbedtls using pkg-config, will emit cargo link statements if successful
313-
if env::var_os("MBEDTLS_LIBRARY_PATH").is_some() || pkg_config::Config::new().statik(cfg!(feature = "static")).probe("mbedtls").is_err() {
314-
// couldn't find using pkg-config or MBEDTLS_LIBRARY_PATH was set, just try
315-
// linking with given library search path
316-
println!("cargo:rustc-link-lib={}mbedtls",
317-
cfg!(feature = "static").then(|| "static=").unwrap_or("dylib=")
318-
);
319-
println!("cargo:rustc-link-lib={}mbedx509",
320-
cfg!(feature = "static").then(|| "static=").unwrap_or("dylib=")
321-
);
322-
println!("cargo:rustc-link-lib={}mbedcrypto",
323-
cfg!(feature = "static").then(|| "static=").unwrap_or("dylib=")
324-
);
402+
if let Some(mbedtls_lib_path) = env::var_os("MBEDTLS_LIBRARY_PATH") {
403+
println!("cargo:rustc-link-search=native={}", mbedtls_lib_path.to_str().unwrap())
404+
}
405+
// Try to find mbedtls using pkg-config, will emit cargo link statements if successful
406+
if env::var_os("MBEDTLS_LIBRARY_PATH").is_some() || pkg_config::Config::new().statik(cfg!(feature = "static")).probe("mbedtls").is_err() {
407+
// couldn't find using pkg-config or MBEDTLS_LIBRARY_PATH was set, just try
408+
// linking with given library search path
409+
println!("cargo:rustc-link-lib={}mbedtls",
410+
cfg!(feature = "static").then(|| "static=").unwrap_or("dylib=")
411+
);
412+
println!("cargo:rustc-link-lib={}mbedx509",
413+
cfg!(feature = "static").then(|| "static=").unwrap_or("dylib=")
414+
);
415+
println!("cargo:rustc-link-lib={}mbedcrypto",
416+
cfg!(feature = "static").then(|| "static=").unwrap_or("dylib=")
417+
);
418+
}
325419
}
326-
}
327-
},
328-
DtlsBackend::GnuTls => {
329-
// gnutls-sys is unmaintained, so we need to link to gnutls ourselves.
420+
},
421+
DtlsBackend::GnuTls => {
422+
// gnutls-sys is unmaintained, so we need to link to gnutls ourselves.
330423

331-
// try pkg-config
332-
if probe_library("gnutls").is_err() {
333-
// if that doesn't work, try using the standard library search path.
334-
println!("cargo:rustc-link-lib=gnutls")
335-
}
336-
},
424+
// try pkg-config
425+
if probe_library("gnutls").is_err() {
426+
// if that doesn't work, try using the standard library search path.
427+
println!("cargo:rustc-link-lib=gnutls")
428+
}
429+
},
430+
}
337431
}
338432
}
339433

340434
bindgen_builder = bindgen_builder
341435
.header("src/wrapper.h")
342436
.default_enum_style(EnumVariation::Rust { non_exhaustive: true })
343-
.formatter(bindgen::Formatter::None)
437+
.rustfmt_bindings(false)
344438
// Causes invalid syntax for some reason, so we have to disable it.
345439
.generate_comments(false)
346440
.dynamic_link_require_all(true)
@@ -353,7 +447,6 @@ fn main() {
353447
// We use the definitions made by the libc crate instead
354448
.blocklist_type("sockaddr(_in|_in6)?")
355449
.blocklist_type("in6?_(addr|port)(_t)?")
356-
.blocklist_type("epoll_event")
357450
.blocklist_type("in6_addr__bindgen_ty_1")
358451
.blocklist_type("(__)?socklen_t")
359452
.blocklist_type("fd_set")

libcoap-sys/src/lib.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@
8686
#![allow(deref_nullptr)]
8787
#![allow(non_snake_case)]
8888

89-
use libc::{epoll_event, fd_set, sockaddr, sockaddr_in, sockaddr_in6, socklen_t, time_t, sa_family_t};
90-
91-
use crate::coap_pdu_type_t::COAP_MESSAGE_RST;
89+
use libc::{fd_set, sockaddr, sockaddr_in, sockaddr_in6, socklen_t, time_t, sa_family_t};
90+
#[cfg(not(target_os = "espidf"))]
91+
use libc::{epoll_event};
9292

9393
// use dtls backend libraries in cases where they set our linker flags, otherwise cargo will
9494
// optimize them out.
@@ -104,7 +104,16 @@ include!(concat!(env!("OUT_DIR"), "\\bindings.rs"));
104104
#[cfg(not(target_family = "windows"))]
105105
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
106106

107-
#[cfg(test)]
107+
#[inline]
108+
pub unsafe fn coap_send_rst(
109+
session: *mut coap_session_t,
110+
request: *const coap_pdu_t,
111+
_type_: coap_pdu_type_t,
112+
) -> coap_mid_t {
113+
coap_send_message_type(session, request, crate::coap_pdu_type_t::COAP_MESSAGE_RST)
114+
}
115+
116+
#[cfg(all(test, not(feature = "esp")))]
108117
mod tests {
109118
use std::{
110119
ffi::c_void,

libcoap/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ dtls_openssl_vendored = ["dtls_openssl", "libcoap-sys/dtls_backend_openssl_vendo
2727
dtls_gnutls = ["libcoap-sys/dtls_backend_gnutls"]
2828
dtls_mbedtls = ["libcoap-sys/dtls_backend_mbedtls"]
2929
dtls_mbedtls_vendored = ["dtls_mbedtls", "libcoap-sys/dtls_backend_mbedtls_vendored"]
30-
tcp = []
30+
tcp = ["libcoap-sys/tcp"]
3131
nightly = []
3232
vendored = ["libcoap-sys/vendored"]
3333

3434
[dependencies]
35-
libcoap-sys = { version = "^0.2.2", path = "../libcoap-sys" }
35+
libcoap-sys = { version = "^0.2.2", path = "../libcoap-sys", default-features = false, features = ["client", "server"] }
3636
libc = { version = "^0.2.95" }
37-
num-derive = { version = "^0.3.3" }
37+
num-derive = { version = "^0.3.3" }
3838
num-traits = { version = "^0.2.14" }
3939
url = { version = "^2.2" }
4040
rand = { version = "^0.8.4" }

libcoap/src/context.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ impl<'a> CoapContext<'a> {
110110
}
111111
// SAFETY: We checked that raw_context is not null.
112112
unsafe {
113-
coap_context_set_block_mode(raw_context, (COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY) as u32);
113+
coap_context_set_block_mode(raw_context, (COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY) as u8);
114114
coap_register_response_handler(raw_context, Some(session_response_handler));
115115
}
116116
let inner = CoapLendableFfiRcCell::new(CoapContextInner {

libcoap/src/resource.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ pub unsafe fn prepare_resource_handler_data<'a, D: Any + ?Sized + Debug>(
9595
match (request, response) {
9696
(Ok(request), Ok(response)) => Ok((resource, session, request, response)),
9797
(v1, v2) => {
98-
coap_send_rst(raw_session, raw_incoming_pdu);
98+
coap_send_rst(raw_session, raw_incoming_pdu, COAP_MESSAGE_RST);
9999
Err(v1.and(v2).err().unwrap())
100100
},
101101
}

libcoap/src/types.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,23 @@ impl From<SocketAddr> for CoapAddress {
128128
size: std::mem::size_of::<sockaddr_in>() as socklen_t,
129129
addr: std::mem::zeroed(),
130130
};
131+
131132
*coap_addr.addr.sin.as_mut() = sockaddr_in {
133+
#[cfg(any(
134+
bsd,
135+
target_os = "aix",
136+
target_os = "haiku",
137+
target_os = "hurd",
138+
target_os = "espidf",
139+
))]
140+
sin_len: (std::mem::size_of::<sockaddr_in>() as u8),
132141
sin_family: AF_INET as sa_family_t,
133142
sin_port: addr.port().to_be(),
134143
sin_addr: in_addr {
135144
s_addr: u32::from_ne_bytes(addr.ip().octets()),
136145
},
137146
sin_zero: Default::default(),
138-
};
147+
};
139148
CoapAddress(coap_addr)
140149
}
141150
},
@@ -148,7 +157,16 @@ impl From<SocketAddr> for CoapAddress {
148157
size: std::mem::size_of::<sockaddr_in6>() as socklen_t,
149158
addr: std::mem::zeroed(),
150159
};
160+
151161
*coap_addr.addr.sin6.as_mut() = sockaddr_in6 {
162+
#[cfg(any(
163+
bsd,
164+
target_os = "aix",
165+
target_os = "haiku",
166+
target_os = "hurd",
167+
target_os = "espidf",
168+
))]
169+
sin6_len: (std::mem::size_of::<sockaddr_in6>() as u8),
152170
sin6_family: AF_INET6 as sa_family_t,
153171
sin6_port: addr.port().to_be(),
154172
sin6_addr: in6_addr {
@@ -157,6 +175,7 @@ impl From<SocketAddr> for CoapAddress {
157175
sin6_flowinfo: addr.flowinfo(),
158176
sin6_scope_id: addr.scope_id(),
159177
};
178+
160179
CoapAddress(coap_addr)
161180
}
162181
},

0 commit comments

Comments
 (0)