I hope this will be of use to someone. (At least it already helped find some bugs in tm.texi.)
Usage: Run the script on a mainline GCC source directory. You'll get two comment-only files, MACHINE.c and MACHINE.h. When you've converted these into an actual working port, please eliminate *ALL* of the template comments before submitting. (Quite apart from them being unwanted by the GCC team [and being butt-ugly, too], they also are a derived work from both GPL code and GFDL documentation, and as such illegal to distribute, as far as I can tell. So, just be a good boy or girl and only use them internally while developing that port.) I just threw this script together; no doubt it can be improved quite a bit. And I expect some of its analyses aren't exactly correct, probably in most cases because the script still lacks any form of comment handling, but also because my understanding of GCC still has a lot of holes. If you break the script, you get to keep all the pieces, but if you improve it, I'd like to hear about it. And here's mktmpl.pl: #! /usr/bin/perl -w ######################################################################## ## ## File: mktmpl.pl ## Author: Kai Henningsen ## Date: 2007-08-27 ## ## Contents: ## Script to create template files for MACHINE.h and MACHINE.c ## by analyzing tm.texi and source files ## ## Copyright (c) 2007 Kai Henningsen ## ## mktmpl.pl is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2, or (at your option) ## any later version. ## ## mktmpl.pl is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You can find a copy of the GPL at ## http://www.gnu.org/licenses/old-licenses/gpl-2.0.html, or write to ## the Free Software Foundation, 51 Franklin Street, Fifth Floor, ## Boston, MA 02110-1301, USA. ## ######################################################################### use strict; use Data::Dumper::Simple; $Data::Dumper::Useqq = 1; use File::Find; # call it as # mktmpl.pl gccsrcdir chdir $ARGV[0] or die "Can't chdir to src directory"; # which argument of these macros is the name? my %nampos = ( '@defmac' => 0, '@defmacx' => 0, '@deftp' => 1, '@deftypefn' => 2, '@deftypefun' => 1, '@deftypefunx' => 1, '@deftypevar' => 1, '@deftypevr' => 2, '@deftypevrx' => 2, ); # some filenames my $tm = "gcc/doc/tm.texi"; my ($mh, $mc) = qw( ../MACHINE.h ../MACHINE.c ); # ... and something that's not a name use constant none => 'not a def'; my %tmpl; # text for the templates my @seq; # the sequence of tm.texi entries, so we can preserve it # scan through tm.texi my $cur = none(); open TM, '<', $tm or die "$tm: $!"; while (<TM>) { push @{$tmpl{$cur}}, " $_" unless /[EMAIL PROTECTED]/; push @{$tmpl{$cur}}, "\n" if /[EMAIL PROTECTED] def/; $cur = none() if /[EMAIL PROTECTED] def/; /[EMAIL PROTECTED]/ or next; # ok, we have a definition line my $l = $_; # handle continuation lines while ($l =~ /[EMAIL PROTECTED]/) { $l =~ s/[EMAIL PROTECTED]//s; $l .= <TM>; } chomp $l; # split into arguments my @l = grep /\S/, split /(\s+|{[^}]*}|(?!{)\S+)/, $l; my $d = shift @l; my $n = $nampos{$d}; warn Dumper(@l) unless defined $n; die "Unknown [EMAIL PROTECTED]: «$d»" unless defined $n; my $name = $l[$n]; my $orgname = $name; # fix various problems in tm.texi as of rev. 127318 $name =~ s/^\*+//; $name =~ s/^{(.*)}$/$1/; $name = $l[$n-1] if $name eq '(void)'; $name = $l[$n+1] if $name eq 'HOST_WIDE_INT'; $name = $l[$n+1] if $name eq 'bool'; $name = $l[$n+1] if $name eq 'char'; $name = $l[$n+1] if $name eq 'int'; $name = $l[$n+1] if $name eq 'machine_mode'; $name = $l[$n+2] if $name eq '*'; $name =~ s/^\*+//; $name =~ s/^{(.*)}$/$1/; # if it's a @defqqqqx, note that the previous one is continued push @{$tmpl{$cur}}, "see $name\n\n" if $d =~ /x$/; unless ($name eq $orgname) { warn "\n", Dumper @l, $name, $orgname, $l; } # enum reg_class $name =~ s/^enum\s+//; # SDB_PUT_... $name =~ s/[EMAIL PROTECTED]/*/; die "split name: «$name»" if $name =~ /\s/; $cur = $name; push @seq, $name; push @{$tmpl{$cur}}, " $l\n"; } close TM or die "$tm: $!"; delete $tmpl{none()}; my (%def, %ifdef, %defaults, %target_def, %seen_in); # scan most of the sources find(\&wanted, '.'); # possible candidate line, with matching ids sub foundone { my ($fn, $fname, $ln, $s, $var) = @_[0..4]; @_ = @_[5..$#_]; #return unless @_; #warn Dumper @_; for my $word (@_) { if (exists $tmpl{$word}) { $$var{$word}++; $defaults{$word}++ if $fn eq 'defaults.h'; $target_def{$word}++ if $fn eq 'target-def.h'; push @{$tmpl{$word}}, "${fname}:$.:$s\n"; } $word = substr($word, 0, 8).'*'; # SDB_PUT_... if (exists $tmpl{$word}) { $$var{$word}++; $defaults{$word}++ if $fn eq 'defaults.h'; $target_def{$word}++ if $fn eq 'target-def.h'; push @{$tmpl{$word}}, "${fname}:$.:$s\n"; } } } # process one file sub wanted { my $fn = $_; # avois these directories if ($fn eq 'config' or $fn eq 'po' or $fn eq '.svn' or $fn eq 'testsuite') { $File::Find::prune = 1; return; } # ... and these files (and any non-files) return if $fn =~ /changelog/i or $fn =~ /\.texi$/ or not -f $fn; my $fname = $File::Find::name; $fname =~ s[^\./][]; # cosmetics # ok, read this source file open SRC, '<', $fn or die "$fname: $!"; my $s; while (defined($s = <SRC>)) { # continuation lines while ($s =~ /\\\s*$/) { $s =~ s/\\\s*$//s; $s .= <SRC>; } chomp $s; # find all identifiers FIXME: should handle comments my @words = grep /\w/, split /(\w+)/, $s; #print Dumper($., $s, @words); # ... and note where we've seen them, if they are interesting for my $word (@words) { $seen_in{$word}{$fname}++ if exists $tmpl{$word}; $word = substr($word, 0, 8).'*'; # SDB_PUT_... $seen_in{$word}{$fname}++ if exists $tmpl{$word}; } # ok, next look at all preprocessor lines that are # concerned with the existence of a macro # and note some of the more interesting ones next unless $s =~ /^\s*#/; $s =~ s/\s+/ /g; foundone $fn, $fname, $., $s, \%def, $s =~ /^\s*#\s*define\s+(\w+)\b/; foundone $fn, $fname, $., $s, {}, $s =~ /^\s*#\s*undef\s+(\w+)\b/; foundone $fn, $fname, $., $s, \%ifdef, $s =~ /^\s*#\s*ifn?def\s+(\w+)\b/; foundone $fn, $fname, $., $s, \%ifdef, $s =~ /^\s*#\s*if\b.*\bdefined\s*\(\s*(\w+)\s*\)/; } close SRC or die "$fname: $!"; } # OK, we can start to write the two files open MH, '>', $mh or die "$mh: $!"; open MC, '>', $mc or die "$mc: $!"; # pretty printer sub printcomment { my $fh = shift; my $line = join('', @_); $line =~ s[/\*][/ *]g; $line =~ s[\*/][* /]g; printf $fh "/*%-*s*/\n", 76, $line; } #print OUT Dumper %seen_in; for my $name (@seq) { my $hc = undef; my $which = undef; # lower case stuff is special if ($name =~ /[A-Z]/) { # make sure we have the right data types $seen_in{$name}{" "}++; delete $seen_in{$name}{" "}; # if we saw this name in anything but a header, # it needs to go in the .h my %si = %{$seen_in{$name}}; #delete $si{'gcc/target-def.h'}; my @si = grep !/\.h$/, keys %si; #printcomment Dumper(@si); $hc = ((scalar @si)? *MH: *MC); $which = ((scalar @si)? 'h': 'c'); } else { if ($name eq 'targetm') { $hc = *MC; $which = 'c'; } elsif ($name eq 'reg_class') { $hc = *MH; $which = 'h'; } else { $hc = *MC; $which = 'e'; } } printcomment $hc, '-' x 76; printcomment $hc, $name, " (", keys %{$seen_in{$name}}? ($which eq 'c'? "belongs in MACHINE.c": $which eq 'h'? "belongs in MACHINE.h": "defined elsewhere"): "completely unused", ")"; if ($name =~ /[A-Z]/) { printcomment $hc, $ifdef{$name}? "may be un": "must be ", "defined"; printcomment $hc, $def{$name}? "has": "no", " default"; } printcomment $hc, "defaults.h" if $defaults{$name}; printcomment $hc, "target-def.h" if $target_def{$name}; printcomment $hc, "Seen in ", join(' ', sort keys %{$seen_in{$name}}); for my $line (split(/\n/, join('', @{$tmpl{$name}}))) { printcomment $hc, $line; } printcomment $hc, '-' x 76; print $hc "\n"; } close MH or die "$mh: $!"; close MC or die "$mc: $!";