Add rudimentary config.test support when configuring modules.
An extra script is added (qtmodule-configtests) which is currently invoked from syncqt (with some derived parameters passed to it). The module can optionally have an entry in the module's sync.profile file in the form of a perl map of "test name" => parameters. Tests can print an advisory message if they fail (e.g. "Install this SDK/dev package"), or abort the syncqt process (e.g. mandatory prereq missing). Also, if the test has a "requires(foo)" line that results in it being skipped, this is also supported. Change-Id: Ic3c820a488a0992c944994d4d7dc283da36742d6 Reviewed-on: http://codereview.qt.nokia.com/928 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Sarah Jane Smith <sarah.j.smith@nokia.com> Reviewed-by: Marius Storm-Olsen <marius.storm-olsen@nokia.com>
This commit is contained in:
parent
7fc3203062
commit
997b2a96c1
337
bin/qtmodule-configtests
Executable file
337
bin/qtmodule-configtests
Executable file
@ -0,0 +1,337 @@
|
||||
#!/usr/bin/perl
|
||||
######################################################################
|
||||
#
|
||||
# Runs any module configuration tests
|
||||
#
|
||||
# Called (currently) from syncqt, and expects a few arguments
|
||||
#
|
||||
# configtests $basedir $out_basedir $qtbasedir $quietmode
|
||||
#
|
||||
# Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
# Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
#
|
||||
######################################################################
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# use packages -------------------------------------------------------
|
||||
use File::Basename;
|
||||
use File::Path 'mkpath';
|
||||
use File::Spec::Functions;
|
||||
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;
|
||||
|
||||
my $qmakeCachePath = catfile($out_basedir, ".qmake.cache");
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# Syntax: executeSomething
|
||||
# Params: A list of things.
|
||||
#
|
||||
# Purpose: Executes the first arg, passing the list.
|
||||
# stderr is redirected to stdout, and the output is captured.
|
||||
# Returns: The output.
|
||||
######################################################################
|
||||
sub executeSomething {
|
||||
my @args = @_;
|
||||
my $program = $args[0];
|
||||
|
||||
my $pid = open(KID_TO_READ, "-|");
|
||||
|
||||
my $output;
|
||||
|
||||
if ($pid) { # parent
|
||||
while (<KID_TO_READ>) {
|
||||
$output = $output . $_;
|
||||
}
|
||||
close(KID_TO_READ) || $! == 0 || warn "\nFailed to execute $program: exited $?";
|
||||
} else {
|
||||
# redirect STDERR to STDOUT
|
||||
open STDERR, ">&STDOUT";
|
||||
|
||||
# Exec something
|
||||
exec ($program, @args) || die "\nCan't exec $program: $!\n";
|
||||
# NOTREACHED
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# 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) = @_;
|
||||
|
||||
my $oldWorkingDir = getcwd();
|
||||
my $ret = 0;
|
||||
|
||||
my @QMAKEARGS = ('CONFIG-=debug_and_release');
|
||||
|
||||
my $testOutDir = catdir($out_basedir, 'config.tests', $testName);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
# Throw it all away
|
||||
executeSomething($QMAKE, @QMAKEARGS);
|
||||
executeSomething($MAKE, 'clean');
|
||||
my $makeOutput = executeSomething(($MAKE));
|
||||
|
||||
# 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 ($^O =~ /win32/i) {
|
||||
# On windows look for $testName.exe
|
||||
if (-e catfile($testOutDir, "$testName.exe")) {
|
||||
$ret = 1;
|
||||
}
|
||||
} else {
|
||||
if (-e catfile($testOutDir, $testName)) {
|
||||
$ret = 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$ret = 2;
|
||||
}
|
||||
|
||||
chdir $oldWorkingDir or die "\nUnable to restore working directory: $!\n";
|
||||
return $ret;
|
||||
}
|
||||
|
||||
# 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;
|
||||
|
||||
# Now run the configuration tests
|
||||
print "Configuration tests:\n";
|
||||
|
||||
while ((my $testName, my $testParameters) = each %configtests) {
|
||||
printf " % *s: ", $maxNameLength, $testName; # right aligned, yes/no lines up
|
||||
|
||||
my $fatalTest = $testParameters->{"fatal"} // 0;
|
||||
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;
|
||||
|
||||
# Strip out any existing config test results
|
||||
$newContents =~ s/^config_test_.*$//gms;
|
||||
$newContents =~ s/^# Compile time test results.*$//gms;
|
||||
|
||||
# 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";
|
||||
}
|
||||
}
|
||||
|
||||
# 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;
|
17
bin/syncqt
17
bin/syncqt
@ -15,6 +15,7 @@ use Cwd;
|
||||
use Cwd 'abs_path';
|
||||
use Config;
|
||||
use strict;
|
||||
use English qw(-no_match_vars );
|
||||
|
||||
# set output basedir to be where ever syncqt is run from
|
||||
our $out_basedir = getcwd();
|
||||
@ -50,6 +51,7 @@ my $module_fwd = "";
|
||||
my $cache_module_fwd = 0;
|
||||
my $developer_build = 0;
|
||||
my $no_module_version_header = 0;
|
||||
my $makefile_generator = "";
|
||||
my @modules_to_sync ;
|
||||
$force_relative = 1 if ( -d "/System/Library/Frameworks" );
|
||||
|
||||
@ -90,6 +92,7 @@ sub showUsage
|
||||
print " easy development\n";
|
||||
print " -no-module-version-header\n";
|
||||
print " Don't create module version header file\n";
|
||||
print " -generator <PATH> Specify the makefile generator setting (e.g. 'UNIX')\n";
|
||||
print " -help This help\n";
|
||||
exit 0;
|
||||
}
|
||||
@ -658,6 +661,9 @@ while ( @ARGV ) {
|
||||
# skip, it's been dealt with at the top of the file
|
||||
shift @ARGV;
|
||||
next;
|
||||
} elsif($arg eq "-generator") {
|
||||
$var = "makefile_generator";
|
||||
$val = shift @ARGV;
|
||||
} elsif($arg =~/^-/) {
|
||||
print "Unknown option: $arg\n\n" if(!$var);
|
||||
showUsage();
|
||||
@ -741,6 +747,8 @@ while ( @ARGV ) {
|
||||
$cache_module_fwd = 1;
|
||||
} elsif ($var eq "developer_build") {
|
||||
$developer_build = 1;
|
||||
} elsif ($var eq "makefile_generator") {
|
||||
$makefile_generator = $val;
|
||||
} elsif ($var eq "no_module_version_header") {
|
||||
$no_module_version_header = 1;
|
||||
} elsif ($var eq "output") {
|
||||
@ -1310,4 +1318,13 @@ if($check_includes) {
|
||||
}
|
||||
}
|
||||
|
||||
# Do configure tests now (pass some things along)
|
||||
# fatal tests have a non zero return
|
||||
unless ($showonly) {
|
||||
my $configtests = dirname($0)."/qtmodule-configtests";
|
||||
if (system($EXECUTABLE_NAME, $configtests, $basedir, $out_basedir, $qtbasedir, $makefile_generator)) {
|
||||
die "$configtests exited with status $?";
|
||||
}
|
||||
}
|
||||
|
||||
exit 0;
|
||||
|
@ -31,7 +31,7 @@ CONFIG = lex yacc warn_on debug uic resources $$CONFIG
|
||||
|
||||
qtPrepareTool(QMAKE_SYNCQT, syncqt)
|
||||
|
||||
MSG = $$quote($$QMAKE_SYNCQT $$QTFWD -outdir $$OUT_PWD $$_PRO_FILE_PWD_)
|
||||
MSG = $$quote($$QMAKE_SYNCQT $$QTFWD -generator $$MAKEFILE_GENERATOR -outdir $$OUT_PWD $$_PRO_FILE_PWD_)
|
||||
!silent:message($$MSG)
|
||||
system($$MSG) {
|
||||
# success! Nothing to do
|
||||
|
Loading…
Reference in New Issue
Block a user