Packaging with lpms
From HadronWiki
Contents |
Build scripts
In order to lpms can build packages, it needs a Python script file including the name and the version of the package. They named in <packagename>-<packageversion>.py format.
These files includes 3 parts; metadata, functions and others. A packager can use lpms' built-in functions as well as his/her own functions. Other things which is not a part of any function, are immediately interpreted. So everything should have done in a function if it's not an extreme condition.
If you want to write spec files(lpms' package build scripts like ebuilds or PKGBUILDs), you'll need to have:
Basic knowledge about Python: You need to be at least familiar Python syntax as spec files are entirely written in Python.
Unix/Linux knowledge: As we're trying to create a complete package management system, you should know what is what about packaging, how to compile a program, package managers etc..
Here you can browse our main repository to see samples: https://gitorious.org/hadron/main-repo/trees/master
Please send any problems or feature requests to our Bug tracker. We're waiting to help.
Metadata
In scripts, there is a part named "metadata" which lpms reads information about the package. Here is an example for the format:
metadata = """ summary @ Terminal for XFCE4 license @ GPL-2 homepage @ http://www.xfce.org src_url @ http://archive.xfce.org/src/apps/terminal/0.4/Terminal-0.4.6.tar.bz2 options @ debug arch @ ~x86 """
As you can see, it starts with metadata = """ and ends with """. And you see the information lines which is formatted with '@' symbol. You can put as much as space before or after it. But every line has to include only one information, therefore only one '@'.
- A metadata has to include "summary", "license" and "homepage" information.
- "src_url" is needed but you can define it in the code section also.
- "options" is optional. If there is a parameter which user can select in the package, packager should include them as options.
Slotting Packages
You can also define a slot for a package in the metadata. If you don't write anything, lpms assumes the slot as 0 (zero) and doesn't support installing of multiple versions.
An example slot line in metadata, from docbook-xml-dtd-4.5.py:
slot @ 4.5
src_url
It's a bit special. You can change it according to the information which comes from "options". You just should put a space between different addresses.
src_url @ http://foo.org/distfiles/foo-x.y.z.tar.bz2 theme(http://foo.org/distfiles/foo-themes-x.y.z.tar.bz2)
For example in this line, if the "theme" option is enabled during installation, also the second URL will be used.
You can define it in the script also. It depends on packager. So this example does the same thing with above example:
if opt("theme"):
src_url = "http://foo.org/distfiles/foo-x.y.z.tar.bz2 http://foo.org/distfiles/foo-themes-x.y.z.tar.bz2"
else:
src_url = "http://foo.org/distfiles/foo-x.y.z.tar.bz"
Build Functions
You can use almost everything as long as it's in Python language to manipulate the package. Lpms has some built-in functions for package operations :
- extract
- prepare
- configure
- build
- install
- post_install
- post_remove
- pre_remove
In these functions: "configure", "build" and "install" are mandatory. Lpms runs these functions even the script doesn't include them. As you can see later, you can build a package without even defining any of these.
extract
Generally you don't need that section but cloning from a vcs (like git), downloading from a dynamic link (like : chromium-bin spec ) you can benefit from this.
prepare
Package can be patched and modified with the help of this function after unpacking. E.g:
def prepare():
patch("foo.patch")
You can use "level" parameter to set patch level. Like:
def prepare():
patch("foo.patch", level=1)
If there are more than one patch, you can use patch function without a file name. It'll try to patch everything named *.patch.
def prepare(): patch(level=1)
If you don't use a level, lpms will use the default level, which is 0 (zero).
If you want to patch lots of files, they may use extension .diff or something else, you can put them in a directory which you've created in the files directory. Then use directory name to patch'em all:
def prepare():
patch("patchfolder", level=1)
configure
Configuration of the source code made in this function. Here is a sample with some parameters and an option which translates to --enable-nls or --disable-nls according to user's options:
def configure():
conf("--enable-foo",
"--enable-bar --with-foo",
config_enable("nls"))
You can also use config_with() to meet the need of "--with-blabla" parameter. If the parameter is different from the option, is possible because there are lots of different parameters, you can use second variable for config_enable() or config_with().
In the next example, If user have opened the "lame" option, we want to give "--with-libmp3lame". Of course if it's disabled, function will give "--without-libmp3lame" parameter:
def configure():
conf("--enable-foo",
"--enable-bar --with-foo",
config_with("lame", "libmp3lame"))
Lpms has the config_enable() and config_with() functions, because they're used in almost every package. But if you're working a package which is exceptional, like we say before, you can use any Python statement you like.
For example you have a package which wants "--mydeveloper-is-weird=enablemp3" and "--mydeveloper-is-weird=disablemp3" to enable/disable mp3 support. Here is a way to handle this:
def configure():
customparams = ""
if opt("mp3"):
customparams += " --mydeveloper-is-weird=enablemp3 "
if not opt("mp3"):
customparams += " --mydeveloper-is-weird=disablemp3 "
conf(
"--enable-something",
config_enable("foo"),
config_enable("bar"), customparams)
As you can see we didn't give any parameter like "--prefix=/usr". Because lpms gives that options by default when we use conf(). Here is a list of default options which lpms assumes in conf().
Some packages could be even weirder, they can't even skip these default options if you can't define like it and they can abort configuration. For situations like this, lpms has raw_configure(). With the raw_configure() function, lpms won't give any parameter as default.
Also you can use functions like autogen() and autoconf() when it's needed, they also accept some parameters. We'll write about them later.
If you don't define a configure() function. Lpms will act its default behavior, It runs conf(). If you don't want that, you should define an empty one:
def configure():
pass
build
This is the function to build sources. This is also very flexible, check:
def build():
make()
if opt("doc"):
make("doc")
This gives the "make" command. After that checks for "doc" option and if it's open, executes "make doc".
If you don't define build function, lpms will run a simple make command in source directory (srcdir). If you don't wan't this, define an empty function like in conf().
install
This is the install part of the package. But this is not the real installation into the system. Instead, package installed to temporary install directory to determine which files are going to be "merged". Yes, the real installation to the system called "merging".
def install():
raw_install("DESTDIR=%s" % install_dir)
install_dir is the temporary directory which is next to the source directory in /var/tmp/lpms. This code is also the default behaviour.
post_install
This part is for doing after-installation jobs like warnings, updating font cache if needed etc...
def post_install():
if opt("plugins"):
notify("Plugins are installed, you can bla bla..")
Example Build Script
metadata = """
summary @ Standard GNU file utilities (chmod, cp, dd, dir, ls...), text utilities (sort, tr, head, wc..), and shell utilities (whoami, who,...)
license @ GPL-3
homepage @ http://www.gnu.org/software/coreutils/
src_url @ http://ftp.gnu.org/gnu/$name/$fullname.tar.gz
arch @ ~x86
"""
def configure():
autoreconf("-v")
conf("--enable-install-program=su",
"--enable-no-install-program=groups,hostname,kill,uptime")
def install():
raw_install("DESTDIR=%s" % install_dir)
As you can see, there is no build() function defined. But lpms runs the "standard procedure", therefor executes it when it should be.
Standard Procedure
If all the package needs standard configure, make and make install to be installed (as most little packages), packager doesn't have to define any of it, or the ones which just wants default behaviour.
So a script "can" be enough just with its metadata. If you don't want any options and configuration etc.
If you don't want to follow this helpful standard procedure, you can disable it by:
standard_procedure = False
For example wgetpaste doesn't need a build function, neither configure:
metadata = """
summary @ Command-line interface to various pastebins
homepage @ http://wgetpaste.zlin.dk/
license @ public-domain
src_url @ http://wgetpaste.zlin.dk/$fullname.tar.bz2
arch @ ~x86
"""
depends = """
runtime @ net-misc/wget
"""
standard_procedure=False
def install():
insexe("%s/wgetpaste" % build_dir, "/usr/bin/wgetpaste")

