file modules/BOSS/modules/utils.py

[No description available]

Namespaces

Name
modules
modules::utils

Source code

################################
#                              #
#  Utility functions for BOSS  #
#                              #
################################

from __future__ import print_function
import xml.etree.ElementTree as ET
from collections import OrderedDict
from operator import itemgetter
import os
import copy

import modules.active_cfg as active_cfg
exec("import configs." + active_cfg.module_name + " as cfg")

import modules.gb as gb
import subprocess
import modules.exceptions as exceptions
import modules.infomsg as infomsg
import shlex
import tempfile



# ====== isComplete ========

def isComplete(class_el):

    is_complete = True

    # Check that class is complete (not only forward declared):
    if ('incomplete' in class_el.keys()) and (class_el.get('incomplete') == '1'):
        is_complete = False

    return is_complete

# ====== END: isComplete ========



# ====== isLoadable ========

def isLoadable(class_el, print_warning=False, check_pure_virtual_members=True):

    import modules.classutils as classutils

    is_loadable = True

    # - Any loadable class should have a "name" XML entry
    if not 'name' in class_el.keys():
        is_loadable = False
        return is_loadable

    class_name = classutils.getClassNameDict(class_el)


    # - Check if class should be ditched. If yes, return right away.
    if class_name['long_templ'] in cfg.ditch:
        is_loadable = False
        return is_loadable

    # - Check if class is a template class. BOSS cannot handle this yet.
    if isTemplateClass(class_el):
        is_loadable = False
        if print_warning:
            reason = "This is a template class. BOSS cannot yet handle this."
            infomsg.ClassNotLoadable(class_name['long_templ'], reason).printMessage()
        return is_loadable

    # - Check that class is complete (not only forward declared).
    if not isComplete(class_el):
        is_loadable = False
        if print_warning:
            reason = "Class is incomplete, at least based on XML file %s" % (gb.xml_file_name)
            infomsg.ClassNotLoadable(class_name['long_templ'], reason).printMessage()
        return is_loadable

    # - Check that class has at least one public constructor.
    constructor_elements = classutils.getAcceptableConstructors(class_el, skip_copy_constructors=True)
    if len(constructor_elements) == 0:
        is_loadable = False
        if print_warning:
            reason = "No (acceptable) public constructors identified."
            infomsg.ClassNotLoadable(class_name['long_templ'], reason).printMessage()
        return is_loadable

    # - Check for pure virtual members.
    if check_pure_virtual_members:
        pure_virtual_members = classutils.pureVirtualMembers(class_el)
        if len(pure_virtual_members) > 0:
            gb.contains_pure_virtual_members.append(class_name['long_templ'])

    return is_loadable

# ====== END: isLoadable ========



# ====== isFundamental ========

def isFundamental(el):

    is_fundamental = False

    if el.tag == 'FundamentalType':
        is_fundamental = True

    return is_fundamental

# ====== END: isFundamental ========



# ====== isKnownClass ========

def isKnownClass(el, class_name=None):

    import modules.classutils as classutils

    is_known = False

    type_dict = findType(el)
    type_el = type_dict['el']

    # - Any known class should have a "name" XML entry
    if not 'name' in type_el.keys():
        is_known = False
        return is_known

    # Get class_name dict if it is not passed in as an argument
    if class_name is None:
        class_name = classutils.getClassNameDict(type_el)

    # Check if standard library class
    if isStdType(el, class_name=class_name):
        is_known = True
        return is_known

    # Check if listed among the user-specified known types
    if isInList(class_name['long_templ'], cfg.known_classes.keys(), return_index=False, ignore_whitespace=True):
        is_known = True
    elif isInList(class_name['long'], cfg.known_classes.keys(), return_index=False, ignore_whitespace=True):
        is_known = True

    return is_known

# ====== END: isKnownClass ========



# ====== isTemplateClass ========

def isTemplateClass(class_el):

    import modules.classutils as classutils

    is_template = False

    class_name = classutils.getClassNameDict(class_el)

    if '<' in class_name['long_templ']:
        is_template = True

    return is_template

# ====== END: isTemplateClass ========




# ====== isTemplateFunction ========

def isTemplateFunction(func_el):

    import modules.funcutils as funcutils

    is_template = False

    func_name = funcutils.getFunctionNameDict(func_el)

    if '<' in func_name['long_templ']:
        is_template = True

    return is_template

# ====== END: isTemplateFunction ========




# ====== isEnumeration ========

def isEnumeration(el):

    is_enumeration = False

    if el.tag == 'Enumeration':
        is_enumeration = True

    return is_enumeration

# ====== END: isEnumeration ========



# ====== isNative ========

def isNative(el):

    # Makes use of global variables:  base_paths

    is_native = False
    can_check_tags = ['Class', 'Constructor', 'Converter', 'Destructor', 'Enumeration',
                      'Field', 'File', 'Function', 'Method', 'OperatorFunction',
                      'OperatorMethod', 'Struct', 'Typedef', 'Union', 'Variable']

    cannot_check_tags = ['Unimplemented']

    if el.tag == 'FundamentalType':
        is_native = False

    elif el.tag in can_check_tags:

        if el.tag == 'File':
            file_el = el
        else:
            file_el = gb.id_dict[el.get('file')]

        check_path = file_el.get('name')

        is_native = False
        for accepted_path in cfg.base_paths:
            if accepted_path in os.path.dirname(check_path):
                is_native = True
                break

    elif el.tag in cannot_check_tags:
        pass

    else:
        raise Exception('Cannot check whether XML element with id="%s" and tag "%s" is native.' % (el.get('id'), el.tag))

    return is_native

# ====== END: isNative ========



# ====== isStdType ========

def isStdType(el, class_name=None):

    # Makes use of global variables:  base_paths

    is_std = False
    can_check_tags = ['Class', 'Struct', 'Union', 'Enumeration']

    if el.tag in can_check_tags:

        # Use the optional class_name dict?
        if class_name is not None:
            if len(class_name['long_templ']) >= 5:
                if class_name['long_templ'][0:5] == 'std::':
                    is_std = True

        elif 'name' in el.keys():
            namespaces_list = getNamespaces(el, include_self=True)
            if namespaces_list[0] == 'std':
                is_std = True

    else:
        is_std = False

    return is_std

# ====== END: isStdType ========



# ====== isConstFunction ========

def isConstFunction(func_el):

    is_const_func = False

    if ('const' in func_el.keys()) and (func_el.get('const')=='1'):
        is_const_func = True

    return is_const_func

# ====== END: isConstFunction ========



# ====== getTemplateBracket ========

def getTemplateBracket(el):

    src_file_name = gb.id_dict[el.get('file')].get('name')
    line_number   = int(el.get('line'))

    f = open(src_file_name, 'r')
    file_content = f.read()
    f.close()
    file_content_nocomments = removeComments(file_content, insert_blanks=True)

    # Find index of the \n in line number line_number
    count = 0
    prev_pos = 0
    for index,char in enumerate(file_content_nocomments):
        if char=='\n':
            count += 1
        if count == line_number:
            break
        if char=='\n':         # STUPID HACK
            prev_pos = index

    newline_pos = index


    # Find the template parameter bracket, e.g. <typename A, typename B>
    search_content = file_content_nocomments[:newline_pos]

    start_pos = 0
    end_pos = search_content.rfind('>')
    if end_pos != -1:
        balance = -1
        for i in range(end_pos-1, -1, -1):
            char = search_content[i]
            if char == '>':
                balance -= 1
            elif char == '<':
                balance += 1
            if (balance == 0):
                start_pos = i
                break
        template_bracket = search_content[start_pos:end_pos+1]
    else:
        template_bracket = '<>'

    # print('TEMPLATE BRACKET: ', template_bracket)

    # Isolate only the template variable names (last word in each entry)
    if template_bracket == '<>':
        temp_var_list = []
    else:
        temp_var_list = template_bracket[1:-1].split(',')
        temp_var_list = [ e.strip() for e in temp_var_list]
        temp_var_list = [ e.split()[-1] for e in temp_var_list]

    # Return result
    return template_bracket, temp_var_list

# ====== END: getTemplateBracket ========



# ====== getSpecTemplateTypes ========

def getSpecTemplateTypes(input_type, byname=False):

    # If input is a string
    if byname:
        input_name = input_type

    # If input is an xml element
    else:
        el = input_type

        # Classes and functions must be treated differently
        if el.tag in ['Class', 'Struct']:
            input_name = el.get('name')
        elif el.tag in ['Function', 'Method', 'OperatorMethod', 'OperatorFunction']:
            namespaces_list = getNamespaces(el, include_self=True)
            input_name = '::'.join(namespaces_list)
        else:
            raise Exception("Don't know how to get template types from XML element with tag: %s" % el.tag)

    # Standardize the spacing between template brackets to simplify the parsing
    while "<<" in input_name:
        input_name = input_name.replace("<<", "< <")
    while ">>" in input_name:
        input_name = input_name.replace(">>", "> >")

    input_name_no_templ, templ_bracket = removeTemplateBracket(input_name, return_bracket=True)
    spec_types = templ_bracket.strip().lstrip('<').rstrip('>').strip()

    # Identify the correct commas
    pos = []
    balance = 0
    for i,c in enumerate(spec_types):
        if c == '<':
            balance += 1
        if c == '>':
            balance -= 1

        if (balance==0) and (c == ','):
            pos.append(i)

    # Construct list of arguments
    spec_types_list = []
    prev_p = 0
    for p in pos:
        spec_types_list.append(spec_types[prev_p:p].strip())
        prev_p = p+1
    spec_types_list.append(spec_types[prev_p:].strip())

    # Return result
    if spec_types_list == ['']:
        return []
    else:
        return spec_types_list

# ====== END: getSpecTemplateTypes ========



# ====== unpackAllSpecTemplateTypes ========

def unpackAllSpecTemplateTypes(input_bracket, result_list):

    # # Help subsequent parsing by standardizing the spacing between template brackets
    # while "<<" in input_bracket:
    #     input_bracket = input_bracket.replace("<<", "< <")
    # while ">>" in input_bracket:
    #     input_bracket = input_bracket.replace(">>", "> >")

    spec_types = getSpecTemplateTypes(input_bracket, byname=True)

    for type_name in spec_types:

        if '<' in type_name:
            result_list.append(type_name)
            unpackAllSpecTemplateTypes(type_name, result_list=result_list)
        else:
            result_list.append(type_name)

# ====== END: unpackAllSpecTemplateTypes ========



