“I’ve found that the problem with comparing ourselves to others is that you’re usually comparing your strength to their weakness or vice versa. Either way, it’s not fair.” – David/Unknown
“You can shave truffles over a dish and call it special, but it’s not; it’s just expensive.” – Rick Bayless
Monty Taylor one of the primary contributors to the drizzle project has done some great work putting together a automake/autoconf framework which has been released as pandora-build The framework came about as a result of trying to maintain and improve the build system for several projects mostly related to drizzle or at least belonging to the drizzle developers. It was a pain to duplicate the work in multiple places and it was obvious the setup would be of great use to other projects and so it was pulled out, cleaned up, and released as a skeletal project. One of the best “features” of the project is that it turns on as much -W* and related flags as possible thus ensuring your code is as clean, portable, and bug-free as the compiler is capable of. It can take some getting used to, but trust me this is a very good thing.
If you go and grab the tarball you’ll have a pretty good start on a build system for you project, but as it stands there’s essentially no documentation on what to do next. Given that I’m intending to write a quick-start guide to getting a project off the ground with pandora-build. This post will also give you a starts on working with dynamically linked libraries and executables that depend on them. We’ll also use build out a basic unit test framework using cppunit.
Before we get started I wanted lament the lack of decent autotools documentation on the internet and to mention a good book/source of information that I grabbed several years ago: GNU Autoconf, Automake, and Libtool. There’s another book on the subject that is not yet available (as of this writing May/2010,) but it looks promising: GNU Autoconf, Automake, and Libtool
Ok, with that taken care of the first you’ll want to do is to make sure you have a few dependencies satisfied, a rough list of which follows:
On ubuntu you can run the following to install them:
sudo apt-get install build-essential automake autoconf autoconf-archive libtool \
python libcppunit-dev
To check out the results of this tutorial download pandora-example.tar.gz.
Once those are out of the way we can move on to downloading a release of pandora-build which can be found on the right-hand side of the pandora-build project page. Next we need to extract the files and rename the directory.
$ tar xvzf pandora-build-*.tar.gz
$ mv pandora-build-*/ pandora-example/
$ cd pandora-example
We can now look around at the base files. Most of them we’ll leave as-is, but there’s a few changes we’ll need to make. We’ll first look at configure.ac and modify the header and AC_INIT information to suit our project. After that we’ll add a AM_PATH_CPPUNIT line just before AC_CONFIG_FILES and add our pkg-config file to AC_CONFIG_FILES directive. Finall we’ll add some parameters to PANDORA_CANONICAL_TARGET to indicate that we want warnings, that we require cxx, and that we want to skip the shared library visibility checks and support, which you actually shouldn’t do, but it complicates the example. (see http://gcc.gnu.org/wiki/Visibility for information about visibility and look for a future post on the subject.) Anyway, after making these changes configure.ac should look like the following:
# pandora-example
AC_DEFUN([PANDORA_EXAMPLE_VERSION],[0.01])
AC_INIT([pandora-example],PANDORA_EXAMPLE_VERSION,
[http://something.pandora-example.com])
AC_CONFIG_SRCDIR([m4/pandora_canonical.m4])
AC_CONFIG_AUX_DIR(config)
PANDORA_CANONICAL_TARGET(warnings-always-on, require-cxx, skip-visibility)
AM_PATH_CPPUNIT(1.9.6)
AC_CONFIG_FILES(Makefile helloworld/helloworld.pc)
AC_OUTPUT
We now need to make a few modifications to Makefile.am, namely removing most of it’s contents leaving only ACLOCAL_AMFLAGS. Following that we’ll place a few variable declarations up top and add a couple include directives. We’ll end up with the following Makefile.am:
# pandora-example ACLOCAL_AMFLAGS= -I m4 # includes append to these: SUFFIXES = TESTS = check_PROGRAMS = noinst_HEADERS = nobase_nodist_include_HEADERS = nobase_dist_include_HEADERS = bin_PROGRAMS = sbin_PROGRAMS = lib_LTLIBRARIES = noinst_LTLIBRARIES = noinst_PROGRAMS = include helloworld/include.am include tests/include.am
We won’t be making use of all of those variables now, but it doesn’t hurt anything to have them in there so that if/when they’re necessary we don’t have to go back and add them. If you prefer feel free to remove everything we’re not immediately using.
Ok, that’s it for the editing portion of things, now we’ll need to create some directories and files. We’ll start by building the directory structure:
$ mkdir -p helloworld/include/helloworld-1.0 tests
And then continue on to create some files in those directories starting with helloworld/include.am:
# vim:ft=automake # library INCNAME=helloworld-1.0 lib_LTLIBRARIES+=helloworld/libhelloworld.la helloworld_libhelloworld_ladir=$(includedir)/$(INCNAME) helloworld_libhelloworld_la_CPPFLAGS=-I. -I$(srcdir)/helloworld/include helloworld_libhelloworld_la_LDFLAGS= helloworld_libhelloworld_la_HEADERS=\ helloworld/include/$(INCNAME)/phrase.h helloworld_libhelloworld_la_SOURCES=\ helloworld/phrase.cpp pkgconfigdir= $(libdir)/pkgconfig pkgconfig_DATA= helloworld/helloworld.pc # exec helloworld_helloworld_CPPFLAGS=-I. -I$(srcdir)/helloworld/include helloworld_helloworld_LDFLAGS= helloworld_helloworld_LDADD=helloworld/libhelloworld.la helloworld_helloworld_SOURCES=\ helloworld/helloworld.cpp bin_PROGRAMS+=helloworld/helloworld
There’s a couple things going on there as we’re defining both a shared library and an executable that depends on it. The first variable, INCNAME, is just a helper that prevents us from having to duplicate a value in multiple places. The lib_LTLIBRARIES line adds a new shared library to the set of things we’re going to build and the next set of lines tell automake about the shared library. I don’t want to get too far in to what’s going on here or else I’ll end up (re-)writing a book, but I do want to point out the use of named and versioned include directories as it’s a useful best-practice. In this case helloworld’s headers will be placed in the directory $(includedir)/helloworld-1.0/ and thus will be included by apps that need them with directives like “#include <helloworld-1.0/parser.h>.” This allows multiple major versions of your library to co-exist on a single system. The final piece of the library section tells automake where to find our pkg-config file and where to install it (more later.)
Ok the exec section is more straightforward, to anyone familiar with automake anyway, and just defines an executable that depends on libhelloworld.so that we defined above and makes sure that the app can get at the headers when it is compiling (before they’re installed or more correctly make sure that it uses the source versions, not installed ones.) The final line bin_PROGRAMS adds our exec to the list of executables that automake should build and install.
Now we’ll move on to the include.am file in the tests directory. It’s similar to the one we just created, but doesn’t create (shared) libraries or installed executables. It instead creates check_PROGRAMS, executables that are built and run with make check.
# vim:ft=automake
TESTS+=\
tests/phrase_test
check_PROGRAMS+=$(TESTS)
tests_phrase_test_CPPFLAGS=$(CPPUNIT_CFLAGS) -I$(srcdir)/helloworld/include
tests_phrase_test_LDFLAGS=$(CPPUNIT_LIBS) helloworld/libhelloworld.la
tests_phrase_test_SOURCES=tests/phrase_test.cpp
We have one more “infrastructure” file to deal with for now which will add pkg-config functionality to our shared library. The following needs to be placed in helloworld/helloworld.pc.in
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: helloworld
Description: Hello World Pandora Build Example
Version: @VERSION@
Libs: -L${libdir} -lhelloworld
Cflags: -I${includedir}
autoconf/configure will take the above file and create helloworld.pc substituting all of the actual values in place of their variables and automake will install it to the appropriate directory thanks to the lines we added to helloworld/include.am’s library section.
That leaves us with the code. We won’t spend too much time on it as that’s not the purpose of the post. I’ll just tell you the file name and provide the contents to place in it. Here we go.
helloworld/include/helloworld-1.0/phrase.h:
#ifndef _HELLOWORLD_1_0_PHRASE_H_
#define _HELLOWORLD_1_0_PHRASE_H_
#include <string>
namespace helloworld
{
class Phrase
{
private:
std::string _text;
public:
Phrase (const std::string & text);
virtual ~Phrase ();
const std::string & getText() const
{
return this->_text;
}
void setText(const std::string & text)
{
this->_text = text;
}
protected:
Phrase (const Phrase & rhs);
Phrase & operator= (const Phrase & rhs);
};
}
#endif // _HELLOWORLD_1_0_PHRASE_H_
helloworld/phrase.cpp:
#include "config.h"
#include "helloworld-1.0/phrase.h"
using namespace helloworld;
Phrase::Phrase (const std::string & text) : _text (text)
{
}
Phrase::~Phrase ()
{
}
Phrase::Phrase (const Phrase & rhs) : _text (rhs._text)
{
}
Phrase & Phrase::operator= (const Phrase & rhs)
{
this->_text = rhs._text;
return *this;
}
helloworld/helloworld.cpp:
#include "config.h"
#include <stdio.h>
#include "helloworld-1.0/phrase.h"
int main (int argc, char ** argv)
{
(void)argc;
(void)argv;
helloworld::Phrase phrase ("Hello World!");
fprintf (stdout, "%s\n", phrase.getText().c_str());
return 0;
}
tests/phrase_test.cpp:
#include <cppunit/BriefTestProgressListener.h>
#include <cppunit/CompilerOutputter.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TestRunner.h>
#include <cppunit/TestResult.h>
#include <cppunit/ui/text/TestRunner.h>
#include <helloworld-1.0/phrase.h>
class PhraseTest : public CPPUNIT_NS::TestFixture
{
CPPUNIT_TEST_SUITE (PhraseTest);
CPPUNIT_TEST (testPhrase);
CPPUNIT_TEST_SUITE_END ();
public:
void setUp ();
void tearDown ();
void testPhrase ();
};
CPPUNIT_TEST_SUITE_REGISTRATION (PhraseTest);
void PhraseTest::setUp ()
{
}
void PhraseTest::tearDown ()
{
}
void PhraseTest::testPhrase ()
{
std::string str = "Hello World!";
helloworld::Phrase phrase (str);
CPPUNIT_ASSERT_EQUAL (str, phrase.getText ());
CPPUNIT_ASSERT ("" != phrase.getText ());
std::string empty = "";
phrase.setText (empty);
CPPUNIT_ASSERT_EQUAL (std::string (empty), phrase.getText ());
CPPUNIT_ASSERT (str != phrase.getText ());
}
int main (int argc, char ** argv)
{
(void)argc;
(void)argv;
// informs test-listener about testresults
CPPUNIT_NS::TestResult testresult;
// register listener for collecting the test-results
CPPUNIT_NS::TestResultCollector collectedresults;
testresult.addListener (&collectedresults);
// register listener for per-test progress output
CPPUNIT_NS::BriefTestProgressListener progress;
testresult.addListener (&progress);
// insert test-suite at test-runner by registry
CPPUNIT_NS::TestRunner testrunner;
testrunner.addTest
(CPPUNIT_NS::TestFactoryRegistry::getRegistry ().makeTest ());
testrunner.run (testresult);
// output results in compiler-format, with a custom error format that works
// with a top-level Makefile.am and include.am's
CPPUNIT_NS::CompilerOutputter compileroutputter (&collectedresults,
std::cerr,
"%p:%l:");
compileroutputter.write ();
// return 0 if tests were successful
return collectedresults.wasSuccessful () ? 0 : 1;
}
Ok that’s it. You can now run autorun.sh, configure, and make and you “should” end up with a working shared library, pkg-config file, and executable. To test it all out do:
$ ./config/autorun.sh
$ ./configure --prefix=/tmp/helloworld-test
$ make
$ make check
$ make install
Congrats, you now have a skeletal pandora-build based system. You can go in and add/remove shared libraries, headers, execs, tests, … as needed mostly by copy-n-pasting the above code and stuff you can find in other projects (both libdrizzle and drizzle are good, albeit it complicated, examples.) Feel free to ask questions if you have them and be sure to subscribe as I plan to make this a series of posts on the subject. Some of the planned topics include what needs to be in my repository (.gitignores,) integrating valgrind and other code checking/debugging utilities, logging, distribution/tarring things up, and maybe even building debian and redhat packages.
“Never Eat More Than You Can Lift” – Miss Piggy
I like pizza, a lot, and I enjoy exploring new pizza places and options. I also bought a cool domain a while back, thebestinsf.com and was looking for something to do with it. I decided to combine those two things and write about my exploration of San Francisco Pizza Places. I’m also throwing in posts about options for making pizza at home with store bought dough and sooner or later will get around to writing up some posts on making your own from scratch as I feel like I’ve tried enough recipes and techniques to have something useful to share. Check it out and let me know what you think.
It was just over a year ago when I wrote about my experimentation with No Knead Bread. I’ve made it a handful of times since then, the most recent of which was a couple weeks ago. It turned so well that we decided to make it a regular thing, at least initially planning to do it weekly. And all of two weeks in, so far so good.
It really is that easy; forget about bread machines, spend $45 on an enamaled cast iron dutch oven and 15 minutes measuring and mixing flour and water (you don’t even have to be precise, it will still turn out fine.
On to the sourdough part. About the same time I decided to make the bread I started to see things taking about sourdough (more preferment/poolish than what people think about when they hear sourdough.) First it was in the context of pizza, which I plan to try soon and then it was a blog post I ran across about doing your own starter from scratch. Living in San Francisco I have no excuse not to give it a try so try I did. The process went really well, I wish I’d taken pictures so that I could blog about it. The starter finished up and went in to the fridge Tuesday night and I had been looking for an excuse to use it since putting it in there. About 11:30 last night I thought of a good one, a sourdough adaptation of the no-knead-bread. As mentioned above the hydration ratio is pretty forgiving with this bread so it’s a great place to play around. After you’ve done it a few times you get a feel for what it should look like and if you change things up all you have to do is get it to look roughly that way. (In reality this is always the case with bread/dough making, the look and feel has to be “right” more than you have to put in X grams of flour and Y grams of water, …) Anyway, I threw about a half a cup of starter in to the flour and then added a cup of water and played around adding a bit more until it looked right and then covered it and went to bed. I made it home about 6:00 tonight and turned on the oven when I walked in the door, about an hour later I pulled out a great loaf of tasty bread. It’s not a strong sourdough, as in what you’d expect from San Francisco Sourdough, but there’s a greater depth of flavor than comes from the overnight ferment of the base recipe. The next time around I plan to up the starter to a full cup to punch up the flavor a bit, regardless I’ll be doing this a lot, probably every time I make the stuff from now on. mmmmmmmmmm
I moved in to a new place this past summer and brought my moderately priced projector and cheap ($100) 100″ screen along with me. The problem is in the apartment’s layout there’s just no place to set up the screen that’s watchable. What we do have is a set of French doors between the living room and bedroom to work with. Rear projection screens (and even the material) is expensive and what’s worse I couldn’t find anything that would work well in the space — most were too big, all were to expensive for my current tastes/setup. A little bit of brainstorming, internet searching, and back of the napkin schematics later I had a plan.
I was going to build a simple frame and use wax paper as the screen material. The idea of using wax paper came from seeing a window display that utilized sheets of it (or something very similar) glued/tacked to the glass as a projection material. I didn’t have a 78″ diagonal piece of glass laying around and even if I had it would have been seriously heavy. Not that big a deal if it was going to be permanently mounted, but a bit of a problem if the plan was to only put it up when being used (since it would be in the middle of our apartment.) I figured that stretched tight enough the wax paper alone would do a decent job and tests of small pieces with the projector proved that out. The only kink in the plan was that one sheet wasn’t enough. It was letting too much light through and the image lacked saturation. Two sheets did the trick and looked great.
The next problem was dealing with the seams. The solution was to weave the two layers of wax paper. The woven sheets tightened things up a bit and the friction kept them in place so that once I lined up the edges of adjacent sheets they did a really good job of staying put. I wouldn’t say it’s perfect as you can see in the picture with the room lights on, but once their out and you’re watching the movie you hardly, if ever, notice them. By all means if you look for them you can see them, but as soon as you quit paying attention they all but disappear.
Parts List:
If you have the wood working equipment to do properly joined corners you can probably skip out on the angle brackets (I don’t atm.) The wax paper was a little difficult to come by, not really sure why. I had to search around at several grocery stores to find one (Safeway at Church and Market) that did. The stuff exists for sale on the internet in heavier weights and in larger width rolls and if you can come up with the stuff at a good price you may end up with a slightly better screen, but as the pictures show the standard kitchen stuff works.
I won’t go through step-by-step detailed build instructions mainly b/c I didn’t take pictures to go along with them, but an overview follows. I started by cutting the boards with 45 degree ends to length. For my target of 78″ diagonal with a 16:9 ratio that worked out to roughly 68″x40″. The exact measurements aren’t critical, but the two long pieces and the two short pieces have to be exactly the same size as their counterparts. Once you’ve cut them to size you’ll need to drill pilot holes and screw the corners together, 2″x2″ stock will split easily so make sure you pre-drill the full depth with a bit as close to the size of your screw as you can, while still letting it get a grip.
Next, you’ll need a large flat clean surface for the weaving portion: tile, wood, concrete, etc. start by cutting all of the pieces for one direction to length. I started cutting the pieces that would run from top-to-bottom. I then lined them up loosely and then used painters/masking tape to tack them in to place on one side. Once that was done I wove the side-to-side pieces in to place and cut them to length. I then tacked them in to place on one end and started to tighten up all of the seams and alignments. At this point it should be clear that the reason I used masking tape was so that things could be adjusted without fighting a stickier tape and tearing the wax paper every time an adjustment was needed. Once I was pretty happy with the alignment of things I began working my way across the bottom tacking the loose ends of the wax paper strips in place and then continued up the final loose side until all of the wax paper was tacked in to place. After a few minor tweaks and adjustments I went around the perimeter of the frame using packing tape (much stronger, stickier, and longer lasting) to do a more permanent job.
A couple of hooks in the frame and a couple on the top of the door jam and we’re in business.
A full photo set with larger images can be found here