Skip to content

Commit ab0a5a5

Browse files
author
Bogdan Marinescu
committed
Configuration in header files - step 1
The current implementation of the configuration system "compiles" the configuration parameters to macros defined on the command line. This works, but has a few limitations: - it can bring back the maximum command line length issues in Windows. - depending on the type of the configuration parameter, it might require special quoting of its value in the command line. - various 3rd party IDE/tools seem to have some limitations regarding the total length of the macros that can be defined. This commit is the first step in replacing the current mechanism with one that generates configuration in header files that will be automatically included, instead of command line macro definitions. The commit only adds the method for generating the header file, it doesn't actually change the way config is used yet (that will happen in step 2), thus it is backwards compatible. The logic of the configuration system itself is unchanged (in fact, the whole change (not only this commit) is supposed to be completely transparent for the users of the configuration system). The commit also fixes an issue in tools/get_config.py that appeared as a result of a recent PR: the signature of "get_config" in tools/build_api.py changed, but tools/get_config.py was not updated.
1 parent 135a4ee commit ab0a5a5

File tree

3 files changed

+57
-7
lines changed

3 files changed

+57
-7
lines changed

tools/config.py

Lines changed: 53 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,45 @@ 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 %s%s%s%s// set by %s\n" % (m.macro_name, " " * (max_macro_name_len - len(m.macro_name) + 1), str(m.value),
424+
" " * (max_macro_val_len - len(str(m.value)) + 1), m.set_by)
425+
# Then macros
426+
if macros:
427+
header_data += "// Macros\n"
428+
for m in macros.values():
429+
if m.macro_value:
430+
header_data += "#define %s%s%s%s// defined by %s\n" % (m.macro_name, " " * (max_macro_name_len - len(m.macro_name) + 1), str(m.macro_value),
431+
" " * (max_macro_val_len - len(str(m.macro_value)) + 1), m.defined_by)
432+
else:
433+
header_data += "#define %s%s// defined by %s\n" % (m.macro_name, " " * (max_macro_name_len + max_macro_val_len - len(m.macro_name) + 2), m.defined_by)
434+
header_data += "\n#endif\n"
435+
# If fname is given, write "header_data" to it
436+
if fname:
437+
with open(fname, "wt") as f:
438+
f.write(header_data)
439+
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:

0 commit comments

Comments
 (0)