mirror of
https://github.com/mirror/make.git
synced 2025-02-05 17:20:15 +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>
|
2006-02-08 Boris Kolpackov <boris@kolpackov.net>
|
||||||
|
|
||||||
* job.h (struct child): Add dontcare bitfield.
|
* 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 \
|
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 \
|
function.c getopt.c getopt1.c implicit.c job.c main.c \
|
||||||
misc.c read.c remake.c $(remote) rule.c signame.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
|
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
|
- $(info ...) prints its arguments to stdout. No makefile name or
|
||||||
line number info, etc. is printed.
|
line number info, etc. is printed.
|
||||||
- $(flavor ...) returns the flavor of a variable.
|
- $(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:
|
* Changes made for POSIX compatibility:
|
||||||
- Only touch targets (under -t) if they have at least one command.
|
- 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.
|
* Sequences:: Defining canned sequences of commands.
|
||||||
* Empty Commands:: Defining useful, do-nothing 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}
|
Recursive Use of @code{make}
|
||||||
|
|
||||||
* MAKE Variable:: The special effects of using @samp{$(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.
|
* Syntax of Functions:: How to write a function call.
|
||||||
* Text Functions:: General-purpose text manipulation functions.
|
* Text Functions:: General-purpose text manipulation functions.
|
||||||
* File Name Functions:: Functions for manipulating file names.
|
* File Name Functions:: Functions for manipulating file names.
|
||||||
|
* Conditional Functions:: Functions that implement conditions.
|
||||||
* Foreach Function:: Repeat some text with controlled variation.
|
* Foreach Function:: Repeat some text with controlled variation.
|
||||||
* If Function:: Conditionally expand a value.
|
|
||||||
* Call Function:: Expand a user-defined function.
|
* Call Function:: Expand a user-defined function.
|
||||||
* Value Function:: Return the un-expanded value of a variable.
|
* Value Function:: Return the un-expanded value of a variable.
|
||||||
* Eval Function:: Evaluate the arguments as makefile syntax.
|
* 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.
|
* Syntax of Functions:: How to write a function call.
|
||||||
* Text Functions:: General-purpose text manipulation functions.
|
* Text Functions:: General-purpose text manipulation functions.
|
||||||
* File Name Functions:: Functions for manipulating file names.
|
* File Name Functions:: Functions for manipulating file names.
|
||||||
|
* Conditional Functions:: Functions that implement conditions.
|
||||||
* Foreach Function:: Repeat some text with controlled variation.
|
* Foreach Function:: Repeat some text with controlled variation.
|
||||||
* If Function:: Conditionally expand a value.
|
|
||||||
* Call Function:: Expand a user-defined function.
|
* Call Function:: Expand a user-defined function.
|
||||||
* Value Function:: Return the un-expanded value of a variable.
|
* Value Function:: Return the un-expanded value of a variable.
|
||||||
* Eval Function:: Evaluate the arguments as makefile syntax.
|
* 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
|
@code{CFLAGS} was specified with a command argument (@pxref{Override
|
||||||
Directive, , The @code{override} Directive}).
|
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
|
@section Functions for File Names
|
||||||
@cindex functions, for file names
|
@cindex functions, for file names
|
||||||
@cindex file name functions
|
@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.
|
@code{wildcard} function to test for existence.
|
||||||
@end table
|
@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
|
@section The @code{foreach} Function
|
||||||
@findex foreach
|
@findex foreach
|
||||||
@cindex words, iterating over
|
@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,
|
whose name is @samp{Esta escrito en espanol!} (es un nombre bastante largo,
|
||||||
no?), but it is more likely to be a mistake.
|
no?), but it is more likely to be a mistake.
|
||||||
|
|
||||||
@node If Function, Call Function, Foreach Function, Functions
|
@node Call Function, Value 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
|
|
||||||
@section The @code{call} Function
|
@section The @code{call} Function
|
||||||
@findex call
|
@findex call
|
||||||
@cindex functions, user defined
|
@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;
|
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 *
|
static char *
|
||||||
func_wildcard (char *o, char **argv, const char *funcname UNUSED)
|
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("error"), 0, 1, 1, func_error},
|
||||||
{ STRING_SIZE_TUPLE("warning"), 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("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("value"), 0, 1, 1, func_value},
|
||||||
{ STRING_SIZE_TUPLE("eval"), 0, 1, 1, func_eval},
|
{ STRING_SIZE_TUPLE("eval"), 0, 1, 1, func_eval},
|
||||||
#ifdef EXPERIMENTAL
|
#ifdef EXPERIMENTAL
|
||||||
|
10
hash.c
10
hash.c
@ -126,18 +126,18 @@ hash_find_item (struct hash_table *ht, const void *key)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void *
|
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 **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);
|
hash_insert_at (ht, item, slot);
|
||||||
return ((HASH_VACANT (old_item)) ? 0 : old_item);
|
return (void *)((HASH_VACANT (old_item)) ? 0 : old_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
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))
|
if (HASH_VACANT (old_item))
|
||||||
{
|
{
|
||||||
ht->ht_fill++;
|
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));
|
unsigned long cardinality, unsigned long size));
|
||||||
void **hash_find_slot __P((struct hash_table *ht, void const *key));
|
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_find_item __P((struct hash_table *ht, void const *key));
|
||||||
void *hash_insert __P((struct hash_table *ht, void *item));
|
void *hash_insert __P((struct hash_table *ht, const void *item));
|
||||||
void *hash_insert_at __P((struct hash_table *ht, void *item, void const *slot));
|
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 __P((struct hash_table *ht, void const *item));
|
||||||
void *hash_delete_at __P((struct hash_table *ht, void const *slot));
|
void *hash_delete_at __P((struct hash_table *ht, void const *slot));
|
||||||
void hash_delete_items __P((struct hash_table *ht));
|
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)
|
initialize_global_hash_tables (void)
|
||||||
{
|
{
|
||||||
init_hash_global_variable_set ();
|
init_hash_global_variable_set ();
|
||||||
|
strcache_init ();
|
||||||
init_hash_files ();
|
init_hash_files ();
|
||||||
hash_init_directories ();
|
hash_init_directories ();
|
||||||
hash_init_function_table ();
|
hash_init_function_table ();
|
||||||
@ -2974,6 +2975,7 @@ print_data_base (void)
|
|||||||
print_rule_data_base ();
|
print_rule_data_base ();
|
||||||
print_file_data_base ();
|
print_file_data_base ();
|
||||||
print_vpath_data_base ();
|
print_vpath_data_base ();
|
||||||
|
strcache_print_stats ("#");
|
||||||
|
|
||||||
when = time ((time_t *) 0);
|
when = time ((time_t *) 0);
|
||||||
printf (_("\n# Finished Make data base on %s\n"), ctime (&when));
|
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
|
struct floc
|
||||||
{
|
{
|
||||||
char *filenm;
|
const char *filenm;
|
||||||
unsigned long lineno;
|
unsigned long lineno;
|
||||||
};
|
};
|
||||||
#define NILF ((struct floc *)0)
|
#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));
|
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
|
#ifdef HAVE_VFORK_H
|
||||||
# include <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 dep *deps;
|
||||||
struct ebuffer ebuf;
|
struct ebuffer ebuf;
|
||||||
const struct floc *curfile;
|
const struct floc *curfile;
|
||||||
|
char *expanded = 0;
|
||||||
|
char *included = 0;
|
||||||
int makefile_errno;
|
int makefile_errno;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
ebuf.floc.filenm = filename;
|
ebuf.floc.filenm = strcache_add (filename);
|
||||||
ebuf.floc.lineno = 1;
|
ebuf.floc.lineno = 1;
|
||||||
|
|
||||||
if (ISDB (DB_VERBOSE))
|
if (ISDB (DB_VERBOSE))
|
||||||
@ -337,7 +339,7 @@ eval_makefile (char *filename, int flags)
|
|||||||
in which case it was already done. */
|
in which case it was already done. */
|
||||||
if (!(flags & RM_NO_TILDE) && filename[0] == '~')
|
if (!(flags & RM_NO_TILDE) && filename[0] == '~')
|
||||||
{
|
{
|
||||||
char *expanded = tilde_expand (filename);
|
expanded = tilde_expand (filename);
|
||||||
if (expanded != 0)
|
if (expanded != 0)
|
||||||
filename = expanded;
|
filename = expanded;
|
||||||
}
|
}
|
||||||
@ -354,16 +356,18 @@ eval_makefile (char *filename, int flags)
|
|||||||
register unsigned int i;
|
register unsigned int i;
|
||||||
for (i = 0; include_directories[i] != 0; ++i)
|
for (i = 0; include_directories[i] != 0; ++i)
|
||||||
{
|
{
|
||||||
char *name = concat (include_directories[i], "/", filename);
|
included = concat (include_directories[i], "/", filename);
|
||||||
ebuf.fp = fopen (name, "r");
|
ebuf.fp = fopen (included, "r");
|
||||||
if (ebuf.fp == 0)
|
if (ebuf.fp)
|
||||||
free (name);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
filename = name;
|
filename = included;
|
||||||
break;
|
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. */
|
/* Add FILENAME to the chain of read makefiles. */
|
||||||
@ -374,8 +378,6 @@ eval_makefile (char *filename, int flags)
|
|||||||
deps->file = lookup_file (filename);
|
deps->file = lookup_file (filename);
|
||||||
if (deps->file == 0)
|
if (deps->file == 0)
|
||||||
deps->file = enter_file (xstrdup (filename));
|
deps->file = enter_file (xstrdup (filename));
|
||||||
if (filename != ebuf.floc.filenm)
|
|
||||||
free (filename);
|
|
||||||
filename = deps->file->name;
|
filename = deps->file->name;
|
||||||
deps->changed = flags;
|
deps->changed = flags;
|
||||||
deps->ignore_mtime = 0;
|
deps->ignore_mtime = 0;
|
||||||
@ -384,6 +386,11 @@ eval_makefile (char *filename, int flags)
|
|||||||
if (flags & RM_DONTCARE)
|
if (flags & RM_DONTCARE)
|
||||||
deps->file->dontcare = 1;
|
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 the makefile can't be found at all, give up entirely. */
|
||||||
|
|
||||||
if (ebuf.fp == 0)
|
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>
|
2006-02-08 Boris Kolpackov <boris@kolpackov.net>
|
||||||
|
|
||||||
* scripts/features/parallelism: Add a test for bug #15641.
|
* scripts/features/parallelism: Add a test for bug #15641.
|
||||||
@ -471,9 +481,8 @@
|
|||||||
|
|
||||||
2002-07-11 Paul D. Smith <psmith@gnu.org>
|
2002-07-11 Paul D. Smith <psmith@gnu.org>
|
||||||
|
|
||||||
* run_make_tests.pl (valid_option): Add support for Valgrind
|
* run_make_tests.pl (valid_option): Add support for Valgrind. Use
|
||||||
<http://developer.kde.org/~sewardj/>. Use -valgrind option to the
|
-valgrind option to the test suite.
|
||||||
test suite.
|
|
||||||
(set_more_defaults): Set up the file descriptor to capture
|
(set_more_defaults): Set up the file descriptor to capture
|
||||||
Valgrind output. We have to unset its close-on-exec flag; we
|
Valgrind output. We have to unset its close-on-exec flag; we
|
||||||
hardcode the value for F_SETFD (2) rather than load it; hopefully
|
hardcode the value for F_SETFD (2) rather than load it; hopefully
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# (and others)
|
# (and others)
|
||||||
|
|
||||||
$valgrind = 0; # invoke make with valgrind
|
$valgrind = 0; # invoke make with valgrind
|
||||||
|
$valgrind_args = '--num-callers=15 --tool=memcheck --leak-check=full';
|
||||||
$pure_log = undef;
|
$pure_log = undef;
|
||||||
|
|
||||||
require "test_driver.pl";
|
require "test_driver.pl";
|
||||||
@ -314,7 +315,8 @@ sub set_more_defaults
|
|||||||
open(VALGRIND, "> valgrind.out")
|
open(VALGRIND, "> valgrind.out")
|
||||||
|| die "Cannot open valgrind.out: $!\n";
|
|| die "Cannot open valgrind.out: $!\n";
|
||||||
# -q --leak-check=yes
|
# -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
|
# F_SETFD is 2
|
||||||
fcntl(VALGRIND, 2, 0) or die "fcntl(setfd) failed: $!\n";
|
fcntl(VALGRIND, 2, 0) or die "fcntl(setfd) failed: $!\n";
|
||||||
system("echo Starting on `date` 1>&".fileno(VALGRIND));
|
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";
|
print "Finding tests...\n";
|
||||||
opendir (SCRIPTDIR, $scriptpath)
|
opendir (SCRIPTDIR, $scriptpath)
|
||||||
|| &error ("Couldn't opendir $scriptpath: $!\n");
|
|| &error ("Couldn't opendir $scriptpath: $!\n");
|
||||||
@dirs = grep (!/^(\.\.?|CVS|RCS)$/, readdir (SCRIPTDIR) );
|
@dirs = grep (!/^(\..*|CVS|RCS)$/, readdir (SCRIPTDIR) );
|
||||||
closedir (SCRIPTDIR);
|
closedir (SCRIPTDIR);
|
||||||
foreach $dir (@dirs)
|
foreach $dir (@dirs)
|
||||||
{
|
{
|
||||||
next if ($dir =~ /^\.\.?$/ || $dir eq 'CVS' || $dir eq 'RCS'
|
next if ($dir =~ /^(\..*|CVS|RCS)$/ || ! -d "$scriptpath/$dir");
|
||||||
|| ! -d "$scriptpath/$dir");
|
|
||||||
push (@rmdirs, $dir);
|
push (@rmdirs, $dir);
|
||||||
mkdir ("$workpath/$dir", 0777)
|
mkdir ("$workpath/$dir", 0777)
|
||||||
|| &error ("Couldn't mkdir $workpath/$dir: $!\n");
|
|| &error ("Couldn't mkdir $workpath/$dir: $!\n");
|
||||||
opendir (SCRIPTDIR, "$scriptpath/$dir")
|
opendir (SCRIPTDIR, "$scriptpath/$dir")
|
||||||
|| &error ("Couldn't opendir $scriptpath/$dir: $!\n");
|
|| &error ("Couldn't opendir $scriptpath/$dir: $!\n");
|
||||||
@files = grep (!/^(\.\.?|CVS|RCS)$/, readdir (SCRIPTDIR) );
|
@files = grep (!/^(\..*|CVS|RCS|.*~)$/, readdir (SCRIPTDIR) );
|
||||||
closedir (SCRIPTDIR);
|
closedir (SCRIPTDIR);
|
||||||
foreach $test (@files)
|
foreach $test (@files)
|
||||||
{
|
{
|
||||||
next if $test =~ /~$/ || -d $test;
|
-d $test and next;
|
||||||
push (@TESTS, "$dir/$test");
|
push (@TESTS, "$dir/$test");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user