summaryrefslogtreecommitdiff
path: root/run-cc.pl
diff options
context:
space:
mode:
Diffstat (limited to 'run-cc.pl')
-rwxr-xr-xrun-cc.pl100
1 files changed, 100 insertions, 0 deletions
diff --git a/run-cc.pl b/run-cc.pl
new file mode 100755
index 000000000..29af15393
--- /dev/null
+++ b/run-cc.pl
@@ -0,0 +1,100 @@
+#!/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.
+
+my @msgfilters = (
+ [ qr/^(.*) warning: cannot pass objects of non-POD type `(.*)' through `\.\.\.'; call will abort at runtime/ => sub {
+ my ($msg, $where, $type) = @_;
+ my $errstr = "$where error: cannot pass objects of non-POD type `$type' through `...'\n";
+ if ($type =~ m/::string/) {
+ $errstr .= "$where (Did you forget to call c_str()?)\n";
+ }
+ die $errstr;
+ } ],
+);
+
+my $pid;
+
+my ($r_stderr, $w_stderr);
+
+my $cc = shift(@ARGV);
+
+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) {
+ 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";
+}