diff options
-rw-r--r-- | ChangeLog | 41 | ||||
-rw-r--r-- | Makefile.am | 6 | ||||
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | NMakefile.template | 1 | ||||
-rw-r--r-- | SMakefile.template | 4 | ||||
-rw-r--r-- | build_w32.bat | 11 | ||||
-rw-r--r-- | doc/make.texi | 91 | ||||
-rw-r--r-- | dosbuild.bat | 3 | ||||
-rw-r--r-- | function.c | 7 | ||||
-rw-r--r-- | job.c | 387 | ||||
-rw-r--r-- | job.h | 22 | ||||
-rw-r--r-- | main.c | 100 | ||||
-rw-r--r-- | make.1 | 22 | ||||
-rw-r--r-- | make.lnk | 2 | ||||
-rw-r--r-- | make_msvc_net2003.vcproj | 6 | ||||
-rw-r--r-- | makefile.vms | 7 | ||||
-rw-r--r-- | makeint.h | 10 | ||||
-rw-r--r-- | misc.c | 177 | ||||
-rw-r--r-- | output.c | 601 | ||||
-rw-r--r-- | output.h | 37 | ||||
-rw-r--r-- | po/POTFILES.in | 1 | ||||
-rw-r--r-- | remote-cstms.c | 1 | ||||
-rw-r--r-- | tests/ChangeLog | 7 | ||||
-rw-r--r-- | tests/scripts/features/output-sync | 23 | ||||
-rw-r--r-- | tests/scripts/variables/GNUMAKEFLAGS | 6 | ||||
-rw-r--r-- | tests/scripts/variables/MAKEFLAGS | 4 | ||||
-rw-r--r-- | tests/scripts/variables/MAKE_RESTARTS | 2 |
27 files changed, 893 insertions, 689 deletions
@@ -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) @@ -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
@@ -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) @@ -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') @@ -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 */ @@ -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); -} @@ -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 @@ -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 @@ -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; @@ -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#'"); |