# ====== getAllTemplateTypes ========

def getAllTemplateTypes(type_name):

    type_name_parts = []
    current_type_name = type_name
    while True:

        namespace, short_type_name = removeNamespace(current_type_name, return_namespace=True)
        type_name_parts.append(short_type_name)
        if namespace == "":
            break
        else:
            current_type_name = namespace

    all_template_types = []
    for type_part in type_name_parts:
        unpackAllSpecTemplateTypes(type_part, all_template_types)

    return all_template_types

# ====== END: getAllTemplateTypes ========



# ====== getBasicTypeName ========

def getBasicTypeName(type_name):

    # If type name contains a template brackets
    if '<' in type_name:

        type_name_notempl, templ_bracket = removeTemplateBracket(type_name, return_bracket=True)
        before_bracket, after_bracket = type_name.rsplit(templ_bracket,1)

        if (len(after_bracket) > 0) and (after_bracket[0] == ' '):
            space_after_bracket = True
        else:
            space_after_bracket = False

        # Remove asterix and/or ampersand
        before_bracket = before_bracket.replace('*', '').replace('&', '')
        after_bracket  = after_bracket.replace('*', '').replace('&', '')

        # Remove 'const' and 'volatile'
        before_bracket_list = before_bracket.split()
        before_bracket_list = [item for item in before_bracket_list if item != 'const']
        before_bracket_list = [item for item in before_bracket_list if item != 'volatile']
        before_bracket = ' '.join(before_bracket_list)

        after_bracket_list = after_bracket.split()
        after_bracket_list = [item for item in after_bracket_list if item != 'const']
        after_bracket_list = [item for item in after_bracket_list if item != 'volatile']
        after_bracket = ' '.join(after_bracket_list)

        basic_type_name = before_bracket + templ_bracket + ' '*space_after_bracket + after_bracket

    # If no template bracket
    else:

        basic_type_name = type_name

        # Remove asterix and/or ampersand
        basic_type_name = basic_type_name.replace('*', '').replace('&', '')

        # Remove 'const' and 'volatile'
        basic_type_name_list = basic_type_name.split()
        basic_type_name_list = [item for item in basic_type_name_list if item != 'const']
        basic_type_name_list = [item for item in basic_type_name_list if item != 'volatile']
        basic_type_name = ' '.join(basic_type_name_list)

    # Return result
    return basic_type_name

# ====== END: getBasicTypeName ========


# ====== removeComments ========

def removeComments(content, insert_blanks=False):

    # Prepare list for storing tuples of the form: (start_position, stop_position)
    content_lenght = len(content)
    comment_sections = []

    #
    # Locate comments:
    #

    # -- One-line comments
    temp_startpos = 0
    while True:

        # Find start of comment
        search_pos = content[temp_startpos:].find('//')
        if search_pos == -1:
            break
        else:
            comment_start = temp_startpos + search_pos

            # Find end of comment
            search_pos = content[comment_start:].find('\n')
            if search_pos == -1:
                comment_end = content_lenght - 1
            else:
                comment_end = comment_start + search_pos

            # Store positions
            comment_sections.append( (comment_start, comment_end) )

            # Update loop variable
            temp_startpos = comment_end


    # -- Multi-line comments
    temp_startpos = 0
    while True:

        # Find start of comment
        search_pos = content[temp_startpos:].find('/*')

        # Are we done?
        if search_pos == -1:
            break
        # Check for the potentially confusing case of comments starting with "//*"
        elif (search_pos > 0) and (content[search_pos-1] == '/'):
            # This is really a single-line comment which has been dealt with above,
            # so we don't add it to the list of comment positions
            comment_start= temp_startpos + search_pos
            search_pos = content[comment_start:].find('\n')
            if search_pos == -1:
                comment_end = content_lenght - 1
            else:
                comment_end = comment_start + search_pos
            # Update loop variable
            temp_startpos = comment_end
        # Now for the proper multi-line comments
        else:
            comment_start = temp_startpos + search_pos

            # Find end of comment
            search_pos = content[comment_start:].find('*/')
            if search_pos == -1:
                comment_end = content_lenght - 1
            else:
                comment_end = comment_start + search_pos + 1

            # Store positions
            comment_sections.append( (comment_start, comment_end) )

            # Update loop variable
            temp_startpos = comment_end


    # Sort comment_sections from last to first, depending on stop position
    comment_sections = sorted(comment_sections, key=itemgetter(1), reverse=True)


    # Remove comments
    prev_start_pos = 0
    prev_stop_pos  = 0

    for start_pos, stop_pos in comment_sections:
        new_lenght = len(content)

        # Skip if the current comment was contained within the previous removed comment
        if (start_pos > prev_start_pos) and (stop_pos < prev_stop_pos):
            continue
        # If not, go on to remove comment
        else:
            # Insert whitespace?
            if insert_blanks == False:
                content = content.replace( content[start_pos:stop_pos+1], '')
            else:
                # Construct string of spaces and newlines to replace comments
                insert_string = ''
                for char in content[start_pos:stop_pos+1]:
                    insert_string += ' '*(char!='\n') + '\n'*(char=='\n')

                # Perform replacement
                content = content.replace( content[start_pos:stop_pos+1], insert_string )

            # Update loop variables
            prev_start_pos = start_pos
            prev_stop_pos  = stop_pos

    return content

# ====== END: removeComments ========



# ====== findType ========

def findType(el_input):

    # check_keywords = ['const']
    # additional_keywords = []

    cv_qualifiers = []
    is_reference = False
    pointerness = 0
    found_function_pointer = False
    is_array = False
    array_limits = []

    el = el_input

    if el.tag in ['FundamentalType', 'Class', 'Struct', 'Enumeration']:
        type_id = el.get('id')

    elif el.tag in ['Constructor']:
        type_id = el.get('context')
        el = gb.id_dict[type_id]

    else:
        type_id = el.get('type')
        prev_tag = ''
        while ('type' in el.keys()) or ('returns' in el.keys()):


            # Get xml id to move further through the xml file
            if el.tag in ['FunctionType', 'Function', 'Method', 'OperatorMethod']:

                if (el.tag == 'FunctionType') and (prev_tag == 'PointerType'):
                    found_function_pointer = True
                type_id = el.get('returns')
            else:
                type_id = el.get('type')

            # id='_0' refer to elements not resolved by CastXML. Should be safe to stop here.
            if type_id == '_0':
                break

            # Check for reference or pointer type
            if el.tag == 'ReferenceType':
                is_reference = True
            if el.tag == 'PointerType':
                pointerness += 1

            # Pick up any extra keywords (e.g. 'const') from non-function xml elements
            if el.tag == 'CvQualifiedType':
                if 'const' in el.keys():
                    if 'const' not in cv_qualifiers:
                        cv_qualifiers.append('const')
                if 'volatile' in el.keys():
                    if 'volatile' not in cv_qualifiers:
                        cv_qualifiers.append('volatile')

            # Pick up any array indices
            if el.tag == 'ArrayType':
                is_array = True
                if el.get('max') != '':
                    max_index_str = el.get('max')
                    max_index = int(max_index_str.strip('u'))
                    array_limits.append(max_index + 1)


            # Store tag (to identify function pointers)
            prev_tag = el.tag

            # change xlm element 'el'
            el = gb.id_dict[type_id]

    # When we exit the loop, 'el' is at the final element.
    # Now get the full name, including any namespaces.
    name_and_namespaces = getNamespaces(el, include_self=True)
    typename = '::'.join(name_and_namespaces)


    type_dict = OrderedDict([])
    type_dict['name']                = typename
    type_dict['cv_qualifiers']       = cv_qualifiers
    type_dict['is_reference']        = is_reference
    type_dict['pointerness']         = pointerness
    type_dict['id']                  = type_id
    type_dict['el']                  = el
    type_dict['is_function_pointer'] = found_function_pointer
    type_dict['is_array']            = is_array
    type_dict['array_limits']        = tuple(array_limits)

    return type_dict

# ====== END: findType ========



# ====== findNewLinePos ========

def findNewLinePos(content, line_number):
    count = 0
    for index,char in enumerate(content):
        if char=='\n':
            count += 1
        if count == line_number:
            break
    newline_pos = index

    return newline_pos

# ====== END: findNewLinePos ========



# ====== getBracketPositions ========

def getBracketPositions(content, delims=['{','}']):

    # Input:
    # - Content string
    # - List of left and right delimiters
    #
    # Output:
    # - List of bracket positions: [l_pos, r_pos]
    #

    l_delim, r_delim = delims

    # Cannot handle identical left and right delimiters
    if l_delim == r_delim:
        raise Exception('Left and right delimiters cannot be identical.')

    # Prepare search
    bracket_count  = 0
    start_count = False
    balance     = False
    l_pos = 0
    r_pos = 0

    # Search
    for i,c in enumerate(content):

        if c == l_delim:
            bracket_count += 1
            if not start_count:
                l_pos = i
                start_count = True

        if start_count and c == r_delim:
            bracket_count -= 1
            r_pos = i

        if start_count and bracket_count == 0:
            balance = True
            break

    # If brackets did not balance, raise exception
    if not balance:
        raise ValueError("No matching right delimiter for the first left delimiter.")
    # Else, return the found bracket positions
    else:
        return [l_pos, r_pos]

# ====== END: getBracketPositions ========



# ====== addIndentation ========

def addIndentation(content, indent):

    if indent == 0:
        new_content = content

    else:
        lines = content.split('\n')

        new_content = '\n'.join( [' '*indent + line for line in lines] )

        # If the last char in content was a newline,
        # remove the indentation that was added after that newline
        if lines[-1] == '':
            new_content = new_content[:-indent]

    return new_content

# ====== END: addIndentation ========



# ====== getNamespaces ========

def getNamespaces(xml_el, include_self=False, xml_file_name=''):

    namespaces = []

    if include_self:
        if 'name' in xml_el.keys():
            namespaces.append(xml_el.get('name'))
        else:
            namespaces.append('')

    current_xml_el = xml_el
    while 'context' in current_xml_el.keys():
        context_id = current_xml_el.get('context')
        if xml_file_name == '':
            context_xml_el = gb.id_dict[context_id]
        else:
            context_xml_el = gb.all_id_dict[xml_file_name][context_id]

        # if 'name' in current_xml_el.keys():
        if 'name' in context_xml_el.keys():
            context_name = context_xml_el.get('name')
            namespaces.append(context_name)
        else:
            break
            #continue

        current_xml_el = context_xml_el

    namespaces.reverse()

    # If present, remove the first, default namespace in the XML file from the list (with name='::')
    if (len(namespaces) > 0) and (namespaces[0] == '::'):
        namespaces = namespaces[1:]

    return namespaces

