From 315dcc41638ebfe6a4a83a6f6b47e23d699cb2eb Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Fri, 3 Feb 2006 23:18:34 +0000 Subject: [PATCH] * Add "exit-status" and "no-output" options to SHELL builtin. * Add optional exit result value to EXIT builtin. [SVN r32534] --- historic/jam/doc/bjam.qbk | 13 +++-- historic/jam/src/builtins.c | 76 +++++++++++++++++++++++------ historic/jam/test/builtin_shell.jam | 46 +++++++++++++++++ historic/jam/test/test.bat | 11 +++++ historic/jam/test/test.sh | 9 ++++ jam_src/builtins.c | 76 +++++++++++++++++++++++------ 6 files changed, 196 insertions(+), 35 deletions(-) create mode 100644 historic/jam/test/builtin_shell.jam create mode 100644 historic/jam/test/test.bat create mode 100644 historic/jam/test/test.sh diff --git a/historic/jam/doc/bjam.qbk b/historic/jam/doc/bjam.qbk index a03874f9d..cda5265c0 100644 --- a/historic/jam/doc/bjam.qbk +++ b/historic/jam/doc/bjam.qbk @@ -698,10 +698,10 @@ ECHO /args/ ; Blurts out the message /args/ to stdout. [pre -EXIT /args/ ; +rule EXIT ( /message/ * : /result-value/ ? ) ] -Blurts out the message args to stdout and then exits with a failure status. +Blurts out the /message/ to stdout and then exits with a failure status if no /result-value/ is given, otherwise it exits with the given /result-value/. "=Echo=", "=echo=", "=Exit=", and "=exit=" are accepted as aliases for =ECHO= and =EXIT=, since it is hard to tell that these are built-in rules and not part of the language, like "=include=". @@ -768,7 +768,14 @@ local PSDK-location = rule SHELL ( /command/ : * ) ] -=SHELL= executes /command/, and then returns the standard output of /command/. =SHELL= only works on platforms with a =popen()= function in the C library. On platforms without a working =popen()= function, =SHELL= is implemented as a no-op. =SHELL= works on Unix, MacOS X, and most Windows compilers. =SHELL= is a no-op on Metrowerks compilers under Windows. +=SHELL= executes /command/, and then returns the standard output of /command/. =SHELL= only works on platforms with a =popen()= function in the C library. On platforms without a working =popen()= function, =SHELL= is implemented as a no-op. =SHELL= works on Unix, MacOS X, and most Windows compilers. =SHELL= is a no-op on Metrowerks compilers under Windows. There is a variable set of allowed options as additional arguments: + +[variablelist + [[=exit-status=] [In addition to the output the result status of the executed command is returned as a second element of the result.]] + [[=no-output=] [Don't capture the output of the command. Instead an empty ("") string value is returned in place of the output.]] +] + +Because the Perforce/Jambase defines a =Shell= rule which hides the builtin =COMMAND= can be used as an alias for =SHELL=. [endsect] diff --git a/historic/jam/src/builtins.c b/historic/jam/src/builtins.c index e1a223704..c9e708a48 100644 --- a/historic/jam/src/builtins.c +++ b/historic/jam/src/builtins.c @@ -98,10 +98,14 @@ load_builtins() bind_builtin( "ECHO" , builtin_echo, 0, 0 ) ) ); - duplicate_rule( "exit" , - duplicate_rule( "Exit" , - bind_builtin( "EXIT" , - builtin_exit, 0, 0 ) ) ); + + { + char * args[] = { "message", "*", ":", "result-value", "?", 0 }; + duplicate_rule( "exit" , + duplicate_rule( "Exit" , + bind_builtin( "EXIT" , + builtin_exit, 0, args ) ) ); + } { char * args[] = { "directories", "*", ":", "patterns", "*", ":", "case-insensitive", "?", 0 }; @@ -327,7 +331,7 @@ load_builtins() # endif { - char * args[] = { "command", 0 }; + char * args[] = { "command", ":", "*", 0 }; bind_builtin( "SHELL", builtin_shell, 0, args ); bind_builtin( "COMMAND", @@ -496,13 +500,20 @@ builtin_echo( LIST * builtin_exit( - PARSE *parse, - FRAME *frame ) + PARSE *parse, + FRAME *frame ) { - list_print( lol_get( frame->args, 0 ) ); - printf( "\n" ); - exit( EXITBAD ); /* yeech */ - return L0; + list_print( lol_get( frame->args, 0 ) ); + printf( "\n" ); + if ( lol_get( frame->args, 1 ) ) + { + exit ( atoi( lol_get( frame->args, 1 )->string ) ); + } + else + { + exit( EXITBAD ); /* yeech */ + } + return L0; } /* @@ -1663,31 +1674,64 @@ bjam_import_rule(PyObject* self, PyObject* args) LIST *builtin_shell( PARSE *parse, FRAME *frame ) { - LIST* arg = lol_get( frame->args, 0 ); - LIST* result = 0; + LIST* command = lol_get( frame->args, 0 ); + LIST* result = 0; string s; int ret; char buffer[1024]; FILE *p = NULL; + int exit_status = -1; + int exit_status_opt = 0; + int no_output_opt = 0; + + /* Process the variable args options. */ + { + int a = 1; + LIST * arg = lol_get( frame->args, a ); + while ( arg ) + { + if ( strcmp("exit-status", arg->string) == 0 ) + { + exit_status_opt = 1; + } + else if ( strcmp("no-output", arg->string) == 0 ) + { + no_output_opt = 1; + } + arg = lol_get( frame->args, ++a ); + } + } string_new( &s ); fflush(NULL); - p = popen(arg->string, "r"); + p = popen(command->string, "r"); if ( p == NULL ) return L0; while ( (ret = fread(buffer, sizeof(char), sizeof(buffer)-1, p)) > 0 ) { buffer[ret] = 0; - string_append( &s, buffer ); + if ( ! no_output_opt ) + { + string_append( &s, buffer ); + } } - pclose(p); + exit_status = pclose(p); + /* The command output is returned first. */ result = list_new( L0, newstr(s.value) ); string_free(&s); + + /* The command exit result next. */ + if ( exit_status_opt ) + { + sprintf (buffer, "%d", exit_status); + result = list_new( result, newstr( buffer ) ); + } + return result; } diff --git a/historic/jam/test/builtin_shell.jam b/historic/jam/test/builtin_shell.jam new file mode 100644 index 000000000..91761a042 --- /dev/null +++ b/historic/jam/test/builtin_shell.jam @@ -0,0 +1,46 @@ +#~ Copyright 2006 Rene Rivera. +#~ Distributed under the Boost Software License, Version 1.0. +#~ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +ECHO --- Testing SHELL builtin... ; + +local result = 0 ; +local rule error ( message * ) +{ + local b = [ BACKTRACE ] ; + ECHO "$(b[9]):$(b[10]): error:" $(message) ; +} +local rule assert ( expected + : test ? : obtained + ) +{ + test ?= "(==)" ; + local r = 0 ; + if $(test) = "(==)" && $(expected) != $(obtained) + { + error [FAILED] '$(expected)' $(test) '$(obtained)' ; + r = 1 ; + } + else if $(test) = "(!=)" && $(expected) = $(obtained) + { + error [FAILED] '$(expected)' $(test) '$(obtained)' ; + r = 1 ; + } + result = [ CALC $(result) + $(r) ] ; +} + +#~ --------------------------------------------------------------------- + +local c = "date" ; +if $(NT) { c = "PATH" ; } + +assert "" : (!=) : [ SHELL $(c) ] ; +assert "" : (==) : [ SHELL $(c) : no-output ] ; +assert "" 0 : (!=) : [ SHELL $(c) : exit-status ] ; +assert "" 0 : (==) : [ SHELL $(c) : no-output : exit-status ] ; +assert "" : (!=) : [ COMMAND $(c) ] ; +assert "" : (==) : [ COMMAND $(c) : no-output ] ; +assert "" 0 : (!=) : [ COMMAND $(c) : exit-status ] ; +assert "" 0 : (==) : [ COMMAND $(c) : no-output : exit-status ] ; + +#~ --------------------------------------------------------------------- + +EXIT --- Complete : $(result) ; diff --git a/historic/jam/test/test.bat b/historic/jam/test/test.bat new file mode 100644 index 000000000..4ae831f0f --- /dev/null +++ b/historic/jam/test/test.bat @@ -0,0 +1,11 @@ +@ECHO OFF + +REM ~ Copyright 2006 Rene Rivera. +REM ~ Distributed under the Boost Software License, Version 1.0. +REM ~ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +set BJAM=..\..\build\jam_src\bin.ntx86\bjam + +@ECHO ON + +%BJAM% -f builtin_shell.jam diff --git a/historic/jam/test/test.sh b/historic/jam/test/test.sh new file mode 100644 index 000000000..2e5d4a73a --- /dev/null +++ b/historic/jam/test/test.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +#~ Copyright 2002-2005 Rene Rivera. +#~ Distributed under the Boost Software License, Version 1.0. +#~ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +BJAM=`ls -1 ../../build/jam_src/bin.*/bjam` + +${BJAM} -f builtin_shell.jam diff --git a/jam_src/builtins.c b/jam_src/builtins.c index e1a223704..c9e708a48 100644 --- a/jam_src/builtins.c +++ b/jam_src/builtins.c @@ -98,10 +98,14 @@ load_builtins() bind_builtin( "ECHO" , builtin_echo, 0, 0 ) ) ); - duplicate_rule( "exit" , - duplicate_rule( "Exit" , - bind_builtin( "EXIT" , - builtin_exit, 0, 0 ) ) ); + + { + char * args[] = { "message", "*", ":", "result-value", "?", 0 }; + duplicate_rule( "exit" , + duplicate_rule( "Exit" , + bind_builtin( "EXIT" , + builtin_exit, 0, args ) ) ); + } { char * args[] = { "directories", "*", ":", "patterns", "*", ":", "case-insensitive", "?", 0 }; @@ -327,7 +331,7 @@ load_builtins() # endif { - char * args[] = { "command", 0 }; + char * args[] = { "command", ":", "*", 0 }; bind_builtin( "SHELL", builtin_shell, 0, args ); bind_builtin( "COMMAND", @@ -496,13 +500,20 @@ builtin_echo( LIST * builtin_exit( - PARSE *parse, - FRAME *frame ) + PARSE *parse, + FRAME *frame ) { - list_print( lol_get( frame->args, 0 ) ); - printf( "\n" ); - exit( EXITBAD ); /* yeech */ - return L0; + list_print( lol_get( frame->args, 0 ) ); + printf( "\n" ); + if ( lol_get( frame->args, 1 ) ) + { + exit ( atoi( lol_get( frame->args, 1 )->string ) ); + } + else + { + exit( EXITBAD ); /* yeech */ + } + return L0; } /* @@ -1663,31 +1674,64 @@ bjam_import_rule(PyObject* self, PyObject* args) LIST *builtin_shell( PARSE *parse, FRAME *frame ) { - LIST* arg = lol_get( frame->args, 0 ); - LIST* result = 0; + LIST* command = lol_get( frame->args, 0 ); + LIST* result = 0; string s; int ret; char buffer[1024]; FILE *p = NULL; + int exit_status = -1; + int exit_status_opt = 0; + int no_output_opt = 0; + + /* Process the variable args options. */ + { + int a = 1; + LIST * arg = lol_get( frame->args, a ); + while ( arg ) + { + if ( strcmp("exit-status", arg->string) == 0 ) + { + exit_status_opt = 1; + } + else if ( strcmp("no-output", arg->string) == 0 ) + { + no_output_opt = 1; + } + arg = lol_get( frame->args, ++a ); + } + } string_new( &s ); fflush(NULL); - p = popen(arg->string, "r"); + p = popen(command->string, "r"); if ( p == NULL ) return L0; while ( (ret = fread(buffer, sizeof(char), sizeof(buffer)-1, p)) > 0 ) { buffer[ret] = 0; - string_append( &s, buffer ); + if ( ! no_output_opt ) + { + string_append( &s, buffer ); + } } - pclose(p); + exit_status = pclose(p); + /* The command output is returned first. */ result = list_new( L0, newstr(s.value) ); string_free(&s); + + /* The command exit result next. */ + if ( exit_status_opt ) + { + sprintf (buffer, "%d", exit_status); + result = list_new( result, newstr( buffer ) ); + } + return result; }