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.
Diff: build_api.py
- Revision:
- 9:2d27d77ada5c
- Parent:
- 8:a8ac6ed29081
- Child:
- 13:ab47a20b66f0
--- a/build_api.py Tue Jun 07 11:35:02 2016 +0100
+++ b/build_api.py Tue Jun 14 11:07:30 2016 +0100
@@ -19,13 +19,13 @@
import tempfile
import colorama
-
+from copy import copy
from types import ListType
from shutil import rmtree
from os.path import join, exists, basename, abspath, normpath
from os import getcwd, walk
from time import time
-from copy import copy
+import fnmatch
from tools.utils import mkdir, run_cmd, run_cmd_ext, NotSupportedException, ToolException
from tools.paths import MBED_TARGETS_PATH, MBED_LIBRARIES, MBED_API, MBED_HAL, MBED_COMMON
@@ -128,7 +128,7 @@
for path in src_paths:
profile = get_build_profile(path)
-
+
# If the configuration object was not yet created, create it now
config = config or Config(target, src_paths)
@@ -210,9 +210,9 @@
resources.objects.extend(objects)
# Link Program
- res, needed_update = toolchain.link_program(resources, build_path, name)
+ res, _ = toolchain.link_program(resources, build_path, name)
- if report != None and needed_update:
+ if report != None:
end = time()
cur_result["elapsed_time"] = end - start
cur_result["output"] = toolchain.get_output()
@@ -237,8 +237,6 @@
if toolchain_output:
cur_result["output"] += toolchain_output
- cur_result["output"] += str(e)
-
add_result_to_report(report, cur_result)
# Let Exception propagate
@@ -307,26 +305,43 @@
toolchain.info("Building library %s (%s, %s)" % (name, target.name, toolchain_name))
# Scan Resources
- resources = []
- for src_path in src_paths:
- resources.append(toolchain.scan_resources(src_path))
+ resources = None
+ for path in src_paths:
+ # Scan resources
+ resource = toolchain.scan_resources(path)
+
+ # Copy headers, objects and static libraries - all files needed for static lib
+ toolchain.copy_files(resource.headers, build_path, rel_path=resource.base_path)
+ toolchain.copy_files(resource.objects, build_path, rel_path=resource.base_path)
+ toolchain.copy_files(resource.libraries, build_path, rel_path=resource.base_path)
+ if resource.linker_script:
+ toolchain.copy_files(resource.linker_script, build_path, rel_path=resource.base_path)
+
+ # Extend resources collection
+ if not resources:
+ resources = resource
+ else:
+ resources.add(resource)
+
+ # We need to add if necessary additional include directories
+ if inc_dirs:
+ if type(inc_dirs) == ListType:
+ resources.inc_dirs.extend(inc_dirs)
+ else:
+ resources.inc_dirs.append(inc_dirs)
# Add extra include directories / files which are required by library
# This files usually are not in the same directory as source files so
# previous scan will not include them
if inc_dirs_ext is not None:
for inc_ext in inc_dirs_ext:
- resources.append(toolchain.scan_resources(inc_ext))
+ resources.add(toolchain.scan_resources(inc_ext))
# Dependencies Include Paths
- dependencies_include_dir = []
if dependencies_paths is not None:
for path in dependencies_paths:
lib_resources = toolchain.scan_resources(path)
- dependencies_include_dir.extend(lib_resources.inc_dirs)
-
- if inc_dirs:
- dependencies_include_dir.extend(inc_dirs)
+ resources.inc_dirs.extend(lib_resources.inc_dirs)
if archive:
# Use temp path when building archive
@@ -337,29 +352,21 @@
# Handle configuration
config = Config(target)
-
- # Copy headers, objects and static libraries
- for resource in resources:
- toolchain.copy_files(resource.headers, build_path, rel_path=resource.base_path)
- toolchain.copy_files(resource.objects, build_path, rel_path=resource.base_path)
- toolchain.copy_files(resource.libraries, build_path, rel_path=resource.base_path)
- if resource.linker_script:
- toolchain.copy_files(resource.linker_script, build_path, rel_path=resource.base_path)
- config.add_config_files(resource.json_files)
-
+ # Update the configuration with any .json files found while scanning
+ config.add_config_files(resources.json_files)
+ # And add the configuration macros to the toolchain
toolchain.add_macros(config.get_config_data_macros())
# Compile Sources
- objects = []
- for resource in resources:
- objects.extend(toolchain.compile_sources(resource, abspath(tmp_path), dependencies_include_dir))
+ for path in src_paths:
+ src = toolchain.scan_resources(path)
+ objects = toolchain.compile_sources(src, abspath(tmp_path), resources.inc_dirs)
+ resources.objects.extend(objects)
if archive:
- needed_update = toolchain.build_library(objects, build_path, name)
- else:
- needed_update = True
+ toolchain.build_library(objects, build_path, name)
- if report != None and needed_update:
+ if report != None:
end = time()
cur_result["elapsed_time"] = end - start
cur_result["output"] = toolchain.get_output()
@@ -382,6 +389,144 @@
if toolchain_output:
cur_result["output"] += toolchain_output
+ add_result_to_report(report, cur_result)
+
+ # Let Exception propagate
+ raise e
+
+######################
+### Legacy methods ###
+######################
+
+def build_lib(lib_id, target, toolchain_name, options=None, verbose=False, clean=False, macros=None, notify=None, jobs=1, silent=False, report=None, properties=None, extra_verbose=False):
+ """ Legacy method for building mbed libraries
+ Function builds library in proper directory using all dependencies and macros defined by user.
+ """
+ lib = Library(lib_id)
+ if not lib.is_supported(target, toolchain_name):
+ print 'Library "%s" is not yet supported on target %s with toolchain %s' % (lib_id, target.name, toolchain)
+ return False
+
+ # We need to combine macros from parameter list with macros from library definition
+ MACROS = lib.macros if lib.macros else []
+ if macros:
+ macros.extend(MACROS)
+ else:
+ macros = MACROS
+
+ src_paths = lib.source_dir
+ build_path = lib.build_dir
+ dependencies_paths = lib.dependencies
+ inc_dirs = lib.inc_dirs
+ inc_dirs_ext = lib.inc_dirs_ext
+
+ """ src_path: the path of the source directory
+ build_path: the path of the build directory
+ target: ['LPC1768', 'LPC11U24', 'LPC2368']
+ toolchain: ['ARM', 'uARM', 'GCC_ARM', 'GCC_CR']
+ library_paths: List of paths to additional libraries
+ clean: Rebuild everything if True
+ notify: Notify function for logs
+ verbose: Write the actual tools command lines if True
+ inc_dirs: additional include directories which should be included in build
+ inc_dirs_ext: additional include directories which should be copied to library directory
+ """
+ if type(src_paths) != ListType:
+ src_paths = [src_paths]
+
+ # The first path will give the name to the library
+ name = basename(src_paths[0])
+
+ if report != None:
+ start = time()
+ id_name = name.upper()
+ description = name
+ vendor_label = target.extra_labels[0]
+ cur_result = None
+ prep_report(report, target.name, toolchain_name, id_name)
+ cur_result = create_result(target.name, toolchain_name, id_name, description)
+
+ if properties != None:
+ prep_properties(properties, target.name, toolchain_name, vendor_label)
+
+ for src_path in src_paths:
+ if not exists(src_path):
+ error_msg = "The library source folder does not exist: %s", src_path
+
+ if report != None:
+ cur_result["output"] = error_msg
+ cur_result["result"] = "FAIL"
+ add_result_to_report(report, cur_result)
+
+ raise Exception(error_msg)
+
+ try:
+ # Toolchain instance
+ toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options, macros=macros, notify=notify, silent=silent, extra_verbose=extra_verbose, profile=profile)
+ toolchain.VERBOSE = verbose
+ toolchain.jobs = jobs
+ toolchain.build_all = clean
+
+ toolchain.info("Building library %s (%s, %s)" % (name.upper(), target.name, toolchain_name))
+
+ # Scan Resources
+ resources = []
+ for src_path in src_paths:
+ resources.append(toolchain.scan_resources(src_path))
+
+ # Add extra include directories / files which are required by library
+ # This files usually are not in the same directory as source files so
+ # previous scan will not include them
+ if inc_dirs_ext is not None:
+ for inc_ext in inc_dirs_ext:
+ resources.append(toolchain.scan_resources(inc_ext))
+
+ # Dependencies Include Paths
+ dependencies_include_dir = []
+ if dependencies_paths is not None:
+ for path in dependencies_paths:
+ lib_resources = toolchain.scan_resources(path)
+ dependencies_include_dir.extend(lib_resources.inc_dirs)
+
+ if inc_dirs:
+ dependencies_include_dir.extend(inc_dirs)
+
+ # Create the desired build directory structure
+ bin_path = join(build_path, toolchain.obj_path)
+ mkdir(bin_path)
+ tmp_path = join(build_path, '.temp', toolchain.obj_path)
+ mkdir(tmp_path)
+
+ # Copy Headers
+ for resource in resources:
+ toolchain.copy_files(resource.headers, build_path, rel_path=resource.base_path)
+ dependencies_include_dir.extend(toolchain.scan_resources(build_path).inc_dirs)
+
+ # Compile Sources
+ objects = []
+ for resource in resources:
+ objects.extend(toolchain.compile_sources(resource, tmp_path, dependencies_include_dir))
+
+ needed_update = toolchain.build_library(objects, bin_path, name)
+
+ if report != None and needed_update:
+ end = time()
+ cur_result["elapsed_time"] = end - start
+ cur_result["output"] = toolchain.get_output()
+ cur_result["result"] = "OK"
+
+ add_result_to_report(report, cur_result)
+
+ except Exception, e:
+ if report != None:
+ end = time()
+ cur_result["result"] = "FAIL"
+ cur_result["elapsed_time"] = end - start
+
+ toolchain_output = toolchain.get_output()
+ if toolchain_output:
+ cur_result["output"] += toolchain_output
+
cur_result["output"] += str(e)
add_result_to_report(report, cur_result)
@@ -389,34 +534,6 @@
# Let Exception propagate
raise e
-def build_lib(lib_id, target, toolchain, options=None, verbose=False, clean=False, macros=None, notify=None, jobs=1, silent=False, report=None, properties=None, extra_verbose=False):
- """ Wrapper for build_library function.
- Function builds library in proper directory using all dependencies and macros defined by user.
- """
- lib = Library(lib_id)
- if lib.is_supported(target, toolchain):
- # We need to combine macros from parameter list with macros from library definition
- MACROS = lib.macros if lib.macros else []
- if macros:
- MACROS.extend(macros)
-
- return build_library(lib.source_dir, lib.build_dir, target, toolchain, lib.dependencies, options,
- verbose=verbose,
- silent=silent,
- clean=clean,
- macros=MACROS,
- notify=notify,
- inc_dirs=lib.inc_dirs,
- inc_dirs_ext=lib.inc_dirs_ext,
- jobs=jobs,
- report=report,
- properties=properties,
- extra_verbose=extra_verbose)
- else:
- print 'Library "%s" is not yet supported on target %s with toolchain %s' % (lib_id, target.name, toolchain)
- return False
-
-
# We do have unique legacy conventions about how we build and package the mbed library
def build_mbed_libs(target, toolchain_name, options=None, verbose=False, clean=False, macros=None, notify=None, jobs=1, silent=False, report=None, properties=None, extra_verbose=False):
""" Function returns True is library was built and false if building was skipped """
@@ -445,6 +562,9 @@
return False
+ for path in src_paths:
+ profile = get_build_profile(path)
+
try:
# Toolchain
toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options, macros=macros, notify=notify, silent=silent, extra_verbose=extra_verbose)
@@ -506,12 +626,12 @@
for o in separate_objects:
objects.remove(o)
- needed_update = toolchain.build_library(objects, BUILD_TOOLCHAIN, "mbed")
+ toolchain.build_library(objects, BUILD_TOOLCHAIN, "mbed")
for o in separate_objects:
toolchain.copy_files(o, BUILD_TOOLCHAIN)
- if report != None and needed_update:
+ if report != None:
end = time()
cur_result["elapsed_time"] = end - start
cur_result["output"] = toolchain.get_output()
@@ -538,6 +658,7 @@
# Let Exception propagate
raise e
+
def get_unique_supported_toolchains():
""" Get list of all unique toolchains supported by targets """
unique_supported_toolchains = []
@@ -554,12 +675,12 @@
from prettytable import PrettyTable # Only use it in this function so building works without extra modules
# All tests status table print
- columns = ["Platform"] + unique_supported_toolchains
- pt = PrettyTable(["Platform"] + unique_supported_toolchains)
+ columns = ["Target"] + unique_supported_toolchains
+ pt = PrettyTable(["Target"] + unique_supported_toolchains)
# Align table
for col in columns:
pt.align[col] = "c"
- pt.align["Platform"] = "l"
+ pt.align["Target"] = "l"
perm_counter = 0
target_counter = 0
@@ -571,25 +692,21 @@
target_counter += 1
row = [target] # First column is platform name
- default_toolchain = TARGET_MAP[target].default_toolchain
for unique_toolchain in unique_supported_toolchains:
- text = "-"
- if default_toolchain == unique_toolchain:
- text = "Default"
- perm_counter += 1
- elif unique_toolchain in TARGET_MAP[target].supported_toolchains:
+ if unique_toolchain in TARGET_MAP[target].supported_toolchains:
text = "Supported"
perm_counter += 1
+ else:
+ text = "-"
+
row.append(text)
pt.add_row(row)
result = pt.get_html_string() if verbose_html else pt.get_string()
result += "\n"
- result += "*Default - default on-line compiler\n"
- result += "*Supported - supported off-line compiler\n"
- result += "\n"
- result += "Total platforms: %d\n"% (target_counter)
- result += "Total permutations: %d"% (perm_counter)
+ result += "Supported targets: %d\n"% (target_counter)
+ if target_counter == 1:
+ result += "Supported toolchains: %d"% (perm_counter)
return result
@@ -824,6 +941,66 @@
with open(filename, 'w+') as f:
f.write(template.render(failing_builds=build_report_failing, passing_builds=build_report_passing))
+
+def scan_for_source_paths(path, exclude_paths=None):
+ ignorepatterns = []
+ paths = []
+
+ def is_ignored(file_path):
+ for pattern in ignorepatterns:
+ if fnmatch.fnmatch(file_path, pattern):
+ return True
+ return False
+
+
+ """ os.walk(top[, topdown=True[, onerror=None[, followlinks=False]]])
+ When topdown is True, the caller can modify the dirnames list in-place
+ (perhaps using del or slice assignment), and walk() will only recurse into
+ the subdirectories whose names remain in dirnames; this can be used to prune
+ the search, impose a specific order of visiting, or even to inform walk()
+ about directories the caller creates or renames before it resumes walk()
+ again. Modifying dirnames when topdown is False is ineffective, because in
+ bottom-up mode the directories in dirnames are generated before dirpath
+ itself is generated.
+ """
+ for root, dirs, files in walk(path, followlinks=True):
+ # Remove ignored directories
+ # Check if folder contains .mbedignore
+ if ".mbedignore" in files :
+ with open (join(root,".mbedignore"), "r") as f:
+ lines=f.readlines()
+ lines = [l.strip() for l in lines] # Strip whitespaces
+ lines = [l for l in lines if l != ""] # Strip empty lines
+ lines = [l for l in lines if not re.match("^#",l)] # Strip comment lines
+ # Append root path to glob patterns
+ # and append patterns to ignorepatterns
+ ignorepatterns.extend([join(root,line.strip()) for line in lines])
+
+ for d in copy(dirs):
+ dir_path = join(root, d)
+
+ # Always ignore hidden directories
+ if d.startswith('.'):
+ dirs.remove(d)
+
+ # Remove dirs that already match the ignorepatterns
+ # to avoid travelling into them and to prevent them
+ # on appearing in include path.
+ if is_ignored(join(dir_path,"")):
+ dirs.remove(d)
+
+ if exclude_paths:
+ for exclude_path in exclude_paths:
+ rel_path = relpath(dir_path, exclude_path)
+ if not (rel_path.startswith('..')):
+ dirs.remove(d)
+ break
+
+ # Add root to include paths
+ paths.append(root)
+
+ return paths
+
def get_build_profile(path):
profile = None
builds = get_build_ids(path)
