Skip to content

Commit 68eacae

Browse files
committed
ggml: Load ggml library from detected paths
- Auto-select lib/ or bin/ directories - Add backend loading functions
1 parent 0a9b2e4 commit 68eacae

3 files changed

Lines changed: 84 additions & 43 deletions

File tree

CMakeLists.txt

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,31 @@ function(llama_cpp_python_install_target target)
1717
return()
1818
endif()
1919

20-
install(
21-
TARGETS ${target}
22-
LIBRARY DESTINATION llama_cpp/lib
23-
RUNTIME DESTINATION llama_cpp/lib
24-
ARCHIVE DESTINATION llama_cpp/lib
25-
FRAMEWORK DESTINATION llama_cpp/lib
26-
RESOURCE DESTINATION llama_cpp/lib
20+
# Define install destinations to avoid code duplication
21+
set(INSTALL_DIRS
22+
"${CMAKE_CURRENT_SOURCE_DIR}/llama_cpp/lib"
23+
"${SKBUILD_PLATLIB_DIR}/llama_cpp/lib"
2724
)
2825

29-
# Windows DLL dependencies
30-
if (WIN32)
26+
foreach(DIR ${INSTALL_DIRS})
3127
install(
32-
FILES $<TARGET_RUNTIME_DLLS:${target}>
33-
DESTINATION llama_cpp/lib
34-
OPTIONAL
28+
TARGETS ${target}
29+
LIBRARY DESTINATION ${DIR}
30+
RUNTIME DESTINATION ${DIR}
31+
ARCHIVE DESTINATION ${DIR}
32+
FRAMEWORK DESTINATION ${DIR}
33+
RESOURCE DESTINATION ${DIR}
3534
)
36-
endif()
35+
36+
# Automatically handle Windows DLL installation for each target
37+
if (WIN32)
38+
install(
39+
FILES $<TARGET_RUNTIME_DLLS:${target}>
40+
DESTINATION ${DIR}
41+
OPTIONAL # Prevent errors if the target has no DLLs
42+
)
43+
endif()
44+
endforeach()
3745

3846
# Proper RPATH handling
3947
if(UNIX)

llama_cpp/_ctypes_extensions.py

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,23 @@
2020

2121

2222
# Load the library
23-
def load_shared_library(lib_base_name: str, base_path: pathlib.Path):
24-
"""Platform independent shared library loader"""
25-
# Searching for the library in the current directory under the name "libllama" (default name
26-
# for llamacpp) and "llama" (default name for this repo)
27-
lib_paths: List[pathlib.Path] = []
28-
# Determine the file extension based on the platform
23+
def load_shared_library(lib_base_name: str, base_paths: Union[pathlib.Path, list[pathlib.Path]]):
24+
if isinstance(base_paths, pathlib.Path):
25+
base_paths = [base_paths]
26+
27+
lib_names = []
28+
2929
if sys.platform.startswith("linux") or sys.platform.startswith("freebsd"):
30-
lib_paths += [
31-
base_path / f"lib{lib_base_name}.so",
32-
]
30+
lib_names = [f"lib{lib_base_name}.so"]
3331
elif sys.platform == "darwin":
34-
lib_paths += [
35-
base_path / f"lib{lib_base_name}.so",
36-
base_path / f"lib{lib_base_name}.dylib",
32+
lib_names = [
33+
f"lib{lib_base_name}.dylib",
34+
f"lib{lib_base_name}.so",
3735
]
3836
elif sys.platform == "win32":
39-
lib_paths += [
40-
base_path / f"{lib_base_name}.dll",
41-
base_path / f"lib{lib_base_name}.dll",
37+
lib_names = [
38+
f"{lib_base_name}.dll",
39+
f"lib{lib_base_name}.dll",
4240
]
4341
else:
4442
raise RuntimeError("Unsupported platform")
@@ -47,11 +45,13 @@ def load_shared_library(lib_base_name: str, base_path: pathlib.Path):
4745

4846
# Add the library directory to the DLL search path on Windows (if needed)
4947
if sys.platform == "win32":
50-
os.add_dll_directory(str(base_path))
48+
for base_path in base_paths:
49+
os.add_dll_directory(str(base_path))
5150
os.environ["PATH"] = str(base_path) + os.pathsep + os.environ["PATH"]
5251

53-
if sys.platform == "win32" and sys.version_info >= (3, 8):
54-
os.add_dll_directory(str(base_path))
52+
if sys.platform == "win32" and sys.version_info >= (3, 9):
53+
for base_path in base_paths:
54+
os.add_dll_directory(str(base_path))
5555
if "CUDA_PATH" in os.environ:
5656
cuda_path = os.environ["CUDA_PATH"]
5757
sub_dirs_to_add = [
@@ -75,16 +75,21 @@ def load_shared_library(lib_base_name: str, base_path: pathlib.Path):
7575

7676
cdll_args["winmode"] = ctypes.RTLD_GLOBAL
7777

78+
errors = []
79+
7880
# Try to load the shared library, handling potential errors
79-
for lib_path in lib_paths:
80-
if lib_path.exists():
81-
try:
82-
return ctypes.CDLL(str(lib_path), **cdll_args) # type: ignore
83-
except Exception as e:
84-
raise RuntimeError(f"Failed to load shared library '{lib_path}': {e}")
85-
86-
raise FileNotFoundError(
87-
f"Shared library with base name '{lib_base_name}' not found"
81+
for base_path in base_paths:
82+
for lib_name in lib_names:
83+
lib_path = base_path / lib_name
84+
if lib_path.exists():
85+
try:
86+
return ctypes.CDLL(str(lib_path), **cdll_args)
87+
except Exception as e:
88+
errors.append(f"{lib_path}: {e}")
89+
90+
raise RuntimeError(
91+
f"Failed to load '{lib_base_name}' from {base_paths}\n"
92+
+ "\n".join(errors)
8893
)
8994

9095

llama_cpp/_ggml.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
import pathlib
88
import ctypes
99

10-
import llama_cpp._ctypes_extensions as ctypes_ext
10+
from llama_cpp._ctypes_extensions import (
11+
load_shared_library,
12+
byref,
13+
ctypes_function_for_shared_library,
14+
)
1115

1216
from typing import (
1317
Callable,
@@ -17,8 +21,15 @@
1721
TYPE_CHECKING,
1822
)
1923

20-
libggml_base_path = pathlib.Path(os.path.abspath(os.path.dirname(__file__))) / "lib"
21-
libggml = ctypes_ext.load_shared_library("ggml", libggml_base_path)
24+
libggml_base_path = pathlib.Path(os.path.abspath(os.path.dirname(__file__)))
25+
libggml_base_paths = [
26+
libggml_base_path / "lib",
27+
libggml_base_path / "bin",
28+
]
29+
30+
libggml = load_shared_library("ggml", libggml_base_paths)
31+
32+
ggml_function = ctypes_function_for_shared_library(libggml)
2233

2334
# // ====== ggml.h ======
2435

@@ -361,3 +372,20 @@ class ggml_opt_optimizer_params(ctypes.Structure):
361372
ggml_backend_sched_eval_callback = ctypes.CFUNCTYPE(
362373
ctypes.c_bool, ctypes.c_void_p, ctypes.c_bool, ctypes.c_void_p
363374
)
375+
376+
# //
377+
# // Backend registry
378+
# //
379+
380+
# // Load all known backends from dynamic libraries
381+
# GGML_API void ggml_backend_load_all(void);
382+
@ggml_function("ggml_backend_load_all", [], None)
383+
def ggml_backend_load_all():
384+
"""Load all known backends from dynamic libraries"""
385+
...
386+
387+
# GGML_API void ggml_backend_load_all_from_path(const char * dir_path);
388+
@ggml_function("ggml_backend_load_all_from_path", [ctypes.c_char_p], None)
389+
def ggml_backend_load_all_from_path(dir_path: ctypes.c_char_p):
390+
"""Load all known backends from path"""
391+
...

0 commit comments

Comments
 (0)