diff -u -r -N -X ../excluded_list ../make.cvs/AUTHORS ./AUTHORS --- ../make.cvs/AUTHORS 2007-07-04 21:35:15.000000000 +0200 +++ ./AUTHORS 2007-08-04 23:50:21.000000000 +0200 @@ -60,6 +60,7 @@ Andreas Schwab Carl Staelin (Princeton University) Ian Stewartson (Data Logic Limited) + Ramon Garcia Fernandez With suggestions/comments/bug reports from a cast of ... well ... hundreds, anyway :) diff -u -r -N -X ../excluded_list ../make.cvs/ChangeLog ./ChangeLog --- ../make.cvs/ChangeLog 2007-08-15 15:53:53.000000000 +0200 +++ ./ChangeLog 2007-08-19 21:45:11.000000000 +0200 @@ -11,6 +11,53 @@ then (order-only for example) but they will be this time. Fixes Savannah bug #'s 3330 and 15919. +2007-08-04 Ramon Garcia + + * Makefile.am : add new source files + custom_outofdate.c, persistent_state.c + + * command.c (set_file_variables) + read.c (uniquize_deps) dep.h (uniquize_deps) : Allow the function + set_file_variables to be called several times, without + overwritting file->deps. This is necessary for evaluating + variables in the scope of another target. The function + uniquize_deps was modified to avoid overwritting the argument list. + + * custom_outofdate.c, .h: Implementation of + user defined out of date handling + + * remake.c: Hooks to call user defined out of date handling + + * tests/scripts/variables/OUTOFDATE*: Test cases of custom of of + date. + + * expand.c (reference_variable): New feature allowing the + variables in a scope of another target with target::name + + * test/scripts/features/changescope: new test case for + this feature + + * function.c (func_changed_value): New builtin function + changed_value handles persistent variables. Returns true + if the value is different from the one in the latest + successful compilation. + + * persistent_state.c: New file, handles the storage + of persistent variables, used by changed_value. + + * test/scripts/functions/changed_value, changed_value_env: Test cases + of persistent variables. + + * read.c: Hools for cached variables. + + * target_cache_var.c, target_cache_var.h: implementation of cached variables + + * test/scripts/functions/cached: test case for cached variables. + + * string_hash.c, string_hash.h: new files for hashes string->string + + * doc/make.texi: documentation for features added + 2007-07-13 Paul Smith * file.c (expand_deps): Use variable_buffer as the start of the diff -u -r -N -X ../excluded_list ../make.cvs/Makefile.am ./Makefile.am --- ../make.cvs/Makefile.am 2007-07-04 21:35:16.000000000 +0200 +++ ./Makefile.am 2007-08-04 20:08:53.000000000 +0200 @@ -42,7 +42,9 @@ 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 \ + misc.c read.c remake.c custom_outofdate.c persistent_state.c \ + target_cache_var.c string_hash.c \ + $(remote) rule.c signame.c \ strcache.c variable.c version.c vpath.c hash.c EXTRA_make_SOURCES = vmsjobs.c remote-stub.c remote-cstms.c diff -u -r -N -X ../excluded_list ../make.cvs/commands.c ./commands.c --- ../make.cvs/commands.c 2007-07-04 21:35:17.000000000 +0200 +++ ./commands.c 2007-08-04 19:03:32.000000000 +0200 @@ -147,6 +147,7 @@ char *qp; char *bp; unsigned int len; + struct dep_ref *uniq_dep_ref, *dr; /* Compute first the value for $+, which is supposed to contain duplicate dependencies as they were listed in the makefile. */ @@ -194,12 +195,15 @@ really matter for the purpose of updating targets, but it might make some names be listed twice for $^ and $?. */ - uniquize_deps (file->deps); + uniquize_deps (file->deps, &uniq_dep_ref); bar_len = 0; - for (d = file->deps; d != 0; d = d->next) - if (d->ignore_mtime) - bar_len += strlen (dep_name (d)) + 1; + for (dr = uniq_dep_ref; dr != 0; dr = dr->next) + { + d = dr->ref; + if (d->ignore_mtime) + bar_len += strlen (dep_name (d)) + 1; + } if (bar_len == 0) bar_len++; @@ -215,9 +219,10 @@ bar_value = xrealloc (bar_value, bar_max = bar_len); bp = bar_value; - for (d = file->deps; d != 0; d = d->next) + for (dr = uniq_dep_ref; dr != 0; dr = dr->next) { - const char *c = dep_name (d); + d = dr->ref; + char *c = dep_name (d); #ifndef NO_ARCHIVES if (ar_name (c)) @@ -259,7 +264,9 @@ bp[bp > bar_value ? -1 : 0] = '\0'; DEFINE_VARIABLE ("|", 1, bar_value); + + free_dep_ref(uniq_dep_ref); } #undef DEFINE_VARIABLE diff -u -r -N -X ../excluded_list ../make.cvs/custom_outofdate.c ./custom_outofdate.c --- ../make.cvs/custom_outofdate.c 1970-01-01 01:00:00.000000000 +0100 +++ ./custom_outofdate.c 2007-08-02 03:08:03.000000000 +0200 @@ -0,0 +1,80 @@ +/* Custom out of date handling for GNU Make. +Copyright (C) 2007 Free Software Foundation, Inc. +Contributed by Ramón García Fernández +with the support a grant from the Google Summer of Code program 2007 +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 3, 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 "filedef.h" +#include "custom_outofdate.h" +#include "variable.h" +#include "commands.h" + + +/* Determinate if there are user defined dependencies for target target */ + +int +has_custom_outofdate (struct file *target) +{ + int ret; + struct variable_set_list *save; + save = current_variable_set_list; + initialize_file_variables (target, 0); + set_file_variables (target); + current_variable_set_list = target->variables; + ret = lookup_variable (".OUT_OF_DATE", sizeof (".OUT_OF_DATE") - 1) != NULL; + current_variable_set_list = save; + return ret; +} + +/* Determinate if target is out of date compared to src, using user defined dependencies */ +int +check_custom_outofdate (struct file *target, struct file *src) +{ + int ret = 0; + struct variable_set_list *save; + char *cmd_eval = NULL; + int cmd_eval_len; + struct variable *ood = NULL; + struct variable_set_list *context = NULL; + save = current_variable_set_list; + current_variable_set_list = target->variables; + ood = lookup_variable (".OUT_OF_DATE", sizeof (".OUT_OF_DATE") - 1); + if (ood == NULL) + { + fprintf (stderr, + "Bug: if .OUT_OF_DATE not defined, this function should not have been invoked"); + goto out; + } + set_file_variables (target); + context = create_new_variable_set (); + define_variable_in_set ("<", 1, src->name, + o_automatic, 0, context->set, NILF); + current_variable_set_list = context; + cmd_eval = allocated_variable_expand (ood->value); + cmd_eval_len = strlen (cmd_eval); + ret = (cmd_eval_len > 0); +out: + if (context != NULL) + { + free_variable_set (context); + } + if (cmd_eval != NULL) + { + free (cmd_eval); + } + current_variable_set_list = save; + return ret; +} diff -u -r -N -X ../excluded_list ../make.cvs/custom_outofdate.h ./custom_outofdate.h --- ../make.cvs/custom_outofdate.h 1970-01-01 01:00:00.000000000 +0100 +++ ./custom_outofdate.h 2007-08-02 03:08:03.000000000 +0200 @@ -0,0 +1,24 @@ +/* Custom out of date for GNU Make. +Copyright (C) 2007 Free Software Foundation, Inc. +Contributed by Ramón García Fernández +with the support a grant from the Google Summer of Code program 2007 +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 3, 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. */ + +#ifndef CUSTOM_OUTOFDATE_H +#define CUSTOM_OUTOFDATE_H 1 +int check_custom_outofdate (struct file *target, struct file *src); + +int has_custom_outofdate (struct file *target); +#endif diff -u -r -N -X ../excluded_list ../make.cvs/dep.h ./dep.h --- ../make.cvs/dep.h 2007-07-04 21:35:17.000000000 +0200 +++ ./dep.h 2007-08-03 01:16:31.000000000 +0200 @@ -77,4 +77,13 @@ struct dep *read_all_makefiles (const char **makefiles); int eval_buffer (char *buffer); int update_goal_chain (struct dep *goals); -void uniquize_deps (struct dep *); + +struct dep_ref +{ + struct dep* ref; + struct dep_ref* next; +}; + +extern void uniquize_deps (struct dep *src, struct dep_ref **uniq_dep_ref); + +extern void free_dep_ref (struct dep_ref *dep_ref); diff -u -r -N -X ../excluded_list ../make.cvs/expand.c ./expand.c --- ../make.cvs/expand.c 2007-07-04 21:35:18.000000000 +0200 +++ ./expand.c 2007-08-04 20:17:48.000000000 +0200 @@ -25,6 +25,7 @@ #include "commands.h" #include "variable.h" #include "rule.h" +#include "target_cache_var.h" /* Initially, any errors reported when expanding strings will be reported against the file where the error appears. */ @@ -164,7 +165,51 @@ struct variable *v; char *value; - v = lookup_variable (name, length); + char *dcolon; + int free_value = 0; + struct variable_set_list *context = current_variable_set_list; + struct variable_set_list *save = current_variable_set_list; + + /* if the name is of the form xxx::var, the expand var in the context of target xxx */ + dcolon = lindex(name, name + length, ':'); + if (!(dcolon != NULL && dcolon + 1 < name + length && *(dcolon + 1) == ':')) { + dcolon = NULL; + } + if (dcolon != NULL) { + /* detected :: */ + const char *var_name; + unsigned int var_length; + char *context_name; + const char *context_name_s; + unsigned int context_length; + struct file *context_f; + struct variable_set_list *save; + var_name = dcolon + 2; + var_length = length - (var_name - name); + context_name_s = name; + context_length = dcolon - name; + context_name = xmalloc(context_length + 1); + memcpy(context_name, context_name_s, context_length); + context_name[context_length] = '\0'; + context_f = lookup_file(context_name); + free(context_name); + if (!context_f) { + warn_undefined (context_name_s, context_length); + return o; + } + save = current_variable_set_list; + /* FIXME: This should not be done here, but it appears that the target variables are not + initialized at this stage. A clearner solution should be found */ + initialize_file_variables(context_f, 0); + set_file_variables(context_f); + context = context_f->variables; + current_variable_set_list = context; + v = lookup_variable (var_name, var_length); + current_variable_set_list = save; + } else { + v = lookup_variable (name, length); + } + if (v == 0) warn_undefined (name, length); @@ -173,11 +218,38 @@ if (v == 0 || (*v->value == '\0' && !v->append)) return o; - value = (v->recursive ? recursively_expand (v) : v->value); + if (v->recursive) + { + int found_cached = 0; + struct file *current_target = NULL; + current_variable_set_list = context; + if (v->target_cached) + { + current_target = find_current_target(); + if (current_target != NULL) + { + value = lookup_target_cached(current_target, v); + free_value = 0; + } + else + value = NULL; + found_cached = value != NULL; + } + if (!found_cached) + { + value = recursively_expand(v); + free_value = 1; + } + if (!found_cached && v->target_cached && current_target != NULL) + store_target_cached(current_target, v, value); + current_variable_set_list = save; + } + else + value = v->value; o = variable_buffer_output (o, value, strlen (value)); - if (v->recursive) + if (free_value) free (value); return o; @@ -308,6 +380,9 @@ Is the resultant text a substitution reference? */ colon = lindex (beg, end, ':'); + if (colon != NULL && colon + 1 < end && *(colon + 1) == ':') { + colon = lindex(colon + 2, end, ':'); + } if (colon) { /* This looks like a substitution reference: $(FOO:A=B). */ @@ -379,6 +454,7 @@ free (value); } } + } if (colon == 0) diff -u -r -N -X ../excluded_list ../make.cvs/file.c ./file.c --- ../make.cvs/file.c 2007-07-14 04:57:46.000000000 +0200 +++ ./file.c 2007-08-03 01:19:18.000000000 +0200 @@ -121,7 +121,7 @@ name = "./"; #endif - file_key.hname = name; + file_key.hname = (char*) name; f = hash_find_item (&files, &file_key); #if defined(VMS) && !defined(WANT_CASE_SENSITIVE_TARGETS) if (*name != '.') diff -u -r -N -X ../excluded_list ../make.cvs/filedef.h ./filedef.h --- ../make.cvs/filedef.h 2007-07-04 21:35:18.000000000 +0200 +++ ./filedef.h 2007-08-20 13:39:51.000000000 +0200 @@ -46,13 +46,18 @@ file could be renamed, call `check_renamed' (below). */ struct file *renamed; - /* List of variable sets used for this file. */ + /* List of variable sets used for this file. It points to inheritable_variables */ struct variable_set_list *variables; + /* List of variable sets that are transmitted to prerequisites */ + struct variable_set_list *inheritable_variables; + /* Pattern-specific variable reference for this target, or null if there isn't one. Also see the pat_searched flag, below. */ struct variable_set_list *pat_variables; + struct variable_set_list *inheritable_pat_variables; + /* Immediate dependent that caused this target to be remade, or nil if there isn't one. */ struct file *parent; @@ -64,6 +69,12 @@ short int update_status; /* Status of the last attempt to update, or -1 if none has been made. */ + struct hash_table persistent_state; /* Storage of persistent variables + associated with this target */ + + struct hash_table variable_cache; /* Cached variables associated with this target */ + + enum cmd_state /* State of the commands. */ { /* Note: It is important that cs_not_started be zero. */ cs_not_started, /* Not yet started. */ diff -u -r -N -X ../excluded_list ../make.cvs/function.c ./function.c --- ../make.cvs/function.c 2007-07-04 21:35:18.000000000 +0200 +++ ./function.c 2007-08-17 15:57:25.000000000 +0200 @@ -23,6 +23,7 @@ #include "job.h" #include "commands.h" #include "debug.h" +#include "persistent_state.h" #ifdef _AMIGA #include "amiga.h" @@ -1398,6 +1399,45 @@ return o; } + + + +static char * +func_changed_value (char *o, char **argv, const char *funcname UNUSED) +{ + const char *target = argv[0]; + const char *var_name = argv[1]; + const char *new_value = argv[2]; + char *old_value = NULL; + unsigned int old_value_size = 0; + char *result; + unsigned int new_value_size = strlen(new_value); + load_variable(target, var_name, &old_value, &old_value_size); + /* filename will be directory(target)/.targetname.varname */ + /* scaping special chars from varname */ + result = o; + if (old_value_size == new_value_size && + (old_value_size == 0 || memcmp(old_value, new_value, old_value_size) == 0)) + result = o; + else + { + /* Arrange target to write persistent values after its compilation */ + struct file* target_file = lookup_file(target); + if (target_file == NULL) + fprintf (stderr, "changed-value: unknown target %s\n", target); + else + { + schedule_store_variable (target_file, var_name, new_value, new_value_size); + result = variable_buffer_output (o, "true", sizeof("true")); + } + } + + if (old_value != NULL) + free(old_value); + return result; +} + + /* \r is replaced on UNIX as well. Is this desirable? */ @@ -2082,6 +2122,7 @@ { 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}, + { STRING_SIZE_TUPLE("changed-value"), 3, 3, 1, func_changed_value}, #ifdef EXPERIMENTAL { STRING_SIZE_TUPLE("eq"), 2, 2, 1, func_eq}, { STRING_SIZE_TUPLE("not"), 0, 1, 1, func_not}, diff -u -r -N -X ../excluded_list ../make.cvs/persistent_state.c ./persistent_state.c --- ../make.cvs/persistent_state.c 1970-01-01 01:00:00.000000000 +0100 +++ ./persistent_state.c 2007-08-20 13:39:03.000000000 +0200 @@ -0,0 +1,242 @@ +/* Saving and loading persistent state - implementation for GNU Make. +Copyright (C) 2007 Free Software Foundation, Inc. +Contributed by Ramón García Fernández +with the support a grant from the Google Summer of Code program 2007 +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 3, 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 "filedef.h" +#include "persistent_state.h" +#include "string_hash.h" + +/* FIXME: duplicated code from function.c. Move this to a header file */ + +#ifdef VMS +# define IS_PATHSEP(c) ((c) == ']') +#else +# ifdef HAVE_DOS_PATHS +# define IS_PATHSEP(c) ((c) == '/' || (c) == '\\') +# else +# define IS_PATHSEP(c) ((c) == '/') +# endif +#endif + + + + +/* Grow a buffer 'added_size' bytes */ +static void +enlarge_buffer (char **buffer, + unsigned int *alloc_len, unsigned int *len, + unsigned int added_size) +{ + *len += added_size; + if (*len > *alloc_len) { + *alloc_len = 2*(*alloc_len) > *len ? 2*(*alloc_len) : *len; + *buffer = xrealloc(*buffer, *alloc_len); + } +} + +/* + Write a char in radix 16 to a buffer, that is, if char is 0xab, + then buffer[0] = a, buffer[1] = b + */ +static void +write_hex (char c, char *buffer) +{ + static const char hexchars[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F'}; + buffer[0] = hexchars[(c & 0xF0) >> 4]; + buffer[1] = hexchars[c & 0xF]; +} + +/* Determinate the filename for a state file + This uses the format [target directory]/.[target name].[var name] + var name is encoded in case it contains path characters + + The caller must free filename +*/ + +static char* +filename_state (const char *target, const char *var_name) +{ + unsigned int target_len = strlen(target); + unsigned int var_len = strlen(var_name); + const char* target_dir = target; + char* result = NULL; + unsigned int result_len, result_alloc_len; + unsigned int target_dir_len; + unsigned int i; + int last_written; + target_dir_len = target_len; + while (target_dir_len > 0) + { + if (IS_PATHSEP(target_dir[target_dir_len - 1])) + break; + target_dir_len--; + } + result = xmalloc(target_len + 2); + result_len = target_dir_len; + result_alloc_len = target_len + 2; + memcpy(result, target, target_dir_len); + last_written = target_dir_len; + enlarge_buffer(&result, &result_alloc_len, &result_len, 1); + result[last_written] = '.'; + last_written = result_len; + enlarge_buffer(&result, &result_alloc_len, &result_len, target_len - target_dir_len); + memcpy(&result[last_written], &target[target_dir_len], target_len - target_dir_len); + last_written = result_len; + enlarge_buffer(&result, &result_alloc_len, &result_len, 1); + result[last_written] = '.'; + last_written = result_len; + for (i = 0; i < var_len; i++) + { + if (IS_PATHSEP(var_name[i]) || var_name[i] == '%') + { + last_written = result_len; + enlarge_buffer(&result, &result_alloc_len, &result_len, 3); + result[last_written] = '%'; + write_hex(var_name[i], &result[last_written + 1]); + } + else + { + last_written = result_len; + enlarge_buffer(&result, &result_alloc_len, &result_len, 1); + result[last_written] = var_name[i]; + } + } + last_written = result_len; + enlarge_buffer(&result, &result_alloc_len, &result_len, 1); + result[last_written] = '\0'; + return result; +} + +static const unsigned int PERSISTENT_VAR_BUCKETS = 16; + +/* Arrange that a persistent value will be stored after the successful + compilation of a target */ +void +schedule_store_variable (struct file *target, + const char *var_name, const char *value, unsigned int value_len) +{ + struct hash_table *persistent_state = &target->persistent_state; + struct string_hash_entry key; + struct string_hash_entry *var; + struct string_hash_entry **slot; + unsigned int name_len; + if (persistent_state->ht_vec == NULL) + string_hash_init(persistent_state, PERSISTENT_VAR_BUCKETS); + name_len = strlen(var_name); + key.name = (char*) var_name; + key.name_len = name_len; + slot = (struct string_hash_entry**) hash_find_slot(persistent_state, &key); + if (!HASH_VACANT(*slot)) + { + var = *slot; + if (var->value_len == value_len && + memcmp(var->value, value, value_len) == 0) { + return; + } + fprintf(stderr, "Warning: persistent variable name %s of target %s overwritten\n", + var_name, target->name); + free((*slot)->value); + } + else + { + var = (struct string_hash_entry*) xmalloc(sizeof(struct string_hash_entry)); + hash_insert_at(persistent_state, var, slot); + var->name_len = name_len; + var->name = xmalloc(var->name_len + 1); + memcpy(var->name, var_name, var->name_len + 1); + persistent_state->ht_fill++; + } + var->value = xmalloc(value_len); + memcpy(var->value, value, value_len); + var->value_len = value_len; + + +} + + +/* Load a persistent variable. Returns a malloced string variable, that should be freed by caller */ +void +load_variable (const char *target, + const char *var_name, char **value, unsigned int *value_len) +{ + char *filename; + FILE *file; + unsigned int value_alloc_size = 0; + + filename = filename_state (target, var_name); + file = fopen (filename, "r"); + if (file == NULL && errno != ENOENT) + fprintf (stderr, "changed_value: could not open state file %s: %s\n", + filename, strerror(errno)); + *value = NULL; + if (file != NULL) + { + while (1) + { + int value_alloc_size_2 = BUFSIZ + value_alloc_size; + int nread; + if (*value != NULL) + *value = xrealloc(*value, value_alloc_size_2); + else + *value = xmalloc(value_alloc_size_2); + nread = + fread(&(*value)[*value_len], 1, value_alloc_size_2 - value_alloc_size, file); + if (nread < 0) + { + fprintf (stderr, "changed_value: error reading state file %s: %s\n", + filename, strerror(errno)); + break; + } + *value_len += nread; + value_alloc_size = value_alloc_size_2; + if (*value_len < value_alloc_size) + break; + } + fclose(file); + } + +} + + +static void save_persistent_var_map_fn (void const *item, void *arg) +{ + const struct string_hash_entry *v = (const struct string_hash_entry*) item; + const char *target_name = (const char *) arg; + char *var_filename = filename_state (target_name, v->name); + FILE *var_file = fopen(var_filename, "w"); + if (var_file == NULL) { + fprintf(stderr, "Cannot write to state file %s\n", var_filename); + goto out; + } + fwrite(v->value, 1, v->value_len, var_file); +out: + if (var_file != NULL) + fclose(var_file); + free(var_filename); + +} + +/* Save to disk the variables previously recorded with schedule_store_variable. + This rouble should be run after a successful compilation of a target */ +void +save_persistent_variables (struct file *target) +{ + hash_map_arg(&target->persistent_state, save_persistent_var_map_fn, (void*) target->name); +} diff -u -r -N -X ../excluded_list ../make.cvs/persistent_state.h ./persistent_state.h --- ../make.cvs/persistent_state.h 1970-01-01 01:00:00.000000000 +0100 +++ ./persistent_state.h 2007-08-04 20:44:16.000000000 +0200 @@ -0,0 +1,37 @@ +/* Saving and loading persistent stat for GNU Make. +Copyright (C) 2007 Free Software Foundation, Inc. +Contributed by Ramón García Fernández +with the support a grant from the Google Summer of Code program 2007 +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 3, 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. */ + +#ifndef PERSISTENT_STATE_H +#define PERSISTENT_STATE_H + + + +void +schedule_store_variable(struct file *target, + const char *var_name, const char *new_value, unsigned int new_value_size); + + +void +load_variable(const char *target, + const char *var_name, char **variable, unsigned int *variable_length); + + +void +save_persistent_variables(struct file *target); + +#endif diff -u -r -N -X ../excluded_list ../make.cvs/read.c ./read.c --- ../make.cvs/read.c 2007-07-04 21:35:19.000000000 +0200 +++ ./read.c 2007-08-20 15:40:44.000000000 +0200 @@ -134,7 +134,8 @@ unsigned int commands_idx, int two_colon, const struct floc *flocp); static void record_target_var (struct nameseq *filenames, char *defn, - enum variable_origin origin, int enabled, + enum variable_origin origin, int exported, + int target_cached, int not_inherit, const struct floc *flocp); static enum make_word_type get_next_mword (char *buffer, char *delim, char **startp, unsigned int *length); @@ -648,6 +649,19 @@ continue; } + if (word1eq ("target-cached")) + { + if (!ignoring) + { + struct variable *v = try_variable_definition (fstart, p2, o_file, 0); + if (!v) + error (fstart, _("invalid `cached' directive")); + else + v->target_cached = 1; + } + + + } if (word1eq ("override")) { if (*p2 == '\0') @@ -860,6 +874,8 @@ enum make_word_type wtype; enum variable_origin v_origin; int exported; + int target_cached; + int not_inherit; char *cmdleft, *semip, *lb_next; unsigned int plen = 0; char *colonp; @@ -1032,7 +1048,9 @@ v_origin = o_file; exported = 0; - if (wtype == w_static) + target_cached = 0; + not_inherit = 0; + while (wtype == w_static) { if (word1eq ("override")) { @@ -1044,6 +1062,20 @@ exported = 1; wtype = get_next_mword (p+wlen, NULL, &p, &wlen); } + else if (word1eq ("target-cached")) + { + target_cached = 1; + wtype = get_next_mword (p+wlen, NULL, &p, &wlen); + } + else if (word1eq ("not-inherit")) + { + not_inherit = 1; + wtype = get_next_mword (p+wlen, NULL, &p, &wlen); + } + else + { + break; + } } if (wtype != w_eol) @@ -1061,7 +1093,7 @@ semip, strlen (semip)+1); p = variable_buffer + l; } - record_target_var (filenames, p, v_origin, exported, fstart); + record_target_var (filenames, p, v_origin, exported, target_cached, not_inherit, fstart); filenames = 0; continue; } @@ -1707,10 +1739,11 @@ void -uniquize_deps (struct dep *chain) +uniquize_deps (struct dep *chain, struct dep_ref **uniq_dep_ref) { struct hash_table deps; - register struct dep **depp; + struct dep *dep; + struct dep_ref **dep_ref; hash_init (&deps, 500, dep_hash_1, dep_hash_2, dep_hash_cmp); @@ -1718,27 +1751,41 @@ really matter for the purpose of updating targets, but it might make some names be listed twice for $^ and $?. */ - depp = &chain; - while (*depp) + dep = chain; + dep_ref = uniq_dep_ref; + while (dep) { - struct dep *dep = *depp; struct dep **dep_slot = (struct dep **) hash_find_slot (&deps, dep); if (HASH_VACANT (*dep_slot)) { + struct dep_ref *dep_link; hash_insert_at (&deps, dep, dep_slot); - depp = &dep->next; - } - else - { - /* Don't bother freeing duplicates. - It's dangerous and little benefit accrues. */ - *depp = dep->next; + dep_link = (struct dep_ref*) xmalloc(sizeof(struct dep_ref)); + dep_link->ref = dep; + *dep_ref = dep_link; + dep_ref = &dep_link->next; } + dep = dep->next; } + *dep_ref = NULL; hash_free (&deps, 0); } +void +free_dep_ref (struct dep_ref *dep_ref) +{ + struct dep_ref *dep_link = dep_ref, *next; + + while (dep_link != NULL) + { + next = dep_link->next; + free(dep_link); + dep_link = next; + } +} + + /* Record target-specific variable values for files FILENAMES. TWO_COLON is nonzero if a double colon was used. @@ -1750,7 +1797,8 @@ static void record_target_var (struct nameseq *filenames, char *defn, - enum variable_origin origin, int exported, + enum variable_origin origin, + int exported, int target_cached, int not_inherit, const struct floc *flocp) { struct nameseq *nextf; @@ -1809,7 +1857,10 @@ initialize_file_variables (f, 1); fname = f->name; - current_variable_set_list = f->variables; + if (not_inherit == 0) + current_variable_set_list = f->inheritable_variables; + else + current_variable_set_list = f->variables; v = try_variable_definition (flocp, defn, origin, 1); if (!v) error (flocp, _("Malformed target-specific variable definition")); @@ -1820,6 +1871,8 @@ v->origin = origin; v->per_target = 1; v->export = exported ? v_export : v_default; + v->target_cached = target_cached; + v->not_inherit = not_inherit; /* If it's not an override, check to see if there was a command-line setting. If so, reset the value. */ diff -u -r -N -X ../excluded_list ../make.cvs/remake.c ./remake.c --- ../make.cvs/remake.c 2007-08-15 15:53:53.000000000 +0200 +++ ./remake.c 2007-08-17 10:25:11.000000000 +0200 @@ -23,6 +23,8 @@ #include "dep.h" #include "variable.h" #include "debug.h" +#include "custom_outofdate.h" +#include "persistent_state.h" #include @@ -62,8 +64,9 @@ static int update_file (struct file *file, unsigned int depth); static int update_file_1 (struct file *file, unsigned int depth); -static int check_dep (struct file *file, unsigned int depth, - FILE_TIMESTAMP this_mtime, int *must_make_ptr); +static int check_dep (struct file *file, unsigned int depth, + struct file *this_file, FILE_TIMESTAMP this_mtime, + int *must_make_ptr); static int touch_file (struct file *file); static void remake_file (struct file *file); static FILE_TIMESTAMP name_mtime (const char *name); @@ -372,6 +375,7 @@ { register FILE_TIMESTAMP this_mtime; int noexist, must_make, deps_changed; + int has_custom_ood; int dep_status = 0; register struct dep *d, *lastd; int running = 0; @@ -428,24 +432,37 @@ might get implicit commands that apply to its initial name, only to have that name replaced with another found by VPATH search. */ - this_mtime = file_mtime (file); - check_renamed (file); - noexist = this_mtime == NONEXISTENT_MTIME; - if (noexist) - DBF (DB_BASIC, _("File `%s' does not exist.\n")); - else if (ORDINARY_MTIME_MIN <= this_mtime && this_mtime <= ORDINARY_MTIME_MAX - && file->low_resolution_time) - { - /* Avoid spurious rebuilds due to low resolution time stamps. */ - int ns = FILE_TIMESTAMP_NS (this_mtime); - if (ns != 0) - error (NILF, _("*** Warning: .LOW_RESOLUTION_TIME file `%s' has a high resolution time stamp"), - file->name); - this_mtime += FILE_TIMESTAMPS_PER_S - 1 - ns; - } - must_make = noexist; + /* There is lot of code here that deals with mtime. What to do when + using out of date? I considered this code to be either optimization + specific to out of date by mtime, or workarounds to issues of clocks + not synchronized with NFS servers. Thus, that code is disabled if the + user requested custom out of date handling */ + + has_custom_ood = has_custom_outofdate (file); + if (!has_custom_ood) + { + this_mtime = file_mtime (file); + check_renamed (file); + noexist = this_mtime == NONEXISTENT_MTIME; + if (noexist) + DBF (DB_BASIC, _("File `%s' does not exist.\n")); + else if (ORDINARY_MTIME_MIN <= this_mtime && this_mtime <= ORDINARY_MTIME_MAX + && file->low_resolution_time) + { + /* Avoid spurious rebuilds due to low resolution time stamps. */ + int ns = FILE_TIMESTAMP_NS (this_mtime); + if (ns != 0) + error (NILF, _("*** Warning: .LOW_RESOLUTION_TIME file `%s' has a high resolution time stamp"), + file->name); + this_mtime += FILE_TIMESTAMPS_PER_S - 1 - ns; + } + + } + else + noexist = 0; + must_make = noexist; /* If file was specified as a target with no commands, come up with some default commands. */ @@ -477,7 +494,11 @@ check_renamed (d->file); - mtime = file_mtime (d->file); + if (!has_custom_ood) + mtime = file_mtime (d->file); + else + mtime = UNKNOWN_MTIME; + check_renamed (d->file); if (is_updating (d->file)) @@ -506,7 +527,7 @@ } - dep_status |= check_dep (d->file, depth, this_mtime, &maybe_make); + dep_status |= check_dep (d->file, depth, file, this_mtime, &maybe_make); /* Restore original dontcare flag. */ if (rebuilding_makefiles) @@ -533,7 +554,7 @@ if (dep_status != 0 && !keep_going_flag) break; - if (!running) + if (!running && !has_custom_ood) /* The prereq is considered changed if the timestamp has changed while it was built, OR it doesn't exist. */ d->changed = ((file_mtime (d->file) != mtime) @@ -643,7 +664,12 @@ deps_changed = 0; for (d = file->deps; d != 0; d = d->next) { - FILE_TIMESTAMP d_mtime = file_mtime (d->file); + FILE_TIMESTAMP d_mtime; + if (!has_custom_ood) + d_mtime = file_mtime (d->file); + else + d_mtime = UNKNOWN_MTIME; + check_renamed (d->file); if (! d->ignore_mtime) @@ -652,7 +678,7 @@ /* %%% In version 4, remove this code completely to implement not remaking deps if their deps are newer than their parents. */ - if (d_mtime == NONEXISTENT_MTIME && !d->file->intermediate) + if (!has_custom_ood && d_mtime == NONEXISTENT_MTIME && !d->file->intermediate) /* We must remake if this dep does not exist and is not intermediate. */ must_make = 1; @@ -664,7 +690,10 @@ /* Set D->changed if either this dep actually changed, or its dependent, FILE, is older or does not exist. */ - d->changed |= noexist || d_mtime > this_mtime; + if (!has_custom_ood) + d->changed |= noexist || d_mtime > this_mtime; + else + d->changed = check_custom_outofdate(file, d->file); if (!noexist && ISDB (DB_BASIC|DB_VERBOSE)) { @@ -867,8 +896,12 @@ i = 1; file->last_mtime = i == 0 ? UNKNOWN_MTIME : NEW_MTIME; + } + if (file->update_status == 0) + save_persistent_variables(file); + if (file->double_colon) { /* If this is a double colon rule and it is the last one to be @@ -929,10 +962,11 @@ static int check_dep (struct file *file, unsigned int depth, - FILE_TIMESTAMP this_mtime, int *must_make_ptr) + struct file *this_file, FILE_TIMESTAMP this_mtime, int *must_make_ptr) { struct dep *d; int dep_status = 0; + int ood; ++depth; start_updating (file); @@ -942,13 +976,20 @@ /* If this is a non-intermediate file, update it and record whether it is newer than THIS_MTIME. */ FILE_TIMESTAMP mtime; + int ood; dep_status = update_file (file, depth); check_renamed (file); - mtime = file_mtime (file); - check_renamed (file); - if (mtime == NONEXISTENT_MTIME || mtime > this_mtime) + if (has_custom_outofdate (this_file)) { + ood = check_custom_outofdate (this_file, file); + } else { + mtime = file_mtime (file); + ood = mtime == NONEXISTENT_MTIME || mtime > this_mtime; + } + if (ood != 0) { *must_make_ptr = 1; } + check_renamed (file); + } else { /* FILE is an intermediate file. */ @@ -972,9 +1013,19 @@ check_renamed (file); mtime = file_mtime (file); check_renamed (file); - if (mtime != NONEXISTENT_MTIME && mtime > this_mtime) - /* If the intermediate file actually exists and is newer, then we - should remake from it. */ + + + /* NOTE user_dependencies: what should we do if there are intermediate files + * I decided to compare the parent non-intermediate target with the prerequisite + * this is the current make behaviour, but with mtime comparison + */ + if (has_custom_outofdate (this_file)) { + ood = check_custom_outofdate (this_file, file); + } else { + ood = mtime != NONEXISTENT_MTIME && mtime > this_mtime; + } + + if (ood) *must_make_ptr = 1; else { @@ -1016,7 +1067,7 @@ d->file->parent = file; maybe_make = *must_make_ptr; - dep_status |= check_dep (d->file, depth, this_mtime, + dep_status |= check_dep (d->file, depth, this_file, this_mtime, &maybe_make); if (! d->ignore_mtime) *must_make_ptr = maybe_make; @@ -1112,8 +1163,7 @@ /* This is a nonexistent target file we cannot make. Pretend it was successfully remade. */ file->update_status = 0; - else - { + else { /* This is a dependency file we cannot remake. Fail. */ if (!rebuilding_makefiles || !file->dontcare) complain (file); @@ -1128,15 +1178,16 @@ if (!touch_flag || file->cmds->any_recurse) { execute_file_commands (file); - return; + goto end; } /* This tells notice_finished_file it is ok to touch the file. */ file->update_status = 0; } - /* This does the touching under -t. */ notice_finished_file (file); +end: + delete_target_cached (file); } /* Return the mtime of a file, given a `struct file'. diff -u -r -N -X ../excluded_list ../make.cvs/string_hash.c ./string_hash.c --- ../make.cvs/string_hash.c 1970-01-01 01:00:00.000000000 +0100 +++ ./string_hash.c 2007-08-04 20:44:22.000000000 +0200 @@ -0,0 +1,39 @@ +/* Specialization of hashes for storing string->string */ + +#include "hash.h" +#include "string_hash.h" + +static unsigned long +string_hash_1 (const void *keyv) +{ + const struct string_hash_entry *v = (const struct string_hash_entry *) keyv; + return_STRING_N_HASH_1(v->name, v->name_len); +} + +static unsigned long +string_hash_2 (const void *keyv) +{ + const struct string_hash_entry *v = (const struct string_hash_entry *) keyv; + return_STRING_N_HASH_2(v->name, v->name_len); +} + +static int +string_hash_cmp (const void *xv, const void *yv) +{ + const struct string_hash_entry *v1 = (const struct string_hash_entry *) xv; + const struct string_hash_entry *v2 = (const struct string_hash_entry *) yv; + int result = v1->name_len - v2->name_len; + if (result != 0) + return result; + return_STRING_N_COMPARE(v1->name, v2->name, v1->name_len); +} + + +/* Create a hash whose keys are "struct string_hash_entry" */ +void +string_hash_init (struct hash_table *ht, unsigned long size) +{ + hash_init (ht, size, + string_hash_1, string_hash_2, string_hash_cmp); + +} diff -u -r -N -X ../excluded_list ../make.cvs/string_hash.h ./string_hash.h --- ../make.cvs/string_hash.h 1970-01-01 01:00:00.000000000 +0100 +++ ./string_hash.h 2007-08-04 20:44:22.000000000 +0200 @@ -0,0 +1,16 @@ +/* Specialization of hashes for storing string->string */ + +#ifndef STRING_HASH_H +#define STRING_HASH_H 1 + +struct string_hash_entry +{ + unsigned int name_len; + char *name; + unsigned int value_len; + char *value; +}; + +void string_hash_init (struct hash_table *ht, unsigned long size); + +#endif diff -u -r -N -X ../excluded_list ../make.cvs/target_cache_var.c ./target_cache_var.c --- ../make.cvs/target_cache_var.c 1970-01-01 01:00:00.000000000 +0100 +++ ./target_cache_var.c 2007-08-04 20:44:48.000000000 +0200 @@ -0,0 +1,103 @@ +/* Target cached variables for GNU Make. +Copyright (C) 2007 Free Software Foundation, Inc. +Contributed by Ramón García Fernández +with the support a grant from the Google Summer of Code program 2007 + +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 3, 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 "filedef.h" +#include "variable.h" +#include "target_cache_var.h" +#include "string_hash.h" + +struct file * +find_current_target (void) +{ + const char* target_name; + struct variable* at = lookup_variable ("@", 1); + if (at == NULL) + return NULL; + target_name = at->value; + return lookup_file (target_name); +} + +char * +lookup_target_cached(struct file *target, const struct variable *v) +{ + struct string_hash_entry** slot; + struct string_hash_entry key; + if (target->variable_cache.ht_vec == NULL) + return NULL; + key.name = v->name; + key.name_len = v->length; + slot = (struct string_hash_entry**) hash_find_slot((struct hash_table*) &target->variable_cache, &key); + if (HASH_VACANT(*slot)) + return NULL; + return (*slot)->value; +} + +void +store_target_cached(struct file *target, const struct variable *v, const char *expanded_value) +{ + struct string_hash_entry key, **slot; + struct hash_table *hash = &target->variable_cache; + if (hash->ht_vec == NULL) + string_hash_init(hash, 16); + key.name = v->name; + key.name_len = v->length; + slot = (struct string_hash_entry**) hash_find_slot(hash, &key); + if (HASH_VACANT(*slot)) + { + struct string_hash_entry *var = (struct string_hash_entry*) xmalloc(sizeof(struct string_hash_entry)); + unsigned int expanded_value_len = strlen(expanded_value) + 1; + hash_insert_at(hash, var, slot); + var->name_len = v->length; + var->name = xmalloc(var->name_len + 1); + memcpy(var->name, v->name, var->name_len + 1); + var->value = xmalloc(expanded_value_len); + memcpy(var->value, expanded_value, expanded_value_len); + var->value_len = expanded_value_len; + hash->ht_fill++; + } + else + { + fprintf(stderr, "Strange: cached variable overwrite attempt\n"); + } +} + + + +static void +delete_entry(const void* o) +{ + struct string_hash_entry *entry = (struct string_hash_entry *) o; + free(entry->name); + free(entry->value); +} + + +void +delete_target_cached(struct file *target) +{ + if (target->variable_cache.ht_vec == NULL) + return; + hash_map(&target->variable_cache, delete_entry); + hash_free(&target->variable_cache, 1); +} + + + diff -u -r -N -X ../excluded_list ../make.cvs/target_cache_var.h ./target_cache_var.h --- ../make.cvs/target_cache_var.h 1970-01-01 01:00:00.000000000 +0100 +++ ./target_cache_var.h 2007-08-04 20:44:48.000000000 +0200 @@ -0,0 +1,29 @@ +#ifndef TARGET_CACHE_VAR_H +#define TARGET_CACHE_VAR_H +/* Target cached variables for GNU Make. +Copyright (C) 2007 Free Software Foundation, Inc. +Contributed by Ramón García Fernández +with the support a grant from the Google Summer of Code program 2007 +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 3, 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. */ + +struct file *find_current_target(void); + +char *lookup_target_cached(struct file *target, const struct variable *v); + +void store_target_cached(struct file *target, const struct variable *v, const char *expanded_value); + +void delete_target_cached(struct file *target); + +#endif diff -u -r -N -X ../excluded_list ../make.cvs/tests/scripts/features/changescope ./tests/scripts/features/changescope --- ../make.cvs/tests/scripts/features/changescope 1970-01-01 01:00:00.000000000 +0100 +++ ./tests/scripts/features/changescope 2007-08-02 03:08:09.000000000 +0200 @@ -0,0 +1,89 @@ +# -*-perl-*- + +$description = "The following test checks the references of variables \n" . + "in a scope different from the present one."; + +# Test #1: a simple test + +open(MAKEFILE,"> $makefile"); + +# The Contents of the MAKEFILE ... + +print MAKEFILE <<'EOM'; +SHELL = /bin/sh + +VA=global va +VB=global vb + +a: VA=va at a + +a: + @echo $(VA) + @echo $(b::VA) + @echo ${b::VB} + @echo ${b::@} + +b: VA=va at b + +EOM + +# END of Contents of MAKEFILE + +close(MAKEFILE); + + +&run_make_with_options($makefile,"",&get_logfile); + + +$answer = "va at a\n" . + "va at b\n" . + "global vb\n" . + "b\n"; + +&compare_output($answer,&get_logfile(1)); + +# Test #2 : check that the parent child relationship and different flavour +# of variable does not interfere with scopes. + +open(MAKEFILE,"> $makefile"); + +# Contents of the MAKEFILE + +print MAKEFILE <<'EOM'; +SHELL = /bin/sh + +a: vx = avx +a: vi = vi at a ${vx} +a: vd := vd at a +a: b + @cat b ${b::vi} ${b::vd} > a + cat a + +b: vx = bvx +b: vi = vi at b ${vx} +b: vd := vd at b +b: + @cat ${a::vi} ${a::vd} > b + +EOM + +close(MAKEFILE); +# END of Contents of MAKEFILE + + +$answer = "vi at a avx\n" . + "vd at a\n" . + "vi at b bvx\n" . + "vd at b\n". + + +&compare_output($answer,&get_logfile(1)); + + + +close(MAKEFILE); + +unlink(qw(a b)); + +1; + diff -u -r -N -X ../excluded_list ../make.cvs/tests/scripts/functions/changed_value ./tests/scripts/functions/changed_value --- ../make.cvs/tests/scripts/functions/changed_value 1970-01-01 01:00:00.000000000 +0100 +++ ./tests/scripts/functions/changed_value 2007-08-02 03:08:09.000000000 +0200 @@ -0,0 +1,52 @@ +# -*-perl-*- + +$description = "This test checks the implementation of the function \n" . + "changed-value \n"; + +open(MAKEFILE,"> $makefile"); + +print MAKEFILE <<'EOM'; +SHELL = /bin/sh +a: + @echo ${changed-value $@,b,${shell cat b}} +EOM + +close(MAKEFILE); + +open(B, "> b"); +print B "ab"; +close(B); + +# Test #1: first run changed is true + +&run_make_with_options($makefile,"",&get_logfile); + +$answer = "true\n"; + +&compare_output($answer,&get_logfile(1)); + +# Test #2: second run changed is empty + +&run_make_with_options($makefile,"",&get_logfile); + +$answer = "\n"; + +&compare_output($answer,&get_logfile(1)); + +open(B, "> b"); +print B "abb"; +close(B); + +# Test #3: third run, we changed b, so changed is true again + +&run_make_with_options($makefile,"",&get_logfile); + +$answer = "true\n"; + +&compare_output($answer,&get_logfile(1)); + +unlink(qw(b .a.b)); + + + +1; diff -u -r -N -X ../excluded_list ../make.cvs/tests/scripts/functions/changed_value_env ./tests/scripts/functions/changed_value_env --- ../make.cvs/tests/scripts/functions/changed_value_env 1970-01-01 01:00:00.000000000 +0100 +++ ./tests/scripts/functions/changed_value_env 2007-08-02 03:08:09.000000000 +0200 @@ -0,0 +1,50 @@ +# -*-perl-*- + +$description = "This test checks the implementation of the function \n" . + "changed-value with a variable passed in the command line\n"; + +open(MAKEFILE,"> $makefile"); + +print MAKEFILE <<'EOM'; +SHELL = /bin/sh +all: + @echo ${changed-value $@,var,${FREE}} +EOM + +close(MAKEFILE); + +# Test #1: first run changed is false ("previous" value of FREE is empty) + +&run_make_with_options($makefile,"",&get_logfile); + +$answer = "\n"; + +&compare_output($answer,&get_logfile(1)); + +# Test #2: returns true, because we change FREE + +&run_make_with_options($makefile,"FREE=freedom",&get_logfile); +# +$answer = "true\n"; +# +&compare_output($answer,&get_logfile(1)); + +## Test #3: third run, we run with the same value of FREE + +&run_make_with_options($makefile,"FREE=freedom",&get_logfile); + +$answer = "\n"; + +&compare_output($answer,&get_logfile(1)); + +# Test #4: third run, we change the value of FREE + +&run_make_with_options($makefile,"FREE=beer",&get_logfile); + +$answer = "true\n"; + +&compare_output($answer,&get_logfile(1)); + +unlink(qw(.all.var)); + +1; diff -u -r -N -X ../excluded_list ../make.cvs/tests/scripts/variables/OUTOFDATE1 ./tests/scripts/variables/OUTOFDATE1 --- ../make.cvs/tests/scripts/variables/OUTOFDATE1 1970-01-01 01:00:00.000000000 +0100 +++ ./tests/scripts/variables/OUTOFDATE1 2007-08-02 03:08:09.000000000 +0200 @@ -0,0 +1,42 @@ +# -*-perl-*- + +$description = "test basic usage of user defined dependencies \n"; + +open(MAKEFILE,"> $makefile"); + +# The Contents of the MAKEFILE ... + +print MAKEFILE <<'EOM'; +SHELL = /bin/sh +.OUT_OF_DATE="true" +a: b c + @echo makinga + @cat b c > a +EOM +# end of the contents of MAKEFILE +close(MAKEFILE); + +open(B, "> b"); +print B "theb"; +close(B); +open(C, "> c"); +print C "thec"; +close(C); + + +&run_make_with_options($makefile,"",&get_logfile); + +&run_make_with_options($makefile,"",&get_logfile); + +unlink(qw(a b c)); + +$answer = "makinga\n"; + + +&compare_output($answer,&get_logfile(1)); + +1; + + + + diff -u -r -N -X ../excluded_list ../make.cvs/tests/scripts/variables/OUTOFDATE2 ./tests/scripts/variables/OUTOFDATE2 --- ../make.cvs/tests/scripts/variables/OUTOFDATE2 1970-01-01 01:00:00.000000000 +0100 +++ ./tests/scripts/variables/OUTOFDATE2 2007-08-02 03:08:09.000000000 +0200 @@ -0,0 +1,149 @@ +# -*-perl-*- + +$description = "test of user defined out of date determination \n" . + "checks specialization of rules\n"; + + +# This is identical to the touch function provided by the test framework, +# but without chaning actual content of the file f. + +sub touch_time +{ + local ($file); + + foreach $file (@_) { + (open(T, ">> $file") && close(T)) + || &error("Couldn't touch $file: $!\n", 1); + } +} + +# Identical to utouch(), but call touch_time() instead of touch(), +# so that the content of the file is not changed + +sub utouch_time +{ + local ($off) = shift; + local ($file); + + &touch_time(@_); + + local (@s) = stat($_[0]); + + utime($s[8]+$off, $s[9]+$off, @_); +} + + + +open(MAKEFILE,"> $makefile"); + +# The Contents of the MAKEFILE ... + +print MAKEFILE <<'EOM'; +SHELL = /bin/sh + +OUT_OF_DATE_DATE = ${shell if [ "$<" -nt "$@" ]; then echo true; fi} +OUT_OF_DATE_CONTENT=${shell thismd5=$$(md5sum "$<" | awk '{print $$1}');if [ ! -f ".$<.md5" ]; then touch ".$<.md5"; fi; oldmd5=$$(cat ".$<.md5");if [ "$$thismd5" != "$$oldmd5" ]; then echo "true"; echo "$$thismd5" > ".$<.md5"; fi} + +a: .OUT_OF_DATE=${OUT_OF_DATE_CONTENT} +a: b c d + @echo makinga + @cat b c d > a + +b: .OUT_OF_DATE=${OUT_OF_DATE_DATE} + +b: sb + @echo makingb + @cat sb > b + +c: sc + @echo makingc + @cat sc > c + +d: sd + @echo makingd + @cat sd > d + + +o: so + @echo making o + @cat so > o + +# continue here +EOM +# end of the contents of MAKEFILE +close(MAKEFILE); + +open(SB, "> sb"); +print SB "theb"; +close(SB); + +open(SC, "> sc"); +print SC "thec"; +close(SC); + +open(SD, "> sd"); +print SD "thed"; +close(SD); + +open(SO, "> so"); +print SO "theo"; +close(SO); + + +# first, run a complete make + +&run_make_with_options($makefile,"",&get_logfile); + +# First test: a repeated make should not need make anything + +&run_make_with_options($makefile,"-s",&get_logfile); + +$answer = ""; + +&compare_output($answer,&get_logfile(1)); + +# Second test: After touching b, make should rebuild b, becase +# b dependency is defined by time + +&touch_time('sb'); + +&utouch_time(-5, 'b'); + +&run_make_with_options($makefile,"-s",&get_logfile); + +$answer = "makingb\n"; + +&compare_output($answer,&get_logfile(1)); + +# Third test: since c dependency is defined by content, it +# should not be rebuilt after touching it. + +&touch_time('sc'); +&utouch_time(-5, 'c'); + +&run_make_with_options($makefile,"-s",&get_logfile); + +$answer = ""; + +&compare_output($answer,&get_logfile(1)); + +# Fourth test: after chaning c content, make should rebuild it + +&touch('sc'); +&utouch(-5, 'c'); + +&run_make_with_options($makefile,"-s",&get_logfile); + +$answer = "makingc\n". + "makinga\n"; + +&compare_output($answer,&get_logfile(1)); + + +unlink(qw(a b c d sb sd sc o so .b.md5 .c.md5 .d.md5 .sc.md5 .sd.md5)); + +1; + + + + diff -u -r -N -X ../excluded_list ../make.cvs/tests/scripts/variables/cached ./tests/scripts/variables/cached --- ../make.cvs/tests/scripts/variables/cached 1970-01-01 01:00:00.000000000 +0100 +++ ./tests/scripts/variables/cached 2007-08-05 14:40:59.000000000 +0200 @@ -0,0 +1,81 @@ +# -*-perl-*- + +$description = "This test checks the implementation of the target-cached \n" . + "variable flavour \n"; + +# Test #1 simple verification that target cached variables are actually cached + +open(MAKEFILE,"> $makefile"); + +print MAKEFILE <<'EOM'; + + +target-cached count = ${shell v=$$(cat v); v=$$[$$v+1]; echo $$v > v ; echo $$v} + +test: a c + +a: b + @echo ${b::count} + +c: b + @echo ${b::count} +EOM +close(MAKEFILE); + +open(B, "> b"); +print B "ab"; +close(B); + +open(V, "> v"); +print V "0"; +close(V); + +&run_make_with_options($makefile,"",&get_logfile); + +$answer = "1\n1\n"; + +&compare_output($answer,&get_logfile(1)); + + +# Test #2 check that target cached variables are cleared after the succesful recompilation of its target + +open(MAKEFILE,"> $makefile"); + +print MAKEFILE <<'EOM'; + +target-cached count = ${shell v=$$(cat v); v=$$[$$v+1]; echo $$v > v ; echo $$v} + +test: a c + +a: b + @echo ${b::count} + +c: b + @echo ${b::count} + +b: + @echo ${count} +EOM +close(MAKEFILE); + +open(V, "> v"); +print V "0"; +close(V); + +unlink(b); + +&run_make_with_options($makefile,"",&get_logfile); + +$answer = "1\n2\n2\n"; + +&compare_output($answer,&get_logfile(1)); + + +#unlink(qw(b v)); + + + + +1; + + diff -u -r -N -X ../excluded_list ../make.cvs/tests/scripts/variables/not_inherited ./tests/scripts/variables/not_inherited --- ../make.cvs/tests/scripts/variables/not_inherited 1970-01-01 01:00:00.000000000 +0100 +++ ./tests/scripts/variables/not_inherited 2007-08-20 19:17:19.000000000 +0200 @@ -0,0 +1,59 @@ +# -*-perl-*- + +$description = "This test checks the feature of the variabes not inherited \n" . + "from a target to its dependencies \n"; + +# Test #1 simple verification that non inherited variables are not inherited + +open(MAKEFILE,"> $makefile"); + +print MAKEFILE <<'EOM'; +CFLAGS=global + +a: not-inherit CFLAGS=froma + +a: b + @echo a CFLAGS are $(CFLAGS) + +b: + @echo b CFLAGS are $(CFLAGS) +EOM +close(MAKEFILE); + + +&run_make_with_options($makefile,"",&get_logfile); + +$answer = "b CFLAGS are global\na CFLAGS are froma\n"; + +&compare_output($answer,&get_logfile(1)); + +# Test #2 verification with patterns + +open(MAKEFILE,"> $makefile"); + +print MAKEFILE <<'EOM'; +CFLAGS=global +CXFLAGS=global + +%.a: not-inherit CFLAGS=froma +%.a: CXFLAGS=froma + +t.a: t.b + @echo a CFLAGS are $(CFLAGS) + @echo a CXFLAGS are $(CXFLAGS) + +t.b: + @echo b CFLAGS are $(CFLAGS) + @echo b CXFLAGS are $(CXFLAGS) +EOM +close(MAKEFILE); + +&run_make_with_options($makefile,"",&get_logfile); + +$answer = "b CFLAGS are global\n" + . "b CXFLAGS are froma\n" + . "a CFLAGS are froma\n" + . "a CXFLAGS are froma\n"; + +&compare_output($answer,&get_logfile(1)); + diff -u -r -N -X ../excluded_list ../make.cvs/user_dep.c ./user_dep.c --- ../make.cvs/user_dep.c 1970-01-01 01:00:00.000000000 +0100 +++ ./user_dep.c 2007-06-10 12:51:23.000000000 +0200 @@ -0,0 +1,53 @@ +#include "make.h" +#include "filedef.h" +#include "user_dep.h" +#include "variable.h" + + + +/* Determinate if there are user defined dependencies for target target */ +int has_user_dep(struct file* target) +{ + int ret; + struct variable_set_list *save; + save = current_variable_set_list; + current_variable_set_list = target->variables; + ret = lookup_variable(".OUT_OF_DATE", sizeof(".OUT_OF_DATE") - 1) != NULL; + current_variable_set_list = save; + return ret; +} + +/* Determinate if target is out of date compared to src, using user defined dependencies */ +int check_user_dep(struct file* target, struct file* src) +{ + int ret = 0; + struct variable_set_list* save; + char* cmd_eval = NULL; + int cmd_eval_len; + struct variable* ood = NULL; + struct variable_set_list* context = NULL; + save = current_variable_set_list; + current_variable_set_list = target->variables; + ood = lookup_variable(".OUT_OF_DATE", sizeof(".OUT_OF_DATE") - 1); + if (ood == NULL) { + fprintf(stderr, "Bug: if .OUT_OF_DATE not defined, this function should not have been invoked"); + goto out; + } + context = create_new_variable_set(); + define_variable_in_set("<", 1, src->name, + o_automatic, 0, + context->set, NILF); + current_variable_set_list = context; + cmd_eval = allocated_variable_expand (ood->value); + cmd_eval_len = strlen(cmd_eval); + ret = (cmd_eval_len > 0); + out: + if (context != NULL) { + free_variable_set(context); + } + if (cmd_eval != NULL) { + free(cmd_eval); + } + current_variable_set_list = save; + return ret; +} diff -u -r -N -X ../excluded_list ../make.cvs/user_dep.h ./user_dep.h --- ../make.cvs/user_dep.h 1970-01-01 01:00:00.000000000 +0100 +++ ./user_dep.h 2007-06-10 00:06:48.000000000 +0200 @@ -0,0 +1,3 @@ +int check_user_dep(struct file* target, struct file* src); + +int has_user_dep(struct file* target); diff -u -r -N -X ../excluded_list ../make.cvs/variable.c ./variable.c --- ../make.cvs/variable.c 2007-07-04 21:35:20.000000000 +0200 +++ ./variable.c 2007-08-20 20:21:53.000000000 +0200 @@ -222,6 +222,8 @@ v->per_target = 0; v->append = 0; v->export = v_default; + v->target_cached = 0; + v->exportable = 1; if (*name != '_' && (*name < 'A' || *name > 'Z') @@ -443,18 +445,31 @@ void initialize_file_variables (struct file *file, int reading) { - struct variable_set_list *l = file->variables; + struct variable_set_list *l = file->variables, *li = file->inheritable_variables; + + if (li == NULL) + { + li = (struct variable_set_list *) + xmalloc (sizeof (struct variable_set_list)); + li->set = xmalloc (sizeof (struct variable_set)); + hash_init (&li->set->table, PERFILE_VARIABLE_BUCKETS, + variable_hash_1, variable_hash_2, variable_hash_cmp); + file->inheritable_variables = li; + } - if (l == 0) + if (l == NULL) { l = (struct variable_set_list *) xmalloc (sizeof (struct variable_set_list)); l->set = xmalloc (sizeof (struct variable_set)); hash_init (&l->set->table, PERFILE_VARIABLE_BUCKETS, variable_hash_1, variable_hash_2, variable_hash_cmp); + l->next = file->inheritable_variables; file->variables = l; } + + /* If this is a double-colon, then our "parent" is the "root" target for this double-colon rule. Since that rule has the same name, parent, etc. we can just use its variables as the "next" for ours. */ @@ -463,15 +478,16 @@ { initialize_file_variables (file->double_colon, reading); l->next = file->double_colon->variables; + li->next = file->double_colon->inheritable_variables; return; } if (file->parent == 0) - l->next = &global_setlist; + file->inheritable_variables->next = &global_setlist; else { initialize_file_variables (file->parent, reading); - l->next = file->parent->variables; + file->inheritable_variables->next = file->parent->inheritable_variables; } /* If we're not reading makefiles and we haven't looked yet, see if @@ -489,14 +505,23 @@ /* We found at least one. Set up a new variable set to accumulate all the pattern variables that match this target. */ - file->pat_variables = create_new_variable_set (); - current_variable_set_list = file->pat_variables; - do { /* We found one, so insert it into the set. */ struct variable *v; + if (p->variable.not_inherit == 0) + { + if (file->inheritable_pat_variables == 0) + file->inheritable_pat_variables = create_new_variable_set (); + current_variable_set_list = file->inheritable_pat_variables; + } + else + { + if (file->pat_variables == 0) + file->pat_variables = create_new_variable_set (); + current_variable_set_list = file->pat_variables; + } if (p->variable.flavor == f_simple) { @@ -524,14 +549,20 @@ current_variable_set_list = global; } file->pat_searched = 1; + /* If we have a pattern variable match, set it up. */ + + if (file->pat_variables != 0) + { + file->pat_variables->next = l->next; + l->next = file->pat_variables; + } + } - /* If we have a pattern variable match, set it up. */ - - if (file->pat_variables != 0) + if (file->inheritable_pat_variables != 0) { - file->pat_variables->next = l->next; - l->next = file->pat_variables; + file->inheritable_pat_variables->next = li->next; + li->next = file->inheritable_pat_variables; } } diff -u -r -N -X ../excluded_list ../make.cvs/variable.h ./variable.h --- ../make.cvs/variable.h 2007-07-04 21:35:20.000000000 +0200 +++ ./variable.h 2007-08-20 13:13:30.000000000 +0200 @@ -66,6 +66,12 @@ unsigned int exp_count:EXP_COUNT_BITS; /* If >1, allow this many self-referential expansions. */ + unsigned int target_cached:1; + /* Nonzero means that the value of this variable + is cached in the context of the current target */ + unsigned int not_inherit:1; /* If set, variable should not be inherited + from one rule to child rules*/ + enum variable_flavor flavor ENUM_BITFIELD (3); /* Variable flavor. */ enum variable_origin diff -u -r -N -X ../excluded_list ../make.cvs/doc/make.texi ./doc/make.texi --- ../make.cvs/doc/make.texi 2007-08-15 15:53:54.000000000 +0200 +++ ./doc/make.texi 2007-08-21 00:31:58.000000000 +0200 @@ -145,6 +145,8 @@ with another makefile. * Reading Makefiles:: How makefiles are parsed. * Secondary Expansion:: How and when secondary expansion is performed. +* Custom up to date:: Customize the method that 'make' uses + to determinate if a target needs updating. Writing Rules @@ -243,6 +245,11 @@ * Pattern-specific:: Target-specific variable values can be applied to a group of targets that match a pattern. +* Changing evaluation context:: Getting the value of a variable in the context + of another target. +* Target cached variables:: Variables that are evaluated only once + for each target + Advanced Features for Reference to Variables * Substitution Refs:: Referencing a variable with @@ -268,6 +275,8 @@ * Origin Function:: Find where a variable got its value. * Flavor Function:: Find out the flavor of a variable. * Shell Function:: Substitute the output of a shell command. +* Changed-Value Function:: Check if the value of an expression changed + since last invocation of this rule. * Make Control Functions:: Functions that control how make runs. How to Run @code{make} @@ -943,6 +952,8 @@ with another makefile. * Reading Makefiles:: How makefiles are parsed. * Secondary Expansion:: How and when secondary expansion is performed. +* Custom up to date:: Customize the method that 'make' uses to determinate + if a target needs updating. @end menu @node Makefile Contents, Makefile Names, Makefiles, Makefiles @@ -1606,7 +1617,7 @@ general rule is true for explicit rules, pattern rules, suffix rules, static pattern rules, and simple prerequisite definitions. address@hidden Secondary Expansion, , Reading Makefiles, Makefiles address@hidden Secondary Expansion, Custom up to date, Reading Makefiles, Makefiles @section Secondary Expansion @cindex secondary expansion @cindex expansion, secondary @@ -1808,6 +1819,208 @@ foo.h}. If you are not interested in this reconstruction, you can use @code{$$*} instead of @code{%} in the prerequisites list. + address@hidden Custom up to date, , Secondary Expansion, Makefiles address@hidden Custom up to date address@hidden Out of date determination address@hidden Up to date determination address@hidden .OUT_OF_DATE + address@hidden allows users to customize the way it determinates if a file +needs updating. This is by setting the variable @code{.OUT_OF_DATE}. +If this variable is defined, then make evaluates it, and if the result is not empty +then the target is recompiled. Like any other variable, it can be defined in +the command line, in a target, or in a pattern, thus allowing the user to +choose the scope of its customization. By manipulating this variable, it is possible +to decide the updating of target by a change in content, comparing its timestamp +with the timestamp when it was last compiled, and so on. It is also possible to have +a dependency on the value of a variable. + +Currently, this feature relies mostly on shell commands to perform useful out of date +determinations. When @code{GNU make} integrates scripting, writing user-defined update +criteria will be easier and the makefiles will run faster. + +For instance, writing + address@hidden +.OUT_OF_DATE=true address@hidden example + +causes make to rebuild everything unconditionally. + +The variable @code{.OUT_OF_DATE} is evaluated for each pair (target, +prerequisite), and the result is an ``or'' of the results for the +prerequisites of a target. During its evaluation @code{$$<} stands for +this prerequisite and @code{$$@@} stands for the current +target. Bellow we will see some practical applications. + + address@hidden +* Using changes in content:: Using content change + for out of date determination +* Specializing for file types:: How to use a different criterion + for each prerequisite of a target +* Dependencies on variables:: How to make a target + depend on the value of a variable address@hidden menu + address@hidden Using changes in content, Specializing for file types, ,Custom up to date address@hidden Using changes in content address@hidden content based up to date +Often it is better to use changes in content for deciding when to +update a target rather than modification times. Modification times do +not work well when files are stored in a NFS server that is not +synchronized with the machine where make is running. And their +precision is limited to one second, and therefore can give wrong +results work if the compilation command takes less time, as it often +happens with modern machines. Therefore, it is more reliable to check +if the content of prerequisites changed since last compilation as a +criterion for deciding the up to date status of a target. + +For writing makefiles that depend on content, @samp{make} provides +the builtin function @code{changed-value} @pxref{Changed-Value +Function, ,The @code{changed-value} Function}. This function returns true if the value of a given +expression is different since the last recompilation of some +target. The typical usage of this function in @code{.OUT_OF_DATE} +variable is: + address@hidden +.OUT_OF_DATE=$(changed-value $@@,@var{sometag}.$<,@var{someexpression}) address@hidden example + +Here, @var{sometag} is an arbitrary string, that helps avoiding +collisions with other possible uses of address@hidden @var{someexpression} is an expression that should +have @samp{$<} in its body, and its value depends on the content of the +prerequisite. + +Now we are going to build an example value of @samp{.OUT_OF_DATE} for +MD5 checksums. The expression that gets the MD5 sum of the file address@hidden<} is @samp{$(shell md5sum $< | awk '@{print address@hidden')}. Thus the +complete setting of @samp{.OUT_OF_DATE} is + address@hidden +.OUT_OF_DATE=$(changed-value $@@,sum.$<,\ + $(shell md5sum $< | awk '@{print address@hidden')) address@hidden example + +With this setting either in a Makefile or in the command line, address@hidden uses the changes contents of files as the criterion for deciding when to recompile. + +But this method is inefficient, because if the same prerequisite is used in +different targets, the expensive calculation of the checksum is repeated for each +target. To solve this issue, @code{make} has a feature called target cached variables +(@pxref{Target cached variables}) . Target cached variables are like normal recursively evaluated +variables @pxref{Flavors, ,The Two Flavors of Variables}. But, if the same cached +variable is expanded more than once from the same target, the second and next +times reuse the result of the first evaluation. We declare a cached variable address@hidden for the MD5 sum of the current target. This looks odd, but it will make +sense later. Then @samp{.OUT_OF_DATE} invokes the value of this variable evaluated +from the context of each prerequisite as target @pxref{Changing evaluation +context}. + address@hidden +target-cached hashcode=$(shell md5sum $@@ | awk '@{print address@hidden') +.OUT_OF_DATE=$(changed-value $@@,sum.$<,$($<::hashcode)) address@hidden example + +The complication of using the prerequisite as context for the evaluation of the cached +variable is necessary to ensure correct behavior if this prerequisite is +recompiled. In this case, the cache needs to be cleared, because the checksums +are no longer correct. @code{Make} is aware of the need to clear this cache by making the +prerequisite the target context of the evaluation. + +Note the construct @samp{::} used to denote getting the value of a variable in the context +of another target @pxref{Changing evaluation context} + address@hidden Specializing for file types, Dependencies on variables, Using changes in content, Custom up to date address@hidden Specializing for file types + +Sometimes one might want to use a different method for the +determination of the up to date status of some prerequisites. This can +avoid unnecessary recompilations. For instance, if one java source +file references some java classes, that source must be recompiled only +if the interfaces of this classes change, but not if only the +implementations change. By taking this into account, it is possible to +avoid unnecessary recompilations. + +For instance if java class @samp{Main} references class @samp{Util}, one +could specialize the checksum determination described in the previous section, +the makefile could read: + address@hidden +target-cached hashcode=$(shell md5sum $@@ | awk '@{print address@hidden') +.OUT_OF_DATE=$(changed-value $@@,sum.$<,$($<::hashcode)) +%.class: target-cached not-inherit \ + hashcode=$(shell javap -classpath . -s $* | \ + md5sum - | awk '@{print address@hidden') + + +%.class: %.java + javac $< + +Main.class: Util.class address@hidden example + +Here we have used the flag @code{not-inherit} to prevent the +inheriting of the variable @var{hashcode} from every file address@hidden to its java prerequisite @samp{%.java}, which does not +work because this formula for the checksum is specific for Java +classes. + +This approach might have the inconvenient of the time spent extracting +the interface from a class. In our experiments, this takes much more time +than a simple checksum. The user is adviced to balance this additional time +spent against the benefit of avoiding unnecessary recompilations. + address@hidden Dependencies on variables, , Specializing for file types, Custom up to date address@hidden Dependencies on variables + +It is useful to recompile a target if its compilation flags are different from its +last compilation. This can be done by adding a prerequisite to a rule that marks the +dependency on the variable, and using the method described above for out of date based +on content. + +For instance, let us have a target ai.o that depends on ai.c, and we would like that +it also depends on the value of @samp{CFLAGS}. + +We write: + address@hidden +ai.o: ai.c CFLAGS.variable address@hidden example + +We arrange the makefile to use out of date determination based +on content, and then we specialize for the case of pseudo-targets that +match the pattern @samp{%.variable}, so that the checksum is just the +content of the variable. + address@hidden + +.OUT_OF_DATE=$(changed-value $@@,$<.sum,$($<::content-sum)) + +target-cached content-sum=$(shell md5sum $@@ | awk '@{print address@hidden') + +%.variable: .OUT_OF_DATE= +%.variable: + @@echo + +%.variable: content-sum=$($*) + + +ai.o: ai.c CFLAGS.variable + gcc -c $(CFLAGS) ai.c address@hidden example + +At present a kludge is necessary for this mechanism to work: the +pattern @samp{%.variable} must have a command associated with it. This +is because otherwise @code{make} interprets the pattern as a clearing +on any existing pattern, and therefore the variable specialization +will not happen. This behavior might change in future versions of +make. An empty @samp{.OUT_OF_DATE} for this target ensures that this +recipe is never executed, to avoid the performance penalty of launching +an unnecessary command. + @node Rules, Commands, Makefiles, Top @chapter Writing Rules @cindex writing rules @@ -1957,7 +2170,8 @@ @var{prerequisites}, which consist of file names separated by spaces. (Wildcards and archive members (@pxref{Archives}) are allowed here too.) A target is out of date if it does not exist or if it is older than any -of the prerequisites (by comparison of last-modification times). The +of the prerequisites (by comparison of last-modification times). This +method can be customized, @pxref{Custom up to date}. The idea is that the contents of the target file are computed based on information in the prerequisites, so if any of the prerequisites changes, the contents of the existing target file are no longer necessarily @@ -4863,6 +5077,12 @@ basis. * Pattern-specific:: Target-specific variable values can be applied to a group of targets that match a pattern. +* Changing evaluation context:: Getting the value of a variable in the context + of another target. +* Suppressing inheritance:: Suppressing the inheritance of variables from + a target to its prerequisites. +* Target cached variables:: Variables that are evaluated only once + for each target. @end menu @node Reference, Flavors, Using Variables, Using Variables @@ -5123,6 +5343,7 @@ * Substitution Refs:: Referencing a variable with substitutions on the value. * Computed Names:: Computing the name of the variable to refer to. + @end menu @node Substitution Refs, Computed Names, Advanced, Advanced @@ -5180,7 +5401,7 @@ @noindent sets @samp{bar} to @samp{a.c b.c c.c}. address@hidden Computed Names, , Substitution Refs, Advanced address@hidden Computed Names, ,Substitution Refs, Advanced @subsection Computed Variable Names @cindex nested variable reference @cindex computed variable name @@ -5852,10 +6073,12 @@ multiple targets, and each of those targets has a different value for the same target-specific variable, then the first target to be built will cause that prerequisite to be built and the prerequisite will -inherit the target-specific value from the first target. It will -ignore the target-specific values from any other targets. +inherit the target-specific value from the first target. (Inheritance +can be supressed by declaring a variable @code{not-inherit}) address@hidden inheritance}. It will ignore the target-specific +values from any other targets. address@hidden Pattern-specific, , Target-specific, Using Variables address@hidden Pattern-specific, Changing evaluation context, Target-specific, Using Variables @section Pattern-specific Variable Values @cindex pattern-specific variables @cindex variables, pattern-specific @@ -5901,16 +6124,129 @@ will assign @code{CFLAGS} the value of @samp{-O} for all targets matching the pattern @code{%.o}. address@hidden Changing evaluation context, Suppressing inheritance, Pattern-specific, Using Variables address@hidden Changing evaluation context address@hidden variables, change scope address@hidden variables, change context address@hidden :: + +Sometimes one wants to access a local variable of one target from +another target. Make supports this usage through the syntax + address@hidden +$(@var{another-target}::@var{variable-name}) address@hidden example + +For example, if one wants to use the compilation flags of one file @samp{b.o} +for compiling another file @samp{a.o}: + address@hidden +a.o: a.c + cc $(b.o:CFLAGS) + +b.o: CFLAGS="-g" address@hidden example + +(As usual, either parenthesis or braces are valid for designing the +value of a variable). The result of this expansion is exactly the same +as if @samp{variable-name} were expanded inside the other target. In +particular, if a value from the command line is taking precedence over a +target specific, it will be returned. + +At present the only practical usages for this construction is +specialization of user defined up to date determination address@hidden for file types} and dependencies on variables address@hidden on variables}. + address@hidden Suppressing inheritance, Target cached variables, Changing evaluation context, Using Variables address@hidden Suppressing inheritance address@hidden not-inherit address@hidden inheritance, remove + +By default, @code{make} variables are inherited from targets to their +prerequisites. This feature is useful, and allows one to change the +compilation settings for a subtree of the dependency tree with a +single change. But sometimes this can have unintended effects. For +instance, settings of target-specific variables are intended for a +particular type of file, and should not be propagated to +prerequisites. Variables that modify the behavior of @code{make}, +such as @code{.OUT_OF_DATE} @pxref{Custom up to date} are likely not +to be intended to be inherited. + +For these cases, @code{make} provides the @code{not-inherit} +flag. This flag can only be used for target specific and pattern +specific variables , because it does not make any sense to use it with +global variables. This flag can be combined with ``override'', +``export'' or other flags. That is: + address@hidden address@hidden: not-inherit @var{other flags} @var{variable-assignment} address@hidden example + +A simple example showing its behavior: address@hidden +CFLAGS=global + +a: not-inherit CFLAGS=froma + +a: b + @@echo a CFLAGS are $(CFLAGS) + +b: + @@echo b CFLAGS are $(CFLAGS) address@hidden example +after running @samp{make a} it prints address@hidden +b CFLAGS are global +a CFLAGS are froma address@hidden example +because @samp{a} does not transmit its @samp{CFLAGS} to @samp{b}. + address@hidden Target cached variables , , Suppressing inheritance, Using Variables address@hidden Target cached variables address@hidden variables, Target cached address@hidden target-cached + +Sometimes the evaluation of a variable involves an expensive +operation, such as a MD5 sum of a file. For avoiding repeated +evaluations, @code{make} provides target cached variables. These are +otherwise like recursive expanded variables (@pxref{Flavors, ,The Two +Flavors of Variables}). The difference is that if there are several +invocations of one target cached variable from the same target, the +evaluation is performed only once. + +Target cached variables are declared like normal recursive variables +but prepended with the keyword ``target-cached''. They can be declared +as global variables, in the scope of a pattern or in the scope of a +rule. Only recursive type variable assignments (@samp{=}) are +supported: static assignments do not make sense for caching because +there is no evaluation for them. + +The memory of target cached variables is cleared after the compilation +(successful or not) of a target. The rationale is that these variables +are intended to depend on the content of the target. + +The typical usage is for storing checksums of targets for user defined +up to date determination based on content. For instance + address@hidden +target-cached md5=$(shell md5sum $@@ | awk '@{print address@hidden') address@hidden example + +makes the checksum of any file available as @samp{$(:md5)}. +It is possible to use a variable value for the target context, such as address@hidden($(file)::md5)}. + @node Conditionals, Functions, Using Variables, Top @chapter Conditional Parts of Makefiles @cindex conditionals A @dfn{conditional} causes part of a makefile to be obeyed or ignored depending on the values of variables. Conditionals can compare the -value of one variable to another, or the value of a variable to -a constant string. Conditionals control what @code{make} actually -``sees'' in the makefile, so they @emph{cannot} be used to control shell -commands at the time of address@hidden +value of one variable to another, or the value of a variable to a +constant string. Conditionals control what @code{make} actually +``sees'' in the makefile, so they @emph{cannot} be used to control +shell commands at the time of address@hidden @menu * Conditional Example:: Example of a conditional @@ -6238,6 +6574,8 @@ * Origin Function:: Find where a variable got its value. * Flavor Function:: Find out the flavor of a variable. * Shell Function:: Substitute the output of a shell command. +* Changed-Value Function:: Determinate if a value is the same as in the + last compilation. * Make Control Functions:: Functions that control how make runs. @end menu @@ -7326,7 +7664,7 @@ @end table address@hidden Shell Function, Make Control Functions, Flavor Function, Functions address@hidden Shell Function, Changed-Value Function, Flavor Function, Functions @section The @code{shell} Function @findex shell @cindex commands, expansion @@ -7374,7 +7712,36 @@ @address@hidden(wildcard *.c)}} (as long as at least one @samp{.c} file exists)address@hidden address@hidden Make Control Functions, , Shell Function, Functions address@hidden Changed-Value Function, Make Control Functions, Shell Function, Functions address@hidden changed-value address@hidden persistent address@hidden The @code{changed-value} Function + +This function returns @code{true} is some expression has now the same expression as +when it was evaluated in the previous successful of the given target. The syntax is + address@hidden +$(changed-value @var{target},@var{tag},@var{expression}) address@hidden example + +When this function is called, make reads the value of expression +stored in the last running of @code{make}, in a file named +.target.tag. If the value stored is different from the present one, +the function returns the string @code{true} and arranges to store the +new value if the compilation of the given target finishes +successfully. The @var{tag} is an arbitrary string that helps prevent +collisions between different calls for the same target. The address@hidden will be @samp{$@@} in typical usage. + +This function exists to support up to date determination by content, address@hidden changes in content} (examples of usage can be found +there). This why the function arranges to store the new value only +after a successful recompilation. Otherwise, if the compilation fails, +it is better that the next time make runs again the compilation is +repeated. + + address@hidden Make Control Functions, , Changed-Value Function, Functions @section Functions That Control Make @cindex functions, for controlling make @cindex controlling make @@ -7383,6 +7750,7 @@ provide information to the user of the makefile or to cause make to stop if some sort of environmental error is detected. + @table @code @item $(error @address@hidden) @findex error @@ -11120,7 +11488,8 @@ @node GNU Free Documentation License, Concept Index, Complex Makefile, Top @appendixsec GNU Free Documentation License @cindex FDL, GNU Free Documentation License address@hidden fdl.texi address@hidden DIFF REMOVE do not include this change in patch address@hidden @include fdl.texi @node Concept Index, Name Index, GNU Free Documentation License, Top @unnumbered Index of Concepts