<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>The Electric Cloud Blog</title>
	<atom:link href="http://blog.electric-cloud.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.electric-cloud.com</link>
	<description>This is your source for software build-test-deploy best practices and technical tips and tricks for Electric Cloud solutions</description>
	<lastBuildDate>Wed, 11 Aug 2010 18:37:35 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='blog.electric-cloud.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://0.gravatar.com/blavatar/69f9c69836db8c4404b635236f654808?s=96&#038;d=http://s2.wp.com/i/buttonw-com.png</url>
		<title>The Electric Cloud Blog</title>
		<link>http://blog.electric-cloud.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://blog.electric-cloud.com/osd.xml" title="The Electric Cloud Blog" />
	<atom:link rel='hub' href='http://blog.electric-cloud.com/?pushpress=hub'/>
		<item>
		<title>The last word on SCons performance</title>
		<link>http://blog.electric-cloud.com/2010/08/11/the-last-word-on-scons-performance/</link>
		<comments>http://blog.electric-cloud.com/2010/08/11/the-last-word-on-scons-performance/#comments</comments>
		<pubDate>Wed, 11 Aug 2010 18:33:44 +0000</pubDate>
		<dc:creator>Eric Melski</dc:creator>
				<category><![CDATA[Software Development]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[parallel builds]]></category>
		<category><![CDATA[incremental build]]></category>
		<category><![CDATA[gmake]]></category>
		<category><![CDATA[scons]]></category>

		<guid isPermaLink="false">http://blog.electric-cloud.com/?p=690</guid>
		<description><![CDATA[My previous look at SCons performance compared SCons and gmake on a variety of build scenarios &#8212; full, incremental, and clean. A few people suggested that I try the tips given on the SCons &#8216;GoFastButton&#8217; wiki page, which are said to significantly improve SCons performance (at the cost of some accuracy, of course). Naturally, I [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=690&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>My <a href="http://blog.electric-cloud.com/2010/07/21/a-second-look-at-scons-performance/">previous look</a> at SCons performance compared SCons and gmake on a variety of build scenarios &mdash; full, incremental, and clean.  A few people suggested that I try the tips given on <a href="http://www.scons.org/wiki/GoFastButton">the SCons &#8216;GoFastButton&#8217; wiki page</a>, which are said to significantly improve SCons performance (at the cost of some accuracy, of course).  Naturally, I felt that I had to do one last follow-up exploring this avenue.  And since that meant I would already be running a bunch of builds, I figured I&#8217;d try out SCons&#8217; parallel build features too.  My findings follow.<br />
<span id="more-690"></span></p>
<p><h3>Can SCons &#8220;GoFast&#8221;?</h3>
<p>You can read all about the setup in the previous post, so I&#8217;ll just jump straight to the results.  According to the &#8220;GoFastButton&#8221; recommendations, <span style="background:#eeeeee;"><font face="Courier New">&#8211;max-drift=1 &#8211;implicit-deps-unchanged</font></span> will &#8220;run your build as fast as possible&#8221;, so that&#8217;s what I used.  In all cases, I did an initial, untimed from-scratch build first, to generate the initial dependency graph, then I ran a second timed build.  After that timed run, I ran a clean build and finally ran the full build again, this time with <span style="background:#eeeeee;"><font face="Courier New">-j 2</font></span>, to evaluate the impact of SCons parallel build features.
</p>
<p>
Contrary to my expectations, the GoFast settings had relatively little impact over much of the test range &mdash; only about 5-10% faster than without those flags.  Only the very largest build showed any significant impact, with a 25% improvement.  Unfortunately that impressive result is more likely because SCons uses less memory with GoFast settings enabled.  If you recall from the <a href="http://blog.electric-cloud.com/2010/07/21/a-second-look-at-scons-performance/">previous tests</a>, with 50,000 source files, SCons&#8217; memory footprint was a hefty 2,023 MB &mdash; enough to force my test machine to start swapping.  With the GoFast settings, SCons used &#8220;only&#8221; 1,838 MB &mdash; still an awful lot of memory, but just smaller enough to avoid thrashing the system, with the end result being a substantially improved build time.
</p>
<p>
Building in parallel had a more substantial impact &mdash; reducing build times about 30% on the largest build (compared to a serial SCons build with GoFast settings enabled).  That&#8217;s not as good as I had hoped for (on a large, relatively &#8220;flat&#8221; build such as this, I expected the build to parallelize very well), but it&#8217;s not terrible.  Here are the complete results:
</p>
<p>
<a href="http://ecloud.files.wordpress.com/2010/08/scons_full.png"><img src="http://ecloud.files.wordpress.com/2010/08/scons_full.png?w=300&#038;h=180" alt="SCons full build performance, click for full size" title="scons_full" width="300" height="180" class="aligncenter size-medium wp-image-692" /></a>
</p>
<p>
So, GoFast seems to be a bust on full builds.  It&#8217;s definitely better than vanilla SCons, but still nowhere near as fast as gmake.
</p>
<p>
Things look a little better on &#8220;one-touch&#8221; incremental builds though, where GoFast settings cut build times by about 30% across the board:
</p>
<p>
<a href="http://ecloud.files.wordpress.com/2010/08/scons_incr.png"><img src="http://ecloud.files.wordpress.com/2010/08/scons_incr.png?w=300&#038;h=180" alt="SCons incremental build performance, click for full size" title="scons_incr" width="300" height="180" class="aligncenter size-medium wp-image-693" /></a>
</p>
<p>
Ironically, the most impressive results are on clean builds (<span style="background:#eeeeee;"><font face="Courier New">scons -c</font></span>).  GoFast settings cut build times by about 40% at the low end of the test range, and by more than 50% at the high end of the test range:
</p>
<p>
<a href="http://ecloud.files.wordpress.com/2010/08/scons_clean.png"><img src="http://ecloud.files.wordpress.com/2010/08/scons_clean.png?w=300&#038;h=180" alt="SCons clean build performance, click for full size" title="scons_clean" width="300" height="180" class="aligncenter size-medium wp-image-694" /></a>
</p>
<p>
To my amazement, SCons with GoFast settings actually beats gmake on clean builds.  My guess is that this is probably because SCons handles file deletion in-process, while gmake must invoke a separate process (<span style="background:#eeeeee;"><font face="Courier New">rm</font></span>).
</p>
<p><h3>That&#8217;s all folks!</h3>
</p>
<p>
That&#8217;s it for my analysis of SCons performance.  Thanks to everybody who offered ideas for improving my benchmarks!  I hope you found this series of posts interesting.</p>
<br />Filed under: <a href='http://blog.electric-cloud.com/category/software-development/'>Software Development</a> Tagged: <a href='http://blog.electric-cloud.com/tag/gmake/'>gmake</a>, <a href='http://blog.electric-cloud.com/tag/incremental-build/'>incremental build</a>, <a href='http://blog.electric-cloud.com/tag/parallel-builds/'>parallel builds</a>, <a href='http://blog.electric-cloud.com/tag/performance/'>performance</a>, <a href='http://blog.electric-cloud.com/tag/scons/'>scons</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/ecloud.wordpress.com/690/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/ecloud.wordpress.com/690/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/ecloud.wordpress.com/690/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/ecloud.wordpress.com/690/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/ecloud.wordpress.com/690/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/ecloud.wordpress.com/690/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/ecloud.wordpress.com/690/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/ecloud.wordpress.com/690/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/ecloud.wordpress.com/690/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/ecloud.wordpress.com/690/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/ecloud.wordpress.com/690/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/ecloud.wordpress.com/690/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/ecloud.wordpress.com/690/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/ecloud.wordpress.com/690/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=690&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://blog.electric-cloud.com/2010/08/11/the-last-word-on-scons-performance/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6344c5478a4b5f0e8c546c736b2a7e0d?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=PG" medium="image">
			<media:title type="html">ericm</media:title>
		</media:content>

		<media:content url="http://ecloud.files.wordpress.com/2010/08/scons_full.png?w=300" medium="image">
			<media:title type="html">scons_full</media:title>
		</media:content>

		<media:content url="http://ecloud.files.wordpress.com/2010/08/scons_incr.png?w=300" medium="image">
			<media:title type="html">scons_incr</media:title>
		</media:content>

		<media:content url="http://ecloud.files.wordpress.com/2010/08/scons_clean.png?w=300" medium="image">
			<media:title type="html">scons_clean</media:title>
		</media:content>
	</item>
		<item>
		<title>What&#8217;s new in GNU make 3.82</title>
		<link>http://blog.electric-cloud.com/2010/08/03/gnu-make-3-82-is-out/</link>
		<comments>http://blog.electric-cloud.com/2010/08/03/gnu-make-3-82-is-out/#comments</comments>
		<pubDate>Tue, 03 Aug 2010 22:29:54 +0000</pubDate>
		<dc:creator>Eric Melski</dc:creator>
				<category><![CDATA[News]]></category>
		<category><![CDATA[gmake]]></category>
		<category><![CDATA[gnu make]]></category>

		<guid isPermaLink="false">http://blog.electric-cloud.com/?p=685</guid>
		<description><![CDATA[GNU make 3.82 hit the streets last week, the first new release of the workhouse build tool in over four years. Why so long between releases? To me the answer is obvious: the tool Just Works &#8482;, so there&#8217;s no need to churn out new releases chasing the latest development fad. But as this release [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=685&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.gnu.org/software/make/">GNU make 3.82</a> hit the streets last week, the first new release of the workhouse build tool in over four years.  Why so long between releases?  To me the answer is obvious:  the tool Just Works &#8482;, so there&#8217;s no need to churn out new releases chasing the latest development fad.  But as this release shows, there is still room to innovate, without compromising on the points that make the tool so great.  The two improvements I find most interesting are <font face="Courier New">.ONESHELL</font>, and changes to pattern-search behavior:<br />
<span id="more-685"></span></p>
<p><h3>.ONESHELL</h3>
</p>
<p>
Normally, gmake  executes each line of a rule body or <i>recipe</i> using a separate invocation of the shell.  With gmake 3.82, you can add the special target <font face="Courier New">.ONESHELL</font> to the makefile, which will tell gmake to run the entire recipe using a single shell invocation.  For example, if your makefile contains the following:
</p>
<p><pre>
<div style="background:#ffffce;border:solid thin;width:60ex;margin-left:auto;margin-right:auto;padding:10px;"><font face="Courier New">all:
	@export FOO=1
	@echo FOO is -$$FOO-
</font></div>
</pre>
<p>
Without <font face="Courier New">.ONESHELL</font>, gmake will invoke the shell twice, as follows:
</p>
<p><pre>
<div style="background:#ffffce;border:solid thin;width:60ex;margin-left:auto;margin-right:auto;padding:10px;"><font face="Courier New">sh -c 'export FOO=1'
sh -c 'echo FOO is -$FOO-'</font></div>
</pre>
<p>
Naturally, that will not produce the output you actually want (ie, &#8220;FOO is -1-&#8221;).  With <font face="Courier New">.ONESHELL</font>, gmake instead invokes the shell just once:
</p>
<p><pre>
<div style="background:#ffffce;border:solid thin;width:60ex;margin-left:auto;margin-right:auto;padding:10px;"><font face="Courier New">sh -c 'export FOO=1
    echo FOO is -$FOO-'</font></div>
</pre>
<p>
In addition to making it easier to write multi-line rule bodies, this feature makes it much more palatable to use alternative shells.  For example, you could imagine using Perl as the shell:
</p>
<p><pre>
<div style="background:#ffffce;border:solid thin;width:60ex;margin-left:auto;margin-right:auto;padding:10px;"><font face="Courier New">SHELL=perl
.SHELLFLAGS=-e
.ONESHELL:

all:
	@my $$foo = "1";
	print "FOO is -$$foo-\n";
</font></div>
</pre>
<p>
(Note the use of another 3.82 feature, <font face="Courier New">.SHELLFLAGS</font>, which allows us to control the command-line flags used with the shell; in this case I&#8217;ve set those to &#8220;-e&#8221;, the Perl flag for executing a script from the command-line).
</p>
<p><h3>Pattern search changes</h3>
</p>
<p>
Prior to version 3.82, when gmake finds multiple matches during a pattern search, it prefers patterns declared earlier in the makefile over patterns declared later.  As of 3.82, gmake instead prefers the pattern that results in the shortest stem.  That sounds a bit confusing thanks to the jargon, but I think this will actually cause gmake to better adhere to the <a href="http://en.wikipedia.org/wiki/Principle_of_least_astonishment">principle of least astonishment</a>.  Here&#8217;s an example:
</p>
<p><pre>
<div style="background:#ffffce;border:solid thin;width:60ex;margin-left:auto;margin-right:auto;padding:10px;"><font face="Courier New">all: sub/foo.x

%.x:
	@echo "Prefer first match (stem is $*)."

sub/%.x:
	@echo "Prefer most specific match (stem is $*)."
</font></div>
</pre>
<p>
Compare the output from gmake 3.81 and 3.82:
</p>
<p><div style="background:#eeeeee;border:solid thin;width:60ex;margin-left:auto;margin-right:auto;">
<div style="border-bottom-width:1px;border-bottom-style:solid;"><b>gmake 3.81</b></div>
<div style="background:#ffffce;padding:10px;">
<pre><font face="Courier New">Prefer first match (stem is sub/foo).</font></pre>
</div>
</div>
<p><div style="background:#eeeeee;border:solid thin;margin-left:auto;margin-right:auto;width:60ex;">
<div style="border-bottom-width:1px;border-bottom-style:solid;"><b>gmake 3.82</b></div>
<div style="background:#ffffce;padding:10px;">
<pre><font face="Courier New">Prefer most specific match (stem is foo).</font></pre>
</div>
</div>
<p>
gmake 3.82 prefers the second pattern because it is a more specific match than the first.  Note that this is a significant backwards-incompatibility compared with previous versions of gmake!
</p>
<p><h3>Other changes</h3>
</p>
<p>
Besides those big changes, there are several smaller features, such as:
</p>
<dl>
<dt><b>.RECIPEPREFIX</b></dt>
<dd>This special variable allows you to change the character used to mark the beginning of a command in a recipe from the default TAB character.  I can only assume that the developers added this feature to quell the semi-regular complaints about make&#8217;s sensitivity to whitespace.</dd>
<dt><b>private</b> variable modifier</dt>
<dd>Normally, gmake propagates target-specific variable assignments to the prereqs of the target.  With the <font face="Courier New">private</font> modifier, you can restrict the scope of a target-specific variable assignment, so that it is not inherited by the prereqs.</dd>
<dt><b>undefine</b> directive</dt>
<dd>The inverse of the familiar <font face="Courier New">define</font> directive, <font face="Courier New">undefine</font> lets you completely remove a variable definition.</dd>
<dt><b>define</b> improvements</dt>
<dd>The <font face="Courier New">define</font> directive now supports the same assignment operators that regular variable assignment alows:  <font face="Courier New">:=</font>, <font face="Courier New">?=</font> and <font face="Courier New">+=</font>, for simple, conditional and appending assignments.</dd>
</dl>
<p>
If you want to see the full list, you can find it in the <a href="http://cvs.savannah.gnu.org/viewvc/make/NEWS?revision=2.109&amp;root=make&amp;view=markup">NEWS</a> file in the gmake source tree.
</p>
<p><h3>&#8220;Rumors of my death have been greatly exaggerated&#8230;&#8221;</h3>
</p>
<p>
Some naysayers claim make is outdated, but it&#8217;s clear that make is still alive and kicking (and if a new gmake release isn&#8217;t proof enough, take a look at some of the innovations we&#8217;ve put into <a href="http://www.electric-cloud.com/products/electricaccelerator.php">Electric Make</a>).  A hearty congratulations to everybody who contributed, and especially to Paul Smith for driving the development and release effort.  Keep up the good work!</p>
<br />Filed under: <a href='http://blog.electric-cloud.com/category/news/'>News</a> Tagged: <a href='http://blog.electric-cloud.com/tag/gmake/'>gmake</a>, <a href='http://blog.electric-cloud.com/tag/gnu-make/'>gnu make</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/ecloud.wordpress.com/685/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/ecloud.wordpress.com/685/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/ecloud.wordpress.com/685/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/ecloud.wordpress.com/685/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/ecloud.wordpress.com/685/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/ecloud.wordpress.com/685/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/ecloud.wordpress.com/685/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/ecloud.wordpress.com/685/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/ecloud.wordpress.com/685/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/ecloud.wordpress.com/685/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/ecloud.wordpress.com/685/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/ecloud.wordpress.com/685/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/ecloud.wordpress.com/685/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/ecloud.wordpress.com/685/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=685&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://blog.electric-cloud.com/2010/08/03/gnu-make-3-82-is-out/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6344c5478a4b5f0e8c546c736b2a7e0d?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=PG" medium="image">
			<media:title type="html">ericm</media:title>
		</media:content>
	</item>
		<item>
		<title>Making Automated Tests Truly Automatic</title>
		<link>http://blog.electric-cloud.com/2010/08/02/676/</link>
		<comments>http://blog.electric-cloud.com/2010/08/02/676/#comments</comments>
		<pubDate>Tue, 03 Aug 2010 01:13:50 +0000</pubDate>
		<dc:creator>usmanmuzaffar</dc:creator>
				<category><![CDATA[Build-Test-Deploy Best Practices]]></category>
		<category><![CDATA[ElectricCommander]]></category>

		<guid isPermaLink="false">http://blog.electric-cloud.com/?p=676</guid>
		<description><![CDATA[[A version of this article appeared on eWeek http://www.eweek.com/c/a/Application-Development/How-to-Make-Your-Automated-Software-Tests-Truly-Automatic/] A recent poll of software development professionals showed that the majority would rather be doing their taxes than dealing with their company&#8217;s test infrastructure. The reason: automated software tests require tremendous amounts of manual time and energy to configure, run, and monitor. This can be a [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=676&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><em>[A version of this article appeared on eWeek http://www.eweek.com/c/a/Application-Development/How-to-Make-Your-Automated-Software-Tests-Truly-Automatic/]</em></p>
<p>A <a href="http://www.electric-cloud.com/news/2010-0602.php">recent poll</a> of software development professionals showed that the majority would rather be doing their taxes than dealing with their company&#8217;s test infrastructure. The reason: automated software tests require tremendous amounts of manual time and energy to configure, run, and monitor. This can be a startling revelation to companies that have invested substantial engineering efforts into automated test frameworks specifically to reduce the human cost of continually running large regression suites.</p>
<p><em>What&#8217;s at the root of this disconnect? </em><br />
<span id="more-676"></span><br />
We&#8217;ve had the opportunity recently to interview some of the world&#8217;s largest development organizations and were astonished how similar the problems were across wildly different shops. Test suites take time to configure and launch, there&#8217;s a perpetual arm-wrestling match with IT for resources, and the staff is still spending hours trawling through log files, painstakingly trying to tease out the real defects from the spurious failures and bad tests. Effective automation should obviously be about reducing manual effort, and yet the most successful test systems are backed by large infrastructure teams tasked solely with the care and feeding of the &#8220;automatic&#8221; system.</p>
<p>Three challenges crystallize as the answers to the questions above:</p>
<p>1) It&#8217;s hard to get the environment, including both harness and product, set up on an appropriate resource</p>
<p>2) It&#8217;s hard to invoke the test</p>
<p>3) It&#8217;s hard to mine the results for concrete next actions.</p>
<p>This triumvirate &#8212; resource configuration, test execution, and results monitoring &#8212; is the &#8220;last mile&#8221; of automation, and when  ignored, the entire value of automated test pays a steep penalty. Fortunately, for each of these systemic problems, there are simple though not necessarily easy solutions we can apply to great effect.</p>
<p>The resource selection problem is simply this: where do I run the test?  At most shops, the answer itself quickly leads to a sysadmin puzzle: where or how can I easily log into 20 systems in one click, where do I have ssh keys configured, how will I keep my tests from stomping on my neighbor&#8217;s, how do I fire up VMs from an ESX server for my config, and so on. The correct answer: stop asking this question; get your test harness to do it for you. There are three critical pieces you need to make that a reality and one big trap to avoid. First, tests need to support distributed, remote execution; second, the target host and the build need to be logically divorced and merged together only at test invocation, and third, as much possible, standardize on one host environment for high-volume, frequent &#8216;smoke test&#8217; runs. The pitfall: if the test process is hardcoded to a host (&#8220;version 3.4 can only be tested on Windows 2003 hosts with VS 2008 + our addin&#8221;) the inefficiencies and complexity start to creep in. Standardizing on a host configuration for all active branches can open extraordinary optimization opportunities. Need to run a new system test?  Grab the next available node from the large homogenous grid or cloud, load product and go.</p>
<p>The concept of carefully sub-setting the test suite to run a critical selection of smoke tests first is not new, but it&#8217;s essential to efficient automatic testing. The natural objection from developers is that the project-wide smoke test possibly will not cover the code they most recently modified. Enter the second solution pattern: tests harnesses need to be highly parameterized, but their invocation needs to be easily aliased. This is a common pattern you use all the time: type &#8220;alias&#8221; at the prompt of grizzled Unix command-line hackers and you&#8217;ll see shortcut entries encapsulating commonly used options to programs like &#8220;ls&#8221;; on Windows the Start Menu is nothing but a tree of shortcuts to programs and documents. Effective automated test systems pull the same stunt: a million possible options to pick the smoke tests, the server tests, the UI tests, and so forth &#8212; but they are easily abstracted behind a single, user-specific click on the harness interface. Corollary gotcha: don&#8217;t let your harness sources live with your product sources, it encourages an artificial integration that makes building such an interface much more difficult.</p>
<p>Finally, the most widely cited culprit for consuming valuable man-hours is dealing with the large volumes of data produced by test systems. Automated harnesses are usually great at posting gigabytes of HTML tables with green and red icons to web pages and inboxes, but they often fall far short of making that data <em>actionable</em>. Actionable reporting means that for every data point presented to the user, you ask yourself the question: &#8220;If I show them this, what will they want to see next?&#8221; Too often the email says, &#8220;test run #231 failed&#8221;, but leaves it to the user to identify the offending test; too often the web page says &#8220;test 144&#8230;. FAILED&#8221; without any indication if it&#8217;s been failing since last March or if it&#8217;s been green since 2006 but turned red this morning with the new guy&#8217;s first checkin. Designing the test harness to make error collection &#8212; and just as important, error reporting &#8212; as intelligent as possible can have a dramatic impact on ease of use and productivity.</p>
<p>So while the natural evolution of a test harness will lead it being tedious to configure, slow to run, and cryptic to interpret, there are straightforward strategies to remedy all three problems. And as shops that took the time to refactor the critical parts of resource selection, harness invocation, and results reporting have shown, an easy, fast, efficient test harness may still not top your list of favorite leisure activities, but it&#8217;s a great deal more pleasant than filling in Form 1040.</p>
<p>ElectricCommander provides efficient, effective unit and <a href="http://www.electric-cloud.com/solutions/test_automation.php">system test automation</a> by dynamically provisioning and configuring the target environment, eliminating manual invocation of complex test suites, transforming volumes of test result data into actionable metrics.</p>
<br />Filed under: <a href='http://blog.electric-cloud.com/category/build-test-deploy-best-practices/'>Build-Test-Deploy Best Practices</a>, <a href='http://blog.electric-cloud.com/category/electriccommander/'>ElectricCommander</a>  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/ecloud.wordpress.com/676/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/ecloud.wordpress.com/676/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/ecloud.wordpress.com/676/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/ecloud.wordpress.com/676/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/ecloud.wordpress.com/676/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/ecloud.wordpress.com/676/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/ecloud.wordpress.com/676/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/ecloud.wordpress.com/676/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/ecloud.wordpress.com/676/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/ecloud.wordpress.com/676/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/ecloud.wordpress.com/676/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/ecloud.wordpress.com/676/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/ecloud.wordpress.com/676/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/ecloud.wordpress.com/676/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=676&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://blog.electric-cloud.com/2010/08/02/676/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/96ed2841dcc4f60049cd9d332fd2d26c?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=PG" medium="image">
			<media:title type="html">usmanmuzaffar</media:title>
		</media:content>
	</item>
		<item>
		<title>Bridging the IT – Development Gap</title>
		<link>http://blog.electric-cloud.com/2010/07/29/bridging-the-it-%e2%80%93-development-gap/</link>
		<comments>http://blog.electric-cloud.com/2010/07/29/bridging-the-it-%e2%80%93-development-gap/#comments</comments>
		<pubDate>Fri, 30 Jul 2010 00:12:10 +0000</pubDate>
		<dc:creator>Mike Maciag</dc:creator>
				<category><![CDATA[Build-Test-Deploy Best Practices]]></category>
		<category><![CDATA[Cloud Computing]]></category>
		<category><![CDATA[ElectricCommander]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[Virtualization]]></category>
		<category><![CDATA[virtual]]></category>
		<category><![CDATA[Agile]]></category>
		<category><![CDATA[cloud]]></category>
		<category><![CDATA[IT]]></category>
		<category><![CDATA[server consolidation]]></category>
		<category><![CDATA[Virtual Infrastructure]]></category>

		<guid isPermaLink="false">http://blog.electric-cloud.com/?p=671</guid>
		<description><![CDATA[In the last year I have increasingly run into a new character in application development shops &#8211; IT. IT is not taking over coding tasks, but they are certainly taking a much more active role regarding where application development tasks get run.   This is no surprise, as software development has an insatiable appetite for compute [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=671&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>In the last year I have increasingly run into a new character in application development shops &#8211; IT.</p>
<p>IT is not taking over coding tasks, but they are certainly taking a much more active role regarding where application development tasks get run.   This is no surprise, as software development has an insatiable appetite for compute resources.  Whether it is vast clusters for testing, dozens of machines to run ALM tools, or the incessant requests for “just one more box,” development teams are always asking for something.  Multiply this by the number of development teams in your company, and it is easy to see why this is a big job for IT.</p>
<p>IT’s response is to increasingly look at centralizing where development tasks are run.  The logic is that with a cloud of development resources, they can get more efficiency in sharing them across groups and offer their customers (development) more resources than they would otherwise get.  However, IT’s goals are largely different than those of the development teams.  IT organizations measure their success by how efficient, smooth and cost effective their compute environment is. They want a large, identically configured, scalable, uninterruptible environment that consistently delivers established levels of performance to the downstream customer.  In other words, the more that they can make things the same, the more efficient they can be.</p>
<p>On the surface, these goals are at odds with development.   Development teams are measured on software output and quality, not on resource efficiency.  Each development group often has unique needs to achieve peek productivity and quality.  The matrix of test environments never shrinks, in conflict with IT’s desire to standardize.  If environment customizations and optimizations make development more effective (which they often do) they want their own resources, even if it means they get fewer of them.</p>
<p>How do you bridge these competing goals?  The wrong answer is compromise: it&#8217;s not about finding the midpoint that&#8217;s just useless enough to each party&#8217;s goals that everyone is unhappy.</p>
<p>The right answer is to define an environment that can deliver against both goals simultaneously.  Allow IT to provision the compute cloud &#8211; these are the areas where homogeneity and efficiency shine.  This allows IT to meet development’s needs for peak resource demands by sharing across large pools of compute resources while reducing cost.  Virtualization is an important ingredient in the solution because it meets IT’s need for homogeneity and development’s need for configuration specialization.   However, virtualization is not enough.  What is really needed to bridge the gap is a framework that allows development to maintain control of what processes get run, who runs them, and how and when they end up on the compute cloud.</p>
<p>Is this possible?  Our most successful customers have used ElectricCommander to do just this.</p>
<p>For IT, ElectricCommander enables software development to happen in one large, scalable, reliable development cloud.  For development, they get all of the control that they need, only with a heck of a lot more compute resources. <ins datetime="2010-07-28T16:35" cite="mailto:admin"></ins></p>
<br />Filed under: <a href='http://blog.electric-cloud.com/category/build-test-deploy-best-practices/'>Build-Test-Deploy Best Practices</a>, <a href='http://blog.electric-cloud.com/category/cloud-computing/'>Cloud Computing</a>, <a href='http://blog.electric-cloud.com/category/electriccommander/'>ElectricCommander</a>, <a href='http://blog.electric-cloud.com/category/software-development/'>Software Development</a>, <a href='http://blog.electric-cloud.com/category/virtual/'>virtual</a>, <a href='http://blog.electric-cloud.com/category/virtualization/'>Virtualization</a> Tagged: <a href='http://blog.electric-cloud.com/tag/agile/'>Agile</a>, <a href='http://blog.electric-cloud.com/tag/cloud/'>cloud</a>, <a href='http://blog.electric-cloud.com/tag/cloud-computing/'>Cloud Computing</a>, <a href='http://blog.electric-cloud.com/tag/electriccommander/'>ElectricCommander</a>, <a href='http://blog.electric-cloud.com/tag/it/'>IT</a>, <a href='http://blog.electric-cloud.com/tag/server-consolidation/'>server consolidation</a>, <a href='http://blog.electric-cloud.com/tag/virtual-infrastructure/'>Virtual Infrastructure</a>, <a href='http://blog.electric-cloud.com/tag/virtualization/'>Virtualization</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/ecloud.wordpress.com/671/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/ecloud.wordpress.com/671/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/ecloud.wordpress.com/671/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/ecloud.wordpress.com/671/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/ecloud.wordpress.com/671/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/ecloud.wordpress.com/671/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/ecloud.wordpress.com/671/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/ecloud.wordpress.com/671/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/ecloud.wordpress.com/671/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/ecloud.wordpress.com/671/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/ecloud.wordpress.com/671/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/ecloud.wordpress.com/671/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/ecloud.wordpress.com/671/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/ecloud.wordpress.com/671/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=671&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://blog.electric-cloud.com/2010/07/29/bridging-the-it-%e2%80%93-development-gap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/3f8af1a057fc8f4e5c7e94dd8156a0ac?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=PG" medium="image">
			<media:title type="html">mmaciag</media:title>
		</media:content>
	</item>
		<item>
		<title>A second look at SCons performance</title>
		<link>http://blog.electric-cloud.com/2010/07/21/a-second-look-at-scons-performance/</link>
		<comments>http://blog.electric-cloud.com/2010/07/21/a-second-look-at-scons-performance/#comments</comments>
		<pubDate>Wed, 21 Jul 2010 21:23:25 +0000</pubDate>
		<dc:creator>Eric Melski</dc:creator>
				<category><![CDATA[Build-Test-Deploy Best Practices]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[incremental build]]></category>
		<category><![CDATA[gmake]]></category>
		<category><![CDATA[scons]]></category>

		<guid isPermaLink="false">http://blog.electric-cloud.com/?p=655</guid>
		<description><![CDATA[UPDATE: In response to comments here and elsewhere, I&#8217;ve done another series of SCons builds using the tips on the SCons &#8216;GoFastButton&#8217; wiki page. You can view the results here A few months ago, I took a look at the scalability of SCons, a popular Python-based build tool. The results were disappointing, to say the [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=655&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><strong>UPDATE:</strong> In response to comments here and elsewhere, I&#8217;ve done another series of SCons builds using the tips on <a href="http://www.scons.org/wiki/GoFastButton">the SCons &#8216;GoFastButton&#8217; wiki page</a>.  You can view the results <a href="http://blog.electric-cloud.com/2010/08/11/the-last-word-on-scons-performance/">here</a></p>
<hr />
<p>
A few months ago, I took a look at the scalability of <a href="http://www.scons.org/">SCons</a>, a popular Python-based build tool.  <a href="http://blog.electric-cloud.com/2010/03/08/how-scalable-is-scons/">The results were disappointing</a>, to say the least.  That post stirred up a lot of comments, both <a href="http://blog.electric-cloud.com/2010/03/08/how-scalable-is-scons/#comments">here</a> and in <a href="http://www.reddit.com/r/programming/comments/barcc/how_scalable_is_scons/">other</a> <a href="http://scons.tigris.org/ds/viewMessage.do?dsForumId=1268&amp;dsMessageId=2456717">forums</a>.  Several people pointed out that a comparison with other build tools would be helpful.  Some suggested that SCons&#8217; forte is really <i>incremental</i> builds, rather than the full builds I used for my test.  I think those are valid points, so I decided to revisit this topic.  This time around, I&#8217;ve got head-to-head comparisons between SCons and GNU make, the venerable old workhorse of build tools, as I use each tool to perform full, incremental, and clean builds.  Read on for the gory details &#8212; and lots of graphs.  <b>Spoiler alert:</b> SCons still looks pretty bad.<br />
<span id="more-655"></span>
</p>
<p><h3>The setup</h3>
</p>
<p>
As before, my test system is a dual 2.4 GHz Intel Xeon, with 2 GB RAM.  But since the previous set of tests, my system has been upgraded to RHEL4; there&#8217;s a new version of Python; and there&#8217;s even a new version of SCons &mdash; the big 2.0.  So the setup is now:
</p>
<ul>
<li>RedHat Enterprise Linux 4 (update 8, kernel version 2.6.9-89.ELsmp)</li>
<li>Dual 2.4 GHz Intel Xeon, with hyperthreading enabled</li>
<li>2 GB RAM</li>
<li>Python 2.7</li>
<li>SCons v2.0.0.final.0.r5023</li>
<li>GNU make 3.81</li>
</ul>
<p>
As previously, the test builds consists of a bunch of compiles and links:  <i>N</i> C files, each with a unique associated header, spread across <i>N/500</i> directories (to ensure there are no filesystem scalability effects), are compiled, then bundled into a standard archive library.  Every 20th object is linked into an executable along with the archive.  The build tree is generated using a Perl script, which generates both SConstruct files and Makefiles for building.  One difference between the two builds:  when using GMake, I added the <font face="Courier New">-MMD</font> flag to gcc, to generate additional dependency information; each timed full build was then preceded by a full build to generate all the dependencies and a clean build to nuke all the generated output.  I felt that this gave a more realistic comparison to SCons, which employs elaborate dependency analysis logic to ensure accurate incremental builds.
</p>
<p><h3>Round 1: Full builds</h3>
</p>
<p>
Again, my interest is primarily full, from scratch builds:
</p>
<p>
<a href="http://ecloud.files.wordpress.com/2010/07/scons_full.png"><img src="http://ecloud.files.wordpress.com/2010/07/scons_full.png?w=300&#038;h=180" alt="" title="scons_full" width="300" height="180" class="aligncenter size-medium wp-image-659" /></a>
</p>
<p>
As expected, SCons&#8217; performance is pretty miserable:  by the time we get to a build with several thousand source files, the build time is already over 15 minutes, and with the same n<sup>2</sup> growth we saw last time, the times race past one hour, then two, finally hitting <b>nearly 5 hours</b> for a build containing just 50,000 source files.
</p>
<p>
GNU make shows a much more sedate, linear growth &mdash; in fact it neatly keeps pace with a simple shell script that does all the same compiles and links as the regular build, but without any of the overhead of a build tool.  Yes, I know that a shell script is no substitute for a proper build tool.  It just serves to give us an idea of how long it takes just to do the work in the build &mdash; a lower bound on the build time.  No build tool running serially can run the build any faster than this (obviously we can do better, by a constant factor, if we use parallel build features).
</p>
<p>
Using that lower bound, we can compute the amount of <i>overhead</i> introduced by the build tool.  This is all the time that the tool spends doing things <i>other than</i> actually running the commands needed to execute the build:  parsing SConstruct files or makefiles, building the dependency graph, traversing that graph, computing command-lines, etc.  Viewing this overhead as a percentage of the total build time gives us an easier-to-digest comparison between SCons and GMake:
</p>
<p>
<a href="http://ecloud.files.wordpress.com/2010/07/scons_overhead.png"><img src="http://ecloud.files.wordpress.com/2010/07/scons_overhead.png?w=300&#038;h=180" alt="" title="scons_overhead" width="300" height="180" class="aligncenter size-medium wp-image-657" /></a>
</p>
<p>
Even with relatively few files &mdash; around 2,000 &mdash; <b>over 50% of the total build time is wasted by overhead with SCons</b>.  In comparison, GMake has barely any overhead on a build of that size.  At the other end of the range, <b>SCons overhead accounts for nearly 90% of the total build time</b>.  GMake overhead has increased as a percentage of the total time too, but only to a modest 20% &mdash; still significantly less than SCons.  In fact, with 50,000 files, GMake overhead is less than half of SCons overhead with just 2,000 files!
</p>
<p>
OK, just a couple more things to touch on here before we look at incremental build times:  first, you probably noticed the sudden hook in the graph for SCons full build times, between 45,000 and 50,000 files.  That means that at that level, on my system, some other factor has kicked in to influence the times.  I believe this is because the system has started to page heavily, as SCons&#8217; memory footprint has grown to consume all available RAM.  Second, if you compare the SCons times in this article with those in the previous article, you&#8217;ll see that this time around, the SCons times are a bit better than last time &mdash; about 30% on average.  Keep in mind that there are several differences between this setup and the previous:  a new OS, a new version of Python, a new version of SCons.  Even a new version of the compiler and other tools used during the build.  I did try to pin down exactly which factors contributed to this improvement by testing various combinations of versions of Python and SCons (for example, Python 2.6.2 with SCons 2.0; or Python 2.7 with SCons 1.2.0); none of these tests produced any significant change in performance, so I presume that the performance difference is most likely due to the new operating system and tools.  I choose not to pursue the matter further than that.
</p>
<p><h3>Round 2: Incremental builds</h3>
</p>
<p>
A lot of people claimed that full builds <i>&#8220;don&#8217;t really matter; developers only really do incremental builds&#8221;</i>.  So let&#8217;s take a look at incremental build performance:
</p>
<p>
<a href="http://ecloud.files.wordpress.com/2010/07/scons_incr.png"><img src="http://ecloud.files.wordpress.com/2010/07/scons_incr.png?w=300&#038;h=180" alt="" title="scons_incr" width="300" height="180" class="aligncenter size-medium wp-image-660" /></a>
</p>
<p>
For these builds, I ran a full build, then patched one C file and its associated header.  This caused a rebuild of one object file, followed by a rebuild of the archive and the executable that the object feeds into.  The actual time to execute just those commands is a paltry one-tenth of a second, so with either tool, the incremental time is dominated by the overhead added by the tool itself.  Even so, it&#8217;s obvious that SCons adds considerably more overhead than GMake.  Even with a small build containing just 2,000 files, <b>SCons burns about 35 seconds to do one-tenth of a second of work</b>.  GMake does the same build in about 3 seconds.  For the 50,000 file build, SCons ran for about 25 minutes, again just to do one-tenth of a second of actual work; GMake ran for about 9 minutes.
</p>
<p>
One thing I find especially interesting about this graph is that supposedly the problem with full builds is that they force SCons to constantly rescan the dependency graph.  That shouldn&#8217;t be necessary in an incremental build, and yet we still see what looks like an O(n<sup>2</sup>) growth in build time.
</p>
<p><h3>Round 3: Clean builds</h3>
</p>
<p>
The last build comparison was a clean build:  <font face="Courier new">scons -c</font> versus <font face="Courier New">gmake clean</font>:
</p>
<p>
<a href="http://ecloud.files.wordpress.com/2010/07/scons_clean.png"><img src="http://ecloud.files.wordpress.com/2010/07/scons_clean.png?w=300&#038;h=180" alt="" title="scons_clean" width="300" height="180" class="aligncenter size-medium wp-image-658" /></a>
</p>
<p>
At last we have found a build variant where SCons performance is in the same ballpark as GMake!  Of course, according to the SCons docs, you&#8217;ll never need to do a clean build with SCons (because with SCons your dependencies are perfectly accurate), so maybe this data point is not actually interesting to SCons users.
</p>
<p><h3>Sudden death overtime: Memory usage</h3>
</p>
<p>
One last comparison:  memory usage.  This metric is of particular interest because it puts a hard limit on the maximum size of the build that the tool can handle.  I&#8217;ve learned the importance of memory efficiency the hard way &mdash; long nights in &#8220;firefighting&#8221; mode to improve memory usage to accomodate this or that customer&#8217;s enormous build.  I&#8217;d hoped that the new versions of Python and SCons used in this test would prove beneficial for SCons memory footprint.  Unfortunately, the opposite is true:  memory usage is about 8% worse now than it was the last time I ran these tests, and as you can see here, SCons uses about 4 1/2 times as much memory as GMake:
</p>
<p>
<a href="http://ecloud.files.wordpress.com/2010/07/scons_mem.png"><img src="http://ecloud.files.wordpress.com/2010/07/scons_mem.png?w=300&#038;h=180" alt="" title="scons_mem" width="300" height="180" class="aligncenter size-medium wp-image-661" /></a>
</p>
<p>
With 50,0000 files, SCons uses just a hair less than 2GB of memory, enough to cause my test system (with 2GB of RAM) to start swapping.  In comparison, GMake needs just 440MB of memory for 50,000 files.  At that rate, GMake won&#8217;t start to thrash my system until the build has grown to more than 225,000 files.  Unfortunately, it seems likely that there&#8217;s not much that the SCons developers can do to fix this:  because SCons is implemented in Python, they are at the mercy of the Python runtime implementation.  There&#8217;s just not enough control over low-level details like memory allocation when you&#8217;re using an interpreted language.
</p>
<p><h3>Conclusions</h3>
</p>
<p>
We can clearly see now that it is not simply &#8220;the nature of the beast&#8221; that SCons performance is so bad.  GMake scales much more gracefully, both in terms of elapsed time, and in memory usage, on a variety of scenarios:  full, one-touch incremental, and clean builds.  From the discussions that the previous post sparked, we know that the primary problem is an inefficient O(n<sup>2</sup>) algorithm in the SCons implementation.  It seems that SCons is academically interesting, but ultimately not practical for any non-trivial build (as some of my customers are now finding out the hard way).
</p>
<p>
A lot of SCons fans, in defense of SCons, say things like &#8220;Well, the SCons developers made a concious decision to focus on <i>correctness</i> rather than <i>performance</i>.&#8221;  But what good is a correct system that is so slow it&#8217;s unusable?  For me, the choice is a no-brainer:  my time is too precious to waste on a slow build tool.</p>
<br />Filed under: <a href='http://blog.electric-cloud.com/category/build-test-deploy-best-practices/'>Build-Test-Deploy Best Practices</a>, <a href='http://blog.electric-cloud.com/category/software-development/'>Software Development</a> Tagged: <a href='http://blog.electric-cloud.com/tag/gmake/'>gmake</a>, <a href='http://blog.electric-cloud.com/tag/incremental-build/'>incremental build</a>, <a href='http://blog.electric-cloud.com/tag/performance/'>performance</a>, <a href='http://blog.electric-cloud.com/tag/scons/'>scons</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/ecloud.wordpress.com/655/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/ecloud.wordpress.com/655/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/ecloud.wordpress.com/655/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/ecloud.wordpress.com/655/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/ecloud.wordpress.com/655/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/ecloud.wordpress.com/655/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/ecloud.wordpress.com/655/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/ecloud.wordpress.com/655/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/ecloud.wordpress.com/655/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/ecloud.wordpress.com/655/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/ecloud.wordpress.com/655/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/ecloud.wordpress.com/655/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/ecloud.wordpress.com/655/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/ecloud.wordpress.com/655/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=655&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://blog.electric-cloud.com/2010/07/21/a-second-look-at-scons-performance/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6344c5478a4b5f0e8c546c736b2a7e0d?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=PG" medium="image">
			<media:title type="html">ericm</media:title>
		</media:content>

		<media:content url="http://ecloud.files.wordpress.com/2010/07/scons_full.png?w=300" medium="image">
			<media:title type="html">scons_full</media:title>
		</media:content>

		<media:content url="http://ecloud.files.wordpress.com/2010/07/scons_overhead.png?w=300" medium="image">
			<media:title type="html">scons_overhead</media:title>
		</media:content>

		<media:content url="http://ecloud.files.wordpress.com/2010/07/scons_incr.png?w=300" medium="image">
			<media:title type="html">scons_incr</media:title>
		</media:content>

		<media:content url="http://ecloud.files.wordpress.com/2010/07/scons_clean.png?w=300" medium="image">
			<media:title type="html">scons_clean</media:title>
		</media:content>

		<media:content url="http://ecloud.files.wordpress.com/2010/07/scons_mem.png?w=300" medium="image">
			<media:title type="html">scons_mem</media:title>
		</media:content>
	</item>
		<item>
		<title>Designing for high performance</title>
		<link>http://blog.electric-cloud.com/2010/07/12/designing-for-high-performance/</link>
		<comments>http://blog.electric-cloud.com/2010/07/12/designing-for-high-performance/#comments</comments>
		<pubDate>Mon, 12 Jul 2010 19:13:37 +0000</pubDate>
		<dc:creator>Eric Melski</dc:creator>
				<category><![CDATA[ElectricAccelerator]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[parallel builds]]></category>
		<category><![CDATA[gmake]]></category>
		<category><![CDATA[gnu make]]></category>

		<guid isPermaLink="false">http://blog.electric-cloud.com/?p=648</guid>
		<description><![CDATA[Here&#8217;s the thing about high performance: you can&#8217;t just bolt it on at the end. It&#8217;s got to be baked in from day one. No doubt those of you who are experienced developers are now invoking the venerable Donald Knuth, who once said, &#8220;Premature optimization is the root of all evil.&#8221; But look at it [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=648&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s the thing about high performance:  you can&#8217;t just bolt it on at the end.  It&#8217;s got to be baked in from day one.  No doubt those of you who are experienced developers are now invoking the venerable Donald Knuth, who once said, &#8220;Premature optimization is the root of all evil.&#8221;  But look at it this way:  with <a href="http://jalopnik.com/5497042/how-a-500-craigslist-car-beat-400k-rally-racers">very rare exceptions</a>, no amount of performance tuning will turn an average system into a world class competitor.</p>
<p>
Of course, high performance is the entire raison d&#8217;être for ElectricAccelerator.  We knew from the start that parallelism would be the primary means of achieving our performance goals (although it&#8217;s not the <a href="http://blog.electric-cloud.com/2009/03/11/measuring-electricaccelerator-cache-efficiency/">only</a> <a href="http://blog.electric-cloud.com/2009/04/13/makefile-performance-pattern-specific-variables/">trick</a> we used).  Thanks to <a href="http://en.wikipedia.org/wiki/Amdahl's_law">Amdahl&#8217;s law</a>, we know that in order to accelerate a build by 100x, the serialized portion cannot be more than 1% of the baseline time.  Thus it&#8217;s critical that <i>absolutely everything that <b>can</b> be parallelized, <b>is</b> parallelized.</i>  And I mean <i>everything</i>, even the stuff that you don&#8217;t normally think about, because anything that doesn&#8217;t get parallelized disproportionately saps our performance.  Anything that isn&#8217;t parallelized is a bottleneck.<br />
<span id="more-648"></span>
</p>
<p>
Here&#8217;s an example:  command expansion.  You know, the bit of code that turns something like this:
</p>
<p><pre>
<div style="background:#dee7f7;border:solid thin;width:60ex;margin-left:auto;margin-right:auto;"><font size="-1" face="Courier New">$(CC) $(addprefix -I,$(dir $(SRCS))) -o $@ $&lt;
</font></div>
</pre>
<p>into something like this:
</p>
<p><pre>
<div style="background:#dee7f7;border:solid thin;width:60ex;margin-left:auto;margin-right:auto;"><font size="-1" face="Courier New">gcc -I. -Isubdir -o foo.o foo.c
</font></div>
</pre>
<p>
There&#8217;s no way around this translation.  It has to be performed, for every command invoked during the build.  Even if the command doesn&#8217;t have anything that needs to be expanded.
</p>
<p>
What happens if the expansion itself is time-consuming?  For the sake of demonstration, we can make command expansion slow by sticking <font face="Courier New">$(shell sleep 5)</font> into the command &#8212; because the sleep appears inside a $(shell) function call, gmake is obliged to execute the <font face="Courier New">sleep</font> <a href="http://www.gnu.org/software/make/manual/make.html#Variables-in-Commands">as part of expanding the command</a>.  Here&#8217;s the makefile we&#8217;ll use:
</p>
<p><pre>
<div style="background:#dee7f7;border:solid thin;width:60ex;margin-left:auto;margin-right:auto;"><font size="-1" face="Courier New">all: a b c d e
a b c d e:
        @$(shell sleep 5) echo $@
</font></div>
</pre>
<p>
Now, if you run this with an ordinary, serialized gmake, you&#8217;ll see that it takes about 25 seconds to execute.  No surprise there, with 5 jobs that each have a 5 second sleep:
</p>
<p><pre>
<div style="background:#dee7f7;border:solid thin;width:60ex;margin-left:auto;margin-right:auto;"><font size="-1" face="Courier New">ericm@chester$ time gmake
a
b
c
d
e

real    0m25.092s
user    0m0.012s
sys     0m0.012s
ericm@chester$
</font></div>
</pre>
<p>
Now, see what happens when we run this same makefile with a parallel gmake invocation:  <b>it still takes 25 seconds</b>, even though we have specified more than enough parallel jobs that all five jobs should run simultaneously!
</p>
<p><pre>
<div style="background:#dee7f7;border:solid thin;width:60ex;margin-left:auto;margin-right:auto;"><font size="-1" face="Courier New">ericm@chester$ time gmake -j 8
a
b
c
d
e

real    0m25.016s
user    0m0.012s
sys     0m0.012s
ericm@chester$
</font></div>
</pre>
<p>
What&#8217;s going on here?  Let&#8217;s take a quick look at the core algorithm in gmake:
</p>
<ol>
<li>Find the next target that&#8217;s is runnable (all prereqs up-to-date).</li>
<li>Expand the commands for that target.</li>
<li>Run the command for that target.</li>
<li>If the number of currently running commands is equal to the job limit (1 for serial gmake, <i>N</i> for parallel gmake), wait for a command to finish.</li>
<li>Repeat until finished.</li>
</ol>
<p>
Maybe you see the problem already:  gmake is a single-threaded program.  Even when you specify <tt>-j 8</tt>, there&#8217;s only one thread executing that core algorithm.  Parallelism doesn&#8217;t really enter the picture until the end of the algorithm, where gmake decides whether it can go ahead and start working on another target without waiting for the previous one to finish.
</p>
<p>
Being single-threaded means that command expansion is implicitly serialized:  gmake can only expand commands for a single target at a time.  Too bad for you if that expansion takes any significant amount of time.
</p>
<p>
So, could we fix gmake, so that command expansions could be performed in parallel?  Well, it&#8217;s really hard to take something that wasn&#8217;t designed to be high-performance from the start and transform it into something with world-class performance.  In this case, gmake&#8217;s heritage as a single-threaded application permeates every aspect of its implementation.  For example, command expansion is performed using a single global buffer (see <a href="http://cvs.savannah.gnu.org/viewvc/make/expand.c?revision=1.55&amp;root=make&amp;view=markup">expand.c in the gmake sources</a>).  While it&#8217;s certainly <i>possible</i> to refactor this code, it would be non-trivial to do so.
</p>
<p><h3>Command expansion with ElectricAccelerator</h3>
</p>
<p>
In contrast, Accelerator was designed to be multi-threaded from the start, and we have made a deliberate effort to parallelize absolute everything we can, including even the behind-the-scenes stuff that you probably never thought about before reading this blog.  Like command expansion.  And sure enough, if you try this makefile with Accelerator,  the total run time is about 5 seconds:
</p>
<p><pre>
<div style="background:#dee7f7;border:solid thin;width:70ex;margin-left:auto;margin-right:auto;"><font size="-1" face="Courier New">ericm@chester$ time emake
Starting build: 12705
a
b
c
d
e
Finished build: 12705 Duration: 0:05 (m:s) Cluster availability: 100%

real    0m5.523s
user    0m0.008s
sys     0m0.016s
ericm@chester$
</font></div>
</pre>
<p><h3>Designing for high performance</h3>
</p>
<p>
When truly world-class performance is your goal, you can&#8217;t wait until implementation is complete to start thinking about performance.  That goal has to inform your decisions at all stages of development, from design to implementation.  With Accelerator, our obsessive focus on performance has resulted in an architecture that allows us to parallelize parts of the build process that other tools simply cannot.</p>
<br />Filed under: <a href='http://blog.electric-cloud.com/category/electricaccelerator/'>ElectricAccelerator</a>, <a href='http://blog.electric-cloud.com/category/software-development/'>Software Development</a> Tagged: <a href='http://blog.electric-cloud.com/tag/gmake/'>gmake</a>, <a href='http://blog.electric-cloud.com/tag/gnu-make/'>gnu make</a>, <a href='http://blog.electric-cloud.com/tag/parallel-builds/'>parallel builds</a>, <a href='http://blog.electric-cloud.com/tag/performance/'>performance</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/ecloud.wordpress.com/648/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/ecloud.wordpress.com/648/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/ecloud.wordpress.com/648/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/ecloud.wordpress.com/648/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/ecloud.wordpress.com/648/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/ecloud.wordpress.com/648/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/ecloud.wordpress.com/648/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/ecloud.wordpress.com/648/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/ecloud.wordpress.com/648/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/ecloud.wordpress.com/648/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/ecloud.wordpress.com/648/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/ecloud.wordpress.com/648/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/ecloud.wordpress.com/648/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/ecloud.wordpress.com/648/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=648&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://blog.electric-cloud.com/2010/07/12/designing-for-high-performance/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6344c5478a4b5f0e8c546c736b2a7e0d?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=PG" medium="image">
			<media:title type="html">ericm</media:title>
		</media:content>
	</item>
		<item>
		<title>Visualizing Build Processes</title>
		<link>http://blog.electric-cloud.com/2010/06/29/visualizing-build-processes/</link>
		<comments>http://blog.electric-cloud.com/2010/06/29/visualizing-build-processes/#comments</comments>
		<pubDate>Tue, 29 Jun 2010 18:00:28 +0000</pubDate>
		<dc:creator>Eric Melski</dc:creator>
				<category><![CDATA[Build-Test-Deploy Best Practices]]></category>
		<category><![CDATA[Continuous Integration]]></category>
		<category><![CDATA[ElectricCommander]]></category>
		<category><![CDATA[Build automation]]></category>
		<category><![CDATA[visualization]]></category>

		<guid isPermaLink="false">http://blog.electric-cloud.com/?p=639</guid>
		<description><![CDATA[The other day I was waiting for my continuous integration build to finish. Think about that a minute. See anything wrong with that statement? I was waiting for a continuous integration build to finish. The whole point of continuous integration is to detect integration errors as quickly as possible. After years of growth, our builds [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=639&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>The other day I was waiting for my continuous integration build to finish.  Think about that a minute.  See anything wrong with that statement?  I was <i>waiting</i> for a <i>continuous</i> integration build to finish.  The whole point of continuous integration is to <a href="http://martinfowler.com/articles/continuousIntegration.html">detect integration errors as quickly as possible</a>.  After years of growth, our builds are too long to really live up to that definition.</p>
<p>
Obviously I have to do something about this, but what?  Where do I start?  The build is composed of several distinct phases:  checkout, compile, unit test, package, install and system test.  In addition, the complete process is actually replicated on several platforms simultaneously.  In total, the process encompasses over 200 distinct steps, running in parallel on dozens of machines.<br />
<span id="more-639"></span>
</p>
<p>
Fortunately, ElectricCommander tracks the duration of every step in the process, so I can find the &#8220;long pole&#8221; and focus my efforts there:
</p>
<p><pre>
<div style="background:#deffde;border:solid thin;width:60ex;margin-left:auto;margin-right:auto;"><font size="-1" face="Courier New">&lt;job&gt;
  &lt;jobId&gt;218513&lt;/jobId&gt;
  &lt;jobName&gt;ecloud-5.1.33976-201006251150&lt;/jobName&gt;
  </font><font color="red">&lt;elapsedTime&gt;7988201&lt;/elapsedTime&gt;</font>
  <font color="red">&lt;finish&gt;2010-06-25T15:18:08.234Z&lt;/finish&gt;</font>
  <font color="red">&lt;start&gt;2010-06-25T13:05:00.033Z&lt;/start&gt;</font>
  &lt;jobStep&gt;
    &lt;jobStepId&gt;2796108&lt;/jobStepId&gt;
    &lt;stepName&gt;build-windows&lt;/stepName&gt;
    <font color="red">&lt;elapsedTime&gt;363518&lt;/elapsedTime&gt;</font>
    <font color="red">&lt;finish&gt;2010-06-25T13:25:12.528Z&lt;/finish&gt;</font>
    <font color="red">&lt;start&gt;2010-06-25T13:19:09.010Z&lt;/start&gt;</font>
  &lt;/jobStep&gt;
  &lt;jobStep&gt;
    &lt;jobStepId&gt;2796141&lt;/jobStepId&gt;
    &lt;stepName&gt;build-linux&lt;/stepName&gt;
    <font color="red">&lt;elapsedTime&gt;277503&lt;/elapsedTime&gt;</font>
    <font color="red">&lt;finish&gt;2010-06-25T13:14:08.008Z&lt;/finish&gt;</font>
    <font color="red">&lt;start&gt;2010-06-25T13:09:30.505Z&lt;/start&gt;</font>
  &lt;/jobStep&gt;
  ...
&lt;/job&gt;
</div>
</pre>
<p>
<i>Unfortunately</i>, with hundreds of steps it&#8217;s not at all practical to work with the data in this format.  What I need is something that allows me to visualize the build process, so that I can see at a glance where the time is going.  So I created the <i>Plotter Plugin</i> for ElectricCommander (click to view full-size):
</p>
<p>
<a href="http://ecloud.files.wordpress.com/2010/06/plotter.png"><img src="http://ecloud.files.wordpress.com/2010/06/plotter.png?w=300&#038;h=197" alt="Example output from the Plotter Plugin" title="plotter" width="300" height="197" class="aligncenter size-medium wp-image-641" /></a>
</p>
<p>
Now instead of digging through hundreds of lines of timing information, I can see immediately where the problems are.  Briefly, here&#8217;s what you&#8217;re looking at in this image:
</p>
<ul>
<li>Each colored box represents a single step in the build process.</li>
<li>Hovering over a box will display the name of the step.</li>
<li>Clicking on a box will load the Commander &#8220;Job Step Details&#8221; page for that step.</li>
<li>Boxes are colored according to the outcome of the step (red, yellow or green, corresponding to failure, warning or success, respectively).</li>
<li>Boxes are positioned horizontally according to the start time of the step relative to the start time of the entire process.  The correlary to this is that steps that are running in parallel &#8220;line up&#8221; vertically, at least in part.</li>
<li>Steps which run subprocedures are not colored, and use a dashed outline to group the child steps.</li>
</ul>
<p>
In my case, the first problem is the two large, serialized steps near the top of the image, which correspond to execution of our system test suites on Windows.  Now armed with this information, I can make intelligent decisions about how to improve my end-to-end build time.
</p>
<p>
If you want to try the Plotter Plugin, make sure you&#8217;re running ElectricCommander 3.5 or later, then download the plugin from our <a href="http://plugins.electric-cloud.com/catalog/browse.php">public plugin repository</a>, and use <code>ectool installPlugin</code> to install it.  If you&#8217;re not currently using ElectricCommander, you can <a href="http://www.electric-cloud.com/products/electriccommander-contact.php">get an evaluation copy.</a></p>
<br />Filed under: <a href='http://blog.electric-cloud.com/category/build-test-deploy-best-practices/'>Build-Test-Deploy Best Practices</a>, <a href='http://blog.electric-cloud.com/category/continuous-integration/'>Continuous Integration</a>, <a href='http://blog.electric-cloud.com/category/electriccommander/'>ElectricCommander</a> Tagged: <a href='http://blog.electric-cloud.com/tag/build-automation/'>Build automation</a>, <a href='http://blog.electric-cloud.com/tag/continuous-integration/'>Continuous Integration</a>, <a href='http://blog.electric-cloud.com/tag/electriccommander/'>ElectricCommander</a>, <a href='http://blog.electric-cloud.com/tag/visualization/'>visualization</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/ecloud.wordpress.com/639/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/ecloud.wordpress.com/639/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/ecloud.wordpress.com/639/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/ecloud.wordpress.com/639/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/ecloud.wordpress.com/639/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/ecloud.wordpress.com/639/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/ecloud.wordpress.com/639/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/ecloud.wordpress.com/639/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/ecloud.wordpress.com/639/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/ecloud.wordpress.com/639/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/ecloud.wordpress.com/639/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/ecloud.wordpress.com/639/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/ecloud.wordpress.com/639/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/ecloud.wordpress.com/639/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=639&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://blog.electric-cloud.com/2010/06/29/visualizing-build-processes/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6344c5478a4b5f0e8c546c736b2a7e0d?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=PG" medium="image">
			<media:title type="html">ericm</media:title>
		</media:content>

		<media:content url="http://ecloud.files.wordpress.com/2010/06/plotter.png?w=300" medium="image">
			<media:title type="html">plotter</media:title>
		</media:content>
	</item>
		<item>
		<title>How scalable is SCons?</title>
		<link>http://blog.electric-cloud.com/2010/03/08/how-scalable-is-scons/</link>
		<comments>http://blog.electric-cloud.com/2010/03/08/how-scalable-is-scons/#comments</comments>
		<pubDate>Mon, 08 Mar 2010 17:46:39 +0000</pubDate>
		<dc:creator>Eric Melski</dc:creator>
				<category><![CDATA[ElectricAccelerator]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[scons]]></category>

		<guid isPermaLink="false">http://blog.electric-cloud.com/?p=625</guid>
		<description><![CDATA[The marquee feature in ElectricAccelerator 5.0 is Electrify, a new front-end to the Accelerator cluster that allows us to distribute work from a wide variety of processes in addition to the make-based processes that we have always managed. One example is SCons, an alternative build system implemented in Python that has a small (compared to [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=625&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>The marquee feature in ElectricAccelerator 5.0 is <i>Electrify</i>, a new front-end to the Accelerator cluster that allows us to distribute work from a wide variety of processes in addition to the make-based processes that we have always managed.  One example is <a href="http://www.scons.org/">SCons</a>, an alternative build system implemented in Python that has a small (compared to make) but apparently growing (slowly) market share.  It&#8217;s sometimes touted as <a href="http://freshmeat.net/articles/stop-the-autoconf-insanity-why-we-need-a-new-build-system">an ideal replacement for make</a>, with a <a href="http://scons.org">long list of reasons why it is considered superior</a>.  But not everybody likes it.  Some have reported <a href="http://gamesfromwithin.com/bad-news-for-scons-fans">significant performance problems</a>.  Even the SCons maintainers agree, SCons <a href="http://scons.org/wiki/SconsVsOtherBuildTools">&#8220;Can get slow on big projects&#8221;</a>.</p>
<p>
Of course that caught my eye, since making big projects build fast is what I do.  What exactly does it mean that SCons &#8220;can get slow&#8221; on &#8220;big&#8221; projects?  How slow is slow?  How big is big?  So to satisfy my own curiosity, and so that I might better advise customers seeking to use SCons with Electrify, I set out to answer those questions.  All I needed was some free hardware and some time.  <i>Lots and lots and lots of time.</i>
</p>
<p>
<span id="more-625"></span></p>
<h3>The Setup</h3>
</p>
<p>
My test environment was as follows:
</p>
<ul>
<li>RedHat Desktop 3 (kernel version 2.4.21-58.ELsmp)</li>
<li>Dual 2.4 GHz Intel Xeon with hyperthreading enabled</li>
<li>2 GB RAM</li>
<li>SCons v1.2.0.r3842</li>
<li>Python 2.6.2</li>
</ul>
<p>
The test build consists of (a lot of) compiles and links.  Starting from the bottom, we have <i>N</i> C files each with a unique associated header file.  The C files and headers were spread across <i>N/500</i> directories in order to eliminate filesystem scalability concerns.  Both the C files and the header files are trivial:  the header only includes stdio.h; the C file includes the associated header and a second, shared header, then defines a trivial function.  Objects are collected into groups of 20 and stored into a standard archive.  Every 20th object is linked into an executable along with the archive.  The build is generated using a script written by one of our talented QA engineers for testing Accelerator.
</p>
<p><h3>Overall build time</h3>
</p>
<p>
The primary concern was naturally end-to-end build time for a from-scratch build.  I used the standard Linux <i>time</i> utility to capture this data, and averaged the results from two runs (except for the build with 40,000 C files, because that just took too long):
</p>
<p>
<a href="http://ecloud.files.wordpress.com/2010/03/graph.png"><img src="http://ecloud.files.wordpress.com/2010/03/graph.png?w=300&#038;h=180" alt="Overall build time" title="graph" width="300" height="180" class="aligncenter size-medium wp-image-626" /></a>
</p>
<p>
That doesn&#8217;t look too good!  In fact, that curve looks suspiciously like the function <i>f(x) = x<sup>2</sup></i>.  Let&#8217;s plot that on our graph:
</p>
<p>
<a href="http://ecloud.files.wordpress.com/2010/03/graph_with_xsquared.png"><img src="http://ecloud.files.wordpress.com/2010/03/graph_with_xsquared.png?w=300&#038;h=180" alt="Overall build time compared to x-squared growth" title="graph_with_xsquared" width="300" height="180" class="aligncenter size-medium wp-image-627" /></a>
</p>
<p>
That looks like a pretty close fit &mdash; so it seems that the build duration increases in proportion to the square of the number of input files.  That&#8217;s bad news &mdash; as you can see, that very quickly adds up to outrageously long build times.
</p>
<p><h3>Ruling out other causes</h3>
</p>
<p>
It&#8217;s possible that this performance degradation is due to some factor other than SCons.  To rule out that possibility, I created a shell script that runs the exact set of commands, in the same order, that the SCons-based build had, and timed the execution of that script.  The work done by that script is the <i>actual work</i> of the build:  the bare minimum that must be done to compile and link all of the source files.  By definition, everything else is overhead.  Let&#8217;s add that data to the graph:
</p>
<p>
<a href="http://ecloud.files.wordpress.com/2010/03/graph_with_shell.png"><img src="http://ecloud.files.wordpress.com/2010/03/graph_with_shell.png?w=300&#038;h=180" alt="Overall build time compared to actual work" title="graph_with_shell" width="300" height="180" class="aligncenter size-medium wp-image-628" /></a>
</p>
<p>
As expected, the time needed for the actual work grows linearly in proportion to the number of C files in the build.  That means that the performance degradation is not due to some other component of the system &mdash; if it were, we would have seen a similar problem with the simple shell script.  Instead, the problem is clearly in SCons itself.
</p>
<p><h3>Comparing overhead to actual work</h3>
<p>Now that we know the amount of time for the actual work, we can compute the amount of time spent on overhead introduced by SCons &mdash; that&#8217;s just the difference between the &#8220;overall build time&#8221; and the &#8220;actual work&#8221; lines in our graph.  For example, with 40,000 C files, the SCons build time is about 4 1/2 hours; the actual work time is about 25 minutes.  SCons is adding more than <b>four hours  of overhead</b> to the build!
</p>
<p>
Let&#8217;s put that into terms that are a little easier to grok:  rather than looking at the absolute numbers, we&#8217;ll look at the overhead as a percentage of the total build time.
</p>
<p>
<a href="http://ecloud.files.wordpress.com/2010/03/overhead.png"><img src="http://ecloud.files.wordpress.com/2010/03/overhead.png?w=300&#038;h=180" alt="Overhead from SCons as a percentage of overall build time" title="overhead" width="300" height="180" class="aligncenter size-medium wp-image-629" /></a>
</p>
<p>
Even with only a few hundred files, SCons overhead represents 50% of the total build time; with 10,000 C files, SCons overhead represents 75% of the total build time; and with 40,000 C files, SCons overhead accounts for a whopping 90% of the total time!
</p>
<p><h3>Memory usage</h3>
</p>
<p>
The final metric that I tracked was SCons memory utilization, using the built-in <i>&#8211;debug=memory</i> flag.  This metric is of particular interest to me, since I&#8217;ve spent a lot of time streamlining Accelerator&#8217;s memory usage so that it can accommodate truly enormous builds (millions of compiles).  After the disastrous build time results, I was relieved to see that memory usage seems to grow linearly with the number of source files in the build (NB:  here I&#8217;m counting <i>total</i> source files, including both C files and headers, not only C files):
</p>
<p>
<a href="http://ecloud.files.wordpress.com/2010/03/memory.png"><img src="http://ecloud.files.wordpress.com/2010/03/memory.png?w=300&#038;h=180" alt="SCons memory usage" title="memory" width="300" height="180" class="aligncenter size-medium wp-image-630" /></a>
</p>
<p>
Unfortunately, although the growth is linear, the rate of growth is quite high: each additional source file adds more than 19,000 bytes (!) to the memory footprint.  At that rate, SCons will exhaust the available memory address space for a 32-bit process at only about 110,000 total source files.
</p>
<p><h3>Conclusions</h3>
</p>
<p>
These results paint a pretty grim picture for SCons:  based on the overall build times, I can&#8217;t imagine anybody seriously using SCons for builds with more than a couple thousand files.  And even if you were willing to put up with the long builds, the memory usage data indicates that SCons has a hard limit of around 110,000 total source files.
</p>
<p>
Are there any SCons experts out there able to explain why SCons seems to perform so badly here?</p>
<br />Filed under: <a href='http://blog.electric-cloud.com/category/electricaccelerator/'>ElectricAccelerator</a>, <a href='http://blog.electric-cloud.com/category/software-development/'>Software Development</a> Tagged: <a href='http://blog.electric-cloud.com/tag/electricaccelerator/'>ElectricAccelerator</a>, <a href='http://blog.electric-cloud.com/tag/performance/'>performance</a>, <a href='http://blog.electric-cloud.com/tag/scons/'>scons</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/ecloud.wordpress.com/625/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/ecloud.wordpress.com/625/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/ecloud.wordpress.com/625/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/ecloud.wordpress.com/625/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/ecloud.wordpress.com/625/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/ecloud.wordpress.com/625/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/ecloud.wordpress.com/625/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/ecloud.wordpress.com/625/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/ecloud.wordpress.com/625/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/ecloud.wordpress.com/625/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/ecloud.wordpress.com/625/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/ecloud.wordpress.com/625/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/ecloud.wordpress.com/625/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/ecloud.wordpress.com/625/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=625&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://blog.electric-cloud.com/2010/03/08/how-scalable-is-scons/feed/</wfw:commentRss>
		<slash:comments>26</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6344c5478a4b5f0e8c546c736b2a7e0d?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=PG" medium="image">
			<media:title type="html">ericm</media:title>
		</media:content>

		<media:content url="http://ecloud.files.wordpress.com/2010/03/graph.png?w=300" medium="image">
			<media:title type="html">graph</media:title>
		</media:content>

		<media:content url="http://ecloud.files.wordpress.com/2010/03/graph_with_xsquared.png?w=300" medium="image">
			<media:title type="html">graph_with_xsquared</media:title>
		</media:content>

		<media:content url="http://ecloud.files.wordpress.com/2010/03/graph_with_shell.png?w=300" medium="image">
			<media:title type="html">graph_with_shell</media:title>
		</media:content>

		<media:content url="http://ecloud.files.wordpress.com/2010/03/overhead.png?w=300" medium="image">
			<media:title type="html">overhead</media:title>
		</media:content>

		<media:content url="http://ecloud.files.wordpress.com/2010/03/memory.png?w=300" medium="image">
			<media:title type="html">memory</media:title>
		</media:content>
	</item>
		<item>
		<title>Automating around scarcity by using virtual resources</title>
		<link>http://blog.electric-cloud.com/2010/01/28/automating-around-scarcity-by-using-virtual-resources/</link>
		<comments>http://blog.electric-cloud.com/2010/01/28/automating-around-scarcity-by-using-virtual-resources/#comments</comments>
		<pubDate>Thu, 28 Jan 2010 23:19:47 +0000</pubDate>
		<dc:creator>Erin Curtis</dc:creator>
				<category><![CDATA[Build-Test-Deploy Best Practices]]></category>
		<category><![CDATA[ElectricCommander]]></category>
		<category><![CDATA[Virtualization]]></category>
		<category><![CDATA[automation]]></category>
		<category><![CDATA[resource management]]></category>

		<guid isPermaLink="false">http://blog.electric-cloud.com/?p=621</guid>
		<description><![CDATA[[posted on behalf of Usman Muzaffar, who is on a long flight with no WiFi] Here&#8217;s a sobering truth that shows up often in software automation: people are way better at sharing stuff than computers are. For example: say you have a scarce resource, like a box with special hardware or a service with serial [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=621&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>[posted on behalf of Usman Muzaffar, who is on a long flight with no WiFi]</p>
<p>Here&#8217;s a sobering truth that shows up often in software automation: people are way better at sharing stuff than computers are. For example: say you have a scarce resource, like a box with special hardware or a service with serial access. You&#8217;re tasked with automating a software build/test/release workflow, and part of it needs to talk to this One Big And Fancy Thing. Do you try to teach your build script good playground behavior, so it automatically knows when to wait politely (and when, as deadlines approach, it should bully its way to the top of the slide), or do you declare this problem out-of-scope, and just provide the hook to let the team manage access manually?</p>
<p>The default on that checkbox is: *don&#8217;t automate*, for two reasons. First: letting people handle it means no extra work. More importantly, because we&#8217;ve been doing it our whole life, we&#8217;re actually pretty good at adapting to environments where we have to share things, whether that&#8217;s roads or restrooms or rack space. A small number of people on the same team with similar goals will usual self-organize around a few ground rules with a minimum of fuss. One clear and crisply delivered directive at a weekly team meeting (&#8220;OK guys the new 32-way sol box is for the full server test suite, so give that priority and check with each other before you use it for other stuff&#8221;) is often all it takes.</p>
<p>Second, technically getting the semantics of shared simultaneous access right is a notorious pain in the neck. As in any software automation system, there&#8217;s no credit for a partial answer: it&#8217;s a net loss if your script still needs a babysitter for the corner cases. So that means your solution needs to take selection and queuing and load into account, and have mechanisms for priority and pre-emption and be smart about busted network connections. More fundamentally, at its core it usually boils down to something awfully close to multithreaded programming, with the usual challenges in that space around semaphores, locks, deadlocks, races. Great stuff in a CS course or maybe your server&#8217;s ConnectionPool class &#8212; rathole alert in your build and test system!</p>
<p>So, largely with good reason, the automation train comes to a screeching halt right here. It&#8217;s just not worth the effort to build a system that&#8217;s going to manage the synchronization for parallel access to scarce resources. In other words: when shared resources wind up in the software production system, people show up next to them, and that sucks all the fun (and potential efficiency gains) out of automation. What to do?</p>
<p>One thing worth investigating are tools that can handle this for you.  Solving this was a key goal for our ElectricCommander product. Commander lets you describe your job as a series of command line steps, and each step can be specified to run on a resource. A resource is simply a system that we&#8217;ll remotely execute commands on, and it comes with a sack full of infrastructure goodies you&#8217;d expect like pooling, exclusive reservation, broadcast, security, access control, load balancing, and fault tolerance. As a user of the system, you specify what you want to run, and where you want to run it, press the &#8216;Go&#8217; button and Commander does the rest, queuing steps when resources are oversubscribed and efficiently scheduling around your other constraints. Nice!</p>
<p>Then one day a customer asked us how they could automatically control access to a piece of hardware that simulated network traffic critical to the product&#8217;s system test. This wasn&#8217;t a gadget we could install software on; indeed, we couldn&#8217;t directly connect to it at all, so Commander can’t treat it as a resource. But it soon became evident that we could solve this just as elegantly with a simple tweak to the approach. Fundamentally, we needed the ability to specify that a step 1) needed access, 2) must block when it wasn&#8217;t available, and 3) once acquired, hang on to it until it was done. If something could just take care of this synchronization and queuing, the test could connect to the traffic simulator directly and simply execute as if invoked manually.</p>
<p>In other words: the problem called for a *subset* of Commander resources;  ignore half the stuff in the goodie sack (remote login, execution, fault tolerance, etc.) and you&#8217;re left with a general purpose resource access and acquisition facility. We set up dummy resources (good old 127.0.0.1, always up and ready for this sort of game!), injected them into the workflow and configured the job to hang on to them as long as it was talking to the traffic simulator. It worked beautifully: each test run was guaranteed to get just the access it needed, and for the first time, the customer had safe, parallel end-to-end automation for the full test cycle.</p>
<p>More importantly, this design pattern, since dubbed Virtual Resources, opened a whole new realm of possibilities. Once you start looking for them, there are *lots* of shared things in a software system that aren&#8217;t compute hosts, and they&#8217;re all threatening or overcomplicating automation in some way or another.  We&#8217;ve used Virtual Resources to manage access database tables, SCM labels, virtual machines, filesystem repositories, flaky external systems that don&#8217;t like more than one client talking to them, and our customers keep showing us new ways. It&#8217;s a great example of how the core of a clean design &#8212; a resource is something a job can request and relinquish &#8212; was readily adapted to a wider set of problems around Software Production Automation.</p>
<br />Filed under: <a href='http://blog.electric-cloud.com/category/build-test-deploy-best-practices/'>Build-Test-Deploy Best Practices</a>, <a href='http://blog.electric-cloud.com/category/electriccommander/'>ElectricCommander</a>, <a href='http://blog.electric-cloud.com/category/virtualization/'>Virtualization</a> Tagged: <a href='http://blog.electric-cloud.com/tag/automation/'>automation</a>, <a href='http://blog.electric-cloud.com/tag/resource-management/'>resource management</a>, <a href='http://blog.electric-cloud.com/tag/virtualization/'>Virtualization</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/ecloud.wordpress.com/621/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/ecloud.wordpress.com/621/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/ecloud.wordpress.com/621/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/ecloud.wordpress.com/621/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/ecloud.wordpress.com/621/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/ecloud.wordpress.com/621/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/ecloud.wordpress.com/621/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/ecloud.wordpress.com/621/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/ecloud.wordpress.com/621/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/ecloud.wordpress.com/621/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/ecloud.wordpress.com/621/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/ecloud.wordpress.com/621/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/ecloud.wordpress.com/621/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/ecloud.wordpress.com/621/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=621&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://blog.electric-cloud.com/2010/01/28/automating-around-scarcity-by-using-virtual-resources/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/a900e4b0e626e3038848af796256a60c?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=PG" medium="image">
			<media:title type="html">ec</media:title>
		</media:content>
	</item>
		<item>
		<title>How to quickly navigate an unfamiliar makefile</title>
		<link>http://blog.electric-cloud.com/2009/11/10/how-to-quickly-navigate-an-unfamiliar-makefile/</link>
		<comments>http://blog.electric-cloud.com/2009/11/10/how-to-quickly-navigate-an-unfamiliar-makefile/#comments</comments>
		<pubDate>Tue, 10 Nov 2009 18:21:37 +0000</pubDate>
		<dc:creator>Eric Melski</dc:creator>
				<category><![CDATA[ElectricAccelerator]]></category>
		<category><![CDATA[SparkBuild]]></category>
		<category><![CDATA[annotation]]></category>
		<category><![CDATA[makefile]]></category>

		<guid isPermaLink="false">http://blog.electric-cloud.com/?p=610</guid>
		<description><![CDATA[The other day, I was working with an unfamiliar build and I needed to get familiar with it in a hurry. In this case, I was dealing with a makefile generated by the Perl utility h2xs, but the trick I&#8217;ll show you here works any time you need to find your way around a new [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=610&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>The other day, I was working with an unfamiliar build and I needed to get familiar with it in a hurry.  In this case, I was dealing with a makefile generated by the Perl utility <i>h2xs</i>, but the trick I&#8217;ll show you here works any time you need to find your way around a new build system, whether it&#8217;s something you just downloaded or an internal project you just transferred to.</p>
<p>
What I wanted to do was add a few object files to the link command.  Here&#8217;s the build log, with the link command highlighted:
</p>
<p><pre style="font:80% courier, verdana, monospace;border:1px solid #ccc;width:90%;background:#fff7f0;color:#000;overflow:auto;margin:1em auto 2em;padding:1em;">gcc -c  -I. -D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -O2 -g   -DVERSION=\"0.01\" -DXS_VERSION=\"0.01\" -fPIC "-I/usr/lib/perl/5.10/CORE"   mylib.c
rm -f blib/arch/auto/mylib/mylib.so
<font color="red"><b>gcc  -shared -O2 -g -L/usr/local/lib mylib.o   -o blib/arch/auto/mylib/mylib.so   \
                \
</b></font>
chmod 755 blib/arch/auto/mylib/mylib.so
</pre>
</p>
<p>
Should be easy, right?  I just needed to find that command in the makefile and make my changes.  Wrong.  Read on to see how <i>annotation</i> helped solve this problem.
</p>
<p>
<span id="more-610"></span><br />
The thing is, the command I&#8217;m looking for doesn&#8217;t appear anywhere in the makefile.  All of the rules look like this:
</p>
<p><pre style="font:80% courier, verdana, monospace;border:1px solid #ccc;width:90%;background:#fff7f0;color:#000;overflow:auto;margin:1em auto 2em;padding:1em;">$(CCCMD) $(CCCDLFLAGS) "-I$(PERL_INC)" $(PASTHRU_DEFINE) $(DEFINE) $*.c</pre>
</p>
<p>
There&#8217;s not a single useful literal string in sight, so <i>grep</i> is right out.  And it&#8217;s not like you could just start reading the makefile top-to-bottom to figure out which variables contribute to the command line that produces the output that you see in the log.  The makefile is nearly a thousand lines long with literally hundreds of variable definitions:
</p>
<p><pre style="font:80% courier, verdana, monospace;border:1px solid #ccc;width:90%;height:30ex;background:#fff7f0;color:#000;overflow:auto;margin:1em auto 2em;padding:1em;"># This Makefile is for the mylib extension to perl.
#
# It was generated automatically by MakeMaker version
# 6.42 (Revision: 41145) from the contents of
# Makefile.PL. Don't edit this file, edit Makefile.PL instead.
#
#       ANY CHANGES MADE HERE WILL BE LOST!
#
#   MakeMaker ARGV: ()
#
#   MakeMaker Parameters:

#     ABSTRACT_FROM =&gt; q[lib/mylib.pm]
#     AUTHOR =&gt; q[Eric Melski ]
#     DEFINE =&gt; q[]
#     INC =&gt; q[-I.]
#     LIBS =&gt; [q[]]
#     NAME =&gt; q[mylib]
#     PREREQ_PM =&gt; {  }
#     VERSION_FROM =&gt; q[lib/mylib.pm]

# --- MakeMaker post_initialize section:

# --- MakeMaker const_config section:

# These definitions are from config.sh (via /usr/lib/perl/5.10/Config.pm)

# They may have been overridden via Makefile.PL or on the command line
AR = ar
CC = gcc
CCCDLFLAGS = -fPIC
CCDLFLAGS = -Wl,-E
DLEXT = so
DLSRC = dl_dlopen.xs
EXE_EXT =
FULL_AR = /usr/bin/ar
LD = gcc
LDDLFLAGS = -shared -O2 -g -L/usr/local/lib
LDFLAGS =  -L/usr/local/lib
LIBC = /lib/libc-2.9.so
LIB_EXT = .a
OBJ_EXT = .o
OSNAME = linux
OSVERS = 2.6.24-23-server
RANLIB = :
SITELIBEXP = /usr/local/share/perl/5.10.0
SITEARCHEXP = /usr/local/lib/perl/5.10.0
SO = so
VENDORARCHEXP = /usr/lib/perl5
VENDORLIBEXP = /usr/share/perl5

# --- MakeMaker constants section:
AR_STATIC_ARGS = cr
DIRFILESEP = /
DFSEP = $(DIRFILESEP)
NAME = mylib
NAME_SYM = mylib
VERSION = 0.01
VERSION_MACRO = VERSION
VERSION_SYM = 0_01
DEFINE_VERSION = -D$(VERSION_MACRO)=\"$(VERSION)\"
XS_VERSION = 0.01
XS_VERSION_MACRO = XS_VERSION
XS_DEFINE_VERSION = -D$(XS_VERSION_MACRO)=\"$(XS_VERSION)\"
INST_ARCHLIB = blib/arch
INST_SCRIPT = blib/script
INST_BIN = blib/bin
INST_LIB = blib/lib
INST_MAN1DIR = blib/man1
INST_MAN3DIR = blib/man3
MAN1EXT = 1p
MAN3EXT = 3pm
INSTALLDIRS = site
DESTDIR =
PREFIX = /usr
PERLPREFIX = $(PREFIX)
SITEPREFIX = $(PREFIX)/local
VENDORPREFIX = $(PREFIX)
INSTALLPRIVLIB = $(PERLPREFIX)/share/perl/5.10
DESTINSTALLPRIVLIB = $(DESTDIR)$(INSTALLPRIVLIB)
INSTALLSITELIB = $(SITEPREFIX)/share/perl/5.10.0
DESTINSTALLSITELIB = $(DESTDIR)$(INSTALLSITELIB)
INSTALLVENDORLIB = $(VENDORPREFIX)/share/perl5
DESTINSTALLVENDORLIB = $(DESTDIR)$(INSTALLVENDORLIB)
INSTALLARCHLIB = $(PERLPREFIX)/lib/perl/5.10
DESTINSTALLARCHLIB = $(DESTDIR)$(INSTALLARCHLIB)
INSTALLSITEARCH = $(SITEPREFIX)/lib/perl/5.10.0
DESTINSTALLSITEARCH = $(DESTDIR)$(INSTALLSITEARCH)
INSTALLVENDORARCH = $(VENDORPREFIX)/lib/perl5
DESTINSTALLVENDORARCH = $(DESTDIR)$(INSTALLVENDORARCH)
INSTALLBIN = $(PERLPREFIX)/bin
DESTINSTALLBIN = $(DESTDIR)$(INSTALLBIN)
INSTALLSITEBIN = $(SITEPREFIX)/bin
DESTINSTALLSITEBIN = $(DESTDIR)$(INSTALLSITEBIN)
INSTALLVENDORBIN = $(VENDORPREFIX)/bin
DESTINSTALLVENDORBIN = $(DESTDIR)$(INSTALLVENDORBIN)
INSTALLSCRIPT = $(PERLPREFIX)/bin
DESTINSTALLSCRIPT = $(DESTDIR)$(INSTALLSCRIPT)
INSTALLSITESCRIPT = $(SITEPREFIX)/bin
DESTINSTALLSITESCRIPT = $(DESTDIR)$(INSTALLSITESCRIPT)
INSTALLVENDORSCRIPT = $(VENDORPREFIX)/bin
DESTINSTALLVENDORSCRIPT = $(DESTDIR)$(INSTALLVENDORSCRIPT)
INSTALLMAN1DIR = $(PERLPREFIX)/share/man/man1
DESTINSTALLMAN1DIR = $(DESTDIR)$(INSTALLMAN1DIR)
INSTALLSITEMAN1DIR = $(SITEPREFIX)/man/man1
DESTINSTALLSITEMAN1DIR = $(DESTDIR)$(INSTALLSITEMAN1DIR)
INSTALLVENDORMAN1DIR = $(VENDORPREFIX)/share/man/man1
DESTINSTALLVENDORMAN1DIR = $(DESTDIR)$(INSTALLVENDORMAN1DIR)
INSTALLMAN3DIR = $(PERLPREFIX)/share/man/man3
DESTINSTALLMAN3DIR = $(DESTDIR)$(INSTALLMAN3DIR)
INSTALLSITEMAN3DIR = $(SITEPREFIX)/man/man3
DESTINSTALLSITEMAN3DIR = $(DESTDIR)$(INSTALLSITEMAN3DIR)
INSTALLVENDORMAN3DIR = $(VENDORPREFIX)/share/man/man3
DESTINSTALLVENDORMAN3DIR = $(DESTDIR)$(INSTALLVENDORMAN3DIR)
PERL_LIB = /usr/share/perl/5.10
PERL_ARCHLIB = /usr/lib/perl/5.10
LIBPERL_A = libperl.a
FIRST_MAKEFILE = Makefile
MAKEFILE_OLD = Makefile.old
MAKE_APERL_FILE = Makefile.aperl
PERLMAINCC = $(CC)
PERL_INC = /usr/lib/perl/5.10/CORE
PERL = /usr/bin/perl
FULLPERL = /usr/bin/perl
ABSPERL = $(PERL)
PERLRUN = $(PERL)
FULLPERLRUN = $(FULLPERL)
ABSPERLRUN = $(ABSPERL)
PERLRUNINST = $(PERLRUN) "-I$(INST_ARCHLIB)" "-I$(INST_LIB)"
FULLPERLRUNINST = $(FULLPERLRUN) "-I$(INST_ARCHLIB)" "-I$(INST_LIB)"
ABSPERLRUNINST = $(ABSPERLRUN) "-I$(INST_ARCHLIB)" "-I$(INST_LIB)"
PERL_CORE = 0
PERM_RW = 644
PERM_RWX = 755

MAKEMAKER   = /usr/share/perl/5.10/ExtUtils/MakeMaker.pm
MM_VERSION  = 6.42
MM_REVISION = 41145

# FULLEXT = Pathname for extension directory (eg Foo/Bar/Oracle).
# BASEEXT = Basename part of FULLEXT. May be just equal FULLEXT. (eg Oracle)
# PARENT_NAME = NAME without BASEEXT and no trailing :: (eg Foo::Bar)
# DLBASE  = Basename part of dynamic library. May be just equal BASEEXT.
MAKE = make
FULLEXT = mylib
BASEEXT = mylib
PARENT_NAME =
DLBASE = $(BASEEXT)
VERSION_FROM = lib/mylib.pm
INC = -I.
DEFINE =
OBJECT = $(BASEEXT)$(OBJ_EXT)
LDFROM = $(OBJECT)
LINKTYPE = dynamic
BOOTDEP = 

# Handy lists of source code files:
XS_FILES = mylib.xs
C_FILES  = mylib.c
O_FILES  = mylib.o
H_FILES  = ppport.h
MAN1PODS =
MAN3PODS = lib/mylib.pm

# Where is the Config information that we are using/depend on
CONFIGDEP = $(PERL_ARCHLIB)$(DFSEP)Config.pm $(PERL_INC)$(DFSEP)config.h

# Where to build things
INST_LIBDIR      = $(INST_LIB)
INST_ARCHLIBDIR  = $(INST_ARCHLIB)

INST_AUTODIR     = $(INST_LIB)/auto/$(FULLEXT)
INST_ARCHAUTODIR = $(INST_ARCHLIB)/auto/$(FULLEXT)

INST_STATIC      = $(INST_ARCHAUTODIR)/$(BASEEXT)$(LIB_EXT)
INST_DYNAMIC     = $(INST_ARCHAUTODIR)/$(DLBASE).$(DLEXT)
INST_BOOT        = $(INST_ARCHAUTODIR)/$(BASEEXT).bs

# Extra linker info
EXPORT_LIST        =
PERL_ARCHIVE       =
PERL_ARCHIVE_AFTER = 

TO_INST_PM = lib/mylib.pm

PM_TO_BLIB = lib/mylib.pm \
	blib/lib/mylib.pm

# --- MakeMaker platform_constants section:
MM_Unix_VERSION = 6.42
PERL_MALLOC_DEF = -DPERL_EXTMALLOC_DEF -Dmalloc=Perl_malloc -Dfree=Perl_mfree -Drealloc=Perl_realloc -Dcalloc=Perl_calloc

# --- MakeMaker tool_autosplit section:
# Usage: $(AUTOSPLITFILE) FileToSplit AutoDirToSplitInto
AUTOSPLITFILE = $(ABSPERLRUN)  -e 'use AutoSplit;  autosplit($$ARGV[0], $$ARGV[1], 0, 1, 1)' --

# --- MakeMaker tool_xsubpp section:

XSUBPPDIR = /usr/share/perl/5.10/ExtUtils
XSUBPP = $(XSUBPPDIR)$(DFSEP)xsubpp
XSUBPPRUN = $(PERLRUN) $(XSUBPP)
XSPROTOARG =
XSUBPPDEPS = /usr/share/perl/5.10/ExtUtils/typemap typemap $(XSUBPP)
XSUBPPARGS = -typemap /usr/share/perl/5.10/ExtUtils/typemap -typemap typemap
XSUBPP_EXTRA_ARGS = 

# --- MakeMaker tools_other section:
SHELL = /bin/sh
CHMOD = chmod
CP = cp
MV = mv
NOOP = $(SHELL) -c true
NOECHO = @
RM_F = rm -f
RM_RF = rm -rf
TEST_F = test -f
TOUCH = touch
UMASK_NULL = umask 0
DEV_NULL = &gt; /dev/null 2&gt;&amp;1
MKPATH = $(ABSPERLRUN) "-MExtUtils::Command" -e mkpath
EQUALIZE_TIMESTAMP = $(ABSPERLRUN) "-MExtUtils::Command" -e eqtime
ECHO = echo
ECHO_N = echo -n
UNINST = 0
VERBINST = 0
MOD_INSTALL = $(ABSPERLRUN) -MExtUtils::Install -e 'install({@ARGV}, '\''$(VERBINST)'\'', 0, '\''$(UNINST)'\'');' --
DOC_INSTALL = $(ABSPERLRUN) "-MExtUtils::Command::MM" -e perllocal_install
UNINSTALL = $(ABSPERLRUN) "-MExtUtils::Command::MM" -e uninstall
WARN_IF_OLD_PACKLIST = $(ABSPERLRUN) "-MExtUtils::Command::MM" -e warn_if_old_packlist
MACROSTART =
MACROEND =
USEMAKEFILE = -f
FIXIN = $(PERLRUN) "-MExtUtils::MY" -e "MY-&gt;fixin(shift)"

# --- MakeMaker makemakerdflt section:
makemakerdflt : all
	$(NOECHO) $(NOOP)

# --- MakeMaker dist section:
TAR = tar
TARFLAGS = cvf
ZIP = zip
ZIPFLAGS = -r
COMPRESS = gzip --best
SUFFIX = .gz
SHAR = shar
PREOP = $(NOECHO) $(NOOP)
POSTOP = $(NOECHO) $(NOOP)
TO_UNIX = $(NOECHO) $(NOOP)
CI = ci -u
RCS_LABEL = rcs -Nv$(VERSION_SYM): -q
DIST_CP = best
DIST_DEFAULT = tardist
DISTNAME = mylib
DISTVNAME = mylib-0.01

# --- MakeMaker macro section:

# --- MakeMaker depend section:

# --- MakeMaker cflags section:

CCFLAGS = -D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
OPTIMIZE = -O2 -g
PERLTYPE =
MPOLLUTE = 

# --- MakeMaker const_loadlibs section:

# mylib might depend on some other libraries:
# See ExtUtils::Liblist for details
#

# --- MakeMaker const_cccmd section:
CCCMD = $(CC) -c $(PASTHRU_INC) $(INC) \
	$(CCFLAGS) $(OPTIMIZE) \
	$(PERLTYPE) $(MPOLLUTE) $(DEFINE_VERSION) \
	$(XS_DEFINE_VERSION)

# --- MakeMaker post_constants section:

# --- MakeMaker pasthru section:

PASTHRU = LIBPERL_A="$(LIBPERL_A)"\
	LINKTYPE="$(LINKTYPE)"\
	OPTIMIZE="$(OPTIMIZE)"\
	PREFIX="$(PREFIX)"\
	PASTHRU_DEFINE="$(PASTHRU_DEFINE)"\
	PASTHRU_INC="$(PASTHRU_INC)"

# --- MakeMaker special_targets section:
.SUFFIXES : .xs .c .C .cpp .i .s .cxx .cc $(OBJ_EXT)

.PHONY: all config static dynamic test linkext manifest blibdirs clean realclean disttest distdir

# --- MakeMaker c_o section:

.c.i:
	cc -E -c $(PASTHRU_INC) $(INC) \
	$(CCFLAGS) $(OPTIMIZE) \
	$(PERLTYPE) $(MPOLLUTE) $(DEFINE_VERSION) \
	$(XS_DEFINE_VERSION) $(CCCDLFLAGS) "-I$(PERL_INC)" $(PASTHRU_DEFINE) $(DEFINE) $*.c &gt; $*.i

.c.s:
	$(CCCMD) -S $(CCCDLFLAGS) "-I$(PERL_INC)" $(PASTHRU_DEFINE) $(DEFINE) $*.c

.c$(OBJ_EXT):
	$(CCCMD) $(CCCDLFLAGS) "-I$(PERL_INC)" $(PASTHRU_DEFINE) $(DEFINE) $*.c

.cpp$(OBJ_EXT):
	$(CCCMD) $(CCCDLFLAGS) "-I$(PERL_INC)" $(PASTHRU_DEFINE) $(DEFINE) $*.cpp

.cxx$(OBJ_EXT):
	$(CCCMD) $(CCCDLFLAGS) "-I$(PERL_INC)" $(PASTHRU_DEFINE) $(DEFINE) $*.cxx

.cc$(OBJ_EXT):
	$(CCCMD) $(CCCDLFLAGS) "-I$(PERL_INC)" $(PASTHRU_DEFINE) $(DEFINE) $*.cc

.C$(OBJ_EXT):
	$(CCCMD) $*.C

# --- MakeMaker xs_c section:

.xs.c:
	$(XSUBPPRUN) $(XSPROTOARG) $(XSUBPPARGS) $(XSUBPP_EXTRA_ARGS) $*.xs &gt; $*.xsc &amp;&amp; $(MV) $*.xsc $*.c

# --- MakeMaker xs_o section:

.xs$(OBJ_EXT):
	$(XSUBPPRUN) $(XSPROTOARG) $(XSUBPPARGS) $*.xs &gt; $*.xsc &amp;&amp; $(MV) $*.xsc $*.c
	$(CCCMD) $(CCCDLFLAGS) "-I$(PERL_INC)" $(PASTHRU_DEFINE) $(DEFINE) $*.c

# --- MakeMaker top_targets section:
all :: pure_all manifypods
	$(NOECHO) $(NOOP)

pure_all :: config pm_to_blib subdirs linkext
	$(NOECHO) $(NOOP)

subdirs :: $(MYEXTLIB)
	$(NOECHO) $(NOOP)

config :: $(FIRST_MAKEFILE) blibdirs
	$(NOECHO) $(NOOP)

$(O_FILES): $(H_FILES)

help :
	perldoc ExtUtils::MakeMaker

# --- MakeMaker blibdirs section:
blibdirs : $(INST_LIBDIR)$(DFSEP).exists $(INST_ARCHLIB)$(DFSEP).exists $(INST_AUTODIR)$(DFSEP).exists $(INST_ARCHAUTODIR)$(DFSEP).exists $(INST_BIN)$(DFSEP).exists $(INST_SCRIPT)$(DFSEP).exists $(INST_MAN1DIR)$(DFSEP).exists $(INST_MAN3DIR)$(DFSEP).exists
	$(NOECHO) $(NOOP)

# Backwards compat with 6.18 through 6.25
blibdirs.ts : blibdirs
	$(NOECHO) $(NOOP)

$(INST_LIBDIR)$(DFSEP).exists :: Makefile.PL
	$(NOECHO) $(MKPATH) $(INST_LIBDIR)
	$(NOECHO) $(CHMOD) 755 $(INST_LIBDIR)
	$(NOECHO) $(TOUCH) $(INST_LIBDIR)$(DFSEP).exists

$(INST_ARCHLIB)$(DFSEP).exists :: Makefile.PL
	$(NOECHO) $(MKPATH) $(INST_ARCHLIB)
	$(NOECHO) $(CHMOD) 755 $(INST_ARCHLIB)
	$(NOECHO) $(TOUCH) $(INST_ARCHLIB)$(DFSEP).exists

$(INST_AUTODIR)$(DFSEP).exists :: Makefile.PL
	$(NOECHO) $(MKPATH) $(INST_AUTODIR)
	$(NOECHO) $(CHMOD) 755 $(INST_AUTODIR)
	$(NOECHO) $(TOUCH) $(INST_AUTODIR)$(DFSEP).exists

$(INST_ARCHAUTODIR)$(DFSEP).exists :: Makefile.PL
	$(NOECHO) $(MKPATH) $(INST_ARCHAUTODIR)
	$(NOECHO) $(CHMOD) 755 $(INST_ARCHAUTODIR)
	$(NOECHO) $(TOUCH) $(INST_ARCHAUTODIR)$(DFSEP).exists

$(INST_BIN)$(DFSEP).exists :: Makefile.PL
	$(NOECHO) $(MKPATH) $(INST_BIN)
	$(NOECHO) $(CHMOD) 755 $(INST_BIN)
	$(NOECHO) $(TOUCH) $(INST_BIN)$(DFSEP).exists

$(INST_SCRIPT)$(DFSEP).exists :: Makefile.PL
	$(NOECHO) $(MKPATH) $(INST_SCRIPT)
	$(NOECHO) $(CHMOD) 755 $(INST_SCRIPT)
	$(NOECHO) $(TOUCH) $(INST_SCRIPT)$(DFSEP).exists

$(INST_MAN1DIR)$(DFSEP).exists :: Makefile.PL
	$(NOECHO) $(MKPATH) $(INST_MAN1DIR)
	$(NOECHO) $(CHMOD) 755 $(INST_MAN1DIR)
	$(NOECHO) $(TOUCH) $(INST_MAN1DIR)$(DFSEP).exists

$(INST_MAN3DIR)$(DFSEP).exists :: Makefile.PL
	$(NOECHO) $(MKPATH) $(INST_MAN3DIR)
	$(NOECHO) $(CHMOD) 755 $(INST_MAN3DIR)
	$(NOECHO) $(TOUCH) $(INST_MAN3DIR)$(DFSEP).exists

# --- MakeMaker linkext section:

linkext :: $(LINKTYPE)
	$(NOECHO) $(NOOP)

# --- MakeMaker dlsyms section:

# --- MakeMaker dynamic section:

dynamic :: $(FIRST_MAKEFILE) $(INST_DYNAMIC) $(INST_BOOT)
	$(NOECHO) $(NOOP)

# --- MakeMaker dynamic_bs section:
BOOTSTRAP = $(BASEEXT).bs

# As Mkbootstrap might not write a file (if none is required)
# we use touch to prevent make continually trying to remake it.
# The DynaLoader only reads a non-empty file.
$(BOOTSTRAP) : $(FIRST_MAKEFILE) $(BOOTDEP) $(INST_ARCHAUTODIR)$(DFSEP).exists
	$(NOECHO) $(ECHO) "Running Mkbootstrap for $(NAME) ($(BSLOADLIBS))"
	$(NOECHO) $(PERLRUN) \
		"-MExtUtils::Mkbootstrap" \
		-e "Mkbootstrap('$(BASEEXT)','$(BSLOADLIBS)');"
	$(NOECHO) $(TOUCH) $@
	$(CHMOD) $(PERM_RW) $@

$(INST_BOOT) : $(BOOTSTRAP) $(INST_ARCHAUTODIR)$(DFSEP).exists
	$(NOECHO) $(RM_RF) $@
	- $(CP) $(BOOTSTRAP) $@
	$(CHMOD) $(PERM_RW) $@

# --- MakeMaker dynamic_lib section:

# This section creates the dynamically loadable $(INST_DYNAMIC)
# from $(OBJECT) and possibly $(MYEXTLIB).
ARMAYBE = :
OTHERLDFLAGS =
INST_DYNAMIC_DEP =
INST_DYNAMIC_FIX = 

$(INST_DYNAMIC): $(OBJECT) $(MYEXTLIB) $(BOOTSTRAP) $(INST_ARCHAUTODIR)$(DFSEP).exists $(EXPORT_LIST) $(PERL_ARCHIVE) $(PERL_ARCHIVE_AFTER) $(INST_DYNAMIC_DEP)
	$(RM_F) $@
	$(LD)  $(LDDLFLAGS) $(LDFROM) $(OTHERLDFLAGS) -o $@ $(MYEXTLIB)	\
	  $(PERL_ARCHIVE) $(LDLOADLIBS) $(PERL_ARCHIVE_AFTER) $(EXPORT_LIST)	\
	  $(INST_DYNAMIC_FIX)
	$(CHMOD) $(PERM_RWX) $@

# --- MakeMaker static section:

## $(INST_PM) has been moved to the all: target.
## It remains here for awhile to allow for old usage: "make static"
static :: $(FIRST_MAKEFILE) $(INST_STATIC)
	$(NOECHO) $(NOOP)

# --- MakeMaker static_lib section:

$(INST_STATIC) : $(OBJECT) $(MYEXTLIB) $(INST_ARCHAUTODIR)$(DFSEP).exists
	$(RM_RF) $@
	$(FULL_AR) $(AR_STATIC_ARGS) $@ $(OBJECT) &amp;&amp; $(RANLIB) $@
	$(CHMOD) $(PERM_RWX) $@
	$(NOECHO) $(ECHO) "$(EXTRALIBS)" &gt; $(INST_ARCHAUTODIR)/extralibs.ld

# --- MakeMaker manifypods section:

POD2MAN_EXE = $(PERLRUN) "-MExtUtils::Command::MM" -e pod2man "--"
POD2MAN = $(POD2MAN_EXE)

manifypods : pure_all  \
	lib/mylib.pm
	$(NOECHO) $(POD2MAN) --section=$(MAN3EXT) --perm_rw=$(PERM_RW) \
	  lib/mylib.pm $(INST_MAN3DIR)/mylib.$(MAN3EXT) 

# --- MakeMaker processPL section:

# --- MakeMaker installbin section:

# --- MakeMaker subdirs section:

# none

# --- MakeMaker clean_subdirs section:
clean_subdirs :
	$(NOECHO) $(NOOP)

# --- MakeMaker clean section:

# Delete temporary files but do not touch installed files. We don't delete
# the Makefile here so a later make realclean still has a makefile to use.

clean :: clean_subdirs
	- $(RM_F) \
	  *$(LIB_EXT) core \
	  core.[0-9] $(INST_ARCHAUTODIR)/extralibs.all \
	  core.[0-9][0-9] $(BASEEXT).bso \
	  pm_to_blib.ts core.[0-9][0-9][0-9][0-9] \
	  $(BASEEXT).x $(BOOTSTRAP) \
	  perl$(EXE_EXT) tmon.out \
	  *$(OBJ_EXT) pm_to_blib \
	  $(INST_ARCHAUTODIR)/extralibs.ld blibdirs.ts \
	  core.[0-9][0-9][0-9][0-9][0-9] mylib.c \
	  *perl.core core.*perl.*.? \
	  $(MAKE_APERL_FILE) $(BASEEXT).def \
	  perl core.[0-9][0-9][0-9] \
	  mon.out lib$(BASEEXT).def \
	  perlmain.c perl.exe \
	  so_locations $(BASEEXT).exp
	- $(RM_RF) \
	  blib
	- $(MV) $(FIRST_MAKEFILE) $(MAKEFILE_OLD) $(DEV_NULL)

# --- MakeMaker realclean_subdirs section:
realclean_subdirs :
	$(NOECHO) $(NOOP)

# --- MakeMaker realclean section:
# Delete temporary files (via clean) and also delete dist files
realclean purge ::  clean realclean_subdirs
	- $(RM_F) \
	  $(OBJECT) $(MAKEFILE_OLD) \
	  $(FIRST_MAKEFILE)
	- $(RM_RF) \
	  $(DISTVNAME) 

# --- MakeMaker metafile section:
metafile : create_distdir
	$(NOECHO) $(ECHO) Generating META.yml
	$(NOECHO) $(ECHO) '--- #YAML:1.0' &gt; META_new.yml
	$(NOECHO) $(ECHO) 'name:                mylib' &gt;&gt; META_new.yml
	$(NOECHO) $(ECHO) 'version:             0.01' &gt;&gt; META_new.yml
	$(NOECHO) $(ECHO) 'abstract:            Perl extension for blah blah blah' &gt;&gt; META_new.yml
	$(NOECHO) $(ECHO) 'license:             ~' &gt;&gt; META_new.yml
	$(NOECHO) $(ECHO) 'author:              ' &gt;&gt; META_new.yml
	$(NOECHO) $(ECHO) '    - Eric Melski ' &gt;&gt; META_new.yml
	$(NOECHO) $(ECHO) 'generated_by:        ExtUtils::MakeMaker version 6.42' &gt;&gt; META_new.yml
	$(NOECHO) $(ECHO) 'distribution_type:   module' &gt;&gt; META_new.yml
	$(NOECHO) $(ECHO) 'requires:     ' &gt;&gt; META_new.yml
	$(NOECHO) $(ECHO) 'meta-spec:' &gt;&gt; META_new.yml
	$(NOECHO) $(ECHO) '    url:     http://module-build.sourceforge.net/META-spec-v1.3.html' &gt;&gt; META_new.yml
	$(NOECHO) $(ECHO) '    version: 1.3' &gt;&gt; META_new.yml
	-$(NOECHO) $(MV) META_new.yml $(DISTVNAME)/META.yml

# --- MakeMaker signature section:
signature :
	cpansign -s

# --- MakeMaker dist_basics section:
distclean :: realclean distcheck
	$(NOECHO) $(NOOP)

distcheck :
	$(PERLRUN) "-MExtUtils::Manifest=fullcheck" -e fullcheck

skipcheck :
	$(PERLRUN) "-MExtUtils::Manifest=skipcheck" -e skipcheck

manifest :
	$(PERLRUN) "-MExtUtils::Manifest=mkmanifest" -e mkmanifest

veryclean : realclean
	$(RM_F) *~ */*~ *.orig */*.orig *.bak */*.bak *.old */*.old 

# --- MakeMaker dist_core section:

dist : $(DIST_DEFAULT) $(FIRST_MAKEFILE)
	$(NOECHO) $(ABSPERLRUN) -l -e 'print '\''Warning: Makefile possibly out of date with $(VERSION_FROM)'\''' \
	  -e '    if -e '\''$(VERSION_FROM)'\'' and -M '\''$(VERSION_FROM)'\''  $(DISTVNAME).tar$(SUFFIX)_uu

$(DISTVNAME).tar$(SUFFIX) : distdir
	$(PREOP)
	$(TO_UNIX)
	$(TAR) $(TARFLAGS) $(DISTVNAME).tar $(DISTVNAME)
	$(RM_RF) $(DISTVNAME)
	$(COMPRESS) $(DISTVNAME).tar
	$(POSTOP)

zipdist : $(DISTVNAME).zip
	$(NOECHO) $(NOOP)

$(DISTVNAME).zip : distdir
	$(PREOP)
	$(ZIP) $(ZIPFLAGS) $(DISTVNAME).zip $(DISTVNAME)
	$(RM_RF) $(DISTVNAME)
	$(POSTOP)

shdist : distdir
	$(PREOP)
	$(SHAR) $(DISTVNAME) &gt; $(DISTVNAME).shar
	$(RM_RF) $(DISTVNAME)
	$(POSTOP)

# --- MakeMaker distdir section:
create_distdir :
	$(RM_RF) $(DISTVNAME)
	$(PERLRUN) "-MExtUtils::Manifest=manicopy,maniread" \
		-e "manicopy(maniread(),'$(DISTVNAME)', '$(DIST_CP)');"

distdir : create_distdir distmeta
	$(NOECHO) $(NOOP)

# --- MakeMaker dist_test section:
disttest : distdir
	cd $(DISTVNAME) &amp;&amp; $(ABSPERLRUN) Makefile.PL
	cd $(DISTVNAME) &amp;&amp; $(MAKE) $(PASTHRU)
	cd $(DISTVNAME) &amp;&amp; $(MAKE) test $(PASTHRU)

# --- MakeMaker dist_ci section:

ci :
	$(PERLRUN) "-MExtUtils::Manifest=maniread" \
	  -e "@all = keys %{ maniread() };" \
	  -e "print(qq{Executing $(CI) @all\n}); system(qq{$(CI) @all});" \
	  -e "print(qq{Executing $(RCS_LABEL) ...\n}); system(qq{$(RCS_LABEL) @all});"

# --- MakeMaker distmeta section:
distmeta : create_distdir metafile
	$(NOECHO) cd $(DISTVNAME) &amp;&amp; $(ABSPERLRUN) -MExtUtils::Manifest=maniadd -e 'eval { maniadd({q{META.yml} =&gt; q{Module meta-data (added by MakeMaker)}}) } ' \
	  -e '    or print "Could not add META.yml to MANIFEST: $${'\''@'\''}\n"' --

# --- MakeMaker distsignature section:
distsignature : create_distdir
	$(NOECHO) cd $(DISTVNAME) &amp;&amp; $(ABSPERLRUN) -MExtUtils::Manifest=maniadd -e 'eval { maniadd({q{SIGNATURE} =&gt; q{Public-key signature (added by MakeMaker)}}) } ' \
	  -e '    or print "Could not add SIGNATURE to MANIFEST: $${'\''@'\''}\n"' --
	$(NOECHO) cd $(DISTVNAME) &amp;&amp; $(TOUCH) SIGNATURE
	cd $(DISTVNAME) &amp;&amp; cpansign -s

# --- MakeMaker install section:

install :: pure_install doc_install
	$(NOECHO) $(NOOP)

install_perl :: pure_perl_install doc_perl_install
	$(NOECHO) $(NOOP)

install_site :: pure_site_install doc_site_install
	$(NOECHO) $(NOOP)

install_vendor :: pure_vendor_install doc_vendor_install
	$(NOECHO) $(NOOP)

pure_install :: pure_$(INSTALLDIRS)_install
	$(NOECHO) $(NOOP)

doc_install :: doc_$(INSTALLDIRS)_install
	$(NOECHO) $(NOOP)

pure__install : pure_site_install
	$(NOECHO) $(ECHO) INSTALLDIRS not defined, defaulting to INSTALLDIRS=site

doc__install : doc_site_install
	$(NOECHO) $(ECHO) INSTALLDIRS not defined, defaulting to INSTALLDIRS=site

pure_perl_install :: all
	$(NOECHO) umask 022; $(MOD_INSTALL) \
		$(INST_LIB) $(DESTINSTALLPRIVLIB) \
		$(INST_ARCHLIB) $(DESTINSTALLARCHLIB) \
		$(INST_BIN) $(DESTINSTALLBIN) \
		$(INST_SCRIPT) $(DESTINSTALLSCRIPT) \
		$(INST_MAN1DIR) $(DESTINSTALLMAN1DIR) \
		$(INST_MAN3DIR) $(DESTINSTALLMAN3DIR)
	$(NOECHO) $(WARN_IF_OLD_PACKLIST) \
		$(SITEARCHEXP)/auto/$(FULLEXT)

pure_site_install :: all
	$(NOECHO) umask 02; $(MOD_INSTALL) \
		read $(SITEARCHEXP)/auto/$(FULLEXT)/.packlist \
		write $(DESTINSTALLSITEARCH)/auto/$(FULLEXT)/.packlist \
		$(INST_LIB) $(DESTINSTALLSITELIB) \
		$(INST_ARCHLIB) $(DESTINSTALLSITEARCH) \
		$(INST_BIN) $(DESTINSTALLSITEBIN) \
		$(INST_SCRIPT) $(DESTINSTALLSITESCRIPT) \
		$(INST_MAN1DIR) $(DESTINSTALLSITEMAN1DIR) \
		$(INST_MAN3DIR) $(DESTINSTALLSITEMAN3DIR)
	$(NOECHO) $(WARN_IF_OLD_PACKLIST) \
		$(PERL_ARCHLIB)/auto/$(FULLEXT)

pure_vendor_install :: all
	$(NOECHO) umask 022; $(MOD_INSTALL) \
		$(INST_LIB) $(DESTINSTALLVENDORLIB) \
		$(INST_ARCHLIB) $(DESTINSTALLVENDORARCH) \
		$(INST_BIN) $(DESTINSTALLVENDORBIN) \
		$(INST_SCRIPT) $(DESTINSTALLVENDORSCRIPT) \
		$(INST_MAN1DIR) $(DESTINSTALLVENDORMAN1DIR) \
		$(INST_MAN3DIR) $(DESTINSTALLVENDORMAN3DIR)

doc_perl_install :: all

doc_site_install :: all
	$(NOECHO) $(ECHO) Appending installation info to $(DESTINSTALLSITEARCH)/perllocal.pod
	-$(NOECHO) umask 02; $(MKPATH) $(DESTINSTALLSITEARCH)
	-$(NOECHO) umask 02; $(DOC_INSTALL) \
		"Module" "$(NAME)" \
		"installed into" "$(INSTALLSITELIB)" \
		LINKTYPE "$(LINKTYPE)" \
		VERSION "$(VERSION)" \
		EXE_FILES "$(EXE_FILES)" \
		&gt;&gt; $(DESTINSTALLSITEARCH)/perllocal.pod

doc_vendor_install :: all

uninstall :: uninstall_from_$(INSTALLDIRS)dirs
	$(NOECHO) $(NOOP)

uninstall_from_perldirs ::

uninstall_from_sitedirs ::
	$(NOECHO) $(UNINSTALL) $(SITEARCHEXP)/auto/$(FULLEXT)/.packlist

uninstall_from_vendordirs ::

# --- MakeMaker force section:
# Phony target to force checking subdirectories.
FORCE :
	$(NOECHO) $(NOOP)

# --- MakeMaker perldepend section:

PERL_HDRS = \
	$(PERL_INC)/EXTERN.h		\
	$(PERL_INC)/INTERN.h		\
	$(PERL_INC)/XSUB.h		\
	$(PERL_INC)/av.h		\
	$(PERL_INC)/cc_runtime.h	\
	$(PERL_INC)/config.h		\
	$(PERL_INC)/cop.h		\
	$(PERL_INC)/cv.h		\
	$(PERL_INC)/dosish.h		\
	$(PERL_INC)/embed.h		\
	$(PERL_INC)/embedvar.h		\
	$(PERL_INC)/fakethr.h		\
	$(PERL_INC)/form.h		\
	$(PERL_INC)/gv.h		\
	$(PERL_INC)/handy.h		\
	$(PERL_INC)/hv.h		\
	$(PERL_INC)/intrpvar.h		\
	$(PERL_INC)/iperlsys.h		\
	$(PERL_INC)/keywords.h		\
	$(PERL_INC)/mg.h		\
	$(PERL_INC)/nostdio.h		\
	$(PERL_INC)/op.h		\
	$(PERL_INC)/opcode.h		\
	$(PERL_INC)/patchlevel.h	\
	$(PERL_INC)/perl.h		\
	$(PERL_INC)/perlio.h		\
	$(PERL_INC)/perlsdio.h		\
	$(PERL_INC)/perlsfio.h		\
	$(PERL_INC)/perlvars.h		\
	$(PERL_INC)/perly.h		\
	$(PERL_INC)/pp.h		\
	$(PERL_INC)/pp_proto.h		\
	$(PERL_INC)/proto.h		\
	$(PERL_INC)/regcomp.h		\
	$(PERL_INC)/regexp.h		\
	$(PERL_INC)/regnodes.h		\
	$(PERL_INC)/scope.h		\
	$(PERL_INC)/sv.h		\
	$(PERL_INC)/thread.h		\
	$(PERL_INC)/unixish.h		\
	$(PERL_INC)/util.h

$(OBJECT) : $(PERL_HDRS)

mylib.c : $(XSUBPPDEPS)

# --- MakeMaker makefile section:

$(OBJECT) : $(FIRST_MAKEFILE)

# We take a very conservative approach here, but it's worth it.
# We move Makefile to Makefile.old here to avoid gnu make looping.
$(FIRST_MAKEFILE) : Makefile.PL $(CONFIGDEP)
	$(NOECHO) $(ECHO) "Makefile out-of-date with respect to $?"
	$(NOECHO) $(ECHO) "Cleaning current config before rebuilding Makefile..."
	-$(NOECHO) $(RM_F) $(MAKEFILE_OLD)
	-$(NOECHO) $(MV)   $(FIRST_MAKEFILE) $(MAKEFILE_OLD)
	- $(MAKE) $(USEMAKEFILE) $(MAKEFILE_OLD) clean $(DEV_NULL)
	$(PERLRUN) Makefile.PL
	$(NOECHO) $(ECHO) "==&gt; Your Makefile has been rebuilt.  Please rerun the $(MAKE) command.  &lt;==&quot;
	false

# --- MakeMaker staticmake section:

# --- MakeMaker makeaperl section ---
MAP_TARGET    = perl
FULLPERL      = /usr/bin/perl

$(MAP_TARGET) :: static $(MAKE_APERL_FILE)
	$(MAKE) $(USEMAKEFILE) $(MAKE_APERL_FILE) $@

$(MAKE_APERL_FILE) : $(FIRST_MAKEFILE) pm_to_blib
	$(NOECHO) $(ECHO) Writing \&quot;$(MAKE_APERL_FILE)\&quot; for this $(MAP_TARGET)
	$(NOECHO) $(PERLRUNINST) \
		Makefile.PL DIR= \
		MAKEFILE=$(MAKE_APERL_FILE) LINKTYPE=static \
		MAKEAPERL=1 NORECURS=1 CCCDLFLAGS=

# --- MakeMaker test section:

TEST_VERBOSE=0
TEST_TYPE=test_$(LINKTYPE)
TEST_FILE = test.pl
TEST_FILES = t/*.t
TESTDB_SW = -d

testdb :: testdb_$(LINKTYPE)

test :: $(TEST_TYPE) subdirs-test

subdirs-test ::
	$(NOECHO) $(NOOP)

test_dynamic :: pure_all
	PERL_DL_NONLAZY=1 $(FULLPERLRUN) &quot;-MExtUtils::Command::MM&quot; &quot;-e&quot; &quot;test_harness($(TEST_VERBOSE), &#39;$(INST_LIB)&#39;, &#39;$(INST_ARCHLIB)&#39;)&quot; $(TEST_FILES)

testdb_dynamic :: pure_all
	PERL_DL_NONLAZY=1 $(FULLPERLRUN) $(TESTDB_SW) &quot;-I$(INST_LIB)&quot; &quot;-I$(INST_ARCHLIB)&quot; $(TEST_FILE)

test_ : test_dynamic

test_static :: pure_all $(MAP_TARGET)
	PERL_DL_NONLAZY=1 ./$(MAP_TARGET) &quot;-MExtUtils::Command::MM&quot; &quot;-e&quot; &quot;test_harness($(TEST_VERBOSE), &#39;$(INST_LIB)&#39;, &#39;$(INST_ARCHLIB)&#39;)&quot; $(TEST_FILES)

testdb_static :: pure_all $(MAP_TARGET)
	PERL_DL_NONLAZY=1 ./$(MAP_TARGET) $(TESTDB_SW) &quot;-I$(INST_LIB)&quot; &quot;-I$(INST_ARCHLIB)&quot; $(TEST_FILE)

# --- MakeMaker ppd section:
# Creates a PPD (Perl Package Description) for a binary distribution.
ppd :
	$(NOECHO) $(ECHO) &#39;&lt;SOFTPKG NAME=&quot;$(DISTNAME)&quot; VERSION=&quot;0,01,0,0&quot;&gt;&#39; &gt; $(DISTNAME).ppd
	$(NOECHO) $(ECHO) &#39;    &lt;TITLE&gt;$(DISTNAME)&lt;/TITLE&gt;&#39; &gt;&gt; $(DISTNAME).ppd
	$(NOECHO) $(ECHO) &#39;    &lt;ABSTRACT&gt;Perl extension for blah blah blah&lt;/ABSTRACT&gt;&#39; &gt;&gt; $(DISTNAME).ppd
	$(NOECHO) $(ECHO) &#39;    &lt;AUTHOR&gt;Eric Melski &lt;ericm@&gt;&lt;/AUTHOR&gt;&#39; &gt;&gt; $(DISTNAME).ppd
	$(NOECHO) $(ECHO) &#39;    &lt;IMPLEMENTATION&gt;&#39; &gt;&gt; $(DISTNAME).ppd
	$(NOECHO) $(ECHO) &#39;        &lt;OS NAME=&quot;$(OSNAME)&quot; /&gt;&#39; &gt;&gt; $(DISTNAME).ppd
	$(NOECHO) $(ECHO) &#39;        &lt;ARCHITECTURE NAME=&quot;i486-linux-gnu-thread-multi-5.1&quot; /&gt;&#39; &gt;&gt; $(DISTNAME).ppd
	$(NOECHO) $(ECHO) &#39;        &lt;CODEBASE HREF=&quot;&quot; /&gt;&#39; &gt;&gt; $(DISTNAME).ppd
	$(NOECHO) $(ECHO) &#39;    &lt;/IMPLEMENTATION&gt;&#39; &gt;&gt; $(DISTNAME).ppd
	$(NOECHO) $(ECHO) &#39;&lt;/SOFTPKG&gt;&#39; &gt;&gt; $(DISTNAME).ppd

# --- MakeMaker pm_to_blib section:

pm_to_blib : $(TO_INST_PM)
	$(NOECHO) $(ABSPERLRUN) -MExtUtils::Install -e &#39;pm_to_blib({@ARGV}, &#39;\&#39;&#39;$(INST_LIB)/auto&#39;\&#39;&#39;, &#39;\&#39;&#39;$(PM_FILTER)&#39;\&#39;&#39;)&#39; -- \
	  lib/mylib.pm blib/lib/mylib.pm
	$(NOECHO) $(TOUCH) pm_to_blib

# --- MakeMaker selfdocument section:

# --- MakeMaker postamble section:

# End.
</pre>
</p>
<p>
It seems hopeless.  This makefile suffers from a classic problem:  unless you&#8217;re the guy who wrote it in the first place, it&#8217;s an impenetrable fog.  You&#8217;re in for a world of hurt when you try to understand it, or worse, try to modify it.  Lucky for me &mdash; and for you if you ever find yourself in this situation &mdash; there&#8217;s a powerful new tool in your toolbox:  emake&#8217;s annotated build logs, or simply <i>annotation</i>.
</p>
<p>
If you have ElectricAccelerator or <a href="http://www.sparkbuild.com/">SparkBuild</a>, a free gmake- and NMAKE-compatible make replacement, you just need to enable annotation on your next build:</p>
<pre style="font:80% courier, verdana, monospace;border:1px solid #ccc;width:90%;background:#fff7f0;color:#000;overflow:auto;margin:1em auto 2em;padding:1em;">emake --emake-annodetail=basic &gt; build.xml 2&gt;&amp;1</pre>
</p>
<p>
Now you can search <tt>build.xml</tt> for the log output or the command-line that you&#8217;re interested in.  You can use ElectricInsight or SparkBuild Insight, of course, but for a simple query like this I just use <i>less</i>.  Searching for the log output of the command I&#8217;m looking for (<i>gcc -shared</i>) leads me to this:
</p>
<p><pre style="font:80% courier, verdana, monospace;border:1px solid #ccc;width:90%;background:#fff7f0;color:#000;overflow:auto;margin:1em auto 2em;padding:1em;">...
&lt;job id="J09be71b8" thread="b7b34940" type="rule" name="blib/arch/auto/mylib/mylib.so" file="Makefile" line="469" neededby="J09be7138"&gt;
&lt;output&gt;&lt;![CDATA[]]&gt;&lt;/output&gt;
&lt;command line="470"&gt;
&lt;argv&gt;&lt;![CDATA[rm -f blib/arch/auto/mylib/mylib.so]]&gt;&lt;/argv&gt;
&lt;output&gt;&lt;![CDATA[rm -f blib/arch/auto/mylib/mylib.so
]]&gt;&lt;/output&gt;
&lt;output src="prog"&gt;&lt;![CDATA[]]&gt;&lt;/output&gt;
&lt;/command&gt;
&lt;command line="471-473"&gt;
&lt;argv&gt;&lt;![CDATA[<font color="red"><b>gcc  -shared </b></font>-O2 -g -L/usr/local/lib mylib.o  -o blib/arch/auto/mylib/mylib.so      \
                \
          ]]&gt;&lt;/argv&gt;
&lt;output&gt;&lt;![CDATA[<font color="red"><b>gcc  -shared </b></font>-O2 -g -L/usr/local/lib mylib.o  -o blib/arch/auto/mylib/mylib.so    \
                \

]]&gt;&lt;/output&gt;
&lt;output src="prog"&gt;&lt;![CDATA[]]&gt;&lt;/output&gt;
&lt;/command&gt;
...</pre>
</p>
<p>
Now, I just scan back a few lines to find the start of the <i>&lt;job&gt;</i> tag containing that text.  The <i>file</i> attribute tells me which makefile contains the rule definition, and the <i>line</i> attribute tells me which line it starts on:
</p>
<p><pre style="font:80% courier, verdana, monospace;border:1px solid #ccc;width:90%;background:#fff7f0;color:#000;overflow:auto;margin:1em auto 2em;padding:1em;">...
&lt;job id="J09be71b8" thread="b7b34940" type="rule" name="blib/arch/auto/mylib/mylib.so" <font color="red"><b>file="Makefile" line="469"</b></font> neededby="J09be7138"&gt;
...</pre>
</p>
<p>
<b>Bingo!</b>  The rule I want starts on line 469 of the file <i>Makefile</i>.  Using that as an anchor it&#8217;s now trivial to see how to modify the makefile to produce the result I&#8217;m after.
</p>
<p><h3>The best tool for the job</h3>
</p>
<p>
Trying to figure out how a build system is put together can feel like being dropped in the middle of a dense jungle.  Annotated build logs are like a map leading you back to daylight.  There&#8217;s nothing else like them.</p>
<br />Posted in ElectricAccelerator, SparkBuild Tagged: annotation, ElectricAccelerator, makefile, SparkBuild <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/ecloud.wordpress.com/610/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/ecloud.wordpress.com/610/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/ecloud.wordpress.com/610/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/ecloud.wordpress.com/610/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/ecloud.wordpress.com/610/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/ecloud.wordpress.com/610/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/ecloud.wordpress.com/610/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/ecloud.wordpress.com/610/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/ecloud.wordpress.com/610/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/ecloud.wordpress.com/610/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/ecloud.wordpress.com/610/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/ecloud.wordpress.com/610/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/ecloud.wordpress.com/610/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/ecloud.wordpress.com/610/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.electric-cloud.com&amp;blog=5211544&amp;post=610&amp;subd=ecloud&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://blog.electric-cloud.com/2009/11/10/how-to-quickly-navigate-an-unfamiliar-makefile/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6344c5478a4b5f0e8c546c736b2a7e0d?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=PG" medium="image">
			<media:title type="html">ericm</media:title>
		</media:content>
	</item>
	</channel>
</rss>