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