From 719c04a28bfaf9124abdfc4cbf4bf5a4663be98a Mon Sep 17 00:00:00 2001 From: psmith Date: Fri, 10 Feb 2006 05:29:00 +0000 Subject: [PATCH] - 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. --- ChangeLog | 25 +++++ Makefile.am | 2 +- NEWS | 8 ++ doc/make.texi | 109 +++++++++++++-------- function.c | 106 ++++++++++++++++++++ hash.c | 10 +- hash.h | 4 +- main.c | 2 + make.h | 9 +- read.c | 27 ++++-- strcache.c | 219 ++++++++++++++++++++++++++++++++++++++++++ tests/ChangeLog | 15 ++- tests/run_make_tests.pl | 4 +- tests/scripts/functions/andor | 50 ++++++++++ tests/test_driver.pl | 11 +-- 15 files changed, 532 insertions(+), 69 deletions(-) create mode 100644 strcache.c create mode 100644 tests/scripts/functions/andor diff --git a/ChangeLog b/ChangeLog index fbe12be..592112f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2006-02-10 Paul D. Smith + + 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 + + * 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 * 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 812822e..499fd2e 100644 --- a/doc/make.texi +++ b/doc/make.texi @@ -7,7 +7,7 @@ @c FSF publishers: format makebook.texi instead of using this file directly. -@set RCSID $Id: make.texi,v 1.41 2006/02/06 16:22:01 psmith Exp $ +@set RCSID $Id: make.texi,v 1.42 2006/02/10 05:29:00 psmith Exp $ @set EDITION 0.70 @set VERSION 3.81 @set UPDATED 5 Feb 2006 @@ -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 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 + +#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 + + * 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 * scripts/features/parallelism: Add a test for bug #15641. @@ -471,9 +481,8 @@ 2002-07-11 Paul D. Smith - * run_make_tests.pl (valid_option): Add support for Valgrind - . 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 c373b8f..9a809c6 100644 --- a/tests/test_driver.pl +++ b/tests/test_driver.pl @@ -12,7 +12,7 @@ # this routine controls the whole mess; each test suite sets up a few # variables and then calls &toplevel, which does all the real work. -# $Id: test_driver.pl,v 1.15 2005/07/12 04:35:13 psmith Exp $ +# $Id: test_driver.pl,v 1.16 2006/02/10 05:29:00 psmith Exp $ # The number of test categories we've run @@ -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"); } } -- 2.11.4.GIT