Developer’s Guide — rmlint (2.10.3 Ludicrous Lemur) documentation (original) (raw)

This guide is targeted to people that want to write new features or fix bugs in rmlint.

Bugs

Please use the issue tracker to post and discuss bugs and features:

Philosophy

We try to adhere to some principles when adding features:

Also keep this in mind, if you want to make a feature request.

Making contributions

The code is hosted on GitHub, therefore our preferred way of receiving patches is using GitHub’s pull requests (normal git pull requests are okay too of course).

Note

origin/master should always contain working software. Base your patches and pull requests always on origin/develop.

Here’s a short step-by-step:

  1. Fork it.
  2. Create a branch from develop. (git checkout develop && git checkout -b my_feature)
  3. Commit your changes. (git commit -am "Fixed it all.")
  4. Check if your commit message is good. (If not: git commit --amend)
  5. Push to the branch (git push origin my_feature)
  6. Open a Pull Request against the develop branch.
  7. Enjoy a refreshing Tea and wait until we get back to you.

Here are some other things to check before submitting your contribution:

For language-translations/updates it is also okay to send the .po files via mail at sahib@online.de, since not every translator is necessarily a software developer.

Testsuite

rmlint has a not yet complete but quite powerful testsuite. It is not complete yet (and probably never will), but it’s already a valuable boost of confidence in rmlint's correctness.

The tests are based on pytest and are written in python>=3.0. Every testcase just runs the (previously built) rmlint binary a and parses its json output. So they are technically blackbox-tests.

On every commit, those tests are additionally run on TravisCI.

Control Variables

The behaviour of the testsuite can be controlled by certain environment variables which are:

Additionally slow tests can be omitted with by appending -k 'not slow' to the commandline. More information on this syntax can be found on the pytest documentation.

Before each release we call the testsuite (at least) like this:

$ sudo RM_TS_USE_VALGRIND=1 RM_TS_PRINT_CMD=1 RM_TS_PEDANTIC=1 pytest -s -a 'not slow and not known_issue'

The sudo here is there for executing some tests that need root access (like the creating of bad user and group ids). Most tests will work without.

Coverage

To see which functions need more testcases we use gcov to detect which lines were executed (and how often) by the testsuite. Here’s a short quickstart usinglcov:

$ CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs -ftest-coverage" scons -j4 DEBUG=1 $ sudo RM_TS_USE_VALGRIND=1 RM_TS_PRINT_CMD=1 RM_TS_PEDANTIC=1 pytest -s -a 'slow and not known_issue' $ lcov --capture --directory . --output-file coverage.info $ genhtml coverage.info --output-directory out

The coverage results are updated from time to time here:

Structure

tests ├── test_formatters # Tests for output formatters (like sh or json) ├── test_options # Tests for normal options like --merge-directories etc. ├── test_types # Tests for all lint types rmlint can find └── utils.py # Common utilities shared among tests.

Templates

A template for a testcase looks like this:

from tests.utils import *

def test_basic(usual_setup_usual_teardown): create_file('xxx', 'a') create_file('xxx', 'b')

head, *data, footer = run_rmlint('-a city -S a')

assert footer['duplicate_sets'] == 1
assert footer['total_lint_size'] == 3
assert footer['total_files'] == 2
assert footer['duplicates'] == 1

Rules

Buildsystem Helpers

Environment Variables

CFLAGS:

Extra flags passed to the compiler.

LDFLAGS:

Extra flags passed to the linker.

CC:

Which compiler to use?

Use clang and enable profiling, verbose build and enable debugging

CC=clang CFLAGS='-pg' LDFLAGS='-pg' scons VERBOSE=1 DEBUG=1

Variables

O=:

Set the optimization level.

Valid levels are currently those that may be passed with the GCC/Clang option -O; these include 0, 1, 2, 3, s,fast, g, etc., depending on the compiler version.

