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()
# # 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():
def createFrontendHeader(function_xml_files_dict): # Read all xml files utils.xmlFilesToDicts(function_xml_files_dict.values()) # # Generate typedefs for loaded classes, from ::BACKENDNAME_SAFE_VERSION::class_name # to ::Gambit::Backends::BACKENDNAME_SAFE_VERSION::class_name # outer_namespace_list = ['Gambit', 'Backends', gb.gambit_backend_name_full] typedef_code = '' typedef_code += utils.constrNamespace(outer_namespace_list, 'open', indent=cfg.indent) # Loop over all classes for class_name in gb.classes_done: if not class_name['long'] in gb.factory_info.keys(): continue else: class_typedef_code = '' class_namespace, class_name_short = utils.removeNamespace( class_name['long'], return_namespace=True) if class_namespace == '': class_typedef_code += 'typedef ::' + gb.gambit_backend_name_full + '::' + class_name[ 'long'] + ' ' + class_name['short'] + ';\n' else: class_namespace_list = class_namespace.split('::') class_typedef_code += utils.constrNamespace( class_namespace_list, 'open', indent=cfg.indent) class_typedef_code += ' ' * cfg.indent * len( class_namespace_list ) + 'typedef ::' + gb.gambit_backend_name_full + '::' + class_name[ 'long'] + ' ' + class_name['short'] + ';\n' class_typedef_code += utils.constrNamespace( class_namespace_list, 'close', indent=cfg.indent) class_typedef_code = utils.addIndentation(class_typedef_code, 3 * cfg.indent) typedef_code += class_typedef_code typedef_code += utils.constrNamespace(outer_namespace_list, 'close', indent=cfg.indent) # # Generate code for all the BE_FUNCTION macros # be_function_macro_code = '' for i, func_name in enumerate(gb.functions_done): # Set useful variables xml_file = function_xml_files_dict[func_name['long_templ_args']] # If new xml file, initialise global dicts if xml_file != gb.xml_file_name: gb.xml_file_name = xml_file utils.initGlobalXMLdicts(xml_file, id_and_name_only=True) # Get wrapper function element tree = ET.parse(xml_file) root = tree.getroot() wr_func_el = None for el in root.findall('Function'): if el.get('name') == gb.wr_func_names[i]: wr_func_el = el if wr_func_el is None: continue # Get information about the return type. return_type_dict = utils.findType(wr_func_el) return_el = return_type_dict['el'] pointerness = return_type_dict['pointerness'] is_ref = return_type_dict['is_reference'] return_kw = return_type_dict['cv_qualifiers'] return_kw_str = ' '.join(return_kw) + ' ' * bool(len(return_kw)) return_type = return_type_dict[ 'name'] + '*' * pointerness + '&' * is_ref # Construct argument bracket args = funcutils.getArgs(wr_func_el) args_bracket = funcutils.constrArgsBracket(args, include_arg_name=False, include_arg_type=True, include_namespace=True) # Get mangled symbol # symbol = wr_func_el.get('mangled') symbol = wr_func_el.get('name') be_function_macro_code += 'BE_FUNCTION(' be_function_macro_code += func_name['short'] + ', ' be_function_macro_code += return_type + ', ' be_function_macro_code += args_bracket + ', ' be_function_macro_code += '"' + symbol + '"' + ', ' be_function_macro_code += '"' + func_name['short'] + '"' + ')\n' # # Generate code for the frontend header # frontend_content = '' # - Comment at beginning backend_name_and_version = cfg.gambit_backend_name + ' ' + cfg.gambit_backend_version frontend_content += '//\n' frontend_content += '// Frontend header generated by BOSS for GAMBIT backend %s.\n' % ( backend_name_and_version) frontend_content += '//\n' # - Include statement for the identification header frontend_content += '\n' frontend_content += '#include "' + os.path.join( gb.gambit_backend_incl_dir, gb.backend_types_basedir, gb.gambit_backend_name_full, 'identification.hpp') + '"\n' # - LOAD_LIBRARY macro frontend_content += '\n' frontend_content += 'LOAD_LIBRARY\n' # - Class typedefs frontend_content += '\n' frontend_content += typedef_code # - BE_FUNCTION macros frontend_content += '\n' frontend_content += '// Functions\n' frontend_content += be_function_macro_code # - Descriptions of different things that can go into a frontend header frontend_content += '// Variables\n' frontend_content += '\n' frontend_content += '// Initialisation function (dependencies)\n' frontend_content += '\n' frontend_content += '// Convenience functions (registration)\n' frontend_content += '\n' frontend_content += '// Initialisation function (definition)\n' frontend_content += 'BE_INI_FUNCTION{} END_BE_INI_FUNCTION\n' frontend_content += '\n' frontend_content += '// Convenience functions (definitions)\n' # - Include statement for backend_undefs.hpp frontend_content += '\n' frontend_content += '// End\n' frontend_content += '#include "' + os.path.join( gb.gambit_backend_incl_dir, 'backend_undefs.hpp') + '"\n' # Write to file f = open(gb.frontend_path, 'w') f.write(frontend_content) f.close()