Spaces:
Sleeping
Sleeping
| """ | |
| f2py2e - Fortran to Python C/API generator. 2nd Edition. | |
| See __usage__ below. | |
| Copyright 1999 -- 2011 Pearu Peterson all rights reserved. | |
| Copyright 2011 -- present NumPy Developers. | |
| Permission to use, modify, and distribute this software is given under the | |
| terms of the NumPy License. | |
| NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. | |
| """ | |
| import argparse | |
| import os | |
| import pprint | |
| import re | |
| import sys | |
| from numpy.f2py._backends import f2py_build_generator | |
| from . import ( | |
| __version__, | |
| auxfuncs, | |
| capi_maps, | |
| cb_rules, | |
| cfuncs, | |
| crackfortran, | |
| f90mod_rules, | |
| rules, | |
| ) | |
| from .cfuncs import errmess | |
| f2py_version = __version__.version | |
| numpy_version = __version__.version | |
| # outmess=sys.stdout.write | |
| show = pprint.pprint | |
| outmess = auxfuncs.outmess | |
| MESON_ONLY_VER = (sys.version_info >= (3, 12)) | |
| __usage__ =\ | |
| f"""Usage: | |
| 1) To construct extension module sources: | |
| f2py [<options>] <fortran files> [[[only:]||[skip:]] \\ | |
| <fortran functions> ] \\ | |
| [: <fortran files> ...] | |
| 2) To compile fortran files and build extension modules: | |
| f2py -c [<options>, <build_flib options>, <extra options>] <fortran files> | |
| 3) To generate signature files: | |
| f2py -h <filename.pyf> ...< same options as in (1) > | |
| Description: This program generates a Python C/API file (<modulename>module.c) | |
| that contains wrappers for given fortran functions so that they | |
| can be called from Python. With the -c option the corresponding | |
| extension modules are built. | |
| Options: | |
| -h <filename> Write signatures of the fortran routines to file <filename> | |
| and exit. You can then edit <filename> and use it instead | |
| of <fortran files>. If <filename>==stdout then the | |
| signatures are printed to stdout. | |
| <fortran functions> Names of fortran routines for which Python C/API | |
| functions will be generated. Default is all that are found | |
| in <fortran files>. | |
| <fortran files> Paths to fortran/signature files that will be scanned for | |
| <fortran functions> in order to determine their signatures. | |
| skip: Ignore fortran functions that follow until `:'. | |
| only: Use only fortran functions that follow until `:'. | |
| : Get back to <fortran files> mode. | |
| -m <modulename> Name of the module; f2py generates a Python/C API | |
| file <modulename>module.c or extension module <modulename>. | |
| Default is 'untitled'. | |
| '-include<header>' Writes additional headers in the C wrapper, can be passed | |
| multiple times, generates #include <header> each time. | |
| --[no-]lower Do [not] lower the cases in <fortran files>. By default, | |
| --lower is assumed with -h key, and --no-lower without -h key. | |
| --build-dir <dirname> All f2py generated files are created in <dirname>. | |
| Default is tempfile.mkdtemp(). | |
| --overwrite-signature Overwrite existing signature file. | |
| --[no-]latex-doc Create (or not) <modulename>module.tex. | |
| Default is --no-latex-doc. | |
| --short-latex Create 'incomplete' LaTeX document (without commands | |
| \\documentclass, \\tableofcontents, and \\begin{{document}}, | |
| \\end{{document}}). | |
| --[no-]rest-doc Create (or not) <modulename>module.rst. | |
| Default is --no-rest-doc. | |
| --debug-capi Create C/API code that reports the state of the wrappers | |
| during runtime. Useful for debugging. | |
| --[no-]wrap-functions Create Fortran subroutine wrappers to Fortran 77 | |
| functions. --wrap-functions is default because it ensures | |
| maximum portability/compiler independence. | |
| --[no-]freethreading-compatible Create a module that declares it does or | |
| doesn't require the GIL. The default is | |
| --freethreading-compatible for backward | |
| compatibility. Inspect the Fortran code you are wrapping for | |
| thread safety issues before passing | |
| --no-freethreading-compatible, as f2py does not analyze | |
| fortran code for thread safety issues. | |
| --include-paths <path1>:<path2>:... Search include files from the given | |
| directories. | |
| --help-link [..] List system resources found by system_info.py. See also | |
| --link-<resource> switch below. [..] is optional list | |
| of resources names. E.g. try 'f2py --help-link lapack_opt'. | |
| --f2cmap <filename> Load Fortran-to-Python KIND specification from the given | |
| file. Default: .f2py_f2cmap in current directory. | |
| --quiet Run quietly. | |
| --verbose Run with extra verbosity. | |
| --skip-empty-wrappers Only generate wrapper files when needed. | |
| -v Print f2py version ID and exit. | |
| build backend options (only effective with -c) | |
| [NO_MESON] is used to indicate an option not meant to be used | |
| with the meson backend or above Python 3.12: | |
| --fcompiler= Specify Fortran compiler type by vendor [NO_MESON] | |
| --compiler= Specify distutils C compiler type [NO_MESON] | |
| --help-fcompiler List available Fortran compilers and exit [NO_MESON] | |
| --f77exec= Specify the path to F77 compiler [NO_MESON] | |
| --f90exec= Specify the path to F90 compiler [NO_MESON] | |
| --f77flags= Specify F77 compiler flags | |
| --f90flags= Specify F90 compiler flags | |
| --opt= Specify optimization flags [NO_MESON] | |
| --arch= Specify architecture specific optimization flags [NO_MESON] | |
| --noopt Compile without optimization [NO_MESON] | |
| --noarch Compile without arch-dependent optimization [NO_MESON] | |
| --debug Compile with debugging information | |
| --dep <dependency> | |
| Specify a meson dependency for the module. This may | |
| be passed multiple times for multiple dependencies. | |
| Dependencies are stored in a list for further processing. | |
| Example: --dep lapack --dep scalapack | |
| This will identify "lapack" and "scalapack" as dependencies | |
| and remove them from argv, leaving a dependencies list | |
| containing ["lapack", "scalapack"]. | |
| --backend <backend_type> | |
| Specify the build backend for the compilation process. | |
| The supported backends are 'meson' and 'distutils'. | |
| If not specified, defaults to 'distutils'. On | |
| Python 3.12 or higher, the default is 'meson'. | |
| Extra options (only effective with -c): | |
| --link-<resource> Link extension module with <resource> as defined | |
| by numpy.distutils/system_info.py. E.g. to link | |
| with optimized LAPACK libraries (vecLib on MacOSX, | |
| ATLAS elsewhere), use --link-lapack_opt. | |
| See also --help-link switch. [NO_MESON] | |
| -L/path/to/lib/ -l<libname> | |
| -D<define> -U<name> | |
| -I/path/to/include/ | |
| <filename>.o <filename>.so <filename>.a | |
| Using the following macros may be required with non-gcc Fortran | |
| compilers: | |
| -DPREPEND_FORTRAN -DNO_APPEND_FORTRAN -DUPPERCASE_FORTRAN | |
| When using -DF2PY_REPORT_ATEXIT, a performance report of F2PY | |
| interface is printed out at exit (platforms: Linux). | |
| When using -DF2PY_REPORT_ON_ARRAY_COPY=<int>, a message is | |
| sent to stderr whenever F2PY interface makes a copy of an | |
| array. Integer <int> sets the threshold for array sizes when | |
| a message should be shown. | |
| Version: {f2py_version} | |
| numpy Version: {numpy_version} | |
| License: NumPy license (see LICENSE.txt in the NumPy source code) | |
| Copyright 1999 -- 2011 Pearu Peterson all rights reserved. | |
| Copyright 2011 -- present NumPy Developers. | |
| https://numpy.org/doc/stable/f2py/index.html\n""" | |
| def scaninputline(inputline): | |
| files, skipfuncs, onlyfuncs, debug = [], [], [], [] | |
| f, f2, f3, f5, f6, f8, f9, f10 = 1, 0, 0, 0, 0, 0, 0, 0 | |
| verbose = 1 | |
| emptygen = True | |
| dolc = -1 | |
| dolatexdoc = 0 | |
| dorestdoc = 0 | |
| wrapfuncs = 1 | |
| buildpath = '.' | |
| include_paths, freethreading_compatible, inputline = get_newer_options(inputline) | |
| signsfile, modulename = None, None | |
| options = {'buildpath': buildpath, | |
| 'coutput': None, | |
| 'f2py_wrapper_output': None} | |
| for l in inputline: | |
| if l == '': | |
| pass | |
| elif l == 'only:': | |
| f = 0 | |
| elif l == 'skip:': | |
| f = -1 | |
| elif l == ':': | |
| f = 1 | |
| elif l[:8] == '--debug-': | |
| debug.append(l[8:]) | |
| elif l == '--lower': | |
| dolc = 1 | |
| elif l == '--build-dir': | |
| f6 = 1 | |
| elif l == '--no-lower': | |
| dolc = 0 | |
| elif l == '--quiet': | |
| verbose = 0 | |
| elif l == '--verbose': | |
| verbose += 1 | |
| elif l == '--latex-doc': | |
| dolatexdoc = 1 | |
| elif l == '--no-latex-doc': | |
| dolatexdoc = 0 | |
| elif l == '--rest-doc': | |
| dorestdoc = 1 | |
| elif l == '--no-rest-doc': | |
| dorestdoc = 0 | |
| elif l == '--wrap-functions': | |
| wrapfuncs = 1 | |
| elif l == '--no-wrap-functions': | |
| wrapfuncs = 0 | |
| elif l == '--short-latex': | |
| options['shortlatex'] = 1 | |
| elif l == '--coutput': | |
| f8 = 1 | |
| elif l == '--f2py-wrapper-output': | |
| f9 = 1 | |
| elif l == '--f2cmap': | |
| f10 = 1 | |
| elif l == '--overwrite-signature': | |
| options['h-overwrite'] = 1 | |
| elif l == '-h': | |
| f2 = 1 | |
| elif l == '-m': | |
| f3 = 1 | |
| elif l[:2] == '-v': | |
| print(f2py_version) | |
| sys.exit() | |
| elif l == '--show-compilers': | |
| f5 = 1 | |
| elif l[:8] == '-include': | |
| cfuncs.outneeds['userincludes'].append(l[9:-1]) | |
| cfuncs.userincludes[l[9:-1]] = '#include ' + l[8:] | |
| elif l == '--skip-empty-wrappers': | |
| emptygen = False | |
| elif l[0] == '-': | |
| errmess(f'Unknown option {repr(l)}\n') | |
| sys.exit() | |
| elif f2: | |
| f2 = 0 | |
| signsfile = l | |
| elif f3: | |
| f3 = 0 | |
| modulename = l | |
| elif f6: | |
| f6 = 0 | |
| buildpath = l | |
| elif f8: | |
| f8 = 0 | |
| options["coutput"] = l | |
| elif f9: | |
| f9 = 0 | |
| options["f2py_wrapper_output"] = l | |
| elif f10: | |
| f10 = 0 | |
| options["f2cmap_file"] = l | |
| elif f == 1: | |
| try: | |
| with open(l): | |
| pass | |
| files.append(l) | |
| except OSError as detail: | |
| errmess(f'OSError: {detail!s}. Skipping file "{l!s}".\n') | |
| elif f == -1: | |
| skipfuncs.append(l) | |
| elif f == 0: | |
| onlyfuncs.append(l) | |
| if not f5 and not files and not modulename: | |
| print(__usage__) | |
| sys.exit() | |
| if not os.path.isdir(buildpath): | |
| if not verbose: | |
| outmess(f'Creating build directory {buildpath}\n') | |
| os.mkdir(buildpath) | |
| if signsfile: | |
| signsfile = os.path.join(buildpath, signsfile) | |
| if signsfile and os.path.isfile(signsfile) and 'h-overwrite' not in options: | |
| errmess( | |
| f'Signature file "{signsfile}" exists!!! Use --overwrite-signature to overwrite.\n') | |
| sys.exit() | |
| options['emptygen'] = emptygen | |
| options['debug'] = debug | |
| options['verbose'] = verbose | |
| if dolc == -1 and not signsfile: | |
| options['do-lower'] = 0 | |
| else: | |
| options['do-lower'] = dolc | |
| if modulename: | |
| options['module'] = modulename | |
| if signsfile: | |
| options['signsfile'] = signsfile | |
| if onlyfuncs: | |
| options['onlyfuncs'] = onlyfuncs | |
| if skipfuncs: | |
| options['skipfuncs'] = skipfuncs | |
| options['dolatexdoc'] = dolatexdoc | |
| options['dorestdoc'] = dorestdoc | |
| options['wrapfuncs'] = wrapfuncs | |
| options['buildpath'] = buildpath | |
| options['include_paths'] = include_paths | |
| options['requires_gil'] = not freethreading_compatible | |
| options.setdefault('f2cmap_file', None) | |
| return files, options | |
| def callcrackfortran(files, options): | |
| rules.options = options | |
| crackfortran.debug = options['debug'] | |
| crackfortran.verbose = options['verbose'] | |
| if 'module' in options: | |
| crackfortran.f77modulename = options['module'] | |
| if 'skipfuncs' in options: | |
| crackfortran.skipfuncs = options['skipfuncs'] | |
| if 'onlyfuncs' in options: | |
| crackfortran.onlyfuncs = options['onlyfuncs'] | |
| crackfortran.include_paths[:] = options['include_paths'] | |
| crackfortran.dolowercase = options['do-lower'] | |
| postlist = crackfortran.crackfortran(files) | |
| if 'signsfile' in options: | |
| outmess(f"Saving signatures to file \"{options['signsfile']}\"\n") | |
| pyf = crackfortran.crack2fortran(postlist) | |
| if options['signsfile'][-6:] == 'stdout': | |
| sys.stdout.write(pyf) | |
| else: | |
| with open(options['signsfile'], 'w') as f: | |
| f.write(pyf) | |
| if options["coutput"] is None: | |
| for mod in postlist: | |
| mod["coutput"] = f"{mod['name']}module.c" | |
| else: | |
| for mod in postlist: | |
| mod["coutput"] = options["coutput"] | |
| if options["f2py_wrapper_output"] is None: | |
| for mod in postlist: | |
| mod["f2py_wrapper_output"] = f"{mod['name']}-f2pywrappers.f" | |
| else: | |
| for mod in postlist: | |
| mod["f2py_wrapper_output"] = options["f2py_wrapper_output"] | |
| for mod in postlist: | |
| if options["requires_gil"]: | |
| mod['gil_used'] = 'Py_MOD_GIL_USED' | |
| else: | |
| mod['gil_used'] = 'Py_MOD_GIL_NOT_USED' | |
| # gh-26718 Reset global | |
| crackfortran.f77modulename = '' | |
| return postlist | |
| def buildmodules(lst): | |
| cfuncs.buildcfuncs() | |
| outmess('Building modules...\n') | |
| modules, mnames, isusedby = [], [], {} | |
| for item in lst: | |
| if '__user__' in item['name']: | |
| cb_rules.buildcallbacks(item) | |
| else: | |
| if 'use' in item: | |
| for u in item['use'].keys(): | |
| if u not in isusedby: | |
| isusedby[u] = [] | |
| isusedby[u].append(item['name']) | |
| modules.append(item) | |
| mnames.append(item['name']) | |
| ret = {} | |
| for module, name in zip(modules, mnames): | |
| if name in isusedby: | |
| outmess('\tSkipping module "%s" which is used by %s.\n' % ( | |
| name, ','.join('"%s"' % s for s in isusedby[name]))) | |
| else: | |
| um = [] | |
| if 'use' in module: | |
| for u in module['use'].keys(): | |
| if u in isusedby and u in mnames: | |
| um.append(modules[mnames.index(u)]) | |
| else: | |
| outmess( | |
| f'\tModule "{name}" uses nonexisting "{u}" ' | |
| 'which will be ignored.\n') | |
| ret[name] = {} | |
| dict_append(ret[name], rules.buildmodule(module, um)) | |
| return ret | |
| def dict_append(d_out, d_in): | |
| for (k, v) in d_in.items(): | |
| if k not in d_out: | |
| d_out[k] = [] | |
| if isinstance(v, list): | |
| d_out[k] = d_out[k] + v | |
| else: | |
| d_out[k].append(v) | |
| def run_main(comline_list): | |
| """ | |
| Equivalent to running:: | |
| f2py <args> | |
| where ``<args>=string.join(<list>,' ')``, but in Python. Unless | |
| ``-h`` is used, this function returns a dictionary containing | |
| information on generated modules and their dependencies on source | |
| files. | |
| You cannot build extension modules with this function, that is, | |
| using ``-c`` is not allowed. Use the ``compile`` command instead. | |
| Examples | |
| -------- | |
| The command ``f2py -m scalar scalar.f`` can be executed from Python as | |
| follows. | |
| .. literalinclude:: ../../source/f2py/code/results/run_main_session.dat | |
| :language: python | |
| """ | |
| crackfortran.reset_global_f2py_vars() | |
| f2pydir = os.path.dirname(os.path.abspath(cfuncs.__file__)) | |
| fobjhsrc = os.path.join(f2pydir, 'src', 'fortranobject.h') | |
| fobjcsrc = os.path.join(f2pydir, 'src', 'fortranobject.c') | |
| # gh-22819 -- begin | |
| parser = make_f2py_compile_parser() | |
| args, comline_list = parser.parse_known_args(comline_list) | |
| pyf_files, _ = filter_files("", "[.]pyf([.]src|)", comline_list) | |
| # Checks that no existing modulename is defined in a pyf file | |
| # TODO: Remove all this when scaninputline is replaced | |
| if args.module_name: | |
| if "-h" in comline_list: | |
| modname = ( | |
| args.module_name | |
| ) # Directly use from args when -h is present | |
| else: | |
| modname = validate_modulename( | |
| pyf_files, args.module_name | |
| ) # Validate modname when -h is not present | |
| comline_list += ['-m', modname] # needed for the rest of scaninputline | |
| # gh-22819 -- end | |
| files, options = scaninputline(comline_list) | |
| auxfuncs.options = options | |
| capi_maps.load_f2cmap_file(options['f2cmap_file']) | |
| postlist = callcrackfortran(files, options) | |
| isusedby = {} | |
| for plist in postlist: | |
| if 'use' in plist: | |
| for u in plist['use'].keys(): | |
| if u not in isusedby: | |
| isusedby[u] = [] | |
| isusedby[u].append(plist['name']) | |
| for plist in postlist: | |
| module_name = plist['name'] | |
| if plist['block'] == 'python module' and '__user__' in module_name: | |
| if module_name in isusedby: | |
| # if not quiet: | |
| usedby = ','.join(f'"{s}"' for s in isusedby[module_name]) | |
| outmess( | |
| f'Skipping Makefile build for module "{module_name}" ' | |
| f'which is used by {usedby}\n') | |
| if 'signsfile' in options: | |
| if options['verbose'] > 1: | |
| outmess( | |
| 'Stopping. Edit the signature file and then run f2py on the signature file: ') | |
| outmess(f"{os.path.basename(sys.argv[0])} {options['signsfile']}\n") | |
| return | |
| for plist in postlist: | |
| if plist['block'] != 'python module': | |
| if 'python module' not in options: | |
| errmess( | |
| 'Tip: If your original code is Fortran source then you must use -m option.\n') | |
| raise TypeError('All blocks must be python module blocks but got %s' % ( | |
| repr(plist['block']))) | |
| auxfuncs.debugoptions = options['debug'] | |
| f90mod_rules.options = options | |
| auxfuncs.wrapfuncs = options['wrapfuncs'] | |
| ret = buildmodules(postlist) | |
| for mn in ret.keys(): | |
| dict_append(ret[mn], {'csrc': fobjcsrc, 'h': fobjhsrc}) | |
| return ret | |
| def filter_files(prefix, suffix, files, remove_prefix=None): | |
| """ | |
| Filter files by prefix and suffix. | |
| """ | |
| filtered, rest = [], [] | |
| match = re.compile(prefix + r'.*' + suffix + r'\Z').match | |
| if remove_prefix: | |
| ind = len(prefix) | |
| else: | |
| ind = 0 | |
| for file in [x.strip() for x in files]: | |
| if match(file): | |
| filtered.append(file[ind:]) | |
| else: | |
| rest.append(file) | |
| return filtered, rest | |
| def get_prefix(module): | |
| p = os.path.dirname(os.path.dirname(module.__file__)) | |
| return p | |
| class CombineIncludePaths(argparse.Action): | |
| def __call__(self, parser, namespace, values, option_string=None): | |
| include_paths_set = set(getattr(namespace, 'include_paths', []) or []) | |
| if option_string == "--include_paths": | |
| outmess("Use --include-paths or -I instead of --include_paths which will be removed") | |
| if option_string in {"--include-paths", "--include_paths"}: | |
| include_paths_set.update(values.split(':')) | |
| else: | |
| include_paths_set.add(values) | |
| namespace.include_paths = list(include_paths_set) | |
| def f2py_parser(): | |
| parser = argparse.ArgumentParser(add_help=False) | |
| parser.add_argument("-I", dest="include_paths", action=CombineIncludePaths) | |
| parser.add_argument("--include-paths", dest="include_paths", action=CombineIncludePaths) | |
| parser.add_argument("--include_paths", dest="include_paths", action=CombineIncludePaths) | |
| parser.add_argument("--freethreading-compatible", dest="ftcompat", action=argparse.BooleanOptionalAction) | |
| return parser | |
| def get_newer_options(iline): | |
| iline = (' '.join(iline)).split() | |
| parser = f2py_parser() | |
| args, remain = parser.parse_known_args(iline) | |
| ipaths = args.include_paths | |
| if args.include_paths is None: | |
| ipaths = [] | |
| return ipaths, args.ftcompat, remain | |
| def make_f2py_compile_parser(): | |
| parser = argparse.ArgumentParser(add_help=False) | |
| parser.add_argument("--dep", action="append", dest="dependencies") | |
| parser.add_argument("--backend", choices=['meson', 'distutils'], default='distutils') | |
| parser.add_argument("-m", dest="module_name") | |
| return parser | |
| def preparse_sysargv(): | |
| # To keep backwards bug compatibility, newer flags are handled by argparse, | |
| # and `sys.argv` is passed to the rest of `f2py` as is. | |
| parser = make_f2py_compile_parser() | |
| args, remaining_argv = parser.parse_known_args() | |
| sys.argv = [sys.argv[0]] + remaining_argv | |
| backend_key = args.backend | |
| if MESON_ONLY_VER and backend_key == 'distutils': | |
| outmess("Cannot use distutils backend with Python>=3.12," | |
| " using meson backend instead.\n") | |
| backend_key = "meson" | |
| return { | |
| "dependencies": args.dependencies or [], | |
| "backend": backend_key, | |
| "modulename": args.module_name, | |
| } | |
| def run_compile(): | |
| """ | |
| Do it all in one call! | |
| """ | |
| import tempfile | |
| # Collect dependency flags, preprocess sys.argv | |
| argy = preparse_sysargv() | |
| modulename = argy["modulename"] | |
| if modulename is None: | |
| modulename = 'untitled' | |
| dependencies = argy["dependencies"] | |
| backend_key = argy["backend"] | |
| build_backend = f2py_build_generator(backend_key) | |
| i = sys.argv.index('-c') | |
| del sys.argv[i] | |
| remove_build_dir = 0 | |
| try: | |
| i = sys.argv.index('--build-dir') | |
| except ValueError: | |
| i = None | |
| if i is not None: | |
| build_dir = sys.argv[i + 1] | |
| del sys.argv[i + 1] | |
| del sys.argv[i] | |
| else: | |
| remove_build_dir = 1 | |
| build_dir = tempfile.mkdtemp() | |
| _reg1 = re.compile(r'--link-') | |
| sysinfo_flags = [_m for _m in sys.argv[1:] if _reg1.match(_m)] | |
| sys.argv = [_m for _m in sys.argv if _m not in sysinfo_flags] | |
| if sysinfo_flags: | |
| sysinfo_flags = [f[7:] for f in sysinfo_flags] | |
| _reg2 = re.compile( | |
| r'--((no-|)(wrap-functions|lower|freethreading-compatible)|debug-capi|quiet|skip-empty-wrappers)|-include') | |
| f2py_flags = [_m for _m in sys.argv[1:] if _reg2.match(_m)] | |
| sys.argv = [_m for _m in sys.argv if _m not in f2py_flags] | |
| f2py_flags2 = [] | |
| fl = 0 | |
| for a in sys.argv[1:]: | |
| if a in ['only:', 'skip:']: | |
| fl = 1 | |
| elif a == ':': | |
| fl = 0 | |
| if fl or a == ':': | |
| f2py_flags2.append(a) | |
| if f2py_flags2 and f2py_flags2[-1] != ':': | |
| f2py_flags2.append(':') | |
| f2py_flags.extend(f2py_flags2) | |
| sys.argv = [_m for _m in sys.argv if _m not in f2py_flags2] | |
| _reg3 = re.compile( | |
| r'--((f(90)?compiler(-exec|)|compiler)=|help-compiler)') | |
| flib_flags = [_m for _m in sys.argv[1:] if _reg3.match(_m)] | |
| sys.argv = [_m for _m in sys.argv if _m not in flib_flags] | |
| # TODO: Once distutils is dropped completely, i.e. min_ver >= 3.12, unify into --fflags | |
| reg_f77_f90_flags = re.compile(r'--f(77|90)flags=') | |
| reg_distutils_flags = re.compile(r'--((f(77|90)exec|opt|arch)=|(debug|noopt|noarch|help-fcompiler))') | |
| fc_flags = [_m for _m in sys.argv[1:] if reg_f77_f90_flags.match(_m)] | |
| distutils_flags = [_m for _m in sys.argv[1:] if reg_distutils_flags.match(_m)] | |
| if not (MESON_ONLY_VER or backend_key == 'meson'): | |
| fc_flags.extend(distutils_flags) | |
| sys.argv = [_m for _m in sys.argv if _m not in (fc_flags + distutils_flags)] | |
| del_list = [] | |
| for s in flib_flags: | |
| v = '--fcompiler=' | |
| if s[:len(v)] == v: | |
| if MESON_ONLY_VER or backend_key == 'meson': | |
| outmess( | |
| "--fcompiler cannot be used with meson," | |
| "set compiler with the FC environment variable\n" | |
| ) | |
| else: | |
| from numpy.distutils import fcompiler | |
| fcompiler.load_all_fcompiler_classes() | |
| allowed_keys = list(fcompiler.fcompiler_class.keys()) | |
| nv = ov = s[len(v):].lower() | |
| if ov not in allowed_keys: | |
| vmap = {} # XXX | |
| try: | |
| nv = vmap[ov] | |
| except KeyError: | |
| if ov not in vmap.values(): | |
| print(f'Unknown vendor: "{s[len(v):]}"') | |
| nv = ov | |
| i = flib_flags.index(s) | |
| flib_flags[i] = '--fcompiler=' + nv # noqa: B909 | |
| continue | |
| for s in del_list: | |
| i = flib_flags.index(s) | |
| del flib_flags[i] | |
| assert len(flib_flags) <= 2, repr(flib_flags) | |
| _reg5 = re.compile(r'--(verbose)') | |
| setup_flags = [_m for _m in sys.argv[1:] if _reg5.match(_m)] | |
| sys.argv = [_m for _m in sys.argv if _m not in setup_flags] | |
| if '--quiet' in f2py_flags: | |
| setup_flags.append('--quiet') | |
| # Ugly filter to remove everything but sources | |
| sources = sys.argv[1:] | |
| f2cmapopt = '--f2cmap' | |
| if f2cmapopt in sys.argv: | |
| i = sys.argv.index(f2cmapopt) | |
| f2py_flags.extend(sys.argv[i:i + 2]) | |
| del sys.argv[i + 1], sys.argv[i] | |
| sources = sys.argv[1:] | |
| pyf_files, _sources = filter_files("", "[.]pyf([.]src|)", sources) | |
| sources = pyf_files + _sources | |
| modulename = validate_modulename(pyf_files, modulename) | |
| extra_objects, sources = filter_files('', '[.](o|a|so|dylib)', sources) | |
| library_dirs, sources = filter_files('-L', '', sources, remove_prefix=1) | |
| libraries, sources = filter_files('-l', '', sources, remove_prefix=1) | |
| undef_macros, sources = filter_files('-U', '', sources, remove_prefix=1) | |
| define_macros, sources = filter_files('-D', '', sources, remove_prefix=1) | |
| for i in range(len(define_macros)): | |
| name_value = define_macros[i].split('=', 1) | |
| if len(name_value) == 1: | |
| name_value.append(None) | |
| if len(name_value) == 2: | |
| define_macros[i] = tuple(name_value) | |
| else: | |
| print('Invalid use of -D:', name_value) | |
| # Construct wrappers / signatures / things | |
| if backend_key == 'meson': | |
| if not pyf_files: | |
| outmess('Using meson backend\nWill pass --lower to f2py\nSee https://numpy.org/doc/stable/f2py/buildtools/meson.html\n') | |
| f2py_flags.append('--lower') | |
| run_main(f" {' '.join(f2py_flags)} -m {modulename} {' '.join(sources)}".split()) | |
| else: | |
| run_main(f" {' '.join(f2py_flags)} {' '.join(pyf_files)}".split()) | |
| # Order matters here, includes are needed for run_main above | |
| include_dirs, _, sources = get_newer_options(sources) | |
| # Now use the builder | |
| builder = build_backend( | |
| modulename, | |
| sources, | |
| extra_objects, | |
| build_dir, | |
| include_dirs, | |
| library_dirs, | |
| libraries, | |
| define_macros, | |
| undef_macros, | |
| f2py_flags, | |
| sysinfo_flags, | |
| fc_flags, | |
| flib_flags, | |
| setup_flags, | |
| remove_build_dir, | |
| {"dependencies": dependencies}, | |
| ) | |
| builder.compile() | |
| def validate_modulename(pyf_files, modulename='untitled'): | |
| if len(pyf_files) > 1: | |
| raise ValueError("Only one .pyf file per call") | |
| if pyf_files: | |
| pyff = pyf_files[0] | |
| pyf_modname = auxfuncs.get_f2py_modulename(pyff) | |
| if modulename != pyf_modname: | |
| outmess( | |
| f"Ignoring -m {modulename}.\n" | |
| f"{pyff} defines {pyf_modname} to be the modulename.\n" | |
| ) | |
| modulename = pyf_modname | |
| return modulename | |
| def main(): | |
| if '--help-link' in sys.argv[1:]: | |
| sys.argv.remove('--help-link') | |
| if MESON_ONLY_VER: | |
| outmess("Use --dep for meson builds\n") | |
| else: | |
| from numpy.distutils.system_info import show_all | |
| show_all() | |
| return | |
| if '-c' in sys.argv[1:]: | |
| run_compile() | |
| else: | |
| run_main(sys.argv[1:]) | |