Skip to content

Commit 2084d29

Browse files
committed
Added support for ARM-based Apple Silicon architecture on macOS.
This required a bit of refactoring in the discovery code. We now detect the platform architecture right upfront and use it to inform our expectation of the name of Comsol's binary folder, such as `win64` or `macarm64`. We don't expect it to behave any different on platforms that were previously already supported.
1 parent 918fd0a commit 2084d29

File tree

1 file changed

+59
-51
lines changed

1 file changed

+59
-51
lines changed

mph/discovery.py

Lines changed: 59 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,35 @@ class Backend(TypedDict):
4747
system = platform.system()
4848
log = getLogger(__package__)
4949

50-
architectures = {
51-
'Windows': ['win64'],
52-
'Linux': ['glnxa64'],
53-
'Darwin': ['maci64','macarm64'],
54-
}
5550

51+
@lru_cache(maxsize=1)
52+
def detect_architecture() -> str:
53+
"""
54+
Detects platform architecture to determine name of platform folder.
55+
56+
Comsol binaries (executables and native libraries) are found in sub-folders
57+
with special names specific to the platform architecture, such as `win64`
58+
or `macarm64`. We return the correct folder name based on the detected
59+
platform (OS) and CPU architecture.
60+
"""
61+
system = platform.system()
62+
machine = platform.machine()
63+
bits = platform.architecture()[0]
64+
processor = platform.processor()
65+
if system == 'Windows' and machine == 'AMD64' and bits == '64bit':
66+
log.debug('Platform architecture is 64-bit x86 Windows.')
67+
return 'win64'
68+
if system == 'Linux' and machine == 'x86_64' and bits == '64bit':
69+
log.debug('Platform architecture is 64-bit x86 Linux.')
70+
return 'glnxa64'
71+
if system == 'Darwin' and processor == 'i386' and bits == '64bit':
72+
log.debug('Platform architecture is 64-bit Intel macOS.')
73+
return 'maci64'
74+
if system == 'Darwin' and processor == 'arm':
75+
log.debug('Platform architecture is 64-bit ARM macOS.')
76+
return 'macarm64'
77+
raise OSError('Did not recognize platform architecture.')
5678

57-
#######################
58-
# Version information #
59-
#######################
6079

