changeset 1076:1c15ba60aa64

Switch flag generation from shell to C. This should actually generate FLAG_longopt 0 #defines for disabled bare longopts (ala ls without --color). Put temporary executables under "generated" (including instlist for install).
author Rob Landley <rob@landley.net>
date Thu, 03 Oct 2013 03:18:00 -0500
parents 565eba9b549e
children f2e8247a4fd0
files Makefile scripts/install.sh scripts/make.sh scripts/mkflags.c toys.h
diffstat 5 files changed, 181 insertions(+), 63 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sun Sep 22 11:21:06 2013 -0500
+++ b/Makefile	Thu Oct 03 03:18:00 2013 -0500
@@ -25,16 +25,16 @@
 bloatcheck: toybox_old toybox_unstripped
 	@scripts/bloatcheck toybox_old toybox_unstripped
 
-instlist: toybox
-	$(HOSTCC) -I . scripts/install.c -o instlist
+generated/instlist: toybox
+	$(HOSTCC) -I . scripts/install.c -o generated/instlist
 
-install_flat: instlist
+install_flat: generated/instlist
 	scripts/install.sh --symlink --force
 
 install:
 	scripts/install.sh --long --symlink --force
 
-uninstall_flat: instlist
+uninstall_flat: generated/instlist
 	scripts/install.sh --uninstall
 
 uninstall:
@@ -42,9 +42,10 @@
 
 clean::
 	rm -rf toybox toybox_unstripped generated/config.h generated/Config.in \
-		generated/newtoys.h generated/globals.h instlist testdir \
-		generated/Config.probed generated/oldtoys.h \
-		generated/portability.h .singleconfig
+		generated/newtoys.h generated/globals.h testdir \
+		generated/Config.probed generated/oldtoys.h generated/flags.h \
+		generated/portability.h .singleconfig generated/instlist \
+		generated/mkflags
 
 distclean: clean
 	rm -f toybox_old .config* generated/help.h
--- a/scripts/install.sh	Sun Sep 22 11:21:06 2013 -0500
+++ b/scripts/install.sh	Thu Oct 03 03:18:00 2013 -0500
@@ -35,8 +35,8 @@
 
 echo "Compile instlist..."
 
-$DEBUG $HOSTCC -I . scripts/install.c -o instlist || exit 1
-COMMANDS="$(./instlist $LONG_PATH)"
+$DEBUG $HOSTCC -I . scripts/install.c -o generated/instlist || exit 1
+COMMANDS="$(generated/instlist $LONG_PATH)"
 
 echo "Install commands..."
 
--- a/scripts/make.sh	Sun Sep 22 11:21:06 2013 -0500
+++ b/scripts/make.sh	Thu Oct 03 03:18:00 2013 -0500
@@ -59,70 +59,57 @@
 sed -n -e 's/.*(NEWTOY(\([^,]*\), *\(\("[^"]*"[^,]*\)*\),.*/#define OPTSTR_\1\t\2/p' \
   generated/newtoys.h > generated/oldtoys.h
 
-# Extract list of command letters from processed header file
+if [ ! -e generated/mkflags ]
+then
+  $HOSTCC scripts/mkflags.c -o generated/mkflags || exit 1
+fi
 
