f2py2e.py 27 KB

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