codekingpro/portable-devtools
115k
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 