I'm working to improve the long languishing Linux build process for Bitfighter, and am having problems with make. My process is actually quite simple, and since make is (nearly) universal, I want to stick with it if I can.
Below I've attached my current Makefile, which works, but clumsily so. I'm looking for ways to improve it, and have three specific questions at this point.
First, the project can be built with several options. Let's take debug and dedicated for this example. The dedicated option will exclude all UI code, and create a more efficient binary good for hosting (but not playing) games. The debug option adds a flag to the compiler that activates debugging code. One might want to build the game with either, both, or neither of these options.
So the question is, how do I make this work? As you can see from the comments in the makefile below, debugging is enabled by setting DFLAGS=-DTNL_DEBUG. I'd like to have the user type
make dedicated debug
rather than
make dedicated DFLAGS=-DTNL_DEBUG
How can I rewrite my makefile so that this will work?
Secondly, when I install the lualibs package on different versions of Linux, I get different libraries. For example, on Ubuntu, when I install the lualib package with apt-get, I get lua5.1.a in my /usr/lib folder. On Centos, when I install the same thing with yum, I end up with liblua.a in my /usr/lib folder. How can I get make to figure out which library I've got, and link that in? Obviously the -l directive is not smart enough for that. I'd like the user to not have to worry about where Lua ends up when it gets installed, and for the makefile to just work.
Finally, is there any way to get make to detect whether certain required packages (freeglut, for example) have not been installed, and either install them automatically or at least alert the user to the fact they need to get them installed (as opposed to simply terminating with a cryptic error message)?
Thanks!!
Here is my Makefile.
# Bitfighter Makefile ####################################### # # Configuration # # # Some installs of lua call the lua library by different names, and you # may need to override the default lua library path. For the ServerHitch # CENTOS installs, for example, you will need to specify the lua library # on the make command line: # LUALIB=/usr/lib/liblua.a # # # To compile Bitfighter with debugging enabled, specify # DFLAGS=-DTNL_DEBUG # on the make command line # # # Building with make on Windows is still highly experimental. You will # probably need to add # WFLAGS="-DWIN32 -D_STDCALL_SUPPORTED" THREADLIB= GLUT=-lglut32 INPUT=winJoystick.o # to the make command line to have any hope of getting it to work! :-) # # ####################################### CC=g++ -g -I../tnl -I../glut -I../openal -DTNL_ENABLE_LOGGING THREADLIB= -lpthread GLUT=-lGL -lGLU -lglut INPUT=linuxInput.o OBJECTS_ZAP=\ CTFGame.o\ ...many more... BotNavMeshZone.o\ ../master/masterInterface.o\ CFLAGS= DFLAGS= EXEFILE=bitfighter OPENAL=../openal/linux/libopenal.a LUALIB=-llua5.1 WFLAGS= .c.o: $(CC) $(DFLAGS) $(WFLAGS) -c $(CFLAGS) $< .cpp.o : $(CC) $(DFLAGS) $(WFLAGS) -c $(CFLAGS) $< default: ../exe/bitfighter bitfighter: ../exe/bitfighter dedicated: CFLAGS=-DZAP_DEDICATED dedicated: GLUT= dedicated: OPENAL= dedicated: EXEFILE=bitfighterd dedicated: ../exe/bitfighter ../exe/bitfighter: $(OBJECTS_ZAP) $(CC) -o ../exe/$(EXEFILE) $(OBJECTS_ZAP) ../tnl/libtnl.a \ ../libtomcrypt/libtomcrypt.a \ $(OPENAL) $(GLUT) $(THREADLIB) $(LUALIB) -lstdc++ -lm ../master/masterInterface.o: make -C ../master clean: rm -f $(OBJECTS_ZAP) ../exe/bitfighter ../exe/bitfightered cleano: rm -f $(OBJECTS_ZAP)
Raw make
doesn't really support any of these uses. make
considers targets passed in on the command line to be different programs to be built, or different actions to take, and it has no concept of using two targets passed in to switch independent options for a single build. make
also doesn't have any built in support for checking for versions of packages installed.
It's a bit of a steep learning curve, but the most common solution for all of these problems is to use the GNU autotools toolchain (Autoconf and Automake, specifically). These tools have been written to help write portable, configurable build systems, that can probe the system for libraries in various locations, and generate Makefiles based on configuration options and the user's system.
If you have ever run ./configure; make; make install
, you have probably used a configure
script generated with Autoconf and Automake.
The Wikipedia article provides a bit of an overview, and the Automake manual provides a tutorial introducing the toolchain.
For your usage, what you would probably want to do is create a configure
using Autoconf that takes options like --enable-debug
and --enable-dedicated
, to set options for generating your Makefile. You could then port your Makefile to Automake, or you could simply turn your Makefile into a Makefile.in
with a few variables that Autoconf will fill in when generating the Makefile
.
While the GNU Autotools system is very complete, and supports a lot of platforms, it is a bit baroque. There are some alternative build systems that support some similar auto-configuration behavior, like CMake and SCons, that might be worth looking into if Autotools feels like too much.
For the specific task of detecting certain libraries, and finding the options you need to link to them, pkg-config
can be used; however, not all libraries install pkg-config
definitions, and not all systems even have pkg-config
installed, so it's not a universal solution, but can be a nice quick and easy way to get something building without too much messing with options in the cases in which it does work.