Clone of official tools

Revision:
36:96847d42f010
Child:
40:7d3fa6b99b2b
diff -r da9c89f8be7d -r 96847d42f010 export/gnuarmeclipse/__init__.py
--- /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)
+
+# =============================================================================