From: Bob Rogers <[EMAIL PROTECTED]>
   Date: Sat, 28 Jul 2007 16:30:22 -0400

      From: "jerry gay" <[EMAIL PROTECTED]>
      Date: Sat, 28 Jul 2007 08:35:01 -0700

      . . .

      Parrot::Distribution offers a number of subroutines which allow one to
      specify which subset of parrot files you wish to select for testing.
      this is built into the tests, and if everything is working as it
      should (and i believe it is,) it means that calling the proper
      function from this distribution will provide the proper subset of
      files (e.g. c_language_files.)

   I want to go in the other direction, i.e. what tests should I apply for
   a specific C source file, along the lines of the attached patch.  It
   needs doc, of course, and the correctness of %tests_from_source_category
   is debatable, but that's the general idea.

I forgot to say "thanks" for pointing out the best place to add a "what
kind of file is this?" routine.

      Beyond that, it would be nice if the tests themselves output
   something close to the standard "grep -n" output, i.e. "file:line:" at
   the start of each "hit" line.  That way, if run under Emacs, the
   resulting output buffer could be put into grep-mode, which would make it
   easy to visit these "hits."

Proof of concept attached, better documented this time.  This makes it
easy, for instance, for Emacs to jump to the "fixme" problem in
src/inter_call.c, but the "incorrect spacing between parentheses"
problem doesn't have useful line numbers (and would be hard to fix).

      > P.S.  I have been having problems with getting parrot-porters to accept
      >       my posts, so I don't expect this to appear on the list.

      received just fine :)

   Huh!?  How could it be that you get a copy from the list and not me?  I
   seem to be getting everybody else's posts . . .

Duh.  It's because I had "enhanced" my email forgery detector . . .

                                        -- Bob

* tools/dev/check-coding-stds.pl (added):
   + Check files specified on the command line for coding standard
     compliance.  All appropriate tests are run for each file, based on
     internal rules for each possible classify_file result.
* lib/Parrot/Distribution.pm:
   + (classify_file):  Return source category for a given file.
* editor/parrot.el:
   + (parrot-check-coding-standards):  Do a "grep" using
     tools/dev/check-coding-stds.pl on buffer-file-name.

Diffs between last version checked in and current workfile(s):

