summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog41
-rw-r--r--Makefile.am6
-rw-r--r--NEWS3
-rw-r--r--NMakefile.template1
-rw-r--r--SMakefile.template4
-rw-r--r--build_w32.bat11
-rw-r--r--doc/make.texi91
-rw-r--r--dosbuild.bat3
-rw-r--r--function.c7
-rw-r--r--job.c387
-rw-r--r--job.h22
-rw-r--r--main.c100
-rw-r--r--make.122
-rw-r--r--make.lnk2
-rw-r--r--make_msvc_net2003.vcproj6
-rw-r--r--makefile.vms7
-rw-r--r--makeint.h10
-rw-r--r--misc.c177
-rw-r--r--output.c601
-rw-r--r--output.h37
-rw-r--r--po/POTFILES.in1
-rw-r--r--remote-cstms.c1
-rw-r--r--tests/ChangeLog7
-rw-r--r--tests/scripts/features/output-sync23
-rw-r--r--tests/scripts/variables/GNUMAKEFLAGS6
-rw-r--r--tests/scripts/variables/MAKEFLAGS4
-rw-r--r--tests/scripts/variables/MAKE_RESTARTS2
27 files changed, 893 insertions, 689 deletions
diff --git a/ChangeLog b/ChangeLog
index f8ab2ca..c4319ac 100644
--- 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
diff --git a/Makefile.am b/Makefile.am
index 80afc76..5fd1b62 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 e1785e1..cf234b7 100644
--- 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.
diff --git a/NMakefile.template b/NMakefile.template
index 4a8b897..1b6f6bd 100644
--- a/NMakefile.template
+++ b/NMakefile.template
@@ -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 \
diff --git a/SMakefile.template b/SMakefile.template
index d15f1f6..4dfe691 100644
--- a/SMakefile.template
+++ b/SMakefile.template
@@ -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
diff --git a/build_w32.bat b/build_w32.bat
index 3e35094..148dfac 100644
--- a/build_w32.bat
+++ b/build_w32.bat
@@ -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
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
echo WinDebug\job.obj >>link.dbg
+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
+echo WinDebug\output.obj >>link.dbg
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
echo WinDebug\read.obj >>link.dbg
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
@@ -166,7 +168,7 @@ echo WinDebug\guile.obj >>link.dbg
:LinkDbg
echo off
echo "Linking WinDebug/%make%.exe"
-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
+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
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
link.exe /NOLOGO /SUBSYSTEM:console /INCREMENTAL:yes /PDB:.\WinDebug/%make%.pdb /DEBUG /OUT:.\WinDebug/%make%.exe @link.dbg
if not exist .\WinDebug/%make%.exe echo "WinDebug build failed"
@@ -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
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
echo WinRel\job.obj >>link.rel
+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
+echo WinRel\output.obj >>link.rel
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
echo WinRel\read.obj >>link.rel
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
@@ -243,7 +247,7 @@ echo WinRel\guile.obj >>link.rel
:LinkRel
echo off
echo "Linking WinRel/%make%.exe"
-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
+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
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
link.exe /NOLOGO /SUBSYSTEM:console /INCREMENTAL:no /PDB:.\WinRel/%make%.pdb /OUT:.\WinRel/%make%.exe @link.rel
if not exist .\WinRel/%make%.exe echo "WinRel build failed"
@@ -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
gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c getopt1.c
gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c job.c
+gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c output.c
gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c read.c
gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c version.c
gcc -mthreads -Wall -gdwarf-2 -g3 %OPT% -I. -I./glob -I./w32/include -DWINDOWS32 -DHAVE_CONFIG_H -c getopt.c
@@ -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
Rem the API changes in binary-incompatible manner.
@echo on
-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
+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
@GoTo BuildEnd
:Usage
echo Usage: %0 [options] [gcc]
diff --git a/doc/make.texi b/doc/make.texi
index d1ceefb..975f0b3 100644
--- a/doc/make.texi
+++ b/doc/make.texi
@@ -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}
@@ -11388,6 +11389,11 @@ 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}.
This functionality was provided in GNU @code{make} by the
@@ -11533,11 +11539,6 @@ 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}.
@end itemize
@@ -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
diff --git a/dosbuild.bat b/dosbuild.bat
index 3f5b9b2..4091463 100644
--- a/dosbuild.bat
+++ b/dosbuild.bat
@@ -20,6 +20,7 @@ echo Building Make for MSDOS
rem Echo ON so they will see what is going on.
@echo on
gcc -c -I. -I./glob -DHAVE_CONFIG_H -O2 -g commands.c -o commands.o
+gcc -c -I. -I./glob -DHAVE_CONFIG_H -O2 -g output.c -o output.o
gcc -c -I. -I./glob -DHAVE_CONFIG_H -O2 -g job.c -o job.o
gcc -c -I. -I./glob -DHAVE_CONFIG_H -O2 -g dir.c -o dir.o
gcc -c -I. -I./glob -DHAVE_CONFIG_H -O2 -g file.c -o file.o
@@ -51,7 +52,7 @@ ar rv libglob.a glob.o fnmatch.o
@echo off
cd ..
echo commands.o > respf.$$$
-for %%f in (job dir file misc main read remake rule implicit default variable) do echo %%f.o >> respf.$$$
+for %%f in (job output dir file misc main read remake rule implicit default variable) do echo %%f.o >> respf.$$$
for %%f in (expand function vpath hash strcache version ar arscan signame remote-stub getopt getopt1) do echo %%f.o >> respf.$$$
echo glob/libglob.a >> respf.$$$
rem gcc -c -I. -I./glob -DHAVE_CONFIG_H -O2 -g guile.c -o guile.o
diff --git a/function.c b/function.c
index 9eabd73..3379c90 100644
--- a/function.c
+++ b/function.c
@@ -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 16c164e..28c8e37 100644
--- 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 */
+
#ifdef WINDOWS32
/*
* The macro which references this function is defined in makeint.h.
@@ -472,50 +460,12 @@ is_bourne_compatible_shell (const char *path)
}
-/* 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 ();
}
@@ -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;
}
@@ -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 ba785a7..967fa2b 100644
--- 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 2cfc49b..4785514 100644
--- 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. */
@@ -740,29 +735,6 @@ decode_debug_flags (void)
}
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)
{
const char **pp;
@@ -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);
}
-
-/* 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 df2a169..87ee42e 100644
--- 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
diff --git a/make.lnk b/make.lnk
index b4080a7..0d983bf 100644
--- 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
diff --git a/make_msvc_net2003.vcproj b/make_msvc_net2003.vcproj
index 50492ad..956b321 100644
--- a/make_msvc_net2003.vcproj
+++ b/make_msvc_net2003.vcproj
@@ -172,6 +172,9 @@
RelativePath=".\job.c">
</File>
<File
+ RelativePath=".\output.c">
+ </File>
+ <File
RelativePath=".\main.c">
</File>
<File
@@ -278,6 +281,9 @@
RelativePath=".\job.h">
</File>
<File
+ RelativePath=".\output.h">
+ </File>
+ <File
RelativePath=".\makeint.h">
</File>
<File
diff --git a/makefile.vms b/makefile.vms
index 4534a44..e5950b7 100644
--- a/makefile.vms
+++ b/makefile.vms
@@ -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
diff --git a/makeint.h b/makeint.h
index b51e914..2674eaf 100644
--- a/makeint.h
+++ b/makeint.h
@@ -424,10 +424,6 @@ extern struct rlimit stack_limit;
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 8ae67b9..4858117 100644
--- 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, ...)
}
-/* 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;
-}
-
-/* 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 */
-}
/* 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)
#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
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;
+ }
+ }
+}
+
+/* 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;
+}
+
+
+#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 */
+
+
+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);
+}
+
+
+/* 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;
+}
+
+/* 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
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
diff --git a/po/POTFILES.in b/po/POTFILES.in
index cfe2189..ba156ee 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -31,6 +31,7 @@ job.h
load.c
main.c
misc.c
+output.c
read.c
remake.c
remote-cstms.c
diff --git a/remote-cstms.c b/remote-cstms.c
index e08458f..8d6c635 100644
--- a/remote-cstms.c
+++ b/remote-cstms.c
@@ -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"
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 587ff64..5fa1798 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -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.
diff --git a/tests/scripts/features/output-sync b/tests/scripts/features/output-sync
index b4541fb..55af0a1 100644
--- a/tests/scripts/features/output-sync
+++ b/tests/scripts/features/output-sync
@@ -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);
diff --git a/tests/scripts/variables/GNUMAKEFLAGS b/tests/scripts/variables/GNUMAKEFLAGS
index c4df1c1..edef66e 100644
--- a/tests/scripts/variables/GNUMAKEFLAGS
+++ b/tests/scripts/variables/GNUMAKEFLAGS
@@ -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;
diff --git a/tests/scripts/variables/MAKEFLAGS b/tests/scripts/variables/MAKEFLAGS
index b41d37c..8a5d0f6 100644
--- a/tests/scripts/variables/MAKEFLAGS
+++ b/tests/scripts/variables/MAKEFLAGS
@@ -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.
diff --git a/tests/scripts/variables/MAKE_RESTARTS b/tests/scripts/variables/MAKE_RESTARTS
index 711c627..ef8e368 100644
--- a/tests/scripts/variables/MAKE_RESTARTS
+++ b/tests/scripts/variables/MAKE_RESTARTS
@@ -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#'");