In addition, the level may be debug or release, which indicates that the optimization level should be whatever the build system currently defines to be the default for the associated build mode.

DEBUG=1:

Enable a debugging build.

This turns on extra tests; in particular, it turns on run-time assertions. By default, a debug build excludes optimizations that may hinder debugging, but this may be overridden with the O variable, as usual.

Note that setting DEBUG=1 does not enable the production of debugger symbols; to enable those, use SYMBOLS=1 or GDB=1.

This should always be enabled during development.

SYMBOLS=1:

Enable debugger symbols.

This option instructs the compiler to collect information that will help tools such as gdb present human-readable identifiers for a program’s functions and variables, etc. Note, though, that this information becomes obscured by optimizations, so make sure to set the optimization level appropriately.

GDB=1:

Enable options that help a debugger (such as gdb).

This option is equivalent to DEBUG=1 SYMBOLS=1.

VERBOSE=1:

Print the exact compiler and linker commands. Useful for troubleshooting build errors.

CCFLAGS=:

Set the last compiler options.

Internally, the build system maintains in CCFLAGS the list of options that are supplied to the compiler; this list is composed by combining the relevant environment variables (such as CFLAGS) along with the choices made by other build-time configurations.

This command-line variable makes it possible to override an option in this list by supplying customized command-line options to be appended. For example: GDB=1 CCFLAGS=-g1.

The string that is supplied as the value for this variable is parsed as per a POSIX shell command line, and so it may include shell quoting if necessary.

Arguments

–prefix:

Change the installation prefix. By default this is /usr, but some users might prefer /usr/local or /opt.

–actual-prefix:

This is mainly useful for packagers. The rmlint binary knows where it is installed (which is needed to set e.g. the path to the gettext files). When installing a package, most of the time the build is installed to a local test environment first before being packed to /usr. In this case the --prefix would be set to the path of the temporary build env, while --actual-prefix would be set to /usr.

–libdir:

Some distributions like Fedora use separate libdirectories for 64/32 bit. If this happens, you should set the correct one for 64 bit with--libdir=lib64.

–without-libelf:

Do not link with libelf, which is needed for nonstripped binary detection.

–without-blkid:

Do not link with libblkid, which is needed to differentiate between normal rotational harddisks and non-rotational disks.

–without-json-glib:

Do not link with libjson-glib, which is needed to load json-cache files. Without this library a warning is printed when using --replay.

–without-fiemap:

Do not attempt to use the FIEMAP ioctl(2).

–without-gettext:

Do not link with libintl and do not compile any message catalogs.

–without-gui:

Do not install shredder (GUI).

–without-compile-glib-schemas:

Do not (re)compile system Glib schemas on installation/uninstallation.

All --without-* options come with a --with-* option that inverses its effect. By default rmlint is built with all features available on the system, so you do not need to specify any --with-* option normally.

Notable targets

install:

Install all program parts system-wide.

config:

Print a summary of all features that will be compiled and what the environment looks like.

man:

Build the manpage.

docs:

Build the online html docs (which you are reading now).

test:

Build the tests (requires python and pytest installed). Optionally valgrind can be installed to run the tests through valgrind:

xgettext:

Extract a gettext .pot template from the source.

dist:

Build a tarball suitable for release. Save it underrmlint-$major-$minor-$patch.tar.gz.

release:

Same as dist, but reads the .version file and replaces the current version in the files that are not built by scons.

Sourcecode layout

Hashfunctions

Here is a short comparison of the existing hashfunctions in rmlint (linear scale). For reference: Those plots were rendered with these sources - which are very ugly, sorry.

If you want to add new hashfunctions, you should have some arguments why it is valuable and possibly even benchmark it with the above scripts to see if it’s really that much faster.

Also keep in mind that most of the time the hashfunction is not the bottleneck.

Optimizations

For sake of overview, here is a short list of optimizations implemented in rmlint:

Obvious ones

Subtle ones

Insane ones