Move log_command to Util class.

While I was at it the code has been simplified slightly (e.g. callback
handling) but should not affect functionality.
wilder
Michael Pyne 14 years ago committed by Michael Pyne
parent 3c77a911ee
commit 91f312ee31
  1. 371
      kdesrc-build

@ -45,15 +45,12 @@ use File::Path qw(remove_tree);
use File::Glob ':glob';
use File::Basename; # fileparse
use File::Spec; # tmpdir, rel2abs
use File::Temp qw(tempfile);
use Cwd qw(getcwd);
use LWP::UserAgent;
use URI; # For git-clone snapshot support
use Text::ParseWords qw(parse_line);
use Sys::Hostname;
use Storable 'dclone';
use IO::Handle;
use Errno qw(:POSIX);
use Data::Dumper;
use 5.010_000; # Require Perl 5.10.0
@ -297,7 +294,9 @@ my $run_mode = 'build'; # Determines if updating, building, installing, etc.
use Carp qw(cluck);
use Scalar::Util qw(blessed);
use File::Path qw(make_path);
use Cwd qw(getcwd);
use IPC::Open3;
use Errno qw(:POSIX);
ksb::Debug->import();
@ -307,6 +306,7 @@ my $run_mode = 'build'; # Determines if updating, building, installing, etc.
my $caller = caller;
my @exports = qw(list_has make_exception assert_isa assert_in
croak_runtime croak_internal
log_command
safe_unlink safe_system p_chdir super_mkdir
slurp_program_output prettify_seconds
);
@ -549,6 +549,177 @@ my $run_mode = 'build'; # Determines if updating, building, installing, etc.
return $str;
}
# Subroutine to mark a file as being the error log for a module. This also
# creates a symlink in the module log directory for easy viewing.
# First parameter is the module in question.
# Second parameter is the filename in the log directory of the error log.
sub _setErrorLogfile
{
my $module = assert_isa(shift, 'Module');
my $logfile = shift;
return unless $logfile;
my $logdir = $module->getLogDir();
$module->setOption('#error-log-file', "$logdir/$logfile");
debug ("Logfile for $module is $logfile");
# Setup symlink in the module log directory pointing to the appropriate
# file. Make sure to remove it first if it already exists.
unlink("$logdir/error.log") if -l "$logdir/error.log";
if(-e "$logdir/error.log")
{
# Maybe it was a regular file?
error ("r[b[ * Unable to create symlink to error log file]");
return;
}
symlink "$logfile", "$logdir/error.log";
}
# Subroutine to run a command, optionally filtering on the output of the child
# command.
#
# First parameter is the module object being built (for logging purposes
# and such).
# Second parameter is the name of the log file to use (relative to the log
# directory).
# Third parameter is a reference to an array with the command and its
# arguments. i.e. ['command', 'arg1', 'arg2']
# Fourth parameter (optional) is a reference to a subroutine to have each line
# of child output passed to. This output is not supposed to be printed
# to the screen by the subroutine, normally the output is only logged.
# However this is useful for e.g. munging out the progress of the build.
# USEFUL: When there is no more output from the child, the callback will be
# called with an undef string. (Not just empty, it is also undefined).
# The return value is the shell return code, so 0 is success, and non-zero is
# failure.
#
# NOTE: This function has a special feature. If the command passed into the
# argument reference is 'kdesrc-build', then log_command will, when it
# forks, execute the subroutine named by the second parameter rather than
# executing a child process. The remaining arguments in the list are
# passed to the subroutine that is called.
sub log_command
{
my ($module, $filename, $argRef, $callbackRef) = @_;
assert_isa($module, 'Module');
my @command = @{$argRef};
debug ("log_command(): Module $module, Command: ", join(' ', @command));
# Fork a child, with its stdout connected to CHILD.
my $pid = open(CHILD, '-|');
if ($pid)
{
# Parent
if (!$callbackRef && debugging()) {
# If no other callback given, pass to debug() if debug-mode is on.
$callbackRef = sub { my $line = shift; chomp $line; debug($line); };
}
# Final fallback: Do nothing
$callbackRef //= sub { };
# Filter each line
&{$callbackRef}($_) while (<CHILD>);
# Let callback know there is no more output.
&{$callbackRef}(undef) if defined $callbackRef;
close CHILD;
# If the module fails building, set an internal flag in the module
# options with the name of the log file containing the error message.
# TODO: ($? is set when closing CHILD pipe?)
my $result = $?;
_setErrorLogfile($module, "$filename.log") if $result;
return $result;
}
else
{
# Child. Note here that we need to avoid running our exit cleanup
# handlers in here. For that we need POSIX::_exit.
# Apply altered environment variables.
$module->buildContext()->commitEnvironmentChanges();
if (pretending())
{
pretend ("\tWould have run g['", join ("' '", @command), "'");
POSIX::_exit(0);
}
my $logdir = $module->getLogDir();
if (!$logdir || ! -e $logdir)
{
# Error creating directory for some reason.
error ("\tLogging to std out due to failure creating log dir.");
}
# Redirect STDIN to /dev/null so that the handle is open but fails when
# being read from (to avoid waiting forever for e.g. a password prompt
# that the user can't see.
open (STDIN, '<', "/dev/null") unless exists $ENV{'KDESRC_BUILD_USE_TTY'};
open (STDOUT, "|tee $logdir/$filename.log") or do {
error ("Error opening pipe to tee command.");
# Don't abort, hopefully STDOUT still works.
};
# Make sure we log everything.
# In the case of Qt, we may have forced on progress output so let's
# leave that interactive to keep the logs sane.
if (!($module->buildSystemType() eq 'Qt' &&
$module->buildSystem()->forceProgressOutput()))
{
open (STDERR, ">&STDOUT");
}
# Call internal function, name given by $command[1]
if ($command[0] eq 'kdesrc-build')
{
# No colors!
ksb::Debug::setColorfulOutput(0);
debug ("Calling $command[1]");
my $cmd = $command[1];
splice (@command, 0, 2); # Remove first two elements.
no strict 'refs'; # Disable restriction on symbolic subroutines.
if (! &{$cmd}(@command)) # Call sub
{
POSIX::_exit (EINVAL);
}
POSIX::_exit (0); # Exit child process successfully.
}
# Don't leave empty output files, give an indication of the particular
# command run. Use print to go to stdout.
say "# kdesrc-build running: '", join("' '", @command), "'";
say "# from directory: ", getcwd();
# External command.
exec (@command) or do {
my $cmd_string = join(' ', @command);
error (<<EOF);
r[b[Unable to execute "$cmd_string"]!
$!
Please check your binpath setting (it controls the PATH used by kdesrc-build).
Currently it is set to g[$ENV{PATH}].
EOF
# Don't use return, this is the child still!
POSIX::_exit (1);
};
}
}
1;
}
@ -2316,7 +2487,7 @@ HOME
};
# Use log_command as the check so that an error file gets created.
if (0 != main::log_command($module, 'conflict-check',
if (0 != log_command($module, 'conflict-check',
['kdesrc-build', 'module_has_conflict', $module],
$log_filter)
)
@ -2578,7 +2749,7 @@ HOME
# This variant of log_command runs the sub prune_under_directory($builddir)
# in a forked child, so that we can log its output.
if (main::log_command($module, 'clean-builddir', [ 'kdesrc-build', 'prune_under_directory', $builddir ]))
if (log_command($module, 'clean-builddir', [ 'kdesrc-build', 'prune_under_directory', $builddir ]))
{
error (" r[b[*]\tFailed to clean build directory. Verify the permissions are correct.");
return 0; # False for this function.
@ -2651,8 +2822,7 @@ HOME
}
p_chdir($builddir);
return main::log_command($module, 'qmake',
[ 'qmake', $projectFiles[0] ]) == 0;
return log_command($module, 'qmake', [ 'qmake', $projectFiles[0] ]) == 0;
}
1;
@ -2789,7 +2959,7 @@ HOME
p_chdir("$builddir/$lang");
info ("\tConfiguring to build language $lang");
$result = (main::log_command($self->module(), "cmake-$lang",
$result = (log_command($self->module(), "cmake-$lang",
['cmake', '-DCMAKE_INSTALL_PREFIX=' . $prefix]) == 0) || $result;
}
@ -2832,7 +3002,7 @@ HOME
my $args = [ 'kdesrc-build', 'safe_lndir', $srcdir, $builddir ];
info ("\tSetting up alternate build directory for l10n");
return (0 == main::log_command ($module, 'create-builddir', $args));
return (0 == log_command ($module, 'create-builddir', $args));
}
# Subroutine to create the build system for a module. This involves making
@ -2862,7 +3032,7 @@ HOME
foreach my $lang (@langs) {
my $cmd_ref = [ './scripts/autogen.sh', $lang ];
if (main::log_command ($module, "build-system-$lang", $cmd_ref))
if (log_command ($module, "build-system-$lang", $cmd_ref))
{
error ("\tUnable to create build system for r[$module]");
}
@ -2931,7 +3101,7 @@ HOME
# We scrape the output of the commands, so force the locale to be
# untranslated.
local $ENV{'LC_ALL'} = 'C';
my $result = main::log_command($module, 'test-results', [ 'make', $make_target ]);
my $result = log_command($module, 'test-results', [ 'make', $make_target ]);
if ($result != 0) {
my $logdir = $module->getLogDir();
@ -3067,7 +3237,7 @@ EOF
info ("\tRunning g[configure]...");
$module->setPersistentOption('last-configure-flags', $cur_flags);
return main::log_command($module, "configure", \@commands) == 0;
return log_command($module, "configure", \@commands) == 0;
}
# Skip execution of configure.
@ -5333,183 +5503,6 @@ END
close SIG;
}
# Subroutine to run a command, optionally filtering on the output of the child
# command.
#
# First parameter is the module object being built (for logging purposes
# and such).
# Second parameter is the name of the log file to use (relative to the log
# directory).
# Third parameter is a reference to an array with the command and its
# arguments. i.e. ['command', 'arg1', 'arg2']
# Fourth parameter (optional) is a reference to a subroutine to have each line
# of child output passed to. This output is not supposed to be printed to
# the screen by the subroutine, normally the output is only logged. However
# this is useful for e.g. munging out the progress of the build.
# USEFUL: When there is no more output from the child, the callback will be
# called with an undef string. (Not just empty, it is also undefined).
# The return value is the shell return code, so 0 is success, and non-zero is
# failure.
#
# NOTE: This function has a special feature. If the command passed into the
# argument reference is 'kdesrc-build', then log_command will, when it forks,
# execute the subroutine named by the second parameter rather than executing
# a child process. The remaining arguments in the list are passed to the
# subroutine that is called.
sub log_command
{
my ($module, $filename, $argRef, $callbackRef) = @_;
assert_isa($module, 'Module');
my $pid;
my @command = @{$argRef};
my $logdir = $module->getLogDir();
debug "log_command(): Module $module, Command: ", join(' ', @command);
# Fork a child, with its stdout connected to CHILD.
$pid = open(CHILD, '-|');
if ($pid)
{
# Parent
while (<CHILD>)
{
if (defined $callbackRef)
{
# Call callback with current output.
&{$callbackRef}($_);
}
else
{
chomp $_;
debug $_;
}
}
close CHILD;
# Let callback know there is no more output.
&{$callbackRef}(undef) if defined $callbackRef;
# If the module fails building, set an internal flag in the module
# options with the name of the log file containing the error message.
my $result = $?;
set_error_logfile($module, "$filename.log") if $result;
return $result;
}
else
{
# Child. Note here that we need to avoid running our exit cleanup
# handlers in here. For that we need POSIX::_exit.
# Apply altered environment variables.
$module->buildContext()->commitEnvironmentChanges();
if (pretending)
{
pretend "\tWould have run g['", join ("' '", @command), "'";
POSIX::_exit(0);
}
if (not $logdir or not -e $logdir)
{
# Error creating directory for some reason.
error "\tLogging to std out due to failure creating log dir.";
}
# The stdin redirection used to be commented out because it will cause
# problems for users using make-install-prefix when a password is desired, or
# when svn complains about the SSL signature. I think I've fixed the latter,
# and I've decided that users should configure sudo to not need the password,
# or simply run sudo kdesrc-build instead of using make-install-prefix. Now
# other commands will fail instead of hanging at the terminal. As it stands, it can still
# be canceled using an exported env var just in case.
open (STDIN, "</dev/null") unless exists $ENV{'KDESRC_BUILD_USE_TTY'};
open (STDOUT, "|tee $logdir/$filename.log") or do {
error "Error opening pipe to tee command.";
# Don't abort, hopefully STDOUT still works.
};
# Make sure we log everything.
# In the case of Qt, we may have forced on progress output so let's
# leave that interactive to keep the logs sane.
if(!($module->buildSystemType() eq 'Qt' &&
$module->buildSystem()->forceProgressOutput()))
{
open (STDERR, ">&STDOUT");
}
# Call internal function, name given by $command[1]
if($command[0] eq 'kdesrc-build')
{
# No colors!
ksb::Debug::setColorfulOutput(0);
debug "Calling $command[1]";
my $cmd = $command[1];
splice (@command, 0, 2); # Remove first two elements.
no strict 'refs'; # Disable restriction on symbolic subroutines.
if (! &{$cmd}(@command)) # Call sub
{
POSIX::_exit (EINVAL);
}
POSIX::_exit (0); # Exit child process successfully.
}
# Don't leave empty output files, give an indication of the particular
# command run. Use print to go to stdout.
say "# kdesrc-build running: '", join("' '", @command), "'";
say "# from directory: ", Cwd::getcwd();
# External command.
exec (@command) or do {
my $cmd_string = join(' ', @command);
error <<EOF;
r[b[Unable to execute "$cmd_string"]!
$!
Please check your binpath setting (it controls the PATH used by kdesrc-build).
Currently it is set to g[$ENV{PATH}].
EOF
# Don't use return, this is the child still!
POSIX::_exit (1);
};
}
}
# Subroutine to mark a file as being the error log for a module. This also
# creates a symlink in the module log directory for easy viewing.
# First parameter is the module in question.
# Second parameter is the filename in the log directory of the error log.
sub set_error_logfile
{
my $module = assert_isa(shift, 'Module');
my $logfile = shift;
return unless $logfile;
my $logdir = $module->getLogDir();
$module->setOption('#error-log-file', "$logdir/$logfile");
debug "Logfile for $module is $logfile";
# Setup symlink in the module log directory pointing to the appropriate
# file. Make sure to remove it first if it already exists.
unlink("$logdir/error.log") if -l "$logdir/error.log";
if(-e "$logdir/error.log")
{
# Maybe it was a regular file?
error "r[b[ * Unable to create symlink to error log file]";
return 0;
}
symlink "$logfile", "$logdir/error.log";
}
# Subroutine to run make and process the build process output in order to
# provide completion updates. This procedure takes the same arguments as
# log_command() (described here as well), except that the callback argument is

Loading…
Cancel
Save