Work at SourceForge, help us to make it a better place! We have an immediate need for a Support Technician in our San Francisco or Denver office.

Close

[r36573]: trunk / core / bin / check_dsp Maximize Restore History

Download this file

check_dsp    697 lines (665 with data), 28.2 kB

#! /bin/sh
exec perl -l012 -w -x $0 ${1+"$@"}
#!perl
#line 5

# Purpose: verify that the SOURCE lines in a .dsp file correspond to
# the SOURCES lines in the makefile PLUS the list of Templates files,
# which in turn should correspond to the SOURCE_FILES in CMakeLists.txt.
#
# Second purpose: if the current directory contains a file named
# test_include.cxx, verify that it contains all .h files from the parent dir
# (and of all subdirectories of that parent dir which have no test_include.cxx
# for themselves).
#
# Third purpose: if the current directory contains a file named
# test_template_include.cxx, verify that it contains all .txx files from the
# parent dir (and of all subdirectories of that parent dir which have no
# test_template_include.cxx for themselves).
#
# Command line argument(s): list of directories to check (default: all)
# Command line switch: -s: generate output in the form of a shell script

use strict;

my $shell = '';
while (@ARGV && substr($ARGV[0],0,1) eq '-') {
  if ($ARGV[0] eq '-s') { $shell = 1; shift @ARGV; }
  else { warn "Unrecognised command line option " . (shift @ARGV) . " ignored"; }
}

my @DIRS;
if (@ARGV) {
  @DIRS = @ARGV;
}
else {
  my $V = $ENV{VXLROOT};
  if (defined $V && -d "$V/vcl" && -d "$V/core" && -d "$V/contrib") {
    chomp (@DIRS = `find $V/vcl $V/core $V/contrib $V/v3p -type d -print`);
  }
  elsif (-d 'vcl' && -d 'core' && -d 'contrib') {
    chomp(@DIRS = `find vcl core contrib v3p -type d -print`);
  }
  else
  {
    die "VXLROOT is not defined, no command line arguments were given, and current directory has no vcl, core, contrib";
  }
  @DIRS = grep { ! m!(/\.|/CVS|/\.svn|awfmfc|vgui_example_mfc|b3p/minizip|\.old)$! } @DIRS;
}

