summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Schodet2012-10-20 09:56:17 +0200
committerNicolas Schodet2012-10-21 18:56:29 +0200
commitbeaa75f184d07855ea40da2b1e336cf1fe86a7b8 (patch)
tree129d2e8cbc3d8442308bdcb650abcc275cec5d55
parent143379df9873622ffe938592cd27e89a95361ddf (diff)
digital/ucoolib: add build time configuration system
-rw-r--r--digital/ucoolib/build/config.mk32
-rwxr-xr-xdigital/ucoolib/build/tools/config-gen113
-rw-r--r--digital/ucoolib/build/top.mk4
3 files changed, 149 insertions, 0 deletions
diff --git a/digital/ucoolib/build/config.mk b/digital/ucoolib/build/config.mk
new file mode 100644
index 00000000..6f3f527b
--- /dev/null
+++ b/digital/ucoolib/build/config.mk
@@ -0,0 +1,32 @@
+# ucoolib - Microcontroller object oriented library.
+#
+# Build time configuration system.
+
+PROJECT_CONFIG ?= Config
+MODULES_CONFIG := $(wildcard $(ALL_MODULES:%=$(BASE)/ucoolib/%/Config))
+
+CONFIG_LIST := $(wildcard $(PROJECT_CONFIG)) $(MODULES_CONFIG)
+
+# Ensure that configuration is up to date, using two mechanisms:
+# - make will make sure config.list is up to date as it is included.
+# - comparison with CONFIG_LIST ensures that the list of included
+# configuration (including the project configuration) is the same.
+ifneq ($(MAKECMDGOALS),clean)
+-include $(OBJDIR)/config.list
+ifneq ($(CONFIG_LIST),$(CONFIG_LIST_OLD))
+CONFIG_FORCE := CONFIG_FORCE
+endif
+endif
+
+clean: config-clean
+
+.PHONY: config-clean CONFIG_FORCE
+
+$(OBJDIR)/config.list: $(CONFIG_LIST) $(CONFIG_FORCE)
+ @echo "CONF $(PROJECT_CONFIG)"
+ $Q$(BASE)/build/tools/config-gen -H $(OBJDIR)/config/%.hh \
+ -p $(PROJECT_CONFIG) $(MODULES_CONFIG)
+ $Qecho "CONFIG_LIST_OLD = $(CONFIG_LIST)" > $@
+
+config-clean:
+ rm -rf $(OBJDIR)/config $(OBJDIR)/config.list
diff --git a/digital/ucoolib/build/tools/config-gen b/digital/ucoolib/build/tools/config-gen
new file mode 100755
index 00000000..3feffbe8
--- /dev/null
+++ b/digital/ucoolib/build/tools/config-gen
@@ -0,0 +1,113 @@
+#!/usr/bin/python
+"""Read build configuration and generate header files."""
+import optparse
+import ConfigParser
+import sys
+import re
+import os
+
+def read_config(modules_configs, project_config):
+ """Read configuration definitions and default values from module
+ configuration files, then merge project configuration."""
+ parser = ConfigParser.SafeConfigParser()
+ # Read definitions and default values.
+ for mc in modules_configs:
+ try:
+ mcf = open(mc)
+ except IOError:
+ pass
+ else:
+ with mcf:
+ parser.readfp(mcf)
+ # Save the list of existing items for later check.
+ def config_items(parser):
+ return set('%s:%s' % (section, item)
+ for section in parser.sections()
+ for item in parser.options(section))
+ modules_items = config_items(parser)
+ # Now read project configuration.
+ if project_config is not None:
+ try:
+ pcf = open(project_config)
+ except IOError:
+ pass
+ else:
+ with pcf:
+ parser.readfp(pcf)
+ # Check for unknown items.
+ project_items = config_items(parser)
+ unknown_items = project_items - modules_items
+ if unknown_items:
+ raise RuntimeError("unknown configuration item: "
+ + " ".join(unknown_items))
+ # Check for items with no default value.
+ for section in parser.sections():
+ for key, value in parser.items(section):
+ if value == '':
+ raise RuntimeError("no value given for %s:%s"
+ % (section, key))
+ # OK, convert to more natural structure.
+ return dict((section, parser.items(section))
+ for section in parser.sections())
+
+def write_header(filename, section, section_dict):
+ """Write (update) a section to a C header file."""
+ # Prepare new content.
+ items = [ ]
+ section = section.replace('/', '_').upper()
+ for key, value in section_dict:
+ items.append('#define UCOO_CONFIG_%s_%s (%s)'
+ % (section, key.upper(), value))
+ guard = re.sub(r'\W', '_', filename)
+ content = '\n'.join([
+ '#ifndef %s' % guard,
+ '#define %s' % guard,
+ '// Generated from configuration.',
+ ] + items + [
+ '#endif',
+ '' ])
+ # Check old content.
+ old_content = ''
+ try:
+ hf = open(filename)
+ except IOError:
+ pass
+ else:
+ with hf:
+ old_content = hf.read()
+ if old_content == content:
+ return
+ # Create output directory if needed.
+ dirname = os.path.dirname(filename)
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+ # Write new content.
+ with open(filename, 'w') as hf:
+ hf.write(content)
+
+def write_headers(filename_pattern, config):
+ """Write (update) all sections to C header files."""
+ for section, section_dict in config.iteritems():
+ filename = filename_pattern.replace('%', section)
+ write_header(filename, section, section_dict)
+
+if __name__ == '__main__':
+ parser = optparse.OptionParser(
+ usage='%prog [options] modules configuration files...')
+ parser.add_option('-p', '--project-config', metavar='FILE',
+ help="project configuration file")
+ parser.add_option('-H', '--c-header-template', metavar='TEMPLATE',
+ help="name template for C header files"
+ + " (use % as section placeholder)")
+ options, modules_configs = parser.parse_args()
+
+ try:
+ config = read_config(modules_configs, options.project_config)
+ if options.c_header_template:
+ write_headers(options.c_header_template, config)
+ except RuntimeError, e:
+ print >> sys.stderr, e
+ sys.exit(1)
+ except EnvironmentError, e:
+ print >> sys.stderr, e
+ sys.exit(1)
diff --git a/digital/ucoolib/build/top.mk b/digital/ucoolib/build/top.mk
index 29e8003f..adb4e950 100644
--- a/digital/ucoolib/build/top.mk
+++ b/digital/ucoolib/build/top.mk
@@ -44,6 +44,10 @@ vpath %.cc $(ALL_MODULES:%=$(BASE)/ucoolib/%)
vpath %.c $(ALL_MODULES:%=$(BASE)/ucoolib/%)
vpath %.S $(ALL_MODULES:%=$(BASE)/ucoolib/%)
+# Configuration.
+
+include $(BASE)/build/config.mk
+
# Objects directory.
$(OBJDIR):