User | Revision | Line number | New contents of line |
switches |
0:0e018d759a2a
|
1
|
"""Just a template for subclassing"""
|
switches |
0:0e018d759a2a
|
2
|
import os
|
switches |
0:0e018d759a2a
|
3
|
import sys
|
switches |
0:0e018d759a2a
|
4
|
import logging
|
switches |
0:0e018d759a2a
|
5
|
from os.path import join, dirname, relpath, basename, realpath
|
switches |
0:0e018d759a2a
|
6
|
from itertools import groupby
|
switches |
0:0e018d759a2a
|
7
|
from jinja2 import FileSystemLoader
|
switches |
0:0e018d759a2a
|
8
|
from jinja2.environment import Environment
|
switches |
0:0e018d759a2a
|
9
|
import copy
|
switches |
0:0e018d759a2a
|
10
|
|
switches |
0:0e018d759a2a
|
11
|
from tools.targets import TARGET_MAP
|
switches |
0:0e018d759a2a
|
12
|
|
switches |
0:0e018d759a2a
|
13
|
|
switches |
0:0e018d759a2a
|
14
|
class TargetNotSupportedException(Exception):
|
switches |
0:0e018d759a2a
|
15
|
"""Indicates that an IDE does not support a particular MCU"""
|
switches |
0:0e018d759a2a
|
16
|
pass
|
switches |
0:0e018d759a2a
|
17
|
|
switches |
0:0e018d759a2a
|
18
|
class ExporterTargetsProperty(object):
|
switches |
0:0e018d759a2a
|
19
|
""" Exporter descriptor for TARGETS
|
switches |
0:0e018d759a2a
|
20
|
TARGETS as class attribute for backward compatibility
|
switches |
0:0e018d759a2a
|
21
|
(allows: if in Exporter.TARGETS)
|
switches |
0:0e018d759a2a
|
22
|
"""
|
switches |
0:0e018d759a2a
|
23
|
def __init__(self, func):
|
switches |
0:0e018d759a2a
|
24
|
self.func = func
|
switches |
0:0e018d759a2a
|
25
|
def __get__(self, inst, cls):
|
switches |
0:0e018d759a2a
|
26
|
return self.func(cls)
|
switches |
0:0e018d759a2a
|
27
|
|
switches |
0:0e018d759a2a
|
28
|
class Exporter(object):
|
switches |
0:0e018d759a2a
|
29
|
"""Exporter base class
|
switches |
0:0e018d759a2a
|
30
|
|
switches |
0:0e018d759a2a
|
31
|
This class is meant to be extended by individual exporters, and provides a
|
switches |
0:0e018d759a2a
|
32
|
few helper methods for implementing an exporter with either jinja2 or
|
switches |
0:0e018d759a2a
|
33
|
progen.
|
switches |
0:0e018d759a2a
|
34
|
"""
|
switches |
0:0e018d759a2a
|
35
|
TEMPLATE_DIR = dirname(__file__)
|
switches |
0:0e018d759a2a
|
36
|
DOT_IN_RELATIVE_PATH = False
|
switches |
0:0e018d759a2a
|
37
|
NAME = None
|
switches |
0:0e018d759a2a
|
38
|
TARGETS = None
|
switches |
0:0e018d759a2a
|
39
|
TOOLCHAIN = None
|
switches |
0:0e018d759a2a
|
40
|
|
switches |
0:0e018d759a2a
|
41
|
def __init__(self, target, export_dir, project_name, toolchain,
|
switches |
0:0e018d759a2a
|
42
|
extra_symbols=None, resources=None):
|
switches |
0:0e018d759a2a
|
43
|
"""Initialize an instance of class exporter
|
switches |
0:0e018d759a2a
|
44
|
Positional arguments:
|
switches |
0:0e018d759a2a
|
45
|
target - the target mcu/board for this project
|
switches |
0:0e018d759a2a
|
46
|
export_dir - the directory of the exported project files
|
switches |
0:0e018d759a2a
|
47
|
project_name - the name of the project
|
switches |
0:0e018d759a2a
|
48
|
toolchain - an instance of class toolchain
|
switches |
0:0e018d759a2a
|
49
|
|
switches |
0:0e018d759a2a
|
50
|
Keyword arguments:
|
switches |
0:0e018d759a2a
|
51
|
extra_symbols - a list of extra macros for the toolchain
|
switches |
0:0e018d759a2a
|
52
|
resources - an instance of class Resources
|
switches |
0:0e018d759a2a
|
53
|
"""
|
switches |
0:0e018d759a2a
|
54
|
self.export_dir = export_dir
|
switches |
0:0e018d759a2a
|
55
|
self.target = target
|
switches |
0:0e018d759a2a
|
56
|
self.project_name = project_name
|
switches |
0:0e018d759a2a
|
57
|
self.toolchain = toolchain
|
switches |
0:0e018d759a2a
|
58
|
jinja_loader = FileSystemLoader(os.path.dirname(os.path.abspath(__file__)))
|
switches |
0:0e018d759a2a
|
59
|
self.jinja_environment = Environment(loader=jinja_loader)
|
switches |
0:0e018d759a2a
|
60
|
self.resources = resources
|
switches |
0:0e018d759a2a
|
61
|
self.generated_files = [join(self.TEMPLATE_DIR,"GettingStarted.html")]
|
switches |
0:0e018d759a2a
|
62
|
self.builder_files_dict = {}
|
switches |
0:0e018d759a2a
|
63
|
self.add_config()
|
switches |
0:0e018d759a2a
|
64
|
|
switches |
0:0e018d759a2a
|
65
|
def get_toolchain(self):
|
switches |
0:0e018d759a2a
|
66
|
"""A helper getter function that we should probably eliminate"""
|
switches |
0:0e018d759a2a
|
67
|
return self.TOOLCHAIN
|
switches |
0:0e018d759a2a
|
68
|
|
switches |
0:0e018d759a2a
|
69
|
def add_config(self):
|
switches |
0:0e018d759a2a
|
70
|
"""Add the containgin directory of mbed_config.h to include dirs"""
|
switches |
0:0e018d759a2a
|
71
|
config = self.toolchain.get_config_header()
|
switches |
0:0e018d759a2a
|
72
|
if config:
|
switches |
0:0e018d759a2a
|
73
|
self.resources.inc_dirs.append(
|
switches |
0:0e018d759a2a
|
74
|
dirname(relpath(config,
|
switches |
0:0e018d759a2a
|
75
|
self.resources.file_basepath[config])))
|
switches |
0:0e018d759a2a
|
76
|
|
switches |
0:0e018d759a2a
|
77
|
@property
|
switches |
0:0e018d759a2a
|
78
|
def flags(self):
|
switches |
0:0e018d759a2a
|
79
|
"""Returns a dictionary of toolchain flags.
|
switches |
0:0e018d759a2a
|
80
|
Keys of the dictionary are:
|
switches |
0:0e018d759a2a
|
81
|
cxx_flags - c++ flags
|
switches |
0:0e018d759a2a
|
82
|
c_flags - c flags
|
switches |
0:0e018d759a2a
|
83
|
ld_flags - linker flags
|
switches |
0:0e018d759a2a
|
84
|
asm_flags - assembler flags
|
switches |
0:0e018d759a2a
|
85
|
common_flags - common options
|
switches |
0:0e018d759a2a
|
86
|
"""
|
switches |
0:0e018d759a2a
|
87
|
config_header = self.toolchain.get_config_header()
|
switches |
0:0e018d759a2a
|
88
|
flags = {key + "_flags": copy.deepcopy(value) for key, value
|
switches |
0:0e018d759a2a
|
89
|
in self.toolchain.flags.iteritems()}
|
switches |
0:0e018d759a2a
|
90
|
asm_defines = ["-D" + symbol for symbol in self.toolchain.get_symbols(True)]
|
switches |
0:0e018d759a2a
|
91
|
c_defines = ["-D" + symbol for symbol in self.toolchain.get_symbols()]
|
switches |
0:0e018d759a2a
|
92
|
flags['asm_flags'] += asm_defines
|
switches |
0:0e018d759a2a
|
93
|
flags['c_flags'] += c_defines
|
switches |
0:0e018d759a2a
|
94
|
flags['cxx_flags'] += c_defines
|
switches |
0:0e018d759a2a
|
95
|
if config_header:
|
switches |
0:0e018d759a2a
|
96
|
config_header = relpath(config_header,
|
switches |
0:0e018d759a2a
|
97
|
self.resources.file_basepath[config_header])
|
switches |
0:0e018d759a2a
|
98
|
flags['c_flags'] += self.toolchain.get_config_option(config_header)
|
switches |
0:0e018d759a2a
|
99
|
flags['cxx_flags'] += self.toolchain.get_config_option(
|
switches |
0:0e018d759a2a
|
100
|
config_header)
|
switches |
0:0e018d759a2a
|
101
|
return flags
|
switches |
0:0e018d759a2a
|
102
|
|
switches |
0:0e018d759a2a
|
103
|
def get_source_paths(self):
|
switches |
0:0e018d759a2a
|
104
|
"""Returns a list of the directories where source files are contained"""
|
switches |
0:0e018d759a2a
|
105
|
source_keys = ['s_sources', 'c_sources', 'cpp_sources', 'hex_files',
|
switches |
0:0e018d759a2a
|
106
|
'objects', 'libraries']
|
switches |
0:0e018d759a2a
|
107
|
source_files = []
|
switches |
0:0e018d759a2a
|
108
|
for key in source_keys:
|
switches |
0:0e018d759a2a
|
109
|
source_files.extend(getattr(self.resources, key))
|
switches |
0:0e018d759a2a
|
110
|
return list(set([os.path.dirname(src) for src in source_files]))
|
switches |
0:0e018d759a2a
|
111
|
|
switches |
0:0e018d759a2a
|
112
|
def gen_file(self, template_file, data, target_file):
|
switches |
0:0e018d759a2a
|
113
|
"""Generates a project file from a template using jinja"""
|
switches |
0:0e018d759a2a
|
114
|
jinja_loader = FileSystemLoader(
|
switches |
0:0e018d759a2a
|
115
|
os.path.dirname(os.path.abspath(__file__)))
|
switches |
0:0e018d759a2a
|
116
|
jinja_environment = Environment(loader=jinja_loader)
|
switches |
0:0e018d759a2a
|
117
|
|
switches |
0:0e018d759a2a
|
118
|
template = jinja_environment.get_template(template_file)
|
switches |
0:0e018d759a2a
|
119
|
target_text = template.render(data)
|
switches |
0:0e018d759a2a
|
120
|
|
switches |
0:0e018d759a2a
|
121
|
target_path = join(self.export_dir, target_file)
|
switches |
0:0e018d759a2a
|
122
|
logging.debug("Generating: %s", target_path)
|
switches |
0:0e018d759a2a
|
123
|
open(target_path, "w").write(target_text)
|
switches |
0:0e018d759a2a
|
124
|
self.generated_files += [target_path]
|
switches |
0:0e018d759a2a
|
125
|
|
switches |
0:0e018d759a2a
|
126
|
def make_key(self, src):
|
switches |
0:0e018d759a2a
|
127
|
"""From a source file, extract group name
|
switches |
0:0e018d759a2a
|
128
|
Positional Arguments:
|
switches |
0:0e018d759a2a
|
129
|
src - the src's location
|
switches |
0:0e018d759a2a
|
130
|
"""
|
switches |
0:0e018d759a2a
|
131
|
key = basename(dirname(src))
|
switches |
0:0e018d759a2a
|
132
|
if key == ".":
|
switches |
0:0e018d759a2a
|
133
|
key = basename(realpath(self.export_dir))
|
switches |
0:0e018d759a2a
|
134
|
return key
|
switches |
0:0e018d759a2a
|
135
|
|
switches |
0:0e018d759a2a
|
136
|
def group_project_files(self, sources):
|
switches |
0:0e018d759a2a
|
137
|
"""Group the source files by their encompassing directory
|
switches |
0:0e018d759a2a
|
138
|
Positional Arguments:
|
switches |
0:0e018d759a2a
|
139
|
sources - array of source locations
|
switches |
0:0e018d759a2a
|
140
|
|
switches |
0:0e018d759a2a
|
141
|
Returns a dictionary of {group name: list of source locations}
|
switches |
0:0e018d759a2a
|
142
|
"""
|
switches |
0:0e018d759a2a
|
143
|
data = sorted(sources, key=self.make_key)
|
switches |
0:0e018d759a2a
|
144
|
return {k: list(g) for k,g in groupby(data, self.make_key)}
|
switches |
0:0e018d759a2a
|
145
|
|
switches |
0:0e018d759a2a
|
146
|
@staticmethod
|
switches |
0:0e018d759a2a
|
147
|
def build(project_name, log_name='build_log.txt', cleanup=True):
|
switches |
0:0e018d759a2a
|
148
|
"""Invoke exporters build command within a subprocess.
|
switches |
0:0e018d759a2a
|
149
|
This method is assumed to be executed at the same level as exporter
|
switches |
0:0e018d759a2a
|
150
|
project files and project source code.
|
switches |
0:0e018d759a2a
|
151
|
See uvision/__init__.py, iar/__init__.py, and makefile/__init__.py for
|
switches |
0:0e018d759a2a
|
152
|
example implemenation.
|
switches |
0:0e018d759a2a
|
153
|
|
switches |
0:0e018d759a2a
|
154
|
Positional Arguments:
|
switches |
0:0e018d759a2a
|
155
|
project_name - the name of the project to build; often required by
|
switches |
0:0e018d759a2a
|
156
|
exporter's build command.
|
switches |
0:0e018d759a2a
|
157
|
|
switches |
0:0e018d759a2a
|
158
|
Keyword Args:
|
switches |
0:0e018d759a2a
|
159
|
log_name - name of the build log to create. Written and printed out,
|
switches |
0:0e018d759a2a
|
160
|
deleted if cleanup = True
|
switches |
0:0e018d759a2a
|
161
|
cleanup - a boolean dictating whether exported project files and
|
switches |
0:0e018d759a2a
|
162
|
build log are removed after build
|
switches |
0:0e018d759a2a
|
163
|
|
switches |
0:0e018d759a2a
|
164
|
Returns -1 on failure and 0 on success
|
switches |
0:0e018d759a2a
|
165
|
"""
|
switches |
0:0e018d759a2a
|
166
|
raise NotImplemented("Implement in derived Exporter class.")
|