def check_pointers(parser, codedir=None, mfilter=None, recursive=False): """Checks the modules in the specified code parser to see if they have common, but subtle, pointer bugs in: 1. subroutines with a parameter of intent(out) and user-derived type must* set *all* members of that parameter or they will have an *undefined* status. 2. pointer-type arrays that are not nullified are set to a valid target will return 'T' when passed to `associated`. Best practice is to nullify pointer arrays in user-derived types as the default value on those types. :arg parser: [fortpy.code.CodeParser] with the modules to search *already loaded*. :arg codedir: specify the full path to the library whose modules should be searched, just another way to filter which modules are generating the warnings. :arg mfilter: filter to apply to module names; can use the wildcard standard from bash. """ from fnmatch import fnmatch from fortpy.msg import std, set_verbosity, info set_verbosity(0) W1 = " {} '{}' does not set the value of members '{}' in parameter '{}'." W2 = " Type '{}' does not nullify members '{}' on creation." offenders = {} for (modname, module) in parser.modules.items(): if not recursive and codedir is not None and not codedir.lower() in module.filepath.lower(): continue if mfilter is not None and not fnmatch(module.name.lower(), mfilter.lower()): continue #Test the first condition above for all subroutines in the module; also handle #the recursively defined subroutines. hprinted = False for xname, xvalue in module.executables.items(): oparams, pmembers = _exec_check_pointers(xvalue) if len(oparams) > 0: if not hprinted: info("Best practice suggestions: {}".format(module.filepath)) hprinted = True for oparam in oparams: plist = ', '.join([p.name for p in pmembers[oparam]]) std(W1.format(type(xvalue).__name__, xname, plist, oparam), 0) offenders[xvalue.full_name] = (oparams, pmembers) for tname, tvalue in module.types.items(): result = _type_check_pointers(tvalue) if len(result) > 0: if not hprinted: info("Best practice suggestions: {}".format(module.filepath)) hprinted = True plist = ', '.join([p.name for p in result]) std(W2.format(tname, plist), 0) offenders[xvalue.full_name] = result return offenders
def _prompt_general(header, skeys, sources=None, args=None): msg.okay(header) for i, k in enumerate(skeys): msg.std("{}. {}".format(i, k)) msg.blank(1,0) choice = input("Selection: ") if re.match("[\d]+", choice.strip()): value = int(choice) if sources is None: return value else: if value in sources: sources[value](*args) else: msg.warn("choice '{}' not in list of possibilities.".format(choice)) else: msg.warn("invalid (non-numeric) choice: '{}'".format(choice))
def compile(folder, compiler=None, identifier=None, debug=False, profile=False, quiet=False, moptions=None, inclfortpy=True, vupdates=None, strict=False, inclfpyaux=False): """Runs the makefile in the specified folder to compile the 'all' rule. :arg vupdates: a list of module names for which the output .mod and .o files should have version information attached. """ if inclfortpy: #Before we can compile the library, we need to make sure that we have a fortpy #.mod and .o compiled with the *same* compiler version specified. from fortpy.utility import get_fortpy_templates_dir _ensure_fileversion(compiler, "fortpy", get_fortpy_templates_dir(), folder) options = "" if debug: options += " DEBUG=true" if profile: options += " GPROF=true" if strict: options += " STRICT=true" if moptions is not None: for opt in moptions: options += " {}".format(opt) if identifier is not None: codestr = "cd {}; make -f 'Makefile.{}' F90='{}' FAM='{}'" + options command = codestr.format(folder, identifier, executor(compiler), family(compiler)) else: codestr = "cd {}; make F90='{}' FAM='{}'" + options command = codestr.format(folder, executor(compiler), family(compiler)) #If we are running in quiet mode, we don't want the compile information #to post to stdout; only errors should be redirected. This means we need #to wrap the execution in a subprocess and redirect the std* to PIPE from os import waitpid, path from subprocess import Popen, PIPE pcompile = Popen(command, shell=True, executable="/bin/bash", stdout=PIPE, stderr=PIPE) waitpid(pcompile.pid, 0) if not quiet: output = [x.decode('utf8') for x in pcompile.stdout.readlines()] msg.std(''.join(output)) #else: #We don't need to get these lines since we are purposefully redirecting them. error = [x.decode('utf8') for x in pcompile.stderr.readlines()] code = len(error) if code != 0: msg.err(''.join(error)) #It turns out that the compiler still returns a code of zero, even if the compile #failed because the actual compiler didn't fail; it did its job properly. We need to #check for the existence of errors in the 'compile.log' file. lcount = 0 errors = [] log = path.join(folder, "compile.log") with open(log) as f: for line in f: lcount += 1 if lcount > 21 and lcount < 32: errors.append(line) elif lcount > 21: break if len(errors) > 0: #There are 21 lines in the compile.log file when everything runs correctly #Overwrite code with a bad exit value since we have some other problems. code = 1 #We want to write the first couple of errors to the console and give them the #option to still execute if the compile only generated warnings. msg.warn("compile generated some errors or warnings:") msg.blank() msg.info(''.join(errors)) if vupdates is not None: for modname in vupdates: _vupdate_compiled_module(compiler, modname, folder, rename=False) return (code, len(errors)==0)
def compile(folder, compiler=None, identifier=None, debug=False, profile=False, quiet=False, moptions=None, inclfortpy=True, vupdates=None, strict=False, inclfpyaux=False): """Runs the makefile in the specified folder to compile the 'all' rule. :arg vupdates: a list of module names for which the output .mod and .o files should have version information attached. """ if inclfortpy: #Before we can compile the library, we need to make sure that we have a fortpy #.mod and .o compiled with the *same* compiler version specified. from fortpy.utility import get_fortpy_templates_dir _ensure_fileversion(compiler, "fortpy", get_fortpy_templates_dir(), folder) options = "" if debug: options += " DEBUG=true" if profile: options += " GPROF=true" if strict: options += " STRICT=true" if moptions is not None: for opt in moptions: options += " {}".format(opt) if identifier is not None: codestr = "cd {}; make -f 'Makefile.{}' F90='{}' FAM='{}'" + options command = codestr.format(folder, identifier, executor(compiler), family(compiler)) else: codestr = "cd {}; make F90='{}' FAM='{}'" + options command = codestr.format(folder, executor(compiler), family(compiler)) #If we are running in quiet mode, we don't want the compile information #to post to stdout; only errors should be redirected. This means we need #to wrap the execution in a subprocess and redirect the std* to PIPE from os import waitpid, path from subprocess import Popen, PIPE pcompile = Popen(command, shell=True, executable="/bin/bash", stdout=PIPE, stderr=PIPE, close_fds=True) waitpid(pcompile.pid, 0) if not quiet: output = [x.decode('utf8') for x in pcompile.stdout.readlines()] msg.std(''.join(output)) #else: #We don't need to get these lines since we are purposefully redirecting them. error = [x.decode('utf8') for x in pcompile.stderr.readlines()] code = len(error) if code != 0: msg.err(''.join(error)) pcompile.stdout.close() pcompile.stderr.close() #It turns out that the compiler still returns a code of zero, even if the compile #failed because the actual compiler didn't fail; it did its job properly. We need to #check for the existence of errors in the 'compile.log' file. lcount = 0 errors = [] log = path.join( folder, "compile.{}.log".format( identifier if identifier is not None else "default")) with open(log) as f: for line in f: lcount += 1 if lcount > 21 and lcount < 32: errors.append(line) elif lcount > 21: break if len(errors) > 0: #There are 21 lines in the compile.log file when everything runs correctly #Overwrite code with a bad exit value since we have some other problems. code = 1 #We want to write the first couple of errors to the console and give them the #option to still execute if the compile only generated warnings. msg.warn("compile generated some errors or warnings:") msg.blank() msg.info(''.join(errors)) if vupdates is not None: for modname in vupdates: _vupdate_compiled_module(compiler, modname, folder, rename=False) return (code, len(errors) == 0)