Home | History | Annotate | Line # | Download | only in scripts
drm-scripts-gentree.pl revision 1.1
      1  1.1  mrg #!/usr/bin/perl
      2  1.1  mrg #
      3  1.1  mrg # Original version were part of Gerd Knorr's v4l scripts.
      4  1.1  mrg #
      5  1.1  mrg # Several improvements by (c) 2005-2007 Mauro Carvalho Chehab
      6  1.1  mrg #
      7  1.1  mrg # Largely re-written (C) 2007 Trent Piepho <xyzzy (at] speakeasy.org>
      8  1.1  mrg # Stolen for DRM usage by airlied
      9  1.1  mrg #
     10  1.1  mrg # Theory of Operation
     11  1.1  mrg #
     12  1.1  mrg # This acts as a sort of mini version of cpp, which will process
     13  1.1  mrg # #if/#elif/#ifdef/etc directives to strip out code used to support
     14  1.1  mrg # multiple kernel versions or otherwise not wanted to be sent upstream to
     15  1.1  mrg # git.
     16  1.1  mrg #
     17  1.1  mrg # Conditional compilation directives fall into two catagories,
     18  1.1  mrg # "processed" and "other".  The "other" directives are ignored and simply
     19  1.1  mrg # output as they come in without changes (see 'keep' exception).  The
     20  1.1  mrg # "processed" variaty are evaluated and only the lines in the 'true' part
     21  1.1  mrg # are kept, like cpp would do.
     22  1.1  mrg #
     23  1.1  mrg # If gentree knows the result of an expression, that directive will be
     24  1.1  mrg # "processed", otherwise it will be an "other".  gentree knows the value
     25  1.1  mrg # of LINUX_VERSION_CODE, BTTV_VERSION_CODE, the KERNEL_VERSION(x,y,z)
     26  1.1  mrg # macro, numeric constants like 0 and 1, and a few defines like MM_KERNEL
     27  1.1  mrg # and STV0297_CS2.
     28  1.1  mrg #
     29  1.1  mrg # An exception is if the comment "/*KEEP*/" appears after the expression,
     30  1.1  mrg # in which case that directive will be considered an "other" and not
     31  1.1  mrg # processed, other than to remove the keep comment.
     32  1.1  mrg #
     33  1.1  mrg # Known bugs:
     34  1.1  mrg # don't specify the root directory e.g. '/' or even '////'
     35  1.1  mrg # directives continued with a back-slash will always be ignored
     36  1.1  mrg # you can't modify a source tree in-place, i.e. source dir == dest dir
     37  1.1  mrg 
     38  1.1  mrg use strict;
     39  1.1  mrg use File::Find;
     40  1.1  mrg use Fcntl ':mode';
     41  1.1  mrg 
     42  1.1  mrg my $VERSION = shift;
     43  1.1  mrg my $SRC = shift;
     44  1.1  mrg my $DESTDIR = shift;
     45  1.1  mrg 
     46  1.1  mrg if (!defined($DESTDIR)) {
     47  1.1  mrg 	print "Usage:\ngentree.pl\t<version> <source dir> <dest dir>\n\n";
     48  1.1  mrg 	exit;
     49  1.1  mrg }
     50  1.1  mrg 
     51  1.1  mrg my $BTTVCODE = KERNEL_VERSION(0,9,17);
     52  1.1  mrg my ($LINUXCODE, $extra) = kernel_version($VERSION);
     53  1.1  mrg my $DEBUG = 0;
     54  1.1  mrg 
     55  1.1  mrg my %defs = (
     56  1.1  mrg 	'LINUX_VERSION_CODE' => $LINUXCODE,
     57  1.1  mrg 	'MM_KERNEL' => ($extra =~ /-mm/)?1:0,
     58  1.1  mrg 	'DRM_ODD_MM_COMPAT' => 0,
     59  1.1  mrg 	'I915_HAVE_FENCE' => 1,
     60  1.1  mrg 	'I915_HAVE_BUFFER' => 1,
     61  1.1  mrg 	'VIA_HAVE_DMABLIT' => 1,
     62  1.1  mrg 	'VIA_HAVE_CORE_MM' => 1,
     63  1.1  mrg 	'VIA_HAVE_FENCE' => 1,
     64  1.1  mrg         'VIA_HAVE_BUFFER' => 1,
     65  1.1  mrg 	'SIS_HAVE_CORE_MM' => 1,
     66  1.1  mrg         'DRM_FULL_MM_COMPAT' => 1,   
     67  1.1  mrg 	'__linux__' => 1,
     68  1.1  mrg );
     69  1.1  mrg 
     70  1.1  mrg #################################################################
     71  1.1  mrg # helpers
     72  1.1  mrg 
     73  1.1  mrg sub kernel_version($) {
     74  1.1  mrg 	$_[0] =~ m/(\d+)\.(\d+)\.(\d+)(.*)/;
     75  1.1  mrg 	return ($1*65536 + $2*256 + $3, $4);
     76  1.1  mrg }
     77  1.1  mrg 
     78  1.1  mrg # used in eval()
     79  1.1  mrg sub KERNEL_VERSION($$$) { return $_[0]*65536 + $_[1]*256 + $_[2]; }
     80  1.1  mrg 
     81  1.1  mrg sub evalexp($) {
     82  1.1  mrg 	local $_ = shift;
     83  1.1  mrg 	s|/\*.*?\*/||go;	# delete /* */ comments
     84  1.1  mrg 	s|//.*$||o;		# delete // comments
     85  1.1  mrg 	s/\bdefined\s*\(/(/go;	# defined(foo) to (foo)
     86  1.1  mrg 	while (/\b([_A-Za-z]\w*)\b/go) {
     87  1.1  mrg 		if (exists $defs{$1}) {
     88  1.1  mrg 			my $id = $1; my $pos = $-[0];
     89  1.1  mrg 			s/$id/$defs{$id}/;
     90  1.1  mrg 			pos = $-[0];
     91  1.1  mrg 		} elsif ($1 ne 'KERNEL_VERSION') {
     92  1.1  mrg 			return(undef);
     93  1.1  mrg 		}
     94  1.1  mrg 	}
     95  1.1  mrg 	return(eval($_) ? 1 : 0);
     96  1.1  mrg }
     97  1.1  mrg 
     98  1.1  mrg #################################################################
     99  1.1  mrg # filter out version-specific code
    100  1.1  mrg 
    101  1.1  mrg sub filter_source ($$) {
    102  1.1  mrg 	my ($in,$out) = @_;
    103  1.1  mrg 	my $line;
    104  1.1  mrg 	my $level=0;
    105  1.1  mrg 	my %if = ();
    106  1.1  mrg 	my %state = ();
    107  1.1  mrg 
    108  1.1  mrg 	my @dbgargs = \($level, %state, %if, $line);
    109  1.1  mrg 	sub dbgline($\@) {
    110  1.1  mrg 		my $level = ${$_[1][0]};
    111  1.1  mrg 		printf STDERR ("/* BP %4d $_[0] state=$_[1][1]->{$level} if=$_[1][2]->{$level} level=$level (${$_[1][3]}) */\n", $.) if $DEBUG;
    112  1.1  mrg 	}
    113  1.1  mrg 
    114  1.1  mrg 	open IN, '<', $in or die "Error opening $in: $!\n";
    115  1.1  mrg 	open OUT, '>', $out or die "Error opening $out: $!\n";
    116  1.1  mrg 
    117  1.1  mrg 	print STDERR "File: $in, for kernel $VERSION($LINUXCODE)/\n" if $DEBUG;
    118  1.1  mrg 
    119  1.1  mrg 	while ($line = <IN>) {
    120  1.1  mrg 		chomp $line;
    121  1.1  mrg 		next if ($line =~ m/^#include \"compat.h\"/o);
    122  1.1  mrg #		next if ($line =~ m/[\$]Id:/);
    123  1.1  mrg 
    124  1.1  mrg 		# For "#if 0 /*KEEP*/;" the ; should be dropped too
    125  1.1  mrg 		if ($line =~ m@^\s*#\s*if(n?def)?\s.*?(\s*/\*\s*(?i)keep\s*\*/;?)@) {
    126  1.1  mrg 			$state{$level} = "ifother";
    127  1.1  mrg 			$if{$level} = 1;
    128  1.1  mrg 			dbgline "#if$1 (keep)", @dbgargs;
    129  1.1  mrg 			$line =~ s/\Q$2\E//;
    130  1.1  mrg 			$level++;
    131  1.1  mrg 		}
    132  1.1  mrg 		# handle all ifdef/ifndef lines
    133  1.1  mrg 		elsif ($line =~ /^\s*#\s*if(n?)def\s*(\w+)/o) {
    134  1.1  mrg 			if (exists $defs{$2}) {
    135  1.1  mrg 				$state{$level} = 'if';
    136  1.1  mrg 				$if{$level} = ($1 eq 'n') ? !$defs{$2} : $defs{$2};
    137  1.1  mrg 				dbgline "#if$1def $2", @dbgargs;
    138  1.1  mrg 				$level++;
    139  1.1  mrg 				next;
    140  1.1  mrg 			}
    141  1.1  mrg 			$state{$level} = "ifother";
    142  1.1  mrg 			$if{$level} = 1;
    143  1.1  mrg 			dbgline "#if$1def (other)", @dbgargs;
    144  1.1  mrg 			$level++;
    145  1.1  mrg 		}
    146  1.1  mrg 		# handle all ifs
    147  1.1  mrg 		elsif ($line =~ /^\s*#\s*if\s+(.*)$/o) {
    148  1.1  mrg 			my $res = evalexp($1);
    149  1.1  mrg 			if (defined $res) {
    150  1.1  mrg 				$state{$level} = 'if';
    151  1.1  mrg 				$if{$level} = $res;
    152  1.1  mrg 				dbgline '#if '.($res?'(yes)':'(no)'), @dbgargs;
    153  1.1  mrg 				$level++;
    154  1.1  mrg 				next;
    155  1.1  mrg 			} else {
    156  1.1  mrg 				$state{$level} = 'ifother';
    157  1.1  mrg 				$if{$level} = 1;
    158  1.1  mrg 				dbgline '#if (other)', @dbgargs;
    159  1.1  mrg 				$level++;
    160  1.1  mrg 			}
    161  1.1  mrg 		}
    162  1.1  mrg 		# handle all elifs
    163  1.1  mrg 		elsif ($line =~ /^\s*#\s*elif\s+(.*)$/o) {
    164  1.1  mrg 			my $exp = $1;
    165  1.1  mrg 			$level--;
    166  1.1  mrg 			$level < 0 and die "more elifs than ifs";
    167  1.1  mrg 			$state{$level} =~ /if/ or die "unmatched elif";
    168  1.1  mrg 
    169  1.1  mrg 			if ($state{$level} eq 'if' && !$if{$level}) {
    170  1.1  mrg 				my $res = evalexp($exp);
    171  1.1  mrg 				defined $res or die 'moving from if to ifother';
    172  1.1  mrg 				$state{$level} = 'if';
    173  1.1  mrg 				$if{$level} = $res;
    174  1.1  mrg 				dbgline '#elif1 '.($res?'(yes)':'(no)'), @dbgargs;
    175  1.1  mrg 				$level++;
    176  1.1  mrg 				next;
    177  1.1  mrg 			} elsif ($state{$level} ne 'ifother') {
    178  1.1  mrg 				$if{$level} = 0;
    179  1.1  mrg 				$state{$level} = 'elif';
    180  1.1  mrg 				dbgline '#elif0', @dbgargs;
    181  1.1  mrg 				$level++;
    182  1.1  mrg 				next;
    183  1.1  mrg 			}
    184  1.1  mrg 			$level++;
    185  1.1  mrg 		}
    186  1.1  mrg 		elsif ($line =~ /^\s*#\s*else/o) {
    187  1.1  mrg 			$level--;
    188  1.1  mrg 			$level < 0 and die "more elses than ifs";
    189  1.1  mrg 			$state{$level} =~ /if/ or die "unmatched else";
    190  1.1  mrg 			$if{$level} = !$if{$level} if ($state{$level} eq 'if');
    191  1.1  mrg 			$state{$level} =~ s/^if/else/o; # if -> else, ifother -> elseother, elif -> elif
    192  1.1  mrg 			dbgline '#else', @dbgargs;
    193  1.1  mrg 			$level++;
    194  1.1  mrg 			next if $state{$level-1} !~ /other$/o;
    195  1.1  mrg 		}
    196  1.1  mrg 		elsif ($line =~ /^\s*#\s*endif/o) {
    197  1.1  mrg 			$level--;
    198  1.1  mrg 			$level < 0 and die "more endifs than ifs";
    199  1.1  mrg 			dbgline '#endif', @dbgargs;
    200  1.1  mrg 			next if $state{$level} !~ /other$/o;
    201  1.1  mrg 		}
    202  1.1  mrg 
    203  1.1  mrg 		my $print = 1;
    204  1.1  mrg 		for (my $i=0;$i<$level;$i++) {
    205  1.1  mrg 			next if $state{$i} =~ /other$/o;	# keep code in ifother/elseother blocks
    206  1.1  mrg 			if (!$if{$i}) {
    207  1.1  mrg 				$print = 0;
    208  1.1  mrg 				dbgline 'DEL', @{[\$i, \%state, \%if, \$line]};
    209  1.1  mrg 				last;
    210  1.1  mrg 			}
    211  1.1  mrg 		}
    212  1.1  mrg 		print OUT "$line\n" if $print;
    213  1.1  mrg 	}
    214  1.1  mrg 	close IN;
    215  1.1  mrg 	close OUT;
    216  1.1  mrg }
    217  1.1  mrg 
    218  1.1  mrg #################################################################
    219  1.1  mrg 
    220  1.1  mrg sub parse_dir {
    221  1.1  mrg 	my $file = $File::Find::name;
    222  1.1  mrg 
    223  1.1  mrg 	return if ($file =~ /CVS/);
    224  1.1  mrg 	return if ($file =~ /~$/);
    225  1.1  mrg 
    226  1.1  mrg 	my $f2 = $file;
    227  1.1  mrg 	$f2 =~ s/^\Q$SRC\E/$DESTDIR/;
    228  1.1  mrg 
    229  1.1  mrg 	my $mode = (stat($file))[2];
    230  1.1  mrg 	if ($mode & S_IFDIR) {
    231  1.1  mrg 		print("mkdir -p '$f2'\n");
    232  1.1  mrg 		system("mkdir -p '$f2'");  # should check for error
    233  1.1  mrg 		return;
    234  1.1  mrg 	}
    235  1.1  mrg 	print "from $file to $f2\n";
    236  1.1  mrg 
    237  1.1  mrg 	if ($file =~ m/.*\.[ch]$/) {
    238  1.1  mrg 		filter_source($file, $f2);
    239  1.1  mrg 	} else {
    240  1.1  mrg 		system("cp $file $f2");
    241  1.1  mrg 	}
    242  1.1  mrg }
    243  1.1  mrg 
    244  1.1  mrg 
    245  1.1  mrg # main
    246  1.1  mrg 
    247  1.1  mrg printf "kernel is %s (0x%x)\n",$VERSION,$LINUXCODE;
    248  1.1  mrg 
    249  1.1  mrg # remove any trailing slashes from dir names.  don't pass in just '/'
    250  1.1  mrg $SRC =~ s|/*$||; $DESTDIR =~ s|/*$||;
    251  1.1  mrg 
    252  1.1  mrg print "finding files at $SRC\n";
    253  1.1  mrg 
    254  1.1  mrg find({wanted => \&parse_dir, no_chdir => 1}, $SRC);
    255