| #!/usr/bin/env perl |
| |
| # Copyright (C) 2011, 2012, 2013, 2014, 2020 Apple Inc. All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions |
| # are met: |
| # 1. Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY |
| # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| # Filters the output of build-webkit into a more human-readable format. |
| package FilterBuildWebKit; |
| |
| BEGIN { |
| use Exporter (); |
| our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); |
| $VERSION = 1.00; |
| @ISA = qw(Exporter); |
| @EXPORT = qw(&shouldIgnoreLine); |
| %EXPORT_TAGS = (); |
| @EXPORT_OK = (); |
| } |
| |
| use strict; |
| use warnings; |
| |
| use CGI qw(escapeHTML); |
| use File::Basename; |
| use FindBin; |
| use lib $FindBin::Bin; |
| use Getopt::Long; |
| use VCSUtils; |
| |
| use constant { |
| STYLE_PLAIN => 0, |
| STYLE_HEADER => 1, |
| STYLE_SUCCESS => 2, |
| STYLE_ALERT => 3, |
| |
| HTML_HEADER =><<HTMLHEADER, |
| <html> |
| <head> |
| <title>Build Log</title> |
| <style> |
| body { font-family: Monaco, monospace; font-size: 10px; color: #666; line-height: 1.5em; } |
| h2 { margin: 1.5em 0 0 0; font-size: 1.0em; font-weight: bold; color: blue; } |
| p { margin: 0; padding-left: 1.5em; border-left: 3px solid #fff; } |
| p.alert { border-left-color: red; color: red; margin: 1.5em 0 0 0; } |
| p.alert + p { margin: 1.5em 0 0 0; } |
| p.alert + p.alert { margin: 0; } |
| p.success { color: green; } |
| </style> |
| </head> |
| <body> |
| HTMLHEADER |
| |
| HTML_FOOTER =><<HTMLFOOTER, |
| </body> |
| </html> |
| HTMLFOOTER |
| }; |
| |
| sub printLine($$); |
| sub setLogfileOption($$); |
| sub setOutputFormatOption($$); |
| sub shouldIgnoreLine($$); |
| sub usageAndExit(); |
| |
| # Defined in VCSUtils. |
| sub possiblyColored($$); |
| |
| # Global variables used only in global scope. |
| my $outputPath = "&STDOUT"; |
| my $platform = "mac"; |
| my $showHelp; |
| |
| # Global variables used in global and subroutine scope. |
| our $logUnfilteredOutput; |
| our $outputFormat = "text"; |
| our $unfilteredOutputPath = "build.log"; |
| our $useColor = -t STDOUT; |
| our $inEntitlements = 0; |
| our $inDevicePreparationWarnings = 0; |
| |
| sub usageAndExit() |
| { |
| print STDERR <<__END__; |
| Usage: @{[ basename($0) ]} [options] buildlog1 [buildlog2 ...] |
| build-webkit | @{[ basename($0) ]} [options] |
| -h|--help Show this help message |
| -p|--platform Logfile type (default: $platform) |
| Output Options: |
| -o|--output Path for output (default: STDOUT) |
| -f|--format Output format (default: $outputFormat) |
| text: Plain text |
| html: Standalone HTML document |
| --[no-]color ANSI color output for text (default: on, if -o is STDOUT) |
| Unfiltered Logging Options: |
| -l|--log Save unfiltered output to file (see --log-file) |
| --logfile Path to save unfiltered output (implies --log, default: $unfilteredOutputPath) |
| __END__ |
| exit 1; |
| } |
| |
| sub main() { |
| my $getOptionsResult = GetOptions( |
| 'h|help' => \$showHelp, |
| 'o|output=s' => \$outputPath, |
| 'p|platform=s' => \$platform, |
| 'f|format=s' => \&setOutputFormatOption, |
| 'color!' => \$useColor, |
| 'l|log' => \$logUnfilteredOutput, |
| 'logfile=s' => \&setLogfileOption, |
| ); |
| |
| if (-t STDIN || $showHelp || !$getOptionsResult) { |
| usageAndExit(); |
| } |
| |
| open(OUTPUT_HANDLE, ">$outputPath") or die "Failed to open $outputPath : $!"; |
| if ($logUnfilteredOutput) { |
| open(UNFILTERED_OUTPUT_HANDLE, ">$unfilteredOutputPath") or die "Failed to open $unfilteredOutputPath : $!"; |
| } |
| |
| print OUTPUT_HANDLE HTML_HEADER if ($outputFormat eq "html"); |
| |
| my $buildFinished; |
| my $buildFailed = 0; |
| for (my $previousLine = "", my $line = <>; $line; $previousLine = $line, $line = <>) { |
| print UNFILTERED_OUTPUT_HANDLE $line if $logUnfilteredOutput; |
| |
| chomp($line); |
| |
| next if shouldIgnoreLine($previousLine, $line); |
| |
| $line =~ s/\(in target .* from project .*\)$//g; |
| |
| if ($line =~ /^={10}/) { |
| printLine($line, STYLE_SUCCESS); |
| $buildFinished = 1; |
| } elsif ($line =~ /^===/) { |
| printLine($line, STYLE_HEADER); |
| } elsif ($line =~ /^note: [Uu]sing/) { |
| printLine($line, STYLE_HEADER); |
| } elsif ($line =~ /Checking Dependencies|Check dependencies|Create product structure|Write auxiliary files|LinkStoryboards/) { |
| printLine($line, STYLE_PLAIN); |
| } elsif ($line =~ /\*\* BUILD SUCCEEDED \*\*/) { |
| printLine("Build Succeeded", STYLE_SUCCESS); |
| } elsif ($line =~ /^(\e\[1m)?(PhaseScriptExecution|RuleScriptExecution|ClCompile|CompileC|Distributed-CompileC|Ld|PBXCp|CpResource|CopyPNGFile|CopyTiffFile|CpHeader|Preprocess|Processing|ProcessInfoPlistFile|ProcessPCH|ProcessPCH\+\+|Touch|Libtool|CopyStringsFile|Mig|CreateUniversalBinary|Analyze|AnalyzeShallow|ProcessProductPackaging|CodeSign|Validate|SymLink|Updating|CompileDTraceScript|CompileXIB|StripNIB|CopyPlistFile|GenerateDSYMFile|GenerateTAPI|CompileStoryboard|ExternalBuildToolExecution|CreateBuildDirectory|WriteAuxiliaryFile|RegisterWithLaunchServices|RegisterExecutionPolicyException|MkDir|Strip|MetalLink|CompileMetalFile|ValidateEmbeddedBinary)(\e\[0m)? ("[^"]+"|(\\|(?<=\\)\s|\S)+)?/) { |
| my ($command, $path) = ($2, basename($4)); |
| $path =~ s/("|\\|\.[ah]$)//g; |
| printLine("$command $path", STYLE_PLAIN); |
| } elsif ($line =~ /^(Ditto) (\S+) (\S+)/) { |
| my ($command, $path) = ($1, basename($3)); |
| printLine("$command $path", STYLE_PLAIN); |
| } elsif ($line =~ /^(CompileAssetCatalog) .*/) { |
| printLine("$1", STYLE_PLAIN); |
| } elsif ($line =~ /^\S+mkdir .*?(\S+)$/) { |
| my $path = basename($1); |
| printLine("mkdir $path", STYLE_PLAIN); |
| } elsif ($line =~ /^\S+\/usr\/bin\/tapi reexport .*?(\S+)$/) { |
| my $path = basename($1); |
| printLine("tapi $path", STYLE_PLAIN); |
| } elsif ($line =~ /^plutil .*?(\S+)$/) { |
| my $path = basename($1); |
| printLine("plutil $path", STYLE_PLAIN); |
| } elsif ($line =~ /^cp (\S+)/) { |
| my $path = basename($1); |
| printLine("cp $path", STYLE_PLAIN); |
| } elsif ($line =~ /python(\d\.\d+)? (\S+) (.+)/) { |
| my ($command, $path) = (basename($2), basename($3)); |
| printLine("python $command $path", STYLE_PLAIN); |
| } elsif ($line =~ /^\/\S+?(strip|WebCoreExportFileGenerator) .*?(\/|\> )(\S+)/) { |
| my ($command, $path) = (basename($1), basename($3)); |
| printLine("$command $path", STYLE_PLAIN); |
| } elsif ($line =~ /^offlineasm\: /) { |
| printLine($line, STYLE_PLAIN); |
| } elsif ($line =~ /^Generating bindings for the (\S+) builtin\./) { |
| printLine("Generating $1 builtin", STYLE_PLAIN); |
| } elsif ($line =~ /^Generating (bindings|messages? (header|receiver|dispatcher)|derived source) for (\S+)\.\.\./) { |
| my ($command, $path) = ($1, basename($3)); |
| printLine("Generating $command $path", STYLE_PLAIN); |
| } elsif ($line =~ /^(Generating|Merging) (\S+) (from|for) (\S+)/) { |
| printLine($line, STYLE_PLAIN); |
| } elsif ($line =~ /^Postprocessed ANGLE header:? (\S+)/) { |
| my $path = basename($1); |
| printLine("Postprocessed ANGLE header $path", STYLE_PLAIN); |
| } elsif ($line =~ /^Prepare build/) { |
| printLine($line, STYLE_PLAIN); |
| } elsif ($line =~ /^Signing Identity:/) { |
| printLine($line, STYLE_PLAIN); |
| } elsif ($line =~ /^Pre-processing (\S+) sandbox profile/) { |
| printLine($line, STYLE_PLAIN); |
| } elsif ($line =~ /^Scripts\/generate-unified-source-bundles.rb/) { |
| printLine("Generating unified sources", STYLE_PLAIN); |
| } elsif ($line =~ /^ruby JavaScriptCore\/generator\/main.rb JavaScriptCore\/bytecode\/BytecodeList.rb.*/) { |
| printLine("Generating bytecode list", STYLE_PLAIN); |
| } elsif ($line =~ /^ruby JavaScriptCore\/b3\/air\/opcode_generator.rb JavaScriptCore\/b3\/air\/AirOpcode.opcodes$/) { |
| printLine("Generating opcodes", STYLE_PLAIN); |
| } elsif ($line =~ /^ruby WebCore\/Scripts\/GenerateSettings.rb --input .*/) { |
| printLine("Generating settings", STYLE_PLAIN); |
| } elsif ($line =~ /^ruby "?WebCore\/domjit\/generate-abstract-heap.rb"? (\S+) (\S+)/) { |
| printLine("Generating abstract heap", STYLE_PLAIN); |
| } elsif ($line =~ /^bash -c "perl JavaScriptCorePrivateHeaders\/xxd.pl .* \<\(gzip -cn .*\) .*"/) { |
| printLine("Converting WHLSLStandardLibrary", STYLE_PLAIN); |
| } elsif ($line =~ /^sh .*\/generate-https-upgrade-database\.sh .*\/HTTPSUpgradeList.txt HTTPSUpgradeList.db/) { |
| printLine("Converting HTTPSUpgradeList", STYLE_PLAIN); |
| } elsif ($line =~ /^.*\/GeneratePreferences.rb --input .*\.yaml/) { |
| printLine("Generating preferences", STYLE_PLAIN); |
| } elsif ($line =~ /^### (Generating \.xcfilelists for .*)$/) { |
| printLine("$1", STYLE_PLAIN); |
| } elsif ($line =~ /^(Pre-processing InspectorBackendCommands\.\.\.)$/) { |
| printLine("$1", STYLE_PLAIN); |
| } elsif ($line =~ /^(Unlocking '.*keychain-db')$/) { |
| printLine("$1", STYLE_PLAIN); |
| } elsif ($line =~ /^(Using unified source list files: .*)$/) { |
| printLine("$1", STYLE_PLAIN); |
| } elsif ($line =~ /^(\S+\/cc).*?(\S+)\.(out|exp)/) { |
| my ($command, $path) = (basename($1), basename($2)); |
| printLine("$command $path", STYLE_PLAIN); |
| } elsif ($line =~ /^(File Doesn't Exist, Will Create:)(.*\.entitlements)$/) { |
| my $path = basename($2); |
| printLine("Creating Entitlements File $path", STYLE_PLAIN); |
| } elsif ($line =~ /^(building \S+)$/) { |
| printLine("$1", STYLE_PLAIN); |
| } elsif ($line =~ /^(running build command .*+)$/) { |
| printLine("$1", STYLE_PLAIN); |
| } else { |
| # This only gets hit if stderr is redirected to stdout. |
| if (($line =~ /\*\* BUILD FAILED \*\*/) || ($line =~ /^Build FAILED./)) { |
| $buildFailed = 1; |
| } |
| printLine($line, $buildFinished ? STYLE_SUCCESS : STYLE_ALERT); |
| } |
| } |
| |
| print OUTPUT_HANDLE HTML_FOOTER if ($outputFormat eq "html"); |
| |
| close(OUTPUT_HANDLE); |
| close(UNFILTERED_OUTPUT_HANDLE) if ($logUnfilteredOutput); |
| |
| exit $buildFailed; |
| } |
| |
| sub printLine($$) |
| { |
| my ($line, $style) = @_; |
| |
| if ($outputFormat eq "html") { |
| $line = escapeHTML($line); |
| if ($style == STYLE_HEADER) { print OUTPUT_HANDLE "<h2>$line</h2>"; } |
| elsif ($style == STYLE_SUCCESS) { print OUTPUT_HANDLE "<p class=\"success\">$line</p>"; } |
| elsif ($style == STYLE_ALERT) { print OUTPUT_HANDLE "<p class=\"alert\">$line</p>"; } |
| else { print OUTPUT_HANDLE "<p>$line</p>"; } |
| } else { |
| if ($useColor) { |
| my $colors = "reset"; |
| if ($style == STYLE_HEADER) { $colors = "blue"; } |
| if ($style == STYLE_SUCCESS) { $colors = "green"; } |
| if ($style == STYLE_ALERT) { $colors = "red"; } |
| print OUTPUT_HANDLE possiblyColored($colors, $line); |
| } else { |
| print OUTPUT_HANDLE $line; |
| } |
| } |
| print OUTPUT_HANDLE "\n"; |
| } |
| |
| sub setLogfileOption($$) |
| { |
| my ($opt, $value) = @_; |
| $unfilteredOutputPath = $value; |
| $logUnfilteredOutput = 1; |
| } |
| |
| sub setOutputFormatOption($$) |
| { |
| my ($opt, $value) = @_; |
| $value = lc($value); |
| if ($value ne "html" && $value ne "text") { |
| die "The $opt option must be either \"html\" or \"text\""; |
| } |
| $outputFormat = $value; |
| } |
| |
| sub shouldShowSubsequentLine($) |
| { |
| my ($line) = @_; |
| |
| return 1 if $line =~ /referenced from:$/; |
| return 1 if $line =~ /(note:|error:)/; |
| |
| return 0; |
| } |
| |
| sub shouldIgnoreLine($$) |
| { |
| my ($previousLine, $line) = @_; |
| |
| if ($line =~ /^Entitlements:$/) { |
| $inEntitlements = 1; |
| return 1 |
| } |
| |
| if ($inEntitlements) { |
| $inEntitlements = 0 if $line =~ /^}$/; |
| return 1 |
| } |
| |
| # iPhone preparation errors always start and end with lines containing 'iPhoneConnect:'. |
| if ($inDevicePreparationWarnings) { |
| $inDevicePreparationWarnings = 0 if $line =~ /== END: Underlying device preparation warnings ==/; |
| return 1 |
| } |
| |
| if ($line =~ /iPhoneConnect:/) { |
| $inDevicePreparationWarnings = 1; |
| return 1 |
| } |
| |
| return 1 if $line =~ /^\s*$/; |
| return 1 if $line =~ /^Command line invocation:/; |
| return 1 if $line =~ /^Build settings from command line:/; |
| return 1 if $line =~ /^User defaults from command line:/; |
| return 1 if $line =~ /^Prepare build/; |
| return 1 if $line =~ /^Build system information/; |
| return 1 if $line =~ /^note: Planning build/; |
| return 1 if $line =~ /^note: Constructing build description/; |
| return 1 if $line =~ /^note: Build description (constructed|loaded) in .*/; |
| return 1 if $line =~ /^note: Using build description .*/; |
| return 1 if $line =~ /^note: Using eager compilation/; |
| return 1 if $line =~ /^note: Execution policy exception registration failed and was skipped: Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"/; |
| return 1 if $line =~ /^note: detected encoding of input file as Unicode \(.*\)/; |
| return 1 if $line =~ /make(\[\d+\])?: Nothing to be done for/; |
| return 1 if $line =~ /^JavaScriptCore\/create_hash_table/; |
| return 1 if $line =~ /JavaScriptCore.framework\/PrivateHeaders\/create_hash_table/; |
| return 1 if $line =~ /^JavaScriptCore\/pcre\/dftables/; |
| return 1 if $line =~ /^Creating hashtable for /; |
| return 1 if $line =~ /^Wrote output to /; |
| return 1 if $line =~ /^UNDOCUMENTED: /; |
| return 1 if $line =~ /libtool.*has no symbols/; |
| return 1 if $line =~ /^# Lower case all the values, as CSS values are case-insensitive$/; |
| return 1 if $line =~ /^if sort /; |
| return 1 if $line =~ /set-webkit-configuration/; |
| return 1 if $line =~ /^building file list/; |
| return 1 if $line =~ /^\.\/$/; |
| return 1 if $line =~ /^\S+\.h$/; |
| return 1 if $line =~ /^\S+\/$/; |
| return 1 if $line =~ /^sent \d+ bytes/; |
| return 1 if $line =~ /^total size is/; |
| return 1 if $line =~ /One of the two will be used\. Which one is undefined\./; |
| return 1 if $line =~ /The Legacy Build System will be removed in a future release/; |
| return 1 if $line =~ /^\( (xcodebuild|if) /; |
| return 1 if $line =~ /^warning: can't find additional SDK/; |
| return 1 if $line =~ /^warning: no umbrella header found for target '.*', module map will not be generated$/; |
| return 1 if $line =~ /^warning\: detected internal install, passing entitlements to simulator anyway\./; |
| return 1 if $line =~ /may not function in the Simulator because Ad Hoc/; |
| return 1 if $line =~ /\/usr\/bin\/clang .*? \> \S+.sb/; |
| return 1 if $line =~ / xcodebuild\[[0-9]+:[0-9a-f]+\]\s+DVTAssertions: Warning in .*/; |
| return 1 if $line =~ /^(Details|Object|Method|Function|Thread):/; |
| return 1 if $line =~ /^Please file a bug at /; |
| return 1 if $line =~ /created by an unsupported XCDependencyGraph build$/; |
| return 1 if $line =~ /warning: The assignment of '.*' at ".*" uses \$\(inherited\). In the new build system this will inherit from an earlier definition of '.*' in this xcconfig file or its imports; the old build system would discard earlier definitions. This may result in changes to resolved build setting values./; |
| return 1 if $line =~ /.* com.apple.actool.compilation-results .*/; |
| return 1 if $line =~ /.*\/Assets.car/; |
| return 1 if $line =~ /.*\/assetcatalog_generated_info.plist/; |
| return 1 if $line =~ /^mount: .+ failed with/; |
| return 1 if $line =~ /^Using .+ production environment.$/; |
| return 1 if $line =~ /replacing existing signature$/; |
| return 1 if $line =~ /^Unlocking '.*\.keychain-db'$/; |
| return 1 if $line =~ /^\d+ localizable strings$/; |
| return 1 if $line =~ /^\d+ plural rules$/; |
| return 1 if $line =~ /^The list of exported symbols did not change.$/; |
| return 1 if $line =~ /^ditto: Cannot get the real path for source/; |
| return 1 if $line =~ /^Duplicate Entry Was Skipped:/; |
| return 1 if $line =~ /^Adding .*?entitlements/; |
| return 1 if $line =~ /^Making app bundle launchable/; |
| return 1 if $line =~ /^Finished adding entitlements\.$/; |
| return 1 if $line =~ /^.* will not be code signed because its settings don't specify a development team.$/; |
| return 1 if $line =~ /^Initializing Plist...$/; |
| |
| if ($platform eq "win") { |
| return 1 if $line =~ /^\s*(touch|perl|cat|rm -f|del|python|\/usr\/bin\/g\+\+|gperf|echo|sed|if \[ \-f|WebCore\/generate-export-file) /; |
| return 1 if $line =~ /^\s*(if not exist \"|if errorlevel 1)/; |
| return 1 if $line =~ /(^\s*|MSB3073:\s+)(set |REM |cmd \/c)/; |
| return 1 if $line =~ /^\s*[cC]:\\[pP]rogram [fF]iles.*\\.*\\(CL|midl)\.exe /; |
| return 1 if $line =~ /^\s*Processing .*\.(acf|h|idl)\s*$/; |
| return 1 if $line =~ /^\s*printf /; |
| return 1 if $line =~ /^\s*\/usr\/bin\/bash\s*/; |
| return 1 if $line =~ /^\s*offlineasm: Nothing changed/; |
| return 1 if $line =~ / \d+ File\(s\) copied/; |
| return 1 if $line =~ /^\s*File not found - \*\.h/; |
| return 1 if $line =~ /mkdir\s+\"/; |
| return 1 if $line =~ /xcopy \/y \/d \"/; |
| return 1 if $line =~ /\.obj\"\s*$/; |
| return 1 if $line =~ /:\s+(cmd \/c|set)\s+/; |
| return 1 if $line =~ /MSB3073:\s+$/; |
| return 1 if $line =~ /MSB3073:\s+if not exist/; |
| return 1 if $line =~ /which.exe bash/; |
| } else { |
| return 1 if $line =~ /^(touch|perl|cat|rm -f|python|\/usr\/bin\/g\+\+|\/bin\/ln|gperf|echo|sed|if \[ \-f|WebCore\/generate-export-file|write-file|chmod) /; |
| return 1 if $line =~ /^ / && !shouldShowSubsequentLine($previousLine); |
| return 1 if $line =~ /^printf /; |
| return 1 if $line =~ /^offlineasm: Nothing changed/; |
| } |
| return 1 if $line =~ /^Showing first/; |
| |
| return 0; |
| } |
| |
| __PACKAGE__->main() unless caller(); |
| |
| 1; |