Skip to main content

llama_cpp_bindings_build/
lib.rs

1mod android_ndk;
2mod bindgen_config;
3mod cmake_config;
4mod cpp_wrapper;
5mod cpp_wrapper_mtmd;
6mod glob_paths;
7mod library_asset_extraction;
8mod library_linking;
9mod library_name_extraction;
10mod rebuild_tracking;
11mod shared_libs;
12mod stable_cmake_build_dir;
13mod target_os;
14
15use std::env;
16use std::path::{Path, PathBuf};
17
18use android_ndk::AndroidNdk;
19use stable_cmake_build_dir::stable_cmake_build_dir;
20use target_os::TargetOs;
21
22#[macro_export]
23macro_rules! debug_log {
24    ($($arg:tt)*) => {
25        if std::env::var("BUILD_DEBUG").is_ok() {
26            println!("cargo:warning=[DEBUG] {}", format!($($arg)*));
27        }
28    };
29}
30
31#[derive(Debug)]
32pub struct BuildContext {
33    pub out_dir: PathBuf,
34    pub target_dir: PathBuf,
35    pub cmake_dir: PathBuf,
36    pub llama_src: PathBuf,
37    pub target_os: TargetOs,
38    pub target_triple: String,
39    pub build_shared_libs: bool,
40    pub profile: String,
41    pub static_crt: bool,
42    pub android_ndk: Option<AndroidNdk>,
43}
44
45impl BuildContext {
46    fn detect() -> Self {
47        let target_triple =
48            env::var("TARGET").expect("TARGET env var is required in build scripts");
49        let target_os = TargetOs::from_target_triple(&target_triple)
50            .unwrap_or_else(|error| panic!("Failed to parse target OS: {error}"));
51        let out_dir = PathBuf::from(
52            env::var("OUT_DIR").expect("OUT_DIR env var is required in build scripts"),
53        );
54        let target_dir = cargo_target_dir(&out_dir);
55        let manifest_dir = env::var("CARGO_MANIFEST_DIR")
56            .expect("CARGO_MANIFEST_DIR env var is required in build scripts");
57        let llama_src = Path::new(&manifest_dir).join("llama.cpp");
58
59        let build_shared_libs = env::var("LLAMA_BUILD_SHARED_LIBS")
60            .map_or_else(|_| cfg!(feature = "dynamic-link"), |value| value == "1");
61
62        let profile = env::var("LLAMA_LIB_PROFILE").unwrap_or_else(|_| "Release".to_string());
63
64        let static_crt = env::var("LLAMA_STATIC_CRT")
65            .map(|value| value == "1")
66            .unwrap_or(false);
67
68        let android_ndk = if target_os.is_android() {
69            Some(
70                AndroidNdk::detect(&target_triple)
71                    .unwrap_or_else(|error| panic!("Android NDK detection failed: {error}")),
72            )
73        } else {
74            None
75        };
76
77        let cmake_dir = stable_cmake_build_dir(
78            &target_dir,
79            &target_triple,
80            &profile,
81            static_crt,
82            build_shared_libs,
83        );
84
85        debug_log!("TARGET: {}", target_triple);
86        debug_log!("CARGO_MANIFEST_DIR: {}", manifest_dir);
87        debug_log!("TARGET_DIR: {}", target_dir.display());
88        debug_log!("OUT_DIR: {}", out_dir.display());
89        debug_log!("CMAKE_DIR: {}", cmake_dir.display());
90        debug_log!("BUILD_SHARED: {}", build_shared_libs);
91
92        Self {
93            out_dir,
94            target_dir,
95            cmake_dir,
96            llama_src,
97            target_os,
98            target_triple,
99            build_shared_libs,
100            profile,
101            static_crt,
102            android_ndk,
103        }
104    }
105}
106
107fn cargo_target_dir(out_dir: &Path) -> PathBuf {
108    out_dir
109        .ancestors()
110        .nth(3)
111        .expect("OUT_DIR is not deep enough to determine target directory")
112        .to_path_buf()
113}
114
115fn set_cmake_parallelism() {
116    if let Ok(parallelism) = std::thread::available_parallelism() {
117        // SAFETY: build scripts are single-threaded, so modifying env is safe.
118        unsafe {
119            env::set_var("CMAKE_BUILD_PARALLEL_LEVEL", parallelism.get().to_string());
120        }
121    }
122}
123
124pub fn build() {
125    let context = BuildContext::detect();
126
127    rebuild_tracking::register_rebuild_triggers(&context.llama_src);
128
129    set_cmake_parallelism();
130
131    bindgen_config::generate_bindings(
132        &context.llama_src,
133        &context.out_dir,
134        &context.target_os,
135        &context.target_triple,
136        context.android_ndk.as_ref(),
137    );
138
139    cpp_wrapper::compile_cpp_wrappers(&context.llama_src, &context.target_os);
140
141    let build_dir = cmake_config::configure_and_build(&context);
142
143    cpp_wrapper_mtmd::compile_mtmd(&context.llama_src, &context.target_os);
144
145    library_linking::link_libraries(
146        &context.cmake_dir,
147        &build_dir,
148        &context.target_os,
149        &context.target_triple,
150        context.build_shared_libs,
151        &context.profile,
152    );
153
154    if context.build_shared_libs {
155        shared_libs::copy_shared_libraries(&context.cmake_dir, &context.target_dir);
156    }
157}