Skip to content

Commit c6aa902

Browse files
committed
Progen build tests and project_api
1 parent 8d1d836 commit c6aa902

File tree

8 files changed

+279
-77
lines changed

8 files changed

+279
-77
lines changed

tools/build_test.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import sys
2+
from os.path import join, abspath, dirname, exists, basename
3+
ROOT = abspath(join(dirname(__file__), ".."))
4+
sys.path.insert(0, ROOT)
5+
6+
import argparse
7+
8+
from export import EXPORTERS
9+
from targets import TARGET_NAMES, TARGET_MAP
10+
from project_api import setup_project, perform_export, print_results, get_test_from_name
11+
from project_generator_definitions.definitions import ProGenDef
12+
from utils import args_error
13+
14+
class BuildTest():
15+
def __init__(self, desired_ides, tests, targets):
16+
self.target_ides = {}
17+
for target in targets:
18+
self.target_ides[target] =[]
19+
for ide in desired_ides:
20+
if target in EXPORTERS[ide].TARGETS:
21+
self.target_ides[target].append(ide)
22+
if len(self.target_ides[target]) == 0:
23+
del self.target_ides[target]
24+
25+
successes, failures = self._generate_and_build(tests)
26+
print_results(successes, failures)
27+
28+
@staticmethod
29+
def get_pgen_targets(ides):
30+
targs = []
31+
for ide in ides:
32+
for target in TARGET_NAMES:
33+
if target not in targs and hasattr(TARGET_MAP[target],'progen') \
34+
and ProGenDef(ide).is_supported(TARGET_MAP[target].progen['target']):
35+
targs.append(target)
36+
return targs
37+
38+
def _generate_and_build(self, tests):
39+
successes = []
40+
failures = []
41+
for mcu, ides in self.target_ides.items():
42+
for test in tests:
43+
test = get_test_from_name(test)
44+
for ide in ides:
45+
project_dir, project_name, project_temp = setup_project(mcu, ide, test)
46+
tmp_path, report = perform_export(project_dir, project_name, ide, mcu, project_temp,
47+
progen_build = True)
48+
if report['success']:
49+
successes.append("build for %s::%s\t%s" % (mcu, ide, project_name))
50+
else:
51+
failures.append("%s::%s\t%s for %s" % (mcu, ide, report['errormsg'], project_name))
52+
return successes, failures
53+
54+
55+
if __name__ == '__main__':
56+
accepted_ides = ["iar", "uvision", "uvision5"]
57+
accepted_targets = sorted(BuildTest.get_pgen_targets(accepted_ides))
58+
default_tests = ["MBED_BLINKY"]
59+
parser = argparse.ArgumentParser(description = "Test progen builders. Leave any flag off to run with all possible options.")
60+
parser.add_argument("-i", "--IDEs",
61+
nargs = '+',
62+
dest="ides",
63+
help="tools you wish to perfrom build tests. (%s)" % ', '.join(accepted_ides),
64+
default = accepted_ides)
65+
66+
parser.add_argument("-t", "--tests",
67+
nargs='+',
68+
dest="tests",
69+
help="names of desired test programs",
70+
default = default_tests)
71+
72+
parser.add_argument("-m", "--mcus",
73+
nargs='+',
74+
dest ="targets",
75+
help="generate project for the given MCUs (%s)" % '\n '.join(accepted_targets),
76+
default = TARGET_NAMES)
77+
78+
options = parser.parse_args()
79+
80+
tests = options.tests
81+
ides = options.ides
82+
targets = options.targets
83+
84+
if any(get_test_from_name(test) is None for test in tests):
85+
args_error(parser, "[ERROR] test name not recognized")
86+
87+
if any(target not in accepted_targets for target in targets):
88+
args_error(parser, "[ERROR] mcu must be one of the following:\n %s" % '\n '.join(accepted_targets))
89+
90+
if any(ide not in accepted_ides for ide in ides):
91+
args_error(parser, "[ERROR] ide must be in %s" % ', '.join(accepted_ides))
92+
93+
b = BuildTest(ides, tests, targets)
94+
95+

