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