Packaging with lpms

From HadronWiki

Jump to: navigation, search

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")

Personal tools