Clone of official tools
Diff: export/gnuarmeclipse/__init__.py
- Revision:
- 36:96847d42f010
- Child:
- 40:7d3fa6b99b2b
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/export/gnuarmeclipse/__init__.py Thu Jun 22 11:12:28 2017 -0500 @@ -0,0 +1,1058 @@ +""" +mbed SDK +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. +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. + +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 <ilg@livius.net> +""" + +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, filter_supported +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 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 GNUARMEclipse(Exporter): + NAME = 'GNU ARM Eclipse' + TOOLCHAIN = 'GCC_ARM' + + TARGETS = filter_supported("GCC_ARM", POST_BINARY_WHITELIST) + + # 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() + + 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, + '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, + } + + self.gen_file('gnuarmeclipse/.project.tmpl', jinja_ctx, + '.project', trim_blocks=True, lstrip_blocks=True) + self.gen_file('gnuarmeclipse/.cproject.tmpl', jinja_ctx, + '.cproject', trim_blocks=True, lstrip_blocks=True) + self.gen_file('gnuarmeclipse/makefile.targets.tmpl', jinja_ctx, + 'makefile.targets', trim_blocks=True, lstrip_blocks=True) + self.gen_file('gnuarmeclipse/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)] + if '.' in source_folders: + source_folders.remove('.') + + # print 'source folders' + # print source_folders + + # Source folders were converted before and are guaranteed to + # use the POSIX separator. + top_folders = [f for f in set(s.split('/')[0] + for s in source_folders)] + # print 'top folders' + # print top_folders + + self.source_tree = {} + for top_folder in top_folders: + for root, dirs, files in os.walk(top_folder, topdown=True): + # print root, dirs, files + + # Paths returned by os.walk() must be split with os.dep + # to accomodate Windows weirdness. + parts = root.split(os.sep) + + # Ignore paths that include parts starting with dot. + skip = False + for part in parts: + if part.startswith('.'): + skip = True + break + if skip: + continue + + # Further process only leaf paths, (that do not have + # sub-folders). + if len(dirs) == 0: + # The path is reconstructed using POSIX separators. + self.add_source_folder_to_tree('/'.join(parts)) + + for folder in source_folders: + self.add_source_folder_to_tree(folder, True) + + # print + # print self.source_tree + # self.dump_paths(self.source_tree) + # self.dump_tree(self.source_tree) + + # print 'excludings' + self.excluded_folders = ['BUILD'] + self.recurse_excludings(self.source_tree) + + print 'Source folders: {0}, with {1} exclusions'.format(len(source_folders), len(self.excluded_folders)) + + def add_source_folder_to_tree(self, path, is_used=False): + """ + Decompose a path in an array of folder names and create the tree. + On the second pass the nodes should be already there; mark them + as used. + """ + # print path, is_used + + # All paths arriving here are guaranteed to use the POSIX + # separators, os.walk() paths were also explicitly converted. + parts = path.split('/') + # print parts + node = self.source_tree + prev = None + for part in parts: + if part not in node.keys(): + new_node = {} + new_node['name'] = part + new_node['children'] = {} + if prev != None: + new_node['parent'] = prev + node[part] = new_node + node[part]['is_used'] = is_used + prev = node[part] + node = node[part]['children'] + + def recurse_excludings(self, nodes): + """ + Recurse the tree and collect all unused folders; descend + the hierarchy only for used nodes. + """ + for k in nodes.keys(): + node = nodes[k] + if node['is_used'] == False: + parts = [] + cnode = node + while True: + parts.insert(0, cnode['name']) + if 'parent' not in cnode: + break + cnode = cnode['parent'] + + # Compose a POSIX path. + path = '/'.join(parts) + # print path + self.excluded_folders.append(path) + else: + self.recurse_excludings(node['children']) + + # ------------------------------------------------------------------------- + + @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 + GNUARMEclipse.remove_option(lst, opt) + return other.strip() + + @staticmethod + def remove_option(lst, option): + if option in lst: + lst.remove(option) + +# =============================================================================