Skip to content

Commit c2e3001

Browse files
committed
Add config system, memap, updates to build_api and toolchains
1 parent 773dab5 commit c2e3001

File tree

8 files changed

+1496
-192
lines changed

8 files changed

+1496
-192
lines changed

tools/build_api.py

Lines changed: 200 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,22 @@
1919
import tempfile
2020
import colorama
2121

22-
22+
from copy import copy
2323
from types import ListType
2424
from shutil import rmtree
25-
from os.path import join, exists, basename
25+
from os.path import join, exists, basename, abspath, normpath
26+
from os import getcwd, walk
2627
from time import time
28+
import fnmatch
2729

28-
from tools.utils import mkdir, run_cmd, run_cmd_ext, NotSupportedException
30+
from tools.utils import mkdir, run_cmd, run_cmd_ext, NotSupportedException, ToolException
2931
from tools.paths import MBED_TARGETS_PATH, MBED_LIBRARIES, MBED_API, MBED_HAL, MBED_COMMON
3032
from tools.targets import TARGET_NAMES, TARGET_MAP
3133
from tools.libraries import Library
3234
from tools.toolchains import TOOLCHAIN_CLASSES
3335
from jinja2 import FileSystemLoader
3436
from jinja2.environment import Environment
35-
37+
from tools.config import Config
3638

3739
def prep_report(report, target_name, toolchain_name, id_name):
3840
# Setup report keys
@@ -75,37 +77,90 @@ def add_result_to_report(report, result):
7577
result_wrap = { 0: result }
7678
report[target][toolchain][id_name].append(result_wrap)
7779

80+
def get_config(src_path, target, toolchain_name):
81+
# Convert src_path to a list if needed
82+
src_paths = [src_path] if type(src_path) != ListType else src_path
83+
# We need to remove all paths which are repeated to avoid
84+
# multiple compilations and linking with the same objects
85+
src_paths = [src_paths[0]] + list(set(src_paths[1:]))
86+
87+
# Create configuration object
88+
config = Config(target, src_paths)
89+
90+
# If the 'target' argument is a string, convert it to a target instance
91+
if isinstance(target, str):
92+
try:
93+
target = TARGET_MAP[target]
94+
except KeyError:
95+
raise KeyError("Target '%s' not found" % target)
96+
97+
# Toolchain instance
98+
try:
99+
toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options=None, notify=None, macros=None, silent=True, extra_verbose=False)
100+
except KeyError as e:
101+
raise KeyError("Toolchain %s not supported" % toolchain_name)
102+
103+
# Scan src_path for config files
104+
resources = toolchain.scan_resources(src_paths[0])
105+
for path in src_paths[1:]:
106+
resources.add(toolchain.scan_resources(path))
107+
108+
config.add_config_files(resources.json_files)
109+
return config.get_config_data()
110+
78111
def build_project(src_path, build_path, target, toolchain_name,
79112
libraries_paths=None, options=None, linker_script=None,
80113
clean=False, notify=None, verbose=False, name=None, macros=None, inc_dirs=None,
81-
jobs=1, silent=False, report=None, properties=None, project_id=None, project_description=None, extra_verbose=False):
114+
jobs=1, silent=False, report=None, properties=None, project_id=None, project_description=None,
115+
extra_verbose=False, config=None):
82116
""" This function builds project. Project can be for example one test / UT
83117
"""
84-
# Toolchain instance
85-
toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options, notify, macros, silent, extra_verbose=extra_verbose)
86-
toolchain.VERBOSE = verbose
87-
toolchain.jobs = jobs
88-
toolchain.build_all = clean
118+
119+
# Convert src_path to a list if needed
89120
src_paths = [src_path] if type(src_path) != ListType else src_path
90121

