[U-Boot] [PATCH v4 1/8] Implement autoconf header file
Simon Glass
sjg at chromium.org
Mon Jun 17 16:44:53 CEST 2013
Add support for generating an autoconf.h header file that can be used in
the source instead of #ifdef.
For example, instead of:
#ifdef CONFIG_VERSION_VARIABLE
setenv("ver", version_string); /* set version variable */
#endif
you can do:
if (autoconf_version_variable())
setenv("ver", version_string); /* set version variable */
The compiler will ensure that the dead code is eliminated, so the result
is the same.
Where the value of the CONFIG define is 0, you can use the autoconf_has...()
form. For example CONFIG_BOOTDELAY can be -ve, 0 or +ve, but if it is
defined at all, it affects behaviour:
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
s = getenv ("bootdelay");
#endif
So we use:
if (autoconf_has_bootdelay() && autoconf_bootdelay() >= 0)
s = getenv ("bootdelay");
This later form should only be used for such 'difficult' defines where a
zero value still means that the CONFIG should be considered to be defined.
Signed-off-by: Simon Glass <sjg at chromium.org>
---
Changes in v4: None
Changes in v3:
- Add comment as to why we use [A-Za-z0-9_][A-Za-z0-9_]*
- Rename sed scripts to more useful names
- Update config_xxx() to autoconf_xxx() in comments/README/sed
- Update config_xxx_enabled() to autoconf_has_xxx() in comments/README/sed
Changes in v2:
- Add a grep to the sed/sort pipe to speed up processing
- Fix up a few errors and comments in the original RFC
- Split out changes to main.c into separate patches
- Use autoconf_...() instead of config_...()
- Use autoconf_has_...() instead of config_..._enabled()
Makefile | 43 ++++++++++++++++++++-
README | 87 ++++++++++++++++++++++++++++++++++++++++--
include/common.h | 3 ++
include/config_drop.h | 17 +++++++++
tools/scripts/define2value.sed | 37 ++++++++++++++++++
tools/scripts/define2zero.sed | 32 ++++++++++++++++
6 files changed, 215 insertions(+), 4 deletions(-)
create mode 100644 include/config_drop.h
create mode 100644 tools/scripts/define2value.sed
create mode 100644 tools/scripts/define2zero.sed
diff --git a/Makefile b/Makefile
index 693b3f2..3c2ffd4 100644
--- a/Makefile
+++ b/Makefile
@@ -629,6 +629,7 @@ updater:
# parallel sub-makes creating .depend files simultaneously.
depend dep: $(TIMESTAMP_FILE) $(VERSION_FILE) \
$(obj)include/autoconf.mk \
+ $(obj)include/generated/autoconf.h \
$(obj)include/generated/generic-asm-offsets.h \
$(obj)include/generated/asm-offsets.h
for dir in $(SUBDIRS) $(CPUDIR) $(LDSCRIPT_MAKEFILE_DIR) ; do \
@@ -703,6 +704,45 @@ $(obj)include/autoconf.mk: $(obj)include/config.h
sed -n -f tools/scripts/define2mk.sed > $@.tmp && \
mv $@.tmp $@
+# Create a C header file where every '#define CONFIG_XXX value' becomes
+# '#define autoconf_xxx() value', or '#define autoconf_xxx() 0' where the
+# CONFIG is not used by this board configuration. This allows C code to do
+# things like 'if (autoconf_xxx())' and have the compiler remove the dead code,
+# instead of using '#ifdef CONFIG_XXX...#endif'. Note that in most cases
+# if the autoconf_...() returns 0 then the option is not enabled. In some rare
+# cases such as CONFIG_BOOTDELAY, the config can be enabled but still have a
+# a value of 0. So in addition we a #define autoconf_has_xxx(), setting the
+# value to 0 if the option is disabled, 1 if enabled. This last feature will
+# hopefully be deprecated soon.
+# The file is regenerated when any U-Boot header file changes.
+$(obj)include/generated/autoconf.h: $(obj)include/config.h
+ @$(XECHO) Generating $@ ; \
+ set -e ; \
+ : Extract the config macros to a C header file ; \
+ $(CPP) $(CFLAGS) -DDO_DEPS_ONLY -dM include/common.h | \
+ sed -n -f tools/scripts/define2value.sed > $@.tmp; \
+ : Regenerate our list of all config macros if neeed ; \
+ if [ ! -f $@-all.tmp ] || \
+ find $(src) -name '*.h' -type f -newer $@-all.tmp | \
+ egrep -qv 'include/(autoconf.h|generated|config.h)'; \
+ then \
+ : Extract all config macros from all C header files ; \
+ : We can grep for CONFIG since the value will be dropped ; \
+ ( \
+ find ${src} -name "*.h" -type f | xargs \
+ cat | grep CONFIG | \
+ sed -n -f tools/scripts/define2zero.sed \
+ ) | sort | uniq > $@-all.tmp; \
+ fi; \
+ : Extract the enabled config macros to a C header file ; \
+ $(CPP) $(CFLAGS) -DDO_DEPS_ONLY -dM include/common.h | \
+ sed -n -f tools/scripts/define2zero.sed | \
+ sort > $@-enabled.tmp; \
+ set -e ; \
+ : Find CONFIGs that are not enabled ; \
+ comm -13 $@-enabled.tmp $@-all.tmp >>$@.tmp && \
+ mv $@.tmp $@
+
$(obj)include/generated/generic-asm-offsets.h: $(obj)include/autoconf.mk.dep \
$(obj)lib/asm-offsets.s
@$(XECHO) Generating $@
@@ -785,7 +825,8 @@ include/license.h: tools/bin2header COPYING
unconfig:
@rm -f $(obj)include/config.h $(obj)include/config.mk \
$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp \
- $(obj)include/autoconf.mk $(obj)include/autoconf.mk.dep
+ $(obj)include/autoconf.mk $(obj)include/autoconf.mk.dep \
+ $(obj)include/generated/autoconf.h
%_config:: unconfig
@$(MKCONFIG) -A $(@:_config=)
diff --git a/README b/README
index cd0336c..5ed67e5 100644
--- a/README
+++ b/README
@@ -5711,11 +5711,92 @@ Notes:
* If you modify existing code, make sure that your new code does not
add to the memory footprint of the code ;-) Small is beautiful!
When adding new features, these should compile conditionally only
- (using #ifdef), and the resulting code with the new feature
- disabled must not need more memory than the old code without your
- modification.
+ (avoiding #ifdef where at all possible), and the resulting code with
+ the new feature disabled must not need more memory than the old code
+ without your modification.
* Remember that there is a size limit of 100 kB per message on the
u-boot mailing list. Bigger patches will be moderated. If they are
reasonable and not too big, they will be acknowledged. But patches
bigger than the size limit should be avoided.
+
+
+Use of #ifdef:
+--------------
+Many parts of the U-Boot code base are sprinkled with #ifdefs. This makes
+different boards compile different versions of the source code, meaning
+that we must build all boards to check for failures. It is easy to misspell
+an #ifdef and there is not as much checking of this by the compiler. For
+someone coming new into the code base, #ifdefs are a big turn-off. Multiple
+dependent #ifdefs are harder to do than with if..then..else. Variable
+declarations must be #idefed as well as the code that uses them, often much
+later in the file/function. #ifdef indents don't match code indents and
+have their own separate indent feature. Overall, excessive use of #idef
+hurts readability and makes the code harder to modify and refactor.
+
+In an effort to reduce the use of #ifdef in U-Boot, without requiring lots
+of special static inlines all over the header files, a single autoconf.h
+header file with lower-case function-type macros has been made available.
+
+This file has either:
+
+# #define autoconf_xxx() value
+
+for enabled options, or:
+
+# #define autoconf_xxx() 0
+
+for disabled options. You can therefore generally change code like this:
+
+ #ifdef CONFIG_XXX
+ do_something
+ #else
+ do_something_else
+ #endif
+
+to this:
+
+ if (autoconf_xxx())
+ do_something;
+ else
+ do_something_else;
+
+The compiler will see that autoconf_xxx() evalutes to a constant and will
+eliminate the dead code. The resulting code (and code size) is the same.
+
+Multiple #ifdefs can be converted also:
+
+ #if defined(CONFIG_XXX) && !defined(CONFIG_YYY)
+ do_something
+ #endif
+
+ if (autoconf_xxx() && !autoconf_yyy())
+ do_something;
+
+Where the macro evaluates to a string, it will be non-NULL, so the above
+will work whether the macro is a string or a number.
+
+This takes care of almost all CONFIG macros. Unfortunately there are a few
+cases where a value of 0 does not mean the option is disabled. For example
+CONFIG_BOOTDELAY can be defined to 0, which means that the bootdelay
+code should be used, but with a value of 0. To get around this and other
+sticky cases, an addition macro with an 'has_' prefix is provided, where
+the value is always either 0 or 1:
+
+ // Will work even if boaard config has '#define CONFIG_BOOTDELAY 0'
+ if (autoconf_has_bootdelay())
+ do_something;
+
+(Probably such config options should be deprecated and then we can remove
+this feature)
+
+U-Boot already has a Makefile scheme to permit files to be easily included
+based on CONFIG. This can be used where the code to be compiled exists in
+its own source file. So the following rules apply:
+
+ 1. Use #ifdef to conditionally compile an exported function or variable
+ 2. Use ordinary C code with autoconf_xxx() everywhere else
+ 3. Mark your functions and data structures static where possible
+ 4. Use the autoconf_has_xxx() variants only if essential
+ 5. When changing existing code, first create a new patch to replace
+ #ifdefs in the surrounding area
diff --git a/include/common.h b/include/common.h
index 126891d..de18083 100644
--- a/include/common.h
+++ b/include/common.h
@@ -35,6 +35,9 @@ typedef volatile unsigned short vu_short;
typedef volatile unsigned char vu_char;
#include <config.h>
+#ifndef DO_DEPS_ONLY
+#include <generated/autoconf.h>
+#endif
#include <asm-offsets.h>
#include <linux/bitops.h>
#include <linux/types.h>
diff --git a/include/config_drop.h b/include/config_drop.h
new file mode 100644
index 0000000..bf68b50
--- /dev/null
+++ b/include/config_drop.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2013 Google, Inc
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License Version 2. This file is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _CONFIG_DROP_H
+#define _CONFIG_DROP_H
+
+/* Options which don't seem to be defined by any header in U-Boot */
+#define CONFIG_MENUPROMPT "Auto-boot prompt"
+#define CONFIG_MENUKEY
+#define CONFIG_UPDATE_TFTP
+
+#endif
diff --git a/tools/scripts/define2value.sed b/tools/scripts/define2value.sed
new file mode 100644
index 0000000..205f9fe
--- /dev/null
+++ b/tools/scripts/define2value.sed
@@ -0,0 +1,37 @@
+#
+# Sed script to parse CPP macros and generate a list of CONFIG macros
+#
+# This converts:
+# #define CONFIG_XXX value
+#into:
+# #define autoconf_xxx() value
+# #define autoconf_has_xxx() 1
+
+# Macros with parameters are ignored.
+# (Note we avoid + since it doesn't appear to work)
+/^#define CONFIG_[A-Za-z0-9_][A-Za-z0-9_]*(/ {
+ d
+}
+
+# Only process values prefixed with #define CONFIG_
+/^#define CONFIG_[A-Za-z0-9_][A-Za-z0-9_]*/ {
+ # Strip the #define prefix
+ s/#define[ \t]*CONFIG_/autoconf_/;
+ # Change to form CONFIG_*=VALUE
+ s/[\t ][\t ]*/=/;
+ # Handle lines with no value
+ s/^\([^=]*\)$/\1=/;
+ # Drop trailing spaces
+ s/ *$//;
+ # Change empty values to '1'
+ s/=$/=1/;
+ # Add #define at the start
+ s/^\([^=]*\)=/#define \L\1() /
+ # print the line
+ p
+ # Create autoconf_has_...(), value 1
+ s/().*/() 1/
+ s/\(autoconf_\)/\1has_/
+ # print the line
+ p
+}
diff --git a/tools/scripts/define2zero.sed b/tools/scripts/define2zero.sed
new file mode 100644
index 0000000..95e6860
--- /dev/null
+++ b/tools/scripts/define2zero.sed
@@ -0,0 +1,32 @@
+#
+# Sed script to parse CPP macros and generate a list of autoconf macros
+#
+# This converts:
+# #define CONFIG_XXX value
+#into:
+# #define autoconf_xxx() 0
+# #define autoconf_has_xxx() 0
+
+# Macros with parameters are ignored.
+# (Note we avoid + since it doesn't appear to work)
+/^#define CONFIG_[A-Za-z0-9_][A-Za-z0-9_]*(/ {
+ s/.*//
+}
+
+# Only process values prefixed with #define CONFIG_
+/^#define CONFIG_[A-Za-z0-9_][A-Za-z0-9_]*/ {
+ # Strip the #define prefix
+ s/#define *//;
+ # Remove the value
+ s/[ \t].*//;
+ # Convert to lower case, prepend #define
+ s/CONFIG_\(.*\)/#define autoconf_\L\1/
+ # Append 0
+ s/$/() 0/
+ # print the line
+ p
+ # Create autoconf_has_...(), value 0
+ s/\(autoconf_\)/\1has_/
+ # print the line
+ p
+}
--
1.8.3
More information about the U-Boot
mailing list