Lintian's humble description of "Debian package checker" belies its importance within the Debian GNU/Linux project. An extensive static analysis tool, it's not only used by the vast majority of developers, falling foul of some of its checks even cause uploads to be automatically rejected by the archive maintenance software.
As you may have read in my recent monthly report, I've recently been hacking on Lintian itself. In particular:
- #798983: Check for libjs-* binary package name outside of the web section
- #814326: Warn if filenames contain wildcard characters
- #829744: Add new-package-should-not-package-python2-module tag
- #831864: Warn about Python packages that ship Coverage.py information
- #832096 Check for common typos in debian/rules target names
- #832099: Check for unnecessary SOURCE_DATE_EPOCH assignments
- #832771: Warn about systemd .service files with a missing Install key
However, this rest of this post will go through the steps needed to start contributing yourself.
To demonstrate this I will be walking through submitting a patch for bug #831864 which warns about Python packages that ship .coverage files generated by Coverage.py.
Getting started
First, let's obtain the Lintian sources and create a branch for our work:
$ git clone https://anonscm.debian.org/git/lintian/lintian.git […] $ cd lintian $ git checkout -b warn-about-dotcoverage-files Switched to a new branch 'warn-about-dotcoverage-files'
The most interesting files are under checks/*:
$ ls -l checks/ | head -n 9 total 1356 -rw-r--r-- 1 lamby lamby 6393 Jul 29 14:19 apache2.desc -rw-r--r-- 1 lamby lamby 8619 Jul 29 14:19 apache2.pm -rw-r--r-- 1 lamby lamby 1956 Jul 29 14:19 application-not-library.desc -rw-r--r-- 1 lamby lamby 3285 Jul 29 14:19 application-not-library.pm -rw-r--r-- 1 lamby lamby 544 Jul 29 14:19 automake.desc -rw-r--r-- 1 lamby lamby 1354 Jul 29 14:19 automake.pm -rw-r--r-- 1 lamby lamby 19506 Jul 29 14:19 binaries.desc -rw-r--r-- 1 lamby lamby 25204 Jul 29 14:19 binaries.pm -rw-r--r-- 1 lamby lamby 15641 Aug 24 21:42 changelog-file.desc -rw-r--r-- 1 lamby lamby 19606 Jul 29 14:19 changelog-file.pm
Note that the files are in pairs; a foo.desc file that contains description of the tags and a sibling foo.pm Perl module that actually performs the checks.
Let's add our new tag before we go any further. After poking around, it looks like files.{pm,desc} would be most appropriate, so we'll add our new tag definition to files.desc:
Tag: package-contains-python-coverage-file Severity: normal Certainty: certain Info: The package contains a file that looks like output from the Python coverage.py tool. These are generated by python{,3}-coverage during a test run, noting which parts of the code have been executed. They can then be subsequently analyzed to identify code that could have been executed but was not. . As they are are unlikely to be of utility to end-users, these files should be removed from the package.
The Severity and Certainty fields are documented in the manual. Note the convention of using double spaces after full stops in the Info section.
Extending the testsuite
Lintian has many moving parts based on regular expressions and other subtle logic, so it's especially important to provide tests in order to handle edge cases and to catch any regressions in the future.
We create tests by combining a tiny Debian package that will deliberately violate our check, along with some metadata and the expected output of running Lintian against this package.
The tests themselves are stored under t/tests. There may be an existing test that it would be more appropriate to extend, but I've gone with creating a new directory called files-python-coverage:
$ mkdir -p t/tests/files-python-coverage $ cd t/tests/files-python-coverage
First, we create a simple package, installing dummy file to trigger the check:
$ mkdir -p debian/debian $ touch debian/.coverage $ echo ".coverage /usr/share/files-python-coverage" > debian/debian/install
Note that we do not need a debian/rules file as long as we do not deviate from a "skeleton" debhelper style. We then add the aforementioned metadata to t/tests/files-python-coverage/desc:
Testname: files-python-coverage Sequence: 6000 Version: 1.0 Description: Check for Python .coverage files Test-For: package-contains-python-coverage-file
… and the expected warning to t/tests/files-python-coverage/tags:
$ echo "W: files-python-coverage: package-contains-python-coverage-file" \ "usr/share/files-python-coverage/.coverage" > tags
When we run the testsuite, it should fail because we don't emit the check yet:
$ cd $(git rev-parse --show-toplevel) $ debian/rules runtests onlyrun=tag:package-contains-python-coverage-file […] --- t/tests/files-python-coverage/tags +++ debian/test-out/tests/files-python-coverage/tags.files-python-coverage @@ -1 +0,0 @@ -W: files-python-coverage: package-contains-python-coverage-file usr/share/files-python-coverage/.coverage fail tests::files-python-coverage: output differs! Failed tests (1) tests::files-python-coverage debian/rules:48: recipe for target 'runtests' failed make: *** [runtests] Error 1 $ echo $? 1
Specifying onlyrun= means we only run the tests that are designed to trigger this tag rather than the whole testsuite. This is controlled by the Test-For key in our desc file, not by scanning the tags files.
This recipe for creating a testcase could be used when submitting a regular bug against Lintian — providing a failing testcase not only clarifies misunderstandings resulting from the use of natural language, it also makes it easier, quicker and safer to correct the offending code itself.
Emitting the tag
Now, let's actually implement the check:
tag 'package-installs-python-egg', $file; } + # ---------------- .coverage (coverage.py output) + if ($file->basename eq ".coverage") { + tag 'package-contains-python-coverage-file', $file; + } # ---------------- /usr/lib/site-python
Our testsuite now passes:
$ debian/rules runtests onlyrun=tag:package-contains-python-coverage-file private/generate-profiles.pl .... running tests .... mkdir -p "debian/test-out" t/runtests -k -j 9 t "debian/test-out" tag:package-contains-python-coverage-file ENV[PATH]=[..] pass tests::files-python-coverage if [ "tag:package-contains-python-coverage-file" = "" ]; then touch runtests; fi $ echo $? 0
We should also check that we are are not violating any perlcritic or perltidy failures:
$ debian/rules runtests onlyrun=01-critic/checks ENV[PATH]=..//bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games Test scripts: t/scripts/01-critic/checks.t .. ok All tests successful. Files=1, Tests=53, 13 wallclock secs ( 0.03 usr 0.01 sys + 46.66 cusr 0.26 csys = 46.96 CPU) Result: PASS
Submitting the patch
Lastly, we create a patch for submission to the bug tracking system:
$ git commit -a -m "checks/files: Warn about Python packages which ship" \ "coverage.py information. (Closes: #831864)" $ git format-patch HEAD~ 0001-c-files-Warn-about-Python-packages-which-ship-covera.patch
… and we finally attach it to the existing bug:
To: 831864@bugs.debian.org Cc: 831864-submitter@bugs.debian.org Bcc: control@bugs.debian.org tags 831864 + patch thanks Patch attached. /lamby
Summary
I hope this post will encourage at some extra contributions towards this important tool.
(Be aware that I'm not a Lintian maintainer, so not only should you not treat anything here as gospel and expect this post may be edited over time if clarifications arise.)