-function getflags()
-{
-  FLX="$1"
-  shift
-  sed -n -e "s/.*TOY($FLX"',[ \t]*"\([^"]*\)"[ \t]*,.*)/\1/' \
-         -e 't keep;d;:keep' -e 's/^[<>=][0-9]//' -e 's/[?&^]//' \
-         -e 't keep' -e 's/[><=][0-9][0-9]*//g' -e 's/+.//g' \
-         -e 's/\[[^]]*\]//g' -e 's/[-?^:&#|@* ;]//g' "$@" -e 'p'
-}
+echo generated/flags.h
+
+# Parse files through C preprocessor twice, once to get flags for current
+# .config and once to get flags for allyesconfig
+for I in A B
+do
+  (
+  # define macros and select header files with option string data
+
+  echo "#define NEWTOY(aa,bb,cc) aa $I bb"
+  echo '#define OLDTOY(...)'
+  if [ "$I" == A ]
+  then
+    cat generated/config.h
+  else
+    sed '/USE_.*([^)]*)$/s/$/ __VA_ARGS__/' generated/config.h
+  fi
+  cat generated/newtoys.h
+
+  # Run result through preprocessor, glue together " " gaps leftover from USE
+  # macros, delete comment lines, print any line with a quoted optstring,
+  # turn any non-quoted opstring (NULL or 0) into " " (because fscanf can't
+  # handle "" with nothing in it).
+
+  ) | $CC -E - | \
+    sed -n -e 's/" *"//g;/^#/d;s/"/"/p' -e 's/ *$//;s/ [^" ]*$/ " "/p'
+
+# Sort resulting line pairs and glue them together into triplets of
+#   command "flags" "allflags"
+# to feed into mkflags C program that outputs actual flag macros
+
+done | sort | sed -n 's/ A / /;t skip;d;:skip;h;n;s/[^ ]* B //;H;g;s/\n/ /;p' |\
+generated/mkflags > generated/flags.h || exit 1
 
 # Extract global structure definitions and flag definitions from toys/*/*.c
 
 function getglobals()
 {
-  # Run newtoys.h through the compiler's preprocessor to resolve USE macros
-  # against current config.
-  NEWTOYS="$(cat generated/config.h generated/newtoys.h | $CC -E - | sed 's/" *"//g')"
-
-  # Grab allyesconfig for comparison
-  ALLTOYS="$((sed '/USE_.*([^)]*)$/s/$/ __VA_ARGS__/' generated/config.h && cat generated/newtoys.h) | $CC -E - | sed 's/" *"//g')"
-
   for i in toys/*/*.c
   do
     NAME="$(echo $i | sed 's@.*/\(.*\)\.c@\1@')"
+    DATA="$(sed -n -e '/^GLOBALS(/,/^)/b got;b;:got' \
+            -e 's/^GLOBALS(/struct '"$NAME"'_data {/' \
+            -e 's/^)/};/' -e 'p' $i)"
 
-    echo -e "// $i\n"
-    sed -n -e '/^GLOBALS(/,/^)/b got;b;:got' \
-        -e 's/^GLOBALS(/struct '"$NAME"'_data {/' \
-        -e 's/^)/};/' -e 'p' $i
-
-    LONGFLAGS="$(echo "$NEWTOYS" | getflags "$NAME" -e 's/\(\(([^)]*)\)*\).*/\1/' -e 's/(//g' -e 's/)/ /g')"
-    FLAGS="$(echo "$NEWTOYS" | getflags "$NAME" -e 's/([^)]*)//g')"
-    ZFLAGS="$(echo "$ALLTOYS" | getflags "$NAME" -e 's/([^)]*)//g' -e 's/[-'"$FLAGS"']//g')"
-    LONGFLAGLEN="$(echo "$LONGFLAGS" | wc -w)"
-
-    echo "#ifdef FOR_${NAME}"
-    X=0
-    # Provide values for --longopts with no corresponding short flags
-    for i in $LONGFLAGS
-    do
-      X=$(($X+1))
-      echo -e "#define FLAG_$i\t(1<<$(($LONGFLAGLEN+${#FLAGS}-$X)))"
-    done
-
-    # Provide values for active flags
-    X=0
-    while [ $X -lt ${#FLAGS} ]
-    do
-      echo -ne "#define FLAG_${FLAGS:$X:1}\t"
-      X=$(($X+1))
-      echo "(1<<$((${#FLAGS}-$X)))"
-    done
-
-    # Provide zeroes for inactive flags
-    X=0
-    while [ $X -lt ${#ZFLAGS} ]
-    do
-      echo "#define FLAG_${ZFLAGS:$X:1} 0"
-      X=$(($X+1))
-    done
-    echo "#define TT this.${NAME}"
-    echo "#endif"
+    [ ! -z "$DATA" ] && echo -e "// $i\n\n$DATA\n"
   done
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/mkflags.c	Thu Oct 03 03:18:00 2013 -0500
@@ -0,0 +1,129 @@
+// Take three word input lines on stdin (the three space separated words are
+// command name, option string with current config, option string from
+// allyesconfig; space separated, the last two are and double quotes)
+// and produce flag #defines to stdout.
+
+// This is intentionally crappy code because we control the inputs. It leaks
+// memory like a sieve and segfaults if malloc returns null, but does the job.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+struct flag {
+  struct flag *next;
+  char *command;
+  struct flag *lopt;
+};
+
+// Break down a command string into struct flag list.
+
+struct flag *digest(char *string)
+{
+  struct flag *list = NULL;
+
+  while (*string) {
+    // Groups must be at end.
+    if (*string == '[') break;
+
+    // Longopts
+    if (*string == '(') {
+      struct flag *new = calloc(sizeof(struct flag), 1);
+
+      new->command = ++string;
+
+      // Attach longopt to previous short opt, if any.
+      if (list && list->command) {
+        new->next = list->lopt;
+        list->lopt = new;
+      } else {
+        struct flag *blank = calloc(sizeof(struct flag), 1);
+
+        blank->next = list;
+        blank->lopt = new;
+        list = blank;
+      }
+      while (*++string != ')');  // An empty longopt () would break this.
+      *(string++) = 0;
+      continue;
+    }
+
+    if (strchr("?&^-:#|@*; ", *string)) string++;
+    else if (strchr("=<>", *string)) {
+      while (isdigit(*++string)) {
+        if (!list) {
+           string++;
+           break;
+        }
+      }
+    } else {
+      struct flag *new = calloc(sizeof(struct flag), 1);
+
+      new->command = string++;
+      new->next = list;
+      list = new;
+    }
+  }
+
+  return list;
+}
+
+int main(int argc, char *argv[])
+{
+  char command[256], flags[1023], allflags[1024];
+  unsigned bit;
+
+  for (;;) {
+    struct flag *flist, *aflist, *offlist;
+    unsigned bit = 0;
+
+    if (3 != fscanf(stdin, "%255s \"%1023[^\"]\" \"%1023[^\"]\"\n",
+                    command, flags, allflags)) break;
+
+    printf("// %s %s %s\n", command, flags, allflags);
+    flist = digest(flags);
+    offlist = aflist = digest(allflags);
+
+
+    printf("#ifdef CLEANUP_%s\n#undef CLEANUP_%s\n#undef FOR_%s\n#undef TT\n",
+           command, command, command);
+
+    while (offlist) {
+      struct flag *f = offlist->lopt;
+      while (f) {
+        printf("#undef FLAG_%s\n", f->command);
+        f = f->next;
+      }
+      if (offlist->command) printf("#undef FLAG_%c\n", *offlist->command);
+      offlist = offlist->next;
+    }
+    printf("#endif\n\n");
+
+    printf("#ifdef FOR_%s\n#define TT this.%s\n", command, command);
+
+    while (aflist) {
+      if (aflist->lopt) {
+        if (flist && flist->lopt &&
+            !strcmp(flist->lopt->command, aflist->lopt->command))
+        {
+          printf("#define FLAG_%s (1<<%d)\n", flist->lopt->command, bit);
+          flist->lopt = flist->lopt->next;
+        } else printf("#define FLAG_%s 0\n", aflist->lopt->command);
+        aflist->lopt = aflist->lopt->next;
+      } else {
+        if (flist && (!aflist->command || *aflist->command == *flist->command))
+        {
+          if (aflist->command)
+            printf("#define FLAG_%c (1<<%d)\n", *aflist->command, bit);
+          bit++;
+          flist = flist->next;
+        } else printf("#define FLAG_%c 0\n", *aflist->command);
+        aflist = aflist->next;
+      }
+    }
+    printf("#endif\n\n");
+  }
+
+  return fflush(0) && ferror(stdout);
+}
--- a/toys.h	Sun Sep 22 11:21:06 2013 -0500
+++ b/toys.h	Thu Oct 03 03:18:00 2013 -0500
@@ -64,6 +64,7 @@
 #define OLDTOY(name, oldname, opts, flags)
 #include "generated/newtoys.h"
 #include "generated/oldtoys.h"
+#include "generated/flags.h"
 #include "generated/globals.h"
 
 // These live in main.c