# ====== END: getNamespaces ========



# ====== removeTemplateBracket ========

def removeTemplateBracket(type_name, return_bracket=False):

    if ('<' in type_name) and ('>' in type_name):

        r_pos = type_name.rfind('>')

        if r_pos <= 0:
            raise Exception("Unbalanced template brackets in type name '%s'" % type_name)

        pos = r_pos-1
        count = 1
        while pos > -1:
            if type_name[pos] == '>':
                count += 1
            elif type_name[pos] == '<':
                count -= 1

            if count == 0:
                break

            pos -= 1

        if count != 0:
            raise Exception("Unbalanced template brackets in type name '%s'" % type_name)

        l_pos = pos

        type_name_notempl = type_name[:l_pos] + type_name[r_pos+1:]
        template_bracket  = type_name[l_pos:r_pos+1]

    else:
        type_name_notempl = type_name
        template_bracket  = ''

    if return_bracket:
        return type_name_notempl, template_bracket
    else:
        return type_name_notempl

# ====== END: removeTemplateBracket ========




# ====== removeNamespace ========

def removeNamespace(type_name, return_namespace=False):

    type_name_notempl = removeTemplateBracket(type_name)

    if '::' in type_name_notempl:
        namespace = type_name_notempl.rsplit('::',1)[0]
        new_type_name = type_name.replace(namespace+'::','',1)
    else:
        new_type_name = type_name
        namespace = ''

    if return_namespace:
        return namespace, new_type_name
    else:
        return new_type_name

# ====== END: removeNamespace ========



# ====== removeArgumentBracket ========

def removeArgumentBracket(func_signature, return_args_bracket=False):

    args_bracket_start = func_signature.rfind('(')

    func_signature_noargs = func_signature[:args_bracket_start]
    args_bracket          = func_signature[args_bracket_start:]

    if return_args_bracket:
        return func_signature_noargs, args_bracket
    else:
        return func_signature_noargs

# ====== END: removeArgumentBracket ========



# ====== isAcceptedType ========

def isAcceptedType(input_el):

    is_accepted_type = False

    type_dict = findType(input_el)
    type_el      = type_dict['el']
    type_name    = type_dict['name']
    pointerness  = type_dict['pointerness']
    is_ref       = type_dict['is_reference']
    is_array     = type_dict['is_array']
    array_limits = type_dict['array_limits']


    # BOSS cannot yet handle delarations of arrays with unspecified length
    if (is_array) and (len(array_limits) == 0):
        reason = "BOSS cannot yet handle arrays declared with unspecified length."
        infomsg.TypeNotAccepted(input_el.get('name'), reason).printMessage()
        is_accepted_type = False
        return is_accepted_type

    # BOSS cannot yet handle arrays of loaded types
    if isLoadedClass(type_el) and is_array:
        reason = "BOSS cannot yet handle arrays of a loaded type."
        infomsg.TypeNotAccepted(input_el.get('name'), reason).printMessage()
        is_accepted_type = False
        return is_accepted_type

    # BOSS cannot yet handle function pointers
    if type_dict['is_function_pointer']:
        reason = "BOSS cannot yet handle function pointers."
        infomsg.TypeNotAccepted(input_el.get('name'), reason).printMessage()
        is_accepted_type = False
        return is_accepted_type


    if type_el.tag in ['Class', 'Struct']:
        namespaces_list = getNamespaces(type_el, include_self=True)
        full_name = '::'.join(namespaces_list)
        if (full_name in gb.accepted_types) or (full_name.replace(' ','') in gb.accepted_types):
            is_accepted_type = True

    elif type_el.tag in ['FundamentalType', 'Enumeration']:
        type_name = type_el.get('name')
        if type_name in gb.accepted_types:
            is_accepted_type = True

    else:
        reason = "Cannot determine if XML element with id='%s' and tag '%s' corresponds to an accepted type. Assuming it does not." % (input_el.get('id'), input_el.tag)
        infomsg.TypeNotAccepted(input_el.get('id'), reason).printMessage()
        is_accepted_type = False

    return is_accepted_type

# ====== END: isAcceptedType ========



# ====== isLoadedClass ========

def isLoadedClass(input_type, byname=False, class_name=None):

    is_loaded_class = False

    # If the class_name dict is passed as an argument, use it.
    if class_name is not None:

        if class_name['long_templ'] in cfg.load_classes:
            is_loaded_class = True

    else:

        if byname:
            type_name = input_type

            # Remove '*' and '&'
            type_name = type_name.replace('*','').replace('&','')

            # Remove template bracket
            type_name = type_name.split('<')[0]

            # Check against cfg.load_classes
            if type_name in cfg.load_classes:
                is_loaded_class = True

        else:
            type_dict = findType(input_type)
            type_el = type_dict['el']

            if type_el.tag in ['Class', 'Struct']:

                if type_dict['name'] in cfg.load_classes:
                    is_loaded_class = True

    return is_loaded_class

# ====== END: isLoadedClass ========



# ====== isParentOfLoadedType ========

def isParentOfLoadedType(input_el):

    is_parent = False

    return is_parent

# ====== END: isParentOfLoadedType ========



# ====== constrAbsForwardDeclHeader ========

def constrAbsForwardDeclHeader(file_output_path):

    import modules.classutils as classutils

    # If this is the first time this function is executed, read initial code from header_templates/ folder
    if file_output_path not in gb.new_code.keys():
        f = open(gb.boss_dir+'/header_templates/standard_header_template.hpp')
        initial_code = f.read()
        f.close()
        initial_code_tuple = (0, initial_code)
        gb.new_code[file_output_path] = {'code_tuples':[initial_code_tuple], 'add_include_guard':True}


    current_code = gb.new_code[file_output_path]['code_tuples'][0][1]

    insert_code = ''
    tag_pos = current_code.find('__INSERT_CODE_HERE__')

    current_namespaces = []
    for class_name_long, class_el in gb.loaded_classes_in_xml.items():

        # print([class_name_full], [class_name_full.split('<',1)[0]], [class_name_full.split('<',1)[0].rsplit('::',1)[-1]])
        namespaces    = getNamespaces(class_el)
        has_namespace = bool(len(namespaces))
        namespace_str = '::'.join(namespaces) + '::'*has_namespace

        # class_name       = classutils.getClassNameDict(class_el)
        abstr_class_name = classutils.getClassNameDict(class_el, abstract=True)

        if namespaces != current_namespaces:
            # close current namespace
            insert_code += constrNamespace(current_namespaces, 'close', indent=cfg.indent)
            # open new namespace
            insert_code += constrNamespace(namespaces, 'open', indent=cfg.indent)
            # update current namespace
            current_namespaces = namespaces

        # Add forward decls
        n_indents   = len(namespaces)
        full_indent = ' '*n_indents*cfg.indent

        if '<' in abstr_class_name['long_templ']:
            is_template = True
        else:
            is_template = False

        if is_template:
            template_bracket = getTemplateBracket(class_el)[0]

        if is_template:
            insert_code += full_indent + 'template ' + template_bracket + '\n'
            insert_code += full_indent + 'class ' + abstr_class_name['short'] + ';\n'
            insert_code += full_indent + 'class ' + abstr_class_name['short_templ'] + ';\n'
        else:
            insert_code += full_indent + 'class ' + abstr_class_name['short_templ'] + ';\n'

    # Close current namespace
    insert_code += constrNamespace(current_namespaces, 'close', indent=cfg.indent)
    insert_code += '\n'

    new_code = current_code[:tag_pos] + insert_code + current_code[tag_pos:]


    # Replace other code tags
    new_code = replaceCodeTags(new_code)

    new_code_tuple = (0,new_code)

    # Overwrite existing code tuple
    gb.new_code[file_output_path]['code_tuples'] = [(new_code_tuple)]

# ====== END: constrAbsForwardDeclHeader ========



# ====== constrWrpForwardDeclHeader ========

def constrWrpForwardDeclHeader(file_output_path):

    import modules.classutils as classutils

    # If this is the first time this function is executed, read initial code from header_templates/ folder
    if file_output_path not in gb.new_code.keys():
        f = open(gb.boss_dir+'/header_templates/standard_header_template.hpp')
        initial_code = f.read()
        f.close()
        initial_code_tuple = (0, initial_code)
        gb.new_code[file_output_path] = {'code_tuples':[initial_code_tuple], 'add_include_guard':True}

    current_code = gb.new_code[file_output_path]['code_tuples'][0][1]

    insert_code = ''
    tag_pos = current_code.find('__INSERT_CODE_HERE__')

    for class_name in gb.loaded_classes_in_xml.keys():

        namespace, class_name_short = removeNamespace(class_name, return_namespace=True)

        if namespace == '':
            namespace_list = []
        else:
            namespace_list = namespace.split('::')

        n_indents = len(namespace_list)


        # - Open namespace
        insert_code += constrNamespace(namespace_list, 'open')

        # - Forward declaration
        insert_code += ' '*n_indents*cfg.indent + 'class ' + class_name_short + ';\n'

        # - Close namespace
        insert_code += constrNamespace(namespace_list, 'close')


    new_code = current_code[:tag_pos] + insert_code + current_code[tag_pos:]


    # Replace other code tags
    new_code = replaceCodeTags(new_code)


    new_code_tuple = (0,new_code)

    # Overwrite existing code tuple
    gb.new_code[file_output_path]['code_tuples'] = [(new_code_tuple)]

# ====== END: constrWrpForwardDeclHeader ========



# ====== getParentClasses ========

