Clone of official tools

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