diff --git a/clang/test/Analysis/scan-build/rebuild_index/rebuild_index.test b/clang/test/Analysis/scan-build/rebuild_index/rebuild_index.test new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/scan-build/rebuild_index/rebuild_index.test @@ -0,0 +1,44 @@ +// FIXME: Actually, "perl". +REQUIRES: shell + +// FIXME: Should ideally work on Windows. +UNSUPPORTED: system-windows + +RUN: rm -rf %t.output_dir && mkdir %t.output_dir +RUN: cp %S/report-1.html %t.output_dir +RUN: cp %S/report-2.html %t.output_dir +RUN: cp %S/report-3.html %t.output_dir +RUN: mkdir %t.output_dir/subdirectory +RUN: cp %S/subdirectory/report-4.html %t.output_dir/subdirectory + +RUN: %scan-build --generate-index-only %t.output_dir + +RUN: ls %t.output_dir | FileCheck -check-prefix CHECK-FILES %s + +CHECK-FILES: index.html +CHECK-FILES-NEXT: report-1.html +CHECK-FILES-NEXT: report-2.html + +// report-3.html is a duplicate of report-1.html so it's not present. +CHECK-FILES-NOT: report-3.html +CHECK-FILES-NEXT: scanview.css +CHECK-FILES-NEXT: sorttable.js +CHECK-FILES-NEXT: subdirectory + +RUN: ls %t.output_dir/subdirectory | FileCheck -check-prefix CHECK-SUB %s + +CHECK-SUB: report-4.html + +RUN: cat %t.output_dir/index.html | FileCheck -check-prefix CHECK-INDEX %s + +CHECK-INDEX: cat1 +CHECK-INDEX-NEXT: bug1 +CHECK-INDEX-NEXT: cat2 +CHECK-INDEX-NEXT: bug2 +CHECK-INDEX-NEXT: cat4 +CHECK-INDEX-NEXT: bug4 + +CHECK-INDEX: report-1.html#EndPath +CHECK-INDEX: report-2.html#EndPath +CHECK-INDEX-NOT: report-3.html#EndPath +CHECK-INDEX: subdirectory/report-4.html#EndPath diff --git a/clang/test/Analysis/scan-build/rebuild_index/report-1.html b/clang/test/Analysis/scan-build/rebuild_index/report-1.html new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/scan-build/rebuild_index/report-1.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/clang/test/Analysis/scan-build/rebuild_index/report-2.html b/clang/test/Analysis/scan-build/rebuild_index/report-2.html new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/scan-build/rebuild_index/report-2.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/clang/test/Analysis/scan-build/rebuild_index/report-3.html b/clang/test/Analysis/scan-build/rebuild_index/report-3.html new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/scan-build/rebuild_index/report-3.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/clang/test/Analysis/scan-build/rebuild_index/subdirectory/report-4.html b/clang/test/Analysis/scan-build/rebuild_index/subdirectory/report-4.html new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/scan-build/rebuild_index/subdirectory/report-4.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/clang/tools/scan-build/bin/scan-build b/clang/tools/scan-build/bin/scan-build --- a/clang/tools/scan-build/bin/scan-build +++ b/clang/tools/scan-build/bin/scan-build @@ -72,8 +72,9 @@ MaxLoop => 0, PluginsToLoad => [], AnalyzerDiscoveryMethod => undef, - OverrideCompiler => 0, # The flag corresponding to the --override-compiler command line option. - ForceAnalyzeDebugCode => 0 + OverrideCompiler => 0, # The flag corresponding to the --override-compiler command line option. + ForceAnalyzeDebugCode => 0, + GenerateIndex => 0 # Skip the analysis, only generate index.html. ); lock_keys(%Options); @@ -946,6 +947,41 @@ return $Num; } +sub Finalize { + my $BaseDir = shift; + my $ExitStatus = shift; + + Diag "Analysis run complete.\n"; + if (defined $Options{OutputFormat}) { + if ($Options{OutputFormat} =~ /plist/ || + $Options{OutputFormat} =~ /sarif/) { + Diag "Analysis results (" . + ($Options{OutputFormat} =~ /plist/ ? "plist" : "sarif") . + " files) deposited in '$Options{OutputDir}'\n"; + } + if ($Options{OutputFormat} =~ /html/) { + # Postprocess the HTML directory. + my $NumBugs = Postprocess($Options{OutputDir}, $BaseDir, + $Options{AnalyzerStats}, $Options{KeepEmpty}); + + if ($Options{ViewResults} and -r "$Options{OutputDir}/index.html") { + Diag "Viewing analysis results in '$Options{OutputDir}' using scan-view.\n"; + my $ScanView = Cwd::realpath("$RealBin/scan-view"); + if (! -x $ScanView) { $ScanView = "scan-view"; } + if (! -x $ScanView) { $ScanView = Cwd::realpath("$RealBin/../../scan-view/bin/scan-view"); } + exec $ScanView, "$Options{OutputDir}"; + } + + if ($Options{ExitStatusFoundBugs}) { + exit 1 if ($NumBugs > 0); + exit $ExitStatus; + } + } + } + + exit $ExitStatus; +} + ##----------------------------------------------------------------------------## # RunBuildCommand - Run the build command. ##----------------------------------------------------------------------------## @@ -1259,6 +1295,12 @@ View analysis results in a web browser when the build completes. + --generate-index-only + + Do not perform the analysis, but only regenerate the index.html file + from existing report.html files. Useful for making a custom Static Analyzer + integration into a build system that isn't otherwise supported by scan-build. + ADVANCED OPTIONS: -no-failure-reports @@ -1550,6 +1592,10 @@ } if ($arg eq "-o") { + if (defined($Options{OutputDir})) { + DieDiag("Only one of '-o' or '--generate-index-only' can be specified.\n"); + } + shift @$Args; if (!@$Args) { @@ -1565,6 +1611,27 @@ next; } + if ($arg eq "--generate-index-only") { + if (defined($Options{OutputDir})) { + DieDiag("Only one of '-o' or '--generate-index-only' can be specified.\n"); + } + + shift @$Args; + + if (!@$Args) { + DieDiag("'--generate-index-only' option requires a target directory name.\n"); + } + + # Construct an absolute path. Uses the current working directory + # as a base if the original path was not absolute. + my $OutDir = shift @$Args; + mkpath($OutDir) unless (-e $OutDir); # abs_path wants existing dir + $Options{OutputDir} = abs_path($OutDir); + $Options{GenerateIndex} = 1; + + next; + } + if ($arg =~ /^--html-title(=(.+))?$/) { shift @$Args; @@ -1815,11 +1882,6 @@ ProcessArgs(\@ARGV); # All arguments are now shifted from @ARGV. The rest is a build command, if any. -if (!@ARGV and !$RequestDisplayHelp) { - ErrorDiag("No build command specified.\n\n"); - $ForceDisplayHelp = 1; -} - my $ClangNotFoundErrMsg = FindClang(); if ($ForceDisplayHelp || $RequestDisplayHelp) { @@ -1827,6 +1889,24 @@ exit $ForceDisplayHelp; } +$CmdArgs = HtmlEscape(join(' ', map(ShellEscape($_), @ARGV))); + +# Make sure to use "" to handle paths with spaces. +$ClangVersion = HtmlEscape(`"$Clang" --version`); + +if ($Options{GenerateIndex}) { + Finalize($Options{OutputDir}, 0); +} + +if (!@ARGV and !$RequestDisplayHelp) { + ErrorDiag("No build command specified.\n\n"); + $ForceDisplayHelp = 1; +} + +# Determine the output directory for the HTML reports. +my $BaseDir = $Options{OutputDir}; +$Options{OutputDir} = GetHTMLRunDir($Options{OutputDir}); + DieDiag($ClangNotFoundErrMsg) if (defined $ClangNotFoundErrMsg); $ClangCXX = $Clang; @@ -1846,16 +1926,6 @@ } } -# Make sure to use "" to handle paths with spaces. -$ClangVersion = HtmlEscape(`"$Clang" --version`); - -# Determine where results go. -$CmdArgs = HtmlEscape(join(' ', map(ShellEscape($_), @ARGV))); - -# Determine the output directory for the HTML reports. -my $BaseDir = $Options{OutputDir}; -$Options{OutputDir} = GetHTMLRunDir($Options{OutputDir}); - # Determine the location of ccc-analyzer. my $AbsRealBin = Cwd::realpath($RealBin); my $Cmd = "$AbsRealBin/../libexec/ccc-analyzer"; @@ -1931,33 +2001,4 @@ my $ExitStatus = RunBuildCommand(\@ARGV, $Options{IgnoreErrors}, $Options{KeepCC}, $Cmd, $CmdCXX, \%EnvVars); -if (defined $Options{OutputFormat}) { - if ($Options{OutputFormat} =~ /plist/ || - $Options{OutputFormat} =~ /sarif/) { - Diag "Analysis run complete.\n"; - Diag "Analysis results (" . - ($Options{OutputFormat} =~ /plist/ ? "plist" : "sarif") . - " files) deposited in '$Options{OutputDir}'\n"; - } - if ($Options{OutputFormat} =~ /html/) { - # Postprocess the HTML directory. - my $NumBugs = Postprocess($Options{OutputDir}, $BaseDir, - $Options{AnalyzerStats}, $Options{KeepEmpty}); - - if ($Options{ViewResults} and -r "$Options{OutputDir}/index.html") { - Diag "Analysis run complete.\n"; - Diag "Viewing analysis results in '$Options{OutputDir}' using scan-view.\n"; - my $ScanView = Cwd::realpath("$RealBin/scan-view"); - if (! -x $ScanView) { $ScanView = "scan-view"; } - if (! -x $ScanView) { $ScanView = Cwd::realpath("$RealBin/../../scan-view/bin/scan-view"); } - exec $ScanView, "$Options{OutputDir}"; - } - - if ($Options{ExitStatusFoundBugs}) { - exit 1 if ($NumBugs > 0); - exit $ExitStatus; - } - } -} - -exit $ExitStatus; +Finalize($BaseDir, $ExitStatus);