OpenWrt Forum Archive

Topic: Syntax for Package Makefile

The content of this topic has been archived on 9 Jan 2018. There are no obvious gaps in this topic, but there may still be some posts missing at the end.


I'm trying to build a Kamikaze package on my own, but do not find a complete Syntax for the Package Makefile besides:

I cannot find out things like:
Whats the exact syntax of DEPENDS (@ || ... How to I make a package depend on either PHP4 or PHP5 or on adduser command of busybox).
How do I change file ownerships of a file in a package?

So my question: Is there any clear documentation for Package Makefiles?

Thanks a lot!

If you browse the OpenWRT main page and the wiki you will soon find the following: … ation.html

Search google for "openwrt makefile" also gives some helpful sites. And the best way to learn, besides RTFM, is to download the buildroot and examine the Makefiles of existing packages.

To find out how other packages use depends, you could do the following:

/1/svn/openwrt/branches/8.09/package $ grep -ir depends .|grep -v .svn

An example:

define Package/luci-core
    $(call Package/luci/libtemplate)
    TITLE:=LuCI core libraries

This shows you how the package luci-core depends on the lua package.

Thanks jhalfmoon.
But I still did not find answers to the following questions:
How would I make my package enforce the existence of a user and a group to the system (like www:www)?
How could I change file ownerships of certain files of my package?
Both could be accomplished with a postinstall-script. Is there any way to do this?
How would I add a line to a certain config-file (Like a shared library to php.ini)?

There are two parts to the answer to your question. First you need to add a post-install part in your Makefile. Once you have that, you can script anything you wish, like adding users and setting ownerships. The package for Tor has a nice example of how to do this. I have pasted the relevant part of the Tor Makefile here, which I will comment on afterwards:

define Package/tor/postinst
# # check if we are on real system
if [ -z "$${IPKG_INSTROOT}" ]; then
        # create copies of passwd and group, if we use squashfs
        rootfs=`mount |awk '/root/ { print $$5 }'`
        if [ "$$rootfs" = "squashfs" ]; then
                if [ -h /etc/group ]; then
                        rm /etc/group
                        cp /rom/etc/group /etc/group
                if [ -h /etc/passwd ]; then
                        rm /etc/passwd
                        cp /rom/etc/passwd /etc/passwd
echo ""
# add group if it doesn't exist
if [ -z "$$(grep ^\\$${name}: $${IPKG_INSTROOT}/etc/group)" ]; then
        echo "adding group $$name to /etc/group"
        echo "$${name}:x:$${id}:" >> $${IPKG_INSTROOT}/etc/group
# add user if it doesn't exist
if [ -z "$$(grep ^\\$${name}: $${IPKG_INSTROOT}/etc/passwd)" ]; then
        echo "adding user $name to /etc/passwd"
        echo "$${name}:x:$${id}:$${id}:$${name}:/tmp/.$${name}:/bin/false" >> $${IPKG_INSTROOT}/etc/passwd

So, let's analyze this briefly:
- A post install script is defined by "define Package/tor/postinst" and ends with "endef".

- The text between these two delimiters is the actual script that will be run; notice the #!/bin/sh at the beginning of the script.

- Very important: Postinst scripts can be executed twice; once during build time and a second time during installation on the OpenWRT device. This is somewhat logical because the build process is also an installation process; it installs the application you are compiling into a package. Because of this double execution, you have to make your script explicitly check in what phase it is in: Build or Install. This is what the "if [ -z "$${IPKG_INSTROOT}" ]" part is for. It checks to see if the variable IPKG_INSTROOT is zero-length. If so, then it assumes that it is not in the build phase, but running on a 'real' system. Once this has been confirmed, you can run all sorts of actions, limited only by your imagination.

- All variables in your script should have double-string characters ($$) instead of a single. This is to tell "make" not to interpret the value as a variable, but to just ignore the string and replace the $$ by a single $.

- During the build process, the contents of the postinst section will be copied to build_dir/<architecture>/<package-name>/ipkg/<module-name>/CONTROL/postinst. This is good to know, because now you can check the postinst script after you have built your package. One of the things you can check for, is to see if you accidentally forgot to add double $ characters, for example. If so, then you know your script is broken.

- The above rules also apply to preinst , prerm and postrm scripts.

In the example script above, you can see that it checks for the existence of a certain group and user and then adds these to their respective files if they do not exist. That should answer your question on how to add a user. Your next question "how to change ownership of files" is now a no-brainer. Just use chown in the postinst script.

As to your question: "How would I add a line to a certain config-file?" That is more a general scripting related question than an OpenWRT specific question. You could use the command "patch", but that command is usually not installed on OpenWRT or included in Busybox. So you will have to be creative. This seems to be related to your question: … -line.html

Hope that helps.

jhalfmoon, thanks a lot.
This clarifies things ernormously. Even tough I find it a bit strange that there is one part in the Makefile which will be executed twice (build and installation) - why not two different parts? post-build, post-install ?
And I wonder - now only out of curiosity - whether it is possible to change the ownership of a file already in the squashfs... ;-)

