File Coverage

blib/lib/QMake/Project.pm
Criterion Covered Total %
statement 45 321 14.0
branch 0 92 0.0
condition 0 21 0.0
subroutine 15 56 26.7
pod 13 13 100.0
total 73 503 14.5


line stmt bran cond sub pod time code
1             package QMake::Project;
2 2     2   24379 use strict;
  2         5  
  2         67  
3 2     2   9 use warnings;
  2         4  
  2         73  
4              
5             our $VERSION = '0.85';
6              
7 2     2   10 use Carp;
  2         7  
  2         142  
8 2     2   1791 use English qw(-no_match_vars);
  2         8484  
  2         12  
9 2     2   986 use File::Basename;
  2         3  
  2         143  
10 2     2   1699 use File::Spec::Functions qw(:ALL);
  2         1521  
  2         453  
11 2     2   2367 use File::Temp;
  2         45207  
  2         146  
12 2     2   1283 use File::Which;
  2         1751  
  2         86  
13 2     2   1612 use File::chdir;
  2         3963  
  2         223  
14 2     2   2542 use Getopt::Long qw(GetOptions);
  2         23205  
  2         15  
15 2     2   2247 use IO::File;
  2         1985  
  2         384  
16 2     2   1756 use List::MoreUtils qw(apply);
  2         2536  
  2         165  
17 2     2   1454 use ReleaseAction qw(on_release);
  2         497  
  2         116  
18 2     2   1565 use Scalar::Defer qw(lazy);
  2         24172  
  2         16  
19 2     2   2040 use Text::ParseWords;
  2         2431  
  2         7774  