def getParentClasses(class_el, only_native_classes=False, only_loaded_classes=False):

    import modules.classutils as classutils

    parent_classes = []

    sub_el_list = class_el.findall('Base')
    for sub_el in sub_el_list:

        base_id = sub_el.get('type')
        base_el = gb.id_dict[base_id]

        if (only_loaded_classes) and (not isLoadedClass(base_el)):
            continue
        elif (only_native_classes) and (not isNative(base_el)):
            continue
        else:
            base_access    = sub_el.get('access')
            base_virtual   = bool( int( sub_el.get('virtual') ) )

            base_name_dict       = classutils.getClassNameDict(base_el)
            abstr_base_name_dict = classutils.getClassNameDict(base_el, abstract=True)

            is_accepted_type = isAcceptedType(base_el)
            is_native        = isNative(base_el)
            is_fundamental   = isFundamental(base_el)
            is_std           = isStdType(base_el)
            is_loaded_class  = isLoadedClass(base_el)


            temp_dict = OrderedDict([])
            temp_dict['class_name']       = base_name_dict
            temp_dict['abstr_class_name'] = abstr_base_name_dict
            temp_dict['wrapper_name']     = classutils.toWrapperType(base_name_dict['long'])
            temp_dict['access']           = base_access
            temp_dict['virtual']          = base_virtual
            temp_dict['id']               = base_id

            temp_dict['accepted']         = is_accepted_type
            temp_dict['native']           = is_native
            temp_dict['fundamental']      = is_fundamental
            temp_dict['std']              = is_std
            temp_dict['loaded']           = is_loaded_class

            parent_classes.append(temp_dict)

    return parent_classes

# ====== END: getParentClasses ========



# ====== getAllParentClasses ========

def getAllParentClasses(class_el, only_native_classes=True, only_loaded_classes=False, return_dicts=False, reverse_order=False):

    import modules.classutils as classutils

    parent_classes = []
    done_parent_classes = []

    temp_class_list = [class_el]
    while len(temp_class_list) > 0:
        current_class = temp_class_list.pop()
        if 'bases' in current_class.keys():
            for parent_class_id in current_class.get('bases').split():

                # Remove accessor info from id, e.g. "private:_123" --> "_123"
                parent_class_id = parent_class_id.split(':')[-1]

                parent_class_el = gb.id_dict[parent_class_id]

                if only_loaded_classes and not isLoadedClass(parent_class_el):
                    continue
                elif only_native_classes and not isNative(parent_class_el):
                    continue
                else:
                    if parent_class_el not in done_parent_classes:
                        temp_class_list.append(parent_class_el)
                        if return_dicts:
                            base_name_dict       = classutils.getClassNameDict(parent_class_el)
                            abstr_base_name_dict = classutils.getClassNameDict(parent_class_el, abstract=True)

                            is_accepted_type = isAcceptedType(parent_class_el)
                            is_native        = isNative(parent_class_el)
                            is_fundamental   = isFundamental(parent_class_el)
                            is_std           = isStdType(parent_class_el)
                            is_loaded_class  = isLoadedClass(parent_class_el)

                            temp_dict = OrderedDict([])
                            temp_dict['class_name']       = base_name_dict
                            temp_dict['abstr_class_name'] = abstr_base_name_dict
                            temp_dict['wrapper_name']     = classutils.toWrapperType(base_name_dict['long'])
                            temp_dict['id']               = parent_class_id

                            temp_dict['accepted']         = is_accepted_type
                            temp_dict['native']           = is_native
                            temp_dict['fundamental']      = is_fundamental
                            temp_dict['std']              = is_std
                            temp_dict['loaded']           = is_loaded_class

                            parent_classes.append(temp_dict)
                            done_parent_classes.append(parent_class_el)
                        else:
                            parent_classes.append(parent_class_el)
                            done_parent_classes.append(parent_class_el)

    if reverse_order:
        return parent_classes[::-1]
    else:
        return parent_classes

# ====== END: getAllParentClasses ========



# ====== getAllTypesInClass ========

def getAllTypesInClass(class_el, include_parents=False):

    import modules.classutils as classutils
    import modules.funcutils as funcutils

    all_types = []

    check_member_elements = getMemberElements(class_el)

    class_id = class_el.get('id')
    for mem_el in check_member_elements:

        if mem_el.tag in ['Constructor', 'Destructor', 'Method', 'OperatorMethod']:
            args_list = funcutils.getArgs(mem_el)
            for arg_dict in args_list:

                arg_type_el   = gb.id_dict[arg_dict['id']]
                arg_type_name = classutils.getClassNameDict(arg_type_el)

                arg_type_dict = OrderedDict([])
                arg_type_dict['class_name'] = arg_type_name
                arg_type_dict['el']         = arg_type_el

                all_types.append(arg_type_dict)

        if ('type' in mem_el.keys()) or ('returns' in mem_el.keys()):

            mem_type_dict = findType(mem_el)
            type_el = mem_type_dict['el']

            type_name = classutils.getClassNameDict(type_el)

            type_dict = OrderedDict([])
            type_dict['class_name'] = type_name
            type_dict['el']         = type_el

            all_types.append(type_dict)


    if include_parents:
        parent_classes = getParentClasses(class_el, only_native_classes=False, only_loaded_classes=False)

        for parent_dict in parent_classes:

            small_parent_dict = OrderedDict([])
            small_parent_dict['class_name'] = parent_dict['class_name']
            small_parent_dict['el']         = gb.id_dict[parent_dict['id']]

            all_types.append(small_parent_dict)

    return all_types

# ====== END: getAllTypesInClass ========



# ====== getMemberElements ========

def getMemberElements(el, include_artificial=False):

    member_elements = []

    if 'members' in el.keys():
        for mem_id in el.get('members').split():
            mem_el = gb.id_dict[mem_id]

            # Check if this member element should be ditched
            if mem_el.tag in ['Method', 'OperatorMethod', 'Field', 'Variable']:
                if 'name' in mem_el.keys():
                    namespaces_list = getNamespaces(mem_el, include_self=True)
                    full_name = '::'.join(namespaces_list)
                    if full_name in cfg.ditch:
                        continue

            if include_artificial:
                member_elements.append(mem_el)
            else:
                if not 'artificial' in mem_el.keys():
                    member_elements.append(mem_el)

    return member_elements

# ====== END: getMemberElements ========



# ====== getMemberFunctions ========

def getMemberFunctions(class_el, include_artificial=False, include_inherited=False, only_accepted=True, limit_pointerness=True, include_operators=False):

    import modules.funcutils as funcutils

    all_classes   = [class_el]
    all_members   = []
    all_functions = []

    # If include_inherited=True, append all (native) parent classes
    # the list 'all_classes'
    if include_inherited:
        parent_classes = getAllParentClasses(class_el, only_loaded_classes=True)
        all_classes = all_classes + parent_classes

    # Get all member elements
    for el in all_classes:
        class_members = getMemberElements(el, include_artificial=include_artificial)
        all_members = all_members + class_members

    # Extract only regular member functions (no variables, constructors, destructors, ...)
    for mem_el in all_members:
        if (mem_el.tag == 'Method' or (include_operators==True and mem_el.tag=='OperatorMethod')) and (mem_el.get('access') == 'public'):

            if only_accepted and funcutils.ignoreFunction(mem_el, limit_pointerness=limit_pointerness):
                method_name = mem_el.get('name')
                if mem_el.tag=='OperatorMethod':
                    method_name = 'operator' + method_name

                reason = "Makes use of a non-accepted type."
                infomsg.IgnoredFunction(method_name, reason).printMessage()
                continue

            else:
                all_functions.append(mem_el)

    return all_functions

# ====== END: getMemberFunctions ========



# ====== getAllTypesInFunction ========

def getAllTypesInFunction(func_el):

    import modules.classutils as classutils
    import modules.funcutils as funcutils

    all_types = []

    func_id = func_el.get('id')

    if func_el.tag in ['Function', 'Constructor']:
        args_list = funcutils.getArgs(func_el)
        for arg_dict in args_list:

            arg_type_el   = gb.id_dict[arg_dict['id']]
            arg_type_name = classutils.getClassNameDict(arg_type_el)

            arg_type_dict = OrderedDict([])
            arg_type_dict['class_name'] = arg_type_name
            arg_type_dict['el']         = arg_type_el

            all_types.append(arg_type_dict)

    if ('type' in func_el.keys()) or ('returns' in func_el.keys()) or (func_el.tag=='Constructor' and 'context' in func_el.keys()):

        mem_type_dict = findType(func_el)
        type_el = mem_type_dict['el']

        type_name = classutils.getClassNameDict(type_el)

        type_dict = OrderedDict([])
        type_dict['class_name'] = type_name
        type_dict['el']         = type_el

        all_types.append(type_dict)

    return all_types

# ====== END: getAllTypesInFunction ========



# ====== constrNamespace ========

def constrNamespace(namespaces, open_or_close, indent=cfg.indent):

    if len(namespaces) > 0:
        if namespaces[0] == '::':
            namespaces = namespaces[1:]

    code = ''

    if open_or_close == 'open':
        n_indents = 0
        for ns in namespaces:
            code += ' '*n_indents*indent + 'namespace ' + ns + '\n'
            code += ' '*n_indents*indent + '{' + '\n'
            n_indents += 1

    elif open_or_close == 'close':
        n_indents = len(namespaces)
        for ns in namespaces:
            n_indents -= 1
            code += ' '*n_indents*indent + '}' + '\n'

    else:
        raise ValueError("Second argument must be either 'open' or 'close'.")

    return code

# ====== END: constrNamespace ========



# ====== pointerAndRefCheck ========

def pointerAndRefCheck(input_type, byname=False):

    #
    # Input type should either be an XML element (byname=False)
    # or a string (byname=True)
    #

    if byname:
        type_name = input_type

        # Remove template bracket
        if '<' in type_name:
            type_name = type_name.split('<',1)[0] + type_name.rsplit('>',1)[-1]

        # Check pointerness
        pointerness = type_name.count('*')

        # Check reference
        is_reference = bool('&' in type_name)

    else:
        type_dict = findType(input_type)
        pointerness  = type_dict['pointerness']
        is_reference = type_dict['is_reference']


    return pointerness, is_reference

# ====== END: pointerAndRefCheck ========



# ====== addIncludeGuard ========

def addIncludeGuard(code, file_name, prefix='', suffix='', uppercase=False):

    file_name = file_name.rstrip('.FOR_GAMBIT')

    if suffix == '':
        guard_var = '__' + (prefix + '__')*bool(len(prefix)) + file_name.replace('.','_') + '__'
    else:
        file_name_no_ext, file_ext = os.path.splitext(file_name)
        guard_var = '__' + (prefix + '__')*bool(len(prefix)) + file_name_no_ext.replace('.','_') + '_' + suffix + file_ext.replace('.','_')  + '__'

    # file_name_no_ext, file_ext = os.path.splitext(os.path.basename(file_name))
    # guard_var = '__' + (prefix + '__')*bool(len(prefix)) + file_name_no_ext.replace('.','_') + ('__' + suffix)*bool(len(suffix)) + '__'


    if uppercase:
        guard_var = guard_var.upper()


    guard_code_top    = '#ifndef ' + guard_var + '\n' + '#define ' + guard_var + '\n'
    guard_code_bottom = '#endif /* ' + guard_var + ' */\n'

    new_code = guard_code_top + '\n' + code + '\n' + guard_code_bottom

    return new_code

