From 8a4bb01b58e542e020e1361807378b3a3a195392 Mon Sep 17 00:00:00 2001 From: Patrick Seewald Date: Fri, 26 Aug 2016 07:38:50 +0000 Subject: [PATCH] Prettify: more flexible command line interface Can now be installed as 'fprettify' and can be more easily invoked from command line and can be integrated into editors. - Fixes in reformatter for better editor support - errors / warnings to stderr and can use stdin / stdout - remove unnecessary checks - warnings can be deactivated - backup is optional - prettify has its own directory svn-origin-rev: 17300 --- makefiles/Makefile | 10 +- tools/{ => prettify}/formatting/__init__.py | 0 .../formatting/normalizeFortranFile.py | 37 ++-- .../formatting/reformatFortranFile.py | 57 +++-- tools/{ => prettify}/formatting/replacer.py | 2 +- tools/{ => prettify}/formatting/selftest.py | 0 tools/{prettify.py => prettify/fprettify.py} | 203 ++++++++++-------- tools/prettify/setup.py | 15 ++ 8 files changed, 195 insertions(+), 129 deletions(-) rename tools/{ => prettify}/formatting/__init__.py (100%) rename tools/{ => prettify}/formatting/normalizeFortranFile.py (97%) rename tools/{ => prettify}/formatting/reformatFortranFile.py (95%) rename tools/{ => prettify}/formatting/replacer.py (96%) rename tools/{ => prettify}/formatting/selftest.py (100%) rename tools/{prettify.py => prettify/fprettify.py} (69%) create mode 100755 tools/prettify/setup.py diff --git a/makefiles/Makefile b/makefiles/Makefile index e8c6c01f55..d70e4949fe 100644 --- a/makefiles/Makefile +++ b/makefiles/Makefile @@ -181,7 +181,7 @@ test: testbg: @echo "testing: $(ONEVERSION) : full log in $(TSTDIR)/regtest.log " - @$(TOOLSRC)/regtesting/do_regtest -nobuild -nosvn -arch $(ARCH) -version $(ONEVERSION) -cp2kdir ../../../ $(TESTOPTS) >& $(TSTDIR)/regtest.log + @$(TOOLSRC)/regtesting/do_regtest -nobuild -nosvn -arch $(ARCH) -version $(ONEVERSION) -cp2kdir ../../../ $(TESTOPTS) >& $(TSTDIR)/regtest.log @cat `grep 'regtesting location error_summary file:' $(TSTDIR)/regtest.log | awk '{print $$NF}'` @cat `grep 'regtesting location summary file:' $(TSTDIR)/regtest.log | awk '{print $$NF}'` @grep "Number of FAILED tests 0" $(TSTDIR)/regtest.log >& /dev/null @@ -229,8 +229,8 @@ OTHER_HELP += "install : Print installation help" clean: rm -rf $(LIBCUSMM_DIR)/libcusmm.cu $(LIBCUSMM_DIR)/libcusmm_part*.cu rm -rf $(foreach v, $(VERSION), $(MAINOBJDIR)/$(ARCH)/$(v)/*.o) - rm -rf $(foreach v, $(VERSION), $(MAINOBJDIR)/$(ARCH)/$(v)/*.mod $(MAINOBJDIR)/$(ARCH)/$(v)/*.MOD $(MAINOBJDIR)/$(ARCH)/$(v)/*.smod) - rm -rf $(foreach v, $(VERSION), $(MAINOBJDIR)/$(ARCH)/$(v)/*.dep) + rm -rf $(foreach v, $(VERSION), $(MAINOBJDIR)/$(ARCH)/$(v)/*.mod $(MAINOBJDIR)/$(ARCH)/$(v)/*.MOD $(MAINOBJDIR)/$(ARCH)/$(v)/*.smod) + rm -rf $(foreach v, $(VERSION), $(MAINOBJDIR)/$(ARCH)/$(v)/*.dep) OTHER_HELP += "clean : Remove intermediate object and mod files, but not the libraries and executables, for given ARCH and VERSION" execlean: @@ -300,7 +300,7 @@ TOOL_HELP += "prettyclean : Remove prettify marker files and preprettify directo $(PRETTYOBJDIR)/%.pretty: %.F $(DOXIFYOBJDIR)/%.doxified @mkdir -p $(PRETTYOBJDIR) - cd $(dir $<); $(TOOLSRC)/prettify.py --backup-dir=$(PRETTYOBJDIR) $(notdir $<) + cd $(dir $<); $(TOOLSRC)/prettify/fprettify.py --do-backup --backup-dir=$(PRETTYOBJDIR) $(notdir $<) @touch $@ $(PRETTYOBJDIR)/%.pretty: %.c $(DOXIFYOBJDIR)/%.doxified @@ -427,7 +427,7 @@ CPPSHELL := -D__COMPILE_ARCH="\"$(ARCH)\""\ endif ifneq ($(CPP),) -# always add the SRCDIR to the include path (-I here might not be portable) +# always add the SRCDIR to the include path (-I here might not be portable) CPPFLAGS += $(CPPSHELL) -I$(SRCDIR) else FCFLAGS += $(CPPSHELL) diff --git a/tools/formatting/__init__.py b/tools/prettify/formatting/__init__.py similarity index 100% rename from tools/formatting/__init__.py rename to tools/prettify/formatting/__init__.py diff --git a/tools/formatting/normalizeFortranFile.py b/tools/prettify/formatting/normalizeFortranFile.py similarity index 97% rename from tools/formatting/normalizeFortranFile.py rename to tools/prettify/formatting/normalizeFortranFile.py index 846ec201a8..e17ef34e5f 100644 --- a/tools/formatting/normalizeFortranFile.py +++ b/tools/prettify/formatting/normalizeFortranFile.py @@ -58,7 +58,6 @@ class CharFilter(object): return (pos, char) - def next(self): """ python 2 version""" pos, char = self._it.next() @@ -77,6 +76,7 @@ class CharFilter(object): return (pos, char) + class InputStream(object): """ Class to read logical Fortran lines from a Fortran file. @@ -217,8 +217,10 @@ def parseRoutine(inFile): subF.close() except: import traceback - print("error trying to follow include ", m.group('file')) - print("warning this might lead to the removal of used variables") + sys.stderr.write( + "error trying to follow include " + m.group('file') + '\n') + sys.stderr.write( + "warning this might lead to the removal of used variables\n") traceback.print_exc() if jline: routine['begin'] = lines @@ -357,8 +359,10 @@ def parseRoutine(inFile): subF.close() except: import traceback - print("error trying to follow include ", m.group('file')) - print("warning this might lead to the removal of used variables") + sys.stderr.write( + "error trying to follow include " + m.group('file') + '\n') + sys.stderr.write( + "warning this might lead to the removal of used variables\n") traceback.print_exc() (jline, _, lines) = stream.nextFortranLine() return routine @@ -596,7 +600,7 @@ def writeDeclarations(parsedDeclarations, file): writeExtendedDeclaration(d, file) -def cleanDeclarations(routine, logFile=sys.stdout): +def cleanDeclarations(routine, logFile=sys.stderr): """cleans up the declaration part of the given parsed routine removes unused variables""" global rVar @@ -708,8 +712,8 @@ def cleanDeclarations(routine, logFile=sys.stdout): if argDeclDict.has_key(arg): argDecl.append(argDeclDict[arg]) else: - print("warning, implicitly typed argument '", - arg, "' in routine", routine['name']) + sys.stderr.write("warning, implicitly typed argument '" + + arg + "' in routine " + routine['name'] + '\n') if routine['kind'].lower() == 'function': aDecl = argDecl[:-1] else: @@ -868,8 +872,6 @@ def parseUse(inFile): # parse use m = useParseRe.match(jline) if m: - if comments: - print("jline", jline, "lines", lines) useAtt = {'module': m.group('module'), 'comments': []} if m.group('only'): @@ -1001,7 +1003,7 @@ def prepareImplicitUses(modules): return mods -def cleanUse(modulesDict, rest, implicitUses=None, logFile=sys.stdout): +def cleanUse(modulesDict, rest, implicitUses=None, logFile=sys.stderr): """Removes the unneded modules (the ones that are not used in rest)""" global rUse exceptions = {} @@ -1048,12 +1050,12 @@ def resetModuleN(moduleName, lines): flags=re.IGNORECASE) for i in range(len(lines)): lines[i] = moduleNRe.sub( - " "*indentSize + "CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = '" + + " " * indentSize + "CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = '" + moduleName + "'", lines[i]) -def rewriteFortranFile(inFile, outFile, indent, decl_linelength, decl_offset, logFile=sys.stdout, orig_filename=None): +def rewriteFortranFile(inFile, outFile, indent, decl_linelength, decl_offset, logFile=sys.stderr, orig_filename=None): """rewrites the use statements and declarations of inFile to outFile. It sorts them and removes the repetitions.""" import os.path @@ -1080,9 +1082,6 @@ def rewriteFortranFile(inFile, outFile, indent, decl_linelength, decl_offset, lo if not orig_filename: orig_filename = inFile.name fn = os.path.basename(orig_filename).rsplit(".", 1)[0] - if (m.group('moduleName') != fn): - raise SyntaxError("Module name is different from filename (" + - m.group('moduleName') + "!=" + fn + ")") break try: modulesDict = parseUse(inFile) @@ -1104,7 +1103,7 @@ def rewriteFortranFile(inFile, outFile, indent, decl_linelength, decl_offset, lo nonStPrep = 0 for line in modulesDict['origLines']: if (re.search('^#', line) and not commonUsesRe.match(line)): - print('noMatch', repr(line)) + sys.stderr.write('noMatch ' + repr(line) + '\n') nonStPrep = 1 if nonStPrep: logFile.write( @@ -1124,8 +1123,8 @@ def rewriteFortranFile(inFile, outFile, indent, decl_linelength, decl_offset, lo implicitUses = prepareImplicitUses( implicitUsesRaw['modules']) except: - print ("ERROR trying to parse use statements contained in common", - "uses precompiler file ", inc_absfn) + sys.stderr.write( + "ERROR trying to parse use statements contained in common uses precompiler file " + inc_absfn + '\n') raise cleanUse(modulesDict, rest, implicitUses=implicitUses, logFile=logFile) diff --git a/tools/formatting/reformatFortranFile.py b/tools/prettify/formatting/reformatFortranFile.py similarity index 95% rename from tools/formatting/reformatFortranFile.py rename to tools/prettify/formatting/reformatFortranFile.py index 8dd9d1669b..a75accfbca 100644 --- a/tools/formatting/reformatFortranFile.py +++ b/tools/prettify/formatting/reformatFortranFile.py @@ -38,6 +38,7 @@ import re import sys +import os from formatting.normalizeFortranFile import useParseRe, typeRe, InputStream, CharFilter, ompRe, ompDirRe #========================================================================= @@ -45,7 +46,7 @@ from formatting.normalizeFortranFile import useParseRe, typeRe, InputStream, Cha RE_FLAGS = re.IGNORECASE # all regex should be case insensitive -FORTRAN_DEFAULT_ERROR_MESSAGE = " Syntax error - this script can not handle invalid Fortran files." +FORTRAN_DEFAULT_ERROR_MESSAGE = " Syntax error - this formatter can not handle invalid Fortran files." FORMATTER_ERROR_MESSAGE = " Wrong usage of formatting-specific directives '&', '!&', '!&<' or '!&>'." EOL_STR = r"\s*;?\s*$" # end of fortran line @@ -91,7 +92,8 @@ PROG_RE = re.compile(SOL_STR + r"PROGRAM\s+\w+" + EOL_STR, RE_FLAGS) ENDPROG_RE = re.compile( SOL_STR + r"END\s*PROGRAM(\s+\w+)?" + EOL_STR, RE_FLAGS) -INTERFACE_RE = re.compile(r"^([^\"'!]* )?INTERFACE(\s+\w+)?" + EOL_STR, RE_FLAGS) +INTERFACE_RE = re.compile( + r"^([^\"'!]* )?INTERFACE(\s+\w+)?" + EOL_STR, RE_FLAGS) ENDINTERFACE_RE = re.compile( SOL_STR + r"END\s*INTERFACE(\s+\w+)?" + EOL_STR, RE_FLAGS) @@ -178,7 +180,7 @@ class F90Indenter(object): valid_new = True scopes.append(what_new) if debug: - print(f_line) + sys.stderr.write(f_line + '\n') break # check statements that continue scope @@ -193,7 +195,7 @@ class F90Indenter(object): if what == what_con: valid_con = True if debug: - print(f_line) + sys.stderr.write(f_line + '\n') break # check statements that end scope @@ -208,7 +210,7 @@ class F90Indenter(object): if what == what_end: valid_end = True if debug: - print(f_line) + sys.stderr.write(f_line + '\n') break # deal with line breaks @@ -302,7 +304,7 @@ class F90Aligner(object): if pos + 1 < len(lines): self._line_indents.append(self._br_indent_list[-1]) - if len(self._br_indent_list) > 2 or self._level: + if (len(self._br_indent_list) > 2 or self._level): raise SyntaxError(self._filename + ':' + str(self._line_nr) + ':' + FORTRAN_DEFAULT_ERROR_MESSAGE) @@ -416,12 +418,15 @@ def inspect_ffile_format(infile, indent_size): indents = [] stream = InputStream(infile) prev_offset = 0 + first_indent = -1 while 1: f_line, _, lines = stream.nextFortranLine() if not lines: break offset = len(lines[0]) - len(lines[0].lstrip(' ')) + if f_line.strip() and first_indent == -1: + first_indent = offset indents.append(offset - prev_offset) if not adopt: # do not adopt indentations but impose fixed rel. ind. # but don't impose indentation for blocked do/if constructs @@ -432,7 +437,7 @@ def inspect_ffile_format(infile, indent_size): # can not handle f77 style constructs if F77_STYLE.search(f_line): is_f90 = False - return indents, is_f90 + return indents, first_indent, is_f90 #========================================================================= @@ -441,11 +446,11 @@ def format_single_fline(f_line, whitespace, linebreak_pos, ampersand_sep, filena """ format a single Fortran line - imposes white space formatting and inserts linebreaks. - Takes a logical Fortran line `f_line` as input as well as the positions - of the linebreaks (`linebreak_pos`), and the number of separating whitespace - characters before ampersand (`ampersand_sep`). + Takes a logical Fortran line `f_line` as input as well as the positions + of the linebreaks (`linebreak_pos`), and the number of separating whitespace + characters before ampersand (`ampersand_sep`). `filename` and `line_nr` just for error messages. - The higher `whitespace`, the more white space characters inserted - + The higher `whitespace`, the more white space characters inserted - whitespace = 0, 1, 2 are currently supported. auto formatting can be turned off by setting `auto_format` to False. """ @@ -664,7 +669,7 @@ def format_single_fline(f_line, whitespace, linebreak_pos, ampersand_sep, filena #========================================================================= -def reformat_ffile(infile, outfile, logFile=sys.stdout, indent_size=2, whitespace=2, orig_filename=None): +def reformat_ffile(infile, outfile, logFile=sys.stderr, indent_size=2, whitespace=2, orig_filename=None): """ main method to be invoked for formatting a Fortran file. """ @@ -678,7 +683,8 @@ def reformat_ffile(infile, outfile, logFile=sys.stdout, indent_size=2, whitespac indenter = F90Indenter(orig_filename) - req_indents, is_f90 = inspect_ffile_format(infile, indent_size) + req_indents, first_indent, is_f90 = inspect_ffile_format( + infile, indent_size) infile.seek(0) if not is_f90: @@ -690,6 +696,7 @@ def reformat_ffile(infile, outfile, logFile=sys.stdout, indent_size=2, whitespac nfl = 0 # fortran line counter do_indent = True + use_same_line = False stream = InputStream(infile) skip_blank = False in_manual_block = False @@ -744,23 +751,27 @@ def reformat_ffile(infile, outfile, logFile=sys.stdout, indent_size=2, whitespac is_empty = EMPTY_RE.search(f_line) # blank line or comment only line if useParseRe.match(f_line): - pass # do not touch use statements cleaned up by existing prettify + do_indent = False elif ompDirRe.match(f_line): # move '!$OMP' to line start, otherwise don't format omp directives lines = ['!$OMP' + (len(l) - len(l.lstrip())) * ' ' + ompDirRe.sub('', l, count=1) for l in lines] - pass + do_indent = False elif lines[0].startswith('#'): # preprocessor macros assert len(lines) == 1 + do_indent = False elif EMPTY_RE.search(f_line): # empty lines including comment lines assert len(lines) == 1 if any(comments): if lines[0].startswith('!'): - pass # don't indent unindented comment lines + # don't indent unindented comments + do_indent = False else: indent[0] = indenter.get_fline_indent() elif skip_blank: continue + else: + do_indent = False lines = [l.strip(' ') for l in lines] else: @@ -850,9 +861,12 @@ def reformat_ffile(infile, outfile, logFile=sys.stdout, indent_size=2, whitespac line_length += 1 if do_indent: - ind_use = ind + ind_use = ind + first_indent else: - ind_use = 1 + if use_same_line: + ind_use = 1 + else: + ind_use = 0 if ind_use + line_length <= 133: # 132 plus 1 newline char outfile.write('!$' * is_omp_conditional + ' ' * (ind_use - 2 * is_omp_conditional + @@ -869,12 +883,15 @@ def reformat_ffile(infile, outfile, logFile=sys.stdout, indent_size=2, whitespac logFile.write("*** " + orig_filename + ":" + str(stream.line_nr) + (": auto indentation and whitespace formatting failed due to 132 chars limit, line should be splitted. ***\n")) if debug: - print(' ' * ind_use + line) - # no indentation of blank lines + sys.stderr.write(' ' * ind_use + line + '\n') + + # no indentation of semicolon separated lines if re.search(r";\s*$", f_line, RE_FLAGS): do_indent = False + use_same_line = True else: do_indent = True + use_same_line = False # rm subsequent blank lines skip_blank = is_empty and not any(comments) diff --git a/tools/formatting/replacer.py b/tools/prettify/formatting/replacer.py similarity index 96% rename from tools/formatting/replacer.py rename to tools/prettify/formatting/replacer.py index 9c977cc0af..8096efad4f 100644 --- a/tools/formatting/replacer.py +++ b/tools/prettify/formatting/replacer.py @@ -11,7 +11,7 @@ specialRepl = None def replaceWords(infile, outfile, replacements=repl, specialReplacements=specialRepl, - logFile=sys.stdout): + logFile=sys.stderr): """Replaces the words in infile writing the output to outfile. replacements is a dictionary with the words to replace. diff --git a/tools/formatting/selftest.py b/tools/prettify/formatting/selftest.py similarity index 100% rename from tools/formatting/selftest.py rename to tools/prettify/formatting/selftest.py diff --git a/tools/prettify.py b/tools/prettify/fprettify.py similarity index 69% rename from tools/prettify.py rename to tools/prettify/fprettify.py index ac8e32679b..27683a54a0 100755 --- a/tools/prettify.py +++ b/tools/prettify/fprettify.py @@ -66,7 +66,7 @@ def upcaseOMP(line): return toUpcaseOMPRe.sub(lambda match: match.group("toUpcase").upper(), line) -def upcaseKeywords(infile, outfile, upcase_omp, logFile=sys.stdout): +def upcaseKeywords(infile, outfile, upcase_omp, logFile=sys.stderr): """Writes infile to outfile with all the fortran keywords upcased""" while 1: line = infile.readline() @@ -79,7 +79,7 @@ def upcaseKeywords(infile, outfile, upcase_omp, logFile=sys.stdout): outfile.write(line) -def prettifyFile(infile, normalize_use, decl_linelength, decl_offset, +def prettifyFile(infile, filename, normalize_use, decl_linelength, decl_offset, reformat, indent, whitespace, upcase_keywords, upcase_omp, replace, logFile): """prettifyes the fortran source in infile into a temporary file that is @@ -91,7 +91,7 @@ def prettifyFile(infile, normalize_use, decl_linelength, decl_offset, does not close the input file""" ifile = infile - orig_filename = infile.name + orig_filename = filename tmpfile = None max_pretty_iter = 5 n_pretty_iter = 0 @@ -151,19 +151,34 @@ def prettifyFile(infile, normalize_use, decl_linelength, decl_offset, raise -def prettfyInplace(fileName, bkDir, **kwargs): +def prettfyInplace(fileName, bkDir=None, stdout=False, **kwargs): """Same as prettify, but inplace, replaces only if needed""" - if not os.path.exists(bkDir): + + if fileName == 'stdin': + infile = os.tmpfile() + infile.write(sys.stdin.read()) + else: + infile = open(fileName, 'r') + + if stdout: + outfile = prettifyFile(infile=infile, filename=fileName, **kwargs) + outfile.seek(0) + sys.stdout.write(outfile.read()) + outfile.close() + return + + if bkDir and not os.path.exists(bkDir): os.mkdir(bkDir) - if not os.path.isdir(bkDir): + if bkDir and not os.path.isdir(bkDir): raise Error("bk-dir must be a directory, was " + bkDir) - infile = open(fileName, 'r') - outfile = prettifyFile(infile=infile, **kwargs) + + outfile = prettifyFile(infile=infile, filename=fileName, **kwargs) if (infile == outfile): return infile.seek(0) outfile.seek(0) same = 1 + while 1: l1 = outfile.readline() l2 = infile.readline() @@ -173,52 +188,50 @@ def prettfyInplace(fileName, bkDir, **kwargs): if not l1: break if (not same): - bkName = os.path.join(bkDir, os.path.basename(fileName)) - bName = bkName - i = 0 - while os.path.exists(bkName): - i += 1 - bkName = bName + "." + str(i) + bkFile = None + if bkDir: + bkName = os.path.join(bkDir, os.path.basename(fileName)) + bName = bkName + i = 0 + while os.path.exists(bkName): + i += 1 + bkName = bName + "." + str(i) + bkFile = file(bkName, "w") infile.seek(0) - bkFile = file(bkName, "w") - while 1: - l1 = infile.readline() - if not l1: - break - bkFile.write(l1) - bkFile.close() + if bkFile: + bkFile.write(infile.read()) + bkFile.close() outfile.seek(0) newFile = file(fileName, 'w') - while 1: - l1 = outfile.readline() - if not l1: - break - newFile.write(l1) + newFile.write(outfile.read()) newFile.close() infile.close() outfile.close() -def main(argv): - # future defaults +def main(argv=None): + if argv is None: + argv = sys.argv defaultsDict = {'upcase': 1, 'normalize-use': 1, 'omp-upcase': 1, 'decl-linelength': 100, 'decl-offset': 50, 'reformat': 1, 'indent': 3, 'whitespace': 1, 'replace': 1, - 'backup-dir': 'preprettify'} + 'stdout': 0, + 'do-backup': 0, + 'backup-dir': 'preprettify', + 'report-errors': 1} - usageDesc = ("usage:\n" + argv[0] + """ + usageDesc = ("usage:\nfprettify" +""" [--[no-]upcase] [--[no-]normalize-use] [--[no-]omp-upcase] [--[no-]replace] - [--[no-]reformat] --indent=3 --whitespace=1 [--help] - [--backup-dir=bk_dir] file1 [file2 ...] + [--[no-]reformat] [--indent=3] [--whitespace=1] [--help] + [--[no-]stdout] [--[no-]do-backup] [--backup-dir=bk_dir] [--[no-]report-errors] file1 [file2 ...] Auto-format F90 source file1, file2, ...: + If no files are given, stdin is used. --normalize-use Sorting and alignment of variable declarations and USE statements, removal of unused list entries. The line length of declarations is controlled by --decl-linelength=n, the offset of the variable list is controlled by --decl-offset=n. - --upcase - Upcasing fortran keywords. --reformat Auto-indentation, auto-alignment and whitespace formatting. Amount of whitespace controlled by --whitespace = 0, 1, 2. @@ -228,22 +241,32 @@ def main(argv): - completely disable reformatting by adding a comment '!&'. For manual formatting of a code block, use: - start a manually formatted block with a '!&<' comment and close it with a '!&>' comment. + --upcase + Upcasing fortran keywords. --omp-upcase Upcasing OMP directives. --replace - If requested the replacements performed by the replacer.py script are also performed. (FIXME: what replacements?) + If requested the replacements performed by the replacer.py script are also performed. Note: these replacements are specific to CP2K. + --stdout + write output to stdout + --[no-]do-backup + store backups of original files in backup-dir (--backup-dir option) + --[no-]report-errors + report warnings and errors + + Note: for editor integration, use options --no-normalize-use --no-report-errors Defaults: """ + str(defaultsDict)) replace = None if "--help" in argv: - print(usageDesc) + sys.stderr.write(usageDesc + '\n') return(0) args = [] for arg in argv[1:]: m = re.match( - r"--(no-)?(normalize-use|upcase|omp-upcase|replace|reformat)", arg) + r"--(no-)?(normalize-use|upcase|omp-upcase|replace|reformat|stdout|do-backup|report-errors)", arg) if m: defaultsDict[m.groups()[1]] = not m.groups()[0] else: @@ -258,53 +281,65 @@ def main(argv): defaultsDict[m.groups()[0]] = path else: if arg.startswith('--'): - print('unknown option', arg) + sys.stderr.write('unknown option ' + arg + '\n') else: args.append(arg) - if len(args) < 1: - print(usageDesc) - else: + bkDir = '' + if defaultsDict['do-backup']: bkDir = defaultsDict['backup-dir'] - if not os.path.exists(bkDir): - # Another parallel running instance might just have created the - # dir. - try: - os.mkdir(bkDir) - except: - assert(os.path.exists(bkDir)) - if not os.path.isdir(bkDir): - print("bk-dir must be a directory") - print(usageDesc) - else: - failure = 0 - for fileName in args: - if not os.path.isfile(fileName): - print("file", fileName, "does not exists!") - else: - try: - prettfyInplace(fileName, bkDir=bkDir, logFile=sys.stdout, - normalize_use=defaultsDict[ - 'normalize-use'], - decl_linelength=defaultsDict[ - 'decl-linelength'], - decl_offset=defaultsDict['decl-offset'], - reformat=defaultsDict['reformat'], - indent=defaultsDict['indent'], - whitespace=defaultsDict['whitespace'], - upcase_keywords=defaultsDict['upcase'], - upcase_omp=defaultsDict['omp-upcase'], - replace=defaultsDict['replace']) - except: - failure += 1 - import traceback - sys.stdout.write('-' * 60 + "\n") - traceback.print_exc(file=sys.stdout) - sys.stdout.write('-' * 60 + "\n") - sys.stdout.write( - "Processing file '" + fileName + "'\n") - return(failure > 0) + if bkDir and not os.path.exists(bkDir): + # Another parallel running instance might just have created the + # dir. + try: + os.mkdir(bkDir) + except: + assert(os.path.exists(bkDir)) + if bkDir and not os.path.isdir(bkDir): + sys.stderr.write("bk-dir must be a directory" + '\n') + sys.stderr.write(usageDesc + '\n') + else: + failure = 0 + if not args: + args = ['stdin'] + for fileName in args: + if not os.path.isfile(fileName) and not fileName == 'stdin': + sys.stderr.write("file " + fileName + " does not exists!\n") + else: + stdout = defaultsDict['stdout'] or fileName == 'stdin' + try: + logFile = sys.stderr if defaultsDict[ + 'report-errors'] else open(os.devnull, "w") + prettfyInplace(fileName, bkDir=bkDir, + stdout=stdout, + logFile=logFile, + normalize_use=defaultsDict[ + 'normalize-use'], + decl_linelength=defaultsDict[ + 'decl-linelength'], + decl_offset=defaultsDict[ + 'decl-offset'], + reformat=defaultsDict['reformat'], + indent=defaultsDict['indent'], + whitespace=defaultsDict[ + 'whitespace'], + upcase_keywords=defaultsDict[ + 'upcase'], + upcase_omp=defaultsDict[ + 'omp-upcase'], + replace=defaultsDict['replace']) + except: + failure += 1 + import traceback + sys.stderr.write('-' * 60 + "\n") + traceback.print_exc(file=sys.stderr) + sys.stderr.write('-' * 60 + "\n") + sys.stderr.write( + "Processing file '" + fileName + "'\n") + return(failure > 0) #========================================================================= + + def run_selftest(): # create temporary file with example code fn = os.path.join(tempfile.gettempdir(), "prettify_selftest.F") @@ -315,15 +350,15 @@ def run_selftest(): # call prettify rtn = main([sys.argv[0], fn]) - assert(rtn==0) + assert(rtn == 0) # check if file was altered result = open(fn).read() for i, (l1, l2) in enumerate(zip(result.split("\n"), ref.split("\n"))): if(l1 != l2): - print("Error: Line %d is not invariant."%i) - print("before: "+l1) - print("after : "+l2) + print("Error: Line %d is not invariant." % i) + print("before: " + l1) + print("after : " + l2) os.remove(fn) return(1) @@ -336,7 +371,7 @@ if(__name__ == '__main__'): if(len(sys.argv) == 2 and sys.argv[-1] == "--selftest"): rtn = run_selftest() else: - rtn = main(sys.argv) + rtn = main() sys.exit(rtn) # EOF diff --git a/tools/prettify/setup.py b/tools/prettify/setup.py new file mode 100755 index 0000000000..f6cba7c30e --- /dev/null +++ b/tools/prettify/setup.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +from setuptools import setup + +setup(name='fprettify', + description='auto-formatter for modern fortran source code', + author='Mohamed Fawzi, Patrick Seewald', + license = "GPL", + entry_points={'console_scripts': ['fprettify = fprettify:main']}, + py_modules=['fprettify', + 'formatting.normalizeFortranFile', + 'formatting.reformatFortranFile', + 'formatting.replacer', + 'formatting.selftest'] + )