Home       About Me       Contact       Downloads       Part 57    Part 59   

Part 58: The New Build Process

July 4, 2012

Have you ever had one of those nightmares where you run and run and never get anywhere? The last few weeks of work have been like that.

Back in Part 55 I explained that Shamus Young was interested in using my GUI code, so I was going to put some time into trying to get everything usable by another programmer. To that end, I split my framework up into chunks so that you didn't have to use all of it to use the GUI. That work was done under Windows, and completely broke the Linux and Mac ports.

Rather than fix those right away, I worked on the GUI code and tried to produce something Shamus would use. We got a couple of weeks of work in together, but then Programmer Shamus was hit by lightning or something and morphed into Author Shamus. He's off writing a book and I haven't heard a word out of him lately on my code.

Without someone actually using the GUI, I'm left wondering if it's worth the effort right now. I don't know whether to put work into the GUI so that it's reasonable the next time Shamus becomes a programmer, or whether to leave it and do just as much as I need for my projects.

Since I didn't know what to do about that, I went ahead and tried to get all the code running again under Linux and Mac. It's been months since I did anything with that low level code, and so I had to remember all the details of initializing the display and restructure the interfaces to match the new Windows version.

Unfortunately, the Mac version developed some strange bugs and I got sick of working on it. The Mac has been far and away my most difficult platform to support. I would give up on it except for the idea that Tablets Are The Future, and some day all this code will run on the iPad.

Debug and Release Builds

As I mentioned in Part 57, I realized the build process had gotten unmanageable. After I learned that I could use msbuild and xcodebuild to automate the build process on Windows and Mac, I figured I had everything I needed to redo the build and make it simple.

I suppose that would have been true, but once I started messing with the build, I kept discovering things I wanted to change. For example, under Windows, I build both Release and Debug versions of the libraries and the demos. The Debug version of SeaOfMemes links to the Debug version of the libraries, and Release links to the Release version.

I know next to nothing about the Make program, other the absolute basics I learned many years ago. So I was stumped on how to build Debug and Release versions linked this way under Linux and Mac.

For Linux, I had written a very basic makefile:

FILES = Objects/TestCube.o Objects/MovementApp.o Objects/uiHelpPane.o

# compilation 
Objects/%.o: $(SOURCE)/%.cpp
  $(CC) $(CFLAGS) $(INCDIRS) $(DEFINE) -c $< -o $@

# link 
TestCube : $(FILES) $(FRAMEWORK)
  $(CC) $(LIBDIRS) $(LIBS) -o TestCube $(FILES) $(FRAMEWORK)

I won't go into gory detail here, but the final program TestCube depends on the object files $(FILES) and framework. To build it, make does a link. To build the object files, it compiles the corresponding source files. This is about as basic as a makefile gets.

To have both Debug and Release builds, I thought I needed to introduce some variables for the various compiler options, and set them based on the target. I messed with this for at least a day without getting anywhere. Make is not a programming language, despite all the macros and functions that make it look like one! I finally did it the obvious way:

TARGET = TestCube
FILES = TestCube.o MovementApp.o HelpUI.o

debug: Debug/$(TARGET)
DEBUGFILES = $(addprefix Debug/, $(FILES))

# debug compilation
Debug/%.o: $(SOURCE)/%.cpp
  $(CC) $(DEBUGFLAGS) $(INCDIRS) $(DEFINE) -c $< -o $@

# debug link
Debug/$(TARGET): $(DEBUGFILES)
  $(CC) $(DEBUGLIBDIRS) $(LIBS) -o Debug/$(TARGET) $(DEBUGFILES) $(FRAMEWORK)

release: Release/$(TARGET)
RELEASEFILES = $(addprefix Release/, $(FILES))

# release compilation
Release/%.o: $(SOURCE)/%.cpp
  $(CC) $(RELEASEFLAGS) $(INCDIRS) $(DEFINE) -c $< -o $@

# release link
Release/$(TARGET): $(RELEASEFILES)
  $(CC) $(RELEASELIBDIRS) $(LIBS) -o Release/$(TARGET) $(RELEASEFILES) $(FRAMEWORK)

Here I've just duplicated everything in Debug and Release versions, with different options and directories for each. I create the two lists of Debug object code files and Release object code files with the addprefix macro function.

I'm open to suggestions on a better way, but this does the trick.

In Visual C++, there are completely separate build configurations, and I am just specifying different libraries and options on each. For XCode, it is more of a problem. The examples I'd seen linked to libraries by adding them to the source files, along with the QuartzCore and OpenGL frameworks. But that meant adding a specific version, either Debug or Release.

It look awhile for me to figure out how to set the library path using XCode's macros so that it was different for Debug and Release, and specify my libraries in the link, not as part of the project. It turns out you can specify them under the "Other Linker Flags"...

Paths

Next, I had this problem of specifying the include files and libraries. Under Windows, I normally specify all the include and link paths to Visual C++. I did the same for XCode, and hand-coded the paths in the Linux makefiles.

When there was just one framework library, this was something like "../mgFramework/Include". But now, with the framework pulled apart into pieces, using multiple supporting libraries, and moved into its own directory, the include path looked like this:

../../../Milligram/mgUtil/Include
../../../Milligram/mgPlatform/Include
../../../Milligram/mg2D/Include
../../../Milligram/mg3D/Include
../../../Milligram/mgGUI/Include
../../../Milligram/JpegLib/Include
../../../Milligram/FreeType/Include
../../../Milligram/Ogg/Include
../../../Milligram/Vorbis/Include
../../../Milligram/ZLib/Include

The link path was similar. Not only was this a pain to copy into all the projects, but I hated requiring other programmers to do all this before they could even get started with my code. So I recoded all my include statements in the project to be relative to the root of Milligram, which allows a single include directory of "../../../Milligram".

Linking was a similar problem. I wanted to specify Milligram as the library path, and then just list the libraries:

-lmgUtil -lmgPlatform -lmg2D -lmg3D -lmGUI -lJpegLib -lFreeType -lOgg -lVorbis -lZLib

This is an improvement, although still a nuisance. Again, it took some Googling to find out where in both Visual C++ and XCode I should specify this. It's complicated by the fact that I have multiple platforms and both Debug and Release versions in each. Under XCode, I actually set the library path to

../../../Milligram/BuildOSX/$(CONFIGURATION)

Along the way, I discovered that under Visual C++, you can specify linker options in your source code, and write:

#pragma comment(lib, "mgUtil.lib")

There's a lot of snark from Linux programmers about how unportable and disgusting this is, but it is actually the style Shamus was used to. VC++ programmers can just specify Milligram once in the include and library paths, then include the interfaces to the libraries. That will add the library name to the linker options.

This doesn't affect the Linux and Mac ports, and could even be ignored on Windows if you liked. It's more convenient there though, so I've used it.

The New Downloads

I've updated the Downloads pages with the new builds. Instead of separating out the source for Crafty, McrView and SeaOfMemes, I've combined them all into a single source zip and GitHub project. You need the framework (now called "Milligram") to build any of them, so there wasn't much gained by separating them all out.

There are still separate downloads for all the games, and one big download for all the demos.

I've also rearranged the directory structure. Instead of having "Libs" with the various supporting libraries (FreeType, JpegLib), then my libraries (mgUtil), and the demos at the top level, I've just split it into three top level directories:

Demos
  Landscape
  SaucerMovie
  TestCube
  Trees

Games
  Crafty
  McrView
  SeaOfMemes

Milligram
  mgUtil
  mgPlatform
  mg2D
  mg3D
  mgGUI

  JpegLib
  FreeType
  Ogg
  Vorbis
  ZLib

There are BuildLinux, BuildOSX and BuildWin directories at several levels. Each of these build something for the corresponding platform. At the bottom, the build for a project like mgUtil or TestCube builds just that library or application.

In Demos, Games, and Milligram, there are also Build* directories. These are makefiles that build all the projects in that directory (all the libraries, all the demos, all the games.) You would want to build the Milligram libraries first, then either of the other two.

And at the very top, there are Build* directories that build everything and make the release zips that you can download off this site. Only the root BuildWin directory makefile builds the source zip though.

Download the complete project source at Part 58 Source.

or find it on GitHub at: https://github.com/mgoodfel/SeaOfMemes

In Conclusion

At this point, I really regret supporting multiple platforms and trying to release these demos. Videos of whatever worked would have been so much easier. Still, I've gone pretty far down this road, and I don't intend to stop. I hope it pays off in the end.

As I mentioned in the last part, I need one image on each page to track RSS readers. This week, we have a picture taken in the east SF bay area. It was taken years ago and I have forgotten exactly where. This boat-type thing was out in the edge of the bay doing something or other. I have no idea what.

An old boat working in the muck for mysterious reasons seemed appropriate to this post.

Home       About Me       Contact       Downloads       Part 57    Part 59   

blog comments powered by Disqus