ctranslate2_src_build_support/
lib.rs

1use std::{
2    env,
3    fs::File,
4    path::{Path, PathBuf},
5};
6
7use flate2::Compression;
8use tar::Builder;
9use walkdir::WalkDir;
10
11pub mod dnnl;
12pub mod download;
13pub mod file_changes;
14pub mod native;
15pub mod submodules;
16pub mod windows_crt_patch;
17
18#[derive(Debug, Eq, PartialEq, Copy, Clone)]
19pub enum Os {
20    Win,
21    Mac,
22    Linux,
23    Unknown,
24}
25
26pub fn export(lib_path: &Path, modules: &[PathBuf], modules2: &[PathBuf]) {
27    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
28    let out_dir = out_dir
29        .parent()
30        .unwrap()
31        .parent()
32        .unwrap()
33        .parent()
34        .unwrap();
35
36    let tar_gz = File::create(out_dir.join("vendored.tar.gz")).unwrap();
37    let enc = flate2::write::GzEncoder::new(tar_gz, Compression::default());
38    let mut tar = Builder::new(enc);
39
40    tar.append_dir_all("include", lib_path.join("../include"))
41        .unwrap();
42
43    for module in modules {
44        let mut file = File::open(&module).unwrap();
45        let name = module.file_name().unwrap().to_str().unwrap();
46        tar.append_file(format!("lib/{}", name), &mut file).unwrap();
47    }
48    for module in modules2 {
49        let mut file = File::open(&module).unwrap();
50        let name = module.file_name().unwrap().to_str().unwrap();
51        tar.append_file(format!("dyn/{}", name), &mut file).unwrap();
52    }
53
54    tar.finish().unwrap();
55}
56
57pub fn link_libraries<T: AsRef<Path>>(root: T) -> Vec<PathBuf> {
58    let mut current_dir = None;
59    let mut libs = Vec::new();
60    for entry in WalkDir::new(root).into_iter().filter_map(Result::ok) {
61        let path = entry.path();
62        if path.is_file() {
63            if let Some(file_name) = path
64                .file_name()
65                .and_then(|name| name.to_str())
66                .filter(|name| is_library(name))
67            {
68                let parent = path.parent().unwrap();
69                if Some(parent) != current_dir.as_deref() {
70                    println!("cargo:rustc-link-search={}", parent.display());
71                    current_dir = Some(parent.to_path_buf());
72                }
73                libs.push(path.to_path_buf());
74
75                let lib_name = library_name(file_name);
76                println!("cargo:rustc-link-lib=static={}", lib_name);
77            }
78        }
79    }
80    libs
81}
82
83#[cfg(not(target_os = "windows"))]
84fn is_library(name: &&str) -> bool {
85    name.starts_with("lib") && name.ends_with(".a")
86}
87
88#[cfg(not(target_os = "windows"))]
89fn library_name(name: &str) -> &str {
90    &name[3..name.len() - 2]
91}
92
93#[cfg(target_os = "windows")]
94fn is_library(name: &&str) -> bool {
95    name.ends_with(".lib") && !name.starts_with(".")
96}
97
98#[cfg(target_os = "windows")]
99fn library_name(name: &str) -> &str {
100    &name[0..name.len() - 4]
101}
102
103pub fn link_dynamic_libraries<T: AsRef<Path>>(root: T) -> Vec<PathBuf> {
104    let mut current_dir = None;
105    let mut libs = Vec::new();
106
107    for entry in WalkDir::new(root).into_iter().filter_map(Result::ok) {
108        let path = entry.path();
109        if path.is_file() {
110            if let Some(file_name) = path
111                .file_name()
112                .and_then(|name| name.to_str())
113                .filter(|name| is_dynamic_library(name))
114            {
115                let parent = path.parent().unwrap();
116                if Some(parent) != current_dir.as_deref() {
117                    println!("cargo:rustc-link-search={}", parent.display());
118                    current_dir = Some(parent.to_path_buf());
119                }
120                libs.push(path.to_path_buf());
121
122                let lib_name = dynamic_library_name(file_name);
123                println!("cargo:rustc-link-lib=dylib={}", lib_name);
124            }
125        }
126    }
127
128    libs
129}
130
131#[cfg(target_os = "linux")]
132fn is_dynamic_library(name: &str) -> bool {
133    name.starts_with("lib") && name.ends_with(".so")
134}
135
136#[cfg(target_os = "linux")]
137fn dynamic_library_name(name: &str) -> &str {
138    &name[3..name.len() - 3] // remove "lib" prefix and ".so" suffix
139}
140
141#[cfg(target_os = "macos")]
142fn is_dynamic_library(name: &str) -> bool {
143    name.starts_with("lib") && name.ends_with(".dylib")
144}
145
146#[cfg(target_os = "macos")]
147fn dynamic_library_name(name: &str) -> &str {
148    &name[3..name.len() - 6] // remove "lib" prefix and ".dylib" suffix
149}
150
151#[cfg(target_os = "windows")]
152fn is_dynamic_library(name: &str) -> bool {
153    // On Windows, Cargo links to the .lib import library, but you can also detect .dll
154    name.ends_with(".lib") && !name.starts_with(".")
155}
156
157#[cfg(target_os = "windows")]
158fn dynamic_library_name(name: &str) -> &str {
159    &name[0..name.len() - 4] // remove ".lib"
160}