Oh par2cmdline


Small update: Version 1.9.4 now includes libpar2! Also, a fresh installer for Windows is available here!

I write a tool that makes so-called forward error correction of file trees easy to manage, par2deep. At least, I think it makes it easy, you can (and do!) send me your own opinions on that, I’m always looking for improvements!

One such improvement I have been looking at today and yesterday is seeing if I can create a Python library out of the par2cmdline project, because right now I just generate the commandline options with which to run par2, the compiled executable of the par2cmdline project. It works, but it requires the user to ensure the executable is in their PATH or point par2deep to wherever they have it. Not the best or most robust way to do things, right?

So, a library is what I need, but alas, there wasn’t really one. Although the par2cmdline (auto)makefile creates a static library of (nearly) all of its sources, and then links them up with the main() in par2cmdline.cpp, the interface to that library is not explicitly defined anywhere. At first I thought exposing the functions in libpar.h with pybind11 would be a good idea, but it takes a whole bunch of arguments of which I have no idea, and I also don’t really want to have an idea. I want the commandline interface, I understand its switches because they are defined and documented in the manpage.

commandline.h converts your arguments into the right input for libpar.h. So, I must reuse commandline.h/cpp to interface with the functions in libpar.h. Not hard, turn main() in par2cmdline.cpp into extern "C" par2cmdline() and presto! Woah woah woah, not so fast! I spent the night educating myself on automake, trying to modify Makefile.am into doing what I want, but after some time it because clear, for shared libraries, automake needs external tooling, libtool. Okay… sudo apt install libtool. Nope. No /usr/bin/libtool. Ah, sudo apt install libtool-bin. Still nope. Hmm, must run libtoolize in the source dir. Oh and add LT_INIT to configure. O, and no LDADD, but LIBADD to link libpar2.a. The generate makefile still did not specify a LIBTOOL, so manually adding it seemed to generate something. Nope, it didn’t, more errors in the generated makefile. Well, here I gave up. I’m not interested in build scripts much, I never quite understand them, and they seem to have as much ceremony as using PGP with its 6 trust levels. I just want a shared library, why is that so tricky?

I then tried to make the static library work, but that does not play well with Pythons ctypes CDDL interface. It must be a shared library. It is what I’m used to as well, I’ve written libraries with C interfaces before (medimage and dosia feature them), so what other options do I have? Cutting out the build script. That of course means cutting out the linking of the various seperately compiled object files that the build script generated for each source file in par2cmdline’s /src

Include all the things! The main challenge here was figuring out the right order, because the par2cmdline coding style does not feature a consistent include for every dependency, it lets the linker make sure all the symbols are present. The function signature fortunately is already C-proof (argc,argv), so all I need to do is prepend a fake executable name that the CommandLine class scans for. Presto, libpar2!

I chose not to edit any par2cmdline/src code, such that it will remain straightforward to follow upstream updates. Building does not really require any script anymore, and the interface is just the commandline interface we already know and love.

Have fun! Would be interesting if anyone likes or even uses it! I’ll see to the integration into par2deep shortly.