Brian Daniels / mbed-tools

Fork of mbed-tools by Morpheus

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers build_api.py Source File

build_api.py

00001 """
00002 mbed SDK
00003 Copyright (c) 2011-2013 ARM Limited
00004 
00005 Licensed under the Apache License, Version 2.0 (the "License");
00006 you may not use this file except in compliance with the License.
00007 You may obtain a copy of the License at
00008 
00009     http://www.apache.org/licenses/LICENSE-2.0
00010 
00011 Unless required by applicable law or agreed to in writing, software
00012 distributed under the License is distributed on an "AS IS" BASIS,
00013 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014 See the License for the specific language governing permissions and
00015 limitations under the License.
00016 """
00017 
00018 import re
00019 import tempfile
00020 import colorama
00021 
00022 
00023 from types import ListType
00024 from shutil import rmtree
00025 from os.path import join, exists, basename
00026 from time import time
00027 
00028 from tools.utils import mkdir, run_cmd, run_cmd_ext, NotSupportedException
00029 from tools.paths import MBED_TARGETS_PATH, MBED_LIBRARIES, MBED_API, MBED_HAL, MBED_COMMON
00030 from tools.targets import TARGET_NAMES, TARGET_MAP
00031 from tools.libraries import Library
00032 from tools.toolchains import TOOLCHAIN_CLASSES
00033 from jinja2 import FileSystemLoader
00034 from jinja2.environment import Environment
00035 
00036 
00037 def prep_report(report, target_name, toolchain_name, id_name):
00038     # Setup report keys
00039     if not target_name in report:
00040         report[target_name] = {}
00041 
00042     if not toolchain_name in report[target_name]:
00043         report[target_name][toolchain_name] = {}
00044 
00045     if not id_name in report[target_name][toolchain_name]:
00046         report[target_name][toolchain_name][id_name] = []
00047 
00048 def prep_properties(properties, target_name, toolchain_name, vendor_label):
00049     # Setup test properties
00050     if not target_name in properties:
00051         properties[target_name] = {}
00052 
00053     if not toolchain_name in properties[target_name]:
00054         properties[target_name][toolchain_name] = {}
00055 
00056     properties[target_name][toolchain_name]["target"] = target_name
00057     properties[target_name][toolchain_name]["vendor"] = vendor_label
00058     properties[target_name][toolchain_name]["toolchain"] = toolchain_name
00059 
00060 def create_result(target_name, toolchain_name, id_name, description):
00061     cur_result = {}
00062     cur_result["target_name"] = target_name
00063     cur_result["toolchain_name"] = toolchain_name
00064     cur_result["id"] = id_name
00065     cur_result["description"] = description
00066     cur_result["elapsed_time"] = 0
00067     cur_result["output"] = ""
00068 
00069     return cur_result
00070 
00071 def add_result_to_report(report, result):
00072     target = result["target_name"]
00073     toolchain = result["toolchain_name"]
00074     id_name = result['id']
00075     result_wrap = { 0: result }
00076     report[target][toolchain][id_name].append(result_wrap)
00077 
00078 def build_project (src_path, build_path, target, toolchain_name,
00079         libraries_paths=None, options=None, linker_script=None,
00080         clean=False, notify=None, verbose=False, name=None, macros=None, inc_dirs=None,
00081         jobs=1, silent=False, report=None, properties=None, project_id=None, project_description=None, extra_verbose=False):
00082     """ This function builds project. Project can be for example one test / UT
00083     """
00084     # Toolchain instance
00085     toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options, notify, macros, silent, extra_verbose=extra_verbose)
00086     toolchain.VERBOSE = verbose
00087     toolchain.jobs = jobs
00088     toolchain.build_all = clean
00089     src_paths = [src_path] if type(src_path) != ListType else src_path
00090 
00091     # We need to remove all paths which are repeated to avoid
00092     # multiple compilations and linking with the same objects
00093     src_paths = [src_paths[0]] + list(set(src_paths[1:]))
00094     PROJECT_BASENAME = basename(src_paths[0])
00095 
00096     if name is None:
00097         # We will use default project name based on project folder name
00098         name = PROJECT_BASENAME
00099         toolchain.info("Building project %s (%s, %s)" % (PROJECT_BASENAME.upper(), target.name, toolchain_name))
00100     else:
00101         # User used custom global project name to have the same name for the
00102         toolchain.info("Building project %s to %s (%s, %s)" % (PROJECT_BASENAME.upper(), name, target.name, toolchain_name))
00103 
00104 
00105     if report != None:
00106         start = time()
00107         id_name = project_id.upper()
00108         description = project_description
00109         vendor_label = target.extra_labels[0]
00110         cur_result = None
00111         prep_report(report, target.name, toolchain_name, id_name)
00112         cur_result = create_result(target.name, toolchain_name, id_name, description)
00113 
00114         if properties != None:
00115             prep_properties(properties, target.name, toolchain_name, vendor_label)
00116 
00117     try:
00118         # Scan src_path and libraries_paths for resources
00119         resources = toolchain.scan_resources(src_paths[0])
00120         for path in src_paths[1:]:
00121             resources.add(toolchain.scan_resources(path))
00122         if libraries_paths is not None:
00123             src_paths.extend(libraries_paths)
00124             for path in libraries_paths:
00125                 resources.add(toolchain.scan_resources(path))
00126 
00127         if linker_script is not None:
00128             resources.linker_script = linker_script
00129 
00130         # Build Directory
00131         if clean:
00132             if exists(build_path):
00133                 rmtree(build_path)
00134         mkdir(build_path)
00135 
00136         # We need to add if necessary additional include directories
00137         if inc_dirs:
00138             if type(inc_dirs) == ListType:
00139                 resources.inc_dirs.extend(inc_dirs)
00140             else:
00141                 resources.inc_dirs.append(inc_dirs)
00142         # Compile Sources
00143         for path in src_paths:
00144             src = toolchain.scan_resources(path)
00145             objects = toolchain.compile_sources(src, build_path, resources.inc_dirs)
00146             resources.objects.extend(objects)
00147 
00148 
00149         # Link Program
00150         res, needed_update = toolchain.link_program(resources, build_path, name)
00151 
00152         if report != None and needed_update:
00153             end = time()
00154             cur_result["elapsed_time"] = end - start
00155             cur_result["output"] = toolchain.get_output()
00156             cur_result["result"] = "OK"
00157 
00158             add_result_to_report(report, cur_result)
00159 
00160         return res
00161 
00162     except Exception, e:
00163         if report != None:
00164             end = time()
00165 
00166             if isinstance(e, NotSupportedException):
00167                 cur_result["result"] = "NOT_SUPPORTED"
00168             else:
00169                 cur_result["result"] = "FAIL"
00170 
00171             cur_result["elapsed_time"] = end - start
00172 
00173             toolchain_output = toolchain.get_output()
00174             if toolchain_output:
00175                 cur_result["output"] += toolchain_output
00176 
00177             cur_result["output"] += str(e)
00178 
00179             add_result_to_report(report, cur_result)
00180 
00181         # Let Exception propagate
00182         raise e
00183 
00184 
00185 def build_library (src_paths, build_path, target, toolchain_name,
00186          dependencies_paths=None, options=None, name=None, clean=False,
00187          notify=None, verbose=False, macros=None, inc_dirs=None, inc_dirs_ext=None,
00188          jobs=1, silent=False, report=None, properties=None, extra_verbose=False):
00189     """ src_path: the path of the source directory
00190     build_path: the path of the build directory
00191     target: ['LPC1768', 'LPC11U24', 'LPC2368']
00192     toolchain: ['ARM', 'uARM', 'GCC_ARM', 'GCC_CR']
00193     library_paths: List of paths to additional libraries
00194     clean: Rebuild everything if True
00195     notify: Notify function for logs
00196     verbose: Write the actual tools command lines if True
00197     inc_dirs: additional include directories which should be included in build
00198     inc_dirs_ext: additional include directories which should be copied to library directory
00199     """
00200     if type(src_paths) != ListType:
00201         src_paths = [src_paths]
00202 
00203     # The first path will give the name to the library
00204     name = basename(src_paths[0])
00205 
00206     if report != None:
00207         start = time()
00208         id_name = name.upper()
00209         description = name
00210         vendor_label = target.extra_labels[0]
00211         cur_result = None
00212         prep_report(report, target.name, toolchain_name, id_name)
00213         cur_result = create_result(target.name, toolchain_name, id_name, description)
00214 
00215         if properties != None:
00216             prep_properties(properties, target.name, toolchain_name, vendor_label)
00217 
00218     for src_path in src_paths:
00219         if not exists(src_path):
00220             error_msg = "The library source folder does not exist: %s", src_path
00221 
00222             if report != None:
00223                 cur_result["output"] = error_msg
00224                 cur_result["result"] = "FAIL"
00225                 add_result_to_report(report, cur_result)
00226 
00227             raise Exception(error_msg)
00228 
00229     try:
00230         # Toolchain instance
00231         toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options, macros=macros, notify=notify, silent=silent, extra_verbose=extra_verbose)
00232         toolchain.VERBOSE = verbose
00233         toolchain.jobs = jobs
00234         toolchain.build_all = clean
00235 
00236         toolchain.info("Building library %s (%s, %s)" % (name.upper(), target.name, toolchain_name))
00237 
00238         # Scan Resources
00239         resources = []
00240         for src_path in src_paths:
00241             resources.append(toolchain.scan_resources(src_path))
00242 
00243         # Add extra include directories / files which are required by library
00244         # This files usually are not in the same directory as source files so
00245         # previous scan will not include them
00246         if inc_dirs_ext is not None:
00247             for inc_ext in inc_dirs_ext:
00248                 resources.append(toolchain.scan_resources(inc_ext))
00249 
00250         # Dependencies Include Paths
00251         dependencies_include_dir = []
00252         if dependencies_paths is not None:
00253             for path in dependencies_paths:
00254                 lib_resources = toolchain.scan_resources(path)
00255                 dependencies_include_dir.extend(lib_resources.inc_dirs)
00256 
00257         if inc_dirs:
00258             dependencies_include_dir.extend(inc_dirs)
00259 
00260         # Create the desired build directory structure
00261         bin_path = join(build_path, toolchain.obj_path)
00262         mkdir(bin_path)
00263         tmp_path = join(build_path, '.temp', toolchain.obj_path)
00264         mkdir(tmp_path)
00265 
00266         # Copy Headers
00267         for resource in resources:
00268             toolchain.copy_files(resource.headers, build_path, rel_path=resource.base_path)
00269         dependencies_include_dir.extend(toolchain.scan_resources(build_path).inc_dirs)
00270 
00271         # Compile Sources
00272         objects = []
00273         for resource in resources:
00274             objects.extend(toolchain.compile_sources(resource, tmp_path, dependencies_include_dir))
00275 
00276         needed_update = toolchain.build_library(objects, bin_path, name)
00277 
00278         if report != None and needed_update:
00279             end = time()
00280             cur_result["elapsed_time"] = end - start
00281             cur_result["output"] = toolchain.get_output()
00282             cur_result["result"] = "OK"
00283 
00284             add_result_to_report(report, cur_result)
00285 
00286     except Exception, e:
00287         if report != None:
00288             end = time()
00289             cur_result["result"] = "FAIL"
00290             cur_result["elapsed_time"] = end - start
00291 
00292             toolchain_output = toolchain.get_output()
00293             if toolchain_output:
00294                 cur_result["output"] += toolchain_output
00295 
00296             cur_result["output"] += str(e)
00297 
00298             add_result_to_report(report, cur_result)
00299 
00300         # Let Exception propagate
00301         raise e
00302 
00303 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):
00304     """ Wrapper for build_library function.
00305         Function builds library in proper directory using all dependencies and macros defined by user.
00306     """
00307     lib = Library(lib_id)
00308     if lib.is_supported(target, toolchain):
00309         # We need to combine macros from parameter list with macros from library definition
00310         MACROS = lib.macros if lib.macros else []
00311         if macros:
00312             MACROS.extend(macros)
00313 
00314         return build_library(lib.source_dir, lib.build_dir, target, toolchain, lib.dependencies, options,
00315                       verbose=verbose,
00316                       silent=silent,
00317                       clean=clean,
00318                       macros=MACROS,
00319                       notify=notify,
00320                       inc_dirs=lib.inc_dirs,
00321                       inc_dirs_ext=lib.inc_dirs_ext,
00322                       jobs=jobs,
00323                       report=report,
00324                       properties=properties,
00325                       extra_verbose=extra_verbose)
00326     else:
00327         print 'Library "%s" is not yet supported on target %s with toolchain %s' % (lib_id, target.name, toolchain)
00328         return False
00329 
00330 
00331 # We do have unique legacy conventions about how we build and package the mbed library
00332 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):
00333     """ Function returns True is library was built and false if building was skipped """
00334 
00335     if report != None:
00336         start = time()
00337         id_name = "MBED"
00338         description = "mbed SDK"
00339         vendor_label = target.extra_labels[0]
00340         cur_result = None
00341         prep_report(report, target.name, toolchain_name, id_name)
00342         cur_result = create_result(target.name, toolchain_name, id_name, description)
00343 
00344         if properties != None:
00345             prep_properties(properties, target.name, toolchain_name, vendor_label)
00346 
00347     # Check toolchain support
00348     if toolchain_name not in target.supported_toolchains:
00349         supported_toolchains_text = ", ".join(target.supported_toolchains)
00350         print '%s target is not yet supported by toolchain %s' % (target.name, toolchain_name)
00351         print '%s target supports %s toolchain%s' % (target.name, supported_toolchains_text, 's' if len(target.supported_toolchains) > 1 else '')
00352 
00353         if report != None:
00354             cur_result["result"] = "SKIP"
00355             add_result_to_report(report, cur_result)
00356 
00357         return False
00358 
00359     try:
00360         # Toolchain
00361         toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options, macros=macros, notify=notify, silent=silent, extra_verbose=extra_verbose)
00362         toolchain.VERBOSE = verbose
00363         toolchain.jobs = jobs
00364         toolchain.build_all = clean
00365 
00366         # Source and Build Paths
00367         BUILD_TARGET = join(MBED_LIBRARIES, "TARGET_" + target.name)
00368         BUILD_TOOLCHAIN = join(BUILD_TARGET, "TOOLCHAIN_" + toolchain.name)
00369         mkdir(BUILD_TOOLCHAIN)
00370 
00371         TMP_PATH = join(MBED_LIBRARIES, '.temp', toolchain.obj_path)
00372         mkdir(TMP_PATH)
00373 
00374         # CMSIS
00375         toolchain.info("Building library %s (%s, %s)"% ('CMSIS', target.name, toolchain_name))
00376         cmsis_src = join(MBED_TARGETS_PATH, "cmsis")
00377         resources = toolchain.scan_resources(cmsis_src)
00378 
00379         toolchain.copy_files(resources.headers, BUILD_TARGET)
00380         toolchain.copy_files(resources.linker_script, BUILD_TOOLCHAIN)
00381         toolchain.copy_files(resources.bin_files, BUILD_TOOLCHAIN)
00382 
00383         objects = toolchain.compile_sources(resources, TMP_PATH)
00384         toolchain.copy_files(objects, BUILD_TOOLCHAIN)
00385 
00386         # mbed
00387         toolchain.info("Building library %s (%s, %s)" % ('MBED', target.name, toolchain_name))
00388 
00389         # Common Headers
00390         toolchain.copy_files(toolchain.scan_resources(MBED_API).headers, MBED_LIBRARIES)
00391         toolchain.copy_files(toolchain.scan_resources(MBED_HAL).headers, MBED_LIBRARIES)
00392 
00393         # Target specific sources
00394         HAL_SRC = join(MBED_TARGETS_PATH, "hal")
00395         hal_implementation = toolchain.scan_resources(HAL_SRC)
00396         toolchain.copy_files(hal_implementation.headers + hal_implementation.hex_files + hal_implementation.libraries, BUILD_TARGET, HAL_SRC)
00397         incdirs = toolchain.scan_resources(BUILD_TARGET).inc_dirs
00398         objects = toolchain.compile_sources(hal_implementation, TMP_PATH, [MBED_LIBRARIES] + incdirs)
00399 
00400         # Common Sources
00401         mbed_resources = toolchain.scan_resources(MBED_COMMON)
00402         objects += toolchain.compile_sources(mbed_resources, TMP_PATH, [MBED_LIBRARIES] + incdirs)
00403 
00404         # A number of compiled files need to be copied as objects as opposed to
00405         # being part of the mbed library, for reasons that have to do with the way
00406         # the linker search for symbols in archives. These are:
00407         #   - retarget.o: to make sure that the C standard lib symbols get overridden
00408         #   - board.o: mbed_die is weak
00409         #   - mbed_overrides.o: this contains platform overrides of various weak SDK functions
00410         separate_names, separate_objects = ['retarget.o', 'board.o', 'mbed_overrides.o'], []
00411 
00412         for o in objects:
00413             for name in separate_names:
00414                 if o.endswith(name):
00415                     separate_objects.append(o)
00416 
00417         for o in separate_objects:
00418             objects.remove(o)
00419 
00420         needed_update = toolchain.build_library(objects, BUILD_TOOLCHAIN, "mbed")
00421 
00422         for o in separate_objects:
00423             toolchain.copy_files(o, BUILD_TOOLCHAIN)
00424 
00425         if report != None and needed_update:
00426             end = time()
00427             cur_result["elapsed_time"] = end - start
00428             cur_result["output"] = toolchain.get_output()
00429             cur_result["result"] = "OK"
00430 
00431             add_result_to_report(report, cur_result)
00432 
00433         return True
00434 
00435     except Exception, e:
00436         if report != None:
00437             end = time()
00438             cur_result["result"] = "FAIL"
00439             cur_result["elapsed_time"] = end - start
00440 
00441             toolchain_output = toolchain.get_output()
00442             if toolchain_output:
00443                 cur_result["output"] += toolchain_output
00444 
00445             cur_result["output"] += str(e)
00446 
00447             add_result_to_report(report, cur_result)
00448 
00449         # Let Exception propagate
00450         raise e
00451 
00452 def get_unique_supported_toolchains ():
00453     """ Get list of all unique toolchains supported by targets """
00454     unique_supported_toolchains = []
00455     for target in TARGET_NAMES:
00456         for toolchain in TARGET_MAP[target].supported_toolchains:
00457             if toolchain not in unique_supported_toolchains:
00458                 unique_supported_toolchains.append(toolchain)
00459     return unique_supported_toolchains
00460 
00461 
00462 def mcu_toolchain_matrix (verbose_html=False, platform_filter=None):
00463     """  Shows target map using prettytable """
00464     unique_supported_toolchains = get_unique_supported_toolchains()
00465     from prettytable import PrettyTable # Only use it in this function so building works without extra modules
00466 
00467     # All tests status table print
00468     columns = ["Platform"] + unique_supported_toolchains
00469     pt = PrettyTable(["Platform"] + unique_supported_toolchains)
00470     # Align table
00471     for col in columns:
00472         pt.align[col] = "c"
00473     pt.align["Platform"] = "l"
00474 
00475     perm_counter = 0
00476     target_counter = 0
00477     for target in sorted(TARGET_NAMES):
00478         if platform_filter is not None:
00479             # FIlter out platforms using regex
00480             if re.search(platform_filter, target) is None:
00481                 continue
00482         target_counter += 1
00483 
00484         row = [target]  # First column is platform name
00485         default_toolchain = TARGET_MAP[target].default_toolchain
00486         for unique_toolchain in unique_supported_toolchains:
00487             text = "-"
00488             if default_toolchain == unique_toolchain:
00489                 text = "Default"
00490                 perm_counter += 1
00491             elif unique_toolchain in TARGET_MAP[target].supported_toolchains:
00492                 text = "Supported"
00493                 perm_counter += 1
00494             row.append(text)
00495         pt.add_row(row)
00496 
00497     result = pt.get_html_string() if verbose_html else pt.get_string()
00498     result += "\n"
00499     result += "*Default - default on-line compiler\n"
00500     result += "*Supported - supported off-line compiler\n"
00501     result += "\n"
00502     result += "Total platforms: %d\n"% (target_counter)
00503     result += "Total permutations: %d"% (perm_counter)
00504     return result
00505 
00506 
00507 def get_target_supported_toolchains (target):
00508     """ Returns target supported toolchains list """
00509     return TARGET_MAP[target].supported_toolchains if target in TARGET_MAP else None
00510 
00511 
00512 def static_analysis_scan(target, toolchain_name, CPPCHECK_CMD, CPPCHECK_MSG_FORMAT, options=None, verbose=False, clean=False, macros=None, notify=None, jobs=1, extra_verbose=False):
00513     # Toolchain
00514     toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options, macros=macros, notify=notify, extra_verbose=extra_verbose)
00515     toolchain.VERBOSE = verbose
00516     toolchain.jobs = jobs
00517     toolchain.build_all = clean
00518 
00519     # Source and Build Paths
00520     BUILD_TARGET = join(MBED_LIBRARIES, "TARGET_" + target.name)
00521     BUILD_TOOLCHAIN = join(BUILD_TARGET, "TOOLCHAIN_" + toolchain.name)
00522     mkdir(BUILD_TOOLCHAIN)
00523 
00524     TMP_PATH = join(MBED_LIBRARIES, '.temp', toolchain.obj_path)
00525     mkdir(TMP_PATH)
00526 
00527     # CMSIS
00528     toolchain.info("Static analysis for %s (%s, %s)" % ('CMSIS', target.name, toolchain_name))
00529     cmsis_src = join(MBED_TARGETS_PATH, "cmsis")
00530     resources = toolchain.scan_resources(cmsis_src)
00531 
00532     # Copy files before analysis
00533     toolchain.copy_files(resources.headers, BUILD_TARGET)
00534     toolchain.copy_files(resources.linker_script, BUILD_TOOLCHAIN)
00535 
00536     # Gather include paths, c, cpp sources and macros to transfer to cppcheck command line
00537     includes = ["-I%s"% i for i in resources.inc_dirs]
00538     includes.append("-I%s"% str(BUILD_TARGET))
00539     c_sources = " ".join(resources.c_sources)
00540     cpp_sources = " ".join(resources.cpp_sources)
00541     macros = ["-D%s"% s for s in toolchain.get_symbols() + toolchain.macros]
00542 
00543     includes = map(str.strip, includes)
00544     macros = map(str.strip, macros)
00545 
00546     check_cmd = CPPCHECK_CMD
00547     check_cmd += CPPCHECK_MSG_FORMAT
00548     check_cmd += includes
00549     check_cmd += macros
00550 
00551     # We need to pass some params via file to avoid "command line too long in some OSs"
00552     tmp_file = tempfile.NamedTemporaryFile(delete=False)
00553     tmp_file.writelines(line + '\n' for line in c_sources.split())
00554     tmp_file.writelines(line + '\n' for line in cpp_sources.split())
00555     tmp_file.close()
00556     check_cmd += ["--file-list=%s"% tmp_file.name]
00557 
00558     _stdout, _stderr, _rc = run_cmd(check_cmd)
00559     if verbose:
00560         print _stdout
00561     print _stderr
00562 
00563     # =========================================================================
00564 
00565     # MBED
00566     toolchain.info("Static analysis for %s (%s, %s)" % ('MBED', target.name, toolchain_name))
00567 
00568     # Common Headers
00569     toolchain.copy_files(toolchain.scan_resources(MBED_API).headers, MBED_LIBRARIES)
00570     toolchain.copy_files(toolchain.scan_resources(MBED_HAL).headers, MBED_LIBRARIES)
00571 
00572     # Target specific sources
00573     HAL_SRC = join(MBED_TARGETS_PATH, "hal")
00574     hal_implementation = toolchain.scan_resources(HAL_SRC)
00575 
00576     # Copy files before analysis
00577     toolchain.copy_files(hal_implementation.headers + hal_implementation.hex_files, BUILD_TARGET, HAL_SRC)
00578     incdirs = toolchain.scan_resources(BUILD_TARGET)
00579 
00580     target_includes = ["-I%s" % i for i in incdirs.inc_dirs]
00581     target_includes.append("-I%s"% str(BUILD_TARGET))
00582     target_includes.append("-I%s"% str(HAL_SRC))
00583     target_c_sources = " ".join(incdirs.c_sources)
00584     target_cpp_sources = " ".join(incdirs.cpp_sources)
00585     target_macros = ["-D%s"% s for s in toolchain.get_symbols() + toolchain.macros]
00586 
00587     # Common Sources
00588     mbed_resources = toolchain.scan_resources(MBED_COMMON)
00589 
00590     # Gather include paths, c, cpp sources and macros to transfer to cppcheck command line
00591     mbed_includes = ["-I%s" % i for i in mbed_resources.inc_dirs]
00592     mbed_includes.append("-I%s"% str(BUILD_TARGET))
00593     mbed_includes.append("-I%s"% str(MBED_COMMON))
00594     mbed_includes.append("-I%s"% str(MBED_API))
00595     mbed_includes.append("-I%s"% str(MBED_HAL))
00596     mbed_c_sources = " ".join(mbed_resources.c_sources)
00597     mbed_cpp_sources = " ".join(mbed_resources.cpp_sources)
00598 
00599     target_includes = map(str.strip, target_includes)
00600     mbed_includes = map(str.strip, mbed_includes)
00601     target_macros = map(str.strip, target_macros)
00602 
00603     check_cmd = CPPCHECK_CMD
00604     check_cmd += CPPCHECK_MSG_FORMAT
00605     check_cmd += target_includes
00606     check_cmd += mbed_includes
00607     check_cmd += target_macros
00608 
00609     # We need to pass some parames via file to avoid "command line too long in some OSs"
00610     tmp_file = tempfile.NamedTemporaryFile(delete=False)
00611     tmp_file.writelines(line + '\n' for line in target_c_sources.split())
00612     tmp_file.writelines(line + '\n' for line in target_cpp_sources.split())
00613     tmp_file.writelines(line + '\n' for line in mbed_c_sources.split())
00614     tmp_file.writelines(line + '\n' for line in mbed_cpp_sources.split())
00615     tmp_file.close()
00616     check_cmd += ["--file-list=%s"% tmp_file.name]
00617 
00618     _stdout, _stderr, _rc = run_cmd_ext(check_cmd)
00619     if verbose:
00620         print _stdout
00621     print _stderr
00622 
00623 
00624 def static_analysis_scan_lib(lib_id, target, toolchain, cppcheck_cmd, cppcheck_msg_format,
00625                              options=None, verbose=False, clean=False, macros=None, notify=None, jobs=1, extra_verbose=False):
00626     lib = Library(lib_id)
00627     if lib.is_supported(target, toolchain):
00628         static_analysis_scan_library(lib.source_dir, lib.build_dir, target, toolchain, cppcheck_cmd, cppcheck_msg_format,
00629                       lib.dependencies, options,
00630                       verbose=verbose, clean=clean, macros=macros, notify=notify, jobs=jobs, extra_verbose=extra_verbose)
00631     else:
00632         print 'Library "%s" is not yet supported on target %s with toolchain %s'% (lib_id, target.name, toolchain)
00633 
00634 
00635 def static_analysis_scan_library (src_paths, build_path, target, toolchain_name, cppcheck_cmd, cppcheck_msg_format,
00636          dependencies_paths=None, options=None, name=None, clean=False,
00637          notify=None, verbose=False, macros=None, jobs=1, extra_verbose=False):
00638     """ Function scans library (or just some set of sources/headers) for staticly detectable defects """
00639     if type(src_paths) != ListType:
00640         src_paths = [src_paths]
00641 
00642     for src_path in src_paths:
00643         if not exists(src_path):
00644             raise Exception("The library source folder does not exist: %s", src_path)
00645 
00646     # Toolchain instance
00647     toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options, macros=macros, notify=notify, extra_verbose=extra_verbose)
00648     toolchain.VERBOSE = verbose
00649     toolchain.jobs = jobs
00650 
00651     # The first path will give the name to the library
00652     name = basename(src_paths[0])
00653     toolchain.info("Static analysis for library %s (%s, %s)" % (name.upper(), target.name, toolchain_name))
00654 
00655     # Scan Resources
00656     resources = []
00657     for src_path in src_paths:
00658         resources.append(toolchain.scan_resources(src_path))
00659 
00660     # Dependencies Include Paths
00661     dependencies_include_dir = []
00662     if dependencies_paths is not None:
00663         for path in dependencies_paths:
00664             lib_resources = toolchain.scan_resources(path)
00665             dependencies_include_dir.extend(lib_resources.inc_dirs)
00666 
00667     # Create the desired build directory structure
00668     bin_path = join(build_path, toolchain.obj_path)
00669     mkdir(bin_path)
00670     tmp_path = join(build_path, '.temp', toolchain.obj_path)
00671     mkdir(tmp_path)
00672 
00673     # Gather include paths, c, cpp sources and macros to transfer to cppcheck command line
00674     includes = ["-I%s" % i for i in dependencies_include_dir + src_paths]
00675     c_sources = " "
00676     cpp_sources = " "
00677     macros = ['-D%s' % s for s in toolchain.get_symbols() + toolchain.macros]
00678 
00679     # Copy Headers
00680     for resource in resources:
00681         toolchain.copy_files(resource.headers, build_path, rel_path=resource.base_path)
00682         includes += ["-I%s" % i for i in resource.inc_dirs]
00683         c_sources += " ".join(resource.c_sources) + " "
00684         cpp_sources += " ".join(resource.cpp_sources) + " "
00685 
00686     dependencies_include_dir.extend(toolchain.scan_resources(build_path).inc_dirs)
00687 
00688     includes = map(str.strip, includes)
00689     macros = map(str.strip, macros)
00690 
00691     check_cmd = cppcheck_cmd
00692     check_cmd += cppcheck_msg_format
00693     check_cmd += includes
00694     check_cmd += macros
00695 
00696     # We need to pass some parameters via file to avoid "command line too long in some OSs"
00697     # Temporary file is created to store e.g. cppcheck list of files for command line
00698     tmp_file = tempfile.NamedTemporaryFile(delete=False)
00699     tmp_file.writelines(line + '\n' for line in c_sources.split())
00700     tmp_file.writelines(line + '\n' for line in cpp_sources.split())
00701     tmp_file.close()
00702     check_cmd += ["--file-list=%s"% tmp_file.name]
00703 
00704     # This will allow us to grab result from both stdio and stderr outputs (so we can show them)
00705     # We assume static code analysis tool is outputting defects on STDERR
00706     _stdout, _stderr, _rc = run_cmd_ext(check_cmd)
00707     if verbose:
00708         print _stdout
00709     print _stderr
00710 
00711 
00712 def print_build_results (result_list, build_name):
00713     """ Generate result string for build results """
00714     result = ""
00715     if len(result_list) > 0:
00716         result += build_name + "\n"
00717         result += "\n".join(["  * %s" % f for f in result_list])
00718         result += "\n"
00719     return result
00720 
00721 def write_build_report(build_report, template_filename, filename):
00722     build_report_failing = []
00723     build_report_passing = []
00724 
00725     for report in build_report:
00726         if len(report["failing"]) > 0:
00727             build_report_failing.append(report)
00728         else:
00729             build_report_passing.append(report)
00730 
00731     env = Environment(extensions=['jinja2.ext.with_'])
00732     env.loader = FileSystemLoader('ci_templates')
00733     template = env.get_template(template_filename)
00734 
00735     with open(filename, 'w+') as f:
00736         f.write(template.render(failing_builds=build_report_failing, passing_builds=build_report_passing))