diff --git a/ChangeLog b/ChangeLog index f4a897e8..cc9ed4ee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,15 @@ 2013-09-21 Paul Smith + Output generated while reading makefiles should be synced. + + * main.c (make_sync): Define a context for syncing while reading + makefiles and other top-level operations. + (main): If we request syncing, enable it while we are parsing + options, reading makefiles, etc. to capture that output. Just + before we start to run rules, dump the output if any. + (die): Dump any output we've been syncing before we die + * output.h (OUTPUT_SET): Disable output_context if not syncout. + Stderr generated from shell functions in recipes should be synced. * job.h (FD_STDIN, FD_STDOUT, FD_STDERR): Create new macros to diff --git a/main.c b/main.c index bd8b478a..f0a7174d 100644 --- a/main.c +++ b/main.c @@ -550,6 +550,11 @@ int clock_skew_detected; #endif unsigned short stopchar_map[UCHAR_MAX + 1] = {0}; +/* If output-sync is enabled we'll collect all the output generated due to + options, while reading makefiles, etc. */ + +struct output make_sync; + /* Mask of signals that are being caught with fatal_error_signal. */ @@ -1041,6 +1046,7 @@ main (int argc, char **argv, char **envp) struct dep *read_files; PATH_VAR (current_directory); unsigned int restarts = 0; + unsigned int syncing = 0; #ifdef WINDOWS32 char *unix_path = NULL; char *windows32_path = NULL; @@ -1052,6 +1058,8 @@ main (int argc, char **argv, char **envp) no_default_sh_exe = 1; #endif + output_init (&make_sync); + initialize_stopchar_map(); #ifdef SET_STACK_SIZE @@ -1390,6 +1398,13 @@ main (int argc, char **argv, char **envp) decode_env_switches (STRING_SIZE_TUPLE ("GNUMAKEFLAGS")); decode_env_switches (STRING_SIZE_TUPLE ("MAKEFLAGS")); + /* In output sync mode we need to sync any output generated by reading the + makefiles, such as in $(info ...) or stderr from $(shell ...) etc. */ + + syncing = make_sync.syncout = (output_sync == OUTPUT_SYNC_LINE + || output_sync == OUTPUT_SYNC_TARGET); + OUTPUT_SET (&make_sync); + #if 0 /* People write things like: MFLAGS="CC=gcc -pipe" "CFLAGS=-g" @@ -1399,6 +1414,16 @@ main (int argc, char **argv, char **envp) decode_switches (argc, argv, 0); + /* Reset in case the switches changed our minds. */ + syncing = (output_sync == OUTPUT_SYNC_LINE + || output_sync == OUTPUT_SYNC_TARGET); + + if (make_sync.syncout && ! syncing) + output_close (&make_sync); + + make_sync.syncout = syncing; + OUTPUT_SET (&make_sync); + /* Figure out the level of recursion. */ { struct variable *v = lookup_variable (STRING_SIZE_TUPLE (MAKELEVEL_NAME)); @@ -1907,6 +1932,16 @@ main (int argc, char **argv, char **envp) decode_env_switches (STRING_SIZE_TUPLE ("MFLAGS")); #endif + /* Reset in case the switches changed our minds. */ + syncing = (output_sync == OUTPUT_SYNC_LINE + || output_sync == OUTPUT_SYNC_TARGET); + + if (make_sync.syncout && ! syncing) + output_close (&make_sync); + + make_sync.syncout = syncing; + OUTPUT_SET (&make_sync); + /* If we've disabled builtin rules, get rid of them. */ if (no_builtin_rules_flag && ! old_builtin_rules_flag) { @@ -2077,6 +2112,11 @@ main (int argc, char **argv, char **envp) /* Initialize the remote job module. */ remote_setup (); + /* Dump any output we've collected. */ + + OUTPUT_UNSET (); + output_close (&make_sync); + if (read_files != 0) { /* Update any makefiles if necessary. */ @@ -3358,6 +3398,13 @@ die (int status) clean_jobserver (status); + if (output_context) + { + assert (output_context == &make_sync); + OUTPUT_UNSET (); + output_close (&make_sync); + } + output_close (NULL); /* Try to move back to the original directory. This is essential on diff --git a/output.h b/output.h index 1f0e2d7c..5fef436c 100644 --- a/output.h +++ b/output.h @@ -24,7 +24,7 @@ struct output extern struct output *output_context; extern unsigned int stdio_traced; -#define OUTPUT_SET(_new) do{ if ((_new)->syncout) output_context = (_new); }while(0) +#define OUTPUT_SET(_new) do{ output_context = (_new)->syncout ? (_new) : NULL; }while(0) #define OUTPUT_UNSET() do{ output_context = NULL; }while(0) #define OUTPUT_TRACED() do{ stdio_traced = 1; }while(0) diff --git a/tests/ChangeLog b/tests/ChangeLog index e0e9475e..cbcaa036 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,7 +1,10 @@ 2013-09-21 Paul Smith * scripts/features/output-sync: Test shell functions writing to - stderr in recipes: ensure it's captured via output-sync. + stderr in recipes: ensure it's captured via output-sync. Test + output generated while reading makefiles and make sure it's + captured via output-sync. Make sure that fatal errors dump the + output so it's not lost. * scripts/options/dash-w: Add a test for -w flag. diff --git a/tests/scripts/features/output-sync b/tests/scripts/features/output-sync index e09505fd..3bb99976 100644 --- a/tests/scripts/features/output-sync +++ b/tests/scripts/features/output-sync @@ -267,5 +267,65 @@ all: ; @: $(shell echo foo 1>&2) !, '-w -Oline', "#MAKE#: Entering directory '#PWD#'\nfoo\n#MAKE#: Leaving directory '#PWD#'\n"); +# Ensure that output generated while parsing makefiles is synced +# when appropriate. +run_make_test(q! +$(shell echo foo 1>&2) +all: ; echo bar +!, + '-s -w -Otarget', "#MAKE#: Entering directory '#PWD#'\nfoo\n#MAKE#: Leaving directory '#PWD#'\n#MAKE#: Entering directory '#PWD#'\nbar\n#MAKE#: Leaving directory '#PWD#'\n"); + +# Test recursion +$m1 = get_tmpfile(); +$m2 = get_tmpfile(); + +open(M1, "> $m1"); +print M1 <<'EOF'; +$(shell echo d1 stderr 1>&2) +$(info d1 stdout) +all:; @: +EOF +close(M1); + +open(M2, "> $m2"); +print M2 <<'EOF'; +$(shell echo d2 stderr 1>&2) +$(info d2 stdout) +all:; @: +# Force an ordering on the output +$(shell sleep 1) +EOF +close(M2); + +run_make_test(qq! +all: t1 t2 +t1: ; \@\$(MAKE) -f $m1 +t2: ; \@\$(MAKE) -f $m2 +!, + "-j -Oline", "#MAKE#[1]: Entering directory '#PWD#'\nd1 stderr\nd1 stdout\n#MAKE#[1]: Leaving directory '#PWD#'\n#MAKE#[1]: Entering directory '#PWD#'\nd2 stderr\nd2 stdout\n#MAKE#[1]: Leaving directory '#PWD#'\n"); + +rmfiles($m1, $m2); + +# Ensure that output generated while parsing makefiles is synced +# when appropriate. +$m1 = get_tmpfile(); + +open(M1, "> $m1"); +print M1 <<'EOF'; +$(shell echo d1 stderr 1>&2) +$(info d1 stdout) +$(error d1 failed) +all:; @: +EOF +close(M1); + +run_make_test(qq! +all: t1 +t1: ; -\@\$(MAKE) -f $m1 +!, + "-j -Oline", "#MAKE#[1]: Entering directory '#PWD#'\nd1 stderr\nd1 stdout\n$m1:3: *** d1 failed. Stop.\n#MAKE#[1]: Leaving directory '#PWD#'\n#MAKEFILE#:3: recipe for target 't1' failed\n#MAKE#: [t1] Error 2 (ignored)\n"); + +rmfiles($m1); + # This tells the test driver that the perl test script executed properly. 1;