How I patch suckless projects

Philip Thomas K.

25 December 19

Back

Chances are, you came across this post because you want to start to use some of suckless.org’s utilities. I know the struggle of trying to enter the user-base. There is not a whole lot of information online on how to install, patch and use suckless utilities.

But that is not entirely true. Technically, all of the information required to be a suckless user is on their website. The problem is that you need to be rather tech savvy and have a fairly good understanding of how to patch a C project. And personally I feel there is a reason why.

Why?

I’ll tell you why. Because only those who have the pre-requisites to enter the user-base can properly appreciate the utility. Suckless programs are very minimalist. But that is a reason why they work so well. There is no unnecessary feature in an unpatched suckless project. Any feature that you may want will have to be patched into the project. And that can be a first for many users. The install isn’t quite that obvious neither. You don’t install via any package manager like you may be used to.

How to install

To install a suckless project, I recommend to get a copy of their source code from their website.

I have a folder in my laptop called Builds. Its where I keep the source code of my suckless project builds. I suggest you create such a folder with mkdir, then, cd into it and do the following command to pull the source code from the official repository.

git clone https://git.suckless.org/[NAME OF PROJECT]
# e.g. dwm

Have we installed it yet?

Nope. But we are now miles closer. cd into the folder (be it dwm, st, etc.) and ls. You will see a bunch of files. This is all the stuff your computer will need to install the program. Don’t be overwhelmed. The install is actually rather simple. In your terminal, just execute the following commands:

make
sudo make clean install
make clean
rm -f config.h
git reset --hard origin/master

Woah there! Calm down. I know it seems rather a lot. Let me break it down for you. The make command is a GNU utility to maintain groups of programs. It reads the contents of the file in the directory called makefile to produce the binary (executable program in Linux). We are not done yet. We need to sudo make clean install for that. make clean install removes any binary in your /bin/ folder with the same name (Any previous install of the project) and installs the current build by copying the binary in the current folder to your /bin/ folder so that it can be executed from anywhere in the system. sudo is required by your system to make sure that the system owner is installing the binary. Now we have installed the program.

What’s the rest for?

In my opinion, these commands are also very important. I feel like the usage of these commands help in reducing the confusion that can arise when you start to patch the project. make clean removes the binary and anything else that was produced as a result of the make command from the current directory. rm -f config.h removes the config.h file (quite self-explanatory). The -f option basically removes said file and doesn’t do anything if the file does not exist. We want to remove this file because we will be doing our configurations in a different manner that I feel is more systematic. git reset --hard origin/master is what I use to reset the entire folder back to its original state, as though we just cloned the project from the official repository.

Now we have a clean build folder, and the program is installed in the system. Lovely. Now for the tricky bits.

Patches

Patches can be thought of like plugins for the program. But they work differently. I think its important to know why patches are used instead of plugins. Suckless projects are written in C. That means that they are fast. Not only because of the fact that they are written in C, but because they are written and maintained by the suckless team. These are some of the fastest programs available out there. They make my 7 year old Lenovo ThinkPad T420s running on Arch-Linux look like a running cheetah next to a new DELL with a fresh install of Windows which resembles something along the lines of a walrus on land. They got rid of the bloat and carved out the essential lines of code.

Why is that important? Because if the suckless team sat down to implement a plugin system, no matter how hard they worked, it would:

  1. Include unnecessary lines of code for those that do not need plugins.
  2. Go against the minimalist philosophy by making plugins a feature.
  3. Cause their program to have to read far more files before startup.

And this would all result in the slowing down of the program. So any additional features you may need will have to literally change the source code of the project. This means that only code that you deem necessary will be read on top of the basic required code. It also means that you are not adding any unnecessary files to be read. This is the only solution that would not compromise speed and efficiency too much.

How to patch

Full Disclaimer: The following method is directly the same as HexDSL’s method which I find to be the best method.

So how do we patch a suckless project? The answer I have for you is git. If you already know how to use git, this will merely another walk in the park. If however, the opposite is true, this will be a very good opportunity to learn about branches in git.

First, we need to find a patch to apply to the project. You can normally find these on the official suckless.org website. Most will provide you with the feature you deem necessary. Once you have found it on the website, download the patch using the curl command, or any other method. The patch will be a .diff file.

What is a .diff file?

This is the patch. Let me just briefly explain what the .diff file contains. It contains information on which files need to be changed, a range of values indicating roughly where the change is to take place, what lines to delete, what lines to write in. It is basically a standardised manual on how to change the contents of a file. Your computer is able to read this file and follow the commands to change the file. Simple right?

Before we apply the patch, we need to do some housekeeping first. Enter your build directory and execute the following commands:

make clean
rm -f config.h
git reset --hard origin/master

Now before you apply your first patch, I would recommend that we first make changes to the configuration header. In your build directory there should (currently) be a file called config.def.h. This is the default configuration header. When you run the make command in the directory, another configuration header will be produced that will take precedence over the default. So, in theory to do any configuration, you will need to just change some values in config.h which is the configuration header that is produced after running the make command. But this is assuming you don’t play around with patches, because you tend to run into more problems just leaving it that way. So we must first make a config branch. Run the following commands:

git branch config
git checkout config
git branch

Let’s break it down step by step. First we run git branch config. This command tells git to branch off the clean code that was the result of running git reset --hard origin/master. This creates a branch called config. Now we need to enter this branch if we want to make any configuration changes. We do that by running the next command, git checkout config. This makes the HEAD to point to the config branch. Now, when we run toe command git branch, it lists the current branches, as well as which branch you are currently on. It is a very useful command.

So now we can change the contents of the config.def.h. Make any configurational changes that you need with the editor of your choice (mine is Vim). And now that you have done that, we can commence with the rest of the procedures. Run the following commands:

git add config.def.h
git commit -m "Changed font"
git checkout master
git branch
git merge config -m "Changed font in configuration header"

Now with git add config.def.h we add the configuration header from the working branch directory to the branch staging area, this stages it for the commit. Next, with git commit -m "Message" we commit the changes with a small message that describes the changes made. After this, we can now switch back to the master branch and merge our changes. We switch to the master branch with git checkout master. Then we make sure we are on the correct branch with the git branch command. And lastly we merge the changes made to the files in the config branch to the master branch.

Now we can compile and install our changes:

make
sudo make clean install

Now we have installed our configuration changes. Now for patches.

make clean
rm -f config.h
git reset --hard origin/master

Get ready with your downloaded patch. Put it in the build directory. We are about to branch off clean code.

git branch [Name of patch]
git checkout [patchname]

Now we are ready to apply our patch. This bit is very simple. Just run the following command:

git apply [patch.diff] 

This will make the necessary changes to the necessary files. Now all that is left is to check which files have been changed before we can add them to the staging area:

git status
# Lists all modified files.
git add [file1] [file2] ...

Then we do the rest just like before with the config branch:

git commit -m "Patched"
git checkout master
git branch
git merge [Name of patch]  -m "Patched in [patchname] via git-apply"

So now that the patch has been applied and merged to the master branch, we can make the binary and install it! Haha that is completely wrong. We need to merge the config branch in as well before we compile and install:

git merge config -m config
# Config as message because there have been no changes to the config branch.

The above needs to be done for all branches. It will start to get tedious if you have many patches on the program. Now after all that, all that is left is to compile and install:

make
sudo make clean install

And just to clean up:

make clean
rm -f config.h
git reset --hard origin/master

And that’s just it. I’ll make another post at some other time later on what to do if your patch does not apply well, and the compiler seems to throw a bunch of errors at you.

Best regards,
Philip
Back