@@ -3,6 +3,65 @@ extern crate cmake;
33use cmake:: Config ;
44use std:: { env, path:: PathBuf , process:: Command } ;
55
6+ /// MLX requires macOS >= 14.0 (Sonoma).
7+ #[ cfg( target_os = "macos" ) ]
8+ const MLX_MIN_MACOS_VERSION : & str = "14.0" ;
9+
10+ /// Query the current macOS version via `sw_vers -productVersion`.
11+ #[ cfg( target_os = "macos" ) ]
12+ fn detect_macos_version ( ) -> Option < String > {
13+ let output = Command :: new ( "sw_vers" )
14+ . args ( [ "-productVersion" ] )
15+ . output ( )
16+ . ok ( ) ?;
17+ if !output. status . success ( ) {
18+ return None ;
19+ }
20+ let version = String :: from_utf8_lossy ( & output. stdout ) . trim ( ) . to_string ( ) ;
21+ if version. is_empty ( ) {
22+ None
23+ } else {
24+ Some ( version)
25+ }
26+ }
27+
28+ /// Resolve the macOS deployment target.
29+ ///
30+ /// Priority:
31+ /// 1. `MACOSX_DEPLOYMENT_TARGET` env var (user override)
32+ /// 2. Current system macOS version (via `sw_vers`)
33+ /// 3. MLX minimum (14.0) as final fallback
34+ ///
35+ /// Ensures the resolved target is at least [`MLX_MIN_MACOS_VERSION`].
36+ #[ cfg( target_os = "macos" ) ]
37+ fn resolve_deployment_target ( ) -> String {
38+ let target = env:: var ( "MACOSX_DEPLOYMENT_TARGET" )
39+ . ok ( )
40+ . or_else ( detect_macos_version)
41+ . unwrap_or_else ( || MLX_MIN_MACOS_VERSION . to_string ( ) ) ;
42+
43+ // Parse major version to enforce minimum
44+ let major: u32 = target
45+ . split ( '.' )
46+ . next ( )
47+ . and_then ( |s| s. parse ( ) . ok ( ) )
48+ . unwrap_or ( 14 ) ;
49+ let min_major: u32 = MLX_MIN_MACOS_VERSION
50+ . split ( '.' )
51+ . next ( )
52+ . and_then ( |s| s. parse ( ) . ok ( ) )
53+ . unwrap_or ( 14 ) ;
54+
55+ if major < min_major {
56+ eprintln ! (
57+ "cargo:warning=MACOSX_DEPLOYMENT_TARGET={target} is below MLX minimum ({MLX_MIN_MACOS_VERSION}), using {MLX_MIN_MACOS_VERSION}"
58+ ) ;
59+ MLX_MIN_MACOS_VERSION . to_string ( )
60+ } else {
61+ target
62+ }
63+ }
64+
665/// Find the clang runtime library path dynamically using xcrun
766fn find_clang_rt_path ( ) -> Option < String > {
867 // Use xcrun to find the active toolchain path
@@ -53,6 +112,18 @@ fn build_and_link_mlx_c() {
53112 config. define ( "CMAKE_C_COMPILER" , "/usr/bin/cc" ) ;
54113 config. define ( "CMAKE_CXX_COMPILER" , "/usr/bin/c++" ) ;
55114
115+ // Ensure consistent macOS deployment target across cmake, C++ compiler, and Rust linker.
116+ // Mismatch causes ___isPlatformVersionAtLeast linking errors.
117+ // See: https://github.com/ml-explore/mlx/issues/1602
118+ #[ cfg( target_os = "macos" ) ]
119+ let deployment_target = {
120+ let target = resolve_deployment_target ( ) ;
121+ config. define ( "CMAKE_OSX_DEPLOYMENT_TARGET" , & target) ;
122+ env:: set_var ( "MACOSX_DEPLOYMENT_TARGET" , & target) ;
123+ println ! ( "cargo:rerun-if-env-changed=MACOSX_DEPLOYMENT_TARGET" ) ;
124+ target
125+ } ;
126+
56127 #[ cfg( debug_assertions) ]
57128 {
58129 config. define ( "CMAKE_BUILD_TYPE" , "Debug" ) ;
@@ -97,6 +168,13 @@ fn build_and_link_mlx_c() {
97168 println ! ( "cargo:rustc-link-lib=framework=Accelerate" ) ;
98169 }
99170
171+ // Pass matching deployment target to the Rust linker so all components agree.
172+ #[ cfg( target_os = "macos" ) ]
173+ println ! (
174+ "cargo:rustc-link-arg=-mmacosx-version-min={}" ,
175+ deployment_target
176+ ) ;
177+
100178 // Link against Xcode's clang runtime for ___isPlatformVersionAtLeast symbol
101179 // This is needed on macOS 26+ where the bundled LLVM runtime may be outdated
102180 // See: https://github.com/conda-forge/llvmdev-feedstock/issues/244
0 commit comments