Backup 1

Committer:
borlanic
Date:
Tue Apr 24 11:45:18 2018 +0000
Revision:
0:02dd72d1d465
BaBoRo_test2 - backup 1

Who changed what in which revision?

UserRevisionLine numberNew contents of line
borlanic 0:02dd72d1d465 1 """Just a template for subclassing"""
borlanic 0:02dd72d1d465 2 import os
borlanic 0:02dd72d1d465 3 from abc import abstractmethod, ABCMeta
borlanic 0:02dd72d1d465 4 import logging
borlanic 0:02dd72d1d465 5 from os.path import join, dirname, relpath, basename, realpath, normpath, exists
borlanic 0:02dd72d1d465 6 from itertools import groupby
borlanic 0:02dd72d1d465 7 from jinja2 import FileSystemLoader, StrictUndefined
borlanic 0:02dd72d1d465 8 from jinja2.environment import Environment
borlanic 0:02dd72d1d465 9 import copy
borlanic 0:02dd72d1d465 10
borlanic 0:02dd72d1d465 11 from tools.targets import TARGET_MAP
borlanic 0:02dd72d1d465 12
borlanic 0:02dd72d1d465 13
borlanic 0:02dd72d1d465 14 class TargetNotSupportedException(Exception):
borlanic 0:02dd72d1d465 15 """Indicates that an IDE does not support a particular MCU"""
borlanic 0:02dd72d1d465 16 pass
borlanic 0:02dd72d1d465 17
borlanic 0:02dd72d1d465 18 class ExporterTargetsProperty(object):
borlanic 0:02dd72d1d465 19 """ Exporter descriptor for TARGETS
borlanic 0:02dd72d1d465 20 TARGETS as class attribute for backward compatibility
borlanic 0:02dd72d1d465 21 (allows: if in Exporter.TARGETS)
borlanic 0:02dd72d1d465 22 """
borlanic 0:02dd72d1d465 23 def __init__(self, func):
borlanic 0:02dd72d1d465 24 self.func = func
borlanic 0:02dd72d1d465 25 def __get__(self, inst, cls):
borlanic 0:02dd72d1d465 26 return self.func(cls)
borlanic 0:02dd72d1d465 27
borlanic 0:02dd72d1d465 28 def deprecated_exporter(CLS):
borlanic 0:02dd72d1d465 29 old_init = CLS.__init__
borlanic 0:02dd72d1d465 30 old_name = CLS.NAME
borlanic 0:02dd72d1d465 31 def __init__(*args, **kwargs):
borlanic 0:02dd72d1d465 32 print("==================== DEPRECATION NOTICE ====================")
borlanic 0:02dd72d1d465 33 print("The exporter %s is no longer maintained, and deprecated." % old_name)
borlanic 0:02dd72d1d465 34 print("%s will be removed from mbed OS for the mbed OS 5.6 release." % old_name)
borlanic 0:02dd72d1d465 35 old_init(*args, **kwargs)
borlanic 0:02dd72d1d465 36 CLS.__init__ = __init__
borlanic 0:02dd72d1d465 37 CLS.NAME = "%s (DEPRECATED)" % old_name
borlanic 0:02dd72d1d465 38 return CLS
borlanic 0:02dd72d1d465 39
borlanic 0:02dd72d1d465 40 class Exporter(object):
borlanic 0:02dd72d1d465 41 """Exporter base class
borlanic 0:02dd72d1d465 42
borlanic 0:02dd72d1d465 43 This class is meant to be extended by individual exporters, and provides a
borlanic 0:02dd72d1d465 44 few helper methods for implementing an exporter with either jinja2 or
borlanic 0:02dd72d1d465 45 progen.
borlanic 0:02dd72d1d465 46 """
borlanic 0:02dd72d1d465 47 __metaclass__ = ABCMeta
borlanic 0:02dd72d1d465 48 TEMPLATE_DIR = dirname(__file__)
borlanic 0:02dd72d1d465 49 DOT_IN_RELATIVE_PATH = False
borlanic 0:02dd72d1d465 50 NAME = None
borlanic 0:02dd72d1d465 51 TARGETS = set()
borlanic 0:02dd72d1d465 52 TOOLCHAIN = None
borlanic 0:02dd72d1d465 53 CLEAN_FILES = ("GettingStarted.html",)
borlanic 0:02dd72d1d465 54
borlanic 0:02dd72d1d465 55
borlanic 0:02dd72d1d465 56 def __init__(self, target, export_dir, project_name, toolchain,
borlanic 0:02dd72d1d465 57 extra_symbols=None, resources=None):
borlanic 0:02dd72d1d465 58 """Initialize an instance of class exporter
borlanic 0:02dd72d1d465 59 Positional arguments:
borlanic 0:02dd72d1d465 60 target - the target mcu/board for this project
borlanic 0:02dd72d1d465 61 export_dir - the directory of the exported project files
borlanic 0:02dd72d1d465 62 project_name - the name of the project
borlanic 0:02dd72d1d465 63 toolchain - an instance of class toolchain
borlanic 0:02dd72d1d465 64
borlanic 0:02dd72d1d465 65 Keyword arguments:
borlanic 0:02dd72d1d465 66 extra_symbols - a list of extra macros for the toolchain
borlanic 0:02dd72d1d465 67 resources - an instance of class Resources
borlanic 0:02dd72d1d465 68 """
borlanic 0:02dd72d1d465 69 self.export_dir = export_dir
borlanic 0:02dd72d1d465 70 self.target = target
borlanic 0:02dd72d1d465 71 self.project_name = project_name
borlanic 0:02dd72d1d465 72 self.toolchain = toolchain
borlanic 0:02dd72d1d465 73 jinja_loader = FileSystemLoader(os.path.dirname(os.path.abspath(__file__)))
borlanic 0:02dd72d1d465 74 self.jinja_environment = Environment(loader=jinja_loader)
borlanic 0:02dd72d1d465 75 self.resources = resources
borlanic 0:02dd72d1d465 76 self.generated_files = []
borlanic 0:02dd72d1d465 77 self.static_files = (
borlanic 0:02dd72d1d465 78 join(self.TEMPLATE_DIR, "GettingStarted.html"),
borlanic 0:02dd72d1d465 79 join(self.TEMPLATE_DIR, ".mbed"),
borlanic 0:02dd72d1d465 80 )
borlanic 0:02dd72d1d465 81 self.builder_files_dict = {}
borlanic 0:02dd72d1d465 82 self.add_config()
borlanic 0:02dd72d1d465 83
borlanic 0:02dd72d1d465 84 def get_toolchain(self):
borlanic 0:02dd72d1d465 85 """A helper getter function that we should probably eliminate"""
borlanic 0:02dd72d1d465 86 return self.TOOLCHAIN
borlanic 0:02dd72d1d465 87
borlanic 0:02dd72d1d465 88 def add_config(self):
borlanic 0:02dd72d1d465 89 """Add the containgin directory of mbed_config.h to include dirs"""
borlanic 0:02dd72d1d465 90 config = self.toolchain.get_config_header()
borlanic 0:02dd72d1d465 91 if config:
borlanic 0:02dd72d1d465 92 self.resources.inc_dirs.append(
borlanic 0:02dd72d1d465 93 dirname(relpath(config,
borlanic 0:02dd72d1d465 94 self.resources.file_basepath[config])))
borlanic 0:02dd72d1d465 95
borlanic 0:02dd72d1d465 96 @property
borlanic 0:02dd72d1d465 97 def flags(self):
borlanic 0:02dd72d1d465 98 """Returns a dictionary of toolchain flags.
borlanic 0:02dd72d1d465 99 Keys of the dictionary are:
borlanic 0:02dd72d1d465 100 cxx_flags - c++ flags
borlanic 0:02dd72d1d465 101 c_flags - c flags
borlanic 0:02dd72d1d465 102 ld_flags - linker flags
borlanic 0:02dd72d1d465 103 asm_flags - assembler flags
borlanic 0:02dd72d1d465 104 common_flags - common options
borlanic 0:02dd72d1d465 105 """
borlanic 0:02dd72d1d465 106 config_header = self.toolchain.get_config_header()
borlanic 0:02dd72d1d465 107 flags = {key + "_flags": copy.deepcopy(value) for key, value
borlanic 0:02dd72d1d465 108 in self.toolchain.flags.items()}
borlanic 0:02dd72d1d465 109 asm_defines = self.toolchain.get_compile_options(
borlanic 0:02dd72d1d465 110 self.toolchain.get_symbols(for_asm=True),
borlanic 0:02dd72d1d465 111 filter(None, self.resources.inc_dirs),
borlanic 0:02dd72d1d465 112 for_asm=True)
borlanic 0:02dd72d1d465 113 c_defines = ["-D" + symbol for symbol in self.toolchain.get_symbols()]
borlanic 0:02dd72d1d465 114 flags['asm_flags'] += asm_defines
borlanic 0:02dd72d1d465 115 flags['c_flags'] += c_defines
borlanic 0:02dd72d1d465 116 flags['cxx_flags'] += c_defines
borlanic 0:02dd72d1d465 117 if config_header:
borlanic 0:02dd72d1d465 118 config_header = relpath(config_header,
borlanic 0:02dd72d1d465 119 self.resources.file_basepath[config_header])
borlanic 0:02dd72d1d465 120 flags['c_flags'] += self.toolchain.get_config_option(config_header)
borlanic 0:02dd72d1d465 121 flags['cxx_flags'] += self.toolchain.get_config_option(
borlanic 0:02dd72d1d465 122 config_header)
borlanic 0:02dd72d1d465 123 return flags
borlanic 0:02dd72d1d465 124
borlanic 0:02dd72d1d465 125 def get_source_paths(self):
borlanic 0:02dd72d1d465 126 """Returns a list of the directories where source files are contained"""
borlanic 0:02dd72d1d465 127 source_keys = ['s_sources', 'c_sources', 'cpp_sources', 'hex_files',
borlanic 0:02dd72d1d465 128 'objects', 'libraries']
borlanic 0:02dd72d1d465 129 source_files = []
borlanic 0:02dd72d1d465 130 for key in source_keys:
borlanic 0:02dd72d1d465 131 source_files.extend(getattr(self.resources, key))
borlanic 0:02dd72d1d465 132 return list(set([os.path.dirname(src) for src in source_files]))
borlanic 0:02dd72d1d465 133
borlanic 0:02dd72d1d465 134 def gen_file_dest(self, target_file):
borlanic 0:02dd72d1d465 135 """Generate the project file location in an exported project"""
borlanic 0:02dd72d1d465 136 return join(self.export_dir, target_file)
borlanic 0:02dd72d1d465 137
borlanic 0:02dd72d1d465 138 def gen_file(self, template_file, data, target_file, **kwargs):
borlanic 0:02dd72d1d465 139 """Generates a project file from a template using jinja"""
borlanic 0:02dd72d1d465 140 target_text = self._gen_file_inner(template_file, data, target_file, **kwargs)
borlanic 0:02dd72d1d465 141 target_path = self.gen_file_dest(target_file)
borlanic 0:02dd72d1d465 142 logging.debug("Generating: %s", target_path)
borlanic 0:02dd72d1d465 143 open(target_path, "w").write(target_text)
borlanic 0:02dd72d1d465 144 self.generated_files += [target_path]
borlanic 0:02dd72d1d465 145
borlanic 0:02dd72d1d465 146 def gen_file_nonoverwrite(self, template_file, data, target_file, **kwargs):
borlanic 0:02dd72d1d465 147 """Generates a project file from a template using jinja"""
borlanic 0:02dd72d1d465 148 target_text = self._gen_file_inner(template_file, data, target_file, **kwargs)
borlanic 0:02dd72d1d465 149 target_path = self.gen_file_dest(target_file)
borlanic 0:02dd72d1d465 150 if exists(target_path):
borlanic 0:02dd72d1d465 151 with open(target_path) as fdin:
borlanic 0:02dd72d1d465 152 old_text = fdin.read()
borlanic 0:02dd72d1d465 153 if target_text not in old_text:
borlanic 0:02dd72d1d465 154 with open(target_path, "a") as fdout:
borlanic 0:02dd72d1d465 155 fdout.write(target_text)
borlanic 0:02dd72d1d465 156 else:
borlanic 0:02dd72d1d465 157 logging.debug("Generating: %s", target_path)
borlanic 0:02dd72d1d465 158 open(target_path, "w").write(target_text)
borlanic 0:02dd72d1d465 159 self.generated_files += [target_path]
borlanic 0:02dd72d1d465 160
borlanic 0:02dd72d1d465 161 def _gen_file_inner(self, template_file, data, target_file, **kwargs):
borlanic 0:02dd72d1d465 162 """Generates a project file from a template using jinja"""
borlanic 0:02dd72d1d465 163 jinja_loader = FileSystemLoader(
borlanic 0:02dd72d1d465 164 os.path.dirname(os.path.abspath(__file__)))
borlanic 0:02dd72d1d465 165 jinja_environment = Environment(loader=jinja_loader,
borlanic 0:02dd72d1d465 166 undefined=StrictUndefined, **kwargs)
borlanic 0:02dd72d1d465 167
borlanic 0:02dd72d1d465 168 template = jinja_environment.get_template(template_file)
borlanic 0:02dd72d1d465 169 target_text = template.render(data)
borlanic 0:02dd72d1d465 170 return target_text
borlanic 0:02dd72d1d465 171
borlanic 0:02dd72d1d465 172 target_path = join(self.export_dir, target_file)
borlanic 0:02dd72d1d465 173 logging.debug("Generating: %s", target_path)
borlanic 0:02dd72d1d465 174 open(target_path, "w").write(target_text)
borlanic 0:02dd72d1d465 175 self.generated_files += [target_path]
borlanic 0:02dd72d1d465 176
borlanic 0:02dd72d1d465 177 def make_key(self, src):
borlanic 0:02dd72d1d465 178 """From a source file, extract group name
borlanic 0:02dd72d1d465 179 Positional Arguments:
borlanic 0:02dd72d1d465 180 src - the src's location
borlanic 0:02dd72d1d465 181 """
borlanic 0:02dd72d1d465 182 rel_path = relpath(src, self.resources.file_basepath[src])
borlanic 0:02dd72d1d465 183 path_list = os.path.normpath(rel_path).split(os.sep)
borlanic 0:02dd72d1d465 184 assert len(path_list) >= 1
borlanic 0:02dd72d1d465 185 if len(path_list) == 1:
borlanic 0:02dd72d1d465 186 key = self.project_name
borlanic 0:02dd72d1d465 187 else:
borlanic 0:02dd72d1d465 188 key = path_list[0]
borlanic 0:02dd72d1d465 189 return key
borlanic 0:02dd72d1d465 190
borlanic 0:02dd72d1d465 191 def group_project_files(self, sources):
borlanic 0:02dd72d1d465 192 """Group the source files by their encompassing directory
borlanic 0:02dd72d1d465 193 Positional Arguments:
borlanic 0:02dd72d1d465 194 sources - array of source locations
borlanic 0:02dd72d1d465 195
borlanic 0:02dd72d1d465 196 Returns a dictionary of {group name: list of source locations}
borlanic 0:02dd72d1d465 197 """
borlanic 0:02dd72d1d465 198 data = sorted(sources, key=self.make_key)
borlanic 0:02dd72d1d465 199 return {k: list(g) for k,g in groupby(data, self.make_key)}
borlanic 0:02dd72d1d465 200
borlanic 0:02dd72d1d465 201 @staticmethod
borlanic 0:02dd72d1d465 202 def build(project_name, log_name='build_log.txt', cleanup=True):
borlanic 0:02dd72d1d465 203 """Invoke exporters build command within a subprocess.
borlanic 0:02dd72d1d465 204 This method is assumed to be executed at the same level as exporter
borlanic 0:02dd72d1d465 205 project files and project source code.
borlanic 0:02dd72d1d465 206 See uvision/__init__.py, iar/__init__.py, and makefile/__init__.py for
borlanic 0:02dd72d1d465 207 example implemenation.
borlanic 0:02dd72d1d465 208
borlanic 0:02dd72d1d465 209 Positional Arguments:
borlanic 0:02dd72d1d465 210 project_name - the name of the project to build; often required by
borlanic 0:02dd72d1d465 211 exporter's build command.
borlanic 0:02dd72d1d465 212
borlanic 0:02dd72d1d465 213 Keyword Args:
borlanic 0:02dd72d1d465 214 log_name - name of the build log to create. Written and printed out,
borlanic 0:02dd72d1d465 215 deleted if cleanup = True
borlanic 0:02dd72d1d465 216 cleanup - a boolean dictating whether exported project files and
borlanic 0:02dd72d1d465 217 build log are removed after build
borlanic 0:02dd72d1d465 218
borlanic 0:02dd72d1d465 219 Returns -1 on failure and 0 on success
borlanic 0:02dd72d1d465 220 """
borlanic 0:02dd72d1d465 221 raise NotImplementedError("Implement in derived Exporter class.")
borlanic 0:02dd72d1d465 222
borlanic 0:02dd72d1d465 223 @staticmethod
borlanic 0:02dd72d1d465 224 def clean(project_name):
borlanic 0:02dd72d1d465 225 """Clean a previously exported project
borlanic 0:02dd72d1d465 226 This method is assumed to be executed at the same level as exporter
borlanic 0:02dd72d1d465 227 project files and project source code.
borlanic 0:02dd72d1d465 228 See uvision/__init__.py, iar/__init__.py, and makefile/__init__.py for
borlanic 0:02dd72d1d465 229 example implemenation.
borlanic 0:02dd72d1d465 230
borlanic 0:02dd72d1d465 231 Positional Arguments:
borlanic 0:02dd72d1d465 232 project_name - the name of the project to build; often required by
borlanic 0:02dd72d1d465 233 exporter's build command.
borlanic 0:02dd72d1d465 234
borlanic 0:02dd72d1d465 235 Returns nothing. May raise exceptions
borlanic 0:02dd72d1d465 236 """
borlanic 0:02dd72d1d465 237 raise NotImplementedError("Implement in derived Exporter class.")
borlanic 0:02dd72d1d465 238
borlanic 0:02dd72d1d465 239 @abstractmethod
borlanic 0:02dd72d1d465 240 def generate(self):
borlanic 0:02dd72d1d465 241 """Generate an IDE/tool specific project file"""
borlanic 0:02dd72d1d465 242 raise NotImplementedError("Implement a generate function in Exporter child class")
borlanic 0:02dd72d1d465 243
borlanic 0:02dd72d1d465 244 @classmethod
borlanic 0:02dd72d1d465 245 def is_target_supported(cls, target_name):
borlanic 0:02dd72d1d465 246 """Query support for a particular target
borlanic 0:02dd72d1d465 247
borlanic 0:02dd72d1d465 248 NOTE: override this method if your exporter does not provide a static list of targets
borlanic 0:02dd72d1d465 249
borlanic 0:02dd72d1d465 250 Positional Arguments:
borlanic 0:02dd72d1d465 251 target_name - the name of the target.
borlanic 0:02dd72d1d465 252 """
borlanic 0:02dd72d1d465 253 target = TARGET_MAP[target_name]
borlanic 0:02dd72d1d465 254 return bool(set(target.resolution_order_names).intersection(set(cls.TARGETS))) \
borlanic 0:02dd72d1d465 255 and cls.TOOLCHAIN in target.supported_toolchains
borlanic 0:02dd72d1d465 256
borlanic 0:02dd72d1d465 257
borlanic 0:02dd72d1d465 258 @classmethod
borlanic 0:02dd72d1d465 259 def all_supported_targets(cls):
borlanic 0:02dd72d1d465 260 return [t for t in TARGET_MAP.keys() if cls.is_target_supported(t)]
borlanic 0:02dd72d1d465 261
borlanic 0:02dd72d1d465 262 @staticmethod
borlanic 0:02dd72d1d465 263 def filter_dot(str):
borlanic 0:02dd72d1d465 264 """
borlanic 0:02dd72d1d465 265 Remove the './' or '.\\' prefix, if present.
borlanic 0:02dd72d1d465 266 """
borlanic 0:02dd72d1d465 267 if str == None:
borlanic 0:02dd72d1d465 268 return None
borlanic 0:02dd72d1d465 269 if str[:2] == './':
borlanic 0:02dd72d1d465 270 return str[2:]
borlanic 0:02dd72d1d465 271 if str[:2] == '.\\':
borlanic 0:02dd72d1d465 272 return str[2:]
borlanic 0:02dd72d1d465 273 return str
borlanic 0:02dd72d1d465 274
borlanic 0:02dd72d1d465 275
borlanic 0:02dd72d1d465 276
borlanic 0:02dd72d1d465 277 def apply_supported_whitelist(compiler, whitelist, target):
borlanic 0:02dd72d1d465 278 """Generate a list of supported targets for a given compiler and post-binary hook
borlanic 0:02dd72d1d465 279 white-list."""
borlanic 0:02dd72d1d465 280 if compiler not in target.supported_toolchains:
borlanic 0:02dd72d1d465 281 return False
borlanic 0:02dd72d1d465 282 if not hasattr(target, "post_binary_hook"):
borlanic 0:02dd72d1d465 283 return True
borlanic 0:02dd72d1d465 284 if target.post_binary_hook['function'] in whitelist:
borlanic 0:02dd72d1d465 285 return True
borlanic 0:02dd72d1d465 286 else:
borlanic 0:02dd72d1d465 287 return False