Merge pull request #1957 from mbedmicro/config_headers

Configuration in header files - step 1
pull/1959/head
Martin Kojtal 2016-06-16 18:47:15 +02:00 committed by GitHub
commit 1e18ea230a
8 changed files with 97 additions and 14 deletions

View File

@ -232,8 +232,8 @@ def build_project(src_path, build_path, target, toolchain_name,
prev_features = features
config.validate_config()
# And add the configuration macros to the toolchain
toolchain.add_macros(config.get_config_data_macros())
# Set the toolchain's config header with the config data
toolchain.set_config_header_content(config.get_config_data_header())
# Compile Sources
objects = toolchain.compile_sources(resources, build_path, resources.inc_dirs)
@ -391,8 +391,8 @@ def build_library(src_paths, build_path, target, toolchain_name,
prev_features = features
config.validate_config()
# And add the configuration macros to the toolchain
toolchain.add_macros(config.get_config_data_macros())
# Set the toolchain's config header with the config data
toolchain.set_config_header_content(config.get_config_data_header())
# Copy headers, objects and static libraries - all files needed for static lib
toolchain.copy_files(resources.headers, build_path, resources=resources)

View File

@ -132,8 +132,10 @@ class ConfigMacro:
if len(tmp) != 2:
raise ValueError("Invalid macro definition '%s' in '%s'" % (name, self.defined_by))
self.macro_name = tmp[0]
self.macro_value = tmp[1]
else:
self.macro_name = name
self.macro_value = None
# 'Config' implements the mbed configuration mechanism
class Config:
@ -343,13 +345,13 @@ class Config:
# Return the configuration data in two parts:
# - params: a dictionary with (name, ConfigParam) entries
# - macros: the list of macros defined with "macros" in libraries and in the application
# - macros: the list of macros defined with "macros" in libraries and in the application (as ConfigMacro instances)
def get_config_data(self):
all_params = self.get_target_config_data()
lib_params, macros = self.get_lib_config_data()
all_params.update(lib_params)
self.get_app_config_data(all_params, macros)
return all_params, [m.name for m in macros.values()]
return all_params, macros
# Helper: verify if there are any required parameters without a value in 'params'
def _check_required_parameters(self, params):
@ -363,11 +365,17 @@ class Config:
def parameters_to_macros(params):
return ['%s=%s' % (m.macro_name, m.value) for m in params.values() if m.value is not None]
# Return the macro definitions generated for a dictionary of ConfigMacros (as returned by get_config_data)
# params: a dictionary of (name, ConfigMacro instance) mappings
@staticmethod
def config_macros_to_macros(macros):
return [m.name for m in macros.values()]
# Return the configuration data converted to a list of C macros
def get_config_data_macros(self):
params, macros = self.get_config_data()
self._check_required_parameters(params)
return macros + self.parameters_to_macros(params)
return self.config_macros_to_macros(macros) + self.parameters_to_macros(params)
# Returns any features in the configuration data
def get_features(self):
@ -387,4 +395,43 @@ class Config:
if self.config_errors:
raise self.config_errors[0]
return True
# Return the configuration data converted to the content of a C header file,
# meant to be included to a C/C++ file. The content is returned as a string.
# If 'fname' is given, the content is also written to the file called "fname".
# WARNING: if 'fname' names an existing file, that file will be overwritten!
def get_config_data_header(self, fname = None):
params, macros = self.get_config_data()
self._check_required_parameters(params)
header_data = "// Automatically generated configuration file.\n"
header_data += "// DO NOT EDIT, content will be overwritten.\n\n"
header_data += "#ifndef __MBED_CONFIG_DATA__\n"
header_data += "#define __MBED_CONFIG_DATA__\n\n"
# Compute maximum length of macro names for proper alignment
max_param_macro_name_len = max([len(m.macro_name) for m in params.values() if m.value is not None]) if params else 0
max_direct_macro_name_len = max([len(m.macro_name) for m in macros.values()]) if macros else 0
max_macro_name_len = max(max_param_macro_name_len, max_direct_macro_name_len)
# Compute maximum length of macro values for proper alignment
max_param_macro_val_len = max([len(str(m.value)) for m in params.values() if m.value is not None]) if params else 0
max_direct_macro_val_len = max([len(m.macro_value or "") for m in macros.values()]) if macros else 0
max_macro_val_len = max(max_param_macro_val_len, max_direct_macro_val_len)
# Generate config parameters first
if params:
header_data += "// Configuration parameters\n"
for m in params.values():
if m.value is not None:
header_data += "#define {0:<{1}} {2!s:<{3}} // set by {4}\n".format(m.macro_name, max_macro_name_len, m.value, max_macro_val_len, m.set_by)
# Then macros
if macros:
header_data += "// Macros\n"
for m in macros.values():
if m.macro_value:
header_data += "#define {0:<{1}} {2!s:<{3}} // defined by {4}\n".format(m.macro_name, max_macro_name_len, m.macro_value, max_macro_val_len, m.defined_by)
else:
header_data += "#define {0:<{1}} // defined by {2}\n".format(m.macro_name, max_macro_name_len + max_macro_val_len + 1, m.defined_by)
header_data += "\n#endif\n"
# If fname is given, write "header_data" to it
if fname:
with open(fname, "wt") as f:
f.write(header_data)
return header_data

View File

@ -62,7 +62,7 @@ if __name__ == '__main__':
options.prefix = options.prefix or [""]
try:
params, macros = get_config(options.source_dir, target, toolchain)
params, macros, features = get_config(options.source_dir, target, toolchain)
if not params and not macros:
print "No configuration data available."
_exit(0)
@ -79,7 +79,7 @@ if __name__ == '__main__':
print "Macros"
print "------"
if macros:
print 'Defined with "macros":', macros
print 'Defined with "macros":', Config.config_macros_to_macros(macros)
print "Generated from configuration parameters:", Config.parameters_to_macros(params)
except KeyboardInterrupt, e:

View File

@ -16,7 +16,7 @@ limitations under the License.
"""
from tools.build_api import get_config
from tools.config import ConfigException
from tools.config import ConfigException, Config
import os, sys
# Compare the output of config against a dictionary of known good results
@ -44,6 +44,7 @@ def test_tree(full_name, name):
err_msg = None
try:
cfg, macros, features = get_config(full_name, target, "GCC_ARM")
macros = Config.config_macros_to_macros(macros)
except ConfigException as e:
err_msg = e.message
if err_msg:

View File

@ -270,6 +270,9 @@ class mbedToolchain:
self.flags = deepcopy(self.DEFAULT_FLAGS)
# config_header_content will hold the content of the config header (if used)
self.config_header_content = None
def get_output(self):
return self.output
@ -879,6 +882,26 @@ class mbedToolchain:
map_csv = splitext(map)[0] + "_map.csv"
memap.generate_output('csv-ci', map_csv)
# "Prefix headers" are automatically included by the compiler at the beginning of
# each source file. They are used to provide configuration data.
# header_content - the content of the config header file.
def set_config_header_content(self, header_content):
self.config_header_content = header_content
# Return the location of the config header. This function will create the config
# header first if needed. The header will be written in a file called "mbed_conf.h"
# located in the project's build directory.
# If config headers are not used (self.config_header_content is None), the function
# returns None
def get_config_header(self):
if self.config_header_content is None:
return None
config_file = join(self.build_dir, "mbed_conf.h")
if not exists(config_file):
with open(config_file, "wt") as f:
f.write(self.config_header_content)
return config_file
from tools.settings import ARM_BIN
from tools.settings import GCC_ARM_PATH, GCC_CR_PATH

View File

@ -115,7 +115,11 @@ class ARM(mbedToolchain):
return ["--depend", dep_path]
def get_compile_options(self, defines, includes):
return ['-D%s' % d for d in defines] + ['--via', self.get_inc_file(includes)]
opts = ['-D%s' % d for d in defines] + ['--via', self.get_inc_file(includes)]
config_header = self.get_config_header()
if config_header is not None:
opts = opts + ['--preinclude', config_header]
return opts
@hook_tool
def assemble(self, source, object, includes):

View File

@ -166,7 +166,11 @@ class GCC(mbedToolchain):
return ["-MD", "-MF", dep_path]
def get_compile_options(self, defines, includes):
return ['-D%s' % d for d in defines] + ['@%s' % self.get_inc_file(includes)]
opts = ['-D%s' % d for d in defines] + ['@%s' % self.get_inc_file(includes)]
config_header = self.get_config_header()
if config_header is not None:
opts = opts + ['-include', config_header]
return opts
@hook_tool
def assemble(self, source, object, includes):

View File

@ -127,7 +127,11 @@ class IAR(mbedToolchain):
return ["-l", base + '.s.txt']
def get_compile_options(self, defines, includes):
return ['-D%s' % d for d in defines] + ['-f', self.get_inc_file(includes)]
opts = ['-D%s' % d for d in defines] + ['-f', self.get_inc_file(includes)]
config_header = self.get_config_header()
if config_header is not None:
opts = opts + ['--preinclude', config_header]
return opts
@hook_tool
def assemble(self, source, object, includes):