Tuesday, 10 April 2018

Packaging C/C++ applications

For a long time linux developers have had to deal with the hassle of dependencies while packaging their application. With the introduction of the AppImage format, you can now make a standalone executable to distribute your apps easily across Linux Distros.

Here, I will be describing the basic process for making things work for you.

Tools you need:
1: appimagetool
2: AppRun executable

These tools can be downloaded from: https://github.com/AppImage/AppImageKit/releases
Note: Select the correct tools according to your architecture!

Let's say you have compiled a C/C++ app on your linux with the name "testapp" which uses many third party libraries.
When you see the compiled file size, it will hardly be in kliobytes, and this executable file will not run on any other system directly. The user will need to install the library packages explicitly on their system.
So how do you package it such that it becomes a standalone executable?

Here's how:

1: Download: https://github.com/shivangsgangadia/utility_scripts/blob/master/get_files.py

2: Copy/move "get_files.py" to the same directory as your compiled executable "testapp".

3: Right click -> Open in terminal. 
     This should open a terminal in your current dirctory.

4: Run:
     $ python3 get_files.py testapp
   This will generate a list of external dependencies and copy them from their source to a folder named "data" in the same directory.

5: Now open the text file: "files.txt" that was also created automatically in step 4.

6: In this file, you will see a list of files and their paths that look like: "/usr/lib/...."
    You need to create this path in the directory where you are working.
    Eg: Example of one file in the list:
   libPocoNet.so.60 => /usr/local/lib/libPocoNet.so.60 (0x00007f4e26597000)

Create a folder structure inside the "data" folder like: usr -> local -> lib
Now copy the "libPocoNet.so.60" file from the parent "data" folder to the above created "lib"

Similarly, you need to copy all such files from the "data" folder to their respective subfolder according to the list. Don't worry if the number of files is large, most of them will go in the same sub folder.

7: Rename the "data" folder to "testapp.AppDir".

8: Create directory structure: /usr/bin and place your "testapp" executable in this "bin" folder.

9: Copy the "AppRun" executable to "testapp.AppDir" alongside "usr".
Note: The AppRun executable must be renamed to exactly: "AppRun".
Eg: For my case, the file I downloaded was: "AppRun-x86_64" and I renamed it to "AppRun".

10: Create a "testapp.desktop" file and an icon file.
      The desktop file should look like:

[Desktop Entry]
Name=TestApp
Exec=testapp
Type=Application
Icon=test_app_icon_file


Note: the icon file should not have extensions in the desktop file but should have extensions outside.
Eg: If the name of the icon file in the desktop file is "test_app_icon_file", then save it physically as "test_app_icon_file.png".

11: Get to the parent directory containing "testapp.AppDir". Copy the "appimagefile" you downloaded earlier to this directory and run:
    $ appimagetool testapp.AppDir
Note: The name of appimagetool file should be exact. For my case, the file I downloaded was: appimagetool-x86_64.AppImage, so my command would be:
    $ appimagetool-x86_64.AppImage testapp.AppDir

12: If all processes were done correctly, this should generate an appimage for your app that can be redistributed as a standalone executable.

NOTE: If your main executable file needs to load any other custom created files at runtime, like ".glade" files, you must mention their path in your source code relative to the "AppRun" executable, not to your "testapp" executable.

References: https://github.com/AppImage/AppImageKit/wiki/Creating-AppImages