예제 #1
0
        all_function_names = []

        for xml_file in xml_files:

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

            # Set the global xml id dict. (Needed by the functions called from utils.)
            gb.id_dict = OrderedDict([(el.get('id'), el)
                                      for el in root.getchildren()])

            # Find all available classes
            for el in (root.findall('Class') + root.findall('Struct')):

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

                # Only list native classes
                if utils.isNative(el):
                    all_class_names.append(class_name['long_templ'])

            # Find all available functions
            for el in root.findall('Function'):

                try:
                    func_name = funcutils.getFunctionNameDict(el)
                except KeyError:
                    continue
예제 #2
0
def run():

    # Clear the module-level dict that keeps track of include statements
    includes.clear()

    #
    # Loop over all classes
    #

    for class_name_long, class_el in gb.loaded_classes_in_xml.items():

        # Clear all info messages
        infomsg.clearInfoMessages()

        # Generate dicts with different variations of the class name
        class_name = classutils.getClassNameDict(class_el)
        abstr_class_name = classutils.getClassNameDict(class_el, abstract=True)

        # Print current class
        print
        print '  ' + utils.modifyText(
            'Class:', 'underline') + ' ' + class_name['long_templ']

        # Check if this is a template class
        is_template = utils.isTemplateClass(class_el)

        # Make list of all types used in this class
        all_types_in_class = utils.getAllTypesInClass(class_el,
                                                      include_parents=True)

        # Set a bunch of generally useful variables
        original_class_file_el = gb.id_dict[class_el.get('file')]
        original_file_name = original_class_file_el.get('name')
        original_file_name_base = os.path.basename(original_file_name)
        original_class_file_dir = os.path.split(original_file_name)[0]
        extras_src_file_name = os.path.join(
            gb.boss_output_dir, gb.general_src_file_prefix +
            class_name['short'] + cfg.source_extension)

        short_abstr_class_fname = gb.new_header_files[
            class_name['long']]['abstract']
        abstr_class_fname = os.path.join(gb.boss_output_dir,
                                         short_abstr_class_fname)

        # namespaces    = class_name['long'].split('::')[:-1]
        namespaces = utils.getNamespaces(class_el, include_self=False)
        has_namespace = bool(len(namespaces))

        has_copy_constructor, copy_constructor_id = classutils.checkCopyConstructor(
            class_el, return_id=True)
        has_assignment_operator, assignment_is_artificial = classutils.checkAssignmentOperator(
            class_el)

        if has_assignment_operator and assignment_is_artificial:
            construct_assignment_operator = True
        else:
            construct_assignment_operator = False

        # Register paths of original files in global dict
        gb.original_file_paths[original_file_name_base] = original_file_name

        # Read content of original class file
        f = open(original_file_name, 'r')
        original_file_content = f.read()
        f.close()
        original_file_content_nocomments = utils.removeComments(
            original_file_content, insert_blanks=True)

        # Prepare entries in gb.new_code and includes
        if abstr_class_fname not in gb.new_code.keys():
            gb.new_code[abstr_class_fname] = {
                'code_tuples': [],
                'add_include_guard': True
            }
            gb.new_code[abstr_class_fname + '.FOR_GAMBIT'] = {
                'code_tuples': [],
                'add_include_guard': True
            }
        if original_file_name not in gb.new_code.keys():
            gb.new_code[original_file_name] = {
                'code_tuples': [],
                'add_include_guard': False
            }
        if original_file_name not in includes.keys():
            includes[original_file_name] = []
        if extras_src_file_name not in gb.new_code.keys():
            gb.new_code[extras_src_file_name] = {
                'code_tuples': [],
                'add_include_guard': False
            }

        # Treat the first specialization of a template class differently
        if is_template and class_name['long'] not in template_done:
            template_bracket, template_types = utils.getTemplateBracket(
                class_el)

            empty_templ_class_decl = ''
            empty_templ_class_decl += classutils.constrEmptyTemplClassDecl(
                abstr_class_name['short'],
                namespaces,
                template_bracket,
                indent=cfg.indent)
            empty_templ_class_decl += classutils.constrTemplForwDecl(
                class_name['short'],
                namespaces,
                template_bracket,
                indent=cfg.indent)

            gb.new_code[abstr_class_fname]['code_tuples'].append(
                (0, empty_templ_class_decl))

        # Get template arguments for specialization,
        # and check that they are acceptable
        if is_template and class_name['long'] not in templ_spec_done:
            spec_template_types = utils.getSpecTemplateTypes(class_el)
            for template_type in spec_template_types:
                if (template_type not in gb.accepted_types):
                    raise Exception("The template specialization type '" +
                                    template_type + "' for class " +
                                    class_name['long'] +
                                    " is not among accepted types.")

        #
        # For the backend: Construct code for the abstract class header file and register it
        #

        constrAbstractClassHeaderCode(class_el,
                                      class_name,
                                      abstr_class_name,
                                      namespaces,
                                      is_template,
                                      has_copy_constructor,
                                      construct_assignment_operator,
                                      abstr_class_fname,
                                      file_for_gambit=False)

        #
        # For GAMBIT: Construct code for the abstract class header file and register it
        #

        constrAbstractClassHeaderCode(class_el,
                                      class_name,
                                      abstr_class_name,
                                      namespaces,
                                      is_template,
                                      has_copy_constructor,
                                      construct_assignment_operator,
                                      abstr_class_fname,
                                      file_for_gambit=True)

        #
        # Add abstract class to inheritance list of original class
        #

        addAbsClassToInheritanceList(class_el, class_name, abstr_class_name,
                                     is_template, original_file_name,
                                     original_file_content_nocomments)

        #
        # Generate code for #include statements in orginal header/source file
        #

        addIncludesToOriginalClassFile(class_el, namespaces, is_template,
                                       original_file_name,
                                       original_file_content_nocomments,
                                       original_file_content,
                                       short_abstr_class_fname)

        #
        # Generate additional member functions in the original class:
        # - Abstract class versions of member functions that make use of loaded types.
        # - Extra versions of functions that use default value arguments.
        # - Functions for returning references to public member variables.
        # Declarations go in the original class header while implementations go in a separate source file.
        #

        generateClassMemberInterface(class_el, class_name, abstr_class_name,
                                     namespaces, original_file_name,
                                     original_file_content_nocomments,
                                     original_class_file_el,
                                     extras_src_file_name)

        #
        # Generate factory functions source file
        #

        generateFactoryFunctions(class_el, class_name, is_template)

        #
        # Generate a header containing the GAMBIT wrapper class
        #

        generateWrapperHeader(class_el, class_name, abstr_class_name,
                              namespaces, short_abstr_class_fname,
                              construct_assignment_operator,
                              has_copy_constructor, copy_constructor_id)

        #
        # Construct utility functions for dealing with pointer-to-wrapper from Abstract class.
        # ('wrapper_creator', 'wrapper_deleter', 'set_delete_BEptr')
        #

        constrWrapperUtils(class_name)

        #
        # Add typedef to 'abstracttypedefs.hpp'
        #

        addAbstractTypedefs(abstr_class_name, namespaces)

        #
        # Add typedef to 'wrappertypdefs.hpp'
        #

        addWrapperTypedefs(class_name, namespaces)

        #
        # Add include guards to the original headers
        #
        gb.new_code[original_file_name]['add_include_guard'] = True
        gb.new_code[original_file_name]['include_guard_prefix'] = 'boss'

        #
        # Keep track of classes done
        #

        gb.classes_done.append(class_name)
        if is_template:
            if class_name['long'] not in template_done:
                template_done.append(class_name['long'])
            if class_name['long'] not in templ_spec_done:
                templ_spec_done.append(class_name['long'])

        print
