Skip to content

Commit 3eee3d6

Browse files
ctothclaude
andcommitted
Add ARM64/Apple Silicon architecture support
Enhance library loading to properly detect and support ARM64 architecture (M1+ Macs, ARM servers, modern Raspberry Pi). Replace platform.architecture() with platform.machine() for accurate architecture detection. Changes: - Add arm64_path parameter to load_library() and find_library_path() - Implement architecture-specific path mapping (x86, x86_64, arm64) - Add smart fallback: arm64_path defaults to x64_path for universal binaries - Add debug logging for architecture detection and path selection - Update README with ARM64 examples and architecture support documentation - Maintain full backward compatibility with existing code Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f1c93c6 commit 3eee3d6

2 files changed

Lines changed: 44 additions & 13 deletions

File tree

README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ my_lib = load_library("mylibrary")
2323
# Load a library with custom paths for 32-bit and 64-bit versions
2424
my_lib = load_library("mylibrary", x86_path="./lib/x86", x64_path="./lib/x64")
2525

26+
# Load a library with ARM64 support (M1+ Macs, ARM servers)
27+
my_lib = load_library("mylibrary", x64_path="./lib/x64", arm64_path="./lib/arm64")
28+
2629
# Call a function from the library
2730
result = my_lib.some_function(arg1, arg2)
2831
```
@@ -43,8 +46,8 @@ speech = load_com("SAPI.SpVoice", "SpeechLib.SpVoice")
4346

4447
### libloader
4548

46-
* `load_library(library, x86_path=".", x64_path=".", *args, **kwargs)`: Load a library with the given name.
47-
* `find_library_path(libname, x86_path=".", x64_path=".")`: Finds the path of the given library.
49+
* `load_library(library, x86_path=".", x64_path=".", arm64_path=None, *args, **kwargs)`: Load a library with the given name. If `arm64_path` is not specified on ARM64 systems, falls back to `x64_path`.
50+
* `find_library_path(libname, x86_path=".", x64_path=".", arm64_path=None)`: Finds the path of the given library.
4851
* `get_functype()`: Returns the ctypes functype for the current platform.
4952
* `get_library_extension()`: Get the extension of the library for your current platform.
5053
* `_do_load(file, *args, **kwargs)`: Attempts to actually load the library. Used internally by load_library.
@@ -62,6 +65,16 @@ Libloader automatically handles the differences between platforms:
6265
* macOS: `.dylib` files
6366
* Linux: `.so` files
6467

68+
### Architecture Support
69+
70+
Libloader automatically detects the system architecture and loads the appropriate library:
71+
72+
* **x86** (32-bit): Uses `x86_path`
73+
* **x86_64/AMD64** (64-bit Intel/AMD): Uses `x64_path`
74+
* **ARM64/aarch64** (M1+ Macs, ARM servers): Uses `arm64_path` if specified, otherwise falls back to `x64_path`
75+
76+
This means existing code works on Apple Silicon Macs without modification, while allowing you to provide architecture-specific binaries when needed.
77+
6578
## License
6679

6780
See the LICENSE file for details.

libloader/libloader.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,16 @@ class LibraryLoadError(OSError):
3434
pass
3535

3636

37-
def load_library(library, x86_path=".", x64_path=".", *args, **kwargs):
37+
def load_library(library, x86_path=".", x64_path=".", arm64_path=None, *args, **kwargs):
3838
logger.debug("Attempting to load library: %s", library)
39-
logger.debug("x86_path: %s, x64_path: %s", os.path.abspath(x86_path), os.path.abspath(x64_path))
40-
41-
lib = find_library_path(library, x86_path=x86_path, x64_path=x64_path)
39+
logger.debug(
40+
"x86_path: %s, x64_path: %s, arm64_path: %s",
41+
os.path.abspath(x86_path),
42+
os.path.abspath(x64_path),
43+
os.path.abspath(arm64_path) if arm64_path is not None else None,
44+
)
45+
46+
lib = find_library_path(library, x86_path=x86_path, x64_path=x64_path, arm64_path=arm64_path)
4247
logger.debug("Resolved library path: %s", lib)
4348

4449
loaded = _do_load(lib, *args, **kwargs)
@@ -61,21 +66,34 @@ def _do_load(file, *args, **kwargs):
6166
return None
6267

6368

64-
def find_library_path(libname, x86_path=".", x64_path="."):
69+
def find_library_path(libname, x86_path=".", x64_path=".", arm64_path=None):
6570
system = platform.system()
6671
prefix = TYPES[system]["prefix"]
6772
libname_with_prefix = f"{prefix}{libname}"
6873
logger.debug("Library name with prefix: %s", libname_with_prefix)
6974

70-
arch = platform.architecture()[0]
71-
logger.debug("Detected architecture: %s", arch)
72-
73-
if arch == "64bit":
75+
# Get actual machine architecture
76+
machine = platform.machine().lower()
77+
logger.debug("Detected machine architecture: %s", machine)
78+
79+
# Map machine architecture to the appropriate path
80+
if machine in ("arm64", "aarch64"):
81+
# ARM64 architecture (M1+ Macs, ARM servers, modern Raspberry Pi)
82+
if arm64_path is None:
83+
logger.debug("ARM64 detected, arm64_path not specified, falling back to x64_path")
84+
path = os.path.join(x64_path, libname_with_prefix)
85+
logger.debug("Using fallback x64 path: %s", x64_path)
86+
else:
87+
path = os.path.join(arm64_path, libname_with_prefix)
88+
logger.debug("Using ARM64 path: %s", arm64_path)
89+
elif machine in ("x86_64", "amd64", "x64"):
90+
# 64-bit x86 architecture (Intel/AMD)
7491
path = os.path.join(x64_path, libname_with_prefix)
75-
logger.debug("Using 64-bit path: %s", x64_path)
92+
logger.debug("Using x86_64 path: %s", x64_path)
7693
else:
94+
# 32-bit x86 or other architectures
7795
path = os.path.join(x86_path, libname_with_prefix)
78-
logger.debug("Using 32-bit path: %s", x86_path)
96+
logger.debug("Using x86 path for architecture '%s': %s", machine, x86_path)
7997

8098
ext = get_library_extension()
8199
logger.debug("Using library extension: %s", ext)

0 commit comments

Comments
 (0)