BA
/
BaBoRo1
Embed:
(wiki syntax)
Show/hide line numbers
exporters.py
00001 """Just a template for subclassing""" 00002 import os 00003 from abc import abstractmethod, ABCMeta 00004 import logging 00005 from os.path import join, dirname, relpath, basename, realpath, normpath, exists 00006 from itertools import groupby 00007 from jinja2 import FileSystemLoader, StrictUndefined 00008 from jinja2.environment import Environment 00009 import copy 00010 00011 from tools.targets import TARGET_MAP 00012 00013 00014 class TargetNotSupportedException (Exception): 00015 """Indicates that an IDE does not support a particular MCU""" 00016 pass 00017 00018 class ExporterTargetsProperty (object): 00019 """ Exporter descriptor for TARGETS 00020 TARGETS as class attribute for backward compatibility 00021 (allows: if in Exporter.TARGETS) 00022 """ 00023 def __init__(self, func): 00024 self.func = func 00025 def __get__(self, inst, cls): 00026 return self.func(cls) 00027 00028 def deprecated_exporter(CLS): 00029 old_init = CLS.__init__ 00030 old_name = CLS.NAME 00031 def __init__(*args, **kwargs): 00032 print("==================== DEPRECATION NOTICE ====================") 00033 print("The exporter %s is no longer maintained, and deprecated." % old_name) 00034 print("%s will be removed from mbed OS for the mbed OS 5.6 release." % old_name) 00035 old_init(*args, **kwargs) 00036 CLS.__init__ = __init__ 00037 CLS.NAME = "%s (DEPRECATED)" % old_name 00038 return CLS 00039 00040 class Exporter (object): 00041 """Exporter base class 00042 00043 This class is meant to be extended by individual exporters, and provides a 00044 few helper methods for implementing an exporter with either jinja2 or 00045 progen. 00046 """ 00047 __metaclass__ = ABCMeta 00048 TEMPLATE_DIR = dirname(__file__) 00049 DOT_IN_RELATIVE_PATH = False 00050 NAME = None 00051 TARGETS = set() 00052 TOOLCHAIN = None 00053 CLEAN_FILES = ("GettingStarted.html",) 00054 00055 00056 def __init__ (self, target, export_dir, project_name, toolchain, 00057 extra_symbols=None, resources=None): 00058 """Initialize an instance of class exporter 00059 Positional arguments: 00060 target - the target mcu/board for this project 00061 export_dir - the directory of the exported project files 00062 project_name - the name of the project 00063 toolchain - an instance of class toolchain 00064 00065 Keyword arguments: 00066 extra_symbols - a list of extra macros for the toolchain 00067 resources - an instance of class Resources 00068 """ 00069 self.export_dir = export_dir 00070 self.target = target 00071 self.project_name = project_name 00072 self.toolchain = toolchain 00073 jinja_loader = FileSystemLoader(os.path.dirname(os.path.abspath(__file__))) 00074 self.jinja_environment = Environment(loader=jinja_loader) 00075 self.resources = resources 00076 self.generated_files = [] 00077 self.static_files = ( 00078 join(self.TEMPLATE_DIR , "GettingStarted.html"), 00079 join(self.TEMPLATE_DIR , ".mbed"), 00080 ) 00081 self.builder_files_dict = {} 00082 self.add_config () 00083 00084 def get_toolchain (self): 00085 """A helper getter function that we should probably eliminate""" 00086 return self.TOOLCHAIN 00087 00088 def add_config (self): 00089 """Add the containgin directory of mbed_config.h to include dirs""" 00090 config = self.toolchain .get_config_header() 00091 if config: 00092 self.resources .inc_dirs.append( 00093 dirname(relpath(config, 00094 self.resources .file_basepath[config]))) 00095 00096 @property 00097 def flags (self): 00098 """Returns a dictionary of toolchain flags. 00099 Keys of the dictionary are: 00100 cxx_flags - c++ flags 00101 c_flags - c flags 00102 ld_flags - linker flags 00103 asm_flags - assembler flags 00104 common_flags - common options 00105 """ 00106 config_header = self.toolchain .get_config_header() 00107 flags = {key + "_flags": copy.deepcopy(value) for key, value 00108 in self.toolchain .flags.items()} 00109 asm_defines = self.toolchain .get_compile_options( 00110 self.toolchain .get_symbols(for_asm=True), 00111 filter(None, self.resources .inc_dirs), 00112 for_asm=True) 00113 c_defines = ["-D" + symbol for symbol in self.toolchain .get_symbols()] 00114 flags['asm_flags'] += asm_defines 00115 flags['c_flags'] += c_defines 00116 flags['cxx_flags'] += c_defines 00117 if config_header: 00118 config_header = relpath(config_header, 00119 self.resources .file_basepath[config_header]) 00120 flags['c_flags'] += self.toolchain .get_config_option(config_header) 00121 flags['cxx_flags'] += self.toolchain .get_config_option( 00122 config_header) 00123 return flags 00124 00125 def get_source_paths (self): 00126 """Returns a list of the directories where source files are contained""" 00127 source_keys = ['s_sources', 'c_sources', 'cpp_sources', 'hex_files', 00128 'objects', 'libraries'] 00129 source_files = [] 00130 for key in source_keys: 00131 source_files.extend(getattr(self.resources , key)) 00132 return list(set([os.path.dirname(src) for src in source_files])) 00133 00134 def gen_file_dest (self, target_file): 00135 """Generate the project file location in an exported project""" 00136 return join(self.export_dir , target_file) 00137 00138 def gen_file (self, template_file, data, target_file, **kwargs): 00139 """Generates a project file from a template using jinja""" 00140 target_text = self._gen_file_inner (template_file, data, target_file, **kwargs) 00141 target_path = self.gen_file_dest (target_file) 00142 logging.debug("Generating: %s", target_path) 00143 open(target_path, "w").write(target_text) 00144 self.generated_files += [target_path] 00145 00146 def gen_file_nonoverwrite (self, template_file, data, target_file, **kwargs): 00147 """Generates a project file from a template using jinja""" 00148 target_text = self._gen_file_inner (template_file, data, target_file, **kwargs) 00149 target_path = self.gen_file_dest (target_file) 00150 if exists(target_path): 00151 with open(target_path) as fdin: 00152 old_text = fdin.read() 00153 if target_text not in old_text: 00154 with open(target_path, "a") as fdout: 00155 fdout.write(target_text) 00156 else: 00157 logging.debug("Generating: %s", target_path) 00158 open(target_path, "w").write(target_text) 00159 self.generated_files += [target_path] 00160 00161 def _gen_file_inner(self, template_file, data, target_file, **kwargs): 00162 """Generates a project file from a template using jinja""" 00163 jinja_loader = FileSystemLoader( 00164 os.path.dirname(os.path.abspath(__file__))) 00165 jinja_environment = Environment(loader=jinja_loader, 00166 undefined=StrictUndefined, **kwargs) 00167 00168 template = jinja_environment.get_template(template_file) 00169 target_text = template.render(data) 00170 return target_text 00171 00172 target_path = join(self.export_dir , target_file) 00173 logging.debug("Generating: %s", target_path) 00174 open(target_path, "w").write(target_text) 00175 self.generated_files += [target_path] 00176 00177 def make_key (self, src): 00178 """From a source file, extract group name 00179 Positional Arguments: 00180 src - the src's location 00181 """ 00182 rel_path = relpath(src, self.resources .file_basepath[src]) 00183 path_list = os.path.normpath(rel_path).split(os.sep) 00184 assert len(path_list) >= 1 00185 if len(path_list) == 1: 00186 key = self.project_name 00187 else: 00188 key = path_list[0] 00189 return key 00190 00191 def group_project_files (self, sources): 00192 """Group the source files by their encompassing directory 00193 Positional Arguments: 00194 sources - array of source locations 00195 00196 Returns a dictionary of {group name: list of source locations} 00197 """ 00198 data = sorted(sources, key=self.make_key ) 00199 return {k: list(g) for k,g in groupby(data, self.make_key )} 00200 00201 @staticmethod 00202 def build (project_name, log_name='build_log.txt', cleanup=True): 00203 """Invoke exporters build command within a subprocess. 00204 This method is assumed to be executed at the same level as exporter 00205 project files and project source code. 00206 See uvision/__init__.py, iar/__init__.py, and makefile/__init__.py for 00207 example implemenation. 00208 00209 Positional Arguments: 00210 project_name - the name of the project to build; often required by 00211 exporter's build command. 00212 00213 Keyword Args: 00214 log_name - name of the build log to create. Written and printed out, 00215 deleted if cleanup = True 00216 cleanup - a boolean dictating whether exported project files and 00217 build log are removed after build 00218 00219 Returns -1 on failure and 0 on success 00220 """ 00221 raise NotImplementedError("Implement in derived Exporter class.") 00222 00223 @staticmethod 00224 def clean (project_name): 00225 """Clean a previously exported project 00226 This method is assumed to be executed at the same level as exporter 00227 project files and project source code. 00228 See uvision/__init__.py, iar/__init__.py, and makefile/__init__.py for 00229 example implemenation. 00230 00231 Positional Arguments: 00232 project_name - the name of the project to build; often required by 00233 exporter's build command. 00234 00235 Returns nothing. May raise exceptions 00236 """ 00237 raise NotImplementedError("Implement in derived Exporter class.") 00238 00239 @abstractmethod 00240 def generate (self): 00241 """Generate an IDE/tool specific project file""" 00242 raise NotImplementedError("Implement a generate function in Exporter child class") 00243 00244 @classmethod 00245 def is_target_supported (cls, target_name): 00246 """Query support for a particular target 00247 00248 NOTE: override this method if your exporter does not provide a static list of targets 00249 00250 Positional Arguments: 00251 target_name - the name of the target. 00252 """ 00253 target = TARGET_MAP[target_name] 00254 return bool(set(target.resolution_order_names).intersection(set(cls.TARGETS))) \ 00255 and cls.TOOLCHAIN in target.supported_toolchains 00256 00257 00258 @classmethod 00259 def all_supported_targets(cls): 00260 return [t for t in TARGET_MAP.keys() if cls.is_target_supported(t)] 00261 00262 @staticmethod 00263 def filter_dot (str): 00264 """ 00265 Remove the './' or '.\\' prefix, if present. 00266 """ 00267 if str == None: 00268 return None 00269 if str[:2] == './': 00270 return str[2:] 00271 if str[:2] == '.\\': 00272 return str[2:] 00273 return str 00274 00275 00276 00277 def apply_supported_whitelist (compiler, whitelist, target): 00278 """Generate a list of supported targets for a given compiler and post-binary hook 00279 white-list.""" 00280 if compiler not in target.supported_toolchains: 00281 return False 00282 if not hasattr(target, "post_binary_hook"): 00283 return True 00284 if target.post_binary_hook['function'] in whitelist: 00285 return True 00286 else: 00287 return False
Generated on Tue Jul 12 2022 12:21:51 by