예제 #3
0
def main():

    # print()
    # print()
    # print('  ========================================')
    # print('  ||                                    ||')
    # print('  ||  BOSS - Backend-On-a-Stick-Script  ||')
    # print('  ||                                    ||')
    # print('  ========================================')
    # print()
    # print()

    # Parse command line arguments and options
    usage_string = "Usage: %prog [options] <config module>"
    usage_string += "\n\n"
    usage_string += "<config module> refers to a config file configs/<config module>.py."
    usage_string += "\n\n"
    usage_string += "Example: '%prog example_1_234' will use the config file configs/example_1_234.py."

    parser = OptionParser(usage=usage_string, version="%prog 0.1")
    parser.add_option("-i",
                      "--castxml-cc-id",
                      dest="castxml_cc_id",
                      default="",
                      help="Set castxml-cc-id to ID.",
                      metavar="ID")
    parser.add_option("-c",
                      "--castxml-cc",
                      dest="castxml_cc",
                      default="",
                      help="Set castxml-cc to COMPILER.",
                      metavar="COMPILER")
    parser.add_option(
        "-l",
        "--list",
        action="store_true",
        dest="list_flag",
        default=False,
        help="Output a list of the available classes and functions.")
    parser.add_option("-g",
                      "--generate-only",
                      action="store_true",
                      dest="generate_only_flag",
                      default=False,
                      help="Stop BOSS after code generation step.")
    parser.add_option(
        "-t",
        "--types-header",
        action="store_true",
        dest="types_header_flag",
        default=False,
        help=
        "Generate loaded_types.hpp. (BOSS continues from a previous saved state, no other input required.)"
    )
    parser.add_option(
        "-r",
        "--reset-source",
        dest="reset_info_file_name",
        default="",
        help=
        "Reset source code that has been mangled by BOSS. Requires a RESET_INFO_FILE generated by BOSS.",
        metavar="RESET_INFO_FILE")
    parser.add_option(
        "-I",
        "--include",
        action="append",
        dest="cmdline_include_paths",
        help=
        "Add PATH to the list of paths used by CastXML to search for included header files.",
        metavar="PATH")
    # parser.add_option("-G", "--to-gambit",
    #                   dest="main_gambit_path",
    #                   default="",
    #                   help="Copy all the code needed by GAMBIT to the correct locations within the main GAMBIT path GAMBIT_PATH.",
    #                   metavar="GAMBIT_PATH")

    (options, args) = parser.parse_args()

    # Print banner
    msg = '''\033[1m

  ========================================
  ||                                    ||
  ||  BOSS - Backend-On-a-Stick-Script  ||
  ||                                    ||
  ========================================
    \033[0m'''
    # msg = utils.modifyText(msg,'bold')
    print(msg)

    # Check for conflicting options
    if options.generate_only_flag and options.types_header_flag:
        print()
        print('Conflicting flags: --generate-only and --types-header')
        print()

        sys.exit()

    # Check that arguments list is not empty
    if (len(args) < 1) and not (options.types_header_flag
                                or options.reset_info_file_name):

        print()
        print('Missing input arguments. For instructions, run: boss.py --help')
        print()

        sys.exit()

    # Check platform
    if not (sys.platform.startswith('linux') or sys.platform == 'darwin'):

        print()
        print('Platform "%s" is not supported.' % (sys.platform))
        print()

        sys.exit()

    # Check that CastXML is found and get the correct path
    # (system-wide executable or prebuilt binary in castxml/bin/castxml)
    boss_abs_dir = os.path.dirname(os.path.abspath(__file__))
    local_castxml_path = os.path.join(boss_abs_dir, "castxml/bin/castxml")
    has_castxml_system = True
    has_castxml_local = True
    try:
        subprocess.check_output(["which", "castxml"])
    except subprocess.CalledProcessError:
        print()
        print("Cannot find a system-wide 'castxml' executable.")
        print(
            "CastXML can be installed with 'apt install castxml' (Linux) or 'brew install castxml' (OS X)."
        )
        has_castxml_system = False
        print("Will now look for a prebuilt CastXML binary in %s" %
              (local_castxml_path))

    try:
        subprocess.check_output(["which", local_castxml_path])
    except subprocess.CalledProcessError:
        print()
        print("Cannot find 'castxml' binary in %s." % (local_castxml_path))
        print(
            "To get the prebuilt castxml binary for your system, download a tarball from "
        )
        print()
        print(
            "  https://midas3.kitware.com/midas/download/item/318227/castxml-linux.tar.gz (for Linux)"
        )
        print()
        print("or ")
        print()
        print(
            "  https://midas3.kitware.com/midas/download/item/318762/castxml-macosx.tar.gz (for OS X)"
        )
        print()
        print("and extract it in the main BOSS directory: %s/" %
              (boss_abs_dir))
        print()
        has_castxml_local = False

    # Quit if no version of CastXML is found
    if (not has_castxml_system) and (not has_castxml_local): sys.exit()

    # Get the config file name from command line. Import the correct config module.
    # If reset option is used, then skip this part and simply import configs.example_1_234.

    import modules.active_cfg as active_cfg
    if options.reset_info_file_name == '':

        # Get the config file name from command line input, unless reset option is used
        input_cfg_path = args[0]

        # Import the given config file as a module named 'cfg'.
        input_cfg_dir, input_cfg_filename = os.path.split(input_cfg_path)
        input_cfg_modulename = input_cfg_filename.rstrip('.py')

        active_cfg.module_name = input_cfg_modulename

    try:
        exec("import configs." + active_cfg.module_name + " as cfg", globals())
    except ImportError as e:
        print(
            "Failed to import config module '%s'. Are you sure the file %s exists?"
            % (input_cfg_modulename,
               os.path.join('configs', input_cfg_modulename + '.py')))
        print()
        sys.exit()

    import modules.gb as gb
    import modules.classutils as classutils
    import modules.classparse as classparse
    import modules.funcparse as funcparse
    import modules.funcutils as funcutils
    import modules.utils as utils
    import modules.filehandling as filehandling
    import modules.infomsg as infomsg

    # Write the result of the castxml checks to gb
    gb.has_castxml_system = has_castxml_system
    gb.has_castxml_local = has_castxml_local

    # Construct file name for the BOSS reset file to be created
    reset_info_file_name = 'reset_info.' + gb.gambit_backend_name_full + '.boss'

    # Convert all input and final output paths in the config file to absolute paths
    cfg.input_files = [(gb.boss_dir + '/' + x if not x.startswith('/') else x)
                       for x in cfg.input_files]
    cfg.include_paths = [
        (gb.boss_dir + '/' + x if not x.startswith('/') else x)
        for x in cfg.include_paths
    ]
    cfg.base_paths = [(gb.boss_dir + '/' + x if not x.startswith('/') else x)
                      for x in cfg.base_paths]
    if not cfg.header_files_to.startswith('/'):
        cfg.header_files_to = gb.boss_dir + '/' + cfg.header_files_to
    if not cfg.src_files_to.startswith('/'):
        cfg.src_files_to = gb.boss_dir + '/' + cfg.src_files_to

    # If castxml compiler setting are given as command line input,
    # update the variables in cfg
    if options.castxml_cc_id != '':
        cfg.castxml_cc_id = options.castxml_cc_id
    if options.castxml_cc != '':
        cfg.castxml_cc = options.castxml_cc

    # If additional include paths are given at the command line,
    # add them to the list given in cfg.
    if options.cmdline_include_paths:
        cfg.include_paths = cfg.include_paths + options.cmdline_include_paths

    #
    # If types_header_flag is True: Load saved variables, parse factory function files and generate loaded_types.hpp
    #
    if options.types_header_flag:
        with open('savefile.boss', 'rb') as f:
            gb.classes_done, gb.factory_info = pickle.load(f)

        print('(Continuing from saved state.)')

        print()
        print()
        print(
            utils.modifyText('Generating file loaded_types.hpp:', 'underline'))
        print()

        filehandling.createLoadedTypesHeader()

        print()
        print(utils.modifyText('Done!', 'bold'))
        print()

        sys.exit()

    #
    # If reset option is used: Run the reset function and then quit
    #
    if options.reset_info_file_name != '':
        print()
        print()
        print(utils.modifyText('Reset source code:', 'underline'))
        print()
        print('  Input file: ' + options.reset_info_file_name)
        print()

        filehandling.resetSourceCode(options.reset_info_file_name)

        sys.exit()

    #
    # Check if backend source tree has already been BOSSed. (Look for the backend_undefs.hpp header file.)
    #
    check_file = os.path.join(cfg.header_files_to, gb.gambit_backend_incl_dir,
                              'backend_undefs.hpp')
    if os.path.isfile(check_file):
        print()
        print(
            utils.modifyText(
                'The backend source tree seems to already have been BOSSed.',
                'yellow'))
        print()
        print(
            '  If you want to reBOSS the source tree you must first revert it to the original state with the command:'
        )
        print('  python boss.py -r ' + reset_info_file_name)
        print()

        sys.exit()

    # If the output directory is to be used, delete the current one if it exists.
    if (not options.list_flag) and (not options.types_header_flag):
        try:
            shutil.rmtree(gb.boss_output_dir)
        except OSError as e:
            if e.errno == 2:
                pass
            else:
                raise e

    #
    # Identify standard include paths
    #
    if cfg.auto_detect_stdlib_paths:
        print()
        print(
            utils.modifyText('Identifying standard include paths:',
                             'underline'))
        print()

        utils.identifyStdIncludePaths()

    #
    # Run castxml for all input header/source files
    #

    print()
    print(utils.modifyText('Parsing the input files:', 'underline'))
    print()

    # Create the temp output dir if it does not exist
    filehandling.createOutputDirectories(selected_dirs=['temp'])

    # Sort list of input files
    input_files = cfg.input_files
    input_files.sort()

    xml_files = []
    for i, input_file_path in enumerate(input_files):

        # Get path and filename for the input file
        input_file_dir, input_file_short_name = os.path.split(input_file_path)

        # Construct file name for xml file produced by castxml
        xml_output_path = os.path.join(
            gb.boss_temp_dir, 'tempfile_' + str(i) + '_' +
            input_file_short_name.replace('.', '_') + '.xml')

        # List all include paths
        # include_paths_list = [cfg.include_path] + cfg.additional_include_paths

        # Run castxml
        try:
            utils.castxmlRunner(input_file_path, cfg.include_paths,
                                xml_output_path)
        except Exception as e:
            print()
            print(
                "  The initial CastXML command failed. Some problems can be solved by simply specifying"
            )
            print(
                "  a different C++ compiler in the 'castxml_cc' option in %s. It is currently set to '%s'."
                % (input_cfg_path, cfg.castxml_cc))
            print()
            raise e
            sys.exit(1)

        # Append xml file to list of xml files
        xml_files.append(xml_output_path)

    #
    # END: Run castxml on input files
    #
    print()

    #
    # If -l option is given, printa list of all classes and functions, then exit.
    #

    if options.list_flag:

        all_class_names = []
        all_function_names = []

        for xml_file in xml_files:

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

            # Set the global xml id dict. (Needed by the functions called from utils.)
            gb.id_dict = OrderedDict([(el.get('id'), el)
                                      for el in root.getchildren()])

            # Find all available classes
            for el in (root.findall('Class') + root.findall('Struct')):

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

                # Only list native classes
                if utils.isNative(el):
                    all_class_names.append(class_name['long_templ'])

            # Find all available functions
            for el in root.findall('Function'):

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

                # Only list native functions
                if utils.isNative(el):
                    all_function_names.append(func_name['long_templ_args'])

        # END: Loop over xml files

        # Remove duplicates
        all_class_names = list(OrderedDict.fromkeys(all_class_names))
        all_function_names = list(OrderedDict.fromkeys(all_function_names))

        # Output lists
        print(utils.modifyText('Classes:', 'underline'))
        print()
        for demangled_class_name in all_class_names:
            print('  - ' + demangled_class_name)
        print()
        print(utils.modifyText('Functions:', 'underline'))
        print()
        for demangled_func_name in all_function_names:
            print('  - ' + demangled_func_name)
        print()

        # Exit
        sys.exit()

    #
    # Analyse types and functions
    #

    print(utils.modifyText('Analysing types and functions:', 'underline'))
    print()
    print('  (This may take a little while.)')
    print()
    #
    # 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
    #
    utils.xmlFilesToDicts(xml_files)

    #
    # Look up potential parent classes and add to cfg.load_classes
    #

    if cfg.load_parent_classes:
        utils.addParentClasses()

    #
    # Remove from cfg.load_classes all classes that are not loadable (not found, incomplete, abstract, ...)
    #

    # Remove duplicates from cfg.load_classes
    cfg.load_classes = list(OrderedDict.fromkeys(cfg.load_classes))

    is_loadable = OrderedDict.fromkeys(cfg.load_classes, False)

    # Determine which requested classes are actually loadable
    for xml_file in xml_files:

        # Initialise global dicts
        gb.xml_file_name = xml_file
        utils.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 a requested class is loadable, set the entry in is_loadable to True
                if full_name in cfg.load_classes:

                    if utils.isLoadable(el, print_warning=False):
                        is_loadable[full_name] = True

    # Remove from cfg.load_classes those that are not loadable
    for full_name in is_loadable.keys():

        if not is_loadable[full_name]:

            cfg.load_classes.remove(full_name)

    # Output info on why classes are not loadable
    for xml_file in xml_files:

        # Initialise global dicts
        gb.xml_file_name = xml_file
        utils.initGlobalXMLdicts(xml_file, id_and_name_only=True)

        for full_name in is_loadable.keys():

            if not is_loadable[full_name]:

                # If the class exists, print reason why it is not loadable.
                # If the class is not found, say so.
                try:
                    el = gb.name_dict[full_name]
                except KeyError:
                    el = None

                if el is not None:
                    utils.isLoadable(el, print_warning=True)
                else:
                    reason = "Class not found."
                    infomsg.ClassNotLoadable(full_name, reason).printMessage()

    #
    # Fill the gb.parents_of_loaded_classes list
    #
    utils.fillParentsOfLoadedClassesList()

    #
    # Fill the gb.accepted_types list
    #
    utils.fillAcceptedTypesList()

    #
    # Remove from cfg.load_functions all functions that are not loadable
    #

    # Remove duplicates from cfg.load_functions
    cfg.load_functions = list(OrderedDict.fromkeys(cfg.load_functions))

    for xml_file in xml_files:

        # Initialise global dicts
        gb.xml_file_name = xml_file
        utils.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 == 'Function':

                # Get function name
                try:
                    func_name = funcutils.getFunctionNameDict(el)
                    func_name_long_templ_args = func_name['long_templ_args']
                except KeyError:
                    func_name_long_templ_args = 'UNKNOWN_NAME'
                except:
                    print('  ERROR: Unexpected error!')
                    raise

                if func_name_long_templ_args in cfg.load_functions:

                    is_loadable = not funcutils.ignoreFunction(
                        el, limit_pointerness=True, print_warning=True)

                    if not is_loadable:

                        cfg.load_functions.remove(func_name_long_templ_args)

    #
    # Main loop over all xml files
    #

    # Check that we have something to do...
    if (len(cfg.load_classes) == 0) and (len(cfg.load_functions) == 0):
        print()
        print()
        print('  - No classes or functions to load!')
        print()
        print(utils.modifyText('Done!', 'bold'))

        sys.exit()

    print()
    print()
    print(utils.modifyText('Generating code:', 'underline'))

    for xml_file in xml_files:

        # Output xml file name
        print()
        print()
        print('  ' + utils.modifyText('Current XML file:', 'underline') + ' ' +
              xml_file)
        print()

        #
        # Initialise global dicts with the current XML file
        #

        gb.xml_file_name = xml_file
        utils.initGlobalXMLdicts(xml_file)

        #
        # Parse classes
        #

        classparse.run()

        #
        # Parse functions
        #

        funcparse.run()

        #
        # Create header with forward declarations of all abstract classes
        #

        abs_frwd_decls_header_path = os.path.join(
            gb.boss_output_dir, gb.frwd_decls_abs_fname + cfg.header_extension)
        utils.constrAbsForwardDeclHeader(abs_frwd_decls_header_path)

        #
        # Create header with forward declarations of all wrapper classes
        #

        wrp_frwd_decls_header_path = os.path.join(
            gb.boss_output_dir, gb.frwd_decls_wrp_fname + cfg.header_extension)
        utils.constrWrpForwardDeclHeader(wrp_frwd_decls_header_path)

        # #
        # # Create header with declarations of all enum types
        # #

        # enum_decls_header_path = os.path.join(gb.boss_output_dir, gb.enum_decls_wrp_fname + cfg.header_extension)
        # utils.constrEnumDeclHeader(root.findall('Enumeration'), enum_decls_header_path)

    #
    # END: loop over xml files
    #

    # Check that we have done something...
    if (len(gb.classes_done) == 0) and (len(gb.functions_done) == 0):
        print()
        print()
        print('  - No classes or functions loaded!')
        print()
        print(utils.modifyText('Done!', 'bold'))
        print()

        sys.exit()

    #
    # Clear global dicts
    #

    utils.clearGlobalXMLdicts()

    #
    # Write new files
    #

    # Create all output directories that do not exist.
    filehandling.createOutputDirectories()

    # File writing loop
    for src_file_name, code_dict in gb.new_code.items():

        add_include_guard = code_dict['add_include_guard']
        code_tuples = code_dict['code_tuples']

        code_tuples.sort(key=lambda x: x[0], reverse=True)

        new_src_file_name = os.path.join(gb.boss_output_dir,
                                         os.path.basename(src_file_name))

        if code_tuples == []:
            continue

        boss_backup_exists = False
        if os.path.isfile(src_file_name):
            try:
                f = open(src_file_name + '.backup.boss', 'r')
                boss_backup_exists = True
            except IOError as e:
                if e.errno != 2:
                    raise e
                f = open(src_file_name, 'r')

            f.seek(0)
            file_content = f.read()
            f.close()
            new_file_content = file_content
        else:
            new_file_content = ''

        if not boss_backup_exists and new_file_content:
            f = open(src_file_name + '.backup.boss', 'w')
            f.write(new_file_content)
            f.close()

        for pos, code in code_tuples:

            if pos == -1:
                new_file_content = new_file_content + code
            else:
                new_file_content = new_file_content[:
                                                    pos] + code + new_file_content[
                                                        pos:]

        # Add include guard where requested
        if add_include_guard:

            short_new_src_file_name = os.path.basename(new_src_file_name)

            try:
                prefix = code_dict['include_guard_prefix']
            except KeyError:
                prefix = ''

            new_file_content = utils.addIncludeGuard(
                new_file_content,
                short_new_src_file_name,
                prefix=prefix,
                suffix=gb.gambit_backend_name_full)

        # Do the writing!
        f = open(new_src_file_name, 'w')
        f.write(new_file_content)
        f.close()

    #
    # Copy files from common_headers/ and common_source_files/ and replace any code template tags
    #

    filehandling.createCommonHeaders()
    filehandling.createCommonSourceFiles()

    #
    # Move files to correct directories
    #

    filehandling.moveFilesAround()

    #
    # Run through all the generated files and use the code tags __START_GAMBIT_NAMESPACE__ and __END_GAMBIT_NAMESPACE__ to construct
    # the correct namespace.
    #

    construct_namespace_in_files = glob.glob(
        os.path.join(gb.backend_types_dir_complete, '*')) + glob.glob(
            os.path.join(gb.for_gambit_backend_types_dir_complete, '*'))

    filehandling.replaceNamespaceTags(construct_namespace_in_files,
                                      gb.gambit_backend_namespace,
                                      '__START_GAMBIT_NAMESPACE__',
                                      '__END_GAMBIT_NAMESPACE__')

    #
    # Run through all the generated files and remove tags that are no longer needed
    #

    all_generated_files = glob.glob(os.path.join(
        gb.boss_output_dir, '*')) + glob.glob(
            os.path.join(gb.backend_types_dir_complete, '*')) + glob.glob(
                os.path.join(gb.for_gambit_backend_types_dir_complete, '*'))
    remove_tags_list = [
        '__START_GAMBIT_NAMESPACE__', '__END_GAMBIT_NAMESPACE__',
        '__INSERT_CODE_HERE__'
    ]

    filehandling.removeCodeTagsFromFiles(all_generated_files, remove_tags_list)

    #
    # Copy files to the correct locations within the source tree of the original code
    #

    print()
    print()
    print(
        utils.modifyText('Copying generated files to original source tree:',
                         'underline'))
    print()

    manipulated_files, new_files, new_dirs = filehandling.copyFilesToSourceTree(
        verbose=True)

    # # Create the reset dir if it does not exist
    # filehandling.createOutputDirectories(selected_dirs=['reset'])

    # Save source_target_tuples to be able to undo the changes at a later time
    with open(reset_info_file_name, 'wb') as f:
        pickle.dump([manipulated_files, new_files, new_dirs], f)

    #
    # If generate_only_flag is True, save state and quit
    #

    if options.generate_only_flag:
        with open('savefile.boss', 'wb') as f:
            pickle.dump([gb.classes_done, gb.factory_info], f)
        print()
        print()
        print('Done with code generation. Program state saved.')
        print('To generate loaded_types.hpp, run: boss.py --types-header')
        print()
        sys.exit()

    #
    # Generate header file 'loaded_types.hpp'
    #

    print()
    print()
    print(utils.modifyText('Generating file loaded_types.hpp:', 'underline'))
    print()

    filehandling.createLoadedTypesHeader()

    #
    # Parse any source files for global functions using castxml
    #

    print()
    print()
    print(
        utils.modifyText('Parsing the generated function source files:',
                         'underline'))
    print()

    function_xml_files = filehandling.parseFunctionSourceFiles()

    #
    # Generate GAMBIT frontend header file ''
    #

    print()
    print()
    print(
        utils.modifyText('Generating GAMBIT frontend header file:',
                         'underline'))
    print()

    filehandling.createFrontendHeader(function_xml_files)

    #
    # Done!
    #

    print()
    print(utils.modifyText('Done!', 'bold'))
    print()
    print("  To prepare this backend for use with GAMBIT, do the following:")
    print()
    print(
        "    1. BOSS has added new source files to '%s' and new header files to '%s'."
        % (cfg.src_files_to, cfg.header_files_to))
    print("       Make sure that these are included when building '%s'." %
          (cfg.gambit_backend_name))
    print(
        "    2. Build a shared library (.so) from the '%s' source code that BOSS has edited."
        % (cfg.gambit_backend_name))
    print(
        "    3. Set the correct path to this library in the 'backends_locations.yaml' file in GAMBIT."
    )
    print(
        "    4. Copy the '%s' directory from '%s' to the 'backend_types' directory within GAMBIT."
        % (gb.gambit_backend_name_full,
           gb.for_gambit_backend_types_dir_complete))
    print(
        "    5. Copy the file '%s' from '%s' to the GAMBIT 'frontends' directory."
        % (gb.frontend_fname, gb.frontend_path))
    print()
    print()
