Enhance the output sync mode.
authorPaul Smith <psmith@gnu.org>
Thu, 12 Sep 2013 08:07:52 +0000 (04:07 -0400)
committerPaul Smith <psmith@gnu.org>
Thu, 12 Sep 2013 08:07:52 +0000 (04:07 -0400)
Create a new file, output.c, and collect functions that generate output there.
We introduce a new global context specifying where output should go (to stdout
or to a sync file), and the lowest level output generator chooses where to
write output based on that context.

This allows us to set the context globally, and all operations that write
output (including functions like $(info ...) etc.) will use it.

Removed the "--trace=dir" capability.  It was too confusing.  If you have
directory tracking enabled then output sync will print the enter/leave message
for each synchronized block.  If you don't want that, disable directory
tracking.

27 files changed:
ChangeLog
Makefile.am
NEWS
NMakefile.template
SMakefile.template
build_w32.bat
doc/make.texi
dosbuild.bat
function.c
job.c
job.h
main.c
make.1
make.lnk
make_msvc_net2003.vcproj
makefile.vms
makeint.h
misc.c
output.c [new file with mode: 0644]
output.h [new file with mode: 0644]
po/POTFILES.in
remote-cstms.c
tests/ChangeLog
tests/scripts/features/output-sync
tests/scripts/variables/GNUMAKEFLAGS
tests/scripts/variables/MAKEFLAGS
tests/scripts/variables/MAKE_RESTARTS

index f8ab2cac91efc77f388883e20ad914ce1d93ecaf..c4319accaff6de05fd170208a2ab993a7f768762 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,44 @@
+2013-09-12  Paul Smith  <psmith@gnu.org>
+
+       Rework output to handle synchronization and directory logging more
+       reliably.
+
+       * output.c: New file.  Implement lazy synchronization and
+       directory logging so that we manage them "just in time", and the
+       destination of the output is set via a global state variable.
+       * output.h: New file.
+       * function.c (func_shell_base): Ensure the output is set up before
+       running a shell command, in case it writes to stderr.
+       (func_error): Use outputs() to generate output.
+       * job.h (struct child): Add struct output to track the child's output.
+       * job.c: Use struct output in the child structure to track output.
+       (child_out, sync_init, assign_child_tempfiles, pump_from_tmp)
+       (acquire_semaphore, release_semaphore, sync_output): Move most of
+       the output_sync handling to output.c.
+       (child_error): Set output, then use simple message() and error()
+       not _s versions.
+       * main.c (log_working_directory): Moved to output.c
+       (trace_option, decode_trace_flags) Remove.  Remove support for
+       different trace modes; we don't use it anymore.
+       (die) Invoke output_close() before we exit.
+       * misc.c (message_s, error_s): Removed; no longer needed.
+       (message, error, fatal, perror_with_name, pfatal_with_name): Moved
+       to output.c.
+       * makeint.h: Remove message_s(), error_s(), and
+       log_working_directory().  Remove the TRACE_* macros.
+       * doc/make.texi: Enhance documentation for output sync, and remove
+       MODE assignment for --trace.
+       * make.1: Remove MODE assignment for --trace.
+       * Makefile.am: Add new files.
+       * NMakefile.template: Ditto.
+       * SMakefile.template: Ditto.
+       * build_w32.bat: Ditto.
+       * dosbuild.bat: Ditto.
+       * make.lnk: Ditto.
+       * make_nsvc_net2003.vcproj: Ditto.
+       * makefile.vms: Ditto.
+       * po/POTFILES.in: Ditto.
+
 2013-07-22  Paul Smith  <psmith@gnu.org>
 
        * implicit.c (pattern_search): Use PARSE_SIMPLE_SEQ() even for
index 80afc76633cf233abeb8c71b4e71d0406733f96e..5fd1b62ec7efaf4515833d4993f2379d360ab8cc 100644 (file)
@@ -41,8 +41,8 @@ endif
 
 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 load.c \
-               loadapi.c main.c misc.c read.c remake.c rule.c signame.c \
-               strcache.c variable.c version.c vpath.c hash.c \
+               loadapi.c main.c misc.c output.c read.c remake.c rule.c \
+               signame.c strcache.c variable.c version.c vpath.c hash.c \
                $(remote)
 
 if HAVE_GUILE
@@ -52,7 +52,7 @@ endif
 EXTRA_make_SOURCES = vmsjobs.c remote-stub.c remote-cstms.c
 
 noinst_HEADERS = commands.h dep.h filedef.h job.h makeint.h rule.h variable.h \
-               debug.h getopt.h gettext.h hash.h
+               debug.h getopt.h gettext.h hash.h output.h
 
 make_LDADD =   @LIBOBJS@ @ALLOCA@ $(GLOBLIB) @GETLOADAVG_LIBS@ @LIBINTL@ \
                $(GUILE_LIBS)
diff --git a/NEWS b/NEWS
index e1785e1374a2b6768366020ebe1233dbbfe78ae0..cf234b702ce7c88db47f8c2f78399e49feee60b0 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -86,8 +86,7 @@ http://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=101&set
 * Setting the -r and -R options in MAKEFLAGS inside a makefile now works as
   expected, removing all built-in rules and variables, respectively.
 
-* On failure, the makefile name and linenumber of the recipe that failed are
-  shown.
+* If a recipe fails, the makefile name and linenumber of the recipe are shown.
 
 * A .RECIPEPREFIX setting is remembered per-recipe and variables expanded
   in that recipe also use that recipe prefix setting.
index 4a8b8977e17dfa0a1d2ef17f9392a881f1f0ff24..1b6f6bd6deb791c90043938825a7c0efe6a71211 100644 (file)
@@ -91,6 +91,7 @@ OBJS = \
        $(OUTDIR)/job.obj \
        $(OUTDIR)/main.obj \
        $(OUTDIR)/misc.obj \
+       $(OUTDIR)/output.obj \
        $(OUTDIR)/read.obj \
        $(OUTDIR)/remake.obj \
        $(OUTDIR)/remote-stub.obj \
index d15f1f633be4f2a90b2382e8b70b79b8deaea8e5..4dfe691f64c6994d33e0ba36d463055972ced7be 100644 (file)
@@ -128,7 +128,8 @@ CTAGS = ctags -w
 objs = commands.o job.o dir.o file.o misc.o main.o read.o remake.o   \
        rule.o implicit.o default.o variable.o expand.o function.o    \
        vpath.o version.o ar.o arscan.o signame.o strcache.o hash.o   \
-       remote-$(REMOTE).o $(GLOB) $(GETOPT) $(ALLOCA) $(extras) $(guile)
+       output.o remote-$(REMOTE).o $(GLOB) $(GETOPT) $(ALLOCA)       \
+       $(extras) $(guile)
 
 srcs = $(srcdir)commands.c $(srcdir)job.c $(srcdir)dir.c             \
        $(srcdir)file.c $(srcdir)getloadavg.c $(srcdir)misc.c         \
@@ -141,6 +142,7 @@ srcs = $(srcdir)commands.c $(srcdir)job.c $(srcdir)dir.c             \
        $(srcdir)signame.c $(srcdir)signame.h $(GETOPT_SRC)           \
        $(srcdir)commands.h $(srcdir)dep.h $(srcdir)file.h            \
        $(srcdir)job.h $(srcdir)makeint.h $(srcdir)rule.h             \
+       $(srcdir)output.c $(srcdir)output.h                           \
        $(srcdir)variable.h $(ALLOCA_SRC) $(srcdir)config.h.in
 
 
index 3e350942d7e00483a86727b46abc21d88852ba82..148dfac9c91b7c843eafb31a33ed50de6dd4dcb3 100644 (file)
@@ -122,6 +122,8 @@ cl.exe /nologo /MT /W4 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D
 echo WinDebug\getopt1.obj >>link.dbg\r
 cl.exe /nologo /MT /W4 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinDebug/ /Fp.\WinDebug/%make%.pch /Fo.\WinDebug/ /Fd.\WinDebug/%make%.pdb /c job.c\r
 echo WinDebug\job.obj >>link.dbg\r
+cl.exe /nologo /MT /W4 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinDebug/ /Fp.\WinDebug/%make%.pch /Fo.\WinDebug/ /Fd.\WinDebug/%make%.pdb /c output.c\r
+echo WinDebug\output.obj >>link.dbg\r
 cl.exe /nologo /MT /W4 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinDebug/ /Fp.\WinDebug/%make%.pch /Fo.\WinDebug/ /Fd.\WinDebug/%make%.pdb /c read.c\r
 echo WinDebug\read.obj >>link.dbg\r
 cl.exe /nologo /MT /W4 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinDebug/ /Fp.\WinDebug/%make%.pch /Fo.\WinDebug/ /Fd.\WinDebug/%make%.pdb /c version.c\r
@@ -166,7 +168,7 @@ echo WinDebug\guile.obj >>link.dbg
 :LinkDbg\r
 echo off\r
 echo "Linking WinDebug/%make%.exe"\r
-rem link.exe %GUILELIBS% kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib w32\subproc\windebug\subproc.lib /NOLOGO /SUBSYSTEM:console /INCREMENTAL:yes /PDB:.\WinDebug/%make%.pdb /DEBUG /OUT:.\WinDebug/%make%.exe .\WinDebug/variable.obj  .\WinDebug/rule.obj  .\WinDebug/remote-stub.obj  .\WinDebug/commands.obj  .\WinDebug/file.obj  .\WinDebug/getloadavg.obj  .\WinDebug/default.obj  .\WinDebug/signame.obj  .\WinDebug/expand.obj  .\WinDebug/dir.obj  .\WinDebug/main.obj  .\WinDebug/getopt1.obj  .\WinDebug/job.obj  .\WinDebug/read.obj  .\WinDebug/version.obj  .\WinDebug/getopt.obj  .\WinDebug/arscan.obj  .\WinDebug/remake.obj  .\WinDebug/hash.obj  .\WinDebug/strcache.obj  .\WinDebug/misc.obj  .\WinDebug/ar.obj  .\WinDebug/function.obj  .\WinDebug/vpath.obj  .\WinDebug/implicit.obj  .\WinDebug/dirent.obj  .\WinDebug/glob.obj  .\WinDebug/fnmatch.obj  .\WinDebug/pathstuff.obj\r
+rem link.exe %GUILELIBS% kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib w32\subproc\windebug\subproc.lib /NOLOGO /SUBSYSTEM:console /INCREMENTAL:yes /PDB:.\WinDebug/%make%.pdb /DEBUG /OUT:.\WinDebug/%make%.exe .\WinDebug/variable.obj  .\WinDebug/rule.obj  .\WinDebug/remote-stub.obj  .\WinDebug/commands.obj  .\WinDebug/file.obj  .\WinDebug/getloadavg.obj  .\WinDebug/default.obj  .\WinDebug/signame.obj  .\WinDebug/expand.obj  .\WinDebug/dir.obj  .\WinDebug/main.obj  .\WinDebug/getopt1.obj  .\WinDebug/job.obj  .\WinDebug/output.obj  .\WinDebug/read.obj  .\WinDebug/version.obj  .\WinDebug/getopt.obj  .\WinDebug/arscan.obj  .\WinDebug/remake.obj  .\WinDebug/hash.obj  .\WinDebug/strcache.obj  .\WinDebug/misc.obj  .\WinDebug/ar.obj  .\WinDebug/function.obj  .\WinDebug/vpath.obj  .\WinDebug/implicit.obj  .\WinDebug/dirent.obj  .\WinDebug/glob.obj  .\WinDebug/fnmatch.obj  .\WinDebug/pathstuff.obj\r
 echo %GUILELIBS% kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib w32\subproc\windebug\subproc.lib >>link.dbg\r
 link.exe /NOLOGO /SUBSYSTEM:console /INCREMENTAL:yes /PDB:.\WinDebug/%make%.pdb /DEBUG /OUT:.\WinDebug/%make%.exe @link.dbg\r
 if not exist .\WinDebug/%make%.exe echo "WinDebug build failed"\r
