Example #1
0
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
Example #2
0
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
Example #3
0
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))           
Example #4
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)
    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)
Example #5
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)