# ====== END: addIncludeGuard ========



# ====== identifyIncludedHeaders ========

def identifyIncludedHeaders(content, only_native=True):

    return_dict = OrderedDict()

    # Remove comments
    content      = removeComments(content, insert_blanks=True)
    content_list = content.split('\n')

    # Parse content and identify header file names
    headers_in_file = []
    for line in content_list:

        line = line.strip()

        if line[0:8] == '#include':

            # Make sure there's a whitespace after '#include' 
            if line[8] != ' ':
                line = line[0:8] + ' ' + line[8:]

            header_file_name = line.split()[1]

            # Skip standard headers (of the form: #include <FILENAME>)
            if header_file_name[0] == '<':
                continue
            else:
                header_file_name = header_file_name.strip('"')
                headers_in_file.append(os.path.basename(header_file_name))
        else:
            continue

    # Connect with XML elements
    for check_file_path, file_el in gb.file_dict.items():

        # - If only_native=True, check for match with cfg.base_paths
        if only_native:
            is_native_file = False
            for accepted_path in cfg.base_paths:
                if accepted_path in os.path.dirname(check_file_path):
                    is_native_file = True
                    break
            if not is_native_file:
                continue

        # - Cut down to file name only
        check_file_name = os.path.basename(check_file_path)

        # - Keep XML id if the corresponding file name mathces with an identified header
        if check_file_name in headers_in_file:
            return_dict[check_file_name] = file_el.get('id')

    return return_dict

# ====== END: identifyIncludedHeaders ========



# ====== isHeader ========

def isHeader(file_el):

    is_header = False

    file_name = file_el.get('name')
    extension = os.path.splitext(file_name)[1]

    if extension != '':
        if extension.lower() in ['.hpp', '.h', '.hh', '.hxx', cfg.header_extension.lower()]:
            is_header = True

    return is_header

# ====== END: isHeader ========



# ====== getIncludeStatements ========

def getIncludeStatements(input_el, convert_loaded_to='none', exclude_types=[],
                         input_element='class', forward_declared='exclude',
                         use_full_path=False, include_parents=False):

    include_statements = []

    # Check string arguments
    convert_loaded_to = convert_loaded_to.lower()
    input_element     = input_element.lower()
    forward_declared  = forward_declared.lower()
    if convert_loaded_to not in ['none', 'abstract', 'wrapper', 'wrapper_decl', 'wrapper_def']:
        raise Exception("getIncludeStatements: Keyword argument 'convert_loaded_to=' must be either 'none', 'abstract', 'wrapper', 'wrapper_decl' or 'wrapper_def'.")
    if input_element not in ['class', 'function']:
        raise Exception("getIncludeStatements: Keyword argument 'input_element=' must be either 'class' or 'function'.")
    if forward_declared not in ['include', 'exclude', 'only']:
        raise Exception("getIncludeStatements: Keyword argument 'forward_declared=' must be either 'include', 'exclude' or 'only'.")


    # Get list of all types used in this class/function (each entry is a dict)
    if input_element == 'class':
        all_types = getAllTypesInClass(input_el, include_parents=include_parents)
    elif input_element == 'function':
        all_types = getAllTypesInFunction(input_el)

    # Get file name and line number of the current class/function
    start_line_number = int( input_el.get('line') )
    start_file_el     = gb.id_dict[ input_el.get('file') ]
    start_file_path   = start_file_el.get('name')

    # Read file from beginning to position of class/function definition
    start_file         = open(start_file_path, 'r')
    start_file_content = start_file.readlines()[0:start_line_number]
    start_file_content = ''.join(start_file_content)
    start_file.close()

    # Identify included header files from this file (utils.identifyIncludedHeaders returns a dict of the form {header_file_name: xml_id})
    included_headers_dict = identifyIncludedHeaders(start_file_content, only_native=True)

    # Move up the header tree and identify all the relevant (native) included headers
    header_paths = [ gb.id_dict[file_id].get('name') for file_id in included_headers_dict.values() ]
    header_paths_done = []

    while len(header_paths) > 0:

        header_path = header_paths.pop()

        # Read header
        header         = open(header_path, 'r')
        header_content = header.read()
        header.close()

        # Identify new headers
        new_included_headers = identifyIncludedHeaders(header_content, only_native=True)

        # Add any new headers to included_headers_dict
        for file_name, file_id in new_included_headers.items():
            if file_name not in included_headers_dict.keys():
                included_headers_dict[file_name] = file_id

        # Add any new headers to the list of header files to check
        new_header_paths = [ gb.id_dict[header_id].get('name') for header_id in new_included_headers.values() ]
        for new_path in new_header_paths:
            if (new_path not in header_paths) and (new_path not in header_paths_done):
                header_paths.append(new_path)

        # Keep track of headers we've done
        header_paths_done.append(header_path)


    # Determine what include statements to generate:

    for type_dict in all_types:

        type_el   = type_dict['el']
        type_name = type_dict['class_name']

        if type_name in exclude_types:
            continue

        if isAcceptedType(type_el):

            if isFundamental(type_el):
                pass

            elif isEnumeration(type_el):
                pass

            elif isLoadedClass(type_el):

                # For each loaded class used in this class/function, check whether the corresponding class definition can be
                # found in the current file (above current class/function) or among the included headers. If no such class
                # definition is found, it must be a case of simply using forward declaration.

                # TODO: Why isn't it enough just to check for the 'incomplete' key in the type_el?
                #       Need to check this again...

                type_file_id = type_el.get('file')
                type_line_number = int(type_el.get('line'))

                if ('incomplete' in type_el.keys() and type_el.get('incomplete')=='1'):
                    is_incomplete = True
                else:
                    is_incomplete = False

                if (type_file_id in included_headers_dict.values()) :
                    type_definition_found = True
                elif (type_file_id == input_el.get('file')) and (type_line_number < start_line_number):
                    type_definition_found = True
                else:
                    type_definition_found = False

                if (not type_definition_found) and (forward_declared=='exclude'):
                    # This must be a case of a type that is only forward declared. Don't include any header (as this will typically lead to a 'header loop').
                    continue

                elif (type_definition_found) and (forward_declared=='only'):
                    # This must be a case of a type that *is* fully declared, so we ignore it if forward_declared=='only'.
                    continue
                else:
                    if convert_loaded_to == 'none':

                        type_file_el = gb.id_dict[type_file_id]
                        type_file_full_path = type_file_el.get('name')

                        # If the xml element we have for the type is only for a forward declaration
                        # we must search all other xml files for the complete type declaration.
                        if is_incomplete:
                            for xml_file_name in gb.all_name_dict.keys():
                                try:
                                    new_type_el = gb.all_name_dict[xml_file_name][type_name['long_templ']]
                                except KeyError:
                                    new_type_el = None

                                if new_type_el is not None:
                                    if 'incomplete' not in new_type_el.keys():
                                        new_type_file_id = new_type_el.get('file')
                                        new_type_file_el = gb.all_id_dict[xml_file_name][new_type_file_id]
                                        # Set new header path and break the loop
                                        type_file_full_path = new_type_file_el.get('name')
                                        break

                        if isHeader(type_file_el):
                            use_path = shortenHeaderPath(type_file_full_path)
                            include_statements.append( '#include "' + use_path + '"')

                        else:
                            reason = "Found declaration of loaded type '%s' in file '%s', but this file is not recognized as a header file." % (type_name['long_templ'], type_file_full_path)
                            infomsg.NoIncludeStatementGenerated(type_name['long_templ'], reason).printMessage()

                    else:
                        if use_full_path:
                            header_key = convert_loaded_to + '_fullpath'
                        else:
                            header_key = convert_loaded_to

                        include_statements.append('#include "' + gb.new_header_files[type_name['long']][header_key] + '"')

            elif isStdType(type_el):
                if type_name['long'] in gb.std_headers:
                    header_name = gb.std_headers[type_name['long']].strip()
                    if (header_name[0] == '<') and (header_name[-1] == '>'):
                        include_statements.append('#include ' + gb.std_headers[type_name['long']])
                    else:
                        include_statements.append('#include "' + gb.std_headers[type_name['long']] + '"')
                else:
                    reason = "The standard type '%s' has no specified header file. Please update modules/gb.py." % type_name['long_templ']
                    infomsg.NoIncludeStatementGenerated(type_name['long_templ'], reason).printMessage()

            else:
                is_known, index = isInList(type_name['long_templ'], cfg.known_classes.keys(), return_index=True, ignore_whitespace=True)
                if not is_known:
                    is_known, index = isInList(type_name['long'], cfg.known_classes.keys(), return_index=True, ignore_whitespace=True)

                if is_known:
                    header_name = list(cfg.known_classes.values())[index]
                    if (header_name[0] == '<') and (header_name[-1] == '>'):
                        include_statements.append('#include ' + header_name)
                    else:
                        include_statements.append('#include "' + header_name + '"')
                else:
                    reason = "The type '%s' has no specified header file. Please update the 'known_classes' dictionary in the config file." % type_name['long_templ']
                    infomsg.NoIncludeStatementGenerated(type_name['long_templ'], reason).printMessage()
        else:
            infomsg.NoIncludeStatementGenerated( type_name['long_templ'] ).printMessage()

    # Remove duplicates and return list (ordered)
    include_statements = list( OrderedDict.fromkeys(include_statements) )
    include_statements = orderIncludeStatements(include_statements)

    return include_statements

# ====== END: getIncludeStatements ========



# ====== getOriginalHeaderPath ========

def getOriginalHeaderPath(el, full_path=False):

    file_id = el.get('file')
    file_el = gb.id_dict[file_id]

    file_full_path = file_el.get('name')

    if isHeader(file_el):

        if full_path:
            header_path = file_full_path
        else:
            header_path = os.path.basename(file_full_path)

    else:
        raise exceptions.ReturnError("The file %s is not recognized as a header file." % file_full_path)

    return header_path

# ====== END: getOriginalHeaderPath ========



# ====== shortenHeaderPath ========

