Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
f2py2e.py789 linesDownload Raw Back to f2py
1"""
2
3f2py2e - Fortran to Python C/API generator. 2nd Edition.
4         See __usage__ below.
5
6Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
7Copyright 2011 -- present NumPy Developers.
8Permission to use, modify, and distribute this software is given under the
9terms of the NumPy License.
10
11NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
12"""
13import argparse
14import os
15import pprint
16import re
17import sys
18
19from numpy.f2py._backends import f2py_build_generator
20
21from . import (
22    __version__,
23    auxfuncs,
24    capi_maps,
25    cb_rules,
26    cfuncs,
27    crackfortran,
28    f90mod_rules,
29    rules,
30)
31from .cfuncs import errmess
32
33f2py_version = __version__.version
34numpy_version = __version__.version
35
36# outmess=sys.stdout.write
37show = pprint.pprint
38outmess = auxfuncs.outmess
39MESON_ONLY_VER = (sys.version_info >= (3, 12))
40
41__usage__ =\
42f"""Usage:
43
441) To construct extension module sources:
45
46      f2py [<options>] <fortran files> [[[only:]||[skip:]] \\
47                                        <fortran functions> ] \\
48                                       [: <fortran files> ...]
49
502) To compile fortran files and build extension modules:
51
52      f2py -c [<options>, <build_flib options>, <extra options>] <fortran files>
53
543) To generate signature files:
55
56      f2py -h <filename.pyf> ...< same options as in (1) >
57
58Description: This program generates a Python C/API file (<modulename>module.c)
59             that contains wrappers for given fortran functions so that they
60             can be called from Python. With the -c option the corresponding
61             extension modules are built.
62
63Options:
64
65  -h <filename>    Write signatures of the fortran routines to file <filename>
66                   and exit. You can then edit <filename> and use it instead
67                   of <fortran files>. If <filename>==stdout then the
68                   signatures are printed to stdout.
69  <fortran functions>  Names of fortran routines for which Python C/API
70                   functions will be generated. Default is all that are found
71                   in <fortran files>.
72  <fortran files>  Paths to fortran/signature files that will be scanned for
73                   <fortran functions> in order to determine their signatures.
74  skip:            Ignore fortran functions that follow until `:'.
75  only:            Use only fortran functions that follow until `:'.
76  :                Get back to <fortran files> mode.
77
78  -m <modulename>  Name of the module; f2py generates a Python/C API
79                   file <modulename>module.c or extension module <modulename>.
80                   Default is 'untitled'.
81
82  '-include<header>'  Writes additional headers in the C wrapper, can be passed
83                      multiple times, generates #include <header> each time.
84
85  --[no-]lower     Do [not] lower the cases in <fortran files>. By default,
86                   --lower is assumed with -h key, and --no-lower without -h key.
87
88  --build-dir <dirname>  All f2py generated files are created in <dirname>.
89                   Default is tempfile.mkdtemp().
90
91  --overwrite-signature  Overwrite existing signature file.
92
93  --[no-]latex-doc Create (or not) <modulename>module.tex.
94                   Default is --no-latex-doc.
95  --short-latex    Create 'incomplete' LaTeX document (without commands
96                   \\documentclass, \\tableofcontents, and \\begin{{document}},
97                   \\end{{document}}).
98
99  --[no-]rest-doc Create (or not) <modulename>module.rst.
100                   Default is --no-rest-doc.
101
102  --debug-capi     Create C/API code that reports the state of the wrappers
103                   during runtime. Useful for debugging.
104
105  --[no-]wrap-functions    Create Fortran subroutine wrappers to Fortran 77
106                   functions. --wrap-functions is default because it ensures
107                   maximum portability/compiler independence.
108
109  --[no-]freethreading-compatible    Create a module that declares it does or
110                   doesn't require the GIL. The default is
111                   --freethreading-compatible for backward
112                   compatibility. Inspect the Fortran code you are wrapping for
113                   thread safety issues before passing
114                   --no-freethreading-compatible, as f2py does not analyze
115                   fortran code for thread safety issues.
116
117  --include-paths <path1>:<path2>:...   Search include files from the given
118                   directories.
119
120  --help-link [..] List system resources found by system_info.py. See also
121                   --link-<resource> switch below. [..] is optional list
122                   of resources names. E.g. try 'f2py --help-link lapack_opt'.
123
124  --f2cmap <filename>  Load Fortran-to-Python KIND specification from the given
125                   file. Default: .f2py_f2cmap in current directory.
126
127  --quiet          Run quietly.
128  --verbose        Run with extra verbosity.
129  --skip-empty-wrappers   Only generate wrapper files when needed.
130  -v               Print f2py version ID and exit.
131
132
133build backend options (only effective with -c)
134[NO_MESON] is used to indicate an option not meant to be used
135with the meson backend or above Python 3.12:
136
137  --fcompiler=         Specify Fortran compiler type by vendor [NO_MESON]
138  --compiler=          Specify distutils C compiler type [NO_MESON]
139
140  --help-fcompiler     List available Fortran compilers and exit [NO_MESON]
141  --f77exec=           Specify the path to F77 compiler [NO_MESON]
142  --f90exec=           Specify the path to F90 compiler [NO_MESON]
143  --f77flags=          Specify F77 compiler flags
144  --f90flags=          Specify F90 compiler flags
145  --opt=               Specify optimization flags [NO_MESON]
146  --arch=              Specify architecture specific optimization flags [NO_MESON]
147  --noopt              Compile without optimization [NO_MESON]
148  --noarch             Compile without arch-dependent optimization [NO_MESON]
149  --debug              Compile with debugging information
150
151  --dep                <dependency>
152                       Specify a meson dependency for the module. This may
153                       be passed multiple times for multiple dependencies.
154                       Dependencies are stored in a list for further processing.
155
156                       Example: --dep lapack --dep scalapack
157                       This will identify "lapack" and "scalapack" as dependencies
158                       and remove them from argv, leaving a dependencies list
159                       containing ["lapack", "scalapack"].
160
161  --backend            <backend_type>
162                       Specify the build backend for the compilation process.
163                       The supported backends are 'meson' and 'distutils'.
164                       If not specified, defaults to 'distutils'. On
165                       Python 3.12 or higher, the default is 'meson'.
166
167Extra options (only effective with -c):
168
169  --link-<resource>    Link extension module with <resource> as defined
170                       by numpy.distutils/system_info.py. E.g. to link
171                       with optimized LAPACK libraries (vecLib on MacOSX,
172                       ATLAS elsewhere), use --link-lapack_opt.
173                       See also --help-link switch. [NO_MESON]
174
175  -L/path/to/lib/ -l<libname>
176  -D<define> -U<name>
177  -I/path/to/include/
178  <filename>.o <filename>.so <filename>.a
179
180  Using the following macros may be required with non-gcc Fortran
181  compilers:
182    -DPREPEND_FORTRAN -DNO_APPEND_FORTRAN -DUPPERCASE_FORTRAN
183
184  When using -DF2PY_REPORT_ATEXIT, a performance report of F2PY
185  interface is printed out at exit (platforms: Linux).
186
187  When using -DF2PY_REPORT_ON_ARRAY_COPY=<int>, a message is
188  sent to stderr whenever F2PY interface makes a copy of an
189  array. Integer <int> sets the threshold for array sizes when
190  a message should be shown.
191
192Version:     {f2py_version}
193numpy Version: {numpy_version}
194License:     NumPy license (see LICENSE.txt in the NumPy source code)
195Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
196Copyright 2011 -- present NumPy Developers.
197https://numpy.org/doc/stable/f2py/index.html\n"""
198
199
200def scaninputline(inputline):
201    files, skipfuncs, onlyfuncs, debug = [], [], [], []
202    f, f2, f3, f5, f6, f8, f9, f10 = 1, 0, 0, 0, 0, 0, 0, 0
203    verbose = 1
204    emptygen = True
205    dolc = -1
206    dolatexdoc = 0
207    dorestdoc = 0
208    wrapfuncs = 1
209    buildpath = '.'
210    include_paths, freethreading_compatible, inputline = get_newer_options(inputline)
211    signsfile, modulename = None, None
212    options = {'buildpath': buildpath,
213               'coutput': None,
214               'f2py_wrapper_output': None}
215    for l in inputline:
216        if l == '':
217            pass
218        elif l == 'only:':
219            f = 0
220        elif l == 'skip:':
221            f = -1
222        elif l == ':':
223            f = 1
224        elif l[:8] == '--debug-':
225            debug.append(l[8:])
226        elif l == '--lower':
227            dolc = 1
228        elif l == '--build-dir':
229            f6 = 1
230        elif l == '--no-lower':
231            dolc = 0
232        elif l == '--quiet':
233            verbose = 0
234        elif l == '--verbose':
235            verbose += 1
236        elif l == '--latex-doc':
237            dolatexdoc = 1
238        elif l == '--no-latex-doc':
239            dolatexdoc = 0
240        elif l == '--rest-doc':
241            dorestdoc = 1
242        elif l == '--no-rest-doc':
243            dorestdoc = 0
244        elif l == '--wrap-functions':
245            wrapfuncs = 1
246        elif l == '--no-wrap-functions':
247            wrapfuncs = 0
248        elif l == '--short-latex':
249            options['shortlatex'] = 1
250        elif l == '--coutput':
251            f8 = 1
252        elif l == '--f2py-wrapper-output':
253            f9 = 1
254        elif l == '--f2cmap':
255            f10 = 1
256        elif l == '--overwrite-signature':
257            options['h-overwrite'] = 1
258        elif l == '-h':
259            f2 = 1
260        elif l == '-m':
261            f3 = 1
262        elif l[:2] == '-v':
263            print(f2py_version)
264            sys.exit()
265        elif l == '--show-compilers':
266            f5 = 1
267        elif l[:8] == '-include':
268            cfuncs.outneeds['userincludes'].append(l[9:-1])
269            cfuncs.userincludes[l[9:-1]] = '#include ' + l[8:]
270        elif l == '--skip-empty-wrappers':
271            emptygen = False
272        elif l[0] == '-':
273            errmess(f'Unknown option {repr(l)}\n')
274            sys.exit()
275        elif f2:
276            f2 = 0
277            signsfile = l
278        elif f3:
279            f3 = 0
280            modulename = l
281        elif f6:
282            f6 = 0
283            buildpath = l
284        elif f8:
285            f8 = 0
286            options["coutput"] = l
287        elif f9:
288            f9 = 0
289            options["f2py_wrapper_output"] = l
290        elif f10:
291            f10 = 0
292            options["f2cmap_file"] = l
293        elif f == 1:
294            try:
295                with open(l):
296                    pass
297                files.append(l)
298            except OSError as detail:
299                errmess(f'OSError: {detail!s}. Skipping file "{l!s}".\n')
300        elif f == -1:
301            skipfuncs.append(l)
302        elif f == 0:
303            onlyfuncs.append(l)
304    if not f5 and not files and not modulename:
305        print(__usage__)
306        sys.exit()
307    if not os.path.isdir(buildpath):
308        if not verbose:
309            outmess(f'Creating build directory {buildpath}\n')
310        os.mkdir(buildpath)
311    if signsfile:
312        signsfile = os.path.join(buildpath, signsfile)
313    if signsfile and os.path.isfile(signsfile) and 'h-overwrite' not in options:
314        errmess(
315            f'Signature file "{signsfile}" exists!!! Use --overwrite-signature to overwrite.\n')
316        sys.exit()
317
318    options['emptygen'] = emptygen
319    options['debug'] = debug
320    options['verbose'] = verbose
321    if dolc == -1 and not signsfile:
322        options['do-lower'] = 0
323    else:
324        options['do-lower'] = dolc
325    if modulename:
326        options['module'] = modulename
327    if signsfile:
328        options['signsfile'] = signsfile
329    if onlyfuncs:
330        options['onlyfuncs'] = onlyfuncs
331    if skipfuncs:
332        options['skipfuncs'] = skipfuncs
333    options['dolatexdoc'] = dolatexdoc
334    options['dorestdoc'] = dorestdoc
335    options['wrapfuncs'] = wrapfuncs
336    options['buildpath'] = buildpath
337    options['include_paths'] = include_paths
338    options['requires_gil'] = not freethreading_compatible
339    options.setdefault('f2cmap_file', None)
340    return files, options
341
342
343def callcrackfortran(files, options):
344    rules.options = options
345    crackfortran.debug = options['debug']
346    crackfortran.verbose = options['verbose']
347    if 'module' in options:
348        crackfortran.f77modulename = options['module']
349    if 'skipfuncs' in options:
350        crackfortran.skipfuncs = options['skipfuncs']
351    if 'onlyfuncs' in options:
352        crackfortran.onlyfuncs = options['onlyfuncs']
353    crackfortran.include_paths[:] = options['include_paths']
354    crackfortran.dolowercase = options['do-lower']
355    postlist = crackfortran.crackfortran(files)
356    if 'signsfile' in options:
357        outmess(f"Saving signatures to file \"{options['signsfile']}\"\n")
358        pyf = crackfortran.crack2fortran(postlist)
359        if options['signsfile'][-6:] == 'stdout':
360            sys.stdout.write(pyf)
361        else:
362            with open(options['signsfile'], 'w') as f:
363                f.write(pyf)
364    if options["coutput"] is None:
365        for mod in postlist:
366            mod["coutput"] = f"{mod['name']}module.c"
367    else:
368        for mod in postlist:
369            mod["coutput"] = options["coutput"]
370    if options["f2py_wrapper_output"] is None:
371        for mod in postlist:
372            mod["f2py_wrapper_output"] = f"{mod['name']}-f2pywrappers.f"
373    else:
374        for mod in postlist:
375            mod["f2py_wrapper_output"] = options["f2py_wrapper_output"]
376    for mod in postlist:
377        if options["requires_gil"]:
378            mod['gil_used'] = 'Py_MOD_GIL_USED'
379        else:
380            mod['gil_used'] = 'Py_MOD_GIL_NOT_USED'
381    # gh-26718 Reset global
382    crackfortran.f77modulename = ''
383    return postlist
384
385
386def buildmodules(lst):
387    cfuncs.buildcfuncs()
388    outmess('Building modules...\n')
389    modules, mnames, isusedby = [], [], {}
390    for item in lst:
391        if '__user__' in item['name']:
392            cb_rules.buildcallbacks(item)
393        else:
394            if 'use' in item:
395                for u in item['use'].keys():
396                    if u not in isusedby:
397                        isusedby[u] = []
398                    isusedby[u].append(item['name'])
399            modules.append(item)
400            mnames.append(item['name'])
401    ret = {}
402    for module, name in zip(modules, mnames):
403        if name in isusedby:
404            outmess('\tSkipping module "%s" which is used by %s.\n' % (
405                name, ','.join('"%s"' % s for s in isusedby[name])))
406        else:
407            um = []
408            if 'use' in module:
409                for u in module['use'].keys():
410                    if u in isusedby and u in mnames:
411                        um.append(modules[mnames.index(u)])
412                    else:
413                        outmess(
414                            f'\tModule "{name}" uses nonexisting "{u}" '
415                            'which will be ignored.\n')
416            ret[name] = {}
417            dict_append(ret[name], rules.buildmodule(module, um))
418    return ret
419
420
421def dict_append(d_out, d_in):
422    for (k, v) in d_in.items():
423        if k not in d_out:
424            d_out[k] = []
425        if isinstance(v, list):
426            d_out[k] = d_out[k] + v
427        else:
428            d_out[k].append(v)
429
430
431def run_main(comline_list):
432    """
433    Equivalent to running::
434
435        f2py <args>
436
437    where ``<args>=string.join(<list>,' ')``, but in Python.  Unless
438    ``-h`` is used, this function returns a dictionary containing
439    information on generated modules and their dependencies on source
440    files.
441
442    You cannot build extension modules with this function, that is,
443    using ``-c`` is not allowed. Use the ``compile`` command instead.
444
445    Examples
446    --------
447    The command ``f2py -m scalar scalar.f`` can be executed from Python as
448    follows.
449
450    .. literalinclude:: ../../source/f2py/code/results/run_main_session.dat
451        :language: python
452
453    """
454    crackfortran.reset_global_f2py_vars()
455    f2pydir = os.path.dirname(os.path.abspath(cfuncs.__file__))
456    fobjhsrc = os.path.join(f2pydir, 'src', 'fortranobject.h')
457    fobjcsrc = os.path.join(f2pydir, 'src', 'fortranobject.c')
458    # gh-22819 -- begin
459    parser = make_f2py_compile_parser()
460    args, comline_list = parser.parse_known_args(comline_list)
461    pyf_files, _ = filter_files("", "[.]pyf([.]src|)", comline_list)
462    # Checks that no existing modulename is defined in a pyf file
463    # TODO: Remove all this when scaninputline is replaced
464    if args.module_name:
465        if "-h" in comline_list:
466            modname = (
467                args.module_name
468            )  # Directly use from args when -h is present
469        else:
470            modname = validate_modulename(
471                pyf_files, args.module_name
472            )  # Validate modname when -h is not present
473        comline_list += ['-m', modname]  # needed for the rest of scaninputline
474    # gh-22819 -- end
475    files, options = scaninputline(comline_list)
476    auxfuncs.options = options
477    capi_maps.load_f2cmap_file(options['f2cmap_file'])
478    postlist = callcrackfortran(files, options)
479    isusedby = {}
480    for plist in postlist:
481        if 'use' in plist:
482            for u in plist['use'].keys():
483                if u not in isusedby:
484                    isusedby[u] = []
485                isusedby[u].append(plist['name'])
486    for plist in postlist:
487        module_name = plist['name']
488        if plist['block'] == 'python module' and '__user__' in module_name:
489            if module_name in isusedby:
490                # if not quiet:
491                usedby = ','.join(f'"{s}"' for s in isusedby[module_name])
492                outmess(
493                    f'Skipping Makefile build for module "{module_name}" '
494                    f'which is used by {usedby}\n')
495    if 'signsfile' in options:
496        if options['verbose'] > 1:
497            outmess(
498                'Stopping. Edit the signature file and then run f2py on the signature file: ')
499            outmess(f"{os.path.basename(sys.argv[0])} {options['signsfile']}\n")
500        return
501    for plist in postlist:
502        if plist['block'] != 'python module':
503            if 'python module' not in options:
504                errmess(
505                    'Tip: If your original code is Fortran source then you must use -m option.\n')
506            raise TypeError('All blocks must be python module blocks but got %s' % (
507                repr(plist['block'])))
508    auxfuncs.debugoptions = options['debug']
509    f90mod_rules.options = options
510    auxfuncs.wrapfuncs = options['wrapfuncs']
511
512    ret = buildmodules(postlist)
513
514    for mn in ret.keys():
515        dict_append(ret[mn], {'csrc': fobjcsrc, 'h': fobjhsrc})
516    return ret
517
518
519def filter_files(prefix, suffix, files, remove_prefix=None):
520    """
521    Filter files by prefix and suffix.
522    """
523    filtered, rest = [], []
524    match = re.compile(prefix + r'.*' + suffix + r'\Z').match
525    if remove_prefix:
526        ind = len(prefix)
527    else:
528        ind = 0
529    for file in [x.strip() for x in files]:
530        if match(file):
531            filtered.append(file[ind:])
532        else:
533            rest.append(file)
534    return filtered, rest
535
536
537def get_prefix(module):
538    p = os.path.dirname(os.path.dirname(module.__file__))
539    return p
540
541
542class CombineIncludePaths(argparse.Action):
543    def __call__(self, parser, namespace, values, option_string=None):
544        include_paths_set = set(getattr(namespace, 'include_paths', []) or [])
545        if option_string == "--include_paths":
546            outmess("Use --include-paths or -I instead of --include_paths which will be removed")
547        if option_string in {"--include-paths", "--include_paths"}:
548            include_paths_set.update(values.split(':'))
549        else:
550            include_paths_set.add(values)
551        namespace.include_paths = list(include_paths_set)
552
553def f2py_parser():
554    parser = argparse.ArgumentParser(add_help=False)
555    parser.add_argument("-I", dest="include_paths", action=CombineIncludePaths)
556    parser.add_argument("--include-paths", dest="include_paths", action=CombineIncludePaths)
557    parser.add_argument("--include_paths", dest="include_paths", action=CombineIncludePaths)
558    parser.add_argument("--freethreading-compatible", dest="ftcompat", action=argparse.BooleanOptionalAction)
559    return parser
560
561def get_newer_options(iline):
562    iline = (' '.join(iline)).split()
563    parser = f2py_parser()
564    args, remain = parser.parse_known_args(iline)
565    ipaths = args.include_paths
566    if args.include_paths is None:
567        ipaths = []
568    return ipaths, args.ftcompat, remain
569
570def make_f2py_compile_parser():
571    parser = argparse.ArgumentParser(add_help=False)
572    parser.add_argument("--dep", action="append", dest="dependencies")
573    parser.add_argument("--backend", choices=['meson', 'distutils'], default='distutils')
574    parser.add_argument("-m", dest="module_name")
575    return parser
576
577def preparse_sysargv():
578    # To keep backwards bug compatibility, newer flags are handled by argparse,
579    # and `sys.argv` is passed to the rest of `f2py` as is.
580    parser = make_f2py_compile_parser()
581
582    args, remaining_argv = parser.parse_known_args()
583    sys.argv = [sys.argv[0]] + remaining_argv
584
585    backend_key = args.backend
586    if MESON_ONLY_VER and backend_key == 'distutils':
587        outmess("Cannot use distutils backend with Python>=3.12,"
588                " using meson backend instead.\n")
589        backend_key = "meson"
590
591    return {
592        "dependencies": args.dependencies or [],
593        "backend": backend_key,
594        "modulename": args.module_name,
595    }
596
597def run_compile():
598    """
599    Do it all in one call!
600    """
601    import tempfile
602
603    # Collect dependency flags, preprocess sys.argv
604    argy = preparse_sysargv()
605    modulename = argy["modulename"]
606    if modulename is None:
607        modulename = 'untitled'
608    dependencies = argy["dependencies"]
609    backend_key = argy["backend"]
610    build_backend = f2py_build_generator(backend_key)
611
612    i = sys.argv.index('-c')
613    del sys.argv[i]
614
615    remove_build_dir = 0
616    try:
617        i = sys.argv.index('--build-dir')
618    except ValueError:
619        i = None
620    if i is not None:
621        build_dir = sys.argv[i + 1]
622        del sys.argv[i + 1]
623        del sys.argv[i]
624    else:
625        remove_build_dir = 1
626        build_dir = tempfile.mkdtemp()
627
628    _reg1 = re.compile(r'--link-')
629    sysinfo_flags = [_m for _m in sys.argv[1:] if _reg1.match(_m)]
630    sys.argv = [_m for _m in sys.argv if _m not in sysinfo_flags]
631    if sysinfo_flags:
632        sysinfo_flags = [f[7:] for f in sysinfo_flags]
633
634    _reg2 = re.compile(
635        r'--((no-|)(wrap-functions|lower|freethreading-compatible)|debug-capi|quiet|skip-empty-wrappers)|-include')
636    f2py_flags = [_m for _m in sys.argv[1:] if _reg2.match(_m)]
637    sys.argv = [_m for _m in sys.argv if _m not in f2py_flags]
638    f2py_flags2 = []
639    fl = 0
640    for a in sys.argv[1:]:
641        if a in ['only:', 'skip:']:
642            fl = 1
643        elif a == ':':
644            fl = 0
645        if fl or a == ':':
646            f2py_flags2.append(a)
647    if f2py_flags2 and f2py_flags2[-1] != ':':
648        f2py_flags2.append(':')
649    f2py_flags.extend(f2py_flags2)
650    sys.argv = [_m for _m in sys.argv if _m not in f2py_flags2]
651    _reg3 = re.compile(
652        r'--((f(90)?compiler(-exec|)|compiler)=|help-compiler)')
653    flib_flags = [_m for _m in sys.argv[1:] if _reg3.match(_m)]
654    sys.argv = [_m for _m in sys.argv if _m not in flib_flags]
655    # TODO: Once distutils is dropped completely, i.e. min_ver >= 3.12, unify into --fflags
656    reg_f77_f90_flags = re.compile(r'--f(77|90)flags=')
657    reg_distutils_flags = re.compile(r'--((f(77|90)exec|opt|arch)=|(debug|noopt|noarch|help-fcompiler))')
658    fc_flags = [_m for _m in sys.argv[1:] if reg_f77_f90_flags.match(_m)]
659    distutils_flags = [_m for _m in sys.argv[1:] if reg_distutils_flags.match(_m)]
660    if not (MESON_ONLY_VER or backend_key == 'meson'):
661        fc_flags.extend(distutils_flags)
662    sys.argv = [_m for _m in sys.argv if _m not in (fc_flags + distutils_flags)]
663
664    del_list = []
665    for s in flib_flags:
666        v = '--fcompiler='
667        if s[:len(v)] == v:
668            if MESON_ONLY_VER or backend_key == 'meson':
669                outmess(
670                    "--fcompiler cannot be used with meson,"
671                    "set compiler with the FC environment variable\n"
672                    )
673            else:
674                from numpy.distutils import fcompiler
675                fcompiler.load_all_fcompiler_classes()
676                allowed_keys = list(fcompiler.fcompiler_class.keys())
677                nv = ov = s[len(v):].lower()
678                if ov not in allowed_keys:
679                    vmap = {}  # XXX
680                    try:
681                        nv = vmap[ov]
682                    except KeyError:
683                        if ov not in vmap.values():
684                            print(f'Unknown vendor: "{s[len(v):]}"')
685                    nv = ov
686                i = flib_flags.index(s)
687                flib_flags[i] = '--fcompiler=' + nv  # noqa: B909
688                continue
689    for s in del_list:
690        i = flib_flags.index(s)
691        del flib_flags[i]
692    assert len(flib_flags) <= 2, repr(flib_flags)
693
694    _reg5 = re.compile(r'--(verbose)')
695    setup_flags = [_m for _m in sys.argv[1:] if _reg5.match(_m)]
696    sys.argv = [_m for _m in sys.argv if _m not in setup_flags]
697
698    if '--quiet' in f2py_flags:
699        setup_flags.append('--quiet')
700
701    # Ugly filter to remove everything but sources
702    sources = sys.argv[1:]
703    f2cmapopt = '--f2cmap'
704    if f2cmapopt in sys.argv:
705        i = sys.argv.index(f2cmapopt)
706        f2py_flags.extend(sys.argv[i:i + 2])
707        del sys.argv[i + 1], sys.argv[i]
708        sources = sys.argv[1:]
709
710    pyf_files, _sources = filter_files("", "[.]pyf([.]src|)", sources)
711    sources = pyf_files + _sources
712    modulename = validate_modulename(pyf_files, modulename)
713    extra_objects, sources = filter_files('', '[.](o|a|so|dylib)', sources)
714    library_dirs, sources = filter_files('-L', '', sources, remove_prefix=1)
715    libraries, sources = filter_files('-l', '', sources, remove_prefix=1)
716    undef_macros, sources = filter_files('-U', '', sources, remove_prefix=1)
717    define_macros, sources = filter_files('-D', '', sources, remove_prefix=1)
718    for i in range(len(define_macros)):
719        name_value = define_macros[i].split('=', 1)
720        if len(name_value) == 1:
721            name_value.append(None)
722        if len(name_value) == 2:
723            define_macros[i] = tuple(name_value)
724        else:
725            print('Invalid use of -D:', name_value)
726
727    # Construct wrappers / signatures / things
728    if backend_key == 'meson':
729        if not pyf_files:
730            outmess('Using meson backend\nWill pass --lower to f2py\nSee https://numpy.org/doc/stable/f2py/buildtools/meson.html\n')
731            f2py_flags.append('--lower')
732            run_main(f" {' '.join(f2py_flags)} -m {modulename} {' '.join(sources)}".split())
733        else:
734            run_main(f" {' '.join(f2py_flags)} {' '.join(pyf_files)}".split())
735
736    # Order matters here, includes are needed for run_main above
737    include_dirs, _, sources = get_newer_options(sources)
738    # Now use the builder
739    builder = build_backend(
740        modulename,
741        sources,
742        extra_objects,
743        build_dir,
744        include_dirs,
745        library_dirs,
746        libraries,
747        define_macros,
748        undef_macros,
749        f2py_flags,
750        sysinfo_flags,
751        fc_flags,
752        flib_flags,
753        setup_flags,
754        remove_build_dir,
755        {"dependencies": dependencies},
756    )
757
758    builder.compile()
759
760
761def validate_modulename(pyf_files, modulename='untitled'):
762    if len(pyf_files) > 1:
763        raise ValueError("Only one .pyf file per call")
764    if pyf_files:
765        pyff = pyf_files[0]
766        pyf_modname = auxfuncs.get_f2py_modulename(pyff)
767        if modulename != pyf_modname:
768            outmess(
769                f"Ignoring -m {modulename}.\n"
770                f"{pyff} defines {pyf_modname} to be the modulename.\n"
771            )
772            modulename = pyf_modname
773    return modulename
774
775def main():
776    if '--help-link' in sys.argv[1:]:
777        sys.argv.remove('--help-link')
778        if MESON_ONLY_VER:
779            outmess("Use --dep for meson builds\n")
780        else:
781            from numpy.distutils.system_info import show_all
782            show_all()
783        return
784
785    if '-c' in sys.argv[1:]:
786        run_compile()
787    else:
788        run_main(sys.argv[1:])
789 
codekingpro/portable-devtools · Team Ai