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

RFC: various Lintian checks for OCaml packages



retitle 528367 new Lintian checks for OCaml packages
tags 528367 + patch
thanks

Stephane Glondu a écrit :
> It would be nice to emit a Lintian error in the following circumstances:
> [...]

There are of course many other checks that we (OCaml maintainers) would
like Lintian to perform. Attached are some of them.

I am sending this right now to get some feedback from the Lintian
maintainers about the coding style, the structure of the code, etc.


Cheers,

-- 
Stéphane
From cd01e78475309be7e411ffe94835a024861b0de7 Mon Sep 17 00:00:00 2001
From: Stephane Glondu <steph@glondu.net>
Date: Mon, 20 Jul 2009 10:01:41 +0200
Subject: [PATCH] Add ar-info collection script

To detect dangling .cmx (#528367), we need to know the list of files
inside .a files. This information might useful for other, not
OCaml-related, checks.

Signed-off-by: Stephane Glondu <steph@glondu.net>
---
 collection/ar-info      |   54 +++++++++++++++++++++++++++++++++++++++++++++++
 collection/ar-info.desc |    7 ++++++
 2 files changed, 61 insertions(+), 0 deletions(-)
 create mode 100755 collection/ar-info
 create mode 100644 collection/ar-info.desc

diff --git a/collection/ar-info b/collection/ar-info
new file mode 100755
index 0000000..c88ce25
--- /dev/null
+++ b/collection/ar-info
@@ -0,0 +1,54 @@
+#!/usr/bin/perl -w
+# ar-info -- lintian collection script
+#
+# Copyright © 2009 Stéphane Glondu
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, you can find it on the World Wide
+# Web at http://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+use strict;
+
+($#ARGV == 1) or fail("syntax: ar-info <pkg> <type>");
+my $pkg = shift;
+my $type = shift;
+
+-f "fields/source" or fail("ar-info invoked in wrong directory");
+
+unlink("ar-info");
+chdir("unpacked")
+    or fail("cannot chdir to unpacked directory: $!");
+
+open(INDEX, '<', "../index")
+    or fail("cannot open index file: $!");
+open(OUT, '>', "../ar-info");
+
+while (<INDEX>) {
+    chomp;
+    next unless /\.a$/;
+    my $file = (split(" ", $_, 6))[5];
+    open(OBJS, '-|', "ar t $file")
+        or fail("cannot read file: $!");
+    print OUT "$file:";
+    while (<OBJS>) {
+        chomp;
+        print OUT " $_";
+    }
+    close(OBJS);
+    print OUT "\n";
+}
+
+close(INDEX);
+close(OUT);
diff --git a/collection/ar-info.desc b/collection/ar-info.desc
new file mode 100644
index 0000000..eaecfcf
--- /dev/null
+++ b/collection/ar-info.desc
@@ -0,0 +1,7 @@
+Collector-Script: ar-info
+Author: Stéphane Glondu <steph@glondu.net>
+Info: This script runs the "ar t" command over all .a files of package.
+Type: binary
+Unpack-Level: 2
+Version: 1
+Order: 1
-- 
1.6.3.3

From 097ef9c668da753f6ea0636f9669d7285cab2412 Mon Sep 17 00:00:00 2001
From: Stephane Glondu <steph@glondu.net>
Date: Mon, 20 Jul 2009 10:01:31 +0200
Subject: [PATCH] Add various checks about OCaml binary packages

New lintian check script checks/ocaml{,.desc}:
 * ocaml-dangling-cmx, ocaml-dangling-cmxa (Closes: #528367)
 * ocaml-dangling-cmi
 * ocaml-dangling-cmxs
 * ocaml-stray-cmo
 * ocaml-dev-file-not-in-usr-lib-ocaml
 * ocaml-dev-file-in-nondev-package
 * ocaml-meta-without-suggestion-to-findlib

Signed-off-by: Stephane Glondu <steph@glondu.net>
---
 checks/ocaml      |  185 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 checks/ocaml.desc |   65 +++++++++++++++++++
 2 files changed, 250 insertions(+), 0 deletions(-)
 create mode 100644 checks/ocaml
 create mode 100644 checks/ocaml.desc

diff --git a/checks/ocaml b/checks/ocaml
new file mode 100644
index 0000000..2c62817
--- /dev/null
+++ b/checks/ocaml
@@ -0,0 +1,185 @@
+# ocaml -- lintian check script -*- perl -*-
+#
+# Copyright © 2009 Stéphane Glondu
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, you can find it on the World Wide
+# Web at http://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::ocaml;
+use strict;
+use Tags;
+use File::Basename;
+
+sub run {
+
+my $pkg = shift;
+my $type = shift;
+my $info = shift;
+
+# Collect information about .a files from ar-info dump
+my %provided_o;
+open ARINFO, "ar-info";
+while (<ARINFO>) {
+    chomp;
+    if (/^\.\/([^:]+): (.*)$/) {
+        my $filename = $1;
+        my $dirname = dirname($filename);
+        foreach (split(" ", $2)) {
+            # Note: a .o may be legitimately in several different .a
+            $provided_o{"$dirname/$_"} = $filename;
+        }
+    }
+}
+close ARINFO;
+
+# is it a library package?
+my $is_lib_package = 0;
+if ($pkg =~ /^lib/) {
+    $is_lib_package = 1;
+}
+
+# is it a development package?
+my $is_dev_package = 0;
+if ($pkg =~ /(-dev|^camlp[45](-extra)?|^ocaml(-nox|-interp|-compiler-libs)?)$/) {
+    $is_dev_package = 1;
+}
+
+# for libraries outside /usr/lib/ocaml
+my $outside_number = 0;
+my $outside_prefix;
+
+# dangling .cmi files (we show only $max_cmi of them)
+my $cmi_number = 0;
+my $max_cmi = 3;
+
+# dev files in nondev package
+my $dev_number = 0;
+my $dev_prefix;
+
+# does the package provide a META file?
+my $has_meta = 0;
+
+foreach my $file (sort keys %{$info->file_info}) {
+    my $fileinfo = $info->file_info->{$file};
+
+    # For each .cmxa file, there must be a matching .a file (#528367)
+    $_ = $file;
+    if (s/\.cmxa$/.a/ && !(exists $info->file_info->{$_})) {
+        tag "ocaml-dangling-cmxa", $file;
+    }
+
+    # For each .cmxs file, there must be a matching .cma or .cmo file
+    # (at least, in library packages)
+    if ($is_lib_package) {
+        $_ = $file;
+        if (s/\.cmxs$/.cm/
+            && !(exists $info->file_info->{"${_}a"})
+            && !(exists $info->file_info->{"${_}o"})) {
+            tag "ocaml-dangling-cmxs", $file;
+        }
+    }
+
+    # The .cmx counterpart: for each .cmx file, there must be a
+    # matching .o file, which can be there by itself, or embedded in a
+    # .a file in the same directory
+    $_ = $file;
+    if (s/\.cmx$/.o/
+        && !(exists $info->file_info->{$_})
+        && !(exists $provided_o{$_})) {
+        tag "ocaml-dangling-cmx", $file;
+    }
+
+    # $somename.cmi should be shipped with $somename.mli or $somename.ml
+    $_ = $file;
+    if ($is_dev_package && s/\.cmi$/.ml/
+        && !(exists $info->file_info->{"${_}i"})
+        && !(exists $info->file_info->{$_})) {
+        $cmi_number++;
+        if ($cmi_number <= $max_cmi) {
+            tag "ocaml-dangling-cmi", $file;
+        }
+    }
+
+    # non-dev packages should not ship .cmi, .cmx or .cmxa files
+    if ($file =~ m/\.cm(i|xa?)$/) {
+        $dev_number++;
+        if (defined $dev_prefix) {
+            chop $dev_prefix while ($file !~ m@^$dev_prefix@);
+        } else {
+            $dev_prefix = $file;
+        }
+    }
+
+    # $somename.cmo should usually not be shipped with $somename.cma
+    $_ = $file;
+    if (s/\.cma$/.cmo/ && exists $info->file_info->{$_}) {
+        tag "ocaml-stray-cmo", $file;
+    }
+
+    # development files outside /usr/lib/ocaml (.cmi, .cmx, .cmxa)
+    # .cma, .cmo and .cmxs are excluded because they can be plugins
+    if ($file =~ m/\.cm(i|xa?)$/ && $file !~ m@^usr/lib/ocaml/@) {
+        $outside_number++;
+        if (defined $outside_prefix) {
+            chop $outside_prefix while ($file !~ m@^$outside_prefix@);
+        } else {
+            $outside_prefix = $file;
+        }
+    }
+
+    # if there is a META file, ocaml-findlib should be at least
+    # suggested
+    $has_meta = 1 if $file =~ m@/META(\..*)?$@;
+
+}
+
+if ($is_dev_package) {
+    # summary about .cmi files
+    if ($cmi_number > $max_cmi) {
+        tag "ocaml-dangling-cmi", ($cmi_number-$max_cmi), "more file(s) not shown";
+    }
+    # summary about /usr/lib/ocaml
+    if ($outside_number) {
+        $outside_prefix = dirname($outside_prefix);
+        tag "ocaml-dev-file-not-in-usr-lib-ocaml", "$outside_number file(s) in /$outside_prefix";
+    }
+    if ($has_meta) {
+        my $findlib = 0;
+        for my $field (qw(depends recommends suggests)) {
+            if (defined $info->field($field)) {
+                $_ = $info->field($field);
+                $findlib = 1 if /ocaml-findlib/;
+            }
+        }
+        tag "ocaml-meta-without-suggestion-to-findlib" unless $findlib;
+    }
+} else {
+    # summary about dev files
+    if ($dev_number > 0) {
+        $dev_prefix = dirname($dev_prefix);
+        tag "ocaml-dev-file-in-nondev-package", "$dev_number file(s) in /$dev_prefix";
+    }
+}
+
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: ts=8 sw=4 noet syntax=perl
diff --git a/checks/ocaml.desc b/checks/ocaml.desc
new file mode 100644
index 0000000..8d21a50
--- /dev/null
+++ b/checks/ocaml.desc
@@ -0,0 +1,65 @@
+Check-Script: ocaml
+Author: Stephane Glondu <steph@glondu.net>
+Abbrev: ocaml
+Type: binary
+Unpack-Level: 1
+Info: This looks for common mistakes in OCaml binary packages.
+Needs-Info: file-info, ar-info
+
+Tag: ocaml-dangling-cmxa
+Severity: serious
+Certainty: certain
+Info: This package provides a .cmxa library without its implementation
+ (.a static library).
+
+Tag: ocaml-dangling-cmx
+Severity: serious
+Certainty: certain
+Info: This package provides a .cmx module without its implementation
+ (.o object file which may be embedded in a .a static library
+ installed in the same directory).
+
+Tag: ocaml-dangling-cmxs
+Severity: normal
+Certainty: possible
+Info: This package seems to be a library package, and provides a
+ native plugin (.cmxs). If the plugin is meant to be used as a
+ library for other plugins, it should be shipped as bytecode
+ (.cma or .cmo) as well.
+
+Tag: ocaml-dev-file-not-in-usr-lib-ocaml
+Severity: pedantic
+Certainty: possible
+Info: This development package installs OCaml development files (.cmi,
+ .cmx or .cmxa) outside /usr/lib/ocaml. Such files are used only by
+ compilation and should be in a subdirectory of OCaml standard library
+ path.
+
+Tag: ocaml-stray-cmo
+Severity: normal
+Certainty: wild-guess
+Info: This package installs a .cma file and a .cmo file with the same
+ base name. Most of the time, the module provided by the .cmo file is
+ also linked in the .cma file, so the .cmo file is useless.
+
+Tag: ocaml-dangling-cmi
+Severity: wishlist
+Certainty: possible
+Info: This package installs a compiled interface (.cmi) without its
+ text version (.mli). The text version should also be installed for
+ documentation purpose. If the module involved doesn't have a .mli,
+ its source code (.ml) should be installed instead.
+
+Tag: ocaml-dev-file-in-nondev-package
+Severity: pedantic
+Certainty: possible
+Info: This package doesn't appear to be a development package, but
+ installs OCaml development files (.cmi, .cmx or .cmxa). These files
+ should be moved to a development package.
+
+Tag: ocaml-meta-without-suggestion-to-findlib
+Severity: pedantic
+Certainty: certain
+Info: This development package installs a META file, but doesn't
+ depend on ocaml-findlib. Libraries with META file are easier to use
+ with findlib; the package should at least suggest ocaml-findlib.
-- 
1.6.3.3


Reply to: