| #!/usr/bin/env perl |
| # |
| # The LLVM Compiler Infrastructure |
| # |
| # This file is distributed under the University of Illinois Open Source |
| # License. See LICENSE.TXT for details. |
| # |
| ##===----------------------------------------------------------------------===## |
| # |
| # A script designed to wrap a build so that all calls to gcc are intercepted |
| # and piped to the static analyzer. |
| # |
| ##===----------------------------------------------------------------------===## |
| |
| use strict; |
| use warnings; |
| use File::Temp qw/ :mktemp /; |
| |
| my $Verbose = 0; # Verbose output from this script. |
| my $Prog = "scan-build"; |
| |
| ##----------------------------------------------------------------------------## |
| # GetHTMLRunDir - Construct an HTML directory name for the current run. |
| ##----------------------------------------------------------------------------## |
| |
| sub GetHTMLRunDir { |
| |
| die "Not enough arguments." if (@_ == 0); |
| |
| my $Dir = shift @_; |
| |
| # Get current date and time. |
| |
| my @CurrentTime = localtime(); |
| |
| my $year = $CurrentTime[5] + 1900; |
| my $day = $CurrentTime[3]; |
| my $month = $CurrentTime[4] + 1; |
| |
| my $DateString = "$year-$month-$day"; |
| |
| # Determine the run number. |
| |
| my $RunNumber; |
| |
| if (-d $Dir) { |
| |
| if (! -r $Dir) { |
| die "error: '$Dir' exists but is not readable.\n"; |
| } |
| |
| # Iterate over all files in the specified directory. |
| |
| my $max = 0; |
| |
| opendir(DIR, $Dir); |
| my @FILES= readdir(DIR); |
| closedir(DIR); |
| |
| foreach my $f (@FILES) { |
| |
| my @x = split/-/, $f; |
| |
| next if (scalar(@x) != 4); |
| next if ($x[0] != $year); |
| next if ($x[1] != $month); |
| next if ($x[2] != $day); |
| |
| if ($x[3] > $max) { |
| $max = $x[3]; |
| } |
| } |
| |
| $RunNumber = $max + 1; |
| } |
| else { |
| |
| if (-x $Dir) { |
| die "error: '$Dir' exists but is not a directory.\n"; |
| } |
| |
| # $Dir does not exist. It will be automatically created by the |
| # clang driver. Set the run number to 1. |
| |
| $RunNumber = 1; |
| } |
| |
| die "RunNumber must be defined!" if (!defined($RunNumber)); |
| |
| # Append the run number. |
| |
| return "$Dir/$DateString-$RunNumber"; |
| } |
| |
| sub SetHtmlEnv { |
| |
| die "Wrong number of arguments." if (scalar(@_) != 2); |
| |
| my $Args = shift; |
| my $Dir = shift; |
| |
| die "No build command." if (scalar(@$Args) == 0); |
| |
| my $Cmd = $$Args[0]; |
| |
| if ($Cmd =~ /configure/) { |
| return; |
| } |
| |
| if ($Verbose) { |
| print "$Prog: Emitting reports for this run to '$Dir'.\n"; |
| } |
| |
| $ENV{'CCC_ANALYZER_HTML'} = $Dir; |
| } |
| |
| ##----------------------------------------------------------------------------## |
| # Postprocess - Postprocess the results of an analysis scan. |
| ##----------------------------------------------------------------------------## |
| |
| sub Postprocess { |
| |
| my $Dir = shift; |
| |
| die "No directory specified." if (!defined($Dir)); |
| |
| if (! -d $Dir) { |
| return; |
| } |
| |
| opendir(DIR, $Dir); |
| my @files = grep(/^report-.*\.html$/,readdir(DIR)); |
| closedir(DIR); |
| |
| if (scalar(@files) == 0) { |
| print "$Prog: Removing directory '$Dir' because it contains no reports.\n"; |
| `rm -fR $Dir`; |
| return; |
| } |
| |
| |
| } |
| |
| ##----------------------------------------------------------------------------## |
| # RunBuildCommand - Run the build command. |
| ##----------------------------------------------------------------------------## |
| |
| sub RunBuildCommand { |
| |
| my $Args = shift; |
| my $IgnoreErrors = shift; |
| my $Cmd = $Args->[0]; |
| |
| if ($Cmd eq "gcc" or $Cmd eq "cc" or $Cmd eq "llvm-gcc") { |
| shift @$Args; |
| unshift @$Args, "ccc-analyzer" |
| } |
| elsif ($IgnoreErrors) { |
| if ($Cmd eq "make" or $Cmd eq "gmake") { |
| push @$Args, "-k"; |
| } |
| elsif ($Cmd eq "xcodebuild") { |
| push @$Args, "-PBXBuildsContinueAfterErrors=YES"; |
| } |
| } |
| |
| system(@$Args); |
| } |
| |
| ##----------------------------------------------------------------------------## |
| # DisplayHelp - Utility function to display all help options. |
| ##----------------------------------------------------------------------------## |
| |
| sub DisplayHelp { |
| |
| print <<ENDTEXT |
| USAGE: $Prog [options] <build command> [build options] |
| |
| OPTIONS: |
| |
| -o - Target directory for HTML report files. Subdirectories |
| will be created as needed to represent separate "runs" of |
| the analyzer. If this option is not specified, a directory |
| is created in /tmp to store the reports. |
| |
| -?, -h - Display this message. |
| --help |
| |
| -k - Add "keep on going option" to the specified build command. |
| --keep-going This command currently supports make and xcodebuild. |
| This is a helper option; one can specify the arguments |
| directly as build options. |
| |
| -v - Verbose output from $Prog and the analyzer. |
| A second "-v" increases verbosity. |
| |
| BUILD OPTIONS |
| |
| You can specify any build option acceptable to the build command. For |
| example: |
| |
| $Prog -o /tmp/myhtmldir make -j4 |
| |
| The above causes analysis reports to be deposited in /tmp/myhtmldir (or |
| rather a subdirectory corresponding to this particular running of the |
| analyzer), and causes "make" to be run with the "-j4" option, allowing |
| parallel builds (and parallel invocations of the analyzer). |
| |
| NOTE: The analyzer will work for most parallel builds, but not distributed |
| builds (such as using distcc). |
| |
| ENDTEXT |
| } |
| |
| ##----------------------------------------------------------------------------## |
| # Process command-line arguments. |
| ##----------------------------------------------------------------------------## |
| |
| my $HtmlDir; # Parent directory to store HTML files. |
| my $IgnoreErrors = 0; # Ignore build errors. |
| |
| if (!@ARGV) { |
| DisplayHelp(); |
| exit 1; |
| } |
| |
| while (@ARGV) { |
| |
| # Scan for options we recognize. |
| |
| my $arg = $ARGV[0]; |
| |
| if ($arg eq "-?" or $arg eq "-h" or $arg eq "--help") { |
| DisplayHelp(); |
| exit 0; |
| } |
| |
| if ($arg eq "-o") { |
| shift @ARGV; |
| |
| if (!@ARGV) { |
| die "'-o' option requires a target directory name."; |
| } |
| |
| $HtmlDir = shift @ARGV; |
| next; |
| } |
| |
| if ($arg eq "-k" or $arg eq "--keep-going") { |
| shift @ARGV; |
| $IgnoreErrors = 1; |
| next; |
| } |
| |
| if ($arg eq "-v") { |
| shift @ARGV; |
| $Verbose++; |
| next; |
| } |
| |
| last; |
| } |
| |
| if (!@ARGV) { |
| print STDERR "$Prog: No build command specified.\n\n"; |
| DisplayHelp(); |
| exit 1; |
| } |
| |
| # Determine the output directory for the HTML reports. |
| |
| if (!defined($HtmlDir)) { |
| |
| $HtmlDir = mkdtemp("/tmp/$Prog-XXXXXX"); |
| |
| if (!defined($HtmlDir)) { |
| die "error: Cannot create HTML directory in /tmp.\n"; |
| } |
| |
| if (!$Verbose) { |
| print "$Prog: Using '$HtmlDir' as base HTML report directory.\n"; |
| } |
| } |
| |
| $HtmlDir = GetHTMLRunDir($HtmlDir); |
| |
| # Set the appropriate environment variables. |
| |
| SetHtmlEnv(\@ARGV, $HtmlDir); |
| |
| $ENV{'CC'} = "ccc-analyzer"; |
| |
| if ($Verbose >= 2) { |
| $ENV{'CCC_ANALYZER_VERBOSE'} = 1; |
| } |
| |
| # Run the build. |
| |
| RunBuildCommand(\@ARGV, $IgnoreErrors); |
| |
| # Postprocess the HTML directory. |
| |
| Postprocess($HtmlDir); |