User | Revision | Line number | New contents of line |
nexpaq |
0:6c56fb4bc5f0
|
1
|
"""Just a template for subclassing"""
|
nexpaq |
0:6c56fb4bc5f0
|
2
|
import os
|
nexpaq |
0:6c56fb4bc5f0
|
3
|
import sys
|
nexpaq |
0:6c56fb4bc5f0
|
4
|
import logging
|
nexpaq |
0:6c56fb4bc5f0
|
5
|
from os.path import join, dirname, relpath
|
nexpaq |
0:6c56fb4bc5f0
|
6
|
from itertools import groupby
|
nexpaq |
0:6c56fb4bc5f0
|
7
|
from jinja2 import FileSystemLoader
|
nexpaq |
0:6c56fb4bc5f0
|
8
|
from jinja2.environment import Environment
|
nexpaq |
0:6c56fb4bc5f0
|
9
|
|
nexpaq |
0:6c56fb4bc5f0
|
10
|
from tools.targets import TARGET_MAP
|
nexpaq |
0:6c56fb4bc5f0
|
11
|
from project_generator.tools import tool
|
nexpaq |
0:6c56fb4bc5f0
|
12
|
from project_generator.tools_supported import ToolsSupported
|
nexpaq |
0:6c56fb4bc5f0
|
13
|
from project_generator.settings import ProjectSettings
|
nexpaq |
0:6c56fb4bc5f0
|
14
|
from project_generator_definitions.definitions import ProGenDef
|
nexpaq |
0:6c56fb4bc5f0
|
15
|
|
nexpaq |
0:6c56fb4bc5f0
|
16
|
|
nexpaq |
0:6c56fb4bc5f0
|
17
|
class OldLibrariesException(Exception):
|
nexpaq |
0:6c56fb4bc5f0
|
18
|
"""Exception that indicates an export can not complete due to an out of date
|
nexpaq |
0:6c56fb4bc5f0
|
19
|
library version.
|
nexpaq |
0:6c56fb4bc5f0
|
20
|
"""
|
nexpaq |
0:6c56fb4bc5f0
|
21
|
pass
|
nexpaq |
0:6c56fb4bc5f0
|
22
|
|
nexpaq |
0:6c56fb4bc5f0
|
23
|
class FailedBuildException(Exception):
|
nexpaq |
0:6c56fb4bc5f0
|
24
|
"""Exception that indicates that a build failed"""
|
nexpaq |
0:6c56fb4bc5f0
|
25
|
pass
|
nexpaq |
0:6c56fb4bc5f0
|
26
|
|
nexpaq |
0:6c56fb4bc5f0
|
27
|
class TargetNotSupportedException(Exception):
|
nexpaq |
0:6c56fb4bc5f0
|
28
|
"""Indicates that an IDE does not support a particular MCU"""
|
nexpaq |
0:6c56fb4bc5f0
|
29
|
pass
|
nexpaq |
0:6c56fb4bc5f0
|
30
|
|
nexpaq |
0:6c56fb4bc5f0
|
31
|
class ExporterTargetsProperty(object):
|
nexpaq |
0:6c56fb4bc5f0
|
32
|
""" Exporter descriptor for TARGETS
|
nexpaq |
0:6c56fb4bc5f0
|
33
|
TARGETS as class attribute for backward compatibility
|
nexpaq |
0:6c56fb4bc5f0
|
34
|
(allows: if in Exporter.TARGETS)
|
nexpaq |
0:6c56fb4bc5f0
|
35
|
"""
|
nexpaq |
0:6c56fb4bc5f0
|
36
|
def __init__(self, func):
|
nexpaq |
0:6c56fb4bc5f0
|
37
|
self.func = func
|
nexpaq |
0:6c56fb4bc5f0
|
38
|
def __get__(self, inst, cls):
|
nexpaq |
0:6c56fb4bc5f0
|
39
|
return self.func(cls)
|
nexpaq |
0:6c56fb4bc5f0
|
40
|
|
nexpaq |
0:6c56fb4bc5f0
|
41
|
class Exporter(object):
|
nexpaq |
0:6c56fb4bc5f0
|
42
|
"""Exporter base class
|
nexpaq |
0:6c56fb4bc5f0
|
43
|
|
nexpaq |
0:6c56fb4bc5f0
|
44
|
This class is meant to be extended by individual exporters, and provides a
|
nexpaq |
0:6c56fb4bc5f0
|
45
|
few helper methods for implementing an exporter with either jinja2 or
|
nexpaq |
0:6c56fb4bc5f0
|
46
|
progen.
|
nexpaq |
0:6c56fb4bc5f0
|
47
|
"""
|
nexpaq |
0:6c56fb4bc5f0
|
48
|
TEMPLATE_DIR = dirname(__file__)
|
nexpaq |
0:6c56fb4bc5f0
|
49
|
DOT_IN_RELATIVE_PATH = False
|
nexpaq |
0:6c56fb4bc5f0
|
50
|
NAME = None
|
nexpaq |
0:6c56fb4bc5f0
|
51
|
TARGETS = None
|
nexpaq |
0:6c56fb4bc5f0
|
52
|
TOOLCHAIN = None
|
nexpaq |
0:6c56fb4bc5f0
|
53
|
|
nexpaq |
0:6c56fb4bc5f0
|
54
|
def __init__(self, target, export_dir, project_name, toolchain,
|
nexpaq |
0:6c56fb4bc5f0
|
55
|
extra_symbols=None, resources=None):
|
nexpaq |
0:6c56fb4bc5f0
|
56
|
"""Initialize an instance of class exporter
|
nexpaq |
0:6c56fb4bc5f0
|
57
|
Positional arguments:
|
nexpaq |
0:6c56fb4bc5f0
|
58
|
target - the target mcu/board for this project
|
nexpaq |
0:6c56fb4bc5f0
|
59
|
export_dir - the directory of the exported project files
|
nexpaq |
0:6c56fb4bc5f0
|
60
|
project_name - the name of the project
|
nexpaq |
0:6c56fb4bc5f0
|
61
|
toolchain - an instance of class toolchain
|
nexpaq |
0:6c56fb4bc5f0
|
62
|
|
nexpaq |
0:6c56fb4bc5f0
|
63
|
Keyword arguments:
|
nexpaq |
0:6c56fb4bc5f0
|
64
|
extra_symbols - a list of extra macros for the toolchain
|
nexpaq |
0:6c56fb4bc5f0
|
65
|
resources - an instance of class Resources
|
nexpaq |
0:6c56fb4bc5f0
|
66
|
"""
|
nexpaq |
0:6c56fb4bc5f0
|
67
|
self.export_dir = export_dir
|
nexpaq |
0:6c56fb4bc5f0
|
68
|
self.target = target
|
nexpaq |
0:6c56fb4bc5f0
|
69
|
self.project_name = project_name
|
nexpaq |
0:6c56fb4bc5f0
|
70
|
self.toolchain = toolchain
|
nexpaq |
0:6c56fb4bc5f0
|
71
|
jinja_loader = FileSystemLoader(os.path.dirname(os.path.abspath(__file__)))
|
nexpaq |
0:6c56fb4bc5f0
|
72
|
self.jinja_environment = Environment(loader=jinja_loader)
|
nexpaq |
0:6c56fb4bc5f0
|
73
|
self.resources = resources
|
nexpaq |
0:6c56fb4bc5f0
|
74
|
self.generated_files = []
|
nexpaq |
0:6c56fb4bc5f0
|
75
|
self.builder_files_dict = {}
|
nexpaq |
0:6c56fb4bc5f0
|
76
|
|
nexpaq |
0:6c56fb4bc5f0
|
77
|
def get_toolchain(self):
|
nexpaq |
0:6c56fb4bc5f0
|
78
|
"""A helper getter function that we should probably eliminate"""
|
nexpaq |
0:6c56fb4bc5f0
|
79
|
return self.TOOLCHAIN
|
nexpaq |
0:6c56fb4bc5f0
|
80
|
|
nexpaq |
0:6c56fb4bc5f0
|
81
|
@property
|
nexpaq |
0:6c56fb4bc5f0
|
82
|
def flags(self):
|
nexpaq |
0:6c56fb4bc5f0
|
83
|
"""Returns a dictionary of toolchain flags.
|
nexpaq |
0:6c56fb4bc5f0
|
84
|
Keys of the dictionary are:
|
nexpaq |
0:6c56fb4bc5f0
|
85
|
cxx_flags - c++ flags
|
nexpaq |
0:6c56fb4bc5f0
|
86
|
c_flags - c flags
|
nexpaq |
0:6c56fb4bc5f0
|
87
|
ld_flags - linker flags
|
nexpaq |
0:6c56fb4bc5f0
|
88
|
asm_flags - assembler flags
|
nexpaq |
0:6c56fb4bc5f0
|
89
|
common_flags - common options
|
nexpaq |
0:6c56fb4bc5f0
|
90
|
"""
|
nexpaq |
0:6c56fb4bc5f0
|
91
|
config_header = self.toolchain.get_config_header()
|
nexpaq |
0:6c56fb4bc5f0
|
92
|
flags = {key + "_flags": value for key, value
|
nexpaq |
0:6c56fb4bc5f0
|
93
|
in self.toolchain.flags.iteritems()}
|
nexpaq |
0:6c56fb4bc5f0
|
94
|
asm_defines = ["-D" + symbol for symbol in self.toolchain.get_symbols(True)]
|
nexpaq |
0:6c56fb4bc5f0
|
95
|
c_defines = ["-D" + symbol for symbol in self.toolchain.get_symbols()]
|
nexpaq |
0:6c56fb4bc5f0
|
96
|
flags['asm_flags'] += asm_defines
|
nexpaq |
0:6c56fb4bc5f0
|
97
|
flags['c_flags'] += c_defines
|
nexpaq |
0:6c56fb4bc5f0
|
98
|
flags['cxx_flags'] += c_defines
|
nexpaq |
0:6c56fb4bc5f0
|
99
|
if config_header:
|
nexpaq |
0:6c56fb4bc5f0
|
100
|
config_header = relpath(config_header,
|
nexpaq |
0:6c56fb4bc5f0
|
101
|
self.resources.file_basepath[config_header])
|
nexpaq |
0:6c56fb4bc5f0
|
102
|
flags['c_flags'] += self.toolchain.get_config_option(config_header)
|
nexpaq |
0:6c56fb4bc5f0
|
103
|
flags['cxx_flags'] += self.toolchain.get_config_option(
|
nexpaq |
0:6c56fb4bc5f0
|
104
|
config_header)
|
nexpaq |
0:6c56fb4bc5f0
|
105
|
return flags
|
nexpaq |
0:6c56fb4bc5f0
|
106
|
|
nexpaq |
0:6c56fb4bc5f0
|
107
|
def get_source_paths(self):
|
nexpaq |
0:6c56fb4bc5f0
|
108
|
"""Returns a list of the directories where source files are contained"""
|
nexpaq |
0:6c56fb4bc5f0
|
109
|
source_keys = ['s_sources', 'c_sources', 'cpp_sources', 'hex_files',
|
nexpaq |
0:6c56fb4bc5f0
|
110
|
'objects', 'libraries']
|
nexpaq |
0:6c56fb4bc5f0
|
111
|
source_files = []
|
nexpaq |
0:6c56fb4bc5f0
|
112
|
for key in source_keys:
|
nexpaq |
0:6c56fb4bc5f0
|
113
|
source_files.extend(getattr(self.resources, key))
|
nexpaq |
0:6c56fb4bc5f0
|
114
|
return list(set([os.path.dirname(src) for src in source_files]))
|
nexpaq |
0:6c56fb4bc5f0
|
115
|
|
nexpaq |
0:6c56fb4bc5f0
|
116
|
def progen_get_project_data(self):
|
nexpaq |
0:6c56fb4bc5f0
|
117
|
""" Get ProGen project data """
|
nexpaq |
0:6c56fb4bc5f0
|
118
|
# provide default data, some tools don't require any additional
|
nexpaq |
0:6c56fb4bc5f0
|
119
|
# tool specific settings
|
nexpaq |
0:6c56fb4bc5f0
|
120
|
|
nexpaq |
0:6c56fb4bc5f0
|
121
|
def make_key(src):
|
nexpaq |
0:6c56fb4bc5f0
|
122
|
"""turn a source file into it's group name"""
|
nexpaq |
0:6c56fb4bc5f0
|
123
|
key = os.path.basename(os.path.dirname(src))
|
nexpaq |
0:6c56fb4bc5f0
|
124
|
if not key:
|
nexpaq |
0:6c56fb4bc5f0
|
125
|
key = os.path.basename(os.path.normpath(self.export_dir))
|
nexpaq |
0:6c56fb4bc5f0
|
126
|
return key
|
nexpaq |
0:6c56fb4bc5f0
|
127
|
|
nexpaq |
0:6c56fb4bc5f0
|
128
|
def grouped(sources):
|
nexpaq |
0:6c56fb4bc5f0
|
129
|
"""Group the source files by their encompassing directory"""
|
nexpaq |
0:6c56fb4bc5f0
|
130
|
data = sorted(sources, key=make_key)
|
nexpaq |
0:6c56fb4bc5f0
|
131
|
return {k: list(g) for k, g in groupby(data, make_key)}
|
nexpaq |
0:6c56fb4bc5f0
|
132
|
|
nexpaq |
0:6c56fb4bc5f0
|
133
|
if self.toolchain.get_config_header():
|
nexpaq |
0:6c56fb4bc5f0
|
134
|
config_header = self.toolchain.get_config_header()
|
nexpaq |
0:6c56fb4bc5f0
|
135
|
config_header = relpath(config_header,
|
nexpaq |
0:6c56fb4bc5f0
|
136
|
self.resources.file_basepath[config_header])
|
nexpaq |
0:6c56fb4bc5f0
|
137
|
else:
|
nexpaq |
0:6c56fb4bc5f0
|
138
|
config_header = None
|
nexpaq |
0:6c56fb4bc5f0
|
139
|
|
nexpaq |
0:6c56fb4bc5f0
|
140
|
# we want to add this to our include dirs
|
nexpaq |
0:6c56fb4bc5f0
|
141
|
config_dir = os.path.dirname(config_header) if config_header else []
|
nexpaq |
0:6c56fb4bc5f0
|
142
|
|
nexpaq |
0:6c56fb4bc5f0
|
143
|
project_data = tool.get_tool_template()
|
nexpaq |
0:6c56fb4bc5f0
|
144
|
|
nexpaq |
0:6c56fb4bc5f0
|
145
|
project_data['target'] = TARGET_MAP[self.target].progen['target']
|
nexpaq |
0:6c56fb4bc5f0
|
146
|
project_data['source_paths'] = self.get_source_paths()
|
nexpaq |
0:6c56fb4bc5f0
|
147
|
project_data['include_paths'] = self.resources.inc_dirs + [config_dir]
|
nexpaq |
0:6c56fb4bc5f0
|
148
|
project_data['include_files'] = grouped(self.resources.headers)
|
nexpaq |
0:6c56fb4bc5f0
|
149
|
project_data['source_files_s'] = grouped(self.resources.s_sources)
|
nexpaq |
0:6c56fb4bc5f0
|
150
|
project_data['source_files_c'] = grouped(self.resources.c_sources)
|
nexpaq |
0:6c56fb4bc5f0
|
151
|
project_data['source_files_cpp'] = grouped(self.resources.cpp_sources)
|
nexpaq |
0:6c56fb4bc5f0
|
152
|
project_data['source_files_obj'] = grouped(self.resources.objects)
|
nexpaq |
0:6c56fb4bc5f0
|
153
|
project_data['source_files_lib'] = grouped(self.resources.libraries)
|
nexpaq |
0:6c56fb4bc5f0
|
154
|
project_data['output_dir']['path'] = self.export_dir
|
nexpaq |
0:6c56fb4bc5f0
|
155
|
project_data['linker_file'] = self.resources.linker_script
|
nexpaq |
0:6c56fb4bc5f0
|
156
|
project_data['macros'] = []
|
nexpaq |
0:6c56fb4bc5f0
|
157
|
project_data['build_dir'] = 'build'
|
nexpaq |
0:6c56fb4bc5f0
|
158
|
project_data['template'] = None
|
nexpaq |
0:6c56fb4bc5f0
|
159
|
project_data['name'] = self.project_name
|
nexpaq |
0:6c56fb4bc5f0
|
160
|
project_data['output_type'] = 'exe'
|
nexpaq |
0:6c56fb4bc5f0
|
161
|
project_data['debugger'] = None
|
nexpaq |
0:6c56fb4bc5f0
|
162
|
return project_data
|
nexpaq |
0:6c56fb4bc5f0
|
163
|
|
nexpaq |
0:6c56fb4bc5f0
|
164
|
def progen_gen_file(self, project_data):
|
nexpaq |
0:6c56fb4bc5f0
|
165
|
""" Generate project using ProGen Project API
|
nexpaq |
0:6c56fb4bc5f0
|
166
|
Positional arguments:
|
nexpaq |
0:6c56fb4bc5f0
|
167
|
tool_name - the tool for which to generate project files
|
nexpaq |
0:6c56fb4bc5f0
|
168
|
project_data - a dict whose base key, values are specified in
|
nexpaq |
0:6c56fb4bc5f0
|
169
|
progen_get_project_data, the items will have been
|
nexpaq |
0:6c56fb4bc5f0
|
170
|
modified by Exporter subclasses
|
nexpaq |
0:6c56fb4bc5f0
|
171
|
|
nexpaq |
0:6c56fb4bc5f0
|
172
|
Keyword arguments:
|
nexpaq |
0:6c56fb4bc5f0
|
173
|
progen_build - A boolean that determines if the tool will build the
|
nexpaq |
0:6c56fb4bc5f0
|
174
|
project
|
nexpaq |
0:6c56fb4bc5f0
|
175
|
"""
|
nexpaq |
0:6c56fb4bc5f0
|
176
|
if not self.check_supported(self.NAME):
|
nexpaq |
0:6c56fb4bc5f0
|
177
|
raise TargetNotSupportedException("Target not supported")
|
nexpaq |
0:6c56fb4bc5f0
|
178
|
settings = ProjectSettings()
|
nexpaq |
0:6c56fb4bc5f0
|
179
|
exporter = ToolsSupported().get_tool(self.NAME)
|
nexpaq |
0:6c56fb4bc5f0
|
180
|
self.builder_files_dict = {self.NAME:exporter(project_data, settings).export_project()}
|
nexpaq |
0:6c56fb4bc5f0
|
181
|
for middle in self.builder_files_dict.values():
|
nexpaq |
0:6c56fb4bc5f0
|
182
|
for field, thing in middle.iteritems():
|
nexpaq |
0:6c56fb4bc5f0
|
183
|
if field == "files":
|
nexpaq |
0:6c56fb4bc5f0
|
184
|
for filename in thing.values():
|
nexpaq |
0:6c56fb4bc5f0
|
185
|
self.generated_files.append(filename)
|
nexpaq |
0:6c56fb4bc5f0
|
186
|
|
nexpaq |
0:6c56fb4bc5f0
|
187
|
def progen_build(self):
|
nexpaq |
0:6c56fb4bc5f0
|
188
|
"""Build a project that was already generated by progen"""
|
nexpaq |
0:6c56fb4bc5f0
|
189
|
print("Project {} exported, building for {}...".format(
|
nexpaq |
0:6c56fb4bc5f0
|
190
|
self.project_name, self.NAME))
|
nexpaq |
0:6c56fb4bc5f0
|
191
|
sys.stdout.flush()
|
nexpaq |
0:6c56fb4bc5f0
|
192
|
builder = ToolsSupported().get_tool(self.NAME)
|
nexpaq |
0:6c56fb4bc5f0
|
193
|
result = builder(self.builder_files_dict[self.NAME], ProjectSettings()).build_project()
|
nexpaq |
0:6c56fb4bc5f0
|
194
|
if result == -1:
|
nexpaq |
0:6c56fb4bc5f0
|
195
|
raise FailedBuildException("Build Failed")
|
nexpaq |
0:6c56fb4bc5f0
|
196
|
|
nexpaq |
0:6c56fb4bc5f0
|
197
|
def check_supported(self, ide):
|
nexpaq |
0:6c56fb4bc5f0
|
198
|
"""Indicated if this combination of IDE and MCU is supported"""
|
nexpaq |
0:6c56fb4bc5f0
|
199
|
if self.target not in self.TARGETS or \
|
nexpaq |
0:6c56fb4bc5f0
|
200
|
self.TOOLCHAIN not in TARGET_MAP[self.target].supported_toolchains:
|
nexpaq |
0:6c56fb4bc5f0
|
201
|
return False
|
nexpaq |
0:6c56fb4bc5f0
|
202
|
if not ProGenDef(ide).is_supported(
|
nexpaq |
0:6c56fb4bc5f0
|
203
|
TARGET_MAP[self.target].progen['target']):
|
nexpaq |
0:6c56fb4bc5f0
|
204
|
return False
|
nexpaq |
0:6c56fb4bc5f0
|
205
|
return True
|
nexpaq |
0:6c56fb4bc5f0
|
206
|
|
nexpaq |
0:6c56fb4bc5f0
|
207
|
def gen_file(self, template_file, data, target_file):
|
nexpaq |
0:6c56fb4bc5f0
|
208
|
"""Generates a project file from a template using jinja"""
|
nexpaq |
0:6c56fb4bc5f0
|
209
|
jinja_loader = FileSystemLoader(
|
nexpaq |
0:6c56fb4bc5f0
|
210
|
os.path.dirname(os.path.abspath(__file__)))
|
nexpaq |
0:6c56fb4bc5f0
|
211
|
jinja_environment = Environment(loader=jinja_loader)
|
nexpaq |
0:6c56fb4bc5f0
|
212
|
|
nexpaq |
0:6c56fb4bc5f0
|
213
|
template = jinja_environment.get_template(template_file)
|
nexpaq |
0:6c56fb4bc5f0
|
214
|
target_text = template.render(data)
|
nexpaq |
0:6c56fb4bc5f0
|
215
|
|
nexpaq |
0:6c56fb4bc5f0
|
216
|
target_path = join(self.export_dir, target_file)
|
nexpaq |
0:6c56fb4bc5f0
|
217
|
logging.debug("Generating: %s", target_path)
|
nexpaq |
0:6c56fb4bc5f0
|
218
|
open(target_path, "w").write(target_text)
|
nexpaq |
0:6c56fb4bc5f0
|
219
|
self.generated_files += [target_path]
|