summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Smith <psmith@gnu.org>2013-09-12 04:07:52 -0400
committerPaul Smith <psmith@gnu.org>2013-09-12 04:07:52 -0400
commitdeff9dacc97cc20015d3018992f2c77cb7fab102 (patch)
tree3da393310f9936a22aa211e6870a23e98b6fdebe
parent40a49f244da5b417af8bede84ac221cee2318d88 (diff)
downloadgunmake-deff9dacc97cc20015d3018992f2c77cb7fab102.tar.gz
Enhance the output sync mode.
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.
-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#'");