1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
|
#
# python3 setup_cle2000.py install --home=.
#
from sys import version_info
if version_info[0] == 3 and version_info[1] >= 12:
from setuptools import setup, Extension
elif version_info[0] > 3:
from setuptools import setup, Extension
else:
from distutils.core import setup, Extension
import sys
import os
def _detect_gfortran_dir():
import subprocess, os
try:
out = subprocess.check_output(['gfortran', '-print-file-name=libgfortran.dylib'], text=True).strip()
if out and out != 'libgfortran.dylib':
d = os.path.dirname(out)
if os.path.isdir(d):
return d
except Exception:
pass
return None
def _has_any(paths):
for p in paths:
if any(os.path.exists(pfx) for pfx in _expand_suffixes(p)):
return True
return False
def _expand_suffixes(base):
# Consider common library suffixes across UNIX
# Static is last resort; we prefer shared for Python extension link
exts = ['.dylib', '.so', '.a']
return [base + ext for ext in exts]
def _detect_flang_dir():
"""Best-effort detection of the flang/Fortran runtime library directory on UNIX.
Priority order:
1) FLANGLIB or FLANG_LIBDIR (explicit path)
2) From LLVMTOOLS (../lib or ../lib64)
3) llvm-config --libdir
4) flang-new location heuristic (bin -> lib or sibling llvm lib)
5) Homebrew (macOS): brew --prefix flang, then /opt/homebrew/opt/flang/lib
6) Common system locations (/usr/lib, /usr/lib64, /usr/local/lib, /opt/local/lib)
Returns the first directory that appears to contain flang/Fortran runtime libs.
"""
import subprocess
candidates = []
# 1) Explicit overrides
for env_name in ('FLANGLIB', 'FLANG_LIBDIR'):
d = os.environ.get(env_name)
if d and os.path.isdir(d):
candidates.append(d)
# 2) Derive from LLVMTOOLS (often a bin path)
t = os.environ.get('LLVMTOOLS')
if t:
for sub in ('..',):
for libname in ('lib', 'lib64'):
cand = os.path.abspath(os.path.join(t, sub, libname))
if os.path.isdir(cand):
candidates.append(cand)
# 3) llvm-config --libdir
try:
libdir = subprocess.check_output(['llvm-config', '--libdir'], text=True).strip()
if os.path.isdir(libdir):
candidates.append(libdir)
except Exception:
pass
# 4) Locate flang-new executable and probe nearby
try:
fl = subprocess.check_output(['which', 'flang-new'], text=True).strip()
if fl and os.path.isabs(fl):
base = os.path.dirname(os.path.dirname(fl)) # prefix
for libname in ('lib', 'lib64'):
cand = os.path.join(base, libname)
if os.path.isdir(cand):
candidates.append(cand)
except Exception:
pass
# 5) macOS Homebrew
if sys.platform == 'darwin':
try:
prefix = subprocess.check_output(['brew', '--prefix', 'flang'], text=True).strip()
cand = os.path.join(prefix, 'lib')
if os.path.isdir(cand):
candidates.append(cand)
except Exception:
pass
for cand in ('/opt/homebrew/opt/flang/lib', '/usr/local/opt/flang/lib'):
if os.path.isdir(cand):
candidates.append(cand)
# 6) Fallback common UNIX lib dirs
for cand in ('/usr/lib', '/usr/lib64', '/usr/local/lib', '/opt/local/lib'):
if os.path.isdir(cand):
candidates.append(cand)
# Validate candidates by presence of known Fortran runtime libs
def looks_valid(d):
probes = [
os.path.join(d, 'libFortranRuntime'),
os.path.join(d, 'libFortranDecimal'),
os.path.join(d, 'libflang_rt.runtime'),
os.path.join(d, 'libflang'),
]
return _has_any(probes)
seen = set()
for d in candidates:
if not d or d in seen:
continue
seen.add(d)
if looks_valid(d):
return d
return None
def _compute_flang_link_args(libdir):
"""Return a list of extra link args appropriate for the platform and runtime present.
We prefer the new LLVM flang runtime (FortranRuntime/FortranDecimal). If not present,
fall back to classic flang libraries when detected. On macOS, add clang runtime only when needed.
"""
args = []
def haslib(name):
if not libdir:
return False
return _has_any([os.path.join(libdir, name)])
if haslib('libFortranRuntime') and haslib('libFortranDecimal'):
args += ['-lFortranRuntime', '-lFortranDecimal']
elif haslib('libflang_rt.runtime') and haslib('libFortranDecimal'):
args += ['-lflang_rt.runtime', '-lFortranDecimal']
elif haslib('libflang'):
args += ['-lflang']
if haslib('libflangrti'):
args += ['-lflangrti']
else:
args += []
if sys.platform == 'darwin':
args += ['-lclang_rt.osx']
return args
def _compute_openmp_link_args(compiler):
"""Return OpenMP runtime link args based on the compiler suite.
- LLVMTOOLS -> libomp (and Homebrew paths on macOS)
- default (gfortran) -> libgomp
"""
args = []
if compiler == "LLVMTOOLS":
# LLVM OpenMP
if sys.platform == 'darwin':
# Add Homebrew libomp search and rpath paths for both arm64 and x86_64
for p in ('/opt/homebrew/opt/libomp/lib', '/usr/local/opt/libomp/lib'):
args += ['-L'+p, '-Wl,-rpath,'+p]
args += ['-lomp']
else:
# GCC toolchain
args += ['-lgomp']
return args
def main():
import os
from sysconfig import get_config_var
mach = os.path.basename(os.getcwd())
Code = os.environ.get("CODE_EMBEDDED", None) # Code selection
Compiler = os.environ.get("COMPILER", None) # Compiler selection
# Directory containing Fortran runtime libraries
if Compiler == "LLVMTOOLS":
FortranLib = _detect_flang_dir()
else:
FortranLib = _detect_gfortran_dir()
HDF5Lib = os.environ.get("HDF5_API", None) # directory with libhdf5
pylib = os.path.basename(get_config_var("LIBDIR")) # get lib or lib64
print("install Cle2000 binding to", Code, "on directory",mach, "pylib=",pylib, "Compiler=",Compiler)
if Compiler == "NVTOOLS":
libdir="../../lib/"+mach+"_nvidia"
libUtl="../../../Utilib/lib/"+mach+"_nvidia"
libTri="../../../Trivac/lib/"+mach+"_nvidia"
libDra="../../../Dragon/lib/"+mach+"_nvidia"
libDon="../../../Donjon/lib/"+mach+"_nvidia"
extralink=["-lnvcpumath","-lnvf","-lnvc"]
elif Compiler == "LLVMTOOLS":
libdir="../../lib/"+mach+"_llvm"
libUtl="../../../Utilib/lib/"+mach+"_llvm"
libTri="../../../Trivac/lib/"+mach+"_llvm"
libDra="../../../Dragon/lib/"+mach+"_llvm"
libDon="../../../Donjon/lib/"+mach+"_llvm"
extralink=_compute_flang_link_args(FortranLib)
elif Compiler == "INTELTOOLS":
libdir="../../lib/"+mach+"_intel"
libUtl="../../../Utilib/lib/"+mach+"_intel"
libTri="../../../Trivac/lib/"+mach+"_intel"
libDra="../../../Dragon/lib/"+mach+"_intel"
libDon="../../../Donjon/lib/"+mach+"_intel"
extralink=[ ]
else:
libdir="../../lib/"+mach
libUtl="../../../Utilib/lib/"+mach
libTri="../../../Trivac/lib/"+mach
libDra="../../../Dragon/lib/"+mach
libDon="../../../Donjon/lib/"+mach
extralink=["-lgfortran", ]
print("debug Compiler=",Compiler,"libdir=",libdir,"Code=",Code)
if FortranLib:
print("debug FortranLib=", FortranLib)
if HDF5Lib:
print("debug HDF5Lib=", HDF5Lib)
# Build helper lists with None filtered out
def _dirs(*args):
return [d for d in args if d]
if Code == "GANLIB":
setup (name="Cle2000",
version="5.0",
description="Python bindings for Cle-2000 with GANLIB",
author="Alain Hebert",
author_email="alain.hebert@polymtl.ca",
license="LGPL",
ext_modules=[Extension('cle2000',sources=['cle2000module.c'],
extra_link_args = extralink,
include_dirs=["../../../Ganlib/src"],
library_dirs=_dirs(libdir,FortranLib,HDF5Lib),
runtime_library_dirs=_dirs(FortranLib,HDF5Lib),
libraries=["Ganlib","hdf5"] ) ])
elif Code == "TRIVAC":
setup (name="Cle2000",
version="5.0",
description="Python bindings for Cle-2000 with TRIVAC",
author="Alain Hebert",
author_email="alain.hebert@polymtl.ca",
license="LGPL",
ext_modules=[Extension('cle2000',sources=['cle2000module.c'],
define_macros=[('__trivac__', None)],
extra_link_args = extralink,
include_dirs=["../../../Ganlib/src"],
library_dirs=_dirs(libdir,FortranLib,HDF5Lib,libUtl,libTri),
runtime_library_dirs=_dirs(FortranLib,HDF5Lib),
libraries=["Trivac","Utilib","Ganlib","hdf5"] ) ])
elif Code == "DRAGON":
setup (name="Cle2000",
version="5.0",
description="Python bindings for Cle-2000 with DRAGON",
author="Alain Hebert",
author_email="alain.hebert@polymtl.ca",
license="LGPL",
ext_modules=[Extension('cle2000',sources=['cle2000module.c'],
define_macros=[('__dragon__', None)],
extra_link_args = extralink,
include_dirs=["../../../Ganlib/src"],
library_dirs=_dirs(libdir,FortranLib,HDF5Lib,libUtl,libTri,libDra),
runtime_library_dirs=_dirs(FortranLib,HDF5Lib),
libraries=["Dragon","Trivac","Utilib","Ganlib","hdf5"] ) ])
elif Code == "DONJON":
setup (name="Cle2000",
version="5.0",
description="Python bindings for Cle-2000 with DONJON",
author="Alain Hebert",
author_email="alain.hebert@polymtl.ca",
license="LGPL",
ext_modules=[Extension('cle2000',sources=['cle2000module.c'],
define_macros=[('__donjon__', None)],
extra_link_args = extralink,
include_dirs=["../../../Ganlib/src"],
library_dirs=_dirs(libdir,FortranLib,HDF5Lib,libUtl,libTri,libDra,libDon),
runtime_library_dirs=_dirs(FortranLib,HDF5Lib),
libraries=["Donjon","Dragon","Trivac","Utilib","Ganlib","hdf5"] ) ])
elif Code == "GANLIB_OMP":
setup (name="Cle2000",
version="5.0",
description="Python bindings for Cle-2000 with GANLIB_OMP",
author="Alain Hebert",
author_email="alain.hebert@polymtl.ca",
license="LGPL",
ext_modules=[Extension('cle2000',sources=['cle2000module.c'],
extra_link_args = _compute_openmp_link_args(Compiler)+extralink,
include_dirs=["../../../Ganlib/src"],
library_dirs=_dirs(libdir,FortranLib,HDF5Lib),
runtime_library_dirs=_dirs(FortranLib,HDF5Lib),
libraries=["Ganlib","hdf5"] ) ])
elif Code == "TRIVAC_OMP":
setup (name="Cle2000",
version="5.0",
description="Python bindings for Cle-2000 with TRIVAC_OMP",
author="Alain Hebert",
author_email="alain.hebert@polymtl.ca",
license="LGPL",
ext_modules=[Extension('cle2000',sources=['cle2000module.c'],
define_macros=[('__trivac__', None)],
extra_link_args = _compute_openmp_link_args(Compiler)+extralink,
include_dirs=["../../../Ganlib/src"],
library_dirs=_dirs(libdir,FortranLib,HDF5Lib,libUtl,libTri),
runtime_library_dirs=_dirs(FortranLib,HDF5Lib),
libraries=["Trivac","Utilib","Ganlib","hdf5"] ) ])
elif Code == "DRAGON_OMP":
setup (name="Cle2000",
version="5.0",
description="Python bindings for Cle-2000 with DRAGON_OMP",
author="Alain Hebert",
author_email="alain.hebert@polymtl.ca",
license="LGPL",
ext_modules=[Extension('cle2000',sources=['cle2000module.c'],
define_macros=[('__dragon__', None)],
extra_link_args = _compute_openmp_link_args(Compiler)+extralink,
include_dirs=["../../../Ganlib/src"],
library_dirs=_dirs(libdir,FortranLib,HDF5Lib,libUtl,libTri,libDra),
runtime_library_dirs=_dirs(FortranLib,HDF5Lib),
libraries=["Dragon","Trivac","Utilib","Ganlib","hdf5"] ) ])
elif Code == "DONJON_OMP":
setup (name="Cle2000",
version="5.0",
description="Python bindings for Cle-2000 with DONJON_OMP",
author="Alain Hebert",
author_email="alain.hebert@polymtl.ca",
license="LGPL",
ext_modules=[Extension('cle2000',sources=['cle2000module.c'],
define_macros=[('__donjon__', None)],
extra_link_args = _compute_openmp_link_args(Compiler)+extralink,
include_dirs=["../../../Ganlib/src"],
library_dirs=_dirs(libdir,FortranLib,HDF5Lib,libUtl,libTri,libDra,libDon),
runtime_library_dirs=_dirs(FortranLib,HDF5Lib),
libraries=["Donjon","Dragon","Trivac","Utilib","Ganlib","hdf5"] ) ])
else:
raise ValueError(Code+" is not implemented for setup.py bindings")
if __name__ == "__main__":
main()
|