- New code capability: a read-only string cache. Start of solution for
authorPaul Smith <psmith@gnu.org>
Fri, 10 Feb 2006 05:29:00 +0000 (05:29 +0000)
committerPaul Smith <psmith@gnu.org>
Fri, 10 Feb 2006 05:29:00 +0000 (05:29 +0000)
  Savannah bug #15182, but not much uses it yet.  Coming shortly.
- Added short-circuiting $(and ..) and $(or ...) functions.

15 files changed:
ChangeLog
Makefile.am
NEWS
doc/make.texi
function.c
hash.c
hash.h
main.c
make.h
read.c
strcache.c [new file with mode: 0644]
tests/ChangeLog
tests/run_make_tests.pl
tests/scripts/functions/andor [new file with mode: 0644]
tests/test_driver.pl

index fbe12be453167ebb675214601c8930bed99e4867..592112fb721f58d8fa0bb064aec28077b637959c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2006-02-10  Paul D. Smith  <psmith@gnu.org>
+
+       A new internal capability: the string cache is a read-only cache
+       of strings, with a hash table interface for fast lookup.  Nothing
+       in the cache will ever be freed, so there's no need for reference
+       counting, etc.  This is the beginning of a full solution for
+       Savannah bug #15182, but for now we only store makefile names here.
+
+       * strcache.c: New file.  Implement a read-only string cache.
+       * make.h: Add prototypes for new functions.
+       * main.c (initialize_global_hash_tables): Initialize the string cache.
+       (print_data_base): Print string cache stats.
+       * read.c (eval_makefile): Use the string cache to store makefile
+       names.  Rewrite the string allocation to be sure we free everything.
+
+2006-02-09  Paul D. Smith  <psmith@gnu.org>
+
+       * function.c (func_or): Implement a short-circuiting OR function.
+       (func_and): Implement a short-circuiting AND function.
+       (function_table_init): Update the table with the new functions.
+       * doc/make.texi (Conditional Functions): Changed the "if" section
+       to one on general conditional functions.  Added documentation for
+       $(and ...) and $(or ...) functions.
+       * NEWS: Note new $(and ...) and $(or ...) functions.
+
 2006-02-08  Boris Kolpackov  <boris@kolpackov.net>
 
        * job.h (struct child): Add dontcare bitfield.
index ebd83a72f4f206faeb8aa43b983fe4124bb34df0..6c0abc969abc58698996f4cfe31e16b1e3c0f7a7 100644 (file)
@@ -24,7 +24,7 @@ endif
 make_SOURCES = ar.c arscan.c commands.c default.c dir.c expand.c file.c \
                function.c getopt.c getopt1.c implicit.c job.c main.c \
                misc.c read.c remake.c $(remote) rule.c signame.c \
-               variable.c version.c vpath.c hash.c
+               strcache.c variable.c version.c vpath.c hash.c
 
 EXTRA_make_SOURCES = vmsjobs.c remote-stub.c remote-cstms.c
 
diff --git a/NEWS b/NEWS
index 1bf8301752c831b8db28525f24318cb846885c5c..7289f398f2e05208e5bb71cc26d885733ca408a3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -95,6 +95,14 @@ Version 3.81beta4
    - $(info ...) prints its arguments to stdout.  No makefile name or
      line number info, etc. is printed.
    - $(flavor ...) returns the flavor of a variable.
+   - $(or ...) provides a short-circuiting OR conditional: each argument
+     is expanded.  The first true (non-empty) argument is returned; no
+     further arguments are expanded.  Expands to empty if there are no
+     true arguments.
+   - $(and ...) provides a short-circuiting AND conditional: each
+     argument is expanded.  The first false (empty) argument is
+     returned; no further arguments are expanded.  Expands to the last
+     argument if all arguments are true.
 
 * Changes made for POSIX compatibility:
    - Only touch targets (under -t) if they have at least one command.
index 96d0f71f96e9bc27adae48038aaa68f5a09de808..b17a2341aa192e0eb27542fcc0cdb72304a2a46c 100644 (file)
@@ -224,6 +224,16 @@ Writing the Commands in Rules
 * Sequences::                   Defining canned sequences of commands.
 * Empty Commands::              Defining useful, do-nothing commands.
 
+Command Syntax
+
+* Splitting Lines::             Breaking long command lines for readability.
+* Variables in Commands::       Using @code{make} variables in commands.
+
+Command Execution
+
+* Choosing the Shell::          How @code{make} chooses the shell used
+                                  to run commands.
+
 Recursive Use of @code{make}
 
 * MAKE Variable::               The special effects of using @samp{$(MAKE)}.
@@ -268,8 +278,8 @@ Functions for Transforming Text
 * Syntax of Functions::         How to write a function call.
 * Text Functions::              General-purpose text manipulation functions.
 * File Name Functions::         Functions for manipulating file names.
+* Conditional Functions::       Functions that implement conditions.
 * Foreach Function::            Repeat some text with controlled variation.
-* If Function::                 Conditionally expand a value.
 * Call Function::               Expand a user-defined function.
 * Value Function::              Return the un-expanded value of a variable.
 * Eval Function::               Evaluate the arguments as makefile syntax.
@@ -6199,8 +6209,8 @@ call, just as a variable might be substituted.
 * Syntax of Functions::         How to write a function call.
 * Text Functions::              General-purpose text manipulation functions.
 * File Name Functions::         Functions for manipulating file names.
+* Conditional Functions::       Functions that implement conditions.
 * Foreach Function::            Repeat some text with controlled variation.
-* If Function::                 Conditionally expand a value.
 * Call Function::               Expand a user-defined function.
 * Value Function::              Return the un-expanded value of a variable.
 * Eval Function::               Evaluate the arguments as makefile syntax.
@@ -6620,7 +6630,7 @@ used so that the new value is assigned even if the previous value of
 @code{CFLAGS} was specified with a command argument (@pxref{Override
 Directive, , The @code{override} Directive}).
 
-@node File Name Functions, Foreach Function, Text Functions, Functions
+@node File Name Functions, Conditional Functions, Text Functions, Functions
 @section Functions for File Names
 @cindex functions, for file names
 @cindex file name functions
@@ -6796,7 +6806,60 @@ the file names to refer to an existing file or directory.  Use the
 @code{wildcard} function to test for existence.
 @end table
 
