2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-15 00:52:16 +00:00
Files
build/src/engine/execvms.cpp
Rene Rivera e206f0d602 Initial implementation of dynamic response files.
This implements the ability for response file "@()" substitution to
dynamically adjust to either expand the content or create the response
file depending on the possible command line length. This should
reduce the create of such response temp files improving build
performance.
2020-12-13 21:30:03 -06:00

426 lines
10 KiB
C++

/*
* Copyright 1993, 1995 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/* This file is ALSO:
* Copyright 2001-2004 David Abrahams.
* Copyright 2015 Artur Shepilko.
* 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)
*/
/*
* execvms.c - execute a shell script, ala VMS.
*
* The approach is this:
*
* If the command is a single line, and shorter than WRTLEN (what we believe to
* be the maximum line length), we just system() it.
*
* If the command is multi-line, or longer than WRTLEN, we write the command
* block to a temp file, splitting long lines (using "-" at the end of the line
* to indicate contiuation), and then source that temp file. We use special
* logic to make sure we do not continue in the middle of a quoted string.
*
* 05/04/94 (seiwald) - async multiprocess interface; noop on VMS
* 12/20/96 (seiwald) - rewritten to handle multi-line commands well
* 01/14/96 (seiwald) - do not put -'s between "'s
* 01/19/15 (shepilko)- adapt for jam-3.1.19
*/
#include "jam.h"
#include "lists.h"
#include "execcmd.h"
#include "output.h"
#ifdef OS_VMS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <times.h>
#include <unistd.h>
#include <errno.h>
#define WRTLEN 240
#define MIN( a, b ) ((a) < (b) ? (a) : (b))
#define CHAR_DQUOTE '"'
#define VMS_PATH_MAX 1024
#define VMS_COMMAND_MAX 1024
#define VMS_WARNING 0
#define VMS_SUCCESS 1
#define VMS_ERROR 2
#define VMS_FATAL 4
char commandbuf[ VMS_COMMAND_MAX ] = { 0 };
static int get_status(int vms_status);
static clock_t get_cpu_time();
/*
* exec_check() - preprocess and validate the command.
*/
int exec_check
(
string const * command,
LIST * * pShell,
int32_t * error_length,
int32_t * error_max_length
)
{
int const is_raw_cmd = 1;
/* We allow empty commands for non-default shells since we do not really
* know what they are going to do with such commands.
*/
if ( !command->size && ( is_raw_cmd || list_empty( *pShell ) ) )
return EXEC_CHECK_NOOP;
return is_raw_cmd
? EXEC_CHECK_OK
: check_cmd_for_too_long_lines( command->value, shell_maxline(), error_length,
error_max_length );
}
/*
* exec_cmd() - execute system command.
*/
void exec_cmd
(
string const * command,
int flags,
ExecCmdCallback func,
void * closure,
LIST * shell
)
{
char * s;
char * e;
char * p;
int vms_status;
int status;
int rstat = EXEC_CMD_OK;
int exit_reason = EXIT_OK;
timing_info time_info;
timestamp start_dt;
struct tms start_time;
struct tms end_time;
char * cmd_string = command->value;
/* Start the command */
timestamp_current( &time_info.start );
times( &start_time );
/* See if command is more than one line discounting leading/trailing white
* space.
*/
for ( s = cmd_string; *s && isspace( *s ); ++s );
e = p = strchr( s, '\n' );
while ( p && isspace( *p ) )
++p;
/* If multi line or long, write to com file. Otherwise, exec directly. */
if ( ( p && *p ) || ( e - s > WRTLEN ) )
{
FILE * f;
/* Create temp file invocation. */
if ( !*commandbuf )
{
OBJECT * tmp_filename = 0;
tmp_filename = path_tmpfile();
/* Get tmp file name is VMS-format. */
{
string os_filename[ 1 ];
string_new( os_filename );
path_translate_to_os( object_str( tmp_filename ), os_filename );
object_free( tmp_filename );
tmp_filename = object_new( os_filename->value );
string_free( os_filename );
}
commandbuf[0] = '@';
strncat( commandbuf + 1, object_str( tmp_filename ),
VMS_COMMAND_MAX - 2);
}
/* Open tempfile. */
if ( !( f = fopen( commandbuf + 1, "w" ) ) )
{
err_printf( "[errno %d] failed to wite cmd_string file '%s': %s",
errno, commandbuf + 1, strerror(errno) );
rstat = EXEC_CMD_FAIL;
exit_reason = EXIT_FAIL;
times( &end_time );
timestamp_current( &time_info.end );
time_info.system = (double)( end_time.tms_cstime -
start_time.tms_cstime ) / 100.;
time_info.user = (double)( end_time.tms_cutime -
start_time.tms_cutime ) / 100.;
(*func)( closure, rstat, &time_info, "" , "", exit_reason );
return;
}
/* Running from TMP, so explicitly set default to CWD. */
{
char * cwd = NULL;
int cwd_buf_size = VMS_PATH_MAX;
while ( !(cwd = getcwd( NULL, cwd_buf_size ) ) /* alloc internally */
&& errno == ERANGE )
{
cwd_buf_size += VMS_PATH_MAX;
}
if ( !cwd )
{
perror( "can not get current working directory" );
exit( EXITBAD );
}
fprintf( f, "$ SET DEFAULT %s\n", cwd);
free( cwd );
}
/* For each line of the command. */
while ( *cmd_string )
{
char * s = strchr( cmd_string,'\n' );
int len = s ? s + 1 - cmd_string : strlen( cmd_string );
fputc( '$', f );
/* For each chunk of a line that needs to be split. */
while ( len > 0 )
{
char * q = cmd_string;
char * qe = cmd_string + MIN( len, WRTLEN );
char * qq = q;
int quote = 0;
/* Look for matching "s -- expected in the same line. */
for ( ; q < qe; ++q )
if ( ( *q == CHAR_DQUOTE ) && ( quote = !quote ) )
qq = q;
/* When needs splitting and is inside an open quote,
* back up to opening quote and split off at it.
* When the quoted string spans over a chunk,
* pass string as a whole.
* If no matching quote found, dump the rest of command.
*/
if ( len > WRTLEN && quote )
{
q = qq;
if ( q == cmd_string )
{
for ( q = qe; q < ( cmd_string + len )
&& *q != CHAR_DQUOTE ; ++q) {}
q = ( *q == CHAR_DQUOTE) ? ( q + 1 ) : ( cmd_string + len );
}
}
fwrite( cmd_string, ( q - cmd_string ), 1, f );
len -= ( q - cmd_string );
cmd_string = q;
if ( len )
{
fputc( '-', f );
fputc( '\n', f );
}
}
}
fclose( f );
if ( DEBUG_EXECCMD )
{
FILE * f;
char buf[ WRTLEN + 1 ] = { 0 };
if ( (f = fopen( commandbuf + 1, "r" ) ) )
{
int nbytes;
printf( "Command file: %s\n", commandbuf + 1 );
do
{
nbytes = fread( buf, sizeof( buf[0] ), sizeof( buf ) - 1, f );
if ( nbytes ) fwrite(buf, sizeof( buf[0] ), nbytes, stdout);
}
while ( !feof(f) );
fclose(f);
}
}
/* Execute command file */
vms_status = system( commandbuf );
status = get_status( vms_status );
unlink( commandbuf + 1 );
}
else
{
/* Execute single line command. Strip trailing newline before execing.
* TODO:Call via popen() with capture of the output may be better here.
*/
if ( e ) *e = 0;
status = VMS_SUCCESS; /* success on empty command */
if ( *s )
{
vms_status = system( s );
status = get_status( vms_status );
}
}
times( &end_time );
timestamp_current( &time_info.end );
time_info.system = (double)( end_time.tms_cstime -
start_time.tms_cstime ) / 100.;
time_info.user = (double)( end_time.tms_cutime -
start_time.tms_cutime ) / 100.;
/* Fail for error or fatal error. OK on OK, warning or info exit. */
if ( ( status == VMS_ERROR ) || ( status == VMS_FATAL ) )
{
rstat = EXEC_CMD_FAIL;
exit_reason = EXIT_FAIL;
}
(*func)( closure, rstat, &time_info, "" , "", exit_reason );
}
void exec_wait()
{
return;
}
/* get_status() - returns status of the VMS command execution.
- Map VMS status to its severity (lower 3-bits)
- W-DCL-IVVERB is returned on unrecognized command -- map to general ERROR
*/
int get_status( int vms_status )
{
#define VMS_STATUS_DCL_IVVERB 0x00038090
int status;
switch (vms_status)
{
case VMS_STATUS_DCL_IVVERB:
status = VMS_ERROR;
break;
default:
status = vms_status & 0x07; /* $SEVERITY bits */
}
return status;
}
#define __NEW_STARLET 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <ssdef.h>
#include <stsdef.h>
#include <jpidef.h>
#include <efndef.h>
#include <iosbdef.h>
#include <iledef.h>
#include <lib$routines.h>
#include <starlet.h>
/*
* get_cpu_time() - returns CPU time in CLOCKS_PER_SEC since process start.
* on error returns (clock_t)-1.
*
* Intended to emulate (system + user) result of *NIX times(), if CRTL times()
* is not available.
* However, this accounts only for the current process. To account for child
* processes, these need to be directly spawned/forked via exec().
* Moreover, child processes should be running a C main program or a program
* that calls VAXC$CRTL_INIT or DECC$CRTL_INIT.
*/
clock_t get_cpu_time()
{
clock_t result = (clock_t) 0;
IOSB iosb;
int status;
long cputime = 0;
ILE3 jpi_items[] = {
{ sizeof( cputime ), JPI$_CPUTIM, &cputime, NULL }, /* longword int, 10ms */
{ 0 },
};
status = sys$getjpiw (EFN$C_ENF, 0, 0, jpi_items, &iosb, 0, 0);
if ( !$VMS_STATUS_SUCCESS( status ) )
{
lib$signal( status );
result = (clock_t) -1;
return result;
}
result = ( cputime / 100 ) * CLOCKS_PER_SEC;
return result;
}
int32_t shell_maxline()
{
return MAXLINE;
}
# endif /* VMS */