@@ -199,6 +201,8 @@ cl.exe /nologo /MT /W4 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIND
 echo WinRel\getopt1.obj >>link.rel\r
 cl.exe /nologo /MT /W4 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinRel/ /Fp.\WinRel/%make%.pch /Fo.\WinRel/ /c job.c\r
 echo WinRel\job.obj >>link.rel\r
+cl.exe /nologo /MT /W4 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinRel/ /Fp.\WinRel/%make%.pch /Fo.\WinRel/ /c output.c\r
+echo WinRel\output.obj >>link.rel\r
 cl.exe /nologo /MT /W4 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinRel/ /Fp.\WinRel/%make%.pch /Fo.\WinRel/ /c read.c\r
 echo WinRel\read.obj >>link.rel\r
 cl.exe /nologo /MT /W4 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WINDOWS32 /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /FR.\WinRel/ /Fp.\WinRel/%make%.pch /Fo.\WinRel/ /c version.c\r
@@ -243,7 +247,7 @@ echo WinRel\guile.obj >>link.rel
 :LinkRel\r
 echo off\r
 echo "Linking WinRel/%make%.exe"\r
-rem link.exe %GUILELIBS% kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib w32\subproc\winrel\subproc.lib /NOLOGO /SUBSYSTEM:console /INCREMENTAL:no /PDB:.\WinRel/%make%.pdb /OUT:.\WinRel/%make%.exe .\WinRel/variable.obj  .\WinRel/rule.obj  .\WinRel/remote-stub.obj  .\WinRel/commands.obj  .\WinRel/file.obj  .\WinRel/getloadavg.obj  .\WinRel/default.obj  .\WinRel/signame.obj  .\WinRel/expand.obj  .\WinRel/dir.obj  .\WinRel/main.obj  .\WinRel/getopt1.obj  .\WinRel/job.obj  .\WinRel/read.obj  .\WinRel/version.obj  .\WinRel/getopt.obj  .\WinRel/arscan.obj  .\WinRel/remake.obj  .\WinRel/misc.obj  .\WinRel/hash.obj  .\WinRel/strcache.obj  .\WinRel/ar.obj  .\WinRel/function.obj  .\WinRel/vpath.obj  .\WinRel/implicit.obj  .\WinRel/dirent.obj  .\WinRel/glob.obj  .\WinRel/fnmatch.obj  .\WinRel/pathstuff.obj\r
+rem link.exe %GUILELIBS% kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib w32\subproc\winrel\subproc.lib /NOLOGO /SUBSYSTEM:console /INCREMENTAL:no /PDB:.\WinRel/%make%.pdb /OUT:.\WinRel/%make%.exe .\WinRel/variable.obj  .\WinRel/rule.obj  .\WinRel/remote-stub.obj  .\WinRel/commands.obj  .\WinRel/file.obj  .\WinRel/getloadavg.obj  .\WinRel/default.obj  .\WinRel/signame.obj  .\WinRel/expand.obj  .\WinRel/dir.obj  .\WinRel/main.obj  .\WinRel/getopt1.obj  .\WinRel/job.obj  .\WinRel/output.obj  .\WinRel/read.obj  .\WinRel/version.obj  .\WinRel/getopt.obj  .\WinRel/arscan.obj  .\WinRel/remake.obj  .\WinRel/misc.obj  .\WinRel/hash.obj  .\WinRel/strcache.obj  .\WinRel/ar.obj  .\WinRel/function.obj  .\WinRel/vpath.obj  .\WinRel/implicit.obj  .\WinRel/dirent.obj  .\WinRel/glob.obj  .\WinRel/fnmatch.obj  .\WinRel/pathstuff.obj\r
 echo %GUILELIBS% kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib w32\subproc\winrel\subproc.lib >>link.rel\r
 link.exe /NOLOGO /SUBSYSTEM:console /INCREMENTAL:no /PDB:.\WinRel/%make%.pdb /OUT:.\WinRel/%make%.exe @link.rel\r
 if not exist .\WinRel/%make%.exe echo "WinRel build failed"\r
@@ -265,6 +269,7 @@ gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32
 gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H %GUILECFLAGS% -c main.c\r
 gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c getopt1.c\r
 gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c job.c\r
+gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c output.c\r
 gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c read.c\r
 gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c version.c\r
 gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c getopt.c\r
@@ -294,7 +299,7 @@ gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% %GUILECFLAGS% -I. -I./glob -I./w32/inclu
 Rem The version NN of libgnumake-NN.dll.a should be bumped whenever\r
 Rem the API changes in binary-incompatible manner.\r
 @echo on\r
-gcc -mthreads -gdwarf-2 -g3 -o gnumake.exe variable.o rule.o remote-stub.o commands.o file.o getloadavg.o default.o signame.o expand.o dir.o main.o getopt1.o %GUILEOBJ% job.o read.o version.o getopt.o arscan.o remake.o misc.o hash.o strcache.o ar.o function.o vpath.o implicit.o loadapi.o load.o glob.o fnmatch.o pathstuff.o posixfcn.o w32_misc.o sub_proc.o w32err.o %GUILELIBS% -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32 -Wl,--out-implib=libgnumake-1.dll.a\r
+gcc -mthreads -gdwarf-2 -g3 -o gnumake.exe variable.o rule.o remote-stub.o commands.o file.o getloadavg.o default.o signame.o expand.o dir.o main.o getopt1.o %GUILEOBJ% job.o output.o read.o version.o getopt.o arscan.o remake.o misc.o hash.o strcache.o ar.o function.o vpath.o implicit.o loadapi.o load.o glob.o fnmatch.o pathstuff.o posixfcn.o w32_misc.o sub_proc.o w32err.o %GUILELIBS% -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32 -Wl,--out-implib=libgnumake-1.dll.a\r
 @GoTo BuildEnd\r
 :Usage\r
 echo Usage: %0 [options] [gcc]\r
index d1ceefb52402b141ab3756cbfb2ffca80b30c257..975f0b372d3b2ffedb1fdea78c7a34415faa19c8 100644 (file)
@@ -4130,6 +4130,11 @@ Additionally, if there are multiple recursive @code{make} invocations
 running in parallel, they will communicate so that only one of them is
 generating output at a time.
 
+If working directory printing is enabled (@pxref{-w Option, ,The
+@samp{--print-directory} Option}), the enter/leave messages are
+printed around each output grouping.  If you prefer not to see these
+messages add the @samp{--no-print-directory} option to @code{MAKEFLAGS}.
+
 There are four levels of granularity when synchronizing output,
 specified by giving an argument to the option (e.g.,  @samp{-Oline} or
 @samp{--output-sync=recurse}).
@@ -4196,12 +4201,12 @@ completed.
 Some programs invoked by @code{make} may behave differently if they
 determine they're writing output to a terminal versus a file (often
 described as ``interactive'' vs. ``non-interactive'' modes).  For
-example, many programs that can display colorized output often will
-not do so if they determine they are not displaying on a terminal.  If
-your makefile invokes a program like this then using the output
+example, many programs that can display colorized output will not do
+so if they determine they are not writing to a terminal.  If your
+makefile invokes a program like this then using the output
 synchronization options will cause the program to believe it's running
-in ``non-interactive'' mode even when it's ultimately writing to the
-terminal.
+in ``non-interactive'' mode even though the output will ultimately go
+to the terminal.
 
 @node Parallel Input,  , Parallel Output, Parallel
 @subsection Input During Parallel Execution
@@ -8708,22 +8713,6 @@ prerequisites, and do not remake anything on account of changes in
 are ignored.  @xref{Avoiding Compilation, ,Avoiding Recompilation of
 Some Files}.@refill
 
-@item -p
-@cindex @code{-p}
-@itemx --print-data-base
-@cindex @code{--print-data-base}
-@cindex data base of @code{make} rules
-@cindex predefined rules and variables, printing
-Print the data base (rules and variable values) that results from
-reading the makefiles; then execute as usual or as otherwise
-specified.  This also prints the version information given by the
-@samp{-v} switch (see below).  To print the data base without trying
-to remake any files, use @w{@samp{make -qp}}.  To print the data base
-of predefined rules and variables, use @w{@samp{make -p -f /dev/null}}.
-The data base output contains file name and line number information for
-recipe and variable definitions, so it can be a useful debugging tool
-in complex environments.
-
 @item -O[@var{type}]
 @cindex @code{-O}
 @itemx --output-sync[=@var{type}]
@@ -8743,6 +8732,22 @@ from each line in the recipe is grouped together.  With the type
 together.  With the type @samp{none}, no output synchronization is
 performed.  @xref{Parallel Output, ,Output During Parallel Execution}.
 
+@item -p
+@cindex @code{-p}
+@itemx --print-data-base
+@cindex @code{--print-data-base}
+@cindex data base of @code{make} rules
+@cindex predefined rules and variables, printing
+Print the data base (rules and variable values) that results from
+reading the makefiles; then execute as usual or as otherwise
+specified.  This also prints the version information given by the
+@samp{-v} switch (see below).  To print the data base without trying
+to remake any files, use @w{@samp{make -qp}}.  To print the data base
+of predefined rules and variables, use @w{@samp{make -p -f /dev/null}}.
+The data base output contains file name and line number information for
+recipe and variable definitions, so it can be a useful debugging tool
+in complex environments.
+
 @item -q
 @cindex @code{-q}
 @itemx --question
@@ -8814,17 +8819,13 @@ instead of running their recipes.  This is used to pretend that the
 recipes were done, in order to fool future invocations of
 @code{make}.  @xref{Instead of Execution, ,Instead of Executing Recipes}.
 
-@item --trace[=@var{mode}]
+@item --trace
 @cindex @code{--trace}
-Show tracing information for @code{make} execution.  With no mode or
-the type @samp{rule}, print the entire recipe to be executed, even for
-recipes that are normally silent (due to @code{.SILENT} or @samp{@@}).
-Also print the makefile name and line number where the recipe was
-defined, and information on why the target is being rebuilt.  With the
-type @samp{dir}, directory enter/leave lines are shown around each
-synchronized output segment.  These modes are cumulative and can be
-set with multiple instances of the @code{--trace} flag.  With the type
-@samp{none}, all tracing is disabled.
+Show tracing information for @code{make} execution.  Prints the entire
+recipe to be executed, even for recipes that are normally silent (due
+to @code{.SILENT} or @samp{@@}).  Also prints the makefile name and
+line number where the recipe was defined, and information on why the
+target is being rebuilt.
 
 @item -v
 @cindex @code{-v}
@@ -11387,6 +11388,11 @@ The concept of doing several things at once (parallelism) exists in
 many incarnations of @code{make} and similar programs, though not in the
 System V or BSD implementations.  @xref{Execution, ,Recipe Execution}.
 
+@item
+A number of different build tools that support parallelism also
+support collecting output and displaying as a single block.
+@xref{Parallel Output, ,Output During Parallel Execution}.
+
 @item
 Modified variable references using pattern substitution come from
 SunOS 4.  @xref{Reference, ,Basics of Variable References}.
@@ -11532,11 +11538,6 @@ functionality in that it will check out SCCS files for makefiles.
 Various new built-in implicit rules.
 @xref{Catalogue of Rules, ,Catalogue of Implicit Rules}.
 