def shortenHeaderPath(full_path):

    shorter_path = full_path

    # Split path into individual parts
    path_parts = pathSplitAll(full_path)

    for incl_path in cfg.include_paths:

        incl_path_parts = pathSplitAll(incl_path)
        n = len(incl_path_parts)

        if len(path_parts) >= n:
            if path_parts[0:n] == incl_path_parts:
                shorter_path = os.path.join(*path_parts[n:])
                break

    return shorter_path

# ====== END: shortenHeaderPath ========



# ====== constrNamespaceFromTags ========

def constrNamespaceFromTags(content, new_namespace, open_tag, close_tag):

    new_namespace_list = new_namespace.split('::')

    new_content = content
    while True:

        prev_content = new_content

        # Find tag positions
        open_pos  = prev_content.find(open_tag)
        close_pos = prev_content.find(close_tag)

        if (open_pos == -1) and (close_pos == -1):
            # No tags found. Do nothing more.
            # return content
            break
        elif (open_pos == -1) or (close_pos == -1):
            raise Exception('Matching pair of namespace tags %s and %s not found in given content.' % (open_tag, close_tag))
        else:
            pass

        # Split content into three parts
        content_before = prev_content[:open_pos]
        content_within = prev_content[open_pos:close_pos]
        content_after  = prev_content[close_pos:]

        # Remove the namespace tags
        content_within = content_within.replace(open_tag, '')
        content_after  = content_after.replace(close_tag, '', 1)

        # Add indentation to middle part
        content_within = addIndentation(content_within, cfg.indent*len(new_namespace_list))

        # Contruct new namespace and combine code
        open_new_namespace_code  = constrNamespace(new_namespace_list, 'open', indent=cfg.indent)
        close_new_namespace_code = constrNamespace(new_namespace_list, 'close', indent=cfg.indent)

        new_content = content_before + open_new_namespace_code + content_within + close_new_namespace_code + content_after

    return new_content

# ====== END: constrNamespaceFromTags ========



# ====== replaceCodeTags ========

def replaceCodeTags(input, file_input=False, write_file=False):

    # Input is either a file name or a string with content
    if file_input:
        f = open(input, 'r')
        new_content = f.read()
        f.close()
    else:
        new_content = input

    # Replace various tags in template code with code specific for the current backend
    new_content = new_content.replace('__BACKEND_NAME__'         ,  cfg.gambit_backend_name)
    new_content = new_content.replace('__BACKEND_VERSION__'      ,  cfg.gambit_backend_version)
    new_content = new_content.replace('__BACKEND_SAFE_VERSION__' ,  gb.gambit_backend_safeversion)
    new_content = new_content.replace('__BACKEND_REFERENCE__'    ,  cfg.gambit_backend_reference)
    new_content = new_content.replace('__CODE_SUFFIX__'          ,  gb.code_suffix)

    new_content = new_content.replace('__PATH_TO_FRWD_DECLS_ABS_CLASSES_HEADER__', os.path.join(gb.backend_types_basedir, gb.gambit_backend_name_full, gb.frwd_decls_abs_fname + cfg.header_extension))
    new_content = new_content.replace('__PATH_TO_IDENTIFICATION_HEADER__'        , os.path.join(gb.backend_types_basedir, gb.gambit_backend_name_full, 'identification.hpp'))
    new_content = new_content.replace('__PATH_TO_BACKEND_UNDEFS_HEADER__'        , os.path.join(gb.gambit_backend_incl_dir, "backend_undefs.hpp"))


    # Should a new file be written?
    if file_input and write_file:
        f.open(input,'w')
        f.write(new_content)
        f.close()

    # Return code
    return new_content

# ====== END: replaceCodeTags ========



# ====== removeCodeTags ========

def removeCodeTags(content, remove_tags_list):

    new_content = content
    # Remove tags
    for tag in remove_tags_list:
        new_content = new_content.replace(tag, '')

    # Return code
    return new_content

# ====== END: removeCodeTags ========



# ====== constrLoadedTypesHeaderContent ======

def constrLoadedTypesHeaderContent():

    #
    # Construct the code lines for the loaded classes, containg all the factory symbols and argument brackets for that class
    #
    class_lines = []

    # Loop over all classes
    for class_name in gb.classes_done:

        if not class_name['long'] in gb.factory_info.keys():
            reason = "Probably there are no public and accepted constructors."
            infomsg.NoLoadedTypesEntry(class_name['long'], reason).printMessage()

        else:

            class_line = '  (( /*class*/'

            namespace, class_name_short = removeNamespace(class_name['long'], return_namespace=True)

            if namespace == '':
                namespace_list = []
            else:
                namespace_list = namespace.split('::')

            for ns_part in namespace_list:
                class_line += '(' + ns_part + ')'

            class_line += '(' + class_name['short'] + '),'


            class_line += '    /*constructors*/'

            for info_dict in gb.factory_info[ class_name['long'] ]:
                class_line += '(("' + info_dict['name'] + '",' + info_dict['args_bracket'].replace(' ::', ' ').replace('(::', '(') + ')) '

            class_line += ')) \\'
            class_lines.append(class_line)

    class_lines_code  = ''
    class_lines_code += '#define ' + gb.gambit_backend_name_full + '_all_data \\\n'
    class_lines_code += '\n'.join(class_lines) + '\n'


    #
    # Construct include guards with additional  ' 1' appended to the line starting with #define
    #
    incl_guard = addIncludeGuard('', 'loaded_types.hpp', prefix='', suffix=gb.gambit_backend_name_full)
    incl_guard_lines = incl_guard.split('\n')

    incl_guard_start = '\n'.join(incl_guard_lines[:2]) + ' 1\n'
    incl_guard_end   = incl_guard_lines[-2] + '\n'


    #
    # Construct include statements, surrounded by pragma directives
    #
    incl_statements_code = ''

    for pragma_directive in cfg.pragmas_begin:
        incl_statements_code += pragma_directive.strip() + '\n'

    for class_name in gb.classes_done:
        if class_name['long'] in gb.factory_info.keys():
            namespace, class_name_short = removeNamespace(class_name['long'], return_namespace=True)
            incl_statements_code += '#include "' + gb.wrapper_header_prefix + class_name['short'] + cfg.header_extension + '"\n'
    incl_statements_code += '#include "identification.hpp"\n'

    for pragma_directive in cfg.pragmas_end:
        incl_statements_code += pragma_directive.strip() + '\n'


    #
    # Combine everything to construct header code
    #
    code  = ''
    code += incl_guard_start

    code += '\n'
    code += incl_statements_code

    code += '\n'
    code += '// Indicate which types are provided by this backend, and what the symbols of their factories are.\n'
    code += class_lines_code

    code += '\n'
    code += '// If the default version has been loaded, set it as default.\n'
    code += '#if ALREADY_LOADED(CAT_3(BACKENDNAME,_,CAT(Default_,BACKENDNAME)))\n'
    code += '  SET_DEFAULT_VERSION_FOR_LOADING_TYPES(BACKENDNAME,SAFE_VERSION,CAT(Default_,BACKENDNAME))\n'
    code += '#endif\n'

    code += '\n'
    code += '// Undefine macros to avoid conflict with other backends.\n'
    code += '#include "' + os.path.join(gb.gambit_backend_incl_dir, "backend_undefs.hpp") + '"\n'

    code += '\n'
    code += incl_guard_end

    return code

# ====== END: constrLoadedTypesHeaderContent ======



# ====== constrEnumDeclHeader ========

def constrEnumDeclHeader(enum_el_list, file_output_path):

    import modules.classutils as classutils

    # If this is the first time this function is executed, read initial code from header_templates/ folder
    if file_output_path not in gb.new_code.keys():
        f = open(gb.boss_dir+'/header_templates/standard_header_template.hpp')
        initial_code = f.read()
        f.close()
        initial_code_tuple = (0, initial_code)
        gb.new_code[file_output_path] = {'code_tuples':[initial_code_tuple], 'add_include_guard':True}


    current_code = gb.new_code[file_output_path]['code_tuples'][0][1]

    insert_code = ''
    tag_pos = current_code.find('__INSERT_CODE_HERE__')

    for enum_el in enum_el_list:

        # Skip any enumerated type that is not native to the external code
        if not isNative(enum_el):
            continue

        # Get enum names and values
        enum_members_list = []
        for sub_el in list(enum_el):
            if sub_el.tag == 'EnumValue':
                member_string = sub_el.get('name') + '=' + sub_el.get('init')
                enum_members_list.append(member_string)
        all_members_string = ', '.join(enum_members_list)

        # Get namespace list
        namespace_list = getNamespaces(enum_el)

        n_indents = len(namespace_list)

        # - Open namespace
        insert_code += constrNamespace(namespace_list, 'open')

        insert_code += ' '*n_indents*cfg.indent + 'typedef enum {' + all_members_string + '} ' + enum_el.get('name') + ';\n'

        # - Close namespace
        insert_code += constrNamespace(namespace_list, 'close')

    new_code = current_code[:tag_pos] + insert_code + current_code[tag_pos:]


    # Replace other code tags
    new_code = replaceCodeTags(new_code)


    new_code_tuple = (0,new_code)

    # Overwrite existing code tuple
    gb.new_code[file_output_path]['code_tuples'] = [(new_code_tuple)]

# ====== END: constrEnumDeclHeader ========



# ====== castxmlRunner ========

# Calls castxml from the shell (via modules.shelltimeout).

