Managing multiple projects depending on different versions of OpenLilyLib can sometimes get tricky. This post introduces a little wrapper for lilypond, called ollc, to automatically handle such dependencies.

OpenLilyLib is “a place to store and collaborate on LilyPond tools”, similar in spirit to the Lilypond Snippet Repository (LSR). With respect to the LSR, its goal is to provide more flexibility for both contributors and users, by allowing multi-file extensions, scripts, etc., and by leveraging git to provide version control and a collaboration platform.

Regular OpenLilyLib users are encouraged to clone the OpenLilyLib repository in a directory on their own machine (say in ~/openlilylib), and then include it in the scores that need some of the extensions provided by OpenLilyLib. Suppose you are working on a score, located in ~/scores/score1. To compile it including OpenLilyLib code, you just have to issue the following command

$ cd ~/scores/score1
$ lilypond -I ~/openlilylib -I ~/openlilylib/ly

The advantage of this approach (as opposed to copying and pasting code in your own project) is that by performing a single git pull inside the ~/openlilylib directory you get all the latest updates and bugfixes, for all the projects including that directory. While this is of great convenience, it can also be the source of some problems.

A motivating example

OpenLilyLib is undergoing a fundamental reorganization that involves, among other things, renaming some files and changing the directory structure, and changing the interface of some functions. Moreover, some of the libraries provided by OpenLilyLib are not yet stable. For instance, GridLY was just updated to version 0.6.0, which simplifies the public interface a the cost of breaking backwards compatibility.

Now, imagine the following situation. You just finished working on the previously mentioned score1 using a version of OpenLilyLib that provides GridLY 0.5.0. You want to start a new score, say score2, using the latest version of GridLY, i.e. 0.6.0. Hence you update your OpenLilyLib clone

$ cd ~/openlilylib
$ git pull

and proceed to work on score2

$ cd ~/scores/score2
$ lilypond -I ~/openlilylib -I ~/openlilylib/ly

Afterwards, you notice that there is a typo in score1 but, when you try to fix it, your score no longer compiles, because the last update pulled in some breaking changes! Now, you could follow the migration guide and update the source files of score1, however it is unlikely that you want to spend time to do this upgrade only to fix a typo! Fear not, for OpenLilyLib is versioned using git: you could just go inside ~/openlilylib, inspect the output of git log, find the commit that merged GridLY 0.6.0 into master, check it out, and then recompile your score. It sounds like a lot of work to do, and indeed it is. While it works, this solution is completely unacceptable.

A simpler solution…

Enter ollc, a tool to automate the management of OpenLilyLib versions. This simple script is able to clone the OpenLilyLib repository to a predefined location on its first run, and adds to Lilypond’s include path all the relevant OpenLilyLib directories. Most importantly, it keeps track of the version of OpenLilyLib that was used to compile a score. This way, you are guaranteed that each time you compile the same score you always use the same version of OpenLilyLib, unless you explicitly want to update to another version.

So, let’s see how this would work in the previous example. You are working on score1, using a version of OpenLilyLib that provides GridLY 0.5.0. You can compile the score with

$ cd ~/scores/score1
$ ollc

The previous command will clone OpenLilyLib (if needed), it will invoke lilypond to compile the score, and will create a file named ollc.conf with the following contents

revision = a1aab4eab7334ed95f9edf793e385bb05200fbd6

The revision string is the git revision of OpenLilyLib version used to compile score1.

Now you start to work on score2. Since you want to work with the latest version of GridLY in this score, you can compile the score with this command

$ cd ~/scores/score2
$ ollc --rev master

The --rev master option tells ollc to pull the latest changes from the upstream branch master of OpenLilyLib. Again, the program creates a file named ollc.conf with the following contents

revision = 049ecdd5762c76573f5c58ae45db834f4be44f40

See how the git revision is different from the one stored in ~/scores/score1/ollc.conf?

Now, if you go back to score1 to fix some typos and compile the score again, ollc will read the revision identifier from ollc.conf. It will then checkout the local copy of OpenLilyLib to that revision and add that version to Lilypond’s include path! Now you can do any modifications you want without being forced to upgrade your score’s sources, nor to manually handle the checouts of OpenLilyLib. If at some point you want to upgrade score1 to the latest OpenLilyLib version, you just have to call ollc --rev master and it will pull the latest changes, overwriting the previous version of ollc.conf.

… but there’s still work to do

The approach adopted by ollc solves the problem of maintaining multiple projects depending on different versions of OpenLilyLib. However, it doesn’t handle the following use case, where a score uses both GridLY and ScholarLY. Suppose that we want the latest version of ScholarLY, but we do not want to upgrade GridLY. Unfortunately, we are stuck.

The solution to this problem requires the introduction of a full featured package manager, whose discussion is beyond the scope of this post.