import os import copy from os.path import relpath, join, exists, dirname, basename from os import makedirs 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 def toolchain_flags(self, toolchain): """Returns a dictionary of toolchain flags. Keys of the dictionary are: cxx_flags - c++ flags c_flags - c flags ld_flags - linker flags asm_flags - assembler flags common_flags - common options The difference from the above is that it takes a parameter. """ # Note: use the config options from the currently selected toolchain. config_header = self.toolchain.get_config_header() flags = {key + "_flags": copy.deepcopy(value) for key, value in toolchain.flags.iteritems()} if config_header: config_header = relpath(config_header, self.resources.file_basepath[config_header]) header_options = self.toolchain.get_config_option(config_header) flags['c_flags'] += header_options flags['cxx_flags'] += header_options return flags @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() print 'Include folders: {0}'.format(len(self.resources.inc_dirs)) print 'Symbols: {0}'.format(len(self.toolchain.get_symbols())) self.ld_script = self.filter_dot( self.resources.linker_script) print 'Linker script: {0}'.format(self.ld_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() print print 'Build configuration: {0}'.format(opts['name']) 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) print 'Common flags:', ' '.join(flags['common_flags']) print 'C++ flags:', ' '.join(flags['cxx_flags']) print 'C flags:', ' '.join(flags['c_flags']) print 'ASM flags:', ' '.join(flags['asm_flags']) print 'Linker flags:', ' '.join(flags['ld_flags']) 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() print print 'Create a GNU ARM Netbeans C++ managed project' print 'Project name: {0}'.format(self.project_name) print 'Target: {0}'.format(self.toolchain.target.name) print 'Toolchain: {0}'.format(self.TOOLCHAIN) 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('nb/mbedignore.tmpl', jinja_ctx, '.mbedignore') self.gen_file('nb/Makefile.tmpl', jinja_ctx, 'Makefile') print print 'Done. Import the \'{0}\' project in Netbeans.'.format(self.project_name) # ------------------------------------------------------------------------- @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 = [] for element in dir_list: if element in prev_dir_list: matched.append(element) # 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('') # 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('') for i in range(dir_depth - diff, dir_depth): output.append('') 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('') # Save the Current Dir prev_dir = cur_dir output.append('' + str(item) + '') if cur_dir != '': # close all open tags output.append('' * 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