joey shelton / LED_Demo

Dependencies:   MAX44000 PWM_Tone_Library nexpaq_mdk

Fork of LED_Demo by Maxim nexpaq

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers exporters.py Source File

exporters.py

00001 """Just a template for subclassing"""
00002 import os
00003 import sys
00004 import logging
00005 from os.path import join, dirname, relpath
00006 from itertools import groupby
00007 from jinja2 import FileSystemLoader
00008 from jinja2.environment import Environment
00009 
00010 from tools.targets import TARGET_MAP
00011 from project_generator.tools import tool
00012 from project_generator.tools_supported import ToolsSupported
00013 from project_generator.settings import ProjectSettings
00014 from project_generator_definitions.definitions import ProGenDef
00015 
00016 
00017 class OldLibrariesException (Exception):
00018     """Exception that indicates an export can not complete due to an out of date
00019     library version.
00020     """
00021     pass
00022 
00023 class FailedBuildException (Exception):
00024     """Exception that indicates that a build failed"""
00025     pass
00026 
00027 class TargetNotSupportedException (Exception):
00028     """Indicates that an IDE does not support a particular MCU"""
00029     pass
00030 
00031 class ExporterTargetsProperty (object):
00032     """ Exporter descriptor for TARGETS
00033     TARGETS as class attribute for backward compatibility
00034     (allows: if in Exporter.TARGETS)
00035     """
00036     def __init__(self, func):
00037         self.func = func
00038     def __get__(self, inst, cls):
00039         return self.func(cls)
00040 
00041 class Exporter (object):
00042     """Exporter base class
00043 
00044     This class is meant to be extended by individual exporters, and provides a
00045     few helper methods for implementing an exporter with either jinja2 or
00046     progen.
00047     """
00048     TEMPLATE_DIR = dirname(__file__)
00049     DOT_IN_RELATIVE_PATH = False
00050     NAME = None
00051     TARGETS = None
00052     TOOLCHAIN = None
00053 
00054     def __init__ (self, target, export_dir, project_name, toolchain,
00055                  extra_symbols=None, resources=None):
00056         """Initialize an instance of class exporter
00057         Positional arguments:
00058         target        - the target mcu/board for this project
00059         export_dir    - the directory of the exported project files
00060         project_name  - the name of the project
00061         toolchain     - an instance of class toolchain
00062 
00063         Keyword arguments:
00064         extra_symbols - a list of extra macros for the toolchain
00065         resources     - an instance of class Resources
00066         """
00067         self.export_dir  = export_dir
00068         self.target  = target
00069         self.project_name  = project_name
00070         self.toolchain  = toolchain
00071         jinja_loader = FileSystemLoader(os.path.dirname(os.path.abspath(__file__)))
00072         self.jinja_environment  = Environment(loader=jinja_loader)
00073         self.resources  = resources
00074         self.generated_files  = []
00075         self.builder_files_dict  = {}
00076 
00077     def get_toolchain (self):
00078         """A helper getter function that we should probably eliminate"""
00079         return self.TOOLCHAIN 
00080 
00081     @property
00082     def flags (self):
00083         """Returns a dictionary of toolchain flags.
00084         Keys of the dictionary are:
00085         cxx_flags    - c++ flags
00086         c_flags      - c flags
00087         ld_flags     - linker flags
00088         asm_flags    - assembler flags
00089         common_flags - common options
00090         """
00091         config_header = self.toolchain .get_config_header()
00092         flags = {key + "_flags": value for key, value
00093                  in self.toolchain .flags.iteritems()}
00094         asm_defines = ["-D" + symbol for symbol in self.toolchain .get_symbols(True)]
00095         c_defines = ["-D" + symbol for symbol in self.toolchain .get_symbols()]
00096         flags['asm_flags'] += asm_defines
00097         flags['c_flags'] += c_defines
00098         flags['cxx_flags'] += c_defines
00099         if config_header:
00100             config_header = relpath(config_header,
00101                                     self.resources .file_basepath[config_header])
00102             flags['c_flags'] += self.toolchain .get_config_option(config_header)
00103             flags['cxx_flags'] += self.toolchain .get_config_option(
00104                 config_header)
00105         return flags
00106 
00107     def get_source_paths (self):
00108         """Returns a list of the directories where source files are contained"""
00109         source_keys = ['s_sources', 'c_sources', 'cpp_sources', 'hex_files',
00110                        'objects', 'libraries']
00111         source_files = []
00112         for key in source_keys:
00113             source_files.extend(getattr(self.resources , key))
00114         return list(set([os.path.dirname(src) for src in source_files]))
00115 
00116     def progen_get_project_data (self):
00117         """ Get ProGen project data  """
00118         # provide default data, some tools don't require any additional
00119         # tool specific settings
00120 
00121         def make_key(src):
00122             """turn a source file into it's group name"""
00123             key = os.path.basename(os.path.dirname(src))
00124             if not key:
00125                 key = os.path.basename(os.path.normpath(self.export_dir ))
00126             return key
00127 
00128         def grouped(sources):
00129             """Group the source files by their encompassing directory"""
00130             data = sorted(sources, key=make_key)
00131             return {k: list(g) for k, g in groupby(data, make_key)}
00132 
00133         if self.toolchain .get_config_header():
00134             config_header = self.toolchain .get_config_header()
00135             config_header = relpath(config_header,
00136                                     self.resources .file_basepath[config_header])
00137         else:
00138             config_header = None
00139 
00140         # we want to add this to our include dirs
00141         config_dir = os.path.dirname(config_header) if config_header else []
00142 
00143         project_data = tool.get_tool_template()
00144 
00145         project_data['target'] = TARGET_MAP[self.target ].progen['target']
00146         project_data['source_paths'] = self.get_source_paths ()
00147         project_data['include_paths'] = self.resources .inc_dirs + [config_dir]
00148         project_data['include_files'] = grouped(self.resources .headers)
00149         project_data['source_files_s'] = grouped(self.resources .s_sources)
00150         project_data['source_files_c'] = grouped(self.resources .c_sources)
00151         project_data['source_files_cpp'] = grouped(self.resources .cpp_sources)
00152         project_data['source_files_obj'] = grouped(self.resources .objects)
00153         project_data['source_files_lib'] = grouped(self.resources .libraries)
00154         project_data['output_dir']['path'] = self.export_dir 
00155         project_data['linker_file'] = self.resources .linker_script
00156         project_data['macros'] = []
00157         project_data['build_dir'] = 'build'
00158         project_data['template'] = None
00159         project_data['name'] = self.project_name 
00160         project_data['output_type'] = 'exe'
00161         project_data['debugger'] = None
00162         return project_data
00163 
00164     def progen_gen_file (self, project_data):
00165         """ Generate project using ProGen Project API
00166         Positional arguments:
00167         tool_name    - the tool for which to generate project files
00168         project_data - a dict whose base key, values are specified in
00169                        progen_get_project_data, the items will have been
00170                        modified by Exporter subclasses
00171 
00172         Keyword arguments:
00173         progen_build - A boolean that determines if the tool will build the
00174                        project
00175         """
00176         if not self.check_supported (self.NAME ):
00177             raise TargetNotSupportedException("Target not supported")
00178         settings = ProjectSettings()
00179         exporter = ToolsSupported().get_tool(self.NAME )
00180         self.builder_files_dict  = {self.NAME :exporter(project_data, settings).export_project()}
00181         for  middle in self.builder_files_dict .values():
00182             for field, thing in middle.iteritems():
00183                 if field == "files":
00184                     for filename in thing.values():
00185                         self.generated_files .append(filename)
00186 
00187     def progen_build (self):
00188         """Build a project that was already generated by progen"""
00189         print("Project {} exported, building for {}...".format(
00190             self.project_name , self.NAME ))
00191         sys.stdout.flush()
00192         builder = ToolsSupported().get_tool(self.NAME )
00193         result = builder(self.builder_files_dict [self.NAME ], ProjectSettings()).build_project()
00194         if result == -1:
00195             raise FailedBuildException("Build Failed")
00196 
00197     def check_supported (self, ide):
00198         """Indicated if this combination of IDE and MCU is supported"""
00199         if self.target  not in self.TARGETS  or \
00200            self.TOOLCHAIN  not in TARGET_MAP[self.target ].supported_toolchains:
00201             return False
00202         if not ProGenDef(ide).is_supported(
00203                 TARGET_MAP[self.target ].progen['target']):
00204             return False
00205         return True
00206 
00207     def gen_file (self, template_file, data, target_file):
00208         """Generates a project file from a template using jinja"""
00209         jinja_loader = FileSystemLoader(
00210             os.path.dirname(os.path.abspath(__file__)))
00211         jinja_environment = Environment(loader=jinja_loader)
00212 
00213         template = jinja_environment.get_template(template_file)
00214         target_text = template.render(data)
00215 
00216         target_path = join(self.export_dir , target_file)
00217         logging.debug("Generating: %s", target_path)
00218         open(target_path, "w").write(target_text)
00219         self.generated_files  += [target_path]