-@item
-The built-in variable @samp{MAKE_VERSION} gives the version number of
-@code{make}.
-@vindex MAKE_VERSION
-
 @item
 Load dynamic objects which can modify the behavior of @code{make}.
 @xref{Loading Objects, ,Loading Dynamic Objects}.
@@ -11883,6 +11884,11 @@ Evaluate @var{text} then read the results as makefile commands.
 Expands to the empty string.@*
 @xref{Eval Function, ,The @code{eval} Function}.
 
+@item $(file @var{op} @var{filename},@var{text})
+Expand the arguments, then open the file @var{filename} using mode
+@var{op} and write @var{text} to that file.@*
+@xref{File Function, ,The @code{file} Function}.
+
 @item $(value @var{var})
 Evaluates to the contents of the variable @var{var}, with no expansion
 performed on it.@*
@@ -11982,6 +11988,18 @@ The name with which @code{make} was invoked.  Using this variable in
 recipes has special meaning.  @xref{MAKE Variable, ,How the
 @code{MAKE} Variable Works}.
 
+@item MAKE_VERSION
+
+The built-in variable @samp{MAKE_VERSION} expands to the version
+number of the GNU @code{make} program.
+@vindex MAKE_VERSION
+
+@item MAKE_HOST
+
+The built-in variable @samp{MAKE_HOST} expands to a string
+representing the host that GNU @code{make} was built to run on.
+@vindex MAKE_HOST
+
 @item MAKELEVEL
 
 The number of levels of recursion (sub-@code{make}s).@*
@@ -12007,6 +12025,7 @@ you'd like to set GNU @code{make}-specific flags in a POSIX-compliant
 makefile.  This variable will be seen by GNU @code{make} and ignored
 by other @code{make} implementations.  It's not needed if you only use
 GNU @code{make}; just use @code{MAKEFLAGS} directly.
+@xref{Options/Recursion, ,Communicating Options to a Sub-@code{make}}.
 
 @item MAKECMDGOALS
 
index 3f5b9b20b13ec61229cae22402d911f14f3cfecc..4091463a04c1f3a248b8bc75261f033d8f21bcb8 100644 (file)
@@ -20,6 +20,7 @@ echo Building Make for MSDOS
 rem Echo ON so they will see what is going on.\r
 @echo on\r
 gcc  -c -I. -I./glob -DHAVE_CONFIG_H -O2 -g commands.c -o commands.o\r
+gcc  -c -I. -I./glob -DHAVE_CONFIG_H -O2 -g output.c -o output.o\r
 gcc  -c -I. -I./glob -DHAVE_CONFIG_H -O2 -g job.c -o job.o\r
 gcc  -c -I. -I./glob -DHAVE_CONFIG_H -O2 -g dir.c -o dir.o\r
 gcc  -c -I. -I./glob -DHAVE_CONFIG_H -O2 -g file.c -o file.o\r
@@ -51,7 +52,7 @@ ar rv libglob.a glob.o fnmatch.o
 @echo off\r
 cd ..\r
 echo commands.o > respf.$$$\r
-for %%f in (job dir file misc main read remake rule implicit default variable) do echo %%f.o >> respf.$$$\r
+for %%f in (job output dir file misc main read remake rule implicit default variable) do echo %%f.o >> respf.$$$\r
 for %%f in (expand function vpath hash strcache version ar arscan signame remote-stub getopt getopt1) do echo %%f.o >> respf.$$$\r
 echo glob/libglob.a >> respf.$$$\r
 rem gcc  -c -I. -I./glob -DHAVE_CONFIG_H -O2 -g guile.c -o guile.o\r
index 9eabd73a313b3a4d0d035f23d7e7871626aa860b..3379c90498313e751df1a3a7bb63fa260684c16d 100644 (file)
@@ -1089,8 +1089,8 @@ func_error (char *o, char **argv, const char *funcname)
       break;
 
     case 'i':
-      printf ("%s\n", msg);
-      fflush (stdout);
+      outputs (0, msg);
+      outputs (0, "\n");
       break;
 
     default:
@@ -1675,6 +1675,9 @@ func_shell_base (char *o, char **argv, int trim_newlines)
   else
     error_prefix = "";
 
+  /* Set up the output in case the shell writes something.  */
+  output_start ();
+
 #if defined(__MSDOS__)
   fpipe = msdos_openpipe (pipedes, &pid, argv[0]);
   if (pipedes[0] < 0)
diff --git a/job.c b/job.c
index 16c164ea775c007e3e57e1065e3e89f4ae3fac67..28c8e375658249868d78ecfafb1f10c17f45f33a 100644 (file)
--- a/job.c
+++ b/job.c
@@ -242,20 +242,8 @@ unsigned long job_counter = 0;
 /* Number of jobserver tokens this instance is currently using.  */
 
 unsigned int jobserver_tokens = 0;
-
-#ifdef OUTPUT_SYNC
-
-/* Semaphore for use in -j mode with output_sync. */
-static sync_handle_t sync_handle = -1;
-
-/* Is make's stdout going to the same place as stderr?  */
-static int combined_output = 0;
-
-#define STREAM_OK(_s)       ((fcntl (fileno (_s), F_GETFD) != -1) || (errno != EBADF))
-
-#define FD_NOT_EMPTY(_f)    ((_f) >= 0 && lseek ((_f), 0, SEEK_END) > 0)
-#endif /* OUTPUT_SYNC */
 \f
+
 #ifdef WINDOWS32
 /*
  * The macro which references this function is defined in makeint.h.
@@ -472,50 +460,12 @@ is_bourne_compatible_shell (const char *path)
 }
 
 \f
-/* Write a message in the child's context.  Write it to the child's output
-   sync file if present, otherwise to the terminal.  */
-
-static void
-child_out (const struct child *child, const char *msg, int out)
-{
-  int fd = out ? child->outfd : child->errfd;
-
-  if (!msg || msg[0] == '\0')
-    return;
-
-  if (fd >= 0)
-    {
-      int len = strlen (msg);
-      int b;
-
-      lseek (fd, 0, SEEK_END);
-      while (1)
-        {
-          EINTRLOOP (b, write (fd, msg, len));
-          if (b == len)
-            break;
-          if (b <= 0)
-            return;
-          len -= b;
-          msg += b;
-        }
-      EINTRLOOP (b, write (fd, "\n", 1));
-    }
-  else
-    {
-      FILE *f = out ? stdout : stderr;
-      fputs (msg, f);
-      putc ('\n', f);
-      fflush (f);
-    }
-}
-
 /* Write an error message describing the exit status given in
    EXIT_CODE, EXIT_SIG, and COREDUMP, for the target TARGET_NAME.
    Append "(ignored)" if IGNORED is nonzero.  */
 
 static void