def castxmlRunner(input_file_path, include_paths_list, xml_output_path, use_castxml_path=None):

    # Avoid including intel headers when in "gnu mode" by
    # temporarily unsetting some environment variables
    if 'gnu' in cfg.castxml_cc_id:
        temp_env_vars = {}
        for var_name in ['CPATH', 'C_INCLUDE_PATH', 'CPLUS_INCLUDE_PATH']:
            try:
                if 'intel' in os.environ[var_name].lower():
                    temp_env_vars[var_name] = str(os.environ[var_name])
                    os.environ[var_name] = ''
            except KeyError:
                pass


    # Set the use_castxml_path if it is not already set
    castxml_system_path = 'castxml'
    castxml_local_path = os.path.join(gb.boss_dir,"castxml/bin/castxml")
    if use_castxml_path is None:
        if gb.has_castxml_system:
            use_castxml_path = castxml_system_path
        elif gb.has_castxml_local:
            use_castxml_path = castxml_local_path
        else:
            raise Exception('No castxml binary found.')


    # Construct castxml command to run
    castxml_cmd = use_castxml_path + ' --castxml-gccxml -x c++'

    # Add castxml settings from cfg file
    castxml_cmd += ' --castxml-cc-' + cfg.castxml_cc_id + ' "(" ' + cfg.castxml_cc
    if cfg.castxml_cc_opt != '':
        castxml_cmd += ' ' + cfg.castxml_cc_opt
    castxml_cmd += ' ")" '

    # - Add include paths
    for incl_path in include_paths_list:
        castxml_cmd += ' -I' + incl_path

    # - Add standard include paths
    for std_incl_path in gb.std_include_paths:
        castxml_cmd += ' -I' + std_incl_path

    # - Add the input file path (full path)
    castxml_cmd += ' ' + input_file_path

    # - Add castxml option that specifies the xml output file: input_file_short_name.xml
    castxml_cmd += ' -o ' + xml_output_path

    # Run castxml
    print('  Running command: ' + castxml_cmd)

    did_fail = False
    error_message = ''
    output_tmpfile = tempfile.TemporaryFile()
    try:
        p = subprocess.Popen(shlex.split(castxml_cmd), stdout=output_tmpfile, stderr=output_tmpfile)
        p.wait()
    except subprocess.CalledProcessError as e:
        did_fail = True
        error_message = e.message

    # Reset environment variables
    if 'gnu' in cfg.castxml_cc_id:
        for var_name, value in temp_env_vars.items():
            os.environ[var_name] = value

    # Get output from tempfile
    output_tmpfile.seek(0)
    output = output_tmpfile.read()
    output_tmpfile.close()

    # Any error that did not result in a CalledProcessError?
    if (p.returncode != 0):
        did_fail = True

    if did_fail:
        print('  ' + modifyText('ERROR: CastXML failed.','red'))       
        # Get error message
        print()
        print(modifyText('START CASTXML OUTPUT','underline'))
        print()
        print(output)
        print(modifyText('END CASTXML OUTPUT','underline'))
        print()
        if error_message != '':
            print("CalledProcessError.message:", error_message)
            print()

    # If it fails with the syste-wide castxml binary, try again with the local one
    if (did_fail and use_castxml_path==castxml_system_path and gb.has_castxml_local):
        print('  ' + modifyText('Will retry with castxml binary in ' + castxml_local_path,'yellow') )
        did_fail = False
        use_castxml_path = castxml_local_path
        castxmlRunner(input_file_path, include_paths_list, xml_output_path, use_castxml_path=use_castxml_path)


    # If it fails with icpc, try again with g++.
    if ("gnu" in cfg.castxml_cc_id) and ("icpc" in cfg.castxml_cc) and did_fail:
        print()
        print('  ' + modifyText('Will retry with --castxml-cc=g++','yellow'))
        print()
        cfg.castxml_cc = 'g++'
        castxmlRunner(input_file_path, include_paths_list, xml_output_path, use_castxml_path=use_castxml_path)
    # Print error report
    elif did_fail:
        raise Exception('castxml failed')

    else:
        print('  ' + modifyText('Command finished successfully.','green'))
    print()

# ====== END: castxmlRunner ========




# ====== pathSplitAll ========

def pathSplitAll(path):

    all_parts = []

    current_path = path
    while True:

        parts = os.path.split(current_path)

        if parts[0] == current_path:  # Stopping criterion for absolute paths
            all_parts.insert(0, parts[0])
            break

        elif parts[1] == current_path: # Stopping criterion for relative paths
            all_parts.insert(0, parts[1])
            break

        else:
            current_path = parts[0]
            all_parts.insert(0, parts[1])

    return all_parts

# ====== pathSplitAll ========



# ====== fillAcceptedTypesList ========

def fillAcceptedTypesList():

    import modules.classutils as classutils

    # Sets to store type names
    fundamental_types = set()
    std_types         = set()
    known_classes     = set()
    # enumeration_types = set()
    loaded_classes    = set()

    # Keep track of how many types have been checked
    type_counter = 0
    print()

    #
    # Collect names of all fundamental, std, enumeration, known and loaded types that are acceptable
    #
    for xml_file in gb.all_id_dict.keys():

        # Reset some variables for each new xml file
        new_fundamental_types   = []
        new_std_types           = []
        new_known_classes       = []
        # new_enumeration_types   = []
        new_loaded_classes      = []


        initGlobalXMLdicts(xml_file)


        #
        # Loop over all named elements in the xml file
        #

        for full_name, el in gb.name_dict.items():


            # Only consider types
            if el.tag not in ['Class', 'Struct', 'FundamentalType', 'Enumeration']:
                continue

            type_counter += 1
            if type_counter%500 == 0:
                print('  - %i types classified...' % (type_counter))


            # To save a bit of time, construct class name dict once and pass to remaining checks
            class_name = classutils.getClassNameDict(el)


            # Skip problematic types
            if isProblematicType(el):
                continue

            #
            # Known class?
            #
            is_known_class = isKnownClass(el, class_name=class_name)
            if is_known_class:
                new_known_classes.append(full_name)


            # Skip incomplete types
            if ('incomplete' in el.keys()) and (el.get('incomplete') == '1'):
                continue

            #
            # Fundamental type?
            #
            is_fundamental = isFundamental(el)
            if is_fundamental:
                new_fundamental_types.append(full_name)


            #
            # Std type?
            #
            is_std_type = isStdType(el, class_name=class_name)
            if is_std_type:
                new_std_types.append(full_name)


            # #
            # # Enumeration type?
            # #
            # is_enumeration = isEnumeration(el)
            # if is_enumeration:
            #     new_enumeration_types.append( '::'.join( getNamespaces(el, include_self=True) ) )


            #
            # Loaded type?
            #
            is_loaded_class = isLoadedClass(el, byname=False, class_name=class_name)
            if is_loaded_class:
                new_loaded_classes.append(full_name)



        #
        # Update sets of types
        #
        fundamental_types = fundamental_types.union(set(new_fundamental_types))
        std_types         = std_types.union(set(new_std_types))
        known_classes     = known_classes.union(set(new_known_classes))
        # enumeration_types = enumeration_types.union(set(new_enumeration_types))
        loaded_classes    = loaded_classes.union(set(new_loaded_classes))


    # Print final number of types classified
    print('  - %i types classified.' % (type_counter))

    # Fill global list
    gb.accepted_types = list(loaded_classes) + list(known_classes) + list(fundamental_types) + list(std_types)
    # gb.accepted_types = list(loaded_classes) + list(fundamental_types) + list(std_types) + list(enumeration_types)

# ====== END: fillAcceptedTypesList ========




# ====== isProblematicType ========

# Use this to identify types that BOSS currently have problems with,
# but that (hopefully) should be fixed in the future.

def isProblematicType(el):

    is_problematic = False


    #
    # Check: types that use native types as template arguments
    #

    if el.tag in ['Class', 'Struct', 'FundamentalType']:

        # Get list of all template arguments (unpack any nested template arguments)
        unpacked_template_args = getAllTemplateTypes(el.get('name'))

        # If no template arguments, continue
        if unpacked_template_args == []:
            pass

        else:
            for templ_arg in unpacked_template_args:

                # Remove asterix and/or ampersand
                base_templ_arg = getBasicTypeName(templ_arg)

                # Check that this type name is listed in gb.name_dict
                try:
                    type_el = gb.name_dict[base_templ_arg]
                except KeyError:
                    type_el = None

                if type_el is not None:

                    # If this is a native type, the input type is problematic for BOSS
                    if isNative(type_el):

                        is_problematic = True
                        return is_problematic

    return is_problematic

# ====== END: isProblematicType ========



# ====== addParentClasses ========

# Adds parent classes to cfg.load_classes.

def addParentClasses():

    import modules.classutils as classutils

    for xml_file in gb.all_id_dict.keys():

        # If new xml file, initialise global dicts
        if xml_file != gb.xml_file_name:
            gb.xml_file_name = xml_file
            initGlobalXMLdicts(xml_file, id_and_name_only=True)

        # Loop over all named elements in the xml file
        for full_name, el in gb.name_dict.items():

            if el.tag in ['Class', 'Struct']:

                if isLoadedClass(el):

                    parents_el_list = getAllParentClasses(el, only_native_classes=True)

                    for parent_el in parents_el_list:

                        # Skip classes that are not loadable (incomplete, abstract, ...)
                        if not isLoadable(el, print_warning=True):
                            continue

                        class_name = classutils.getClassNameDict(parent_el)

                        # - Update cfg.load_classes
                        if class_name['long_templ'] not in cfg.load_classes:
                            cfg.load_classes.append(class_name['long_templ'])

# ====== END: addParentClasses ========




# ====== fillParentsOfLoadedClassesList ========

# Adds parent classes to cfg.load_classes.

def fillParentsOfLoadedClassesList():

    import modules.classutils as classutils

    messages = []

    for xml_file in gb.all_id_dict.keys():

        # If new xml file, initialise global dicts
        if xml_file != gb.xml_file_name:
            gb.xml_file_name = xml_file
            initGlobalXMLdicts(xml_file, id_and_name_only=True)

        # Loop over all named elements in the xml file
        for full_name, el in gb.name_dict.items():

            if el.tag in ['Class', 'Struct']:

                if isLoadedClass(el):

                    parents_el_list = getAllParentClasses(el, only_native_classes=True)

                    for parent_el in parents_el_list:

                        # Skip classes that are not loadable (incomplete, abstract, ...)
                        if not isLoadable(parent_el, print_warning=True, check_pure_virtual_members=False):
                            continue

                        class_name = classutils.getClassNameDict(parent_el)

                        # Append to gb.parents_of_loaded_classes
                        if class_name['long_templ'] not in gb.parents_of_loaded_classes:
                            gb.parents_of_loaded_classes.append(class_name['long_templ'])

                        # Print info
                        msg = '  - %s is parent of %s.' % (class_name['long_templ'], full_name)
                        if msg not in messages:
                            print(msg)
                            messages.append(msg)

# ====== END: fillParentsOfLoadedClassesList ========




# ====== xmlFilesToDicts ========

    #
    # Read all xml elements of all files and store in two dict of dicts:
    #
    # 1. all_id_dict:    file name --> xml id --> xml element
    # 2. all_name_dict:  file name --> name   --> xml element
    #

