mirror of
https://github.com/mirror/make.git
synced 2025-01-27 21:00:22 +08:00
- 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.
This commit is contained in:
parent
d0c4e92f11
commit
5a7a42cfce
25
ChangeLog
25
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.
|
||||
|
@ -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
|
||||
|
||||
|
8
NEWS
8
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.
|
||||
|
107
doc/make.texi
107
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
|
||||
|
106
function.c
106
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
|
||||
|
10
hash.c
10
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++;
|
||||
|
4
hash.h
4
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));
|
||||
|
2
main.c
2
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));
|
||||
|
9
make.h
9
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>
|
||||
|
27
read.c
27
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)
|
||||
|
219
strcache.c
Normal file
219
strcache.c
Normal file
@ -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);
|
||||
}
|
@ -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
|
||||
|
@ -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));
|
||||
|
50
tests/scripts/functions/andor
Normal file
50
tests/scripts/functions/andor
Normal file
@ -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;
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user