[Nottingham-pm] [Fwd: Compiling Software In A Perl Script]

Duncan John Fyfe djf at star.le.ac.uk
Mon Mar 13 02:15:34 PST 2006


On Sun, 2006-03-12 at 22:36 +0000, Robert Postill wrote:
> Hi,
> 
> I'm developing an installer for a PHP-based app at work after getting 
> sick and tired of building the thing by hand.  What I want is to collate 
> the output from the compilation of Apache, PHP and MySQL into a single 
> point, check that it's all OK and then save the output for later support 
> perusal.  All of these apps are standard configure && make && make 
> install jobs.  I need to make sure they have the right flags set (e.g. 
> mbstring and LDAP support) and I also need to make sure they all land in 
> one place (so uninstallation is a matter of rm -rf <install_dir>).  Now 
> in classic Perl noob mode I'm thinking of using the system(command) to 
> get my operations run.  Then redirecting the output to a file so I can 
> check the actions succeeded.  So my questions are:
> 1. This is portable to AIX and the more niche unices right?
> 2. Is there a more efficient way of collecting the output rather than 
> directing the stderr and stdout to a file?
> 3.  Does this sound like high order lunacy to anyone for some reason?
> 

Hello Rob, good to know you are alive.

Quick answers:

0. Lunacy would be trying to make a higher level make script to do
this ;) 

1. perldoc -f open
For how to run a command and accumulate output to/from a pipe.

2. Wrap the logging (see below) so you can format the output and make it
more parseable.

3. Things become messy if you need to extract environment variables from
one sub shell for use in another.  
eg.

system("make ldap"); # Somewhere in here FUNKY_GIBBON gets set.
.
.
.
system("make datainstall"); #This needs to know the value of FUNKY_GIBBON.

4. Have a look at the debian (or other distro) package scripts for
apache, php etc.  maybe they have thought of quirks you need to think of
too (especially for the unistall).

5. Identify components/files which traditionally live outside of the
install tree (log files, lock files, pid files, /etc/apache/...) you
might need to do something special to bring them in tree (if that is
wise) or there might be some you want left behind (logs ?) when you rm
-rf everything else.

Here we have apache2 installed so:
	/www
	/www/apache2/...
	/www/documents/...

It keeps it under one tree but also segregates the publically accessible
bits (/www/documents) from the not so publically accessible bits
(everything else) like password files.


A little more perl:

I wrap system and open as below to reduce effort handling errors and
formatting the output.  Msg and Exception are logging and exception
modules of my own devising but it should be pretty obvious what they are
doing and they can easily be replaced with warn/die etc.

In my case Msg (Exception logs through Msg) formats the messages making
them more parsable (among other requirements) than simple warn/die etc.

so...
	$Exec::Silent=0;
	Exec::run("a_command --dowhatimean");

would produce:

#COMMAND
# [COMMAND] [2006-03-10T16:40:56]
a_command --dowhatimean
# [OUTPUT] [2006-03-10T16:51:08] doingwhat youmean
# one potato
# two potato
# three potato
# four
# [ERROR] [2006-03-10T16:51:31 no more potatoes.

It is then a trivial exercise for my log analysis scripts to scan the
log files and pick out errors, the command that caused them and any
other ouput from that command.
(yep, the astute observer would also note that this also gives me shell
executable log files).

###
###  PERL PACKAGE BEGINS HERE
###
package Exec;
use strict;
use Msg;
use Exception;

use vars qw($Silent);
# Do or do not log command output by default.
$Silent = 0;

sub system
{
	# Wrapper for system command with added Exception throwing.
	# system( @_ )
	Msg->command(@_); 
	my $rv = system(@_);
	Msg->info("System RV",$rv);
	if ($rv == -1) 
	{
		Exception->error({id => 'SysFail'
			,text => [@_,'System command failed.']
		});
	}
	elsif ($rv & 127) 
	{
		Exception->error( {id => 'DeathbySignal'
			, text => [@_,sprintf( "Received signal signal %d, %s coredump" 
				, ($rv & 127)
				, ($rv & 128) 
					? 'with' 
					: 'without'
			)]
			,signal => ($rv & 127)
			,coredump => ($rv & 128)
		});
	}
	elsif ( $rv >> 8 ) 
	{
		Exception->error({id => 'ExecAbend' 
			, text =>  [@_,sprintf ( "Exited with value %d", $rv >> 8 )]
			, value => $rv >> 8
		});
	}
}

sub run
{
	# Wrapper for using open to run commands with added Exception throwing.
	# Returns an annonymous list of the output from the command.
	Msg->command(@_);
	my $cmd = shift;
	my $pipe;
	
	open($pipe,"$cmd 2>&1 |")
		or Exception->error('COMMAND','Error opening external command:',$cmd)
	;
	my @out = (<$pipe>);
	close $pipe;
	my $rv = $?;
	Msg->output(@out) unless $Silent;

	if ($rv == -1) 
	{
		Exception->error({id => 'ExecSysFail'
			,text => [@_,'Opening external command failed.']
		});
	}
	elsif ($rv & 127) 
	{
		Exception->error( {id => 'DeathbySignal'
			, text => [@_,sprintf( "Received signal signal %d, %s coredump" 
				, ($rv & 127)
				, ($rv & 128) 
					? 'with' 
					: 'without'
			)]
			,signal => ($rv & 127)
			,coredump => ($rv & 128)
		});
	}
	elsif ( $? >> 8 ) 
	{
		Exception->error({id => 'ExecAbend' 
			, text =>  [@_,sprintf ( "Exited with value %d", $rv >> 8 )]
			, value => $rv >> 8
		});
	}

	return [@out];
}
1;




More information about the Nottingham-pm mailing list