зеркало из https://github.com/dotnet/msbuild.git
295 строки
10 KiB
Perl
Executable File
295 строки
10 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
|
|
use strict;
|
|
use Getopt::Long;
|
|
use Data::Dumper;
|
|
use File::Path qw(make_path);
|
|
use File::Spec::Functions qw(rel2abs catfile canonpath);
|
|
use File::Basename;
|
|
use FindBin qw($RealBin);
|
|
use Cwd qw(abs_path);
|
|
use POSIX;
|
|
|
|
#set the timestamp in case we need to create a directory
|
|
use constant DATETIME=>strftime("%Y-%m-%d_%H-%M-%S", localtime);
|
|
|
|
# The source solution
|
|
my $solutionToBuild = catfile($RealBin, 'build.proj');
|
|
|
|
my $usage = <<"USAGE";
|
|
Usage build.pl [-root=<outputRoot>] [-fullbuild] [-verify] [-tests] [-all] [-quiet] [-silent]
|
|
|
|
The script can build MSBuild.exe using mono, verify by
|
|
rebuilding with the generated MSBuild.exe and run tests.
|
|
If the output root is not specified, the bin and packages
|
|
subdirectories are created in the source tree (as well as
|
|
bin-verify and packages-verify if verification build runs).
|
|
If output root is provided, a dated subdirectory is created
|
|
under the output root and the same subdirectories appear
|
|
there. The tests are run either on the xbuild generated
|
|
binaries or, if verification is done, on the verification
|
|
binaries.
|
|
|
|
Options:
|
|
root - specifies the output root. The root is the
|
|
source tree by default.
|
|
fullBuild - do a rebuild instead of the incremental build.
|
|
This only makes a difference without the root
|
|
options, as the rooted build are always clean.
|
|
verify - rebuild the source with the binaries generated
|
|
by the xbuild.
|
|
tests - run the tests.
|
|
package - create NuGet package
|
|
testName - name of the nunit test to run (used with -tests).
|
|
Use full name, including the namespace.
|
|
fixture - name of the nunit test fixture to be run (used
|
|
with -tests)
|
|
all - shorthand for -verify and -tests flags.
|
|
quiet - don't show any build output, only summaries
|
|
silent - show nothing, not even summaries
|
|
|
|
USAGE
|
|
|
|
|
|
# Quick check if we are in the right place and that mono is installed
|
|
# (just checking for solution and xbuild presence)
|
|
my $xbuild = ($^O eq 'MSWin32') ? `where xbuild` : `which xbuild`;
|
|
chomp $xbuild;
|
|
die ("Solution $solutionToBuild does not exist\n") unless -e $solutionToBuild;
|
|
die ("xbuild was not found") unless $^O eq "MSWin32" || -e $xbuild;
|
|
|
|
my $buildRoot;
|
|
my $runTests;
|
|
my $testName;
|
|
my $fixture;
|
|
my $verification;
|
|
my $quiet;
|
|
my $silent;
|
|
my $createPackage;
|
|
my $fullBuild;
|
|
my $allSteps;
|
|
my $help;
|
|
|
|
die $usage unless GetOptions(
|
|
'root=s' => \$buildRoot,
|
|
'verify' => \$verification,
|
|
'tests' => \$runTests,
|
|
'package' => \$createPackage,
|
|
'testName=s' => \$testName,
|
|
'fixture=s' => \$fixture,
|
|
'quiet' => \$quiet,
|
|
'silent' => \$silent,
|
|
'fullBuild' => \$fullBuild,
|
|
'all' => \$allSteps,
|
|
'help' => \$help
|
|
);
|
|
|
|
if ($help) {
|
|
print $usage;
|
|
exit (0);
|
|
}
|
|
|
|
# The all steps flag override some other
|
|
if ($allSteps) {
|
|
$verification = 1;
|
|
$runTests = 1;
|
|
}
|
|
|
|
# We need nunit-console to run tests
|
|
my $nunitConsole;
|
|
if ($runTests) {
|
|
# Find the nunit console program
|
|
my $n = ($^O eq 'MSWin32') ? `where nunit-console` : `which nunit-console`;
|
|
chomp $n;
|
|
|
|
die ("Tests are requested, but nunit-console was not found") unless -e $n;
|
|
|
|
# Resolve any links
|
|
$nunitConsole = abs_path($n);
|
|
# Use version 4 if found
|
|
$n = $nunitConsole . '4';
|
|
$nunitConsole = $n if -e $n;
|
|
}
|
|
|
|
my $slash;
|
|
my $installedBuild;
|
|
|
|
if ($^O eq "MSWin32") {
|
|
# Find the nunit console program
|
|
my @n = `where msbuild`;
|
|
print "For MSBuild: got @n[0]\n";
|
|
chomp @n;
|
|
print "Chomped MSBuild: got @n[0]\n";
|
|
|
|
die ("Installed MSBuild was not found") unless -e @n[0];
|
|
|
|
# Resolve any links
|
|
$installedBuild = abs_path(@n[0]);
|
|
$slash = '\\';
|
|
}
|
|
else {
|
|
$installedBuild = $xbuild;
|
|
$slash = '/';
|
|
}
|
|
|
|
# Find the location where we're going to store results
|
|
# If no root is specifed, use the script location (we'll create
|
|
# bin, packages, bin-verify, packages-verify directories and the
|
|
# logs there.
|
|
# If root is specified, make a dated subdirectory there. That
|
|
# will be our root.
|
|
if (!$buildRoot) {
|
|
$buildRoot = canonpath($RealBin);
|
|
}
|
|
else {
|
|
$buildRoot = catfile(rel2abs($buildRoot), DATETIME);
|
|
# Just make sure it's not a file
|
|
die ("Verification root '$buildRoot' exists and is a file") if -f $buildRoot;
|
|
make_path($buildRoot);
|
|
}
|
|
|
|
# The regex to parse build logs
|
|
(my $tmpRoot = $buildRoot) =~ s!\\!\\\\!g;
|
|
print "buildRoot: $buildRoot, tmpRoot: $tmpRoot\n";
|
|
my $extractRegex = qr!^\s*Build binary target directory: '($tmpRoot.+?)[\\/]?'!;
|
|
|
|
my $msbuildPath;
|
|
my $exitCode;
|
|
my $errorCount;
|
|
|
|
# Run the first build
|
|
($exitCode, $errorCount, $msbuildPath) = runbuild("\"$installedBuild\"", '', '/', ($^O ne "MSWin32"));
|
|
|
|
die ("Build with xbuild failed (code $exitCode)") unless $exitCode == 0;
|
|
die ("Build with xbuild failed (error count $errorCount") unless $errorCount == 0;
|
|
die ("Build succeeded, but MSBuild.exe binary was not found at $msbuildPath") unless -e $msbuildPath;
|
|
|
|
# Use the MSBuild.exe we created and rebuild (if requested)
|
|
if ($verification) {
|
|
my $MSBuildProgram = catfile($msbuildPath, 'MSBuild.exe');
|
|
$MSBuildProgram = 'mono ' . "\"$MSBuildProgram\"" unless $^O eq "MSWin32";
|
|
my $newMSBuildPath;
|
|
($exitCode, $errorCount, $newMSBuildPath) = runbuild($MSBuildProgram, '-verify', '-', 0);
|
|
die ("Build with msbuild failed (code $exitCode)") unless $exitCode == 0;
|
|
die ("Build with msbuild failed (error count $errorCount") unless $errorCount == 0;
|
|
print "New MSBuild path: $newMSBuildPath\n";
|
|
$msbuildPath = $newMSBuildPath if -e $newMSBuildPath;
|
|
}
|
|
|
|
if ($runTests) {
|
|
# Get the dlls for testing
|
|
my @file = glob catfile($msbuildPath, '*UnitTest*.dll');
|
|
runtests (@file);
|
|
}
|
|
|
|
# This functions runs a build.
|
|
# runbuild(program, suffix, switch)
|
|
# program -- the build (xbuild or msbuild)
|
|
# suffix -- appended to log and output directory names
|
|
# switch -- either - or /
|
|
sub runbuild {
|
|
die ('runbuild sub was not called correctly') unless @_ == 4;
|
|
my ($program, $suffix, $switch, $overrideToolset) = @_;
|
|
|
|
# Get paths of output directories and the log
|
|
(my $binDir = catfile($buildRoot, "bin$suffix")) =~ s:(?<![\/])$:$slash:;
|
|
#$binDir =~ s:(?<![\/])$:\\:;
|
|
print "BinDir = $binDir\n";
|
|
(my $packagesDir = catfile ($buildRoot, "packages$suffix")) =~ s:(?<![\/])$:$slash:;
|
|
my $logFile = catfile($buildRoot, "MSBuild${suffix}.log");
|
|
|
|
# If we need to rebuild, add a switch for the task
|
|
my $rebuildSwitch = $fullBuild ? "${switch}t:Rebuild " : "";
|
|
|
|
# If we need to create NuGet package, add a witch for the property
|
|
my $packageProperty = $createPackage ? "${switch}p:BuildNugetPackage=true " : "";
|
|
|
|
# Except on Windows, we need to specifiy 4.0 toolse
|
|
my $toolSet = $overrideToolset ? "${switch}tv:4.0 " : "";
|
|
my $configSwitch = $^O eq "MSWin32" ? "${switch}p:Configuration=Debug " : "${switch}p:Configuration=Debug-MONO ";
|
|
|
|
# Generate and print the command we run
|
|
my $command = "$program ${switch}nologo ${switch}v:q " .
|
|
"$rebuildSwitch $configSwitch $toolSet $packageProperty" .
|
|
"${switch}p:BinDir=$binDir ${switch}p:PackagesDir=$packagesDir " .
|
|
"${switch}fl \"${switch}flp:LogFile=$logFile;V=diag\" ${switch}p:BuildSamples=false " .
|
|
" $solutionToBuild";
|
|
print $command . "\n" unless $silent;
|
|
|
|
# Run build, parsing it's output to count errors and warnings
|
|
# Harakiri if can't run
|
|
open(BUILD_OUTPUT, "$command 2>&1 |") or die "Cannot run $program, error $!";
|
|
my $warningCount = 0;
|
|
my $errorCount = 0;
|
|
for (<BUILD_OUTPUT>) {
|
|
print $_ unless ($quiet || $silent);
|
|
m/:\s+error / && ($errorCount++, next);
|
|
m/:\s+warning / && ($warningCount++, next);
|
|
}
|
|
|
|
die "Failed to run $program, exit code $!" if $! != 0;
|
|
close BUILD_OUTPUT;
|
|
my $exitCode = $? >> 8;
|
|
|
|
# Search the log for the full output path
|
|
my $msbuildPath;
|
|
if (open LOG, '<', $logFile) {
|
|
m/$extractRegex/ && ($msbuildPath = $1, last) for <LOG>;
|
|
close (LOG);
|
|
}
|
|
else {
|
|
# It's not an error if the path cannot be found. At worst, we cannot verify
|
|
print "Warning: Cannot open log file $logFile: $!" if $! && !$silent;
|
|
}
|
|
|
|
print "Errors: $errorCount, Warnings $warningCount\n" unless $silent;
|
|
return ($exitCode, $errorCount, $msbuildPath);
|
|
}
|
|
|
|
# This function runs the test. It gets the list of dlls to test.
|
|
sub runtests {
|
|
my @files = @_;
|
|
|
|
# Create directory for output
|
|
my $testResultsDir = catfile($buildRoot, 'TestResults');
|
|
make_path($testResultsDir);
|
|
|
|
# Output file names in that directory
|
|
my $xmlResultFile = catfile($testResultsDir, 'Results.xml');
|
|
my $outputFile = catfile($testResultsDir, 'TestOutput.txt');
|
|
|
|
# Build the command to run the test
|
|
my $command = '';
|
|
my $excludeCategories = '';
|
|
if ($^O ne 'MSWin32') {
|
|
$excludeCategories = "WindowsOnly";
|
|
}
|
|
if ($testName) {
|
|
$command = "\"$nunitConsole\" -run:$testName -xml:$xmlResultFile " . join (' ', @files);
|
|
} elsif ($fixture) {
|
|
$command = "\"$nunitConsole\" -fixture:$fixture -exclude:$excludeCategories -xml:$xmlResultFile " . join (' ', @files);
|
|
} else {
|
|
$command = "\"$nunitConsole\" -exclude:$excludeCategories -xml:$xmlResultFile " . join (' ', @files);
|
|
}
|
|
print $command . "\n" unless $silent;
|
|
|
|
# Run it silently
|
|
system("$command 2>&1 >$outputFile");
|
|
|
|
# Count the passed/failed tests by readin the output XML file
|
|
my $testsFailed = 0;
|
|
my $testsSucceeded = 0;
|
|
|
|
my $testRegex = qr!^\s*<test-case.+executed="True".+success="((?:True)|(?:False))"!;
|
|
|
|
if (open LOG, '<', $xmlResultFile) {
|
|
m/$testRegex/ && ($1 eq 'True' ? $testsSucceeded++ : $testsFailed++) for <LOG>;
|
|
close (LOG);
|
|
my $testsRan = $testsSucceeded + $testsFailed;
|
|
print "Tests ran: $testsRan, tests succeeded: $testsSucceeded, tests failed: $testsFailed\n" unless $silent;
|
|
}
|
|
else {
|
|
print "Warning: Cannot open log file $xmlResultFile: $!\n" if $! && !$silent;
|
|
}
|
|
}
|