summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Smith <psmith@gnu.org>2006-02-10 05:29:00 +0000
committerPaul Smith <psmith@gnu.org>2006-02-10 05:29:00 +0000
commit5a7a42cfce638f52f702b4d317c45c7186b8c0b4 (patch)
treef212e35eac087bedeb88d64d96fb5a4c8c25d08c
parentd0c4e92f1145110793ffb04596019a864c88d2fc (diff)
downloadgunmake-5a7a42cfce638f52f702b4d317c45c7186b8c0b4.tar.gz
- New code capability: a read-only string cache. Start of solution for
Savannah bug #15182, but not much uses it yet. Coming shortly. - Added short-circuiting $(and ..) and $(or ...) functions.
-rw-r--r--ChangeLog25
-rw-r--r--Makefile.am2
-rw-r--r--NEWS8
-rw-r--r--doc/make.texi107
-rw-r--r--function.c106
-rw-r--r--hash.c10
-rw-r--r--hash.h4
-rw-r--r--main.c2
-rw-r--r--make.h9
-rw-r--r--read.c27
-rw-r--r--strcache.c219
-rw-r--r--tests/ChangeLog15
-rwxr-xr-xtests/run_make_tests.pl4
-rw-r--r--tests/scripts/functions/andor50
-rw-r--r--tests/test_driver.pl9
15 files changed, 530 insertions, 67 deletions
diff --git a/ChangeLog b/ChangeLog
index fbe12be..592112f 100644
--- 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.
diff --git a/Makefile.am b/Makefile.am
index ebd83a7..6c0abc9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 1bf8301..7289f39 100644
--- 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.
diff --git a/doc/make.texi b/doc/make.texi
index 96d0f71..b17a234 100644
--- a/doc/make.texi
+++ b/doc/make.texi
@@ -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
diff --git a/function.c b/function.c
index 95872f0..62f67f0 100644
--- a/function.c
+++ b/function.c
@@ -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 5eed69c..f8b6530 100644
--- 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 405f1da..b6537b6 100644
--- 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 8e9187d..01904d7 100644
--- 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 9724780..c930e78 100644
--- 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 677fa4c..5ec8707 100644
--- 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
index 0000000..b20a511
--- /dev/null
+++ b/strcache.c
@@ -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);
+}
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 3e05ab8..950c71a 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -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.
@@ -471,9 +481,8 @@
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
diff --git a/tests/run_make_tests.pl b/tests/run_make_tests.pl
index b7614e2..5c500ef 100755
--- a/tests/run_make_tests.pl
+++ b/tests/run_make_tests.pl
@@ -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
index 0000000..62e0c2e
--- /dev/null
+++ b/tests/scripts/functions/andor
@@ -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;
diff --git a/tests/test_driver.pl b/tests/test_driver.pl
index 4b4e72b..631fa1a 100644
--- a/tests/test_driver.pl
+++ b/tests/test_driver.pl
@@ -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");
}
}