| File: | config/auto/pmc.pm |
| Coverage: | 93.6% |
| line | stmt | bran | cond | sub | code |
|---|---|---|---|---|---|
| 1 | # Copyright (C) 2001-2011, Parrot Foundation. | ||||
| 2 | |||||
| 3 - 11 | =head1 NAME config/auto/pmc.pm - PMC Files =head1 DESCRIPTION Prepare PMC files for inclusion. =cut | ||||
| 12 | |||||
| 13 | package auto::pmc; | ||||
| 14 | |||||
| 15 | 3 3 3 | use strict; | |||
| 16 | 3 3 3 | use warnings; | |||
| 17 | |||||
| 18 | 3 3 3 | use base qw(Parrot::Configure::Step); | |||
| 19 | |||||
| 20 | 3 3 3 | use File::Basename qw/basename/; | |||
| 21 | 3 3 3 | use File::Spec::Functions qw/catfile/; | |||
| 22 | |||||
| 23 | 3 3 3 | use Parrot::Configure::Utils ':auto'; | |||
| 24 | |||||
| 25 | sub _init { | ||||
| 26 | 3 | my $self = shift; | |||
| 27 | return { | ||||
| 28 | 282 | description => 'Which pmc files should be compiled in', | |||
| 29 | result => '', | ||||
| 30 | PMC_PARENTS => {}, | ||||
| 31 | 3 | srcpmc => [ sort map { basename($_) } glob "./src/pmc/*.pmc" ], | |||
| 32 | }; | ||||
| 33 | } | ||||
| 34 | |||||
| 35 | sub runstep { | ||||
| 36 | 2 | my ( $self, $conf ) = @_; | |||
| 37 | |||||
| 38 | # $pmc_list is a string holding a space-delimited list of currently active | ||||
| 39 | # PMCs, sorted (largely) on the basis of src/pmc/pmc.num. | ||||
| 40 | # (By 'current', we take into account the fact that there are PMCs listed | ||||
| 41 | # in src/pmc/pmc.num that no longer exist but whose index numbers are | ||||
| 42 | # never deleted.) | ||||
| 43 | 2 2 | my $pmc_list = get_sorted_pmc_str( @{ $self->{srcpmc} } ); | |||
| 44 | |||||
| 45 | # names of class files for src/pmc/Makefile | ||||
| 46 | 2 | ( my $TEMP_pmc_o = $pmc_list ) =~ s/\.pmc/\$(O)/g; | |||
| 47 | 2 | ( my $TEMP_pmc_str = $pmc_list ) =~ s/\.pmc/\.str/g; | |||
| 48 | |||||
| 49 | # calls to pmc2c.pl for src/pmc/Makefile | ||||
| 50 | 2 | my $TEMP_pmc_build = <<"E_NOTE"; | |||
| 51 | |||||
| 52 | # the following part of the Makefile was built by 'config/auto/pmc.pm' | ||||
| 53 | |||||
| 54 | E_NOTE | ||||
| 55 | |||||
| 56 | 2 | $TEMP_pmc_build .= <<END; | |||
| 57 | PMC2C_FILES = \\ | ||||
| 58 | lib/Parrot/Pmc2c/Pmc2cMain.pm \\ | ||||
| 59 | lib/Parrot/Pmc2c/Parser.pm \\ | ||||
| 60 | lib/Parrot/Pmc2c/Dumper.pm \\ | ||||
| 61 | lib/Parrot/Pmc2c/PMC.pm \\ | ||||
| 62 | lib/Parrot/Pmc2c/Method.pm \\ | ||||
| 63 | lib/Parrot/Pmc2c/PCCMETHOD.pm \\ | ||||
| 64 | lib/Parrot/Pmc2c/MULTI.pm \\ | ||||
| 65 | lib/Parrot/Pmc2c/Library.pm \\ | ||||
| 66 | lib/Parrot/Pmc2c/UtilFunctions.pm \\ | ||||
| 67 | lib/Parrot/Pmc2c/PMC/default.pm \\ | ||||
| 68 | lib/Parrot/Pmc2c/PMC/Null.pm \\ | ||||
| 69 | lib/Parrot/Pmc2c/PMC/RO.pm | ||||
| 70 | END | ||||
| 71 | |||||
| 72 | 2 | my %universal_deps; | |||
| 73 | 2 | while (<DATA>) { | |||
| 74 | 118 | next if /^#/; | |||
| 75 | 106 | next if /^\s*$/; | |||
| 76 | 104 | chomp; | |||
| 77 | 104 | $universal_deps{$_} = 1; | |||
| 78 | } | ||||
| 79 | |||||
| 80 | 2 | for my $pmc ( split( /\s+/, $pmc_list ) ) { | |||
| 81 | 188 | $pmc =~ s/\.pmc$//; | |||
| 82 | |||||
| 83 | # make each pmc depend upon its parent. | ||||
| 84 | 188 | my $parent_dumps = ''; | |||
| 85 | $parent_dumps .= "src/pmc/$_.dump " | ||||
| 86 | 188 | foreach reverse( ( $self->pmc_parents($pmc) ) ); | |||
| 87 | 188 | my $parent_headers = ''; | |||
| 88 | $parent_headers .= "include/pmc/pmc_$_.h " | ||||
| 89 | 188 | for $self->pmc_parents($pmc); | |||
| 90 | |||||
| 91 | # add dependencies that result from METHOD usage. | ||||
| 92 | 188 | my $pmc_fname = catfile('src', 'pmc', "$pmc.pmc"); | |||
| 93 | 188 | my $pccmethod_depend = ''; | |||
| 94 | 188 | my %o_deps = %universal_deps; | |||
| 95 | 188 | $o_deps{"src/pmc/$pmc.c"} = 1; | |||
| 96 | 188 | $o_deps{"src/pmc/$pmc.str"} = 1; | |||
| 97 | 188 | $o_deps{"include/pmc/pmc_$pmc.h"} = 1; | |||
| 98 | |||||
| 99 | 188 | if (contains_pccmethod($pmc_fname)) { | |||
| 100 | 124 | $pccmethod_depend = 'lib/Parrot/Pmc2c/PCCMETHOD.pm'; | |||
| 101 | 124 | $o_deps{"include/pmc/pmc_fixedintegerarray.h"} = 1; | |||
| 102 | 124 | if ($pmc ne 'fixedintegerarray') { | |||
| 103 | 122 | $pccmethod_depend .= ' include/pmc/pmc_fixedintegerarray.h'; | |||
| 104 | } | ||||
| 105 | } | ||||
| 106 | |||||
| 107 | 188 | my $include_headers = get_includes($pmc_fname); | |||
| 108 | 188 | my $cc_shared = $conf->data->get('cc_shared'); | |||
| 109 | 188 | my $cc_o_out = $conf->data->get('cc_o_out'); | |||
| 110 | 188 | my $warnings = $conf->data->get('ccwarn'); | |||
| 111 | 188 | my $optimize = $conf->data->get('optimize'); | |||
| 112 | |||||
| 113 | 188 | foreach my $header (split ' ', $parent_headers) { | |||
| 114 | 262 | $o_deps{$header} = 1; | |||
| 115 | } | ||||
| 116 | 188 | foreach my $header (split ' ', $include_headers) { | |||
| 117 | 140 | $o_deps{$header} = 1; | |||
| 118 | } | ||||
| 119 | |||||
| 120 | # includes of includes | ||||
| 121 | # (cheat. The right way to handle this is to do what | ||||
| 122 | # checkdepend.t does.) | ||||
| 123 | 188 | if (exists $o_deps{'include/parrot/oplib/core_ops.h'} ) { | |||
| 124 | 4 | $o_deps{'include/parrot/runcore_api.h'} = 1; | |||
| 125 | } | ||||
| 126 | |||||
| 127 | 188 | my $o_deps = " " . join(" \\\n ", keys %o_deps); | |||
| 128 | 188 | $TEMP_pmc_build .= <<END | |||
| 129 | src/pmc/$pmc.c : src/pmc/$pmc.dump | ||||
| 130 | \t\$(PMC2CC) src/pmc/$pmc.pmc | ||||
| 131 | |||||
| 132 | src/pmc/$pmc.dump : vtable.dump $parent_dumps src/pmc/$pmc.pmc \$(PMC2C_FILES) $pccmethod_depend | ||||
| 133 | \t\$(PMC2CD) src/pmc/$pmc.pmc | ||||
| 134 | |||||
| 135 | include/pmc/pmc_$pmc.h: src/pmc/$pmc.c | ||||
| 136 | |||||
| 137 | ## SUFFIX OVERRIDE -Warnings | ||||
| 138 | src/pmc/$pmc\$(O): \\ | ||||
| 139 | $o_deps | ||||
| 140 | \t\$(CC) \$(CFLAGS) $optimize $cc_shared $warnings -I\$(\@D) $cc_o_out\$@ -c src/pmc/$pmc.c | ||||
| 141 | |||||
| 142 | END | ||||
| 143 | } | ||||
| 144 | |||||
| 145 | |||||
| 146 | # build list of libraries for link line in Makefile | ||||
| 147 | 2 | ( my $TEMP_pmc_classes_o = $TEMP_pmc_o ) =~ s{^| }{ src/pmc/}g; | |||
| 148 | 2 | ( my $TEMP_pmc_classes_str = $TEMP_pmc_str ) =~ s{^| }{ src/pmc/}g; | |||
| 149 | 2 | ( my $TEMP_pmc_classes_pmc = $pmc_list ) =~ s{^| }{ src/pmc/}g; | |||
| 150 | |||||
| 151 | # Gather the actual names (with MixedCase) of all of the non-abstract | ||||
| 152 | # built-in PMCs in rough hierarchical order. | ||||
| 153 | 2 | my %parents; | |||
| 154 | |||||
| 155 | 2 | PMC: for my $pmc_file ( split( /\s+/, $pmc_list ) ) { | |||
| 156 | |||||
| 157 | 188 | open my $PMC, "<", "src/pmc/$pmc_file" | |||
| 158 | or die "open src/pmc/$pmc_file: $!"; | ||||
| 159 | |||||
| 160 | 188 | my ($const, $name); | |||
| 161 | |||||
| 162 | 188 | while (<$PMC>) { | |||
| 163 | 20966 | if (/^pmclass (\w+)(.*)/) { | |||
| 164 | 188 | $name = $1; | |||
| 165 | 188 | my $decl = $2; | |||
| 166 | 188 | $decl .= <$PMC> until $decl =~ s/\{.*//; | |||
| 167 | |||||
| 168 | 188 | $const = 1 if $decl =~ /\bconst_too\b/; | |||
| 169 | 188 | next PMC if $decl =~ /\bextension\b/; | |||
| 170 | |||||
| 171 | # the default PMC gets handled specially | ||||
| 172 | 188 | last if $name eq 'default'; | |||
| 173 | |||||
| 174 | 186 | my $parent = 'default'; | |||
| 175 | |||||
| 176 | 186 | if ($decl =~ /extends (\w+)/) { | |||
| 177 | 68 | $parent = $1; | |||
| 178 | } | ||||
| 179 | |||||
| 180 | # set a marker not to initialize an abstract PMC | ||||
| 181 | 186 | if ($decl =~ /\babstract\b/) { | |||
| 182 | 0 0 | unshift @{ $parents{$name} }, '(abstract)'; | |||
| 183 | } | ||||
| 184 | |||||
| 185 | # please note that normal and Const PMCs must be in this order | ||||
| 186 | 186 186 | push @{ $parents{$parent} }, $name; | |||
| 187 | 186 0 | push @{ $parents{$parent} }, "Const$name" if $const; | |||
| 188 | |||||
| 189 | 186 | last; | |||
| 190 | } | ||||
| 191 | } | ||||
| 192 | |||||
| 193 | 188 | close $PMC; | |||
| 194 | |||||
| 195 | 188 | die "No pmclass declaration found in $pmc_file" | |||
| 196 | unless defined $name; | ||||
| 197 | } | ||||
| 198 | |||||
| 199 | 2 | my @names = ('default', $self->order_pmcs_by_hierarchy( \%parents )); | |||
| 200 | |||||
| 201 | 2 | $conf->data->set( | |||
| 202 | pmc => $pmc_list, | ||||
| 203 | pmc_names => join( ' ', @names ), | ||||
| 204 | TEMP_pmc_o => $TEMP_pmc_o, | ||||
| 205 | TEMP_pmc_build => $TEMP_pmc_build, | ||||
| 206 | TEMP_pmc_classes_o => $TEMP_pmc_classes_o, | ||||
| 207 | TEMP_pmc_classes_str => $TEMP_pmc_classes_str, | ||||
| 208 | TEMP_pmc_classes_pmc => $TEMP_pmc_classes_pmc, | ||||
| 209 | ); | ||||
| 210 | |||||
| 211 | 2 | return 1; | |||
| 212 | } | ||||
| 213 | |||||
| 214 | # Return the (lowercased) name of the immediate parent of the given | ||||
| 215 | # (lowercased) pmc name. | ||||
| 216 | sub pmc_parent { | ||||
| 217 | 524 | my ($self, $pmc) = @_; | |||
| 218 | |||||
| 219 | 524 | return $self->{PMC_PARENTS}{$pmc} if defined $self->{PMC_PARENTS}{$pmc}; | |||
| 220 | |||||
| 221 | 186 | local $/; | |||
| 222 | 186 | open( my $PMC, '<', "src/pmc/$pmc.pmc" ) | |||
| 223 | or die "open src/pmc/$pmc.pmc failed: $!"; | ||||
| 224 | 186 | local $_ = <$PMC>; | |||
| 225 | 186 | close $PMC; | |||
| 226 | |||||
| 227 | # Throw out everything but the pmclass declaration | ||||
| 228 | 186 | s/^.*?pmclass//s; | |||
| 229 | 186 | s/\{.*$//s; | |||
| 230 | |||||
| 231 | 186 | return $self->{PMC_PARENTS}{$pmc} = lc($1) if m/extends\s+(\w+)/; | |||
| 232 | 118 | return $self->{PMC_PARENTS}{$pmc} = 'default'; | |||
| 233 | } | ||||
| 234 | |||||
| 235 | # Return an array of all | ||||
| 236 | sub pmc_parents { | ||||
| 237 | 376 | my ($self, $pmc) = @_; | |||
| 238 | |||||
| 239 | 376 | my @parents = ($pmc); | |||
| 240 | 376 | push @parents, $self->pmc_parent( $parents[-1] ) | |||
| 241 | until $parents[-1] eq 'default'; | ||||
| 242 | |||||
| 243 | 376 | shift @parents; | |||
| 244 | 376 | return @parents; | |||
| 245 | } | ||||
| 246 | |||||
| 247 | # Internal sub get_pmc_order parses src/pmc/pmc.num. The hash it builds | ||||
| 248 | # includes both active and deactivated PMCs. | ||||
| 249 | sub get_pmc_order { | ||||
| 250 | 7 | open my $IN, '<', 'src/pmc/pmc.num' or die "Can't read src/pmc/pmc.num"; | |||
| 251 | 6 | my %order; | |||
| 252 | 6 | while (<$IN>) { | |||
| 253 | 186 | next unless (/^(\w+\.\w+)\s+(\d+)$/); | |||
| 254 | 118 | $order{$1} = $2; | |||
| 255 | } | ||||
| 256 | 6 | close $IN; | |||
| 257 | 6 | return \%order; | |||
| 258 | } | ||||
| 259 | |||||
| 260 | sub get_sorted_pmc_str { | ||||
| 261 | 4 | my @pmcs = @_; | |||
| 262 | 4 | my $pmc_order = get_pmc_order(); | |||
| 263 | 4 | my $n = keys %$pmc_order; | |||
| 264 | 4 | my @sorted_pmcs; | |||
| 265 | |||||
| 266 | 4 | for my $pmc (@pmcs) { | |||
| 267 | 196 | if ( exists $pmc_order->{$pmc} ) { | |||
| 268 | 68 | $sorted_pmcs[ $pmc_order->{$pmc} ] = $pmc; | |||
| 269 | } | ||||
| 270 | else { | ||||
| 271 | 128 | $sorted_pmcs[ $n++ ] = $pmc; | |||
| 272 | } | ||||
| 273 | } | ||||
| 274 | |||||
| 275 | # With the test for definedness below, we account for PMCs which have been | ||||
| 276 | # deactivated but whose index numbers remain in src/pmc/pmc.num. | ||||
| 277 | 4 240 | my $active_pmcs = [ grep { defined $_ } @sorted_pmcs ]; | |||
| 278 | |||||
| 279 | # At this point we check to see whether any active_pmcs are missing from | ||||
| 280 | # the MANIFEST. We warn about any such missing PMCs but (for the time | ||||
| 281 | # being at least) we proceed to compose $pmc_str. | ||||
| 282 | 4 | my $seen_manifest = pmcs_in_manifest(); | |||
| 283 | 4 | check_pmcs_against_manifest( $active_pmcs, $seen_manifest ); | |||
| 284 | 4 4 | return join(' ' => @{ $active_pmcs }); | |||
| 285 | } | ||||
| 286 | |||||
| 287 | sub pmcs_in_manifest { | ||||
| 288 | 6 | my $manifest = shift || 'MANIFEST'; | |||
| 289 | 6 | my %seen_manifest = (); | |||
| 290 | 6 | open my $MAN, '<', $manifest | |||
| 291 | or die "Unable to open MANIFEST: $!"; | ||||
| 292 | 6 | while (my $f = <$MAN>) { | |||
| 293 | 6641 | chomp $f; | |||
| 294 | 6641 | if ($f =~ m{^src/pmc/(.*\.pmc)}) { | |||
| 295 | 293 | my $pmc = $1; | |||
| 296 | 293 | $seen_manifest{$pmc}++; | |||
| 297 | } | ||||
| 298 | } | ||||
| 299 | 6 | close $MAN or die "Unable to close MANIFEST: $!"; | |||
| 300 | 6 | return \%seen_manifest; | |||
| 301 | } | ||||
| 302 | |||||
| 303 | sub check_pmcs_against_manifest { | ||||
| 304 | 4 | my ($active_pmcs, $seen_manifest) = @_; | |||
| 305 | 196 4 | my @missing_from_manifest = grep { ! exists $seen_manifest->{$_} } | |||
| 306 | 4 | @{ $active_pmcs }; | |||
| 307 | 4 | if (@missing_from_manifest) { | |||
| 308 | 1 | warn "PMCs found in /src/pmc not found in MANIFEST: @missing_from_manifest"; | |||
| 309 | } | ||||
| 310 | } | ||||
| 311 | |||||
| 312 | sub contains_pccmethod { | ||||
| 313 | 191 | my $file = shift; | |||
| 314 | 191 | open( my $fh, '<', $file ) or die "Can't read '$file': $!\n"; | |||
| 315 | |||||
| 316 | 190 | local $_; | |||
| 317 | 190 | while (<$fh>) { | |||
| 318 | 84362 | next unless /\bMETHOD\b/; | |||
| 319 | 125 | return 1; | |||
| 320 | } | ||||
| 321 | |||||
| 322 | 65 | return; | |||
| 323 | } | ||||
| 324 | |||||
| 325 | # Given a PMC file name, get a list of all the includes it specifies | ||||
| 326 | sub get_includes { | ||||
| 327 | 188 | my $file = shift; | |||
| 328 | 188 | open( my $fh, '<', $file ) or die "Can't read '$file': $!\n"; | |||
| 329 | |||||
| 330 | 188 | my @retval; | |||
| 331 | 188 | local $_; | |||
| 332 | 188 | while (<$fh>) { | |||
| 333 | 107864 | next unless /^\s*# *include\s+"(.*)"\s+$/; | |||
| 334 | 150 | my $include = $1; | |||
| 335 | 150 | if ($include =~ m{^parrot}) { # main parrot include dir | |||
| 336 | 62 | next if $include eq "parrot/parrot.h"; # already implicit everywhere. | |||
| 337 | 54 | next if $include eq "parrot/io.h"; # already implicit everywhere. | |||
| 338 | 52 | $include = "include/" . $include; | |||
| 339 | } elsif ($include =~ m/^pmc_|\.str$/) { # local pmc header | ||||
| 340 | 2 | $include = "src/pmc/" . $include; | |||
| 341 | } elsif ($include =~ m/^pmc\/pmc_/) { # local pmc header | ||||
| 342 | 72 | $include = "include/" . $include; | |||
| 343 | } elsif ($include =~ m/^imcc/) { # IMCC header. | ||||
| 344 | 4 | $include = "include/" . $include; | |||
| 345 | } elsif ($include =~ m{^\.\./}) { # relative to include/ dir... | ||||
| 346 | 10 | $include =~ s{^\.\./}{}; | |||
| 347 | } | ||||
| 348 | 140 | push @retval, $include; | |||
| 349 | } | ||||
| 350 | |||||
| 351 | 188 | return join(' ', @retval); | |||
| 352 | } | ||||
| 353 | |||||
| 354 | sub order_pmcs_by_hierarchy { | ||||
| 355 | 2 | my ($self, $parents) = @_; | |||
| 356 | |||||
| 357 | 2 | return $self->get_kids_for_parent( $parents, 'default' ); | |||
| 358 | } | ||||
| 359 | |||||
| 360 | sub get_kids_for_parent { | ||||
| 361 | 188 | my ($self, $parents, $parent) = @_; | |||
| 362 | |||||
| 363 | 188 | my @kids; | |||
| 364 | |||||
| 365 | 188 188 | for my $kid (@{ $parents->{$parent} }) { | |||
| 366 | # skip abstract PMCs | ||||
| 367 | 186 | next if $kid eq '(abstract)'; | |||
| 368 | 186 | push @kids, $kid unless exists $parents->{$kid} | |||
| 369 | && $parents->{$kid}[0] eq '(abstract)'; | ||||
| 370 | |||||
| 371 | # and avoid infinite loops | ||||
| 372 | 186 | next if $kid eq $parent; | |||
| 373 | 186 | push @kids, $self->get_kids_for_parent($parents, $kid); | |||
| 374 | } | ||||
| 375 | |||||
| 376 | 188 | return @kids; | |||
| 377 | } | ||||
| 378 | |||||
| 379 | 1; | ||||
| 380 | |||||