[FreeBSD Logo] [Apple Logo]
FreeBSD and MacPorts


General

Project Home
C/Unix Programmer's Guide
FreeBSD and MacPorts
Which FreeBSD is best?
VEX Robotics under Unix
Creating Port-friendly Projects

Port Descriptions

APE
Legoctl/Roboctl
NBC/NXC
IFI Reloaded QT
IFI PIC Tools

Management Scripts

update_jb_ports
update_jb_ports.md5

Links

FreeBSD Ports Project
MacPorts Project

Frameworks and source

FreeBSD port frameworks
MacPorts frameworks
Source code

What's port-friendly code?

A port-friendly project is one that is coded and packaged in a way that makes it easy to build within a ports system, such as Debian packages, Fink, FreeBSD ports, MacPorts, NetBSD pkgsrc, etc.

Why be port-friendly?

Basically, the more port-friendly your code is, the more people will use it. Developing port-friendly code reduces wasted time and effort for everyone involved with a software package.
  • For end users, software that's part of a ports system is trivial to install on their computers.
  • For porters (developers porting a package to a new platform), it means fewer modifications to the code and/or build system to make it work within their ports system.
  • For you, the primary developer, it means leveraging existing ports that your project depends on, so that you don't have to deal with the extra code in your project. It means letting someone else maintain the libraries your project needs, and letting the ports system automatically download and install them for you.
  • The port itself documents the changes necessary to port a project to a particular platform, so by developing a port, you're creating a detailed log of all important patches. These patches can (and should) be sent to the primary developer(s) for inclusion in the next release, so that future ports can be made simpler. Generally, patching a project for one platform will lead to easier porting on others as well.
  • For shared libraries, programs built with the same library port can all share the library's code at runtime, which saves memory and loading time.
If you're a non-conformist like me, it might take a little coercion to get you to invest the up-front effort in learning how to develop within a ports system developed by others. Most of us in the software industry are creative-minded, and inclined to do things our own way. It's much more fun to invent something new, than to study what others have done before. If you're reluctant to spend time learning how to do things somebody else's way, when your way has always worked fine, trust me: I know how you feel.

What finally convinced me to "conform" was the realization of how many hours I had wasted by not using a ports system over many years. However, once I adapted to developing for FreeBSD ports and MacPorts, I was able to simply throw away a lot of custom-made build systems, which I had spent many hours developing and would have spent many more maintaining. It's also satisfying to know that my programs will be more accessible to users of Debian, NetBSD, OpenBSD, etc. I'll never be one to advocate following the crowd in general, but conforming to standard in software development has enormous benefits for everyone.

How to be port-friendly

Here are some tips for making your code port-friendly:
  1. Develop your programs within a ports system, using ports for dependencies. Don't manually download and include source code in your project for commonly used libraries (e.g. libjpeg) and tools (e.g. toolame). This is extra work for you, extra compile time, and may cause incompatibilities with other programs on your computer that use a different version of a library. Since it will be extra work for you to update these libraries, you'll probably end up still using them long after they become obsolete. Let the ports system do all this grunt work for you. Odds are, there's already a port for your platform, and someone else is doing the work of keeping it up-to-date. If there isn't, make one. Doing so will be a good investment of your time in the long run, and will save many other developers time.
  2. Write portable code. Choose languages and libraries that run on many platforms (e.g. C, C++, gtk, qt, opengl). Avoid lacing code with #ifdefs to handle different platforms. It may be necessary sometimes, but odds are you can write your code in a way that doesn't require this. Get to know POSIX and other standards. If you can write code that compiles on a couple of platforms without special conditionals, odds are good that future porters won't have to tweak your code either. Test on multiple platforms if possible. It's a lot easier these days, with free systems like the BSDs and Linux available, as well as Mac OS X joining the POSIX world, and Parallels allowing you to run multiple operating systems at once.
  3. Use GNU autotools for complex projects. It's the most automated system available for making your code work on any Unix-like platform.
  4. If you need to run your program on Windows and Unix, things will be a little trickier, but it's still possible to avoid a lot of redundant coding. There are cross-platform GUI systems like QT, portable build systems like cmake, and portable graphics libraries like OpenGL. By choosing your tools carefully, you can write most of your code so that it will compile and run on just about any operating system.
  5. Use standard variables in your Makefiles to control build and install operations (e.g. PREFIX, CC, CFLAGS).
  6. Respect the end-user's build environment. Instead of brute-force assignments to variables in your Makefiles, use conditional assignments or appends:

        PREFIX  ?= /usr/local
        CC      ?= gcc
        CFLAGS  += -Wall -O
        

    This way, if variables are already set in the user's environment, they won't be clobbered by your Makefile.

    This also allows ports to override the defaults without having to modify the Makefile. For example, a MacPort might issue the following command to install a program with the above definitions:

        make PREFIX=/opt/local install
        

  7. Make sure the Makefile supports all the standard targets, at minimum "all", "install", and "clean". Install should install stripped binaries with 555 permissions, and other files with 444 permissions.

    You can start with this skeletal Makefile that can be easily adapted for most simple projects.

  8. Follow standard naming conventions for the source archive, and the directory it extracts to. The standard used for archives is name-version.tar.gz, which would extract to a directory called name-version. For example,
  9. Follow standard installation structure, so users know where to find things. Installing in /usr/local is a good standard to follow, but do so using variables, rather than hard-coding, and allow the user to override:

        PREFIX  ?= /usr/local
        
        install:
    	    install -c ${bin} ${PREFIX}/bin
        

    FreeBSD users: run "man hier" and check the handbook.

    Some common standards:

    ${PREFIX}/bin User binaries
    ${PREFIX}/sbin System binaries
    ${PREFIX}/lib Libraries
    ${PREFIX}/man/man1 Man pages for commands
    ${PREFIX}/man/man3 Man pages for functions
    ${PREFIX}/share Architecture-independent files
    ${PREFIX}/share/doc Docs other than man pages
    ${PREFIX}/share/examples Example configuration files

A Simple Example

As a simple example, the wavegen port consists of a source archive called wavegen-1.0.tar.gz, which extracts to wavegen-1.0. The Makefile defaults to gcc for the compiler, and /usr/local for the installation prefix. The user (or port) can override without modifying the Makefile. For example, a MacPort might use

    make PREFIX=/opt/local install
    

while Debian user with the Intel compiler might use

    make CC=icc CFLAGS=-O -I/usr/local
    

The Makefile:

    OBJS    = wavegen.o
    CC      ?= gcc
    PREFIX  ?= /usr/local
    CFLAGS  ?= -Wall -O -I${PREFIX}/include
    
    all:    wavegen
    
    wavegen:        $(OBJS)
	    $(CC) -o wavegen $(OBJS) -L${PREFIX}/lib -lgsl -lgslcblas -ltsp -lm
    
    wavegen.o: wavegen.c wavegen.h protos.h
	    $(CC) -c $(CFLAGS) wavegen.c
    
    reallyclean:    clean
	    rm -f .*.bak
    
    clean:
	    rm -f *.o wavegen *.nr
    
    install: wavegen
	    mkdir -p ${PREFIX}/bin ${PREFIX}/man/man1
	    install -c -m 555 wavegen ${PREFIX}/bin
	    install -c -m 444 wavegen.1 ${PREFIX}/man/man1
    

For some other examples of port-friendly software projects, check out the source code for mcweject, libpare, ape, and a few others in the distfiles directory.