@@ -47,16 +47,35 @@ class Backend(TypedDict):
4747system = platform .system ()
4848log = 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
6180def 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-
419427def backend (version : str = None ) -> Backend :
420428 """
421429 Returns information about the Comsol back-end.
0 commit comments