]> git.draconx.ca Git - dxcommon.git/commitdiff
Add a script to embed config.h into installed headers.
authorNick Bowler <nbowler@draconx.ca>
Sun, 3 Dec 2023 03:33:49 +0000 (22:33 -0500)
committerNick Bowler <nbowler@draconx.ca>
Sun, 3 Dec 2023 06:07:47 +0000 (01:07 -0500)
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
scripts/bake-config.awk [new file with mode: 0755]
tests/scripts.at

index 63f102374ce1138d6a13ab54c5a08cf89c1309cc..f811cde03e642632738d928f9d4725fe58b0aa92 100644 (file)
@@ -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 (executable)
index 0000000..56e2151
--- /dev/null
@@ -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;
+}
index a510f2529e9a2a718e86279d447240f8850b5bb7..3a91bd0689887cb3ba98ba8a4e8920b45fc24024 100644 (file)
@@ -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])