How to Build and Publish Snap Packages with Linux Shared Libraries

When you want to release a proprietary tool or SDK, which is not open source, to Linux platforms, it is a nightmare. You have to make multiple packages and do more tests to guarantee compatibility for different Linux distributions. Is it possible to only create one pre-built package? We can use Snap, which is a software deployment and package management system, working across a range of Linux distributions.

Linux distributions

Installing Snap and Snapcraft

Follow the guide to install the tool on different Linux distributions.

Here are the steps for Debian:

$ sudo apt update
$ sudo apt install snapd
$ sudo snap install snapcraft –classic

I initially tried to install snapcraft on WSL but failed with the error message:

Interacting with snapd is not yet supported on Windows Subsystem for Linux.
This command has been left available for documentation purposes only.

Then I selected Ubuntu 20.04 as the alternative. We can install the hello-world package to make a test:

$ sudo snap install hello-world
hello-world 6.4 from Canonical✓ installed
$ hello-world
Hello World!

Building and Publishing Snap Package with Snapcraft

Snapcraft is a command-line tool used to build and publish snap packages.

Run the following command to generate an empty project with a snapcraft.yaml file:

$ snapcraft init
Created snap/snapcraft.yaml.

In the following paragraphs, I will take Dynamsoft Barcode Reader SDK as an example, showing how to package a Linux app and its dependent shared library.

Download Dynamsoft Barcode Reader for Linux.

Extract the header file and shared library from the SDK and put them into the project.

snap project structure

The essential part of the Makefile is “install”. We have to copy both the executable file and shared library to the corresponding directory:

CC=gcc
CCFLAGS=-c

LDFLAGS=-L ./lib
DBRLIB=-lDynamsoftBarcodeReader

STDLIB=-lstdc++

TARGET=barcode-reader
OBJECT=ReadBarcode.o
SOURCE=ReadBarcode.cpp

# build rule for target.
$(TARGET): $(OBJECT)
	$(CC) -o $(TARGET) $(OBJECT) $(STDLIB) $(DBRLIB) $(LDFLAGS)

# target to build an object file
$(OBJECT): $(SOURCE)
	$(CC) $(CCFLAGS) $(SOURCE)

install:
	mkdir -p $(DESTDIR)/usr/bin/
	cp ./barcode-reader $(DESTDIR)/usr/bin/
	mkdir -p $(DESTDIR)/usr/lib
	cp ./lib/libDynamsoftBarcodeReader.so $(DESTDIR)/usr/lib

# the clean target
.PHONY : clean
clean: 
	rm -f $(OBJECT) $(TARGET)

Edit the snapcraft.yaml file:

name: barcode-reader 
base: core18 
version: '7.4' 
summary: Barcode SDK for 1D barcode, QR Code, Data Matrix, PDF417, Aztec Code, MaxiCode 
description: |
  An enterprise-class barcode SDK that enables you to efficiently embed barcode reading functionality in your web, desktop or mobile applications with a few lines of code.
icon: snap/gui/logo.png
grade: stable 
confinement: strict  

parts:
  barcode-reader:
    plugin: make
    build-packages: [gcc, g++, make]
    stage-packages: [libstdc++6]
    source: src

apps:
  barcode-reader:
    command: barcode-reader
    plugs: [home]

Each snap package is sandboxed, running in a constrained environment, and thus I need to add the home plugin in order to make my barcode reader app load image files from $HOME directory.

We can build or clean the snap package as follows:

snapcraft
snapcraft clean dbr -s pull

Before publishing the package to the snap store, we’d better test it locally. Run the command to install the package from local disk:

sudo snap install --dangerous barcode-reader_7.4_amd64.snap

Run the app:

barcode-reader ~/images/AllSupportedBarcodeTypes.tif 
snap barcode reader

If everything works fine, we can publish the package to the snap store:

$ snapcraft login
$ snapcraft push --release=stable barcode-reader_7.4_amd64.snap

Here is my page: https://snapcraft.io/barcode-reader.

Lastly, we should beautify the store presence by editing relevant information.

How to Build a Linux App with the Shared library inside Snap Package

Install the barcode-reader package:

$ sudo snap install barcode-reader

Find the directory of the shared library and set the LD_LIBRARY_PATH:

$ export LD_LIBRARY_PATH=/snap/barcode-reader/current/usr/lib/

Build your own barcode reading app:

$ gcc -o app <source.cpp> -L /snap/barcode-reader/current/usr/lib/  -lDynamsoftBarcodeReader -lstdc++ 
$ ./app

Instead, we can use -rpath to build the barcode app without setting LD_LIBRARY_PATH :

$ gcc -o app <source.cpp> -L /snap/barcode-reader/current/usr/lib/  -lDynamsoftBarcodeReader -lstdc++ -Wl,-rpath=/snap/barcode-reader/current/usr/lib/
$ ./app

Source Code

https://github.com/yushulx/snap-package