def xmlFilesToDicts(xml_files):

    for xml_file in xml_files:

        gb.all_id_dict[xml_file]   = OrderedDict()
        gb.all_name_dict[xml_file] = OrderedDict()

        tree = ET.parse(xml_file)
        root = tree.getroot()

        for el in list(root):

            # Fill id-based dict
            gb.all_id_dict[xml_file][el.get('id')] = el

        for el in list(root):

            # Determine name
            if 'name' in el.keys():
                namespaces_list = getNamespaces(el, include_self=True, xml_file_name=xml_file)
                full_name = '::'.join(namespaces_list)
            else:
                # Skip elements that don't have a name
                continue

            # Fill name-based dict
            gb.all_name_dict[xml_file][full_name] = el

# ====== END: xmlFilesToDicts ========



# ====== clearGlobalXMLdicts ========

def clearGlobalXMLdicts():

    # Clear a bunch of global dicts
    gb.id_dict.clear()
    gb.name_dict.clear()

    gb.file_dict.clear()
    gb.std_types_dict.clear()
    gb.typedef_dict.clear()
    gb.loaded_classes_in_xml.clear()
    gb.func_dict.clear()

# ====== END: clearGlobalXMLdicts ========



# ====== initGlobalXMLdicts ========

def initGlobalXMLdicts(xml_path, id_and_name_only=False):

    import modules.classutils as classutils
    import modules.funcutils as funcutils

    # Clear dicts
    clearGlobalXMLdicts()

    # Set some global dicts directly
    gb.id_dict   = copy.deepcopy( gb.all_id_dict[xml_path] )
    gb.name_dict = copy.deepcopy( gb.all_name_dict[xml_path] )

    # Stop here?
    if id_and_name_only:
        return


    #
    # Loop over all elements in this xml file
    # to fill the remaining dicts. (The order is important!)
    #

    for xml_id, el in gb.id_dict.items():


        # Update global dict: file name --> file xml element
        if el.tag == 'File':
            gb.file_dict[el.get('name')] = el


        # Update global dict: std type --> type xml element
        if isStdType(el):
            class_name = classutils.getClassNameDict(el)
            gb.std_types_dict[class_name['long_templ']] = el


        # Update global dict of loaded classes in this xml file: class name --> class xml element
        if el.tag in ['Class', 'Struct']:

            try:
                class_name = classutils.getClassNameDict(el)
            except KeyError:
                continue

            # Check if we have done this class already
            if class_name in gb.classes_done:
                infomsg.ClassAlreadyDone( class_name['long_templ'] ).printMessage()
                continue

            # Check that class is requested
            if (class_name['long_templ'] in cfg.load_classes):

                # Check that class is complete
                if isComplete(el):

                    # Store class xml element
                    gb.loaded_classes_in_xml[class_name['long_templ']] = el


        # Update global dict: typedef name --> typedef xml element
        if el.tag == 'Typedef':

            # Only accept native typedefs:
            if isNative(el):

                typedef_name = el.get('name')

                type_dict = findType(el)
                type_el = type_dict['el']

                # If underlying type is a fundamental or standard type, accept it right away
                if isFundamental(type_el) or isStdType(type_el):
                    gb.typedef_dict[typedef_name] = el

                # If underlying type is a class/struct, check if it's acceptable
                elif type_el.tag in ['Class', 'Struct']:

                    type_name = classutils.getClassNameDict(type_el)

                    if type_name['long_templ'] in cfg.load_classes:
                        gb.typedef_dict[typedef_name] = el

                # If neither fundamental or class/struct, ignore it.
                else:
                    pass


        # Update global dict: function name --> function xml element
        if el.tag == 'Function':

            try:
                func_name = funcutils.getFunctionNameDict(el)
            except KeyError:
                continue

            # Check if we have done this function already
            if func_name in gb.functions_done:
                infomsg.FunctionAlreadyDone( func_name['long_templ_args'] ).printMessage()
                continue

            if func_name['long_templ_args'] in cfg.load_functions:
                gb.func_dict[func_name['long_templ_args']] = el



        # Add entries to global dict: new header files
        if el in gb.loaded_classes_in_xml.values():

            class_name = classutils.getClassNameDict(el)

            class_name_short = class_name['short']
            class_name_long  = class_name['long']

            if class_name_long not in gb.new_header_files.keys():

                abstract_header_name     = gb.abstr_header_prefix + class_name_short + cfg.header_extension
                wrapper_header_name      = gb.wrapper_header_prefix + class_name_short + cfg.header_extension
                wrapper_decl_header_name = gb.wrapper_header_prefix + class_name_short + '_decl' + cfg.header_extension
                wrapper_def_header_name  = gb.wrapper_header_prefix + class_name_short + '_def'  + cfg.header_extension

                abstract_header_fullpath     = os.path.join(gb.backend_types_basedir, gb.gambit_backend_name_full, gb.abstr_header_prefix + class_name_short + cfg.header_extension )
                wrapper_header_fullpath      = os.path.join(gb.backend_types_basedir, gb.gambit_backend_name_full, gb.wrapper_header_prefix + class_name_short + cfg.header_extension )
                wrapper_decl_header_fullpath = os.path.join(gb.backend_types_basedir, gb.gambit_backend_name_full, gb.wrapper_header_prefix + class_name_short + '_decl' + cfg.header_extension )
                wrapper_def_header_fullpath  = os.path.join(gb.backend_types_basedir, gb.gambit_backend_name_full, gb.wrapper_header_prefix + class_name_short + '_def'  + cfg.header_extension )

                gb.new_header_files[class_name_long] = {    'abstract': abstract_header_name,
                                                            'wrapper': wrapper_header_name,
                                                            'wrapper_decl': wrapper_decl_header_name,
                                                            'wrapper_def': wrapper_def_header_name,
                                                            'abstract_fullpath': abstract_header_fullpath,
                                                            'wrapper_fullpath': wrapper_header_fullpath,
                                                            'wrapper_decl_fullpath': wrapper_decl_header_fullpath,
                                                            'wrapper_def_fullpath': wrapper_def_header_fullpath    }

    #
    # END: Loop over all elements in this xml file
    #


# ====== END: initGlobalXMLdicts ========




# ====== identifyStdIncludePaths ========

def identifyStdIncludePaths():

    # Shell command: Pipe an include statement to the compiler and use
    # verbose mode to print the header search paths.
    command = 'echo "#include <iostream>" | ' + cfg.castxml_cc + ' -v -x c++ -c -'

    # Run command
    print('  Running command: ' + command)

    did_fail = False
    error_message = ''
    output_tmpfile = tempfile.TemporaryFile()
    try:
        p = subprocess.Popen(shlex.split(command), stdout=output_tmpfile, stderr=output_tmpfile)
        p.wait()
    except subprocess.CalledProcessError as e:
        did_fail = True
        error_message = e.message

    # Reset environment variables
    if 'gnu' in cfg.castxml_cc_id:
        for var_name, value in temp_env_vars.items():
            os.environ[var_name] = value

    # Get output from tempfile
    output_tmpfile.seek(0)
    output = output_tmpfile.read()
    output_tmpfile.close()

    # Any error that did not result in a CalledProcessError?
    if p.returncode != 0:
        did_fail = True

    if did_fail:
        print('  ' + modifyText('ERROR: Shell command failed.','red'))
        # Get error message
        print()
        print(modifyText('START SHELL COMMAND OUTPUT','underline'))
        print()
        print(output)
        print(modifyText('END SHELL COMMAND OUTPUT','red'))
        print()
        if error_message != '':
            print("CalledProcessError.message:", error_message)
            print()
        raise Exception('Shell command failed')

    else:
        print('  ' + modifyText('Command finished successfully.','green'))
    print()


    std_include_paths = []
    output_lines = output.split('\n')

    try:
        start_i = output_lines.index("#include <...> search starts here:")
        end_i   = output_lines.index("End of search list.")
    except ValueError:
        print('  ' + modifyText('WARNING: Could not identify standard include paths.\n  Add them manually in the config file if necessary.','yellow'))
        print()
    else:
        for line in output_lines[start_i+1:end_i]:
            std_include_paths.append( line.strip().split()[0] )

        # Filter out Intel-specific paths to avoid conflict with gnu headers
        if (cfg.castxml_cc_id == 'gnu') or (cfg.castxml_cc_id == 'gnu-c'):

            len_before_filter = len(std_include_paths)
            std_include_paths = [path for path in std_include_paths if 'intel' not in path]
            len_after_filter = len(std_include_paths)

            if len_after_filter < len_before_filter:
                print('  (Filtered out Intel paths to avoid conflicts with gcc headers.)')
                print()

        print('  Identified %i standard include paths:' % len(std_include_paths))
        for path in std_include_paths:
            print('  - ' + path)
        print()

    # Set global list
    gb.std_include_paths = std_include_paths

# ====== END: identifyStdIncludePaths ========




# ====== isInList ========

def isInList(search_entry, search_list, return_index=True, ignore_whitespace=True):

    # In case search_list is passed in as a dict_keys object (Python3) 
    # instead of as a regular list, convert it to a list
    search_list = list(search_list)

    # Search for entry
    try:
        i = search_list.index(search_entry)
        if return_index:
            return True, i
        else:
            return True
    except ValueError:
        pass

    # Search for entry after removing all whitespace
    if ignore_whitespace:
        search_entry_no_ws = "".join(search_entry.split())
        search_list_no_ws = ["".join(e.split()) for e in search_list]
        try:
            i = search_list_no_ws.index(search_entry_no_ws)
            if return_index:
                return True, i
            else:
                return True
        except ValueError:
            pass

    # Entry not found
    if return_index:
        return False, -1
    else:
        return False

# ====== END: isInList ========




# ====== modifyText ========

def modifyText(msg, mod):

    if mod not in gb.textmods.keys():
        return msg

    msg = gb.textmods[mod] + msg + gb.textmods['end']

    return msg

# ====== END: modifyText ========


# ====== orderIncludeStatements ========

def orderIncludeStatements(include_statements):

    ordered_include_statements = []

    # This is not the fastest solution, but an easy way to 
    # to keep the existing order within each group of headers

    # Add standard headers (not Boost headers)
    for s in include_statements:
        if "<" in s:
            if "<boost/" not in s:
                ordered_include_statements.append(s)

    # Add BOSS-generated and/or backend-specific headers
    for s in include_statements:
        if "<" not in s:
            ordered_include_statements.append(s)

    # Add Boost headers
    for s in include_statements:
        if "<boost/" in s:
            ordered_include_statements.append(s)

    # Check that we haven't missed any include statements
    assert len(ordered_include_statements) == len(include_statements)

    # Return ordered list of include statements
    return ordered_include_statements

# ====== END: orderIncludeStatements ========

Updated on 2022-08-03 at 12:58:09 +0000