Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
export/exporters.py@24:25bff2709c20, 2016-08-01 (annotated)
- Committer:
- screamer
- Date:
- Mon Aug 01 09:10:17 2016 +0100
- Revision:
- 24:25bff2709c20
- Parent:
- 23:fbae331171fa
- Child:
- 29:1210849dba19
Major update to tools from ARMmbed/mbed-os
Who changed what in which revision?
| User | Revision | Line number | New contents of line | 
|---|---|---|---|
| screamer | 0:66f3b5499f7f | 1 | """Just a template for subclassing""" | 
| screamer | 0:66f3b5499f7f | 2 | import uuid, shutil, os, logging, fnmatch | 
| screamer | 0:66f3b5499f7f | 3 | from os import walk, remove | 
| screamer | 0:66f3b5499f7f | 4 | from os.path import join, dirname, isdir, split | 
| screamer | 0:66f3b5499f7f | 5 | from copy import copy | 
| screamer | 0:66f3b5499f7f | 6 | from jinja2 import Template, FileSystemLoader | 
| screamer | 0:66f3b5499f7f | 7 | from jinja2.environment import Environment | 
| screamer | 0:66f3b5499f7f | 8 | from contextlib import closing | 
| screamer | 0:66f3b5499f7f | 9 | from zipfile import ZipFile, ZIP_DEFLATED | 
| screamer | 13:ab47a20b66f0 | 10 | from operator import add | 
| screamer | 0:66f3b5499f7f | 11 | |
| screamer | 0:66f3b5499f7f | 12 | from tools.utils import mkdir | 
| screamer | 0:66f3b5499f7f | 13 | from tools.toolchains import TOOLCHAIN_CLASSES | 
| screamer | 0:66f3b5499f7f | 14 | from tools.targets import TARGET_MAP | 
| screamer | 0:66f3b5499f7f | 15 | |
| screamer | 0:66f3b5499f7f | 16 | from project_generator.generate import Generator | 
| screamer | 0:66f3b5499f7f | 17 | from project_generator.project import Project | 
| screamer | 0:66f3b5499f7f | 18 | from project_generator.settings import ProjectSettings | 
| screamer | 0:66f3b5499f7f | 19 | |
| screamer | 13:ab47a20b66f0 | 20 | from tools.config import Config | 
| screamer | 13:ab47a20b66f0 | 21 | |
| screamer | 0:66f3b5499f7f | 22 | class OldLibrariesException(Exception): pass | 
| screamer | 0:66f3b5499f7f | 23 | |
| screamer | 23:fbae331171fa | 24 | class FailedBuildException(Exception) : pass | 
| screamer | 23:fbae331171fa | 25 | |
| screamer | 24:25bff2709c20 | 26 | # Exporter descriptor for TARGETS | 
| screamer | 24:25bff2709c20 | 27 | # TARGETS as class attribute for backward compatibility (allows: if in Exporter.TARGETS) | 
| screamer | 24:25bff2709c20 | 28 | class ExporterTargetsProperty(object): | 
| screamer | 24:25bff2709c20 | 29 | def __init__(self, func): | 
| screamer | 24:25bff2709c20 | 30 | self.func = func | 
| screamer | 24:25bff2709c20 | 31 | def __get__(self, inst, cls): | 
| screamer | 24:25bff2709c20 | 32 | return self.func(cls) | 
| screamer | 24:25bff2709c20 | 33 | |
| screamer | 0:66f3b5499f7f | 34 | class Exporter(object): | 
| screamer | 0:66f3b5499f7f | 35 | TEMPLATE_DIR = dirname(__file__) | 
| screamer | 0:66f3b5499f7f | 36 | DOT_IN_RELATIVE_PATH = False | 
| screamer | 0:66f3b5499f7f | 37 | |
| screamer | 13:ab47a20b66f0 | 38 | def __init__(self, target, inputDir, program_name, build_url_resolver, extra_symbols=None, sources_relative=True): | 
| screamer | 0:66f3b5499f7f | 39 | self.inputDir = inputDir | 
| screamer | 0:66f3b5499f7f | 40 | self.target = target | 
| screamer | 0:66f3b5499f7f | 41 | self.program_name = program_name | 
| screamer | 0:66f3b5499f7f | 42 | self.toolchain = TOOLCHAIN_CLASSES[self.get_toolchain()](TARGET_MAP[target]) | 
| screamer | 0:66f3b5499f7f | 43 | self.build_url_resolver = build_url_resolver | 
| screamer | 0:66f3b5499f7f | 44 | jinja_loader = FileSystemLoader(os.path.dirname(os.path.abspath(__file__))) | 
| screamer | 0:66f3b5499f7f | 45 | self.jinja_environment = Environment(loader=jinja_loader) | 
| screamer | 0:66f3b5499f7f | 46 | self.extra_symbols = extra_symbols | 
| screamer | 13:ab47a20b66f0 | 47 | self.config_macros = [] | 
| screamer | 13:ab47a20b66f0 | 48 | self.sources_relative = sources_relative | 
| screamer | 13:ab47a20b66f0 | 49 | self.config_header = None | 
| screamer | 0:66f3b5499f7f | 50 | |
| screamer | 0:66f3b5499f7f | 51 | def get_toolchain(self): | 
| screamer | 0:66f3b5499f7f | 52 | return self.TOOLCHAIN | 
| screamer | 0:66f3b5499f7f | 53 | |
| screamer | 13:ab47a20b66f0 | 54 | @property | 
| screamer | 13:ab47a20b66f0 | 55 | def flags(self): | 
| screamer | 13:ab47a20b66f0 | 56 | return self.toolchain.flags | 
| screamer | 13:ab47a20b66f0 | 57 | |
| screamer | 13:ab47a20b66f0 | 58 | @property | 
| screamer | 13:ab47a20b66f0 | 59 | def progen_flags(self): | 
| screamer | 13:ab47a20b66f0 | 60 | if not hasattr(self, "_progen_flag_cache") : | 
| screamer | 13:ab47a20b66f0 | 61 | self._progen_flag_cache = dict([(key + "_flags", value) for key,value in self.flags.iteritems()]) | 
| screamer | 13:ab47a20b66f0 | 62 | if self.config_header: | 
| screamer | 13:ab47a20b66f0 | 63 | self._progen_flag_cache['c_flags'] += self.toolchain.get_config_option(self.config_header) | 
| screamer | 13:ab47a20b66f0 | 64 | self._progen_flag_cache['cxx_flags'] += self.toolchain.get_config_option(self.config_header) | 
| screamer | 13:ab47a20b66f0 | 65 | return self._progen_flag_cache | 
| screamer | 13:ab47a20b66f0 | 66 | |
| screamer | 0:66f3b5499f7f | 67 | def __scan_and_copy(self, src_path, trg_path): | 
| screamer | 0:66f3b5499f7f | 68 | resources = self.toolchain.scan_resources(src_path) | 
| screamer | 0:66f3b5499f7f | 69 | |
| screamer | 0:66f3b5499f7f | 70 | for r_type in ['headers', 's_sources', 'c_sources', 'cpp_sources', | 
| screamer | 0:66f3b5499f7f | 71 | 'objects', 'libraries', 'linker_script', | 
| screamer | 13:ab47a20b66f0 | 72 | 'lib_builds', 'lib_refs', 'hex_files', 'bin_files']: | 
| screamer | 0:66f3b5499f7f | 73 | r = getattr(resources, r_type) | 
| screamer | 0:66f3b5499f7f | 74 | if r: | 
| screamer | 13:ab47a20b66f0 | 75 | self.toolchain.copy_files(r, trg_path, resources=resources) | 
| screamer | 0:66f3b5499f7f | 76 | return resources | 
| screamer | 0:66f3b5499f7f | 77 | |
| screamer | 0:66f3b5499f7f | 78 | @staticmethod | 
| screamer | 0:66f3b5499f7f | 79 | def _get_dir_grouped_files(files): | 
| screamer | 0:66f3b5499f7f | 80 | """ Get grouped files based on the dirname """ | 
| screamer | 0:66f3b5499f7f | 81 | files_grouped = {} | 
| screamer | 0:66f3b5499f7f | 82 | for file in files: | 
| screamer | 0:66f3b5499f7f | 83 | rel_path = os.path.relpath(file, os.getcwd()) | 
| screamer | 0:66f3b5499f7f | 84 | dir_path = os.path.dirname(rel_path) | 
| screamer | 0:66f3b5499f7f | 85 | if dir_path == '': | 
| screamer | 0:66f3b5499f7f | 86 | # all files within the current dir go into Source_Files | 
| screamer | 0:66f3b5499f7f | 87 | dir_path = 'Source_Files' | 
| screamer | 0:66f3b5499f7f | 88 | if not dir_path in files_grouped.keys(): | 
| screamer | 0:66f3b5499f7f | 89 | files_grouped[dir_path] = [] | 
| screamer | 0:66f3b5499f7f | 90 | files_grouped[dir_path].append(file) | 
| screamer | 0:66f3b5499f7f | 91 | return files_grouped | 
| screamer | 0:66f3b5499f7f | 92 | |
| screamer | 0:66f3b5499f7f | 93 | def progen_get_project_data(self): | 
| screamer | 0:66f3b5499f7f | 94 | """ Get ProGen project data """ | 
| screamer | 0:66f3b5499f7f | 95 | # provide default data, some tools don't require any additional | 
| screamer | 0:66f3b5499f7f | 96 | # tool specific settings | 
| screamer | 0:66f3b5499f7f | 97 | code_files = [] | 
| screamer | 0:66f3b5499f7f | 98 | for r_type in ['c_sources', 'cpp_sources', 's_sources']: | 
| screamer | 0:66f3b5499f7f | 99 | for file in getattr(self.resources, r_type): | 
| screamer | 0:66f3b5499f7f | 100 | code_files.append(file) | 
| screamer | 0:66f3b5499f7f | 101 | |
| screamer | 0:66f3b5499f7f | 102 | sources_files = code_files + self.resources.hex_files + self.resources.objects + \ | 
| screamer | 0:66f3b5499f7f | 103 | self.resources.libraries | 
| screamer | 0:66f3b5499f7f | 104 | sources_grouped = Exporter._get_dir_grouped_files(sources_files) | 
| screamer | 0:66f3b5499f7f | 105 | headers_grouped = Exporter._get_dir_grouped_files(self.resources.headers) | 
| screamer | 0:66f3b5499f7f | 106 | |
| screamer | 0:66f3b5499f7f | 107 | project_data = { | 
| screamer | 0:66f3b5499f7f | 108 | 'common': { | 
| screamer | 0:66f3b5499f7f | 109 | 'sources': sources_grouped, | 
| screamer | 0:66f3b5499f7f | 110 | 'includes': headers_grouped, | 
| screamer | 0:66f3b5499f7f | 111 | 'build_dir':'.build', | 
| screamer | 0:66f3b5499f7f | 112 | 'target': [TARGET_MAP[self.target].progen['target']], | 
| screamer | 0:66f3b5499f7f | 113 | 'macros': self.get_symbols(), | 
| screamer | 0:66f3b5499f7f | 114 | 'export_dir': [self.inputDir], | 
| screamer | 0:66f3b5499f7f | 115 | 'linker_file': [self.resources.linker_script], | 
| screamer | 0:66f3b5499f7f | 116 | } | 
| screamer | 0:66f3b5499f7f | 117 | } | 
| screamer | 0:66f3b5499f7f | 118 | return project_data | 
| screamer | 0:66f3b5499f7f | 119 | |
| screamer | 23:fbae331171fa | 120 | def progen_gen_file(self, tool_name, project_data, progen_build=False): | 
| screamer | 13:ab47a20b66f0 | 121 | """ Generate project using ProGen Project API """ | 
| screamer | 0:66f3b5499f7f | 122 | settings = ProjectSettings() | 
| screamer | 0:66f3b5499f7f | 123 | project = Project(self.program_name, [project_data], settings) | 
| screamer | 0:66f3b5499f7f | 124 | # TODO: Fix this, the inc_dirs are not valid (our scripts copy files), therefore progen | 
| screamer | 0:66f3b5499f7f | 125 | # thinks it is not dict but a file, and adds them to workspace. | 
| screamer | 0:66f3b5499f7f | 126 | project.project['common']['include_paths'] = self.resources.inc_dirs | 
| screamer | 13:ab47a20b66f0 | 127 | project.generate(tool_name, copied=not self.sources_relative) | 
| screamer | 23:fbae331171fa | 128 | if progen_build: | 
| screamer | 23:fbae331171fa | 129 | print("Project exported, building...") | 
| screamer | 23:fbae331171fa | 130 | result = project.build(tool_name) | 
| screamer | 23:fbae331171fa | 131 | if result == -1: | 
| screamer | 23:fbae331171fa | 132 | raise FailedBuildException("Build Failed") | 
| screamer | 0:66f3b5499f7f | 133 | |
| screamer | 0:66f3b5499f7f | 134 | def __scan_all(self, path): | 
| screamer | 0:66f3b5499f7f | 135 | resources = [] | 
| screamer | 0:66f3b5499f7f | 136 | |
| screamer | 0:66f3b5499f7f | 137 | for root, dirs, files in walk(path): | 
| screamer | 0:66f3b5499f7f | 138 | for d in copy(dirs): | 
| screamer | 0:66f3b5499f7f | 139 | if d == '.' or d == '..': | 
| screamer | 0:66f3b5499f7f | 140 | dirs.remove(d) | 
| screamer | 0:66f3b5499f7f | 141 | |
| screamer | 0:66f3b5499f7f | 142 | for file in files: | 
| screamer | 0:66f3b5499f7f | 143 | file_path = join(root, file) | 
| screamer | 0:66f3b5499f7f | 144 | resources.append(file_path) | 
| screamer | 0:66f3b5499f7f | 145 | |
| screamer | 0:66f3b5499f7f | 146 | return resources | 
| screamer | 0:66f3b5499f7f | 147 | |
| screamer | 13:ab47a20b66f0 | 148 | def scan_and_copy_resources(self, prj_paths, trg_path, relative=False): | 
| screamer | 0:66f3b5499f7f | 149 | # Copy only the file for the required target and toolchain | 
| screamer | 0:66f3b5499f7f | 150 | lib_builds = [] | 
| screamer | 13:ab47a20b66f0 | 151 | # Create the configuration object | 
| screamer | 13:ab47a20b66f0 | 152 | if isinstance(prj_paths, basestring): | 
| screamer | 13:ab47a20b66f0 | 153 | prj_paths = [prj_paths] | 
| screamer | 13:ab47a20b66f0 | 154 | config = Config(self.target, prj_paths) | 
| screamer | 0:66f3b5499f7f | 155 | for src in ['lib', 'src']: | 
| screamer | 13:ab47a20b66f0 | 156 | resources = self.__scan_and_copy(join(prj_paths[0], src), trg_path) | 
| screamer | 13:ab47a20b66f0 | 157 | for path in prj_paths[1:]: | 
| screamer | 13:ab47a20b66f0 | 158 | resources.add(self.__scan_and_copy(join(path, src), trg_path)) | 
| screamer | 13:ab47a20b66f0 | 159 | |
| screamer | 0:66f3b5499f7f | 160 | lib_builds.extend(resources.lib_builds) | 
| screamer | 0:66f3b5499f7f | 161 | |
| screamer | 0:66f3b5499f7f | 162 | # The repository files | 
| screamer | 13:ab47a20b66f0 | 163 | #for repo_dir in resources.repo_dirs: | 
| screamer | 13:ab47a20b66f0 | 164 | # repo_files = self.__scan_all(repo_dir) | 
| screamer | 13:ab47a20b66f0 | 165 | # for path in prj_paths: | 
| screamer | 13:ab47a20b66f0 | 166 | # self.toolchain.copy_files(repo_files, trg_path, rel_path=join(path, src)) | 
| screamer | 0:66f3b5499f7f | 167 | |
| screamer | 0:66f3b5499f7f | 168 | # The libraries builds | 
| screamer | 0:66f3b5499f7f | 169 | for bld in lib_builds: | 
| screamer | 0:66f3b5499f7f | 170 | build_url = open(bld).read().strip() | 
| screamer | 0:66f3b5499f7f | 171 | lib_data = self.build_url_resolver(build_url) | 
| screamer | 0:66f3b5499f7f | 172 | lib_path = lib_data['path'].rstrip('\\/') | 
| screamer | 0:66f3b5499f7f | 173 | self.__scan_and_copy(lib_path, join(trg_path, lib_data['name'])) | 
| screamer | 0:66f3b5499f7f | 174 | |
| screamer | 0:66f3b5499f7f | 175 | # Create .hg dir in mbed build dir so it's ignored when versioning | 
| screamer | 0:66f3b5499f7f | 176 | hgdir = join(trg_path, lib_data['name'], '.hg') | 
| screamer | 0:66f3b5499f7f | 177 | mkdir(hgdir) | 
| screamer | 0:66f3b5499f7f | 178 | fhandle = file(join(hgdir, 'keep.me'), 'a') | 
| screamer | 0:66f3b5499f7f | 179 | fhandle.close() | 
| screamer | 0:66f3b5499f7f | 180 | |
| screamer | 0:66f3b5499f7f | 181 | if not relative: | 
| screamer | 0:66f3b5499f7f | 182 | # Final scan of the actual exported resources | 
| screamer | 13:ab47a20b66f0 | 183 | resources = self.toolchain.scan_resources(trg_path) | 
| screamer | 13:ab47a20b66f0 | 184 | resources.relative_to(trg_path, self.DOT_IN_RELATIVE_PATH) | 
| screamer | 0:66f3b5499f7f | 185 | else: | 
| screamer | 0:66f3b5499f7f | 186 | # use the prj_dir (source, not destination) | 
| screamer | 13:ab47a20b66f0 | 187 | resources = self.toolchain.scan_resources(prj_paths[0]) | 
| screamer | 13:ab47a20b66f0 | 188 | for path in prj_paths[1:]: | 
| screamer | 13:ab47a20b66f0 | 189 | resources.add(toolchain.scan_resources(path)) | 
| screamer | 13:ab47a20b66f0 | 190 | |
| screamer | 13:ab47a20b66f0 | 191 | # Loads the resources into the config system which might expand/modify resources based on config data | 
| screamer | 13:ab47a20b66f0 | 192 | self.resources = config.load_resources(resources) | 
| screamer | 13:ab47a20b66f0 | 193 | |
| screamer | 13:ab47a20b66f0 | 194 | if hasattr(self, "MBED_CONFIG_HEADER_SUPPORTED") and self.MBED_CONFIG_HEADER_SUPPORTED : | 
| screamer | 13:ab47a20b66f0 | 195 | # Add the configuration file to the target directory | 
| screamer | 13:ab47a20b66f0 | 196 | self.config_header = self.toolchain.MBED_CONFIG_FILE_NAME | 
| screamer | 13:ab47a20b66f0 | 197 | config.get_config_data_header(join(trg_path, self.config_header)) | 
| screamer | 13:ab47a20b66f0 | 198 | self.config_macros = [] | 
| screamer | 13:ab47a20b66f0 | 199 | else: | 
| screamer | 13:ab47a20b66f0 | 200 | # And add the configuration macros to the toolchain | 
| screamer | 13:ab47a20b66f0 | 201 | self.config_macros = config.get_config_data_macros() | 
| screamer | 0:66f3b5499f7f | 202 | |
| screamer | 0:66f3b5499f7f | 203 | def gen_file(self, template_file, data, target_file): | 
| screamer | 0:66f3b5499f7f | 204 | template_path = join(Exporter.TEMPLATE_DIR, template_file) | 
| screamer | 0:66f3b5499f7f | 205 | template = self.jinja_environment.get_template(template_file) | 
| screamer | 0:66f3b5499f7f | 206 | target_text = template.render(data) | 
| screamer | 0:66f3b5499f7f | 207 | |
| screamer | 0:66f3b5499f7f | 208 | target_path = join(self.inputDir, target_file) | 
| screamer | 0:66f3b5499f7f | 209 | logging.debug("Generating: %s" % target_path) | 
| screamer | 0:66f3b5499f7f | 210 | open(target_path, "w").write(target_text) | 
| screamer | 0:66f3b5499f7f | 211 | |
| screamer | 0:66f3b5499f7f | 212 | def get_symbols(self, add_extra_symbols=True): | 
| screamer | 0:66f3b5499f7f | 213 | """ This function returns symbols which must be exported. | 
| screamer | 0:66f3b5499f7f | 214 | Please add / overwrite symbols in each exporter separately | 
| screamer | 0:66f3b5499f7f | 215 | """ | 
| screamer | 13:ab47a20b66f0 | 216 | symbols = self.toolchain.get_symbols() + self.config_macros | 
| screamer | 0:66f3b5499f7f | 217 | # We have extra symbols from e.g. libraries, we want to have them also added to export | 
| screamer | 0:66f3b5499f7f | 218 | if add_extra_symbols: | 
| screamer | 0:66f3b5499f7f | 219 | if self.extra_symbols is not None: | 
| screamer | 0:66f3b5499f7f | 220 | symbols.extend(self.extra_symbols) | 
| screamer | 0:66f3b5499f7f | 221 | return symbols | 
| screamer | 0:66f3b5499f7f | 222 | |
| screamer | 0:66f3b5499f7f | 223 | def zip_working_directory_and_clean_up(tempdirectory=None, destination=None, program_name=None, clean=True): | 
| screamer | 0:66f3b5499f7f | 224 | uid = str(uuid.uuid4()) | 
| screamer | 0:66f3b5499f7f | 225 | zipfilename = '%s.zip'%uid | 
| screamer | 0:66f3b5499f7f | 226 | |
| screamer | 0:66f3b5499f7f | 227 | logging.debug("Zipping up %s to %s" % (tempdirectory, join(destination, zipfilename))) | 
| screamer | 0:66f3b5499f7f | 228 | # make zip | 
| screamer | 0:66f3b5499f7f | 229 | def zipdir(basedir, archivename): | 
| screamer | 0:66f3b5499f7f | 230 | assert isdir(basedir) | 
| screamer | 0:66f3b5499f7f | 231 | fakeroot = program_name + '/' | 
| screamer | 0:66f3b5499f7f | 232 | with closing(ZipFile(archivename, "w", ZIP_DEFLATED)) as z: | 
| screamer | 0:66f3b5499f7f | 233 | for root, _, files in os.walk(basedir): | 
| screamer | 0:66f3b5499f7f | 234 | # NOTE: ignore empty directories | 
| screamer | 0:66f3b5499f7f | 235 | for fn in files: | 
| screamer | 0:66f3b5499f7f | 236 | absfn = join(root, fn) | 
| screamer | 0:66f3b5499f7f | 237 | zfn = fakeroot + '/' + absfn[len(basedir)+len(os.sep):] | 
| screamer | 0:66f3b5499f7f | 238 | z.write(absfn, zfn) | 
| screamer | 0:66f3b5499f7f | 239 | |
| screamer | 0:66f3b5499f7f | 240 | zipdir(tempdirectory, join(destination, zipfilename)) | 
| screamer | 0:66f3b5499f7f | 241 | |
| screamer | 0:66f3b5499f7f | 242 | if clean: | 
| screamer | 0:66f3b5499f7f | 243 | shutil.rmtree(tempdirectory) | 
| screamer | 0:66f3b5499f7f | 244 | |
| screamer | 0:66f3b5499f7f | 245 | return join(destination, zipfilename) | 
