So last time I convinced you that packaging for /opt is a Good Thing for add-on software (e.g. app store stuff).[1]  But now that you are convinced… this is a total re-think of how packaging works. Here are some tips on how to get it done without totally messing up your non-opt installs.

Suppose our company is indie.biz and our program is called Floobydust.  On unix-like operating systems we’re used to installing our software like this:

  • Executable programs go in /usr/bin/
  • Libraries go in /usr/lib/
  • Desktop files go in /usr/share/applications/
  • Our extra stuff goes in /usr/share/floobydust/

But now, everything must go in /opt.  This means that:

  • Our program won’t be in $PATH
  • Our libraries won’t be found by ld.so [4]
  • Our desktop files won’t be discovered
  • Our hard-coded "/usr/share/floobydust" strings are now bugs

So… how do you handle this?  In fact… how can I handle both cases without having to totally re-write my build system?

Step 1: Use PREFIX

Most build systems allow the user to define a PREFIX at build-time.  This PREFIX will determine the location of the bin, lib, and share folders that we’ll use for install.  All the paths above set PREFIX=/usr.  It typically defaults to PREFIX=/usr/local.  So, what happens when you set PREFIX=/opt/indie.biz/floobydust ?[3]  Then…

  • Executable programs go in /opt/indie.biz/floobydust/
  • Libraries go in /opt/indie.biz/floobydust/lib/
  • Desktop files go in /opt/indie.biz/floobydust/share/applications/[2]
  • Our extra stuff goes in /opt/indie.biz/floobydust/share/floobydust/

Now, it’s essentially the same thing as if we had set any other prefix.  Note also that you may choose to use PREFIX=/opt/indie.biz — as long as you make sure that all your programs cooperate!

Step 2: Use a redirect script to set up your private libraries

When installed in /usr/bin, ld.so (the dynamic linker) will search in /usr/local/lib, /usr/lib, and /lib for the shared object (.so) files that you need. However, you have installed them to /opt/indie.biz/floobydust/lib instead. To get ld.so to find your libraries, you need to modify LD_LIBRARY_PATH to include your package’s lib folder. You can do this by renaming your program to something like floobydust-1.2.3, and creating a redirect script called floobydust like this:

#!/bin/bash
LD_LIBRARY_PATH="/opt/indie.biz/floobydust/lib:$LD_LIBRARY_PATH"
export LD_LIBRARY_PATH
exec /opt/indie.biz/floobydust/bin/floobydust-1.2.3 "$@"

So, we (a) set LD_LIBRARY_PATH, (b) call our program, and (c) pass it all the command-line arguments (that’s the “$@” thing). However, this script only covers the happy case… so here’s the script that you would really use:

#!/bin/bash
PREFIX=/opt/indie.biz/floobydust
BINDIR="${PREFIX}/bin"
LIBDIR="${PREFIX}/lib"
EXEFILE=floobydust-1.2.3

LD_LIBRARY_PATH="${LIBDIR}${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
export LD_LIBRARY_PATH
PATH="${BINDIR}${PATH:+:$PATH}"
export PATH

exec "${BINDIR}/${EXEFILE}" "$@"

This is something that you could reuse in several different applications. Also note the shell expansion trick for LD_LIBRARY_PATH and PATH that makes sure we don’t end up with a trailing colon ("/opt/indie.biz/floobydust/lib:").

This method will help no matter where you installed your libs. You can configure the path at compile time.

Step 3: Namespace your .desktop file

Add your organization name to your desktop file indie.biz.floobydust.desktop. Since you are using your domain name, it’s extremely unlikely that you will conflict with anyone else’s .desktop file. Then install the file using the desktop-file-install utility (which will usually install to /usr/share/applications). You can optionally have that command do the file-name mangling for you. (Read the manual page for it.)

Also, be sure to put the full path to your redirect script from Step 2 (/opt/indie.biz/floobydust/bin/floobydust), as well as any icons.  Most build systems will let you make your desktop file a configure script (typ. floobydust.desktop.in) that gets modified for your compile-time configuration options.

Step 4: Make sure you don’t hard-code paths in your application

This is actually step 1, but it’s boring so I put it last.  If some part of your program looks to /usr/share/floobydust without thinking first… you need to make your application configurable.[5]

What about $PATH??

Are you really shipping command-line apps through an app store? There isn’t really a solution to this (and still be MeeGo Compliant.) The .desktop file is the replacement for a $PATH, and the redirect script from step 2 can reset it if we need it internally. Another option is to create symlinks in /usr/bin. While this makes the app non-compliant, it’s a good compromise for a multi-distribution package.

