Anders Blomdell / mbed-sdk-tools
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers __init__.py Source File

__init__.py

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