import os import subprocess import datetime from pkg_resources import parse_version from setuptools_scm import get_version ############################################################################### def filtered_glob(env, pattern, omit=[], ondisk=True, source=False, strings=False): return list(filter( lambda f: os.path.basename(f.path) not in omit, env.Glob(pattern))) # Create a builder for f2py def f2py_generator(source, target, env, for_signature): suffix = ".so" main = source[0] objects = " ".join(o.get_path() for o in source[1:] if str(o).endswith('.o')) moduledir = os.path.dirname(target[0].abspath) modulefile = str(target[0]) modulename = modulefile.replace(suffix, '').replace('/', '.') compiler = env['FORTRAN'] cmd = f"$F2PY --fcompiler={compiler} " cmd += f" $F2PY_OPTS -m {modulename} -c {objects} {main}" cmd += f" && cp {'/'.join(modulename.split('.'))}.*.so {modulefile}" #cmd += " 1>/dev/null 2>/dev/null" return cmd def install_module(env, module): destdir = os.path.dirname(module[0].srcnode().abspath) env.Install(destdir, module) env.Alias('install', destdir) return None def version_action(target, source, env): with open(str(target[0]), 'w') as fd: v = get_version(root='../', relative_to='./', version_scheme="post-release") v = parse_version(v) if v._version.post[-1] == 0: version = v.base_version else: version = v.public fd.write(version + '\n') now = datetime.datetime.now().isoformat() fd.write(f'compiled on {now}\n') p = subprocess.run([env['FORTRAN'], "--version"], stdout=subprocess.PIPE) fd.write(p.stdout.decode('utf8')) f2py_bld = Builder(generator=f2py_generator) version_bld = Builder(action=version_action) # define the default build environment std = Environment(tools=['default', 'fortran'], F2PY_OPTS=[], LIBS=[]) std.AddMethod(filtered_glob, "FilteredGlob") std.AddMethod(install_module, "InstallModule") std['BUILDERS']['F2py'] = f2py_bld std['BUILDERS']['Version'] = version_bld ############################################################################### # Define the command line options AddOption('--dbg', dest='dbg', action='store_true', help='add debugging symbols') AddOption('--verbose', dest='verbose', action='store_true', help='add debugging symbols') AddOption('--compiler', dest='compiler', default='gfortran', choices=['gfortran', 'ifort'], help='The Fortran compiler to use') AddOption('--f2py_path', dest='f2py_path', default='f2py3', help='The full path to the f2py compiler') # define environments gfortran_env = std.Clone(tools=['gfortran']) #gfortran_env.Replace(FORTRANFLAGS=['-fPIC', '-O2', '-ffast-math', '-mcmodel=large', '-fdefault-real-8', '-fdefault-double-8']) gfortran_env.Replace(FORTRANFLAGS=['-fPIC', '-mcmodel=large']) ifort_env = std.Clone(tools=['ifort']) # parse options if GetOption('compiler') == 'gfortran': env = gfortran_env #env.Append(F2PY_OPTS='--opt=-O2') elif GetOption('compiler') == 'ifort': env = ifort_env f2py_path = GetOption('f2py_path') p = subprocess.run(['which', f2py_path], stdout=subprocess.PIPE) if p.returncode != 0: raise Exception('Invalid path for f2py compiler!') else: env.Append(F2PY=p.stdout.decode('utf8').strip()) if GetOption('dbg'): gfortran_env.Append(FORTRANFLAGS = ['-g', '-Wall', '-Wextra', '-Warray-temporaries', '-Wconversion', '-fbacktrace', '-ffree-line-length-0', '-fcheck=all', '-ffpe-trap=zero,overflow,underflow,invalid,denormal', '-finit-real=nan'], F2PY_OPTS = ['--noopt', '--debug-capi', '--debug']) if GetOption('verbose'): env.Replace(FORTRANCOMSTR = "") Export('env') SConscript('msspec/spec/fortran/SConstruct', variant_dir='build/build_spec') SConscript('msspec/phagen/fortran/SConstruct', variant_dir='build/build_phagen') version_file = env.Version('VERSION', []) Depends(version_file, "install")