Anders Blomdell / mbed-sdk-tools
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers __init__.py Source File

__init__.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 from __future__ import print_function, absolute_import
00018 from builtins import str
00019 
00020 from os.path import splitext, basename, relpath, join, abspath, dirname,\
00021     exists, normpath
00022 from os import remove
00023 import sys
00024 from subprocess import check_output, CalledProcessError, Popen, PIPE
00025 import shutil
00026 from jinja2.exceptions import TemplateNotFound
00027 from tools.resources import FileType
00028 from tools.export.exporters import Exporter, apply_supported_whitelist
00029 from tools.utils import NotSupportedException
00030 from tools.targets import TARGET_MAP
00031 
00032 SHELL_ESCAPE_TABLE = {
00033     "(": "\(",
00034     ")": "\)",
00035 }
00036 
00037 
00038 def shell_escape(string):
00039     return "".join(SHELL_ESCAPE_TABLE.get(char, char) for char in string)
00040 
00041 
00042 class Makefile (Exporter):
00043     """Generic Makefile template that mimics the behavior of the python build
00044     system
00045     """
00046 
00047     DOT_IN_RELATIVE_PATH = True
00048 
00049     MBED_CONFIG_HEADER_SUPPORTED = True
00050 
00051     PREPROCESS_ASM = False
00052 
00053     POST_BINARY_WHITELIST = set([
00054         "MCU_NRF51Code.binary_hook",
00055         "TEENSY3_1Code.binary_hook",
00056         "LPCTargetCode.lpc_patch",
00057         "LPC4088Code.binary_hook"
00058     ])
00059 
00060     @classmethod
00061     def is_target_supported(cls, target_name):
00062         target = TARGET_MAP[target_name]
00063         return apply_supported_whitelist(
00064             cls.TOOLCHAIN, cls.POST_BINARY_WHITELIST, target)
00065 
00066     def generate (self):
00067         """Generate the makefile
00068 
00069         Note: subclasses should not need to override this method
00070         """
00071         if not self.resources.linker_script:
00072             raise NotSupportedException("No linker script found.")
00073 
00074         self.resources.win_to_unix()
00075 
00076         to_be_compiled = [normpath(splitext(src)[0]) + ".o" for src in
00077                           self.resources.s_sources +
00078                           self.resources.c_sources +
00079                           self.resources.cpp_sources]
00080 
00081         libraries = [self.prepare_lib(basename(lib)) for lib
00082                      in self.libraries]
00083         sys_libs = [self.prepare_sys_lib(lib) for lib
00084                     in self.toolchain.sys_libs]
00085 
00086         ctx = {
00087             'name': self.project_name,
00088             'to_be_compiled': to_be_compiled,
00089             'object_files': self.resources.objects,
00090             'include_paths': list(set([ normpath(p) for p
00091                                         in self.resources.inc_dirs])),
00092             'library_paths': self.resources.lib_dirs,
00093             'linker_script': normpath(self.resources.linker_script),
00094             'libraries': libraries,
00095             'ld_sys_libs': sys_libs,
00096             'hex_files': self.resources.hex_files,
00097             'vpath': (["../../.."]
00098                       if (basename(dirname(dirname(self.export_dir)))
00099                           == "projectfiles")
00100                       else [".."]),
00101             'cc_cmd': basename(self.toolchain.cc[0]),
00102             'cppc_cmd': basename(self.toolchain.cppc[0]),
00103             'asm_cmd': basename(self.toolchain.asm[0]),
00104             'ld_cmd': basename(self.toolchain.ld[0]),
00105             'elf2bin_cmd': basename(self.toolchain.elf2bin),
00106             'link_script_ext': self.toolchain.LINKER_EXT,
00107             'link_script_option': self.LINK_SCRIPT_OPTION,
00108             'user_library_flag': self.USER_LIBRARY_FLAG,
00109             'needs_asm_preproc': self.PREPROCESS_ASM ,
00110             'shell_escape': shell_escape,
00111         }
00112 
00113         if hasattr(self.toolchain, "preproc"):
00114             ctx['pp_cmd'] = " ".join(
00115                 [basename(self.toolchain.preproc[0])] +
00116                 self.toolchain.preproc[1:] +
00117                 self.toolchain.ld[1:]
00118             )
00119         else:
00120             ctx['pp_cmd'] = None
00121 
00122         for key in ['include_paths', 'library_paths', 'linker_script',
00123                     'hex_files']:
00124             if isinstance(ctx[key], list):
00125                 ctx[key] = [ctx['vpath'][0] + "/" + t for t in ctx[key]]
00126             else:
00127                 ctx[key] = ctx['vpath'][0] + "/" + ctx[key]
00128         if "../." not in ctx["include_paths"]:
00129             ctx["include_paths"] += ['../.']
00130         for key in ['include_paths', 'library_paths', 'hex_files',
00131                     'to_be_compiled']:
00132             ctx[key] = sorted(ctx[key])
00133         ctx.update(self.format_flags ())
00134         ctx['asm_flags'].extend(self.toolchain.asm[1:])
00135         ctx['c_flags'].extend(self.toolchain.cc[1:])
00136         ctx['cxx_flags'].extend(self.toolchain.cppc[1:])
00137 
00138         # Add the virtual path the the include option in the ASM flags
00139         new_asm_flags = []
00140         for flag in ctx['asm_flags']:
00141             if flag.startswith('-I'):
00142                 new_asm_flags.append("-I"+
00143                                      normpath(join(ctx['vpath'][0],
00144                                                    flag[2:])))
00145             elif flag.startswith('--preinclude='):
00146                 new_asm_flags.append("--preinclude={}/{}".format(ctx['vpath'][0], flag[13:]))
00147             else:
00148                 new_asm_flags.append(flag)
00149         ctx['asm_flags'] = new_asm_flags
00150 
00151         for templatefile in \
00152             ['makefile/%s_%s.tmpl' % (self.TEMPLATE,
00153                                       self.target.lower())] + \
00154             ['makefile/%s_%s.tmpl' % (self.TEMPLATE,
00155                                       label.lower()) for label
00156              in self.toolchain.target.extra_labels] +\
00157             ['makefile/%s.tmpl' % self.TEMPLATE]:
00158             try:
00159                 self.gen_file(templatefile, ctx, 'Makefile')
00160                 break
00161             except TemplateNotFound:
00162                 pass
00163         else:
00164             raise NotSupportedException("This make tool is in development")
00165 
00166     def format_flags (self):
00167         """Format toolchain flags for Makefile"""
00168         flags = {}
00169         for k, v in self.flags.items():
00170             if k in ['c_flags', 'cxx_flags']:
00171                 flags[k] = map(lambda x: x.replace('"', '\\"'), v)
00172             else:
00173                 flags[k] = v
00174 
00175         return flags
00176 
00177     @staticmethod
00178     def clean(_):
00179         remove("Makefile")
00180         # legacy .build directory cleaned if exists
00181         if exists('.build'):
00182             shutil.rmtree('.build')
00183         if exists('BUILD'):
00184             shutil.rmtree('BUILD')
00185 
00186     @staticmethod
00187     def build (project_name, log_name="build_log.txt", cleanup=True):
00188         """ Build Make project """
00189         # > Make -j
00190         cmd = ["make", "-j"]
00191 
00192         # Build the project
00193         p = Popen(cmd, stdout=PIPE, stderr=PIPE)
00194         out, err = p.communicate()
00195         ret_code = p.returncode
00196 
00197         out_string = "=" * 10 + "STDOUT" + "=" * 10 + "\n"
00198         out_string += out
00199         out_string += "=" * 10 + "STDERR" + "=" * 10 + "\n"
00200         out_string += err
00201 
00202         if ret_code == 0:
00203             out_string += "SUCCESS"
00204         else:
00205             out_string += "FAILURE"
00206 
00207         print(out_string)
00208 
00209         if log_name:
00210             # Write the output to the log file
00211             with open(log_name, 'w+') as f:
00212                 f.write(out_string)
00213 
00214         # Cleanup the exported and built files
00215         if cleanup:
00216             remove(log_name)
00217             Makefile.clean(project_name)
00218 
00219         if ret_code != 0:
00220             # Seems like something went wrong.
00221             return -1
00222         else:
00223             return 0
00224 
00225 
00226 class GccArm (Makefile ):
00227     """GCC ARM specific makefile target"""
00228     NAME = 'Make-GCC-ARM'
00229     TEMPLATE = 'make-gcc-arm'
00230     TOOLCHAIN = "GCC_ARM"
00231     LINK_SCRIPT_OPTION = "-T"
00232     USER_LIBRARY_FLAG = "-L"
00233 
00234     @staticmethod
00235     def prepare_lib(libname):
00236         if "lib" == libname[:3]:
00237             libname = libname[3:-2]
00238         return "-l" + libname
00239 
00240     @staticmethod
00241     def prepare_sys_lib(libname):
00242         return "-l" + libname
00243 
00244 
00245 class Arm (Makefile ):
00246     """ARM Compiler generic makefile target"""
00247     LINK_SCRIPT_OPTION = "--scatter"
00248     USER_LIBRARY_FLAG = "--userlibpath "
00249     TEMPLATE = 'make-arm'
00250 
00251     @staticmethod
00252     def prepare_lib(libname):
00253         return libname
00254 
00255     @staticmethod
00256     def prepare_sys_lib(libname):
00257         return libname
00258 
00259     def generate (self):
00260         if self.resources.linker_script:
00261             sct_file = self.resources.get_file_refs(FileType.LD_SCRIPT)[-1]
00262             new_script = self.toolchain.correct_scatter_shebang(
00263                 sct_file.path, join("..", dirname(sct_file.name)))
00264             if new_script is not sct_file:
00265                 self.resources.add_files_to_type(
00266                     FileType.LD_SCRIPT, [new_script])
00267                 self.generated_files.append(new_script)
00268         return super(Arm, self).generate()
00269 
00270 class Armc5 (Arm ):
00271     """ARM Compiler 5 (armcc) specific makefile target"""
00272     NAME = 'Make-ARMc5'
00273     TOOLCHAIN = "ARM"
00274     PREPROCESS_ASM = True
00275 
00276 class Armc6 (Arm ):
00277     """ARM Compiler 6 (armclang) specific generic makefile target"""
00278     NAME = 'Make-ARMc6'
00279     TOOLCHAIN = "ARMC6"
00280 
00281 
00282 class IAR (Makefile ):
00283     """IAR specific makefile target"""
00284     NAME = 'Make-IAR'
00285     TEMPLATE = 'make-iar'
00286     TOOLCHAIN = "IAR"
00287     LINK_SCRIPT_OPTION = "--config"
00288     USER_LIBRARY_FLAG = "-L"
00289 
00290     @staticmethod
00291     def prepare_lib(libname):
00292         if "lib" == libname[:3]:
00293             libname = libname[3:]
00294         return "-l" + splitext(libname)[0]
00295 
00296     @staticmethod
00297     def prepare_sys_lib(libname):
00298         if "lib" == libname[:3]:
00299             libname = libname[3:]
00300         return "-l" + splitext(libname)[0]