joey shelton / LED_Demo

Dependencies:   MAX44000 PWM_Tone_Library nexpaq_mdk

Fork of LED_Demo by Maxim nexpaq

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-2016 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 from types import ListType
00021 from shutil import rmtree
00022 from os.path import join, exists, basename, abspath, normpath
00023 from os import linesep, remove
00024 from time import time
00025 
00026 from tools.utils import mkdir, run_cmd, run_cmd_ext, NotSupportedException,\
00027     ToolException, InvalidReleaseTargetException
00028 from tools.paths import MBED_TARGETS_PATH, MBED_LIBRARIES, MBED_API, MBED_HAL,\
00029     MBED_COMMON, MBED_CONFIG_FILE
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 from tools.config import Config
00036 
00037 RELEASE_VERSIONS = ['2', '5']
00038 
00039 def prep_report (report, target_name, toolchain_name, id_name):
00040     """Setup report keys
00041 
00042     Positional arguments:
00043     report - the report to fill
00044     target_name - the target being used
00045     toolchain_name - the toolchain being used
00046     id_name - the name of the executable or library being built
00047     """
00048     if not target_name in report:
00049         report[target_name] = {}
00050 
00051     if not toolchain_name in report[target_name]:
00052         report[target_name][toolchain_name] = {}
00053 
00054     if not id_name in report[target_name][toolchain_name]:
00055         report[target_name][toolchain_name][id_name] = []
00056 
00057 def prep_properties (properties, target_name, toolchain_name, vendor_label):
00058     """Setup test properties
00059 
00060     Positional arguments:
00061     properties - the dict to fill
00062     target_name - the target the test is targeting
00063     toolchain_name - the toolchain that will compile the test
00064     vendor_label - the vendor
00065     """
00066     if not target_name in properties:
00067         properties[target_name] = {}
00068 
00069     if not toolchain_name in properties[target_name]:
00070         properties[target_name][toolchain_name] = {}
00071 
00072     properties[target_name][toolchain_name]["target"] = target_name
00073     properties[target_name][toolchain_name]["vendor"] = vendor_label
00074     properties[target_name][toolchain_name]["toolchain"] = toolchain_name
00075 
00076 def create_result (target_name, toolchain_name, id_name, description):
00077     """Create a result dictionary
00078 
00079     Positional arguments:
00080     target_name - the target being built for
00081     toolchain_name - the toolchain doing the building
00082     id_name - the name of the executable or library being built
00083     description - a human readable description of what's going on
00084     """
00085     cur_result = {}
00086     cur_result["target_name"] = target_name
00087     cur_result["toolchain_name"] = toolchain_name
00088     cur_result["id"] = id_name
00089     cur_result["description"] = description
00090     cur_result["elapsed_time"] = 0
00091     cur_result["output"] = ""
00092 
00093     return cur_result
00094 
00095 def add_result_to_report (report, result):
00096     """Add a single result to a report dictionary
00097 
00098     Positional arguments:
00099     report - the report to append to
00100     result - the result to append
00101     """
00102     target = result["target_name"]
00103     toolchain = result["toolchain_name"]
00104     id_name = result['id']
00105     result_wrap = {0: result}
00106     report[target][toolchain][id_name].append(result_wrap)
00107 
00108 def get_config (src_paths, target, toolchain_name):
00109     """Get the configuration object for a target-toolchain combination
00110 
00111     Positional arguments:
00112     src_paths - paths to scan for the configuration files
00113     target - the device we are building for
00114     toolchain_name - the string that identifies the build tools
00115     """
00116     # Convert src_paths to a list if needed
00117     if type(src_paths) != ListType:
00118         src_paths = [src_paths]
00119 
00120     # Pass all params to the unified prepare_resources()
00121     toolchain = prepare_toolchain(src_paths, target, toolchain_name)
00122 
00123     # Scan src_path for config files
00124     resources = toolchain.scan_resources(src_paths[0])
00125     for path in src_paths[1:]:
00126         resources.add(toolchain.scan_resources(path))
00127 
00128     # Update configuration files until added features creates no changes
00129     prev_features = set()
00130     while True:
00131         # Update the configuration with any .json files found while scanning
00132         toolchain.config.add_config_files(resources.json_files)
00133 
00134         # Add features while we find new ones
00135         features = toolchain.config.get_features()
00136         if features == prev_features:
00137             break
00138 
00139         for feature in features:
00140             if feature in resources.features:
00141                 resources += resources.features[feature]
00142 
00143         prev_features = features
00144     toolchain.config.validate_config()
00145 
00146     cfg, macros = toolchain.config.get_config_data()
00147     features = toolchain.config.get_features()
00148     return cfg, macros, features
00149 
00150 def is_official_target (target_name, version):
00151     """ Returns True, None if a target is part of the official release for the
00152     given version. Return False, 'reason' if a target is not part of the
00153     official release for the given version.
00154 
00155     Positional arguments:
00156     target_name - Name if the target (ex. 'K64F')
00157     version - The release version string. Should be a string contained within
00158               RELEASE_VERSIONS
00159     """
00160 
00161     result = True
00162     reason = None
00163     target = TARGET_MAP[target_name]
00164 
00165     if hasattr(target, 'release_versions') \
00166        and version in target.release_versions:
00167         if version == '2':
00168             # For version 2, either ARM or uARM toolchain support is required
00169             required_toolchains = set(['ARM', 'uARM'])
00170 
00171             if not len(required_toolchains.intersection(
00172                     set(target.supported_toolchains))) > 0:
00173                 result = False
00174                 reason = ("Target '%s' must support " % target.name) + \
00175                     ("one of the folowing toolchains to be included in the") + \
00176                     ((" mbed 2.0 official release: %s" + linesep) %
00177                      ", ".join(required_toolchains)) + \
00178                     ("Currently it is only configured to support the ") + \
00179                     ("following toolchains: %s" %
00180                      ", ".join(target.supported_toolchains))
00181 
00182         elif version == '5':
00183             # For version 5, ARM, GCC_ARM, and IAR toolchain support is required
00184             required_toolchains = set(['ARM', 'GCC_ARM', 'IAR'])
00185             required_toolchains_sorted = list(required_toolchains)
00186             required_toolchains_sorted.sort()
00187             supported_toolchains = set(target.supported_toolchains)
00188             supported_toolchains_sorted = list(supported_toolchains)
00189             supported_toolchains_sorted.sort()
00190 
00191             if not required_toolchains.issubset(supported_toolchains):
00192                 result = False
00193                 reason = ("Target '%s' must support " % target.name) + \
00194                     ("ALL of the folowing toolchains to be included in the") + \
00195                     ((" mbed OS 5.0 official release: %s" + linesep) %
00196                      ", ".join(required_toolchains_sorted)) + \
00197                     ("Currently it is only configured to support the ") + \
00198                     ("following toolchains: %s" %
00199                      ", ".join(supported_toolchains_sorted))
00200 
00201             elif not target.default_lib == 'std':
00202                 result = False
00203                 reason = ("Target '%s' must set the " % target.name) + \
00204                     ("'default_lib' to 'std' to be included in the ") + \
00205                     ("mbed OS 5.0 official release." + linesep) + \
00206                     ("Currently it is set to '%s'" % target.default_lib)
00207 
00208         else:
00209             result = False
00210             reason = ("Target '%s' has set an invalid release version of '%s'" %
00211                       version) + \
00212                 ("Please choose from the following release versions: %s" %
00213                  ', '.join(RELEASE_VERSIONS))
00214 
00215     else:
00216         result = False
00217         if not hasattr(target, 'release_versions'):
00218             reason = "Target '%s' " % target.name
00219             reason += "does not have the 'release_versions' key set"
00220         elif not version in target.release_versions:
00221             reason = "Target '%s' does not contain the version '%s' " % \
00222                      (target.name, version)
00223             reason += "in its 'release_versions' key"
00224 
00225     return result, reason
00226 
00227 def transform_release_toolchains (toolchains, version):
00228     """ Given a list of toolchains and a release version, return a list of
00229     only the supported toolchains for that release
00230 
00231     Positional arguments:
00232     toolchains - The list of toolchains
00233     version - The release version string. Should be a string contained within
00234               RELEASE_VERSIONS
00235     """
00236     if version == '5':
00237         return ['ARM', 'GCC_ARM', 'IAR']
00238     else:
00239         return toolchains
00240 
00241 
00242 def get_mbed_official_release (version):
00243     """ Given a release version string, return a tuple that contains a target
00244     and the supported toolchains for that release.
00245     Ex. Given '2', return (('LPC1768', ('ARM', 'GCC_ARM')),
00246                            ('K64F', ('ARM', 'GCC_ARM')), ...)
00247 
00248     Positional arguments:
00249     version - The version string. Should be a string contained within
00250               RELEASE_VERSIONS
00251     """
00252 
00253     mbed_official_release = (
00254         tuple(
00255             tuple(
00256                 [
00257                     TARGET_MAP[target].name,
00258                     tuple(transform_release_toolchains(
00259                         TARGET_MAP[target].supported_toolchains, version))
00260                 ]
00261             ) for target in TARGET_NAMES \
00262             if (hasattr(TARGET_MAP[target], 'release_versions')
00263                 and version in TARGET_MAP[target].release_versions)
00264         )
00265     )
00266 
00267     for target in mbed_official_release:
00268         is_official, reason = is_official_target(target[0], version)
00269 
00270         if not is_official:
00271             raise InvalidReleaseTargetException(reason)
00272 
00273     return mbed_official_release
00274 
00275 
00276 def prepare_toolchain (src_paths, target, toolchain_name,
00277                       macros=None, options=None, clean=False, jobs=1,
00278                       notify=None, silent=False, verbose=False,
00279                       extra_verbose=False, config=None,
00280                       app_config=None):
00281     """ Prepares resource related objects - toolchain, target, config
00282 
00283     Positional arguments:
00284     src_paths - the paths to source directories
00285     target - ['LPC1768', 'LPC11U24', 'LPC2368', etc.]
00286     toolchain_name - ['ARM', 'uARM', 'GCC_ARM', 'GCC_CR']
00287 
00288     Keyword arguments:
00289     macros - additional macros
00290     options - general compiler options like debug-symbols or small-build
00291     clean - Rebuild everything if True
00292     jobs - how many compilers we can run at once
00293     notify - Notify function for logs
00294     silent - suppress printing of progress indicators
00295     verbose - Write the actual tools command lines used if True
00296     extra_verbose - even more output!
00297     config - a Config object to use instead of creating one
00298     app_config - location of a chosen mbed_app.json file
00299     """
00300 
00301     # We need to remove all paths which are repeated to avoid
00302     # multiple compilations and linking with the same objects
00303     src_paths = [src_paths[0]] + list(set(src_paths[1:]))
00304 
00305     # If the configuration object was not yet created, create it now
00306     config = config or Config(target, src_paths, app_config=app_config)
00307 
00308     # If the 'target' argument is a string, convert it to a target instance
00309     if isinstance(target, basestring):
00310         try:
00311             target = TARGET_MAP[target]
00312         except KeyError:
00313             raise KeyError("Target '%s' not found" % target)
00314 
00315     # Toolchain instance
00316     try:
00317         toolchain = TOOLCHAIN_CLASSES[toolchain_name](
00318             target, options, notify, macros, silent,
00319             extra_verbose=extra_verbose)
00320     except KeyError:
00321         raise KeyError("Toolchain %s not supported" % toolchain_name)
00322 
00323     toolchain.config = config
00324     toolchain.jobs = jobs
00325     toolchain.build_all = clean
00326     toolchain.VERBOSE = verbose
00327 
00328     return toolchain
00329 
00330 def scan_resources (src_paths, toolchain, dependencies_paths=None,
00331                    inc_dirs=None, base_path=None):
00332     """ Scan resources using initialized toolcain
00333 
00334     Positional arguments
00335     src_paths - the paths to source directories
00336     toolchain - valid toolchain object
00337     dependencies_paths - dependency paths that we should scan for include dirs
00338     inc_dirs - additional include directories which should be added to
00339                the scanner resources
00340     """
00341 
00342     # Scan src_path
00343     resources = toolchain.scan_resources(src_paths[0], base_path=base_path)
00344     for path in src_paths[1:]:
00345         resources.add(toolchain.scan_resources(path, base_path=base_path))
00346 
00347     # Scan dependency paths for include dirs
00348     if dependencies_paths is not None:
00349         for path in dependencies_paths:
00350             lib_resources = toolchain.scan_resources(path)
00351             resources.inc_dirs.extend(lib_resources.inc_dirs)
00352 
00353     # Add additional include directories if passed
00354     if inc_dirs:
00355         if type(inc_dirs) == ListType:
00356             resources.inc_dirs.extend(inc_dirs)
00357         else:
00358             resources.inc_dirs.append(inc_dirs)
00359 
00360     # Load resources into the config system which might expand/modify resources
00361     # based on config data
00362     resources = toolchain.config.load_resources(resources)
00363 
00364     # Set the toolchain's configuration data
00365     toolchain.set_config_data(toolchain.config.get_config_data())
00366 
00367     return resources
00368 
00369 def build_project (src_paths, build_path, target, toolchain_name,
00370                   libraries_paths=None, options=None, linker_script=None,
00371                   clean=False, notify=None, verbose=False, name=None,
00372                   macros=None, inc_dirs=None, jobs=1, silent=False,
00373                   report=None, properties=None, project_id=None,
00374                   project_description=None, extra_verbose=False, config=None,
00375                   app_config=None):
00376     """ Build a project. A project may be a test or a user program.
00377 
00378     Positional arguments:
00379     src_paths - a path or list of paths that contain all files needed to build
00380                 the project
00381     build_path - the directory where all of the object files will be placed
00382     target - the MCU or board that the project will compile for
00383     toolchain_name - the name of the build tools
00384 
00385     Keyword arguments:
00386     libraries_paths - The location of libraries to include when linking
00387     options - general compiler options like debug-symbols or small-build
00388     linker_script - the file that drives the linker to do it's job
00389     clean - Rebuild everything if True
00390     notify - Notify function for logs
00391     verbose - Write the actual tools command lines used if True
00392     name - the name of the project
00393     macros - additional macros
00394     inc_dirs - additional directories where include files may be found
00395     jobs - how many compilers we can run at once
00396     silent - suppress printing of progress indicators
00397     report - a dict where a result may be appended
00398     properties - UUUUHHHHH beats me
00399     project_id - the name put in the report
00400     project_description - the human-readable version of what this thing does
00401     extra_verbose - even more output!
00402     config - a Config object to use instead of creating one
00403     app_config - location of a chosen mbed_app.json file
00404     """
00405 
00406     # Convert src_path to a list if needed
00407     if type(src_paths) != ListType:
00408         src_paths = [src_paths]
00409     # Extend src_paths wiht libraries_paths
00410     if libraries_paths is not None:
00411         src_paths.extend(libraries_paths)
00412 
00413     # Build Directory
00414     if clean and exists(build_path):
00415         rmtree(build_path)
00416     mkdir(build_path)
00417 
00418     # Pass all params to the unified prepare_toolchain()
00419     toolchain = prepare_toolchain(
00420         src_paths, target, toolchain_name, macros=macros, options=options,
00421         clean=clean, jobs=jobs, notify=notify, silent=silent, verbose=verbose,
00422         extra_verbose=extra_verbose, config=config, app_config=app_config)
00423 
00424     # The first path will give the name to the library
00425     if name is None:
00426         name = basename(normpath(abspath(src_paths[0])))
00427     toolchain.info("Building project %s (%s, %s)" %
00428                    (name, toolchain.target.name, toolchain_name))
00429 
00430     # Initialize reporting
00431     if report != None:
00432         start = time()
00433         # If project_id is specified, use that over the default name
00434         id_name = project_id.upper() if project_id else name.upper()
00435         description = project_description if project_description else name
00436         vendor_label = toolchain.target.extra_labels[0]
00437         prep_report(report, toolchain.target.name, toolchain_name, id_name)
00438         cur_result = create_result(toolchain.target.name, toolchain_name,
00439                                    id_name, description)
00440         if properties != None:
00441             prep_properties(properties, toolchain.target.name, toolchain_name,
00442                             vendor_label)
00443 
00444     try:
00445         # Call unified scan_resources
00446         resources = scan_resources(src_paths, toolchain, inc_dirs=inc_dirs)
00447 
00448         # Change linker script if specified
00449         if linker_script is not None:
00450             resources.linker_script = linker_script
00451 
00452         # Compile Sources
00453         objects = toolchain.compile_sources(resources, build_path,
00454                                             resources.inc_dirs)
00455         resources.objects.extend(objects)
00456 
00457         # Link Program
00458         res, _ = toolchain.link_program(resources, build_path, name)
00459 
00460         if report != None:
00461             end = time()
00462             cur_result["elapsed_time"] = end - start
00463             cur_result["output"] = toolchain.get_output()
00464             cur_result["result"] = "OK"
00465             cur_result["memory_usage"] = toolchain.map_outputs
00466 
00467             add_result_to_report(report, cur_result)
00468 
00469         return res
00470 
00471     except Exception as exc:
00472         if report != None:
00473             end = time()
00474 
00475             if isinstance(exc, NotSupportedException):
00476                 cur_result["result"] = "NOT_SUPPORTED"
00477             else:
00478                 cur_result["result"] = "FAIL"
00479 
00480             cur_result["elapsed_time"] = end - start
00481 
00482             toolchain_output = toolchain.get_output()
00483             if toolchain_output:
00484                 cur_result["output"] += toolchain_output
00485 
00486             add_result_to_report(report, cur_result)
00487 
00488         # Let Exception propagate
00489         raise
00490 
00491 def build_library (src_paths, build_path, target, toolchain_name,
00492                   dependencies_paths=None, options=None, name=None, clean=False,
00493                   archive=True, notify=None, verbose=False, macros=None,
00494                   inc_dirs=None, jobs=1, silent=False, report=None,
00495                   properties=None, extra_verbose=False, project_id=None,
00496                   remove_config_header_file=False, app_config=None):
00497     """ Build a library
00498 
00499     Positional arguments:
00500     src_paths - a path or list of paths that contain all files needed to build
00501                 the library
00502     build_path - the directory where all of the object files will be placed
00503     target - the MCU or board that the project will compile for
00504     toolchain_name - the name of the build tools
00505 
00506     Keyword arguments:
00507     dependencies_paths - The location of libraries to include when linking
00508     options - general compiler options like debug-symbols or small-build
00509     name - the name of the library
00510     clean - Rebuild everything if True
00511     archive - whether the library will create an archive file
00512     notify - Notify function for logs
00513     verbose - Write the actual tools command lines used if True
00514     macros - additional macros
00515     inc_dirs - additional directories where include files may be found
00516     jobs - how many compilers we can run at once
00517     silent - suppress printing of progress indicators
00518     report - a dict where a result may be appended
00519     properties - UUUUHHHHH beats me
00520     extra_verbose - even more output!
00521     project_id - the name that goes in the report
00522     remove_config_header_file - delete config header file when done building
00523     app_config - location of a chosen mbed_app.json file
00524     """
00525 
00526     # Convert src_path to a list if needed
00527     if type(src_paths) != ListType:
00528         src_paths = [src_paths]
00529 
00530     # Build path
00531     if archive:
00532         # Use temp path when building archive
00533         tmp_path = join(build_path, '.temp')
00534         mkdir(tmp_path)
00535     else:
00536         tmp_path = build_path
00537 
00538     # Clean the build directory
00539     if clean and exists(tmp_path):
00540         rmtree(tmp_path)
00541     mkdir(tmp_path)
00542 
00543     # Pass all params to the unified prepare_toolchain()
00544     toolchain = prepare_toolchain(
00545         src_paths, target, toolchain_name, macros=macros, options=options,
00546         clean=clean, jobs=jobs, notify=notify, silent=silent, verbose=verbose,
00547         extra_verbose=extra_verbose, app_config=app_config)
00548 
00549     # The first path will give the name to the library
00550     if name is None:
00551         name = basename(normpath(abspath(src_paths[0])))
00552     toolchain.info("Building library %s (%s, %s)" %
00553                    (name, toolchain.target.name, toolchain_name))
00554 
00555     # Initialize reporting
00556     if report != None:
00557         start = time()
00558         # If project_id is specified, use that over the default name
00559         id_name = project_id.upper() if project_id else name.upper()
00560         description = name
00561         vendor_label = toolchain.target.extra_labels[0]
00562         prep_report(report, toolchain.target.name, toolchain_name, id_name)
00563         cur_result = create_result(toolchain.target.name, toolchain_name,
00564                                    id_name, description)
00565         if properties != None:
00566             prep_properties(properties, toolchain.target.name, toolchain_name,
00567                             vendor_label)
00568 
00569     for src_path in src_paths:
00570         if not exists(src_path):
00571             error_msg = "The library source folder does not exist: %s", src_path
00572             if report != None:
00573                 cur_result["output"] = error_msg
00574                 cur_result["result"] = "FAIL"
00575                 add_result_to_report(report, cur_result)
00576             raise Exception(error_msg)
00577 
00578     try:
00579         # Call unified scan_resources
00580         resources = scan_resources(src_paths, toolchain,
00581                                    dependencies_paths=dependencies_paths,
00582                                    inc_dirs=inc_dirs)
00583 
00584 
00585         # Copy headers, objects and static libraries - all files needed for
00586         # static lib
00587         toolchain.copy_files(resources.headers, build_path, resources=resources)
00588         toolchain.copy_files(resources.objects, build_path, resources=resources)
00589         toolchain.copy_files(resources.libraries, build_path,
00590                              resources=resources)
00591         toolchain.copy_files(resources.json_files, build_path,
00592                              resources=resources)
00593         if resources.linker_script:
00594             toolchain.copy_files(resources.linker_script, build_path,
00595                                  resources=resources)
00596 
00597         if resources.hex_files:
00598             toolchain.copy_files(resources.hex_files, build_path,
00599                                  resources=resources)
00600 
00601         # Compile Sources
00602         objects = toolchain.compile_sources(resources, abspath(tmp_path),
00603                                             resources.inc_dirs)
00604         resources.objects.extend(objects)
00605 
00606         if archive:
00607             toolchain.build_library(objects, build_path, name)
00608 
00609         if remove_config_header_file:
00610             config_header_path = toolchain.get_config_header()
00611             if config_header_path:
00612                 remove(config_header_path)
00613 
00614         if report != None:
00615             end = time()
00616             cur_result["elapsed_time"] = end - start
00617             cur_result["output"] = toolchain.get_output()
00618             cur_result["result"] = "OK"
00619 
00620 
00621             add_result_to_report(report, cur_result)
00622         return True
00623 
00624     except Exception as exc:
00625         if report != None:
00626             end = time()
00627 
00628             if isinstance(exc, ToolException):
00629                 cur_result["result"] = "FAIL"
00630             elif isinstance(exc, NotSupportedException):
00631                 cur_result["result"] = "NOT_SUPPORTED"
00632 
00633             cur_result["elapsed_time"] = end - start
00634 
00635             toolchain_output = toolchain.get_output()
00636             if toolchain_output:
00637                 cur_result["output"] += toolchain_output
00638 
00639             add_result_to_report(report, cur_result)
00640 
00641         # Let Exception propagate
00642         raise
00643 
00644 ######################
00645 ### Legacy methods ###
00646 ######################
00647 
00648 def build_lib(lib_id, target, toolchain_name, options=None, verbose=False,
00649               clean=False, macros=None, notify=None, jobs=1, silent=False,
00650               report=None, properties=None, extra_verbose=False):
00651     """ Legacy method for building mbed libraries
00652 
00653     Positional arguments:
00654     lib_id - the library's unique identifier
00655     target - the MCU or board that the project will compile for
00656     toolchain_name - the name of the build tools
00657 
00658     Keyword arguments:
00659     options - general compiler options like debug-symbols or small-build
00660     clean - Rebuild everything if True
00661     verbose - Write the actual tools command lines used if True
00662     macros - additional macros
00663     notify - Notify function for logs
00664     jobs - how many compilers we can run at once
00665     silent - suppress printing of progress indicators
00666     report - a dict where a result may be appended
00667     properties - UUUUHHHHH beats me
00668     extra_verbose - even more output!
00669     """
00670     lib = Library(lib_id)
00671     if not lib.is_supported(target, toolchain_name):
00672         print('Library "%s" is not yet supported on target %s with toolchain %s'
00673               % (lib_id, target.name, toolchain_name))
00674         return False
00675 
00676     # We need to combine macros from parameter list with macros from library
00677     # definition
00678     lib_macros = lib.macros if lib.macros else []
00679     if macros:
00680         macros.extend(lib_macros)
00681     else:
00682         macros = lib_macros
00683 
00684     src_paths = lib.source_dir
00685     build_path = lib.build_dir
00686     dependencies_paths = lib.dependencies
00687     inc_dirs = lib.inc_dirs
00688     inc_dirs_ext = lib.inc_dirs_ext
00689 
00690     if type(src_paths) != ListType:
00691         src_paths = [src_paths]
00692 
00693     # The first path will give the name to the library
00694     name = basename(src_paths[0])
00695 
00696     if report != None:
00697         start = time()
00698         id_name = name.upper()
00699         description = name
00700         vendor_label = target.extra_labels[0]
00701         cur_result = None
00702         prep_report(report, target.name, toolchain_name, id_name)
00703         cur_result = create_result(target.name, toolchain_name, id_name,
00704                                    description)
00705 
00706         if properties != None:
00707             prep_properties(properties, target.name, toolchain_name,
00708                             vendor_label)
00709 
00710     for src_path in src_paths:
00711         if not exists(src_path):
00712             error_msg = "The library source folder does not exist: %s", src_path
00713 
00714             if report != None:
00715                 cur_result["output"] = error_msg
00716                 cur_result["result"] = "FAIL"
00717                 add_result_to_report(report, cur_result)
00718 
00719             raise Exception(error_msg)
00720 
00721     try:
00722         # Toolchain instance
00723         toolchain = TOOLCHAIN_CLASSES[toolchain_name](
00724             target, options, macros=macros, notify=notify, silent=silent,
00725             extra_verbose=extra_verbose)
00726         toolchain.VERBOSE = verbose
00727         toolchain.jobs = jobs
00728         toolchain.build_all = clean
00729 
00730         toolchain.info("Building library %s (%s, %s)" %
00731                        (name.upper(), target.name, toolchain_name))
00732 
00733         # Take into account the library configuration (MBED_CONFIG_FILE)
00734         config = Config(target)
00735         toolchain.config = config
00736         config.add_config_files([MBED_CONFIG_FILE])
00737 
00738         # Scan Resources
00739         resources = []
00740         for src_path in src_paths:
00741             resources.append(toolchain.scan_resources(src_path))
00742 
00743         # Add extra include directories / files which are required by library
00744         # This files usually are not in the same directory as source files so
00745         # previous scan will not include them
00746         if inc_dirs_ext is not None:
00747             for inc_ext in inc_dirs_ext:
00748                 resources.append(toolchain.scan_resources(inc_ext))
00749 
00750         # Dependencies Include Paths
00751         dependencies_include_dir = []
00752         if dependencies_paths is not None:
00753             for path in dependencies_paths:
00754                 lib_resources = toolchain.scan_resources(path)
00755                 dependencies_include_dir.extend(lib_resources.inc_dirs)
00756 
00757         if inc_dirs:
00758             dependencies_include_dir.extend(inc_dirs)
00759 
00760         # Add other discovered configuration data to the configuration object
00761         for res in resources:
00762             config.load_resources(res)
00763         toolchain.set_config_data(toolchain.config.get_config_data())
00764 
00765         # Create the desired build directory structure
00766         bin_path = join(build_path, toolchain.obj_path)
00767         mkdir(bin_path)
00768         tmp_path = join(build_path, '.temp', toolchain.obj_path)
00769         mkdir(tmp_path)
00770 
00771         # Copy Headers
00772         for resource in resources:
00773             toolchain.copy_files(resource.headers, build_path,
00774                                  resources=resource)
00775 
00776         dependencies_include_dir.extend(
00777             toolchain.scan_resources(build_path).inc_dirs)
00778 
00779         # Compile Sources
00780         objects = []
00781         for resource in resources:
00782             objects.extend(toolchain.compile_sources(resource, tmp_path,
00783                                                      dependencies_include_dir))
00784 
00785         needed_update = toolchain.build_library(objects, bin_path, name)
00786 
00787         if report != None and needed_update:
00788             end = time()
00789             cur_result["elapsed_time"] = end - start
00790             cur_result["output"] = toolchain.get_output()
00791             cur_result["result"] = "OK"
00792 
00793             add_result_to_report(report, cur_result)
00794         return True
00795 
00796     except Exception:
00797         if report != None:
00798             end = time()
00799             cur_result["result"] = "FAIL"
00800             cur_result["elapsed_time"] = end - start
00801 
00802             toolchain_output = toolchain.get_output()
00803             if toolchain_output:
00804                 cur_result["output"] += toolchain_output
00805 
00806             add_result_to_report(report, cur_result)
00807 
00808         # Let Exception propagate
00809         raise
00810 
00811 # We do have unique legacy conventions about how we build and package the mbed
00812 # library
00813 def build_mbed_libs (target, toolchain_name, options=None, verbose=False,
00814                     clean=False, macros=None, notify=None, jobs=1, silent=False,
00815                     report=None, properties=None, extra_verbose=False):
00816     """ Function returns True is library was built and false if building was
00817     skipped
00818 
00819     Positional arguments:
00820     target - the MCU or board that the project will compile for
00821     toolchain_name - the name of the build tools
00822 
00823     Keyword arguments:
00824     options - general compiler options like debug-symbols or small-build
00825     verbose - Write the actual tools command lines used if True
00826     clean - Rebuild everything if True
00827     macros - additional macros
00828     notify - Notify function for logs
00829     jobs - how many compilers we can run at once
00830     silent - suppress printing of progress indicators
00831     report - a dict where a result may be appended
00832     properties - UUUUHHHHH beats me
00833     extra_verbose - even more output!
00834     """
00835 
00836     if report != None:
00837         start = time()
00838         id_name = "MBED"
00839         description = "mbed SDK"
00840         vendor_label = target.extra_labels[0]
00841         cur_result = None
00842         prep_report(report, target.name, toolchain_name, id_name)
00843         cur_result = create_result(target.name, toolchain_name, id_name,
00844                                    description)
00845 
00846         if properties != None:
00847             prep_properties(properties, target.name, toolchain_name,
00848                             vendor_label)
00849 
00850     # Check toolchain support
00851     if toolchain_name not in target.supported_toolchains:
00852         supported_toolchains_text = ", ".join(target.supported_toolchains)
00853         print('%s target is not yet supported by toolchain %s' %
00854               (target.name, toolchain_name))
00855         print('%s target supports %s toolchain%s' %
00856               (target.name, supported_toolchains_text, 's'
00857                if len(target.supported_toolchains) > 1 else ''))
00858 
00859         if report != None:
00860             cur_result["result"] = "SKIP"
00861             add_result_to_report(report, cur_result)
00862 
00863         return False
00864 
00865     try:
00866         # Toolchain
00867         toolchain = TOOLCHAIN_CLASSES[toolchain_name](
00868             target, options, macros=macros, notify=notify, silent=silent,
00869             extra_verbose=extra_verbose)
00870         toolchain.VERBOSE = verbose
00871         toolchain.jobs = jobs
00872         toolchain.build_all = clean
00873 
00874         # Take into account the library configuration (MBED_CONFIG_FILE)
00875         config = Config(target)
00876         toolchain.config = config
00877         config.add_config_files([MBED_CONFIG_FILE])
00878         toolchain.set_config_data(toolchain.config.get_config_data())
00879 
00880         # Source and Build Paths
00881         build_target = join(MBED_LIBRARIES, "TARGET_" + target.name)
00882         build_toolchain = join(build_target, "TOOLCHAIN_" + toolchain.name)
00883         mkdir(build_toolchain)
00884 
00885         tmp_path = join(MBED_LIBRARIES, '.temp', toolchain.obj_path)
00886         mkdir(tmp_path)
00887 
00888         # CMSIS
00889         toolchain.info("Building library %s (%s, %s)" %
00890                        ('CMSIS', target.name, toolchain_name))
00891         cmsis_src = join(MBED_TARGETS_PATH, "cmsis")
00892         resources = toolchain.scan_resources(cmsis_src)
00893 
00894         toolchain.copy_files(resources.headers, build_target)
00895         toolchain.copy_files(resources.linker_script, build_toolchain)
00896         toolchain.copy_files(resources.bin_files, build_toolchain)
00897 
00898         objects = toolchain.compile_sources(resources, tmp_path)
00899         toolchain.copy_files(objects, build_toolchain)
00900 
00901         # mbed
00902         toolchain.info("Building library %s (%s, %s)" %
00903                        ('MBED', target.name, toolchain_name))
00904 
00905         # Common Headers
00906         toolchain.copy_files(toolchain.scan_resources(MBED_API).headers,
00907                              MBED_LIBRARIES)
00908         toolchain.copy_files(toolchain.scan_resources(MBED_HAL).headers,
00909                              MBED_LIBRARIES)
00910 
00911         # Target specific sources
00912         hal_src = join(MBED_TARGETS_PATH, "hal")
00913         hal_implementation = toolchain.scan_resources(hal_src)
00914         toolchain.copy_files(hal_implementation.headers +
00915                              hal_implementation.hex_files +
00916                              hal_implementation.libraries,
00917                              build_target, resources=hal_implementation)
00918         incdirs = toolchain.scan_resources(build_target).inc_dirs
00919         objects = toolchain.compile_sources(hal_implementation, tmp_path,
00920                                             [MBED_LIBRARIES] + incdirs)
00921 
00922         # Common Sources
00923         mbed_resources = toolchain.scan_resources(MBED_COMMON)
00924         objects += toolchain.compile_sources(mbed_resources, tmp_path,
00925                                              [MBED_LIBRARIES] + incdirs)
00926 
00927         # A number of compiled files need to be copied as objects as opposed to
00928         # being part of the mbed library, for reasons that have to do with the
00929         # way the linker search for symbols in archives. These are:
00930         #   - retarget.o: to make sure that the C standard lib symbols get
00931         #                 overridden
00932         #   - board.o: mbed_die is weak
00933         #   - mbed_overrides.o: this contains platform overrides of various
00934         #                       weak SDK functions
00935         separate_names, separate_objects = ['retarget.o', 'board.o',
00936                                             'mbed_overrides.o'], []
00937 
00938         for obj in objects:
00939             for name in separate_names:
00940                 if obj.endswith(name):
00941                     separate_objects.append(obj)
00942 
00943         for obj in separate_objects:
00944             objects.remove(obj)
00945 
00946         toolchain.build_library(objects, build_toolchain, "mbed")
00947 
00948         for obj in separate_objects:
00949             toolchain.copy_files(obj, build_toolchain)
00950 
00951         if report != None:
00952             end = time()
00953             cur_result["elapsed_time"] = end - start
00954             cur_result["output"] = toolchain.get_output()
00955             cur_result["result"] = "OK"
00956 
00957             add_result_to_report(report, cur_result)
00958 
00959         return True
00960 
00961     except Exception as exc:
00962         if report != None:
00963             end = time()
00964             cur_result["result"] = "FAIL"
00965             cur_result["elapsed_time"] = end - start
00966 
00967             toolchain_output = toolchain.get_output()
00968             if toolchain_output:
00969                 cur_result["output"] += toolchain_output
00970 
00971             cur_result["output"] += str(exc)
00972 
00973             add_result_to_report(report, cur_result)
00974 
00975         # Let Exception propagate
00976         raise
00977 
00978 
00979 def get_unique_supported_toolchains (release_targets=None):
00980     """ Get list of all unique toolchains supported by targets
00981 
00982     Keyword arguments:
00983     release_targets - tuple structure returned from get_mbed_official_release().
00984                       If release_targets is not specified, then it queries all
00985                       known targets
00986     """
00987     unique_supported_toolchains = []
00988 
00989     if not release_targets:
00990         for target in TARGET_NAMES:
00991             for toolchain in TARGET_MAP[target].supported_toolchains:
00992                 if toolchain not in unique_supported_toolchains:
00993                     unique_supported_toolchains.append(toolchain)
00994     else:
00995         for target in release_targets:
00996             for toolchain in target[1]:
00997                 if toolchain not in unique_supported_toolchains:
00998                     unique_supported_toolchains.append(toolchain)
00999 
01000     return unique_supported_toolchains
01001 
01002 
01003 def mcu_toolchain_matrix (verbose_html=False, platform_filter=None,
01004                          release_version='5'):
01005     """  Shows target map using prettytable
01006 
01007     Keyword arguments:
01008     verbose_html - emit html instead of a simple table
01009     platform_filter - remove results that match the string
01010     release_version - get the matrix for this major version number
01011     """
01012     # Only use it in this function so building works without extra modules
01013     from prettytable import PrettyTable
01014 
01015     if isinstance(release_version, basestring):
01016         # Force release_version to lowercase if it is a string
01017         release_version = release_version.lower()
01018     else:
01019         # Otherwise default to printing all known targets and toolchains
01020         release_version = 'all'
01021 
01022 
01023     version_release_targets = {}
01024     version_release_target_names = {}
01025 
01026     for version in RELEASE_VERSIONS:
01027         version_release_targets[version] = get_mbed_official_release(version)
01028         version_release_target_names[version] = [x[0] for x in
01029                                                  version_release_targets[
01030                                                      version]]
01031 
01032     if release_version in RELEASE_VERSIONS:
01033         release_targets = version_release_targets[release_version]
01034     else:
01035         release_targets = None
01036 
01037     unique_supported_toolchains = get_unique_supported_toolchains(
01038         release_targets)
01039     prepend_columns = ["Target"] + ["mbed OS %s" % x for x in RELEASE_VERSIONS]
01040 
01041     # All tests status table print
01042     columns = prepend_columns + unique_supported_toolchains
01043     table_printer = PrettyTable(columns)
01044     # Align table
01045     for col in columns:
01046         table_printer.align[col] = "c"
01047     table_printer.align["Target"] = "l"
01048 
01049     perm_counter = 0
01050     target_counter = 0
01051 
01052     target_names = []
01053 
01054     if release_targets:
01055         target_names = [x[0] for x in release_targets]
01056     else:
01057         target_names = TARGET_NAMES
01058 
01059     for target in sorted(target_names):
01060         if platform_filter is not None:
01061             # FIlter out platforms using regex
01062             if re.search(platform_filter, target) is None:
01063                 continue
01064         target_counter += 1
01065 
01066         row = [target]  # First column is platform name
01067 
01068         for version in RELEASE_VERSIONS:
01069             if target in version_release_target_names[version]:
01070                 text = "Supported"
01071             else:
01072                 text = "-"
01073             row.append(text)
01074 
01075         for unique_toolchain in unique_supported_toolchains:
01076             if unique_toolchain in TARGET_MAP[target].supported_toolchains:
01077                 text = "Supported"
01078                 perm_counter += 1
01079             else:
01080                 text = "-"
01081 
01082             row.append(text)
01083         table_printer.add_row(row)
01084 
01085     result = table_printer.get_html_string() if verbose_html \
01086              else table_printer.get_string()
01087     result += "\n"
01088     result += "Supported targets: %d\n"% (target_counter)
01089     if target_counter == 1:
01090         result += "Supported toolchains: %d"% (perm_counter)
01091     return result
01092 
01093 
01094 def get_target_supported_toolchains (target):
01095     """ Returns target supported toolchains list
01096 
01097     Positional arguments:
01098     target - the target to get the supported toolchains of
01099     """
01100     return TARGET_MAP[target].supported_toolchains if target in TARGET_MAP \
01101         else None
01102 
01103 
01104 def static_analysis_scan (target, toolchain_name, cppcheck_cmd,
01105                          cppcheck_msg_format, options=None, verbose=False,
01106                          clean=False, macros=None, notify=None, jobs=1,
01107                          extra_verbose=False):
01108     """Perform static analysis on a target and toolchain combination
01109 
01110     Positional arguments:
01111     target - the target to fake the build for
01112     toolchain_name - pretend you would compile with this toolchain
01113     cppcheck_cmd - the command used to do static analysis
01114     cppcheck_msg_format - the format of the check messages
01115 
01116     Keyword arguments:
01117     options - things like debug-symbols, or small-build, etc.
01118     verbose - more printing!
01119     clean - start from a clean slate
01120     macros - extra macros to compile with
01121     notify - the notification event handling function
01122     jobs - number of commands to run at once
01123     extra_verbose - even moar printing
01124     """
01125     # Toolchain
01126     toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options,
01127                                                   macros=macros, notify=notify,
01128                                                   extra_verbose=extra_verbose)
01129     toolchain.VERBOSE = verbose
01130     toolchain.jobs = jobs
01131     toolchain.build_all = clean
01132 
01133     # Source and Build Paths
01134     build_target = join(MBED_LIBRARIES, "TARGET_" + target.name)
01135     build_toolchain = join(build_target, "TOOLCHAIN_" + toolchain.name)
01136     mkdir(build_toolchain)
01137 
01138     tmp_path = join(MBED_LIBRARIES, '.temp', toolchain.obj_path)
01139     mkdir(tmp_path)
01140 
01141     # CMSIS
01142     toolchain.info("Static analysis for %s (%s, %s)" %
01143                    ('CMSIS', target.name, toolchain_name))
01144     cmsis_src = join(MBED_TARGETS_PATH, "cmsis")
01145     resources = toolchain.scan_resources(cmsis_src)
01146 
01147     # Copy files before analysis
01148     toolchain.copy_files(resources.headers, build_target)
01149     toolchain.copy_files(resources.linker_script, build_toolchain)
01150 
01151     # Gather include paths, c, cpp sources and macros to transfer to cppcheck
01152     # command line
01153     includes = ["-I%s"% i for i in resources.inc_dirs]
01154     includes.append("-I%s"% str(build_target))
01155     c_sources = " ".join(resources.c_sources)
01156     cpp_sources = " ".join(resources.cpp_sources)
01157     macros = ["-D%s"% s for s in toolchain.get_symbols() + toolchain.macros]
01158 
01159     includes = [inc.strip() for inc in includes]
01160     macros = [mac.strip() for mac in macros]
01161 
01162     check_cmd = cppcheck_cmd
01163     check_cmd += cppcheck_msg_format
01164     check_cmd += includes
01165     check_cmd += macros
01166 
01167     # We need to pass some params via file to avoid "command line too long in
01168     # some OSs"
01169     tmp_file = tempfile.NamedTemporaryFile(delete=False)
01170     tmp_file.writelines(line + '\n' for line in c_sources.split())
01171     tmp_file.writelines(line + '\n' for line in cpp_sources.split())
01172     tmp_file.close()
01173     check_cmd += ["--file-list=%s"% tmp_file.name]
01174 
01175     _stdout, _stderr, _ = run_cmd(check_cmd)
01176     if verbose:
01177         print _stdout
01178     print _stderr
01179 
01180     # =========================================================================
01181 
01182     # MBED
01183     toolchain.info("Static analysis for %s (%s, %s)" %
01184                    ('MBED', target.name, toolchain_name))
01185 
01186     # Common Headers
01187     toolchain.copy_files(toolchain.scan_resources(MBED_API).headers,
01188                          MBED_LIBRARIES)
01189     toolchain.copy_files(toolchain.scan_resources(MBED_HAL).headers,
01190                          MBED_LIBRARIES)
01191 
01192     # Target specific sources
01193     hal_src = join(MBED_TARGETS_PATH, "hal")
01194     hal_implementation = toolchain.scan_resources(hal_src)
01195 
01196     # Copy files before analysis
01197     toolchain.copy_files(hal_implementation.headers +
01198                          hal_implementation.hex_files, build_target,
01199                          resources=hal_implementation)
01200     incdirs = toolchain.scan_resources(build_target)
01201 
01202     target_includes = ["-I%s" % i for i in incdirs.inc_dirs]
01203     target_includes.append("-I%s"% str(build_target))
01204     target_includes.append("-I%s"% str(hal_src))
01205     target_c_sources = " ".join(incdirs.c_sources)
01206     target_cpp_sources = " ".join(incdirs.cpp_sources)
01207     target_macros = ["-D%s"% s for s in
01208                      toolchain.get_symbols() + toolchain.macros]
01209 
01210     # Common Sources
01211     mbed_resources = toolchain.scan_resources(MBED_COMMON)
01212 
01213     # Gather include paths, c, cpp sources and macros to transfer to cppcheck
01214     # command line
01215     mbed_includes = ["-I%s" % i for i in mbed_resources.inc_dirs]
01216     mbed_includes.append("-I%s"% str(build_target))
01217     mbed_includes.append("-I%s"% str(MBED_COMMON))
01218     mbed_includes.append("-I%s"% str(MBED_API))
01219     mbed_includes.append("-I%s"% str(MBED_HAL))
01220     mbed_c_sources = " ".join(mbed_resources.c_sources)
01221     mbed_cpp_sources = " ".join(mbed_resources.cpp_sources)
01222 
01223     target_includes = [inc.strip() for inc in target_includes]
01224     mbed_includes = [inc.strip() for inc in mbed_includes]
01225     target_macros = [mac.strip() for mac in target_macros]
01226 
01227     check_cmd = cppcheck_cmd
01228     check_cmd += cppcheck_msg_format
01229     check_cmd += target_includes
01230     check_cmd += mbed_includes
01231     check_cmd += target_macros
01232 
01233     # We need to pass some parames via file to avoid "command line too long in
01234     # some OSs"
01235     tmp_file = tempfile.NamedTemporaryFile(delete=False)
01236     tmp_file.writelines(line + '\n' for line in target_c_sources.split())
01237     tmp_file.writelines(line + '\n' for line in target_cpp_sources.split())
01238     tmp_file.writelines(line + '\n' for line in mbed_c_sources.split())
01239     tmp_file.writelines(line + '\n' for line in mbed_cpp_sources.split())
01240     tmp_file.close()
01241     check_cmd += ["--file-list=%s"% tmp_file.name]
01242 
01243     _stdout, _stderr, _ = run_cmd_ext(check_cmd)
01244     if verbose:
01245         print _stdout
01246     print _stderr
01247 
01248 
01249 def static_analysis_scan_lib (lib_id, target, toolchain, cppcheck_cmd,
01250                              cppcheck_msg_format, options=None, verbose=False,
01251                              clean=False, macros=None, notify=None, jobs=1,
01252                              extra_verbose=False):
01253     """Perform static analysis on a library as if it were to be compiled for a
01254     particular target and toolchain combination
01255     """
01256     lib = Library(lib_id)
01257     if lib.is_supported(target, toolchain):
01258         static_analysis_scan_library(
01259             lib.source_dir, lib.build_dir, target, toolchain, cppcheck_cmd,
01260             cppcheck_msg_format, lib.dependencies, options, verbose=verbose,
01261             clean=clean, macros=macros, notify=notify, jobs=jobs,
01262             extra_verbose=extra_verbose)
01263     else:
01264         print('Library "%s" is not yet supported on target %s with toolchain %s'
01265               % (lib_id, target.name, toolchain))
01266 
01267 
01268 def static_analysis_scan_library (src_paths, build_path, target, toolchain_name,
01269                                  cppcheck_cmd, cppcheck_msg_format,
01270                                  dependencies_paths=None, options=None,
01271                                  name=None, clean=False, notify=None,
01272                                  verbose=False, macros=None, jobs=1,
01273                                  extra_verbose=False):
01274     """ Function scans library for statically detectable defects
01275 
01276     Positional arguments:
01277     src_paths - the list of library paths to scan
01278     build_path - the location directory of result files
01279     target - the target to fake the build for
01280     toolchain_name - pretend you would compile with this toolchain
01281     cppcheck_cmd - the command used to do static analysis
01282     cppcheck_msg_format - the format of the check messages
01283 
01284     Keyword arguments:
01285     dependencies_paths - the paths to sources that this library depends on
01286     options - things like debug-symbols, or small-build, etc.
01287     name - the name of this library
01288     clean - start from a clean slate
01289     notify - the notification event handling function
01290     verbose - more printing!
01291     macros - extra macros to compile with
01292     jobs - number of commands to run at once
01293     extra_verbose - even moar printing
01294     """
01295     if type(src_paths) != ListType:
01296         src_paths = [src_paths]
01297 
01298     for src_path in src_paths:
01299         if not exists(src_path):
01300             raise Exception("The library source folder does not exist: %s",
01301                             src_path)
01302 
01303     # Toolchain instance
01304     toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options,
01305                                                   macros=macros, notify=notify,
01306                                                   extra_verbose=extra_verbose)
01307     toolchain.VERBOSE = verbose
01308     toolchain.jobs = jobs
01309 
01310     # The first path will give the name to the library
01311     name = basename(src_paths[0])
01312     toolchain.info("Static analysis for library %s (%s, %s)" %
01313                    (name.upper(), target.name, toolchain_name))
01314 
01315     # Scan Resources
01316     resources = []
01317     for src_path in src_paths:
01318         resources.append(toolchain.scan_resources(src_path))
01319 
01320     # Dependencies Include Paths
01321     dependencies_include_dir = []
01322     if dependencies_paths is not None:
01323         for path in dependencies_paths:
01324             lib_resources = toolchain.scan_resources(path)
01325             dependencies_include_dir.extend(lib_resources.inc_dirs)
01326 
01327     # Create the desired build directory structure
01328     bin_path = join(build_path, toolchain.obj_path)
01329     mkdir(bin_path)
01330     tmp_path = join(build_path, '.temp', toolchain.obj_path)
01331     mkdir(tmp_path)
01332 
01333     # Gather include paths, c, cpp sources and macros to transfer to cppcheck
01334     # command line
01335     includes = ["-I%s" % i for i in dependencies_include_dir + src_paths]
01336     c_sources = " "
01337     cpp_sources = " "
01338     macros = ['-D%s' % s for s in toolchain.get_symbols() + toolchain.macros]
01339 
01340     # Copy Headers
01341     for resource in resources:
01342         toolchain.copy_files(resource.headers, build_path, resources=resource)
01343         includes += ["-I%s" % i for i in resource.inc_dirs]
01344         c_sources += " ".join(resource.c_sources) + " "
01345         cpp_sources += " ".join(resource.cpp_sources) + " "
01346 
01347     dependencies_include_dir.extend(
01348         toolchain.scan_resources(build_path).inc_dirs)
01349 
01350     includes = [inc.strip() for inc in includes]
01351     macros = [mac.strip() for mac in macros]
01352 
01353     check_cmd = cppcheck_cmd
01354     check_cmd += cppcheck_msg_format
01355     check_cmd += includes
01356     check_cmd += macros
01357 
01358     # We need to pass some parameters via file to avoid "command line too long
01359     # in some OSs". A temporary file is created to store e.g. cppcheck list of
01360     # files for command line
01361     tmp_file = tempfile.NamedTemporaryFile(delete=False)
01362     tmp_file.writelines(line + '\n' for line in c_sources.split())
01363     tmp_file.writelines(line + '\n' for line in cpp_sources.split())
01364     tmp_file.close()
01365     check_cmd += ["--file-list=%s"% tmp_file.name]
01366 
01367     # This will allow us to grab result from both stdio and stderr outputs (so
01368     # we can show them) We assume static code analysis tool is outputting
01369     # defects on STDERR
01370     _stdout, _stderr, _ = run_cmd_ext(check_cmd)
01371     if verbose:
01372         print _stdout
01373     print _stderr
01374 
01375 
01376 def print_build_results (result_list, build_name):
01377     """ Generate result string for build results
01378 
01379     Positional arguments:
01380     result_list - the list of results to print
01381     build_name - the name of the build we are printing result for
01382     """
01383     result = ""
01384     if len(result_list) > 0:
01385         result += build_name + "\n"
01386         result += "\n".join(["  * %s" % f for f in result_list])
01387         result += "\n"
01388     return result
01389 
01390 def print_build_memory_usage (report):
01391     """ Generate result table with memory usage values for build results
01392     Aggregates (puts together) reports obtained from self.get_memory_summary()
01393 
01394     Positional arguments:
01395     report - Report generated during build procedure.
01396     """
01397     from prettytable import PrettyTable
01398     columns_text = ['name', 'target', 'toolchain']
01399     columns_int = ['static_ram', 'stack', 'heap', 'total_ram', 'total_flash']
01400     table = PrettyTable(columns_text + columns_int)
01401 
01402     for col in columns_text:
01403         table.align[col] = 'l'
01404 
01405     for col in columns_int:
01406         table.align[col] = 'r'
01407 
01408     for target in report:
01409         for toolchain in report[target]:
01410             for name in report[target][toolchain]:
01411                 for dlist in report[target][toolchain][name]:
01412                     for dlistelem in dlist:
01413                         # Get 'memory_usage' record and build table with
01414                         # statistics
01415                         record = dlist[dlistelem]
01416                         if 'memory_usage' in record and record['memory_usage']:
01417                             # Note that summary should be in the last record of
01418                             # 'memory_usage' section. This is why we are
01419                             # grabbing last "[-1]" record.
01420                             row = [
01421                                 record['description'],
01422                                 record['target_name'],
01423                                 record['toolchain_name'],
01424                                 record['memory_usage'][-1]['summary'][
01425                                     'static_ram'],
01426                                 record['memory_usage'][-1]['summary']['stack'],
01427                                 record['memory_usage'][-1]['summary']['heap'],
01428                                 record['memory_usage'][-1]['summary'][
01429                                     'total_ram'],
01430                                 record['memory_usage'][-1]['summary'][
01431                                     'total_flash'],
01432                             ]
01433                             table.add_row(row)
01434 
01435     result = "Memory map breakdown for built projects (values in Bytes):\n"
01436     result += table.get_string(sortby='name')
01437     return result
01438 
01439 def write_build_report (build_report, template_filename, filename):
01440     """Write a build report to disk using a template file
01441 
01442     Positional arguments:
01443     build_report - a report generated by the build system
01444     template_filename - a file that contains the template for the style of build
01445                         report
01446     filename - the location on disk to write the file to
01447     """
01448     build_report_failing = []
01449     build_report_passing = []
01450 
01451     for report in build_report:
01452         if len(report["failing"]) > 0:
01453             build_report_failing.append(report)
01454         else:
01455             build_report_passing.append(report)
01456 
01457     env = Environment(extensions=['jinja2.ext.with_'])
01458     env.loader = FileSystemLoader('ci_templates')
01459     template = env.get_template(template_filename)
01460 
01461     with open(filename, 'w+') as placeholder:
01462         placeholder.write(template.render(
01463             failing_builds=build_report_failing,
01464             passing_builds=build_report_passing))