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-2017 ARM Limited
00004 Portions Copyright (c) 2017-2018 Analog Devices, Inc.
00005 
00006 Licensed under the Apache License, Version 2.0 (the "License");
00007 you may not use this file except in compliance with the License.
00008 You may obtain a copy of the License at
00009 
00010     http://www.apache.org/licenses/LICENSE-2.0
00011 
00012 Unless required by applicable law or agreed to in writing, software
00013 distributed under the License is distributed on an "AS IS" BASIS,
00014 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015 See the License for the specific language governing permissions and
00016 limitations under the License.
00017 """
00018 
00019 import copy
00020 import os
00021 import sys
00022 import shutil
00023 import tempfile
00024 
00025 from subprocess import Popen, PIPE
00026 
00027 from tools.targets import TARGET_MAP
00028 from tools.export.exporters import Exporter
00029 
00030 from collections import namedtuple
00031 
00032 
00033 # Container for CCES option type and value
00034 Option = namedtuple('Option', ['type', 'value'])
00035 
00036 """
00037 Tuple of supported device names
00038 """
00039 SUPPORTED_DEVICES = ("ADuCM3027", "ADuCM3029", "ADuCM360", "ADuCM4050")
00040 
00041 class CCES (Exporter):
00042     """
00043     mbed exporter for Analog Devices' CrossCore Embedded Studio(TM)
00044     """
00045     NAME = 'CrossCore Embedded Studio'
00046     TOOLCHAIN = 'GCC_ARM'
00047 
00048     @classmethod
00049     def is_target_supported (cls, target_name):
00050         """Query support for a particular target
00051 
00052         Positional Arguments:
00053         target_name - the name of the target.
00054         """
00055         target = TARGET_MAP[target_name]
00056         return (cls.TOOLCHAIN in target.supported_toolchains) \
00057             and hasattr(target, "device_name") \
00058             and (target.device_name in SUPPORTED_DEVICES)
00059 
00060     @property
00061     def flags (self):
00062         """Returns a dictionary of toolchain flags.
00063         Keys of the dictionary are:
00064         cxx_flags    - c++ flags
00065         c_flags      - c flags
00066         ld_flags     - linker flags
00067         asm_flags    - assembler flags
00068         common_flags - common options
00069 
00070         Skip macros because headless tools handles them separately
00071         """
00072         flags = {key + "_flags": copy.deepcopy(value) for key, value \
00073                     in self.toolchain.flags.iteritems()}
00074         config_header = self.config_header_ref
00075         if config_header:
00076             config_header = "\\\"" + self.format_inc_path (config_header.name) \
00077                                 + "\\\""
00078             header_options = self.toolchain.get_config_option(config_header)
00079             flags['c_flags'] += header_options
00080             flags['cxx_flags'] += header_options
00081         return flags
00082 
00083     @staticmethod
00084     def format_path (path, prefix):
00085         """
00086         Formats the given source path relative to the project directory
00087         using the prefix
00088         """
00089         new_path = path
00090         if new_path.startswith("./"):
00091             new_path = new_path[2:]
00092         return prefix + new_path.replace("\\", "\\\\")
00093 
00094     @staticmethod
00095     def format_inc_path (path):
00096         """
00097         Formats the given include path relative to the project directory
00098         """
00099         return CCES.format_path(path, "${ProjDirPath}/../")
00100 
00101     @staticmethod
00102     def format_src_path (path):
00103         """
00104         Formats the given source path relative to the project directory
00105         """
00106         return CCES.format_path(path, "PARENT-1-PROJECT_LOC/")
00107 
00108     @staticmethod
00109     def clean_flags (container, flags_to_remove):
00110         """
00111         Some flags are handled by CCES already, so there's no need
00112         to include them twice.
00113         """
00114         for flag in flags_to_remove:
00115             if flag in container:
00116                 container.remove(flag)
00117 
00118     @staticmethod
00119     def parse_flags (flags, options, booleans):
00120         """
00121         Parse the values in `booleans`, insert them into the
00122         `options` dictionary and remove them from `flags`
00123         """
00124         for flag, flag_id in booleans.items():
00125             value = "false"
00126             if flag in flags:
00127                 value = "true"
00128                 flags.remove(flag)
00129             options[flag_id] = Option("baseId", value)
00130 
00131     @staticmethod
00132     def convert_common_options (prefix, options, flags):
00133         """
00134         Converts the common flags into CCES options and removes them
00135         from the flags list
00136         """
00137         # remove these flags without converting to option
00138         # since they are added by CCES
00139         remove = ["-c"]
00140         CCES.clean_flags(flags, remove)
00141 
00142         value = prefix + "option.dwarfversion.enumerated.v2"
00143         for flag in flags:
00144             if flag.startswith("-gdwarf"):
00145                 value = prefix + "option.dwarfversion.enumerated.v" + flag[-1]
00146                 flags.remove(flag)
00147                 break
00148         option = Option("baseId", value)
00149         options[prefix + "option.dwarfversion"] = option
00150 
00151     @staticmethod
00152     def convert_assembler_options (flags):
00153         """
00154         Converts the assembler flags into CCES options and removes them
00155         from the flags list
00156         """
00157         options = {}
00158 
00159         # remove these flags without converting to option
00160         # since they are added by CCES
00161         remove = ["-x", "assembler-with-cpp"]
00162         CCES.clean_flags(flags, remove)
00163 
00164         booleans = {"-v": "arm.assembler.option.verbose",
00165                     "-g": "arm.assembler.option.debuginfo"}
00166 
00167         CCES.parse_flags(flags, options, booleans)
00168 
00169         CCES.convert_common_options("arm.assembler.", options, flags)
00170 
00171         return options
00172 
00173     @staticmethod
00174     def convert_compiler_options (flags):
00175         """
00176         Converts the compiler flags into CCES options and removes them
00177         from the flags list
00178         """
00179         options = {}
00180 
00181         enable_optimization = "true"
00182         value = "arm.base.compiler.option.optimization.og"
00183         for flag in flags:
00184             if flag.startswith("-O"):
00185                 value = "arm.base.compiler.option.optimization.o" + flag[2:]
00186                 if flag[2:] == "0":
00187                     enable_optimization = "false"
00188                 flags.remove(flag)
00189                 break
00190         option = Option("baseId", value)
00191         options["arm.base.compiler.option.optimization"] = option
00192 
00193         option = Option("baseId", enable_optimization)
00194         options["arm.base.compiler.option.optimization.enable"] = option
00195 
00196         booleans = {"-g": "arm.base.compiler.option.debug",
00197                     "-save-temps": \
00198                         "arm.base.compiler.option.savetemps",
00199                     "-ffunction-sections": \
00200                         "arm.c.compiler.option.elimination.code",
00201                     "-fdata-sections": \
00202                         "arm.c.compiler.option.elimination.data",
00203                     "-pedantic": "arm.base.compiler.option.pedantic",
00204                     "-pedantic-errors": \
00205                         "arm.base.compiler.option.pedanticerrors",
00206                     "-w": "arm.base.compiler.option.inhibitallwarnings",
00207                     "-Wall": "arm.base.compiler.option.allwarnings",
00208                     "-Wextra": "arm.base.compiler.option.extrawarnings",
00209                     "-Werror": "arm.base.compiler.option.warningaserror",
00210                     "-Wconversion": \
00211                         "arm.base.compiler.option.conversionwarning"}
00212 
00213         CCES.parse_flags(flags, options, booleans)
00214 
00215         CCES.convert_common_options("arm.base.compiler.", options, flags)
00216 
00217         return options
00218 
00219     @staticmethod
00220     def convert_linker_options (flags):
00221         """
00222         Converts the linker flags into CCES options and removes them
00223         from the flags list
00224         """
00225         options = {}
00226 
00227         booleans = {"-nostartfiles": "arm.linker.option.nostart",
00228                     "-nodefaultlibs": "arm.linker.option.nodeflibs",
00229                     "-nostdlib": "arm.linker.option.nostdlibs",
00230                     "-s": "arm.linker.option.strip",
00231                     "-Wl,--gc-sections": "arm.linker.option.elimination"}
00232 
00233         CCES.parse_flags(flags, options, booleans)
00234 
00235         return options
00236 
00237     @staticmethod
00238     def get_cces_path (root):
00239         """
00240         Returns the path to the CCES executable
00241         """
00242         cces_path = None
00243 
00244         if sys.platform == 'win32' or sys.platform == 'cygwin':
00245             cces_path = os.path.join(root, "Eclipse", "ccesc.exe")
00246         elif sys.platform.startswith('linux'):
00247             cces_path = os.path.join(root, "Eclipse", "cces")
00248         elif sys.platform == 'darwin':
00249             cces_path = os.path.join(root, "MacOS", "cces")
00250         else:
00251             print("Unsupported operating system '%s'" % sys.platform)
00252             return None
00253 
00254         return cces_path
00255 
00256     @staticmethod
00257     def get_project_create_command (cces_path, workspace, project_name):
00258         """
00259         Generate the headless tools projectcreate command string
00260         with the given parameters
00261         """
00262         cmd = [
00263             "\"%s\"" % cces_path,
00264             "-nosplash",
00265             "-consoleLog",
00266             "-application com.analog.crosscore.headlesstools",
00267             "-data", workspace,
00268             "-project", project_name,
00269             "-command projectcreate",
00270             "-input-file", "cces.json"
00271         ]
00272         return ' '.join(cmd)
00273 
00274     @staticmethod
00275     def get_project_build_command (cces_path, workspace, project_name):
00276         """
00277         Generate the headless tools build command string
00278         with the given parameters
00279         """
00280         cmd = [
00281             "\"%s\"" % cces_path,
00282             "-nosplash",
00283             "-consoleLog",
00284             "-application com.analog.crosscore.headlesstools",
00285             "-data", workspace,
00286             "-project", project_name,
00287             "-cleanBuild all"
00288         ]
00289         return ' '.join(cmd)
00290 
00291     # override
00292     def generate (self):
00293         """
00294         Generate the CCES project files using headless builder.
00295         """
00296 
00297         self.resources.win_to_unix()
00298 
00299         asm_defines = self.toolchain.get_symbols(True)
00300         c_defines = self.toolchain.get_symbols()
00301 
00302         include_dirs = [self.format_inc_path (d) for d \
00303                         in self.resources.inc_dirs if d]
00304 
00305         srcs = self.resources.s_sources + \
00306                 self.resources.c_sources + \
00307                 self.resources.cpp_sources + \
00308                 self.resources.headers
00309 
00310         srcs_dict = {}
00311         for src in srcs:
00312             srcs_dict[src] = self.format_src_path (src)
00313 
00314         ld_script = self.format_inc_path (self.resources.linker_script)
00315 
00316         asm_flags = self.flags ['asm_flags']
00317         c_flags = self.flags ['c_flags'] + self.flags ['common_flags']
00318         cxx_flags = self.flags ['cxx_flags'] + self.flags ['common_flags']
00319 
00320         libs = []
00321         for libpath in self.libraries:
00322             lib = os.path.splitext(os.path.basename(libpath))[0]
00323             libs.append(lib[3:]) # skip 'lib' prefix
00324 
00325         ld_flags = self.flags ['ld_flags'] + ["-l" + lib for lib \
00326                     in self.toolchain.sys_libs]
00327 
00328         proc = self.toolchain.target.device_name
00329         cpu = self.toolchain.target.core.lower()
00330         fpu = None
00331         float_abi = None
00332 
00333         # parse toolchain CPU flags
00334         for flag in self.toolchain.cpu:
00335             if flag.startswith("-mcpu="):
00336                 cpu = flag[len("-mcpu="):]
00337             elif flag.startswith("-mfpu="):
00338                 fpu = flag[len("-mfpu="):]
00339             elif flag.startswith("-mfloat-abi="):
00340                 float_abi = flag[len("-mfloat-abi="):]
00341 
00342         # remove toolchain CPU flags. We'll handle them separately
00343         # in the generated .json file
00344         self.clean_flags (c_flags, self.toolchain.cpu)
00345         self.clean_flags (cxx_flags, self.toolchain.cpu)
00346         self.clean_flags (ld_flags, self.toolchain.cpu)
00347 
00348         ld_opts = self.convert_linker_options (ld_flags)
00349         asm_opts = self.convert_assembler_options (asm_flags)
00350         c_opts = self.convert_compiler_options (c_flags)
00351         cxx_opts = self.convert_compiler_options (cxx_flags)
00352 
00353         project = "cces"
00354         json = "cces.json"
00355         local_location = project
00356 
00357         jinja_ctx = {
00358             'project' : self.project_name,
00359             'cpu' : cpu,
00360             'proc' : proc,
00361             'family' : "ARM",
00362             'asm_defines' : asm_defines,
00363             'c_defines' : c_defines,
00364             'fpu' : fpu,
00365             'float_abi' : float_abi,
00366             'ld_script' : ld_script,
00367             'local_location' : local_location,
00368             'srcs': srcs_dict,
00369             'include_dirs' : include_dirs,
00370             'ld_opts' : ld_opts,
00371             'ld_flags' : ld_flags,
00372             'asm_opts' : asm_opts,
00373             'asm_flags' : asm_flags,
00374             'c_opts' : c_opts,
00375             'c_flags' : c_flags,
00376             'cxx_opts' : cxx_opts,
00377             'cxx_flags' : cxx_flags,
00378         }
00379 
00380         self.gen_file('cces/cces.json.tmpl', jinja_ctx,
00381                       json, trim_blocks=True, lstrip_blocks=True)
00382 
00383         # generate a readme on how to create the CCES project
00384         # using the generated .json file
00385 
00386         cces_paths = {
00387             "Windows" : "%CCES_HOME%\\Eclipse\\ccesc.exe",
00388             "Linux" : "${CCES_HOME}/Eclipse/cces",
00389             "MacOS" : "${CCES_HOME}/MacOS/cces"
00390         }
00391 
00392         commands = {"create":{}, "build":{}}
00393         for operating_system, path in cces_paths.items():
00394             commands["create"][operating_system] = \
00395                                 CCES.get_project_create_command(path, \
00396                                 "WORKSPACE", project)
00397             commands["build"][operating_system] = \
00398                                 CCES.get_project_build_command(path, \
00399                                 "WORKSPACE", project)
00400 
00401         jinja_ctx = {
00402             'commands' : commands
00403         }
00404 
00405         self.gen_file('cces/README.md.tmpl', jinja_ctx, "README.md")
00406 
00407         print("CCES files generated.")
00408 
00409 
00410     @staticmethod
00411     def clean(_):
00412         os.remove('cces.json')
00413         os.remove('README.md')
00414 
00415     @staticmethod
00416     def build (project_name, log_name='build_log.txt', cleanup=True):
00417         """
00418         Build the generated CCES project using headless builder.
00419         """
00420         # create the project by importing .json file using CCES headless builder
00421         cces_home = os.getenv("CCES_HOME")
00422         if cces_home is None:
00423             print("Failed to build project: " + \
00424                 "'CCES_HOME' environment variable not defined.")
00425             return -1
00426 
00427         cces_path = CCES.get_cces_path(cces_home)
00428         if cces_path is None:
00429             return -1
00430 
00431         workspace = tempfile.mkdtemp()
00432 
00433         cmd = CCES.get_project_create_command(cces_path, workspace, \
00434                 project_name)
00435         print(cmd)
00436         process = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
00437         out, err = process.communicate()
00438         ret_code = process.returncode
00439 
00440         # cleanup workspace
00441         if os.path.exists(workspace):
00442             shutil.rmtree(workspace, True)
00443             CCES.clean(project_name)
00444 
00445         # check return code for failure
00446         if ret_code != 0:
00447             for line in out.split("\n"):
00448                 print(line)
00449             for line in err.split("\n"):
00450                 print(line)
00451 
00452             print("Failed to create project. Return code: %d" % ret_code)
00453             return -1
00454 
00455         # build the project
00456         workspace = tempfile.mkdtemp()
00457 
00458         cmd = CCES.get_project_build_command(cces_path, workspace, project_name)
00459         print(cmd)
00460         process = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
00461         out, err = process.communicate()
00462         ret_code = process.returncode
00463 
00464         if log_name:
00465             with open(log_name, 'w+') as log_file:
00466                 log_file.write(out)
00467                 log_file.write(err)
00468                 if ret_code != 0:
00469                     log_file.write("Failed to build project. Return code: %d"\
00470                                     % ret_code)
00471 
00472         # cleanup workspace
00473         if os.path.exists(workspace):
00474             shutil.rmtree(workspace)
00475 
00476         # check return code for failure
00477         if ret_code == 0:
00478             return 0
00479 
00480         for line in out.split("\n"):
00481             print(line)
00482         for line in err.split("\n"):
00483             print(line)
00484 
00485         print("Failed to build project. Return code: %d" % ret_code)
00486         return -1