Files
ollama-ollama/x/imagegen/mlx/mlx_dynamic.c
Daniel Hiltgen 10e51c5177 MLX: add header vendoring and remove go build tag (#14642)
* prefer rocm v6 on windows

Avoid building with v7 - more changes are needed

* MLX: add header vendoring and remove go build tag

This switches to using a vendoring approach for the mlx-c headers so that Go
can build without requiring a cmake first.  This enables building the new MLX
based code by default.  Every time cmake runs, the headers are refreshed, so we
can easily keep them in sync when we bump mlx versions.  Basic Windows
and Linux support are verified.

* ci: harden for flaky choco repo servers

CI sometimes fails due to choco not actually installing cache.  Since it just speeds up the build, we can proceed without.

* review comments
2026-03-09 17:24:45 -07:00

94 lines
2.6 KiB
C

// mlx_dynamic.c - Dynamic loading wrapper for MLX-C library
// This file provides runtime dynamic loading of libmlxc instead of link-time binding
#include "mlx_dynamic.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
typedef HMODULE lib_handle_t;
static char win_error_buffer[256] = {0};
static const char* get_win_error(void) {
DWORD err = GetLastError();
snprintf(win_error_buffer, sizeof(win_error_buffer), "error code %lu", err);
return win_error_buffer;
}
#define LIB_ERROR() get_win_error()
#else
#include <dlfcn.h>
typedef void* lib_handle_t;
#define LIB_ERROR() dlerror()
#endif
static lib_handle_t mlx_handle = NULL;
static int mlx_initialized = 0;
static char mlx_error_buffer[512] = {0};
#ifdef _WIN32
// Windows: Load library from a path with dependency resolution.
// Temporarily adds the library's directory to the DLL search path
// so that dependencies (like mlx.dll) in the same directory are found.
static int try_load_win(const char* path) {
if (!path) return 0;
// Extract directory and add to DLL search path for dependency resolution
char dir_path[MAX_PATH];
strncpy(dir_path, path, MAX_PATH - 1);
dir_path[MAX_PATH - 1] = '\0';
char* last_slash = strrchr(dir_path, '\\');
if (!last_slash) last_slash = strrchr(dir_path, '/');
if (last_slash) {
*last_slash = '\0';
SetDllDirectoryA(dir_path);
}
mlx_handle = LoadLibraryA(path);
SetDllDirectoryA(NULL);
return mlx_handle != NULL;
}
#endif
// Try to load library from a specific path
static int try_load_lib(const char* path) {
if (!path) return 0;
#ifdef _WIN32
return try_load_win(path);
#else
mlx_handle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
return mlx_handle != NULL;
#endif
}
// Initialize the MLX dynamic library from a specific path.
// Returns 0 on success, -1 on failure.
int mlx_dynamic_init_path(const char* path) {
if (mlx_initialized) {
return 0;
}
if (try_load_lib(path)) {
mlx_initialized = 1;
snprintf(mlx_error_buffer, sizeof(mlx_error_buffer),
"MLX: Successfully loaded %s", path ? path : "library");
return 0;
}
const char* err = LIB_ERROR();
snprintf(mlx_error_buffer, sizeof(mlx_error_buffer),
"MLX: Failed to load %s: %s", path ? path : "(null)", err ? err : "unknown error");
return -1;
}
// Get the last error message
const char* mlx_dynamic_error(void) {
return mlx_error_buffer;
}
// Get the library handle (for use by generated wrappers)
void* mlx_get_handle(void) {
return mlx_handle;
}