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
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
// Copyright (c) 2025 Gavin Henry <ghenry@sentrypeer.org>
extern crate bindgen;
extern crate cc;
use walkdir::WalkDir;
use std::env;
use std::path::PathBuf;
use std::process::Command;
fn main() {
// Initialize and update the Git submodule
Command::new("git")
.args(["submodule", "update", "--init", "--recursive"])
.status()
.expect("Failed to initialize/update submodules");
let submodule_path = "vendor/librecast";
let dst = PathBuf::from(env::var("OUT_DIR").unwrap());
// Try to find the librecast library using pkg-config first
if pkg_config::Config::new()
.atleast_version("0.11.1")
.probe("librecast")
.is_ok()
{
// If found, the necessary env vars will get set automatically
} else {
let src = PathBuf::from(submodule_path);
// Copy everything from the submodule to our OUT_DIR using WalkDir
for entry in WalkDir::new(&src).into_iter().filter_map(Result::ok) {
let src_path = entry.path();
let dst_path = dst.join(src_path.strip_prefix(&src).unwrap());
// Create the destination directory if it doesn't exist
if src_path.is_dir() {
std::fs::create_dir_all(&dst_path).expect("Failed to create directory");
} else {
// Skip symlinks, as all the tests are like so:
// 0000-0114.test -> ../0000-0114.test which don't
// exist in the test directory
if src_path.is_symlink() {
continue;
}
std::fs::copy(src_path, &dst_path).expect("Failed to copy a file");
}
}
// We need to get our headers from liblcrq-sys crate and set them
// in the CPPFLAGS environment variable for the configure script
let include_path = env::var_os("DEP_LIBLCRQ_INCLUDE")
.expect("DEP_LIBLCRQ_INCLUDE environment variable not set");
let include_for_env = format!("-I{}", include_path.display());
unsafe { env::set_var("CPPFLAGS", include_for_env) };
// Set LDFLAGS environment variable too using the INCLUDE path but
// swapping it to out/src
let lib_path = PathBuf::from(include_path);
let lib_search_path = lib_path.join("..").join("src");
let lib_for_env = format!("-L{}", lib_search_path.display());
unsafe { env::set_var("LDFLAGS", lib_for_env) };
let status = Command::new("./configure")
.args(["--with-blake3", "--with-sodium", "--with-lcrq"])
.current_dir(&dst)
.status()
.expect("Failed to find ./configure");
if !status.success() {
panic!("./configure failed to complete or run in {}", dst.display());
}
let status = Command::new("make")
.current_dir(&dst)
.status()
.expect("Failed to run make");
if !status.success() {
panic!("Failed to run make in {}", dst.display());
}
// Set our shared library path
println!("cargo:rustc-link-search={}/src", dst.display());
// Search for headers in our OUT_DIR
println!("cargo:include={}/include", dst.display());
// Link against the librecast library
println!("cargo:rustc-link-lib=librecast");
// Link against the lcrq library
println!("cargo:rustc-link-lib=lcrq");
// Set our LD_LIBRARY_PATH for dynamic linking for librecast src and lcrq
// src directories, as they are different
println!(
"cargo:rustc-env=LD_LIBRARY_PATH={}/src:{}",
dst.display(),
lib_search_path.display()
);
}
// lcrq.h is in the include directory of the liblcrq-sys crate
let liblcrq_include_path =
PathBuf::from(env::var_os("DEP_LIBLCRQ_INCLUDE").expect("DEP_LIBLCRQ_INCLUDE not set"));
// Generate bindings
let bindings = bindgen::Builder::default()
.header("wrapper.h")
// Where to look for lcrq.h
.clang_arg(format!("-I{}", liblcrq_include_path.display()))
// Look in our OUT_DIR for our normal headers
.clang_arg(format!("-I{}/include", dst.display()))
// https://github.com/rust-lang/rust-bindgen/issues/1693
// TODO: As per - https://github.com/rust-lang/rust-bindgen/issues/687#issuecomment-450750547
.blocklist_item("IPPORT_RESERVED")
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.expect("Unable to generate bindings");
// Write the bindings to the $OUT_DIR/bindings.rs file
bindings
.write_to_file(dst.join("bindings.rs"))
.expect("Couldn't write bindings.rs!");
}