1 use std::{env, fs, path::PathBuf};
2
3 static VERSION: &str = "1.0.27";
4
link(name: &str, bundled: bool)5 fn link(name: &str, bundled: bool) {
6 use std::env::var;
7 let target = var("TARGET").unwrap();
8 let target: Vec<_> = target.split('-').collect();
9 if target.get(2) == Some(&"windows") {
10 println!("cargo:rustc-link-lib=dylib={}", name);
11 if bundled && target.get(3) == Some(&"gnu") {
12 let dir = var("CARGO_MANIFEST_DIR").unwrap();
13 println!("cargo:rustc-link-search=native={}/{}", dir, target[0]);
14 }
15 }
16 }
17
link_framework(name: &str)18 pub fn link_framework(name: &str) {
19 println!("cargo:rustc-link-lib=framework={}", name);
20 }
21
get_macos_major_version() -> Option<usize>22 fn get_macos_major_version() -> Option<usize> {
23 if !cfg!(target_os = "macos") {
24 return None;
25 }
26
27 let output = std::process::Command::new("sw_vers")
28 .args(["-productVersion"])
29 .output()
30 .unwrap();
31 let version = std::str::from_utf8(&output.stdout).unwrap().trim_end();
32 let components: Vec<&str> = version.split('.').collect();
33 let major: usize = components[0].parse().unwrap();
34 Some(major)
35 }
36
find_libusb_pkg(statik: bool) -> bool37 fn find_libusb_pkg(statik: bool) -> bool {
38 if std::env::var("CARGO_CFG_TARGET_ENV") == Ok("msvc".into()) {
39 #[cfg(target_os = "windows")]
40 return match vcpkg::Config::new().find_package("libusb") {
41 Ok(_) => true,
42 Err(e) => {
43 if pkg_config::probe_library("libusb-1.0").is_ok() {
44 true
45 } else {
46 println!("Can't find libusb pkg: {:?}", e);
47 false
48 }
49 }
50 };
51 }
52 // https://github.com/rust-lang/rust/issues/96943
53 let needs_rustc_issue_96943_workaround: bool = get_macos_major_version()
54 .map(|major| major >= 11)
55 .unwrap_or_default();
56
57 match pkg_config::Config::new().statik(statik).probe("libusb-1.0") {
58 Ok(l) => {
59 for lib in l.libs {
60 if statik {
61 if needs_rustc_issue_96943_workaround && lib == "objc" {
62 continue;
63 }
64 println!("cargo:rustc-link-lib=static={}", lib);
65 }
66 }
67 // Provide metadata and include directory for dependencies
68 if statik {
69 println!("cargo:static=1");
70 }
71 assert!(l.include_paths.len() <= 1); // Cannot have multiple env vars with same name
72 for path in l.include_paths {
73 println!("cargo:include={}", path.to_str().unwrap());
74 }
75 println!("cargo:version_number={}", l.version);
76 true
77 }
78 Err(e) => {
79 println!("Can't find libusb pkg: {:?}", e);
80 false
81 }
82 }
83 }
84
make_source()85 fn make_source() {
86 let libusb_source = PathBuf::from("libusb");
87
88 /*
89 Example environment variables and values:
90
91 CARGO_CFG_TARGET_ARCH: aarch64
92 CARGO_CFG_TARGET_ENDIAN: little
93 CARGO_CFG_TARGET_ENV:
94 CARGO_CFG_TARGET_FAMILY: unix
95 CARGO_CFG_TARGET_OS: android
96 CARGO_CFG_TARGET_POINTER_WIDTH: 64
97 CARGO_CFG_TARGET_VENDOR: unknown
98 CARGO_CFG_UNIX:
99 */
100
101 // Provide metadata and include directory for dependencies
102 println!("cargo:vendored=1");
103 println!("cargo:static=1");
104 let include_dir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("include");
105 fs::create_dir_all(&include_dir).unwrap();
106 fs::copy(
107 libusb_source.join("libusb/libusb.h"),
108 include_dir.join("libusb.h"),
109 )
110 .unwrap();
111 println!("cargo:include={}", include_dir.to_str().unwrap());
112
113 fs::File::create(format!("{}/{}", include_dir.display(), "config.h")).unwrap();
114 let mut base_config = cc::Build::new();
115 base_config.include(&include_dir);
116 base_config.include(libusb_source.join("libusb"));
117
118 base_config.define("PRINTF_FORMAT(a, b)", Some(""));
119 base_config.define("ENABLE_LOGGING", Some("1"));
120
121 if std::env::var("CARGO_CFG_TARGET_ENV") == Ok("msvc".into()) {
122 fs::copy(
123 libusb_source.join("msvc/config.h"),
124 include_dir.join("config.h"),
125 )
126 .unwrap();
127 }
128
129 if std::env::var("CARGO_CFG_TARGET_OS") == Ok("macos".into()) {
130 base_config.define("OS_DARWIN", Some("1"));
131 base_config.define("TARGET_OS_OSX", Some("1"));
132 base_config.file(libusb_source.join("libusb/os/darwin_usb.c"));
133 link_framework("CoreFoundation");
134 link_framework("IOKit");
135 link_framework("Security");
136 link("objc", false);
137 }
138
139 if std::env::var("CARGO_CFG_TARGET_OS") == Ok("linux".into())
140 || std::env::var("CARGO_CFG_TARGET_OS") == Ok("android".into())
141 {
142 base_config.define("OS_LINUX", Some("1"));
143 base_config.define("HAVE_ASM_TYPES_H", Some("1"));
144 base_config.define("_GNU_SOURCE", Some("1"));
145 base_config.define("HAVE_TIMERFD", Some("1"));
146 base_config.define("HAVE_EVENTFD", Some("1"));
147 base_config.file(libusb_source.join("libusb/os/linux_netlink.c"));
148 base_config.file(libusb_source.join("libusb/os/linux_usbfs.c"));
149 }
150
151 if std::env::var("CARGO_CFG_TARGET_FAMILY") == Ok("unix".into()) {
152 base_config.define("HAVE_SYS_TIME_H", Some("1"));
153 base_config.define("HAVE_NFDS_T", Some("1"));
154 base_config.define("PLATFORM_POSIX", Some("1"));
155 base_config.define("HAVE_CLOCK_GETTIME", Some("1"));
156 base_config.define(
157 "DEFAULT_VISIBILITY",
158 Some("__attribute__((visibility(\"default\")))"),
159 );
160
161 if let Ok(lib) = pkg_config::probe_library("libudev") {
162 base_config.define("USE_UDEV", Some("1"));
163 base_config.define("HAVE_LIBUDEV", Some("1"));
164 base_config.file(libusb_source.join("libusb/os/linux_udev.c"));
165 for path in lib.include_paths {
166 base_config.include(path.to_str().unwrap());
167 }
168 };
169
170 println!("Including posix!");
171 base_config.file(libusb_source.join("libusb/os/events_posix.c"));
172 base_config.file(libusb_source.join("libusb/os/threads_posix.c"));
173 }
174
175 if std::env::var("CARGO_CFG_TARGET_OS") == Ok("windows".into()) {
176 if std::env::var("CARGO_CFG_TARGET_ENV") == Ok("msvc".into()) {
177 base_config.flag("/source-charset:utf-8");
178 }
179
180 base_config.warnings(false);
181 base_config.define("OS_WINDOWS", Some("1"));
182 base_config.file(libusb_source.join("libusb/os/events_windows.c"));
183 base_config.file(libusb_source.join("libusb/os/threads_windows.c"));
184 base_config.file(libusb_source.join("libusb/os/windows_common.c"));
185 base_config.file(libusb_source.join("libusb/os/windows_usbdk.c"));
186 base_config.file(libusb_source.join("libusb/os/windows_winusb.c"));
187
188 base_config.define("DEFAULT_VISIBILITY", Some(""));
189 base_config.define("PLATFORM_WINDOWS", Some("1"));
190 link("user32", false);
191 }
192
193 base_config.file(libusb_source.join("libusb/core.c"));
194 base_config.file(libusb_source.join("libusb/descriptor.c"));
195 base_config.file(libusb_source.join("libusb/hotplug.c"));
196 base_config.file(libusb_source.join("libusb/io.c"));
197 base_config.file(libusb_source.join("libusb/strerror.c"));
198 base_config.file(libusb_source.join("libusb/sync.c"));
199
200 base_config.compile("usb-vendored");
201 println!("cargo:version_number={}", VERSION);
202 }
203
main()204 fn main() {
205 println!("cargo:rerun-if-env-changed=LIBUSB_STATIC");
206 let statik = {
207 if cfg!(target_os = "macos") {
208 match std::env::var("LIBUSB_STATIC").unwrap_or_default().as_ref() {
209 "" | "0" => false,
210 _ => true,
211 }
212 } else {
213 std::env::var("CARGO_CFG_TARGET_FEATURE")
214 .map(|s| s.contains("crt-static"))
215 .unwrap_or_default()
216 }
217 };
218
219 let is_freebsd = std::env::var("CARGO_CFG_TARGET_OS") == Ok("freebsd".into());
220
221 if (!is_freebsd && cfg!(feature = "vendored")) || !find_libusb_pkg(statik) {
222 make_source();
223 }
224 }
225