>> I find it a bit strange that there is one part in the Makefile which will be executed twice (build and installation) - why not two different parts?

It is a bit strange. I must correct myself thouqh. It is not really during the actual build process that the postinst gets called, but during the build-install process. If your package is configured as a module, then postinst wil never be called during build-intall, because it will not be 'installed' on the image. Only when you configure the package as a part of the firmware image is when it will have the postinst script called.

>> And I wonder - now only out of curiosity - whether it is possible to change the ownership of a file already in the squashfs... ;-)

This will not be a problem. OpenWRT uses an overlay filesystem by default. That means you can change and delete files that are present in the squashfs. There are acually two filesystems: One read-only (squashfs) and one writable (mini-fo). The changes you make to the read-only part get written to the writable filesystem and are automatically laid 'over' the read-only filesystem. This gives the impression of a writable filesystem. One of the consequences of this is that when you delete a file, you actually do not gain any extra space. Read more about the system that is used over here:

I'm not sure if I got this right.
My package needs to add a line to php.ini. This is no problem:

define Package/mypackage/postinst
  if [ -z "$$(grep \"^\\zend_extension = /opt/\" $${IPKG_INSTROOT}/etc/php.ini)" ]; then
        echo "adding myextension to /etc/php.ini"
        echo "zend_extension = /opt/" >> $${IPKG_INSTROOT}/etc/php.ini

But it seems this code is'nt executed, when I build the firmware. How do I ensure the line is added in build-install and install?

1. Are you really sure your code does not get executed? Try reducing your code to a single line: "touch /tmp/test123" . Now compile your package and see if the file /tmp/test123 is created.

2. Also, make sure the following definitions are all in your Makefile:

define Package/mypackage
define Package/mypackage/description
define Package/mypackage/postinst
define Package/mypackage/install
$(eval $(call BuildPackage,mypackage))

3. Your script assumes that PHP (or addition to PHP) is already installed. But for this to be true, your package must have a dependency defined on PHP, so that your package gets installed AFTER PHP. Do you have this dependency defined?

define Package/mypackage
  DEPENDS:= +php

ok, code does get executed, but /etc/php.ini was overwritten by content of ./files :-(
everything fine now....

Building Kernel Package:
I am new to openwrt dev. I could not figure out much documentation how the kernel package is built. (Except few lines in build root). I have even tried out @oldwiki.
1. Can someone please explain the following syntax.
$(call Build/Prepare/Default)
$(call Build/Compile/$(PKG_NAME)) # I have defined PKG_NAME as datamodule.
Is build any script or so. If so where I can find out the same.
2. Is it necessary to have Makefile under $PKG_BUILD_DIR. My structure of code is
     +src <Subdir>
          Makefile  ( i have makefile here).

If my questions looks too trivial, please send me the link to documentation.

Thanks in advance.


Hi bxxt, I must say that I do not really understand what you are asking or what you want to do. But I can say this: The best way to learn how packages are formatted is to look at existing packages. If you want to create a new package, you can best start by copying an existing package and modifying that.


Exactly I wanted to know the :
1. meaning of syntax

$(call Build/Prepare/Default)
$(call Build/Compile/<PKG>)
What and how about above.

2. Inside openwrt/package/mypkg
contains makefile. During make prepare, my package gets untarred from openwrt/dl directory to

                                            src <Directory>
                                                  Makefile <Makefile to build mypkg>
                                                  subdirs   <Other subdirectory>

When I try make package/mypkg-compile V=99, control does not reach to Makefile under build_dir/linux_<target>/mypkg/src/Makefile

More over I have in openwrt/package/mypkg/Makefile as
include $(TOPDIR)/
include $(INCLUDE_DIR)/

include $(INCLUDE_DIR)/
  mypkg_FILES+= $(PKG_BUILD_DIR)/src/mypkg

define KernelPackage/mypkg
  SUBMENU:=mypkg kernel object
  TITLE:=kernel object for mypkg from wios (Prop : Motorola)

define KernelPackage/mypkg/description
This package contains a mypkg ported from wios.
define Build/Prepare
    $(call Build/Prepare/Default)

define Build/Compile
    $(call Build/Compile/mypkg)

define Build/Install
$(eval $(call KernelPackage,mypkg))

Hi there,

I'm trying to write a package Makefile to have a new kernel module build and added to the image in OpenWRT-DD.

So far, I've succeeded having a Makefile that compiles the kernel module, but it seems to just install it as if it were for a package that should be installed with opkg. The module appears in the staging root, but not in the build root, and therefore not in the final image.

I'm uusing KernelPackage defines, similar to the ones in package/kernel/spi-gpio-custom.

The only way I figure out to make this work is to explicitly copy the module into $(TARGET_DIR)/lib/modules/$(LINUX_VERSION)/ in the Build/Compile.

I suspect there is a right way to make a package to install stuff to be part of the target image, which I'm not doing at the moment.

Could anybody that knows, let me know?


The discussion might have continued from here.