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