6180
def parse(version: str) -> tuple[str, int, int, int, int]:
6281
"""
@@ -98,11 +117,7 @@ def parse(version: str) -> tuple[str, int, int, int, int]:
98117
return (name, major, minor, patch, build)
99118

100119

101-
######################
102-
# Back-end discovery #
103-
######################
104-
105-
def search_registry() -> list[Path]:
120+
def search_registry(architecture: str) -> list[Path]:
106121
"""Returns Comsol executables found in the Windows Registry."""
107122

108123
log.debug('Searching Windows Registry for Comsol executables.')
@@ -154,22 +169,21 @@ def search_registry() -> list[Path]:
154169
root = Path(value[0])
155170
log.debug(f'Checking installation folder "{root}".')
156171

157-
# Only add to list if Comsol executable exists in valid sub-folder.
158-
for arch in architectures[system]:
159-
comsol = root/'bin'/arch/'comsol.exe'
160-
if comsol.is_file():
161-
break
162-
else:
163-
log.debug('Did not find Comsol executable.')
172+
# Make sure Comsol executable exists in platform-specific sub-folder.
173+
comsol = root/'bin'/architecture/'comsol.exe'
174+
if not comsol.is_file():
175+
log.debug(f'Did not find Comsol executable in "{comsol.parent}".')
164176
continue
165177
log.debug(f'Found Comsol executable "{comsol}".')
178+
179+
# Accept Comsol installation as one of the backends.
166180
executables.append(comsol)
167181

168182
# Return list with file-system paths of Comsol executables.
169183
return executables
170184

171185

172-
def search_disk() -> list[Path]:
186+
def search_disk(architecture: str) -> list[Path]:
173187
"""Returns Comsol executables found on the file system."""
174188

175189
log.debug('Searching file system for Comsol executables.')
@@ -186,6 +200,8 @@ def search_disk() -> list[Path]:
186200
Path('/Applications'),
187201
Path.home() / 'Applications',
188202
]
203+
else:
204+
raise ValueError('Unexpected value "{system}" for "system".')
189205

190206
# Look for Comsol executables at those locations.
191207
folders = [item for location in locations
@@ -202,15 +218,14 @@ def search_disk() -> list[Path]:
202218
log.debug('No sub-folder named "multiphysics".')
203219
root = folder
204220

205-
# Only add to list if Comsol executable exists in valid sub-folder.
206-
for arch in architectures[system]:
207-
comsol = root/'bin'/arch/'comsol'
208-
if comsol.is_file():
209-
break
210-
else:
211-
log.debug('Did not find Comsol executable.')
221+
# Make sure Comsol executable exists in platform-specific sub-folder.
222+
comsol = root/'bin'/architecture/'comsol.exe'
223+
if not comsol.is_file():
224+
log.debug(f'Did not find Comsol executable in "{comsol.parent}".')
212225
continue
213226
log.debug(f'Found Comsol executable "{comsol}".')
227+
228+
# Accept Comsol installation as one of the backends.
214229
executables.append(comsol)
215230

216231
# Return list with file-system paths of Comsol executables.
@@ -260,11 +275,14 @@ def find_backends() -> list[Backend]:
260275
log.debug('Searching system for available Comsol back-ends.')
261276
backends = []
262277

278+
# Detect platform architecture.
279+
arch = detect_architecture()
280+
263281
# Search system for Comsol executables.
264282
if system == 'Windows':
265-
executables = search_registry()
283+
executables = search_registry(arch)
266284
elif system in ('Linux', 'Darwin'):
267-
executables = search_disk()
285+
executables = search_disk(arch)
268286
else:
269287
error = f'Unsupported operating system "{system}".'
270288
log.error(error)
@@ -286,25 +304,15 @@ def find_backends() -> list[Backend]:
286304
# That file is usually in the same folder as the Comsol executable.
287305
# Though on Linux and macOS, the executable may also be a script
288306
# that sits one folder up (for some reason).
289-
folders = [comsol.parent]
290-
for arch in architectures[system]:
291-
folders.append(comsol.parent/arch)
292-
for folder in folders:
293-
ini = folder/'comsol.ini'
307+
ini_name = 'comsol.ini'
308+
for ini in [comsol.parent/ini_name, comsol.parent/arch/ini_name]:
294309
if ini.is_file():
295310
break
296311
else:
297-
log.debug(f'Did not find Java VM configuration "{ini.name}".')
312+
log.debug(f'Did not find Java VM configuration "{ini_name}".')
298313
continue
299314
log.debug(f'Found Java VM configuration "{ini}".')
300315

301-
# Make sure that parent folder has name of a valid architecture.
302-
arch = ini.parent.name
303-
if arch not in architectures[system]:
304-
log.debug('Its parent folder does not name a valid architecture.')
305-
continue
306-
log.debug(f'System architecture is "{arch}".')
307-
308316
# The actual executable is the one sitting right next to "comsol.ini".
309317
comsol = ini.parent/comsol.name
310318
if not comsol.is_file():
@@ -348,11 +356,15 @@ def find_backends() -> list[Backend]:
348356

349357
# The root folder of the Comsol installation is up two levels.
350358
root = ini.parent.parent.parent
351-
log.debug(f'Root folder is "{root}".')
359+
log.debug(f'Comsol root folder is "{root}".')
352360

353-
# Check that folder with Comsol Java API exists.
354-
api = root/'plugins'
355-
if not api.exists():
361+
# Check that folders with Comsol Java API exists.
362+
plugins = root/'plugins'
363+
if not plugins.exists():
364+
log.debug('Did not find Comsol Java plug-ins in root folder.')
365+
continue
366+
apiplugins = root/'apiplugins'
367+
if not apiplugins.exists():
356368
log.debug('Did not find Comsol Java API plug-ins in root folder.')
357369
continue
358370

@@ -412,10 +424,6 @@ def find_backends() -> list[Backend]:
412424
return backends
413425

414426

415-
######################
416-
# Back-end selection #
417-
######################
418-
419427
def backend(version: str = None) -> Backend:
420428
"""
421429
Returns information about the Comsol back-end.

0 commit comments

Comments
 (0)