Skip to content

Commit 1e18ea2

Browse files
authored
Merge pull request #1957 from mbedmicro/config_headers
Configuration in header files - step 1
2 parents 42bc1a4 + a164224 commit 1e18ea2

File tree

8 files changed

+97
-14
lines changed

8 files changed

+97
-14
lines changed

tools/build_api.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,8 @@ def build_project(src_path, build_path, target, toolchain_name,
232232
prev_features = features
233233
config.validate_config()
234234

235-
# And add the configuration macros to the toolchain
236-
toolchain.add_macros(config.get_config_data_macros())
235+
# Set the toolchain's config header with the config data
236+
toolchain.set_config_header_content(config.get_config_data_header())
237237

238238
# Compile Sources
239239
objects = toolchain.compile_sources(resources, build_path, resources.inc_dirs)
@@ -391,8 +391,8 @@ def build_library(src_paths, build_path, target, toolchain_name,
391391
prev_features = features
392392
config.validate_config()
393393

394-
# And add the configuration macros to the toolchain
395-
toolchain.add_macros(config.get_config_data_macros())
394+
# Set the toolchain's config header with the config data
395+
toolchain.set_config_header_content(config.get_config_data_header())
396396

397397
# Copy headers, objects and static libraries - all files needed for static lib
398398
toolchain.copy_files(resources.headers, build_path, resources=resources)

tools/config.py

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,10 @@ def __init__(self, name, unit_name, unit_kind):
132132
if len(tmp) != 2:
133133
raise ValueError("Invalid macro definition '%s' in '%s'" % (name, self.defined_by))
134134
self.macro_name = tmp[0]
135+
self.macro_value = tmp[1]
135136
else:
136137
self.macro_name = name
138+
self.macro_value = None
137139

138140
# 'Config' implements the mbed configuration mechanism
139141
class Config:
@@ -343,13 +345,13 @@ def get_app_config_data(self, params, macros):
343345

344346
# Return the configuration data in two parts:
345347
# - params: a dictionary with (name, ConfigParam) entries
346-
# - macros: the list of macros defined with "macros" in libraries and in the application
348+
# - macros: the list of macros defined with "macros" in libraries and in the application (as ConfigMacro instances)
347349
def get_config_data(self):
348350
all_params = self.get_target_config_data()
349351
lib_params, macros = self.get_lib_config_data()
350352
all_params.update(lib_params)
351353
self.get_app_config_data(all_params, macros)
352-
return all_params, [m.name for m in macros.values()]
354+
return all_params, macros
353355

354356
# Helper: verify if there are any required parameters without a value in 'params'
355357
def _check_required_parameters(self, params):
@@ -363,11 +365,17 @@ def _check_required_parameters(self, params):
363365
def parameters_to_macros(params):
364366
return ['%s=%s' % (m.macro_name, m.value) for m in params.values() if m.value is not None]
365367

368+
# Return the macro definitions generated for a dictionary of ConfigMacros (as returned by get_config_data)
369+
# params: a dictionary of (name, ConfigMacro instance) mappings
370+
@staticmethod
371+
def config_macros_to_macros(macros):
372+
return [m.name for m in macros.values()]
373+
366374
# Return the configuration data converted to a list of C macros
367375
def get_config_data_macros(self):
368376
params, macros = self.get_config_data()
369377
self._check_required_parameters(params)
370-
return macros + self.parameters_to_macros(params)
378+
return self.config_macros_to_macros(macros) + self.parameters_to_macros(params)
371379

372380
# Returns any features in the configuration data
373381
def get_features(self):
@@ -387,4 +395,43 @@ def validate_config(self):
387395
if self.config_errors:
388396
raise self.config_errors[0]
389397
return True
390-
398+
399+
# Return the configuration data converted to the content of a C header file,
400+
# meant to be included to a C/C++ file. The content is returned as a string.
401+
# If 'fname' is given, the content is also written to the file called "fname".
402+
# WARNING: if 'fname' names an existing file, that file will be overwritten!
403+
def get_config_data_header(self, fname = None):
404+
params, macros = self.get_config_data()
405+
self._check_required_parameters(params)
406+
header_data = "// Automatically generated configuration file.\n"
407+
header_data += "// DO NOT EDIT, content will be overwritten.\n\n"
408+
header_data += "#ifndef __MBED_CONFIG_DATA__\n"
409+
header_data += "#define __MBED_CONFIG_DATA__\n\n"
410+
# Compute maximum length of macro names for proper alignment
411+
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
412+
max_direct_macro_name_len = max([len(m.macro_name) for m in macros.values()]) if macros else 0
413+
max_macro_name_len = max(max_param_macro_name_len, max_direct_macro_name_len)
414+
# Compute maximum length of macro values for proper alignment
415+
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
416+
max_direct_macro_val_len = max([len(m.macro_value or "") for m in macros.values()]) if macros else 0
417+
max_macro_val_len = max(max_param_macro_val_len, max_direct_macro_val_len)
418+
# Generate config parameters first
419+
if params:
420+
header_data += "// Configuration parameters\n"
421+
for m in params.values():
422+
if m.value is not None:
423+
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)
424+
# Then macros
425+
if macros:
426+
header_data += "// Macros\n"
427+
for m in macros.values():
428+
if m.macro_value:
429+
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)
430+
else:
431+
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)
432+
header_data += "\n#endif\n"
433+
# If fname is given, write "header_data" to it
434+
if fname:
435+
with open(fname, "wt") as f:
436+
f.write(header_data)
437+
return header_data

