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