-child_error (const struct child *child,
+child_error (struct child *child,
              int exit_code, int exit_sig, int coredump, int ignored)
 {
   const char *pre = "*** ";
@@ -523,9 +473,7 @@ child_error (const struct child *child,
   const char *dump = "";
   const struct file *f = child->file;
   const gmk_floc *flocp = &f->cmds->fileinfo;
-  const char *msg;
   const char *nm;
-  unsigned int l;
 
   if (ignored && silent_flag)
     return;
@@ -548,31 +496,29 @@ child_error (const struct child *child,
       nm = a;
     }
 
-  msg = message_s (strlen (nm) + strlen (f->name), 0,
-                   _("%s: recipe for target '%s' failed"), nm, f->name);
-  child_out (child, msg, 1);
+  OUTPUT_SET (&child->output);
 
-  l = strlen (pre) + strlen (f->name) + strlen (post);
+  message (0, _("%s: recipe for target '%s' failed"), nm, f->name);
 
 #ifdef VMS
   if ((exit_code & 1) != 0)
-    return;
+    {
+      OUTPUT_UNSET ();
+      return;
+    }
 
-  msg = error_s (l + INTEGER_LENGTH, NILF,
-                 _("%s[%s] Error 0x%x%s"), pre, f->name, exit_code, post);
+  error (NILF, _("%s[%s] Error 0x%x%s"), pre, f->name, exit_code, post);
 #else
   if (exit_sig == 0)
-    msg = error_s (l + INTEGER_LENGTH, NILF,
-                   _("%s[%s] Error %d%s"), pre, f->name, exit_code, post);
+    error (NILF, _("%s[%s] Error %d%s"), pre, f->name, exit_code, post);
   else
     {
       const char *s = strsignal (exit_sig);
-      msg = error_s (l + strlen (s) + strlen (dump), NILF,
-                     _("%s[%s] %s%s%s"), pre, f->name, s, dump, post);
+      error (NILF, _("%s[%s] %s%s%s"), pre, f->name, s, dump, post);
     }
 #endif /* VMS */
 
-  child_out (child, msg, 0);
+  OUTPUT_UNSET ();
 }
 \f
 
@@ -609,206 +555,6 @@ child_handler (int sig UNUSED)
   */
 }
 
-#ifdef OUTPUT_SYNC
-
-/* Set up the sync handle and configure combined_output.
-   Disables output_sync on error.  */
-static void
-sync_init ()
-{
-#ifdef WINDOWS32
-  if ((!STREAM_OK (stdout) && !STREAM_OK (stderr))
-      || (sync_handle = create_mutex ()) == -1)
-    {
-      perror_with_name ("output-sync suppressed: ", "stderr");
-      output_sync = 0;
-    }
-  else
-    {
-      combined_output = same_stream (stdout, stderr);
-      prepare_mutex_handle_string (sync_handle);
-    }
-
-#else
-  if (STREAM_OK (stdout))
-    {
-      struct stat stbuf_o, stbuf_e;
-
-      sync_handle = fileno (stdout);
-      combined_output =
-        fstat (fileno (stdout), &stbuf_o) == 0 &&
-        fstat (fileno (stderr), &stbuf_e) == 0 &&
-        stbuf_o.st_dev == stbuf_e.st_dev &&
-        stbuf_o.st_ino == stbuf_e.st_ino;
-    }
-  else if (STREAM_OK (stderr))
-    sync_handle = fileno (stderr);
-  else
-    {
-      perror_with_name ("output-sync suppressed: ", "stderr");
-      output_sync = 0;
-    }
-#endif
-}
-
-/* Adds file descriptors to the child structure to support output_sync; one
-   for stdout and one for stderr as long as they are open.  If stdout and
-   stderr share a device they can share a temp file too.
-   Will reset output_sync on error.  */
-static void
-assign_child_tempfiles (struct child *c)
-{
-  /* If we don't have a temp file, get one.  */
-  if (c->outfd < 0 && c->errfd < 0)
-    {
-      if (STREAM_OK (stdout))
-        {
-          c->outfd = open_tmpfd ();
-          if (c->outfd < 0)
-            goto error;
-          CLOSE_ON_EXEC (c->outfd);
-        }
-
-      if (STREAM_OK (stderr))
-        {
-          if (c->outfd >= 0 && combined_output)
-            c->errfd = c->outfd;
-          else
-            {
-              c->errfd = open_tmpfd ();
-              if (c->errfd < 0)
-                goto error;
-              CLOSE_ON_EXEC (c->errfd);
-            }
-        }
-    }
-
-  return;
-
- error:
-  if (c->outfd >= 0)
-    {
-      close (c->outfd);
-      c->outfd = -1;
-    }
-  output_sync = 0;
-}
-
-/* Support routine for sync_output() */
-static void
-pump_from_tmp (int from, FILE *to)
-{
-  static char buffer[8192];
-
-#ifdef WINDOWS32
-  int prev_mode;
-
-  /* "from" is opened by open_tmpfd, which does it in binary mode, so
-     we need the mode of "to" to match that.  */
-  prev_mode = _setmode (fileno (to), _O_BINARY);
-#endif
-
-  if (lseek (from, 0, SEEK_SET) == -1)
-    perror ("lseek()");
-
-  while (1)
-    {
-      int len;
-      EINTRLOOP (len, read (from, buffer, sizeof (buffer)));
-      if (len < 0)
-        perror ("read()");
-      if (len <= 0)
-        break;
-      if (fwrite (buffer, len, 1, to) < 1)
-        perror ("fwrite()");
-    }
-
-#ifdef WINDOWS32
-  /* Switch "to" back to its original mode, so that log messages by
-     Make have the same EOL format as without --output-sync.  */
-  _setmode (fileno (to), prev_mode);
-#endif
-}
-
-/* Support routine for sync_output() */
-static void *
-acquire_semaphore (void)
-{
-  static struct flock fl;
-
-  fl.l_type = F_WRLCK;
-  fl.l_whence = SEEK_SET;
-  fl.l_start = 0;
-  fl.l_len = 1;
-  if (fcntl (sync_handle, F_SETLKW, &fl) != -1)
-    return &fl;
-  perror ("fcntl()");
-  return NULL;
-}
-
-/* Support routine for sync_output() */
-static void
-release_semaphore (void *sem)
-{
-  struct flock *flp = (struct flock *)sem;
-  flp->l_type = F_UNLCK;
-  if (fcntl (sync_handle, F_SETLKW, flp) == -1)
-    perror ("fcntl()");
-}
-
-/* Synchronize the output of jobs in -j mode to keep the results of
-   each job together. This is done by holding the results in temp files,
-   one for stdout and potentially another for stderr, and only releasing
-   them to "real" stdout/stderr when a semaphore can be obtained. */
-
-static void
-sync_output (struct child *c)
-{
-  int outfd_not_empty = FD_NOT_EMPTY (c->outfd);
-  int errfd_not_empty = FD_NOT_EMPTY (c->errfd);
-
-  if (outfd_not_empty || errfd_not_empty)
-    {
-      /* Try to acquire the semaphore.  If it fails, dump the output
-         unsynchronized; still better than silently discarding it.  */
-      void *sem = acquire_semaphore ();
-
-      /* We've entered the "critical section" during which a lock is held.  We
-         want to keep it as short as possible.  */
-
-      /* Log the working directory.  Force it if we're doing dir tracing.  */
-      log_working_directory (1, (trace_flag & TRACE_DIRECTORY));
-
-      if (outfd_not_empty)
-        pump_from_tmp (c->outfd, stdout);
-      if (errfd_not_empty && c->errfd != c->outfd)
-        pump_from_tmp (c->errfd, stderr);
-
-      /* If we're doing dir tracing, force the leave message.  */
-      if (trace_flag & TRACE_DIRECTORY)
-        log_working_directory (0, 1);
-
-      /* Exit the critical section.  */
-      if (sem)
-        release_semaphore (sem);
-
-      /* Truncate and reset the output, in case we use it again.  */
-      if (c->outfd >= 0)
-        {
-          int e;
-          lseek (c->outfd, 0, SEEK_SET);
-          EINTRLOOP (e, ftruncate (c->outfd, 0));
-        }
-      if (c->errfd >= 0 && c->errfd != c->outfd)
-        {
-          int e;
-          lseek (c->errfd, 0, SEEK_SET);
-          EINTRLOOP (e, ftruncate (c->errfd, 0));
-        }
-    }
-}
-#endif /* OUTPUT_SYNC */
-
 extern int shell_function_pid, shell_function_completed;
 
 /* Reap all dead children, storing the returned status and the new command
@@ -1152,7 +898,7 @@ reap_children (int block, int err)
                   /* If we're sync'ing per line, write the previous line's
                      output before starting the next one.  */
                   if (output_sync == OUTPUT_SYNC_LINE)
-                    sync_output (c);
+                    output_dump (&c->output);
 #endif
                   /* Check again whether to start remotely.
                      Whether or not we want to changes over time.
@@ -1186,7 +932,7 @@ reap_children (int block, int err)
 
 #ifdef OUTPUT_SYNC
       /* Synchronize any remaining parallel output.  */
-      sync_output (c);
+      output_dump (&c->output);
 #endif /* OUTPUT_SYNC */
 
       /* At this point c->file->update_status is success or failed.  But
@@ -1243,10 +989,7 @@ reap_children (int block, int err)
 static void
 free_child (struct child *child)
 {
-  if (child->outfd >= 0)
-    close (child->outfd);
-  if (child->errfd >= 0 && child->errfd != child->outfd)
-    close (child->errfd);
+  output_close (&child->output);
 
   if (!jobserver_tokens)
     fatal (NILF, "INTERNAL: Freeing child %p (%s) but no tokens left!\n",
@@ -1388,8 +1131,6 @@ start_job_command (struct child *child)
 #if !defined(_AMIGA) && !defined(WINDOWS32)
   static int bad_stdin = -1;
 #endif
-  int print_cmd;
-  int sync_cmd;
   int flags;
   char *p;
 #ifdef VMS
@@ -1513,43 +1254,30 @@ start_job_command (struct child *child)
           child->file->update_status = us_success;
           notice_finished_file (child->file);
         }
+
+      OUTPUT_UNSET();
       return;
     }
 
-  print_cmd = (just_print_flag || (trace_flag & TRACE_RULE)
-               || (!(flags & COMMANDS_SILENT) && !silent_flag));
-
-#ifdef OUTPUT_SYNC
-  if (output_sync && sync_handle == -1)
-    sync_init ();
-#endif
-
   /* Are we going to synchronize this command's output?  Do so if either we're
-     in SYNC_MAKE mode or this command is not recursive.  We'll also check
+     in SYNC_RECURSE mode or this command is not recursive.  We'll also check
      output_sync separately below in case it changes due to error.  */
-  sync_cmd = output_sync && (output_sync == OUTPUT_SYNC_RECURSE
-                             || !(flags & COMMANDS_RECURSE));
+  child->output.syncout = output_sync && (output_sync == OUTPUT_SYNC_RECURSE
+                                          || !(flags & COMMANDS_RECURSE));
+
+  OUTPUT_SET (&child->output);
 
 #ifdef OUTPUT_SYNC
-  if (sync_cmd)
-    {
-      /* If syncing, make sure we have temp files.
-         Write the command to the temp file so it's output in order.  */
-      assign_child_tempfiles (child);
-      if (print_cmd)
-        child_out (child, p, 1);
-    }
-  else
+  if (! child->output.syncout)
     /* We don't want to sync this command: to avoid misordered
        output ensure any already-synced content is written.  */
-    sync_output (child);
+    output_dump (&child->output);
 #endif /* OUTPUT_SYNC */
 
-  /* If we're not syncing, print out the command.  If silent, we call
-     'message' with null so it can log the working directory before the
-     command's own error messages appear.  */
-  if (! sync_cmd)
-    message (0, print_cmd ? "%s" : NULL, p);
+  /* Print the command if appropriate.  */
+  if (just_print_flag || trace_flag
+      || (!(flags & COMMANDS_SILENT) && !silent_flag))
+    message (0, "%s", p);
 
   /* Tell update_goal_chain that a command has been started on behalf of
      this target.  It is important that this happens here and not in
@@ -1598,6 +1326,9 @@ start_job_command (struct child *child)
       goto next_command;
     }
 
+  /* We're sure we're going to invoke a command: set up the output.  */
+  output_start ();
+
   /* Flush the output streams so they won't have things written twice.  */
 
   fflush (stdout);
@@ -1754,15 +1485,17 @@ start_job_command (struct child *child)
 #ifdef OUTPUT_SYNC
           /* Divert child output if output_sync in use.  Don't capture
              recursive make output unless we are synchronizing "make" mode.  */
-          if (sync_cmd)
+          if (child->output.syncout)
             {
               int outfd = fileno (stdout);
               int errfd = fileno (stderr);
 
-              if ((child->outfd >= 0 && (close (outfd) == -1
-                                         || dup2 (child->outfd, outfd) == -1))
-                  || (child->errfd >= 0 && (close (errfd) == -1
-                                            || dup2 (child->errfd, errfd) == -1)))
+              if ((child->output.out >= 0
+                   && (close (outfd) == -1
+                       || dup2 (child->output.out, outfd) == -1))
+                  || (child->output.err >= 0
+                      && (close (errfd) == -1
+                          || dup2 (child->output.err, errfd) == -1)))
                 perror_with_name ("output-sync: ", "dup2()");
             }
 #endif /* OUTPUT_SYNC */
@@ -1867,9 +1600,9 @@ start_job_command (struct child *child)
 #ifdef OUTPUT_SYNC
           /* Divert child output if output_sync in use.  Don't capture
              recursive make output unless we are synchronizing "make" mode.  */
-          if (sync_cmd)
+          if (child->output.syncout)
             hPID = process_easy (argv, child->environment,
-                                 child->outfd, child->errfd);
+                                 child->output.out, child->output.err);
           else
 #endif
             hPID = process_easy (argv, child->environment, -1, -1);
@@ -1906,12 +1639,13 @@ start_job_command (struct child *child)
   free (argv);
 #endif
 
+  OUTPUT_UNSET();
   return;
 
  error:
   child->file->update_status = us_failed;
   notice_finished_file (child->file);
-  return;
+  OUTPUT_UNSET();
 }
 
 /* Try to start a child running.
@@ -2000,6 +1734,22 @@ new_job (struct file *file)
   /* Chop the commands up into lines if they aren't already.  */
   chop_commands (cmds);
 
+  /* Start the command sequence, record it in a new
+     'struct child', and add that to the chain.  */
+
+  c = xcalloc (sizeof (struct child));
+  output_init (&c->output, output_sync);
+
+  c->file = file;
+  c->sh_batch_file = NULL;
+
+  /* Cache dontcare flag because file->dontcare can be changed once we
+     return. Check dontcare inheritance mechanism for details.  */
+  c->dontcare = file->dontcare;
+
+  /* Start saving output in case the expansion uses $(info ...) etc.  */
+  OUTPUT_SET (&c->output);
+
   /* Expand the command lines and store the results in LINES.  */
   lines = xmalloc (cmds->ncommand_lines * sizeof (char *));
   for (i = 0; i < cmds->ncommand_lines; ++i)
@@ -2104,18 +1854,7 @@ new_job (struct file *file)
                                                      file);
     }
 
-  /* Start the command sequence, record it in a new
-     'struct child', and add that to the chain.  */
-
-  c = xcalloc (sizeof (struct child));
-  c->file = file;
   c->command_lines = lines;
-  c->sh_batch_file = NULL;
-  c->outfd = c->errfd = -1;
-
-  /* Cache dontcare flag because file->dontcare can be changed once we
-     return. Check dontcare inheritance mechanism for details.  */
-  c->dontcare = file->dontcare;
 
   /* Fetch the first command line to be run.  */
   job_next_command (c);
@@ -2251,7 +1990,7 @@ new_job (struct file *file)
 
   /* Trace the build.
      Use message here so that changes to working directories are logged.  */
-  if (trace_flag & TRACE_RULE)
+  if (trace_flag)
     {
       char *newer = allocated_variable_expand_for_file ("$?", c->file);
       const char *nm;
@@ -2274,7 +2013,6 @@ new_job (struct file *file)
       free (newer);
     }
 
-
   /* The job is now primed.  Start it running.
      (This will notice if there is in fact no recipe.)  */
   start_waiting_job (c);
@@ -2285,6 +2023,7 @@ new_job (struct file *file)
     while (file->command_state == cs_running)
       reap_children (1, 0);
 
