The BSDBuild module that is used to produce executable files is called
build.prog.mk
. It is used to build programs from source files
in different languages, such as C, C++, Objective C, assembler, Lex and Yacc.
In this tutorial, we will use BSDBuild to compile an executable program from C sources, and install a manual page. We also want to generate a configure script that will look for an OpenGL library and link our executable against it.
If BSDBuild is not already installed on your system, install it first.
$ wget http://stable.hypertriton.com/bsdbuild/bsdbuild-2.x.tar.gz $ tar -xzf bsdbuild-2.x.tar.gz $ (cd bsdbuild-2.x && ./configure && make all install)
People building your application do not need to have BSDBuild installed, but you need to bundle the necessary components with your application. mkify reduces this job to executing a single command. To save some space, it will also only install the modules you actually need.
The redistributable components of BSDBuild consist in a number of small make include files (*.mk files) and scripts. These files are normally placed in a directory called ./mk somewhere (usually at the top-level directory) of your source tree. While this may seem like a disadvantage, this actually reduces the amount of gunk that ends up in your tree by orders of magnitude compared to other systems which use generated makefiles.
The mkify program is used to copy the required files into your
application directory. It also creates sample Makefile and
configure.in files if they do not exist. Since we are going to
use the build.prog.mk
module, we pass the prog
argument to the
script.
$ cd MyApp $ mkify prog
You can run mkify again in the future to upgrade your includes to a later BSDBuild version, but updates to the make includes are very rare and you do not need to bother re-running mkify on your projects after having upgraded BSDBuild unless you need some specific functionality that has been added to the new release.
The mkconfigure script generates a ./configure script from a configure.in file. Our configure scripts offer a similar user interface than GNU-style configure scripts, but the internals are different. Our configure.in allows special directives (see the mkconfigure manual for a complete list), along with Bourne shell code fragments that will be inserted as-is in the configure script.
# Register some configure options specific to our application. REGISTER("--enable-threads", "Thread support [default: no]") REGISTER("--enable-gui", "Graphical user interface [default: yes]") # Name and version of the application, written to config/progname.h # and config/version.h. This is completely optional. HDEFINE(PROGNAME, "MyApp") HDEFINE(VERSION, "1.0") # Check for a C compiler. If one exists, ${HAVE_CC} will be set. CHECK(cc) # Check for OpenGL. If it works, then ${HAVE_OPENGL} will be set. CHECK(opengl) # OpenGL is required by our program, so we fail if it is not found. if [ "${HAVE_OPENGL}" = "no" ]; then echo "Sorry, this program requires OpenGL." exit 1 fi # Alternatively, we can use REQUIRE() which will cause the configure # script to exit if the test fails: # # REQUIRE(opengl) # The MDEFINE() directive sets a makefile define. if [ "${enable_gui}" != "no" ]; then MDEFINE(PROG_TYPE, "gui") else MDEFINE(PROG_TYPE, "cli") fi # Define THREADS if --enable-threads was given. if [ "${enable_threads}" = "yes" ]; then HDEFINE(THREADS, 1) else HDEFINE(THREADS, 0) fi
The REGISTER()
directives create new options to ./configure.
If the user passes the --with-foo
option, "${with_foo}"
will be
set to "yes"
. If the user passes --without-foo
, it will be set
to "no"
.
The next two directives define PROGNAME
and VERSION
. They are not
required but they are useful when it comes to packaging.
The HDEFINE()
directive generates header files such as
./config/progname.h and ./config/version.h.
The CHECK()
directives performs a test. The BSDBuild distribution
includes a number of
built-in tests
for different libraries and programs.
The cc
test checks for a C compiler, and opengl
checks for an
OpenGL library.
All tests define a number of variables following a standard naming
convention, such as HAVE_FOO
, FOO_LIBS
and
FOO_CFLAGS
.
In this case, our sample application requires OpenGL in order to work,
so we fail if the opengl
test returned a HAVE_OPENGL
value of "no". Instead of manually testing HAVE_OPENGL
and
failing, we could have also used REQUIRE(opengl)
.
The CHECK(opengl)
test, assuming it was successful, will define the
make variables OPENGL_CFLAGS
and OPENGL_LIBS
, and we will be able
to use them directly in our makefiles, like this:
CFLAGS+= ${OPENGL_CFLAGS} LIBS+= ${OPENGL_LIBS}
The next piece of code sets the make variable $PROG_TYPE
using the
MDEFINE()
directive. This variable will be visible throughout our
makefiles. This particular variable ($PROG_TYPE
) happens to be
recognized by build.prog.mk.
Note that there is a subtle difference between a test like:
if [ "${enable_threads}" = "yes" ]
and a test like:
if [ "${enable_threads}" != "no" ]
The first test will evaluate to true if and only if the user has
explicitely provided the --enable-threads
option.
The second test will evaluate to true unless the user has explicitely
passed --disable-threads
.
To generate the configure script, execute:
$ cat configure.in | mkconfigure > configure $ chmod 755 configure
With BSDBuild, makefiles can be edited directly. A simple Makefile might look like:
TOP= . include ${TOP}/Makefile.config PROG= MyApp SRCS= MySource1.c \ MySource2.c CFLAGS+= ${OPENGL_CFLAGS} LIBS+= ${OPENGL_LIBS} include ${TOP}/mk/build.prog.mk
The definition of TOP
is required in all BSDBuild makefiles and
determines the top-level source directory. The modules will always look
for the BSDBuild scripts in the ${TOP}
/mk/ directory.
PROG
defines the name of the executable. The actual filename might
have different prefixes or suffixes depending on the target platform.
SRCS
is simply the list of source files to compile and link into
the program. The extensions of the files determine their language and which
compiler to use. The full list of supported languages is documented in the
build.prog.mk manual.
Since our sample application requires OpenGL, we append the contents of
OPENGL_CFLAGS
and OPENGL_LIBS
to our CFLAGS
and LIBS
, respectively. These values are defined in
Makefile.config, which is generated by the ./configure script
(more specifically, the CHECK(opengl)
line).
the variables used by build.prog.mk
are documented in its
manual page.