From d4f7fab64222a7d3ce15e86c5c7b32f7cdd3ce93 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Sat, 2 Dec 2023 22:33:49 -0500 Subject: [PATCH] Add a script to embed config.h into installed headers. When installing a library, sometimes it is difficult to avoid that certain aspects of the interface depend on the build configuration. While it is possible use configuration header templates to create reasonably installable header files, this script enables a different approach: just write code (mostly) normally and then patch up any configuration macro references at install time. --- Makefile.am | 9 +++-- scripts/bake-config.awk | 84 +++++++++++++++++++++++++++++++++++++++++ tests/scripts.at | 32 ++++++++++++++++ 3 files changed, 121 insertions(+), 4 deletions(-) create mode 100755 scripts/bake-config.awk diff --git a/Makefile.am b/Makefile.am index 63f1023..f811cde 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,10 +9,11 @@ ACLOCAL_AMFLAGS = -I m4 AM_CPPFLAGS = -I$(top_srcdir)/src $(STUB_INCLUDES) EXTRA_DIST = scripts/fix-gnulib.pl scripts/fix-ltdl.pl \ - scripts/gen-options.awk scripts/gen-strtab.awk \ - scripts/gen-tree.awk scripts/join.awk scripts/pe-subsys.awk \ - src/copysym.h src/help.h src/pack.h src/tap.h t/getopt/getopt.h \ - t/nls/gettext.h t/nls/mbswidth.h tests/data/gnulib.mk + scripts/bake-config.awk scripts/gen-options.awk \ + scripts/gen-strtab.awk scripts/gen-tree.awk scripts/join.awk \ + scripts/pe-subsys.awk src/copysym.h src/help.h src/pack.h \ + src/tap.h t/getopt/getopt.h t/nls/gettext.h t/nls/mbswidth.h \ + tests/data/gnulib.mk check_LIBRARIES = t/libdummy.a t/libempty.a diff --git a/scripts/bake-config.awk b/scripts/bake-config.awk new file mode 100755 index 0000000..56e2151 --- /dev/null +++ b/scripts/bake-config.awk @@ -0,0 +1,84 @@ +#!/bin/awk -f +# +# Copyright © 2023 Nick Bowler +# +# Usage: bake-config.awk config.h some_header.h +# +# Looks for #define and #undef lines in config.h to identify configuration +# macros, then looks for uses of those macros in some_header.h and substitutes +# them accordingly. The result is a header file that does not directly depend +# on config.h, which can be installed normally. +# +# Presently, two kinds of substitions are made. +# +# - For a #define in config.h, any occurrence of that macro is substituted with +# its replacement text. +# +# - For an #undef in config.h, any occurrence of that macro in an #if or #elif +# directive is replaced with a literal 0. +# +# Almost no attempt is made to understand C syntax so the replacement is very +# simplistic, and won't work well in many cases. Inspect the output! +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +BEGIN { + filenum = 0; + idclass = "[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0-9_]"; +} + +FNR == 1 { filenum++; } +filenum == 1 && $1 == "#define" && $2 !~ /[(]/ { + key = $2; + $1 = $2 = ""; + sub(/^ */, "", $0); + config[key] = $0 " /* " key " */"; +} + +# Autoconf comments out #undef lines in config.h, so we're looking for +# /* #undef FOO */ +filenum == 1 && $2 == "#undef" { + undef[$3] = 0 " /* " $3 " */"; +} + +filenum == 2 { + CHANGED = 0; + tok = $0; + + # Mark identifier boundaries to avoid replacing substrings. Use ` as + # it is not valid in C code (outside of string/character literals). + gsub(idclass"+", "`&`", tok); + + # Replace all occurrences of configuration macros normally. + tok = do_replace(tok, config); + + # Replace explicitly undefined configuration macros in #if/#elif + if ($0 ~ /^[ \t]*#[ \t]*(el)?if[ \t]/) + tok = do_replace(tok, undef); + + if (CHANGED) { + gsub(/`/, "", tok); + print tok; + } else { + print $0; + } +} + +function do_replace(s, config, result, t) +{ + while (match(s, "`" idclass "+`")) { + t = substr(s, RSTART+1, RLENGTH-2); + if (t in config) { + result = result substr(s, 1, RSTART - 1) config[t]; + CHANGED = 1; + } else { + result = result substr(s, 1, RSTART + RLENGTH - 1); + } + + s = substr(s, RSTART + RLENGTH); + } + + return result s; +} diff --git a/tests/scripts.at b/tests/scripts.at index a510f25..3a91bd0 100644 --- a/tests/scripts.at +++ b/tests/scripts.at @@ -6,6 +6,38 @@ dnl There is NO WARRANTY, to the extent permitted by law. AT_BANNER([Script tests]) +AT_SETUP([bake-config.awk]) +AT_KEYWORDS([bake-config awk script scripts]) + +AT_DATA([cfg.h], +[[#define hello world +/* #undef HAVE_STUFF */ +#define HAVE_OTHER_STUFF 1 +/* #undef HAVE_CRAZY_STUFF */ +]]) + +AT_DATA([lib.h], +[[#if HAVE_STUFF +# define foo hello__ +#elif HAVE_CRAZY_STUFF +# define foo hello +#elif HAVE_OTHER_STUFF +# define foo __hello +#endif +]]) + +AT_CHECK([$AWK -f "$srcdir/scripts/bake-config.awk" cfg.h lib.h], [0], +[[#if 0 /* HAVE_STUFF */ +# define foo hello__ +#elif 0 /* HAVE_CRAZY_STUFF */ +# define foo world /* hello */ +#elif 1 /* HAVE_OTHER_STUFF */ +# define foo __hello +#endif +]]) + +AT_CLEANUP + m4_define([TEST_GEN_OPTIONS], [AT_KEYWORDS([gen-options awk script scripts])dnl AT_DATA([m4_default([$2], [options.def])], [$1]) -- 2.43.2