File Coverage: lib/Makefile/Update/Makefile.pm

# File Coverage

File: | lib/Makefile/Update/Makefile.pm
Coverage: | 91.1%

line | stmt | bran | cond | sub | code
--- | --- | --- | --- | --- | ---
1 |  |  |  |  | package Makefile::Update::Makefile;
2 |  |  |  |  | # ABSTRACT: Update lists of files in makefile variables.
3 |
4 | 12 12 12 |  |  | 12 | use Exporter qw(import);
5 |  |  |  |  | our @EXPORT = qw(update_makefile);
6 |
7 | 12 12 12 |  |  | 12 | use strict;
8 | 12 12 12 |  |  | 12 | use warnings;
9 |
10 |  |  |  |  | # VERSION
11 |
12 - 24 |  |
    =head1 SYNOPSIS

    This can be used to update the contents of a variable containing a list of
    files in a makefile.

        use Makefile::Update::Makefile;
        Makefile::Update::upmake('GNUmakefile', \&update_makefile, $vars);

    =head1 SEE ALSO

    Makefile::Update

    =cut
25 |
26 |  |  |  |  | =func update_makefile
27 |
28 |  |  |  |  | Update variable definitions in a makefile format with the data from the hash
29 |  |  |  |  | ref containing all the file lists.
30 |
31 |  |  |  |  | Only most straightforward cases of variable or target definitions are
32 |  |  |  |  | recognized here, i.e. just "var := value", "var = value" or "target: value".
33 |  |  |  |  | In particular we don't support any GNU make extensions such as "export" or
34 |  |  |  |  | "override" without speaking of anything more complex.
35 |
36 |  |  |  |  | On top of it, currently the value should contain a single file per line with
37 |  |  |  |  | none at all on the first line (but this restriction could be relaxed later if
38 |  |  |  |  | needed), i.e. the only supported case is
39 |
40 |  |  |  |  | var = \
41 |  |  |  |  | foo \
42 |  |  |  |  | bar \
43 |  |  |  |  | baz
44 |
45 |  |  |  |  | and it must be followed by an empty line, too.
46 |
47 |  |  |  |  | Notice that if any of the "files" in the variable value looks like a makefile
48 |  |  |  |  | variable, i.e. has "$(foo)" form, it is ignored by this function, i.e. not
49 |  |  |  |  | removed even if it doesn't appear in the list of files (which will never be
50 |  |  |  |  | the case normally).
51 |
52 |  |  |  |  | Takes the (open) file handles of the files to read and to write and the file
53 |  |  |  |  | lists hash ref as arguments.
54 |
55 |  |  |  |  | Returns 1 if any changes were made.
56 |  |  |  |  | =cut
57 |
58 |  |  |  |  | sub update_makefile
59 |  |  |  |  | {
60 | 60 |  |  | 60 | my ($in, $out, $vars) = @_;
61 |
62 |  |  |  |  | # Variable whose contents is being currently replaced and its original
63 |  |  |  |  | # name in the makefile.
64 | 60 |  |  |  | my ($var, $makevar);
65 |
66 |  |  |  |  | # Hash with files defined for the specified variable as keys and 0 or 1
67 |  |  |  |  | # depending on whether we have seen them in the input file as values.
68 | 0 |  |  |  | my %files;
69 |
70 |  |  |  |  | # Array of lines in the existing makefile.
71 | 0 |  |  |  | my @values;
72 |
73 |  |  |  |  | # True if the values are in alphabetical order: we use this to add new
74 |  |  |  |  | # entries in alphabetical order too if the existing ones use it, otherwise
75 |  |  |  |  | # we just append them at the end.
76 | 60 |  |  |  | my $sorted = 1;
77 |
78 |  |  |  |  | # Extensions of the files in the files list (they're keys of this hash,
79 |  |  |  |  | # the values are not used), there can be more than one (e.g. ".c" and
80 |  |  |  |  | # ".cpp").
81 | 60 |  |  |  | my %src_exts;
82 |
83 |  |  |  |  | # Extension of the files in the makefiles: here there can also be more
84 |  |  |  |  | # than one, but in this case we just give up and don't perform any
85 |  |  |  |  | # extensions translation because we don't have enough information to do it
86 |  |  |  |  | # (e.g. which extension should be used for the new files in the makefile?).
87 |  |  |  |  | # Such case is indicated by make_ext being empty (as opposed to its
88 |  |  |  |  | # initial undefined value).
89 |  |  |  |  | my $make_ext;
90 |
91 |  |  |  |  | # Helper to get the extension. Note that the "extension" may be a make
92 |  |  |  |  | # variable, e.g. the file could be something like "foo.$(obj)", so don't
93 |  |  |  |  | # restrict it to just word characters.
94 | 264 | 100 |  | 264 | sub _get_ext { $_[0] =~ /(\.\S+)$/ ? $1 : undef }
95 |
96 |  |  |  |  | # Indent and the part after the value (typically some amount of spaces and
97 |  |  |  |  | # a backslash) for normal lines and, separately, for the last one, as it
98 |  |  |  |  | # may or not have backslash after it.
99 | 60 |  |  |  | my ($indent, $tail, $last_tail);
100 |
101 |  |  |  |  | # We can't use the usual check for EOF inside while itself because this
102 |  |  |  |  | # wouldn't work for files with no new line after the last line, so check
103 |  |  |  |  | # for the EOF manually.
104 | 60 |  |  |  | my $eof = 0;
105 |
106 |  |  |  |  | # Set to 1 if we made any changes.
107 | 60 |  |  |  | my $changed = 0;
108 | 60 |  |  |  | while (1) {
109 | 339 |  |  |  | my $line = <$in>;
110 | 339 | 100 |  |  | if (defined $line) {
111 | 279 |  |  |  | chomp $line;
112 |  |  |  |  | } else {
113 | 60 |  |  |  | $line = '';
114 | 60 |  |  |  | $eof = 1;
115 |  |  |  |  | }
116 |
117 |  |  |  |  | # If we're inside the variable definition, parse the current line as
118 |  |  |  |  | # another file name,
119 | 339 | 100 |  |  | if (defined $var) {
120 | 204 | 100 |  |  | if ($line =~ /^(?<indent>\s*)(?<file>[^ ]+)(?<tail>\s*\\?)$/) {
121 | 135 | 100 |  |  | if (defined $indent) {
122 |  |  |  |  | warn qq{Inconsistent indent at line $. in the } .
123 |  |  |  |  | qq{definition of the variable "$makevar".\n}
124 | 12 12 12 66 | 100 |  | 12 | if $+{indent} ne $indent;
125 |  |  |  |  | } else {
126 | 69 |  |  |  | $indent = $+{indent};
127 |  |  |  |  | }
128 |
129 | 135 |  |  |  | $last_tail = $+{tail};
130 | 135 |  |  |  | my $file_orig = $+{file};
131 |
132 | 135 | 100 |  |  | $tail = $last_tail if !defined $tail;
133 |
134 |  |  |  |  | # Check if we have something with the correct extension and
135 |  |  |  |  | # preserve unchanged all the rest -- we don't want to remove
136 |  |  |  |  | # expansions of other makefile variables from this one, for
137 |  |  |  |  | # example, but such expansions would never be in the files
138 |  |  |  |  | # list as they don't make sense for the other formats.
139 | 135 |  |  |  | my $file = $file_orig;
140 | 135 | 100 |  |  | if (defined (my $file_ext = _get_ext($file))) {
141 | 132 | 100 |  |  | if (defined $make_ext) {
142 | 63 | 50 |  |  | if ($file_ext ne $make_ext) {
143 |  |  |  |  | # As explained in the comment before make_ext
144 |  |  |  |  | # definition, just don't do anything in this case.
145 | 0 |  |  |  | $make_ext = '';
146 |  |  |  |  | }
147 |  |  |  |  | } else {
148 | 69 |  |  |  | $make_ext = $file_ext;
149 |  |  |  |  | }
150 |
151 |  |  |  |  | # We need to try this file with all of the source
152 |  |  |  |  | # extensions we have as it can correspond to any of them.
153 | 132 |  |  |  | for my $src_ext (keys %src_exts) {
154 | 147 | 100 |  |  | if ($file_ext ne $src_ext) {
155 | 51 |  |  |  | (my $file_try = $file) =~ s/\Q$file_ext\E$/$src_ext/;
156 | 51 | 100 |  |  | if (exists $files{$file_try}) {
157 | 21 |  |  |  | $file = $file_try;
158 |  |  |  |  | last
159 | 21 |  |  |  | }
160 |  |  |  |  | }
161 |  |  |  |  | }
162 |
163 | 132 | 100 |  |  | if (!exists $files{$file}) {
164 |  |  |  |  | # This file was removed.
165 | 42 |  |  |  | $changed = 1;
166 |
167 |  |  |  |  | # Don't store this line in @values below.
168 | 42 |  |  |  | next;
169 |  |  |  |  | }
170 |  |  |  |  | }
171 |
172 | 93 | 100 |  |  | if (exists $files{$file}) {
173 | 90 | 100 |  |  | if ($files{$file}) {
174 | 3 |  |  |  | warn qq{Duplicate file "$file" in the definition of the } .
175 |  |  |  |  | qq{variable "$makevar" at line $.\n}
176 |  |  |  |  | } else {
177 | 87 |  |  |  | $files{$file} = 1;
178 |  |  |  |  | }
179 |  |  |  |  | }
180 |
181 |  |  |  |  | # Are we still sorted?
182 | 93 | 100 | 100 |  | if (@values && lc $line lt $values[-1]) {
183 | 9 |  |  |  | $sorted = 0;
184 |  |  |  |  | }
185 |
186 | 93 |  |  |  | push @values, $line;
187 | 93 |  |  |  | next;
188 |  |  |  |  | }
189 |
190 |  |  |  |  | # If the last line had a continuation character, the file list
191 |  |  |  |  | # should only end if there is nothing else on the following line.
192 | 69 | 100 | 100 |  | if ($last_tail =~ /\\$/ && $line =~ /\S/) {
193 | 3 |  |  |  | warn qq{Expected blank line at line $..\n};
194 |  |  |  |  | }
195 |
196 |  |  |  |  | # End of variable definition, add new lines.
197 |
198 |  |  |  |  | # We can only map the extensions if we have a single extension to
199 |  |  |  |  | # map them to (i.e. make_ext is not empty) and we only need to do
200 |  |  |  |  | # it if are using more than one extension in the source files list
201 |  |  |  |  | # or the single extension that we use is different from make_ext.
202 | 69 | 50 |  |  | if (defined $make_ext) {
203 | 69 | 50 | 50 |  | if ($make_ext eq '' ||
204 |  |  |  |  | (keys %src_exts == 1 && exists $src_exts{$make_ext})) {
205 | 57 |  |  |  | undef $make_ext
206 |  |  |  |  | }
207 |  |  |  |  | }
208 |
209 | 69 |  |  |  | my $new_files = 0;
210 | 69 |  |  |  | while (my ($file, $seen) = each(%files)) {
211 | 129 | 100 |  |  | next if $seen;
212 |
213 |  |  |  |  | # This file was wasn't present in the input, add it.
214 |
215 |  |  |  |  | # If this is the first file we add, ensure that the last line
216 |  |  |  |  | # present in the makefile so far has the line continuation
217 |  |  |  |  | # character at the end as this might not have been the case.
218 | 42 | 100 |  |  | if (!$new_files) {
219 | 36 |  |  |  | $new_files = 1;
220 |
221 | 36 | 100 | 100 |  | if (@values && $values[-1] !~ /\\$/) {
222 | 12 |  |  |  | $values[-1] .= $tail;
223 |  |  |  |  | }
224 |  |  |  |  | }
225 |
226 |  |  |  |  | # Next give it the right extension.
227 | 42 | 100 |  |  | if (defined $make_ext) {
228 | 9 |  |  |  | $file =~ s/\.\S+$/$make_ext/
229 |  |  |  |  | }
230 |
231 |  |  |  |  | # Finally store it.
232 | 42 |  |  |  | push @values, "$indent$file$tail";
233 |  |  |  |  | }
234 |
235 | 69 | 100 |  |  | if ($new_files) {
236 | 36 |  |  |  | $changed = 1;
237 |
238 |  |  |  |  | # Sort them if necessary using the usual Schwartzian transform.
239 | 36 | 50 |  |  | if ($sorted) {
240 | 78 |  |  |  | @values = map { $_->[0] }
241 | 72 |  |  |  | sort { $a->[1] cmp $b->[1] }
242 | 36 78 |  |  |  | map { [$_, lc $_] } @values;
243 |  |  |  |  | }
244 |
245 |  |  |  |  | # Fix up the tail of the last line to be the same as that of
246 |  |  |  |  | # the previous last line.
247 | 36 |  |  |  | $values[-1] =~ s/\s*\\$/$last_tail/;
248 |  |  |  |  | }
249 |
250 | 69 |  |  |  | undef $var;
251 |
252 | 69 |  |  |  | print $out join("\n", @values), "\n";
253 |  |  |  |  | }
254 |
255 |  |  |  |  | # We're only interested in variable or target declarations, and does
256 |  |  |  |  | # not look like target-specific variable (this would contain an equal
257 |  |  |  |  | # sign after the target).
258 | 204 | 100 |  |  | if ($line =~ /^\s*(?<var>\S+)\s*(?::?=|:)(?<tail>[^=]*)$/) {
259 | 72 |  |  |  | $makevar = $+{var};
260 | 72 |  |  |  | my $tail = $+{tail};
261 |
262 |  |  |  |  | # And only those of them for which we have values, but this is
263 |  |  |  |  | # where it gets tricky as we try to be smart to accommodate common
264 |  |  |  |  | # use patterns with minimal effort.
265 | 72 | 100 |  |  | if (exists $vars->{$makevar}) {
266 | 18 |  |  |  | $var = $makevar;
267 |  |  |  |  | } else {
268 |  |  |  |  | # Helper: return name if a variable with such name exists or
269 |  |  |  |  | # undef otherwise.
270 | 54 81 | 100 |  | 81 | my $var_if_exists = sub { exists $vars->{$_[0]} ? $_[0] : undef };
271 |
272 | 54 | 100 100 50 | 67 |  | if ($makevar =~ /^objects$/i || $makevar =~ /^obj$/i) {
273 |  |  |  |  | # Special case: map it to "sources" as we work with the
274 |  |  |  |  | # source, not object, files.
275 | 3 |  |  |  | $var = $var_if_exists->('sources');
276 |  |  |  |  | } elsif ($makevar =~ /^(\w+)_(objects|obj|sources|src)$/i) {
277 |  |  |  |  | # Here we deal with "foo_sources" typically found in
278 |  |  |  |  | # hand-written makefiles but also "foo_SOURCES" used in
279 |  |  |  |  | # automake ones, but the latter also uses libfoo_a_SOURCES
280 |  |  |  |  | # for static libraries and libfoo_la_SOURCES for the
281 |  |  |  |  | # libtool libraries, be smart about it to allow defining
282 |  |  |  |  | # just "foo" or "foo_sources" variables usable with all
283 |  |  |  |  | # kinds of make/project files.
284 | 48 |  | 100 |  | $var = $var_if_exists->($1) || $var_if_exists->("$1_sources");
285 | 48 | 50 | 50 |  | if (!defined $var && $2 eq 'SOURCES' && $1 =~ /^(\w+)_l?a$/) {
286 | 9 |  | 67 |  | $var = $var_if_exists->($1) || $var_if_exists->("$1_sources");
287 | 9 | 100 | 67 |  | if (!defined $var && $1 =~ /^lib(\w+)$/) {
288 | 3 |  | 33 |  | $var = $var_if_exists->($1) || $var_if_exists->("$1_sources");
289 |  |  |  |  | }
290 |  |  |  |  | }
291 |  |  |  |  | } elsif ($makevar =~ /^(\w+)\$\(\w+\)/) {
292 |  |  |  |  | # This one is meant to catch relatively common makefile
293 |  |  |  |  | # constructions like "target$(exe_ext)".
294 | 3 |  |  |  | $var = $var_if_exists->($1);
295 |  |  |  |  | }
296 |  |  |  |  | }
297 |
298 | 72 | 50 |  |  | if (defined $var) {
299 | 72 | 100 |  |  | if ($tail !~ /\s*\\$/) {
300 | 3 |  |  |  | warn qq{Unsupported format for variable "$makevar" at line $..\n};
301 | 3 |  |  |  | undef $var;
302 |  |  |  |  | } else {
303 | 69 129 69 |  |  |  | %files = map { $_ => 0 } @{$vars->{$var}};
304 |
305 | 69 |  |  |  | @values = ();
306 |
307 |  |  |  |  | # Find all the extensions used by files in this variable.
308 | 69 69 |  |  |  | for my $file (@{$vars->{$var}}) {
309 | 129 | 50 |  |  | if (defined (my $src_ext = _get_ext($file))) {
310 | 129 |  |  |  | $src_exts{$src_ext} = 1;
311 |  |  |  |  | }
312 |  |  |  |  | }
313 |
314 |  |  |  |  | # Not known yet.
315 | 69 |  |  |  | undef $make_ext;
316 |
317 | 69 |  |  |  | undef $indent;
318 | 69 |  |  |  | $tail = $tail;
319 | 69 |  |  |  | undef $last_tail;
320 |
321 |  |  |  |  | # Not unsorted so far.
322 | 69 |  |  |  | $sorted = 1;
323 |  |  |  |  | }
324 |  |  |  |  | }
325 |  |  |  |  | }
326 |
327 | 204 |  |  |  | print $out "$line";
328 |
329 |  |  |  |  | # Don't add an extra new line at the EOF if it hadn't been there.
330 | 204 | 100 |  |  | last if $eof;
331 |
332 | 144 |  |  |  | print $out "\n";
333 |  |  |  |  | }
334 |
335 |  |  |  |  | $changed
336 | 60 |  |  |  | }