From bccb277dda1a4dcc6729824a7c9d544086f147c3 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Thu, 1 Aug 2002 13:16:57 +0000 Subject: New variables, .VARIABLES and .TARGETS. --- ChangeLog | 25 ++++++++++++++++ NEWS | 7 ++++- doc/make.texi | 36 ++++++++++++++++++++-- file.c | 49 ++++++++++++++++++++++++++++++ filedef.h | 1 + main.c | 4 +++ make.h | 4 +-- variable.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- variable.h | 10 ++++--- 9 files changed, 210 insertions(+), 21 deletions(-) diff --git a/ChangeLog b/ChangeLog index 48adc23..0567746 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2002-08-01 Paul D. Smith + + Add new introspection variables .VARIABLES and .TARGETS. + + * variable.c (handle_special_var): New function. If the variable + reference passed in is "special" (.VARIABLES or .TARGETS), + calculate the new value if necessary. .VARIABLES is handled here: + walk through the hash of defined variables and construct a value + which is a list of the names. .TARGETS is handled by + build_target_list(). + (lookup_variable): Invoke handle_special_var(). + * file.c (build_target_list): Walk through the hask of known files + and construct a list of the names of all the ones marked as + targets. + * main.c (main): Initialize them to empty (and as simple variables). + * doc/make.texi (Special Variables): Document them. + * NEWS: Mention them. + + * variable.h (struct variable): Add a new flag "exportable" which + is true if the variable name is valid for export. + * variable.c (define_variable_in_set): Set "exportable" when a new + variable is defined. + (target_environment): Use the "exportable" flag instead of + re-checking the name here... an efficiency improvement. + 2002-07-10 Paul D. Smith * variable.c (pop_variable_scope): Remove variable made unused by diff --git a/NEWS b/NEWS index 724458b..bd4af15 100644 --- a/NEWS +++ b/NEWS @@ -26,7 +26,7 @@ Version 3.80 This syntax is only valid within explicit and static pattern rules: it cannot be used in implicit (suffix or pattern) rules. Edouard G. Parmelan provided a patch implementing this feature; however, I - decided to implemented it myself in a different way. + decided to implemented it in a different way. * A new function is defined: $(quote ...). The argument to this function is the _name_ of a variable. The result of the function is @@ -44,6 +44,11 @@ Version 3.80 list when a makefile is just being read (before any includes) is the name of the current makefile. +* GNU make now supports some simple introspection capability: two new + built-in variables are defined: $(.VARIABLES) and $(.TARGETS). These + expand to a complete list of variables and targets, respectively, + defined by all makefiles at the time the variables are expanded. + * The arguments to $(call ...) functions were being stored in $1, $2, etc. as recursive variables, even though they are fully expanded before assignment. This means that escaped dollar signs ($$ etc.) diff --git a/doc/make.texi b/doc/make.texi index 9a13b78..291a294 100644 --- a/doc/make.texi +++ b/doc/make.texi @@ -149,6 +149,7 @@ Writing Makefiles * Include:: How one makefile can use another makefile. * MAKEFILES Variable:: The environment can specify extra makefiles. * MAKEFILE_LIST Variable:: Discover which makefiles have been read. +* Special Variables:: Other special variables. * Remaking Makefiles:: How makefiles get remade. * Overriding Makefiles:: How to override part of one makefile with another makefile. @@ -929,6 +930,7 @@ reading a data base called the @dfn{makefile}. * Include:: How one makefile can use another makefile. * MAKEFILES Variable:: The environment can specify extra makefiles. * MAKEFILE_LIST Variable:: Discover which makefiles have been read. +* Special Variables:: Other special variables. * Remaking Makefiles:: How makefiles get remade. * Overriding Makefiles:: How to override part of one makefile with another makefile. @@ -1184,7 +1186,7 @@ This is a very bad idea, because such makefiles will fail to work if run by anyone else. It is much better to write explicit @code{include} directives in the makefiles. @xref{Include, , Including Other Makefiles}. -@node MAKEFILE_LIST Variable, Remaking Makefiles, MAKEFILES Variable, Makefiles +@node MAKEFILE_LIST Variable, Special Variables, MAKEFILES Variable, Makefiles @comment node-name, next, previous, up @section The Variable @code{MAKEFILE_LIST} @cindex makefiles, and @code{MAKEFILE_LIST} variable @@ -1232,7 +1234,37 @@ name2 = inc.mk Variables}, for more information on simply-expanded (@code{:=}) variable definitions. -@node Remaking Makefiles, Overriding Makefiles, MAKEFILE_LIST Variable, Makefiles +@node Special Variables, Remaking Makefiles, MAKEFILE_LIST Variable, Makefiles +@comment node-name, next, previous, up +@section Other Special Variables +@cindex makefiles, and special variables +@cindex special variables + +GNU @code{make} also supports two other special variables. Note that +any value you assign to these variables will be ignored; they will +always return their special value. + +@vindex $(.VARIABLES) +@vindex .VARIABLES @r{(list of variables)} +The first special variable is @code{.VARIABLES}. When expanded, the +value consists of a list of the @emph{names} of all global variables +defined in all makefiles read up until that point. This includes +variables which have empty values, as well as built-in variables +(@pxref{Implicit Variables, , Variables Used by Implicit Rules}), but +does not include any variables which are only defined in a +target-specific context. + +@vindex $(.TARGETS) +@vindex .TARGETS @r{(list of targets)} +The second special variable is @code{.TARGETS}. When expanded, the +value consists of a list of all targets defined in all makefiles read +up until that point. Note it's not enough for a file to be simply +mentioned in the makefile to be listed in this variable, even if it +would match an implicit rule and become an ``implicit target''. The +file must appear as a target, on the left-hand side of a ``:'', to be +considered a target for the purposes of this variable. + +@node Remaking Makefiles, Overriding Makefiles, Special Variables, Makefiles @section How Makefiles Are Remade @cindex updating makefiles diff --git a/file.c b/file.c index cfb11bb..ecb83d8 100644 --- a/file.c +++ b/file.c @@ -765,6 +765,55 @@ print_file_data_base () hash_print_stats (&files, stdout); } +#define EXPANSION_INCREMENT(_l) ((((_l) / 500) + 1) * 500) + +char * +build_target_list (value) + char *value; +{ + static unsigned long last_targ_count = 0; + + if (files.ht_fill != last_targ_count) + { + unsigned long max = EXPANSION_INCREMENT (strlen (value)); + unsigned long len; + char *p; + struct file **fp = (struct file **) files.ht_vec; + struct file **end = &fp[files.ht_size]; + + /* Make sure we have at least MAX bytes in the allocated buffer. */ + value = xrealloc (value, max); + + p = value; + len = 0; + for (; fp < end; ++fp) + if (!HASH_VACANT (*fp) && (*fp)->is_target) + { + struct file *f = *fp; + int l = strlen (f->name); + + len += l + 1; + if (len > max) + { + unsigned long off = p - value; + + max += EXPANSION_INCREMENT (l + 1); + value = xrealloc (value, max); + p = &value[off]; + } + + bcopy (f->name, p, l); + p += l; + *(p++) = ' '; + } + *(p-1) = '\0'; + + last_targ_count = files.ht_fill; + } + + return value; +} + void init_hash_files () { diff --git a/filedef.h b/filedef.h index 7ee2902..b7d6e67 100644 --- a/filedef.h +++ b/filedef.h @@ -111,6 +111,7 @@ extern void rehash_file PARAMS ((struct file *file, char *name)); extern void set_command_state PARAMS ((struct file *file, int state)); extern void notice_finished_file PARAMS ((struct file *file)); extern void init_hash_files PARAMS ((void)); +extern char *build_target_list PARAMS ((char *old_list)); #if FILE_TIMESTAMP_HI_RES # define FILE_TIMESTAMP_STAT_MODTIME(fname, st) \ diff --git a/main.c b/main.c index 7079e01..10d6124 100644 --- a/main.c +++ b/main.c @@ -988,6 +988,10 @@ int main (int argc, char ** argv) atexit (msdos_return_to_initial_directory); #endif + /* Initialize the special variables. */ + define_variable (".VARIABLES", 10, "", o_default, 0); + define_variable (".TARGETS", 8, "", o_default, 0); + /* Read in variables from the environment. It is important that this be done before $(MAKE) is figured out so its definitions will not be from the environment. */ diff --git a/make.h b/make.h index c69bd96..44d7b39 100644 --- a/make.h +++ b/make.h @@ -297,10 +297,10 @@ extern char *strsignal PARAMS ((int signum)); - It's guaranteed to evaluate its argument exactly once. NOTE! Make relies on this behavior, don't change it! - It's typically faster. - Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that + POSIX 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that only '0' through '9' are digits. Prefer ISDIGIT to isdigit() unless it's important to use the locale's definition of `digit' even when the - host does not conform to Posix. */ + host does not conform to POSIX. */ #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) #ifndef iAPX286 diff --git a/variable.c b/variable.c index 5495fbc..7f55f8f 100644 --- a/variable.c +++ b/variable.c @@ -157,8 +157,86 @@ define_variable_in_set (name, length, value, origin, recursive, set, flocp) v->append = 0; v->export = v_default; + v->exportable = 1; + if (*name != '_' && (*name < 'A' || *name > 'Z') + && (*name < 'a' || *name > 'z')) + v->exportable = 0; + else + { + for (++name; *name != '\0'; ++name) + if (*name != '_' && (*name < 'a' || *name > 'z') + && (*name < 'A' || *name > 'Z') && !ISDIGIT(*name)) + break; + + if (*name != '\0') + v->exportable = 0; + } + return v; } + +/* If the variable passed in is "special", handle its special nature. + Currently there are two such variables, both used for introspection: + .MAKE_VARS expands to a list of all the variables defined in this instance + of make. + .MAKE_TARGETS expands to a list of all the targets defined in this + instance of make. + Returns the variable reference passed in. */ + +#define EXPANSION_INCREMENT(_l) ((((_l) / 500) + 1) * 500) + +static struct variable * +handle_special_var (var) + struct variable *var; +{ + static unsigned long last_var_count = 0; + + + if (streq (var->name, ".MAKE_TARGETS")) + var->value = build_target_list (var->value); + + else if (streq (var->name, ".MAKE_VARS") + && global_variable_set.table.ht_fill != last_var_count) + { + unsigned long max = EXPANSION_INCREMENT (strlen (var->value)); + unsigned long len; + char *p; + struct variable **vp = (struct variable **) global_variable_set.table.ht_vec; + struct variable **end = &vp[global_variable_set.table.ht_size]; + + /* Make sure we have at least MAX bytes in the allocated buffer. */ + var->value = xrealloc (var->value, max); + + p = var->value; + len = 0; + for (; vp < end; ++vp) + if (!HASH_VACANT (*vp)) + { + struct variable *v = *vp; + int l = v->length; + + len += l + 1; + if (len > max) + { + unsigned long off = p - var->value; + + max += EXPANSION_INCREMENT (l + 1); + var->value = xrealloc (var->value, max); + p = &var->value[off]; + } + + bcopy (v->name, p, l); + p += l; + *(p++) = ' '; + } + *(p-1) = '\0'; + + last_var_count = global_variable_set.table.ht_fill; + } + + return var; +} + /* Lookup a variable whose name is a string starting at NAME and with LENGTH chars. NAME need not be null-terminated. @@ -184,7 +262,7 @@ lookup_variable (name, length) v = (struct variable *) hash_find_item ((struct hash_table *) &set->table, &var_key); if (v) - return v; + return handle_special_var (v); } #ifdef VMS @@ -570,7 +648,6 @@ target_environment (file) { struct variable **new_slot; struct variable *v = *v_slot; - char *p = v->name; /* If this is a per-target variable and it hasn't been touched already then look up the global version and take its export @@ -592,20 +669,14 @@ target_environment (file) /* Only export default variables by explicit request. */ continue; + /* The variable doesn't have a name that can be exported. */ + if (! v->exportable) + continue; + if (! export_all_variables && v->origin != o_command && v->origin != o_env && v->origin != o_env_override) continue; - - if (*p != '_' && (*p < 'A' || *p > 'Z') - && (*p < 'a' || *p > 'z')) - continue; - for (++p; *p != '\0'; ++p) - if (*p != '_' && (*p < 'a' || *p > 'z') - && (*p < 'A' || *p > 'Z') && (*p < '0' || *p > '9')) - continue; - if (*p != '\0') - continue; break; case v_export: diff --git a/variable.h b/variable.h index 806f9e1..15661b5 100644 --- a/variable.h +++ b/variable.h @@ -57,17 +57,19 @@ struct variable char *value; /* Variable value. */ struct floc fileinfo; /* Where the variable was defined. */ unsigned int recursive:1; /* Gets recursively re-evaluated. */ - unsigned int expanding:1; /* Nonzero if currently being expanded. */ - unsigned int exp_count:EXP_COUNT_BITS; - /* If >1, allow this many self-referential - expansions */ unsigned int per_target:1; /* Nonzero if a target-specific variable. */ unsigned int append:1; /* Nonzero if an appending target-specific variable. */ + unsigned int expanding:1; /* Nonzero if currently being expanded. */ + unsigned int exp_count:EXP_COUNT_BITS; + /* If >1, allow this many self-referential + expansions. */ enum variable_origin origin ENUM_BITFIELD (3); /* Variable origin. */ + unsigned int exportable:1; /* Nonzero if the variable _could_ be + exported. */ enum variable_export { v_export, /* Export this variable. */ -- cgit v1.2.3