Re: [PATCH 00/35] UAPI header file split - scripts

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Here are the scripts for splitting the headers.  There are three:

 (1) A script to disintegrate a single header file.

 (2) A script to scan all the Kbuild files and work out what headers are
     exported to userspace.

 (3) A script to make use of the above two and to drive git to build a commit
     per directory of exported headers.

David

#!/usr/bin/perl -w
#
# Disintegrate a file to extract out the userspace API bits into their own file
# in a separate directory.  The original file retains the residue.
#
# The original file is given a #include_next to refer to the UAPI file, and
# both headers will get guards, unless one of them is simply turned into a
# #include_next.
#
# Call as: disintegrate-one.pl <orig_header_file> <uapi_header_file>
#

use File::Path;
use strict;

sub reduce_file(@);

# Don't put a "don't include this in asm" notice in the following files
my %asm_includeable_linux_files = (
    "include/linux/const.h"		=> 1,
    "include/linux/elf-em.h"		=> 1,
    "include/linux/errno.h"		=> 1,
    "include/linux/serial_reg.h"	=> 1,
    );

die if ($#ARGV != 1);
my $linuxhdr = $ARGV[0];
my $uapihdr = $ARGV[1];

#
# The UAPI header file is called the same as the Linux header file as far as
# cpp is concerned - the latter just #include_next's the former, and if the
# latter doesn't exist, the former is used directly.
#
my $inchdr = $linuxhdr;
$inchdr =~ s@.*include/@@;

#
# Read the entire Linux header file into an array of lines.
#
open(FD, '<', $linuxhdr) or die $linuxhdr, ": $!\n";
my @lines = <FD> or die $linuxhdr, ": $!\n";
close(FD) or die $linuxhdr, ": $!\n";

my @kernellines = ();
my @uapilines = ();

#
# If the entire file is just a single #include, then don't change it
#
# We do want to create the API file _if_ the included file is an API file
#
if ($#lines == 0 && $lines[0] =~ /^#\s*include\s+<([^>]+)>/) {
    @kernellines = "#include_next <$1>\n";
    @uapilines = @lines;
    goto output;
}

goto output if ($#lines == -1);

#
# Attempt to disintegrate the file
# - The initial banner comment gets duplicated if there is one
# - The reinclusion guard is duplicated and modified for the API file
# - non-__KERNEL__ lines get put into the API file
#

my $nr_blocks = 0;
sub new_block($$$$)
{
    my ($type, $l, $parent, $prev) = @_;
    my %block = (
	type		=> $type,	# n = normal block, c = conditional block
	l		=> $l + 1,
	next_block	=> undef,
	prev_block	=> $prev,
	nr		=> ++$nr_blocks,
	parent		=> $parent,
    );
    $block{lines} = [] if ($type eq "n");
    $block{kernel_mark} = 0 if ($type ne "n");
    $prev->{next_block} = \%block if ($prev);
    return \%block;
}

#
# First of all, build a tree of normal blocks and conditionals
#
my $first_block = new_block("n", 0, undef, undef);
my $conditional_tree = $first_block;
my $cur_body = $first_block;
my $specified_include_point = 0;

my @conditional_stack = ();
my @body_stack = ( $first_block );

my $l = 0;

if ($lines[$l] =~ m@^/[*]@) {
    my @buffer = ();
    for (; $l <= $#lines; $l++) {
	if ($lines[$l] =~ "(.*)[*]/(.*)") {
	    push @buffer, "$1*/\n";
	    $lines[$l] = $2;
	    $l++;
	    goto got_banner_comment;
	}
	push @buffer, $lines[$l];
    }
  got_banner_comment:
    $first_block->{lines} = \@buffer;
    $first_block->{banner} = 1;
    $first_block->{next_block} = new_block("n", $l, undef, $first_block);
    $cur_body = $first_block->{next_block};
    $body_stack[0] = $cur_body;
}

for (; $l <= $#lines; $l++) {
    my $line = $lines[$l];
    my @buffer = ( $line );

    # parse out the actual CPP directive
    # - this may be split over multiple lines using backslashes and comments
    #   that have embedded newlines
    my $cpp = $line;
  restart:
    $cpp =~ s@\s+$@@g;
    $cpp =~ s@\s+@ @g;

    while ($cpp =~ m@(/[*])@) {
	my $o = index($cpp, "/*");
	if ($cpp =~ m@([*]/)@) {
	    my $c = index($cpp, "*/") + 2;
	    substr($cpp, $o, $c - $o) = "";
	} else {
	    $l++;
	    $cpp .= $lines[$l];
	    push @buffer, $lines[$l];
	    goto restart;
	}
    }

    if ($cpp =~ /^(.*)[\\]$/) {
	$l++;
	$cpp = $1 . $lines[$l];
	push @buffer, $lines[$l];
	goto restart;
    }

    $cpp =~ s@\s+$@@g;
    $cpp =~ s@\s\s+@ @g;

    if ($cpp eq "// DISINTEGRATE: INCLUDE UAPI HERE") {
	die if ($specified_include_point);

	my $marker;
	if ($#{$cur_body->{lines}} == -1) {
	    $marker = $cur_body;
	    $marker->{type} = "i";
	    $marker->{include_point} = undef;
	    delete $marker->{lines};
	} else {
	    my $marker = new_block("i", $l, $cur_body->{parent}, $cur_body);
	    $cur_body = $marker;
	    $body_stack[$#body_stack] = $marker;
	}
	die if ($marker->{lines});

	my $body_block = new_block("n", $l + 1, $cur_body->{parent}, $cur_body);
	$body_stack[$#body_stack] = $body_block;
	$cur_body = $body_block;
	$specified_include_point = 1;
	next;
    }

    my $retain_next = 0;
    if ($line =~ "(.*) // DISINTEGRATE: RETAIN\n") {
	$line = "$1\n";
	$retain_next = 1;
    }

    if ($line =~ /^#/) {
	#print "r:\e[36m", $cpp, "\e[m@@@ $l\n";

	# handle conditional macros
	if ($cpp =~ /^#\s*if/) {
	    #print "#if ", $#conditional_stack + 1, ": ", $#body_stack + 1, "\n";
	    my $cond_block = new_block("c", $l, $cur_body->{parent}, $cur_body);
	    $cond_block->{clauses} = [];	# #if..#elif..#elif..#else..#endif
	    $cond_block->{retain} = 1 if ($retain_next == 1);
	    push @conditional_stack, $cond_block;

	    my $clause = new_block("if", $l, $cond_block, undef);
	    $clause->{cpp} = $cpp;
	    push @{$clause->{lines}}, @buffer;
	    push @{$cond_block->{clauses}}, $clause;

	    my $body_block = new_block("n", $l + 1, $clause, undef);
	    $clause->{body} = $body_block;

	    $cur_body->{next_block} = $cond_block;
	    $body_stack[$#body_stack] = $cond_block;
	    push @body_stack, $body_block;

	    $cur_body = $body_block;

	    die ("Unexpected body types '",
		 join("", map {$_->{type};} @body_stack),
		 "' after #if\n")
		if ($body_stack[$#body_stack]->{type} ne "n" ||
		    $body_stack[$#body_stack - 1]->{type} ne "c");

	    if ($#conditional_stack == 0 &&
		$cpp =~ /^#\s*ifndef\s+([_A-Za-z0-9]+)/) {
		# keep an eye open for a guard's #define or an include-order check's #error
		my $macro = $1;
		$cur_body->{check_first_line} = $macro
		    if ($macro ne "__KERNEL__" && $macro !~ /^CONFIG_/);
	    }
	    next;
	}

	my $cond_block = $conditional_stack[$#conditional_stack];
	my $cur_clause = $cond_block->{clauses}->[$#{$cond_block->{clauses}}];

	if ($cpp =~ /^#\s*elif/) {
	    die if ($#conditional_stack < 0);
	    die if (exists $cond_block->{has_else});

	    my $clause = new_block("elif", $l, $cond_block, $cur_clause);
	    $clause->{cpp} = $cpp;
	    push @{$clause->{lines}}, @buffer;
	    push @{$cond_block->{clauses}}, $clause;

	    my $body_block = new_block("n", $l + 1, $clause, undef);
	    $clause->{body} = $body_block;

	    $body_stack[$#body_stack] = $body_block;
	    $cur_body = $body_block;
	    next;
	}

	if ($cpp =~ /^#\s*else/) {
	    #print "#else ", $#conditional_stack + 1, ": ", $#body_stack + 1, "\n";
	    die if ($#conditional_stack < 0);
	    die if (exists $cond_block->{has_else});
	    $cond_block->{has_else} = 1;

	    my $clause = new_block("else", $l, $cond_block, $cur_clause);
	    push @{$clause->{lines}}, @buffer;
	    push @{$cond_block->{clauses}}, $clause;

	    my $body_block = new_block("n", $l + 1, $clause, undef);
	    $clause->{body} = $body_block;

	    $body_stack[$#body_stack] = $body_block;
	    $cur_body = $body_block;
	    next;
	}

	if ($cpp =~ /^#\s*endif/) {
	    #print "#endif ", $#conditional_stack + 1, ": ", $#body_stack + 1, "\n";
	    die if ($#conditional_stack < 0);

	    my $clause = new_block("endif", $l, $cond_block, $cur_clause);
	    push @{$clause->{lines}}, @buffer;
	    push @{$cond_block->{clauses}}, $clause;

	    pop @conditional_stack;
	    pop @body_stack;
	    $cur_body = $body_stack[$#body_stack];

	    my $body_block = new_block("n", $l + 1, $cur_body->{parent}, $cur_body);

	    die "Unexpected body type '", $cur_body->{type}, "' at #endif\n"
		if ($cur_body->{type} ne "c");

	    $body_stack[$#body_stack] = $body_block;
	    $cur_body = $body_block;
	    next;
	}

	if (($cpp =~ /^#\s*define\s+([_A-Za-z0-9]+)$/ ||
	     $cpp =~ /^#\s*define\s+([_A-Za-z0-9]+)\s+1$/) &&
	    exists $cur_body->{check_first_line}
	    ) {
	    my $macro = $1;
	    #print "GUARD $macro\n";
	    if ($macro eq $cur_body->{check_first_line}) {
		$cond_block->{guard_label} = $cur_body->{check_first_line};
		$cur_clause->{guard} = \@buffer;
		delete $cur_body->{check_first_line};
		next;
	    }
	}

	if ($cpp =~ /^#\s*error/ &&
	    exists $cur_body->{check_first_line}
	    ) {
	    delete $cur_body->{check_first_line};
	    $cur_clause->{order_check} = \@buffer;
	    next;
	}
    }

    delete $cur_body->{check_first_line} if (exists $cur_body->{check_first_line});
    push @{$cur_body->{lines}}, @buffer;
}

die "Conditional level mismatch (", $#conditional_stack, ")\n"
    if ($#conditional_stack != -1);
die "Body level mismatch (", $#body_stack, ")\n"
    if ($#body_stack != 0);

###############################################################################
#
# Dump the parse tree
#
###############################################################################
sub dump_tree(@);
sub dump_tree(@)
{
    my $root = $#_ >= 0 ? $_[0] : $first_block;
    my $level = $#_ >= 1 ? $_[1] : 0;

    for (my $block = $root; $block; $block = $block->{next_block}) {
	my $l = $block->{l};
	my $nr = $block->{nr};
	my $lines = $block->{lines};
	print " " x $level;
	if ($block->{type} eq "n") {
	    if ($#{$lines} < 0) {
		print '- Empty (line ', $l, " nr ", $nr, ")\n";
	    } elsif ($#{$lines} == 0) {
		print '- Body (line ', $l, " nr ", $nr, ")\n";
	    } else {
		print '- Body (lines ', $l, "-", $l + $#{$lines} + 1, " nr ", $nr, ")";
		print " BANNER" if (exists $block->{banner});
		print "\n";
	    }
	    #print map {"\t\t\t>" . $_; } @{$block->{lines}} if (exists $block->{lines});
	} elsif ($block->{type} eq "i") {
		print '- UAPI Inclusion (line ', $l, " nr ", $nr, ")\n";
	} elsif ($block->{type} eq "c") {
	    my $clauses = $block->{clauses};
	    die "Must be at least 2 clauses\n" if ($#{$clauses} < 1);
	    print '@ Cond (line ', $l, " nr ", $nr, ") ", $#{$clauses}, " clauses";
	    if (exists $block->{kernel_mark}) {
		print " KO" if ($block->{kernel_mark} & 1);
		print " UO" if ($block->{kernel_mark} & 2);
		print " IK" if ($block->{kernel_mark} & 4);
		print " IU" if ($block->{kernel_mark} & 8);
		print " GUARD" if (exists $block->{guard});
		print " ORDER_CHECK" if (exists $block->{order_check});
		print " RETAIN" if (exists $block->{retain});
	    }
	    print "\n";
	    foreach my $clause (@{$clauses}) {
		my $nr = $clause->{nr};
		print " " x ($level + 1);
		print "#", $clause->{type}, " nr ", $nr;
		if (exists $clause->{kernel_mark}) {
		    print " KO" if ($clause->{kernel_mark} & 1);
		    print " UO" if ($clause->{kernel_mark} & 2);
		    print " IK" if ($clause->{kernel_mark} & 4);
		    print " IU" if ($clause->{kernel_mark} & 8);
		}
		print "\n";
		#print map {"\t\t\t>" . $_; } @{$clause->{lines}} if (exists $clause->{lines});
		dump_tree($clause->{body}, $level + 2);
	    }
	} else {
	    die;
	}
    }
}

###############################################################################
#
# Validate the parse tree structure
#
###############################################################################
sub validate_tree(@);
sub validate_tree(@)
{
    my ($parent, $body) = @_;

    if ($#_ == -1) {
	$parent = undef;
	$body = $first_block;
    }

    my $previous = undef;

    #print "-->validate_tree(", $parent ? $parent->{nr} : "-", ",", $body->{nr}, ")\n";

    for (my $block = $body; $block; $previous = $block, $block = $block->{next_block}) {
	my $nr = $block->{nr};

	die $nr, ": Unset parent\n" unless (exists $block->{parent});
	if (!$parent) {
	    die $nr, ": Unexpected parent\n" if ($block->{parent});
	} else {
	    die $nr, ": Missing parent\n" if (!$block->{parent});
	    die $nr, ": Incorrect parent", $block->{parent}->{nr}, "!=", $parent->{nr}, "\n"
		unless ($block->{parent} == $parent);
	}

	if ($previous) {
	    die($nr, ": Incorrect prev_block ",  $block->{prev_block}->{nr},
		" not ", $previous->{nr}, "\n")
		unless ($block->{prev_block} == $previous);
	} else {
	    die $nr, ": Unexpected prev_block ", $block->{prev_block}->{nr}, "\n"
		if ($block->{prev_block});
	}


	if ($block->{type} eq "n") {
	    die $nr, ": Missing line array\n" unless (exists $block->{lines});
	    die $nr, ": Unexpected __KERNEL__ mark\n" if (exists $block->{kernel_mark});
	    die $nr, ": Unexpected guard\n" if (exists $block->{guard});
	    die $nr, ": Unexpected order check\n" if (exists $block->{order_check});

	} elsif ($block->{type} eq "i") {
	    die $nr, ": Unexpected line array\n" if (exists $block->{lines});
	    die $nr, ": Unexpected guard\n" if (exists $block->{guard});
	    die $nr, ": Unexpected order check\n" if (exists $block->{order_check});

	} elsif ($block->{type} eq "c") {
	    die $nr, ": Unexpected line array\n" if (exists $block->{lines});
	    die $nr, ": Missing clause array\n" unless (exists $block->{clauses});

	    my $clauses = $block->{clauses};
	    my $nc = $#{$clauses};
	    die $nr, ": Must be at least 2 clauses\n" if ($nc < 1);

	    die $nr, ": Missing #if clause\n" if ($clauses->[0]->{type} ne "if");
	    die $nr, ": Missing #endif clause\n" if ($clauses->[$nc]->{type} ne "endif");
	    if ($nc >= 2) {
		for (my $i = 1; $i < $nc - 1; $i++) {
		    my $j = $clauses->[$i]->{nr};
		    die $nr, ": Missing #elif clause [$j]\n"
			if ($clauses->[$i]->{type} ne "elif");
		}

		my $j = $clauses->[$nc - 1]->{nr};
		die $nr, ": Missing #elif/#else clause [$j]\n"
		    if ($clauses->[$nc - 1]->{type} ne "elif" &&
			$clauses->[$nc - 1]->{type} ne "else");
	    }

	    foreach my $clause (@{$clauses}) {
		my $j = $clause->{nr};

		die "$j: Clause missing parent\n" unless ($clause->{parent});
		die "$j: Clause has wrong parent: ", $clause->{parent}->{nr}, "\n"
		    unless ($clause->{parent} == $block);
		die "$j: Unexpected body in #endif: ", $clause->{body}->{nr}, "\n"
		    if ($clause->{type} eq "endif" && exists $clause->{body});
		die "$j: Missing clause line array\n" unless (exists $clause->{lines});

		validate_tree($clause, $clause->{body}) if (exists $clause->{body});
	    }
	} else {
	    die "$nr: Invalid block type: '", $block->{type}, "'\n";
	}
    }

    #print "<--validate_tree()\n";
}

validate_tree();
#dump_tree(); exit 123;

###############################################################################
#
# Eliminate empty bodies from the tree
#
###############################################################################
sub discard_empties($);
sub discard_empties($)
{
    my ($block) = @_;

    while ($block) {
	die unless exists $block->{type};
	if ($block->{type} eq "n") {
	    if ($#{$block->{lines}} < 0) {
		#print "EMPTY: ", $block->{nr};
		my $parent = $block->{parent};
		my $prev = $block->{prev_block};
		my $next = $block->{next_block};
		delete $block->{type};

		if ($next) {
		    #print " next";
		    die if ($next->{prev_block} != $block);
		    $next->{prev_block} = $prev;
		}

		if ($prev) {
		    #print " prev";
		    die if ($prev->{next_block} != $block);
		    $prev->{next_block} = $next;
		} else {
		    if ($parent) {
			#print " parent(", $parent->{nr}, ")";
			die unless ($parent->{body} == $block);
			$parent->{body} = $next;
		    } else {
			#print " root";
			die "Mismatch ", $first_block->{nr}, " != ", $block->{nr}, "\n"
			    if ($first_block != $block);
			$first_block = $next;
		    }
		}
		die if ($next && $block == $next);
		$block = $next;
		#print "\n";
		next;
	    } else {
		$block = $block->{next_block};
		next;
	    }
	} elsif ($block->{type} eq "i") {
	    ;
	} elsif ($block->{type} eq "c") {
	    my $clauses = $block->{clauses};
	    die "Must be at least 2 clauses\n" if ($#{$clauses} < 1);
	    foreach my $clause (@{$clauses}) {
		discard_empties($clause->{body});
	    }
	} else {
	    die;
	}

	$block = $block->{next_block};
	next;
    }
}

discard_empties($first_block);
validate_tree();

###############################################################################
#
# Mark up single variable only __KERNEL__ conditions and percolate marks up the
# tree.
#
###############################################################################
sub mark__KERNEL__($);
sub mark__KERNEL__($)
{
    my ($first) = @_;
    my $combined_kernel_mark = 0;

    for (my $block = $_[0]; $block; $block = $block->{next_block}) {
	next if ($block->{type} ne "c");

	my $clauses = $block->{clauses};
	my $cpp = $clauses->[0]->{cpp};

	my $kernel_mark = 0;

	if ($block->{retain}) {
	    ;
	} elsif ($cpp =~ /^#\s*ifdef\s+__KERNEL__/ ||
	    ($cpp =~ /^#\s*if\s+defined\s*\(\s*__KERNEL__\s*\)/ &&
	     $cpp !~ /[|][|]|[&][&]/)) {
	    $kernel_mark |= 1 | 4;
	} elsif ($cpp =~ /^#\s*ifndef\s+__KERNEL__/ ||
		 ($cpp =~ /^#\s*if\s+!\s*defined\s*\(\s*__KERNEL__\s*\)/ &&
		  $cpp !~ /[|][|]|[&][&]/)) {
	    $kernel_mark |= 2 | 8;
	}

	if ($kernel_mark) {
	    $clauses->[0]->{kernel_mark} = $kernel_mark;
	    if ($#{$clauses} > 1) {
		die $linuxhdr, ":", $clauses->[1]->{l}, ": __KERNEL__ guard has #elif clause\n"
		    if ($#{$clauses} > 2 || $clauses->[1]->{type} eq "elif");
		$clauses->[1]->{kernel_mark} = $kernel_mark ^ 15;
		$kernel_mark = 15;
		$clauses->[2]->{kernel_mark} = $kernel_mark;
	    } else {
		$clauses->[1]->{kernel_mark} = $kernel_mark;
	    }
	}

	foreach my $clause (@{$clauses}) {
	    die $linuxhdr, ":", $clause->{l}, ": #elif contains __KERNEL__\n"
		if ($clause->{type} eq "elif" && $clause->{cpp} =~ /__KERNEL__/);

	    if (exists $clause->{body}) {
		my $k = mark__KERNEL__($clause->{body});
		if ($k) {
		    die $linuxhdr, ":", $clause->{l}, ": Body contains nested __KERNEL__\n"
			if ($kernel_mark & 3);
		    $clause->{kernel_mark} |= $k | 8;
		    $kernel_mark = $k | 8;
		}
	    }
	}

	$block->{kernel_mark} = $kernel_mark;

	$combined_kernel_mark |= $kernel_mark;
    }

    return $combined_kernel_mark & (4 | 8);
}

mark__KERNEL__($first_block);
validate_tree();
#dump_tree(); exit 123;

###############################################################################
#
# Determine reinclusion guards and validate inclusion order checks that are
# outside the guards
#
###############################################################################
sub determine_guards()
{
    for (my $block = $first_block; $block; $block = $block->{next_block}) {
	next if ($block->{type} ne "c");

	if ($block->{clauses}->[0]->{guard}) {
	    $block->{guard} = 1;
	    $block->{kernel_mark} = 8;
	    die unless (exists $block->{guard_label});
	} elsif ($block->{clauses}->[0]->{order_check}) {
	    $block->{order_check} = 1;
	    $block->{kernel_mark} = 8;
	    die $linuxhdr, ":", $block->{l}, ": Inclusion order check with multiple clauses\n"
		unless ($#{$block->{clauses}} == 1);
	    die $linuxhdr, ":", $block->{l}, ": Inclusion order check with extra body\n"
		if ($block->{clauses}->[0]->{body});
	}
    }
}

determine_guards();
#dump_tree();
validate_tree();

###############################################################################
#
# Render the two header files
#
###############################################################################
my $include_next_at = -1;

sub render(@);
sub render(@)
{
    my $root = $#_ >= 0 ? $_[0] : $first_block;
    my $parent_kernel_mark = $#_ >= 1 ? $_[1] : 8;

    for (my $block = $root; $block; $block = $block->{next_block}) {
	my $kernel_mark = $parent_kernel_mark;

	#push @kernellines, "KM$kernel_mark\n";

	if ($block->{type} eq "n") {
	    my $is_banner = exists $block->{banner};
	    my $lines = $block->{lines};
	    push @kernellines, @{$lines} if ($is_banner || !($kernel_mark & 8));
	    push @uapilines, @{$lines} if ($is_banner || $kernel_mark & 8);
	    next;
	}

	if ($block->{type} eq "i") {
	    push @kernellines, "#include_next <$inchdr>\n";
	    next;
	}

	if ($block->{type} eq "c") {
	    my $clauses = $block->{clauses};

	    if ($block->{order_check}) {
		push(@uapilines,
		     @{$clauses->[0]->{lines}},
		     @{$clauses->[0]->{order_check}},
		     @{$clauses->[1]->{lines}});
		next;
	    }

	    if ($block->{guard}) {
		push(@kernellines,
		     @{$clauses->[0]->{lines}},
		     @{$clauses->[0]->{guard}});
		push @kernellines, "\n";

		if ($linuxhdr =~ m!^include/! &&
		    $linuxhdr !~ m@^include/asm-generic/@ &&
		    !exists($asm_includeable_linux_files{$linuxhdr})) {
		    push @kernellines, "#ifdef __ASSEMBLY__\n";
		    push @kernellines, "#error include/linux headers may not be included in .S files\n";
		    push @kernellines, "#endif\n";
		    push @kernellines, "\n";
		}

		push @kernellines, "#define __EXPORTED_HEADERS__\n"
		    if ($linuxhdr eq "include/linux/types.h");

		unless ($specified_include_point) {
		    push @kernellines, "#include_next <$inchdr>\n";
		    $include_next_at = $#kernellines;
		    push @kernellines, "\n";
		}

		push(@uapilines,
		     "#ifndef _UAPI" . $block->{guard_label} . "\n",
		     "#define _UAPI" . $block->{guard_label} . "\n");

		render($block->{clauses}->[0]->{body}, $kernel_mark);
		push(@kernellines,
		     @{$clauses->[1]->{lines}});
		push(@uapilines,
		     "#endif /* _UAPI" . $block->{guard_label} . " */\n");
		next;
	    }

	    $kernel_mark = $block->{kernel_mark} if ($block->{kernel_mark});

	    if (($kernel_mark & 3) == 0) {
		# no mention of __KERNEL__ only
		foreach my $clause (@{$clauses}) {
		    my $lines = $clause->{lines};

		    push @kernellines, @{$clause->{lines}} if ($kernel_mark & 4);
		    push @uapilines, @{$clause->{lines}} if ($kernel_mark & 8);
		    render($clause->{body}, $kernel_mark)
			if (exists $clause->{body});
		}
	    } elsif (($kernel_mark & 3) == 1) {
		# #ifdef __KERNEL__
		render($block->{clauses}->[0]->{body}, 4);
	    } elsif (($kernel_mark & 3) == 2) {
		# #ifndef __KERNEL__
		push @uapilines, @{$block->{clauses}->[0]->{lines}};
		render($block->{clauses}->[0]->{body}, 8);
		push @uapilines, @{$block->{clauses}->[1]->{lines}};
	    } else {
		if ($block->{clauses}->[0]->{kernel_mark} & 1) {
		    # #ifdef __KERNEL__ ... #else
		    render($block->{clauses}->[0]->{body}, 4);

		    my @iflines = @{$block->{clauses}->[0]->{lines}};
		    $iflines[0] =~ s/#(\s*if)def/#$1ndef/;
		    $iflines[0] =~ s/#(\s*if\s+)defined/#$1!defined/;
		    push @uapilines, @iflines;
		    render($block->{clauses}->[1]->{body}, 8);
		    push @uapilines, @{$block->{clauses}->[2]->{lines}};
		} else {
		    # #ifndef __KERNEL__ ... #else
		    render($block->{clauses}->[1]->{body}, 4);

		    push @uapilines, @{$block->{clauses}->[0]->{lines}};
		    render($block->{clauses}->[0]->{body}, 8);
		    push @uapilines, @{$block->{clauses}->[2]->{lines}};
		}
	    }
	}
    }
}

render();

###############################################################################
#
# See if a file actually has anything left in it after all the blank lines,
# comments and CPP conditionals and inclusions are removed.
#
# Returns:
#  0: non-reducible
#  1: reducible to nothing
#  2: reducible to a single #include
#  3: reducible to a multiple #includes
#
###############################################################################
sub reduce_file(@)
{
    my (@lines) = @_;

    my $blocks = 0;
    my $level = 0;
    my $guarded = 0;
    my $guardname = "";
    my $guarddefline = -2;
    my $first_if = 1;
    my $includes = 0;

    for (my $l = 0; $l <= $#lines; $l++) {
	my $line = $lines[$l];
	my $suppress = 0;

	# parse out the blocks
	# - this may be split over multiple lines using backslashes and comments
	#   that have embedded newlines
	my $block = $line;
      restart:
	$block =~ s@\s+$@@g;
	$block =~ s@\s+@ @g;

	while ($block =~ m@(/[*])@) {
	    my $o = index($block, "/*");
	    if ($block =~ m@([*]/)@) {
		my $c = index($block, "*/") + 2;
		substr($block, $o, $c - $o) = "";
	    } else {
		$l++;
		$block .= $lines[$l];
		#push @buffer, $lines[$l];
		goto restart;
	    }
	}

	if ($block =~ /^(.*)[\\]$/) {
	    $l++;
	    $block = $1 . $lines[$l];
	    #push @buffer, $lines[$l];
	    goto restart;
	}

	$block =~ s@\s+$@@g;
	$block =~ s@\s\s+@ @g;

	if ($line =~ /^#/) {
	    # handle conditional macros
	    if ($block =~ /^#\s*if/) {
		$level++;
		if ($block =~ /^#\s*ifndef\s+([_A-Za-z0-9]+)/ && $first_if == 1) {
		    $guardname = $1;
		    $guarddefline = $l + 1;
		    $first_if = 0;
		    next;
		}

		$first_if = 0;
	    } elsif ($block =~ /^#\s*endif/) {
		$level--;
		next;
	    } elsif ($block =~ /^#\s*else/) {
		next;
	    } elsif ($block =~ /^#\s*elif/) {
		next;
	    } elsif ($l == $guarddefline && $block =~ /^#\s*define\s+$guardname/) {
		next;
	    } elsif ($block =~ /^#\s*include_next/) {
		next;
	    } elsif ($block =~ /^#\s*include/) {
		$includes++;
		next;
	    } elsif ($block =~ /^#error only arch headers may be included in asm/) {
		next;
	    }
 
	} elsif ($block eq "") {
	    next;
	}

	#print "[", $block, "]\n";
	$blocks++;
    }

    die $linuxhdr, ": #if/#endif level mismatch ($level)\n"
	if ($level != 0);

    return 0 if ($blocks > 0);
    return 2 if ($includes == 1);
    return 3 if ($includes > 0);
    return 1;
}

###############################################################################
#
# Attempt to slide down the #include_next to after the #include set if it was
# added automatically (if it was manually specified, then we leave it where it
# is).
#
###############################################################################
sub slide_include_next()
{
    # first of all, locate the #include_next
    my $include_next = $kernellines[$include_next_at];
    die if ($include_next !~ m/#include_next/);

    my $here = -1;
    my $if_level = 0;
    my $cond_include = 0;
    for (my $l = $include_next_at + 1; $l <= $#kernellines; $l++) {
	my $line = $kernellines[$l];
	chomp $line;

	if ($line eq "") {
	} elsif ($line =~ /#\s*include\s+<([^>]*)>/) {
	    last if ($1 eq "linux/byteorder/generic.h");
	    if ($if_level == 0) {
		$here = $l;
	    } else {
		$cond_include = 1;
	    }
	} elsif ($line =~ /#\s*if/) {
	    $if_level++;
	} elsif ($line =~ /#\s*else/) {
	} elsif ($line =~ /#\s*elif/) {
	} elsif ($line =~ /#\s*endif/) {
	    $if_level--;
	    if ($if_level == 0) {
		$here = $l if ($cond_include);
		$cond_include = 0;
	    }
	} else {
	    last;
	}
    }

    if ($here > -1) {
	splice @kernellines, $include_next_at, 2;
	splice @kernellines, $here - 1, 0, $include_next;
	splice @kernellines, $here, 0, "\n" unless ($kernellines[$here] eq "\n");
    }
}

slide_include_next() if ($include_next_at > -1);

###############################################################################
#
#
#
###############################################################################
output:
    ;

#print @kernellines;
#print "_" x 79, "\n";
#print @uapilines;

my $kred = reduce_file(@kernellines);
my $ured = reduce_file(@uapilines);

#print "kred: ", $kred, "\n";
#print "ured: ", $ured, "\n";
#exit 123;

if ($ured == 2) {
    @uapilines = grep /^#\s*include/, @uapilines;
} elsif ($ured == 1) {
    @uapilines = ();
}

if ($kred == 1) {
    # if all we're doing is #include_next'ing the UAPI header, then we may as
    # well delete the file and let CPP include the UAPI header directly
    @kernellines = ();
    @uapilines = @lines;
}

my $uapidir = $uapihdr;
$uapidir = $1 if ($uapidir =~ m!(.*)/!);
mkpath($uapidir) if (! -d $uapidir);

if ($#kernellines >= 0) {
    # we must create a UAPI header, even if it is blank
    open(FD, '>', $uapihdr) or die "$uapihdr: $!\n";
    print FD @uapilines or die "$uapihdr: $!\n";
    close FD or die "$uapihdr: $!\n";

    my $linuxhdrorig = $linuxhdr . ".orig";
    open(FD, '>', $linuxhdrorig) or die $linuxhdrorig, ": $!\n";
    print FD @kernellines or die $linuxhdrorig, ": $!\n";
    close FD or die $linuxhdrorig, ": $!\n";
    rename $linuxhdrorig, $linuxhdr or die $linuxhdr, ": $!\n";
} else {
    rename $linuxhdr, $uapihdr or die $uapihdr, ": $!\n";
}

###############################################################################
#
# Add the file to the uapi Kbuild file
#
###############################################################################
my $uapi_kbuild = $uapidir . "/Kbuild";

die "$uapi_kbuild: Not found\n" unless (-f $uapi_kbuild);

my $hdrname;
$uapihdr =~ m@([^/]*)$@, $hdrname = $1;

open(FD, '>>', $uapi_kbuild) or die "$uapi_kbuild: $!\n";

if ($linuxhdr eq "include/linux/a.out.h" ||
    $linuxhdr eq "include/linux/kvm.h" ||
    $linuxhdr eq "include/linux/kvm_para.h"
    ) {
    print FD
	"\n",
	'ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/asm/', $hdrname, " \\\n",
	"\t\t", '  $(srctree)/include/asm-$(SRCARCH)/', $hdrname, " \\\n",
	"\t\t", '  $(INSTALL_HDR_PATH)/include/asm-*/', $hdrname, "),)\n",
	"header-y += ", $hdrname, "\n",
	"endif\n\n"
	or die "$uapi_kbuild: $!\n";
} else {
    print FD "header-y += $hdrname\n" or die "$uapi_kbuild: $!\n";
}

close FD or die "$uapi_kbuild: $!\n";


###############################################################################
#
# Delete the file from the include/ Kbuild file
#
###############################################################################
my $linuxdir;
$linuxhdr =~ m@(.*)/[^/]*$@, $linuxdir = $1;
my $linux_kbuild = $linuxdir . "/Kbuild";

open(FD, '<', $linux_kbuild) or die $linux_kbuild, ": $!\n";
my @kblines = <FD> or die $linux_kbuild, ": $!\n";
close(FD) or die $linux_kbuild, ": $!\n";

my $temp = $linux_kbuild . ".temp";
open(FD, '>', $temp) or die "$temp: $!\n";
foreach my $kbline (@kblines) {
    if ($kbline =~ m@^header-y\s+[+]=\s+([a-zA-Z0-9_.-]+)@) {
	next if ($1 eq $hdrname);
    }
    print FD $kbline or die "$temp: $!\n";
}
close FD or die "$temp: $!\n";
rename $temp, $linux_kbuild or die "$temp -> $linux_kbuild: $!\n";
#!/usr/bin/perl -w

use File::Find;

#
# We assume that only Kbuild files in include directories are pertinent to
# determining which headers are UAPI headers.
#
@kbuilds = ();
%headers = ();
sub find_Kbuild()
{
    push(@kbuilds, $File::Find::name) if ($_ eq "Kbuild");
    $headers{$File::Find::name} = 1 if ($_ =~ /[.]h$/ || $_ =~ /[.]agh$/);
}

find(\&find_Kbuild, "arch", "include");

# Read the common arch list
open FD, '<include/asm-generic/Kbuild.asm' or die "open Kbuild.asm: $!\n";
my @kbuild_asm = <FD>;
close FD or die;

my %uapihdrs = ();

foreach my $i (sort(grep { $_ !~ m@uapi/@ } @kbuilds)) {
    #print "[[[ $i ]]]\n";

    my $dir = $i;
    $dir =~ m@(^.*)/@, $dir = $1;

    open FD, '<', $i or die "open $i: $!\n";
    my @lines = <FD>;
    close FD or die;

    for (my $l = 0; $l <= $#lines; $l++) {
	my $line = $lines[$l];

	# parse out the blocks
	# - this may be split over multiple lines using backslashes
	my $block = $line;
      restart:
	$block =~ s@#.*$@@;
	$block =~ s@\s+$@@g;
	$block =~ s@\s+@ @g;

	if ($block =~ /^(.*)[\\]$/) {
	    $l++;
	    $block = $1 . $lines[$l];
	    goto restart;
	}

	$block =~ s@\s+$@@g;
	$block =~ s@\s\s+@ @g;

	if ($block =~ m@^include include/asm-generic/Kbuild.asm@) {
	    push @lines, @kbuild_asm;
	}

	if ($block =~ m@^header-y\s*[+:]?=\s*(.*)@ ||
	    $block =~ m@^opt-header\s*[+:]?=\s*(.*)@ ||
	    $block =~ m@^asm-headers\s*[+:]?=\s*(.*)@
	    ) {
	    foreach $h (map { "$dir/" . $_ } grep m@[^/]$@, split /\s+/, $1) {
		if (exists $headers{$h}) {
		    $uapihdrs{$h} = 1;
		}
	    }
	}
    }
}

if ($#ARGV == -1) {
    # no arguments: all listed header files
    print map { $_ . "\n"; } sort keys %uapihdrs;
} else {
    # any arguments: all unlisted header files
    print map { $_ . "\n"; } sort grep { !exists $uapihdrs{$_}; } keys %headers;
}
#!/usr/bin/perl -w
#
# Perform header disintegration of the user API, producing commits on a
# dir-by-dir basis to GIT
#

use strict;
use File::Basename;

my $main_branch = "uapi-split";

my $UAPI = "uapi";

$ENV{UAPI} = "uapi";

my $execdir = dirname($0);

sub commit($@) {
    my ($dirname, @files) = @_;

    system("git commit -m 'UAPI: Disintegrate $dirname\n\nSigned-off-by: David Howells <dhowells\@redhat.com>\n' " . join(" ", @files)) == 0 or die;
}

###############################################################################
#
#
#
###############################################################################
system("git checkout $main_branch") == 0 or die;

my $curdir = "xxxxx";

my @headerlist = sort {
    dirname($a) cmp dirname($b) || $a cmp $b;
} `$execdir/genlist.pl`;

my @files = ();
foreach my $origfile (@headerlist) {
    chomp $origfile;
    if (! -f $origfile) {
	print "Skip $origfile\n";
	next;
    }

    my $odir = dirname($origfile);

    if ($odir ne $curdir) {
	print "[]";

	commit($curdir, @files) unless ($curdir eq "xxxxx");
	$curdir = $odir;
	@files = ();
    }

    print "$origfile\n";
    my $uapifile = $origfile;
    $uapifile =~ s@include/@$UAPI/@;
    my $udir = dirname($uapifile);

    system("$execdir/disintegrate-one.pl $origfile $uapifile") == 0 or die;

    if (-r $uapifile) {
	push @files, $uapifile;
	system("git add $uapifile") == 0 or die;
    }
    push @files, "$udir/Kbuild", $origfile, "$odir/Kbuild";
}

commit($curdir, @files) unless ($curdir eq "xxxxx");

[Index of Archives]     [Linux Kernel]     [Kernel Newbies]     [x86 Platform Driver]     [Netdev]     [Linux Wireless]     [Netfilter]     [Bugtraq]     [Linux Filesystems]     [Yosemite Discussion]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]

  Powered by Linux