General
Project Home
Consulting Services
C/Unix Programmer's Guide
FreeBSD and MacPorts
Which FreeBSD?
Creating Port-friendly Projects
Port Descriptions
APE
Roboctl
NBC/NXC
Links
FreeBSD Ports Project
MacPorts Project
Ports 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, Gentoo Portage, 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 will be cleaner. 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. This is especially important for large, popular
libraries like GTK, QT, etc.
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 and follow
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 found myself happy to 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 standards in software development has enormous benefits for everyone.
How to be port-friendly
Here are some tips for making your code port-friendly:
-
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.
Projects that embed common libraries usually end up using outdated
versions long after they become obsolete, because it's extra work
to update the project with new versions all the time.
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.
-
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.
-
Use GNU autotools for complex projects. It's the most automated system
available for making your code work on any Unix-like platform.
-
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.
-
Use standard variables in your Makefiles to control build and install
operations (e.g. PREFIX, CC, CFLAGS).
-
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 patch
the Makefile. For example, a MacPort might issue the following command
to install a program with the above definitions:
make PREFIX=/opt/local install
-
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.
-
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. This is the default for most ports systems, so if you
follow it, the port maintainers have an easier time porting your
code.
-
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 a 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.
|