mbed-os/tools/export/nb/__init__.py

402 lines
15 KiB
Python
Raw Normal View History

2017-11-20 09:02:08 +00:00
import os
import copy
2018-02-21 15:26:14 +00:00
import shutil
2017-11-20 09:02:08 +00:00
from os.path import relpath, join, exists, dirname, basename
2017-11-20 09:02:08 +00:00
from os import makedirs
from json import load
2017-11-20 09:02:08 +00:00
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
2017-11-20 09:02:08 +00:00
POST_BINARY_WHITELIST = set([
"TEENSY3_1Code.binary_hook",
"MCU_NRF51Code.binary_hook",
"LPCTargetCode.lpc_patch",
"LPC4088Code.binary_hook"
])
2017-11-20 09:02:08 +00:00
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)
2017-11-27 16:03:39 +00:00
@staticmethod
def prepare_sys_lib(libname):
return "-l" + libname
def toolchain_flags(self, toolchain):
2017-11-20 09:02:08 +00:00
"""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.
2017-11-20 09:02:08 +00:00
"""
# Note: use the config options from the currently selected toolchain.
2017-11-20 09:02:08 +00:00
config_header = self.toolchain.get_config_header()
2017-11-20 09:02:08 +00:00
flags = {key + "_flags": copy.deepcopy(value) for key, value
in toolchain.flags.iteritems()}
2017-11-20 09:02:08 +00:00
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
2017-11-20 09:02:08 +00:00
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)
2017-11-20 09:02:08 +00:00
return defines
2017-11-20 09:02:08 +00:00
@staticmethod
def get_includes_and_remove_from_flags(flags_in, str_key):
includes = []
flags_temp = copy.deepcopy(flags_in)
2017-11-20 09:02:08 +00:00
next_is_include = False
for f in flags_temp[str_key]:
2017-11-20 09:02:08 +00:00
f = f.strip()
if next_is_include:
includes.append(f)
flags_in[str_key].remove(f)
2017-11-20 09:02:08 +00:00
next_is_include = False
continue
if f == "-include":
flags_in[str_key].remove(f)
2017-11-20 09:02:08 +00:00
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()
2017-11-20 09:02:08 +00:00
# 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
2017-11-20 09:02:08 +00:00
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]
2017-11-20 09:02:08 +00:00
2017-11-27 16:03:39 +00:00
sys_libs = [self.prepare_sys_lib(lib) for lib
in self.toolchain.sys_libs]
2017-11-29 07:01:31 +00:00
preproc = " ".join([basename(self.toolchain.preproc[0])] +
2018-01-09 14:57:52 +00:00
self.toolchain.preproc[1:] +
self.toolchain.ld[1:])
2017-11-27 16:03:39 +00:00
if 'nbproject' in include_paths:
include_paths.remove('nbproject')
2017-11-20 09:02:08 +00:00
jinja_ctx = {
2017-11-20 09:02:08 +00:00
'name': self.project_name,
2017-11-27 16:03:39 +00:00
'target': self.toolchain.target.name,
2017-11-20 09:02:08 +00:00
'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'],
2017-11-20 09:02:08 +00:00
'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,
2017-11-20 09:02:08 +00:00
'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,
2017-11-27 16:03:39 +00:00
'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
2017-11-20 09:02:08 +00:00
}
return jinja_ctx
2017-11-20 09:02:08 +00:00
def generate(self):
"""Generate Makefile, configurations.xml & project.xml Netbeans project file
"""
jinja_ctx = self.create_jinja_ctx()
2017-11-20 09:02:08 +00:00
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)
2017-11-20 09:02:08 +00:00
if not exists(join(self.export_dir, 'nbproject')):
makedirs(join(self.export_dir, 'nbproject'))
2017-11-20 09:02:08 +00:00
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')
2017-11-20 09:02:08 +00:00
print
print 'Done. Import the \'{0}\' project in Netbeans.'.format(self.project_name)
2017-11-20 09:02:08 +00:00
2018-02-21 15:26:14 +00:00
@staticmethod
def clean(_):
shutil.rmtree("nbproject")
remove("Makefile")
# -------------------------------------------------------------------------
2017-11-20 09:02:08 +00:00
@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)):
2017-11-20 09:02:08 +00:00
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)
2017-11-20 09:02:08 +00:00
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