Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers arm.py Source File

arm.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 from __future__ import print_function, absolute_import
00018 from builtins import str
00019 
00020 import re
00021 from copy import copy
00022 from os.path import join, dirname, splitext, basename, exists, relpath, isfile
00023 from os import makedirs, write, curdir, remove
00024 from tempfile import mkstemp
00025 from shutil import rmtree
00026 
00027 from tools.toolchains import mbedToolchain, TOOLCHAIN_PATHS
00028 from tools.hooks import hook_tool
00029 from tools.utils import mkdir, NotSupportedException
00030 
00031 class ARM(mbedToolchain):
00032     LINKER_EXT = '.sct'
00033     LIBRARY_EXT = '.ar'
00034 
00035     STD_LIB_NAME = "%s.ar"
00036     DIAGNOSTIC_PATTERN  = re.compile('"(?P<file>[^"]+)", line (?P<line>\d+)( \(column (?P<column>\d+)\)|): (?P<severity>Warning|Error|Fatal error): (?P<message>.+)')
00037     INDEX_PATTERN  = re.compile('(?P<col>\s*)\^')
00038     DEP_PATTERN = re.compile('\S+:\s(?P<file>.+)\n')
00039     SHEBANG = "#! armcc -E"
00040     SUPPORTED_CORES = ["Cortex-M0", "Cortex-M0+", "Cortex-M3", "Cortex-M4",
00041                        "Cortex-M4F", "Cortex-M7", "Cortex-M7F", "Cortex-M7FD", "Cortex-A9"]
00042 
00043     @staticmethod
00044     def check_executable():
00045         """Returns True if the executable (armcc) location specified by the
00046          user exists OR the executable can be found on the PATH.
00047          Returns False otherwise."""
00048         return mbedToolchain.generic_check_executable("ARM", 'armcc', 2, 'bin')
00049 
00050     def __init__(self, target, notify=None, macros=None,
00051                  silent=False, extra_verbose=False, build_profile=None,
00052                  build_dir=None):
00053         mbedToolchain.__init__(self, target, notify, macros, silent,
00054                                build_dir=build_dir,
00055                                extra_verbose=extra_verbose,
00056                                build_profile=build_profile)
00057         if target.core not in self.SUPPORTED_CORES:
00058             raise NotSupportedException(
00059                 "this compiler does not support the core %s" % target.core)
00060 
00061         if target.core == "Cortex-M0+":
00062             cpu = "Cortex-M0"
00063         elif target.core == "Cortex-M4F":
00064             cpu = "Cortex-M4.fp"
00065         elif target.core == "Cortex-M7FD":
00066             cpu = "Cortex-M7.fp.dp"
00067         elif target.core == "Cortex-M7F":
00068             cpu = "Cortex-M7.fp.sp"
00069         else:
00070             cpu = target.core
00071 
00072         ARM_BIN = join(TOOLCHAIN_PATHS['ARM'], "bin")
00073         ARM_INC = join(TOOLCHAIN_PATHS['ARM'], "include")
00074         
00075         main_cc = join(ARM_BIN, "armcc")
00076 
00077         self.flags['common'] += ["--cpu=%s" % cpu]
00078 
00079         self.asm = [main_cc] + self.flags['common'] + self.flags['asm']
00080         self.cc = [main_cc] + self.flags['common'] + self.flags['c']
00081         self.cppc = [main_cc] + self.flags['common'] + self.flags['c'] + self.flags['cxx']
00082 
00083         self.ld = [join(ARM_BIN, "armlink")] + self.flags['ld']
00084 
00085         self.ar = join(ARM_BIN, "armar")
00086         self.elf2bin = join(ARM_BIN, "fromelf")
00087 
00088         self.SHEBANG += " --cpu=%s" % cpu
00089 
00090     def parse_dependencies(self, dep_path):
00091         dependencies = []
00092         for line in open(dep_path).readlines():
00093             match = ARM.DEP_PATTERN.match(line)
00094             if match is not None:
00095                 #we need to append chroot, because when the .d files are generated the compiler is chrooted
00096                 dependencies.append((self.CHROOT if self.CHROOT else '') + match.group('file'))
00097         return dependencies
00098 
00099     def parse_output(self, output):
00100         msg = None
00101         for line in output.splitlines():
00102             match = ARM.DIAGNOSTIC_PATTERN.match(line)
00103             if match is not None:
00104                 if msg is not None:
00105                     self.cc_info(msg)
00106                     msg = None
00107                 msg = {
00108                     'severity': match.group('severity').lower(),
00109                     'file': match.group('file'),
00110                     'line': match.group('line'),
00111                     'col': match.group('column') if match.group('column') else 0,
00112                     'message': match.group('message'),
00113                     'text': '',
00114                     'target_name': self.target.name,
00115                     'toolchain_name': self.name
00116                 }
00117             elif msg is not None:
00118                 # Determine the warning/error column by calculating the ^ position
00119                 match = ARM.INDEX_PATTERN.match(line)
00120                 if match is not None:
00121                     msg['col'] = len(match.group('col'))
00122                     self.cc_info(msg)
00123                     msg = None
00124                 else:
00125                     msg['text'] += line+"\n"
00126         
00127         if msg is not None:
00128             self.cc_info(msg)
00129 
00130     def get_dep_option(self, object):
00131         base, _ = splitext(object)
00132         dep_path = base + '.d'
00133         return ["--depend", dep_path]
00134 
00135     def get_config_option(self, config_header):
00136         return ['--preinclude=' + config_header]
00137 
00138     def get_compile_options(self, defines, includes, for_asm=False):        
00139         opts = ['-D%s' % d for d in defines]
00140         if self.RESPONSE_FILES:
00141             opts += ['--via', self.get_inc_file(includes)]
00142         else:
00143             opts += ["-I%s" % i for i in includes]
00144 
00145         if not for_asm:
00146             config_header = self.get_config_header()
00147             if config_header is not None:
00148                 opts = opts + self.get_config_option(config_header)
00149         return opts
00150 
00151     @hook_tool
00152     def assemble(self, source, object, includes):
00153         # Preprocess first, then assemble
00154         dir = join(dirname(object), '.temp')
00155         mkdir(dir)
00156         tempfile = join(dir, basename(object) + '.E.s')
00157         
00158         # Build preprocess assemble command
00159         cmd_pre = self.asm + self.get_compile_options(self.get_symbols(True), includes) + ["-E", "-o", tempfile, source]
00160 
00161         # Build main assemble command
00162         cmd = self.asm + ["-o", object, tempfile]
00163 
00164         # Call cmdline hook
00165         cmd_pre = self.hook.get_cmdline_assembler(cmd_pre)
00166         cmd = self.hook.get_cmdline_assembler(cmd)
00167        
00168         # Return command array, don't execute
00169         return [cmd_pre, cmd]
00170 
00171     @hook_tool
00172     def compile(self, cc, source, object, includes):
00173         # Build compile command
00174         cmd = cc + self.get_compile_options(self.get_symbols(), includes)
00175         
00176         cmd.extend(self.get_dep_option(object))
00177             
00178         cmd.extend(["-o", object, source])
00179 
00180         # Call cmdline hook
00181         cmd = self.hook.get_cmdline_compiler(cmd)
00182 
00183         return [cmd]
00184 
00185     def compile_c(self, source, object, includes):
00186         return self.compile(self.cc, source, object, includes)
00187 
00188     def compile_cpp(self, source, object, includes):
00189         return self.compile(self.cppc, source, object, includes)
00190 
00191     def correct_scatter_shebang(self, scatter_file, base_path=curdir):
00192         """Correct the shebang at the top of a scatter file.
00193 
00194         Positional arguments:
00195         scatter_file -- the scatter file to correct
00196 
00197         Return:
00198         The location of the correct scatter file
00199 
00200         Side Effects:
00201         This method MAY write a new scatter file to disk
00202         """
00203         with open(scatter_file, "r") as input:
00204             lines = input.readlines()
00205             if (lines[0].startswith(self.SHEBANG) or
00206                 not lines[0].startswith("#!")):
00207                 return scatter_file
00208             else:
00209                 new_scatter = join(self.build_dir, ".link_script.sct")
00210                 self.SHEBANG += " -I %s" % relpath(dirname(scatter_file),
00211                                                    base_path)
00212                 if self.need_update(new_scatter, [scatter_file]):
00213                     with open(new_scatter, "w") as out:
00214                         out.write(self.SHEBANG)
00215                         out.write("\n")
00216                         out.write("".join(lines[1:]))
00217 
00218                 return new_scatter
00219 
00220     @hook_tool
00221     def link(self, output, objects, libraries, lib_dirs, scatter_file):
00222         base, _ = splitext(output)
00223         map_file = base + ".map"
00224         args = ["-o", output, "--info=totals", "--map", "--list=%s" % map_file]
00225         args.extend(objects)
00226         args.extend(libraries)
00227         if lib_dirs:
00228             args.extend(["--userlibpath", ",".join(lib_dirs)])
00229         if scatter_file:
00230             new_scatter = self.correct_scatter_shebang(scatter_file)
00231             args.extend(["--scatter", new_scatter])
00232 
00233         cmd_pre = self.ld + args
00234         cmd = self.hook.get_cmdline_linker(cmd_pre)
00235 
00236         if self.RESPONSE_FILES:
00237             cmd_linker = cmd[0]
00238             link_files = self.get_link_file(cmd[1:])
00239             cmd = [cmd_linker, '--via', link_files]
00240 
00241         self.cc_verbose("Link: %s" % ' '.join(cmd))
00242         self.default_cmd(cmd)
00243 
00244     @hook_tool
00245     def archive(self, objects, lib_path):
00246         if self.RESPONSE_FILES:
00247             param = ['--via', self.get_arch_file(objects)]
00248         else:
00249             param = objects
00250         self.default_cmd([self.ar, '-r', lib_path] + param)
00251 
00252     @hook_tool
00253     def binary(self, resources, elf, bin):
00254         _, fmt = splitext(bin)
00255         bin_arg = {".bin": "--bin", ".hex": "--i32"}[fmt]
00256         cmd = [self.elf2bin, bin_arg, '-o', bin, elf]
00257         cmd = self.hook.get_cmdline_binary(cmd)
00258 
00259         # remove target binary file/path
00260         if exists(bin):
00261             if isfile(bin):
00262                 remove(bin)
00263             else:
00264                 rmtree(bin)
00265 
00266         self.cc_verbose("FromELF: %s" % ' '.join(cmd))
00267         self.default_cmd(cmd)
00268 
00269     @staticmethod
00270     def name_mangle(name):
00271         return "_Z%i%sv" % (len(name), name)
00272 
00273     @staticmethod
00274     def make_ld_define(name, value):
00275         return "--predefine=\"-D%s=0x%x\"" % (name, value)
00276 
00277     @staticmethod
00278     def redirect_symbol(source, sync, build_dir):
00279         if not exists(build_dir):
00280             makedirs(build_dir)
00281         handle, filename = mkstemp(prefix=".redirect-symbol.", dir=build_dir)
00282         write(handle, "RESOLVE %s AS %s\n" % (source, sync))
00283         return "--edit=%s" % filename
00284 
00285 
00286 class ARM_STD(ARM):
00287     def __init__(self, target, notify=None, macros=None,
00288                  silent=False, extra_verbose=False, build_profile=None,
00289                  build_dir=None):
00290         ARM.__init__(self, target, notify, macros, silent,
00291                      build_dir=build_dir, extra_verbose=extra_verbose,
00292                      build_profile=build_profile)
00293         if "ARM" not in target.supported_toolchains:
00294             raise NotSupportedException("ARM compiler support is required for ARM build")
00295 
00296 
00297 class ARM_MICRO(ARM):
00298     PATCHED_LIBRARY = False
00299     def __init__(self, target, notify=None, macros=None,
00300                  silent=False, extra_verbose=False, build_profile=None,
00301                  build_dir=None):
00302         ARM.__init__(self, target, notify, macros, silent,
00303                      build_dir=build_dir, extra_verbose=extra_verbose,
00304                      build_profile=build_profile)
00305         if not set(("ARM", "uARM")).intersection(set(target.supported_toolchains)):
00306             raise NotSupportedException("ARM/uARM compiler support is required for ARM build")
00307 
00308 class ARMC6(ARM_STD):
00309     SHEBANG = "#! armclang -E --target=arm-arm-none-eabi -x c"
00310     SUPPORTED_CORES = ["Cortex-M0", "Cortex-M0+", "Cortex-M3", "Cortex-M4",
00311                        "Cortex-M4F", "Cortex-M7", "Cortex-M7F", "Cortex-M7FD",
00312                        "Cortex-M23", "Cortex-M23-NS", "Cortex-M33",
00313                        "CortexM33-NS", "Cortex-A9"]
00314     @staticmethod
00315     def check_executable():
00316         return mbedToolchain.generic_check_executable("ARMC6", "armclang", 1)
00317 
00318     def __init__(self, target, *args, **kwargs):
00319         mbedToolchain.__init__(self, target, *args, **kwargs)
00320         if target.core not in self.SUPPORTED_CORES:
00321             raise NotSupportedException(
00322                 "this compiler does not support the core %s" % target.core)
00323 
00324         if not set(("ARM", "ARMC6")).intersection(set(target.supported_toolchains)):
00325             raise NotSupportedException("ARM/ARMC6 compiler support is required for ARMC6 build")
00326 
00327         if target.core.lower().endswith("fd"):
00328             self.flags['common'].append("-mcpu=%s" % target.core.lower()[:-2])
00329             self.flags['ld'].append("--cpu=%s" % target.core.lower()[:-2])
00330             self.SHEBANG += " -mcpu=%s" % target.core.lower()[:-2]
00331         elif target.core.lower().endswith("f"):
00332             self.flags['common'].append("-mcpu=%s" % target.core.lower()[:-1])
00333             self.flags['ld'].append("--cpu=%s" % target.core.lower()[:-1])
00334             self.SHEBANG += " -mcpu=%s" % target.core.lower()[:-1]
00335         elif target.core.lower().endswith("ns"):
00336             self.flags['common'].append("-mcpu=%s" % target.core.lower()[:-3])
00337             self.flags['ld'].append("--cpu=%s" % target.core.lower()[:-3])
00338             self.SHEBANG += " -mcpu=%s" % target.core.lower()[:-3]
00339         else:
00340             self.flags['common'].append("-mcpu=%s" % target.core.lower())
00341             self.flags['ld'].append("--cpu=%s" % target.core.lower())
00342             self.SHEBANG += " -mcpu=%s" % target.core.lower()
00343 
00344         if target.core == "Cortex-M4F":
00345             self.flags['common'].append("-mfpu=fpv4-sp-d16")
00346             self.flags['common'].append("-mfloat-abi=hard")
00347         elif target.core == "Cortex-M7F":
00348             self.flags['common'].append("-mfpu=fpv5-sp-d16")
00349             self.flags['common'].append("-mfloat-abi=softfp")
00350         elif target.core == "Cortex-M7FD":
00351             self.flags['common'].append("-mfpu=fpv5-d16")
00352             self.flags['common'].append("-mfloat-abi=softfp")
00353         elif target.core.startswith("Cortex-M23"):
00354             self.flags['common'].append("-march=armv8-m.base")
00355         elif target.core.startswith("Cortex-M33"):
00356             self.flags['common'].append("-march=armv8-m.main")
00357 
00358         if target.core == "Cortex-M23" or target.core == "Cortex-M33":
00359             self.flags['common'].append("-mcmse")
00360 
00361         # Create Secure library
00362         if target.core == "Cortex-M23" or self.target.core == "Cortex-M33":
00363             build_dir = kwargs['build_dir']
00364             secure_file = join(build_dir, "cmse_lib.o")
00365             self.flags["ld"] += ["--import_cmse_lib_out=%s" % secure_file]
00366 
00367         asm_cpu = {
00368             "Cortex-M0+": "Cortex-M0",
00369             "Cortex-M4F": "Cortex-M4.fp",
00370             "Cortex-M7F": "Cortex-M7.fp.sp",
00371             "Cortex-M7FD": "Cortex-M7.fp.dp",
00372             "Cortex-M23-NS": "Cortex-M23",
00373             "Cortex-M33-NS": "Cortex-M33" }.get(target.core, target.core)
00374 
00375         self.flags['asm'].append("--cpu=%s" % asm_cpu)
00376 
00377         self.cc = ([join(TOOLCHAIN_PATHS["ARMC6"], "armclang")] +
00378                    self.flags['common'] + self.flags['c'])
00379         self.cppc = ([join(TOOLCHAIN_PATHS["ARMC6"], "armclang")] +
00380                      self.flags['common'] + self.flags['cxx'])
00381         self.asm = [join(TOOLCHAIN_PATHS["ARMC6"], "armasm")] + self.flags['asm']
00382         self.ld = [join(TOOLCHAIN_PATHS["ARMC6"], "armlink")] + self.flags['ld']
00383         self.ar = [join(TOOLCHAIN_PATHS["ARMC6"], "armar")]
00384         self.elf2bin = join(TOOLCHAIN_PATHS["ARMC6"], "fromelf")
00385 
00386     def parse_dependencies(self, dep_path):
00387         return mbedToolchain.parse_dependencies(self, dep_path)
00388 
00389     def is_not_supported_error(self, output):
00390         return "#error [NOT_SUPPORTED]" in output
00391 
00392     def parse_output(self, output):
00393         pass
00394 
00395     def get_config_option(self, config_header):
00396         return ["-include", config_header]
00397 
00398     def get_compile_options(self, defines, includes, for_asm=False):
00399         opts = ['-D%s' % d for d in defines]
00400         opts.extend(["-I%s" % i for i in includes])
00401         if for_asm:
00402             return ["--cpreproc",
00403                     "--cpreproc_opts=%s" % ",".join(self.flags['common'] + opts)]
00404         else:
00405             config_header = self.get_config_header()
00406             if config_header:
00407                 opts.extend(self.get_config_option(config_header))
00408             return opts
00409 
00410     @hook_tool
00411     def assemble(self, source, object, includes):
00412         cmd_pre = copy(self.asm)
00413         cmd_pre.extend(self.get_compile_options(
00414             self.get_symbols(True), includes, for_asm=True))
00415         cmd_pre.extend(["-o", object, source])
00416         return [self.hook.get_cmdline_assembler(cmd_pre)]
00417 
00418     @hook_tool
00419     def compile(self, cc, source, object, includes):
00420         cmd = copy(cc)
00421         cmd.extend(self.get_compile_options(self.get_symbols(), includes))
00422         cmd.extend(["-o", object, source])
00423         cmd = self.hook.get_cmdline_compiler(cmd)
00424         return [cmd]