Index: tools/dev/check-coding-stds.pl
===================================================================
--- tools/dev/check-coding-stds.pl      (revision 0)
+++ tools/dev/check-coding-stds.pl      (revision 0)
@@ -0,0 +1,145 @@
+#! perl
+# Copyright (C) 2007, The Perl Foundation.
+# $Id:$
+
+# Docs at the bottom.
+
+use strict;
+use warnings;
+
+use lib 'lib';
+
+use Parrot::Distribution;
+use Getopt::Long;
+
+my $dist = Parrot::Distribution->new();
+my $verbose_p = 0;
+
+GetOptions('verbose+' => \$verbose_p)
+    or usage();
+
+usage()
+    unless @ARGV;
+
+### Subroutines.
+
+sub usage {
+    die "Usage:  $0 file ...\n";
+}
+
+my @common_tests   = qw(fixme.t);
+my @c_tests        = qw(c_code_coda.t c_indent.t c_struct.t cppcomments.t
+                        tabs.t c_parens.t trailing_space.t);
+my %tests_from_source_category
+    = (c_source    => [ @common_tests, @c_tests,
+                       qw(check_toxxx.t check_isxxx.t cuddled_else.t) ],
+       c_header           => [ @common_tests, @c_tests, qw(c_header_guards.t) 
],
+       perl_source => [ @common_tests, "perlcritic.t --listfiles" ],
+       pir_source  => [ @common_tests, qw(pir_code_coda.t) ],
+       pmc_source  => [ @common_tests ],
+       yacc_source => [ @common_tests ],
+       lex_source  => [ @common_tests ],
+       ops_source  => [ @common_tests ],
+    );
+
+my $parrot_home_directory = $dist->{_path}
+    or die;
+my $t_codingstd_dir = "$parrot_home_directory/t/codingstd";
+
+sub test_files_for_category {
+    my $category = shift;
+
+    my $tests = $tests_from_source_category{$category};
+    return
+       unless $tests;
+    return map { "$t_codingstd_dir/$_"; } @$tests;
+}
+
+### Main code.
+
+my $n_error_lines = 0;
+for my $file (@ARGV) {
+    my $class = $dist->classify_file($file);
+    if (! $class) {
+       warn "Can't classify '$file'";
+       next;
+    }
+    warn("Processing $file (category $class):\n")
+       if $verbose_p;
+    for my $test (test_files_for_category($class)) {
+       warn("   Running test $test\n")
+           if $verbose_p;
+       open(my $test_output, "perl '$test' '$file' 2>&1 |")
+           or die;
+       # Attempt to present each test failure in "grep -n" style, for
+       # postprocessing by other tools.
+       while (defined(my $line = <$test_output>)) {
+           next
+               unless $line =~ s/^# *//;
+           $line =~ s/file '([^'']+)', line (\d+):/$1:$2:/;
+           print $line;
+           $n_error_lines++;
+       }
+    }
+}
+# This looks wrong, but see the documentation.
+exit($n_error_lines ? 0 : 1);
+
+__END__
+
+=head1 NAME
+
+tools/dev/check-coding-stds.pl - Check files for coding standard compliance
+
+=head1 SYNOPSIS
+
+    % perl tools/dev/check-coding-stds.pl [--verbose] file ...
+
+=head1 DESCRIPTION
+
+The C<check-coding-stds.pl> script runs all relevant coding standard
+tests for each file named on the command line.  The tests are chosen
+based on the file type (e.g. C<c_header>, C<perl_source> identified
+for each file.
+
+The output of each test is postprocessed to strip away details of the
+C<Test::More> mechanism and make specific coding standard violations
+easier to find.  Specifically, the output from normal tests is thrown
+away completely, and the failures reported on stderr are converted
+where possible into the standard "file:line:" format produced by
+C<grep -n> for further processing by other tools.
+
+As C<grep> does (perhaps counterintuitively), C<check-coding-stds.pl>
+exits with code 0 if one or more violations was found, 1 if all files
+are in full compliance, and 255 if an error happened.
+
+=head2 Options
+
+=over 4
+
+=item C<--verbose>
+
+If specified, emits warning messages for each file and test.  Since
+successful tests produce no output, this is useful for figuring out
+which tests are being run.
+
+=back
+
+=head1 SEE ALSO
+
+=over 4
+
+=item C<t/codingstd/*.t>
+
+=item C<editor/parrot.el>
+
+=back
+
+=cut
+
+# Local Variables:
+#   mode: cperl
+#   cperl-indent-level: 4
+#   fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4:

Property changes on: tools/dev/check-coding-stds.pl
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:keywords
   + Author Date Id Revision

Index: lib/Parrot/Distribution.pm
===================================================================
--- lib/Parrot/Distribution.pm  (revision 20226)
+++ lib/Parrot/Distribution.pm  (working copy)
@@ -86,6 +86,8 @@
     _croak( undef, "Failed to find Parrot distribution root\n" )
         unless $self;
 
+    $self->{_path} = $path;
+
     if ( defined $dist ) {
         $self->_dist_files(
             [
@@ -178,6 +180,7 @@
 =cut
 
 BEGIN {
+    # NB:  This hash gets decorated in the loop below.
     my %file_class = (
         source => {
             c   => { file_exts => ['c'] },
@@ -217,9 +220,10 @@
                 '|' => map { qr{\b$_\b} }
                 map { quotemeta($_) } @ignore_dirs,
                 @exceptions;
+           # Save these for future use.
+           $file_class{$class}{$type}{extension_filter_re} = $filter_ext;
+           $file_class{$class}{$type}{directory_filter_re} = $filter_dir;
 
-            next unless $method;
-
             *{ $method . '_file_directories' } = sub {
                 my $self = shift;
 
@@ -276,6 +280,27 @@
             };
         }
     }
+
+    sub classify_file {
+       # Return the first applicable file classification.  (So far, each file
+       # fits exactly one classification.)
+       my ($self, $file) = @_;
+
+       my @result;
+       for my $class ( keys %file_class ) {
+           for my $type ( keys %{ $file_class{$class} } ) {
+               my $filter_ext
+                   = $file_class{$class}{$type}{extension_filter_re};
+               next
+                   unless $file =~ m|(?i)(?:$filter_ext)|;
+               my $filter_dir
+                   = $file_class{$class}{$type}{directory_filter_re};
+               next
+                   if $file =~ m|(?:$filter_dir)|;
+               return join('_', $type, $class);
+           }
+       }
+    }
 }
 
 =item C<get_c_language_files()>
Index: editor/parrot.el
===================================================================
--- editor/parrot.el    (revision 20226)
+++ editor/parrot.el    (working copy)
@@ -22,3 +22,61 @@
                                    (statement-case-intro . *)
                                    (inextern-lang . 0)
                                    ))))
+
+(defun parrot-home-directory-p (directory)
+  (let* ((file-name (expand-file-name "README" directory))
+        (buffer (get-file-buffer file-name))
+        (delete-p (null buffer)))
+    (unwind-protect 
+        (save-excursion
+          (if (and (not buffer)
+                   (file-readable-p file-name))
+              (setq buffer (find-file-noselect file-name)))
+          (if (and buffer
+                   (set-buffer buffer)
+                   (search-forward "This is Parrot" nil t))
+              directory))
+      (if (and buffer delete-p)
+         (kill-buffer buffer)))))
+
+(defun parrot-home-directory ()
+  "Find a directory at or above the current one which serves at the root of
+the current parrot working copy.  It would be nice to cache this, but that
+would make working with multiple WCs a pain."
+  (or (parrot-home-directory-p ".")
+      (parrot-home-directory-p "..")
+      (let* ((current default-directory)
+            (result nil))
+       (while (> (length current) 1)
+         (if (setq result (parrot-home-directory-p current))
+             (setq current nil)
+             (setq current
+                   (and (string-match "[^/]+/$" current)
+                        (replace-match "" t t current)))))
+       result)))
+
+;; (parrot-home-directory)
+
+(defun parrot-check-coding-standards ()
+  "Run t/codingstd/*.t tests appropriate for the current buffer's file.
+The resulting 'hits', if they have line numbers, can be stepped through
+via \\[next-error] (next-error).  Do 'C-h f compilation-mode RET' to see other
+commands that are available in the compilation buffer."
+  (interactive)
+  (or buffer-file-name
+      (error "Can't do this on %s, which is not a file buffer."
+            (current-buffer)))
+  (and (buffer-modified-p)
+       (y-or-n-p (format "%s is modified; save first? " (current-buffer)))
+       (save-buffer))
+  (require 'compile)
+  (let* ((home (or (parrot-home-directory)
+                  (error "%S is not a Parrot working directory."
+                         default-directory)))
+        (command-name (expand-file-name "tools/dev/check-coding-stds.pl" home))
+        (lib-name (expand-file-name "lib" home))
+        (command (concat "perl -Mlib=" lib-name " "
+                         command-name " " buffer-file-name)))
+    '(message "executing %S with home %S, default %S"
+            command home default-directory)
+    (compilation-start command 'grep-mode)))

End of diffs.

Reply via email to