Clone of official tools
Diff: export/exporters.py
- Revision:
- 43:2a7da56ebd24
- Parent:
- 40:7d3fa6b99b2b
--- a/export/exporters.py Mon Nov 06 13:17:14 2017 -0600 +++ b/export/exporters.py Tue Sep 25 13:43:09 2018 -0500 @@ -2,13 +2,15 @@ import os from abc import abstractmethod, ABCMeta import logging -from os.path import join, dirname, relpath, basename, realpath, normpath +from os.path import join, dirname, relpath, basename, realpath, normpath, exists from itertools import groupby from jinja2 import FileSystemLoader, StrictUndefined from jinja2.environment import Environment import copy from tools.targets import TARGET_MAP +from tools.utils import mkdir +from tools.resources import FileType class TargetNotSupportedException(Exception): @@ -50,6 +52,7 @@ NAME = None TARGETS = set() TOOLCHAIN = None + CLEAN_FILES = ("GettingStarted.html",) def __init__(self, target, export_dir, project_name, toolchain, @@ -85,12 +88,8 @@ return self.TOOLCHAIN def add_config(self): - """Add the containgin directory of mbed_config.h to include dirs""" - config = self.toolchain.get_config_header() - if config: - self.resources.inc_dirs.append( - dirname(relpath(config, - self.resources.file_basepath[config]))) + """Add the containing directory of mbed_config.h to include dirs""" + pass @property def flags(self): @@ -102,9 +101,7 @@ asm_flags - assembler flags common_flags - common options """ - config_header = self.toolchain.get_config_header() - flags = {key + "_flags": copy.deepcopy(value) for key, value - in self.toolchain.flags.iteritems()} + flags = self.toolchain_flags(self.toolchain) asm_defines = self.toolchain.get_compile_options( self.toolchain.get_symbols(for_asm=True), filter(None, self.resources.inc_dirs), @@ -113,14 +110,52 @@ flags['asm_flags'] += asm_defines flags['c_flags'] += c_defines flags['cxx_flags'] += c_defines + config_header = self.config_header_ref if config_header: - config_header = relpath(config_header, - self.resources.file_basepath[config_header]) - flags['c_flags'] += self.toolchain.get_config_option(config_header) - flags['cxx_flags'] += self.toolchain.get_config_option( - config_header) + config_option = self.toolchain.get_config_option( + config_header.name) + flags['c_flags'] += config_option + flags['cxx_flags'] += config_option return flags + @property + def libraries(self): + return [l for l in self.resources.get_file_names(FileType.LIB) + if l.endswith(self.toolchain.LIBRARY_EXT)] + + def toolchain_flags(self, toolchain): + """Returns a dictionary of toolchain flags. + Keys of the dictionary are: + cxx_flags - c++ flags + c_flags - c flags + ld_flags - linker flags + asm_flags - assembler flags + common_flags - common options + + The difference from the above is that it takes a parameter. + """ + flags = {key + "_flags": copy.deepcopy(value) for key, value + in toolchain.flags.items()} + config_header = self.config_header_ref + if config_header: + header_options = self.toolchain.get_config_option( + config_header.name) + flags['c_flags'] += header_options + flags['cxx_flags'] += header_options + return flags + + @property + def config_header_ref(self): + config_header = self.toolchain.get_config_header() + if config_header: + def is_config_header(f): + return f.path == config_header + return list(filter( + is_config_header, self.resources.get_file_refs(FileType.HEADER) + ))[0] + else: + return None + def get_source_paths(self): """Returns a list of the directories where source files are contained""" source_keys = ['s_sources', 'c_sources', 'cpp_sources', 'hex_files', @@ -130,8 +165,36 @@ source_files.extend(getattr(self.resources, key)) return list(set([os.path.dirname(src) for src in source_files])) + def gen_file_dest(self, target_file): + """Generate the project file location in an exported project""" + return join(self.export_dir, target_file) + def gen_file(self, template_file, data, target_file, **kwargs): """Generates a project file from a template using jinja""" + target_text = self._gen_file_inner(template_file, data, target_file, **kwargs) + target_path = self.gen_file_dest(target_file) + mkdir(dirname(target_path)) + logging.debug("Generating: %s", target_path) + open(target_path, "w").write(target_text) + self.generated_files += [target_path] + + def gen_file_nonoverwrite(self, template_file, data, target_file, **kwargs): + """Generates a project file from a template using jinja""" + target_text = self._gen_file_inner(template_file, data, target_file, **kwargs) + target_path = self.gen_file_dest(target_file) + if exists(target_path): + with open(target_path) as fdin: + old_text = fdin.read() + if target_text not in old_text: + with open(target_path, "a") as fdout: + fdout.write(target_text) + else: + logging.debug("Generating: %s", target_path) + open(target_path, "w").write(target_text) + self.generated_files += [target_path] + + def _gen_file_inner(self, template_file, data, target_file, **kwargs): + """Generates a project file from a template using jinja""" jinja_loader = FileSystemLoader( os.path.dirname(os.path.abspath(__file__))) jinja_environment = Environment(loader=jinja_loader, @@ -139,6 +202,7 @@ template = jinja_environment.get_template(template_file) target_text = template.render(data) + return target_text target_path = join(self.export_dir, target_file) logging.debug("Generating: %s", target_path) @@ -150,8 +214,7 @@ Positional Arguments: src - the src's location """ - rel_path = relpath(src, self.resources.file_basepath[src]) - path_list = os.path.normpath(rel_path).split(os.sep) + path_list = os.path.normpath(src).split(os.sep) assert len(path_list) >= 1 if len(path_list) == 1: key = self.project_name @@ -189,12 +252,28 @@ Returns -1 on failure and 0 on success """ - raise NotImplemented("Implement in derived Exporter class.") + raise NotImplementedError("Implement in derived Exporter class.") + + @staticmethod + def clean(project_name): + """Clean a previously exported project + This method is assumed to be executed at the same level as exporter + project files and project source code. + See uvision/__init__.py, iar/__init__.py, and makefile/__init__.py for + example implemenation. + + Positional Arguments: + project_name - the name of the project to build; often required by + exporter's build command. + + Returns nothing. May raise exceptions + """ + raise NotImplementedError("Implement in derived Exporter class.") @abstractmethod def generate(self): """Generate an IDE/tool specific project file""" - raise NotImplemented("Implement a generate function in Exporter child class") + raise NotImplementedError("Implement a generate function in Exporter child class") @classmethod def is_target_supported(cls, target_name): @@ -214,6 +293,20 @@ def all_supported_targets(cls): return [t for t in TARGET_MAP.keys() if cls.is_target_supported(t)] + @staticmethod + def filter_dot(str): + """ + Remove the './' or '.\\' prefix, if present. + """ + if str == None: + return None + if str[:2] == './': + return str[2:] + if str[:2] == '.\\': + return str[2:] + return str + + def apply_supported_whitelist(compiler, whitelist, target): """Generate a list of supported targets for a given compiler and post-binary hook