| 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 | use Exporter qw(import); | |||
| 5 | our @EXPORT = qw(update_makefile); | ||||
| 6 | |||||
| 7 | 12 12 12 | use strict; | |||
| 8 | 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 | 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 | 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 | 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 | if (defined $var) { | |||
| 120 | 204 | if ($line =~ /^(?<indent>\s*)(?<file>[^ ]+)(?<tail>\s*\\?)$/) { | |||
| 121 | 135 | 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 | 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 | $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 | if (defined (my $file_ext = _get_ext($file))) { | |||
| 141 | 132 | if (defined $make_ext) { | |||
| 142 | 63 | 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 | if ($file_ext ne $src_ext) { | |||
| 155 | 51 | (my $file_try = $file) =~ s/\Q$file_ext\E$/$src_ext/; | |||
| 156 | 51 | if (exists $files{$file_try}) { | |||
| 157 | 21 | $file = $file_try; | |||
| 158 | last | ||||
| 159 | 21 | } | |||
| 160 | } | ||||
| 161 | } | ||||
| 162 | |||||
| 163 | 132 | 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 | if (exists $files{$file}) { | |||
| 173 | 90 | 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 | 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 | 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 | if (defined $make_ext) { | |||
| 203 | 69 | 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 | 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 | if (!$new_files) { | |||
| 219 | 36 | $new_files = 1; | |||
| 220 | |||||
| 221 | 36 | if (@values && $values[-1] !~ /\\$/) { | |||
| 222 | 12 | $values[-1] .= $tail; | |||
| 223 | } | ||||
| 224 | } | ||||
| 225 | |||||
| 226 | # Next give it the right extension. | ||||
| 227 | 42 | 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 | if ($new_files) { | |||
| 236 | 36 | $changed = 1; | |||
| 237 | |||||
| 238 | # Sort them if necessary using the usual Schwartzian transform. | ||||
| 239 | 36 | 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 | 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 | 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 | my $var_if_exists = sub { exists $vars->{$_[0]} ? $_[0] : undef }; | |||
| 271 | |||||
| 272 | 54 | 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 | $var = $var_if_exists->($1) || $var_if_exists->("$1_sources"); | |||
| 285 | 48 | if (!defined $var && $2 eq 'SOURCES' && $1 =~ /^(\w+)_l?a$/) { | |||
| 286 | 9 | $var = $var_if_exists->($1) || $var_if_exists->("$1_sources"); | |||
| 287 | 9 | if (!defined $var && $1 =~ /^lib(\w+)$/) { | |||
| 288 | 3 | $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 | if (defined $var) { | |||
| 299 | 72 | 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 | 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 | last if $eof; | |||
| 331 | |||||
| 332 | 144 | print $out "\n"; | |||
| 333 | } | ||||
| 334 | |||||
| 335 | $changed | ||||
| 336 | 60 | } | |||