+  OUTPUT_UNSET ();
   return;
 }
 \f
@@ -3284,11 +3023,13 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
        Then recurse, expanding this command line to get the final
        argument list.  */
 
+    char *new_line;
     unsigned int shell_len = strlen (shell);
     unsigned int line_len = strlen (line);
     unsigned int sflags_len = shellflags ? strlen (shellflags) : 0;
+#ifdef WINDOWS32
     char *command_ptr = NULL; /* used for batch_mode_shell mode */
-    char *new_line;
+#endif
 
 # ifdef __EMX__ /* is this necessary? */
     if (!unixy_shell && shellflags)
@@ -3472,7 +3213,9 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
       memcpy (ap, shellflags, sflags_len);
     ap += sflags_len;
     *(ap++) = ' ';
+#ifdef WINDOWS32
     command_ptr = ap;
+#endif
     for (p = line; *p != '\0'; ++p)
       {
         if (restp != NULL && *p == '\n')
diff --git a/job.h b/job.h
index ba785a7b8f8da7519909ebd0a9f37c686d7c9637..967fa2bf7ec3a1949bec1faa37ec1b0dd2cf0e31 100644 (file)
--- a/job.h
+++ b/job.h
@@ -14,8 +14,7 @@ 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
 this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
-#ifndef SEEN_JOB_H
-#define SEEN_JOB_H
+#include "output.h"
 
 #ifdef HAVE_FCNTL_H
 # include <fcntl.h>
@@ -103,15 +102,14 @@ struct child
     int cstatus;                /* Completion status */
 #endif
 
-    unsigned int command_line;  /* Index into command_lines.  */
-    int          outfd;         /* File descriptor for saving stdout */
-    int          errfd;         /* File descriptor for saving stderr */
-    pid_t        pid;           /* Child process's ID number.  */
-    unsigned int remote:1;      /* Nonzero if executing remotely.  */
-    unsigned int noerror:1;     /* Nonzero if commands contained a '-'.  */
-    unsigned int good_stdin:1;  /* Nonzero if this child has a good stdin.  */
-    unsigned int deleted:1;     /* Nonzero if targets have been deleted.  */
-    unsigned int dontcare:1;    /* Saved dontcare flag.  */
+    unsigned int  command_line; /* Index into command_lines.  */
+    struct output output;       /* Output for this child.  */
+    pid_t         pid;          /* Child process's ID number.  */
+    unsigned int  remote:1;     /* Nonzero if executing remotely.  */
+    unsigned int  noerror:1;    /* Nonzero if commands contained a '-'.  */
+    unsigned int  good_stdin:1; /* Nonzero if this child has a good stdin.  */
+    unsigned int  deleted:1;    /* Nonzero if targets have been deleted.  */
+    unsigned int  dontcare:1;   /* Saved dontcare flag.  */
   };
 
 extern struct child *children;
@@ -153,5 +151,3 @@ extern int fatal_signal_mask;
 #endif
 
 extern unsigned int jobserver_tokens;
-
-#endif /* SEEN_JOB_H */
diff --git a/main.c b/main.c
index 2cfc49bf168cce52a067af870ddf8dbc29c920cc..478551484f09bdae2f4a6ba0f77391b5563acbd0 100644 (file)
--- a/main.c
+++ b/main.c
@@ -159,10 +159,6 @@ int db_level = 0;
 
 static struct stringlist *output_sync_option = 0;
 
-/* Tracing (--trace).  */
-
-static struct stringlist *trace_option = 0;
-
 #ifdef WINDOWS32
 /* Suspend make in main for a short time to allow debugger to attach */
 