tools/export/__init__.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
from tools.utils import mkdir
2323
from tools.export import uvision4, uvision5, codered, gccarm, ds5_5, iar, emblocks, coide, kds, zip, simplicityv3, atmelstudio, sw4stm32, e2studio
24-
from tools.export.exporters import zip_working_directory_and_clean_up, OldLibrariesException
24+
from tools.export.exporters import zip_working_directory_and_clean_up, OldLibrariesException, FailedBuildException
2525
from tools.targets import TARGET_NAMES, EXPORT_MAP, TARGET_MAP
2626

2727
from project_generator_definitions.definitions import ProGenDef
@@ -58,7 +58,8 @@ def online_build_url_resolver(url):
5858

5959

6060
def export(project_path, project_name, ide, target, destination='/tmp/',
61-
tempdir=None, clean=True, extra_symbols=None, make_zip=True, sources_relative=False, build_url_resolver=online_build_url_resolver):
61+
tempdir=None, pgen_build = False, clean=True, extra_symbols=None, make_zip=True, sources_relative=False,
62+
build_url_resolver=online_build_url_resolver, **kwargs):
6263
# Convention: we are using capitals for toolchain and target names
6364
if target is not None:
6465
target = target.upper()
@@ -67,6 +68,10 @@ def export(project_path, project_name, ide, target, destination='/tmp/',
6768
tempdir = tempfile.mkdtemp()
6869

6970
use_progen = False
71+
build = False
72+
if 'progen_build' in kwargs and kwargs.get('progen_build') == True:
73+
build = True
74+
7075
supported = True
7176
report = {'success': False, 'errormsg':''}
7277

@@ -103,10 +108,19 @@ def export(project_path, project_name, ide, target, destination='/tmp/',
103108
try:
104109
exporter = Exporter(target, tempdir, project_name, build_url_resolver, extra_symbols=extra_symbols, sources_relative=sources_relative)
105110
exporter.scan_and_copy_resources(project_path, tempdir, sources_relative)
106-
exporter.generate()
107-
report['success'] = True
111+
if build:
112+
#try to build with pgen ide builders
113+
try:
114+
exporter.generate(progen_build=True)
115+
report['success'] = True
116+
except FailedBuildException, f:
117+
report['errormsg'] = "Build Failed"
118+
else:
119+
exporter.generate()
120+
report['success'] = True
108121
except OldLibrariesException, e:
109122
report['errormsg'] = ERROR_MESSAGE_NOT_EXPORT_LIBS
123+
110124
else:
111125
report['errormsg'] = ERROR_MESSAGE_UNSUPPORTED_TOOLCHAIN % (target, ide)
112126

tools/export/exporters.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
class OldLibrariesException(Exception): pass
2323

24+
class FailedBuildException(Exception) : pass
25+
2426
class Exporter(object):
2527
TEMPLATE_DIR = dirname(__file__)
2628
DOT_IN_RELATIVE_PATH = False
@@ -107,14 +109,18 @@ def progen_get_project_data(self):
107109
}
108110
return project_data
109111

110-
def progen_gen_file(self, tool_name, project_data):
111-
""" Generate project using ProGen Project API """
112+
def progen_gen_file(self, tool_name, project_data, pgen_build = False):
113+
"""" Generate project using ProGen Project API """
112114
settings = ProjectSettings()
113115
project = Project(self.program_name, [project_data], settings)
114116
# TODO: Fix this, the inc_dirs are not valid (our scripts copy files), therefore progen
115117
# thinks it is not dict but a file, and adds them to workspace.
116118
project.project['common']['include_paths'] = self.resources.inc_dirs
117119
project.generate(tool_name, copied=not self.sources_relative)
120+
if pgen_build:
121+
result = project.build(tool_name)
122+
if result == -1:
123+
raise FailedBuildException("Build Failed")
118124

119125
def __scan_all(self, path):
120126
resources = []

tools/export/iar.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class IAREmbeddedWorkbench(Exporter):
4646
# target is not supported yet
4747
continue
4848

49-
def generate(self):
49+
def generate(self, **kwargs):
5050
""" Generates the project files """
5151
project_data = self.progen_get_project_data()
5252
tool_specific = {}
@@ -75,7 +75,10 @@ def generate(self):
7575
# VLA is enabled via template IccAllowVLA
7676
project_data['tool_specific']['iar']['misc']['c_flags'].remove("--vla")
7777
project_data['common']['build_dir'] = os.path.join(project_data['common']['build_dir'], 'iar_arm')
78-
self.progen_gen_file('iar_arm', project_data)
78+
if 'progen_build' in kwargs:
79+
self.progen_gen_file('iar_arm', project_data, True)
80+
else:
81+
self.progen_gen_file('iar_arm', project_data)
7982

8083
# Currently not used, we should reuse folder_name to create virtual folders
8184
class IarFolder():

tools/export/uvision4.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class Uvision4(Exporter):
5050
def get_toolchain(self):
5151
return TARGET_MAP[self.target].default_toolchain
5252

53-
def generate(self):
53+
def generate(self, **kwargs):
5454
""" Generates the project files """
5555
project_data = self.progen_get_project_data()
5656
tool_specific = {}
@@ -96,4 +96,7 @@ def generate(self):
9696
i += 1
9797
project_data['common']['macros'].append('__ASSERT_MSG')
9898
project_data['common']['build_dir'] = join(project_data['common']['build_dir'], 'uvision4')
99-
self.progen_gen_file('uvision', project_data)
99+
if 'progen_build' in kwargs and kwargs.get('progen_build') == True:
100+
self.progen_gen_file('uvision', project_data, True)
101+
else:
102+
self.progen_gen_file('uvision', project_data)

tools/export/uvision5.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class Uvision5(Exporter):
5050
def get_toolchain(self):
5151
return TARGET_MAP[self.target].default_toolchain
5252

53-
def generate(self):
53+
def generate(self, **kwargs):
5454
""" Generates the project files """
5555
project_data = self.progen_get_project_data()
5656
tool_specific = {}
@@ -95,4 +95,7 @@ def generate(self):
9595
project_data['common']['macros'].pop(i)
9696
i += 1
9797
project_data['common']['macros'].append('__ASSERT_MSG')
98-
self.progen_gen_file('uvision5', project_data)
98+
if 'progen_build' in kwargs and kwargs.get('progen_build') == True:
99+
self.progen_gen_file('uvision5', project_data, True)
100+
else:
101+
self.progen_gen_file('uvision5', project_data)

tools/project.py

Lines changed: 53 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,16 @@
55

66
from shutil import move, rmtree
77
from argparse import ArgumentParser
8-
from os import path
98

10-
from tools.paths import EXPORT_DIR, EXPORT_WORKSPACE, EXPORT_TMP
11-
from tools.paths import MBED_BASE, MBED_LIBRARIES
12-
from tools.export import export, setup_user_prj, EXPORTERS, mcu_ide_matrix
13-
from tools.utils import args_error, mkdir
14-
from tools.tests import TESTS, Test, TEST_MAP
9+
from tools.paths import EXPORT_DIR
10+
from tools.export import EXPORTERS, mcu_ide_matrix
11+
from tools.utils import args_error
12+
from tools.tests import TESTS, TEST_MAP
1513
from tools.tests import test_known, test_name_known
1614
from tools.targets import TARGET_NAMES
17-
from tools.libraries import LIBRARIES
18-
from utils import argparse_lowercase_type, argparse_uppercase_type, argparse_filestring_type, argparse_many
15+
from utils import argparse_filestring_type, argparse_many
1916
from utils import argparse_force_lowercase_type, argparse_force_uppercase_type
20-
17+
from project_api import setup_project, perform_export, print_results, get_test_from_name
2118

2219

2320
if __name__ == '__main__':
@@ -128,77 +125,68 @@
128125
if exists(EXPORT_DIR):
129126
rmtree(EXPORT_DIR)
130127

128+
129+
# Target
130+
if options.mcu is None :
131+
args_error(parser, "[ERROR] You should specify an MCU")
132+
mcus = options.mcu
133+
134+
# IDE
135+
if options.ide is None:
136+
args_error(parser, "[ERROR] You should specify an IDE")
137+
ide = options.ide
138+
139+
p, n, src= options.program, options.program_name, options.source_dir
140+
141+
if src is None:
142+
if p is not None and n is not None:
143+
args_error(parser, "[ERROR] specify either '-n' or '-p', not both")
144+
if n:
145+
p = get_test_from_name(n)
146+
if p is None:
147+
args_error(parser, "[ERROR] Program with name '%s' not found" % n)
148+
149+
if p is None or (p < 0) or (p > (len(TESTS) - 1)):
150+
message = "[ERROR] You have to specify one of the following tests:\n"
151+
message += '\n'.join(map(str, sorted(TEST_MAP.values())))
152+
args_error(parser, message)
153+
154+
131155
# Export results
132156
successes = []
133157
failures = []
134-
zip = True
135-
clean = True
158+
zip = src is None
159+
clean = src is None
136160

137161
# source_dir = use relative paths, otherwise sources are copied
138162
sources_relative = True if options.source_dir else False
163+
for mcu in mcus.split(','):
164+
165+
lib_symbols = []
166+
if options.macros:
167+
lib_symbols += options.macros
168+
project_dir, project_name, project_temp = setup_project(mcu,
169+
ide,
170+
p,
171+
src,
172+
options.macros,
173+
options.build)
174+
175+
tmp_path, report = perform_export(project_dir, project_name, ide, mcu,
176+
project_temp, clean, zip, lib_symbols,
177+
sources_relative)
178+
139179

140-
for mcu in options.mcu:
141-
# Program Number or name
142-
p, src, ide = options.program, options.source_dir, options.ide
143-
144-
if src:
145-
# --source is used to generate IDE files to toolchain directly in the source tree and doesn't generate zip file
146-
project_dir = options.source_dir
147-
project_name = TESTS[p] if p else "Unnamed_project"
148-
project_temp = path.join(options.source_dir[0], 'projectfiles', '%s_%s' % (ide, mcu))
149-
mkdir(project_temp)
150-
lib_symbols = []
151-
if options.macros:
152-
lib_symbols += options.macros
153-
zip = False # don't create zip
154-
clean = False # don't cleanup because we use the actual source tree to generate IDE files
155-
else:
156-
test = Test(p)
157-
158-
# Some libraries have extra macros (called by exporter symbols) to we need to pass
159-
# them to maintain compilation macros integrity between compiled library and
160-
# header files we might use with it
161-
lib_symbols = []
162-
if options.macros:
163-
lib_symbols += options.macros
164-
for lib in LIBRARIES:
165-
if lib['build_dir'] in test.dependencies:
166-
lib_macros = lib.get('macros', None)
167-
if lib_macros is not None:
168-
lib_symbols.extend(lib_macros)
169-
170-
if not options.build:
171-
# Substitute the library builds with the sources
172-
# TODO: Substitute also the other library build paths
173-
if MBED_LIBRARIES in test.dependencies:
174-
test.dependencies.remove(MBED_LIBRARIES)
175-
test.dependencies.append(MBED_BASE)
176-
177-
# Build the project with the same directory structure of the mbed online IDE
178-
project_name = test.id
179-
project_dir = [join(EXPORT_WORKSPACE, project_name)]
180-
project_temp = EXPORT_TMP
181-
setup_user_prj(project_dir[0], test.source_dir, test.dependencies)
182-
183-
# Export to selected toolchain
184-
tmp_path, report = export(project_dir, project_name, ide, mcu, project_dir[0], project_temp, clean=clean, make_zip=zip, extra_symbols=lib_symbols, sources_relative=sources_relative)
185180
if report['success']:
186181
if not zip:
187182
zip_path = join(project_temp, project_name)
188183
else:
189184
zip_path = join(EXPORT_DIR, "%s_%s_%s.zip" % (project_name, ide, mcu))
190185
move(tmp_path, zip_path)
186+
191187
successes.append("%s::%s\t%s"% (mcu, ide, zip_path))
192188
else:
193189
failures.append("%s::%s\t%s"% (mcu, ide, report['errormsg']))
194190

195191
# Prints export results
196-
print
197-
if len(successes) > 0:
198-
print "Successful exports:"
199-
for success in successes:
200-
print " * %s"% success
201-
if len(failures) > 0:
202-
print "Failed exports:"
203-
for failure in failures:
204-
print " * %s"% failure
192+
print_results(successes, failures)

0 commit comments

Comments
 (0)