diff --git a/tools/export/gnuarmeclipse/.cproject.tmpl b/tools/export/gnuarmeclipse/.cproject.tmpl index 1071573afd..7146db0eda 100644 --- a/tools/export/gnuarmeclipse/.cproject.tmpl +++ b/tools/export/gnuarmeclipse/.cproject.tmpl @@ -19,24 +19,128 @@ + - + @@ -180,23 +396,135 @@ - - + diff --git a/tools/export/gnuarmeclipse/NOTES.md b/tools/export/gnuarmeclipse/NOTES.md new file mode 100644 index 0000000000..6c3068eec8 --- /dev/null +++ b/tools/export/gnuarmeclipse/NOTES.md @@ -0,0 +1,488 @@ +# GNU ARM Eclipse + +The [GNU ARM Eclipse](http://gnuarmeclipse.github.io) is an open source project that includes a family of Eclipse plug-ins and tools for multi-platform embedded ARM development, based on GNU toolchains. The project is hosted on [GitHub](https://github.com/gnuarmeclipse). + +This exporter aims to create managed GNU ARM Eclipse projects, ready to build, and with as many options properly identified and set in the Eclipse C/C++ Build -> Settings page. + +## Build plug-in configuration options + +All options are prefixed by `ilg.gnuarmeclipse.managedbuild.cross.option.`. + +### Target Processor + +- `arm.target.family` enumerated + - **ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.mcpu.cortex-m3** '-mcpu=cortex-m3' + - cortex-m0 '-mcpu=%s' + - cortex-m0-small-multiply '-mcpu=%s' + - cortex-m0plus '-mcpu=%s' + - cortex-m0plus-small-multiply '-mcpu=%s' + - cortex-m1 '-mcpu=%s' + - cortex-m1-small-multiply '-mcpu=%s' + - cortex-m4 '-mcpu=%s' + - cortex-m7 '-mcpu=%s' + +- `arm.target.architecture` enumerated + - **ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.arch.none** '' + - armv6-m '-march=%s' + - armv7-m '-march=%s' + +- `arm.target.instructionset` enumerated + - **ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.instructionset.thumb** '-mthumb' + - arm '-marm' + +- `arm.target.thumbinterwork` bool + - true `-mthumb-interwork` + +- `arm.target.endianness` enumerated + - **ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.endianness.default** + - little `-mlittle-endian` + - big `-mbig-endian` + +- `arm.target.fpu.abi` enumerated + - **ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.fpu.abi.default** + - soft '-mfloat-abi=soft' + - softfp '-mfloat-abi=softfp' + - hard '-mfloat-abi=hard' + +- `arm.target.fpu.unit` enumerated + - **ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.fpu.unit.default** + - fpv4spd16 '-mfpu=fpv4-sp-d16' + - fpv5d16 '-mfpu=fpv5-d16' + - fpv5spd16 '-mfpu=fpv5-sp-d16' + - ... + +- `arm.target.unalignedaccess` + - **ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.unalignedaccess.default** + - enabled '-munaligned-access' + - disabled '-mno-unaligned-access' + +- `target.other` string + +### Optimization + +- `optimization.level` enumerated + - **ilg.gnuarmeclipse.managedbuild.cross.option.optimization.level.more** '-O2' + - none '-O0' + - optimize '-O1' + - most '-O3' + - size '-Os' + - debug '-Og' + +- `optimization.messagelength` bool + - **false** '' + - true '-fmessage-length=0' + +- `optimization.signedchar` bool + - **false** + - true '-fsigned-char' + +- `optimization.functionsections` bool + - **false** + - true '-ffunction-sections' + +- `optimization.datasections` bool + - **false** + - true '-fdata-sections' + +- `optimization.nocommon` bool + - **false** + - true '-fno-common' + +- `optimization.noinlinefunctions` bool + - **false** '' + - true '-fno-inline-functions' + +- `optimization.freestanding` bool + - **false** '' + - true '-ffreestanding' + +- `optimization.nobuiltin` bool + - **false** '' + - true '-fno-builtin' + +- `optimization.spconstant` bool + - **false** '' + - true '-fsingle-precision-constant' + +- `optimization.PIC` bool + - **false** '' + - true '-fPIC' + +- `optimization.lto` bool + - **false** '' + - true '-flto' + +- `optimization.nomoveloopinvariants` bool + - **false** '' + - true '-fno-move-loop-invariants' + +- `optimization.other` string + +- `` bool + - **false** '' + - true '' + +### Warnings + +- `warnings.syntaxonly` bool + - **false** '' + - true '-fsyntax-only' + +- `warnings.pedantic` bool + - **false** '' + - true '-pedantic' + +- `warnings.pedanticerrors` bool + - **false** '' + - true '-pedantic-errors' + +- `warnings.nowarn` bool + - **false** '' + - true '-w' + +- `warnings.unused` bool + - **false** '' + - true '-Wunused' + +- `warnings.uninitialized` bool + - **false** '' + - true '-Wuninitialized' + +- `warnings.allwarn` bool + - **false** '' + - true '-Wall' + +- `warnings.extrawarn` bool + - **false** '' + - true '-Wextra' + +- `warnings.missingdeclaration` bool + - **false** '' + - true '-Wmissing-declarations' + +- `warnings.conversion` bool + - **false** '' + - true '-Wconversion' + +- `warnings.pointerarith` bool + - **false** '' + - true '-Wpointer-arith' + +- `warnings.padded` bool + - **false** '' + - true '-Wpadded' + +- `warnings.shadow` bool + - **false** '' + - true '-Wshadow' + +- `warnings.logicalop` bool + - **false** '' + - true '-Wlogical-op' + +- `warnings.agreggatereturn` bool + - **false** '' + - true '-Waggregate-return' + +- `warnings.floatequal` bool + - **false** '' + - true '-Wfloat-equal' + +- `warnings.toerrors` bool + - **false** '' + - true '-Werror' + +- `warnings.other` string + +### Debugging + +- `debugging.level` enumerated + - **ilg.gnuarmeclipse.managedbuild.cross.option.debugging.level.default** '-g' + - none '' + - minimal '-g1' + - max '-g3' + +- `debugging.format` enumerated + - **ilg.gnuarmeclipse.managedbuild.cross.option.debugging.format.default** '' + - gdb '-ggdb' + - stabs '-gstabs' + - stabsplus '-gstabs+' + - dwarf2 '-gdwarf-2' + - dwarf3 '-gdwarf-3' + - dwarf4 '-gdwarf-4' + - dwarf5 '-gdwarf-5' + +- `debugging.prof` bool + - **false** '' + - true '-p' + +- `debugging.gprof` bool + - **false** '' + - true '-pg' + +- `debugging.other` string + + +### Compiler common options + +- `verbose` bool + - **false** '' + - true '-v' + +- `savetemps` bool + - **false** '' + - true '--save-temps' + +- `nostdinc` bool + - **false** '' + - true '-nostdinc' + +- `asmlisting` bool + - **false** '' + - true '-Wa,-adhlns="$@.lst"' + +- `preprocessor.preprocessonly` bool + - **false** '' + - true '-E' + +- `dirs.include.files` + - '-include%s' + +- `compiler.other` string + +### Linker common options + +- `mapfilename` string + - '-Wl,-Map,"${BuildArtifactFileBaseName}.map"' + +- `linker.scriptfile` + - '-T %s' + +- `cref` bool + - **false** '' + - true '-Xlinker --cref' + +- `printmap` bool + - **false** '' + - true '-Xlinker --print-map' + +- `linker.nostart` bool + - **false** '' + - true '-nostartfiles' + +- `linker.nodeflibs` bool + - **false** '' + - true '-nodefaultlibs' + +- `linker.nostdlibs` bool + - **false** '' + - true '-nostdlib' + +- `linker.gcsections` bool + - **false** '' + - true '-Xlinker --gc-sections' + +- `linker.printgcsections` bool + - **false** '' + - true '-Xlinker --print-gc-sections' + +- `linker.strip` bool + - **false** '' + - true '-s' + +- `linker.other` string + +- `linker.usenewlibnano` bool + - **false** '' + - true '--specs=nano.specs' + +- `linker.useprintffloat` bool + - **false** '' + - true '-u \_printf\_float' + +- `linker.usescanffloat` bool + - **false** '' + - true '-u \_scanf\_float' + + +### Cross ARM GNU Assembler + +#### Preprocessor + +- `nostdinc` bool + - **false** '' + - true '-nostdinc' + +#### Includes + +#### Warnings + +#### Miscellaneous + +- `verbose` bool + - **false** '' + - true '-v' + +- `assembler.other` string + +### Cross ARM GNU C Compiler + +#### Preprocessor + +- `nostdinc` bool + - **false** '' + - true '-nostdinc' + +#### Includes + +#### Optimization + +- `c.compiler.std` enumerated + - **ilg.gnuarmeclipse.managedbuild.cross.option.c.compiler.std.default** '' + - ansi '-ansi' + - c90 '-std=c90' (c89) + - gnu90 '-std=gnu90' (gnu89) + - c99 '-std=c99' (c9x) + - gnu99 '-std=gnu99' (gnu9x) + - c11 '-std=c11' (c1x) + - gnu11 '-std=gnu11' (gnu1x) + +#### Warnings + +- `c.compiler.warning.missingprototypes` bool + - **false** '' + - true '-Wmissing-prototypes' + +- `c.compiler.warning.strictprototypes` bool + - **false** '' + - true '-Wstrict-prototypes' + +- `c.compiler.warning.badfunctioncast` bool + - **false** '' + - true '-Wbad-function-cast' + +#### Miscellaneous + +- `verbose` bool + - **false** '' + - true '-v' + +- `c.compiler.other` string + +### Cross ARM GNU C++ Compiler + +#### Preprocessor + +- `nostdinc` bool + - **false** '' + - true '-nostdinc' + +- `nostdincpp` bool + - **false** '' + - true '-nostdinc++' + +#### Includes + +#### Optimization + +- `cpp.compiler.std` enumerated + - **ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.std.default** '' + - ansi (-ansi) + - cpp98 '-std=c++98' (c++03) + - gnucpp98 '-std=gnu++98' (gnu++03) + - cpp0x '-std=c++0x' + - gnucpp0x '-std=gnu++0x' + - cpp11 '-std=c++11' + - gnucpp11 '-std=gnu++11' + - cpp1y '-std=c++1y' + - gnucpp1y '-std=gnu++1y' + - cpp14 '-std=c++14' + - gnucpp1 4'-std=gnu++14' + - cpp1z '-std=c++1z' + - gnucpp1z '-std=gnu++1z' + +- `cpp.compiler.abiversion` enumerated + - **ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.abiversion.0** '-fabi-version=0' + - default '' + - 1 '-fabi-version=1' + - 2 '-fabi-version=2' + - 3 '-fabi-version=3' + - 4 '-fabi-version=4' + - 5 '-fabi-version=5' + - 6 '-fabi-version=6' + +- `cpp.compiler.noexceptions` bool + - **false** '' + - true '-fno-exceptions' + +- `cpp.compiler.nortti` bool + - **false** '' + - true '-fno-rtti' + +- `cpp.compiler.nousecxaatexit` bool + - **false** '' + - true '-fno-use-cxa-atexit' + +- `cpp.compiler.nothreadsafestatics` bool + - **false** '' + - true '-fno-threadsafe-statics' + + +#### Warnings + +- `cpp.compiler.warnabi` bool + - **false** '-Wabi' + +- `cpp.compiler.warning.ctordtorprivacy` bool + - **false** '-Wctor-dtor-privacy' + +- `cpp.compiler.warning.noexcept` bool + - **false** '-Wnoexcept' + +- `cpp.compiler.warning.nonvirtualdtor` bool + - **false** '-Wnon-virtual-dtor' + +- `cpp.compiler.warning.strictnullsentinel` bool + - **false** '-Wstrict-null-sentinel' + +- `cpp.compiler.warning.signpromo` bool + - **false** '-Wsign-promo' + +- `cpp.compiler.warneffc` bool + - **false** '' + - true '-Weffc++' + +#### Miscellaneous + +- `verbose` bool + - **false** '' + - true '-v' + +- `cpp.compiler.other` string + +### Cross ARM GNU C++ Linker + +#### General + +- `cpp.linker.nostart` + - **false** '' + - true '-nostartfiles' + +- `cpp.linker.nodeflibs` + - **false** '' + - true '-nodefaultlibs' + +- `cpp.linker.nostdlibs` + - **false** '' + - true '-nostdlib' + +#### Libraries + +#### Miscellaneous + + +## Template + +- `` bool + - **false** '' + - true '' diff --git a/tools/export/gnuarmeclipse/__init__.py b/tools/export/gnuarmeclipse/__init__.py index 8f751f88e7..c67c6267ba 100644 --- a/tools/export/gnuarmeclipse/__init__.py +++ b/tools/export/gnuarmeclipse/__init__.py @@ -26,10 +26,13 @@ from os.path import splitext, basename, relpath, dirname from random import randint import os import copy +# import logging from tools.targets import TARGET_MAP from tools.utils import NotSupportedException +# ============================================================================= + class UID: """ @@ -44,6 +47,8 @@ class UID: # Each invocation generates a new number. u = UID() +# ============================================================================= + class GNUARMEclipse(Exporter): NAME = 'GNU ARM Eclipse' @@ -53,17 +58,7 @@ class GNUARMEclipse(Exporter): TARGETS = [target for target, obj in TARGET_MAP.iteritems() if 'GCC_ARM' in obj.supported_toolchains] - @staticmethod - def filter_dot(s): - """ - Remove the './' prefix, if present. - """ - if s == None: - return None - if s[:2] == './': - return s[2:] - return s - + # override @property def flags(self): """Returns a dictionary of toolchain flags. @@ -88,48 +83,184 @@ class GNUARMEclipse(Exporter): config_header) return flags - def dump_tree(self, node, depth=0): - for k in node.keys(): - n = node[k] - pn = n['parent']['name'] if 'parent' in n.keys() else '' - print ' ' * depth, n['name'], n['is_used'], pn - if len(n['children'].keys()) != 0: - self.dump_tree(n['children'], depth + 1) - - def dump_paths(self, node, depth=0): - for k in node.keys(): - n = node[k] - x = [] - ni = n - while True: - x.insert(0, ni['name']) - if 'parent' not in ni: - break - ni = ni['parent'] - path = '/'.join(x) - print path, n['is_used'] - self.dump_paths(n['children'], depth + 1) - - def recurse_excludings(self, node): + # override + def generate(self): """ - Recurse the tree and collect all unused folders; descend - the hierarchy only for used nodes. + Generate the .project and .cproject files. """ - for k in node.keys(): - n = node[k] - if n['is_used'] == False: - x = [] - ni = n - while True: - x.insert(0, ni['name']) - if 'parent' not in ni: - break - ni = ni['parent'] - path = '/'.join(x) - # print path - self.excluded_folders.append(path) - else: - self.recurse_excludings(n['children']) + 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 'Build configurations: Debug & Release' + + self.resources.win_to_unix() + + # TODO: use some logger to display additional info if verbose + + # There are 4 categories of options, a category common too + # all tools and a specific category for each of the tools. + self.options = {} + self.options['common'] = {} + self.options['as'] = {} + self.options['c'] = {} + self.options['cpp'] = {} + self.options['ld'] = {} + + 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' + ] + + # TODO: get the list from existing .cproject + build_folders = ['Debug', 'Release', 'BUILD'] + + 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.process_options() + + self.options['as']['defines'] = self.toolchain.get_symbols(True) + self.options['c']['defines'] = self.toolchain.get_symbols() + self.options['cpp']['defines'] = self.toolchain.get_symbols() + print 'Symbols: {0}'.format(len(self.options['cpp']['defines'])) + + self.options['common']['include_paths'] = [ + self.filter_dot(s) for s in self.resources.inc_dirs] + print 'Include folders: {0}'.format(len(self.options['common']['include_paths'])) + self.options['common']['excluded_folders'] = '|'.join( + self.excluded_folders) + + self.options['ld']['library_paths'] = [ + self.filter_dot(s) for s in self.resources.lib_dirs] + + self.options['ld']['object_files'] = objects + + self.options['ld']['user_libraries'] = libraries + + self.options['ld']['system_libraries'] = self.system_libraries + + self.options['ld']['script'] = self.filter_dot( + self.resources.linker_script) + print 'Linker script: {0}'.format(self.options['ld']['script']) + + ctx = { + 'name': self.project_name, + + # Compiler & linker command line options + 'options': self.options, + + # Unique IDs used in multiple places. + # Those used only once are implemented with {{u.id}}. + 'debug_config_uid': u.id, + 'debug_tool_c_compiler_uid': u.id, + 'debug_tool_c_compiler_input_uid': u.id, + 'debug_tool_cpp_compiler_uid': u.id, + 'debug_tool_cpp_compiler_input_uid': u.id, + 'release_config_uid': u.id, + 'release_tool_c_compiler_uid': u.id, + 'release_tool_c_compiler_input_uid': u.id, + 'release_tool_cpp_compiler_uid': u.id, + 'release_tool_cpp_compiler_input_uid': u.id, + + # Must be an object with an `id` property, which + # will be called repeatedly, to generate multiple UIDs. + 'u': u, + } + + # TODO: it would be good to have jinja stop if one of the + # expected context values is not defined. + self.gen_file('gnuarmeclipse/.project.tmpl', ctx, '.project') + self.gen_file('gnuarmeclipse/.cproject.tmpl', ctx, '.cproject') + + print 'Done.' + + # override + @staticmethod + def build(project_name, log_name="build_log.txt", cleanup=True): + """ + Build GNU ARM Eclipse project. + """ + + ret_code = 0 + + # TODO: add code to run the build in a headless configuration. + + # Cleanup the exported and built files + if cleanup: + os.remove('.project') + os.remove('.cproject') + # TODO: remove Debug, Release + + if ret_code != 0: + # Seems like something went wrong. + return -1 + else: + return 0 + + # ------------------------------------------------------------------------- + # 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 + + top_folders = [f for f in set(s.split(os.sep)[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): + if len(dirs) == 0: + self.add_source_folder_to_tree(root) + + for folder in source_folders: + self.add_source_folder_to_tree(folder, True) + + # print + # print self.source_tree + # self.dump_paths(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): """ @@ -155,67 +286,105 @@ class GNUARMEclipse(Exporter): p = n[s] n = n[s]['children'] - def compute_exclusions(self): + def recurse_excludings(self, node): """ - 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 + 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)] - source_folders.remove('.') - # print 'source folders' - # print source_folders + for k in node.keys(): + n = node[k] + if n['is_used'] == False: + x = [] + ni = n + while True: + x.insert(0, ni['name']) + if 'parent' not in ni: + break + ni = ni['parent'] + path = '/'.join(x) + # print path + self.excluded_folders.append(path) + else: + self.recurse_excludings(n['children']) - top_folders = [f for f in set(s.split(os.sep)[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): - if len(dirs) == 0: - self.add_source_folder_to_tree(root) - - for folder in source_folders: - self.add_source_folder_to_tree(folder, True) - - # print - # print self.source_tree - # self.dump_paths(self.source_tree) - - # print 'excludings' - self.excluded_folders = [] - self.recurse_excludings(self.source_tree) - - def generate(self): + @staticmethod + def filter_dot(s): """ - Override the parent method with code generation. + Remove the './' prefix, if present. + This function assumes that resources.win_to_unix() + replaced all windows backslashes with slashes. """ - if not self.resources.linker_script: - raise NotSupportedException("No linker script found.") + if s == None: + return None + if s[:2] == './': + return s[2:] + return s - self.resources.win_to_unix() + # ------------------------------------------------------------------------- - print 'libraries' - print self.resources.libraries + def dump_tree(self, node, depth=0): + for k in node.keys(): + n = node[k] + pn = n['parent']['name'] if 'parent' in n.keys() else '' + print ' ' * depth, n['name'], n['is_used'], pn + if len(n['children'].keys()) != 0: + self.dump_tree(n['children'], depth + 1) - libraries = [] - print self.resources.libraries - for lib in self.resources.libraries: - l, _ = splitext(basename(lib)) - libraries.append(l[3:]) + def dump_paths(self, node, depth=0): + for k in node.keys(): + n = node[k] + x = [] + ni = n + while True: + x.insert(0, ni['name']) + if 'parent' not in ni: + break + ni = ni['parent'] + path = '/'.join(x) + print path, n['is_used'] + self.dump_paths(n['children'], depth + 1) + + # ------------------------------------------------------------------------- + + def process_options(self): + """ + 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. + + Althogh this process does not have a very complicated logic, + given the large number of explicit configuration options + used by GNU ARM Eclipse managed build plug-in, it is tedious... + """ + flags = self.flags + + 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. + self.options['as']['other'] = '' + self.options['c']['other'] = '' + self.options['cpp']['other'] = '' + self.options['ld']['other'] = '' MCPUS = { 'Cortex-M0': {'mcpu': 'cortex-m0', 'fpu_unit': None}, @@ -229,119 +398,449 @@ class GNUARMEclipse(Exporter): 'Cortex-M7FD': {'mcpu': 'cortex-m7', 'fpu_unit': 'fpv5d16'}, } + # 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 - try: - # cortex-m0, cortex-m0-small-multiply, cortex-m0plus, - # cortex-m0plus-small-multiply, cortex-m1, cortex-m1-small-multiply, - # cortex-m3, cortex-m4, cortex-m7. - target_mcpu = MCPUS[core]['mcpu'] + self.options['common']['arm.target.family'] = None - # default, fpv4spd16, fpv5d16, fpv5spd16 - target_fpu_unit = MCPUS[core]['fpu_unit'] + # 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: + self.options['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)) + self.options['common']['arm.target.family'] = MCPUS[core]['mcpu'] - # soft, softfp, hard. - target_fpu_abi = 'soft' + self.options['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'} + if arch in archs: + self.options['common']['arm.target.arch'] = archs[arh] + self.remove_option(flags['common_flags'], str) - if target_fpu_unit != None: - target_fpu_abi = 'hard' + self.options['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']: + self.options['common']['arm.target.instructionset'] = 'arm' + self.remove_option(flags['common_flags'], '-marm') + self.remove_option(flags['ld_flags'], '-marm') - except AttributeError: - # TODO filter out projects with toolchain core not supported, - # instead of raising an exception. - raise NotSupportedException( - 'Target core {0} not supported.'.format(core)) + self.options['common']['arm.target.thumbinterwork'] = False + if '-mthumb-interwork' in flags['common_flags']: + self.options['common']['arm.target.thumbinterwork'] = True + self.remove_option(flags['common_flags'], '-mthumb-interwork') - # TODO: get the list from existing .cproject - build_folders = ['Debug', 'Release'] + self.options['common']['arm.target.endianness'] = None + if '-mlittle-endian' in flags['common_flags']: + self.options['common']['arm.target.endianness'] = 'little' + self.remove_option(flags['common_flags'], '-mlittle-endian') + elif '-mbig-endian' in flags['common_flags']: + self.options['common']['arm.target.endianness'] = 'big' + self.remove_option(flags['common_flags'], '-mbig-endian') - 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.options['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: + self.options['common']['arm.target.fpu.unit'] = fpus[fpu] - f = self.flags - print 'common_flags' - print f['common_flags'] - print 'asm_flags' - print f['asm_flags'] - print 'c_flags' - print f['c_flags'] - print 'cxx_flags' - print f['cxx_flags'] - print 'ld_flags' - print f['ld_flags'] + self.remove_option(flags['common_flags'], str) + self.remove_option(flags['ld_flags'], str) + if self.options['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']: + self.options['common'][ + 'arm.target.fpu.unit'] = MCPUS[core]['fpu_unit'] - 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)] - source_folders.remove('.') + # soft, softfp, hard. + str = self.find_options(flags['common_flags'], '-mfloat-abi=') + if str != None: + self.options['common']['arm.target.fpu.abi'] = str[ + len('-mfloat-abi='):] + self.remove_option(flags['common_flags'], str) + self.remove_option(flags['ld_flags'], str) - self.compute_exclusions() + self.options['common']['arm.target.unalignedaccess'] = None + if '-munaligned-access' in flags['common_flags']: + self.options['common']['arm.target.unalignedaccess'] = 'enabled' + self.remove_option(flags['common_flags'], '-munaligned-access') + elif '-mno-unaligned-access' in flags['common_flags']: + self.options['common']['arm.target.unalignedaccess'] = 'disabled' + self.remove_option(flags['common_flags'], '-mno-unaligned-access') - asm_defines = self.toolchain.get_symbols(True) - c_defines = self.toolchain.get_symbols() - cpp_defines = self.toolchain.get_symbols() + # Default optimisation level for Release. + self.options['common']['optimization.level'] = '-Os' - include_paths = [self.filter_dot(s) for s in self.resources.inc_dirs] + # 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: + self.options['common']['optimization.level'] = levels[str] + self.remove_option(flags['common_flags'], str) - library_paths = [self.filter_dot(s) for s in self.resources.lib_dirs] + 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) - linker_script = self.filter_dot(self.resources.linker_script) + self.options['common']['include_files'] = include_files - ctx = { - 'name': self.project_name, - 'excluded_folders': '|'.join(self.excluded_folders), - 'include_paths': include_paths, - 'library_paths': library_paths, - 'object_files': objects, - 'libraries': libraries, - 'linker_script': linker_script, - 'asm_defines': asm_defines, - 'c_defines': c_defines, - 'cpp_defines': cpp_defines, - 'target_mcpu': target_mcpu, - 'target_fpu_abi': target_fpu_abi, - 'target_fpu_unit': target_fpu_unit, + if '-ansi' in flags['c_flags']: + self.options['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: + self.options['c']['compiler.std'] = c_std[std] + self.remove_option(flags['c_flags'], str) - # Unique IDs used each in multiple places. - # Add more if needed. - 'debug_config_uid': u.id, - 'debug_tool_c_compiler_uid': u.id, - 'debug_tool_c_compiler_input_uid': u.id, - 'debug_tool_cpp_compiler_uid': u.id, - 'debug_tool_cpp_compiler_input_uid': u.id, - 'release_config_uid': u.id, - 'release_tool_c_compiler_uid': u.id, - 'release_tool_c_compiler_input_uid': u.id, - 'release_tool_cpp_compiler_uid': u.id, - 'release_tool_cpp_compiler_input_uid': u.id, + if '-ansi' in flags['cxx_flags']: + self.options['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: + self.options['cpp']['compiler.std'] = cpp_std[std] + self.remove_option(flags['cxx_flags'], str) - # Must be an object with an `id` property, which - # will be called repeatedly, to generate multiple UIDs. - 'u': u, + # 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', } - self.gen_file('gnuarmeclipse/.project.tmpl', ctx, '.project') - self.gen_file('gnuarmeclipse/.cproject.tmpl', ctx, '.cproject') + for option in optimization_options: + self.options['common'][optimization_options[option]] = False + if option in flags['common_flags']: + self.options['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: + self.options['common'][warning_options[option]] = False + if option in flags['common_flags']: + self.options['common'][warning_options[option]] = True + self.remove_option(flags['common_flags'], option) + + # Common debug options. + debug_levels = { + '-g': 'default', + '-g1': 'minimal', + '-g3': 'max', + } + self.options['common']['debugging.level'] = 'none' + for option in debug_levels: + if option in flags['common_flags']: + self.options['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', + } + + self.options['common']['debugging.format'] = '' + for option in debug_levels: + if option in flags['common_flags']: + self.options['common'][ + 'debugging.format'] = debug_formats[option] + self.remove_option(flags['common_flags'], option) + + self.options['common']['debugging.prof'] = False + if '-p' in flags['common_flags']: + self.options['common']['debugging.prof'] = True + self.remove_option(flags['common_flags'], '-p') + + self.options['common']['debugging.gprof'] = False + if '-pg' in flags['common_flags']: + self.options['common']['debugging.gprof'] = True + self.remove_option(flags['common_flags'], '-gp') + + # Assembler options. + self.options['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': + self.options['as']['usepreprocessor'] = True + else: + # Collect all other assembler options. + self.options['as']['other'] += ' -x ' + str + + self.remove_option(flags['asm_flags'], '-x') + self.remove_option(flags['asm_flags'], 'assembler-with-cpp') + + self.options['as']['nostdinc'] = False + if '-nostdinc' in flags['asm_flags']: + self.options['as']['nostdinc'] = True + self.remove_option(flags['asm_flags'], '-nostdinc') + + self.options['as']['verbose'] = False + if '-v' in flags['asm_flags']: + self.options['as']['verbose'] = True + self.remove_option(flags['asm_flags'], '-v') + + # C options. + self.options['c']['nostdinc'] = False + if '-nostdinc' in flags['c_flags']: + self.options['c']['nostdinc'] = True + self.remove_option(flags['c_flags'], '-nostdinc') + + self.options['c']['verbose'] = False + if '-v' in flags['c_flags']: + self.options['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: + self.options['c'][warning_options[option]] = False + if option in flags['common_flags']: + self.options['c'][warning_options[option]] = True + self.remove_option(flags['common_flags'], option) + + # C++ options. + self.options['cpp']['nostdinc'] = False + if '-nostdinc' in flags['cxx_flags']: + self.options['cpp']['nostdinc'] = True + self.remove_option(flags['cxx_flags'], '-nostdinc') + + self.options['cpp']['nostdincpp'] = False + if '-nostdinc++' in flags['cxx_flags']: + self.options['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: + self.options['cpp'][optimization_options[option]] = False + if option in flags['cxx_flags']: + self.options['cpp'][optimization_options[option]] = True + self.remove_option(flags['cxx_flags'], option) + if option in flags['common_flags']: + self.options['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: + self.options['cpp'][warning_options[option]] = False + if option in flags['cxx_flags']: + self.options['cpp'][warning_options[option]] = True + self.remove_option(flags['cxx_flags'], option) + if option in flags['common_flags']: + self.options['cpp'][warning_options[option]] = True + self.remove_option(flags['common_flags'], option) + + self.options['cpp']['verbose'] = False + if '-v' in flags['cxx_flags']: + self.options['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: + self.options['ld'][linker_options[option]] = False + if option in flags['ld_flags']: + self.options['ld'][linker_options[option]] = True + self.remove_option(flags['ld_flags'], option) + + self.options['ld']['gcsections'] = False + if '-Wl,--gc-sections' in flags['ld_flags']: + self.options['ld']['gcsections'] = True + self.remove_option(flags['ld_flags'], '-Wl,--gc-sections') + + self.options['ld']['flags'] = [] + to_remove = [] + for opt in flags['ld_flags']: + if opt.startswith('-Wl,--wrap,'): + self.options['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. + self.options['as']['otherwarnings'] = self.find_options( + flags['asm_flags'], '-W') + + self.options['c']['otherwarnings'] = self.find_options( + flags['c_flags'], '-W') + self.options['c']['otheroptimizations'] = self.find_options(flags[ + 'c_flags'], '-f') + + self.options['cpp']['otherwarnings'] = self.find_options( + flags['cxx_flags'], '-W') + self.options['cpp']['otheroptimizations'] = self.find_options( + flags['cxx_flags'], '-f') + + # Other common remaining options are separated by category. + self.options['common']['optimization.other'] = self.find_options( + flags['common_flags'], '-f') + self.options['common']['warnings.other'] = self.find_options( + flags['common_flags'], '-W') + + # Remaining common flags are added to each tool. + self.options['as']['other'] += ' ' + \ + ' '.join(flags['common_flags']) + ' ' + \ + ' '.join(flags['asm_flags']) + self.options['c']['other'] += ' ' + \ + ' '.join(flags['common_flags']) + ' ' + ' '.join(flags['c_flags']) + self.options['cpp']['other'] += ' ' + \ + ' '.join(flags['common_flags']) + ' ' + \ + ' '.join(flags['cxx_flags']) + self.options['ld']['other'] += ' ' + \ + ' '.join(flags['common_flags']) + ' ' + ' '.join(flags['ld_flags']) + + if len(self.system_libraries) > 0: + self.options['ld']['other'] += ' -Wl,--start-group ' + self.options['ld'][ + 'other'] += ' '.join('-l' + s for s in self.system_libraries) + self.options['ld']['other'] += ' -Wl,--end-group ' + + # Strip all 'other' flags, since they might have leading spaces. + self.options['as']['other'] = self.options['as']['other'].strip() + self.options['c']['other'] = self.options['c']['other'].strip() + self.options['cpp']['other'] = self.options['cpp']['other'].strip() + self.options['ld']['other'] = self.options['ld']['other'].strip() + + if False: + print + print self.options + + 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 build(project_name, log_name="build_log.txt", cleanup=True): - """ - Build GNU ARM Eclipse project. - """ - - ret_code = 0 - - # TODO: add code to run the build in a headless configuration. - - # Cleanup the exported and built files - if cleanup: - os.remove('.project') - os.remove('.cproject') - - if ret_code !=0: - # Seems like something went wrong. - return -1 + def find_options(lst, option): + tmp = [str for str in lst if str.startswith(option)] + if len(tmp) > 0: + return tmp[0] else: - return 0 + 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) + +# =============================================================================