diff -u -r -X exclude_diff -N -w make.orig.cvs/AUTHORS make-3.81/AUTHORS --- make.orig.cvs/AUTHORS 2007-07-04 21:35:15.000000000 +0200 +++ make-3.81/AUTHORS 2007-08-04 23:50:21.745896099 +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 -X exclude_diff -N -w make.orig.cvs/ChangeLog make-3.81/ChangeLog --- make.orig.cvs/ChangeLog 2007-07-14 04:57:46.000000000 +0200 +++ make-3.81/ChangeLog 2007-08-05 00:28:32.790382897 +0200 @@ -1,3 +1,49 @@ +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 + + 2007-07-13 Paul Smith * file.c (expand_deps): Use variable_buffer as the start of the diff -u -r -X exclude_diff -N -w make.orig.cvs/commands.c make-3.81/commands.c --- make.orig.cvs/commands.c 2007-07-04 21:35:17.000000000 +0200 +++ make-3.81/commands.c 2007-08-04 19:03:32.825224000 +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) + 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,6 +264,8 @@ bp[bp > bar_value ? -1 : 0] = '\0'; DEFINE_VARIABLE ("|", 1, bar_value); + + free_dep_ref(uniq_dep_ref); } #undef DEFINE_VARIABLE diff -u -r -X exclude_diff -N -w make.orig.cvs/custom_outofdate.c make-3.81/custom_outofdate.c --- make.orig.cvs/custom_outofdate.c 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/custom_outofdate.c 2007-08-02 03:08:03.280476000 +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 -X exclude_diff -N -w make.orig.cvs/custom_outofdate.h make-3.81/custom_outofdate.h --- make.orig.cvs/custom_outofdate.h 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/custom_outofdate.h 2007-08-02 03:08:03.283476000 +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 -X exclude_diff -N -w make.orig.cvs/dep.h make-3.81/dep.h --- make.orig.cvs/dep.h 2007-07-04 21:35:17.000000000 +0200 +++ make-3.81/dep.h 2007-08-03 01:16:31.575942000 +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 -X exclude_diff -N -w make.orig.cvs/expand.c make-3.81/expand.c --- make.orig.cvs/expand.c 2007-07-04 21:35:18.000000000 +0200 +++ make-3.81/expand.c 2007-08-04 20:17:48.972411000 +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; + 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 -X exclude_diff -N -w make.orig.cvs/file.c make-3.81/file.c --- make.orig.cvs/file.c 2007-07-14 04:57:46.000000000 +0200 +++ make-3.81/file.c 2007-08-03 01:19:18.176526000 +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 -X exclude_diff -N -w make.orig.cvs/filedef.h make-3.81/filedef.h --- make.orig.cvs/filedef.h 2007-07-04 21:35:18.000000000 +0200 +++ make-3.81/filedef.h 2007-08-04 23:45:25.283123000 +0200 @@ -64,6 +64,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 -X exclude_diff -N -w make.orig.cvs/function.c make-3.81/function.c --- make.orig.cvs/function.c 2007-07-04 21:35:18.000000000 +0200 +++ make-3.81/function.c 2007-08-02 03:08:09.552519000 +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 == 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 -X exclude_diff -N -w make.orig.cvs/Makefile.am make-3.81/Makefile.am --- make.orig.cvs/Makefile.am 2007-08-04 17:08:36.950233810 +0200 +++ make-3.81/Makefile.am 2007-08-04 20:08:53.463106000 +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 -X exclude_diff -N -w make.orig.cvs/persistent_state.c make-3.81/persistent_state.c --- make.orig.cvs/persistent_state.c 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/persistent_state.c 2007-08-04 20:44:16.859169000 +0200 @@ -0,0 +1,238 @@ +/* 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) +{ + 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; + +/* 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)) + { + fprintf(stderr, "Warning: persistent variable name %s of target %s overwritten\n", + var_name, target->name); + free((*slot)->value); + var = *slot; + } + 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); + 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 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, target->name); +} diff -u -r -X exclude_diff -N -w make.orig.cvs/persistent_state.h make-3.81/persistent_state.h --- make.orig.cvs/persistent_state.h 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/persistent_state.h 2007-08-04 20:44:16.861169000 +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 -X exclude_diff -N -w make.orig.cvs/read.c make-3.81/read.c --- make.orig.cvs/read.c 2007-07-04 21:35:19.000000000 +0200 +++ make-3.81/read.c 2007-08-04 22:38:32.290330000 +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_var, 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,7 @@ enum make_word_type wtype; enum variable_origin v_origin; int exported; + int target_cached; char *cmdleft, *semip, *lb_next; unsigned int plen = 0; char *colonp; @@ -1032,6 +1047,7 @@ v_origin = o_file; exported = 0; + target_cached = 0; if (wtype == w_static) { if (word1eq ("override")) @@ -1044,6 +1060,11 @@ 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); + } } if (wtype != w_eol) @@ -1061,7 +1082,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, fstart); filenames = 0; continue; } @@ -1707,10 +1728,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 +1740,40 @@ 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. @@ -1750,7 +1785,7 @@ 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, const struct floc *flocp) { struct nameseq *nextf; @@ -1820,6 +1855,7 @@ v->origin = origin; v->per_target = 1; v->export = exported ? v_export : v_default; + v->target_cached = target_cached; /* If it's not an override, check to see if there was a command-line setting. If so, reset the value. */ diff -u -r -X exclude_diff -N -w make.orig.cvs/remake.c make-3.81/remake.c --- make.orig.cvs/remake.c 2007-07-04 21:35:19.000000000 +0200 +++ make-3.81/remake.c 2007-08-04 20:54:41.219919000 +0200 @@ -23,6 +23,8 @@ #include "dep.h" #include "variable.h" #include "debug.h" +#include "custom_outofdate.h" +#include "persistent_state.h" #include @@ -63,7 +65,8 @@ 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); + 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); @@ -299,6 +302,8 @@ return f->command_state == cs_finished ? f->update_status : 0; } + delete_target_cached(file); + /* This loop runs until we start commands for a double colon rule, or until the chain is exhausted. */ for (; f != 0; f = f->prev) @@ -372,6 +377,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,6 +434,16 @@ might get implicit commands that apply to its initial name, only to have that name replaced with another found by VPATH search. */ + + /* 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; @@ -444,8 +460,11 @@ this_mtime += FILE_TIMESTAMPS_PER_S - 1 - ns; } - must_make = noexist; + } + else + noexist = 0; + must_make = noexist; /* If file was specified as a target with no commands, come up with some default commands. */ @@ -477,7 +496,11 @@ check_renamed (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 +529,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 +556,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 +666,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 +680,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 +692,10 @@ /* Set D->changed if either this dep actually changed, or its dependent, FILE, is older or does not exist. */ + 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 +898,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 +964,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 +978,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); + if (has_custom_outofdate (this_file)) { + ood = check_custom_outofdate (this_file, file); + } else { mtime = file_mtime (file); - check_renamed (file); - if (mtime == NONEXISTENT_MTIME || mtime > this_mtime) + 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 +1015,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 { @@ -1010,7 +1063,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; @@ -1103,8 +1156,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 -u -r -X exclude_diff -N -w make.orig.cvs/string_hash.c make-3.81/string_hash.c --- make.orig.cvs/string_hash.c 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/string_hash.c 2007-08-04 20:44:22.314337000 +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 -X exclude_diff -N -w make.orig.cvs/string_hash.h make-3.81/string_hash.h --- make.orig.cvs/string_hash.h 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/string_hash.h 2007-08-04 20:44:22.326335000 +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 -X exclude_diff -N -w make.orig.cvs/target_cache_var.c make-3.81/target_cache_var.c --- make.orig.cvs/target_cache_var.c 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/target_cache_var.c 2007-08-04 20:44:48.551334000 +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 -X exclude_diff -N -w make.orig.cvs/target_cache_var.h make-3.81/target_cache_var.h --- make.orig.cvs/target_cache_var.h 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/target_cache_var.h 2007-08-04 20:44:48.620324000 +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 -X exclude_diff -N -w make.orig.cvs/tests/scripts/features/changescope make-3.81/tests/scripts/features/changescope --- make.orig.cvs/tests/scripts/features/changescope 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/tests/scripts/features/changescope 2007-08-02 03:08:09.797482000 +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 -X exclude_diff -N -w make.orig.cvs/tests/scripts/functions/changed_value make-3.81/tests/scripts/functions/changed_value --- make.orig.cvs/tests/scripts/functions/changed_value 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/tests/scripts/functions/changed_value 2007-08-02 03:08:09.800482000 +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 -X exclude_diff -N -w make.orig.cvs/tests/scripts/functions/changed_value_env make-3.81/tests/scripts/functions/changed_value_env --- make.orig.cvs/tests/scripts/functions/changed_value_env 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/tests/scripts/functions/changed_value_env 2007-08-02 03:08:09.933461000 +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 -X exclude_diff -N -w make.orig.cvs/tests/scripts/variables/cached make-3.81/tests/scripts/variables/cached --- make.orig.cvs/tests/scripts/variables/cached 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/tests/scripts/variables/cached 2007-08-04 22:47:56.470261000 +0200 @@ -0,0 +1,41 @@ +# -*-perl-*- + +$description = "This test checks the implementation of the target-cached \n" . + "variable flavour \n"; + +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)); + +#unlink(qw(b v)); + +1; + + diff -u -r -X exclude_diff -N -w make.orig.cvs/tests/scripts/variables/OUTOFDATE1 make-3.81/tests/scripts/variables/OUTOFDATE1 --- make.orig.cvs/tests/scripts/variables/OUTOFDATE1 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/tests/scripts/variables/OUTOFDATE1 2007-08-02 03:08:09.935461000 +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 -X exclude_diff -N -w make.orig.cvs/tests/scripts/variables/OUTOFDATE2 make-3.81/tests/scripts/variables/OUTOFDATE2 --- make.orig.cvs/tests/scripts/variables/OUTOFDATE2 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/tests/scripts/variables/OUTOFDATE2 2007-08-02 03:08:09.939460000 +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 -X exclude_diff -N -w make.orig.cvs/tests/work/features/changescope.mk make-3.81/tests/work/features/changescope.mk --- make.orig.cvs/tests/work/features/changescope.mk 1970-01-01 01:00:00.000000000 +0100 +++ make-3.81/tests/work/features/changescope.mk 2007-08-04 18:50:04.986465713 +0200 @@ -0,0 +1,15 @@ +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 + diff -u -r -X exclude_diff -N -w make.orig.cvs/variable.c make-3.81/variable.c --- make.orig.cvs/variable.c 2007-07-04 21:35:20.000000000 +0200 +++ make-3.81/variable.c 2007-08-04 20:58:36.778983000 +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') diff -u -r -X exclude_diff -N -w make.orig.cvs/variable.h make-3.81/variable.h --- make.orig.cvs/variable.h 2007-07-04 21:35:20.000000000 +0200 +++ make-3.81/variable.h 2007-08-04 20:59:23.086918000 +0200 @@ -66,6 +66,10 @@ 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 */ + enum variable_flavor flavor ENUM_BITFIELD (3); /* Variable flavor. */ enum variable_origin