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