-@node Foreach Function, If Function, File Name Functions, Functions
+@node Conditional Functions, Foreach Function, File Name Functions, Functions
+@section Functions for Conditionals
+@findex if
+@cindex conditional expansion
+There are three functions that provide conditional expansion.  A key
+aspect of these functions is that not all of the arguments are
+expanded initially.  Only those arguments which need to be expanded,
+will be expanded.
+
+@table @code
+@item $(if @var{condition},@var{then-part}[,@var{else-part}])
+@findex if
+The @code{if} function provides support for conditional expansion in a
+functional context (as opposed to the GNU @code{make} makefile
+conditionals such as @code{ifeq} (@pxref{Conditional Syntax, ,Syntax of
+Conditionals}).
+
+The first argument, @var{condition}, first has all preceding and
+trailing whitespace stripped, then is expanded.  If it expands to any
+non-empty string, then the condition is considered to be true.  If it
+expands to an empty string, the condition is considered to be false.
+
+If the condition is true then the second argument, @var{then-part}, is
+evaluated and this is used as the result of the evaluation of the entire
+@code{if} function.
+
+If the condition is false then the third argument, @var{else-part}, is
+evaluated and this is the result of the @code{if} function.  If there is
+no third argument, the @code{if} function evaluates to nothing (the
+empty string).
+
+Note that only one of the @var{then-part} or the @var{else-part} will be
+evaluated, never both.  Thus, either can contain side-effects (such as
+@code{shell} function calls, etc.)
+
+@item $(or @var{condition1}[,@var{condition2}[,@var{condition3}@dots{}]])
+@findex or
+The @code{or} function provides a ``short-circuiting'' OR operation.
+Each argument is expanded, in order.  If an argument expands to a
+non-empty string the processing stops and the result of the expansion
+is that string.  If, after all arguments are expanded, all of them are
+false (empty), then the result of the expansion is the empty string.
+
+@item $(and @var{condition1}[,@var{condition2}[,@var{condition3}@dots{}]])
+@findex and
+The @code{and} function provides a ``short-circuiting'' AND operation.
+Each argument is expanded, in order.  If an argument expands to an
+empty string the processing stops and the result of the expansion is
+the empty string.  If all arguments expand to a non-empty string then
+the result of the expansion is the expansion of the last argument.
+
+@end table
+
+@node Foreach Function, Call Function, Conditional Functions, Functions
 @section The @code{foreach} Function
 @findex foreach
 @cindex words, iterating over
@@ -6884,41 +6947,7 @@ might be useful if the value of @code{find_files} references the variable
 whose name is @samp{Esta escrito en espanol!} (es un nombre bastante largo,
 no?), but it is more likely to be a mistake.
 
-@node If Function, Call Function, Foreach Function, Functions
-@section The @code{if} Function
-@findex if
-@cindex conditional expansion
-
-The @code{if} function provides support for conditional expansion in a
-functional context (as opposed to the GNU @code{make} makefile
-conditionals such as @code{ifeq} (@pxref{Conditional Syntax, ,Syntax of
-Conditionals}).
-
-An @code{if} function call can contain either two or three arguments:
-
-@example
-$(if @var{condition},@var{then-part}[,@var{else-part}])
-@end example
-
-The first argument, @var{condition}, first has all preceding and
-trailing whitespace stripped, then is expanded.  If it expands to any
-non-empty string, then the condition is considered to be true.  If it
-expands to an empty string, the condition is considered to be false.
-
-If the condition is true then the second argument, @var{then-part}, is
-evaluated and this is used as the result of the evaluation of the entire
-@code{if} function.
-
-If the condition is false then the third argument, @var{else-part}, is
-evaluated and this is the result of the @code{if} function.  If there is
-no third argument, the @code{if} function evaluates to nothing (the
-empty string).
-
-Note that only one of the @var{then-part} or the @var{else-part} will be
-evaluated, never both.  Thus, either can contain side-effects (such as
-@code{shell} function calls, etc.)
-
-@node Call Function, Value Function, If Function, Functions
+@node Call Function, Value Function, Foreach Function, Functions
 @section The @code{call} Function
 @findex call
 @cindex functions, user defined
index 95872f01405fb93197af164d9c0684172c73b96f..62f67f0ab3830837bbe03e6320092000c691fc09 100644 (file)
@@ -1230,6 +1230,110 @@ func_if (char *o, char **argv, const char *funcname UNUSED)
   return o;
 }
 
+/*
+  $(or condition1[,condition2[,condition3[...]]])
+
+  A CONDITION is false iff it evaluates to an empty string.  White
+  space before and after CONDITION are stripped before evaluation.
+
+  CONDITION1 is evaluated.  If it's true, then this is the result of
+  expansion.  If it's false, CONDITION2 is evaluated, and so on.  If none of
+  the conditions are true, the expansion is the empty string.
+
+  Once a CONDITION is true no further conditions are evaluated
+  (short-circuiting).
+*/
+
+static char *
+func_or (char *o, char **argv, const char *funcname UNUSED)
+{
+  for ( ; *argv ; ++argv)
+    {
+      const char *begp = *argv;
+      const char *endp = begp + strlen (*argv) - 1;
+      char *expansion;
+      int result = 0;
+
+      /* Find the result of the condition: if it's false keep going.  */
+
+      strip_whitespace (&begp, &endp);
+
+      if (begp > endp)
+        continue;
+
+      expansion = expand_argument (begp, endp+1);
+      result = strlen (expansion);
+
+      /* If the result is false keep going.  */
+      if (!result)
+        {
+          free (expansion);
+          continue;
+        }
+
+      /* It's true!  Keep this result and return.  */
+      o = variable_buffer_output (o, expansion, result);
+      free (expansion);
+      break;
+    }
+
+  return o;
+}
+
+/*
+  $(and condition1[,condition2[,condition3[...]]])
+
+  A CONDITION is false iff it evaluates to an empty string.  White
+  space before and after CONDITION are stripped before evaluation.
+
+  CONDITION1 is evaluated.  If it's false, then this is the result of
+  expansion.  If it's true, CONDITION2 is evaluated, and so on.  If all of
+  the conditions are true, the expansion is the result of the last condition.
+
+  Once a CONDITION is false no further conditions are evaluated
+  (short-circuiting).
+*/
+
+static char *
+func_and (char *o, char **argv, const char *funcname UNUSED)
+{
+  char *expansion;
+  int result;
+
+  while (1)
+    {
+      const char *begp = *argv;
+      const char *endp = begp + strlen (*argv) - 1;
+
+      /* An empty condition is always false.  */
+      strip_whitespace (&begp, &endp);
+      if (begp > endp)
+        return o;
+
+      expansion = expand_argument (begp, endp+1);
+      result = strlen (expansion);
+
+      /* If the result is false, stop here: we're done.  */
+      if (!result)
+        break;
+
+      /* Otherwise the result is true.  If this is the last one, keep this
+         result and quit.  Otherwise go on to the next one!  */
+
+      if (*(++argv))
+        free (expansion);
+      else
+        {
+          o = variable_buffer_output (o, expansion, result);
+          break;
+        }
+    }
+
+  free (expansion);
+
+  return o;
+}
+
 static char *
 func_wildcard (char *o, char **argv, const char *funcname UNUSED)
 {
@@ -1977,6 +2081,8 @@ static struct function_table_entry function_table_init[] =
   { STRING_SIZE_TUPLE("error"),         0,  1,  1,  func_error},
   { STRING_SIZE_TUPLE("warning"),       0,  1,  1,  func_error},
   { STRING_SIZE_TUPLE("if"),            2,  3,  0,  func_if},
+  { STRING_SIZE_TUPLE("or"),            1,  0,  0,  func_or},
+  { STRING_SIZE_TUPLE("and"),           1,  0,  0,  func_and},
   { STRING_SIZE_TUPLE("value"),         0,  1,  1,  func_value},
   { STRING_SIZE_TUPLE("eval"),          0,  1,  1,  func_eval},
 #ifdef EXPERIMENTAL
diff --git a/hash.c b/hash.c
index 5eed69ccb1c4f988744b0024cbb9aff2ee792e65..f8b65305ff3d3b24928fc6037bfd2140daa7e8eb 100644 (file)
--- a/hash.c
+++ b/hash.c
@@ -126,18 +126,18 @@ hash_find_item (struct hash_table *ht, const void *key)
 }
 
 void *
-hash_insert (struct hash_table *ht, void *item)
+hash_insert (struct hash_table *ht, const void *item)
 {
   void **slot = hash_find_slot (ht, item);
-  void *old_item = slot ? *slot : 0;
+  const void *old_item = slot ? *slot : 0;
   hash_insert_at (ht, item, slot);
-  return ((HASH_VACANT (old_item)) ? 0 : old_item);
+  return (void *)((HASH_VACANT (old_item)) ? 0 : old_item);
 }
 
 void *
-hash_insert_at (struct hash_table *ht, void *item, const void *slot)
+hash_insert_at (struct hash_table *ht, const void *item, const void *slot)
 {
-  void *old_item = *(void **) slot;
+  const void *old_item = *(void **) slot;
   if (HASH_VACANT (old_item))
     {
       ht->ht_fill++;
diff --git a/hash.h b/hash.h
index 405f1dab7f913670ebdfdbd867928aec11b953e8..b6537b6c5dcb8229258251dbc1e43a0eaff5f3ce 100644 (file)
--- a/hash.h
+++ b/hash.h
@@ -63,8 +63,8 @@ void hash_load __P((struct hash_table *ht, void *item_table,
                    unsigned long cardinality, unsigned long size));
 void **hash_find_slot __P((struct hash_table *ht, void const *key));
 void *hash_find_item __P((struct hash_table *ht, void const *key));
-void *hash_insert __P((struct hash_table *ht, void *item));
-void *hash_insert_at __P((struct hash_table *ht, void *item, void const *slot));
+void *hash_insert __P((struct hash_table *ht, const void *item));
+void *hash_insert_at __P((struct hash_table *ht, const void *item, void const *slot));
 void *hash_delete __P((struct hash_table *ht, void const *item));
 void *hash_delete_at __P((struct hash_table *ht, void const *slot));
 void hash_delete_items __P((struct hash_table *ht));
diff --git a/main.c b/main.c
index 8e9187d1a23a310934b06bb04f8933cb883fa8df..01904d78ffb5c2f2b5918cb653b505d8ae97f018 100644 (file)
--- a/main.c
+++ b/main.c
@@ -537,6 +537,7 @@ static void
 initialize_global_hash_tables (void)
 {
   init_hash_global_variable_set ();
+  strcache_init ();
   init_hash_files ();
   hash_init_directories ();
   hash_init_function_table ();
@@ -2974,6 +2975,7 @@ print_data_base (void)
   print_rule_data_base ();
   print_file_data_base ();
   print_vpath_data_base ();
+  strcache_print_stats ("#");
 
   when = time ((time_t *) 0);
   printf (_("\n# Finished Make data base on %s\n"), ctime (&when));
diff --git a/make.h b/make.h
index 972478067fcc1d05d3986c855b33f2f8ceeb3071..c930e783bc124942593933e5cb929fa4e3a75a28 100644 (file)
--- a/make.h
+++ b/make.h
@@ -382,7 +382,7 @@ extern int unixy_shell;
 
 struct floc
   {
-    char *filenm;
+    const char *filenm;
     unsigned long lineno;
   };
 #define NILF ((struct floc *)0)
@@ -465,6 +465,13 @@ extern void close_stdout PARAMS ((void));
 
 extern char *strip_whitespace PARAMS ((const char **begpp, const char **endpp));
 
+/* String caching  */
+extern void strcache_init PARAMS ((void));
+extern void strcache_print_stats PARAMS ((const char *prefix));
+extern int strcache_iscached PARAMS ((const char *str));
+extern const char *strcache_add PARAMS ((const char *str));
+extern const char *strcache_add_len PARAMS ((const char *str, int len));
+extern int strcache_setbufsize PARAMS ((int size));
 
 #ifdef  HAVE_VFORK_H
 # include <vfork.h>
diff --git a/read.c b/read.c
index 677fa4c9f92073195b6911b36b23d978e52afc81..5ec87073dc612a2e8fbbf5aa7366f451c495991b 100644 (file)
--- a/read.c
+++ b/read.c
@@ -311,10 +311,12 @@ eval_makefile (char *filename, int flags)
   struct dep *deps;
   struct ebuffer ebuf;
   const struct floc *curfile;
+  char *expanded = 0;
+  char *included = 0;
   int makefile_errno;
   int r;
 
-  ebuf.floc.filenm = filename;
+  ebuf.floc.filenm = strcache_add (filename);
   ebuf.floc.lineno = 1;
 
   if (ISDB (DB_VERBOSE))
@@ -337,7 +339,7 @@ eval_makefile (char *filename, int flags)
      in which case it was already done.  */
   if (!(flags & RM_NO_TILDE) && filename[0] == '~')
     {
-      char *expanded = tilde_expand (filename);
+      expanded = tilde_expand (filename);
       if (expanded != 0)
        filename = expanded;
     }
@@ -354,16 +356,18 @@ eval_makefile (char *filename, int flags)
       register unsigned int i;
       for (i = 0; include_directories[i] != 0; ++i)
        {
-         char *name = concat (include_directories[i], "/", filename);
-         ebuf.fp = fopen (name, "r");
-         if (ebuf.fp == 0)
-           free (name);
-         else
+         included = concat (include_directories[i], "/", filename);
+         ebuf.fp = fopen (included, "r");
+         if (ebuf.fp)
            {
-             filename = name;
+             filename = included;
              break;
            }
+          free (included);
        }
+      /* If we're not using it, we already freed it above.  */
+      if (filename != included)
+        included = 0;
     }
 
   /* Add FILENAME to the chain of read makefiles.  */
@@ -374,8 +378,6 @@ eval_makefile (char *filename, int flags)
   deps->file = lookup_file (filename);
   if (deps->file == 0)
     deps->file = enter_file (xstrdup (filename));
-  if (filename != ebuf.floc.filenm)
-    free (filename);
   filename = deps->file->name;
   deps->changed = flags;
   deps->ignore_mtime = 0;
@@ -384,6 +386,11 @@ eval_makefile (char *filename, int flags)
   if (flags & RM_DONTCARE)
     deps->file->dontcare = 1;
 
+  if (expanded)
+    free (expanded);
+  if (included)
+    free (included);
+
   /* If the makefile can't be found at all, give up entirely.  */
 
   if (ebuf.fp == 0)
diff --git a/strcache.c b/strcache.c
new file mode 100644 (file)
index 0000000..b20a511
--- /dev/null
@@ -0,0 +1,219 @@
+/* Constant string caching for GNU Make.
+Copyright (C) 2006 Free Software Foundation, Inc.
+This file is part of GNU Make.
+
+GNU Make is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Make is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Make; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+Boston, MA 02110-1301 USA.  */
+
+#include "make.h"
+
+#include <assert.h>
+
+#include "hash.h"
+
+/* The size (in bytes) of each cache buffer.  */
+#define CACHE_BUFFER_SIZE   (4096)
+
+
+/* A string cached here will never be freed, so we don't need to worry about
+   reference counting.  We just store the string, and then remember it in a
+   hash so it can be looked up again. */
+
+struct strcache {
+  struct strcache *next;    /* The next block of strings.  */
+  char *end;                /* Pointer to the beginning of the free space.  */
+  int count;                /* # of strings in this buffer (for stats).  */
+  int bytesfree;            /* The amount of the buffer that is free.  */
+  char buffer[1];           /* The buffer comes after this.  */
+};
+
+static int bufsize = CACHE_BUFFER_SIZE;
+static struct strcache *strcache = NULL;
+
+static struct strcache *
+new_cache()
+{
+  struct strcache *new;
+  new = (struct strcache *) xmalloc (sizeof (*new) + bufsize);
+  new->end = new->buffer;
+  new->count = 0;
+  new->bytesfree = bufsize;
+
+  new->next = strcache;
+  strcache = new;
+
+  return new;
+}
+
+static const char *
+add_string(const char *str, int len)
+{
+  struct strcache *best = NULL;
+  struct strcache *sp;
+  const char *res;
+
+  /* If the string we want is too large to fit into a single buffer, then we're
+     screwed; nothing will ever fit!  Change the maximum size of the cache to
+     be big enough.  */
+  if (len > bufsize)
+    bufsize = len * 2;
+
+  /* First, find a cache with enough free space.  We always look through all
+     the blocks and choose the one with the best fit (the one that leaves the
+     least amount of space free).  */
+  for (sp = strcache; sp != NULL; sp = sp->next)
+    if (sp->bytesfree > len && (!best || best->bytesfree > sp->bytesfree))
+      best = sp;
+
+  /* If nothing is big enough, make a new cache.  */
+  if (!best)
+    best = new_cache();
+
+  assert (best->bytesfree > len);
+
+  /* Add the string to the best cache.  */
+  res = best->end;
+  memcpy (best->end, str, len);
+  best->end += len;
+  *(best->end++) = '\0';
+  best->bytesfree -= len + 1;
+  ++best->count;
+
+  return res;
+}
+
+
+/* Hash table of strings in the cache.  */
+
+static unsigned long
+str_hash_1 (const void *key)
+{
+  return_ISTRING_HASH_1 ((const char *) key);
+}
+
+static unsigned long
+str_hash_2 (const void *key)
+{
+  return_ISTRING_HASH_2 ((const char *) key);
+}
+
+static int
+str_hash_cmp (const void *x, const void *y)
+{
+  return_ISTRING_COMPARE ((const char *) x, (const char *) y);
+}
+
+static struct hash_table strings;
+
+static const char *
+add_hash (const char *str, int len)
+{
+  /* Look up the string in the hash.  If it's there, return it.  */
+  char **slot = (char **) hash_find_slot (&strings, str);
+  const char *key = *slot;
+
+  if (!HASH_VACANT (key))
+    return key;
+
+  /* Not there yet so add it to a buffer, then into the hash table.  */
+  key = add_string (str, len);
+  hash_insert_at (&strings, key, slot);
+  return key;
+}
+
+/* Returns true if the string is in the cache; false if not.  */
+int
+strcache_iscached (const char *str)
+{
+  struct strcache *sp;
+
+  for (sp = strcache; sp != 0; sp = sp->next)
+    if (str >= sp->buffer && str < sp->end)
+      return 1;
+
+  return 0;
+}
+
+/* If the string is already in the cache, return a pointer to the cached
+   version.  If not, add it then return a pointer to the cached version.
+   Note we do NOT take control of the string passed in.  */
+const char *
+strcache_add (const char *str)
+{
+  return add_hash (str, strlen (str));
+}
+
+const char *
+strcache_add_len (const char *str, int len)
+{
+  char *key = alloca (len + 1);
+  memcpy (key, str, len);
+  key[len] = '\0';
+
+  return add_hash (key, len);
+}
+
+int
+strcache_setbufsize(int size)
+{
+  if (size > bufsize)
+    bufsize = size;
+  return bufsize;
+}
+
+void
+strcache_init (void)
+{
+  hash_init (&strings, 1000, str_hash_1, str_hash_2, str_hash_cmp);
+}
+
+
+/* Generate some stats output.  */
+
+void
+strcache_print_stats (const char *prefix)
+{
+  int numbuffs = 0, numstrs = 0;
+  int totsize = 0, avgsize, maxsize = 0, minsize = bufsize;
+  int totfree = 0, avgfree, maxfree = 0, minfree = bufsize;
+  const struct strcache *sp;
+
+  for (sp = strcache; sp != NULL; sp = sp->next)
+    {
+      int bf = sp->bytesfree;
+      int sz = (sp->end - sp->buffer) + bf;
+
+      ++numbuffs;
+      numstrs += sp->count;
+
+      totsize += sz;
+      maxsize = (sz > maxsize ? sz : maxsize);
+      minsize = (sz < minsize ? sz : minsize);
+
+      totfree += bf;
+      maxfree = (bf > maxfree ? bf : maxfree);
+      minfree = (bf < minfree ? bf : minfree);
+    }
+
+  avgsize = numbuffs ? (int)(totsize / numbuffs) : 0;
+  avgfree = numbuffs ? (int)(totfree / numbuffs) : 0;
+
+  printf ("\n%s # of strings in strcache: %d\n", prefix, numstrs);
+  printf ("%s # of strcache buffers: %d\n", prefix, numbuffs);
+  printf ("%s strcache size: total = %d / max = %d / min = %d / avg = %d\n",
+          prefix, totsize, maxsize, minsize, avgsize);
+  printf ("%s strcache free: total = %d / max = %d / min = %d / avg = %d\n",
+          prefix, totfree, maxfree, minfree, avgfree);
+}
index 3e05ab8fd189ae1d7d46d5392126137e6a6472fe..950c71a12b9aa0968771417cdcfd53e5e7c3e04c 100644 (file)
@@ -1,3 +1,13 @@
+2006-02-09  Paul D. Smith  <psmith@gnu.org>
+
+       * run_make_tests.pl (set_more_defaults): Update valgrind support
+       for newer versions.
+       * test_driver.pl (toplevel): Skip all hidden files/directories (ones
+       beginning with ".").
+
+       * scripts/functions/andor: Tests for $(and ..) and $(or ...)
+       functions.
+
 2006-02-08  Boris Kolpackov  <boris@kolpackov.net>
 
        * scripts/features/parallelism: Add a test for bug #15641.
 
 2002-07-11  Paul D. Smith  <psmith@gnu.org>
 
-       * run_make_tests.pl (valid_option): Add support for Valgrind
-       <http://developer.kde.org/~sewardj/>.  Use -valgrind option to the
-       test suite.
+       * run_make_tests.pl (valid_option): Add support for Valgrind.  Use
+       -valgrind option to the test suite.
        (set_more_defaults): Set up the file descriptor to capture
        Valgrind output.  We have to unset its close-on-exec flag; we
        hardcode the value for F_SETFD (2) rather than load it; hopefully
index b7614e2078375d5ba6f451f1dd86a8a5fe50bec7..5c500ef5879054830927117b99ae593f13d835f5 100755 (executable)
@@ -12,6 +12,7 @@
 #                        (and others)
 
 $valgrind = 0;              # invoke make with valgrind
+$valgrind_args = '--num-callers=15 --tool=memcheck --leak-check=full';
 $pure_log = undef;
 
 require "test_driver.pl";
@@ -314,7 +315,8 @@ sub set_more_defaults
      open(VALGRIND, "> valgrind.out")
        || die "Cannot open valgrind.out: $!\n";
      #  -q --leak-check=yes
-     $make_path = "valgrind --num-callers=15 --logfile-fd=".fileno(VALGRIND)." $make_path";
+     exists $ENV{VALGRIND_ARGS} and $valgrind_args = $ENV{VALGRIND_ARGS};
+     $make_path = "valgrind --log-fd=".fileno(VALGRIND)." $valgrind_args $make_path";
      # F_SETFD is 2
      fcntl(VALGRIND, 2, 0) or die "fcntl(setfd) failed: $!\n";
      system("echo Starting on `date` 1>&".fileno(VALGRIND));
diff --git a/tests/scripts/functions/andor b/tests/scripts/functions/andor
new file mode 100644 (file)
index 0000000..62e0c2e
--- /dev/null
@@ -0,0 +1,50 @@
+#                                                                    -*-perl-*-
+$description = "Test the and & or functions.\n";
+
+$details = "Try various uses of and & or to ensure they all give the correct
+results.\n";
+
+# TEST #0
+# For $(and ...), it will either be empty or the last value
+run_make_test('
+NEQ = $(subst $1,,$2)
+f =
+t = true
+
+all:
+       @echo 1 $(and    ,$t)
+       @echo 2 $(and $t)
+       @echo 3 $(and $t,)
+       @echo 4 $(and z,true,$f,false)
+       @echo 5 $(and $t,$f,$(info bad short-circuit))
+       @echo 6 $(and $(call NEQ,a,b),true)
+       @echo 7 $(and $(call NEQ,a,a),true)
+       @echo 8 $(and z,true,fal,se) hi
+       @echo 9 $(and ,true,fal,se)there
+       @echo 10 $(and   $(e) ,$t)',
+              '',
+              "1\n2 true\n3\n4\n5\n6 true\n7\n8 se hi\n9 there\n10\n");
+
+# TEST #1
+# For $(or ...), it will either be empty or the first true value
+run_make_test('
+NEQ = $(subst $1,,$2)
+f =
+t = true
+
+all:
+       @echo 1 $(or    ,    )
+       @echo 2 $(or $t)
+       @echo 3 $(or ,$t)
+       @echo 4 $(or z,true,$f,false)
+       @echo 5 $(or $t,$(info bad short-circuit))
+       @echo 6 $(or $(info short-circuit),$t)
+       @echo 7 $(or $(call NEQ,a,b),true)
+       @echo 8 $(or $(call NEQ,a,a),true)
+       @echo 9 $(or z,true,fal,se) hi
+       @echo 10 $(or ,true,fal,se)there
+       @echo 11 $(or   $(e) ,$f)',
+              '',
+              "short-circuit\n1\n2 true\n3 true\n4 z\n5 true\n6 true\n7 b\n8 true\n9 z hi\n10 truethere\n11\n");
+
+1;
index 4b4e72bd4f5b8ff28ecdcdc2d19f240034a928b1..631fa1abbc441391a6d554e21a70e0f15abe6437 100644 (file)
@@ -148,22 +148,21 @@ sub toplevel
     print "Finding tests...\n";
     opendir (SCRIPTDIR, $scriptpath)
        || &error ("Couldn't opendir $scriptpath: $!\n");
-    @dirs = grep (!/^(\.\.?|CVS|RCS)$/, readdir (SCRIPTDIR) );
+    @dirs = grep (!/^(\..*|CVS|RCS)$/, readdir (SCRIPTDIR) );
     closedir (SCRIPTDIR);
     foreach $dir (@dirs)
     {
-      next if ($dir =~ /^\.\.?$/ || $dir eq 'CVS' || $dir eq 'RCS'
-               || ! -d "$scriptpath/$dir");
+      next if ($dir =~ /^(\..*|CVS|RCS)$/ || ! -d "$scriptpath/$dir");
       push (@rmdirs, $dir);
       mkdir ("$workpath/$dir", 0777)
            || &error ("Couldn't mkdir $workpath/$dir: $!\n");
       opendir (SCRIPTDIR, "$scriptpath/$dir")
          || &error ("Couldn't opendir $scriptpath/$dir: $!\n");
-      @files = grep (!/^(\.\.?|CVS|RCS)$/, readdir (SCRIPTDIR) );
+      @files = grep (!/^(\..*|CVS|RCS|.*~)$/, readdir (SCRIPTDIR) );
       closedir (SCRIPTDIR);
       foreach $test (@files)
       {
-        next if $test =~ /~$/ || -d $test;
+        -d $test and next;
        push (@TESTS, "$dir/$test");
       }
     }