init

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers __init__.py Source File

__init__.py

00001 import os
00002 import copy
00003 
00004 from os.path import relpath, join, exists, dirname, basename
00005 from os import makedirs
00006 from json import load
00007 
00008 from tools.export.exporters import Exporter, apply_supported_whitelist
00009 from tools.targets import TARGET_MAP
00010 from tools.utils import NotSupportedException
00011 from tools.build_api import prepare_toolchain
00012 
00013 POST_BINARY_WHITELIST = set([
00014     "TEENSY3_1Code.binary_hook",
00015     "MCU_NRF51Code.binary_hook",
00016     "LPCTargetCode.lpc_patch",
00017     "LPC4088Code.binary_hook"
00018 ])
00019 
00020 
00021 class GNUARMNetbeans(Exporter):
00022     NAME = 'GNU ARM Netbeans'
00023     TOOLCHAIN = 'GCC_ARM'
00024 
00025     @classmethod
00026     def is_target_supported(cls, target_name):
00027         target = TARGET_MAP[target_name]
00028         return apply_supported_whitelist(
00029             cls.TOOLCHAIN, POST_BINARY_WHITELIST, target)
00030 
00031     @staticmethod
00032     def prepare_sys_lib(libname):
00033         return "-l" + libname
00034 
00035     def toolchain_flags(self, toolchain):
00036         """Returns a dictionary of toolchain flags.
00037         Keys of the dictionary are:
00038         cxx_flags    - c++ flags
00039         c_flags      - c flags
00040         ld_flags     - linker flags
00041         asm_flags    - assembler flags
00042         common_flags - common options
00043 
00044         The difference from the above is that it takes a parameter.
00045         """
00046 
00047         # Note: use the config options from the currently selected toolchain.
00048         config_header = self.toolchain.get_config_header()
00049 
00050         flags = {key + "_flags": copy.deepcopy(value) for key, value
00051                  in toolchain.flags.iteritems()}
00052         if config_header:
00053             config_header = relpath(config_header,
00054                                     self.resources.file_basepath[config_header])
00055             header_options = self.toolchain.get_config_option(config_header)
00056             flags['c_flags'] += header_options
00057             flags['cxx_flags'] += header_options
00058         return flags
00059 
00060     @staticmethod
00061     def get_defines_and_remove_from_flags(flags_in, str_key):
00062         defines = []
00063         flags_temp = copy.deepcopy(flags_in)
00064         for f in flags_temp[str_key]:
00065             f = f.strip()
00066             if f.startswith('-D'):
00067                 defines.append(f[2:])
00068                 flags_in[str_key].remove(f)
00069 
00070         return defines
00071 
00072     @staticmethod
00073     def get_includes_and_remove_from_flags(flags_in, str_key):
00074         includes = []
00075         flags_temp = copy.deepcopy(flags_in)
00076         next_is_include = False
00077         for f in flags_temp[str_key]:
00078             f = f.strip()
00079             if next_is_include:
00080                 includes.append(f)
00081                 flags_in[str_key].remove(f)
00082                 next_is_include = False
00083                 continue
00084             if f == "-include":
00085                 flags_in[str_key].remove(f)
00086                 next_is_include = True
00087 
00088         return includes
00089 
00090     @staticmethod
00091     def get_c_std_and_remove_from_flag(flags_in, str_key):
00092         comp_std = ''
00093         c_std = {
00094             'c90': 'c90', 'c89': 'c90', 'gnu90': 'gnu90', 'gnu89': 'gnu90',
00095             'c99': 'c99', 'c9x': 'c99', 'gnu99': 'gnu99', 'gnu9x': 'gnu98',
00096             'c11': 'c11', 'c1x': 'c11', 'gnu11': 'gnu11', 'gnu1x': 'gnu11'
00097         }
00098         cpp_std = {
00099             'c++98': 'cpp98', 'c++03': 'cpp98',
00100             'gnu++98': 'gnucpp98', 'gnu++03': 'gnucpp98',
00101             'c++0x': 'cpp0x', 'gnu++0x': 'gnucpp0x',
00102             'c++11': 'cpp11', 'gnu++11': 'gnucpp11',
00103             'c++1y': 'cpp1y', 'gnu++1y': 'gnucpp1y',
00104             'c++14': 'cpp14', 'gnu++14': 'gnucpp14',
00105             'c++1z': 'cpp1z', 'gnu++1z': 'gnucpp1z',
00106         }
00107 
00108         flags_temp = copy.deepcopy(flags_in)
00109         for f in flags_temp[str_key]:
00110             f = f.strip()
00111             if f.startswith('-std='):
00112                 comp_std = f[len('-std='):]
00113                 flags_in[str_key].remove(f)
00114             elif f.startswith('-'):
00115                 std = f[len('-'):]
00116                 if std in c_std or std in cpp_std:
00117                     comp_std = std
00118                     flags_in[str_key].remove(f)
00119         return comp_std
00120 
00121     def validate_resources(self):
00122         if not self.resources.linker_script:
00123             raise NotSupportedException("No linker script found.")
00124 
00125     def create_jinja_ctx(self):
00126         self.options = {}
00127         flags = {}
00128         self.validate_resources()
00129         # Convert all Backslashes to Forward Slashes
00130         self.resources.win_to_unix()
00131 
00132         print 'Include folders: {0}'.format(len(self.resources.inc_dirs))
00133 
00134         print 'Symbols: {0}'.format(len(self.toolchain.get_symbols()))
00135 
00136         self.ld_script = self.filter_dot(
00137             self.resources.linker_script)
00138         print 'Linker script: {0}'.format(self.ld_script)
00139 
00140         # Read in all profiles, we'll extract compiler options.
00141         profiles = self.get_all_profiles()
00142 
00143         profile_ids = [s.lower() for s in profiles]
00144         profile_ids.sort()
00145         for prof_id in profile_ids:
00146             # There are 4 categories of options, a category common too
00147             # all tools and a specific category for each of the tools.
00148             opts = {}
00149             opts['defines'] = {}
00150             opts['common'] = {}
00151             opts['as'] = {}
00152             opts['c'] = {}
00153             opts['cpp'] = {}
00154             opts['ld'] = {}
00155 
00156             opts['id'] = prof_id
00157             opts['name'] = opts['id'].capitalize()
00158 
00159             print
00160             print 'Build configuration: {0}'.format(opts['name'])
00161 
00162             profile = profiles[prof_id]
00163 
00164             # A small hack, do not bother with src_path again,
00165             # pass an empty string to avoid crashing.
00166             src_paths = ['']
00167             target_name = self.toolchain.target.name
00168 
00169             toolchain = prepare_toolchain(
00170                 src_paths, "", target_name, self.TOOLCHAIN, build_profile=[profile])
00171 
00172             flags = self.toolchain_flags(toolchain)
00173 
00174             print 'Common flags:', ' '.join(flags['common_flags'])
00175             print 'C++ flags:', ' '.join(flags['cxx_flags'])
00176             print 'C flags:', ' '.join(flags['c_flags'])
00177             print 'ASM flags:', ' '.join(flags['asm_flags'])
00178             print 'Linker flags:', ' '.join(flags['ld_flags'])
00179 
00180             opts['defines'] = self.get_defines_and_remove_from_flags(flags, 'common_flags')
00181             opts['forced_includes'] = self.get_includes_and_remove_from_flags(flags, 'common_flags')
00182             opts['common'] = flags['common_flags']
00183             opts['as'] = flags['asm_flags']
00184             opts['c'] = flags['c_flags']
00185             opts['cpp'] = flags['cxx_flags']
00186             opts['ld'] = flags['ld_flags']
00187 
00188             self.options[prof_id] = opts
00189 
00190         sources = []  # list of strings
00191 
00192         forced_includes = self.get_includes_and_remove_from_flags(flags, 'c_flags')
00193         forced_includes += self.get_includes_and_remove_from_flags(flags, 'cxx_flags')
00194 
00195         # Remove Duplicates
00196         forced_includes = list(set(forced_includes))
00197 
00198         c_std = self.get_c_std_and_remove_from_flag(flags, 'c_flags')
00199         cpp_std = self.get_c_std_and_remove_from_flag(flags, 'cxx_flags')
00200 
00201         # Make one list of all resources
00202         for r_type in ['c_sources', 's_sources', 'cpp_sources']:
00203             sources.extend(getattr(self.resources, r_type))
00204 
00205         # Remove all leading './'
00206         c_sources = [self.filter_dot(field) for field in self.resources.c_sources]
00207         cpp_sources = [self.filter_dot(field) for field in self.resources.cpp_sources]
00208         s_sources = [self.filter_dot(field) for field in self.resources.s_sources]
00209         headers = [self.filter_dot(field) for field in self.resources.headers]
00210         sources = [self.filter_dot(field) for field in sources]
00211         include_paths = [self.filter_dot(field) for field in self.resources.inc_dirs]
00212 
00213         sys_libs = [self.prepare_sys_lib(lib) for lib
00214                     in self.toolchain.sys_libs]
00215         preproc = " ".join([basename(self.toolchain.preproc[0])] +
00216                            self.toolchain.preproc[1:] +
00217                            self.toolchain.ld[1:])
00218 
00219         if 'nbproject' in include_paths:
00220             include_paths.remove('nbproject')
00221 
00222         jinja_ctx = {
00223             'name': self.project_name,
00224             'target': self.toolchain.target.name,
00225             'elf_location': join('BUILD', self.project_name) + '.elf',
00226             'c_symbols': self.toolchain.get_symbols(),
00227             'asm_symbols': self.toolchain.get_symbols(True),
00228             'c_flags': flags['c_flags'],
00229             'cxx_flags': flags['cxx_flags'],
00230             'ld_flags': self.flags['ld_flags'],
00231             'asm_flags': self.flags['asm_flags'],
00232             'common_flags': self.flags['common_flags'],
00233             'include_paths': include_paths,
00234             'forced_includes': forced_includes,
00235             'c_sources': c_sources,
00236             'cpp_sources': cpp_sources,
00237             's_sources': s_sources,
00238             'headers': headers,
00239             'headers_folder': self.get_netbeans_file_list(sorted(headers)),
00240             'sources_folder': self.get_netbeans_file_list(sorted(sources)),
00241             'options': self.options,
00242             'c_std': self.get_netbeans_c_std(c_std),
00243             'cpp_std': self.get_netbeans_cpp_std(cpp_std),
00244             'linker_script': self.ld_script,
00245             'linker_libs': sys_libs,
00246             'pp_cmd': preproc,
00247             'cc_cmd': self.toolchain.cc[0],
00248             'cppc_cmd': self.toolchain.cppc[0],
00249             'asm_cmd': self.toolchain.asm[0],
00250             'ld_cmd': self.toolchain.ld[0],
00251             'elf2bin_cmd': self.toolchain.elf2bin
00252         }
00253         return jinja_ctx
00254 
00255     def generate(self):
00256         """Generate Makefile, configurations.xml & project.xml Netbeans project file
00257         """
00258         jinja_ctx = self.create_jinja_ctx()
00259 
00260         print
00261         print 'Create a GNU ARM Netbeans C++ managed project'
00262         print 'Project name: {0}'.format(self.project_name)
00263         print 'Target: {0}'.format(self.toolchain.target.name)
00264         print 'Toolchain: {0}'.format(self.TOOLCHAIN)
00265 
00266         if not exists(join(self.export_dir, 'nbproject')):
00267             makedirs(join(self.export_dir, 'nbproject'))
00268 
00269         self.gen_file('nb/configurations.tmpl', jinja_ctx, 'nbproject/configurations.xml')
00270         self.gen_file('nb/project.tmpl', jinja_ctx, 'nbproject/project.xml')
00271         self.gen_file('nb/mbedignore.tmpl', jinja_ctx, '.mbedignore')
00272         self.gen_file('nb/Makefile.tmpl', jinja_ctx, 'Makefile')
00273 
00274         print
00275         print 'Done. Import the \'{0}\' project in Netbeans.'.format(self.project_name)
00276 
00277     # -------------------------------------------------------------------------
00278 
00279     @staticmethod
00280     def filter_dot(str_in):
00281         """
00282         Remove the './' prefix, if present.
00283         This function assumes that resources.win_to_unix()
00284         replaced all windows backslashes with slashes.
00285         """
00286         if str_in is None:
00287             return None
00288         if str_in[:2] == './':
00289             return str_in[2:]
00290         return str_in
00291 
00292     # -------------------------------------------------------------------------
00293 
00294     @staticmethod
00295     def get_all_profiles():
00296         tools_path = dirname(dirname(dirname(__file__)))
00297         file_names = [join(tools_path, "profiles", fn) for fn in os.listdir(
00298             join(tools_path, "profiles")) if fn.endswith(".json")]
00299 
00300         profiles = {}
00301 
00302         for fn in file_names:
00303             content = load(open(fn))
00304             profile_name = basename(fn).replace(".json", "")
00305             profiles[profile_name] = content
00306 
00307         return profiles
00308 
00309     @staticmethod
00310     def get_netbeans_file_list(file_list):
00311         cur_dir = ''
00312         prev_dir = ''
00313         output = []
00314         folder_count = 1
00315         dir_depth = 0
00316         for item in file_list:
00317             cur_dir = os.path.dirname(item)
00318             dir_temp = os.path.normpath(cur_dir)
00319             prev_dir_temp = os.path.normpath(prev_dir)
00320             dir_list = dir_temp.split(os.sep)
00321             prev_dir_list = prev_dir_temp.split(os.sep)
00322             dir_depth = len(dir_list)
00323 
00324             # Current File is in Directory: Compare the given dir with previous Dir
00325             if cur_dir and prev_dir != cur_dir:
00326                 # evaluate all matched items (from current and previous list)
00327                 matched = []
00328                 # Compare the Element in Previous Dir with the Elements in Current Dir
00329                 # and add the equal Elements to the match-List
00330                 for elem_prev_dir, elem_cur_dir in zip(prev_dir_list, dir_list):
00331                     if elem_prev_dir == elem_cur_dir:
00332                         matched.append(elem_cur_dir)
00333 
00334                 # calculate difference between matched and length
00335                 diff = dir_depth - len(matched)
00336 
00337                 # if previous dir was not root
00338                 if prev_dir != '':
00339                     # if the elements count is not equal we calculate the difference
00340                     if len(dir_list) != len(prev_dir_list):
00341                         dir_depth_prev = len(prev_dir_list)
00342                         delta = dir_depth_prev - len(matched)
00343 
00344                         for i in range(dir_depth_prev - delta, dir_depth_prev):
00345                             output.append('</logicalFolder>')
00346 
00347                     # if the elements count is equal, we subtract the matched length from the total length
00348                     else:
00349                         for i in range(len(matched), len(dir_list)):
00350                             output.append('</logicalFolder>')
00351 
00352                 for i in range(dir_depth - diff, dir_depth):
00353                     output.append('<logicalFolder name="f' + str(folder_count) + '" displayName="' + str(
00354                         dir_list[i]) + '" projectFiles="true">')
00355                     folder_count += 1
00356 
00357             # Current File is in root
00358             else:
00359                 # Close Tag if we are in root and the previous dir wasn't
00360                 if cur_dir == '' and prev_dir != '':
00361                     for i in range(0, len(prev_dir_list)):
00362                         output.append('</logicalFolder>')
00363 
00364             # Save the Current Dir
00365             prev_dir = cur_dir
00366             output.append('<itemPath>' + str(item) + '</itemPath>')
00367 
00368         if cur_dir != '':
00369             # close all open tags
00370             output.append('</logicalFolder>' * dir_depth)
00371 
00372         return output
00373 
00374     @staticmethod
00375     def get_netbeans_c_std(c_std):
00376         c_std_netbeans = 0
00377         if '89' in c_std:
00378             c_std_netbeans = 2
00379         elif '99' in c_std:
00380             c_std_netbeans = 3
00381         elif '11' in c_std:
00382             c_std_netbeans = 10
00383         return c_std_netbeans
00384 
00385     @staticmethod
00386     def get_netbeans_cpp_std(cpp_std):
00387         cpp_std_netbeans = 0
00388         if '98' in cpp_std:
00389             cpp_std_netbeans = 4
00390         elif '11' in cpp_std:
00391             cpp_std_netbeans = 8
00392         elif '14' in cpp_std:
00393             cpp_std_netbeans = 11
00394         return cpp_std_netbeans