예제 #4
0
def ignoreFunction(func_el, limit_pointerness=False, remove_n_args=0, print_warning=True):

    # TODO: When BOSS starts accepting template functions, add a check for the template arguments

    import modules.classutils as classutils

    func_name = getFunctionNameDict(func_el)

    # Should this function be ditched?
    if func_name['long_templ_args'] in cfg.ditch:
        return True

    # Ignore templated functions (BOSS cannot deal with that yet...)
    if utils.isTemplateFunction(func_el):
        if print_warning:
            reason = "Templated function. BOSS cannot deal with this yet."
            infomsg.IgnoredFunction(func_name['long_templ_args'], reason).printMessage()
        return True


    # Check if this is an operator function
    is_operator = False
    if func_el.tag == 'OperatorMethod':
        is_operator = True



    # Check function return type
    if 'returns' in func_el.keys():
        return_type_dict = utils.findType(func_el)
        return_type = return_type_dict['name'] + '*'*return_type_dict['pointerness'] + '&'*return_type_dict['is_reference']
        return_el   = return_type_dict['el']  
        if not utils.isAcceptedType(return_el):
            if print_warning:
                reason = "Non-accepted return type '%s'." % return_type
                infomsg.IgnoredFunction(is_operator*'operator'+func_name['long_templ_args'], reason).printMessage()
            return True 


    # Check argument types
    arg_types_accepted = True
    args = getArgs(func_el)

    use_n_args = len(args) - remove_n_args
    
    if remove_n_args > 0:
        args = args[:-remove_n_args]

    for arg_dict in args:
        arg_type_name = arg_dict['type']
        arg_el = gb.id_dict[arg_dict['id']]

        # Find out if argument type is base type of any accepted type
        is_parent_of_accepted= False
        if utils.isNative(arg_el):
            arg_class_name = classutils.getClassNameDict(arg_el)
            if arg_class_name['long_templ'] in gb.parents_of_loaded_classes:
                is_parent_of_accepted = True


        if arg_dict['function_pointer']:
            if print_warning:
                reason = "Function pointer type argument, '%s'." % arg_dict['name']
                infomsg.IgnoredFunction(is_operator*'operator'+func_name['long_templ_args'], reason).printMessage()
            arg_types_accepted = False
            break
        if (not utils.isAcceptedType(arg_el)):  # and (not is_parent_of_accepted):
            if print_warning:
                reason = "Non-accepted argument type '%s'." % arg_type_name
                infomsg.IgnoredFunction(is_operator*'operator'+func_name['long_templ_args'], reason).printMessage()
            arg_types_accepted = False
            break
        if limit_pointerness == True:
            if utils.isLoadedClass(arg_el):
                if ('**' in arg_type_name) or ('*&' in arg_type_name):
                    if print_warning:
                        reason = "Argument of type pointer-to-pointer/reference-to-pointer to loaded class, '%s'." % arg_type_name
                        infomsg.IgnoredFunction(is_operator*'operator'+func_name['long_templ_args'], reason).printMessage()
                    arg_types_accepted = False
                    break
    if (not arg_types_accepted):
        return True

    # Function accepted (i.e. should *not* be ignored)
    return False