mirror of https://github.com/ARMmbed/mbed-os.git
487 lines
16 KiB
Python
487 lines
16 KiB
Python
"""
|
|
mbed SDK
|
|
Copyright (c) 2011-2017 ARM Limited
|
|
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.
|
|
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
|
|
|
|
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)
|
|
"""
|
|
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) \
|
|
and hasattr(target, "device_name") \
|
|
and (target.device_name in SUPPORTED_DEVICES)
|
|
|
|
@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
|
|
"""
|
|
flags = {key + "_flags": copy.deepcopy(value) for key, value \
|
|
in self.toolchain.flags.iteritems()}
|
|
config_header = self.config_header_ref
|
|
if config_header:
|
|
config_header = "\\\"" + self.format_inc_path(config_header.name) \
|
|
+ "\\\""
|
|
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, prefix):
|
|
"""
|
|
Formats the given source path relative to the project directory
|
|
using the prefix
|
|
"""
|
|
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_to_remove):
|
|
"""
|
|
Some flags are handled by CCES already, so there's no need
|
|
to include them twice.
|
|
"""
|
|
for flag in flags_to_remove:
|
|
if flag in container:
|
|
container.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):
|
|
"""
|
|
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"]
|
|
CCES.clean_flags(flags, remove)
|
|
|
|
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"]
|
|
CCES.clean_flags(flags, remove)
|
|
|
|
booleans = {"-v": "arm.assembler.option.verbose",
|
|
"-g": "arm.assembler.option.debuginfo"}
|
|
|
|
CCES.parse_flags(flags, options, booleans)
|
|
|
|
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"}
|
|
|
|
CCES.parse_flags(flags, options, booleans)
|
|
|
|
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"}
|
|
|
|
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):
|
|
"""
|
|
Generate the CCES project files using headless builder.
|
|
"""
|
|
|
|
self.resources.win_to_unix()
|
|
|
|
asm_defines = self.toolchain.get_symbols(True)
|
|
c_defines = self.toolchain.get_symbols()
|
|
|
|
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 + \
|
|
self.resources.cpp_sources + \
|
|
self.resources.headers
|
|
|
|
srcs_dict = {}
|
|
for src in srcs:
|
|
srcs_dict[src] = self.format_src_path(src)
|
|
|
|
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']
|
|
cxx_flags = self.flags['cxx_flags'] + self.flags['common_flags']
|
|
|
|
libs = []
|
|
for libpath in self.libraries:
|
|
lib = os.path.splitext(os.path.basename(libpath))[0]
|
|
libs.append(lib[3:]) # skip 'lib' prefix
|
|
|
|
ld_flags = self.flags['ld_flags'] + ["-l" + lib for lib \
|
|
in self.toolchain.sys_libs]
|
|
|
|
proc = self.toolchain.target.device_name
|
|
cpu = self.toolchain.target.core.lower()
|
|
fpu = None
|
|
float_abi = None
|
|
|
|
# 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="):]
|
|
|
|
# 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)
|
|
|
|
project = "cces"
|
|
json = "cces.json"
|
|
local_location = 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)
|
|
|
|
# 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 = {
|
|
'commands' : commands
|
|
}
|
|
|
|
self.gen_file('cces/README.md.tmpl', jinja_ctx, "README.md")
|
|
|
|
print("CCES files generated.")
|
|
|
|
|
|
@staticmethod
|
|
def clean(_):
|
|
os.remove('cces.json')
|
|
os.remove('README.md')
|
|
|
|
@staticmethod
|
|
def build(project_name, log_name='build_log.txt', cleanup=True):
|
|
"""
|
|
Build the generated CCES project using headless builder.
|
|
"""
|
|
# 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:
|
|
return -1
|
|
|
|
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)
|
|
CCES.clean(project_name)
|
|
|
|
# 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
|
|
|
|
# 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 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 ret_code == 0:
|
|
return 0
|
|
|
|
for line in out.split("\n"):
|
|
print(line)
|
|
for line in err.split("\n"):
|
|
print(line)
|
|
|
|
print("Failed to build project. Return code: %d" % ret_code)
|
|
return -1
|