init

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers __init__.py Source File

__init__.py

00001 """
00002 mbed SDK
00003 Copyright (c) 2011-2017 ARM Limited
00004 
00005 Licensed under the Apache License, Version 2.0 (the "License");
00006 you may not use this file except in compliance with the License.
00007 You may obtain a copy of the License at
00008 
00009     http://www.apache.org/licenses/LICENSE-2.0
00010 
00011 Unless required by applicable law or agreed to in writing, software
00012 distributed under the License is distributed on an "AS IS" BASIS,
00013 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014 See the License for the specific language governing permissions and
00015 limitations under the License.
00016 
00017 Title: GNU ARM Eclipse (http://gnuarmeclipse.github.io) exporter.
00018 
00019 Description: Creates a managed build project that can be imported by 
00020 the GNU ARM Eclipse plug-ins.
00021 
00022 Author: Liviu Ionescu <ilg@livius.net>
00023 """
00024 
00025 import os
00026 import copy
00027 import tempfile
00028 import shutil
00029 import copy
00030 
00031 from subprocess import call, Popen, PIPE
00032 from os.path import splitext, basename, relpath, dirname, exists, join, dirname
00033 from random import randint
00034 from json import load
00035 
00036 from tools.export.exporters import Exporter, apply_supported_whitelist
00037 from tools.options import list_profiles
00038 from tools.targets import TARGET_MAP
00039 from tools.utils import NotSupportedException
00040 from tools.build_api import prepare_toolchain
00041 
00042 # =============================================================================
00043 
00044 
00045 class UID :
00046     """
00047     Helper class, used to generate unique ids required by .cproject symbols.
00048     """
00049     @property
00050     def id(self):
00051         return "%0.9u" % randint(0, 999999999)
00052 
00053 # Global UID generator instance.
00054 # Passed to the template engine, and referred as {{u.id}}.
00055 # Each invocation generates a new number.
00056 u = UID()
00057 
00058 # =============================================================================
00059 
00060 
00061 POST_BINARY_WHITELIST = set([
00062     "TEENSY3_1Code.binary_hook",
00063     "MCU_NRF51Code.binary_hook",
00064     "LPCTargetCode.lpc_patch",
00065     "LPC4088Code.binary_hook"
00066 ])
00067 
00068 class GNUARMEclipse(Exporter ):
00069     NAME = 'GNU ARM Eclipse'
00070     TOOLCHAIN = 'GCC_ARM'
00071 
00072     @classmethod
00073     def is_target_supported(cls, target_name):
00074         target = TARGET_MAP[target_name]
00075         return apply_supported_whitelist(
00076             cls.TOOLCHAIN, POST_BINARY_WHITELIST, target)
00077 
00078     # override
00079     @property
00080     def flags(self):
00081         """Returns a dictionary of toolchain flags.
00082         Keys of the dictionary are:
00083         cxx_flags    - c++ flags
00084         c_flags      - c flags
00085         ld_flags     - linker flags
00086         asm_flags    - assembler flags
00087         common_flags - common options
00088 
00089         The difference from the parent function is that it does not
00090         add macro definitions, since they are passed separately.
00091         """
00092 
00093         config_header = self.toolchain.get_config_header()
00094         flags = {key + "_flags": copy.deepcopy(value) for key, value
00095                  in self.toolchain.flags.items()}
00096         if config_header:
00097             config_header = relpath(config_header,
00098                                     self.resources.file_basepath[config_header])
00099             flags['c_flags'] += self.toolchain.get_config_option(config_header)
00100             flags['cxx_flags'] += self.toolchain.get_config_option(
00101                 config_header)
00102         return flags
00103 
00104     def toolchain_flags(self, toolchain):
00105         """Returns a dictionary of toolchain flags.
00106         Keys of the dictionary are:
00107         cxx_flags    - c++ flags
00108         c_flags      - c flags
00109         ld_flags     - linker flags
00110         asm_flags    - assembler flags
00111         common_flags - common options
00112 
00113         The difference from the above is that it takes a parameter.
00114         """
00115 
00116         # Note: use the config options from the currently selected toolchain.
00117         config_header = self.toolchain.get_config_header()
00118 
00119         flags = {key + "_flags": copy.deepcopy(value) for key, value
00120                  in toolchain.flags.items()}
00121         if config_header:
00122             config_header = relpath(config_header,
00123                                     self.resources.file_basepath[config_header])
00124             header_options = self.toolchain.get_config_option(config_header)
00125             flags['c_flags'] += header_options
00126             flags['cxx_flags'] += header_options
00127         return flags
00128 
00129     def validate_resources(self):
00130         if not self.resources.linker_script:
00131             raise NotSupportedException("No linker script found.")
00132 
00133     def create_jinja_ctx(self):
00134 
00135         self.validate_resources()
00136 
00137         self.resources.win_to_unix()
00138 
00139         # TODO: use some logger to display additional info if verbose
00140 
00141         libraries = []
00142         # print 'libraries'
00143         # print self.resources.libraries
00144         for lib in self.resources.libraries:
00145             l, _ = splitext(basename(lib))
00146             libraries.append(l[3:])
00147 
00148         self.system_libraries = [
00149             'stdc++', 'supc++', 'm', 'c', 'gcc', 'nosys'
00150         ]
00151 
00152         # Read in all profiles, we'll extract compiler options.
00153         profiles = self.get_all_profiles()
00154 
00155         profile_ids = [s.lower() for s in profiles]
00156         profile_ids.sort()
00157 
00158         # TODO: get the list from existing .cproject
00159         build_folders = [s.capitalize() for s in profile_ids]
00160         build_folders.append('BUILD')
00161         # print build_folders
00162 
00163         objects = [self.filter_dot(s) for s in self.resources.objects]
00164         for bf in build_folders:
00165             objects = [o for o in objects if not o.startswith(bf + '/')]
00166         # print 'objects'
00167         # print objects
00168 
00169         self.compute_exclusions()
00170 
00171         self.include_path = [
00172             self.filter_dot(s) for s in self.resources.inc_dirs]
00173         print 'Include folders: {0}'.format(len(self.include_path))
00174 
00175         self.as_defines = self.toolchain.get_symbols(True)
00176         self.c_defines = self.toolchain.get_symbols()
00177         self.cpp_defines = self.c_defines
00178         print 'Symbols: {0}'.format(len(self.c_defines))
00179 
00180         self.ld_script = self.filter_dot(
00181             self.resources.linker_script)
00182         print 'Linker script: {0}'.format(self.ld_script)
00183 
00184         self.options = {}
00185         for id in profile_ids:
00186 
00187             # There are 4 categories of options, a category common too
00188             # all tools and a specific category for each of the tools.
00189             opts = {}
00190             opts['common'] = {}
00191             opts['as'] = {}
00192             opts['c'] = {}
00193             opts['cpp'] = {}
00194             opts['ld'] = {}
00195 
00196             opts['id'] = id
00197             opts['name'] = opts['id'].capitalize()
00198 
00199             print
00200             print 'Build configuration: {0}'.format(opts['name'])
00201 
00202             profile = profiles[id]
00203 
00204             # A small hack, do not bother with src_path again,
00205             # pass an empty string to avoid crashing.
00206             src_paths = ['']
00207             target_name = self.toolchain.target.name
00208             toolchain = prepare_toolchain(
00209                 src_paths, "", target_name, self.TOOLCHAIN, build_profile=[profile])
00210 
00211             # Hack to fill in build_dir
00212             toolchain.build_dir = self.toolchain.build_dir
00213             toolchain.config = self.toolchain.config
00214             toolchain.set_config_data(self.toolchain.config.get_config_data())
00215 
00216             flags = self.toolchain_flags(toolchain)
00217 
00218             print 'Common flags:', ' '.join(flags['common_flags'])
00219             print 'C++ flags:', ' '.join(flags['cxx_flags'])
00220             print 'C flags:', ' '.join(flags['c_flags'])
00221             print 'ASM flags:', ' '.join(flags['asm_flags'])
00222             print 'Linker flags:', ' '.join(flags['ld_flags'])
00223 
00224             # Most GNU ARM Eclipse options have a parent,
00225             # either debug or release.
00226             if '-O0' in flags['common_flags'] or '-Og' in flags['common_flags']:
00227                 opts['parent_id'] = 'debug'
00228             else:
00229                 opts['parent_id'] = 'release'
00230 
00231             self.process_options(opts, flags)
00232 
00233             opts['as']['defines'] = self.as_defines
00234             opts['c']['defines'] = self.c_defines
00235             opts['cpp']['defines'] = self.cpp_defines
00236 
00237             opts['common']['include_paths'] = self.include_path
00238             opts['common']['excluded_folders'] = '|'.join(
00239                 self.excluded_folders)
00240 
00241             opts['ld']['library_paths'] = [
00242                 self.filter_dot(s) for s in self.resources.lib_dirs]
00243 
00244             opts['ld']['object_files'] = objects
00245             opts['ld']['user_libraries'] = libraries
00246             opts['ld']['system_libraries'] = self.system_libraries
00247             opts['ld']['script'] = join(id.capitalize(),
00248                                         "linker-script-%s.ld" % id)
00249             opts['cpp_cmd'] = '"{}"'.format(toolchain.preproc[0]) + " " + " ".join(toolchain.preproc[1:])
00250 
00251             # Unique IDs used in multiple places.
00252             # Those used only once are implemented with {{u.id}}.
00253             uid = {}
00254             uid['config'] = u.id
00255             uid['tool_c_compiler'] = u.id
00256             uid['tool_c_compiler_input'] = u.id
00257             uid['tool_cpp_compiler'] = u.id
00258             uid['tool_cpp_compiler_input'] = u.id
00259 
00260             opts['uid'] = uid
00261 
00262             self.options[id] = opts
00263 
00264         jinja_ctx = {
00265             'name': self.project_name,
00266             'ld_script': self.ld_script,
00267 
00268             # Compiler & linker command line options
00269             'options': self.options,
00270 
00271             # Must be an object with an `id` property, which
00272             # will be called repeatedly, to generate multiple UIDs.
00273             'u': u,
00274         }
00275         return jinja_ctx
00276 
00277     # override
00278     def generate(self):
00279         """
00280         Generate the .project and .cproject files.
00281         """
00282         jinja_ctx = self.create_jinja_ctx()
00283 
00284         print
00285         print 'Create a GNU ARM Eclipse C++ managed project'
00286         print 'Project name: {0}'.format(self.project_name)
00287         print 'Target: {0}'.format(self.toolchain.target.name)
00288         print 'Toolchain: {0}'.format(self.TOOLCHAIN)
00289 
00290         self.gen_file('gnuarmeclipse/.project.tmpl', jinja_ctx,
00291                       '.project', trim_blocks=True, lstrip_blocks=True)
00292         self.gen_file('gnuarmeclipse/.cproject.tmpl', jinja_ctx,
00293                       '.cproject', trim_blocks=True, lstrip_blocks=True)
00294         self.gen_file('gnuarmeclipse/makefile.targets.tmpl', jinja_ctx,
00295                       'makefile.targets', trim_blocks=True, lstrip_blocks=True)
00296         self.gen_file('gnuarmeclipse/mbedignore.tmpl', jinja_ctx, '.mbedignore')
00297 
00298         print
00299         print 'Done. Import the \'{0}\' project in Eclipse.'.format(self.project_name)
00300 
00301     # override
00302     @staticmethod
00303     def build(project_name, log_name="build_log.txt", cleanup=True):
00304         """
00305         Headless build an Eclipse project.
00306 
00307         The following steps are performed:
00308         - a temporary workspace is created, 
00309         - the project is imported,
00310         - a clean build of all configurations is performed and 
00311         - the temporary workspace is removed.
00312 
00313         The build results are in the Debug & Release folders.
00314 
00315         All executables (eclipse & toolchain) must be in the PATH.
00316 
00317         The general method to start a headless Eclipse build is:
00318 
00319         $ eclipse \
00320         --launcher.suppressErrors \
00321         -nosplash \
00322         -application org.eclipse.cdt.managedbuilder.core.headlessbuild \
00323         -data /path/to/workspace \
00324         -import /path/to/project \
00325         -cleanBuild "project[/configuration] | all"
00326         """
00327 
00328         # TODO: possibly use the log file.
00329 
00330         # Create a temporary folder for the workspace.
00331         tmp_folder = tempfile.mkdtemp()
00332 
00333         cmd = [
00334             'eclipse',
00335             '--launcher.suppressErrors',
00336             '-nosplash',
00337             '-application org.eclipse.cdt.managedbuilder.core.headlessbuild',
00338             '-data', tmp_folder,
00339             '-import', os.getcwd(),
00340             '-cleanBuild', project_name
00341         ]
00342 
00343         p = Popen(' '.join(cmd), shell=True, stdout=PIPE, stderr=PIPE)
00344         out, err = p.communicate()
00345         ret_code = p.returncode
00346         stdout_string = "=" * 10 + "STDOUT" + "=" * 10 + "\n"
00347         err_string = "=" * 10 + "STDERR" + "=" * 10 + "\n"
00348         err_string += err
00349 
00350         ret_string = "SUCCESS\n"
00351         if ret_code != 0:
00352             ret_string += "FAILURE\n"
00353 
00354         print "%s\n%s\n%s\n%s" % (stdout_string, out, err_string, ret_string)
00355 
00356         if log_name:
00357             # Write the output to the log file
00358             with open(log_name, 'w+') as f:
00359                 f.write(stdout_string)
00360                 f.write(out)
00361                 f.write(err_string)
00362                 f.write(ret_string)
00363 
00364         # Cleanup the exported and built files
00365         if cleanup:
00366             if exists(log_name):
00367                 os.remove(log_name)
00368             os.remove('.project')
00369             os.remove('.cproject')
00370             if exists('Debug'):
00371                 shutil.rmtree('Debug')
00372             if exists('Release'):
00373                 shutil.rmtree('Release')
00374             if exists('makefile.targets'):
00375                 os.remove('makefile.targets')
00376 
00377         # Always remove the temporary folder.
00378         if exists(tmp_folder):
00379             shutil.rmtree(tmp_folder)
00380 
00381         if ret_code == 0:
00382             # Return Success
00383             return 0
00384 
00385         # Seems like something went wrong.
00386         return -1
00387 
00388    # -------------------------------------------------------------------------
00389 
00390     @staticmethod
00391     def get_all_profiles():
00392         tools_path = dirname(dirname(dirname(__file__)))
00393         file_names = [join(tools_path, "profiles", fn) for fn in os.listdir(
00394             join(tools_path, "profiles")) if fn.endswith(".json")]
00395 
00396         # print file_names
00397 
00398         profile_names = [basename(fn).replace(".json", "")
00399                          for fn in file_names]
00400         # print profile_names
00401 
00402         profiles = {}
00403 
00404         for fn in file_names:
00405             content = load(open(fn))
00406             profile_name = basename(fn).replace(".json", "")
00407             profiles[profile_name] = content
00408 
00409         return profiles
00410 
00411     # -------------------------------------------------------------------------
00412     # Process source files/folders exclusions.
00413 
00414     def compute_exclusions(self):
00415         """
00416         With the project root as the only source folder known to CDT,
00417         based on the list of source files, compute the folders to not
00418         be included in the build.
00419 
00420         The steps are:
00421         - get the list of source folders, as dirname(source_file)
00422         - compute the top folders (subfolders of the project folder)
00423         - iterate all subfolders and add them to a tree, with all 
00424         nodes markes as 'not used'
00425         - iterate the source folders and mark them as 'used' in the
00426         tree, including all intermediate nodes
00427         - recurse the tree and collect all unused folders; descend
00428         the hierarchy only for used nodes
00429         """
00430         source_folders = [self.filter_dot(s) for s in set(dirname(
00431             src) for src in self.resources.c_sources + self.resources.cpp_sources + self.resources.s_sources)]
00432 
00433         self.excluded_folders = set(self.resources.ignored_dirs) - set(self.resources.inc_dirs)
00434         print 'Source folders: {0}, with {1} exclusions'.format(len(source_folders), len(self.excluded_folders))
00435 
00436 
00437     # -------------------------------------------------------------------------
00438 
00439     @staticmethod
00440     def filter_dot(str):
00441         """
00442         Remove the './' prefix, if present.
00443         This function assumes that resources.win_to_unix()
00444         replaced all windows backslashes with slashes.
00445         """
00446         if str == None:
00447             return None
00448         if str[:2] == './':
00449             return str[2:]
00450         return str
00451 
00452     # -------------------------------------------------------------------------
00453 
00454     def dump_tree(self, nodes, depth=0):
00455         for k in nodes.keys():
00456             node = nodes[k]
00457             parent_name = node['parent'][
00458                 'name'] if 'parent' in node.keys() else ''
00459             print '  ' * depth, node['name'], node['is_used'], parent_name
00460             if len(node['children'].keys()) != 0:
00461                 self.dump_tree(node['children'], depth + 1)
00462 
00463     def dump_paths(self, nodes, depth=0):
00464         for k in nodes.keys():
00465             node = nodes[k]
00466             parts = []
00467             while True:
00468                 parts.insert(0, node['name'])
00469                 if 'parent' not in node:
00470                     break
00471                 node = node['parent']
00472             path = '/'.join(parts)
00473             print path, nodes[k]['is_used']
00474             self.dump_paths(nodes[k]['children'], depth + 1)
00475 
00476     # -------------------------------------------------------------------------
00477 
00478     def process_options(self, opts, flags_in):
00479         """
00480         CDT managed projects store lots of build options in separate
00481         variables, with separate IDs in the .cproject file.
00482         When the CDT build is started, all these options are brought
00483         together to compose the compiler and linker command lines.
00484 
00485         Here the process is reversed, from the compiler and linker
00486         command lines, the options are identified and various flags are
00487         set to control the template generation process.
00488 
00489         Once identified, the options are removed from the command lines.
00490 
00491         The options that were not identified are options that do not 
00492         have CDT equivalents and will be passed in the 'Other options' 
00493         categories.
00494 
00495         Although this process does not have a very complicated logic,
00496         given the large number of explicit configuration options
00497         used by the GNU ARM Eclipse managed build plug-in, it is tedious...
00498         """
00499 
00500         # Make a copy of the flags, to be one by one removed after processing.
00501         flags = copy.deepcopy(flags_in)
00502 
00503         if False:
00504             print
00505             print 'common_flags', flags['common_flags']
00506             print 'asm_flags', flags['asm_flags']
00507             print 'c_flags', flags['c_flags']
00508             print 'cxx_flags', flags['cxx_flags']
00509             print 'ld_flags', flags['ld_flags']
00510 
00511         # Initialise the 'last resort' options where all unrecognised
00512         # options will be collected.
00513         opts['as']['other'] = ''
00514         opts['c']['other'] = ''
00515         opts['cpp']['other'] = ''
00516         opts['ld']['other'] = ''
00517 
00518         MCPUS = {
00519             'Cortex-M0': {'mcpu': 'cortex-m0', 'fpu_unit': None},
00520             'Cortex-M0+': {'mcpu': 'cortex-m0plus', 'fpu_unit': None},
00521             'Cortex-M1': {'mcpu': 'cortex-m1', 'fpu_unit': None},
00522             'Cortex-M3': {'mcpu': 'cortex-m3', 'fpu_unit': None},
00523             'Cortex-M4': {'mcpu': 'cortex-m4', 'fpu_unit': None},
00524             'Cortex-M4F': {'mcpu': 'cortex-m4', 'fpu_unit': 'fpv4spd16'},
00525             'Cortex-M7': {'mcpu': 'cortex-m7', 'fpu_unit': None},
00526             'Cortex-M7F': {'mcpu': 'cortex-m7', 'fpu_unit': 'fpv4spd16'},
00527             'Cortex-M7FD': {'mcpu': 'cortex-m7', 'fpu_unit': 'fpv5d16'},
00528             'Cortex-A9': {'mcpu': 'cortex-a9', 'fpu_unit': 'vfpv3'}
00529         }
00530 
00531         # Remove options that are supplied by CDT
00532         self.remove_option(flags['common_flags'], '-c')
00533         self.remove_option(flags['common_flags'], '-MMD')
00534 
00535         # As 'plan B', get the CPU from the target definition.
00536         core = self.toolchain.target.core
00537 
00538         opts['common']['arm.target.family'] = None
00539 
00540         # cortex-m0, cortex-m0-small-multiply, cortex-m0plus,
00541         # cortex-m0plus-small-multiply, cortex-m1, cortex-m1-small-multiply,
00542         # cortex-m3, cortex-m4, cortex-m7.
00543         str = self.find_options(flags['common_flags'], '-mcpu=')
00544         if str != None:
00545             opts['common']['arm.target.family'] = str[len('-mcpu='):]
00546             self.remove_option(flags['common_flags'], str)
00547             self.remove_option(flags['ld_flags'], str)
00548         else:
00549             if core not in MCPUS:
00550                 raise NotSupportedException(
00551                     'Target core {0} not supported.'.format(core))
00552             opts['common']['arm.target.family'] = MCPUS[core]['mcpu']
00553 
00554         opts['common']['arm.target.arch'] = 'none'
00555         str = self.find_options(flags['common_flags'], '-march=')
00556         arch = str[len('-march='):]
00557         archs = {'armv6-m': 'armv6-m', 'armv7-m': 'armv7-m', 'armv7-a': 'armv7-a'}
00558         if arch in archs:
00559             opts['common']['arm.target.arch'] = archs[arch]
00560             self.remove_option(flags['common_flags'], str)
00561 
00562         opts['common']['arm.target.instructionset'] = 'thumb'
00563         if '-mthumb' in flags['common_flags']:
00564             self.remove_option(flags['common_flags'], '-mthumb')
00565             self.remove_option(flags['ld_flags'], '-mthumb')
00566         elif '-marm' in flags['common_flags']:
00567             opts['common']['arm.target.instructionset'] = 'arm'
00568             self.remove_option(flags['common_flags'], '-marm')
00569             self.remove_option(flags['ld_flags'], '-marm')
00570 
00571         opts['common']['arm.target.thumbinterwork'] = False
00572         if '-mthumb-interwork' in flags['common_flags']:
00573             opts['common']['arm.target.thumbinterwork'] = True
00574             self.remove_option(flags['common_flags'], '-mthumb-interwork')
00575 
00576         opts['common']['arm.target.endianness'] = None
00577         if '-mlittle-endian' in flags['common_flags']:
00578             opts['common']['arm.target.endianness'] = 'little'
00579             self.remove_option(flags['common_flags'], '-mlittle-endian')
00580         elif '-mbig-endian' in flags['common_flags']:
00581             opts['common']['arm.target.endianness'] = 'big'
00582             self.remove_option(flags['common_flags'], '-mbig-endian')
00583 
00584         opts['common']['arm.target.fpu.unit'] = None
00585         # default, fpv4spd16, fpv5d16, fpv5spd16
00586         str = self.find_options(flags['common_flags'], '-mfpu=')
00587         if str != None:
00588             fpu = str[len('-mfpu='):]
00589             fpus = {
00590                 'fpv4-sp-d16': 'fpv4spd16',
00591                 'fpv5-d16': 'fpv5d16',
00592                 'fpv5-sp-d16': 'fpv5spd16'
00593             }
00594             if fpu in fpus:
00595                 opts['common']['arm.target.fpu.unit'] = fpus[fpu]
00596 
00597                 self.remove_option(flags['common_flags'], str)
00598                 self.remove_option(flags['ld_flags'], str)
00599         if opts['common']['arm.target.fpu.unit'] == None:
00600             if core not in MCPUS:
00601                 raise NotSupportedException(
00602                     'Target core {0} not supported.'.format(core))
00603             if MCPUS[core]['fpu_unit']:
00604                 opts['common'][
00605                     'arm.target.fpu.unit'] = MCPUS[core]['fpu_unit']
00606 
00607         # soft, softfp, hard.
00608         str = self.find_options(flags['common_flags'], '-mfloat-abi=')
00609         if str != None:
00610             opts['common']['arm.target.fpu.abi'] = str[
00611                 len('-mfloat-abi='):]
00612             self.remove_option(flags['common_flags'], str)
00613             self.remove_option(flags['ld_flags'], str)
00614 
00615         opts['common']['arm.target.unalignedaccess'] = None
00616         if '-munaligned-access' in flags['common_flags']:
00617             opts['common']['arm.target.unalignedaccess'] = 'enabled'
00618             self.remove_option(flags['common_flags'], '-munaligned-access')
00619         elif '-mno-unaligned-access' in flags['common_flags']:
00620             opts['common']['arm.target.unalignedaccess'] = 'disabled'
00621             self.remove_option(flags['common_flags'], '-mno-unaligned-access')
00622 
00623         # Default optimisation level for Release.
00624         opts['common']['optimization.level'] = '-Os'
00625 
00626         # If the project defines an optimisation level, it is used
00627         # only for the Release configuration, the Debug one used '-Og'.
00628         str = self.find_options(flags['common_flags'], '-O')
00629         if str != None:
00630             levels = {
00631                 '-O0': 'none', '-O1': 'optimize', '-O2': 'more',
00632                 '-O3': 'most', '-Os': 'size', '-Og': 'debug'
00633             }
00634             if str in levels:
00635                 opts['common']['optimization.level'] = levels[str]
00636                 self.remove_option(flags['common_flags'], str)
00637 
00638         include_files = []
00639         for all_flags in [flags['common_flags'], flags['c_flags'], flags['cxx_flags']]:
00640             while '-include' in all_flags:
00641                 ix = all_flags.index('-include')
00642                 str = all_flags[ix + 1]
00643                 if str not in include_files:
00644                     include_files.append(str)
00645                 self.remove_option(all_flags, '-include')
00646                 self.remove_option(all_flags, str)
00647 
00648         opts['common']['include_files'] = include_files
00649 
00650         if '-ansi' in flags['c_flags']:
00651             opts['c']['compiler.std'] = '-ansi'
00652             self.remove_option(flags['c_flags'], str)
00653         else:
00654             str = self.find_options(flags['c_flags'], '-std')
00655             std = str[len('-std='):]
00656             c_std = {
00657                 'c90': 'c90', 'c89': 'c90', 'gnu90': 'gnu90', 'gnu89': 'gnu90',
00658                 'c99': 'c99', 'c9x': 'c99', 'gnu99': 'gnu99', 'gnu9x': 'gnu98',
00659                 'c11': 'c11', 'c1x': 'c11', 'gnu11': 'gnu11', 'gnu1x': 'gnu11'
00660             }
00661             if std in c_std:
00662                 opts['c']['compiler.std'] = c_std[std]
00663                 self.remove_option(flags['c_flags'], str)
00664 
00665         if '-ansi' in flags['cxx_flags']:
00666             opts['cpp']['compiler.std'] = '-ansi'
00667             self.remove_option(flags['cxx_flags'], str)
00668         else:
00669             str = self.find_options(flags['cxx_flags'], '-std')
00670             std = str[len('-std='):]
00671             cpp_std = {
00672                 'c++98': 'cpp98', 'c++03': 'cpp98',
00673                 'gnu++98': 'gnucpp98', 'gnu++03': 'gnucpp98',
00674                 'c++0x': 'cpp0x', 'gnu++0x': 'gnucpp0x',
00675                 'c++11': 'cpp11', 'gnu++11': 'gnucpp11',
00676                 'c++1y': 'cpp1y', 'gnu++1y': 'gnucpp1y',
00677                 'c++14': 'cpp14', 'gnu++14': 'gnucpp14',
00678                 'c++1z': 'cpp1z', 'gnu++1z': 'gnucpp1z',
00679             }
00680             if std in cpp_std:
00681                 opts['cpp']['compiler.std'] = cpp_std[std]
00682                 self.remove_option(flags['cxx_flags'], str)
00683 
00684         # Common optimisation options.
00685         optimization_options = {
00686             '-fmessage-length=0': 'optimization.messagelength',
00687             '-fsigned-char': 'optimization.signedchar',
00688             '-ffunction-sections': 'optimization.functionsections',
00689             '-fdata-sections': 'optimization.datasections',
00690             '-fno-common': 'optimization.nocommon',
00691             '-fno-inline-functions': 'optimization.noinlinefunctions',
00692             '-ffreestanding': 'optimization.freestanding',
00693             '-fno-builtin': 'optimization.nobuiltin',
00694             '-fsingle-precision-constant': 'optimization.spconstant',
00695             '-fPIC': 'optimization.PIC',
00696             '-fno-move-loop-invariants': 'optimization.nomoveloopinvariants',
00697         }
00698 
00699         for option in optimization_options:
00700             opts['common'][optimization_options[option]] = False
00701             if option in flags['common_flags']:
00702                 opts['common'][optimization_options[option]] = True
00703                 self.remove_option(flags['common_flags'], option)
00704 
00705         # Common warning options.
00706         warning_options = {
00707             '-fsyntax-only': 'warnings.syntaxonly',
00708             '-pedantic': 'warnings.pedantic',
00709             '-pedantic-errors': 'warnings.pedanticerrors',
00710             '-w': 'warnings.nowarn',
00711             '-Wunused': 'warnings.unused',
00712             '-Wuninitialized': 'warnings.uninitialized',
00713             '-Wall': 'warnings.allwarn',
00714             '-Wextra': 'warnings.extrawarn',
00715             '-Wmissing-declarations': 'warnings.missingdeclaration',
00716             '-Wconversion': 'warnings.conversion',
00717             '-Wpointer-arith': 'warnings.pointerarith',
00718             '-Wpadded': 'warnings.padded',
00719             '-Wshadow': 'warnings.shadow',
00720             '-Wlogical-op': 'warnings.logicalop',
00721             '-Waggregate-return': 'warnings.agreggatereturn',
00722             '-Wfloat-equal': 'warnings.floatequal',
00723             '-Werror': 'warnings.toerrors',
00724         }
00725 
00726         for option in warning_options:
00727             opts['common'][warning_options[option]] = False
00728             if option in flags['common_flags']:
00729                 opts['common'][warning_options[option]] = True
00730                 self.remove_option(flags['common_flags'], option)
00731 
00732         # Common debug options.
00733         debug_levels = {
00734             '-g': 'default',
00735             '-g1': 'minimal',
00736             '-g3': 'max',
00737         }
00738         opts['common']['debugging.level'] = 'none'
00739         for option in debug_levels:
00740             if option in flags['common_flags']:
00741                 opts['common'][
00742                     'debugging.level'] = debug_levels[option]
00743                 self.remove_option(flags['common_flags'], option)
00744 
00745         debug_formats = {
00746             '-ggdb': 'gdb',
00747             '-gstabs': 'stabs',
00748             '-gstabs+': 'stabsplus',
00749             '-gdwarf-2': 'dwarf2',
00750             '-gdwarf-3': 'dwarf3',
00751             '-gdwarf-4': 'dwarf4',
00752             '-gdwarf-5': 'dwarf5',
00753         }
00754 
00755         opts['common']['debugging.format'] = ''
00756         for option in debug_levels:
00757             if option in flags['common_flags']:
00758                 opts['common'][
00759                     'debugging.format'] = debug_formats[option]
00760                 self.remove_option(flags['common_flags'], option)
00761 
00762         opts['common']['debugging.prof'] = False
00763         if '-p' in flags['common_flags']:
00764             opts['common']['debugging.prof'] = True
00765             self.remove_option(flags['common_flags'], '-p')
00766 
00767         opts['common']['debugging.gprof'] = False
00768         if '-pg' in flags['common_flags']:
00769             opts['common']['debugging.gprof'] = True
00770             self.remove_option(flags['common_flags'], '-gp')
00771 
00772         # Assembler options.
00773         opts['as']['usepreprocessor'] = False
00774         while '-x' in flags['asm_flags']:
00775             ix = flags['asm_flags'].index('-x')
00776             str = flags['asm_flags'][ix + 1]
00777 
00778             if str == 'assembler-with-cpp':
00779                 opts['as']['usepreprocessor'] = True
00780             else:
00781                 # Collect all other assembler options.
00782                 opts['as']['other'] += ' -x ' + str
00783 
00784             self.remove_option(flags['asm_flags'], '-x')
00785             self.remove_option(flags['asm_flags'], 'assembler-with-cpp')
00786 
00787         opts['as']['nostdinc'] = False
00788         if '-nostdinc' in flags['asm_flags']:
00789             opts['as']['nostdinc'] = True
00790             self.remove_option(flags['asm_flags'], '-nostdinc')
00791 
00792         opts['as']['verbose'] = False
00793         if '-v' in flags['asm_flags']:
00794             opts['as']['verbose'] = True
00795             self.remove_option(flags['asm_flags'], '-v')
00796 
00797         # C options.
00798         opts['c']['nostdinc'] = False
00799         if '-nostdinc' in flags['c_flags']:
00800             opts['c']['nostdinc'] = True
00801             self.remove_option(flags['c_flags'], '-nostdinc')
00802 
00803         opts['c']['verbose'] = False
00804         if '-v' in flags['c_flags']:
00805             opts['c']['verbose'] = True
00806             self.remove_option(flags['c_flags'], '-v')
00807 
00808         warning_options = {
00809             '-Wmissing-prototypes': 'warnings.missingprototypes',
00810             '-Wstrict-prototypes': 'warnings.strictprototypes',
00811             '-Wbad-function-cast': 'warnings.badfunctioncast',
00812         }
00813 
00814         for option in warning_options:
00815             opts['c'][warning_options[option]] = False
00816             if option in flags['common_flags']:
00817                 opts['c'][warning_options[option]] = True
00818                 self.remove_option(flags['common_flags'], option)
00819 
00820         # C++ options.
00821         opts['cpp']['nostdinc'] = False
00822         if '-nostdinc' in flags['cxx_flags']:
00823             opts['cpp']['nostdinc'] = True
00824             self.remove_option(flags['cxx_flags'], '-nostdinc')
00825 
00826         opts['cpp']['nostdincpp'] = False
00827         if '-nostdinc++' in flags['cxx_flags']:
00828             opts['cpp']['nostdincpp'] = True
00829             self.remove_option(flags['cxx_flags'], '-nostdinc++')
00830 
00831         optimization_options = {
00832             '-fno-exceptions': 'optimization.noexceptions',
00833             '-fno-rtti': 'optimization.nortti',
00834             '-fno-use-cxa-atexit': 'optimization.nousecxaatexit',
00835             '-fno-threadsafe-statics': 'optimization.nothreadsafestatics',
00836         }
00837 
00838         for option in optimization_options:
00839             opts['cpp'][optimization_options[option]] = False
00840             if option in flags['cxx_flags']:
00841                 opts['cpp'][optimization_options[option]] = True
00842                 self.remove_option(flags['cxx_flags'], option)
00843             if option in flags['common_flags']:
00844                 opts['cpp'][optimization_options[option]] = True
00845                 self.remove_option(flags['common_flags'], option)
00846 
00847         warning_options = {
00848             '-Wabi': 'warnabi',
00849             '-Wctor-dtor-privacy': 'warnings.ctordtorprivacy',
00850             '-Wnoexcept': 'warnings.noexcept',
00851             '-Wnon-virtual-dtor': 'warnings.nonvirtualdtor',
00852             '-Wstrict-null-sentinel': 'warnings.strictnullsentinel',
00853             '-Wsign-promo': 'warnings.signpromo',
00854             '-Weffc++': 'warneffc',
00855         }
00856 
00857         for option in warning_options:
00858             opts['cpp'][warning_options[option]] = False
00859             if option in flags['cxx_flags']:
00860                 opts['cpp'][warning_options[option]] = True
00861                 self.remove_option(flags['cxx_flags'], option)
00862             if option in flags['common_flags']:
00863                 opts['cpp'][warning_options[option]] = True
00864                 self.remove_option(flags['common_flags'], option)
00865 
00866         opts['cpp']['verbose'] = False
00867         if '-v' in flags['cxx_flags']:
00868             opts['cpp']['verbose'] = True
00869             self.remove_option(flags['cxx_flags'], '-v')
00870 
00871         # Linker options.
00872         linker_options = {
00873             '-nostartfiles': 'nostart',
00874             '-nodefaultlibs': 'nodeflibs',
00875             '-nostdlib': 'nostdlibs',
00876         }
00877 
00878         for option in linker_options:
00879             opts['ld'][linker_options[option]] = False
00880             if option in flags['ld_flags']:
00881                 opts['ld'][linker_options[option]] = True
00882                 self.remove_option(flags['ld_flags'], option)
00883 
00884         opts['ld']['gcsections'] = False
00885         if '-Wl,--gc-sections' in flags['ld_flags']:
00886             opts['ld']['gcsections'] = True
00887             self.remove_option(flags['ld_flags'], '-Wl,--gc-sections')
00888 
00889         opts['ld']['flags'] = []
00890         to_remove = []
00891         for opt in flags['ld_flags']:
00892             if opt.startswith('-Wl,--wrap,'):
00893                 opts['ld']['flags'].append(
00894                     '--wrap=' + opt[len('-Wl,--wrap,'):])
00895                 to_remove.append(opt)
00896         for opt in to_remove:
00897             self.remove_option(flags['ld_flags'], opt)
00898 
00899         # Other tool remaining options are separated by category.
00900         opts['as']['otherwarnings'] = self.find_options(
00901             flags['asm_flags'], '-W')
00902 
00903         opts['c']['otherwarnings'] = self.find_options(
00904             flags['c_flags'], '-W')
00905         opts['c']['otheroptimizations'] = self.find_options(flags[
00906             'c_flags'], '-f')
00907 
00908         opts['cpp']['otherwarnings'] = self.find_options(
00909             flags['cxx_flags'], '-W')
00910         opts['cpp']['otheroptimizations'] = self.find_options(
00911             flags['cxx_flags'], '-f')
00912 
00913         # Other common remaining options are separated by category.
00914         opts['common']['optimization.other'] = self.find_options(
00915             flags['common_flags'], '-f')
00916         opts['common']['warnings.other'] = self.find_options(
00917             flags['common_flags'], '-W')
00918 
00919         # Remaining common flags are added to each tool.
00920         opts['as']['other'] += ' ' + \
00921             ' '.join(flags['common_flags']) + ' ' + \
00922             ' '.join(flags['asm_flags'])
00923         opts['c']['other'] += ' ' + \
00924             ' '.join(flags['common_flags']) + ' ' + ' '.join(flags['c_flags'])
00925         opts['cpp']['other'] += ' ' + \
00926             ' '.join(flags['common_flags']) + ' ' + \
00927             ' '.join(flags['cxx_flags'])
00928         opts['ld']['other'] += ' ' + \
00929             ' '.join(flags['common_flags']) + ' ' + ' '.join(flags['ld_flags'])
00930 
00931         if len(self.system_libraries) > 0:
00932             opts['ld']['other'] += ' -Wl,--start-group '
00933             opts['ld'][
00934                 'other'] += ' '.join('-l' + s for s in self.system_libraries)
00935             opts['ld']['other'] += ' -Wl,--end-group '
00936 
00937         # Strip all 'other' flags, since they might have leading spaces.
00938         opts['as']['other'] = opts['as']['other'].strip()
00939         opts['c']['other'] = opts['c']['other'].strip()
00940         opts['cpp']['other'] = opts['cpp']['other'].strip()
00941         opts['ld']['other'] = opts['ld']['other'].strip()
00942 
00943         if False:
00944             print
00945             print opts
00946 
00947             print
00948             print 'common_flags', flags['common_flags']
00949             print 'asm_flags', flags['asm_flags']
00950             print 'c_flags', flags['c_flags']
00951             print 'cxx_flags', flags['cxx_flags']
00952             print 'ld_flags', flags['ld_flags']
00953 
00954     @staticmethod
00955     def find_options(lst, option):
00956         tmp = [str for str in lst if str.startswith(option)]
00957         if len(tmp) > 0:
00958             return tmp[0]
00959         else:
00960             return None
00961 
00962     @staticmethod
00963     def find_options(lst, prefix):
00964         other = ''
00965         opts = [str for str in lst if str.startswith(prefix)]
00966         if len(opts) > 0:
00967             for opt in opts:
00968                 other += ' ' + opt
00969                 GNUARMEclipse.remove_option(lst, opt)
00970         return other.strip()
00971 
00972     @staticmethod
00973     def remove_option(lst, option):
00974         if option in lst:
00975             lst.remove(option)
00976 
00977 # =============================================================================