Many years ago, when building large software projects, I used GNU make (or my own patched version of it), and had developed a set of macros to simplify developing complex build trees.
Since the early 90's my main development machines, run BSD (NetBSD to be precise), and the BSD source tree is good example of a large software project. In following NetBSD's -current development, I learned to use the new BSD Makefiles and make(1), I did an autoconf version of it that I'll refer to as bmake(1) from here on.
Since then all my new projects and many of my old ones use bmake. Because bmake.tar.gz uses GNU's autoconf and is quite portable, I've skipped the effort of making the rest of my distributions support GNU make.
Bmake is derived from NetBSD's make(1), its goal is to be a portable version of same, so new features are added via imports of NetBSD's make (I'm one of the contributors to NetBSD). Thus bmake is kept in sync with NetBSD's make.
Unless you are using NetBSD you will need mk.tar.gz as well, this is a portable collection of mk-files that can augment and even substitute for bsd.*.mk.
Since about 2003, the bmake version typically represents the date of import from NetBSD, and I avoid any code changes which are not strictly related to portability.
It may be interesting to note that I have projects where much of the tree uses the same simple Makefile:
PROG= ${.CURDIR:T} .include <prog.mk>
The example Makefile above, perhaps suggests why I like bmake(1) The important magic is in the line:
.include <prog.mk>
Makefiles for libraries include lib.mk btw. Anyway, apart from reading a bunch of rules from the system macros (/usr/share/mk/* by default) it reads "../Makefile.inc" thus providing a hook for much magic.
In the early 90's bsd.*.mk were strictly targeted at building the BSD src tree, and were not as flexible or portable as I wanted. So I started on my own mk-files.
Another cool feature of bmake(1) is the built in distinction between ${.CURDIR} (where bmake was launched) and ${.OBJDIR} (where it is working).
In the simplest case, if the directory obj exists in the same directory as the Makefile, then bmake will chdir into it before doing anything else. This helps keep src and object files separate. If the OBJMACHINE is set then the default object dir is obj.${MACHINE}. Note that obj can be a symlink off to a separate file system, eg.:
/usr/src/bin/cat/obj -> /usr/obj/bin/cat
in either case, building for multiple architectures is easy.
Even better if MAKEOBJDIRPREFIX is set in the environment (see below) then it is trivial to export a source tree read-only, since there is no longer a need for obj dirs in the tree itself.
The only hassle with MAKEOBJDIR and MAKEOBJDIRPREFIX is that they need to be set in the environment. Actually recent versions of bmake allow the objdir to be set later, by use of .OBJDIR:, but setting MAKEOBJDIR* in the environment is simpler. This feature is used if you use mk-files and set MKOBJDIRS=auto.
If MAKEOBJDIRPREFIX is set in the environment and the directory ${MAKEOBJDIRPREFIX}${.CURDIR} (.CURDIR is set by getcwd()) exists, make(1) will chdir into it rather than look for ./obj as described above. This simple feature allows true read-only source trees. This entire build tree has been built using MAKEOBJDIRPREFIX. Note that MAKEOBJDIRPREFIX is actioned before any Makefiles are read which is why it must be an environment variable.
Recent versions of bmake, also allow variable substitutions on MAKEOBJDIR which makes it even better than MAKEOBJDIRPREFIX. For a neater result you can use something like:
export MAKEOBJDIR="\${.CURDIR:S,$SRCTOP,$OBJTOP,}"
The variables MACHINE and MACHINE_ARCH are built into bmake. They can also be controlled via the makefiles for cross-building.
Each new version of bmake goes through the following on several platforms (NetBSD, FreeBSD, SunOS, Linux, OS/X):
$ tar zxf ~/bmake.tar.gz $ tar zxf ~/mk.tar.gz $ ./bmake/boot-strap
The boot-strap script will build bmake, and run its unit-test suite, and only if that succeeds output the commands to install it.
I have the following content in ~/.bmake-boot-strap.rc to keep things simple:
: echo "Building for $HOST_TARGET" case "$HOST_TARGET" in netbsd*) sys_share_mk=:/usr/share/mk;; *) sys_share_mk=;; esac objdir=./$HOST_TARGET case "$prefix" in */$HOST_TARGET) CONFIGURE_ARGS="$CONFIGURE_ARGS --with-default-sys-path=$prefix/share/mk:`dirname $prefix`/share/mk$sys_share_mk" ;; esac
For building bmake on NetBSD I use the system bsd.*.mk so the following is sufficient:
$ tar zxf ~/bmake.tar.gz $ ./bmake/boot-strap -m none
The example below is building bmake-20091118 on SunOS. Note that the build isn't considered successful unless the unit-tests pass. With the above content in ~/.bmake-boot-strap.rc, boot-strap will print the commands to install into /homes/sjg/sunos5-sparc/bin/ etc.
Also note that the src directories are left untouched and a host-target specific objdir is used. Thus I can use the exact same command sequence to build bmake for all the platforms available:
$ mkdir ~/tmp/x $ cd ~/tmp/x $ tar zxf ../bmake-20091118.tar.gz $ tar zxf ../mk-20091201.tar.gz ./bmake/boot-strap NOTE: reading /homes/sjg/.bmake-boot-strap.rc Building for sunos5-sparc checking for gcc... gcc checking for C compiler default output file name... a.out ... checking if diff -u works... no checking for MACHINE & MACHINE_ARCH... defaults: MACHINE=sunos5, MACHINE_ARCH=sparc Using: MACHINE=sunos5, MACHINE_ARCH=sparc Using: SHELL=/usr/xpg4/bin/sh Using: MKSRC=/homes/sjg/tmp/x/mk configure: creating ./config.status config.status: creating Makefile config.status: creating makefile.boot config.status: creating lst.lib/makefile.boot config.status: creating unit-tests/Makefile config.status: creating config.h ... rm -f bmake *.[ado] */*.[ado] .*.done .depend gcc -g -O2 -I. -I/homes/sjg/tmp/x/bmake -DHAVE_CONFIG_H -D_PATH_DEFSYSPATH=\"/homes/sjg/sunos5-sparc/share/mk:/homes/sjg/share/mk\" -c -o arch.o /homes/sjg/tmp/x/bmake/arch.c gcc -g -O2 -I. -I/homes/sjg/tmp/x/bmake -DHAVE_CONFIG_H -D_PATH_DEFSYSPATH=\"/homes/sjg/sunos5-sparc/share/mk:/homes/sjg/share/mk\" -c -o buf.o /homes/sjg/tmp/x/bmake/buf.c ... gcc *.o lst.lib/*.o -o bmake.boot rm -f *.[ado] */*.[ado] mkdir -p /homes/sjg/tmp/x/sunos5-sparc/mk cp -f sys.mk autoconf.mk autodep.mk auto.obj.mk dep.mk doc.mk dpadd.mk host-target.mk init.mk inc.mk final.mk java.mk lib.mk libs.mk links.mk man.mk nls.mk obj.mk own.mk prlist.mk prog.mk progs.mk scripts.mk subdir.mk target-flags.mk warnings.mk yacc.mk /homes/sjg/tmp/x/sunos5-sparc/mk cp -f sys/AIX.mk sys/HP-UX.mk sys/Linux.mk sys/NetBSD.mk sys/OSF1.mk sys/SunOS.mk sys/Darwin.mk sys/IRIX.mk sys/OpenBSD.mk sys/Generic.mk sys/UnixWare.mk /homes/sjg/tmp/x/sunos5-sparc/mk/sys .. ln -s dep.mk bsd.dep.mk ln -s doc.mk bsd.doc.mk ln -s init.mk bsd.init.mk ln -s lib.mk bsd.lib.mk .. CC="gcc" LIBC= MAKEFLAGS= MAKESYSPATH=`pwd`/mk:/homes/sjg/sunos5-sparc/share/mk:/homes/sjg/tmp/x/mk:/homes/sjg/sunos5-sparc/share/mk:/homes/sjg/share/mk:/usr/share/mk:/usr/local/share/mk:/opt/share/mk ./bmake.boot -f Makefile .. /homes/sjg/tmp/x/sunos5-sparc/bmake -f Makefile > test.out 2>&1 diff /homes/sjg/tmp/x/bmake-20091118/unit-tests/test.exp test.out Commands to install into /homes/sjg/sunos5-sparc/ mkdir -p /homes/sjg/sunos5-sparc/bin cp ./sunos5-sparc/bmake /homes/sjg/sunos5-sparc/bin/bmake-20091118 rm -f /homes/sjg/sunos5-sparc/bin/bmake ln -s bmake-20091118 /homes/sjg/sunos5-sparc/bin/bmake mkdir -p /homes/sjg/share/man/cat1 cp /homes/sjg/tmp/x/bmake-20091118/bmake.cat1 /homes/sjg/share/man/cat1/bmake.1 sh /homes/sjg/tmp/x/mk/install-mk /homes/sjg/share/mk
I just cut/paste those last few lines to install.
Author: | sjg@crufty.net |
---|---|
Revision: | $Id: bmake.txt,v 1.5 2009/12/21 02:35:46 sjg Exp $ |
Copyright: | Crufty.NET |