diff -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/Makefile.am make-3.81/Makefile.am --- make-3.81.orig/Makefile.am 2006-04-01 08:36:40.000000000 +0200 +++ make-3.81/Makefile.am 2007-07-21 21:16:47.959849200 +0200 @@ -41,7 +41,8 @@ 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 \ + $(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 -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/commands.c make-3.81/commands.c --- make-3.81.orig/commands.c 2006-02-20 03:14:01.000000000 +0100 +++ make-3.81/commands.c 2007-07-24 01:56:07.221399895 +0200 @@ -46,7 +46,7 @@ { struct dep *d; char *at, *percent, *star, *less; - + #ifndef NO_ARCHIVES /* If the target is an archive member `lib(member)', then $@ is `lib' and $% is `member'. */ @@ -145,6 +145,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. */ @@ -192,12 +193,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++; @@ -213,8 +217,9 @@ 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) { + d = dr->ref; char *c = dep_name (d); #ifndef NO_ARCHIVES @@ -257,6 +262,8 @@ bp[bp > bar_value ? -1 : 0] = '\0'; DEFINE_VARIABLE ("|", 1, bar_value); + + free_dep_ref(uniq_dep_ref); } #undef DEFINE_VARIABLE diff -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/custom_outofdate.c make-3.81/custom_outofdate.c --- make-3.81.orig/custom_outofdate.c 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/custom_outofdate.c 2007-07-26 03:11:19.065876999 +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 -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/custom_outofdate.h make-3.81/custom_outofdate.h --- make-3.81.orig/custom_outofdate.h 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/custom_outofdate.h 2007-07-26 03:11:57.774971685 +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 -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/dep.h make-3.81/dep.h --- make-3.81.orig/dep.h 2006-03-17 15:24:20.000000000 +0100 +++ make-3.81/dep.h 2007-07-24 02:01:13.614657682 +0200 @@ -82,4 +82,12 @@ extern struct dep *read_all_makefiles PARAMS ((char **makefiles)); extern int eval_buffer PARAMS ((char *buffer)); extern int update_goal_chain PARAMS ((struct dep *goals)); -extern void uniquize_deps PARAMS ((struct dep *)); + +struct dep_ref { + struct dep* ref; + struct dep_ref* next; +}; + +extern void uniquize_deps PARAMS ((struct dep *src, struct dep_ref **uniq_dep_ref)); + +extern void free_dep_ref PARAMS ((struct dep_ref *dep_ref)); diff -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/expand.c make-3.81/expand.c --- make-3.81.orig/expand.c 2006-03-15 04:31:30.000000000 +0100 +++ make-3.81/expand.c 2007-06-30 15:48:32.000000000 +0200 @@ -165,7 +165,50 @@ register struct variable *v; char *value; - v = lookup_variable (name, length); + char *dcolon; + 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); @@ -174,7 +217,13 @@ if (v == 0 || (*v->value == '\0' && !v->append)) return o; - value = (v->recursive ? recursively_expand (v) : v->value); + if (v->recursive) { + current_variable_set_list = context; + value = recursively_expand(v); + current_variable_set_list = save; + } else { + value = v->value; + } o = variable_buffer_output (o, value, strlen (value)); @@ -298,6 +347,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). */ @@ -368,6 +420,7 @@ free (value); } } + } if (colon == 0) diff -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/filedef.h make-3.81/filedef.h --- make-3.81.orig/filedef.h 2006-02-11 23:16:04.000000000 +0100 +++ make-3.81/filedef.h 2007-07-23 23:53:37.835594779 +0200 @@ -64,6 +64,9 @@ 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 */ + enum cmd_state /* State of the commands. */ { /* Note: It is important that cs_not_started be zero. */ cs_not_started, /* Not yet started. */ diff -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/function.c make-3.81/function.c --- make-3.81.orig/function.c 2006-04-01 08:36:40.000000000 +0200 +++ make-3.81/function.c 2007-07-24 02:02:05.305771879 +0200 @@ -23,6 +23,7 @@ #include "job.h" #include "commands.h" #include "debug.h" +#include "persistent_state.h" #ifdef _AMIGA #include "amiga.h" @@ -1387,6 +1388,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 == 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? */ @@ -2081,6 +2121,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 -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/persistent_state.c make-3.81/persistent_state.c --- make-3.81.orig/persistent_state.c 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/persistent_state.c 2007-07-26 03:13:00.711370343 +0200 @@ -0,0 +1,272 @@ +/* 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" + + +/* 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) +{ + const static 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; + 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; + +struct persistent_var +{ + unsigned int name_len; + char *name; + unsigned int value_len; + char *value; +}; + +static unsigned long +persistent_var_hash_1 (const void *keyv) +{ + const struct persistent_var *v = (const struct persistent_var *) keyv; + return_STRING_N_HASH_1(v->name, v->name_len); +} + +static unsigned long +persistent_var_hash_2 (const void *keyv) +{ + const struct persistent_var *v = (const struct persistent_var *) keyv; + return_STRING_N_HASH_2(v->name, v->name_len); +} + +static int +persistent_var_hash_cmp (const void *xv, const void *yv) +{ + const struct persistent_var *v1 = (const struct persistent_var *) xv; + const struct persistent_var *v2 = (const struct persistent_var *) 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); +} + +/* 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 persistent_var key; + struct persistent_var *var; + struct persistent_var **slot; + unsigned int name_len; + if (persistent_state->ht_vec == NULL) + hash_init(persistent_state, PERSISTENT_VAR_BUCKETS, + persistent_var_hash_1, persistent_var_hash_2, persistent_var_hash_cmp); + name_len = strlen(var_name); + key.name = (char*) var_name; + key.name_len = name_len; + slot = (struct persistent_var**) hash_find_slot(persistent_state, &key); + if (!HASH_VACANT(*slot)) + { + fprintf(stderr, "Warning: persistent variable name %s of target %s overwritten\n", + var_name, target->name); + free((*slot)->value); + var = *slot; + } + else + { + var = (struct persistent_var*) xmalloc(sizeof(struct persistent_var)); + 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); + var->value = xmalloc(value_len); + memcpy(var->value, value, value_len); + var->value_len = value_len; + + persistent_state->ht_fill++; + } + +} + + +/* 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; + if (*value != NULL) + *value = xrealloc(*value, value_alloc_size_2); + else + *value = xmalloc(value_alloc_size_2); + int nread; + 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 persistent_var *v = (const struct persistent_var*) 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, target->name); +} diff -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/persistent_state.h make-3.81/persistent_state.h --- make-3.81.orig/persistent_state.h 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/persistent_state.h 2007-07-26 03:13:09.685001359 +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 -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/read.c make-3.81/read.c --- make-3.81.orig/read.c 2006-03-17 15:24:20.000000000 +0100 +++ make-3.81/read.c 2007-07-24 02:03:04.393757636 +0200 @@ -1716,10 +1716,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); @@ -1727,26 +1728,39 @@ 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)) { 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; + struct dep_ref* link = (struct dep_ref*) xmalloc(sizeof(struct dep_ref)); + link->ref = dep; + *dep_ref = link; + dep_ref = &link->next; } + dep = dep->next; } + *dep_ref = NULL; hash_free (&deps, 0); } + +void +free_dep_ref (struct dep_ref *dep_ref) +{ + struct dep_ref *link = dep_ref, *next; + + while (link != NULL) + { + next = link->next; + free(link); + link = next; + } +} + /* Record target-specific variable values for files FILENAMES. TWO_COLON is nonzero if a double colon was used. diff -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/remake.c make-3.81/remake.c --- make-3.81.orig/remake.c 2006-03-20 03:36:37.000000000 +0100 +++ make-3.81/remake.c 2007-07-23 22:34:20.847303169 +0200 @@ -23,6 +23,8 @@ #include "dep.h" #include "variable.h" #include "debug.h" +#include "custom_outofdate.h" +#include "persistent_state.h" #include @@ -62,7 +64,7 @@ static int update_file PARAMS ((struct file *file, unsigned int depth)); static int update_file_1 PARAMS ((struct file *file, unsigned int depth)); -static int check_dep PARAMS ((struct file *file, unsigned int depth, FILE_TIMESTAMP this_mtime, int *must_make_ptr)); +static int check_dep PARAMS ((struct file *file, unsigned int depth, struct file *this_file, FILE_TIMESTAMP this_mtime, int *must_make_ptr)); static int touch_file PARAMS ((struct file *file)); static void remake_file PARAMS ((struct file *file)); static FILE_TIMESTAMP name_mtime PARAMS ((char *name)); @@ -371,6 +373,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; @@ -427,24 +430,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; - } + /* 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. */ @@ -476,7 +492,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)) @@ -505,7 +525,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) @@ -532,7 +552,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. This causes the Linux kernel build to break. We'll defer this @@ -644,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) @@ -653,10 +678,10 @@ /* %%% 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) - /* We must remake if this dep does not - exist and is not intermediate. */ - must_make = 1; + 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; #endif /* Set DEPS_CHANGED if this dep actually changed. */ @@ -665,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)) { @@ -868,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 @@ -931,10 +963,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); @@ -944,12 +977,19 @@ /* 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 { @@ -976,7 +1016,19 @@ check_renamed (file); mtime = file_mtime (file); check_renamed (file); - if (mtime != NONEXISTENT_MTIME && mtime > this_mtime) + + + /* 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; /* Otherwise, update all non-intermediate files we depend on, if necessary, and see whether any of them is more @@ -1012,7 +1064,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; @@ -1105,8 +1157,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); diff -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/tests/scripts/features/changescope make-3.81/tests/scripts/features/changescope --- make-3.81.orig/tests/scripts/features/changescope 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/tests/scripts/features/changescope 2007-07-22 21:55:50.375363980 +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 -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/tests/scripts/functions/changed_value make-3.81/tests/scripts/functions/changed_value --- make-3.81.orig/tests/scripts/functions/changed_value 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/tests/scripts/functions/changed_value 2007-07-22 20:39:15.682313213 +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 -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/tests/scripts/functions/changed_value_env make-3.81/tests/scripts/functions/changed_value_env --- make-3.81.orig/tests/scripts/functions/changed_value_env 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/tests/scripts/functions/changed_value_env 2007-07-22 20:40:48.518150524 +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 -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/tests/scripts/variables/OUTOFDATE1 make-3.81/tests/scripts/variables/OUTOFDATE1 --- make-3.81.orig/tests/scripts/variables/OUTOFDATE1 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/tests/scripts/variables/OUTOFDATE1 2007-07-08 11:09:08.186651727 +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 -r -u --new-file --exclude-from=exclude_diff make-3.81.orig/tests/scripts/variables/OUTOFDATE2 make-3.81/tests/scripts/variables/OUTOFDATE2 --- make-3.81.orig/tests/scripts/variables/OUTOFDATE2 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/tests/scripts/variables/OUTOFDATE2 2007-07-09 04:15:34.369558940 +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; + + + +