From f95b6b3a9c6a0891c3f8558ca038aea8cdb5ea74 Mon Sep 17 00:00:00 2001 From: psmith Date: Mon, 28 Feb 2005 07:48:22 +0000 Subject: [PATCH] * New feature: -L option * New function: $(info ...) * Disallow $(eval ...) to create prereq relationships inside command scripts (caused core dumps) * Try to allow more tests to succeed in Windows/DOS by sanitizing CRLF and \ * Various bug fixes and code cleanups (see the ChangeLog entry) --- ChangeLog | 73 +++++++++++++++++---- NEWS | 20 +++++- configure.in | 15 ++++- dir.c | 12 ++-- doc/make.texi | 19 +++++- file.c | 12 ++++ filedef.h | 3 + function.c | 38 +++++++---- implicit.c | 2 +- main.c | 48 +++++++++----- make.h | 5 +- misc.c | 4 +- read.c | 9 ++- remake.c | 75 +++++++++++++++++++++- tests/ChangeLog | 30 +++++++-- tests/run_make_tests.pl | 13 ++-- tests/scripts/features/patspecific_vars | 2 +- tests/scripts/functions/eval | 10 +++ tests/scripts/misc/general4 | 5 +- tests/scripts/options/symlinks | 47 ++++++++++++++ tests/scripts/variables/MAKE | 4 +- .../variables/{MAKEFILE_LIST => MFILE_LIST} | 0 tests/test_driver.pl | 51 +++++++++------ variable.c | 2 +- w32/subproc/NMakefile | 1 + 25 files changed, 393 insertions(+), 107 deletions(-) create mode 100644 tests/scripts/options/symlinks rename tests/scripts/variables/{MAKEFILE_LIST => MFILE_LIST} (100%) diff --git a/ChangeLog b/ChangeLog index becece8..4696420 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,29 +1,68 @@ -Mon Feb 28 00:18:20 2005 Boris Kolpackov +2005-02-27 Paul D. Smith + + * misc.c (end_of_token): Make argument const. + * make.h: Update prototype. + + * function.c (abspath, func_realpath, func_abspath): Use + PATH_VAR() and GET_PATH_MAX instead of PATH_MAX. + * dir.c (downcase): Use PATH_VAR() instead of PATH_MAX. + * read.c (record_files): Ditto. + * variable.c (do_variable_definition): Ditto. + + * function.c (func_error): Create a new function $(info ...) that + simply prints the message to stdout with no extras. + (function_table_init): Add new function to the table. + * NEWS: Add $(info ...) reference. + * doc/make.texi (Make Control Functions): Document it. + + New feature: if the system supports symbolic links, and the user + provides the -L/--check-symlink-time flag, then use the latest + mtime between the symlink(s) and the target file. + + * configure.in (MAKE_SYMLINKS): Check for lstat() and + readlink(). If both are available, define MAKE_SYMLINKS. + * main.c: New variable: check_symlink_flag. + (usage): Add a line for -L/--check-symlink-times to the help string. + (switches): Add -L/--check-symlink-times command line argument. + (main): If MAKE_SYMLINKS is not defined but the user specified -L, + print a warning and disable it again. + * make.h: Declare check_symlink_flag. + * remake.c (name_mtime): If MAKE_SYMLINKS and check_symlink_flag, + if the file is a symlink then check each link in the chain and + choose the NEWEST mtime we find as the mtime for the file. The + newest mtime might be the file itself! + * NEWS: Add information about this new feature. + * doc/make.texi (Options Summary): Add -L/--check-symlink-times docs. + + Avoid core dumps described in Savannah bug # 12124: + + * file.c: New variable snapped_deps remember whether we've run + snap_deps(). + (snap_deps): Set it. + * filedef.h: Extern it. + * read.c (record_files): Check snapped_deps; if it's set then + we're trying to eval a new target/prerequisite relationship from + within a command script, which we don't support. Fatal. + +2005-02-28 Boris Kolpackov Implementation of the .DEFAULT_TARGET special variable. - * read.c (eval): If necessary, update default_target_name - when reading rules. - + * read.c (eval): If necessary, update default_target_name when + reading rules. * read.c (record_files): Update default_target_file if default_target_name has changed. - * main.c (default_target_name): Define. - * main.c (main): Enter .DEFAULT_TARGET as make variable. If default_target_name is set use default_target_file as a root target to make. - * filedef.h (default_target_name): Declare. - * dep.h (free_dep_chain): * misc.c (free_dep_chain): Change to operate on struct nameseq and change name to free_ns_chain. - * file.c (snap_deps): Update to use free_ns_chain. - -Sun Feb 27 22:03:36 2005 Boris Kolpackov +2005-02-27 Boris Kolpackov Implementation of the second expansion in explicit rules, static pattern rules and implicit rules. @@ -69,6 +108,12 @@ Sun Feb 27 22:03:36 2005 Boris Kolpackov * make.h (strip_whitespace): Declare. * function.c (strip_whitespace): Remove static specifier. +2005-02-26 Paul D. Smith + + * main.c (main): Check for ferror() when reading makefiles from stdin. + Apparently some shells in Windows don't close pipes properly and + require this check. + 2005-02-24 Jonathan Grant * configure.in: Add MinGW configuration options, and extra w32 code @@ -83,6 +128,12 @@ Sun Feb 27 22:03:36 2005 Boris Kolpackov * tests/run_make_tests.pl, tests/test_driver.pl: MSYS testing environment support. +2004-04-16 Dmitry V. Levin + + * function.c (func_shell): When initializing error_prefix, check + that reading file name is not null. This fixes long-standing + segfault in cases like "make 'a1=$(shell :)' 'a2:=$(a1)'". + 2005-02-09 Paul D. Smith * maintMakefile: Update the CVS download URL to simplify them. diff --git a/NEWS b/NEWS index 219ffb3..eb5a226 100644 --- a/NEWS +++ b/NEWS @@ -13,7 +13,14 @@ reporting bugs. Version 3.81beta2 * GNU make is ported to OS/2. - Port provided by Andreas Buening . + +* GNU make is ported to MinGW. + +* New command-line option: -L (--check-symlink-times). On systems that + support symbolic links, if this option is given then GNU make will + use the most recent modification time of any symbolic links that are + used to resolve target files. The default behavior remains as it + always has: use the modification time of the actual target file only. * All pattern-specific variables that match a given target are now used (previously only the first match was used). @@ -27,8 +34,13 @@ Version 3.81beta2 * Implemented a solution for the "thundering herd" problem with "-j -l". This version of GNU make uses an algorithm suggested by Thomas Riedl to track the number of jobs started in the - last second and adjust GNU make's view of the system's load average - accordingly. + last second and artificially adjust GNU make's view of the system's + load average accordingly. + +* New special variables available in this release: + - .DEFAULT_TARGET: Contains the name of the default target make will + use if no targets are provided on the command line. It can be set + to change the default target. * New functions available in this release: - $(lastword ...) returns the last word in the list. This gives @@ -39,6 +51,8 @@ Version 3.81beta2 - $(realpath ...) returns the canonical pathname for each path provided. The canonical pathname is the absolute pathname, with all symbolic links resolved as well. + - $(info ...) prints informative messages to stdout. No makefile + name or line number info, etc. is printed, just the message. * Changes made for POSIX compatibility: - Only touch targets (under -t) if they have at least one command. diff --git a/configure.in b/configure.in index f748c0d..0c8cee2 100644 --- a/configure.in +++ b/configure.in @@ -1,9 +1,9 @@ # Process this file with autoconf to produce a configure script. -AC_INIT([GNU make],[3.81beta2],[bug-make@gnu.org]) +AC_INIT([GNU make],[3.81rc1],[bug-make@gnu.org]) AC_PREREQ(2.59) -AC_REVISION([[$Id: configure.in,v 1.132 2005/02/26 01:41:48 psmith Exp $]]) +AC_REVISION([[$Id: configure.in,v 1.133 2005/02/28 07:48:22 psmith Exp $]]) # Autoconf setup AC_CONFIG_AUX_DIR(config) @@ -136,7 +136,8 @@ fi AC_CHECK_FUNCS( memcpy memmove strchr strdup mkstemp mktemp fdopen \ bsd_signal dup2 getcwd realpath sigsetmask sigaction \ getgroups seteuid setegid setlinebuf setreuid setregid \ - getrlimit setrlimit setvbuf pipe strerror strsignal) + getrlimit setrlimit setvbuf pipe strerror strsignal \ + lstat readlink) AC_FUNC_SETVBUF_REVERSED @@ -280,6 +281,14 @@ case "$ac_cv_func_pipe/$ac_cv_func_sigaction/$make_cv_sa_restart/$has_wait_nohan [Define this to enable job server support in GNU make.]);; esac +# if we have both lstat() and readlink() then we can support symlink +# timechecks. +case "$ac_cv_func_lstat/$ac_cv_func_readlink" in + yes/yes) + AC_DEFINE(MAKE_SYMLINKS, 1, + [Define this to enable symbolic link timestamp checking.]);; +esac + # Find the SCCS commands, so we can include them in our default rules. AC_CACHE_CHECK(for location of SCCS get command, make_cv_path_sccs_get, [ diff --git a/dir.c b/dir.c index 976e0b4..832b562 100644 --- a/dir.c +++ b/dir.c @@ -122,11 +122,7 @@ dosify (char *filename) static char * downcase (char *filename) { -#ifdef _AMIGA - static char new_filename[136]; -#else - static char new_filename[PATH_MAX]; -#endif + static PATH_VAR (new_filename); char *df; int i; @@ -1152,10 +1148,10 @@ read_dirstream (__ptr_t stream) } static void -ansi_free(void *p) +ansi_free (void *p) { - if (p) - free(p); + if (p) + free(p); } /* On 64 bit ReliantUNIX (5.44 and above) in LFS mode, stat() is actually a diff --git a/doc/make.texi b/doc/make.texi index 793d6e9..e655850 100644 --- a/doc/make.texi +++ b/doc/make.texi @@ -7,7 +7,7 @@ @c FSF publishers: format makebook.texi instead of using this file directly. -@set RCSID $Id: make.texi,v 1.24 2005/02/16 05:03:42 psmith Exp $ +@set RCSID $Id: make.texi,v 1.25 2005/02/28 07:48:23 psmith Exp $ @set EDITION 0.61 @set VERSION 3.80 @set UPDATED 23 Feb 2003 @@ -6750,6 +6750,13 @@ and the resulting message is displayed, but processing of the makefile continues. The result of the expansion of this function is the empty string. + +@item $(info @var{text}@dots{}) +@findex info +@cindex printing messages +This function does nothing more than print its (expanded) argument(s) +to standard output. No makefile name or line number is added. The +result of the expansion of this function is the empty string. @end table @node Running, Implicit Rules, Functions, Top @@ -7366,6 +7373,16 @@ other jobs running and the load average is at least @var{load} (a floating-point number). With no argument, removes a previous load limit. @xref{Parallel, ,Parallel Execution}. +@item -L +@cindex @code{-L} +@itemx --check-symlink-times +@cindex @code{--check-symlink-times} +On systems that support symbolic links, this option causes @code{make} +to consider the timestamps on any symbolic links in addition to the +timestamp on the file referenced by those links. When this option is +provided, the most recent timestamp among the file and the symbolic +links is taken as the modification time for this target file. + @item -n @cindex @code{-n} @itemx --just-print diff --git a/file.c b/file.c index 62a6a68..45bcaef 100644 --- a/file.c +++ b/file.c @@ -31,6 +31,15 @@ Boston, MA 02111-1307, USA. */ #include "hash.h" +/* Remember whether snap_deps has been invoked: we need this to be sure we + don't add new rules (via $(eval ...)) afterwards. In the future it would + be nice to support this, but it means we'd need to re-run snap_deps() or + at least its functionality... it might mean changing snap_deps() to be run + per-file, so we can invoke it after the eval... or remembering which files + in the hash have been snapped (a new boolean flag?) and having snap_deps() + only work on files which have not yet been snapped. */ +int snapped_deps = 0; + /* Hash table of files the makefile knows how to make. */ static unsigned long @@ -611,6 +620,9 @@ snap_deps (void) f = lookup_file (".NOTPARALLEL"); if (f != 0 && f->is_target) not_parallel = 1; + + /* Remember that we've done this. */ + snapped_deps = 1; } /* Set the `command_state' member of FILE and all its `also_make's. */ diff --git a/filedef.h b/filedef.h index 2822e8a..ae45f89 100644 --- a/filedef.h +++ b/filedef.h @@ -198,3 +198,6 @@ extern FILE_TIMESTAMP f_mtime PARAMS ((struct file *file, int search)); #define check_renamed(file) \ while ((file)->renamed != 0) (file) = (file)->renamed /* No ; here. */ + +/* Have we snapped deps yet? */ +extern int snapped_deps; diff --git a/function.c b/function.c index b344e66..66d23fd 100644 --- a/function.c +++ b/function.c @@ -1082,12 +1082,23 @@ func_error (char *o, char **argv, const char *funcname) } strcpy (p, *argvp); - if (*funcname == 'e') - fatal (reading_file, "%s", msg); + switch (*funcname) { + case 'e': + fatal (reading_file, "%s", msg); - /* The warning function expands to the empty string. */ - error (reading_file, "%s", msg); + case 'w': + error (reading_file, "%s", msg); + break; + case 'i': + printf ("%s\n", msg); + break; + + default: + fatal (reading_file, "Internal error: func_error: '%s'", funcname); + } + + /* The warning function expands to the empty string. */ return o; } @@ -1472,7 +1483,7 @@ func_shell (char *o, char **argv, const char *funcname UNUSED) envp = environ; /* For error messages. */ - if (reading_file != 0) + if (reading_file && reading_file->filenm) { error_prefix = (char *) alloca (strlen (reading_file->filenm)+11+4); sprintf (error_prefix, @@ -1752,7 +1763,7 @@ abspath (const char *name, char *apath) if (name[0] == '\0' || apath == NULL) return NULL; - apath_limit = apath + PATH_MAX; + apath_limit = apath + GET_PATH_MAX; if (name[0] != '/') { @@ -1826,13 +1837,12 @@ func_realpath (char *o, char **argv, const char *funcname UNUSED) char *path = 0; int doneany = 0; unsigned int len = 0; - - char in[PATH_MAX]; - char out[PATH_MAX]; + PATH_VAR (in); + PATH_VAR (out); while ((path = find_next_token (&p, &len)) != 0) { - if (len < PATH_MAX) + if (len < GET_PATH_MAX) { strncpy (in, path, len); in[len] = '\0'; @@ -1868,13 +1878,12 @@ func_abspath (char *o, char **argv, const char *funcname UNUSED) char *path = 0; int doneany = 0; unsigned int len = 0; - - char in[PATH_MAX]; - char out[PATH_MAX]; + PATH_VAR (in); + PATH_VAR (out); while ((path = find_next_token (&p, &len)) != 0) { - if (len < PATH_MAX) + if (len < GET_PATH_MAX) { strncpy (in, path, len); in[len] = '\0'; @@ -1939,6 +1948,7 @@ static struct function_table_entry function_table_init[] = { STRING_SIZE_TUPLE("origin"), 0, 1, 1, func_origin}, { STRING_SIZE_TUPLE("foreach"), 3, 3, 0, func_foreach}, { STRING_SIZE_TUPLE("call"), 1, 0, 1, func_call}, + { STRING_SIZE_TUPLE("info"), 0, 1, 1, func_error}, { STRING_SIZE_TUPLE("error"), 0, 1, 1, func_error}, { STRING_SIZE_TUPLE("warning"), 0, 1, 1, func_error}, { STRING_SIZE_TUPLE("if"), 2, 3, 0, func_if}, diff --git a/implicit.c b/implicit.c index 7c1f4b7..b844419 100644 --- a/implicit.c +++ b/implicit.c @@ -1,5 +1,5 @@ /* Implicit rule searching for GNU Make. -Copyright (C) 1988,89,90,91,92,93,94,97,2000 Free Software Foundation, Inc. +Copyright (C) 1988,1989,1990,1991,1992,1993,1994,1997,2000,2004,2005 Free Software Foundation, Inc. This file is part of GNU Make. GNU Make is free software; you can redistribute it and/or modify diff --git a/main.c b/main.c index 1035b8c..c20be9b 100644 --- a/main.c +++ b/main.c @@ -193,6 +193,10 @@ int no_builtin_variables_flag = 0; int keep_going_flag; int default_keep_going_flag = 0; +/* Nonzero means check symlink mtimes. */ + +int check_symlink_flag = 0; + /* Nonzero means print directory before starting and when done (-w). */ int print_directory_flag = 0; @@ -316,6 +320,8 @@ static const char *const usage[] = -l [N], --load-average[=N], --max-load[=N]\n\ Don't start multiple jobs unless load is below N.\n"), N_("\ + -L, --check-symlink-times Use the latest mtime between symlinks and target.\n"), + N_("\ -n, --just-print, --dry-run, --recon\n\ Don't actually run any commands; just print them.\n"), N_("\ @@ -363,50 +369,52 @@ static const struct command_switch switches[] = { 'D', flag, (char *) &suspend_flag, 1, 1, 0, 0, 0, "suspend-for-debug" }, #endif { 'e', flag, (char *) &env_overrides, 1, 1, 0, 0, 0, - "environment-overrides", }, + "environment-overrides", }, { 'f', string, (char *) &makefiles, 0, 0, 0, 0, 0, "file" }, { 'h', flag, (char *) &print_usage_flag, 0, 0, 0, 0, 0, "help" }, { 'i', flag, (char *) &ignore_errors_flag, 1, 1, 0, 0, 0, - "ignore-errors" }, + "ignore-errors" }, { 'I', string, (char *) &include_directories, 1, 1, 0, 0, 0, - "include-dir" }, + "include-dir" }, { 'j', positive_int, (char *) &job_slots, 1, 1, 0, (char *) &inf_jobs, - (char *) &default_job_slots, "jobs" }, + (char *) &default_job_slots, "jobs" }, { CHAR_MAX+2, string, (char *) &jobserver_fds, 1, 1, 0, 0, 0, - "jobserver-fds" }, + "jobserver-fds" }, { 'k', flag, (char *) &keep_going_flag, 1, 1, 0, 0, - (char *) &default_keep_going_flag, "keep-going" }, + (char *) &default_keep_going_flag, "keep-going" }, #ifndef NO_FLOAT { 'l', floating, (char *) &max_load_average, 1, 1, 0, - (char *) &default_load_average, (char *) &default_load_average, - "load-average" }, + (char *) &default_load_average, (char *) &default_load_average, + "load-average" }, #else { 'l', positive_int, (char *) &max_load_average, 1, 1, 0, - (char *) &default_load_average, (char *) &default_load_average, - "load-average" }, + (char *) &default_load_average, (char *) &default_load_average, + "load-average" }, #endif + { 'L', flag, (char *) &check_symlink_flag, 1, 1, 0, 0, 0, + "check-symlink-times" }, { 'm', ignore, 0, 0, 0, 0, 0, 0, 0 }, { 'n', flag, (char *) &just_print_flag, 1, 1, 1, 0, 0, "just-print" }, { 'o', string, (char *) &old_files, 0, 0, 0, 0, 0, "old-file" }, { 'p', flag, (char *) &print_data_base_flag, 1, 1, 0, 0, 0, - "print-data-base" }, + "print-data-base" }, { 'q', flag, (char *) &question_flag, 1, 1, 1, 0, 0, "question" }, { 'r', flag, (char *) &no_builtin_rules_flag, 1, 1, 0, 0, 0, "no-builtin-rules" }, { 'R', flag, (char *) &no_builtin_variables_flag, 1, 1, 0, 0, 0, - "no-builtin-variables" }, + "no-builtin-variables" }, { 's', flag, (char *) &silent_flag, 1, 1, 0, 0, 0, "silent" }, { 'S', flag_off, (char *) &keep_going_flag, 1, 1, 0, 0, (char *) &default_keep_going_flag, "no-keep-going" }, { 't', flag, (char *) &touch_flag, 1, 1, 1, 0, 0, "touch" }, { 'v', flag, (char *) &print_version_flag, 1, 1, 0, 0, 0, "version" }, { 'w', flag, (char *) &print_directory_flag, 1, 1, 0, 0, 0, - "print-directory" }, + "print-directory" }, { CHAR_MAX+3, flag, (char *) &inhibit_print_directory_flag, 1, 1, 0, 0, 0, - "no-print-directory" }, + "no-print-directory" }, { 'W', string, (char *) &new_files, 0, 0, 0, 0, 0, "what-if" }, { CHAR_MAX+4, flag, (char *) &warn_undefined_variables_flag, 1, 1, 0, 0, 0, - "warn-undefined-variables" }, + "warn-undefined-variables" }, { 0 } }; @@ -1461,7 +1469,7 @@ main (int argc, char **argv, char **envp) outfile = open_tmpfile (&stdin_nm, template); if (outfile == 0) pfatal_with_name (_("fopen (temporary file)")); - while (!feof (stdin)) + while (!feof (stdin) && ! ferror (stdin)) { char buf[2048]; unsigned int n = fread (buf, 1, sizeof (buf), stdin); @@ -1702,6 +1710,14 @@ main (int argc, char **argv, char **envp) } #endif +#ifndef MAKE_SYMLINKS + if (check_symlink_flag) + { + error (NILF, _("Symbolic links not supported: disabling -L.")); + check_symlink_flag = 0; + } +#endif + /* Set up MAKEFLAGS and MFLAGS again, so they will be right. */ define_makeflags (1, 0); diff --git a/make.h b/make.h index e9ea18a..5696965 100644 --- a/make.h +++ b/make.h @@ -366,7 +366,6 @@ extern int strcmpi (const char *,const char *); extern void sync_Path_environment(void); extern int kill(int pid, int sig); -extern int safe_stat(char *file, struct stat *sb); extern char *end_of_token_w32(char *s, char stopchar); extern int find_and_set_default_shell(char *token); @@ -420,7 +419,7 @@ extern char *xrealloc PARAMS ((char *, unsigned int)); extern char *xstrdup PARAMS ((const char *)); extern char *find_next_token PARAMS ((char **, unsigned int *)); extern char *next_token PARAMS ((const char *)); -extern char *end_of_token PARAMS ((char *)); +extern char *end_of_token PARAMS ((const char *)); extern void collapse_continuations PARAMS ((char *)); extern void remove_comments PARAMS((char *)); extern char *lindex PARAMS ((const char *, const char *, int)); @@ -496,7 +495,7 @@ extern char **environ; extern int just_print_flag, silent_flag, ignore_errors_flag, keep_going_flag; extern int print_data_base_flag, question_flag, touch_flag, always_make_flag; extern int env_overrides, no_builtin_rules_flag, no_builtin_variables_flag; -extern int print_version_flag, print_directory_flag; +extern int print_version_flag, print_directory_flag, check_symlink_flag; extern int warn_undefined_variables_flag, posix_pedantic, not_parallel; extern int clock_skew_detected, rebuilding_makefiles; diff --git a/misc.c b/misc.c index 53c1923..63936f7 100644 --- a/misc.c +++ b/misc.c @@ -432,11 +432,11 @@ lindex (const char *s, const char *limit, int c) /* Return the address of the first whitespace or null in the string S. */ char * -end_of_token (char *s) +end_of_token (const char *s) { while (*s != '\0' && !isblank ((unsigned char)*s)) ++s; - return s; + return (char *)s; } #ifdef WINDOWS32 diff --git a/read.c b/read.c index 0862b48..d235c6a 100644 --- a/read.c +++ b/read.c @@ -1822,6 +1822,13 @@ record_files (struct nameseq *filenames, char *pattern, char *pattern_percent, char **targets = 0, **target_percents = 0; struct commands *cmds; + /* If we've already snapped deps, that means we're in an eval being + resolved after the makefiles have been read in. We can't add more rules + at this time, since they won't get snapped and we'll get core dumps. + See Savannah bug # 12124. */ + if (snapped_deps) + fatal (flocp, _("prerequisites cannot be defined in command scripts")); + if (commands_idx > 0) { cmds = (struct commands *) xmalloc (sizeof (struct commands)); @@ -1910,7 +1917,7 @@ record_files (struct nameseq *filenames, char *pattern, char *pattern_percent, if (find_percent (this->name) != 0) { - char stem[PATH_MAX]; + PATH_VAR (stem); char *o; char *buffer = variable_expand (""); diff --git a/remake.c b/remake.c index 70acad3..e1def64 100644 --- a/remake.c +++ b/remake.c @@ -1313,9 +1313,16 @@ f_mtime (struct file *file, int search) /* Return the mtime of the file or archive-member reference NAME. */ +/* First, we check with stat(). If the file does not exist, then we return + NONEXISTENT_MTIME. If it does, and the symlink check flag is set, then + examine each indirection of the symlink and find the newest mtime. + This causes one duplicate stat() when -L is being used, but the code is + much cleaner. */ + static FILE_TIMESTAMP name_mtime (char *name) { + FILE_TIMESTAMP mtime; struct stat st; int e; @@ -1326,8 +1333,74 @@ name_mtime (char *name) perror_with_name ("stat:", name); return NONEXISTENT_MTIME; } + mtime = FILE_TIMESTAMP_STAT_MODTIME (name, st); + +#ifdef MAKE_SYMLINKS +#ifndef S_ISLNK +# define S_ISLNK(_m) (((_m)&S_IFMT)==S_IFLNK) +#endif + if (check_symlink_flag) + { + PATH_VAR (lpath); + + /* Check each symbolic link segment (if any). Find the latest mtime + amongst all of them (and the target file of course). + Note that we have already successfully dereferenced all the links + above. So, if we run into any error trying to lstat(), or + readlink(), or whatever, something bizarre-o happened. Just give up + and use whatever mtime we've already computed at that point. */ + strcpy (lpath, name); + while (1) + { + FILE_TIMESTAMP ltime; + PATH_VAR (lbuf); + long llen; + char *p; + + EINTRLOOP (e, lstat (lpath, &st)); + if (e) + { + /* Eh? Just take what we have. */ + perror_with_name ("lstat: ", lpath); + break; + } + + /* If this is not a symlink, we're done (we started with the real + file's mtime so we don't need to test it again). */ + if (!S_ISLNK (st.st_mode)) + break; - return FILE_TIMESTAMP_STAT_MODTIME (name, st); + /* If this mtime is newer than what we had, keep the new one. */ + ltime = FILE_TIMESTAMP_STAT_MODTIME (lpath, st); + if (ltime > mtime) + mtime = ltime; + + /* Set up to check the file pointed to by this link. */ + EINTRLOOP (llen, readlink (lpath, lbuf, GET_PATH_MAX)); + if (llen < 0) + { + /* Eh? Just take what we have. */ + perror_with_name ("readlink: ", lpath); + break; + } + lbuf[llen] = '\0'; + + /* If the target is fully-qualified or the source is just a + filename, then the new path is the target. Otherwise it's the + source directory plus the target. */ + if (lbuf[0] == '/' || (p = strrchr (lpath, '/')) == NULL) + strcpy (lpath, lbuf); + else if ((p - lpath) + llen + 2 > GET_PATH_MAX) + /* Eh? Path too long! Again, just go with what we have. */ + break; + else + /* Create the next step in the symlink chain. */ + strcpy (p+1, lbuf); + } + } +#endif + + return mtime; } diff --git a/tests/ChangeLog b/tests/ChangeLog index 462c2e6..41c9b34 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,24 +1,42 @@ -Mon Feb 28 00:31:14 2005 Boris Kolpackov +2005-02-28 Paul D. Smith + + * scripts/options/symlinks: New file to test checking of symlink + timestamps. Can't use filename dash-L because it conflicts with + dash-l on case-insensitive filesystems. + + * scripts/variables/MAKEFILE_LIST, scripts/variables/MFILE_LIST: + Rename MAKEFILE_LIST test to MFILE_LIST, for systems that need 8.3 + unique filenames. + +2005-02-28 Boris Kolpackov * scripts/variables/DEFAULT_TARGET: Test the .DEFAULT_TARGET special variable. -Sun Feb 27 23:33:32 2005 Boris Kolpackov +2005-02-27 Boris Kolpackov * scripts/features/se_explicit: Test the second expansion in explicit rules. - * scripts/features/se_implicit: Test the second expansion in implicit rules. - * scripts/features/se_statpat: Test the second expansion in static pattern rules. - - * tests/scripts/variables/automatic: Fix to work with the second + * scripts/variables/automatic: Fix to work with the second expansion. * scripts/misc/general4: Add a test for bug #12091. +2005-02-27 Paul D. Smith + + * scripts/functions/eval: Check that eval of targets within + command scripts fails. See Savannah bug # 12124. + +2005-02-26 Paul D. Smith + + * test_driver.pl (compare_output): If a basic comparison of the + log and answer doesn't match, try harder: change all backslashes + to slashes and all CRLF to LF. This helps on DOS/Windows systems. + 2005-02-09 Paul D. Smith * scripts/features/recursion: Test command line variable settings: diff --git a/tests/run_make_tests.pl b/tests/run_make_tests.pl index 5276d29..ca711b2 100755 --- a/tests/run_make_tests.pl +++ b/tests/run_make_tests.pl @@ -38,7 +38,7 @@ sub valid_option return 1; } -# This doesn't work--it _should_! Someone needs to fix this badly. +# This doesn't work--it _should_! Someone badly needs to fix this. # # elsif ($option =~ /^-work([-_]?dir)?$/) # { @@ -241,20 +241,15 @@ sub set_more_defaults # On DOS/Windows system the filesystem apparently can't track # timestamps with second granularity (!!). Change the sleep time # needed to force a file to be considered "old". - # $wtime = $port_type eq 'UNIX' ? 1 : $port_type eq 'OS/2' ? 2 : 4; print "Port type: $port_type\n" if $debug; print "Make path: $make_path\n" if $debug; # Find the full pathname of Make. For DOS systems this is more - # complicated, so we ask make itself. The following shell code does not - # work on W32 (MinGW/MSYS) - - if ($port_type ne 'W32') { - $make_path = `sh -c 'echo "all:;\@echo \\\$(MAKE)" | $make_path -f-'`; - chop $make_path; - } + # complicated, so we ask make itself. + $make_path = `sh -c 'echo "all:;\@echo \\\$(MAKE)" | $make_path -f-'`; + chop $make_path; print "Make\t= `$make_path'\n" if $debug; $string = `$make_path -v -f /dev/null 2> /dev/null`; diff --git a/tests/scripts/features/patspecific_vars b/tests/scripts/features/patspecific_vars index 31359cf..9e98b43 100644 --- a/tests/scripts/features/patspecific_vars +++ b/tests/scripts/features/patspecific_vars @@ -67,7 +67,7 @@ run_make_test(' /%: export foo := foo /bar: - @test "$(foo)" == "$$foo" + @test "$(foo)" = "$$foo" ', '', ''); diff --git a/tests/scripts/functions/eval b/tests/scripts/functions/eval index c69a110..bc43053 100644 --- a/tests/scripts/functions/eval +++ b/tests/scripts/functions/eval @@ -158,4 +158,14 @@ $(eval $(FOO)) ', '', 'hello world'); + +# We don't allow new target/prerequisite relationships to be defined within a +# command script, because these are evaluated after snap_deps() and that +# causes lots of problems (like core dumps!) +# See Savannah bug # 12124. + +run_make_test('deps: ; $(eval deps: foo)', '', + '#MAKEFILE#:1: *** prerequisites cannot be defined in command scripts. Stop.', + 512); + 1; diff --git a/tests/scripts/misc/general4 b/tests/scripts/misc/general4 index 3b4595f..63320e2 100644 --- a/tests/scripts/misc/general4 +++ b/tests/scripts/misc/general4 @@ -24,13 +24,10 @@ close(MAKEFILE); $answer = "mkdir -p dir/subdir\ntouch dir/subdir/file.b\ncp dir/subdir/file.b dir/subdir/file.a\n"; &compare_output($answer,&get_logfile(1)); - # Test implicit rules &touch('foo.c'); -run_make_test(' -foo: foo.o -', +run_make_test('foo: foo.o', 'CC="@echo cc" OUTPUT_OPTION=', 'cc -c foo.c cc foo.o -o foo'); diff --git a/tests/scripts/options/symlinks b/tests/scripts/options/symlinks new file mode 100644 index 0000000..4dcc67a --- /dev/null +++ b/tests/scripts/options/symlinks @@ -0,0 +1,47 @@ +# -*-perl-*- + +$description = "Test the -L option."; + +$details = "Verify that symlink handling with and without -L works properly."; + +# Only run these tests if the system sypports symlinks +if (eval { symlink("",""); 1 }) { + + # Set up a symlink sym -> dep + # We'll make both dep and targ older than sym + $pwd =~ m%/([^/]+)$%; + $dirnm = $1; + &utouch(-10, 'dep'); + &utouch(-5, 'targ'); + symlink("../$dirnm/dep", 'sym'); + + # Without -L, nothing should happen + # With -L, it should update targ + run_make_test('targ: sym ; @echo make $@ from $<', '', + "#MAKE#: `targ' is up to date."); + run_make_test(undef, '-L', "make targ from sym"); + + # Now update dep; in all cases targ should be out of date. + &touch('dep'); + run_make_test(undef, '', "make targ from sym"); + run_make_test(undef, '-L', "make targ from sym"); + + # Now update targ; in all cases targ should be up to date. + &touch('targ'); + run_make_test(undef, '', "#MAKE#: `targ' is up to date."); + run_make_test(undef, '-L', "#MAKE#: `targ' is up to date."); + + # Add in a new link between sym and dep. Be sure it's newer than targ. + sleep(1); + rename('dep', 'dep1'); + symlink('dep1', 'dep'); + + # Without -L, nothing should happen + # With -L, it should update targ + run_make_test(undef, '', "#MAKE#: `targ' is up to date."); + run_make_test(undef, '-L', "make targ from sym"); + + rmfiles('targ', 'dep', 'sym', 'dep1'); +} + +1; diff --git a/tests/scripts/variables/MAKE b/tests/scripts/variables/MAKE index 7c4cf0a..079c57e 100644 --- a/tests/scripts/variables/MAKE +++ b/tests/scripts/variables/MAKE @@ -1,3 +1,5 @@ +# -*-perl-*- + $description = "The following test creates a makefile to test MAKE \n" ."(very generic)"; @@ -26,7 +28,7 @@ $answer = "$mkpath\n$mkpath -f $makefile foo\n" &run_make_with_options($makefile,"",&get_logfile,0); -&delete("foo"); +&rmfiles("foo"); # COMPARE RESULTS &compare_output($answer,&get_logfile(1)); diff --git a/tests/scripts/variables/MAKEFILE_LIST b/tests/scripts/variables/MFILE_LIST similarity index 100% rename from tests/scripts/variables/MAKEFILE_LIST rename to tests/scripts/variables/MFILE_LIST diff --git a/tests/test_driver.pl b/tests/test_driver.pl index fcefceb..62dbe0f 100644 --- a/tests/test_driver.pl +++ b/tests/test_driver.pl @@ -12,7 +12,7 @@ # this routine controls the whole mess; each test suite sets up a few # variables and then calls &toplevel, which does all the real work. -# $Id: test_driver.pl,v 1.13 2004/09/21 05:39:04 psmith Exp $ +# $Id: test_driver.pl,v 1.14 2005/02/28 07:48:23 psmith Exp $ # The number of test categories we've run @@ -451,13 +451,13 @@ sub run_each_test $status = "ok ($tests_passed passed)"; for ($i = $num_of_tmpfiles; $i; $i--) { - &delete ($tmp_filename . &num_suffix ($i) ); + &rmfiles ($tmp_filename . &num_suffix ($i) ); } for ($i = $num_of_logfiles ? $num_of_logfiles : 1; $i; $i--) { - &delete ($log_filename . &num_suffix ($i) ); - &delete ($base_filename . &num_suffix ($i) ); + &rmfiles ($log_filename . &num_suffix ($i) ); + &rmfiles ($base_filename . &num_suffix ($i) ); } } elsif ($code > 0) { @@ -500,7 +500,7 @@ sub run_each_test # If the keep flag is not set, this subroutine deletes all filenames that # are sent to it. -sub delete +sub rmfiles { local(@files) = @_; @@ -611,7 +611,7 @@ sub error sub compare_output { local($answer,$logfile) = @_; - local($slurp); + local($slurp, $answer_matched) = ('', 0); print "Comparing Output ........ " if $debug; @@ -624,14 +624,29 @@ sub compare_output ++$tests_run; - if ($slurp eq $answer && $test_passed) + if ($slurp eq $answer) { + $answer_matched = 1; + } else { + # See if it is a slash or CRLF problem + local ($answer_mod) = $answer; + + $answer_mod =~ tr,\\,/,; + $answer_mod =~ s,\r\n,\n,gs; + + $slurp =~ tr,\\,/,; + $slurp =~ s,\r\n,\n,gs; + + $answer_matched = ($slurp eq $answer_mod); + } + + if ($answer_matched && $test_passed) { print "ok\n" if $debug; ++$tests_passed; return 1; } - if ($slurp ne $answer) { + if (! $answer_matched) { print "DIFFERENT OUTPUT\n" if $debug; &create_file (&get_basefile, $answer); @@ -639,9 +654,9 @@ sub compare_output print "\nCreating Difference File ...\n" if $debug; # Create the difference file + local($command) = "diff -c " . &get_basefile . " " . $logfile; &run_command_with_output(&get_difffile,$command); - } $suite_passed = 0; @@ -729,15 +744,11 @@ sub run_command { local ($code); - if ($debug) - { - print "\nrun_command: @_\n"; - $code = system @_; - print "run_command: \"@_\" returned $code.\n"; - return $code; - } + print "\nrun_command: @_\n" if $debug; + $code = system @_; + print "run_command: \"@_\" returned $code.\n" if $debug; - return system @_; + return $code; } # run one command (passed as a list of arg 0 - n, with arg 0 being the @@ -753,10 +764,8 @@ sub run_command_with_output &attach_default_output ($filename); $code = system @_; &detach_default_output; - if ($debug) - { - print "run_command_with_output: \"@_\" returned $code.\n"; - } + + print "run_command_with_output: '@_' returned $code.\n" if $debug; return $code; } diff --git a/variable.c b/variable.c index 495aef4..4c38316 100644 --- a/variable.c +++ b/variable.c @@ -1048,7 +1048,7 @@ do_variable_definition (const struct floc *flocp, const char *varname, if ((origin == o_file || origin == o_override) && strcmp (varname, "SHELL") == 0) { - char shellpath[PATH_MAX]; + PATH_VAR (shellpath); extern char * __dosexec_find_on_path (const char *, char *[], char *); /* See if we can find "/bin/sh.exe", "/bin/sh.com", etc. */ diff --git a/w32/subproc/NMakefile b/w32/subproc/NMakefile index 66afe65..d14fcc4 100644 --- a/w32/subproc/NMakefile +++ b/w32/subproc/NMakefile @@ -23,6 +23,7 @@ # LIB = lib CC = cl +MAKE = nmake OUTDIR=. MAKEFILE=NMakefile -- 2.11.4.GIT