2 ######################################################################
4 # Runs any module configuration tests
6 # Called (currently) from syncqt, and expects a few arguments
8 # configtests $basedir $out_basedir $qtbasedir $quietmode
10 # Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
11 # Contact: Nokia Corporation (qt-info@nokia.com)
13 ######################################################################
18 # use packages -------------------------------------------------------
20 use File::Path 'mkpath';
21 use File::Spec::Functions;
26 # Which file to look for the %configtests variable in
27 my $configTestSource = "sync.profile";
31 warn " $0 <module base directory> <module output directory> <QtBase directory> <generator spec>\n";
35 # These might be needed in sync.profile
36 our $basedir = $ARGV[0];
37 our $out_basedir = $ARGV[1];
38 our $qtbasedir = $ARGV[2];
39 my $generator = $ARGV[3];
43 my $qmakeCachePath = catfile($out_basedir, ".qmake.cache");
45 my $QMAKE = catfile($qtbasedir, "bin", ($^O =~ /win32/i) ? 'qmake.exe' : 'qmake');
47 # try the qmake from the path (e.g. this is a shadow build)
51 # Need to use the right make
52 # SYMBIAN_UNIX/MINGW should fall back to the non SYMBIAN ones
53 my $MAKE = 'make'; # default, only works on unix
54 if ($generator =~ /UNIX|XCODE/i) { # XCODE = make?
56 } elsif ($generator =~ /MINGW/i) {
57 $MAKE = 'mingw32-make';
58 } elsif ($generator =~ /MSVC.NET|MSBUILD/i) {
61 # Unhandled (at least): BMAKE, GBUILD, SYMBIAN_ABLD, SYMBIAN_SBSV2
62 warn "Unrecognized generator spec ($generator) - assuming '$MAKE'\n";
65 ######################################################################
66 # Syntax: fileContents(filename)
67 # Params: filename, string, filename of file to return contents
69 # Purpose: Get the contents of a file.
70 # Returns: String with contents of the file, or empty string if file
72 # Warning: Dies if it does exist but script cannot get read access.
73 ######################################################################
76 my $filecontents = "";
78 open(I, "< $filename") || die "Could not open $filename for reading, read block?";
87 ######################################################################
88 # Syntax: loadConfigTests()
90 # Purpose: Loads the config tests from the source basedir into %configtests.
92 ######################################################################
94 my $configprofile = catfile($basedir, $configTestSource);
96 unless ($result = do $configprofile) {
97 die "configtests couldn't parse $configprofile: $@\n" if $@;
98 # We don't check for non null output, since that is valid
102 ######################################################################
103 # Syntax: hashesAreDifferent
105 # Purpose: Compares two hashes. (must have same key=value for everything)
106 # Returns: 0 if they are the same, 1 otherwise
107 ######################################################################
108 sub hashesAreDifferent {
112 if (keys %a != keys %b) {
116 my %cmp = map { $_ => 1 } keys %a;
117 for my $key (keys %b) {
118 last unless exists $cmp{$key};
119 last unless $a{$key} eq $b{$key};
129 ######################################################################
130 # Syntax: executeSomething
131 # Params: A list of things.
133 # Purpose: Executes the first arg, passing the list.
134 # stderr is redirected to stdout, and the output is captured.
135 # Returns: The output.
136 ######################################################################
137 sub executeSomething {
139 my $program = $args[0];
141 my $pid = open(KID_TO_READ, "-|");
146 while (<KID_TO_READ>) {
147 $output = $output . $_;
149 close(KID_TO_READ) || $! == 0 || warn "\nFailed to execute $program: exited $?";
151 # redirect STDERR to STDOUT
152 open STDERR, ">&STDOUT";
155 exec ($program, @args) || die "\nCan't exec $program: $!\n";
162 ######################################################################
163 # Syntax: executeTest()
166 # The testName variable controls the actual config test run - the
167 # source is assumed to be in $basedir/config.tests/$testName, and
168 # when 'qmake; make clean; make' is run, is expected to produce a file
169 # $out_basedir/config.tests/$testName/$testName. If this test passes,
170 # then 'config_test_$testName = yes' will be written to $out_basedir/.qmake.cache
172 # Purpose: Runs a configuration time test.
173 # Returns: 0 if the test fails, 1 if it passes, 2 if the test is skipped
174 # (e.g. .pro file has requires(x) and x is not satisfied)
175 ######################################################################
179 my $oldWorkingDir = getcwd();
182 my @QMAKEARGS = ('CONFIG-=debug_and_release');
184 my $testOutDir = catdir($out_basedir, 'config.tests', $testName);
186 if (abs_path($basedir) eq abs_path($out_basedir)) {
187 chdir $testOutDir or die "\nUnable to change to config test directory ($testOutDir): $!\n";
188 } else { # shadow build
189 if (! -e $testOutDir) {
190 mkpath $testOutDir or die "\nUnable to create shadow build config test directory ($testOutDir): $!\n";
192 chdir $testOutDir or die "\nUnable to change to config test directory ($testOutDir): $!\n";
194 push (@QMAKEARGS, catdir($basedir, 'config.tests', $testName));
198 executeSomething($QMAKE, @QMAKEARGS);
199 executeSomething($MAKE, 'clean');
200 my $makeOutput = executeSomething(($MAKE));
202 # If make prints "blah blah blah\nSkipped." we consider this a skipped test
203 if ($makeOutput !~ qr(^Skipped\.$)ms) {
205 # Check the test exists (can't reliably execute, especially for cross compilation)
206 if ($^O =~ /win32/i) {
207 # On windows look for $testName.exe
208 if (-e catfile($testOutDir, "$testName.exe")) {
212 if (-e catfile($testOutDir, $testName)) {
220 chdir $oldWorkingDir or die "\nUnable to restore working directory: $!\n";
224 # Now run configuration tests
225 # %configtests is a map from config test name to a map of parameters
229 # "simple" => {fatal => 1, message => "Missing required 'simple' component\n"},
230 # "failed" => {message => "You need to install the FAILED sdk for this to work\n"}
233 # Parameters and their defaults:
234 # - fatal [false] - whether failing this test should abort everything
235 # - message [""] - A special message to display if this test fails
239 # Only do this step for modules that have config tests
240 # (qtbase doesn't). We try to preserve existing contents (and furthermore
241 # only write to .qmake.cache if the tests change)
242 if (abs_path($out_basedir) ne abs_path($qtbasedir)) {
243 # Read any existing content
244 my $existingContents = fileContents($qmakeCachePath);
247 my @fatalTestsEncountered;
249 # Parse the existing results so we can check if we change them
250 while ($existingContents =~ /^config_test_(.*) = (yes|no)$/gm) {
251 $oldTestResults{$1} = $2;
254 # Get the longest length test name so we can pretty print
255 use List::Util qw(max);
256 my $maxNameLength = max map { length $_ } keys %configtests;
261 # Now run the configuration tests
262 print "Configuration tests:\n";
264 while ((my $testName, my $testParameters) = each %configtests) {
265 printf " % *s: ", $maxNameLength, $testName; # right aligned, yes/no lines up
267 my $fatalTest = $testParameters->{"fatal"} // 0;
268 my $message = $testParameters->{"message"};
270 my $testResult = executeTest($testName);
271 my @testResultStrings = ("no\n","yes\n","skipped\n");
273 $newTestResults{$testName} = (($testResult == 1) ? "yes" : "no"); # skipped = no
275 if ($testResult == 0) {
278 print "no (fatal)\n";
279 # Report the fatality at the end, too
280 push (@fatalTestsEncountered, $testName);
284 if (defined($message)) {
286 print "\n" unless chop $message eq "\n";
290 print $testResultStrings[$testResult];
294 # Check if the test results are different
295 if (hashesAreDifferent(\%oldTestResults, \%newTestResults)) {
296 # Generate the new contents
297 my $newContents = $existingContents;
299 # Strip out any existing config test results
300 $newContents =~ s/^config_test_.*$//gms;
301 $newContents =~ s/^# Compile time test results.*$//gms;
303 # Add any remaining content and make sure we start on a new line
304 if ($newContents and chop $newContents ne '\n') {
305 $newContents = $newContents . "\n";
309 if (%newTestResults) {
310 $newContents = $newContents . '# Compile time test results ('.(localtime).")\n";
313 while ((my $testName, my $testResult) = each %newTestResults) {
314 $newContents = $newContents . "config_test_$testName = $testResult\n";
319 open my $cacheFileHandle, ">$qmakeCachePath" or die "Unable to open $qmakeCachePath for writing: $!\n";
321 print $cacheFileHandle $newContents;
323 close $cacheFileHandle or die "Unable to close $qmakeCachePath: $!\n";
326 # Now see if we have to die
327 if (@fatalTestsEncountered) {
328 if ($#fatalTestsEncountered == 0) {
329 warn "Mandatory configuration test (".$fatalTestsEncountered[0].") failed.\n\n";
331 warn "Mandatory configuration tests (". join (", ", @fatalTestsEncountered) . ") failed.\n\n";