2008-02-09 01:55:31 +00:00
#!/usr/bin/perl
### THIS IS DESIGNED TO BE RUN BY MAKE! DO NOT RUN FROM THE SHELL (because it MIGHT sigterm the shell)! ###
use strict ;
use warnings FATAL = > qw( all ) ;
use POSIX ( ) ;
# Runs the compiler, passing it the given arguments.
# Filters select output from the compiler's standard error channel and
# can take different actions as a result.
# NOTE: this is *NOT* a hash (sadly: a hash would stringize all the regexes and thus render them useless, plus you can't index a hash based on regexes anyway)
# even though we use the => in it.
# The subs are passed the message, and anything the regex captured.
2008-02-10 17:22:36 +00:00
my $ cc = shift ( @ ARGV ) ;
2008-02-11 01:39:09 +00:00
my $ showncmdline = 0 ;
# GCC's "location of error stuff", which accumulates the "In file included from" include stack
my $ location = "" ;
2008-02-09 01:55:31 +00:00
my @ msgfilters = (
[ qr/^(.*) warning: cannot pass objects of non-POD type `(.*)' through `\.\.\.'; call will abort at runtime/ = > sub {
my ( $ msg , $ where , $ type ) = @ _ ;
2008-02-15 13:30:46 +00:00
print $ location ;
$ location = "" ;
2008-02-09 01:55:31 +00:00
my $ errstr = "$where error: cannot pass objects of non-POD type `$type' through `...'\n" ;
2008-02-15 13:30:46 +00:00
if ( $ type =~ m/::(basic_)?string/ ) {
2008-02-09 01:55:31 +00:00
$ errstr . = "$where (Did you forget to call c_str()?)\n" ;
}
die $ errstr ;
} ] ,
2008-02-09 20:44:25 +00:00
2008-02-11 01:39:09 +00:00
# Start of an include stack.
[ qr/^In file included from .*[,:]$/ = > sub {
my ( $ msg ) = @ _ ;
$ location = "$msg\n" ;
} ] ,
# Continuation of an include stack.
[ qr/^ from .*[,:]$/ = > sub {
my ( $ msg ) = @ _ ;
$ location . = "$msg\n" ;
} ] ,
# A function, method, constructor, or destructor is the site of a problem
[ qr/In ((con|de)structor|(member )?function)/ = > sub {
my ( $ msg ) = @ _ ;
# If a complete location string is waiting then probably we dropped an error, so drop the location for a new one.
if ( $ location =~ m/In ((con|de)structor|(member )?function)/ ) {
$ location = "$msg\n" ;
} else {
$ location . = "$msg\n" ;
}
} ] ,
2008-02-09 20:44:25 +00:00
[ qr/^.* warning: / = > sub {
my ( $ msg ) = @ _ ;
2008-02-11 01:39:09 +00:00
print $ location ;
$ location = "" ;
2008-02-09 20:44:25 +00:00
print STDERR "\e[33;1m$msg\e[0m\n" ;
} ] ,
[ qr/^.* error: / = > sub {
my ( $ msg ) = @ _ ;
2008-02-11 01:39:09 +00:00
print STDERR "An error occured when executing:\e[37;1m $cc " . join ( ' ' , @ ARGV ) . "\n" unless $ showncmdline ;
$ showncmdline = 1 ;
print $ location ;
$ location = "" ;
2008-02-09 20:44:25 +00:00
print STDERR "\e[31;1m$msg\e[0m\n" ;
} ] ,
2008-02-09 01:55:31 +00:00
) ;
my $ pid ;
my ( $ r_stderr , $ w_stderr ) ;
2008-02-10 17:22:36 +00:00
my $ name = "" ;
2008-02-10 21:57:42 +00:00
my $ action = "" ;
2008-02-10 17:22:36 +00:00
foreach my $ n ( @ ARGV )
{
if ( $ n =~ /\.cpp$/ )
{
2008-02-24 14:07:25 +00:00
if ( $ action eq "BUILD" )
{
$ name . = " " . $ n ;
}
else
{
$ action = "BUILD" ;
$ name = $ n ;
}
}
elsif ( $ action eq "BUILD" ) # .cpp has priority.
{
next ;
2008-02-10 21:57:42 +00:00
}
2008-02-12 01:22:19 +00:00
elsif ( $ n eq "-o" )
{
$ action = $ name = $ n ;
}
elsif ( $ name eq "-o" )
2008-02-10 21:57:42 +00:00
{
$ action = "LINK" ;
2008-02-10 17:22:36 +00:00
$ name = $ n ;
}
}
2008-02-09 01:55:31 +00:00
if ( ! defined ( $ cc ) || $ cc eq "" ) {
die "Compiler not specified!\n" ;
}
pipe ( $ r_stderr , $ w_stderr ) or die "pipe stderr: $!\n" ;
$ pid = fork ;
die "Cannot fork to start gcc! $!\n" unless defined ( $ pid ) ;
if ( $ pid ) {
2008-02-10 17:22:36 +00:00
2008-02-10 21:57:42 +00:00
print "\t\e[1;32m$action:\e[0m\t\t$name\n" unless $ name eq "" ;
2008-02-10 17:22:36 +00:00
2008-02-09 01:55:31 +00:00
my $ fail = 0 ;
# Parent - Close child-side pipes.
close $ w_stderr ;
# Close STDIN to ensure no conflicts with child.
close STDIN ;
# Now read each line of stderr
LINE: while ( defined ( my $ line = <$r_stderr> ) ) {
chomp $ line ;
for my $ filter ( @ msgfilters ) {
my @ caps ;
if ( @ caps = ( $ line =~ $ filter - > [ 0 ] ) ) {
$@ = "" ;
eval {
$ filter - > [ 1 ] - > ( $ line , @ caps ) ;
} ;
if ( $@ ) {
$ fail = 1 ;
print STDERR $@ ;
}
next LINE ;
}
}
print STDERR "$line\n" ;
}
waitpid $ pid , 0 ;
close $ r_stderr ;
my $ exit = $? ;
# Simulate the same exit, so make gets the right termination info.
if ( POSIX:: WIFSIGNALED ( $ exit ) ) {
# Make won't get the right termination info (it gets ours, not the compiler's), so we must tell the user what really happened ourselves!
print STDERR "$cc killed by signal " . POSIX:: WTERMSIGN ( $ exit ) . "\n" ;
kill "TERM" , getppid ( ) ; # Needed for bsd make.
kill "TERM" , $$ ;
}
else {
if ( POSIX:: WEXITSTATUS ( $ exit ) == 0 ) {
if ( $ fail ) {
kill "TERM" , getppid ( ) ; # Needed for bsd make.
kill "TERM" , $$ ;
}
exit 0 ;
} else {
exit POSIX:: WEXITSTATUS ( $ exit ) ;
}
}
} else {
# Child - Close parent-side pipes.
close $ r_stderr ;
# Divert stderr
open STDERR , ">&" , $ w_stderr or die "Cannot divert STDERR: $!\n" ;
# Run the compiler!
exec { $ cc } $ cc , @ ARGV ;
die "exec $cc: $!\n" ;
}