Wrapping up

Whatever we do to install in /opt can translate back to /usr and /usr/local. Using this strategy not only helps in app-store situations, but also creates a safe practice for application-specific private libraries (.so). With a little extra run-time detection, these can also help in supporting relocatable RPM packages. While there are no good solutions for CLI apps, it seems unlikely that many CLI apps will be targeting app stores.

——
[1] Yeah, right! 🙂
[2] Not really… but we’ll get to that.
[3] Note that MeeGo compliance says you can install in /opt/<packagename>. Using your organization name is optional, but recommended.
[4] ld.so is the dynamic linker.  It’s the program that finds the .so files for your app to use.
[5] You are supposed to do this anyway… since you don’t know what PREFIX will be a priori.

The current MeeGo Compliance Spec will often give developers a start when they read this:

An application shall be installed to /opt/packagename/ and, if necessary to the /etc/opt/packagename/ and /var/opt/packagename/ directories.[1]

Most users reply, “Huh?  Why don’t we install in /usr like a normal Linux distro??”  The rationale is given as follows:

The rationale for these rules is to avoid filename clashes between application packages and with system files, by defining portions of the filesystem certain to be unique to that application.[2]

This leaves most devs a little… underwhelmed.  The first response is to appeal to the FHS/LSB… but lets start with an example.

Suppose I write a MeeGo game called “Zombie Professer Shootout.”  Because I’ve got several modules for this game, I have an internal library called libzps.so.0.  Because I don’t know any better, I install it in /usr/lib/libzps.so.0.  Put the package in the App Store.  Done.

Meanwhile, suppose that YOU, Zeek, write a hand MeeGo time management program called “Zeek’s Personal Scheduler.”  For whatever reason, you put a lot of the core functionality in a library called libzps.so.0 so that other people could reuse it in their code.  So, you install it to /usr/lib/libzps.so.0.  Put the package in the App Store.  Done.

First, they install my Zombie game.  Then, months later, they install your Personal Scheduler.  However, the app WILL NOT install because it would overwrite /usr/lib/libzps.so.0.  There’s no way around it.  You have a broken package.

And the user says, “This sucks!”

The whole point of being a MeeGo compliant app is that it’s a single, stand-alone, 3rd party app that can be installed to MeeGo… and it Just Works.  But if we all install in /usr… then we all have to be on the same page with respect to file names, package names, etc.  In a normal linux distro (where all packages are served from one repository), these get discovered and discussed among the developers.  But with MeeGo apps coming from a myriad of sources there is no way that can happen.

So, MeeGo chose to use namespaces by requiring installation to /opt.  The opt folder is defined in the FHS as follows:

/opt is reserved for the installation of add-on application software packages.

A package to be installed in /opt must locate its static files in a separate /opt/<package> or /opt/<provider> directory tree, where <package> is a name that describes the software package and <provider> is the provider’s LANANA registered name.[3]

Note that this is nearly identical to the MeeGo requirement… and serves to solve this exact problem.  It’s a system that allows uncoordinated developers the freedom to organize their package however they need without stepping on anyone else’s toes.  Everything they do is under the namespace of the company’s LANANA registered name (e.g. google.com or something).  Without this, users will be inundated with broken packages and always come to the same conclusion: “This sucks.”

“But how do I find my project’s libs?  What about $PATH?  This is no fun for the developer!”  We’ll talk about that next time.

[1] MeeGo 1.1 Compliance Specification, Section 3.3.4

[2] Ibid.

[3] http://www.pathname.com/fhs/pub/fhs-2.3.html#OPTADDONAPPLICATIONSOFTWAREPACKAGES

Fennec (Firefox Mobile)Fennec is the code name for the latest Firefox Mobile browser.  I was playing with the version on the MeeGo Handset UX (on an Atom-based Ideapad)… and pulled up some Youtube videos.  At first the video seemed a little jumpy… no doubt because of the compositing window manager I was using (mcompositor).

Then I clicked fullscreen.

The quality of the fullscreen video was amazing.  It was like watching a DVD or television.  I didn’t detect any jitter or pixelation… just clean video.  I was impressed!

Other video players in MeeGo are no slouch.  For example, watching the short film “Big Buck Bunny” in the Netbook UX with Banshee (a media player) is also impressively snappy.  And flash video inside the Chromium browser is no slouch.  But in Chromium, if you hit the button for full-screen flash, you’ll just get full-screen white. 😦

This is just one of the things that is really well done with Firefox Mobile.  If you get a chance to play with the latest version — take it.