🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

GNU directory structure & autoconfiscation

Started by
17 comments, last by Sydian 16 years, 1 month ago
I've been working on a small, portable game for some time now, as a hobby. About 6 months ago, I started getting into Linux and the whole open-source movement, so I've decided to release it under the GPL and put it up on SourceForge. The problem is, I want to "autoconfiscate" it so that it is easy for people to "./configure; make; make install" but I'm lost. I've tried reading some "books" on autotools (http://sources.redhat.com/autobook/autobook/autobook.html) and some tutorials, but I still don't understand. I think the first thing that I need to do is reorganize the project into a more standard directory structure. Here's what I have: bin/(binary files after build) bin/data/(maps, images, etc.) build/(intermediate files during build) src/(source code) (you can see the actual files at http://sourceforge.net/projects/odysi if you go to "SVN Browse" under the code section) I was wondering what a more GNU-like directory structure for the files I have might look like. Any help?
Advertisement
Moved to the Everything Unix forum.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

If we do this the easy way, you will have just a "data" directory and a "src" directory.

Say I have two files as part of a project I'm calling "tester":
data/hello.txt
src/hello.c

I will create the following files:
configure.ac
AC_INIT(tester, 0.0.0)AM_INIT_AUTOMAKE()# I like using maintainer mode a lot, it's not required thoughAM_MAINTAINER_MODE# For C, for other languages we'll change thisAC_PROG_CCAC_LANG(C)AC_OUTPUT([Makefiledata/Makefilesrc/Makefile])

Makefile.am
SUBDIRS = data src

data/Makefile.am
testerdir   = $(pkgdatadir)tester_DATA = hello.txt

src/Makefile.am
bin_PROGRAMS    = testertester_SOURCES  = hello.c

Then, we'll have to run a couple programs in-order to create the rest of the files from those. In a shell, we'll run the following:
aclocal
automake --add-missing --copy --foreign
autoconf

If we do not pass "--foreign" to automake, it'll give you tips about making your project GNU standards compliant (things like adding a change log and a license, et cetera).

Now, to build and install the program you'd just do the normal "./configure; make; make install". To make better use of the autotools tool chain, try "./configure --prefix=/some/directory --enable-maintainer-mode" to install to a specific place and enable maintainer mode; if you edit a autotools related file with maintainer mode enabled, it will rebuild the necessary generated files on the next call to "make". Also useful is "make dist".

Generally, instead of adding all of the generated files to your versioning system, just "configure.ac" and the "Makefile.am" files as well as a script (generally called "autogen.sh") than runs the aclocal/automake/autoconf commands as necessary.

If that's too different from what you're accustomed to, we can probably mold the autotools a touch more to your liking. For more tips, feel free to ask.
That does help me a whole lot, but I have some questions still. My program uses a number of third-party libraries (boost, sqlite3, SDL, SDL_mixer, and OpenGL). I managed to make all these link properly using a poorly-made hand-crafted (or rather, hand-butchered) makefile:

PROJ = odysiMAPEDITOR = mapeditorCREATEMAP = createmapBDIR = ../build/BINDIR = ../bin/OBJSCREATEMAP = $(BDIR)createmap.oOBJSPROJ = $(BDIR)main.oOBJSMAPEDITOR = $(BDIR)mapeditor.oOBJS = $(BDIR)image.o $(BDIR)map.o $(BDIR)mobile.o $(BDIR)tileManager.o $(BDIR)characterFactory.oSRCS = mapeditor.cpp image.cpp main.cpp map.cpp mobile.cpp tileManager.cpp characterFactory.cpp createmap.cppCC = g++CFLAGS = -Wall -c `sdl-config --cflags`LFLAGS = -Wall `sdl-config --libs` -lGL -lSDL_mixer -lsqlite3DEPEND = makedepend $(CFLAGS)all: $(BINDIR)$(MAPEDITOR) $(BINDIR)$(PROJ) $(BINDIR)$(CREATEMAP)$(BINDIR)$(CREATEMAP) : $(OBJSCREATEMAP)	$(CC) $(LFLAGS) $(OBJS) $(OBJSCREATEMAP) -o $(BINDIR)$(CREATEMAP)$(BINDIR)$(MAPEDITOR) : $(OBJS) $(OBJSMAPEDITOR)	$(CC) $(LFLAGS) $(OBJS) $(OBJSMAPEDITOR) -o $(BINDIR)$(MAPEDITOR)$(BINDIR)$(PROJ) : $(OBJS) $(OBJSPROJ)	$(CC) $(LFLAGS) $(OBJS) $(OBJSPROJ) -o $(BINDIR)$(PROJ)$(BDIR)%.o : %.cpp	$(CC) $(CFLAGS) $< -o $(BDIR)$@clean :	rm $(BDIR)* $(BINDIR)$(PROJ) $(BINDIR)$(MAPEDITOR)


I know it's a mess, and that's one of the things I am trying to get away from by using autotools and a more standardized approach, but I'm not sure how to reproduce the linking information with automake, and I would also like to have some kind of check for those libraries when the user does "./configure" since most of the problem people have is that they are missing one or more of them.

I don't know why this is confusing me the way it is... If I can write in C++, I aught to be able to write the script to compile my program by now! Hah.

The example you provided helped me a lot with how to organize the directories and make files, though. I really appreciate it.
Also, should I have a Makefile.am for sub-directories within the data directory?

Edit: I went ahead and added one for sub-directories too.

I got it more or less working now, using these files:

configure.ac
AC_INIT(odysi, 0.0.1)AM_INIT_AUTOMAKE()AM_MAINTAINER_MODEAC_PROG_CXXCPPAC_LANG([C++])AC_OUTPUT([Makefiledata/Makefiledata/images/Makefilesrc/Makefile])


Makefile.am
SUBDIRS = data src data/images


data/Makefile.am
odysidir = $(pkgdatadir)odysi_DATA = base.tm characters.tm music.ogg world.map characters.mob objects.tm


data/images/Makefile.am
odysidir = $(pkgdatadir)/imagesodysi_DATA = *.tga


src/Makefile.am
bin_PROGRAMS = odysiodysi_SOURCES = cache.h image.cpp pixel.h tileManager.h characterFactory.cpp image.h map.h SDL_Object.h uncopyable.h characterFactory.h log.h mobile.cpp singleton.h util.h character.h main.cpp mobile.h std.h config.h music.h tile.h map.cpp object.h tileManager.cppodysi_CPPFLAGS = `sdl-config --cflags`odysi_LDFLAGS = `sdl-config --libs` -lGL -lSDL_mixer -lsqlite3


autogen.sh
aclocalautomake --add-missing --copy --foreignautoconf


...

My only two questions (besides if there is anything obviously wrong with the above) is: how do I make my program know where to look for the data files? and how do I make configure know to look for the libraries (SDL, etc.)?

[Edited by - Sydian on May 31, 2008 9:02:40 PM]
"CPP" in Automake-speak is "C PreProcessor" rather than C++. Try "CXX" when you're trying to talk to the C++ compiler rather than the preprocessor (in general, on most platforms, either will work the same way, but right is right :P). "LDADD" tends to be the correct suffix for programs linking to libraries ("LDFLAGS" being used for passing other options to the linker). Wildcards inside of Makefile.am files don't tend to work too well (much like other Makefiles), which unfortunately means you have to list each file to be portable and non-hackish.

For any library that provides a pkg-config file, using autoconf to check for it is dead simple. It's not much harder in other situations, but we'll start with SDL and pkg-config.

Inside configure.ac:
PKG_CHECK_MODULES(SDL, [sdl])AC_SUBST(SDL_CFLAGS)AC_SUBST(SDL_LIBS)

An example Makefile.am using the information gathered:
bin_PROGRAMS = testertester_SOURCES = tester.ctester_CFLAGS  = @SDL_CFLAGS@tester_LDADD   = @SDL_LIBS@

You'll need to run aclocal again after adding these macros to your configure.ac. and regenerate your autotools files if maintainer mode isn't enabled. By default PKG_CHECK_MODULES requires success, but we can have it look for optional libraries as well. The pkg-config macro can check for minimum versions too.

Subdirectory Makefile.am files can contain further SUBDIRS variables if you'd like, but it doesn't particularly matter in your situation. Each subdirectory of your data directory does not require its own Makefile.am, you can just directly reference the files in the parent directory's Makefile.am.

Making your program know where to find its configured data directory isn't too hard. Here's the easiest (but not perhaps the best) way:
tester_CPPFLAGS = -DPKGDATADIR="\"$(pkgdatadir)\""

That will make the macro "PKGDATADIR" in your program equal to the value contained within "pkgdatadir" in the Makefile as a string literal. There is such a thing as a configuration header that may prove useful for you, but it gets complicated quickly, so I'll skip that for now.
Awesome, I got it all working, and looked into the pkg-config command and saw sqlite3 there, so copied what you suggested for SDL for Sqlite3, and it worked great. Now there only three more things I need to somehow check for in config:

boost (just headers)
SDL_mixer
OpenGL

I didn't see any of them when I did "pkg-config --list-all". Any tips?

At any rate, my program is a whole lot better off now than it was this morning. Thanks for all the help!
OpenGL is a little tricky if you want to maximize portability (MacOS X being the secondary case in this one). Here is how I've done it before:
HAVE_OPENGL="0"AC_CHECK_HEADER([GL/gl.h],[	OPENGL_HEADER="<GL/gl.h>"	HAVE_OPENGL="1"],[])AC_CHECK_HEADER([OpenGL/gl.h],[	OPENGL_HEADER="<OpenGL/gl.h>"	HAVE_OPENGL="1"],[])AC_SUBST(OPENGL_HEADER)AC_SUBST(HAVE_OPENGL)AM_CONDITIONAL(HAVE_OPENGL, test x$HAVE_OPENGL = x1)

Boost is even weirder (to do it right), but someone has already done the work for us on that one. Create a subdirectory named "m4" in the project root. Go here and download the M4 macros for the parts of Boost that you're using and place them in that directory. Add a line for each section of Boost that you're using to your "configure.ac" like so (just a snippet from one of my own autoconf setups):
AX_BOOST_BASE()AX_BOOST_FILESYSTEM()AX_BOOST_REGEX()AX_BOOST_SIGNALS()

Now we must run aclocal again and tell it to include the "m4" subdirectory: aclocal -I m4

For SDL_mixer something like this should work (untested):
AC_CHECK_HEADER([SDL/SDL_mixer.h],[],[	AC_ERROR([Couldn't detect SDL_mixer.])])

All that does is check for the header, and we assume the library is there.
Thanks a lot; it all works exactly like I want it to now. :-)

Only a small question left: is there an easy way to have it put intermediate build files in a different directory than the source code, or is this not commonly done?
Quote: Original post by Sydian
Only a small question left: is there an easy way to have it put intermediate build files in a different directory than the source code, or is this not commonly done?

By default, built files will be placed in the directory of the Makefile. So, adding to my previous example, if we create a directory named build and create a Makefile.am there like this:
bin_PROGRAMS   = testertester_SOURCES = ../src/hello.c

The files hello.o and tester will be placed in build rather than src. That's the easiest way that I am aware of to get the results you want.

This topic is closed to new replies.

Advertisement