PyForTool
Python-fortran-tool
Loading...
Searching...
No Matches
scripting.py
1"""
2This module contains functions usefull to build scripts around the pyfortool library
3"""
4
5import sys
6from multiprocessing import cpu_count, get_context
7from multiprocessing.managers import BaseManager
8import re
9import shlex
10import os
11import argparse
12import logging
13import traceback
14
15from pyfortool.pyfortool import PYFT
16from pyfortool.tree import Tree
17from pyfortool.util import isint, PYFTError
18from pyfortool import __version__
19
20
21def task(filename):
22 """
23 Function to use on each file
24 :param clsPYFT: PYFT class to use
25 :param filename: file name
26 """
27 global PYFT # noqa: F824
28 global allFileArgs # noqa: F824
29 allArgs, orderedOptions = allFileArgs[filename]
30 try:
31 # Opening and reading of the FORTRAN file
32 with PYFT(filename, filename,
33 parserOptions=getParserOptions(allArgs), verbosity=allArgs.logLevel,
34 wrapH=allArgs.wrapH,
35 enableCache=allArgs.enableCache) as pft:
36
37 # apply the transformation in the order they were specified
38 for arg in orderedOptions:
39 logging.debug('Applying %s on %s', arg, filename)
40 applyTransfo(pft, arg, allArgs,
41 filename if filename == allArgs.plotCentralFile else None)
42 logging.debug(' -> Done')
43
44 # Writing
45 if not allArgs.dryRun:
46 pft.write()
47
48 # Reporting
49 return (0, filename)
50
51 except Exception as exc: # pylint: disable=broad-except
52 logging.error("The following error has occurred in the file %s", filename)
53 # Three-argument form: the single-argument form needs python >= 3.10
54 traceback.print_exception(type(exc), exc, exc.__traceback__, file=sys.stdout)
55 sys.stdout.flush()
56 return (1, filename)
57
58
59class TreeManager(BaseManager):
60 """
61 Manager sharing a single Tree instance between the pyfortool_parallel processes
62 """
63
64
65TreeManager.register('Tree', Tree)
66
67
68def poolInit(cls, afa):
69 """
70 Pool initializer
71 """
72 # After many, many attempts, it seems very difficult (if not impossible)
73 # to do without global variables
74 global PYFT # pylint: disable=global-statement
75 global allFileArgs # pylint: disable=global-statement
76 PYFT = cls
77 allFileArgs = afa
78
79
80def mainParallel(argv=None):
81 """
82 Core of the pyfortool_parallel.py command
83 :param argv: list of arguments (including the 'programm' name in first position)
84 as would be obtained by sys.argv. Or None to use sys.argv
85 """
86 parser = argparse.ArgumentParser(description='Python FORTRAN tool', allow_abbrev=False,
87 epilog="The argument order matters.")
88
89 updateParser(parser, withInput=False, withOutput=False, withXml=False, withPlotCentralFile=True,
90 treeIsOptional=False, nbPar=True, restrictScope=False)
91 commonArgs, getFileArgs = getArgs(parser, argv)
92
93 # The 'fork' start method is requested explicitly. The parallel configuration installed
94 # by PYFT.setParallel below is held in class attributes, and child processes can only
95 # receive it by inheriting the parent memory. Python 3.14 made 'forkserver' the default
96 # on Linux, under which the workers would start from a freshly imported (and therefore
97 # unconfigured) PYFT class.
98 ctx = get_context('fork')
99
100 # Manager to share the Tree instance
101 with TreeManager(ctx=ctx) as manager:
102 # Set-up the Tree instance
103 sharedTree = getDescTree(commonArgs, manager.Tree)
104
105 # Prepare PYFT to be used in parallel (locks taken from the same 'fork' context)
106 PYFT.setParallel(sharedTree, clsLock=ctx.Lock, clsRLock=ctx.RLock)
107 try:
108 # Set-up the processes
109 allFileArgs = {file: getFileArgs(file) for file in sharedTree.getFiles()}
110 logging.info('Executing in parallel on %i files with a maximum of %i processes',
111 len(allFileArgs), commonArgs.nbPar)
112 with ctx.Pool(commonArgs.nbPar, initializer=poolInit,
113 initargs=(PYFT, allFileArgs)) as pool:
114 result = pool.map(task, sharedTree.getFiles())
115
116 # Writting the descTree object
117 sharedTree.toJson(commonArgs.descTree)
118
119 # General error
120 errors = [item[1] for item in result if item[0] != 0]
121 status = len(errors)
122 if status != 0:
123 logging.error('List of files with error:')
124 for error in errors:
125 logging.error(' - %s', error)
126 raise PYFTError(f"Errors have been reported in {status} file(s).")
127 finally:
128 # Unset parallel processing, even on error: the shared tree lives in the
129 # manager process, which is stopped when leaving the 'with' block
130 PYFT.unsetParallel()
131
132
133def main(argv=None):
134 """
135 Core of the pyfortool.py command
136 :param argv: list of arguments (including the 'programm' name in first position)
137 as would be obtained by sys.argv. Or None to use sys.argv
138 """
139 parser = argparse.ArgumentParser(description='Python FORTRAN tool', allow_abbrev=False,
140 epilog="The argument order matters.")
141
142 updateParser(parser, withInput=True, withOutput=True, withXml=True, withPlotCentralFile=False,
143 treeIsOptional=True, nbPar=False, restrictScope=True)
144 args, orderedOptions = getArgs(parser, argv)[1]()
145
146 parserOptions = getParserOptions(args)
147 descTree = getDescTree(args)
148
149 try:
150 # Opening and reading of the FORTRAN file
151 pft = PYFT(args.INPUT, args.OUTPUT, parserOptions=parserOptions,
152 verbosity=args.logLevel, wrapH=args.wrapH, tree=descTree,
153 enableCache=args.enableCache)
154 if args.restrictScope != '':
155 pft = pft.getScopeNode(args.restrictScope)
156
157 # apply the transformation in the order they were specified
158 for arg in orderedOptions:
159 logging.debug('Applying %s on %s', arg, args.INPUT)
160 applyTransfo(pft, arg, args, plotCentralFile=args.INPUT)
161 logging.debug(' -> Done')
162
163 # Writing
164 if descTree is not None:
165 descTree.toJson(args.descTree)
166 if args.xml is not None:
167 pft.mainScope.writeXML(args.xml)
168 if not args.dryRun:
169 pft.mainScope.write()
170
171 # Closing
172 pft.mainScope.close()
173
174 except: # noqa E722
175 # 'exept' everything and re-raise error systematically
176 logging.error("The following error has occurred in the file %s", args.INPUT)
177 raise
178
179
180ARG_UPDATE_CNT = ('--alignContinuation', '--addBeginContinuation',
181 '--removeBeginContinuation',
182 '--emoveALLContinuation')
183
184
185def getArgs(parser, argv):
186 """
187 Parse arguments and interpret the --optsByEnv option
188 :param parser: argparse parser
189 :param argv: list of arguments (including the 'programm' name in first position)
190 as would be obtained by sys.argv. Or None to use sys.argv.
191 :return: a tuple with
192 - an argparse namespace containing common arguments (not using the --optsEnv option)
193 - a function taking a filename as input and returning
194 - an argparse namespace with the common arguments and the ones added by
195 interpreting the --optsEnv option
196 - an ordered list of arguments
197 """
198 if argv is None:
199 argv = sys.argv
200 args = parser.parse_args(argv[1:])
201
202 def getFileArgs(filename=args.INPUT if hasattr(args, 'INPUT') else None):
203 """
204 :param filename: name of source code file
205 :return: argparse namespace to use with this file and
206 a list given the order in which the arguments were provided
207 """
208 # Decode the --optsByEnv option
209 arguments = argv[1:]
210 if args.optsByEnv is not None:
211 extra = ''
212 for line in [] if args.optsByEnv is None else os.environ[args.optsByEnv].split('\n'):
213 if ':=:' in line:
214 if re.match(line.split(':=:')[0], filename):
215 extra = line.split(':=:')[1]
216 else:
217 extra = line
218 index = arguments.index('--optsByEnv')
219 arguments = arguments[:index] + shlex.split(extra) + arguments[index + 2:] # keep order
220
221 # Compute the ordered list
222 updateCnt = False
223 optList = []
224 for arg in arguments:
225 if arg.startswith('--') and arg not in optList:
226 if arg in ARG_UPDATE_CNT:
227 if not updateCnt:
228 updateCnt = True
229 optList.append(arg)
230 else:
231 optList.append(arg)
232
233 return parser.parse_args(arguments), optList
234
235 return args, getFileArgs
236
237
239 """
240 Get the options to use for the fxtran parser
241 :param args: arguments parsed by the argparse parser
242 """
243 if args.parserOption is None:
244 parserOptions = PYFT.DEFAULT_FXTRAN_OPTIONS.copy()
245 else:
246 parserOptions = [el for elements in args.parserOption for el in elements]
247 if args.addIncludes:
248 parserOptions = [opt for opt in parserOptions if opt not in ('-no-include', '-noinclude')]
249 return parserOptions
250
251
252def getDescTree(args, cls=Tree):
253 """
254 get the Tree object built with the parsed arguments
255 :param args: arguments parsed by the argparse parser
256 :param cls: class to use (usefull for manager)
257 :return: a Tree instance
258 """
259 parserOptions = getParserOptions(args)
260 if args.descTree:
261 descTree = cls(tree=args.tree, descTreeFile=args.descTree,
262 parserOptions=parserOptions,
263 wrapH=args.wrapH, verbosity=args.logLevel)
264 else:
265 descTree = None
266 return descTree
267
268
269def updateParser(parser, withInput, withOutput, withXml, withPlotCentralFile, treeIsOptional,
270 nbPar, restrictScope):
271 """
272 Updates an argparse parser with arguments common to all the different tools
273 :param parser: parser in which arguments are added
274 :param withOutput: do we need the INPUT argument
275 :param withOutput: do we need the OUTPUT argument
276 :param withXml: do we need to be able to define an XML output file
277 :param withPlotCentralFile: to add the --plotCentralFile argument
278 :param treeIsOptional: is the --tree argument optional?
279 :param nbPar: number of parallel processes
280 :param restrictScope: can we specify the scope path
281 """
282
283 # ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
284 # IMPORTANT NOTE
285 # Argument order matters but argparse is not able to give the order
286 # Therefore, arguments are processed twice. The first time by argparse to fully decode them.
287 # Then a second pass is made direcly on sys.argv. This mechanism has two implications:
288 # allow_abbrev must be set to False in ArgumentParser
289 # only long argument options are allowed (begining with two dashes)
290 # ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
291 assert not parser.allow_abbrev, 'parser must be created with allow_abbrev=False'
292
293 parser.add_argument('--version', action='version',
294 version='%(prog)s {version}'.format(version=__version__))
295 parser.add_argument('--simplify', default=False, action='store_true',
296 help='After a deletion, recursively deletes the code ' +
297 'and variables that have become useless')
298 parser.add_argument('--logLevel', default='warning',
299 help='Provide logging level. Example --logLevel debug (default is warning)')
300 parser.add_argument('--enableCache', default=False, action='store_true',
301 help='Precompute parent of each xml node and store the result')
302 if nbPar:
303 parser.add_argument('--nbPar', default=cpu_count(), type=int,
304 help='Number of parallel processes, 0 to get as many processes ' +
305 'as the number of cores (default=0)')
306 parser.add_argument('--optsByEnv', default=None, type=str,
307 help='Name of the environment variable containing additional arguments ' +
308 'to use. These arguments are processed after all other arguments. ' +
309 'The variable can contain a multi-lines string. The ' +
310 'variable is read line by line and the last applicable line is ' +
311 'used. A line can take one of these two forms: ' +
312 '1) "FILE_DESCRIPTOR:=:OPTIONS" (where FILE_DESCRIPTOR is a ' +
313 'regular expression to test against the filename. If there ' +
314 'is a match, the OPTIONS can be used for the file) and ' +
315 '2) "OPTIONS" (if the line doesn\'t contain the FILE_DESCRIPTOR ' +
316 'part, it applies to all source code).')
317
318 if restrictScope:
319 parser.add_argument('--restrictScope', default='', type=str, metavar='SCOPEPATH',
320 help="Limit the action to this scope path (SUBROUTINE/FUNCTION/" +
321 "MODULE/TYPE). It is '/'-separated path with each element " +
322 "having the form 'module:<name of the module>', " +
323 "'sub:<name of the subroutine>', " +
324 "'func:<name of the function>' or 'type:<name of the type>'.")
325
326 # Inputs and outputs
327 updateParserInputsOutputs(parser, withInput, withOutput, withXml)
328
329 # fxtran
330 updateParserFxtran(parser)
331
332 # Variables
334
335 # Cosmetics
337
338 # Applications
340
341 # openACC
342 updateParserOpenACC(parser)
343
344 # Checks
345 updateParserChecks(parser)
346
347 # Statements
349
350 # Misc
351 updateParserMisc(parser)
352
353 # Tree
354 updateParserTree(parser, withPlotCentralFile, treeIsOptional)
355
356 # Preprocessot
358
359
360def updateParserInputsOutputs(parser, withInput, withOutput, withXml):
361 """
362 Updates an argparse parser with input/output arguments
363 :param parser: parser in which arguments are added
364 :param withOutput: do we need the INPUT argument
365 :param withOutput: do we need the OUTPUT argument
366 :param withXml: do we need to be able to define an XML output file
367 """
368 gInOut = parser.add_argument_group('Input and output')
369 if withInput:
370 gInOut.add_argument('INPUT', help='FORTRAN input file')
371 if withOutput:
372 gInOut.add_argument('OUTPUT', default=None, help='FORTRAN output file', nargs='?')
373 gInOut.add_argument('--renamefF', default=False, action='store_true',
374 help='Put file extension in upper case')
375 gInOut.add_argument('--renameFf', default=False, action='store_true',
376 help='Put file extension in lower case')
377 if withXml:
378 gInOut.add_argument('--xml', default=None, type=str,
379 help='Output file for xml')
380 gInOut.add_argument('--dryRun', default=False, action='store_true',
381 help='Dry run without writing the FORTRAN file (the xml ' +
382 'is still written')
383
384
386 """
387 Updates an argparse parser with fxtran arguments
388 """
389 gParser = parser.add_argument_group('fxtran parser relative options')
390 gParser.add_argument('--parserOption', nargs='*', action='append',
391 help='Option to pass to fxtran, defaults ' +
392 f'to {PYFT.DEFAULT_FXTRAN_OPTIONS}')
393 gParser.add_argument('--wrapH', default=False, action='store_true',
394 help='Wrap .h file content into a MODULE to enable the reading')
395
396
398 """
399 Updates an argparse parser with variables arguments
400 """
401 gVariables = parser.add_argument_group('Options to deal with variables')
402 gVariables.add_argument('--showVariables', default=False, action='store_true',
403 help='Show the declared variables')
404 gVariables.add_argument('--removeVariable', nargs=2, action='append',
405 metavar=('SCOPEPATH', 'VARNAME'),
406 help="Variable to remove from declaration. The first argument " +
407 "is the SUBROUTINE/FUNCTION/MODULE/TYPE where the variable " +
408 "is declared. It is '/'-separated path with each element having " +
409 "the form 'module:<name of the module>', " +
410 "'sub:<name of the subroutine>', " +
411 "'func:<name of the function>' or 'type:<name of the type>'. " +
412 "The second argument is the variable name")
413 gVariables.add_argument('--attachArraySpecToEntity', default=False, action='store_true',
414 help='Find all T-decl-stmt elements that have a child element ' +
415 'attribute with attribute-N=DIMENSION and move the attribute ' +
416 'into EN-N elements')
417 gVariables.add_argument('--addVariable', nargs=4, action='append',
418 metavar=('SCOPEPATH', 'VARNAME', 'DECLARATION', 'POSITION'),
419 help='Add a variable. First argument is the scope path (as for ' +
420 'the --removeVariable option. The second is the variable ' +
421 'name, the third is the declarative statement to insert, ' +
422 'the fourth is the position (python indexing) the new ' +
423 'variable will have in the calling statment of the ' +
424 'routine (non-integer value for a local variable).')
425 gVariables.add_argument('--addModuleVariable', nargs=3, action='append',
426 metavar=('SCOPEPATH', 'MODULENAME', 'VARNAME'),
427 help='Add a USE statement. The first argument is the scope path ' +
428 '(as for the --removeVariable option). The second is the module ' +
429 'name; the third is the variable name.')
430 gVariables.add_argument('--showUnusedVariables', default=False, action='store_true',
431 help='Show a list of unused variables.')
432 gVariables.add_argument('--removeUnusedLocalVariables',
433 help='Remove unused local variables, excluding some variables (comma-' +
434 'separated list or NONE to exclude nothing).')
435 gVariables.add_argument('--removePHYEXUnusedLocalVariables',
436 help='Remove unused local variables, excluding some variables (comma-' +
437 'separated list or NONE to exclude nothing). This option takes ' +
438 'into account the mnh_expand directives to prevent from ' +
439 'removing useful variables.')
440 gVariables.add_argument('--addExplicitArrayBounds', action='store_true',
441 help='Adds explicit bounds to arrays that already have parentheses.')
442 gVariables.add_argument('--addArrayParentheses', action='store_true',
443 help='Adds parentheses to arrays (A => A(:))')
444 gVariables.add_argument('--modifyAutomaticArrays', metavar="DECL#START#END",
445 help='Transform all automatic arrays declaration using the templates.' +
446 ' The DECL part of the template will replace the declaration ' +
447 'statement, the START part will be inserted as the first ' +
448 'executable statement while the END part will be inserted as ' +
449 'the last executable statement. Each part ' +
450 'of the template can use the following place holders: ' +
451 '"{doubledotshape}", "{shape}", "{lowUpList}", "{name}" and ' +
452 '"{type}" which are, respectively modified into ' +
453 '":, :, :", "I, I:J, 0:I", "1, I, I, J, 0, I", "A", "REAL" ' +
454 'if the original declaration statement ' +
455 'was "A(I, I:J, 0:I)". For example, the template ' +
456 '"{type}, DIMENSION({doubledotshape}), ALLOCATABLE :: ' +
457 '{name}#ALLOCATE({name}({shape}))#DEALLOCATE({name})"' +
458 'will replace automatic arrays by allocatables.')
459 gVariables.add_argument('--replaceAutomaticWithAllocatable', action='store_true',
460 help='Replace all automatic arrays with allocatable arrays.')
461 gVariables.add_argument('--addArgInTree', default=None, action='append', nargs=3,
462 metavar=('VARNAME', 'DECLARATION', 'POSITION'),
463 help='Add an argument variable. The first argument is the variable ' +
464 'name, the second one is the declarative statement to insert, ' +
465 'the third one is the position (python indexing) the new ' +
466 'variable will have in the calling statement of the ' +
467 'routine. Needs the --stopScopes argument')
468 gVariables.add_argument('--addONLY', default=False, action='store_true',
469 help='Add missing ONLY clause to USE statements.')
470
471
473 """
474 Updates an argparse parser with cosmetics arguments
475 """
476 gCosmetics = parser.add_argument_group('Cosmetics options')
477 gCosmetics.add_argument('--upperCase', default=False, action='store_true',
478 help='Put FORTRAN code in upper case letters')
479 gCosmetics.add_argument('--lowerCase', default=False, action='store_true',
480 help='Put FORTRAN code in lower case letters')
481 gCosmetics.add_argument('--changeIfStatementsInIfConstructs', default=False,
482 action='store_true',
483 help='Find all if-statement and convert it to if-then-statement')
484 gCosmetics.add_argument('--indent', default=False, action='store_true',
485 help='Correct indentation')
486 gCosmetics.add_argument('--removeIndent', default=False, action='store_true',
487 help='Remove indentation')
488 gCosmetics.add_argument('--removeEmptyLines', default=False, action='store_true',
489 help='Remove empty lines')
490 gCosmetics.add_argument('--removeComments', default=False, action='store_true',
491 help='Remove comments')
492 gCosmetics.add_argument('--updateSpaces', default=False, action='store_true',
493 help='Updates spaces around operators, commas, parenthesis and ' +
494 'at the end of line')
495 gCosmetics.add_argument('--alignContinuation', default=False, action='store_true',
496 help='Align the beginings of continued lines')
497 gCosmetics.add_argument('--addBeginContinuation', default=False, action='store_true',
498 help='Add missing continuation characters (\'&\') at the ' +
499 'begining of lines')
500 gCosmetics.add_argument('--removeBeginContinuation', default=False, action='store_true',
501 help='Remove continuation characters (\'&\') at the begining of lines')
502 gCosmetics.add_argument('--removeALLContinuation', default=False, action='store_true',
503 help='Remove all continuation characters(\'&\')')
504 gCosmetics.add_argument('--prettify', default=False, action='store_true',
505 help='Prettify the source code (indentation, spaces...)')
506 gCosmetics.add_argument('--minify', default=False, action='store_true',
507 help='Simplify the source code (indentation, spaces...)')
508 gCosmetics.add_argument('--removeEmptyCONTAINS', default=False, action='store_true',
509 help='Remove useless CONTAINS statements')
510 gCosmetics.add_argument('--formatModuleUse', default=False, action='store_true',
511 help='Order USE declarations by module type (MODD_, MODE_, ' +
512 'MODI_, MODN_) and alphabetically within each group')
513
514
516 """
517 Updates an argparse parser with applications arguments
518 """
519 gApplications = parser.add_argument_group('Options to apply upper level transformation')
520 gApplications.add_argument('--deleteDrHook', default=False, action='store_true',
521 help='Delete DR HOOK use')
522 gApplications.add_argument('--addDrHook', default=False, action='store_true',
523 help='Add DR HOOK')
524 gApplications.add_argument('--deleteBudgetDDH', default=False, action='store_true',
525 help='Delete Budget/DDH use')
526 gApplications.add_argument('--deleteRoutineCallsMesoNHGPU', default=False, action='store_true',
527 help='Delete parts of the code not compatible with MesoNH-OpenACC' +
528 'such as OCND2 blocks')
529 gApplications.add_argument('--splitModuleRoutineFile', default=False, action='store_true',
530 help='Split a file')
531 gApplications.add_argument('--deleteNonColumnCallsPHYEX', default=False, action='store_true',
532 help='Delete call to PHYEX routines that needs information on ' +
533 'horizontal points (multiple column dependency')
534 gApplications.add_argument('--removeIJDim', default=False, action='store_true',
535 help='Remove I and J dimensions (1, KLON). ' +
536 'Needs the --stopScopes argument.')
537 gApplications.add_argument('--expandAllArraysPHYEX', default=False, action='store_true',
538 help='Expand all array syntax (computing and where block) ' +
539 'using PHYEX conventions')
540 gApplications.add_argument('--expandAllArraysPHYEXConcurrent', default=False,
541 action='store_true',
542 help='Expand all array syntax with DO CONCURRENT loops ' +
543 '(computing and where block) using PHYEX conventions')
544 gApplications.add_argument('--expandAllArrays', default=False, action='store_true',
545 help='Expand all array syntax (computing and where block) ' +
546 'using mnh directives if present')
547 gApplications.add_argument('--expandAllArraysConcurrent', default=False, action='store_true',
548 help='Expand all array syntax with DO CONCURRENT loops ' +
549 '(computing and where block) using mnh directives if present')
550 gApplications.add_argument('--inlineContainedSubroutinesPHYEX', default=False,
551 action='store_true',
552 help='Inline containted subroutines in main routine, using ' +
553 'PHYEX conventions')
554 gApplications.add_argument('--addStack', metavar='MODEL', type=str,
555 help='Add local arrays to the stack. The argument is the ' +
556 'the model name in which stack must be added ("AROME" ' +
557 'or "MESONH"). Needs the --stopScopes argument for AROME.')
558 gApplications.add_argument('--addIncludes', default=False, action='store_true',
559 help='Add .h includes in the file and remove the INCLUDE statement')
560 gApplications.add_argument('--addSubmodulePHYEX', default=False, action='store_true',
561 help='Add SUBMODULE and INTERFACE of subroutines in PHYEX')
562 gApplications.add_argument('--mnhExpand', default=False, action='store_true',
563 help='Apply the mnh_expand directives with DO loops')
564 gApplications.add_argument('--mnhExpandConcurrent', default=False, action='store_true',
565 help='Apply the mnh_expand directives with DO CONCURRENT loops')
566 gApplications.add_argument('--addMPPDB_CHECKS', default=False, action='store_true',
567 help='Add MPPDB_CHEKS bit-repro checking routines of MesoNH for ' +
568 'all in and inout arrays in subroutines')
569 gApplications.add_argument('--addPrints', default=False, action='store_true',
570 help='Add Prints of min/maxval and shape of all in, out, inout ' +
571 'arguments of all scopes')
572 gApplications.add_argument('--shumanFUNCtoCALL', default=False, action='store_true',
573 help='Transform shuman functions to call statements ' +
574 '(legacy version)')
575 gApplications.add_argument('--shugradTransfo', default=False, action='store_true',
576 help='Transform shuman functions to call statements')
577 gApplications.add_argument('--mathFunctoBRFunc', default=False, action='store_true',
578 help='Convert intrinsic math functions **, LOG, ATAN, **2, **3, ' +
579 '**4, EXP, COS, SIN, ATAN2 into a self defined function BR_ ' +
580 'for MesoNH bit-repro.')
581 gApplications.add_argument('--convertTypesInCompute', default=False, action='store_true',
582 help='Use single variable instead of variable contained in ' +
583 'structure in compute statement for optimization issue ')
584 gApplications.add_argument('--buildModi', default=False, action='store_true',
585 help='Builds the corresponding modi_ file')
586 gApplications.add_argument('--removeExtraDOinMnhDoConcurrent', default=False,
587 action='store_true',
588 help='Remove DO and ENDDO instructions inside !$mnh_do_concurrent')
589 gApplications.add_argument('--convertuseModuleToIncludes', default=False,
590 action='store_true',
591 help='Convert USE MODULE, ONLY: ROUTINE to #include routine.intfb.h')
592
593
595 """
596 Updates an argparse parser with openACC arguments
597 """
598 gOpenACC = parser.add_argument_group('OpenACC')
599 gOpenACC.add_argument('--addACCData', default=False, action='store_true',
600 help='Add !$acc data present and !$acc end data directives')
601 gOpenACC.add_argument('--addACCRoutineSeq', default=False, action='store_true',
602 help='Add "!$acc routine seq" to routines under stopScopes')
603 gOpenACC.add_argument('--craybyPassDOCONCURRENT', default=False, action='store_true',
604 help='remove acc loop independant collapse for BR_ fonctions and ' +
605 'mnh_undef(OPENACC) macro' +
606 ' use DO CONCURRENT with mnh_undef(LOOP)')
607 gOpenACC.add_argument('--removeACC', default=False, action='store_true',
608 help='remove all ACC directives')
609 gOpenACC.add_argument('--removebyPassDOCONCURRENT', default=False, action='store_true',
610 help='remove macro !$mnh_(un)def(OPENACC) and !$mnh_(un)def(LOOP) ' +
611 'directives')
612 gOpenACC.add_argument('--buildACCTypeHelpers', default=False, action='store_true',
613 help='build module files containing helpers to copy user ' +
614 'type structures')
615 gOpenACC.add_argument('--allocatetoHIP', default=False, action='store_true',
616 help='convert (DE)ALLOCATE to (DE)ALLOCATE_HIP on variables only sent ' +
617 'to the GPU via acc enter data copyin/create (for GPU AMD MI250X)')
618
619
621 """
622 Updates an argparse parser with checks arguments
623 """
624
625 gChecks = parser.add_argument_group('Check options')
626 gChecks.add_argument('--checkIMPLICIT', choices={'Warn', 'Err'}, default=None,
627 help='Send a warning or raise an error if the "IMPLICIT NONE" ' +
628 'is missing')
629 gChecks.add_argument('--checkINTENT', choices={'Warn', 'Err'}, default=None,
630 help='Send a warning or raise an error if the "INTENT" ' +
631 'attribute is missing for a dummy argument')
632 gChecks.add_argument('--checkOpInCall', choices={'Warn', 'Err'}, default=None,
633 help='Send a warning or raise an error if a call argument is an '
634 'operation.')
635 gChecks.add_argument('--checkUnusedLocalVar', choices={'Warn', 'Err'}, default=None,
636 help='Send a warning or raise an error if some local '
637 'variables are unused.')
638 gChecks.add_argument('--checkPHYEXUnusedLocalVar', choices={'Warn', 'Err'}, default=None,
639 help='Send a warning or raise an error if some local '
640 'variables are unused (excluding variables needed '
641 'for mnh_expand directives).')
642 gChecks.add_argument('--checkEmptyParensInCall', choices={'Warn', 'Err'}, default=None,
643 help='Send a warning or raise an error if a call argument is an '
644 'array with empty parens.')
645 gChecks.add_argument('--checkONLY', choices={'Warn', 'Err'}, default=None,
646 help='Send a warning or raise an error if a USE statement is not '
647 'followed by an ONLY clause.')
648 gChecks.add_argument('--checkKeyDim', choices={'Warn', 'Err'}, default=None,
649 help='Send a warning or raise an error if MERGE-based array '
650 'dimensions are inconsistent across scopes.')
651 gChecks.add_argument('--checkTopLevelUSE', choices={'Warn', 'Err'}, default=None,
652 help='Send a warning or raise an error if a USE statement in '
653 'top-level module is present')
654
655
657 """
658 Updates an argparse parser with statements arguments
659 """
660
661 gStatement = parser.add_argument_group('Statements options')
662 gStatement.add_argument('--removeCall', action='append',
663 help="Call to remove from the source code. The argument " +
664 "is the subprogram name")
665 gStatement.add_argument('--removePrints', default=False, action='store_true',
666 help="Remove print statements from the source code.")
667 gStatement.add_argument('--inlineContainedSubroutines', default=False, action='store_true',
668 help='Inline containted subroutines in main routine')
669 gStatement.add_argument('--setFalseIfStmt', default=None,
670 help='Replace this value by .FALSE. in if statements')
671 gStatement.add_argument('--checkEmptyParensInMnhExpand', choices={'Warn', 'Err'}, default=None,
672 help='Send a warning or raise an error if an empty parens ' +
673 'is found in an mnh_expand directive.')
674
675
677 """
678 Updates an argparse parser with misc arguments
679 """
680 gMisc = parser.add_argument_group('Miscellaneous')
681 gMisc.add_argument('--showScopes', default=False, action='store_true',
682 help='Show the different scopes found in the source code')
683 gMisc.add_argument('--empty', default=False, action='store_true',
684 help='Empty the different scopes')
685
686
687def updateParserTree(parser, withPlotCentralFile, treeIsOptional):
688 """
689 Updates an argparse parser with statements arguments
690 :param withPlotCentralFile: to add the --plotCentralFile argumen
691 :param treeIsOptional: is the --tree argument optional?
692 """
693 gTree = parser.add_argument_group('Tree')
694 gTree.add_argument('--tree', default=None, action='append', required=not treeIsOptional,
695 help='Directories where source code must be searched for')
696 gTree.add_argument('--descTree', default=None, required=not treeIsOptional,
697 help='File to write and/or read the description of the tree.')
698 if withPlotCentralFile:
699 gTree.add_argument('--plotCentralFile', default=None, type=str,
700 help='Central file of the plot')
701 gTree.add_argument('--plotCompilTree', default=None,
702 help='File name for compilation dependency graph (.dot or image extension)')
703 gTree.add_argument('--plotExecTree', default=None,
704 help='File name for execution dependency graph (.dot or image extension)')
705 gTree.add_argument('--plotMaxUpper', default=None, type=int,
706 help='Maximum number of upper elements in the plot tree')
707 gTree.add_argument('--plotMaxLower', default=None, type=int,
708 help='Maximum number of lower elements in the plot tree')
709 gTree.add_argument('--stopScopes', default=None, type=str,
710 help='#-separated list of scopes ' +
711 'where the recursive inclusion of an argument variable ' +
712 'must stop (needed for some transformations).')
713
714
716 """
717 Updates an argparse parser with statements arguments
718 """
719 gCpp = parser.add_argument_group('Preprocessor')
720 gCpp.add_argument('--applyCPPifdef', nargs='*', action='append',
721 help="This option is followed by the list of defined or undefined " +
722 "CPP keys. " +
723 "All #ifdef and #ifndef concerning these keys are evaluated. " +
724 "Undefined keys are preceded by a percentage sign.")
725
726
727def applyTransfo(pft, arg, args, plotCentralFile):
728 """
729 Apply transformation on a PYFT instance
730 :param pft: PYFT instance
731 :param arg: argument to deal with
732 :param args: parsed argparsed arguments
733 :param plotCentralFile: central file for plots
734 """
735 simplify = {'simplify': args.simplify}
736 parserOptions = getParserOptions(args)
737 stopScopes = args.stopScopes.split('#') if args.stopScopes is not None else None
738
739 # File name manipulations
740 applyTransfoFileName(pft, arg)
741
742 # Variables
743 applyTransfoVariables(pft, arg, args, simplify, parserOptions, stopScopes)
744
745 # Applications
746 applyTransfoApplications(pft, arg, args, simplify, parserOptions, stopScopes)
747
748 # OpenACC
749 applyTransfoOpenACC(pft, arg, args, stopScopes)
750
751 # Cosmetics
752 applyTransfoCosmetics(pft, arg, args)
753
754 # Checks
755 applyTransfoChecks(pft, arg, args, stopScopes)
756
757 # Statements
758 applyTransfoStatements(pft, arg, args, simplify)
759
760 # Misc
761 applyTransfoMisc(pft, arg, args, simplify)
762
763 # Tree
764 applyTransfoTree(pft, arg, args, plotCentralFile)
765
766 # Preprocessor
767 applyTransfoPreprocessor(pft, arg, args)
768
769
771 """
772 Apply file name transformations on a PYFT instance
773 :param pft: PYFT instance
774 :param arg: argument to deal with
775 :param args: parsed argparsed arguments
776 """
777
778 # File name manipulations
779 if arg == '--renamefF':
780 pft.renameUpper()
781 elif arg == '--renameFf':
782 pft.renameLower()
783
784
785def applyTransfoVariables(pft, arg, args, simplify, parserOptions, stopScopes):
786 """
787 Apply variables transformations on a PYFT instance
788 :param pft: PYFT instance
789 :param arg: argument to deal with
790 :param args: parsed argparsed arguments
791 :param simplify: kwargs to simplify
792 :param parserOptions: fxtran parser options
793 :param stopScopes: upper limit in call tree for some transformations
794 """
795 if arg == '--showVariables':
796 pft.varList.showVarList()
797 elif arg == '--attachArraySpecToEntity':
798 pft.attachArraySpecToEntity()
799 elif arg == '--removeVariable':
800 pft.removeVar(args.removeVariable, **simplify)
801 elif arg == '--addVariable':
802 pft.addVar([[v[0], v[1], v[2], (int(v[3]) if isint(v[3]) else None)]
803 for v in args.addVariable])
804 elif arg == '--addModuleVariable':
805 pft.addModuleVar([[v[0], v[1], v[2]] for v in args.addModuleVariable])
806 elif arg == '--showUnusedVariables':
807 pft.showUnusedVar()
808 elif arg == '--removeUnusedLocalVariables':
809 pft.removeUnusedLocalVar(
810 [item.strip() for item in args.removeUnusedLocalVariables.split(',')]
811 if args.removeUnusedLocalVariables != 'NONE' else None, **simplify)
812 elif arg == '--removePHYEXUnusedLocalVariables':
813 pft.removePHYEXUnusedLocalVar(
814 [item.strip() for item in args.removePHYEXUnusedLocalVariables.split(',')]
815 if args.removePHYEXUnusedLocalVariables != 'NONE' else None, **simplify)
816 elif arg == '--addExplicitArrayBounds':
817 pft.addExplicitArrayBounds()
818 elif arg == '--addArrayParentheses':
819 pft.addArrayParentheses()
820 elif arg == '--modifyAutomaticArrays':
821 pft.modifyAutomaticArrays(*(args.modifyAutomaticArrays.split('#')))
822 elif arg == '--replaceAutomaticWithAllocatable':
823 pft.modifyAutomaticArrays(
824 "{type}, DIMENSION({doubledotshape}), ALLOCATABLE :: {name}",
825 "ALLOCATE({name}({shape}))", "DEALLOCATE({name})")
826 elif arg == '--addArgInTree':
827 for varName, declStmt, pos in args.addArgInTree:
828 pft.addArgInTree(varName, declStmt, int(pos), stopScopes,
829 parserOptions=parserOptions,
830 wrapH=args.wrapH)
831 elif arg == '--addONLY':
832 pft.addONLY(parserOptions=parserOptions, wrapH=args.wrapH)
833
834
835def applyTransfoApplications(pft, arg, args, simplify, parserOptions, stopScopes):
836 """
837 Apply applications transformations on a PYFT instance
838 :param pft: PYFT instance
839 :param arg: argument to deal with
840 :param args: parsed argparsed arguments
841 :param simplify: kwargs to simplify
842 :param parserOptions: fxtran parser options
843 :param stopScopes: upper limit in call tree for some transformations
844 """
845 if arg == '--addStack':
846 pft.addStack(args.addStack, stopScopes,
847 parserOptions=parserOptions,
848 wrapH=args.wrapH)
849 elif arg == '--deleteDrHook':
850 pft.deleteDrHook(**simplify)
851 elif arg == '--addDrHook':
852 pft.addDrHook()
853 elif arg == '--deleteBudgetDDH':
854 pft.deleteBudgetDDH(**simplify)
855 elif arg == '--deleteRoutineCallsMesoNHGPU':
856 pft.deleteRoutineCallsMesoNHGPU(**simplify)
857 elif arg == '--deleteNonColumnCallsPHYEX':
858 pft.deleteNonColumnCallsPHYEX(**simplify)
859 elif arg == '--addMPPDB_CHECKS':
860 pft.addMPPDB_CHECKS()
861 elif arg == '--addPrints':
862 pft.addMPPDB_CHECKS(printsMode=True)
863 elif arg == '--addSubmodulePHYEX':
864 pft.addSubmodulePHYEX()
865 # mnhExpand must be before inlineContainedSubroutines as inlineContainedSubroutines
866 # can change variable names used by mnh_expand directives
867 assert not (args.mnhExpand and args.mnhExpandConcurrent), \
868 "Only one of --mnhExpand and --mnhExpandConcurrent"
869 if arg == '--mnhExpand':
870 pft.removeArraySyntax(everywhere=False, addAccIndependentCollapse=False)
871 elif arg == '--mnhExpandConcurrent':
872 pft.removeArraySyntax(concurrent=True, everywhere=False)
873 elif arg == '--inlineContainedSubroutines':
874 pft.inlineContainedSubroutines(**simplify)
875 elif arg == '--inlineContainedSubroutinesPHYEX':
876 pft.inlineContainedSubroutinesPHYEX(**simplify)
877 elif arg == '--expandAllArrays':
878 pft.removeArraySyntax()
879 elif arg == '--expandAllArraysConcurrent':
880 pft.removeArraySyntax(concurrent=True)
881 elif arg == '--expandAllArraysPHYEX':
882 pft.expandAllArraysPHYEX()
883 elif arg == '--expandAllArraysPHYEXConcurrent':
884 pft.expandAllArraysPHYEX(concurrent=True)
885 elif arg == '--removeIJDim':
886 pft.removeIJDim(stopScopes,
887 parserOptions=parserOptions,
888 wrapH=args.wrapH, **simplify)
889 elif arg == '--shumanFUNCtoCALL':
890 pft.shumanFUNCtoCALL()
891 elif arg == '--shugradTransfo':
892 pft.shugradTransfo()
893 elif arg == '--buildACCTypeHelpers':
894 pft.buildACCTypeHelpers()
895 elif arg == '--mathFunctoBRFunc':
896 pft.mathFunctoBRFunc()
897 elif arg == '--convertTypesInCompute':
898 pft.convertTypesInCompute()
899 elif arg == '--buildModi':
900 pft.buildModi()
901 elif arg == '--splitModuleRoutineFile':
902 pft.splitModuleRoutineFile()
903 elif arg == '--removeExtraDOinMnhDoConcurrent':
904 pft.removeExtraDOinMnhDoConcurrent()
905 elif arg == '--convertuseModuleToIncludes':
906 pft.convertuseModuleToIncludes()
907
908
909def applyTransfoOpenACC(pft, arg, args, stopScopes): # pylint: disable=unused-argument
910 """
911 Apply openACC transformations on a PYFT instance
912 :param pft: PYFT instance
913 :param arg: argument to deal with
914 :param args: parsed argparsed arguments
915 :param stopScopes: upper limit in call tree for some transformations
916 """
917 if arg == '--addACCData':
918 pft.addACCData()
919 elif arg == '--craybyPassDOCONCURRENT':
920 pft.craybyPassDOCONCURRENT()
921 elif arg == '--removebyPassDOCONCURRENT':
922 pft.removebyPassDOCONCURRENT()
923 elif arg == '--addACCRoutineSeq':
924 pft.addACCRoutineSeq(stopScopes)
925 elif arg == '--removeACC':
926 pft.removeACC()
927 elif arg == '--allocatetoHIP':
928 pft.allocatetoHIP()
929
930
931def applyTransfoCosmetics(pft, arg, args):
932 """
933 Apply cosmetics transformations on a PYFT instance
934 :param pft: PYFT instance
935 :param arg: argument to deal with
936 :param args: parsed argparsed arguments
937 """
938 if arg == '--upperCase':
939 pft.upperCase()
940 elif arg == '--lowerCase':
941 pft.lowerCase()
942 elif arg == '--changeIfStatementsInIfConstructs':
943 pft.changeIfStatementsInIfConstructs()
944 elif arg == '--indent':
945 pft.indent()
946 elif arg == '--removeIndent':
947 pft.indent(indentProgramunit=0, indentBranch=0)
948 elif arg == '--removeEmptyLines':
949 pft.removeEmptyLines()
950 elif arg == '--removeComments':
951 pft.removeComments()
952 elif arg == '--updateSpaces':
953 pft.updateSpaces()
954 elif arg in ARG_UPDATE_CNT:
955 pft.updateContinuation(align=args.alignContinuation,
956 addBegin=args.addBeginContinuation,
957 removeBegin=args.removeBeginContinuation,
958 removeALL=args.removeALLContinuation)
959 elif arg == '--prettify':
960 pft.indent()
961 pft.upperCase()
962 pft.removeEmptyLines()
963 pft.updateSpaces()
964 pft.updateContinuation()
965 elif arg == '--minify':
966 pft.indent(indentProgramunit=0, indentBranch=0)
967 pft.upperCase()
968 pft.removeComments()
969 pft.removeEmptyLines()
970 pft.updateSpaces()
971 pft.updateContinuation(align=False, removeALL=True, addBegin=False)
972 elif arg == '--removeEmptyCONTAINS':
973 pft.removeEmptyCONTAINS()
974 elif arg == '--formatModuleUse':
975 pft.formatModuleUse()
976
977
978def applyTransfoChecks(pft, arg, args, stopScopes):
979 """
980 Apply checks transformations on a PYFT instance
981 :param pft: PYFT instance
982 :param arg: argument to deal with
983 :param args: parsed argparsed arguments
984 :param stopScopes: list of scope paths or None
985 """
986 if arg == '--checkIMPLICIT':
987 pft.checkImplicitNone(args.checkIMPLICIT == 'Err')
988 elif arg == '--checkINTENT':
989 pft.checkIntent(args.checkINTENT == 'Err')
990 elif arg == '--checkOpInCall':
991 pft.checkOpInCall(args.checkOpInCall == 'Err')
992 elif arg == '--checkUnusedLocalVar':
993 pft.checkUnusedLocalVar(args.checkUnusedLocalVar == 'Err')
994 elif arg == '--checkPHYEXUnusedLocalVar':
995 pft.checkPHYEXUnusedLocalVar(args.checkPHYEXUnusedLocalVar == 'Err')
996 elif arg == '--checkEmptyParensInCall':
997 pft.checkEmptyParensInCall(args.checkEmptyParensInCall == 'Err')
998 elif arg == '--checkONLY':
999 pft.checkONLY(args.checkONLY == 'Err')
1000 elif arg == '--checkKeyDim':
1001 pft.checkKeyDimConsistency(args.checkKeyDim == 'Err',
1002 stopScopes=stopScopes)
1003 elif arg == '--checkTopLevelUSE':
1004 pft.checkTopLevelUSE(args.checkTopLevelUSE == 'Err')
1005 elif arg == '--checkEmptyParensInMnhExpand':
1006 pft.checkEmptyParensInMnhExpand(args.checkEmptyParensInMnhExpand == 'Err')
1007
1008
1009def applyTransfoStatements(pft, arg, args, simplify):
1010 """
1011 Apply statements transformations on a PYFT instance
1012 :param pft: PYFT instance
1013 :param arg: argument to deal with
1014 :param args: parsed argparsed arguments
1015 :param simplify: kwargs to simplify
1016 """
1017 if arg == '--removeCall':
1018 for rc in args.removeCall:
1019 pft.removeCall(rc, **simplify)
1020 elif arg == '--removePrints':
1021 pft.removePrints(**simplify)
1022 elif arg == '--setFalseIfStmt':
1023 pft.setFalseIfStmt(args.setFalseIfStmt, **simplify)
1024
1025
1026def applyTransfoMisc(pft, arg, args, simplify): # pylint: disable=unused-argument
1027 """
1028 Apply misc transformations on a PYFT instance
1029 :param pft: PYFT instance
1030 :param arg: argument to deal with
1031 :param args: parsed argparsed arguments
1032 :param simplify: kwargs to simplify
1033 """
1034 if arg == '--showScopes':
1035 pft.showScopesList()
1036 elif arg == '--empty':
1037 pft.empty(**simplify)
1038
1039
1040def applyTransfoTree(pft, arg, args, plotCentralFile):
1041 """
1042 Apply tree transformations on a PYFT instance
1043 :param pft: PYFT instance
1044 :param arg: argument to deal with
1045 :param args: parsed argparsed arguments
1046 :param plotCentralFile: central file for plots
1047 """
1048 if arg == '--plotCompilTree' and plotCentralFile is not None:
1049 pft.tree.plotCompilTreeFromFile(plotCentralFile, args.plotCompilTree,
1050 args.plotMaxUpper, args.plotMaxLower)
1051 elif arg == '--plotExecTree' and plotCentralFile is not None:
1052 pft.tree.plotExecTreeFromFile(plotCentralFile, args.plotExecTree,
1053 args.plotMaxUpper, args.plotMaxLower)
1054
1055
1056def applyTransfoPreprocessor(pft, arg, args):
1057 """
1058 Apply preprocessor transformations on a PYFT instance
1059 :param pft: PYFT instance
1060 :param arg: argument to deal with
1061 :param args: parsed argparsed arguments
1062 """
1063 if arg == '--applyCPPifdef':
1064 pft.applyCPPifdef([k for aList in args.applyCPPifdef for k in aList])
1065
1066
1067if __name__ == "__main__":
1068 main()
applyTransfoApplications(pft, arg, args, simplify, parserOptions, stopScopes)
Definition scripting.py:835
updateParserVariables(parser)
Definition scripting.py:397
applyTransfoStatements(pft, arg, args, simplify)
applyTransfoTree(pft, arg, args, plotCentralFile)
applyTransfoPreprocessor(pft, arg, args)
updateParserCosmetics(parser)
Definition scripting.py:472
applyTransfoVariables(pft, arg, args, simplify, parserOptions, stopScopes)
Definition scripting.py:785
updateParserInputsOutputs(parser, withInput, withOutput, withXml)
Definition scripting.py:360
updateParserApplications(parser)
Definition scripting.py:515
updateParserFxtran(parser)
Definition scripting.py:385
updateParserChecks(parser)
Definition scripting.py:620
updateParserOpenACC(parser)
Definition scripting.py:594
updateParserPreprocessor(parser)
Definition scripting.py:715
mainParallel(argv=None)
Definition scripting.py:80
applyTransfoOpenACC(pft, arg, args, stopScopes)
Definition scripting.py:909
updateParserStatements(parser)
Definition scripting.py:656
getArgs(parser, argv)
Definition scripting.py:185
applyTransfo(pft, arg, args, plotCentralFile)
Definition scripting.py:727
getDescTree(args, cls=Tree)
Definition scripting.py:252
applyTransfoMisc(pft, arg, args, simplify)
updateParser(parser, withInput, withOutput, withXml, withPlotCentralFile, treeIsOptional, nbPar, restrictScope)
Definition scripting.py:270
applyTransfoCosmetics(pft, arg, args)
Definition scripting.py:931
applyTransfoFileName(pft, arg)
Definition scripting.py:770
updateParserTree(parser, withPlotCentralFile, treeIsOptional)
Definition scripting.py:687
applyTransfoChecks(pft, arg, args, stopScopes)
Definition scripting.py:978
poolInit(cls, afa)
Definition scripting.py:68
updateParserMisc(parser)
Definition scripting.py:676