@@ -374,7 +370,7 @@ static const char *const usage[] =
     N_("\
   -t, --touch                 Touch targets instead of remaking them.\n"),
     N_("\
-  --trace[=MODE]              Print tracing information.\n"),
+  --trace                     Print tracing information.\n"),
     N_("\
   -v, --version               Print the version number of make and exit.\n"),
     N_("\
@@ -442,7 +438,7 @@ static const struct command_switch switches[] =
     /* These are long-style options.  */
     { CHAR_MAX+1, string, &db_flags, 1, 1, 0, "basic", 0, "debug" },
     { CHAR_MAX+2, string, &jobserver_fds, 1, 1, 0, 0, 0, "jobserver-fds" },
-    { CHAR_MAX+3, string, &trace_option, 1, 1, 0, "rule", 0, "trace" },
+    { CHAR_MAX+3, flag, &trace_flag, 1, 1, 0, 0, 0, "trace" },
     { CHAR_MAX+4, flag, &inhibit_print_directory_flag, 1, 1, 0, 0, 0,
       "no-print-directory" },
     { CHAR_MAX+5, flag, &warn_undefined_variables_flag, 1, 1, 0, 0, 0,
@@ -533,10 +529,9 @@ int one_shell;
 
 int output_sync = OUTPUT_SYNC_NONE;
 
-/* One of TRACE_* if the "--trace" option was given.  Enables various types of
-   tracing.  */
+/* Nonzero if the "--trace" option was given.  */
 
-int trace_flag = TRACE_NONE;
+int trace_flag = 0;
 
 /* Nonzero if we have seen the '.NOTPARALLEL' target.
    This turns off parallel builds for this invocation of make.  */
@@ -739,29 +734,6 @@ decode_debug_flags (void)
     verify_flag = 1;
 }
 
-static void
-decode_trace_flags (void)
-{
-  const char **pp;
-
-  if (!trace_option)
-    return;
-
-  for (pp=trace_option->list; *pp; ++pp)
-    {
-      const char *p = *pp;
-
-      if (streq (p, "none"))
-        trace_flag = TRACE_NONE;
-      else if (streq (p, "rule"))
-        trace_flag |= TRACE_RULE;
-      else if (streq (p, "dir"))
-        trace_flag |= TRACE_DIRECTORY;
-      else
-        fatal (NILF, _("unknown trace mode '%s'"), p);
-    }
-}
-
 static void
 decode_output_sync_flags (void)
 {
@@ -2268,8 +2240,6 @@ main (int argc, char **argv, char **envp)
           if (print_data_base_flag)
             print_data_base ();
 
-          log_working_directory (0, 0);
-
           clean_jobserver (0);
 
           if (makefiles != 0)
@@ -2888,7 +2858,6 @@ decode_switches (int argc, char **argv, int env)
   /* If there are any options that need to be decoded do it now.  */
   decode_debug_flags ();
   decode_output_sync_flags ();
-  decode_trace_flags ();
 }
 
 /* Decode switches from environment variable ENVAR (which is LEN chars long).
@@ -3249,7 +3218,7 @@ print_version (void)
      year, and none of the rest of it should be translated (including the
      word "Copyright"), so it hardly seems worth it.  */
 
-  printf ("%sCopyright (C) 1988-2012 Free Software Foundation, Inc.\n",
+  printf ("%sCopyright (C) 1988-2013 Free Software Foundation, Inc.\n",
           precede);
 
   printf (_("%sLicense GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
@@ -3402,6 +3371,8 @@ die (int status)
 
       clean_jobserver (status);
 
+      output_close (NULL);
+
       /* Try to move back to the original directory.  This is essential on
          MS-DOS (where there is really only one process), and on Unix it
          puts core files in the original directory instead of the -C
@@ -3413,64 +3384,7 @@ die (int status)
           int _x UNUSED;
           _x = chdir (directory_before_chdir);
         }
-
-      log_working_directory (0, 0);
     }
 
   exit (status);
 }
-\f
-/* Write a message indicating that we've just entered or
-   left (according to ENTERING) the current directory.  */
-
-void
-log_working_directory (int entering, int force)
-{
-  static int entered = 0;
-
-  /* Print nothing without the flag.  Don't print the entering message
-     again if we already have.  Don't print the leaving message if we
-     haven't printed the entering message.  */
-  if (! print_directory_flag || (!force && entering == entered))
-    return;
-
-  if (!force)
-    entered = entering;
-
-  if (print_data_base_flag)
-    fputs ("# ", stdout);
-
-  /* Use entire sentences to give the translators a fighting chance.  */
-
-  if (makelevel == 0)
-    if (starting_directory == 0)
-      if (entering)
-        printf (_("%s: Entering an unknown directory\n"), program);
-      else
-        printf (_("%s: Leaving an unknown directory\n"), program);
-    else
-      if (entering)
-        printf (_("%s: Entering directory '%s'\n"),
-                program, starting_directory);
-      else
-        printf (_("%s: Leaving directory '%s'\n"),
-                program, starting_directory);
-  else
-    if (starting_directory == 0)
-      if (entering)
-        printf (_("%s[%u]: Entering an unknown directory\n"),
-                program, makelevel);
-      else
-        printf (_("%s[%u]: Leaving an unknown directory\n"),
-                program, makelevel);
-    else
-      if (entering)
-        printf (_("%s[%u]: Entering directory '%s'\n"),
-                program, makelevel, starting_directory);
-      else
-        printf (_("%s[%u]: Leaving directory '%s'\n"),
-                program, makelevel, starting_directory);
-
-  /* Flush stdout to be sure this comes before any stderr output.  */
-  fflush (stdout);
-}
diff --git a/make.1 b/make.1
index df2a169cc19c75e8975dc74e8c49df25987f8e9e..87ee42edb33b4af94ba173b9a995bf82c81473ae 100644 (file)
--- a/make.1
+++ b/make.1
@@ -289,25 +289,9 @@ This is used to pretend that the commands were done, in order to fool
 future invocations of
 .BR make .
 .TP 0.5i
-.B \-\-trace\fR[=\fImode\fR]
-Print information about the commands invoked by
-.BR make .
-If
-.I mode
-is not specified or is
-.B rule
-information about the disposition of each target is printed.  If
-.I mode
-is
-.B dir
-then directory enter/leave trace statements are shown for each synchronized
-output segment (see
-.BR \-O ).
-If
-.I mode
-is
-.B none
-then no tracing is performed.
+.B \-\-trace
+Information about the disposition of each target is printed (why the target is
+being rebuilt and what commands are run to rebuild it).
 .TP 0.5i
 \fB\-v\fR, \fB\-\-version\fR
 Print the version of the
index b4080a7e5bb0d8b2f168ba502736557853e6611c..0d983bfffb030150c59165dd57d4153881e46e5f 100644 (file)
--- a/make.lnk
+++ b/make.lnk
@@ -1,4 +1,4 @@
-FROM LIB:cres.o "commands.o"+"job.o"+"dir.o"+"file.o"+"misc.o"+"main.o"+"read.o"+"remake.o"+"rule.o"+"implicit.o"+"default.o"+"variable.o"+"expand.o"+"function.o"+"vpath.o"+"version.o"+"ar.o"+"arscan.o"+"signame.o"+"remote-stub.o"+"getopt.o"+"getopt1.o"+"alloca.o"+"amiga.o"+"hash.o"+"strcache.o"
+FROM LIB:cres.o "commands.o"+"job.o"+"dir.o"+"file.o"+"misc.o"+"main.o"+"read.o"+"remake.o"+"rule.o"+"implicit.o"+"default.o"+"variable.o"+"expand.o"+"function.o"+"vpath.o"+"version.o"+"ar.o"+"arscan.o"+"signame.o"+"remote-stub.o"+"getopt.o"+"getopt1.o"+"alloca.o"+"amiga.o"+"hash.o"+"strcache.o"+"output.o"
 TO "make.new"
 LIB glob/glob.lib LIB:sc.lib LIB:amiga.lib
 QUIET
index 50492ad085b919259147a4404553a14a2feb7583..956b321ec7725001b7c0d5a486eb8c91358a5d5d 100644 (file)
                        <File\r
                                RelativePath=".\job.c">\r
                        </File>\r
+                       <File\r
+                               RelativePath=".\output.c">\r
+                       </File>\r
                        <File\r
                                RelativePath=".\main.c">\r
                        </File>\r
                        <File\r
                                RelativePath=".\job.h">\r
                        </File>\r
+                       <File\r
+                               RelativePath=".\output.h">\r
+                       </File>\r
                        <File\r
                                RelativePath=".\makeint.h">\r
                        </File>\r
index 4534a44b4374edbf6f14790c86d218916f7c1cc9..e5950b7e7de75d2036d84cbbe5aaf3cf2907a457 100644 (file)
@@ -90,17 +90,17 @@ manext = 1
 
 #guile = ,guile.obj
 
-objs = commands.obj,job.obj,dir.obj,file.obj,misc.obj,hash.obj,\
+objs = commands.obj,job.obj,output.obj,dir.obj,file.obj,misc.obj,hash.obj,\
        load.obj,main.obj,read.obj,remake.obj,rule.obj,implicit.obj,\
        default.obj,variable.obj,expand.obj,function.obj,strcache.obj,\
        vpath.obj,version.obj\
        $(ARCHIVES)$(ALLOCA)$(extras)$(getopt)$(glob)$(guile)
 
-srcs = commands.c job.c dir.c file.c misc.c guile.c hash.c \
+srcs = commands.c job.c output.c dir.c file.c misc.c guile.c hash.c \
        load.c main.c read.c remake.c rule.c implicit.c \
        default.c variable.c expand.c function.c strcache.c \
        vpath.c version.c vmsfunctions.c vmsify.c $(ARCHIVES_SRC) $(ALLOCASRC) \
-       commands.h dep.h filedef.h job.h makeint.h rule.h variable.h
+       commands.h dep.h filedef.h job.h output.h makeint.h rule.h variable.h
 
 
 .PHONY: all doc
@@ -135,6 +135,7 @@ guile.obj: guile.c makeint.h debug.h dep.h gmk-default.h
 hash.obj: hash.c makeint.h hash.h
 implicit.obj: implicit.c makeint.h rule.h dep.h filedef.h debug.h variable.h job.h commands.h
 job.obj: job.c vmsjobs.c makeint.h commands.h job.h filedef.h variable.h debug.h
+output.obj: output.c vmsjobs.c makeint.h output.h filedef.h debug.h
 load.obj: load.c makeint.h debug.h filedef.h variable.h
 main.obj: main.c makeint.h commands.h dep.h filedef.h variable.h job.h rule.h debug.h getopt.h
 misc.obj: misc.c makeint.h dep.h debug.h
index b51e914fccee6f7a7c05f5e954f4a8af7a65d65e..2674eaf074271e3875581de67d576238267854a1 100644 (file)
--- a/makeint.h
+++ b/makeint.h
@@ -424,10 +424,6 @@ extern struct rlimit stack_limit;
 
 \f
 const char *concat (unsigned int, ...);
-const char *message_s (unsigned int length, int prefix, const char *fmt, ...)
-              __attribute__ ((__format__ (__printf__, 3, 4)));
-const char *error_s (unsigned int length, const gmk_floc *flocp, const char *fmt, ...)
-              __attribute__ ((__format__ (__printf__, 3, 4)));
 void message (int prefix, const char *fmt, ...)
               __attribute__ ((__format__ (__printf__, 2, 3)));
 void error (const gmk_floc *flocp, const char *fmt, ...)
@@ -436,9 +432,9 @@ void fatal (const gmk_floc *flocp, const char *fmt, ...)
                    __attribute__ ((noreturn, __format__ (__printf__, 2, 3)));
 
 void die (int) __attribute__ ((noreturn));
-void log_working_directory (int, int);
 void pfatal_with_name (const char *) __attribute__ ((noreturn));
 void perror_with_name (const char *, const char *);
+#define xstrlen(_s) ((_s)==NULL ? 0 : strlen (_s))
 void *xmalloc (unsigned int);
 void *xcalloc (unsigned int);
 void *xrealloc (void *, unsigned int);
@@ -574,10 +570,6 @@ int strncasecmp (const char *s1, const char *s2, int n);
 #define OUTPUT_SYNC_TARGET  2
 #define OUTPUT_SYNC_RECURSE 3
 
-#define TRACE_NONE      0x0
-#define TRACE_RULE      0x1
-#define TRACE_DIRECTORY 0x2
-
 extern const gmk_floc *reading_file;
 extern const gmk_floc **expanding_var;
 
diff --git a/misc.c b/misc.c
index 8ae67b96eb0efb26260602f8465f5db700677fc5..48581178518bb398c8650d6409fefee3458f16a2 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -152,7 +152,7 @@ concat (unsigned int num, ...)
   while (num-- > 0)
     {
       const char *s = va_arg (args, const char *);
-      unsigned int l = s ? strlen (s) : 0;
+      unsigned int l = xstrlen (s);
 
       if (l == 0)
         continue;
@@ -183,163 +183,8 @@ concat (unsigned int num, ...)
 }
 \f
 
-/* Return a formatted string buffer.
-   LENGTH must be the maximum length of all format arguments, stringified.
-   If we had a standard-compliant vsnprintf() this would be a lot simpler.
-   Maybe in the future we'll include gnulib's version.  */
-
-const char *
-message_s (unsigned int length, int prefix, const char *fmt, ...)
-{
-  static char *buffer = NULL;
-  static unsigned int bsize = 0;
-  char *bp;
-  va_list args;
-
-  /* Compute the maximum buffer size we'll need, and make sure we have it.  */
-  length += strlen (fmt) + strlen (program) + 4 + INTEGER_LENGTH + 2;
-  if (length > bsize)
-    {
-      bsize = length * 2;
-      buffer = xrealloc (buffer, bsize);
-    }
-
-  bp = buffer;
-  if (prefix)
-    {
-      if (makelevel == 0)
-        sprintf (bp, "%s: ", program);
-      else
-        sprintf (bp, "%s[%u]: ", program, makelevel);
-      bp += strlen (buffer);
-    }
-
-  va_start (args, fmt);
-  vsprintf (bp, fmt, args);
-  va_end (args);
-
-  return buffer;
-}
-
-/* Return a formatted error message in a buffer.
-   LENGTH must be the maximum length of all format arguments, stringified.  */
-
-const char *
-error_s (unsigned int length, const gmk_floc *flocp, const char *fmt, ...)
-{
-  static char *buffer = NULL;
-  static unsigned int bsize = 0;
-  char *bp;
-  va_list args;
-
-  /* Compute the maximum buffer size we'll need, and make sure we have it.  */
-  length += (strlen (fmt) + strlen (program) + 4 + INTEGER_LENGTH + 2
-             + (flocp && flocp->filenm ? strlen (flocp->filenm) : 0));
-  if (length > bsize)
-    {
-      bsize = length * 2;
-      buffer = xrealloc (buffer, bsize);
-    }
-
-  bp = buffer;
-  if (flocp && flocp->filenm)
-    sprintf (bp, "%s:%lu: ", flocp->filenm, flocp->lineno);
-  else if (makelevel == 0)
-    sprintf (bp, "%s: ", program);
-  else
-    sprintf (bp, "%s[%u]: ", program, makelevel);
-  bp += strlen (bp);
-
-  va_start (args, fmt);
-  vsprintf (bp, fmt, args);
-  va_end (args);
-
-  return buffer;
-}
-\f
-/* Print a message on stdout.  We could use message_s() to format it but then
-   we'd need a va_list version...  */
-
-void
-message (int prefix, const char *fmt, ...)
-{
-  va_list args;
-
-  log_working_directory (1, 0);
-
-  if (fmt != 0)
-    {
-      if (prefix)
-        {
-          if (makelevel == 0)
-            printf ("%s: ", program);
-          else
-            printf ("%s[%u]: ", program, makelevel);
-        }
-      va_start (args, fmt);
-      vfprintf (stdout, fmt, args);
-      va_end (args);
-      putchar ('\n');
-    }
-
-  fflush (stdout);
-}
-
-/* Print an error message.  */
-
-void
-error (const gmk_floc *flocp, const char *fmt, ...)
-{
-  va_list args;
-
-  log_working_directory (1, 0);
-
-  if (flocp && flocp->filenm)
-    fprintf (stderr, "%s:%lu: ", flocp->filenm, flocp->lineno);
-  else if (makelevel == 0)
-    fprintf (stderr, "%s: ", program);
-  else
-    fprintf (stderr, "%s[%u]: ", program, makelevel);
-
-  va_start (args, fmt);
-  vfprintf (stderr, fmt, args);
-  va_end (args);
-
-  putc ('\n', stderr);
-  fflush (stderr);
-}
-
-/* Print an error message and exit.  */
-
-void
-fatal (const gmk_floc *flocp, const char *fmt, ...)
-{
-  va_list args;
-
-  log_working_directory (1, 0);
-
-  if (flocp && flocp->filenm)
-    fprintf (stderr, "%s:%lu: *** ", flocp->filenm, flocp->lineno);
-  else if (makelevel == 0)
-    fprintf (stderr, "%s: *** ", program);
-  else
-    fprintf (stderr, "%s[%u]: *** ", program, makelevel);
-
-  va_start (args, fmt);
-  vfprintf (stderr, fmt, args);
-  va_end (args);
-
-  fputs (_(".  Stop.\n"), stderr);
-
-  log_working_directory (0, 1);
-
-  die (2);
-}
-
 #ifndef HAVE_STRERROR
-
 #undef  strerror
-
 char *
 strerror (int errnum)
 {
@@ -356,24 +201,6 @@ strerror (int errnum)
   return buf;
 }
 #endif
-
-/* Print an error message from errno.  */
-
-void
-perror_with_name (const char *str, const char *name)
-{
-  error (NILF, _("%s%s: %s"), str, name, strerror (errno));
-}
-
-/* Print an error message from errno and exit.  */
-
-void
-pfatal_with_name (const char *name)
-{
-  fatal (NILF, _("%s: %s"), name, strerror (errno));
-
-  /* NOTREACHED */
-}
 \f
 /* Like malloc but get fatal error if memory is exhausted.  */
 /* Don't bother if we're using dmalloc; it provides these for us.  */
@@ -578,7 +405,6 @@ free_ns_chain (struct nameseq *ns)
 \f
 
 #if !HAVE_STRCASECMP && !HAVE_STRICMP && !HAVE_STRCMPI
-
 /* If we don't have strcasecmp() (from POSIX), or anything that can substitute
    for it, define our own version.  */
 
@@ -604,7 +430,6 @@ strcasecmp (const char *s1, const char *s2)
 #endif
 
 #if !HAVE_STRNCASECMP && !HAVE_STRNICMP && !HAVE_STRNCMPI
-
 /* If we don't have strncasecmp() (from POSIX), or anything that can
    substitute for it, define our own version.  */
 
diff --git a/output.c b/output.c
new file mode 100644 (file)
index 0000000..6d621c2
--- /dev/null
+++ b/output.c
@@ -0,0 +1,601 @@
+/* Output to stdout / stderr for GNU make
+Copyright (C) 2013 Free Software Foundation, Inc.
+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 of the License, 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
+this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "makeint.h"
+#include "job.h"
+
+/* GNU make no longer supports pre-ANSI89 environments.  */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#else
+# include <sys/file.h>
+#endif
+
+struct output *output_context = NULL;
+static unsigned int stdio_traced = 0;
+
+#define OUTPUT_NONE (-1)
+
+#define OUTPUT_ISSET(_out) ((_out)->out >= 0 || (_out)->err >= 0)
+
+/* I really want to move to gnulib.  However, this is a big undertaking
+   especially for non-UNIX platforms: how to get bootstrapping to work, etc.
+   I don't want to take the time to do it right now.  Use a hack to get a
+   useful version of vsnprintf() for Windows.  */
+#ifdef _MSC_VER
+#define va_copy(_d, _s) ((_d) = (_s))
+#define snprintf msc_vsnprintf
+static int
+msc_vsnprintf(char *str, size_t size, const char *format, va_list ap)
+{
+  int len = -1;
+
+  if (size > 0)
+    len = _vsnprintf_s (str, size, _TRUNCATE, format, ap);
+  if (len == -1)
+    len = _vscprintf (format, ap);
+
+  return len;
+}
+#endif
+
+/* Write a string to the current STDOUT or STDERR.  */
+static void
+_outputs (int is_err, const char *msg)
+{
+  if (! output_context || ! output_context->syncout)
+    {
+      FILE *f = is_err ? stderr : stdout;
+      fputs (msg, f);
+      fflush (f);
+    }
+  else
+    {
+      int fd = is_err ? output_context->err : output_context->out;
+      int len = strlen (msg);
+      int r;
+
+      EINTRLOOP (r, lseek (fd, 0, SEEK_END));
+      while (1)
+        {
+          EINTRLOOP (r, write (fd, msg, len));
+          if (r == len)
+            break;
+          if (r <= 0)
+            return;
+          len -= r;
+          msg += r;
+        }
+    }
+}
+\f
+/* Write a message indicating that we've just entered or
+   left (according to ENTERING) the current directory.  */
+
+static int
+log_working_directory (int entering)
+{
+  static char *buf = NULL;
+  static unsigned int len = 0;
+  unsigned int need;
+  const char *fmt;
+  char *p;
+
+  /* Only print if directory logging is enabled.  */
+  if (entering && ! print_directory_flag)
+    return 0;
+
+  /* Get enough space for the longest possible output.  */
+  need = strlen (program) + INTEGER_LENGTH + 2 + 1;
+  if (starting_directory)
+    need += strlen (starting_directory);
+
+  /* Use entire sentences to give the translators a fighting chance.  */
+  if (makelevel == 0)
+    if (starting_directory == 0)
+      if (entering)
+        fmt = _("%s: Entering an unknown directory\n");
+      else
+        fmt = _("%s: Leaving an unknown directory\n");
+    else
+      if (entering)
+        fmt = _("%s: Entering directory '%s'\n");
+      else
+        fmt = _("%s: Leaving directory '%s'\n");
+  else
+    if (starting_directory == 0)
+      if (entering)
+        fmt = _("%s[%u]: Entering an unknown directory\n");
+      else
+        fmt = _("%s[%u]: Leaving an unknown directory\n");
+    else
+      if (entering)
+        fmt = _("%s[%u]: Entering directory '%s'\n");
+      else
+        fmt = _("%s[%u]: Leaving directory '%s'\n");
+
+  need += strlen (fmt);
+
+  if (need > len)
+    {
+      buf = xrealloc (buf, need);
+      len = need;
+    }
+
+  p = buf;
+  if (print_data_base_flag)
+    {
+      *(p++) = '#';
+      *(p++) = ' ';
+    }
+
+  if (makelevel == 0)
+    if (starting_directory == 0)
+      sprintf (p, fmt , program);
+    else
+      sprintf (p, fmt, program, starting_directory);
+  else if (starting_directory == 0)
+    sprintf (p, fmt, program, makelevel);
+  else
+    sprintf (p, fmt, program, makelevel, starting_directory);
+
+  _outputs (0, buf);
+
+  return 1;
+}
+\f
+
+#ifdef OUTPUT_SYNC
+
+/* Semaphore for use in -j mode with output_sync. */
+static sync_handle_t sync_handle = -1;
+
+#define STREAM_OK(_s)       ((fcntl (fileno (_s), F_GETFD) != -1) || (errno != EBADF))
+
+#define FD_NOT_EMPTY(_f)    ((_f) != OUTPUT_NONE && lseek ((_f), 0, SEEK_END) > 0)
+
+/* Set up the sync handle.  Disables output_sync on error.  */
+static int
+sync_init ()
+{
+  int combined_output;
+
+#ifdef WINDOWS32
+  if ((!STREAM_OK (stdout) && !STREAM_OK (stderr))
+      || (sync_handle = create_mutex ()) == -1)
+    {
+      perror_with_name ("output-sync suppressed: ", "stderr");
+      output_sync = 0;
+    }
+  else
+    {
+      combined_output = same_stream (stdout, stderr);
+      prepare_mutex_handle_string (sync_handle);
+    }
+
+#else
+  if (STREAM_OK (stdout))
+    {
+      struct stat stbuf_o, stbuf_e;
+
+      sync_handle = fileno (stdout);
+      combined_output = (fstat (fileno (stdout), &stbuf_o) == 0
+                         && fstat (fileno (stderr), &stbuf_e) == 0
+                         && stbuf_o.st_dev == stbuf_e.st_dev
+                         && stbuf_o.st_ino == stbuf_e.st_ino);
+    }
+  else if (STREAM_OK (stderr))
+    sync_handle = fileno (stderr);
+  else
+    {
+      perror_with_name ("output-sync suppressed: ", "stderr");
+      output_sync = 0;
+    }
+#endif
+
+  return combined_output;
+}
+
+/* Support routine for output_sync() */
+static void
+pump_from_tmp (int from, FILE *to)
+{
+  static char buffer[8192];
+
+#ifdef WINDOWS32
+  int prev_mode;
+
+  /* "from" is opened by open_tmpfd, which does it in binary mode, so
+     we need the mode of "to" to match that.  */
+  prev_mode = _setmode (fileno (to), _O_BINARY);
+#endif
+
+  if (lseek (from, 0, SEEK_SET) == -1)
+    perror ("lseek()");
+
+  while (1)
+    {
+      int len;
+      EINTRLOOP (len, read (from, buffer, sizeof (buffer)));
+      if (len < 0)
+        perror ("read()");
+      if (len <= 0)
+        break;
+      if (fwrite (buffer, len, 1, to) < 1)
+        perror ("fwrite()");
+    }
+
+#ifdef WINDOWS32
+  /* Switch "to" back to its original mode, so that log messages by
+     Make have the same EOL format as without --output-sync.  */
+  _setmode (fileno (to), prev_mode);
+#endif
+}
+
+/* Obtain the lock for writing output.  */
+static void *
+acquire_semaphore (void)
+{
+  static struct flock fl;
+
+  fl.l_type = F_WRLCK;
+  fl.l_whence = SEEK_SET;
+  fl.l_start = 0;
+  fl.l_len = 1;
+  if (fcntl (sync_handle, F_SETLKW, &fl) != -1)
+    return &fl;
+  perror ("fcntl()");
+  return NULL;
+}
+
+/* Release the lock for writing output.  */
+static void
+release_semaphore (void *sem)
+{
+  struct flock *flp = (struct flock *)sem;
+  flp->l_type = F_UNLCK;
+  if (fcntl (sync_handle, F_SETLKW, flp) == -1)
+    perror ("fcntl()");
+}
+
+/* Synchronize the output of jobs in -j mode to keep the results of
+   each job together. This is done by holding the results in temp files,
+   one for stdout and potentially another for stderr, and only releasing
+   them to "real" stdout/stderr when a semaphore can be obtained. */
+
+void
+output_dump (struct output *out)
+{
+  int outfd_not_empty = FD_NOT_EMPTY (out->out);
+  int errfd_not_empty = FD_NOT_EMPTY (out->err);
+
+  if (outfd_not_empty || errfd_not_empty)
+    {
+      int logged = 0;
+
+      /* Try to acquire the semaphore.  If it fails, dump the output
+         unsynchronized; still better than silently discarding it.  */
+      void *sem = acquire_semaphore ();
+
+      /* Log the working directory, if we need to.  */
+      if (out->syncout)
+        logged = log_working_directory (1);
+
+      /* We've entered the "critical section" during which a lock is held.  We
+         want to keep it as short as possible.  */
+      if (outfd_not_empty)
+        pump_from_tmp (out->out, stdout);
+      if (errfd_not_empty && out->err != out->out)
+        pump_from_tmp (out->err, stderr);
+
+      if (logged)
+        log_working_directory (0);
+
+      /* Exit the critical section.  */
+      if (sem)
+        release_semaphore (sem);
+
+      /* Truncate and reset the output, in case we use it again.  */
+      if (out->out != OUTPUT_NONE)
+        {
+          int e;
+          lseek (out->out, 0, SEEK_SET);
+          EINTRLOOP (e, ftruncate (out->out, 0));
+        }
+      if (out->err != OUTPUT_NONE && out->err != out->out)
+        {
+          int e;
+          lseek (out->err, 0, SEEK_SET);
+          EINTRLOOP (e, ftruncate (out->err, 0));
+        }
+    }
+}
+
+/* Adds file descriptors to the child structure to support output_sync; one
+   for stdout and one for stderr as long as they are open.  If stdout and
+   stderr share a device they can share a temp file too.
+   Will reset output_sync on error.  */
+static void
+setup_tmpfile (struct output *out)
+{
+  /* Is make's stdout going to the same place as stderr?  */
+  static int combined_output = -1;
+
+  if (combined_output < 0)
+    combined_output = sync_init ();
+
+  if (STREAM_OK (stdout))
+    {
+      int fd = open_tmpfd ();
+      if (fd < 0)
+        goto error;
+      CLOSE_ON_EXEC (fd);
+      out->out = fd;
+    }
+
+  if (STREAM_OK (stderr))
+    {
+      if (out->out != OUTPUT_NONE && combined_output)
+        out->err = out->out;
+      else
+        {
+          int fd = open_tmpfd ();
+          if (fd < 0)
+            goto error;
+          CLOSE_ON_EXEC (fd);
+          out->err = fd;
+        }
+    }
+
+  return;
+
+  /* If we failed to create a temp file, disable output sync going forward.  */
+ error:
+  output_close (out);
+  output_sync = 0;
+}
+#endif /* OUTPUT_SYNC */
+\f
+
+void
+output_init (struct output *out, unsigned int syncout)
+{
+  out->out = out->err = OUTPUT_NONE;
+  out->syncout = !!syncout;
+}
+
+void
+output_close (struct output *out)
+{
+  if (! out)
+    {
+      if (stdio_traced)
+        log_working_directory (0);
+      return;
+    }
+
+#ifdef OUTPUT_SYNC
+  output_dump (out);
+#endif
+
+  if (out->out >= 0)
+    close (out->out);
+  if (out->err >= 0 && out->err != out->out)
+    close (out->err);
+
+  output_init (out, 0);
+}
+
+/* We're about to run a sub-process so ensure we've got our output set up.  */
+void
+output_start ()
+{
+  if (! output_context)
+    {
+      if (! stdio_traced)
+        stdio_traced = log_working_directory (1);
+    }
+#ifdef OUTPUT_SYNC
+  else if (output_context->syncout && ! OUTPUT_ISSET(output_context))
+    setup_tmpfile (output_context);
+#endif
+}
+
+void
+outputs (int is_err, const char *msg)
+{
+  /* For stdio, an empty msg means we're about to invoke a shell command,
+     which may or may not generate output, so log the directory.  */
+  if (! output_context && ! stdio_traced)
+    stdio_traced = log_working_directory (1);
+
+  /* Don't bother to do anything with empty strings.  */
+  if (! msg || *msg == '\0')
+    return;
+
+#ifdef OUTPUT_SYNC
+  if (output_context)
+    {
+      /* Create a temporary file to write to, if necessary.  */
+      if (output_context->syncout && ! OUTPUT_ISSET(output_context))
+        setup_tmpfile (output_context);
+    }
+#endif
+
+  _outputs (is_err, msg);
+}
+\f
+
+/* Return formatted string buffers.
+   If we move to gnulib we can use vasnprintf() etc. to make this simpler.
+   Note these functions use a static buffer, so each call overwrites the
+   results of the previous call.  */
+
+static struct fmtstring
+  {
+    char *buffer;
+    unsigned int size;
+    unsigned int len;
+  } fmtbuf = { NULL, 0, 0 };
+
+/* Concatenate a formatted string onto the format buffer.  */
+static const char *
+vfmtconcat (const char *fmt, va_list args)
+{
+  va_list vcopy;
+  int tot;
+  int unused = fmtbuf.size - fmtbuf.len;
+
+  va_copy(vcopy, args);
+
+  tot = vsnprintf (&fmtbuf.buffer[fmtbuf.len], unused, fmt, args);
+  assert (tot >= 0);
+
+  if (tot >= unused)
+    {
+      fmtbuf.size += tot * 2;
+      fmtbuf.buffer = xrealloc (fmtbuf.buffer, fmtbuf.size);
+
+      unused = fmtbuf.size - fmtbuf.len;
+      tot = vsnprintf (&fmtbuf.buffer[fmtbuf.len], unused, fmt, vcopy);
+    }
+
+  va_end(vcopy);
+
+  fmtbuf.len += tot;
+
+  return fmtbuf.buffer;
+}
+
+static const char *
+fmtconcat (const char *fmt, ...)
+{
+  const char *p;
+  va_list args;
+
+  va_start (args, fmt);
+  p = vfmtconcat (fmt, args);
+  va_end (args);
+
+  return p;
+}
+\f
+/* Print a message on stdout.  */
+
+void
+message (int prefix, const char *fmt, ...)
+{
+  va_list args;
+
+  assert (fmt != NULL);
+
+  fmtbuf.len = 0;
+
+  if (prefix)
+    {
+      if (makelevel == 0)
+        fmtconcat ("%s: ", program);
+      else
+        fmtconcat ("%s[%u]: ", program, makelevel);
+    }
+
+  va_start (args, fmt);
+  vfmtconcat (fmt, args);
+  va_end (args);
+
+  fmtconcat ("\n");
+
+  outputs (0, fmtbuf.buffer);
+}
+
+/* Print an error message.  */
+
+void
+error (const gmk_floc *flocp, const char *fmt, ...)
+{
+  va_list args;
+
+  assert (fmt != NULL);
+
+  fmtbuf.len = 0;
+
+  if (flocp && flocp->filenm)
+    fmtconcat ("%s:%lu: ", flocp->filenm, flocp->lineno);
+  else if (makelevel == 0)
+    fmtconcat ("%s: ", program);
+  else
+    fmtconcat ("%s[%u]: ", program, makelevel);
+
+  va_start (args, fmt);
+  vfmtconcat (fmt, args);
+  va_end (args);
+
+  fmtconcat ("\n");
+
+  outputs (1, fmtbuf.buffer);
+}
+
+/* Print an error message and exit.  */
+
+void
+fatal (const gmk_floc *flocp, const char *fmt, ...)
+{
+  va_list args;
+
+  assert (fmt != NULL);
+
+  fmtbuf.len = 0;
+
+  if (flocp && flocp->filenm)
+    fmtconcat ("%s:%lu: *** ", flocp->filenm, flocp->lineno);
+  else if (makelevel == 0)
+    fmtconcat ("%s: *** ", program);
+  else
+    fmtconcat ("%s[%u]: *** ", program, makelevel);
+
+  va_start (args, fmt);
+  vfmtconcat (fmt, args);
+  va_end (args);
+
+  fmtconcat (_(".  Stop.\n"));
+  outputs (1, fmtbuf.buffer);
+
+  die (2);
+}
+
+/* Print an error message from errno.  */
+
+void
+perror_with_name (const char *str, const char *name)
+{
+  error (NILF, _("%s%s: %s"), str, name, strerror (errno));
+}
+
+/* Print an error message from errno and exit.  */
+
+void
+pfatal_with_name (const char *name)
+{
+  fatal (NILF, _("%s: %s"), name, strerror (errno));
+
+  /* NOTREACHED */
+}
diff --git a/output.h b/output.h
new file mode 100644 (file)
index 0000000..a918504
--- /dev/null
+++ b/output.h
@@ -0,0 +1,37 @@
+/* Output to stdout / stderr for GNU make
+Copyright (C) 2013 Free Software Foundation, Inc.
+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 of the License, 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
+this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+struct output
+  {
+    int out;
+    int err;
+    unsigned int syncout:1;     /* True if we want to synchronize output.  */
+ };
+
+extern struct output *output_context;
+
+#define OUTPUT_SET(_new)    do{ if ((_new)->syncout) output_context = (_new); }while(0)
+#define OUTPUT_UNSET()      do{ output_context = NULL; }while(0)
+
+void output_init (struct output *out, unsigned int syncout);
+void output_close (struct output *out);
+
+void output_start (void);
+void outputs (int is_err, const char *msg);
+
+#ifdef OUTPUT_SYNC
+void output_dump (struct output *out);
+#endif
index cfe2189a90c1405662f77d6d14ebc123e71149cd..ba156ee79fc0565847b136b570c26295ebdd8c9e 100644 (file)
@@ -31,6 +31,7 @@ job.h
 load.c
 main.c
 misc.c
+output.c
 read.c
 remake.c
 remote-cstms.c
index e08458f9eb51b9e36245b5695cb5bd8296b948c7..8d6c635d660f7e3a65441a6657b74d006abcc98b 100644 (file)
@@ -19,7 +19,6 @@ You should have received a copy of the GNU General Public License along with
 this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "makeint.h"
-#include "job.h"
 #include "filedef.h"
 #include "commands.h"
 #include "job.h"
index 587ff647feb69f901aa8cd826bc234496d486e9d..5fa1798632a3a63e4128f620969dd434c6a1b0b5 100644 (file)
@@ -1,3 +1,10 @@
+2013-09-12  Paul Smith  <psmith@gnu.org>
+
+       * scripts/features/output-sync: Modify for output sync behavior.
+       * scripts/variables/MAKE_RESTARTS: Ditto.
+       * scripts/variables/MAKEFLAGS: Remove mode for --trace.
+       * scripts/variables/GNUMAKEFLAGS: Ditto.
+
 2013-07-22  Paul Smith  <psmith@gnu.org>
 
        * scripts/features/rule_glob: Add tests for wildcards in rules.
index b4541fb1eb4558d20f946fec003f5ada191ea20c..55af0a1ba02066027c1294ead71c5131efeeca9f 100644 (file)
@@ -135,6 +135,8 @@ foo: end
 #MAKE#[1]: Entering directory '#PWD#/bar'
 bar: start
 bar: end
+#MAKE#[1]: Leaving directory '#PWD#/bar'
+#MAKE#[1]: Entering directory '#PWD#/bar'
 baz: start
 baz: end
 #MAKE#[1]: Leaving directory '#PWD#/bar'\n", 0, 6);
@@ -146,6 +148,9 @@ baz: end
 
 unlink(@syncfiles);
 run_make_test(qq!
+x=1
+\$xMAKEFLAGS += --no-print-directory
+
 all: make-foo make-bar
 
 make-foo: ; \$(MAKE) -C foo
@@ -157,14 +162,28 @@ $sleep_command 1 ; #MAKEPATH# -C bar
 #MAKE#[1]: Entering directory '#PWD#/bar'
 bar: start
 bar: end
+#MAKE#[1]: Leaving directory '#PWD#/bar'
 #MAKE#[1]: Entering directory '#PWD#/foo'
 foo: start
 foo: end
 #MAKE#[1]: Leaving directory '#PWD#/foo'
+#MAKE#[1]: Entering directory '#PWD#/bar'
 baz: start
 baz: end
 #MAKE#[1]: Leaving directory '#PWD#/bar'\n", 0, 6);
 
+# Rerun but this time suppress the directory tracking
+unlink(@syncfiles);
+run_make_test(undef, '-j --output-sync=target x=',
+              "#MAKEPATH# -C foo
+$sleep_command 1 ; #MAKEPATH# -C bar
+bar: start
+bar: end
+foo: start
+foo: end
+baz: start
+baz: end\n", 0, 6);
+
 # Test that messages from make itself are enclosed with
 # "Entering/Leaving directory" messages.
 unlink(@syncfiles);
@@ -209,10 +228,14 @@ make-bar: ; $sleep_command 1 ; \$(MAKE) -C bar bar-job!,
 $sleep_command 1 ; #MAKEPATH# -C bar bar-job
 #MAKE#[1]: Entering directory '#PWD#/foo'
 foo: start
+#MAKE#[1]: Leaving directory '#PWD#/foo'
 #MAKE#[1]: Entering directory '#PWD#/bar'
 bar: start
+#MAKE#[1]: Leaving directory '#PWD#/bar'
+#MAKE#[1]: Entering directory '#PWD#/bar'
 bar: end
 #MAKE#[1]: Leaving directory '#PWD#/bar'
+#MAKE#[1]: Entering directory '#PWD#/foo'
 foo: end
 #MAKE#[1]: Leaving directory '#PWD#/foo'\n", 0, 6);
 
index c4df1c1a1fa7d8dcb47bcefc3a7d84a84ecface5..edef66e3d96a7727c11fe9caac001bb87611fba2 100644 (file)
@@ -14,11 +14,13 @@ all: ; @echo $(MAKEFLAGS)
 
 # Long arguments mean everything is prefixed with "-"
 
-$extraENV{'GNUMAKEFLAGS'} = '--no-print-directory -e -r -R --trace=none --trace=dir';
+$extraENV{'GNUMAKEFLAGS'} = '--no-print-directory -e -r -R --trace';
 
 run_make_test(q!
 all: ; @echo $(MAKEFLAGS)
 !,
-              '', 'erR --trace=none --trace=dir --trace=none --trace=dir --no-print-directory');
+              '', "#MAKEFILE#:2: target 'all' does not exist
+echo erR --trace --no-print-directory
+erR --trace --no-print-directory");
 
 1;
index b41d37cdb1218694cc35d02e0dd950a0703132e7..8a5d0f6a9f061e83d5db0f7ce6d1ed7321bcf8e6 100644 (file)
@@ -14,7 +14,9 @@ all: ; @echo $(MAKEFLAGS)
 run_make_test(q!
 all: ; @echo $(MAKEFLAGS)
 !,
-              '--no-print-directory -e -r -R --trace=none --trace=dir', 'erR --trace=none --trace=dir --no-print-directory');
+              '--no-print-directory -e -r -R --trace', "#MAKEFILE#:2: target 'all' does not exist
+echo erR --trace --no-print-directory
+erR --trace --no-print-directory");
 
 
 # Recursive invocations of make should accumulate MAKEFLAGS values.
index 711c62737a1d86c1563546a2d26f0aeaacfc71aa..ef8e368f9d62cf536bc86734fc77aadae52859a6 100644 (file)
@@ -52,8 +52,8 @@ MAKE_RESTARTS=1
 foo.x:1: bar.x: No such file or directory
 MAKE_RESTARTS=2
 recurse MAKE_RESTARTS=
-MAKE_RESTARTS=
 #MAKE#[1]: Entering directory '#PWD#'
+MAKE_RESTARTS=
 all MAKE_RESTARTS=
 #MAKE#[1]: Leaving directory '#PWD#'");