Back to Top

Wednesday, April 08, 2009

Compiling software for OpenWrt (and creating packages)

2726596365_66a64212fb_b From my experience, compiling software is not especially hard, but most of the tutorials out there are somewhat dated (as this one will be in 6-7 months). But at least until then it can be useful, and hopefully I will find the time to update it later on. I’m using the trunk version of OpenWrt, which a little more up-to-date than 8.09, but most probably everything described here works with 8.09 (the latest release).

I’ve taken inspiration from the following sources:

The main ideas would be:

  • The easiest way to start is by copying an existing makefile and editing it to fit your needs
  • OpenWrt has an advanced build system which does all the following things:
    • Download the application from its original source (and verify the integrity of the archive using md5sum)
    • Apply some local patches to it
    • Configure / build it
    However, for local development you most likely won't need this. An alternative solution (which will be used in the tutorial later on) would be to copy the source to the build directory in the preparation faze.
  • Makefiles are very sensitive to tabs (so you have to have tabs and not 4 or 8 spaces in certain locations) and also, errors in them are very cryptic (for example "missing separator"). If your build fails, the first thing you should check is that you have your tabs in order. Also verify that your editor doesn’t have some kind of “transform tabs to spaces” option active. For example, if you are using mcedit with the default color-scheme, it will highlight correct tabs in red, as can be seen in the screenshot below.
    mcedit_openwrt_package Also, you might have observed that not all the sections use tabs, some are ok with spaces. However rules for which section should use what are not clear to me, so my recommendation is to stick with tabs everywhere. For a quick make tutorial, you can check out this site. A last word of warning on this matter: copy-pasting from this blogpost will almost certainly mess things up (convert tabs to spaces, etc), so please double check the source after copying it.

Our goal (taken from the first linke tutorial) is to get the following little C program to compile and run:


/****************
* Helloworld.c
* The most simplistic C program ever written.
* An epileptic monkey on crack could write this code.
*****************/
#include <stdio.h>

int main(void)
{
 printf("Hell! O' world, why won't my code compile?\n\n");
 return 0;
}

The first step is to create a Makefile for it:


# build helloworld executable when user executes "make"
helloworld: helloworld.o
 $(CC) $(LDFLAGS) helloworld.o -o helloworld
helloworld.o: helloworld.c
 $(CC) $(CFLAGS) -c helloworld.c

# remove object files and executable when user executes "make clean"
clean:
 rm *.o helloworld

Place the files in the packages/helloworld/src directory of either the checked-out OpenWrt source or the Openwrt SDK. Now change into this directory and make sure that everything builds on our local system (without the crosscompiling magic):


make
./helloworld <-- this should output the message
make clean   <-- clean up after ourselves

Now to create the openwrt makefile. This will be located one level up (ie. packages/helloworld/Makefile):


#
# Copyright (C) 2008 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
# $Id$

include $(TOPDIR)/rules.mk

PKG_NAME:=helloworld
PKG_RELEASE:=1

include $(INCLUDE_DIR)/package.mk

define Package/helloworld
 SECTION:=utils
 CATEGORY:=Utilities
 TITLE:=Helloworld -- prints a snarky message  
endef

define Build/Prepare
 mkdir -p $(PKG_BUILD_DIR)
 $(CP) ./src/* $(PKG_BUILD_DIR)/
endef

define Build/Configure
endef

define Build/Compile
 $(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
endef

define Package/helloworld/install
 $(INSTALL_DIR) $(1)/bin
 $(INSTALL_BIN) $(PKG_BUILD_DIR)/helloworld $(1)/bin/
endef

$(eval $(call BuildPackage,helloworld))

The makefile should be pretty self-explanatory. A couple things I would like to highlight:

  • Build/Prepare is the step where we copy our source-code to the build directory. This is a hack to circumvent the need of downloading the tgz file, but it is a hack which works well (you might want to add the –r switch to cp if you have nested directories in src – this isn’t the case for this simple example)
  • In the Build/Compile step it is very important to include the $(TARGET_CONFIGURE_OPTS) part. Without it, the thing will build, but it will link with the standard libc, rather than the ulibc available on the Openwrt router. Tracking down this error is made harder by the unintuitive error messages. Specifically, you will see something like this on your router: “/bin/ash: The command /bin/helloworld can not be found”, even though you see that the file exists and it has execute permissions! To verify that your issues are caused by this problem, simply too a “less /bin/helloworld” on your router and check to see if you have strings indicating glibc (instead of ulibc).

Now you are ready to compile:

  • If you are using the SDK, simply go to its root directory and issue the make V=99 command
  • If you are building the complete tree, you have to first do “make menuconfig”, make sure that your package is checked for build (you should see the letter M near it) and then issue make V=99. Be aware that compiling the full tree can take a considerable time (more than a hour in some cases).

Your package should now be ready. Copy the package (you will find it in the bin/packages/target-... subfolder) to your router (or better yet, a Qemu VM running OpenWrt – for safety) and test that everything works:


scp helloworld.ipkg root@router:/root
[root@router]# opkg install helloworld.ipkg
[root@router]# helloworld <-- the message should be printed

This would be all :-). Because of simplicity, this tutorial doesn’t cover the the calling of configuration scripts. Also, as far as I’ve seen, there is no easy way to include parts of other projects. For example, if I wish to create a package for LuaFileSystem, I would need lua.h (and some other, related files). However, I haven’t found an easy way reference it from the lua package, and have opted for putting a local copy in the src/lua subdirectory.

Picture taken from cantrell.david's buddy icon with permission.

3 comments:

  1. GeertVc3:37 PM

    Hi,

    You're missing the part in your first example (Helloworld.c). Only #include is mentioned, not the header file itself...

    ReplyDelete
  2. @GeertVc: thanks for the heads-up. The full line was there, but HTML interprets the "less than" character as "start of tag", so it needs to be encoded if the literal character is needed. Fixed.

    ReplyDelete
  3. Hi,

    I couldn't find these files:

    include $(TOPDIR)/rules.mk

    include $(INCLUDE_DIR)/package.mk

    ReplyDelete