91122
# We need to remove all paths which are repeated to avoid
92123
# multiple compilations and linking with the same objects
93124
src_paths = [src_paths[0]] + list(set(src_paths[1:]))
94-
PROJECT_BASENAME = basename(src_paths[0])
125+
first_src_path = src_paths[0] if src_paths[0] != "." and src_paths[0] != "./" else getcwd()
126+
abs_path = abspath(first_src_path)
127+
project_name = basename(normpath(abs_path))
128+
129+
# If the configuration object was not yet created, create it now
130+
config = config or Config(target, src_paths)
131+
132+
# If the 'target' argument is a string, convert it to a target instance
133+
if isinstance(target, str):
134+
try:
135+
target = TARGET_MAP[target]
136+
except KeyError:
137+
raise KeyError("Target '%s' not found" % target)
138+
139+
# Toolchain instance
140+
try:
141+
toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options, notify, macros, silent, extra_verbose=extra_verbose)
142+
except KeyError as e:
143+
raise KeyError("Toolchain %s not supported" % toolchain_name)
144+
145+
toolchain.VERBOSE = verbose
146+
toolchain.jobs = jobs
147+
toolchain.build_all = clean
95148

96149
if name is None:
97150
# We will use default project name based on project folder name
98-
name = PROJECT_BASENAME
99-
toolchain.info("Building project %s (%s, %s)" % (PROJECT_BASENAME.upper(), target.name, toolchain_name))
151+
name = project_name
152+
toolchain.info("Building project %s (%s, %s)" % (project_name, target.name, toolchain_name))
100153
else:
101154
# User used custom global project name to have the same name for the
102-
toolchain.info("Building project %s to %s (%s, %s)" % (PROJECT_BASENAME.upper(), name, target.name, toolchain_name))
155+
toolchain.info("Building project %s to %s (%s, %s)" % (project_name, name, target.name, toolchain_name))
103156

104157

