2011-06-30 04:51:16 +00:00
|
|
|
#!/usr/bin/perl
|
2011-09-09 08:37:30 +00:00
|
|
|
#############################################################################
|
|
|
|
##
|
2012-01-05 04:03:39 +00:00
|
|
|
## Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
2011-09-09 08:37:30 +00:00
|
|
|
## All rights reserved.
|
2012-01-20 03:06:31 +00:00
|
|
|
## Contact: http://www.qt-project.org/
|
2011-09-09 08:37:30 +00:00
|
|
|
##
|
|
|
|
## This file is part of the build configuration tools of the Qt Toolkit.
|
|
|
|
##
|
|
|
|
## $QT_BEGIN_LICENSE:LGPL$
|
|
|
|
## GNU Lesser General Public License Usage
|
|
|
|
## This file may be used under the terms of the GNU Lesser General Public
|
|
|
|
## License version 2.1 as published by the Free Software Foundation and
|
|
|
|
## appearing in the file LICENSE.LGPL included in the packaging of this
|
|
|
|
## file. Please review the following information to ensure the GNU Lesser
|
|
|
|
## General Public License version 2.1 requirements will be met:
|
|
|
|
## http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
##
|
|
|
|
## In addition, as a special exception, Nokia gives you certain additional
|
|
|
|
## rights. These rights are described in the Nokia Qt LGPL Exception
|
|
|
|
## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
##
|
|
|
|
## GNU General Public License Usage
|
|
|
|
## Alternatively, this file may be used under the terms of the GNU General
|
|
|
|
## Public License version 3.0 as published by the Free Software Foundation
|
|
|
|
## and appearing in the file LICENSE.GPL included in the packaging of this
|
|
|
|
## file. Please review the following information to ensure the GNU General
|
|
|
|
## Public License version 3.0 requirements will be met:
|
|
|
|
## http://www.gnu.org/copyleft/gpl.html.
|
|
|
|
##
|
|
|
|
## Other Usage
|
|
|
|
## Alternatively, this file may be used in accordance with the terms and
|
|
|
|
## conditions contained in a signed written agreement between you and Nokia.
|
|
|
|
##
|
|
|
|
##
|
|
|
|
##
|
|
|
|
##
|
|
|
|
##
|
|
|
|
## $QT_END_LICENSE$
|
|
|
|
##
|
|
|
|
#############################################################################
|
|
|
|
|
2011-06-30 04:51:16 +00:00
|
|
|
#
|
|
|
|
# Runs any module configuration tests
|
|
|
|
#
|
|
|
|
# Called (currently) from syncqt, and expects a few arguments
|
|
|
|
#
|
|
|
|
# configtests $basedir $out_basedir $qtbasedir $quietmode
|
|
|
|
#
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
|
|
|
|
# use packages -------------------------------------------------------
|
|
|
|
use File::Basename;
|
|
|
|
use File::Path 'mkpath';
|
2012-01-18 10:02:48 +00:00
|
|
|
use File::Spec::Functions qw/ :ALL /;
|
|
|
|
use File::Temp qw/ :POSIX /;
|
2011-06-30 04:51:16 +00:00
|
|
|
use Cwd;
|
|
|
|
use Cwd 'abs_path';
|
|
|
|
use Config;
|
|
|
|
|
|
|
|
# Which file to look for the %configtests variable in
|
|
|
|
my $configTestSource = "sync.profile";
|
|
|
|
|
|
|
|
if ($#ARGV < 3) {
|
|
|
|
warn "Usage:\n";
|
|
|
|
warn " $0 <module base directory> <module output directory> <QtBase directory> <generator spec>\n";
|
|
|
|
exit 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
# These might be needed in sync.profile
|
|
|
|
our $basedir = $ARGV[0];
|
|
|
|
our $out_basedir = $ARGV[1];
|
|
|
|
our $qtbasedir = $ARGV[2];
|
|
|
|
my $generator = $ARGV[3];
|
|
|
|
|
|
|
|
our %configtests;
|
|
|
|
|
2012-01-18 10:02:48 +00:00
|
|
|
my $absOutDir = abs_path($out_basedir);
|
|
|
|
my $qmakeCachePath = catfile($absOutDir, '.qmake.cache');
|
|
|
|
my $configLogPath = catfile($absOutDir, 'config.log');
|
2011-06-30 04:51:16 +00:00
|
|
|
|
|
|
|
my $QMAKE = catfile($qtbasedir, "bin", ($^O =~ /win32/i) ? 'qmake.exe' : 'qmake');
|
|
|
|
if (!-x $QMAKE) {
|
|
|
|
# try the qmake from the path (e.g. this is a shadow build)
|
|
|
|
$QMAKE = 'qmake';
|
|
|
|
}
|
|
|
|
|
|
|
|
# Need to use the right make
|
|
|
|
# SYMBIAN_UNIX/MINGW should fall back to the non SYMBIAN ones
|
|
|
|
my $MAKE = 'make'; # default, only works on unix
|
|
|
|
if ($generator =~ /UNIX|XCODE/i) { # XCODE = make?
|
|
|
|
$MAKE = 'make';
|
|
|
|
} elsif ($generator =~ /MINGW/i) {
|
|
|
|
$MAKE = 'mingw32-make';
|
|
|
|
} elsif ($generator =~ /MSVC.NET|MSBUILD/i) {
|
|
|
|
$MAKE = 'nmake';
|
|
|
|
} else {
|
|
|
|
# Unhandled (at least): BMAKE, GBUILD, SYMBIAN_ABLD, SYMBIAN_SBSV2
|
|
|
|
warn "Unrecognized generator spec ($generator) - assuming '$MAKE'\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
######################################################################
|
|
|
|
# Syntax: fileContents(filename)
|
|
|
|
# Params: filename, string, filename of file to return contents
|
|
|
|
#
|
|
|
|
# Purpose: Get the contents of a file.
|
|
|
|
# Returns: String with contents of the file, or empty string if file
|
|
|
|
# doens't exist.
|
|
|
|
# Warning: Dies if it does exist but script cannot get read access.
|
|
|
|
######################################################################
|
|
|
|
sub fileContents {
|
|
|
|
my ($filename) = @_;
|
|
|
|
my $filecontents = "";
|
|
|
|
if (-e $filename) {
|
|
|
|
open(I, "< $filename") || die "Could not open $filename for reading, read block?";
|
|
|
|
local $/;
|
|
|
|
binmode I;
|
|
|
|
$filecontents = <I>;
|
|
|
|
close I;
|
|
|
|
}
|
|
|
|
return $filecontents;
|
|
|
|
}
|
|
|
|
|
|
|
|
######################################################################
|
|
|
|
# Syntax: loadConfigTests()
|
|
|
|
#
|
|
|
|
# Purpose: Loads the config tests from the source basedir into %configtests.
|
|
|
|
# Returns: Nothing
|
|
|
|
######################################################################
|
|
|
|
sub loadConfigTests {
|
|
|
|
my $configprofile = catfile($basedir, $configTestSource);
|
|
|
|
my $result;
|
|
|
|
unless ($result = do $configprofile) {
|
|
|
|
die "configtests couldn't parse $configprofile: $@\n" if $@;
|
|
|
|
# We don't check for non null output, since that is valid
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
######################################################################
|
|
|
|
# Syntax: hashesAreDifferent
|
|
|
|
#
|
|
|
|
# Purpose: Compares two hashes. (must have same key=value for everything)
|
|
|
|
# Returns: 0 if they are the same, 1 otherwise
|
|
|
|
######################################################################
|
|
|
|
sub hashesAreDifferent {
|
|
|
|
my %a = %{$_[0]};
|
|
|
|
my %b = %{$_[1]};
|
|
|
|
|
|
|
|
if (keys %a != keys %b) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
my %cmp = map { $_ => 1 } keys %a;
|
|
|
|
for my $key (keys %b) {
|
|
|
|
last unless exists $cmp{$key};
|
|
|
|
last unless $a{$key} eq $b{$key};
|
|
|
|
delete $cmp{$key};
|
|
|
|
}
|
|
|
|
if (%cmp) {
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-18 10:02:48 +00:00
|
|
|
|
2011-06-30 04:51:16 +00:00
|
|
|
######################################################################
|
2012-01-18 10:02:48 +00:00
|
|
|
# Syntax: executeLoggedCommand()
|
|
|
|
# Params: path to executable, arguments
|
|
|
|
#
|
|
|
|
# This function is equivalent to system(), except that the command
|
|
|
|
# details and output is placed in the configure log (only).
|
2011-06-30 04:51:16 +00:00
|
|
|
#
|
2012-01-18 10:02:48 +00:00
|
|
|
# Purpose: run a command and log the output
|
|
|
|
# Returns: exit code and output.
|
2011-06-30 04:51:16 +00:00
|
|
|
######################################################################
|
2012-01-18 10:02:48 +00:00
|
|
|
sub executeLoggedCommand {
|
|
|
|
my (@command_with_args) = @_;
|
|
|
|
|
|
|
|
# Redirect all stdout, stderr into the config.log
|
|
|
|
my ($save_stdout, $save_stderr);
|
|
|
|
open($save_stdout, '>&', STDOUT) || die "save STDOUT: $!";
|
|
|
|
open($save_stderr, '>&', STDERR) || die "save STDERR: $!";
|
|
|
|
|
|
|
|
my $tmpName = File::Temp::tempnam(File::Spec->tmpdir(), 'log');
|
|
|
|
open(STDOUT, '>', $tmpName) || die "open $tmpName: $!";
|
|
|
|
open(STDERR, '>&', STDOUT) || die "redirect STDERR to STDOUT: $!";
|
|
|
|
|
|
|
|
print "+ @command_with_args\n";
|
|
|
|
my $exitCode = system(@command_with_args) >> 8;
|
|
|
|
|
|
|
|
# Put them back.
|
|
|
|
close(STDOUT);
|
|
|
|
close(STDERR);
|
|
|
|
open(STDOUT, '>&', $save_stdout) || die "restoring STDOUT: $!";
|
|
|
|
open(STDERR, '>&', $save_stderr) || die "restoring STDERR: $!";
|
|
|
|
|
|
|
|
# Append output to config log and return it.
|
|
|
|
my ($tmpFile, $configLog);
|
|
|
|
my $out = '';
|
|
|
|
open($tmpFile, '<', $tmpName) || die "open $tmpName: $!";
|
|
|
|
open($configLog, '>>', $configLogPath) || die "open $configLogPath: $!";
|
|
|
|
while (my $line = <$tmpFile>) {
|
|
|
|
print $configLog $line;
|
|
|
|
$out .= $line;
|
2012-01-13 01:44:25 +00:00
|
|
|
}
|
2012-01-18 10:02:48 +00:00
|
|
|
close($tmpFile);
|
|
|
|
close($configLog);
|
|
|
|
unlink($tmpName);
|
|
|
|
return ($exitCode, $out);
|
2011-06-30 04:51:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
######################################################################
|
|
|
|
# Syntax: executeTest()
|
|
|
|
# Params: testName
|
|
|
|
#
|
|
|
|
# The testName variable controls the actual config test run - the
|
|
|
|
# source is assumed to be in $basedir/config.tests/$testName, and
|
|
|
|
# when 'qmake; make clean; make' is run, is expected to produce a file
|
|
|
|
# $out_basedir/config.tests/$testName/$testName. If this test passes,
|
|
|
|
# then 'config_test_$testName = yes' will be written to $out_basedir/.qmake.cache
|
|
|
|
#
|
|
|
|
# Purpose: Runs a configuration time test.
|
|
|
|
# Returns: 0 if the test fails, 1 if it passes, 2 if the test is skipped
|
|
|
|
# (e.g. .pro file has requires(x) and x is not satisfied)
|
|
|
|
######################################################################
|
|
|
|
sub executeTest {
|
|
|
|
my ($testName) = @_;
|
|
|
|
|
2012-01-18 10:02:48 +00:00
|
|
|
{
|
|
|
|
my $fh;
|
|
|
|
open($fh, '>>', $configLogPath) || die "open $configLogPath: $!";
|
|
|
|
print $fh 'executing config test "',$testName, "\":\n";
|
|
|
|
close($fh);
|
|
|
|
}
|
|
|
|
|
2011-06-30 04:51:16 +00:00
|
|
|
my $oldWorkingDir = getcwd();
|
2012-01-13 01:44:25 +00:00
|
|
|
my $ret = 0;
|
2011-06-30 04:51:16 +00:00
|
|
|
|
2011-11-14 12:16:12 +00:00
|
|
|
my @QMAKEARGS = ('CONFIG-=debug_and_release', 'CONFIG-=app_bundle');
|
2011-06-30 04:51:16 +00:00
|
|
|
|
2012-01-18 10:02:48 +00:00
|
|
|
my $testOutDir = catdir($absOutDir, 'config.tests', $testName);
|
2011-06-30 04:51:16 +00:00
|
|
|
|
2011-07-25 00:10:54 +00:00
|
|
|
# Since we might be cross compiling, look for barename (Linux) and .exe (Win32/Symbian)
|
|
|
|
my $testOutFile1 = catfile($testOutDir, "$testName.exe");
|
|
|
|
my $testOutFile2 = catfile($testOutDir, $testName);
|
|
|
|
|
2011-06-30 04:51:16 +00:00
|
|
|
if (abs_path($basedir) eq abs_path($out_basedir)) {
|
|
|
|
chdir $testOutDir or die "\nUnable to change to config test directory ($testOutDir): $!\n";
|
|
|
|
} else { # shadow build
|
|
|
|
if (! -e $testOutDir) {
|
|
|
|
mkpath $testOutDir or die "\nUnable to create shadow build config test directory ($testOutDir): $!\n";
|
|
|
|
}
|
|
|
|
chdir $testOutDir or die "\nUnable to change to config test directory ($testOutDir): $!\n";
|
|
|
|
|
|
|
|
push (@QMAKEARGS, catdir($basedir, 'config.tests', $testName));
|
|
|
|
}
|
|
|
|
|
2011-07-25 00:10:54 +00:00
|
|
|
# First remove existing stuff (XXX this probably needs generator specific code, but hopefully
|
|
|
|
# the target removal below will suffice)
|
|
|
|
if (-e "Makefile") {
|
2012-01-18 10:02:48 +00:00
|
|
|
executeLoggedCommand($MAKE, 'distclean');
|
2011-07-25 00:10:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# and any targets that we might find that weren't distcleaned
|
|
|
|
unlink $testOutFile1, $testOutFile2;
|
|
|
|
|
|
|
|
# Run qmake && make
|
2012-01-18 10:02:48 +00:00
|
|
|
executeLoggedCommand($QMAKE, @QMAKEARGS);
|
|
|
|
my ($makeExitCode, $makeOutput) = executeLoggedCommand($MAKE);
|
2012-01-13 01:44:25 +00:00
|
|
|
|
|
|
|
# If make prints "blah blah blah\nSkipped." we consider this a skipped test
|
|
|
|
if ($makeOutput !~ qr(^Skipped\.$)ms) {
|
|
|
|
# Check the test exists (can't reliably execute, especially for cross compilation)
|
|
|
|
if (-e $testOutFile1 or -e $testOutFile2) {
|
|
|
|
$ret = 1;
|
|
|
|
}
|
2011-06-30 04:51:16 +00:00
|
|
|
} else {
|
|
|
|
$ret = 2;
|
|
|
|
}
|
|
|
|
|
2012-01-18 10:02:48 +00:00
|
|
|
my $fh;
|
|
|
|
open($fh, '>>', $configLogPath) || die "open $configLogPath: $!";
|
|
|
|
print $fh 'config test "',$testName, '" completed with result ',$ret, "\n";
|
|
|
|
close($fh);
|
|
|
|
|
2011-06-30 04:51:16 +00:00
|
|
|
chdir $oldWorkingDir or die "\nUnable to restore working directory: $!\n";
|
|
|
|
return $ret;
|
|
|
|
}
|
|
|
|
|
2012-01-18 10:02:48 +00:00
|
|
|
# Remove existing config.log
|
|
|
|
if (-e $configLogPath) {
|
|
|
|
unlink($configLogPath) || die "unlink $configLogPath: $!";
|
|
|
|
}
|
|
|
|
|
2011-06-30 04:51:16 +00:00
|
|
|
# Now run configuration tests
|
|
|
|
# %configtests is a map from config test name to a map of parameters
|
|
|
|
# e.g:
|
|
|
|
#
|
|
|
|
# %configtests = (
|
|
|
|
# "simple" => {fatal => 1, message => "Missing required 'simple' component\n"},
|
|
|
|
# "failed" => {message => "You need to install the FAILED sdk for this to work\n"}
|
|
|
|
# );
|
|
|
|
#
|
|
|
|
# Parameters and their defaults:
|
|
|
|
# - fatal [false] - whether failing this test should abort everything
|
|
|
|
# - message [""] - A special message to display if this test fails
|
|
|
|
#
|
|
|
|
loadConfigTests();
|
|
|
|
|
|
|
|
# Only do this step for modules that have config tests
|
|
|
|
# (qtbase doesn't). We try to preserve existing contents (and furthermore
|
|
|
|
# only write to .qmake.cache if the tests change)
|
|
|
|
if (abs_path($out_basedir) ne abs_path($qtbasedir)) {
|
|
|
|
# Read any existing content
|
|
|
|
my $existingContents = fileContents($qmakeCachePath);
|
|
|
|
my %oldTestResults;
|
|
|
|
my %newTestResults;
|
|
|
|
my @fatalTestsEncountered;
|
|
|
|
|
|
|
|
# Parse the existing results so we can check if we change them
|
|
|
|
while ($existingContents =~ /^config_test_(.*) = (yes|no)$/gm) {
|
|
|
|
$oldTestResults{$1} = $2;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Get the longest length test name so we can pretty print
|
|
|
|
use List::Util qw(max);
|
|
|
|
my $maxNameLength = max map { length $_ } keys %configtests;
|
|
|
|
|
|
|
|
# Turn off buffering
|
|
|
|
$| = 1;
|
|
|
|
|
2012-01-18 10:02:48 +00:00
|
|
|
# Remove existing config.log
|
|
|
|
if (-e $configLogPath) {
|
|
|
|
unlink($configLogPath) || die "unlink $configLogPath: $!";
|
|
|
|
}
|
|
|
|
|
2011-06-30 04:51:16 +00:00
|
|
|
# Now run the configuration tests
|
2011-07-27 00:38:16 +00:00
|
|
|
print "Configuration tests:\n" if (%configtests);
|
2011-06-30 04:51:16 +00:00
|
|
|
|
|
|
|
while ((my $testName, my $testParameters) = each %configtests) {
|
|
|
|
printf " % *s: ", $maxNameLength, $testName; # right aligned, yes/no lines up
|
|
|
|
|
2011-08-24 02:28:50 +00:00
|
|
|
my $fatalTest = $testParameters->{"fatal"};
|
2011-06-30 04:51:16 +00:00
|
|
|
my $message = $testParameters->{"message"};
|
|
|
|
|
|
|
|
my $testResult = executeTest($testName);
|
|
|
|
my @testResultStrings = ("no\n","yes\n","skipped\n");
|
|
|
|
|
|
|
|
$newTestResults{$testName} = (($testResult == 1) ? "yes" : "no"); # skipped = no
|
|
|
|
|
|
|
|
if ($testResult == 0) {
|
|
|
|
# Failed test
|
|
|
|
if ($fatalTest) {
|
|
|
|
print "no (fatal)\n";
|
|
|
|
# Report the fatality at the end, too
|
|
|
|
push (@fatalTestsEncountered, $testName);
|
|
|
|
} else {
|
|
|
|
print "no\n";
|
|
|
|
}
|
|
|
|
if (defined($message)) {
|
|
|
|
print $message;
|
|
|
|
print "\n" unless chop $message eq "\n";
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
# yes or skipped
|
|
|
|
print $testResultStrings[$testResult];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Check if the test results are different
|
|
|
|
if (hashesAreDifferent(\%oldTestResults, \%newTestResults)) {
|
|
|
|
# Generate the new contents
|
|
|
|
my $newContents = $existingContents;
|
|
|
|
|
2011-09-07 02:00:04 +00:00
|
|
|
# Strip out any existing config test results
|
2011-08-30 00:50:55 +00:00
|
|
|
$newContents =~ s/^config_test_[^\$]*$//gm;
|
|
|
|
$newContents =~ s/^# Compile time test results[^\$]*$//gm;
|
2011-06-30 04:51:16 +00:00
|
|
|
|
|
|
|
# Add any remaining content and make sure we start on a new line
|
|
|
|
if ($newContents and chop $newContents ne '\n') {
|
|
|
|
$newContents = $newContents . "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
# Results and header
|
|
|
|
if (%newTestResults) {
|
|
|
|
$newContents = $newContents . '# Compile time test results ('.(localtime).")\n";
|
|
|
|
|
|
|
|
# Results
|
|
|
|
while ((my $testName, my $testResult) = each %newTestResults) {
|
|
|
|
$newContents = $newContents . "config_test_$testName = $testResult\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-30 00:50:55 +00:00
|
|
|
# Remove blank lines
|
|
|
|
$newContents =~ s/^[\s]*$//gms;
|
|
|
|
|
2011-06-30 04:51:16 +00:00
|
|
|
# and open the file
|
|
|
|
open my $cacheFileHandle, ">$qmakeCachePath" or die "Unable to open $qmakeCachePath for writing: $!\n";
|
|
|
|
|
|
|
|
print $cacheFileHandle $newContents;
|
|
|
|
|
|
|
|
close $cacheFileHandle or die "Unable to close $qmakeCachePath: $!\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
# Now see if we have to die
|
|
|
|
if (@fatalTestsEncountered) {
|
|
|
|
if ($#fatalTestsEncountered == 0) {
|
|
|
|
warn "Mandatory configuration test (".$fatalTestsEncountered[0].") failed.\n\n";
|
|
|
|
} else {
|
|
|
|
warn "Mandatory configuration tests (". join (", ", @fatalTestsEncountered) . ") failed.\n\n";
|
|
|
|
}
|
|
|
|
exit -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
exit 0;
|