tools/get_config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
options.prefix = options.prefix or [""]
6363

6464
try:
65-
params, macros = get_config(options.source_dir, target, toolchain)
65+
params, macros, features = get_config(options.source_dir, target, toolchain)
6666
if not params and not macros:
6767
print "No configuration data available."
6868
_exit(0)
@@ -79,7 +79,7 @@
7979
print "Macros"
8080
print "------"
8181
if macros:
82-
print 'Defined with "macros":', macros
82+
print 'Defined with "macros":', Config.config_macros_to_macros(macros)
8383
print "Generated from configuration parameters:", Config.parameters_to_macros(params)
8484

8585
except KeyboardInterrupt, e:

tools/test/config_test/config_test.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"""
1717

1818
from tools.build_api import get_config
19-
from tools.config import ConfigException
19+
from tools.config import ConfigException, Config
2020
import os, sys
2121

2222
# Compare the output of config against a dictionary of known good results
@@ -44,6 +44,7 @@ def test_tree(full_name, name):
4444
err_msg = None
4545
try:
4646
cfg, macros, features = get_config(full_name, target, "GCC_ARM")
47+
macros = Config.config_macros_to_macros(macros)
4748
except ConfigException as e:
4849
err_msg = e.message
4950
if err_msg:

tools/toolchains/__init__.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,9 @@ def __init__(self, target, options=None, notify=None, macros=None, silent=False,
270270

271271
self.flags = deepcopy(self.DEFAULT_FLAGS)
272272

273+
# config_header_content will hold the content of the config header (if used)
274+
self.config_header_content = None
275+
273276
def get_output(self):
274277
return self.output
275278

@@ -879,6 +882,26 @@ def mem_stats(self, map):
879882
map_csv = splitext(map)[0] + "_map.csv"
880883
memap.generate_output('csv-ci', map_csv)
881884

885+
# "Prefix headers" are automatically included by the compiler at the beginning of
886+
# each source file. They are used to provide configuration data.
887+
# header_content - the content of the config header file.
888+
def set_config_header_content(self, header_content):
889+
self.config_header_content = header_content
890+
891+
# Return the location of the config header. This function will create the config
892+
# header first if needed. The header will be written in a file called "mbed_conf.h"
893+
# located in the project's build directory.
894+
# If config headers are not used (self.config_header_content is None), the function
895+
# returns None
896+
def get_config_header(self):
897+
if self.config_header_content is None:
898+
return None
899+
config_file = join(self.build_dir, "mbed_conf.h")
900+
if not exists(config_file):
901+
with open(config_file, "wt") as f:
902+
f.write(self.config_header_content)
903+
return config_file
904+
882905

883906
from tools.settings import ARM_BIN
884907
from tools.settings import GCC_ARM_PATH, GCC_CR_PATH

tools/toolchains/arm.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,11 @@ def get_dep_option(self, object):
115115
return ["--depend", dep_path]
116116

117117
def get_compile_options(self, defines, includes):
118-
return ['-D%s' % d for d in defines] + ['--via', self.get_inc_file(includes)]
118+
opts = ['-D%s' % d for d in defines] + ['--via', self.get_inc_file(includes)]
119+
config_header = self.get_config_header()
120+
if config_header is not None:
121+
opts = opts + ['--preinclude', config_header]
122+
return opts
119123

120124
@hook_tool
121125
def assemble(self, source, object, includes):

tools/toolchains/gcc.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,11 @@ def get_dep_option(self, object):
166166
return ["-MD", "-MF", dep_path]
167167

168168
def get_compile_options(self, defines, includes):
169-
return ['-D%s' % d for d in defines] + ['@%s' % self.get_inc_file(includes)]
169+
opts = ['-D%s' % d for d in defines] + ['@%s' % self.get_inc_file(includes)]
170+
config_header = self.get_config_header()
171+
if config_header is not None:
172+
opts = opts + ['-include', config_header]
173+
return opts
170174

171175
@hook_tool
172176
def assemble(self, source, object, includes):

tools/toolchains/iar.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,11 @@ def cc_extra(self, object):
127127
return ["-l", base + '.s.txt']
128128

129129
def get_compile_options(self, defines, includes):
130-
return ['-D%s' % d for d in defines] + ['-f', self.get_inc_file(includes)]
130+
opts = ['-D%s' % d for d in defines] + ['-f', self.get_inc_file(includes)]
131+
config_header = self.get_config_header()
132+
if config_header is not None:
133+
opts = opts + ['--preinclude', config_header]
134+
return opts
131135

132136
@hook_tool
133137
def assemble(self, source, object, includes):

0 commit comments

Comments
 (0)