20              
21             my $WINDOWS = ($OSNAME =~ m{win32}i);
22              
23             # Magic string denoting we've deliberately exited qmake early
24             my $MAGIC_QMAKE_EXIT_STRING = __PACKAGE__.':EXITING';
25              
26             sub new
27             {
28 0     0 1   my ($class, $file) = @_;
29              
30 0           my $self = bless {
31             _die_on_error => 1, # whether to die when an error occurs
32             _qmake_count => 0, # number of times qmake has been run (for testing)
33             }, $class;
34              
35 0 0         if ($file) {
36 0 0 0       if (-d $file || $file =~ m{\.pr.$}i) {
37 0           $self->set_project_file( $file );
38             } else {
39 0           $self->set_makefile( $file );
40             }
41             }
42              
43 0           $self->set_make( $self->_default_make( ) );
44              
45 0           return $self;
46             }
47              
48             sub set_makefile
49             {
50 0     0 1   my ($self, $makefile) = @_;
51              
52 0           $self->{ _makefile } = $makefile;
53 0           delete $self->{ _project_file };
54 0           $self->{ _resolved } = {};
55              
56 0           return;
57             }
58              
59             sub makefile
60             {
61 0     0 1   my ($self) = @_;
62 0           return $self->{ _makefile };
63             }
64              
65             sub set_project_file
66             {
67 0     0 1   my ($self, $file) = @_;
68              
69 0           $self->{ _project_file } = $file;
70 0           delete $self->{ _makefile };
71 0           $self->{ _resolved } = {};
72              
73 0           return;
74             }
75              
76             sub project_file
77             {
78 0     0 1   my ($self) = @_;
79 0           return $self->{ _project_file };
80             }
81              
82             sub set_make
83             {
84 0     0 1   my ($self, $make) = @_;
85              
86 0           $self->{ _make } = $make;
87              
88 0           return;
89             }
90              
91             sub make
92             {
93 0     0 1   my ($self) = @_;
94              
95 0           return $self->{ _make };
96             }
97              
98             sub set_qmake
99             {
100 0     0 1   my ($self, $qmake) = @_;
101              
102 0           $self->{ _qmake } = $qmake;
103              
104 0           return;
105             }
106              
107             sub qmake
108             {
109 0     0 1   my ($self) = @_;
110              
111 0           return $self->{ _qmake };
112             }
113              
114             sub _find_qmake
115             {
116 0     0     my ($self) = @_;
117 0 0         if (!$self->{ _found_qmake }) {
118 0           my @qmakes = qw(qmake qmake-qt5 qmake-qt4);
119 0           foreach my $qmake (@qmakes) {
120 0 0         if (my $found = which( $qmake )) {
121 0           $self->{ _found_qmake } = $found;
122 0           last;
123             }
124             }
125             }
126 0           return $self->{ _found_qmake };
127             }
128              
129             sub _qmake
130             {
131 0     0     my ($self) = @_;
132 0 0         if (my $qmake = $self->qmake()) {
133 0           return $qmake;
134             }
135 0           return $self->_find_qmake();
136             }
137              
138             # Returns a reasonable default make command based on the platform.
139             sub _default_make
140             {
141 0     0     my ($self) = @_;
142              
143 0 0         if ($WINDOWS) {
144 0           return 'nmake';
145             }
146              
147 0           return 'make';
148             }
149              
150             sub die_on_error
151             {
152 0     0 1   my ($self) = @_;
153              
154 0           return $self->{ _die_on_error };
155             }
156              
157             sub set_die_on_error
158             {
159 0     0 1   my ($self, $value) = @_;
160              
161 0           $self->{ _die_on_error } = $value;
162 0           return;
163             }
164              
165             sub _prepare_variable
166             {
167 0     0     my ($self, @variable) = @_;
168              
169 0           foreach my $variable (@variable) {
170 0           $self->{ _to_resolve }{ variable }{ $variable } = 1;
171             }
172              
173 0           return;
174             }
175              
176             sub _prepare_test
177             {
178 0     0     my ($self, @test) = @_;
179              
180 0           foreach my $test (@test) {
181 0           $self->{ _to_resolve }{ test }{ $test } = 1;
182             }
183              
184 0           return;
185             }
186              
187             sub _qx_or_croak
188             {
189 0     0     my ($self, $cmd) = @_;
190              
191 0           my $output = qx($cmd);
192 0 0         if ($? != 0) {
193             # If $output contains this magic, we deliberately exited, and the status can
194             # be ignored.
195 0 0         if ($output !~ m/\Q$MAGIC_QMAKE_EXIT_STRING\E/) {
196 0           croak __PACKAGE__.": command `$cmd', in directory $CWD, exited with status $?, output follows:\n$output\n";
197             }
198 0           return $output;
199             }
200              
201 0           return $output;
202             }
203              
204             # Returns a copy of %ENV with any make-related environment variables removed.
205             sub _make_cleaned_env
206             {
207 0     0     my ($self) = @_;
208              
209 0           my %out = %ENV;
210              
211 0           delete @out{qw(
212             MAKEFLAGS
213             MAKELEVEL
214             MFLAGS
215             )};
216              
217 0           return %out;
218             }
219              
220             # Returns the qmake command (as a single string) used to generate the given makefile.
221             # Croaks on error.
222             sub _discover_qmake_command
223             {
224 0     0     my ($self, %args) = @_;
225              
226 0           my $make = $self->make( );
227 0           my $makefile = $args{ makefile };
228              
229             # Make sure we do not accidentally inherit any environment from
230             # some calling make (e.g. if we are invoked via `make check')
231 0           local %ENV = $self->_make_cleaned_env( );
232              
233 0           my $cmd = qq{"$make" -f "$makefile" -n qmake 2>&1};
234 0           my $output = $self->_qx_or_croak( $cmd );
235 0           my @lines = reverse split( /\n/, $output );
236              
237 0           my $out;
238 0           while (my $line = shift @lines) {
239 0 0         $line or next;
240             # last line should be the qmake command
241 0 0         if ($line =~ m{qmake}i) {
242 0           $out = $line;
243 0           chomp $out;
244 0           last;
245             }
246             }
247              
248 0 0         if (!$out) {
249 0           croak __PACKAGE__.": could not figure out qmake command used to generate $makefile\n"
250             ."Output from command ($cmd):\n$output\n";
251             }
252              
253 0           return $out;
254             }
255              
256             # Given a qmake $command line (single string containing both command and args),
257             # parses it and returns a hashref with the following:
258             #
259             # qmake => path to the qmake binary
260             # makefile => path to the makefile
261             # projectfiles => arrayref, paths to the project (.pro) file(s)
262             # args => arrayref, all args not covered by any of the above
263             #
264             sub _parse_qmake_command
265             {
266 0     0     my ($self, $command) = @_;
267              
268 0           my $qmake;
269             my $makefile;
270 0           my @projectfiles;
271 0           my @args;
272              
273             # Getopt callbacks to accept a known qmake option onto @args
274             my $sub_accept_option_with_value = sub {
275             # Getopt already removes the -, we need to put it back
276 0     0     my ($option, $value) = @_;
277 0           push @args, "-$option", "$value";
278 0           };
279             my $sub_accept_option_without_value = sub {
280 0     0     my ($option) = @_;
281 0           push @args, "-$option";
282 0           };
283              
284             # Getopt callback to accept an unknown qmake argument.
285             # This includes determining whether the argument should be handled as a
286             # .pro file; the logic for this must match qmake's own logic in option.cpp.
287             my $sub_accept_nonoption = sub {
288 0     0     my ($arg) = @_;
289              
290 0 0         if ($arg =~ m{=}) {
    0          
291             # Arg containing '=' => it is a user variable assignment.
292             # Nothing special to be done.
293 0           push @args, $arg;
294             }
295             elsif ($arg =~ m{^-}) {
296             # Arg starts with '-' => it is probably a qmake argument we haven't
297             # handled. For example, a new qmake argument added after this
298             # script was created. In this case, our code needs to be updated
299             # to handle it safely, so we'll warn about it, then keep going.
300 0           warn __PACKAGE__ . ": in ($command), the meaning of $arg is unknown.\n";
301 0           push @args, $arg;
302             }
303             else {
304             # Otherwise, it is a .pro file.
305 0           push @projectfiles, $arg;
306             }
307 0           };
308              
309 0           Getopt::Long::Configure( 'permute', 'pass_through' );
310              
311             {
312 0           local @ARGV = $self->_split_command_to_words( $command );
  0            
313              
314             # The first element is the qmake binary itself
315 0           $qmake = shift @ARGV;
316              
317 0           GetOptions(
318             # All of these options are directly accepted into @args with no
319             # special behavior
320 0           map( { $_ => $sub_accept_option_without_value } qw(
321             project
322             makefile
323             Wnone
324             Wall
325             Wparser
326             Wlogic
327             Wdeprecated
328             d
329             help
330             v
331             after
332             norecursive
333             recursive
334             nocache
335             nodepend
336             nomoc
337             nopwd
338             macx
339             unix
340             win32
341             )),
342             map( { $_ => $sub_accept_option_with_value } qw(
343             unset=s
344             query=s
345             cache=s
346             spec=s
347             t=s
348             tp=s
349             )),
350              
351             # "-o " tells us which makefile to use
352 0     0     'o=s' => sub { (undef, $makefile) = @_ },
353              
354             # anything else should be either a variable assignment or
355             # a .pro file, pass it to our function for handling these
356 0 0         '<>' => $sub_accept_nonoption,
357             ) || croak __PACKAGE__.": command ($command) could not be parsed";
358             }
359              
360             return {
361 0           qmake => $qmake,
362             makefile => $makefile,
363             projectfiles => \@projectfiles,
364             args => \@args,
365             };
366             }
367              
368             # Given a single string representing a qmake command, split it into
369             # a list of arguments as qmake's own main() would receive
370             sub _split_command_to_words
371             {
372 0     0     my ($self, $cmd) = @_;
373              
374 0 0         if ($WINDOWS) {
375             # In theory, we should be using CommandLineToArgvW here.
376             # But do we really need to? It seems quite annoying to use that
377             # from within perl (e.g. requires Inline::C or Win32::API).
378             #
379             # From reading the qmake sources, where the "qmake:" target is
380             # written, it appears that the command-line is simple enough that
381             # this will never actually matter. Basically, the only special
382             # construct is if a path contains spaces, in which case double
383             # quotes are used around that path.
384             #
385             # Therefore, the Windows command-line handling is compatible
386             # with the Unix command-line handling, except that \ does not
387             # have a special meaning (so we have to escape it to keep them
388             # as-is).
389 0           $cmd =~ s{\\}{\\\\}g;
390             }
391              
392 0           return Text::ParseWords::shellwords( $cmd );
393             }
394              
395             sub _resolve
396             {
397 0     0     my ($self) = @_;
398              
399 0           eval {
400 0           $self->_resolve_impl( );
401             };
402 0 0         if ($@) {
403 0           my $error = $@;
404             # Make sure the error visibly comes from us
405 0           my $pkg = __PACKAGE__;
406 0 0         if ($error !~ m{^\Q$pkg\E: }) {
407 0           $error = "$pkg: $error";
408             }
409              
410 0 0         if ($self->{ _die_on_error }) {
411 0           croak $error;
412             }
413 0           carp $error;
414             }
415              
416 0           return;
417             }
418              
419             sub _resolve_files_from_makefile
420             {
421 0     0     my ($self, $makefile) = @_;
422              
423 0           local $CWD = dirname( $makefile );
424 0           $makefile = basename( $makefile );
425              
426 0           my $original_qmake_command = $self->_discover_qmake_command( makefile => $makefile );
427 0           my $parsed_qmake_command = $self->_parse_qmake_command( $original_qmake_command );
428              
429             # We must have exactly one makefile and one .pro file to proceed
430             my $croak_command_error = sub {
431 0     0     croak __PACKAGE__.": in ($original_qmake_command), @_";
432 0           };
433              
434             my $parsed_makefile = $parsed_qmake_command->{ makefile }
435 0   0       || $croak_command_error->( 'the output makefile could not be determined' );
436 0           my @projectfiles = @{$parsed_qmake_command->{ projectfiles }};
  0            
437 0 0         if (@projectfiles == 0) {
438 0           $croak_command_error->( 'the input .pro file could not be determined' );
439             }
440 0 0         if (@projectfiles > 1) {
441 0           $croak_command_error->( 'this is an unusual, unsupported qmake command' );
442             }
443              
444 0           my $projectfile = $projectfiles[0];
445 0 0         if (!file_name_is_absolute( $projectfile )) {
446 0           $projectfile = rel2abs( $projectfile, dirname( $parsed_makefile ) );
447             }
448              
449             return (
450             $parsed_qmake_command->{ qmake },
451             $parsed_qmake_command->{ args },
452 0           $projectfile,
453             $parsed_makefile
454             );
455             }
456              
457             sub _resolve_files
458             {
459 0     0     my ($self) = @_;
460              
461 0 0         if (my $makefile = $self->makefile( )) {
462 0           return $self->_resolve_files_from_makefile( $makefile );
463             }
464              
465 0   0       my $project_file = $self->project_file( )
466             || croak __PACKAGE__.': no makefile or project file set';
467              
468 0 0         if (-f $project_file) {
469 0           return ($self->_qmake(), undef, $project_file, catfile( dirname( $project_file ), 'Makefile' ));
470             }
471              
472 0 0         if (-d $project_file) {
473 0           my $qmake = $self->_qmake();
474 0           my $makefile = catfile( $project_file, 'Makefile' );
475 0           my @candidates = glob( catfile( $project_file, '*.pro' ) );
476 0 0         if (@candidates == 1) {
477 0           return ($qmake, undef, $candidates[0], $makefile);
478             }
479              
480 0           my $project_basename = basename( $project_file );
481 0           @candidates = grep { lc(basename($_, '.pro')) eq lc($project_basename) } @candidates;
  0            
482 0 0         if (@candidates == 1) {
483 0           return ($qmake, undef, $candidates[0], $makefile);
484             }
485              
486 0           @candidates = grep { basename($_, '.pro') eq $project_basename } @candidates;
  0            
487 0 0         if (@candidates == 1) {
488 0           return ($qmake, undef, $candidates[0], $makefile);
489             }
490              
491 0           croak __PACKAGE__.": could not resolve project file in directory $project_file";
492             }
493              
494 0           croak __PACKAGE__.": $project_file is not an existing directory or file";
495             }
496              
497             sub _resolve_impl
498             {
499 0     0     my ($self) = @_;
500              
501 0           my $to_resolve = delete $self->{ _to_resolve };
502 0 0         if (!$to_resolve) {
503 0           return $self->{ _resolved };
504             }
505              
506 0           my ($qmake, $qmake_args, $projectfile, $makefile) = $self->_resolve_files();
507              
508             # We're ready to run our qmake.
509             #
510             # We need to rewrite the input, and we don't care about the output, so we use
511             # temporary files for both of these.
512             #
513             # Note that the temporary files must be in the same directory as the real input/output
514             # files, because this significantly affects the behavior of qmake (e.g. values of $$PWD,
515             # $$_PRO_FILE_PWD_)
516 0           my $pkg_safe = __PACKAGE__;
517 0           $pkg_safe =~ s{[^a-zA-Z0-9]}{_}g;
518              
519 0           my $temp_makefile = File::Temp->new(
520             TEMPLATE => "${pkg_safe}_Makefile.XXXXXX",
521             DIR => dirname( $makefile ),
522             UNLINK => 1,
523             );
524             # qmake may silently create various other makefiles behind our back (e.g. Debug, Release
525             # makefiles), so we have to arrange to delete those too.
526 0           my $delete_other_makefiles = $self->_delete_files_on_destroy( "$temp_makefile.*" );
527              
528 0           my $temp_projectfile = File::Temp->new(
529             TEMPLATE => "${pkg_safe}_XXXXXX",
530             SUFFIX => '.pro',
531             DIR => dirname( $projectfile ),
532             UNLINK => 1,
533             );
534              
535 0           my $temp_qmakefeatures_dir = File::Temp->newdir(
536             "${pkg_safe}_XXXXXX",
537             TMPDIR => 1,
538             CLEANUP => 1,
539             );
540              
541             local $ENV{ QMAKEFEATURES } = "$temp_qmakefeatures_dir"
542             . ($ENV{ QMAKEFEATURES }
543             ? ':'.$ENV{ QMAKEFEATURES }
544 0 0         : '')
545             ;
546              
547 0           $self->_write_modified_pro_prf(
548             input_filename => $projectfile,
549             output_pro => $temp_projectfile,
550             output_qmakefeatures => $temp_qmakefeatures_dir,
551             to_resolve => $to_resolve,
552             );
553              
554             # Special case: default value of TARGET is defined by the .pro file name.
555             # We changed the .pro file name, but we can keep the old target by
556             # passing it on the command-line.
557 0           my $initial_target = fileparse( $projectfile, qr{\..+\z} );
558              
559             # If it has a space, it needs to be double-quoted (i.e. quoted in shell,
560             # and also quoted in qmake)
561 0 0         if ($initial_target =~ m{ }) {
562 0           $initial_target = qq{"$initial_target"};
563             }
564              
565 0 0         my $qmake_command = $self->_shquote(
566             $qmake,
567             '-o',
568             $temp_makefile,
569             "TARGET=$initial_target",
570             $temp_projectfile,
571 0           @{$qmake_args || []},
572             );
573 0           my $qmake_output = $self->_qx_or_croak( "$qmake_command 2>&1" );
574              
575             # _parse_qmake_output merges with current _resolved
576 0           $self->_parse_qmake_output( $qmake_output );
577              
578 0           ++$self->{ _qmake_count };
579              
580 0           return $self->{ _resolved };
581             }
582              
583             # Returns a handle which, when it goes out of scope, will delete
584             # all the files matching $glob.
585             sub _delete_files_on_destroy
586             {
587 0     0     my ($self, $glob) = @_;
588              
589             return on_release {
590 0     0     my @files = glob( $glob );
591 0 0         return unless @files;
592              
593 0 0         if (unlink( @files ) != @files) {
594 0           warn __PACKAGE__.': failed to remove some of ('
595             .join(', ', @files)
596             ."): $!";
597             }
598 0           };
599             }
600              
601             sub _write_modified_pro_prf
602             {
603 0     0     my ($self, %args) = @_;
604              
605 0           my $input_filename = $args{ input_filename };
606 0           my $output_pro = $args{ output_pro };
607 0           my $output_qmakefeatures = $args{ output_qmakefeatures };
608 0           my $to_resolve = $args{ to_resolve };
609 0           my $pkg = __PACKAGE__;
610              
611 0           my $prf_basename = '_perl_qmake_project_magic';
612 0           my $prf_name = "$output_qmakefeatures/$prf_basename.prf";
613              
614 0   0       my $input_fh = IO::File->new( $input_filename, '<' )
615             || croak "$pkg: open $input_filename for read: $!";
616 0   0       my $prf_fh = IO::File->new( $prf_name, '>' )
617             || croak "$pkg: open $prf_name for write: $!";
618              
619             # Copy the input .pro file unmodified ...
620 0           while (my $line = <$input_fh>) {
621 0           $output_pro->print( $line );
622             }
623              
624             # Then arrange our .prf to be loaded.
625             # CONFIG are loaded from right-to-left, so we put ourself at
626             # the beginning to be loaded last.
627 0           $output_pro->printflush( qq|\n\nCONFIG=$prf_basename \$\$CONFIG\n| );
628              
629             # And write all code to resolve the desired values to our prf.
630             # Set PWD back to the value from the .pro file, to hide that we're
631             # in a temporary .prf
632 0           $prf_fh->print( qq|PWD="\$\$_PRO_FILE_PWD_"\n| );
633 0           $prf_fh->print( qq|message("${pkg}::BEGIN")\n| );
634              
635             # The name of a qmake variable which we can safely use without fear of colliding
636             # with any real qmake variables.
637 0           my $x = $pkg;
638 0           $x =~ s{[^a-zA-Z0-9]}{_}g;
639              
640             # For each variable we've been asked to resolve, make qmake output lines like:
641             #
642             # QMake::Project::variable:CONFIG:val1
643             # QMake::Project::variable:CONFIG:val2
644             # ...etc
645             #
646             # Most qmake variables are lists; in fact, all "normal" qmake variables
647             # are lists, but a few special substitutions (e.g. _PRO_FILE_PWD_) use
648             # special code. We always try with "for" first to get proper lists,
649             # then fall back to a plain message otherwise.
650             #
651 0 0         foreach my $v (keys %{ $to_resolve->{ variable } || {} }) {
  0            
652 0           $prf_fh->print( <<"END_QMAKE" );
653              
654             unset(found_$x)
655             for($x,$v) {
656             message("${pkg}::variable:$v:\$\$$x") # normal variable (list)
657             found_$x=1
658             }
659             isEmpty(found_$x):message("${pkg}::variable:$v:\$\$$v") # special variable
660              
661             END_QMAKE
662              
663             }
664              
665             # For each test we've been asked to resolve, make qmake output lines like:
666             #
667             # QMake::Project::test:EXPR1:1
668             # QMake::Project::test:EXPR2:0
669             # ...etc
670             #
671 0 0         foreach my $test (keys %{ $to_resolve->{ test } || {} }) {
  0            
672 0           $prf_fh->print(
673             qq|$x=0\n$test:$x=1\nmessage("${pkg}::test:$test:\$\$$x")\n|
674             );
675             }
676              
677 0           $prf_fh->printflush( qq|\nunset($x)\nmessage("${pkg}::END")\n| );
678              
679             # We've output everything we need.
680             # Kill qmake, to avoid wasting time creating the Makefile.
681             # In a basic benchmark (on Linux), this seems to save ~10-15% of runtime.
682 0           $prf_fh->printflush( qq|error($MAGIC_QMAKE_EXIT_STRING)\n| );
683              
684 0           return;
685             }
686              
687             sub _parse_qmake_output
688             {
689 0     0     my ($self, $output) = @_;
690              
691 0           my $pkg = quotemeta( __PACKAGE__ );
692 0           my $resolved = {
693             variable => {},
694             test => {},
695             };
696              
697 0           my @lines = split( /\n/, $output );
698 0           my $parsing = 0;
699 0           foreach my $line (@lines) {
700             # We only parse between our BEGIN and END blocks, just in case something
701             # somewhere else is outputting lines which could confuse us.
702 0 0         if ($line =~ m/\b${pkg}::BEGIN/) {
    0          
703 0           $parsing = 1;
704             }
705             elsif ($line =~ m/\b${pkg}::END/) {
706 0           $parsing = 0;
707 0           last;
708             }
709 0 0         next unless $parsing;
710              
711 0 0         if ($line =~ m/\b${pkg}::variable:([^:]+):(.+)\z/) {
    0          
712 0           push @{ $resolved->{ variable }{ $1 } }, $2;
  0            
713             }
714             elsif ($line =~ m/\b${pkg}::test:([^:]+):(.+)\z/) {
715 0           $resolved->{ test }{ $1 } = $2;
716             }
717             }
718              
719             # Now merge what we resolved this time with what we resolved previously
720 0           my %resolved_variable = %{ $resolved->{ variable } };
  0            
721 0           my %resolved_test = %{ $resolved->{ test } };
  0            
722             $self->{ _resolved }{ variable } = {(
723 0 0         %{ $self->{ _resolved }{ variable } || {} },
  0            
724             %resolved_variable,
725             )};
726             $self->{ _resolved }{ test } = {(
727 0 0         %{ $self->{ _resolved }{ test } || {} },
  0            
728             %resolved_test,
729             )};
730              
731 0           return;
732             }
733              
734             # Given an arguments list, returns a single string representing that command in a shell.
735             # This is far from complete, it only needs to work for all the qmake commands we're likely
736             # to run, in sh and cmd.
737             sub _shquote
738             {
739 0     0     my ($self, @command) = @_;
740              
741             # [ q{"Hello", world!}, q{nice day today} ] => q{"\"Hello\", world!" "nice day today"}
742              
743 0     0     @command = apply { s{"}{\\"}g } @command;
  0            
744 0           @command = map { qq{"$_"} } @command;
  0            
745              
746 0           return join(' ', @command);
747             }
748              
749             sub values ## no critic (Subroutines::ProhibitBuiltinHomonyms)
750             # Yes, there is a builtin values(), but we are trying to follow the
751             # API of the QMakeProject class in qmake/project.cpp, and this should
752             # be harmless if always invoked using $object-> syntax.
753             {
754 0     0 1   my ($self, $key) = @_;
755              
756 0           $self->_prepare_variable( $key );
757              
758 0           return $self->_lazy_value( project => $self, key => $key, type => 'variable' );
759             }
760              
761             sub test
762             {
763 0     0 1   my ($self, $key) = @_;
764              
765 0           $self->_prepare_test( $key );
766              
767 0           return $self->_lazy_value( project => $self, key => $key, type => 'test' );
768             }
769              
770             sub _lazy_value
771             {
772 0     0     my ($self, %args) = @_;
773              
774             my $get = sub {
775 0     0     $self->_resolve( );
776 0           my $resolved = $self->{ _resolved }{ $args{ type } }{ $args{ key } };
777 0 0 0       if (defined($resolved) && ref($resolved) eq 'ARRAY') {
778 0 0         return wantarray ? @{ $resolved } : $resolved->[0];
  0            
779             }
780              
781             # If there was an error, and we wantarray, make sure we return ()
782             # rather than (undef)
783 0 0 0       if (wantarray && !defined($resolved)) {
784 0           return ();
785             }
786              
787 0           return $resolved;
788 0           };
789              
790 0 0         if (wantarray) {
791 0           return $get->( );
792             }
793              
794 0     0     return lazy { $get->( ) };
  0            
795             }
796              
797             1;
798              
799             =head1 NAME
800              
801             QMake::Project - evaluate qmake project files
802              
803             =head1 SYNOPSIS
804              
805             use QMake::Project;
806              
807             # Load a project from a .pro file
808             my $prj = QMake::Project->new( 'test.pro' );
809              
810             # Perform arbitrary tests; may be anything usable from a qmake scope
811             my $testcase = $prj->test( 'testcase' );
812             my $insignificant = $prj->test( 'insignificant_test' );
813              
814             # May also load from a qmake-generated Makefile
815             $prj->set_makefile( 'path/to/Makefile' );
816              
817             # Retrieve arbitrary values (scalars or lists)
818             my $target = $prj->values( 'TARGET' );
819              
820             return unless $testcase;
821              
822             my $status = system( $target, '-silent' );
823             return unless $status;
824             if ($insignificant) {
825             warn "Test $target failed; ignoring, since it is insignificant";
826             return;
827             }
828             die "Test $target failed with exit status $status";
829              
830             Given a qmake project, provides an API for accessing any
831             qmake variables or tests (scopes).
832              
833             =head1 DESCRIPTION
834              
835             For projects using qmake, .pro files are a convenient place to include
836             all sorts of metadata. This module facilitates the extraction of this
837             metadata.
838              
839             =head2 HOW IT WORKS
840              
841             The qmake language is undefined, and there is no library form of qmake.
842             This means that only qmake (the binary) can parse qmake (the language).
843             Therefore, this module does not parse any qmake .pro files itself.
844             qmake does all the parsing.
845              
846             Values are resolved using a process like the following:
847              
848             =over
849              
850             =item *
851              
852             If a qmake-generated makefile is given, it is used to determine the
853             correct qmake command, arguments and .pro file for this test.
854              
855             =item *
856              
857             A temporary .pro file is created containing the content of the real .pro
858             file, as well as some additional code which outputs all of the requested
859             variables / tests.
860              
861             =item *
862              
863             qmake is run over the temporary .pro file. The Makefile generated by
864             this qmake run is discarded. The standard output of qmake is parsed to
865             determine the values of the evaluated variables/tests.
866              
867             =back
868              
869             =head2 DELAYED EVALUATION
870              
871             Running qmake can be relatively slow (e.g. a few seconds for a cold
872             run), and therefore the amount of qmake runs should be minimized.
873             This is accomplished by delayed evaluation.
874              
875             Essentially, repeated calls to the B or B functions
876             will not result in any qmake runs, until one of the values returned
877             by these functions is used. This is accomplished by returning
878             deferred values via L.
879              
880             For example, consider this code:
881              
882             my $project = QMake::Project->new( 'test.pro' );
883             my $target = $project->values( 'TARGET' );
884             my $target_path = $project->values( 'target.path' );
885              
886             say "$target will be installed to $target_path"; # QMAKE EXECUTED HERE!
887              
888             There is a single qmake execution, occurring only when the values
889             are used by the caller.
890              
891             This means that writing the code a bit differently would potentially
892             have much worse performance:
893              
894             #### BAD EXAMPLE ####
895             my $project = QMake::Project->new( 'test.pro' );
896              
897             my $target = $project->values( 'TARGET' );
898             say "Processing $target"; # QMAKE EXECUTED HERE!
899              
900             my $target_path = $project->values( 'target.path' );
901             say " -> will be installed to $target_path"; # QMAKE EXECUTED HERE!
902              
903             Therefore it is good to keep the delayed evaluation in mind, to avoid writing
904             poorly performing code.
905              
906             As a caveat to all of the above, a list evaluation is never delayed. This is
907             because the size of the list must always be known when a list is returned.
908              
909             my $project = QMake::Project->new( 'test.pro' );
910             my $target = $project->values( 'TARGET' );
911             my @config = $project->values( 'CONFIG' ); # QMAKE EXECUTED HERE!
912              
913             say "configuration of $target: ".join(' ', @CONFIG);
914              
915             =head2 ERROR HANDLING
916              
917             By default, all errors are considered fatal, and raised as exceptions.
918             This includes errors encountered during delayed evaluation.
919              
920             Errors can be made into non-fatal warnings by calling C.
921              
922             All exceptions and warnings match the pattern C.
923              
924             =head2 FUNCTIONS
925              
926             The following functions are provided:
927              
928             =over
929              
930             =item B()
931              
932             =item B( MAKEFILE )
933              
934             =item B( PROJECTFILE )
935              
936             =item B( DIRECTORY )
937              
938             Returns a new B representing the qmake project for
939             the given MAKEFILE, PROJECTFILE or DIRECTORY.
940              
941             If passed a makefile, the makefile must be generated from a qmake project
942             and contain a valid 'qmake' target.
943              
944             If passed a directory, the project file will be resolved according to the
945             same rules used by qmake when invoked on a directory.
946              
947             If no argument is provided, one of B or B
948             must be called before attempting to retrieve any values from the project.
949              
950             This function will handle a filename matching /\.pr.$/ as a project file
951             and any other filename as a makefile. If this is not appropriate, call
952             one of the B or B functions.
953              
954             =item B( EXPRESSION )
955              
956             Returns a true value if the given qmake EXPRESSION evaluated to true,
957             a false value otherwise.
958              
959             EXPRESSION must be a valid qmake "test" expression, as in the following
960             construct:
961              
962             EXPRESSION:message("The expression is true!")
963              
964             Compound expressions are fine. For example:
965              
966             if ($project->test( 'testcase:CONFIG(debug, debug|release)' )) {
967             say "Found testcase in debug mode. Running test in debugger.";
968             ...
969             }
970              
971             The actual evaluation of the expression might be delayed until the returned
972             value is used in a boolean context. See L for more
973             details.
974              
975             =item B( VARIABLE )
976              
977             Returns the value(s) of the given qmake VARIABLE.
978              
979             VARIABLE may be any valid qmake variable name, without any leading $$.
980              
981             Note that (almost) all qmake variables are inherently lists. A variable
982             with a single value, such as TARGET, is a list with one element. A variable
983             such as CONFIG contains many elements.
984              
985             In scalar context, this function will return only the variable's first value.
986             In list context, it will return all values.
987              
988             Example:
989              
990             my $target = $project->values( 'TARGET' );
991             my @testdata = $project->values( 'TESTDATA' );
992              
993             if (@testdata) {
994             say "Deploying testdata for $target";
995             ...
996             }
997              
998             In scalar context, the actual evaluation of the variable might be delayed
999             until the returned value is used in a string, integer or boolean context.
1000             See L for more details. In list context, evaluation is
1001             never delayed, due to implementation difficulties.
1002              
1003             =item B()
1004              
1005             =item B( MAKEFILE )
1006              
1007             Get or set the makefile referred to by this project.
1008              
1009             Note that changing the makefile invalidates any values resolved via the
1010             old makefile, and unsets the project file.
1011              
1012             =item B()
1013              
1014             =item B( PROJECTFILE )
1015              
1016             =item B( DIRECTORY )
1017              
1018             Get or set the project file (.pro file) referred to by this project.
1019              
1020             Note that changing the project file invalidates any values resolved via
1021             the old project file, and unsets the makefile.
1022              
1023             =item B()
1024              
1025             =item B( MAKE )
1026              
1027             Get or set the "make" binary (with no arguments) to be used for parsing
1028             the makefile. It should rarely be required to use these functions, as
1029             there is a reasonable default.
1030              
1031             =item B()
1032              
1033             =item B( QMAKE )
1034              
1035             Get or set the "qmake" binary (with no arguments).
1036             If unset (the default), the first existing 'qmake', 'qmake-qt5' or
1037             'qmake-qt4' command in PATH will be used.
1038              
1039             =item B()
1040              
1041             =item B( BOOL )
1042              
1043             Get or set whether to raise exceptions when an error occurs.
1044             By default, exceptions are raised.
1045              
1046             Calling C will cause errors to be reported as
1047             warnings only. When errors occur, undefined values will be returned
1048             by C and C.
1049              
1050             =back
1051              
1052             =head1 COMPATIBILITY
1053              
1054             This module should work with qmake from Qt 3, Qt 4 and Qt 5.
1055              
1056             =head1 CAVEATS
1057              
1058             jom <= 1.0.11 should not be used as the make command with this module,
1059             due to a bug in those versions of jom (QTCREATORBUG-7170).
1060              
1061             Write permissions are required to both the directory containing the .pro
1062             file and the directory containing the Makefile.
1063              
1064             The module tries to ensure that all evaluations are performed after
1065             qmake has processed default_post.prf and CONFIG - so, for example, if a
1066             .pro file contains CONFIG+=debug, QMAKE_CXXFLAGS would contain (e.g.) -g,
1067             as expected. However, certain code could break this (such as
1068             some .prf files loaded via CONFIG themselves re-ordering the CONFIG
1069             variable).
1070              
1071             It is possible to use this module to run arbitrary qmake code. It goes
1072             without saying that users are discouraged from abusing this :)
1073              
1074             Various exotic constructs may cause this code to fail; for example, .pro
1075             files with side effects. The rule of thumb is: if C works for
1076             your project, then this package should also work.
1077              
1078             This module is (somewhat obviously) using qmake in a way it was not
1079             designed to be used. Although it appears to work well in practice, it's
1080             fair to call this module one big hack.
1081              
1082             =head1 LICENSE AND COPYRIGHT
1083              
1084             Copyright 2012 Nokia Corporation and/or its subsidiary(-ies).
1085              
1086             Copyright 2012 Rohan McGovern.
1087              
1088             This program is free software; you can redistribute it and/or
1089             modify it under the terms of the GNU Lesser General Public
1090             License version 2.1 as published by the Free Software Foundation.
1091              
1092             This program is distributed in the hope that it will be useful,
1093             but WITHOUT ANY WARRANTY; without even the implied warranty of
1094             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1095             Lesser General Public License for more details.
1096              
1097             You should have received a copy of the GNU Lesser General Public
1098             License along with this program; if not, write to the Free
1099             Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
1100             02111-1307 USA.
1101              
1102             =cut