Clone of official tools
Diff: export/nb/__init__.py
- Revision:
- 43:2a7da56ebd24
diff -r 2cf3f29fece1 -r 2a7da56ebd24 export/nb/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/export/nb/__init__.py Tue Sep 25 13:43:09 2018 -0500 @@ -0,0 +1,358 @@ +from __future__ import print_function, absolute_import +from builtins import str + +import os +import copy +import shutil + +from os.path import relpath, join, exists, dirname, basename +from os import makedirs, remove +from json import load + +from tools.export.exporters import Exporter, apply_supported_whitelist +from tools.targets import TARGET_MAP +from tools.utils import NotSupportedException +from tools.build_api import prepare_toolchain + +POST_BINARY_WHITELIST = set([ + "TEENSY3_1Code.binary_hook", + "MCU_NRF51Code.binary_hook", + "LPCTargetCode.lpc_patch", + "LPC4088Code.binary_hook" +]) + + +class GNUARMNetbeans(Exporter): + NAME = 'GNU ARM Netbeans' + TOOLCHAIN = 'GCC_ARM' + + @classmethod + def is_target_supported(cls, target_name): + target = TARGET_MAP[target_name] + return apply_supported_whitelist( + cls.TOOLCHAIN, POST_BINARY_WHITELIST, target) + + @staticmethod + def prepare_sys_lib(libname): + return "-l" + libname + + @staticmethod + def get_defines_and_remove_from_flags(flags_in, str_key): + defines = [] + flags_temp = copy.deepcopy(flags_in) + for f in flags_temp[str_key]: + f = f.strip() + if f.startswith('-D'): + defines.append(f[2:]) + flags_in[str_key].remove(f) + + return defines + + @staticmethod + def get_includes_and_remove_from_flags(flags_in, str_key): + includes = [] + flags_temp = copy.deepcopy(flags_in) + next_is_include = False + for f in flags_temp[str_key]: + f = f.strip() + if next_is_include: + includes.append(f) + flags_in[str_key].remove(f) + next_is_include = False + continue + if f == "-include": + flags_in[str_key].remove(f) + next_is_include = True + + return includes + + @staticmethod + def get_c_std_and_remove_from_flag(flags_in, str_key): + comp_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' + } + 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', + } + + flags_temp = copy.deepcopy(flags_in) + for f in flags_temp[str_key]: + f = f.strip() + if f.startswith('-std='): + comp_std = f[len('-std='):] + flags_in[str_key].remove(f) + elif f.startswith('-'): + std = f[len('-'):] + if std in c_std or std in cpp_std: + comp_std = std + flags_in[str_key].remove(f) + return comp_std + + def validate_resources(self): + if not self.resources.linker_script: + raise NotSupportedException("No linker script found.") + + def create_jinja_ctx(self): + self.options = {} + flags = {} + self.validate_resources() + # Convert all Backslashes to Forward Slashes + self.resources.win_to_unix() + + self.ld_script = self.filter_dot( + self.resources.linker_script) + + # Read in all profiles, we'll extract compiler options. + profiles = self.get_all_profiles() + + profile_ids = [s.lower() for s in profiles] + profile_ids.sort() + for prof_id in profile_ids: + # There are 4 categories of options, a category common too + # all tools and a specific category for each of the tools. + opts = {} + opts['defines'] = {} + opts['common'] = {} + opts['as'] = {} + opts['c'] = {} + opts['cpp'] = {} + opts['ld'] = {} + + opts['id'] = prof_id + opts['name'] = opts['id'].capitalize() + + profile = profiles[prof_id] + + # A small hack, do not bother with src_path again, + # pass an empty string to avoid crashing. + src_paths = [''] + target_name = self.toolchain.target.name + + toolchain = prepare_toolchain( + src_paths, "", target_name, self.TOOLCHAIN, build_profile=[profile]) + + flags = self.toolchain_flags(toolchain) + + opts['defines'] = self.get_defines_and_remove_from_flags(flags, 'common_flags') + opts['forced_includes'] = self.get_includes_and_remove_from_flags(flags, 'common_flags') + opts['common'] = flags['common_flags'] + opts['as'] = flags['asm_flags'] + opts['c'] = flags['c_flags'] + opts['cpp'] = flags['cxx_flags'] + opts['ld'] = flags['ld_flags'] + + self.options[prof_id] = opts + + sources = [] # list of strings + + forced_includes = self.get_includes_and_remove_from_flags(flags, 'c_flags') + forced_includes += self.get_includes_and_remove_from_flags(flags, 'cxx_flags') + + # Remove Duplicates + forced_includes = list(set(forced_includes)) + + c_std = self.get_c_std_and_remove_from_flag(flags, 'c_flags') + cpp_std = self.get_c_std_and_remove_from_flag(flags, 'cxx_flags') + + # Make one list of all resources + for r_type in ['c_sources', 's_sources', 'cpp_sources']: + sources.extend(getattr(self.resources, r_type)) + + # Remove all leading './' + c_sources = [self.filter_dot(field) for field in self.resources.c_sources] + cpp_sources = [self.filter_dot(field) for field in self.resources.cpp_sources] + s_sources = [self.filter_dot(field) for field in self.resources.s_sources] + headers = [self.filter_dot(field) for field in self.resources.headers] + sources = [self.filter_dot(field) for field in sources] + include_paths = [self.filter_dot(field) for field in self.resources.inc_dirs] + + sys_libs = [self.prepare_sys_lib(lib) for lib + in self.toolchain.sys_libs] + preproc = " ".join([basename(self.toolchain.preproc[0])] + + self.toolchain.preproc[1:] + + self.toolchain.ld[1:]) + + if 'nbproject' in include_paths: + include_paths.remove('nbproject') + + jinja_ctx = { + 'name': self.project_name, + 'target': self.toolchain.target.name, + 'elf_location': join('BUILD', self.project_name) + '.elf', + 'c_symbols': self.toolchain.get_symbols(), + 'asm_symbols': self.toolchain.get_symbols(True), + 'c_flags': flags['c_flags'], + 'cxx_flags': flags['cxx_flags'], + 'ld_flags': self.flags['ld_flags'], + 'asm_flags': self.flags['asm_flags'], + 'common_flags': self.flags['common_flags'], + 'include_paths': include_paths, + 'forced_includes': forced_includes, + 'c_sources': c_sources, + 'cpp_sources': cpp_sources, + 's_sources': s_sources, + 'headers': headers, + 'headers_folder': self.get_netbeans_file_list(sorted(headers)), + 'sources_folder': self.get_netbeans_file_list(sorted(sources)), + 'options': self.options, + 'c_std': self.get_netbeans_c_std(c_std), + 'cpp_std': self.get_netbeans_cpp_std(cpp_std), + 'linker_script': self.ld_script, + 'linker_libs': sys_libs, + 'pp_cmd': preproc, + 'cc_cmd': self.toolchain.cc[0], + 'cppc_cmd': self.toolchain.cppc[0], + 'asm_cmd': self.toolchain.asm[0], + 'ld_cmd': self.toolchain.ld[0], + 'elf2bin_cmd': self.toolchain.elf2bin + } + return jinja_ctx + + def generate(self): + """Generate Makefile, configurations.xml & project.xml Netbeans project file + """ + jinja_ctx = self.create_jinja_ctx() + + if not exists(join(self.export_dir, 'nbproject')): + makedirs(join(self.export_dir, 'nbproject')) + + self.gen_file('nb/configurations.tmpl', jinja_ctx, 'nbproject/configurations.xml') + self.gen_file('nb/project.tmpl', jinja_ctx, 'nbproject/project.xml') + self.gen_file_nonoverwrite('nb/mbedignore.tmpl', jinja_ctx, + '.mbedignore') + self.gen_file('nb/Makefile.tmpl', jinja_ctx, 'Makefile') + + print('Done. Import the \'{0}\' project in Netbeans.'.format(self.project_name)) + + @staticmethod + def clean(_): + shutil.rmtree("nbproject") + remove("Makefile") + + # ------------------------------------------------------------------------- + + @staticmethod + def filter_dot(str_in): + """ + Remove the './' prefix, if present. + This function assumes that resources.win_to_unix() + replaced all windows backslashes with slashes. + """ + if str_in is None: + return None + if str_in[:2] == './': + return str_in[2:] + return str_in + + # ------------------------------------------------------------------------- + + @staticmethod + def get_all_profiles(): + tools_path = dirname(dirname(dirname(__file__))) + file_names = [join(tools_path, "profiles", fn) for fn in os.listdir( + join(tools_path, "profiles")) if fn.endswith(".json")] + + profiles = {} + + for fn in file_names: + content = load(open(fn)) + profile_name = basename(fn).replace(".json", "") + profiles[profile_name] = content + + return profiles + + @staticmethod + def get_netbeans_file_list(file_list): + cur_dir = '' + prev_dir = '' + output = [] + folder_count = 1 + dir_depth = 0 + for item in file_list: + cur_dir = os.path.dirname(item) + dir_temp = os.path.normpath(cur_dir) + prev_dir_temp = os.path.normpath(prev_dir) + dir_list = dir_temp.split(os.sep) + prev_dir_list = prev_dir_temp.split(os.sep) + dir_depth = len(dir_list) + + # Current File is in Directory: Compare the given dir with previous Dir + if cur_dir and prev_dir != cur_dir: + # evaluate all matched items (from current and previous list) + matched = [] + # Compare the Element in Previous Dir with the Elements in Current Dir + # and add the equal Elements to the match-List + for elem_prev_dir, elem_cur_dir in zip(prev_dir_list, dir_list): + if elem_prev_dir == elem_cur_dir: + matched.append(elem_cur_dir) + + # calculate difference between matched and length + diff = dir_depth - len(matched) + + # if previous dir was not root + if prev_dir != '': + # if the elements count is not equal we calculate the difference + if len(dir_list) != len(prev_dir_list): + dir_depth_prev = len(prev_dir_list) + delta = dir_depth_prev - len(matched) + + for i in range(dir_depth_prev - delta, dir_depth_prev): + output.append('</logicalFolder>') + + # if the elements count is equal, we subtract the matched length from the total length + else: + for i in range(len(matched), len(dir_list)): + output.append('</logicalFolder>') + + for i in range(dir_depth - diff, dir_depth): + output.append('<logicalFolder name="f' + str(folder_count) + '" displayName="' + str( + dir_list[i]) + '" projectFiles="true">') + folder_count += 1 + + # Current File is in root + else: + # Close Tag if we are in root and the previous dir wasn't + if cur_dir == '' and prev_dir != '': + for i in range(0, len(prev_dir_list)): + output.append('</logicalFolder>') + + # Save the Current Dir + prev_dir = cur_dir + output.append('<itemPath>' + str(item) + '</itemPath>') + + if cur_dir != '': + # close all open tags + output.append('</logicalFolder>' * dir_depth) + + return output + + @staticmethod + def get_netbeans_c_std(c_std): + c_std_netbeans = 0 + if '89' in c_std: + c_std_netbeans = 2 + elif '99' in c_std: + c_std_netbeans = 3 + elif '11' in c_std: + c_std_netbeans = 10 + return c_std_netbeans + + @staticmethod + def get_netbeans_cpp_std(cpp_std): + cpp_std_netbeans = 0 + if '98' in cpp_std: + cpp_std_netbeans = 4 + elif '11' in cpp_std: + cpp_std_netbeans = 8 + elif '14' in cpp_std: + cpp_std_netbeans = 11 + return cpp_std_netbeans