diff --git a/tools/export/mcuxpresso/.cproject.tmpl b/tools/export/mcuxpresso/.cproject.tmpl new file mode 100644 index 0000000000..8f88a761f2 --- /dev/null +++ b/tools/export/mcuxpresso/.cproject.tmpl @@ -0,0 +1,140 @@ + + + + {% for cfg_key in options %} + {% set opts = options[cfg_key] %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% endfor %} + + + + + + + + + + + diff --git a/tools/export/mcuxpresso/.project.tmpl b/tools/export/mcuxpresso/.project.tmpl new file mode 100644 index 0000000000..cc88934389 --- /dev/null +++ b/tools/export/mcuxpresso/.project.tmpl @@ -0,0 +1,28 @@ + + + + {{name}} + This file was automagically generated by mbed.org. For more information, see http://mbed.org/handbook/Exporting-To-GNU-ARM-Eclipse + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/tools/export/mcuxpresso/__init__.py b/tools/export/mcuxpresso/__init__.py index d31700d2ca..7ee0bad49e 100644 --- a/tools/export/mcuxpresso/__init__.py +++ b/tools/export/mcuxpresso/__init__.py @@ -1,6 +1,6 @@ """ mbed SDK -Copyright (c) 2011-2016 ARM Limited +Copyright (c) 2011-2017 ARM Limited Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,40 +13,955 @@ 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. + +Title: GNU ARM Eclipse (http://gnuarmeclipse.github.io) exporter. + +Description: Creates a managed build project that can be imported by +the GNU ARM Eclipse plug-ins. + +Author: Liviu Ionescu """ -from os.path import splitext, basename -from tools.export.gnuarmeclipse import GNUARMEclipse +import os +import copy +import tempfile +import shutil +import copy + +from subprocess import call, Popen, PIPE +from os.path import splitext, basename, relpath, dirname, exists, join, dirname +from random import randint +from json import load + +from tools.export.exporters import Exporter, apply_supported_whitelist +from tools.options import list_profiles +from tools.targets import TARGET_MAP +from tools.utils import NotSupportedException +from tools.build_api import prepare_toolchain + +# ============================================================================= -class MCUXpresso(GNUARMEclipse): +class UID: + """ + Helper class, used to generate unique ids required by .cproject symbols. + """ + @property + def id(self): + return "%0.9u" % randint(0, 999999999) + +# Global UID generator instance. +# Passed to the template engine, and referred as {{u.id}}. +# Each invocation generates a new number. +u = UID() + +# ============================================================================= + + +POST_BINARY_WHITELIST = set([ + "TEENSY3_1Code.binary_hook", + "MCU_NRF51Code.binary_hook", + "LPCTargetCode.lpc_patch", + "LPC4088Code.binary_hook" +]) + +class MCUXpresso(Exporter): NAME = 'MCUXpresso' TOOLCHAIN = 'GCC_ARM' - MBED_CONFIG_HEADER_SUPPORTED = True + @classmethod + def is_target_supported(cls, target_name): + target = TARGET_MAP[target_name] + return apply_supported_whitelist( + cls.TOOLCHAIN, POST_BINARY_WHITELIST, target) - TARGETS = [ - 'LPC54114', - ] + # override + @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 + The difference from the parent function is that it does not + add macro definitions, since they are passed 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 = relpath(config_header, + self.resources.file_basepath[config_header]) + flags['c_flags'] += self.toolchain.get_config_option(config_header) + flags['cxx_flags'] += self.toolchain.get_config_option( + config_header) + return flags + + def toolchain_flags(self, toolchain): + """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 + + The difference from the above is that it takes a parameter. + """ + + # Note: use the config options from the currently selected toolchain. + config_header = self.toolchain.get_config_header() + + flags = {key + "_flags": copy.deepcopy(value) for key, value + in toolchain.flags.iteritems()} + if config_header: + config_header = relpath(config_header, + self.resources.file_basepath[config_header]) + header_options = self.toolchain.get_config_option(config_header) + flags['c_flags'] += header_options + flags['cxx_flags'] += header_options + return flags + + # override def generate(self): + """ + Generate the .project and .cproject files. + """ + if not self.resources.linker_script: + raise NotSupportedException("No linker script found.") + + print + print 'Create a GNU ARM Eclipse C++ managed project' + print 'Project name: {0}'.format(self.project_name) + print 'Target: {0}'.format(self.toolchain.target.name) + print 'Toolchain: {0}'.format(self.TOOLCHAIN) + self.resources.win_to_unix() + + # TODO: use some logger to display additional info if verbose + libraries = [] + # print 'libraries' + # print self.resources.libraries for lib in self.resources.libraries: l, _ = splitext(basename(lib)) libraries.append(l[3:]) + self.system_libraries = [ + 'stdc++', 'supc++', 'm', 'c', 'gcc', 'nosys' + ] + + # Read in all profiles, we'll extract compiler options. + profiles = self.get_all_profiles() + + profile_ids = [s.lower() for s in profiles] + profile_ids.sort() + + # TODO: get the list from existing .cproject + build_folders = [s.capitalize() for s in profile_ids] + build_folders.append('BUILD') + # print build_folders + + objects = [self.filter_dot(s) for s in self.resources.objects] + for bf in build_folders: + objects = [o for o in objects if not o.startswith(bf + '/')] + # print 'objects' + # print objects + self.compute_exclusions() - ctx = { + self.include_path = [ + self.filter_dot(s) for s in self.resources.inc_dirs] + print 'Include folders: {0}'.format(len(self.include_path)) + + self.as_defines = self.toolchain.get_symbols(True) + self.c_defines = self.toolchain.get_symbols() + self.cpp_defines = self.c_defines + print 'Symbols: {0}'.format(len(self.c_defines)) + + self.ld_script = self.filter_dot( + self.resources.linker_script) + print 'Linker script: {0}'.format(self.ld_script) + + self.options = {} + for id in profile_ids: + + # There are 4 categories of options, a category common too + # all tools and a specific category for each of the tools. + opts = {} + opts['common'] = {} + opts['as'] = {} + opts['c'] = {} + opts['cpp'] = {} + opts['ld'] = {} + + opts['id'] = id + opts['name'] = opts['id'].capitalize() + + print + print 'Build configuration: {0}'.format(opts['name']) + + profile = profiles[id] + + # A small hack, do not bother with src_path again, + # pass an empty string to avoid crashing. + src_paths = [''] + target_name = self.toolchain.target.name + toolchain = prepare_toolchain( + src_paths, "", target_name, self.TOOLCHAIN, build_profile=[profile]) + + # Hack to fill in build_dir + toolchain.build_dir = self.toolchain.build_dir + + flags = self.toolchain_flags(toolchain) + + print 'Common flags:', ' '.join(flags['common_flags']) + print 'C++ flags:', ' '.join(flags['cxx_flags']) + print 'C flags:', ' '.join(flags['c_flags']) + print 'ASM flags:', ' '.join(flags['asm_flags']) + print 'Linker flags:', ' '.join(flags['ld_flags']) + + # Most GNU ARM Eclipse options have a parent, + # either debug or release. + if '-O0' in flags['common_flags'] or '-Og' in flags['common_flags']: + opts['parent_id'] = 'debug' + else: + opts['parent_id'] = 'release' + + self.process_options(opts, flags) + + opts['as']['defines'] = self.as_defines + opts['c']['defines'] = self.c_defines + opts['cpp']['defines'] = self.cpp_defines + + opts['common']['include_paths'] = self.include_path + opts['common']['excluded_folders'] = '|'.join( + self.excluded_folders) + + opts['ld']['library_paths'] = [ + self.filter_dot(s) for s in self.resources.lib_dirs] + + opts['ld']['object_files'] = objects + opts['ld']['user_libraries'] = libraries + opts['ld']['system_libraries'] = self.system_libraries + opts['ld']['script'] = join(id.capitalize(), + "linker-script-%s.ld" % id) + opts['cpp_cmd'] = " ".join(toolchain.preproc) + + # Unique IDs used in multiple places. + # Those used only once are implemented with {{u.id}}. + uid = {} + uid['config'] = u.id + uid['tool_c_compiler'] = u.id + uid['tool_c_compiler_input'] = u.id + uid['tool_cpp_compiler'] = u.id + uid['tool_cpp_compiler_input'] = u.id + + opts['uid'] = uid + + self.options[id] = opts + + jinja_ctx = { 'name': self.project_name, - 'include_paths': self.resources.inc_dirs, - 'linker_script': self.resources.linker_script, - 'object_files': self.resources.objects, - 'libraries': libraries, - 'exclude': '|'.join(self.excluded_folders), - 'symbols': self.toolchain.get_symbols() + 'ld_script': self.ld_script, + + # Compiler & linker command line options + 'options': self.options, + + # Must be an object with an `id` property, which + # will be called repeatedly, to generate multiple UIDs. + 'u': u, } - ctx.update(self.flags) - self.gen_file('mcuxpresso/%s_project.tmpl' % self.target.lower(), ctx, '.project') - self.gen_file('mcuxpresso/%s_cproject.tmpl' % self.target.lower(), ctx, '.cproject') + + self.gen_file('mcuxpresso/.project.tmpl', jinja_ctx, + '.project', trim_blocks=True, lstrip_blocks=True) + self.gen_file('mcuxpresso/.cproject.tmpl', jinja_ctx, + '.cproject', trim_blocks=True, lstrip_blocks=True) +# self.gen_file('mcuxpresso/makefile.targets.tmpl', jinja_ctx, +# 'makefile.targets', trim_blocks=True, lstrip_blocks=True) + self.gen_file('mcuxpresso/mbedignore.tmpl', jinja_ctx, '.mbedignore') + + print + print 'Done. Import the \'{0}\' project in Eclipse.'.format(self.project_name) + + # override + @staticmethod + def build(project_name, log_name="build_log.txt", cleanup=True): + """ + Headless build an Eclipse project. + + The following steps are performed: + - a temporary workspace is created, + - the project is imported, + - a clean build of all configurations is performed and + - the temporary workspace is removed. + + The build results are in the Debug & Release folders. + + All executables (eclipse & toolchain) must be in the PATH. + + The general method to start a headless Eclipse build is: + + $ eclipse \ + --launcher.suppressErrors \ + -nosplash \ + -application org.eclipse.cdt.managedbuilder.core.headlessbuild \ + -data /path/to/workspace \ + -import /path/to/project \ + -cleanBuild "project[/configuration] | all" + """ + + # TODO: possibly use the log file. + + # Create a temporary folder for the workspace. + tmp_folder = tempfile.mkdtemp() + + cmd = [ + 'eclipse', + '--launcher.suppressErrors', + '-nosplash', + '-application org.eclipse.cdt.managedbuilder.core.headlessbuild', + '-data', tmp_folder, + '-import', os.getcwd(), + '-cleanBuild', project_name + ] + + p = Popen(' '.join(cmd), shell=True, stdout=PIPE, stderr=PIPE) + out, err = p.communicate() + ret_code = p.returncode + stdout_string = "=" * 10 + "STDOUT" + "=" * 10 + "\n" + err_string = "=" * 10 + "STDERR" + "=" * 10 + "\n" + err_string += err + + ret_string = "SUCCESS\n" + if ret_code != 0: + ret_string += "FAILURE\n" + + print "%s\n%s\n%s\n%s" % (stdout_string, out, err_string, ret_string) + + if log_name: + # Write the output to the log file + with open(log_name, 'w+') as f: + f.write(stdout_string) + f.write(out) + f.write(err_string) + f.write(ret_string) + + # Cleanup the exported and built files + if cleanup: + if exists(log_name): + os.remove(log_name) + os.remove('.project') + os.remove('.cproject') + if exists('Debug'): + shutil.rmtree('Debug') + if exists('Release'): + shutil.rmtree('Release') + if exists('makefile.targets'): + os.remove('makefile.targets') + + # Always remove the temporary folder. + if exists(tmp_folder): + shutil.rmtree(tmp_folder) + + if ret_code == 0: + # Return Success + return 0 + + # Seems like something went wrong. + return -1 + + # ------------------------------------------------------------------------- + + @staticmethod + def get_all_profiles(): + tools_path = dirname(dirname(dirname(__file__))) + file_names = [join(tools_path, "profiles", fn) for fn in os.listdir( + join(tools_path, "profiles")) if fn.endswith(".json")] + + # print file_names + + profile_names = [basename(fn).replace(".json", "") + for fn in file_names] + # print profile_names + + profiles = {} + + for fn in file_names: + content = load(open(fn)) + profile_name = basename(fn).replace(".json", "") + profiles[profile_name] = content + + return profiles + + # ------------------------------------------------------------------------- + # Process source files/folders exclusions. + + def compute_exclusions(self): + """ + With the project root as the only source folder known to CDT, + based on the list of source files, compute the folders to not + be included in the build. + + The steps are: + - get the list of source folders, as dirname(source_file) + - compute the top folders (subfolders of the project folder) + - iterate all subfolders and add them to a tree, with all + nodes markes as 'not used' + - iterate the source folders and mark them as 'used' in the + tree, including all intermediate nodes + - recurse the tree and collect all unused folders; descend + the hierarchy only for used nodes + """ + source_folders = [self.filter_dot(s) for s in set(dirname( + src) for src in self.resources.c_sources + self.resources.cpp_sources + self.resources.s_sources)] + + self.excluded_folders = set(self.resources.ignored_dirs) - set(self.resources.inc_dirs) + print 'Source folders: {0}, with {1} exclusions'.format(len(source_folders), len(self.excluded_folders)) + + + # ------------------------------------------------------------------------- + + @staticmethod + def filter_dot(str): + """ + Remove the './' prefix, if present. + This function assumes that resources.win_to_unix() + replaced all windows backslashes with slashes. + """ + if str == None: + return None + if str[:2] == './': + return str[2:] + return str + + # ------------------------------------------------------------------------- + + def dump_tree(self, nodes, depth=0): + for k in nodes.keys(): + node = nodes[k] + parent_name = node['parent'][ + 'name'] if 'parent' in node.keys() else '' + print ' ' * depth, node['name'], node['is_used'], parent_name + if len(node['children'].keys()) != 0: + self.dump_tree(node['children'], depth + 1) + + def dump_paths(self, nodes, depth=0): + for k in nodes.keys(): + node = nodes[k] + parts = [] + while True: + parts.insert(0, node['name']) + if 'parent' not in node: + break + node = node['parent'] + path = '/'.join(parts) + print path, nodes[k]['is_used'] + self.dump_paths(nodes[k]['children'], depth + 1) + + # ------------------------------------------------------------------------- + + def process_options(self, opts, flags_in): + """ + CDT managed projects store lots of build options in separate + variables, with separate IDs in the .cproject file. + When the CDT build is started, all these options are brought + together to compose the compiler and linker command lines. + + Here the process is reversed, from the compiler and linker + command lines, the options are identified and various flags are + set to control the template generation process. + + Once identified, the options are removed from the command lines. + + The options that were not identified are options that do not + have CDT equivalents and will be passed in the 'Other options' + categories. + + Although this process does not have a very complicated logic, + given the large number of explicit configuration options + used by the GNU ARM Eclipse managed build plug-in, it is tedious... + """ + + # Make a copy of the flags, to be one by one removed after processing. + flags = copy.deepcopy(flags_in) + + if False: + print + print 'common_flags', flags['common_flags'] + print 'asm_flags', flags['asm_flags'] + print 'c_flags', flags['c_flags'] + print 'cxx_flags', flags['cxx_flags'] + print 'ld_flags', flags['ld_flags'] + + # Initialise the 'last resort' options where all unrecognised + # options will be collected. + opts['as']['other'] = '' + opts['c']['other'] = '' + opts['cpp']['other'] = '' + opts['ld']['other'] = '' + + MCPUS = { + 'Cortex-M0': {'mcpu': 'cortex-m0', 'fpu_unit': None}, + 'Cortex-M0+': {'mcpu': 'cortex-m0plus', 'fpu_unit': None}, + 'Cortex-M1': {'mcpu': 'cortex-m1', 'fpu_unit': None}, + 'Cortex-M3': {'mcpu': 'cortex-m3', 'fpu_unit': None}, + 'Cortex-M4': {'mcpu': 'cortex-m4', 'fpu_unit': None}, + 'Cortex-M4F': {'mcpu': 'cortex-m4', 'fpu_unit': 'fpv4spd16'}, + 'Cortex-M7': {'mcpu': 'cortex-m7', 'fpu_unit': None}, + 'Cortex-M7F': {'mcpu': 'cortex-m7', 'fpu_unit': 'fpv4spd16'}, + 'Cortex-M7FD': {'mcpu': 'cortex-m7', 'fpu_unit': 'fpv5d16'}, + 'Cortex-A9': {'mcpu': 'cortex-a9', 'fpu_unit': 'vfpv3'} + } + + # Remove options that are supplied by CDT + self.remove_option(flags['common_flags'], '-c') + self.remove_option(flags['common_flags'], '-MMD') + + # As 'plan B', get the CPU from the target definition. + core = self.toolchain.target.core + + opts['common']['arm.target.family'] = None + + # cortex-m0, cortex-m0-small-multiply, cortex-m0plus, + # cortex-m0plus-small-multiply, cortex-m1, cortex-m1-small-multiply, + # cortex-m3, cortex-m4, cortex-m7. + str = self.find_options(flags['common_flags'], '-mcpu=') + if str != None: + opts['common']['arm.target.family'] = str[len('-mcpu='):] + self.remove_option(flags['common_flags'], str) + self.remove_option(flags['ld_flags'], str) + else: + if core not in MCPUS: + raise NotSupportedException( + 'Target core {0} not supported.'.format(core)) + opts['common']['arm.target.family'] = MCPUS[core]['mcpu'] + + opts['common']['arm.target.arch'] = 'none' + str = self.find_options(flags['common_flags'], '-march=') + arch = str[len('-march='):] + archs = {'armv6-m': 'armv6-m', 'armv7-m': 'armv7-m', 'armv7-a': 'armv7-a'} + if arch in archs: + opts['common']['arm.target.arch'] = archs[arch] + self.remove_option(flags['common_flags'], str) + + opts['common']['arm.target.instructionset'] = 'thumb' + if '-mthumb' in flags['common_flags']: + self.remove_option(flags['common_flags'], '-mthumb') + self.remove_option(flags['ld_flags'], '-mthumb') + elif '-marm' in flags['common_flags']: + opts['common']['arm.target.instructionset'] = 'arm' + self.remove_option(flags['common_flags'], '-marm') + self.remove_option(flags['ld_flags'], '-marm') + + opts['common']['arm.target.thumbinterwork'] = False + if '-mthumb-interwork' in flags['common_flags']: + opts['common']['arm.target.thumbinterwork'] = True + self.remove_option(flags['common_flags'], '-mthumb-interwork') + + opts['common']['arm.target.endianness'] = None + if '-mlittle-endian' in flags['common_flags']: + opts['common']['arm.target.endianness'] = 'little' + self.remove_option(flags['common_flags'], '-mlittle-endian') + elif '-mbig-endian' in flags['common_flags']: + opts['common']['arm.target.endianness'] = 'big' + self.remove_option(flags['common_flags'], '-mbig-endian') + + opts['common']['arm.target.fpu.unit'] = None + # default, fpv4spd16, fpv5d16, fpv5spd16 + str = self.find_options(flags['common_flags'], '-mfpu=') + if str != None: + fpu = str[len('-mfpu='):] + fpus = { + 'fpv4-sp-d16': 'fpv4spd16', + 'fpv5-d16': 'fpv5d16', + 'fpv5-sp-d16': 'fpv5spd16' + } + if fpu in fpus: + opts['common']['arm.target.fpu.unit'] = fpus[fpu] + + self.remove_option(flags['common_flags'], str) + self.remove_option(flags['ld_flags'], str) + if opts['common']['arm.target.fpu.unit'] == None: + if core not in MCPUS: + raise NotSupportedException( + 'Target core {0} not supported.'.format(core)) + if MCPUS[core]['fpu_unit']: + opts['common'][ + 'arm.target.fpu.unit'] = MCPUS[core]['fpu_unit'] + + # soft, softfp, hard. + str = self.find_options(flags['common_flags'], '-mfloat-abi=') + if str != None: + opts['common']['arm.target.fpu.abi'] = str[ + len('-mfloat-abi='):] + self.remove_option(flags['common_flags'], str) + self.remove_option(flags['ld_flags'], str) + + opts['common']['arm.target.unalignedaccess'] = None + if '-munaligned-access' in flags['common_flags']: + opts['common']['arm.target.unalignedaccess'] = 'enabled' + self.remove_option(flags['common_flags'], '-munaligned-access') + elif '-mno-unaligned-access' in flags['common_flags']: + opts['common']['arm.target.unalignedaccess'] = 'disabled' + self.remove_option(flags['common_flags'], '-mno-unaligned-access') + + # Default optimisation level for Release. + opts['common']['optimization.level'] = '-Os' + + # If the project defines an optimisation level, it is used + # only for the Release configuration, the Debug one used '-Og'. + str = self.find_options(flags['common_flags'], '-O') + if str != None: + levels = { + '-O0': 'none', '-O1': 'optimize', '-O2': 'more', + '-O3': 'most', '-Os': 'size', '-Og': 'debug' + } + if str in levels: + opts['common']['optimization.level'] = levels[str] + self.remove_option(flags['common_flags'], str) + + include_files = [] + for all_flags in [flags['common_flags'], flags['c_flags'], flags['cxx_flags']]: + while '-include' in all_flags: + ix = all_flags.index('-include') + str = all_flags[ix + 1] + if str not in include_files: + include_files.append(str) + self.remove_option(all_flags, '-include') + self.remove_option(all_flags, str) + + opts['common']['include_files'] = include_files + + if '-ansi' in flags['c_flags']: + opts['c']['compiler.std'] = '-ansi' + self.remove_option(flags['c_flags'], str) + else: + str = self.find_options(flags['c_flags'], '-std') + std = str[len('-std='):] + c_std = { + 'c90': 'c90', 'c89': 'c90', 'gnu90': 'gnu90', 'gnu89': 'gnu90', + 'c99': 'c99', 'c9x': 'c99', 'gnu99': 'gnu99', 'gnu9x': 'gnu98', + 'c11': 'c11', 'c1x': 'c11', 'gnu11': 'gnu11', 'gnu1x': 'gnu11' + } + if std in c_std: + opts['c']['compiler.std'] = c_std[std] + self.remove_option(flags['c_flags'], str) + + if '-ansi' in flags['cxx_flags']: + opts['cpp']['compiler.std'] = '-ansi' + self.remove_option(flags['cxx_flags'], str) + else: + str = self.find_options(flags['cxx_flags'], '-std') + std = str[len('-std='):] + cpp_std = { + 'c++98': 'cpp98', 'c++03': 'cpp98', + 'gnu++98': 'gnucpp98', 'gnu++03': 'gnucpp98', + 'c++0x': 'cpp0x', 'gnu++0x': 'gnucpp0x', + 'c++11': 'cpp11', 'gnu++11': 'gnucpp11', + 'c++1y': 'cpp1y', 'gnu++1y': 'gnucpp1y', + 'c++14': 'cpp14', 'gnu++14': 'gnucpp14', + 'c++1z': 'cpp1z', 'gnu++1z': 'gnucpp1z', + } + if std in cpp_std: + opts['cpp']['compiler.std'] = cpp_std[std] + self.remove_option(flags['cxx_flags'], str) + + # Common optimisation options. + optimization_options = { + '-fmessage-length=0': 'optimization.messagelength', + '-fsigned-char': 'optimization.signedchar', + '-ffunction-sections': 'optimization.functionsections', + '-fdata-sections': 'optimization.datasections', + '-fno-common': 'optimization.nocommon', + '-fno-inline-functions': 'optimization.noinlinefunctions', + '-ffreestanding': 'optimization.freestanding', + '-fno-builtin': 'optimization.nobuiltin', + '-fsingle-precision-constant': 'optimization.spconstant', + '-fPIC': 'optimization.PIC', + '-fno-move-loop-invariants': 'optimization.nomoveloopinvariants', + } + + for option in optimization_options: + opts['common'][optimization_options[option]] = False + if option in flags['common_flags']: + opts['common'][optimization_options[option]] = True + self.remove_option(flags['common_flags'], option) + + # Common warning options. + warning_options = { + '-fsyntax-only': 'warnings.syntaxonly', + '-pedantic': 'warnings.pedantic', + '-pedantic-errors': 'warnings.pedanticerrors', + '-w': 'warnings.nowarn', + '-Wunused': 'warnings.unused', + '-Wuninitialized': 'warnings.uninitialized', + '-Wall': 'warnings.allwarn', + '-Wextra': 'warnings.extrawarn', + '-Wmissing-declarations': 'warnings.missingdeclaration', + '-Wconversion': 'warnings.conversion', + '-Wpointer-arith': 'warnings.pointerarith', + '-Wpadded': 'warnings.padded', + '-Wshadow': 'warnings.shadow', + '-Wlogical-op': 'warnings.logicalop', + '-Waggregate-return': 'warnings.agreggatereturn', + '-Wfloat-equal': 'warnings.floatequal', + '-Werror': 'warnings.toerrors', + } + + for option in warning_options: + opts['common'][warning_options[option]] = False + if option in flags['common_flags']: + opts['common'][warning_options[option]] = True + self.remove_option(flags['common_flags'], option) + + # Common debug options. + debug_levels = { + '-g': 'default', + '-g1': 'minimal', + '-g3': 'max', + } + opts['common']['debugging.level'] = 'none' + for option in debug_levels: + if option in flags['common_flags']: + opts['common'][ + 'debugging.level'] = debug_levels[option] + self.remove_option(flags['common_flags'], option) + + debug_formats = { + '-ggdb': 'gdb', + '-gstabs': 'stabs', + '-gstabs+': 'stabsplus', + '-gdwarf-2': 'dwarf2', + '-gdwarf-3': 'dwarf3', + '-gdwarf-4': 'dwarf4', + '-gdwarf-5': 'dwarf5', + } + + opts['common']['debugging.format'] = '' + for option in debug_levels: + if option in flags['common_flags']: + opts['common'][ + 'debugging.format'] = debug_formats[option] + self.remove_option(flags['common_flags'], option) + + opts['common']['debugging.prof'] = False + if '-p' in flags['common_flags']: + opts['common']['debugging.prof'] = True + self.remove_option(flags['common_flags'], '-p') + + opts['common']['debugging.gprof'] = False + if '-pg' in flags['common_flags']: + opts['common']['debugging.gprof'] = True + self.remove_option(flags['common_flags'], '-gp') + + # Assembler options. + opts['as']['usepreprocessor'] = False + while '-x' in flags['asm_flags']: + ix = flags['asm_flags'].index('-x') + str = flags['asm_flags'][ix + 1] + + if str == 'assembler-with-cpp': + opts['as']['usepreprocessor'] = True + else: + # Collect all other assembler options. + opts['as']['other'] += ' -x ' + str + + self.remove_option(flags['asm_flags'], '-x') + self.remove_option(flags['asm_flags'], 'assembler-with-cpp') + + opts['as']['nostdinc'] = False + if '-nostdinc' in flags['asm_flags']: + opts['as']['nostdinc'] = True + self.remove_option(flags['asm_flags'], '-nostdinc') + + opts['as']['verbose'] = False + if '-v' in flags['asm_flags']: + opts['as']['verbose'] = True + self.remove_option(flags['asm_flags'], '-v') + + # C options. + opts['c']['nostdinc'] = False + if '-nostdinc' in flags['c_flags']: + opts['c']['nostdinc'] = True + self.remove_option(flags['c_flags'], '-nostdinc') + + opts['c']['verbose'] = False + if '-v' in flags['c_flags']: + opts['c']['verbose'] = True + self.remove_option(flags['c_flags'], '-v') + + warning_options = { + '-Wmissing-prototypes': 'warnings.missingprototypes', + '-Wstrict-prototypes': 'warnings.strictprototypes', + '-Wbad-function-cast': 'warnings.badfunctioncast', + } + + for option in warning_options: + opts['c'][warning_options[option]] = False + if option in flags['common_flags']: + opts['c'][warning_options[option]] = True + self.remove_option(flags['common_flags'], option) + + # C++ options. + opts['cpp']['nostdinc'] = False + if '-nostdinc' in flags['cxx_flags']: + opts['cpp']['nostdinc'] = True + self.remove_option(flags['cxx_flags'], '-nostdinc') + + opts['cpp']['nostdincpp'] = False + if '-nostdinc++' in flags['cxx_flags']: + opts['cpp']['nostdincpp'] = True + self.remove_option(flags['cxx_flags'], '-nostdinc++') + + optimization_options = { + '-fno-exceptions': 'optimization.noexceptions', + '-fno-rtti': 'optimization.nortti', + '-fno-use-cxa-atexit': 'optimization.nousecxaatexit', + '-fno-threadsafe-statics': 'optimization.nothreadsafestatics', + } + + for option in optimization_options: + opts['cpp'][optimization_options[option]] = False + if option in flags['cxx_flags']: + opts['cpp'][optimization_options[option]] = True + self.remove_option(flags['cxx_flags'], option) + if option in flags['common_flags']: + opts['cpp'][optimization_options[option]] = True + self.remove_option(flags['common_flags'], option) + + warning_options = { + '-Wabi': 'warnabi', + '-Wctor-dtor-privacy': 'warnings.ctordtorprivacy', + '-Wnoexcept': 'warnings.noexcept', + '-Wnon-virtual-dtor': 'warnings.nonvirtualdtor', + '-Wstrict-null-sentinel': 'warnings.strictnullsentinel', + '-Wsign-promo': 'warnings.signpromo', + '-Weffc++': 'warneffc', + } + + for option in warning_options: + opts['cpp'][warning_options[option]] = False + if option in flags['cxx_flags']: + opts['cpp'][warning_options[option]] = True + self.remove_option(flags['cxx_flags'], option) + if option in flags['common_flags']: + opts['cpp'][warning_options[option]] = True + self.remove_option(flags['common_flags'], option) + + opts['cpp']['verbose'] = False + if '-v' in flags['cxx_flags']: + opts['cpp']['verbose'] = True + self.remove_option(flags['cxx_flags'], '-v') + + # Linker options. + linker_options = { + '-nostartfiles': 'nostart', + '-nodefaultlibs': 'nodeflibs', + '-nostdlib': 'nostdlibs', + } + + for option in linker_options: + opts['ld'][linker_options[option]] = False + if option in flags['ld_flags']: + opts['ld'][linker_options[option]] = True + self.remove_option(flags['ld_flags'], option) + + opts['ld']['gcsections'] = False + if '-Wl,--gc-sections' in flags['ld_flags']: + opts['ld']['gcsections'] = True + self.remove_option(flags['ld_flags'], '-Wl,--gc-sections') + + opts['ld']['flags'] = [] + to_remove = [] + for opt in flags['ld_flags']: + if opt.startswith('-Wl,--wrap,'): + opts['ld']['flags'].append( + '--wrap=' + opt[len('-Wl,--wrap,'):]) + to_remove.append(opt) + for opt in to_remove: + self.remove_option(flags['ld_flags'], opt) + + # Other tool remaining options are separated by category. + opts['as']['otherwarnings'] = self.find_options( + flags['asm_flags'], '-W') + + opts['c']['otherwarnings'] = self.find_options( + flags['c_flags'], '-W') + opts['c']['otheroptimizations'] = self.find_options(flags[ + 'c_flags'], '-f') + + opts['cpp']['otherwarnings'] = self.find_options( + flags['cxx_flags'], '-W') + opts['cpp']['otheroptimizations'] = self.find_options( + flags['cxx_flags'], '-f') + + # Other common remaining options are separated by category. + opts['common']['optimization.other'] = self.find_options( + flags['common_flags'], '-f') + opts['common']['warnings.other'] = self.find_options( + flags['common_flags'], '-W') + + # Remaining common flags are added to each tool. + opts['as']['other'] += ' ' + \ + ' '.join(flags['common_flags']) + ' ' + \ + ' '.join(flags['asm_flags']) + opts['c']['other'] += ' ' + \ + ' '.join(flags['common_flags']) + ' ' + ' '.join(flags['c_flags']) + opts['cpp']['other'] += ' ' + \ + ' '.join(flags['common_flags']) + ' ' + \ + ' '.join(flags['cxx_flags']) + opts['ld']['other'] += ' ' + \ + ' '.join(flags['common_flags']) + ' ' + ' '.join(flags['ld_flags']) + + if len(self.system_libraries) > 0: + opts['ld']['other'] += ' -Wl,--start-group ' + opts['ld'][ + 'other'] += ' '.join('-l' + s for s in self.system_libraries) + opts['ld']['other'] += ' -Wl,--end-group ' + + # Strip all 'other' flags, since they might have leading spaces. + opts['as']['other'] = opts['as']['other'].strip() + opts['c']['other'] = opts['c']['other'].strip() + opts['cpp']['other'] = opts['cpp']['other'].strip() + opts['ld']['other'] = opts['ld']['other'].strip() + + if False: + print + print opts + + print + print 'common_flags', flags['common_flags'] + print 'asm_flags', flags['asm_flags'] + print 'c_flags', flags['c_flags'] + print 'cxx_flags', flags['cxx_flags'] + print 'ld_flags', flags['ld_flags'] + + @staticmethod + def find_options(lst, option): + tmp = [str for str in lst if str.startswith(option)] + if len(tmp) > 0: + return tmp[0] + else: + return None + + @staticmethod + def find_options(lst, prefix): + other = '' + opts = [str for str in lst if str.startswith(prefix)] + if len(opts) > 0: + for opt in opts: + other += ' ' + opt + MCUXpresso.remove_option(lst, opt) + return other.strip() + + @staticmethod + def remove_option(lst, option): + if option in lst: + lst.remove(option) + +# ============================================================================= diff --git a/tools/export/mcuxpresso/cproject_common.tmpl b/tools/export/mcuxpresso/cproject_common.tmpl index 4fa43963bf..f6c557bca6 100644 --- a/tools/export/mcuxpresso/cproject_common.tmpl +++ b/tools/export/mcuxpresso/cproject_common.tmpl @@ -1,7 +1,9 @@ - + {% for cfg_key in options %} + {% set opts = options[cfg_key] %} + @@ -130,64 +132,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + {% endfor %} diff --git a/tools/export/mcuxpresso/mbedignore.tmpl b/tools/export/mcuxpresso/mbedignore.tmpl new file mode 100644 index 0000000000..175b9673f4 --- /dev/null +++ b/tools/export/mcuxpresso/mbedignore.tmpl @@ -0,0 +1,3 @@ +{%- for config in options.values() -%} +{{config.name}}/* +{% endfor -%}