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