From 60b22dba19acc26d7548f6f7e89061b8a278072c Mon Sep 17 00:00:00 2001 From: Mike Harrington Date: Mon, 27 Nov 2017 13:43:19 -0500 Subject: [PATCH 1/3] Added exporter for Analog Devices' CrossCore Embedded Studio --- tools/export/__init__.py | 5 +- tools/export/cces/__init__.py | 418 +++++++++++++++++++++++++++++++ tools/export/cces/cces.json.tmpl | 313 +++++++++++++++++++++++ 3 files changed, 734 insertions(+), 2 deletions(-) create mode 100644 tools/export/cces/__init__.py create mode 100644 tools/export/cces/cces.json.tmpl diff --git a/tools/export/__init__.py b/tools/export/__init__.py index 060aea787f..0654ab72ad 100644 --- a/tools/export/__init__.py +++ b/tools/export/__init__.py @@ -30,7 +30,7 @@ from ..toolchains import Resources from ..targets import TARGET_NAMES from . import (lpcxpresso, ds5_5, iar, makefile, embitz, coide, kds, simplicity, atmelstudio, mcuxpresso, sw4stm32, e2studio, zip, cmsis, uvision, - cdt, vscode, gnuarmeclipse, qtcreator, cmake, nb) + cdt, vscode, gnuarmeclipse, qtcreator, cmake, nb, cces) EXPORTERS = { u'uvision5': uvision.Uvision, @@ -60,7 +60,8 @@ EXPORTERS = { u'vscode_gcc_arm' : vscode.VSCodeGcc, u'vscode_iar' : vscode.VSCodeIAR, u'vscode_armc5' : vscode.VSCodeArmc5, - u'cmake_gcc_arm': cmake.GccArm + u'cmake_gcc_arm': cmake.GccArm, + u'cces' : cces.CCES } ERROR_MESSAGE_UNSUPPORTED_TOOLCHAIN = """ diff --git a/tools/export/cces/__init__.py b/tools/export/cces/__init__.py new file mode 100644 index 0000000000..04e7ddaeda --- /dev/null +++ b/tools/export/cces/__init__.py @@ -0,0 +1,418 @@ +""" +mbed SDK +Copyright (c) 2011-2017 ARM Limited +Portions Copyright (c) 2017 Analog Devices, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import copy +import os +import sys +import shutil +import tempfile + +from subprocess import Popen, PIPE + +from tools.targets import TARGET_MAP +from tools.export.exporters import Exporter + +class Option: + """ + Container for CCES option type and value + """ + def __init__(self, opt_type, value): + self.type = opt_type + self.value = value + + +class CCES(Exporter): + """ + mbed exporter for Analog Devices' CrossCore Embedded Studio(TM) + """ + NAME = 'CrossCore Embedded Studio' + TOOLCHAIN = 'GCC_ARM' + + @classmethod + def is_target_supported(cls, target_name): + """Query support for a particular target + + Positional Arguments: + target_name - the name of the target. + """ + target = TARGET_MAP[target_name] + return cls.TOOLCHAIN in target.supported_toolchains + + @property + def flags(self): + """Returns a dictionary of toolchain flags. + Keys of the dictionary are: + cxx_flags - c++ flags + c_flags - c flags + ld_flags - linker flags + asm_flags - assembler flags + common_flags - common options + + Skip macros because headless tools handles them separately + """ + config_header = self.toolchain.get_config_header() + flags = {key + "_flags": copy.deepcopy(value) for key, value + in self.toolchain.flags.iteritems()} + if config_header: + config_header = os.path.relpath(config_header, + self.resources.file_basepath[config_header]) + config_header = "\\\"" + self.format_path(config_header) + "\\\"" + header_options = self.toolchain.get_config_option(config_header) + flags['c_flags'] += header_options + flags['cxx_flags'] += header_options + return flags + + @staticmethod + def format_path(path): + """ + Formats the given path relative to the project directory + """ + return os.path.abspath(path).replace("\\", "\\\\") + + @staticmethod + def clean_flags(container, flags): + """ + Some flags are handled by CCES already, so there's no need + to include them twice. + """ + for flag in container: + if flag in flags: + flags.remove(flag) + + @staticmethod + def convert_common_options(prefix, options, flags): + """ + Converts the common flags into CCES options and removes them + from the flags list + """ + # remove these flags without converting to option + # since they are added by CCES + remove = [ "-c" ] + for flag in remove: + if flag in flags: + flags.remove(flag) + + value = prefix + "option.dwarfversion.enumerated.v2" + for flag in flags: + if flag.startswith("-gdwarf"): + value = prefix + "option.dwarfversion.enumerated.v" + flag[-1] + flags.remove(flag) + break + option = Option("baseId", value) + options[prefix + "option.dwarfversion"] = option + + @staticmethod + def convert_assembler_options(flags): + """ + Converts the assembler flags into CCES options and removes them + from the flags list + """ + options = {} + + # remove these flags without converting to option + # since they are added by CCES + remove = [ "-x", "assembler-with-cpp" ] + for flag in remove: + if flag in flags: + flags.remove(flag) + + booleans = {"-v": "arm.assembler.option.verbose", + "-g": "arm.assembler.option.debuginfo"} + + for flag in booleans: + value = "false" + if flag in flags: + value = "true" + flags.remove(flag) + option = Option("baseId", value) + options[booleans[flag]] = option + + CCES.convert_common_options("arm.assembler.", options, flags) + + return options + + @staticmethod + def convert_compiler_options(flags): + """ + Converts the compiler flags into CCES options and removes them + from the flags list + """ + options = {} + + enable_optimization = "true" + value = "arm.base.compiler.option.optimization.og" + for flag in flags: + if flag.startswith("-O"): + value = "arm.base.compiler.option.optimization.o" + flag[2:] + if flag[2:] == "0": + enable_optimization = "false" + flags.remove(flag) + break + option = Option("baseId", value) + options["arm.base.compiler.option.optimization"] = option + + option = Option("baseId", enable_optimization) + options["arm.base.compiler.option.optimization.enable"] = option + + booleans = {"-g": "arm.base.compiler.option.debug", + "-save-temps": "arm.base.compiler.option.savetemps", + "-ffunction-sections": "arm.c.compiler.option.elimination.code", + "-fdata-sections": "arm.c.compiler.option.elimination.data", + "-pedantic": "arm.base.compiler.option.pedantic", + "-pedantic-errors": "arm.base.compiler.option.pedanticerrors", + "-w": "arm.base.compiler.option.inhibitallwarnings", + "-Wall": "arm.base.compiler.option.allwarnings", + "-Wextra": "arm.base.compiler.option.extrawarnings", + "-Werror": "arm.base.compiler.option.warningaserror", + "-Wconversion": "arm.base.compiler.option.conversionwarning"} + + for flag in booleans: + value = "false" + if flag in flags: + value = "true" + flags.remove(flag) + option = Option("baseId", value) + options[booleans[flag]] = option + + CCES.convert_common_options("arm.base.compiler.", options, flags) + + return options + + @staticmethod + def convert_linker_options(flags): + """ + Converts the linker flags into CCES options and removes them + from the flags list + """ + options = {} + + booleans = {"-nostartfiles": "arm.linker.option.nostart", + "-nodefaultlibs": "arm.linker.option.nodeflibs", + "-nostdlib": "arm.linker.option.nostdlibs", + "-s": "arm.linker.option.strip", + "-Wl,--gc-sections": "arm.linker.option.elimination"} + + for flag in booleans: + value = "false" + if flag in flags: + value = "true" + flags.remove(flag) + option = Option("baseId", value) + options[booleans[flag]] = option + + return options + + # override + def generate(self): + """ + Generate the CCES project files using headless builder. + """ + + self.resources.win_to_unix() + + t = TARGET_MAP[self.target] + + proc = t.device_name + fpu = None + float_abi = None + + if t.core == "Cortex-M4F": + cpu = "cortex-m4" + fpu = "fpv4-sp-d16" + float_abi = "softfp" + else: + cpu = t.core.lower() + + asm_defines = self.toolchain.get_symbols(True) + c_defines = self.toolchain.get_symbols() + + include_dirs = [self.format_path(d) for d in self.resources.inc_dirs if d] + + srcs = self.resources.s_sources + \ + self.resources.c_sources + \ + self.resources.cpp_sources + \ + self.resources.headers + + srcs_dict = {} + for src in srcs: + srcs_dict[src] = self.format_path(src) + + ld_script = self.format_path(self.resources.linker_script) + + asm_flags = self.flags['asm_flags'] + c_flags = self.flags['c_flags'] + self.flags['common_flags'] + cxx_flags = self.flags['cxx_flags'] + self.flags['common_flags'] + + libs = [] + for libpath in self.resources.libraries: + lib, ext = os.path.splitext(os.path.basename(libpath)) + libs.append(lib[3:]) # skip 'lib' prefix + + system_libs = [ + '-lstdc++', '-lsupc++', '-lc', '-lgcc' + ] + + ld_flags = self.flags['ld_flags'] + system_libs + + flags = ["-mcpu="+cpu, "-mthumb"] + if fpu is not None: + flags.append("-mfpu="+fpu) + flags.append("-mfloat-abi="+float_abi) + + self.clean_flags(c_flags, flags) + self.clean_flags(cxx_flags, flags) + self.clean_flags(ld_flags, flags) + + ld_opts = self.convert_linker_options(ld_flags) + asm_opts = self.convert_assembler_options(asm_flags) + c_opts = self.convert_compiler_options(c_flags) + cxx_opts = self.convert_compiler_options(cxx_flags) + + workspace = tempfile.mkdtemp() + project = "cces" + json = "cces.json" + local_location = self.format_path(project) + + jinja_ctx = { + 'project' : self.project_name, + 'cpu' : cpu, + 'proc' : proc, + 'family' : "ARM", + 'asm_defines' : asm_defines, + 'c_defines' : c_defines, + 'fpu' : fpu, + 'float_abi' : float_abi, + 'ld_script' : ld_script, + 'local_location' : local_location, + 'srcs': srcs_dict, + 'include_dirs' : include_dirs, + 'ld_opts' : ld_opts, + 'ld_flags' : ld_flags, + 'asm_opts' : asm_opts, + 'asm_flags' : asm_flags, + 'c_opts' : c_opts, + 'c_flags' : c_flags, + 'cxx_opts' : cxx_opts, + 'cxx_flags' : cxx_flags, + } + + self.gen_file('cces/cces.json.tmpl', jinja_ctx, + json, trim_blocks=True, lstrip_blocks=True) + + # import .json file using CCES headless builder + cces_path = os.getenv("CCES_HOME") + if cces_path is None: + print "Failed to export project: 'CCES_HOME' environment variable not defined." + return -1 + + if sys.platform == 'win32' or sys.platform == 'cygwin': + cces_path = os.path.join(cces_path, "Eclipse", "ccesc.exe") + elif sys.platform.startswith('linux'): + cces_path = os.path.join(cces_path, "Eclipse", "cces") + elif sys.platform == 'darwin': + cces_path = os.path.join(cces_path, "MacOS", "cces") + else: + print "Unsupported operating system '%s'" % sys.platform + return -1 + + cmd = [ + "\"%s\"" % cces_path, + "-nosplash", + "-consoleLog", + "-application com.analog.crosscore.headlesstools", + "-data", workspace, + "-project", os.path.join(self.export_dir, project), + "-command projectcreate", + "-input-file", os.path.join(self.export_dir, json) + ] + print ' '.join(cmd) + p = Popen(' '.join(cmd), shell=True, stdout=PIPE, stderr=PIPE) + out, err = p.communicate() + rc = p.returncode + + # cleanup workspace + if os.path.exists(workspace): + shutil.rmtree(workspace) + + # check return code for failure + if rc == 0: + return 0 + + for line in out.split("\n"): + print line + for line in err.split("\n"): + print line + + print "Failed to export project. Return code: %d" % rc + return -1 + + @staticmethod + def build(project_name, log_name='build_log.txt', cleanup=True): + """ + Build the generated CCES project using headless builder. + """ + workspace = tempfile.mkdtemp() + project = "cces" + cces_path = os.getenv("CCES_HOME") + if cces_path is None: + print "Failed to build project: 'CCES_HOME' environment variable not defined." + return -1 + + if sys.platform == 'win32' or sys.platform == 'cygwin': + cces_path = os.path.join(cces_path, "Eclipse", "ccesc.exe") + elif sys.platform.startswith('linux'): + cces_path = os.path.join(cces_path, "Eclipse", "cces") + elif sys.platform == 'darwin': + cces_path = os.path.join(cces_path, "MacOS", "cces") + else: + print "Unsupported operating system '%s'" % sys.platform + return -1 + + cmd = [ + "\"%s\"" % cces_path, + "-nosplash", + "-consoleLog", + "-application com.analog.crosscore.headlesstools", + "-data", workspace, + "-project", project, + "-cleanBuild all" + ] + p = Popen(' '.join(cmd), shell=True, stdout=PIPE, stderr=PIPE) + out, err = p.communicate() + rc = p.returncode + + if log_name: + with open(log_name, 'w+') as f: + f.write(out) + f.write(err) + if rc != 0: + f.write("Failed to build project. Return code: %d" % rc) + + # cleanup workspace + if os.path.exists(workspace): + shutil.rmtree(workspace) + + # check return code for failure + if rc == 0: + return 0 + + print out + print err + + print "Failed to build project. Return code: %d" % rc + return -1 diff --git a/tools/export/cces/cces.json.tmpl b/tools/export/cces/cces.json.tmpl new file mode 100644 index 0000000000..11389fd41f --- /dev/null +++ b/tools/export/cces/cces.json.tmpl @@ -0,0 +1,313 @@ +{ + "project" : { + "schema" : "1.1", + "configuration" : { + {% for config in ["arm.toolchain.gcc.target.exe.release", "arm.toolchain.gcc.target.exe.debug"] %} + "{{ config }}" : { + "buildSteps" : { + "postbuild" : "", + "prebuild" : "", + "prebuilddes" : "", + "postbuilddes" : "" + }, + "tools" : { + "arm.toolchain.gcc.assembler" : { + {% for opt in asm_opts %} + "{{ opt }}" : { + "type" : "{{ asm_opts[opt].type }}", + "value": "{{ asm_opts[opt].value }}" + }, + {% endfor %} + "arm.toolchain.gcc.assembler.option.instructionset" : { + "type" : "baseId", + "value" : "-mthumb" + }, + {% if float_abi %} + "-mfloat-abi=${value}" : { + "type" : "command", + "value" : "{{ float_abi }}" + }, + {% endif %} + "-mcpu=${value}" : { + "type" : "command", + "value" : "{{ cpu }}" + }, + "arm.assembler.option.assemblerswitch" : { + "type" : "baseId", + "value" : "true" + }, + "arm.assembler.option.additionaloptions" : { + "type" : "command", + "value" : [ + {% for flag in asm_flags %} + "{{ flag }}"{{ "," if not loop.last else "" }} + {% endfor %} + ] + }, + "-mproc=${value}" : { + "type" : "command", + "value" : " {{ proc }}" + }, + "-D" : { + "type" : "command", + "value" : [ + {% for def in asm_defines %} + "{{ def }}"{{ "," if not loop.last else "" }} + {% endfor %} + ] + }, + {% if fpu %} + "-mfpu=${value}" : { + "type" : "command", + "value" : "{{ fpu }}" + }, + {% endif %} + "-I" : { + "type" : "command", + "value" : [ + {% for dir in include_dirs %} + "\"{{ dir }}\""{{ "," if not loop.last else "" }} + {% endfor %} + ] + } + }, + "arm.toolchain.gcc.cpp.linker" : { + {% for opt in ld_opts %} + "{{ opt }}" : { + "type" : "{{ ld_opts[opt].type }}", + "value": "{{ ld_opts[opt].value }}" + }, + {% endfor %} + "arm.toolchain.gcc.cpp.linker.option.instructionset" : { + "type" : "baseId", + "value" : "-mthumb" + }, + "arm.linker.option.additionaloptions" : { + "type" : "command", + "value" : [ + {% for flag in ld_flags %} + "{{ flag }}"{{ "," if not loop.last else "" }} + {% endfor %} + ] + }, + {% if float_abi %} + "-mfloat-abi=${value}" : { + "type" : "command", + "value" : "{{ float_abi }}" + }, + {% endif %} + "-T" : { + "type" : "command", + "value" : "{{ ld_script }}" + }, + "-mcpu=${value}" : { + "type" : "command", + "value" : "{{ cpu }}" + }, + "arm.linker.option.userlibs" : { + "type" : "baseId", + "value" : [ ] + }, + "arm.cpp.linker.option.shared" : { + "type" : "baseId", + "value" : "false" + }, + "arm.toolchain.gcc.cpp.linker.option.specs" : { + "type" : "baseId", + "value" : "arm.toolchain.gcc.c.linker.option.specs.nosys" + }, + "-mproc=${value}" : { + "type" : "command", + "value" : " {{ proc }}" + }, + "arm.c.linker.mathslib" : { + "type" : "baseId", + "value" : "true" + }, + {% if fpu %} + "-mfpu=${value}" : { + "type" : "command", + "value" : "{{ fpu }}" + }, + {% endif %} + "-L" : { + "type" : "command", + "value" : [ ] + }, + "-l" : { + "type" : "command", + "value" : [ ] + } + }, + "arm.toolchain.gcc.c.compiler" : { + {% for opt in c_opts %} + "{{ opt }}" : { + "type" : "{{ c_opts[opt].type }}", + "value": "{{ c_opts[opt].value }}" + }, + {% endfor %} + "-U" : { + "type" : "command", + "value" : [ ] + }, + "arm.base.compiler.option.additionaloptions" : { + "type" : "command", + "value" : [ + {% for flag in c_flags %} + "{{ flag }}"{{ "," if not loop.last else "" }} + {% endfor %} + ] + }, + "arm.toolchain.gcc.c.compiler.option.instructionset" : { + "type" : "baseId", + "value" : "-mthumb" + }, + "arm.base.compiler.option.compilerswitch.hide" : { + "type" : "baseId", + "value" : "-c" + }, + "arm.toolchain.cpp.compiler.option.coreid" : { + "type" : "baseId", + "value" : "0" + }, + {% if float_abi %} + "-mfloat-abi=${value}" : { + "type" : "command", + "value" : "{{ float_abi }}" + }, + {% endif %} + "-mcpu=${value}" : { + "type" : "command", + "value" : "{{ cpu }}" + }, + "-mproc=${value}" : { + "type" : "command", + "value" : " {{ proc }}" + }, + "-D" : { + "type" : "command", + "value" : [ + {% for def in c_defines %} + "{{ def }}"{{ "," if not loop.last else "" }} + {% endfor %} + ] + }, + "arm.base.compiler.option.noadiinclude" : { + "type" : "baseId", + "value" : "false" + }, + {% if fpu %} + "-mfpu=${value}" : { + "type" : "command", + "value" : "{{ fpu }}" + }, + {% endif %} + "-I" : { + "type" : "command", + "value" : [ + {% for dir in include_dirs %} + "\"{{ dir }}\""{{ "," if not loop.last else "" }} + {% endfor %} + ] + } + }, + "arm.toolchain.gcc.cpp.compiler" : { + {% for opt in cxx_opts %} + "{{ opt }}" : { + "type" : "{{ cxx_opts[opt].type }}", + "value": "{{ cxx_opts[opt].value }}" + }, + {% endfor %} + "-U" : { + "type" : "command", + "value" : [ ] + }, + "arm.base.compiler.option.additionaloptions" : { + "type" : "command", + "value" : [ + {% for flag in cxx_flags %} + "{{ flag }}"{{ "," if not loop.last else "" }} + {% endfor %} + ] + }, + "arm.toolchain.gcc.cpp.compiler.option.instructionset" : { + "type" : "baseId", + "value" : "-mthumb" + }, + "arm.base.compiler.option.compilerswitch.hide" : { + "type" : "baseId", + "value" : "-c" + }, + "arm.toolchain.cpp.compiler.option.coreid" : { + "type" : "baseId", + "value" : "0" + }, + {% if float_abi %} + "-mfloat-abi=${value}" : { + "type" : "command", + "value" : "{{ float_abi }}" + }, + {% endif %} + "-mcpu=${value}" : { + "type" : "command", + "value" : "{{ cpu }}" + }, + "-mproc=${value}" : { + "type" : "command", + "value" : " {{ proc }}" + }, + "-D" : { + "type" : "command", + "value" : [ + {% for def in c_defines %} + "{{ def }}"{{ "," if not loop.last else "" }} + {% endfor %} + ] + }, + "arm.base.compiler.option.noadiinclude" : { + "type" : "baseId", + "value" : "false" + }, + {% if fpu %} + "-mfpu=${value}" : { + "type" : "command", + "value" : "{{ fpu }}" + }, + {% endif %} + "-I" : { + "type" : "command", + "value" : [ + {% for dir in include_dirs %} + "\"{{ dir }}\""{{ "," if not loop.last else "" }} + {% endfor %} + ] + } + } + } + }{{ "," if not loop.last else "" }} + {% endfor %} + }, + "srcFiles" : [ + {% for src in srcs %} + { + "path" : "{{ srcs[src] }}", + "location" : "{{ src }}", + "linked" : true + }{{ "," if not loop.last else "" }} + {% endfor %} + ], + "basicInfo" : { + "artifact" : "", + "name" : "{{ project }}", + "projectType" : "Executable", + "localLocation" : "{{ local_location }}", + "family" : "{{ family }}", + "toolChain" : "arm.gcc.toolchain", + "activecfg" : "Debug", + "language" : "C++", + {% if not fpu %} + "fpu" : "NO_FPU" + {% endif %} + } + } +} From d93bdafbf1ed92dc4ab4b6e79747cdc7370d7e36 Mon Sep 17 00:00:00 2001 From: Mike Harrington Date: Tue, 6 Feb 2018 10:50:10 -0500 Subject: [PATCH 2/3] Resolved pylint errors and made changes based on feedback. Added README describing how to use headless tools after exporting. --- tools/export/cces/README.md.tmpl | 13 ++ tools/export/cces/__init__.py | 355 ++++++++++++++++++------------- 2 files changed, 218 insertions(+), 150 deletions(-) create mode 100755 tools/export/cces/README.md.tmpl diff --git a/tools/export/cces/README.md.tmpl b/tools/export/cces/README.md.tmpl new file mode 100755 index 0000000000..92cd646186 --- /dev/null +++ b/tools/export/cces/README.md.tmpl @@ -0,0 +1,13 @@ +CrossCore Embedded Studio (CCES) can generate IDE projects from the command line using .json input files. + +Run the following headless tools command to create your CrossCore Embedded Studio project using the .json file generated by the ARM mbed exporter: +> {{ project_create_command }} + +where "WORKSPACE" is the path to the desired CCES workspace directory. + +Once the CrossCore Embedded Studio project is generated, you can import the project into the IDE for development and debugging. You can also use headless tools to build the project with the following command: +> {{ project_build_command }} + +where "WORKSPACE" is the path to the desired CCES workspace directory. + +For more information on how to use CrossCore Embedded Studio and headless tools, please see the CrossCore Embedded Studio Help. diff --git a/tools/export/cces/__init__.py b/tools/export/cces/__init__.py index 04e7ddaeda..db7f6a3771 100644 --- a/tools/export/cces/__init__.py +++ b/tools/export/cces/__init__.py @@ -1,7 +1,7 @@ """ mbed SDK Copyright (c) 2011-2017 ARM Limited -Portions Copyright (c) 2017 Analog Devices, Inc. +Portions Copyright (c) 2017-2018 Analog Devices, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -27,15 +27,17 @@ from subprocess import Popen, PIPE from tools.targets import TARGET_MAP from tools.export.exporters import Exporter -class Option: - """ - Container for CCES option type and value - """ - def __init__(self, opt_type, value): - self.type = opt_type - self.value = value +from collections import namedtuple +# Container for CCES option type and value +Option = namedtuple('Option', ['type', 'value']) + +""" +Tuple of supported device names +""" +SUPPORTED_DEVICES = ("ADuCM3027", "ADuCM3029", "ADuCM360", "ADuCM4050") + class CCES(Exporter): """ mbed exporter for Analog Devices' CrossCore Embedded Studio(TM) @@ -51,7 +53,9 @@ class CCES(Exporter): target_name - the name of the target. """ target = TARGET_MAP[target_name] - return cls.TOOLCHAIN in target.supported_toolchains + return (cls.TOOLCHAIN in target.supported_toolchains) \ + and hasattr(target, "device_name") \ + and (target.device_name in SUPPORTED_DEVICES) @property def flags(self): @@ -66,23 +70,42 @@ class CCES(Exporter): Skip macros because headless tools handles them separately """ config_header = self.toolchain.get_config_header() - flags = {key + "_flags": copy.deepcopy(value) for key, value + flags = {key + "_flags": copy.deepcopy(value) for key, value \ in self.toolchain.flags.iteritems()} if config_header: - config_header = os.path.relpath(config_header, + config_header = os.path.relpath(config_header, \ self.resources.file_basepath[config_header]) - config_header = "\\\"" + self.format_path(config_header) + "\\\"" + config_header = "\\\"" + self.format_inc_path(config_header) \ + + "\\\"" header_options = self.toolchain.get_config_option(config_header) flags['c_flags'] += header_options flags['cxx_flags'] += header_options return flags @staticmethod - def format_path(path): + def format_path(path, prefix): """ - Formats the given path relative to the project directory + Formats the given source path relative to the project directory + using the prefix """ - return os.path.abspath(path).replace("\\", "\\\\") + new_path = path + if new_path.startswith("./"): + new_path = new_path[2:] + return prefix + new_path.replace("\\", "\\\\") + + @staticmethod + def format_inc_path(path): + """ + Formats the given include path relative to the project directory + """ + return CCES.format_path(path, "${ProjDirPath}/../") + + @staticmethod + def format_src_path(path): + """ + Formats the given source path relative to the project directory + """ + return CCES.format_path(path, "PARENT-1-PROJECT_LOC/") @staticmethod def clean_flags(container, flags): @@ -94,6 +117,19 @@ class CCES(Exporter): if flag in flags: flags.remove(flag) + @staticmethod + def parse_flags(flags, options, booleans): + """ + Parse the values in `booleans`, insert them into the + `options` dictionary and remove them from `flags` + """ + for flag, flag_id in booleans.items(): + value = "false" + if flag in flags: + value = "true" + flags.remove(flag) + options[flag_id] = Option("baseId", value) + @staticmethod def convert_common_options(prefix, options, flags): """ @@ -102,7 +138,7 @@ class CCES(Exporter): """ # remove these flags without converting to option # since they are added by CCES - remove = [ "-c" ] + remove = ["-c"] for flag in remove: if flag in flags: flags.remove(flag) @@ -126,7 +162,7 @@ class CCES(Exporter): # remove these flags without converting to option # since they are added by CCES - remove = [ "-x", "assembler-with-cpp" ] + remove = ["-x", "assembler-with-cpp"] for flag in remove: if flag in flags: flags.remove(flag) @@ -134,13 +170,7 @@ class CCES(Exporter): booleans = {"-v": "arm.assembler.option.verbose", "-g": "arm.assembler.option.debuginfo"} - for flag in booleans: - value = "false" - if flag in flags: - value = "true" - flags.remove(flag) - option = Option("baseId", value) - options[booleans[flag]] = option + CCES.parse_flags(flags, options, booleans) CCES.convert_common_options("arm.assembler.", options, flags) @@ -152,7 +182,7 @@ class CCES(Exporter): Converts the compiler flags into CCES options and removes them from the flags list """ - options = {} + options = {} enable_optimization = "true" value = "arm.base.compiler.option.optimization.og" @@ -170,24 +200,23 @@ class CCES(Exporter): options["arm.base.compiler.option.optimization.enable"] = option booleans = {"-g": "arm.base.compiler.option.debug", - "-save-temps": "arm.base.compiler.option.savetemps", - "-ffunction-sections": "arm.c.compiler.option.elimination.code", - "-fdata-sections": "arm.c.compiler.option.elimination.data", + "-save-temps": \ + "arm.base.compiler.option.savetemps", + "-ffunction-sections": \ + "arm.c.compiler.option.elimination.code", + "-fdata-sections": \ + "arm.c.compiler.option.elimination.data", "-pedantic": "arm.base.compiler.option.pedantic", - "-pedantic-errors": "arm.base.compiler.option.pedanticerrors", + "-pedantic-errors": \ + "arm.base.compiler.option.pedanticerrors", "-w": "arm.base.compiler.option.inhibitallwarnings", "-Wall": "arm.base.compiler.option.allwarnings", "-Wextra": "arm.base.compiler.option.extrawarnings", "-Werror": "arm.base.compiler.option.warningaserror", - "-Wconversion": "arm.base.compiler.option.conversionwarning"} + "-Wconversion": \ + "arm.base.compiler.option.conversionwarning"} - for flag in booleans: - value = "false" - if flag in flags: - value = "true" - flags.remove(flag) - option = Option("baseId", value) - options[booleans[flag]] = option + CCES.parse_flags(flags, options, booleans) CCES.convert_common_options("arm.base.compiler.", options, flags) @@ -207,16 +236,64 @@ class CCES(Exporter): "-s": "arm.linker.option.strip", "-Wl,--gc-sections": "arm.linker.option.elimination"} - for flag in booleans: - value = "false" - if flag in flags: - value = "true" - flags.remove(flag) - option = Option("baseId", value) - options[booleans[flag]] = option + CCES.parse_flags(flags, options, booleans) return options + @staticmethod + def get_cces_path(root): + """ + Returns the path to the CCES executable + """ + cces_path = None + + if sys.platform == 'win32' or sys.platform == 'cygwin': + cces_path = os.path.join(root, "Eclipse", "ccesc.exe") + elif sys.platform.startswith('linux'): + cces_path = os.path.join(root, "Eclipse", "cces") + elif sys.platform == 'darwin': + cces_path = os.path.join(root, "MacOS", "cces") + else: + print("Unsupported operating system '%s'" % sys.platform) + return None + + return cces_path + + @staticmethod + def get_project_create_command(cces_path, workspace, project_name): + """ + Generate the headless tools projectcreate command string + with the given parameters + """ + cmd = [ + "\"%s\"" % cces_path, + "-nosplash", + "-consoleLog", + "-application com.analog.crosscore.headlesstools", + "-data", workspace, + "-project", project_name, + "-command projectcreate", + "-input-file", "cces.json" + ] + return ' '.join(cmd) + + @staticmethod + def get_project_build_command(cces_path, workspace, project_name): + """ + Generate the headless tools build command string + with the given parameters + """ + cmd = [ + "\"%s\"" % cces_path, + "-nosplash", + "-consoleLog", + "-application com.analog.crosscore.headlesstools", + "-data", workspace, + "-project", project_name, + "-cleanBuild all" + ] + return ' '.join(cmd) + # override def generate(self): """ @@ -225,23 +302,11 @@ class CCES(Exporter): self.resources.win_to_unix() - t = TARGET_MAP[self.target] - - proc = t.device_name - fpu = None - float_abi = None - - if t.core == "Cortex-M4F": - cpu = "cortex-m4" - fpu = "fpv4-sp-d16" - float_abi = "softfp" - else: - cpu = t.core.lower() - asm_defines = self.toolchain.get_symbols(True) c_defines = self.toolchain.get_symbols() - include_dirs = [self.format_path(d) for d in self.resources.inc_dirs if d] + include_dirs = [self.format_inc_path(d) for d \ + in self.resources.inc_dirs if d] srcs = self.resources.s_sources + \ self.resources.c_sources + \ @@ -250,9 +315,9 @@ class CCES(Exporter): srcs_dict = {} for src in srcs: - srcs_dict[src] = self.format_path(src) + srcs_dict[src] = self.format_src_path(src) - ld_script = self.format_path(self.resources.linker_script) + ld_script = self.format_inc_path(self.resources.linker_script) asm_flags = self.flags['asm_flags'] c_flags = self.flags['c_flags'] + self.flags['common_flags'] @@ -260,33 +325,40 @@ class CCES(Exporter): libs = [] for libpath in self.resources.libraries: - lib, ext = os.path.splitext(os.path.basename(libpath)) + lib = os.path.splitext(os.path.basename(libpath))[0] libs.append(lib[3:]) # skip 'lib' prefix - system_libs = [ - '-lstdc++', '-lsupc++', '-lc', '-lgcc' - ] + ld_flags = self.flags['ld_flags'] + ["-l" + lib for lib \ + in self.toolchain.sys_libs] - ld_flags = self.flags['ld_flags'] + system_libs + proc = self.toolchain.target.device_name + cpu = self.toolchain.target.core.lower() + fpu = None + float_abi = None - flags = ["-mcpu="+cpu, "-mthumb"] - if fpu is not None: - flags.append("-mfpu="+fpu) - flags.append("-mfloat-abi="+float_abi) + # parse toolchain CPU flags + for flag in self.toolchain.cpu: + if flag.startswith("-mcpu="): + cpu = flag[len("-mcpu="):] + elif flag.startswith("-mfpu="): + fpu = flag[len("-mfpu="):] + elif flag.startswith("-mfloat-abi="): + float_abi = flag[len("-mfloat-abi="):] - self.clean_flags(c_flags, flags) - self.clean_flags(cxx_flags, flags) - self.clean_flags(ld_flags, flags) + # remove toolchain CPU flags. We'll handle them separately + # in the generated .json file + self.clean_flags(c_flags, self.toolchain.cpu) + self.clean_flags(cxx_flags, self.toolchain.cpu) + self.clean_flags(ld_flags, self.toolchain.cpu) ld_opts = self.convert_linker_options(ld_flags) asm_opts = self.convert_assembler_options(asm_flags) c_opts = self.convert_compiler_options(c_flags) cxx_opts = self.convert_compiler_options(cxx_flags) - workspace = tempfile.mkdtemp() project = "cces" json = "cces.json" - local_location = self.format_path(project) + local_location = project jinja_ctx = { 'project' : self.project_name, @@ -314,105 +386,88 @@ class CCES(Exporter): self.gen_file('cces/cces.json.tmpl', jinja_ctx, json, trim_blocks=True, lstrip_blocks=True) - # import .json file using CCES headless builder - cces_path = os.getenv("CCES_HOME") - if cces_path is None: - print "Failed to export project: 'CCES_HOME' environment variable not defined." - return -1 + # generate a readme on how to create the CCES project + # using the generated .json file - if sys.platform == 'win32' or sys.platform == 'cygwin': - cces_path = os.path.join(cces_path, "Eclipse", "ccesc.exe") - elif sys.platform.startswith('linux'): - cces_path = os.path.join(cces_path, "Eclipse", "cces") - elif sys.platform == 'darwin': - cces_path = os.path.join(cces_path, "MacOS", "cces") - else: - print "Unsupported operating system '%s'" % sys.platform - return -1 + jinja_ctx = { + 'project_create_command' : CCES.get_project_create_command("cces", \ + "WORKSPACE", project), + 'project_build_command' : CCES.get_project_build_command("cces", \ + "WORKSPACE", project) + } - cmd = [ - "\"%s\"" % cces_path, - "-nosplash", - "-consoleLog", - "-application com.analog.crosscore.headlesstools", - "-data", workspace, - "-project", os.path.join(self.export_dir, project), - "-command projectcreate", - "-input-file", os.path.join(self.export_dir, json) - ] - print ' '.join(cmd) - p = Popen(' '.join(cmd), shell=True, stdout=PIPE, stderr=PIPE) - out, err = p.communicate() - rc = p.returncode + self.gen_file('cces/README.md.tmpl', jinja_ctx, "README.md") - # cleanup workspace - if os.path.exists(workspace): - shutil.rmtree(workspace) - - # check return code for failure - if rc == 0: - return 0 - - for line in out.split("\n"): - print line - for line in err.split("\n"): - print line - - print "Failed to export project. Return code: %d" % rc - return -1 + print("CCES files generated.") @staticmethod def build(project_name, log_name='build_log.txt', cleanup=True): """ Build the generated CCES project using headless builder. """ - workspace = tempfile.mkdtemp() - project = "cces" - cces_path = os.getenv("CCES_HOME") + # create the project by importing .json file using CCES headless builder + cces_home = os.getenv("CCES_HOME") + if cces_home is None: + print("Failed to build project: " + \ + "'CCES_HOME' environment variable not defined.") + return -1 + + cces_path = CCES.get_cces_path(cces_home) if cces_path is None: - print "Failed to build project: 'CCES_HOME' environment variable not defined." return -1 - if sys.platform == 'win32' or sys.platform == 'cygwin': - cces_path = os.path.join(cces_path, "Eclipse", "ccesc.exe") - elif sys.platform.startswith('linux'): - cces_path = os.path.join(cces_path, "Eclipse", "cces") - elif sys.platform == 'darwin': - cces_path = os.path.join(cces_path, "MacOS", "cces") - else: - print "Unsupported operating system '%s'" % sys.platform + workspace = tempfile.mkdtemp() + + cmd = CCES.get_project_create_command(cces_path, workspace, \ + project_name) + print(cmd) + process = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) + out, err = process.communicate() + ret_code = process.returncode + + # cleanup workspace + if os.path.exists(workspace): + shutil.rmtree(workspace, True) + + # check return code for failure + if ret_code != 0: + for line in out.split("\n"): + print(line) + for line in err.split("\n"): + print(line) + + print("Failed to create project. Return code: %d" % ret_code) return -1 - cmd = [ - "\"%s\"" % cces_path, - "-nosplash", - "-consoleLog", - "-application com.analog.crosscore.headlesstools", - "-data", workspace, - "-project", project, - "-cleanBuild all" - ] - p = Popen(' '.join(cmd), shell=True, stdout=PIPE, stderr=PIPE) - out, err = p.communicate() - rc = p.returncode + # build the project + workspace = tempfile.mkdtemp() + + cmd = CCES.get_project_build_command(cces_path, workspace, project_name) + print(cmd) + process = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) + out, err = process.communicate() + ret_code = process.returncode if log_name: - with open(log_name, 'w+') as f: - f.write(out) - f.write(err) - if rc != 0: - f.write("Failed to build project. Return code: %d" % rc) + with open(log_name, 'w+') as log_file: + log_file.write(out) + log_file.write(err) + if ret_code != 0: + log_file.write("Failed to build project. Return code: %d"\ + % ret_code) # cleanup workspace if os.path.exists(workspace): shutil.rmtree(workspace) # check return code for failure - if rc == 0: + if ret_code == 0: return 0 - print out - print err + for line in out.split("\n"): + print(line) + for line in err.split("\n"): + print(line) - print "Failed to build project. Return code: %d" % rc + print("Failed to build project. Return code: %d" % ret_code) return -1 From 8c87c2692ca438f9100ff9bf6cd476746aa41b84 Mon Sep 17 00:00:00 2001 From: Mike Harrington Date: Tue, 13 Feb 2018 14:56:54 -0500 Subject: [PATCH 3/3] Updated README.md to include instructions for different operating systems. Reduced duplicate code. --- tools/export/cces/README.md.tmpl | 24 ++++++++++++++++++------ tools/export/cces/__init__.py | 28 ++++++++++++++++++---------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/tools/export/cces/README.md.tmpl b/tools/export/cces/README.md.tmpl index 92cd646186..8a556debf3 100755 --- a/tools/export/cces/README.md.tmpl +++ b/tools/export/cces/README.md.tmpl @@ -1,13 +1,25 @@ -CrossCore Embedded Studio (CCES) can generate IDE projects from the command line using .json input files. +# Create and build CrossCore Embedded Studio projects +CrossCore Embedded Studio (CCES) can generate and build IDE projects from the command line using .json input files. + +## Create a new project Run the following headless tools command to create your CrossCore Embedded Studio project using the .json file generated by the ARM mbed exporter: -> {{ project_create_command }} +{% for operating_system, command in commands['create'].items() %} +### {{ operating_system }} +> {{ command }} -where "WORKSPACE" is the path to the desired CCES workspace directory. +{% endfor %} +where "CCES_HOME" is an environment variable pointing to the root CCES installation directory and "WORKSPACE" is the path to the desired CCES workspace directory. -Once the CrossCore Embedded Studio project is generated, you can import the project into the IDE for development and debugging. You can also use headless tools to build the project with the following command: -> {{ project_build_command }} +Once the CrossCore Embedded Studio project is generated, you can import the project into the IDE for development and debugging. -where "WORKSPACE" is the path to the desired CCES workspace directory. +## Build a project +Once created, you can use headless tools to build the project with the following command: +{% for operating_system, command in commands['build'].items() %} +### {{ operating_system }} +> {{ command }} + +{% endfor %} +where "CCES_HOME" is an environment variable pointing to the root CCES installation directory and "WORKSPACE" is the path to the desired CCES workspace directory. For more information on how to use CrossCore Embedded Studio and headless tools, please see the CrossCore Embedded Studio Help. diff --git a/tools/export/cces/__init__.py b/tools/export/cces/__init__.py index db7f6a3771..2d90fa93e8 100644 --- a/tools/export/cces/__init__.py +++ b/tools/export/cces/__init__.py @@ -139,9 +139,7 @@ class CCES(Exporter): # remove these flags without converting to option # since they are added by CCES remove = ["-c"] - for flag in remove: - if flag in flags: - flags.remove(flag) + CCES.clean_flags(flags, remove) value = prefix + "option.dwarfversion.enumerated.v2" for flag in flags: @@ -163,9 +161,7 @@ class CCES(Exporter): # remove these flags without converting to option # since they are added by CCES remove = ["-x", "assembler-with-cpp"] - for flag in remove: - if flag in flags: - flags.remove(flag) + CCES.clean_flags(flags, remove) booleans = {"-v": "arm.assembler.option.verbose", "-g": "arm.assembler.option.debuginfo"} @@ -389,11 +385,23 @@ class CCES(Exporter): # generate a readme on how to create the CCES project # using the generated .json file + cces_paths = { + "Windows" : "%CCES_HOME%\\Eclipse\\ccesc.exe", + "Linux" : "${CCES_HOME}/Eclipse/cces", + "MacOS" : "${CCES_HOME}/MacOS/cces" + } + + commands = {"create":{}, "build":{}} + for operating_system, path in cces_paths.items(): + commands["create"][operating_system] = \ + CCES.get_project_create_command(path, \ + "WORKSPACE", project) + commands["build"][operating_system] = \ + CCES.get_project_build_command(path, \ + "WORKSPACE", project) + jinja_ctx = { - 'project_create_command' : CCES.get_project_create_command("cces", \ - "WORKSPACE", project), - 'project_build_command' : CCES.get_project_build_command("cces", \ - "WORKSPACE", project) + 'commands' : commands } self.gen_file('cces/README.md.tmpl', jinja_ctx, "README.md")