bmake
*****

Introduction
============

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>

macros
======

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_.

.OBJDIR
=======

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``.

``MAKEOBJDIRPREFIX``
--------------------

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.


``MAKEOBJDIR``
--------------

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,}"


``MACHINE`` and ``MACHINE_ARCH``
--------------------------------

The variables ``MACHINE`` and ``MACHINE_ARCH`` are built into bmake.
They can also be controlled via the makefiles for cross-building.

``MACHINE``
	This is normally used to represent a specific cpu such as
	``i386``.

``MACHINE_ARCH``
	In some cases a range of cpu's have a common architecture such
	as ``mips`` or ``m68k``.  ``MACHINE_ARCH`` is used to select
	common include dirs, toolchains etc.

``HOST_TARGET``
	When cross building it can be useful to differentiate things
	build for the build host rather than the target.
	``HOST_MACHINE`` and ``HOST_MACHINE_ARCH`` as well as
	``TARGET_*`` can be useful.


Building bmake
==============

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.

------

.. _NetBSD: http://www.netbsd.org/
.. _mk-files: mk-files.htm
.. _`make(1)`: make_1.htm
.. _`bmake(1)`: bmake_1.htm
.. _bmake.tar.gz: http://www.crufty.net/ftp/pub/sjg/bmake.tar.gz
.. _mk.tar.gz: http://www.crufty.net/ftp/pub/sjg/mk.tar.gz

:Author: sjg@crufty.net
:Revision: $Id: bmake.txt,v 1.5 2009/12/21 02:35:46 sjg Exp $
:Copyright: Crufty.NET

