@@ -5,6 +5,23 @@ use std::path::{Path, PathBuf};
55use std:: process:: Command ;
66use walkdir:: DirEntry ;
77
8+ enum WindowsVariant {
9+ Msvc ,
10+ Other ,
11+ }
12+
13+ enum AppleVariant {
14+ MacOS ,
15+ Other ,
16+ }
17+
18+ enum TargetOs {
19+ Windows ( WindowsVariant ) ,
20+ Apple ( AppleVariant ) ,
21+ Linux ,
22+ Android ,
23+ }
24+
825macro_rules! debug_log {
926 ( $( $arg: tt) * ) => {
1027 if std:: env:: var( "BUILD_DEBUG" ) . is_ok( ) {
@@ -13,6 +30,30 @@ macro_rules! debug_log {
1330 } ;
1431}
1532
33+ fn parse_target_os ( ) -> Result < ( TargetOs , String ) , String > {
34+ let target = env:: var ( "TARGET" ) . unwrap ( ) ;
35+
36+ if target. contains ( "windows" ) {
37+ if target. ends_with ( "-windows-msvc" ) {
38+ Ok ( ( TargetOs :: Windows ( WindowsVariant :: Msvc ) , target) )
39+ } else {
40+ Ok ( ( TargetOs :: Windows ( WindowsVariant :: Other ) , target) )
41+ }
42+ } else if target. contains ( "linux" ) {
43+ Ok ( ( TargetOs :: Linux , target) )
44+ } else if target. contains ( "apple" ) {
45+ if target. ends_with ( "-apple-darwin" ) {
46+ Ok ( ( TargetOs :: Apple ( AppleVariant :: MacOS ) , target) )
47+ } else {
48+ Ok ( ( TargetOs :: Apple ( AppleVariant :: Other ) , target) )
49+ }
50+ } else if target. contains ( "android" ) {
51+ Ok ( ( TargetOs :: Android , target) )
52+ } else {
53+ Err ( target)
54+ }
55+ }
56+
1657fn get_cargo_target_dir ( ) -> Result < PathBuf , Box < dyn std:: error:: Error > > {
1758 let out_dir = env:: var ( "OUT_DIR" ) ?;
1859 let path = PathBuf :: from ( out_dir) ;
@@ -132,7 +173,8 @@ fn is_hidden(e: &DirEntry) -> bool {
132173fn main ( ) {
133174 println ! ( "cargo:rerun-if-changed=build.rs" ) ;
134175
135- let target = env:: var ( "TARGET" ) . unwrap ( ) ;
176+ let ( target_os, target_triple) =
177+ parse_target_os ( ) . unwrap_or_else ( |t| panic ! ( "Failed to parse target os {t}" ) ) ;
136178 let out_dir = PathBuf :: from ( env:: var ( "OUT_DIR" ) . unwrap ( ) ) ;
137179
138180 let target_dir = get_cargo_target_dir ( ) . unwrap ( ) ;
@@ -152,7 +194,7 @@ fn main() {
152194 println ! ( "cargo:rerun-if-env-changed=LLAMA_BUILD_SHARED_LIBS" ) ;
153195 println ! ( "cargo:rerun-if-env-changed=LLAMA_STATIC_CRT" ) ;
154196
155- debug_log ! ( "TARGET: {}" , target ) ;
197+ debug_log ! ( "TARGET: {}" , target_triple ) ;
156198 debug_log ! ( "CARGO_MANIFEST_DIR: {}" , manifest_dir) ;
157199 debug_log ! ( "TARGET_DIR: {}" , target_dir. display( ) ) ;
158200 debug_log ! ( "OUT_DIR: {}" , out_dir. display( ) ) ;
@@ -232,15 +274,29 @@ fn main() {
232274 if build_shared_libs { "ON" } else { "OFF" } ,
233275 ) ;
234276
235- if cfg ! ( target_os = "macos" ) {
277+ if matches ! ( target_os, TargetOs :: Apple ( _ ) ) {
236278 config. define ( "GGML_BLAS" , "OFF" ) ;
237279 }
238280
239- if cfg ! ( windows) {
240- config. static_crt ( static_crt) ;
281+ if ( cfg ! ( debug_assertions)
282+ || std:: env:: var ( "PROFILE" ) . as_ref ( ) . map ( String :: as_str) == Ok ( "debug" ) )
283+ && matches ! ( target_os, TargetOs :: Windows ( WindowsVariant :: Msvc ) )
284+ && profile == "Release"
285+ {
286+ // Debug Rust builds under MSVC turn off optimization even though we're ideally building the release profile of llama.cpp.
287+ // Looks like an upstream bug:
288+ // https://github.com/rust-lang/cmake-rs/issues/240
289+ // For now explicitly reinject the optimization flags that a CMake Release build is expected to have on in this scenario.
290+ // This fixes CPU inference performance when part of a Rust debug build.
291+ for flag in & [ "/O2" , "/DNDEBUG" , "/Ob2" ] {
292+ config. cflag ( flag) ;
293+ config. cxxflag ( flag) ;
294+ }
241295 }
242296
243- if target. contains ( "android" ) {
297+ config. static_crt ( static_crt) ;
298+
299+ if matches ! ( target_os, TargetOs :: Android ) {
244300 // build flags for android taken from this doc
245301 // https://github.com/ggerganov/llama.cpp/blob/master/docs/android.md
246302 let android_ndk = env:: var ( "ANDROID_NDK" )
@@ -257,21 +313,21 @@ fn main() {
257313 } else {
258314 config. define ( "ANDROID_PLATFORM" , "android-28" ) ;
259315 }
260- if target . contains ( "aarch64" ) {
316+ if target_triple . contains ( "aarch64" ) {
261317 config. cflag ( "-march=armv8.7a" ) ;
262318 config. cxxflag ( "-march=armv8.7a" ) ;
263- } else if target . contains ( "armv7" ) {
319+ } else if target_triple . contains ( "armv7" ) {
264320 config. cflag ( "-march=armv8.7a" ) ;
265321 config. cxxflag ( "-march=armv8.7a" ) ;
266- } else if target . contains ( "x86_64" ) {
322+ } else if target_triple . contains ( "x86_64" ) {
267323 config. cflag ( "-march=x86-64" ) ;
268324 config. cxxflag ( "-march=x86-64" ) ;
269- } else if target . contains ( "i686" ) {
325+ } else if target_triple . contains ( "i686" ) {
270326 config. cflag ( "-march=i686" ) ;
271327 config. cxxflag ( "-march=i686" ) ;
272328 } else {
273329 // Rather than guessing just fail.
274- panic ! ( "Unsupported Android target {target }" ) ;
330+ panic ! ( "Unsupported Android target {target_triple }" ) ;
275331 }
276332 config. define ( "GGML_LLAMAFILE" , "OFF" ) ;
277333 if cfg ! ( feature = "shared-stdcxx" ) {
@@ -282,16 +338,19 @@ fn main() {
282338
283339 if cfg ! ( feature = "vulkan" ) {
284340 config. define ( "GGML_VULKAN" , "ON" ) ;
285- if cfg ! ( windows) {
286- let vulkan_path = env:: var ( "VULKAN_SDK" )
287- . expect ( "Please install Vulkan SDK and ensure that VULKAN_SDK env variable is set" ) ;
288- let vulkan_lib_path = Path :: new ( & vulkan_path) . join ( "Lib" ) ;
289- println ! ( "cargo:rustc-link-search={}" , vulkan_lib_path. display( ) ) ;
290- println ! ( "cargo:rustc-link-lib=vulkan-1" ) ;
291- }
292-
293- if cfg ! ( target_os = "linux" ) {
294- println ! ( "cargo:rustc-link-lib=vulkan" ) ;
341+ match target_os {
342+ TargetOs :: Windows ( _) => {
343+ let vulkan_path = env:: var ( "VULKAN_SDK" ) . expect (
344+ "Please install Vulkan SDK and ensure that VULKAN_SDK env variable is set" ,
345+ ) ;
346+ let vulkan_lib_path = Path :: new ( & vulkan_path) . join ( "Lib" ) ;
347+ println ! ( "cargo:rustc-link-search={}" , vulkan_lib_path. display( ) ) ;
348+ println ! ( "cargo:rustc-link-lib=vulkan-1" ) ;
349+ }
350+ TargetOs :: Linux => {
351+ println ! ( "cargo:rustc-link-lib=vulkan" ) ;
352+ }
353+ _ => ( ) ,
295354 }
296355 }
297356
@@ -302,7 +361,7 @@ fn main() {
302361 // Android doesn't have OpenMP support AFAICT and openmp is a default feature. Do this here
303362 // rather than modifying the defaults in Cargo.toml just in case someone enables the OpenMP feature
304363 // and tries to build for Android anyway.
305- if cfg ! ( feature = "openmp" ) && !target . contains ( "android" ) {
364+ if cfg ! ( feature = "openmp" ) && !matches ! ( target_os , TargetOs :: Android ) {
306365 config. define ( "GGML_OPENMP" , "ON" ) ;
307366 } else {
308367 config. define ( "GGML_OPENMP" , "OFF" ) ;
@@ -341,38 +400,41 @@ fn main() {
341400 }
342401
343402 // OpenMP
344- if cfg ! ( feature = "openmp" ) && target . contains ( "gnu" ) {
403+ if cfg ! ( feature = "openmp" ) && target_triple . contains ( "gnu" ) {
345404 println ! ( "cargo:rustc-link-lib=gomp" ) ;
346405 }
347406
348- // Windows debug
349- if cfg ! ( all( debug_assertions, windows) ) {
350- println ! ( "cargo:rustc-link-lib=dylib=msvcrtd" ) ;
351- }
352-
353- // // macOS
354- if cfg ! ( target_os = "macos" ) {
355- println ! ( "cargo:rustc-link-lib=framework=Foundation" ) ;
356- println ! ( "cargo:rustc-link-lib=framework=Metal" ) ;
357- println ! ( "cargo:rustc-link-lib=framework=MetalKit" ) ;
358- println ! ( "cargo:rustc-link-lib=framework=Accelerate" ) ;
359- println ! ( "cargo:rustc-link-lib=c++" ) ;
360- }
361-
362- // Linux
363- if cfg ! ( target_os = "linux" ) {
364- println ! ( "cargo:rustc-link-lib=dylib=stdc++" ) ;
365- }
366-
367- if target. contains ( "apple" ) {
368- // On (older) OSX we need to link against the clang runtime,
369- // which is hidden in some non-default path.
370- //
371- // More details at https://github.com/alexcrichton/curl-rust/issues/279.
372- if let Some ( path) = macos_link_search_path ( ) {
373- println ! ( "cargo:rustc-link-lib=clang_rt.osx" ) ;
374- println ! ( "cargo:rustc-link-search={}" , path) ;
407+ match target_os {
408+ TargetOs :: Windows ( WindowsVariant :: Msvc ) => {
409+ if cfg ! ( debug_assertions) {
410+ println ! ( "cargo:rustc-link-lib=dylib=msvcrtd" ) ;
411+ }
412+ }
413+ TargetOs :: Linux => {
414+ println ! ( "cargo:rustc-link-lib=dylib=stdc++" ) ;
415+ }
416+ TargetOs :: Apple ( variant) => {
417+ println ! ( "cargo:rustc-link-lib=framework=Foundation" ) ;
418+ println ! ( "cargo:rustc-link-lib=framework=Metal" ) ;
419+ println ! ( "cargo:rustc-link-lib=framework=MetalKit" ) ;
420+ println ! ( "cargo:rustc-link-lib=framework=Accelerate" ) ;
421+ println ! ( "cargo:rustc-link-lib=c++" ) ;
422+
423+ match variant {
424+ AppleVariant :: MacOS => {
425+ // On (older) OSX we need to link against the clang runtime,
426+ // which is hidden in some non-default path.
427+ //
428+ // More details at https://github.com/alexcrichton/curl-rust/issues/279.
429+ if let Some ( path) = macos_link_search_path ( ) {
430+ println ! ( "cargo:rustc-link-lib=clang_rt.osx" ) ;
431+ println ! ( "cargo:rustc-link-search={}" , path) ;
432+ }
433+ }
434+ AppleVariant :: Other => ( ) ,
435+ }
375436 }
437+ _ => ( ) ,
376438 }
377439
378440 // copy DLLs to target
0 commit comments