-
Notifications
You must be signed in to change notification settings - Fork 1
/
build.rs
412 lines (387 loc) · 19.7 KB
/
build.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
// SPDX-License-Identifier: BSD-2-CLAUSE
/*
* build.rs - build script for libcoap Rust bindings.
* This file is part of the libcoap-sys crate, see the README and LICENSE files for
* more information and terms of use.
* Copyright © 2021-2023 The NAMIB Project Developers, all rights reserved.
* See the README as well as the LICENSE file for more information.
*/
use std::{
default::Default,
env,
io::ErrorKind,
path::{Path, PathBuf},
process::Command,
};
use bindgen::EnumVariation;
use pkg_config::probe_library;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum DtlsBackend {
GnuTls,
OpenSsl,
MbedTls,
TinyDtls,
}
impl ToString for DtlsBackend {
fn to_string(&self) -> String {
match self {
DtlsBackend::GnuTls => "gnutls",
DtlsBackend::OpenSsl => "openssl",
DtlsBackend::MbedTls => "mbedtls",
DtlsBackend::TinyDtls => "tinydtls",
}
.to_string()
}
}
fn main() {
println!("cargo:rerun-if-changed=src/libcoap/");
println!("cargo:rerun-if-changed=src/wrapper.h");
let mut bindgen_builder = bindgen::Builder::default();
// Read required environment variables.
let orig_pkg_config = std::env::var_os("PKG_CONFIG_PATH").map(|v| String::from(v.to_str().unwrap()));
let out_dir = env::var_os("OUT_DIR").unwrap();
if cfg!(feature = "esp") {
embuild::espidf::sysenv::output();
let esp_idf_path = embuild::espidf::sysenv::idf_path().ok_or("missing IDF path").unwrap();
println!("cargo:warning={}", &esp_idf_path);
let esp_idf_buildroot = env::var("DEP_ESP_IDF_ROOT").unwrap();
let esp_include_path = embuild::espidf::sysenv::cincl_args().ok_or("missing IDF cincl args").unwrap();
let arg_splitter = regex::Regex::new(r##"(?:[^\\]"[^"]*[^\\]")?(\s)"##).unwrap();
let apostrophe_remover = regex::Regex::new(r##"^"(?<content>.*)"$"##).unwrap();
let esp_clang_args = arg_splitter.split(
esp_include_path.args.as_str()
).map(|x| apostrophe_remover.replace(x.trim(), "$content").to_string()).collect::<Vec<String>>();
bindgen_builder = bindgen_builder
.clang_args(&esp_clang_args)
.clang_arg("-target")
.clang_arg("xtensa") // Will need to be adjusted for RISC-V ESPs, workaround according to https://github.com/esp-rs/esp-idf-sys/blob/7a0747614cdd3a65cf4ac8094bbeebfee980dbb2/build/build.rs#L119
.clang_arg("-DESP_PLATFORM")
.clang_arg(format!("-I{}/components/lwip/lwip/src/include", esp_idf_path))
.clang_arg(format!("-I{}/managed_components/espressif__coap/libcoap/include", esp_idf_buildroot))
.clang_arg(format!("-I{}/build/config/", esp_idf_buildroot))
;
//for arg in &esp_clang_args {
// println!("cargo:warning={}", arg);
//}
}
let mut dtls_backend = Option::None;
if cfg!(feature = "dtls") {
// We can only select one TLS backend at a time for libcoap, but cargo does not support mutually
// exclusive features, and it would be really bad if a project that uses multiple dependencies
// which depend on different TLS backends would not compile.
// Therefore, if multiple TLS backend features are enabled, we choose one based on the following
// priority order: gnutls > openssl > mbedtls > tinydtls, matching the order specified in
// https://github.com/obgm/libcoap/blob/develop/configure.ac#L494
let mut multiple_backends = false;
if cfg!(feature = "dtls_backend_tinydtls") {
dtls_backend = Some(DtlsBackend::TinyDtls);
}
if cfg!(feature = "dtls_backend_mbedtls") {
if dtls_backend.is_some() {
multiple_backends = true;
}
println!("cargo:rerun-if-env-changed=MBEDTLS_LIBRARY_PATH");
dtls_backend = Some(DtlsBackend::MbedTls);
}
if cfg!(feature = "dtls_backend_openssl") {
if dtls_backend.is_some() {
multiple_backends = true;
}
dtls_backend = Some(DtlsBackend::OpenSsl);
}
if cfg!(feature = "dtls_backend_gnutls") {
if dtls_backend.is_some() {
multiple_backends = true;
}
dtls_backend = Some(DtlsBackend::GnuTls);
}
if multiple_backends {
println!("cargo:warning=Multiple DTLS backends enabled for libcoap-sys. Only one can be enabled, choosing {:?} as the backend to use. This can cause problems.", dtls_backend.as_ref().unwrap());
}
if dtls_backend.is_none() {
println!("cargo:warning=No DTLS backend selected for libcoap-sys, aborting build.");
panic!("No DTLS backend selected for libcoap-sys, aborting build")
}
}
// Build vendored library if feature was set.
if cfg!(feature = "vendored") {
let libcoap_src_dir = Path::new(&out_dir).join("libcoap");
// Read Makeflags into vector of strings
//let make_flags: Vec<String> = std::env::var_os("CARGO_MAKEFLAGS")
// .unwrap()
// .into_string()
// .unwrap()
// .split(' ')
// .map(String::from)
// .collect();
// Even though libcoap supports out-of-source builds, autogen.sh (or the corresponding
// autotools) modify files in the source tree, which causes verification problems when
// running cargo package.
// Therefore, we copy the libcoap source over to the output directory and build from there.
let copy_options = fs_extra::dir::CopyOptions {
overwrite: true,
..Default::default()
};
match std::fs::remove_dir_all(&libcoap_src_dir) {
Ok(_) => {},
Err(e) if e.kind() == ErrorKind::NotFound => {},
e => e.unwrap(),
}
fs_extra::dir::copy(
Path::new(env!("CARGO_MANIFEST_DIR")).join("src").join("libcoap"),
Path::new(&out_dir),
©_options,
)
.unwrap();
let current_dir_backup = env::current_dir().unwrap();
env::set_current_dir(&libcoap_src_dir).expect("unable to change to libcoap build dir");
Command::new(libcoap_src_dir.join("autogen.sh")).status().unwrap();
let mut build_config = autotools::Config::new(libcoap_src_dir);
build_config.out_dir(&out_dir);
if let Some(dtls_backend) = dtls_backend {
build_config
.enable("dtls", None)
.with(dtls_backend.to_string().as_str(), None);
// If DTLS is vendored we need to tell libcoap about the vendored version
match dtls_backend {
DtlsBackend::TinyDtls => {
// We do not ship tinydtls with our source distribution. Instead, we use tinydtls-sys.
build_config.without("submodule-tinydtls", None);
// If tinydtls-sys is built with the vendored feature, the library is built alongside
// the Rust crate. To use the version built by the tinydtls-sys build script, we use the
// environment variables set by the build script.
if let Some(tinydtls_include) = env::var_os("DEP_TINYDTLS_INCLUDE") {
build_config.env(
"TinyDTLS_CFLAGS",
format!(
"-I{} -I{}",
tinydtls_include.to_str().unwrap(),
Path::new(tinydtls_include.to_str().unwrap())
.join("tinydtls")
.to_str()
.unwrap()
),
);
};
if let Some(tinydtls_libs) = env::var_os("DEP_TINYDTLS_LIBS") {
build_config.env("TinyDTLS_LIBS", format!("-L{}", tinydtls_libs.to_str().unwrap()));
build_config.env(
"PKG_CONFIG_PATH",
Path::new(tinydtls_libs.as_os_str())
.join("lib")
.join("pkgconfig")
.into_os_string(),
);
}
},
DtlsBackend::OpenSsl => {
// Set include path according to the path provided by openssl-sys (relevant if
// openssl-sys is vendored)
if let Some(openssl_include) = env::var_os("DEP_OPENSSL_INCLUDE") {
build_config.env("OpenSSL_CFLAGS", format!("-I{}", openssl_include.to_str().unwrap()));
build_config.env(
"PKG_CONFIG_PATH",
Path::new(openssl_include.as_os_str())
.parent()
.unwrap()
.join("lib")
.join("pkgconfig")
.into_os_string(),
);
}
},
DtlsBackend::MbedTls => {
// Set include path according to the path provided by mbedtls-sys (relevant if
// mbedtls-sys is vendored).
// libcoap doesn't support overriding the MbedTLS CFLAGS, but doesn't set those
// either, so we just set CFLAGS and hope they propagate.
if let Some(mbedtls_include) = env::var_os("DEP_MBEDTLS_INCLUDE") {
let mbedtls_library_path = Path::new(env::var_os("DEP_MBEDTLS_CONFIG_H").unwrap().as_os_str())
.parent()
.unwrap()
.join("build")
.join("library");
build_config.env("MbedTLS_CFLAGS", format!("-I{}", mbedtls_include.to_str().unwrap()));
build_config.env("MbedTLS_LIBS", format!("-lmbedtls -lmbedcrypto -lmbedx509 -L{}", mbedtls_library_path.to_str().unwrap()));
} else {
// If we're not vendoring mbedtls, allow manually setting the mbedtls
// library path (required if linking statically, which is always the case
// when vendoring libcoap).
if let Some(mbedtls_lib_path) = env::var_os("MBEDTLS_LIBRARY_PATH") {
build_config.env("MbedTLS_LIBS", format!("-lmbedtls -lmbedcrypto -lmbedx509 -L{}", mbedtls_lib_path.to_str().unwrap()));
if let Some(mbedtls_include_path) = env::var_os("MBEDTLS_INCLUDE_PATH") {
build_config.env("MbedTLS_CFLAGS", format!("-I{}", mbedtls_include_path.to_str().unwrap()));
}
} else {
// mbedtls will get pkg-config support in the near future, prepare for that
if let Ok(lib) = &pkg_config::Config::new().cargo_metadata(false).probe("mbedtls") {
let mut lib_flags = "-lmbedtls -lmbedcrypto -lmbedx509".to_string();
lib_flags.push_str(lib.link_paths.iter().map(|x| format!("-L{} ", x.display())).collect::<String>().as_str());
build_config.env("MbedTLS_LIBS", lib_flags);
build_config.env("MbedTLS_CFLAGS", lib.link_paths.iter().map(|x| format!("-I{}", x.display())).collect::<String>());
} else {
println!("cargo:warning=You have enabled libcoap vendoring with mbedtls, but haven't provided a static library path for mbedtls (MBEDTLS_LIBRARY_PATH environment variable is unset). Building might fail because of that.");
}
}
}
},
DtlsBackend::GnuTls => {
// Vendoring not supported
},
}
} else {
build_config.disable("dtls", None);
}
build_config
// Set Makeflags
//.make_args(make_flags)
// Disable shared library compilation because the vendored library will always be
// statically linked
.disable("shared", None)
// Disable any documentation for vendored C library
.disable("documentation", None)
.disable("doxygen", None)
.disable("manpages", None)
// This would install the license into the documentation directory, but we don't use the
// generated documentation anywhere.
.disable("license-install", None)
// Disable tests and examples as well as test coverage
.disable("tests", None)
.disable("examples", None)
.disable("gcov", None)
// TODO allow multithreaded access
.disable("thread-safe", None);
// Enable debug symbols if enabled in Rust
match std::env::var_os("DEBUG").unwrap().to_str().unwrap() {
"0" | "false" => {},
_ => {
build_config.with("debug", None);
},
}
// Enable dependency features based on selected cargo features.
build_config
.enable("async", Some(if cfg!(feature = "async") { "yes" } else { "no" }))
.enable("tcp", Some(if cfg!(feature = "tcp") { "yes" } else { "no" }))
.enable("server-mode", Some(if cfg!(feature = "server") { "yes" } else { "no" }))
.enable("client-mode", Some(if cfg!(feature = "client") { "yes" } else { "no" }))
.with("epoll", Some(if cfg!(feature = "epoll") { "yes" } else { "no" }));
// Run build
let dst = build_config.build();
// Add the built library to the search path
println!("cargo:rustc-link-search=native={}", dst.join("lib").to_str().unwrap());
println!("cargo:include={}", dst.join("include").to_str().unwrap());
bindgen_builder = bindgen_builder
.clang_arg(format!("-I{}", dst.join("include").to_str().unwrap()))
.clang_arg(format!("-L{}", dst.join("lib").to_str().unwrap()));
env::set_var(
"PKG_CONFIG_PATH",
format!(
"{}:{}",
dst.join("lib").join("pkgconfig").to_str().unwrap(),
orig_pkg_config.as_ref().map(String::clone).unwrap_or_else(String::new)
),
);
env::set_current_dir(current_dir_backup).expect("unable to switch back to source dir");
}
// Tell cargo to link libcoap.
println!(
"cargo:rustc-link-lib={}{}",
cfg!(feature = "static").then(|| "static=").unwrap_or("dylib="),
format!(
"coap-3-{}",
&dtls_backend
.as_ref()
.map(|v| v.to_string())
.unwrap_or_else(|| "notls".to_string())
)
.as_str()
);
// For the DTLS libraries, we need to tell cargo which external libraries to link.
// Note that these linker instructions have to be added *after* the linker instruction
// for libcoap itself, as some linkers require dependencies to be in reverse order.
if let Some(dtls_backend) = dtls_backend {
match dtls_backend {
DtlsBackend::TinyDtls => {
// Handled by tinydtls-sys
},
DtlsBackend::OpenSsl => {
// Handled by openssl-sys
},
DtlsBackend::MbedTls => {
// If mbedtls is vendored, mbedtls-sys-auto already takes care of linking.
if env::var_os("DEP_MBEDTLS_INCLUDE").is_none() {
// We aren't using mbedtls-sys-auto if we aren't vendoring (as it doesn't support
// mbedtls >= 3.0.0), so we need to tell cargo to link to mbedtls ourselves.
if let Some(mbedtls_lib_path) = env::var_os("MBEDTLS_LIBRARY_PATH") {
println!("cargo:rustc-link-search=native={}", mbedtls_lib_path.to_str().unwrap())
}
// Try to find mbedtls using pkg-config, will emit cargo link statements if successful
if env::var_os("MBEDTLS_LIBRARY_PATH").is_some() || pkg_config::Config::new().statik(cfg!(feature = "static")).probe("mbedtls").is_err() {
// couldn't find using pkg-config or MBEDTLS_LIBRARY_PATH was set, just try
// linking with given library search path
println!("cargo:rustc-link-lib={}mbedtls",
cfg!(feature = "static").then(|| "static=").unwrap_or("dylib=")
);
println!("cargo:rustc-link-lib={}mbedx509",
cfg!(feature = "static").then(|| "static=").unwrap_or("dylib=")
);
println!("cargo:rustc-link-lib={}mbedcrypto",
cfg!(feature = "static").then(|| "static=").unwrap_or("dylib=")
);
}
}
},
DtlsBackend::GnuTls => {
// gnutls-sys is unmaintained, so we need to link to gnutls ourselves.
// try pkg-config
if probe_library("gnutls").is_err() {
// if that doesn't work, try using the standard library search path.
println!("cargo:rustc-link-lib=gnutls")
}
},
}
}
bindgen_builder = bindgen_builder
.header("src/wrapper.h")
.default_enum_style(EnumVariation::Rust { non_exhaustive: true })
.formatter(bindgen::Formatter::None)
// Causes invalid syntax for some reason, so we have to disable it.
.generate_comments(false)
.dynamic_link_require_all(true)
.allowlist_function("coap_.*")
.allowlist_type("coap_.*")
.allowlist_var("coap_.*")
.allowlist_function("COAP_.*")
.allowlist_type("COAP_.*")
.allowlist_var("COAP_.*")
// We use the definitions made by the libc crate instead
.blocklist_type("sockaddr(_in|_in6)?")
.blocklist_type("in6?_(addr|port)(_t)?")
.blocklist_type("epoll_event")
.blocklist_type("in6_addr__bindgen_ty_1")
.blocklist_type("(__)?socklen_t")
.blocklist_type("fd_set")
.blocklist_type("sa_family_t")
.blocklist_type("(__)?time_t")
.blocklist_type("__fd_mask")
// Are generated because they are typedef-ed inside of the C headers, blocklisting them
// will instead replace them with the appropriate rust types.
// See https://github.com/rust-lang/rust-bindgen/issues/1215 for an open issue concerning
// this problem.
.blocklist_type("__(u)?int(8|16|32|64|128)_t")
.size_t_is_usize(true);
if !cfg!(feature = "vendored") {
// Triggers a rebuild on every cargo build invocation if used for the vendored version, as
// the included headers seem to come from our built version.
// Should be fine though, as we already printed `cargo:rerun-if-changed=src/libcoap/` at the
// start of the file.
bindgen_builder = bindgen_builder.parse_callbacks(Box::new(bindgen::CargoCallbacks));
}
let bindings = bindgen_builder.generate().unwrap();
let out_path = PathBuf::from(out_dir);
bindings.write_to_file(out_path.join("bindings.rs")).unwrap();
match orig_pkg_config.as_ref() {
Some(value) => env::set_var("PKG_CONFIG_PATH", value),
None => env::remove_var("PKG_CONFIG_PATH"),
}
}