105158
if report != None:
106159
start = time()
107-
id_name = project_id.upper()
108-
description = project_description
160+
161+
# If project_id is specified, use that over the default name
162+
id_name = project_id.upper() if project_id else name.upper()
163+
description = project_description if project_description else name
109164
vendor_label = target.extra_labels[0]
110165
cur_result = None
111166
prep_report(report, target.name, toolchain_name, id_name)
@@ -139,13 +194,18 @@ def build_project(src_path, build_path, target, toolchain_name,
139194
resources.inc_dirs.extend(inc_dirs)
140195
else:
141196
resources.inc_dirs.append(inc_dirs)
197+
198+
# Update the configuration with any .json files found while scanning
199+
config.add_config_files(resources.json_files)
200+
# And add the configuration macros to the toolchain
201+
toolchain.add_macros(config.get_config_data_macros())
202+
142203
# Compile Sources
143204
for path in src_paths:
144205
src = toolchain.scan_resources(path)
145206
objects = toolchain.compile_sources(src, build_path, resources.inc_dirs)
146207
resources.objects.extend(objects)
147208

148-
149209
# Link Program
150210
res, needed_update = toolchain.link_program(resources, build_path, name)
151211

@@ -181,11 +241,11 @@ def build_project(src_path, build_path, target, toolchain_name,
181241
# Let Exception propagate
182242
raise e
183243

184-
185244
def build_library(src_paths, build_path, target, toolchain_name,
186-
dependencies_paths=None, options=None, name=None, clean=False,
245+
dependencies_paths=None, options=None, name=None, clean=False, archive=True,
187246
notify=None, verbose=False, macros=None, inc_dirs=None, inc_dirs_ext=None,
188-
jobs=1, silent=False, report=None, properties=None, extra_verbose=False):
247+
jobs=1, silent=False, report=None, properties=None, extra_verbose=False,
248+
project_id=None):
189249
""" src_path: the path of the source directory
190250
build_path: the path of the build directory
191251
target: ['LPC1768', 'LPC11U24', 'LPC2368']
@@ -201,11 +261,16 @@ def build_library(src_paths, build_path, target, toolchain_name,
201261
src_paths = [src_paths]
202262

203263
# The first path will give the name to the library
204-
name = basename(src_paths[0])
264+
project_name = basename(src_paths[0] if src_paths[0] != "." and src_paths[0] != "./" else getcwd())
265+
if name is None:
266+
# We will use default project name based on project folder name
267+
name = project_name
205268

206269
if report != None:
207270
start = time()
208-
id_name = name.upper()
271+
272+
# If project_id is specified, use that over the default name
273+
id_name = project_id.upper() if project_id else name.upper()
209274
description = name
210275
vendor_label = target.extra_labels[0]
211276
cur_result = None
@@ -233,47 +298,71 @@ def build_library(src_paths, build_path, target, toolchain_name,
233298
toolchain.jobs = jobs
234299
toolchain.build_all = clean
235300

236-
toolchain.info("Building library %s (%s, %s)" % (name.upper(), target.name, toolchain_name))
301+
toolchain.info("Building library %s (%s, %s)" % (name, target.name, toolchain_name))
237302

238303
# Scan Resources
239-
resources = []
240-
for src_path in src_paths:
241-
resources.append(toolchain.scan_resources(src_path))
304+
resources = None
305+
for path in src_paths:
306+
# Scan resources
307+
resource = toolchain.scan_resources(path)
308+
309+
# Copy headers, objects and static libraries - all files needed for static lib
310+
toolchain.copy_files(resource.headers, build_path, rel_path=resource.base_path)
311+
toolchain.copy_files(resource.objects, build_path, rel_path=resource.base_path)
312+
toolchain.copy_files(resource.libraries, build_path, rel_path=resource.base_path)
313+
if resource.linker_script:
314+
toolchain.copy_files(resource.linker_script, build_path, rel_path=resource.base_path)
315+
316+
# Extend resources collection
317+
if not resources:
318+
resources = resource
319+
else:
320+
resources.add(resource)
321+
322+
# We need to add if necessary additional include directories
323+
if inc_dirs:
324+
if type(inc_dirs) == ListType:
325+
resources.inc_dirs.extend(inc_dirs)
326+
else:
327+
resources.inc_dirs.append(inc_dirs)
242328

243329
# Add extra include directories / files which are required by library
244330
# This files usually are not in the same directory as source files so
245331
# previous scan will not include them
246332
if inc_dirs_ext is not None:
247333
for inc_ext in inc_dirs_ext:
248-
resources.append(toolchain.scan_resources(inc_ext))
334+
resources.add(toolchain.scan_resources(inc_ext))
249335

250336
# Dependencies Include Paths
251-
dependencies_include_dir = []
252337
if dependencies_paths is not None:
253338
for path in dependencies_paths:
254339
lib_resources = toolchain.scan_resources(path)
255-
dependencies_include_dir.extend(lib_resources.inc_dirs)
340+
resources.inc_dirs.extend(lib_resources.inc_dirs)
256341

257-
if inc_dirs:
258-
dependencies_include_dir.extend(inc_dirs)
259-
260-
# Create the desired build directory structure
261-
bin_path = join(build_path, toolchain.obj_path)
262-
mkdir(bin_path)
263-
tmp_path = join(build_path, '.temp', toolchain.obj_path)
264-
mkdir(tmp_path)
342+
if archive:
343+
# Use temp path when building archive
344+
tmp_path = join(build_path, '.temp')
345+
mkdir(tmp_path)
346+
else:
347+
tmp_path = build_path
265348

266-
# Copy Headers
267-
for resource in resources:
268-
toolchain.copy_files(resource.headers, build_path, rel_path=resource.base_path)
269-
dependencies_include_dir.extend(toolchain.scan_resources(build_path).inc_dirs)
349+
# Handle configuration
350+
config = Config(target)
351+
# Update the configuration with any .json files found while scanning
352+
config.add_config_files(resources.json_files)
353+
# And add the configuration macros to the toolchain
354+
toolchain.add_macros(config.get_config_data_macros())
270355

271356
# Compile Sources
272-
objects = []
273-
for resource in resources:
274-
objects.extend(toolchain.compile_sources(resource, tmp_path, dependencies_include_dir))
357+
for path in src_paths:
358+
src = toolchain.scan_resources(path)
359+
objects = toolchain.compile_sources(src, abspath(tmp_path), resources.inc_dirs)
360+
resources.objects.extend(objects)
275361

276-
needed_update = toolchain.build_library(objects, bin_path, name)
362+
if archive:
363+
needed_update = toolchain.build_library(resources.objects, build_path, name)
364+
else:
365+
needed_update = True
277366

278367
if report != None and needed_update:
279368
end = time()
@@ -286,7 +375,12 @@ def build_library(src_paths, build_path, target, toolchain_name,
286375
except Exception, e:
287376
if report != None:
288377
end = time()
289-
cur_result["result"] = "FAIL"
378+
379+
if isinstance(e, ToolException):
380+
cur_result["result"] = "FAIL"
381+
elif isinstance(e, NotSupportedException):
382+
cur_result["result"] = "NOT_SUPPORTED"
383+
290384
cur_result["elapsed_time"] = end - start
291385

292386
toolchain_output = toolchain.get_output()
@@ -734,3 +828,63 @@ def write_build_report(build_report, template_filename, filename):
734828

735829
with open(filename, 'w+') as f:
736830
f.write(template.render(failing_builds=build_report_failing, passing_builds=build_report_passing))
831+
832+
833+
def scan_for_source_paths(path, exclude_paths=None):
834+
ignorepatterns = []
835+
paths = []
836+
837+
def is_ignored(file_path):
838+
for pattern in ignorepatterns:
839+
if fnmatch.fnmatch(file_path, pattern):
840+
return True
841+
return False
842+
843+
844+
""" os.walk(top[, topdown=True[, onerror=None[, followlinks=False]]])
845+
When topdown is True, the caller can modify the dirnames list in-place
846+
(perhaps using del or slice assignment), and walk() will only recurse into
847+
the subdirectories whose names remain in dirnames; this can be used to prune
848+
the search, impose a specific order of visiting, or even to inform walk()
849+
about directories the caller creates or renames before it resumes walk()
850+
again. Modifying dirnames when topdown is False is ineffective, because in
851+
bottom-up mode the directories in dirnames are generated before dirpath
852+
itself is generated.
853+
"""
854+
for root, dirs, files in walk(path, followlinks=True):
855+
# Remove ignored directories
856+
# Check if folder contains .mbedignore
857+
if ".mbedignore" in files :
858+
with open (join(root,".mbedignore"), "r") as f:
859+
lines=f.readlines()
860+
lines = [l.strip() for l in lines] # Strip whitespaces
861+
lines = [l for l in lines if l != ""] # Strip empty lines
862+
lines = [l for l in lines if not re.match("^#",l)] # Strip comment lines
863+
# Append root path to glob patterns
864+
# and append patterns to ignorepatterns
865+
ignorepatterns.extend([join(root,line.strip()) for line in lines])
866+
867+
for d in copy(dirs):
868+
dir_path = join(root, d)
869+
870+
# Always ignore hidden directories
871+
if d.startswith('.'):
872+
dirs.remove(d)
873+
874+
# Remove dirs that already match the ignorepatterns
875+
# to avoid travelling into them and to prevent them
876+
# on appearing in include path.
877+
if is_ignored(join(dir_path,"")):
878+
dirs.remove(d)
879+
880+
if exclude_paths:
881+
for exclude_path in exclude_paths:
882+
rel_path = relpath(dir_path, exclude_path)
883+
if not (rel_path.startswith('..')):
884+
dirs.remove(d)
885+
break
886+
887+
# Add root to include paths
888+
paths.append(root)
889+
890+
return paths

0 commit comments

Comments
 (0)