Build shadow header files when necessary on Linux
authorAaron M. Ucko <ucko@debian.org>
Tue, 7 Jul 2009 00:51:00 +0000 (17:51 -0700)
committerDerrick Brashear <shadow@dementia.org>
Fri, 10 Jul 2009 13:58:09 +0000 (07:58 -0600)
The current kernel module build infrastructure relies on the ability to
create symlinks from known directory names used in the AFS code to the
actual locations of the kernel header files.  This breaks if there is no
single kernel header tree and instead multiple trees are layered together
by kbuild using compile-time -I include paths.

Attempt to detect this case by seeing if linux/types.h is in the kernel
header directory where we expect it.  If not, rather than creating
symlinks for h, sys, and netinet, create directories and populate them
with single-line headers that just include the corresponding linux/*.h
header.  The list of headers for which to do this is generated dynamically
by analyzing the AFS kernel source code and looking for relevant #include
directives.

This patch has been part of the Debian OpenAFS package since
1.4.10+dfsg1-1.  The check for whether we have layered kernel header trees
may be specific to Debian and may require modification later if other
Linux distributions do something similar.

FIXES 124583

Reviewed-on: http://gerrit.openafs.org/6
Verified-by: Russ Allbery <rra@stanford.edu>
Reviewed-by: Derrick Brashear <shadow@dementia.org>
Verified-by: Derrick Brashear <shadow@dementia.org>

src/libafs/make_kbuild_makefile.pl

index 9116869..3de680e 100755 (executable)
@@ -94,12 +94,61 @@ foreach (keys %all_objs) {
   symlink($deps{$_}, "$KDIR/$src") or die "$KDIR/$src: $!\n";
 }
 
-foreach $src (qw(h sys netinet)) {
-  if (-e "$KDIR/$src" || -l "$KDIR/$src") {
-    unlink("$KDIR/$src") or die "$KDIR/$src: $!\n";
+%remap = ('h' => 'linux', 'netinet' => 'linux', 'sys' => 'linux');
+if (-f "$vars{LINUX_KERNEL_PATH}/include/linux/types.h") {
+  foreach $src (keys %remap) {
+    if (-e "$KDIR/$src" || -l "$KDIR/$src") {
+      unlink("$KDIR/$src") or die "$KDIR/$src: $!\n";
+    }
+    symlink("$vars{LINUX_KERNEL_PATH}/include/linux", "$KDIR/$src")
+      or die "$KDIR/$src: $!\n";
+  }
+} else {
+  foreach $src (keys %remap) {
+    system ('rm', '-rf', "$KDIR/$src"); # too crude?
+    mkdir("$KDIR/$src", 0777) or die "$KDIR/$src: $!\n";
+  }
+  %seen = ();
+  @q = <$KDIR/*.[Sc]>;
+  @include_dirs = map { /^\// ? $_ : "$KDIR/$_" }
+    split /[\s\\]*-I/, $vars{COMMON_INCLUDE};
+  push @include_dirs, "$vars{TOP_SRCDIR}/../include/rx", "$vars{TOP_SRCDIR}/rx";
+  while (@q) {
+    $src = shift @q;
+    $content = new IO::File($src, O_RDONLY) or die "$src: $!\n";
+  LINE:
+    while (<$content>) {
+      chomp;
+      if (/^\s*#\s*include\s*[<"](?:\.\.\/)?([^\/>"]*)(.*?)[>"]/) {
+       $inc = "$1$2";
+       if (exists $seen{$inc}) {
+         next;
+       } elsif (exists $remap{$1}  &&  $2 !~ /.\//) {
+         $H = new IO::File("$KDIR/$inc", O_WRONLY|O_CREAT|O_TRUNC, 0666)
+           or die "$KDIR/$inc: $!\n";
+         print $H "#include <linux$2>\n";
+         $H->close() or die "$KDIR/$inc: $!\n";
+       } else {
+         for $dir (@include_dirs) {
+           if (-f "$dir/$inc") {
+             push @q, "$dir/$inc";
+             $seen{$inc} = 1;
+             next LINE;
+           }
+         }
+         if ($1 =~ /^(arpa|asm|.*fs|i?net|kern|ksys|linux|mach|rpc|scsi|vm)$/
+             ||  !length($2)) {
+           # Safe to ignore silently.
+         } else {
+           warn "Ignoring $_ ($inc not found)\n";
+         }
+       }
+       $seen{$inc} = 1;
+      } elsif (/^\s*#\s*include/) {
+       warn "Ignoring $_ (unrecognized syntax)\n";
+      }
+    }
   }
-  symlink("$vars{LINUX_KERNEL_PATH}/include/linux", "$KDIR/$src")
-    or die "$KDIR/$src: $!\n";
 }
 
 $cflags = "$vars{CFLAGS} $vars{COMMON_INCLUDE}";