foreach my $dir (@DIRS)
{
  # remove "makefiles" or "dsps" prefix:
  $dir =~ s!^(makefiles|dsps)/!!;
  # make that the embedding directory, if this is not a directory name:
  $dir =~ s!/[^/]+$ !!x unless -d $dir;
  # remove possible slash at end of directory name:
  $dir =~ s![\\/]+$ !!x;
  # Now read directory content and open relevant files (like CMakeLists.txt, makefile and *.dsp)
  # Possibly, makefile is located in  the parallel source tree, viz. in "makefiles/$dir", instead of directly in $dir
  # and similarly for *.dsp, in "dsps/$dir"; moreover, this is the first place to look.
  next unless opendir BASEDIR,$dir;
  my @file_list = grep {! m/^(\..*|CVS|\.svn)$/} readdir(BASEDIR);
  closedir(BASEDIR);
  my @sd = ();
  if    ($dir =~ m!\bvcl$!)   { @sd = qw(vcl_sys iso generic gcc egcs gcc-295 gcc-libstdcxx-v3 sgi sunpro win32-vc60 win32-vc70 win32-vc8 emulation borland55 borland56 mwerks stlport internal); }
  elsif ($dir =~ m!\bvil(|1|3d)$!) { @sd = qw(file_formats); }
  elsif ($dir =~ m!\bvgl$!)   { @sd = qw(internals); }
  elsif ($dir =~ m!\bvpl$!)   { @sd = qw(os_unix os_win32); }
  elsif ($dir =~ m!\bvpdl$!)  { @sd = qw(vpdt); }
  elsif ($dir =~ m!\bvipl$!)  { @sd = qw(filter section accessors vipl_with_section vipl_with_vbl_array_2d vipl_with_vnl_matrix vipl_with_section/accessors vipl_with_vbl_array_2d/accessors vipl_with_vnl_matrix/accessors); }
  elsif ($dir =~ m!\bvepl$!)  { @sd = qw(accessors section); }
  elsif ($dir =~ m!\bvgui$!)  { @sd = qw(internals vrml impl/qt impl/qt4 impl/wx impl/gtk impl/gtk2 impl/mfc impl/glut impl/win32); }
  elsif ($dir =~ m!\bosl$!)   { @sd = qw(internals); }
  elsif ($dir =~ m!\bbwm$!)   { @sd = qw(process); }
  elsif ($dir =~ m!\bboxm2/ocl$!)   { @sd = qw(cl cl/bit); }
  elsif ($dir =~ m!\b(bbas/bxml/bsvg/pro|bpro/core/(brip|bvgl|icam|sdet|vidl|vpgl|vil)_pro|bseg/(bapl|bbgm|bmdl|boxm|brec|bvpl|bvxm)/pro|bseg/(boxm/algo(|/rt|/sp)|boxm/ocl|bvpl/bvpl_octree|bvxm/algo|bvxm/grid)/pro)$!) { @sd = qw(processes); }
  if    ($dir =~ m!\bbvpl/pro$!)  { push @sd, 'processes/util'; }

  foreach my $sd (@sd) {
    opendir SUBDIR,"$dir/$sd";
    push @file_list, map { "$sd/$_" } grep {! m/^(\..*|CVS|\.svn)$/} readdir(SUBDIR);
    closedir(SUBDIR);
  }

  # verify #includes in test_include.cxx
  if (-f "$dir/test_include.cxx" && $dir !~ m/^v3p/)
  {
    my %updir_files = ();
    my %inc_files = ();
    my $prefix=$dir; $prefix =~ s![^/]*$ !!x; $prefix =~ s!^.*\b(vcl|core|contrib/brl/(bbas|bseg)|contrib/gel/mrc|contrib/[^/]*)/!!;
    $prefix =~ s!^bpgl/(icam|ihog)!$1!;
    if (opendir UPDIR,"$dir/..") {
      foreach (grep {m/\.h(|xx)$/} readdir(UPDIR))
      {
        ++ $updir_files{$prefix.$_}
          unless (m/\bdll\.h$/
               || m/\b(i?ascii|latin1|utf8)tab\.h$/
               || m/\bexpat(Dll)?Config\.h$/
               || m/\bbpro_defs\.h$/
               || m/\bmvl2_video_(from|to)_avi_(linux|windows)\.h$/
                 );
      }
      closedir(UPDIR);
    }
    my @subdirs = qw(algo io util file_formats gui pro xio vpdt sample filter section accessors vipl_with_section/accessors vipl_with_vbl_array_2d/accessors vipl_with_vnl_matrix/accessors);
    push @subdirs,'process' if ($dir =~ m!\bbwm/tests$!);
    push @subdirs,'processes' if ($dir =~ m!\b(bpro/core/sdet_pro|bseg/boxm2/ocl/pro|bseg/bvxm/pro)/tests$!);
    foreach my $subdir (@subdirs)
    {
      if (! -d "$dir/../$subdir/tests" && opendir UPDIR,"$dir/../$subdir") {
        foreach (grep {m/\.h(|xx)$/} readdir(UPDIR))
        {
          ++ $updir_files{"$prefix$subdir/$_"};
        }
        closedir(UPDIR);
      }
    }
    open(FILE, "$dir/test_include.cxx");
    while (<FILE>) {
      ++ $inc_files{$1} if (m/^\s*\#\s*include\s*\<(\S+)\>\s*$/);
    }
    close(FILE);
    foreach my $s (keys %inc_files)
    {
      &duplicate_message("#include <$s>", "$dir/test_include.cxx\n", '// !!')
        if ($inc_files{$s} > 1);
      delete $inc_files{$s};
      unless (exists $updir_files{$s})
      {
        &removing_message("#include <$s>", "$dir/test_include.cxx\n", '// !!')
          unless ($s eq 'vxl_config.h' || $s eq 'vgui/vgui_config.h' || $s eq 'vcl_compiler.h');
      }
      delete $updir_files{$s};
    }
    foreach my $s (keys %updir_files) # should already be empty by now!
    {
      &missing_message("#include <$s>", "$dir/test_include.cxx");
      delete $updir_files{$s};
    }
  }

  # verify #includes in test_template_include.cxx
  if (-f "$dir/test_template_include.cxx" && $dir !~ m/^v3p/)
  {
    my %updir_files = ();
    my %inc_files = ();
    my $prefix=$dir; $prefix =~ s![^/]*$ !!x; $prefix =~ s!^.*\b(vcl|core|contrib/brl/(bbas|bseg)|contrib/gel/mrc|contrib/[^/]*)/!!;
    if (opendir UPDIR,"$dir/..") {
      foreach (grep {m/\.txx$/} readdir(UPDIR))
      {
        ++ $updir_files{$prefix.$_};
      }
      closedir(UPDIR);
    }
    foreach my $subdir (qw(algo io file_formats gui pro xio vpdt sample filter section accessors vipl_with_section/accessors vipl_with_vbl_array_2d/accessors vipl_with_vnl_matrix/accessors))
    {
      if (! -d "$dir/../$subdir/tests" && opendir UPDIR,"$dir/../$subdir") {
        foreach (grep {m/\.txx$/} readdir(UPDIR))
        {
          ++ $updir_files{"$prefix$subdir/$_"};
        }
        closedir(UPDIR);
      }
    }
    open(FILE, "$dir/test_template_include.cxx");
    while (<FILE>) {
      ++ $inc_files{$1} if (m/^\s*\#\s*include\s*\<(\S+)\>\s*$/);
    }
    close(FILE);
    foreach my $s (keys %inc_files)
    {
      &duplicate_message("#include <$s>", "$dir/test_template_include.cxx\n", '// !!')
        if ($inc_files{$s} > 1);
      delete $inc_files{$s};
      unless (exists $updir_files{$s})
      {
        &removing_message("#include <$s>", "$dir/test_template_include.cxx\n", '// !!');
      }
      delete $updir_files{$s};
    }
    foreach my $s (keys %updir_files) # should already be empty by now!
    {
      &missing_message("#include <$s>", "$dir/test_template_include.cxx");
      delete $updir_files{$s};
    }
  }

  my %hfiles = (); # full list of existing *.h, *.h.in, *.hxx and *.txx files
  foreach my $fx (grep {m/\.(h|txx|hxx)(\.in)?$/} @file_list)
  {
    next if ($dir =~ m!\bvcl/generic$ !x);
    next if ($fx  =~ m/\b(Resource|StdAfx)\.h$/i);
    ++ $hfiles{$fx};
  }
  my %source = ();
# foreach my $fx (grep {m/\.(c|cxx|cc|cpp|C)$/} @file_list)
# {
#   ++ $source{$fx};
# }
  my %hsource = ();
  my $f = '';
  my $prefix = (-d "dsps/$dir" ? "dsps/$dir" : $dir);
  opendir DSPDIR,$prefix;
  foreach my $fx (map { "$prefix/$_" } grep {m/\.dsp$/} readdir(DSPDIR))
  {
    next unless open(FILE, $fx);
    $f .= " $fx";
    my $start = '';
    binmode FILE;
    while (<FILE>)
    {
      print $shell ? '# ' : '' . "Line of $dir/$fx does not end in <CR><LF>"
        unless m/\r$/;
      $start = 1  if (m/^\# Begin Group \"Source Files\"/);
      $start = 2  if (m/^\# Begin Group \"Header Files\"/);
      $start = '' if (m/^\# End Group/);
      next if (! $start);
      if (m/^SOURCE=\"?\.\\([^\s\"]+)/) {
        my $file = $1; $file =~ s!\\!/!g;
        ++ $source{$file}
          unless ($file eq 'CMakeLists.txt' || $start != 1);
        ++ $hsource{$file}
          unless ($file eq 'CMakeLists.txt' || $start != 2);
      }
    }
    close FILE;
  }
  if ($dir =~ m!/mul/mvl2$ !x) {
    ++ $source{'mvl2_video_to_avi_linux.cxx'}; ++ $source{'mvl2_video_from_avi_linux.cxx'};
    ++ $hsource{'mvl2_video_to_avi_linux.h'}; ++ $hsource{'mvl2_video_from_avi_linux.h'};
  }
  $f =~ s/^ //;

  my $makef = 'makefiles/' . $dir . '/makefile';
  -f $makef or $makef = $dir . '/makefile';
  -f $makef or $makef = 'makefiles/' . $dir . '/makefile';
  my %sources = ();
  my %tests = ();
  my %subdirs = ();
  my %subdirs2 = ();
  my %hsources = ();
  my $testmain = '';
  if (open(MAKEFILE, $makef)) {
    my @aux_sources = ('Templates');
    while (<MAKEFILE>)
    {
      s/\$\(wildcard (.*)\)/$1/g; s/\#.*//; s/\$\(.+?\)//s;
      if (m/^\s*TESTMAIN\s*\:?\=\s*(\S+)/) {
        $testmain = $1;
      }
      while (s/^\s*(SOURCES|CL_SOURCES|MINI_PROG_SOURCES|TESTS|TESTMAIN)\s*\:?\=\s*(\S+)/$1=/) {
        ++ $tests{$2} if ($1 eq 'TESTS');
        my $file = $2;
        print $shell ? '# ' : '' . "$file\tspecified in $makef does not exist"
          unless (-f "$dir/$file");
        ++ $sources{$file};
        ++ $hsources{$file} if ($file =~ s/\.(c|cxx|cc|cpp|C)$/.h/ && -f "$dir/$file");
      }
      while (s/^\s*(MANPAGE_SOURCES)\s*\:?\=\s*(\S+)/$1=/) {
        my $file = $2;
        print $shell ? '# ' : '' . "$file\tspecified in $makef does not exist"
          unless (-f "$dir/$file");
        ++ $hsources{$file};
        ++ $hsources{$file} if ($file =~ s/\.txx$/.h/ && -f "$dir/$file" && ! exists $hsources{$file});
      }
      while (s/^\s*(SOURCES|CL_SOURCES|MINI_PROG_SOURCES|TESTS)\s*\+\=\s*(\S+)/$1+=/)
      {
        my $file = $2;
        ++ $tests{$file} if ($1 eq 'TESTS');
        if ($file eq '${MOCSOURCES}') {
###--     foreach (grep { s/\.cxx$/_mocced.cxx/ && -f "$dir/$_" } keys %sources) { ++ $sources{$_}; }
        }
        else {
          print $shell ? '# ' : '' . "$file\tspecified in $makef does not exist"
            unless (-f "$dir/$file");
          ++ $sources{$file};
          if ($file =~ s/\.(c|cxx|cc|cpp|C)$/.h/) {
            ++ $hsources{$file} if (-f "$dir/$file" && ! exists $hsources{$file});
            # I.e.: add .h files which are not explicitly present in makefile
          }
        }
      }
      while (s/^\s*(MANPAGE_SOURCES)\s*\+\=\s*(\S+)/$1+=/)
      {
        my $file = $2;
        print $shell ? '# ' : '' . "$file\tspecified in $makef does not exist"
          unless (-f "$dir/$file");
        ++ $hsources{$file};
        if ($file =~ s/\.txx$/.h/) {
          ++ $hsources{$file} if (-f "$dir/$file" && ! exists $hsources{$file});
          # I.e.: add .h files which are not explicitly present in makefile
        }
      }
      %subdirs = () if (m/^\s*SUBDIRS\s*\:?\=/);
      ++ $subdirs{$2} while (s/^\s*(SUBDIRS)\s*\+?\:?\=\s*(\S+)/$1+=/);
      @aux_sources = split(/\s+/,$1) if (m/^\s*AUX_SOURCE_DIRECTORY\s*\:?\=\s*(.+)/);
      push @aux_sources, split(/\s+/,$1) if (m/^\s*AUX_SOURCE_DIRECTORY\s*\+\=\s*(.+)/);
    }
    close MAKEFILE;
    # add Templates to sources, if asked
    foreach my $subd (@aux_sources) {
      if (opendir TMPL,"$dir/$subd") {
        foreach (map { "$subd/$_" } grep {! m/^(\..*|CVS|\.svn)$/} readdir(TMPL)) { ++ $sources{$_}; }
        closedir(TMPL);
      }
    }
  }

  my $cmakef = $dir . '/CMakeLists.txt';
  my $cmakefile = '';
  my %source_files = ();
  my %test_files = ();
  my %hsource_files = ();
  my %names = qw(MAKECOMMAND make CMAKE_CURRENT_SOURCE_DIR $dir DIRECTSHOW_ESF_INCLUDE_DIR _ CMAKE_SYSTEM_PROCESSOR x86_64 vxl_SOURCE_DIR $V);
  if (open(CMAKEFILE, $cmakef)) {
    $cmakefile = join '',<CMAKEFILE>;
    close CMAKEFILE;
  }
  # "Exceptions": special cases, to avoid unnecessary warnings:
  $cmakefile =~ s/\#\s*(ADD_LIBRARY\(\s*bsta_io\s+\$\{)/$1/;            # i.e.: remove "#" in bsta/io
  $cmakefile =~ s/\#\s*(fax3sm_winnt\.c)/$1/;                           # i.e.: remove "#" in v3p/tiff
  $cmakefile =~ s/\#\s*(ADD_TEST\s*\(\s*vil1_test_save_load_image)/$1/; # i.e.: remove "#" in vil1/tests
  $cmakefile =~ s/(ADD_LIBRARY\(\S+\s+)MODULE\s+/$1/;                   # i.e.: remove "MODULE" in ADD_LIBRARY
  $cmakefile =~ s/\# (AUX_SOURCE_DIRECTORY)/$1/g if ($dir =~ m!\btbl/v.pl!); # add back Templates subdirs in tbl

  # General manipulations of the CMakeLists.txt content:
  $cmakefile =~ s/\s*\#[^\n]*//g;
  $cmakefile =~ s!\$\{EXECUTABLE_OUTPUT_PATH\}/!!g;
  my $moc_files = '';
  if ($cmakefile =~ s/\bQT_WRAP_CPP\s*\(\s*(\S+)\s+(\S+)\s+(.+?)\)//s) {
    $moc_files = $2;
###-- foreach (map {s/\.h$/_mocced.cxx/; $_} split(/\s+/, $3)) { ++ $source_files{$_}; }
  }
  $cmakefile =~ s/\$\{$moc_files\}//g;
  while ($cmakefile =~ s/\b(SET|FOREACH)\s*\(\s*(\S+)\s*(.*?)\)/$1@($2)/s)
  {
    my $f = $1; # $f is either SET or FOREACH
    my $n = $2; # $n is typically of the form ..._sources or ..._SOURCES
    my $l = $3; # the list of files constituting the definition of $n.
    next if $n =~ m/_(PREFIX|FLAGS|LIBRARIES|PATH|SOURCE_DIR|BINARY_DIR|INCLUDE_DIR)$/;
    next if $n =~ m/^[a-z0-9]+_test_include_sources$/;
    next if $n =~ m/^\${/;
    foreach ($l =~ m/\$\{(.*?)\}/g) { print $shell ? '# ' : '' . "$_ is an undefined variable in $cmakef" unless (defined $names{$_}); }
    while ($l =~ m/\$\{(.*?)\}/) {
      my $a = $1;
      my $b = $names{$a};
      unless (defined $b) { $b=''; warn "$a is an undefined variable in $cmakef"; }
      if ($a eq 'QT_SRCS_DIR' && $b =~ m!impl/qt$ !x) { my $c = "${b}4"; $l =~ s/\$\{$a\}(\S*)/$b$1 $c$1/g; }
      else { $l =~ s/\$\{$a\}/$b/g; }
    }
    # %names is a global hash list, first filled in here
    if ($f eq 'SET') { $names{$n} = $l; }
    if ($f eq 'FOREACH') {
      my ($pat) = $cmakefile =~ m/\bFOREACH@\($n\)\s*(.*?)ENDFOREACH\s*\(\s*$n\s*\)/s;
      foreach my $repl (split /\s+/,$l) {
	my $p = $pat; $p =~ s/\$\{$n\}/$repl/g;
        $cmakefile =~ s/\b(FOREACH@\($n\))/$p$1/;
      }
      $cmakefile =~ s/\bFOREACH@\($n\)\s*(.*?)ENDFOREACH\s*\(\s*$n\s*\)//s;
    }
  }
  while ($cmakefile =~ s/\bAUX_SOURCE_DIRECTORY\s*\(\s*(\S+)\s+(\S+)\s*\)//s)
  {
    my ($g, $n) = ($1, $2);
    if (opendir TMPL,"$dir/$g") {
      $names{$n} .= join '',map { " $g/$_" } grep {m/\.cxx$/} readdir(TMPL);
      closedir(TMPL);
    }
  }
  # now replace all variables by their content:
  foreach my $n (keys %names) { $cmakefile =~ s/\$\{$n\}/$names{$n}/g; }

  while ($cmakefile =~ s/\bGENERATE_TEST_DRIVER\s*\(\s*\S+\s+(\S+)(.*?)\)//s)
  {
    my $n = $1;
    # move "TEST" entries from %source_files to %test_files:
    $cmakefile .= "ADD_EXECUTABLE(x test_driver.cxx $names{$n})\n";
    foreach my $g (split /\s+/, $names{$n}) {
      $cmakefile .= "ADD_TEST(x x $g)\n" if ($g =~ s/\.cxx$//);
      foreach (grep { m/^$g$/ } keys %source_files) { delete $source_files{$_}; }
    }
  }
  while ($cmakefile =~ s/\bGENERATE_TEST_INCLUDE\s*\(.*?\)//s)
  {
    $cmakefile .= "ADD_EXECUTABLE(x test_include.cxx)\n";
  }
  # now try to do a reasonable guess about the IF .. ELSE .. ENDIF constructs:
  while ($cmakefile =~ s/\bIF\s*\(\s*([A-Z0-9_]+_FOUND)\s*\)(.*?)ELSE\s*\(\s*\1\s*\).*?ENDIF\s*\(\s*\1\s*\)/$2/gs) {}
  while ($cmakefile =~ s/\bIF\s*\(\s*(HAS_[A-Z0-9_]+)\s*\)(.*?)ELSE\s*\(\s*\1\s*\).*?ENDIF\s*\(\s*\1\s*\)/$2/gs) {}
  while ($cmakefile =~ s/\bIF\s*\(\s*(BUILD_[A-Z0-9_]+)\s*\)(.*?)ELSE\s*\(\s*\1\s*\).*?ENDIF\s*\(\s*\1\s*\)/$2/gs) {}
  while ($cmakefile =~ s/\bIF\s*\(\s*(NOT.*?)\)(.*?)ELSE\s*\(\s*\1\s*\).*?ENDIF\s*\(\s*\1\s*\)/$2/gs) {}
  while ($cmakefile =~ s/\bIF\s*\(\s*(.*?)\).*?ELSE\s*\(\s*\1\s*\)(.*?)ENDIF\s*\(\s*\1\s*\)/$2/gs) {}

  # remove "WIN32" or "MACOSX_BUNDLE" options from ADD_EXECUTABLE()":
  $cmakefile =~ s/\b(ADD_EXECUTABLE\s*\(\s*\S+)\s+(WIN32\s+)?(MACOSX_BUNDLE\s+)?([^)]+\))/$1 $4/g;

  while ($cmakefile =~ s/\bADD_LIBRARY\s*\(\s*\S+\s+(.*?)\)//s)
  {
    my $g = $1; # list of file names
    $g =~ s/^SHARED\s+//; # ignore optional keyword "SHARED", if present
    foreach (split /\s+/, $g) {
      if (m/^\s*$/) { next; }
      elsif (-f "$dir/$_" && m/\.(h(\.in)?|[th]xx)$/) { ++ $hsource_files{$_}; }
      elsif (-f "$dir/$_")                            { ++ $source_files{$_}; }
      elsif (-f "$dir/$_.cxx") { print "Should replace $_ by $_.cxx in $cmakef"; ++ $source_files{"$_.cxx"}; }
      elsif (-f "$dir/$_.c")   { print "Should replace $_ by $_.c in $cmakef";   ++ $source_files{"$_.c"}; }
      elsif (-f "$dir/$_.cc")  { print "Should replace $_ by $_.cc in $cmakef";  ++ $source_files{"$_.cc"}; }
      elsif (-f "$dir/$_.C")   { print "Should replace $_ by $_.C in $cmakef";   ++ $source_files{"$_.C"}; }
      elsif (-f "$dir/$_.cpp") { print "Should replace $_ by $_.cpp in $cmakef"; ++ $source_files{"$_.cpp"}; }
      else                     { print "No source file $_.* found in $dir"; }
      ++ $hsource_files{$1} if (m/^(.*\.h)\.in$/ && -f "$dir/$1" && ! exists $hsource_files{$1}); # generated .h file
    }
  }
  while ($cmakefile =~ s/\bINSTALL_NOBASE_HEADER_FILES\s*\(\s*\S+\s+(.*?)\)//s)
  {
    my $g = $1; # list of file names
    foreach my $x (grep {! m/^\s*$/} split /\s+/, $g) {
      if (! -f "$dir/$x") { print $shell ? '# ' : '' . "file $x listed in INSTALL_NOBASE_HEADER_FILES does not exist"; }
      elsif ($x =~ m/\.(h|[th]xx|in)$/) { ++ $hsource_files{$x} unless (exists $hsource_files{$x}); }
    }
  }
  while ($cmakefile =~ s/\bADD_EXECUTABLE\s*\(\s*(\S+)((\s+[^\s()]+)+)\s*\)//)
  {
    my $name = $1; $_ = $2; s/\$\{(\S+)\}/$1/g;
    if (m/^\s*\S+_sources$/) {
      $names{$name} = join '',keys %source_files if (%source_files);
      next;
    }
    $names{$name} = $_;
    $names{$name} =~ s/\bbprb_test_process\.(h|cxx)\b//g;
    foreach (split /\s+/) {
      next if ($_ eq '');
      if (m/_sources$/) {
        $names{$name} = join '',keys %source_files if (%source_files);
        next;
      }
      if (m/\.(h(\.in)?|[th]xx)$/) { ++ $hsource_files{$_}; }
      elsif (-f "$dir/$_")         { ++ $source_files{$_}; }
      elsif (-f "$dir/$_.cxx")  { print "Should replace $_ by $_.cxx in $cmakef"; ++ $source_files{"$_.cxx"}; }
      elsif (-f "$dir/$_.c")    { print "Should replace $_ by $_.c in $cmakef";   ++ $source_files{"$_.c"}; }
      elsif (-f "$dir/$_.cc")   { print "Should replace $_ by $_.cc in $cmakef";  ++ $source_files{"$_.cc"}; }
      elsif (-f "$dir/$_.C")    { print "Should replace $_ by $_.C in $cmakef";   ++ $source_files{"$_.C"}; }
      elsif (-f "$dir/$_.cpp")  { print "Should replace $_ by $_.cpp in $cmakef"; ++ $source_files{"$_.cpp"}; }
      else                      { print "No source file $_.* found in $dir"; }
      ++ $hsource_files{$1} if (m/^(.*\.h)\.in$/ && -f "$dir/$1");
    }
  }
  while ($cmakefile =~ s/\bADD_TEST\s*\(\s*\S+\s+(\S+)\s*(([^\s()]+)?)((\s+[^()]+)?)\s*\)//s)
  {
    my $name = $1; my $tstname = $2;
    if (! exists ($names{$name}) ) {
      print $shell ? '# ' : '' . "ADD_TEST($name) without ADD_EXECUTABLE($name)\tin $cmakef";
      next;
    }
    unless ($testmain) {
      foreach (grep { m/\.(cxx|c|cc|C|cpp)$/ } split /\s+/, $names{$name}) { ++ $test_files{$_}; }
    }
    else {
      ++ $test_files{"$tstname.cxx"} if ($tstname ne '' && -f "$dir/$tstname.cxx");
    }
  }
  ++ $subdirs2{$1}
    while ($cmakefile =~ s/\bSUBDIRS\s*\(\s*([^\s\)]+)/SUBDIRS(/);

  next unless ($f || -f $makef || -f $cmakef);
  $f = "dsps/$dir/*.dsp" unless ($f);
  print $shell ? '# ' : '' . "File $makef does not exist" unless (-f $makef);
  print $shell ? '# ' : '' . "File $cmakef does not exist" unless (-f $cmakef || $cmakef =~ m!/doc/book/CMakeLists\.txt$ !x);

# ======= Here starts the comparison of the file lists ===========

  # === compare file lists (%source, %sources, %source_files) ===
  foreach my $s (keys %source_files)
  {
    if ($source_files{$s} > 1) {
      &duplicate_message($s, $cmakef, '# !!')
        unless ($s eq 'impl/glut/menu_hack_none.cxx' ||
                $s eq 'my_pyramid.cxx' ||
                $s eq 'baio_unix.cxx' ||
                $s eq 'baio_windows.cxx')
    }
    delete $source_files{$s};
    unless (exists $sources{$s})
    {
      if (-f "$dir/$s") { &missing_message("SOURCES += $s", $makef); }
      else              { &removing_message($s, $cmakef, '# !!'); }
    }
    elsif ($sources{$s} > 1) {
      &duplicate_message("SOURCES += $s", $makef, '# !!');
    }
    delete $sources{$s};
    unless (exists $source{$s})
    {
      if (-f "$dir/$s") { &missing_message("SOURCE=.\\$s", $f)
                            unless ($s eq 'tif_unix.c' ||
                                    $s eq 'menu_hack_X11.cxx' ||
                                    $s eq 'internals/vgui_accelerate_x11.cxx'); }
      else              { &removing_message($s, $cmakef, '# !!'); }
###--                       unless ($s =~ m/_mocced\.cxx$/); }
    }
    elsif ($source{$s} > 1) {
      &duplicate_message($s, $f, '!!');
    }
    delete $source{$s};
  }
  # at this point, %source_files has been emptied.

  foreach my $s (keys %sources) # should be empty by now!
  {
    if ($sources{$s} > 1) {
      &duplicate_message("SOURCES += $s", $makef, '# !!');
    }
    delete $sources{$s};
    if (-f "$dir/$s") { &missing_message($s, $cmakef); }
###--                          unless ($s =~ m/_mocced\.cxx$/);
    else              { &removing_message("SOURCES += $s", $makef, '# !!'); }
###--                          unless ($s eq '${MOCSOURCES}');
  }
  # at this point, %sources has been emptied.

  foreach my $s (keys %source) # should be empty by now!
  {
    if ($source{$s} > 1) {
      &duplicate_message("SOURCE=.\\$s", $f, '!!');
    }
    delete $source{$s};
    if (-f "$dir/$s") { &missing_message($s, $cmakef); &missing_message("SOURCES += $s", $makef); }
    else              { &removing_message("SOURCE=.\\$s", $f, '!!'); }
  }
  # at this point, %source has been emptied.

  # === compare "tests" file lists (%tests, %test_files) ===
  foreach my $s (keys %test_files)
  {
    if ($test_files{$s} > 1) {
      &duplicate_message("ADD_TEST($s)", $cmakef, '# !!');
    }
    delete $test_files{$s};
    unless (exists $tests{$s})
    {
      if (-f "$dir/$s") { &missing_message("TESTS += $s", $makef); }
      else              { &removing_message("ADD_TEST($s)", $cmakef, '# !!'); }
    }
    elsif ($tests{$s} > 1) {
      &duplicate_message("TESTS += $s", $makef, '# !!');
    }
    delete $tests{$s};
  }
  # at this point, %test_files has been emptied.

  foreach my $s (keys %tests) # should be empty by now!
  {
    if ($tests{$s} > 1) {
      &duplicate_message("TESTS += $s", $makef, '# !!');
    }
    delete $tests{$s};
    if (-f "$dir/$s") { &missing_message("ADD_TEST($s)", $cmakef); }
    else              { &removing_message("TESTS += $s", $makef, '# !!'); }
  }
  # at this point, %tests has been emptied.

  # === compare header file lists (%hsource, %hsources, %hsource_files, %hfiles) ===
  foreach my $s (keys %hsource_files)
  {
    if ($hsource_files{$s} > 1) {
      &duplicate_message($s, $cmakef, '# !!')
        unless ($s =~ m/\bFl_(blender|movie|twowin)_ui\.h$/ ||
                $s eq 'my_pyramid.h' || $s eq 'my_pyramid_functions.txx' || $s eq 'baio.h');
    }
    delete $hsource_files{$s};
    unless (exists $hsources{$s})
    {
      if (-f "$dir/$s") { &missing_message("MANPAGE_SOURCES += $s", $makef); }
      else              { &removing_message("$s", $cmakef, '# !!'); }
    }
    elsif ($hsources{$s} > 1) {
      &duplicate_message("MANPAGE_SOURCES += $s", $makef, '# !!');
    }
    delete $hsources{$s};
    unless (exists $hfiles{$s})
    {
      if (!-f "$dir/$s") { &removing_message("$s", $cmakef, '# !!'); }
#     else               { print "$s\tis missing from filelist in $0 for $dir" unless ($shell); }
    }
    delete $hfiles{$s};
    unless (exists $hsource{$s})
    {
      if (-f "$dir/$s") { &missing_message("SOURCE=.\\$s", $f); }
      else              { &removing_message("$s", $cmakef, '# !!'); }
    }
    elsif ($hsource{$s} > 1) {
      &duplicate_message("SOURCE=.\\$s", $f, '!!');
    }
    delete $hsource{$s};
  }
  # at this point, %hsource_files has been emptied.

  foreach my $s (keys %hfiles) # should be empty by now!
  {
    delete $hfiles{$s};
    unless (exists $hsources{$s})
    {
      &missing_message("MANPAGE_SOURCES += $s", $makef);
    }
    elsif ($hsources{$s} > 1) {
      &duplicate_message("MANPAGE_SOURCES += $s", $makef, '# !!');
    }
    delete $hsources{$s};
    unless (exists $hsource{$s})
    {
      &missing_message("SOURCE=.\\$s", $f)
    }
    elsif ($hsource{$s} > 1) {
      &duplicate_message("SOURCE=.\\$s", $f, '!!');
    }
    delete $hsource{$s};
  }
  # at this point, %hfiles has been emptied.

  foreach my $s (keys %hsources) # should be empty by now!
  {
    if ($hsources{$s} > 1) {
      &duplicate_message("MANPAGE_SOURCES += $s", $makef, '# !!');
    }
    delete $hsources{$s};
    if (-f "$dir/$s") { &missing_message($s, $cmakef); }
    else              { &removing_message("MANPAGE_SOURCES += $s", $makef, '# !!'); }
  }
  # at this point, %hsources has been emptied.

  foreach my $s (keys %hsource) # should be empty by now!
  {
    if ($hsource{$s} > 1) {
      &duplicate_message("SOURCE=.\\$s", $f, '!!');
    }
    delete $hsource{$s};
    if (-f "$dir/$s") { &missing_message($s, $cmakef); &missing_message("SOURCES += $s", $makef); }
    else              { &removing_message("SOURCE=.\\$s", $f, '!!'); }
  }
  # at this point, %hsource has been emptied.

  # === compare SUBDIRS lists (%subdirs, %subdirs2) ===
  foreach my $s (keys %subdirs2)
  {
    if ($subdirs2{$s} > 1) {
      &duplicate_message("SUBDIRS($s)", $cmakef, '# !!')
    }
    delete $subdirs2{$s};
    unless (exists $subdirs{$s})
    {
      if (-d "$dir/$s") { &missing_message("SUBDIRS += $s", $makef); }
      else              { &removing_message("SUBDIRS($s)", $cmakef, '# !!'); }
    }
    elsif ($subdirs{$s} > 1) {
      &duplicate_message("SUBDIRS += $s", $makef, '# !!');
    }
    delete $subdirs{$s};
  }
  # at this point, %subdirs2 has been emptied.

  foreach my $s (keys %subdirs) # should be empty by now!
  {
    if ($subdirs{$s} > 1) {
      &duplicate_message("SUBDIRS += $s", $makef, '# !!');
    }
    delete $subdirs{$s};
    if (-d "$dir/$s") { &missing_message("SUBDIRS($s)", $cmakef); }
    else              { &removing_message("SUBDIRS += $s", $makef, '# !!'); }
  }
  # at this point, %subdirs has been emptied.
}

sub missing_message
{
  my ($txt, $file) = @_; $txt =~ s/\\/\\\\/g if ($shell);
  my $dsp = ($file =~ m/\.dsp$/) ? 1 : '';
  if ($shell && $dsp) { print "echo '# Begin Source File\r' >>\t$file\necho '$txt\r' >>\t$file\necho '# End Source File\r' >>\t$file"; }
  elsif ($shell) { print "echo '$txt' >>\t$file"; }
  else { print "$txt\tis missing from $file"; }
}

sub removing_message
{
  my ($txt, $file, $comment) = @_; $txt =~ s/\\/\\\\/g if ($shell);
  my $eoln = ($file =~ m/\.dsp$/) ? "\r" : '';
  if ($shell) { print "echo '$comment remove $txt$eoln' >>\t$file"; }
  else { print "$txt\tmust be removed from $file"; }
}

sub duplicate_message
{
  my ($txt, $file, $comment) = @_; $txt =~ s/\\/\\\\/g if ($shell);
  my $eoln = ($file =~ m/\.dsp$/) ? "\r" : '';
  if ($shell) { print "echo '$comment remove duplicate $txt$eoln' >>\t$file"; }
  else { print "$txt\t(duplicate) must be removed from $file"; }
}