MeeGo Compliance: Getting used to /opt

March 28, 2011

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.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: