From 2cc7d57a17490d96a310a8a329b86bca10e564c1 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 21 Nov 2008 15:50:37 -0700 Subject: [PATCH] Clean up AS_MESSAGE_LOG_FD usage. * lib/m4sugar/m4sh.m4 (AS_MESSAGE_FD, AS_MESSAGE_LOG_FD) (AS_ORIGINAL_STDIN_FD): Provide default M4sh values. (_AS_ECHO_LOG, AS_MESSAGE, _AS_ERROR_PREPARE, AS_ERROR): Simplify usage. (AS_INIT_GENERATED): Don't shuffle an unchanged AS_MESSAGE_FD. * tests/m4sh.at (AS@&t@_INIT_GENERATED): Update test. (AS@&t@_MESSAGE_FD): New test. * doc/autoconf.texi (Initialization Macros) : Give more details about fd manipulation. (File Descriptor Macros): Describe M4sh defaults for the fds. Signed-off-by: Eric Blake --- ChangeLog | 14 ++++++++ doc/autoconf.texi | 35 +++++++++++++------ lib/m4sugar/m4sh.m4 | 46 +++++++++++++++++++------ tests/m4sh.at | 84 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 157 insertions(+), 22 deletions(-) diff --git a/ChangeLog b/ChangeLog index ec791767..d72ad1fb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2008-11-21 Eric Blake + + Clean up AS_MESSAGE_LOG_FD usage. + * lib/m4sugar/m4sh.m4 (AS_MESSAGE_FD, AS_MESSAGE_LOG_FD) + (AS_ORIGINAL_STDIN_FD): Provide default M4sh values. + (_AS_ECHO_LOG, AS_MESSAGE, _AS_ERROR_PREPARE, AS_ERROR): Simplify + usage. + (AS_INIT_GENERATED): Don't shuffle an unchanged AS_MESSAGE_FD. + * tests/m4sh.at (AS@&t@_INIT_GENERATED): Update test. + (AS@&t@_MESSAGE_FD): New test. + * doc/autoconf.texi (Initialization Macros) : + Give more details about fd manipulation. + (File Descriptor Macros): Describe M4sh defaults for the fds. + 2008-11-21 Eric Blake Use shell function for AS_ERROR. diff --git a/doc/autoconf.texi b/doc/autoconf.texi index e8cab411..957b8be5 100644 --- a/doc/autoconf.texi +++ b/doc/autoconf.texi @@ -12500,15 +12500,21 @@ populates the child script with information learned from the parent (thus, the emitted code is equivalent in effect, but more efficient, than the code output by @code{AS_INIT}, @code{AS_BOURNE_COMPATIBLE}, and @code{AS_SHELL_SANITIZE}). If present, @var{comment} is output near the -beginning of the child, prior to the shell initialization code. The +beginning of the child, prior to the shell initialization code, and is +subject to parameter expansion, command substitution, and backslash +quote removal. The parent script should check the exit status after this macro, in case @var{file} could not be properly created (for example, if the disk was full). If successfully created, the parent script can then proceed to append additional M4sh constructs into the child script. -Note that the child script starts life without a log file open, so you +Note that the child script starts life without a log file open, so if +the parent script uses logging (@pxref{AS_MESSAGE_LOG_FD}), you must temporarily disable any attempts to use the log file until after -emitting code to open a log within the child. Currently, the suggested +emitting code to open a log within the child. On the other hand, if the +parent script has @code{AS_MESSAGE_FD} redirected somewhere besides +@samp{1}, then the child script already has code that copies stdout to +that descriptor. Currently, the suggested idiom for writing a M4sh shell script from within another script is: @example @@ -12583,27 +12589,36 @@ level macros as described below. @defmac AS_MESSAGE_FD @asindex{MESSAGE_FD} The file descriptor for @samp{checking for...} messages and results. -Normally this directs messages to the standard output, however when -@command{configure} is run with the @option{-q} option, messages sent to -@code{AS_MESSAGE_FD} are discarded. +By default, @code{AS_INIT} sets this to @samp{1} for standalone M4sh +clients. However, @code{AC_INIT} shuffles things around to another file +descriptor, in order to allow the @option{-q} option of +@command{configure} to choose whether messages should go to the script's +standard output or be discarded. If you want to display some messages, consider using one of the printing macros (@pxref{Printing Messages}) instead. Copies of messages output via these macros are also recorded in @file{config.log}. @end defmac +@anchor{AS_MESSAGE_LOG_FD} @defmac AS_MESSAGE_LOG_FD @asindex{MESSAGE_LOG_FD} - -The file descriptor for messages logged to @file{config.log}. Macros -that run tools, like @code{AC_COMPILE_IFELSE} (@pxref{Running the +This must either be empty, or expand to a file descriptor for log +messages. By default, @code{AS_INIT} sets this macro to the empty +string for standalone M4sh clients, thus disabling logging. However, +@code{AC_INIT} shuffles things around so that both @command{configure} +and @command{config.status} use @file{config.log} for log messages. +Macros that run tools, like @code{AC_COMPILE_IFELSE} (@pxref{Running the Compiler}), redirect all output to this descriptor. You may want to do so if you develop such a low-level macro. @end defmac @defmac AS_ORIGINAL_STDIN_FD @asindex{ORIGINAL_STDIN_FD} -The file descriptor for the original standard input. +This must expand to a file descriptor for the original standard input. +By default, @code{AS_INIT} sets this macro to @samp{0} for standalone +M4sh clients. However, @code{AC_INIT} shuffles things around for +safety. When @command{configure} runs, it may accidentally execute an interactive command that has the same name as the non-interactive meant diff --git a/lib/m4sugar/m4sh.m4 b/lib/m4sugar/m4sh.m4 index f3dc4aca..0faa9341 100644 --- a/lib/m4sugar/m4sh.m4 +++ b/lib/m4sugar/m4sh.m4 @@ -658,8 +658,30 @@ m4_defun([AS_UNSET], ## 3. Error and warnings at the shell level. ## ## ------------------------------------------ ## -# If AS_MESSAGE_LOG_FD is defined, shell messages are duplicated there -# too. + +# AS_MESSAGE_FD +# ------------- +# Must expand to the fd where messages will be sent. Defaults to 1, +# although a script may reassign this value and use exec to either +# copy stdout to the new fd, or open the new fd on /dev/null. +m4_define([AS_MESSAGE_FD], [1]) + +# AS_MESSAGE_LOG_FD +# ----------------- +# Must expand to either the empty string (when no logging is +# performed), or to the fd of a log file. Defaults to empty, although +# a script may reassign this value and use exec to open a log. When +# not empty, messages to AS_MESSAGE_FD are duplicated to the log, +# along with a LINENO reference. +m4_define([AS_MESSAGE_LOG_FD]) + + +# AS_ORIGINAL_STDIN_FD +# -------------------- +# Must expand to the fd of the script's original stdin. Defaults to +# 0, although the script may reassign this value and use exec to +# shuffle fd's. +m4_define([AS_ORIGINAL_STDIN_FD], [0]) # AS_ESCAPE(STRING, [CHARS = $"`\]) @@ -730,7 +752,7 @@ m4_define([_AS_ECHO], # Log the string to AS_MESSAGE_LOG_FD. m4_defun_init([_AS_ECHO_LOG], [AS_REQUIRE([_AS_LINENO_PREPARE])], -[_AS_ECHO([$as_me:${as_lineno-$LINENO}: $1], [AS_MESSAGE_LOG_FD])]) +[_AS_ECHO([$as_me:${as_lineno-$LINENO}: $1], AS_MESSAGE_LOG_FD)]) # _AS_ECHO_N_PREPARE @@ -765,10 +787,11 @@ m4_define([_AS_ECHO_N], # AS_MESSAGE(STRING, [FD = AS_MESSAGE_FD]) # ---------------------------------------- -# Output "`basename $0`: "STRING to the open file FD. +# Output "`basename $0`: STRING" to the open file FD, and if logging +# is enabled, copy it to the log with a reference to LINENO. m4_defun_init([AS_MESSAGE], [AS_REQUIRE([_AS_ME_PREPARE])], -[m4_ifset([AS_MESSAGE_LOG_FD], +[m4_ifval(AS_MESSAGE_LOG_FD, [{ _AS_ECHO_LOG([$1]) _AS_ECHO([$as_me: $1], [$2]);}], [_AS_ECHO([$as_me: $1], [$2])])[]]) @@ -792,15 +815,15 @@ m4_define([AS_WARN], # otherwise, assume the entire script does not do logging. m4_define([_AS_ERROR_PREPARE], [AS_REQUIRE_SHELL_FN([as_fn_error], - [AS_FUNCTION_DESCRIBE([as_fn_error], [ERROR]m4_ifset([AS_MESSAGE_LOG_FD], + [AS_FUNCTION_DESCRIBE([as_fn_error], [ERROR]m4_ifval(AS_MESSAGE_LOG_FD, [[ [[LINENO LOG_FD]]]]), [Output "`basename @S|@0`: error: ERROR" to stderr.] -m4_ifset([AS_MESSAGE_LOG_FD], +m4_ifval(AS_MESSAGE_LOG_FD, [[If LINENO and LOG_FD are provided, also output the error to LOG_FD, referencing LINENO.]]) [Then exit the script with status $?, using 1 if that was 0.])], [ as_status=$?; test $as_status -eq 0 && as_status=1 -m4_ifset([AS_MESSAGE_LOG_FD], +m4_ifval(AS_MESSAGE_LOG_FD, [m4_pushdef([AS_MESSAGE_LOG_FD], [$[3]])dnl if test "$[3]"; then AS_LINENO_PUSH([$[2]]) @@ -819,7 +842,7 @@ m4_defun_init([AS_ERROR], [m4_append_uniq([_AS_CLEANUP], [m4_divert_text([M4SH-INIT-FN], [_AS_ERROR_PREPARE[]])])], [m4_ifvaln([$2], [{ AS_SET_STATUS([$2])])]dnl -[as_fn_error "_AS_QUOTE([$1])"m4_ifset([AS_MESSAGE_LOG_FD], +[as_fn_error "_AS_QUOTE([$1])"m4_ifval([AS_MESSAGE_LOG_FD], [ "$LINENO" AS_MESSAGE_LOG_FD])[]m4_ifval([$2], [; }])]) @@ -1986,8 +2009,9 @@ _ASEOF cat >>$1 <<\_ASEOF || as_write_fail=1 _AS_SHELL_SANITIZE _AS_PREPARE -exec AS_MESSAGE_FD>&1 -m4_text_box([Main body of $1 script.]) +m4_if(AS_MESSAGE_FD, [1], [], [exec AS_MESSAGE_FD>&1 +])]dnl +[m4_text_box([Main body of $1 script.]) _ASEOF test $as_write_fail = 0 && chmod +x $1[]dnl _m4_popdef([AS_MESSAGE_LOG_FD])])# AS_INIT_GENERATED diff --git a/tests/m4sh.at b/tests/m4sh.at index e85aabe9..0c575ded 100644 --- a/tests/m4sh.at +++ b/tests/m4sh.at @@ -1259,9 +1259,10 @@ AT_CLEANUP ## ------------------- ## AT_SETUP([AS@&t@_INIT_GENERATED]) +AT_KEYWORDS([AS@&t@_MESSAGE AS@&t@_MESSAGE_LOG_FD]) +dnl First run, no logging, tests shell selection AT_DATA_M4SH([script.as], [[dnl -m4_define([AS_MESSAGE_FD], [1]) AS_INIT AS_INIT_GENERATED([child], [echo hello from child]) cat >>child <<\EOF @@ -1286,4 +1287,85 @@ AT_CHECK([grep 'SHELL=.' stdout], [0], [ignore]) sed s/parent/child/ expout AT_CHECK([./child], [0], [expout]) + +dnl Second run, with logging from parent and child, tests fd handling +AT_DATA_M4SH([script.as], [[dnl +AS_INIT +child=${1-child} +m4_define([AS_MESSAGE_LOG_FD], [5]) +exec AS_MESSAGE_LOG_FD>log +AS_INIT_GENERATED([$child], [echo hello1 from $child]) || AS_EXIT([1]) +cat >>$child <<\EOF +m4_pushdef([AS_MESSAGE_LOG_FD]) +AS_MESSAGE([hello2 from ${child}child]) +m4_popdef([AS_MESSAGE_LOG_FD]) +exec AS_MESSAGE_LOG_FD>>log +AS_MESSAGE([hello3 from child]) +EOF +AS_MESSAGE([hello from parent]) +dnl close log in parent before spawning child, for mingw +exec AS_MESSAGE_LOG_FD>- +./$child +]]) + +AT_CHECK_M4SH +AT_CHECK([./script], [0], [[script: hello from parent +hello1 from child +child: hello2 from child +child: hello3 from child +]]) +AT_CHECK([[sed 's,:[0-9][0-9]*:,:0:,' log]], [0], +[[script:0: hello from parent +child:0: hello3 from child +]]) + +# Force write error creating a file on stdout +if test -w /dev/full && test -c /dev/full; then + AT_CHECK([./script /dev/full], [1], [ignore], [ignore]) +fi + +AT_CLEANUP + + +## --------------- ## +## AS_MESSAGE_FD. ## +## --------------- ## + +AT_SETUP([AS@&t@_MESSAGE_FD]) +AT_KEYWORDS([AS@&t@_MESSAGE AS@&t@_MESSAGE_LOG_FD AS@&t_ORIGINAL_STDIN_FD]) +AT_KEYWORDS([AS@&t@_LINENO_PUSH]) + +AT_DATA_M4SH([script.as], [[dnl +AS_INIT +m4_define([AS_ORIGINAL_STDIN_FD], [5]) +m4_define([AS_MESSAGE_LOG_FD], [6]) +m4_define([AS_MESSAGE_FD], [7]) +exec AS_ORIGINAL_STDIN_FD<&0 log +if test $[#] -gt 0; then + exec AS_MESSAGE_FD>/dev/null +else + exec AS_MESSAGE_FD>&1 +fi +AS_LINENO_PUSH([100]) +cat # tests that stdin is neutralized +AS_MESSAGE([hello world]) +cat <&AS_ORIGINAL_STDIN_FD +]]) + +AT_CHECK_M4SH +AT_CHECK([echo goodbye | ./script], [0], +[[script: hello world +goodbye +]]) +AT_CHECK([cat log], [0], +[[script:100: hello world +]]) +rm log +AT_CHECK([echo goodbye | ./script silent], [0], +[[goodbye +]]) +AT_CHECK([cat log], [0], +[[script:100: hello world +]]) + AT_CLEANUP