File Coverage

blib/lib/Makefile/Update/VCProj.pm
Criterion Covered Total %
statement 62 71 87.3
branch 29 40 72.5
condition 2 6 33.3
subroutine 5 5 100.0
pod 1 1 100.0
total 99 123 80.4


line stmt bran cond sub pod time code
1             package Makefile::Update::VCProj;
2             # ABSTRACT: Update list of sources and headers in Visual C++ projects.
3              
4 1     1   518 use Exporter qw(import);
  1         2  
  1         42  
5             our @EXPORT = qw(update_vcproj);
6              
7 1     1   3 use strict;
  1         2  
  1         21  
8 1     1   3 use warnings;
  1         1  
  1         727  
9              
10             our $VERSION = '0.2'; # VERSION
11              
12              
13              
14             sub update_vcproj
15             {
16 1     1 1 2998 my ($in, $out, $sources, $headers, $filter_cb) = @_;
17              
18             # Use standard/default classifier for the files if none is explicitly
19             # specified.
20 1 50       4 if (!defined $filter_cb) {
21             $filter_cb = sub {
22 6     6   6 my ($file) = @_;
23              
24 6 100       27 return 'Source Files' if $file =~ q{\.c(c|pp|xx|\+\+)?$};
25 3 50       18 return 'Header Files' if $file =~ q{\.h(h|pp|xx|\+\+)?$};
26              
27 0         0 warn qq{No filter defined for the file "$file".\n};
28              
29             undef
30 0         0 }
31 1         6 }
32              
33             # Hash mapping the filter to all the files using it (whether sources or
34             # headers).
35 1         2 my %files_by_filter;
36 1         2 foreach my $file (@$sources, @$headers) {
37 6         4 push @{$files_by_filter{$filter_cb->($file)}}, $file
  6         7  
38             }
39              
40             # Name of the current filter, if any.
41 1         1 my $filter;
42              
43             # Hash containing 0 or 1 for each file using the current filter.
44             my %seen;
45              
46             # Indicates whether the closing angle bracket of "" tags is on its
47             # own line (which is how MSVS 2005 and 2008 format their files) or on the
48             # same line as "RelativePath" attribute (which is how MSVS 2003 does it).
49 1         2 my $angle_bracket_on_same_line = 0;
50              
51             # Set to 1 if we made any changes.
52 1         1 my $changed = 0;
53              
54 1         4 while (defined (my $line_with_eol = <$in>)) {
55 32         71 (my $line = $line_with_eol) =~ s/\r?\n$//;
56              
57 32 100       67 if ($line =~ /^\s*
    100          
58 2 50       5 if (defined($filter)) {
59 0         0 warn qq{Nested tag at line $. while parsing filter } .
60             qq{"$filter" is not supported.\n};
61 0         0 next;
62             }
63              
64 2         1 print $out $line_with_eol;
65 2         3 $line_with_eol = <$in>;
66 2 50 33     12 if (defined $line_with_eol &&
67             $line_with_eol =~ /^\s*Name="(.*)"\r?\n$/) {
68 2         3 $filter = $1;
69 2 50       4 if (!exists $files_by_filter{$filter}) {
70             # If we don't have any files for this filter, don't remove
71             # all the files from it, just skip it entirely instead.
72 0         0 undef $filter;
73             } else {
74 2         1 %seen = map { $_ => 0 } @{$files_by_filter{$filter}};
  6         13  
  2         4  
75             }
76             } else {
77 0         0 warn qq{Unrecognized format for tag at line $..\n};
78             }
79             } elsif (defined $filter) {
80 17 100       58 if ($line =~ /^\s*
    100          
81 7         8 my $line_file_start = $line_with_eol;
82              
83 7         5 $line_with_eol = <$in>;
84 7 50 33     28 if (defined $line_with_eol &&
85             $line_with_eol =~ /^\s*RelativePath="(.*)"(>?)\r?\n$/) {
86 7         11 $angle_bracket_on_same_line = $2 eq '>';
87              
88             # Normalize path separators to Unix and remove the leading
89             # dot which MSVC likes to use for some reason.
90 7         8 (my $file = $1) =~ s@\\@/@g;
91 7         19 $file =~ s@^\./@@;
92              
93             # Special hack for resource files that sometimes occur in
94             # the "Source Files" section of MSVC projects too: don't
95             # remove them, even if they don't appear in the master
96             # files list, because they are never going to appear in it.
97 7 50       11 if ($file !~ /\.rc$/) {
98 7 100       12 if (!exists $seen{$file}) {
99             # This file is not in the master file list any
100             # more, delete it from the project file as well by
101             # not copying the lines corresponding to it to the
102             # output.
103 3         3 $changed = 1;
104              
105             # Skip the next line unless we had already seen
106             # the angle bracket.
107 3 100       6 if (!$angle_bracket_on_same_line) {
108 1 50       4 if (<$in> !~ /^\s*>\r?\n$/) {
109 0         0 warn qq{Expected closing '>' on the line $.\n}
110             }
111             }
112              
113             # And skip everything up to and including the
114             # closing tag in any case.
115 3         6 while (<$in>) {
116 3 50       11 last if qr{^\s*\r?\n$}
117             }
118              
119 3         9 next;
120             }
121              
122             # This file is still in the files list, mark it as seen.
123 4 50       7 if ($seen{$file}) {
124 0         0 warn qq{Duplicate file "$file" in the project at line $.\n};
125             } else {
126 4         6 $seen{$file} = 1;
127             }
128             }
129             } else {
130 0         0 warn qq{Unrecognized format for tag inside filter } .
131             qq{"$filter" at line $..\n};
132             }
133              
134             # Don't lose the original line, it won't be printed at the
135             # end of the loop any more.
136 4         4 print $out $line_file_start;
137             } elsif ($line =~ qr{^\s*$}) {
138 2 100       5 my $angle_bracket = $angle_bracket_on_same_line
139             ? '>'
140             : "\n\t\t\t\t>";
141              
142             # Add new files, if any.
143             #
144             # TODO Insert them in alphabetical order.
145 2         12 while (my ($file, $seen) = each(%seen)) {
146 6 100       14 if (!$seen) {
147             # Convert path separator to the one used by MSVC.
148 2         6 $file =~ s@/@\\@g;
149              
150             # And use path even for the files in this directory.
151 2 50       4 $file = ".\\$file" if $file !~ /\\/;
152              
153 2         7 print $out <
154             \t\t\t
155             \t\t\t\tRelativePath="$file"$angle_bracket
156             \t\t\t
157             END
158             ;
159              
160 2         5 $changed = 1;
161             }
162             }
163              
164 2         2 undef $filter;
165             }
166             }
167              
168 29         71 print $out $line_with_eol;
169             }
170              
171             $changed
172 1         9 }
173              
174             __END__