<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Mere Idea Development Blog</title>
	
	<link>http://www.mereidea.com/blog</link>
	<description>Setting up a development environment.</description>
	<lastBuildDate>Fri, 30 Dec 2011 11:50:34 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.3</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/MereIdeaDevelopmentBlog" /><feedburner:info uri="mereideadevelopmentblog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>MereIdeaDevelopmentBlog</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><item>
		<title>Using JUnit for testing in Java/SCons builds</title>
		<link>http://feedproxy.google.com/~r/MereIdeaDevelopmentBlog/~3/jLTiAfVZ-S0/</link>
		<comments>http://www.mereidea.com/blog/2011/12/30/using-junit-for-testing-in-javascons-builds/#comments</comments>
		<pubDate>Fri, 30 Dec 2011 11:50:34 +0000</pubDate>
		<dc:creator>chris</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[SCons]]></category>
		<category><![CDATA[Testing]]></category>
		<category><![CDATA[JUnit]]></category>
		<category><![CDATA[test suites]]></category>
		<category><![CDATA[tests]]></category>

		<guid isPermaLink="false">http://www.mereidea.com/blog/?p=1536</guid>
		<description><![CDATA[This post adds testing to some existing Java code using JUnit. It includes information on how JUnit was installed and tested on Windows and Linux, how tests were added for existing code, how test suites (including a hierarchy of suites) were added, and how the code was built with SCons and the tests run.]]></description>
			<content:encoded><![CDATA[<h2><strong>Current repository revision: 60</strong> (<a href="http://www.mereidea.com/svn/testing/trunk/" target="_blank">Testing</a>)</h2>
<blockquote><p><span style="text-decoration: underline;">Code highlight key</span></p>
<p><span style="color: #808080;">Grey: Code unchanged</span><br />
<span style="color: #ff0000;">Red: Code removed</span><br />
<span style="color: #008000;">Green: Code added</span><br />
<span style="color: #3366ff;">Blue: Code not shown</span></p>
<p><span style="color: #3366ff;"><span style="color: #000000;">→ Denotes the current line and the next are really one line</span></span></p>
</blockquote>
<p>The code can be checked out with:</p>
<pre>
svn checkout http://www.mereidea.com/svn/testing/trunk/mi_java_scons
</pre>
<hr style="width: 50%" />
Now that the initial <a href="http://www.java.com/" target="_blank" title="Java computing platform">Java</a> / <a href="http://www.scons.org" target="_blank" title="SCons build tool">SCons</a> code is in place, some tests can be  added. The framework to be used for testing our <a href="http://www.java.com/" target="_blank" title="Java computing platform">Java</a> code is <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a>.</p>
<p>This is not really a tutorial on <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a>, but will cover how we installed <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> on both Linux and Windows, how we tested the installations with a simple test, how we added <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> based tests and suites into our existing code, how the tests were built with <a href="http://www.scons.org" target="_blank" title="SCons build tool">SCons</a>, and how they were run once built.<br />
&nbsp;</p>
<h2>Installing JUnit</h2>
<p>We want to be able to use <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> on both Linux and Windows environments, and so it was installed on both.</p>
<h3>Getting JUnit</h3>
<p>At the time of writing, <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> can be downloaded from <a href="https://github.com/KentBeck/junit/downloads" target="_blank" title="JUnit download">github</a>. We just downloaded the <strong>Basic jar</strong>. The version at writing was <strong>junit-4.10.jar</strong>.</p>
<h3>Linux setup</h3>
<ol>
<li>Download the <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> <em>jar</em> file.</li>
<li>Create a directory to put it in &#8211; this example assumes <em>/usr/share/junit/</em>.</li>
<li>Copy the <em>jar</em> file into the newly created directory.</li>
<li>Optionally rename the jar from, for example, <strong>junit-4.10.jar</strong> to <strong>junit.jar</strong></li>
<li>Edit <strong>.bashrc</strong> or whatever shell initialization file works for you, and add
<pre>
export CLASSPATH=${CLASSPATH}:/usr/share/junit/junit.jar
</pre>
<p>or whatever line your shell requires to add the directory and file name of the <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> <em>jar</em> to your class path.</li>
<li>Save the <strong>.bashrc</strong> file, re-source it (<strong>source .bashrc</strong>), or open a new shell, and you&#8217;re ready to go!</li>
</ol>
<h3>Windows setup</h3>
<ol>
<li>Download the <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> <em>jar</em> file.</li>
<li>Create a directory to put it in &#8211; this example assumes <em>C:\junit\</em>.</li>
<li>Copy the <em>jar</em> file into the newly created directory.</li>
<li>Optionally rename the jar from, for example, <strong>junit-4.10.jar</strong> to <strong>junit.jar</strong></li>
<li>On Vista/Windows7 click the <strong>Windows (Start)</strong> button, right click on <strong>Computer</strong> and select <strong>Properties</strong>. On the window that pops up, select <strong>Advanced system settings</strong> and, finally, in the dialog that appears click <strong>Environment Variables&#8230;</strong>.</li>
<li>On Windows XP click the <strong>Start</strong> button, right click on <strong>My Computer</strong> and select <strong>Properties</strong>. In the dialog that pops up select the <strong>Advanced</strong> tab then click the <strong>Environment Variables</strong> button.</li>
<li>If the <em>CLASSPATH</em> variable already exists in your <strong>System Variables</strong>, then select it and click <strong>Edit</strong>. If it doesn&#8217;t exist, click <strong>New</strong>.</li>
<li>Either add or enter the path to your <strong>junit.jar</strong> file (<em>C:\junit\junit.jar</em>)</li>
<li>Because, on Windows, if the <em>CLASSPATH</em> variable is set, the <strong>java</strong> command seems to ignore the current directory when searching for classes, ensure <strong>.</strong> is also in the <em>CLASSPATH</em> variable. For example, our <em>CLASSPATH</em> is:
<pre>
.;C:\junit\junit.jar
</pre>
</li>
<li>Click <strong>OK</strong>, close the dialogs, open a new command prompt, and you&#8217;re ready to go!</li>
</ol>
<p>&nbsp;</p>
<h2>Using JUnit 4</h2>
<p>The version of <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> we have installed is version 4.10. Before <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> 4, tests had to extend the <em>TestCase</em> class, and the methods had to have names beginning with <em>test</em>. We use the <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> 4 annotation methods instead of the inherited <em>TestCase</em> as it seems much simpler and much neater to us. Below the same simple test is written to show both methods of creating tests. The class under test for both is:</p>
<pre>
public class StringUtils
{
  public String concatenateWithSpace(String s1, String s2)
  {
     return s1+" "+s2;
  }
}
</pre>
<h3>Before JUnit 4</h3>
<p>As mentioned above, before <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> 4, the test class for the <strong>StringUtils</strong> needed to be inherited from <em>TestCase</em>. This can be written:</p>
<pre>
import junit.framework.*;

public class TestStringUtils extends TestCase
{
  private StringUtils str_utils;

  protected void setUp()
  {
    str_utils = new StringUtils();
  }

  public void testConcatenateWithSpace()
  {
    String s1 = "Test";
    String s2 = "me!";
    String expected = "Test me!";
    String actual = str_utils.concatenateWithSpace(s1,s2);
    assertEquals(actual, expected);
  }
}
</pre>
<p>Compile this with:</p>
<pre>
javac StringUtils.java TestStringUtils.java
</pre>
<p>then you can run the test with:</p>
<pre>
java junit.textui.TestRunner TestStringUtils
</pre>
<h3>With JUnit 4</h3>
<p><a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> 4 enables you to define your tests using annotations. Below is the same test case as before, but rewritten to use <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> 4. The annotation <strong>@Before</strong> identifies the <em>setUp</em> method to be called before each test, and the annotation <strong>@Test</strong> identifies actual tests:</p>
<pre>
import org.junit.Test;
import org.junit.Before;
import static org.junit.Assert.assertEquals;

public class TestStringUtils
{
  private StringUtils str_utils;

  @Before
  public void setUpStringUtils()
  {
    str_utils = new StringUtils();
  }

  @Test
  public void concatenateWithSpace()
  {
    String s1 = "Test";
    String s2 = "me!";
    String expected = "Test me!";
    String actual = str_utils.concatenateWithSpace(s1,s2);
    assertEquals(actual, expected);
  }
}
</pre>
<p>Once compiled (as above), this code can be run with:</p>
<pre>
java org.junit.runner.JUnitCore TestStringUtils
</pre>
<blockquote><p><strong>Note:</strong> With the <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> 4 <strong>jar</strong> you can use either method for writing your tests.</p></blockquote>
<p>&nbsp;</p>
<h2>Adding tests to the example code</h2>
<p>Now we have written a couple of small, stand-alone tests to check our <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> installation we can start adding tests to the <em>mi_java_scons</em> code.</p>
<h3>Testing the Bar class</h3>
<p>The first test we are going to write is for the <strong>Bar</strong> class in the <em>mi_foo.bar</em> package. In the directory <em>src/mi_foo/bar</em> there is a directory <em>tests</em> and in that directory is a file <em>BarTests.java</em>. This file will contain the tests for the <strong>Bar</strong> class. Below is part of the code for that class:</p>
<pre>
<span style="color: #3366ff;">... Initial comments omitted ...</span>
package mi_foo.bar.tests;

// The class to test
import mi_foo.bar.Bar;

// JUnit stuff
import org.junit.Test;
import org.junit.Before;
import static org.junit.Assert.assertEquals;

<span style="color: #3366ff;">// Omit comment</span>
public class BarTests
{
    // The class under test
    private Bar bar;

    // The setup function to create new instances of bar for each test
    @Before
    public void setUpBar()
    {
        bar = new Bar();
    }

    // Test initialisation
    @Test
    public void initialization()
    {
        // Check we start at zero
        assertEquals(0, bar.memoryRecall());
    }

    // Test addition
    @Test
    public void addition()
    {
        assertEquals(5, bar.add(5));
        assertEquals(5+3, bar.add(3));
        assertEquals(5+3+7, bar.add(7));
    }
<span style="color: #3366ff;">... Rest of class omitted ...</span>
</pre>
<p>This test class is relatively straightforward. We create a new package, <em>mi_foo.bar.tests</em>, to which all the tests in this directory will belong. We then include the class under test and the <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> classes we want to use. We then define our <strong>BarTests</strong> class which contains five tests (two shown in the snippet above).</p>
<p>The <strong>BarTests</strong> class includes a private instance of the <strong>Bar</strong> class under test. The <em>setUpBar</em> function will ensure that a new instance of the <strong>Bar</strong> class is created before each test is run. </p>
<blockquote><p><strong>Note:</strong> If we wanted the <strong>Bar</strong> class to be persistent between tests, then we could create a static set up function that uses the <strong>@BeforeClass</strong> annotation instead of <strong>@Before</strong>, and make the <strong>Bar</strong> member static.</p></blockquote>
<p>Following the set up function, the actual tests are defined. Each test begins with an <strong>@Test</strong> annotation. Inside the functions we can use the <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> asserts to test our <strong>Bar</strong> class functions are working as expected. These tests tend to use the <em>assertEquals</em> assertion to check the return value of each function call against an expected value. For the expected values we do the sum (e.g. 5+3+7) rather than put the total because it can make it clearer why the result is what we expect, and can also avoid any errors in our mental arithmetic! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<h3>Testing the Raw class and its console output</h3>
<p>Now that the <strong>Bar</strong> class is tested, we can move onto the <strong>Raw</strong> class. This is slightly complicated by the fact that the output of the function call is text written to the console. This is solved by using a <strong>ByteArrayOutputStream</strong> and using it to create a <strong>PrintStream</strong> to which the console output is redirected. We can then check the contents of this stream and compare it to the expected output.</p>
<p>In the <em>RawTests.java</em> file, the package is declared as in the <strong>BarTests</strong> example, and the necessary imports done, including <em>import java.io.*</em> for the streams. Then the test class itself can be created:</p>
<pre>
<span style="color: #3366ff;">... Package declaration and imports omitted ...</span>
public class RawTests
{
  //! The class under test
  private Raw raw;

  //! Handle things sent to System.out
  private ByteArrayOutputStream out_str = new ByteArrayOutputStream();

  //! The setup function to create a new instance of raw for each test
  @Before
  public void setUpRaw()
  {
    raw = new Raw();
    System.setOut(new PrintStream(out_str));
  }

  //! Test solving all world problems
  @Test
  public void solveAllWorldProblems()
  {
    raw.solveAllWorldProblems();
    String expected = "solveAllWorldProblems: Not Yet Implemented";
    expected += System.getProperty("line.separator");
    assertEquals(expected, out_str.toString());
  }

  //! Clean up the data as necessary
  @After
  public void tearDown()
  {
    System.setOut(null);
  }
}
</pre>
<p>After including a private instance of the class under test, a private instance of a <strong>ByteArrayOutputStream</strong> is also contained in the class. The <em>setUpRaw</em> function initializes the <strong>Raw</strong> class before each test and redirects the console output to the <strong>ByteArrayOutputStream</strong>.</p>
<p>In the test <em>solveAllworldProblems</em>, the <strong>Raw</strong> <em>solveAllWorldProblems</em> function is called, and the result (which will be in our stream) tested against an expected string. Note that we have to append <em>System.getProperty(&#8220;line.separator&#8221;)</em> to the end of our expected string as <em>println</em> is used in the <strong>Raw</strong> function. As the line separator differs on Windows and Linux, we need to make sure we append the correct system dependent line ending, or the test will fail on one system or the other. If, for example, we use &#8216;\n&#8217;, the test will pass on Linux, but fail on Windows.</p>
<p>Once the test is complete, the <strong>@After</strong> function is called. This simply redirects <em>stdout</em> back to the console.</p>
<p>This test enables us to test console output, but perhaps a better way is not to use console output directly in the first place. Using one of the logging systems would be a better option for output, and so the next blog post is going to be about adding logging to our code.</p>
<blockquote><p><strong>Note:</strong> Using <strong>ByteArrayOutputStream</strong> and <strong>PrintStream</strong> to capture console output was taken from <a href="http://stackoverflow.com/a/1119559" target="_blank">this stackoverflow answer</a>.</p></blockquote>
<h3>Adding a test suite</h3>
<p>As we now have more than one test in the <em>bar</em> directory, it would be convenient to be able to run all the tests with one command. This is done by creating a test suite which runs everything in the <em>bar/tests</em> directory. The suite is created in a file in that directory and named <em>MIFooBarTestSuite.java</em>. The name includes the path to the suite. The code for the suite is pretty straightforward:</p>
<pre>
<span style="color: #3366ff;">... Initial comments omitted ...</span>
package mi_foo.bar.tests;

// The JUnit stuff for the suite
import org.junit.runner.RunWith;
import org.junit.runners.Suite;

//! A Suite class to handle the tests
@RunWith(Suite.class)
@Suite.SuiteClasses(
    {
      BarTests.class,
      RawTests.class
    }
  )
public class MIFooBarTestSuite
{
}
</pre>
<p>After the package declaration and the imports, the <strong>@RunWith</strong> annotation is used to denote that this function should be run with the <strong>Suite</strong> class. We then define which classes we want to run with the <strong>Suite</strong> class. In this case, <strong>BarTests</strong> and <strong>RawTests</strong>. These annotations need to belong to a public class, but no code is needed within that class. How we can run this suite, or individual tests is covered <a href="#runtests" title="Running the tests and suites">later</a>.</p>
<h3>Testing the Foo class</h3>
<p>Once the <em>bar</em> sub-directory tests have been set up, the tests in the higher level <em>mi_foo</em> directory can be created.</p>
<p>The test for the <strong>Foo</strong> class is very similar to the test of <strong>Raw</strong>, except the building of the expected string is a little more complicated:</p>
<pre>
<span style="color: #3366ff;">... Initial comments and setUp omitted ...</span>
//! Test displaying a string
@Test
public void displayFoo()
{
  String disp_string = "Test Display String";
  foo.displayFoo(disp_string);
  String lsep = System.getProperty("line.separator");
  String expected = "solveAllWorldProblems: Not Yet Implemented";
  expected += lsep;
  expected += "foobar.Foo showing string: \n\t";
  expected += disp_string + lsep;
  assertEquals(expected, out_str.toString());
}
<span style="color: #3366ff;">... tearDown function omitted ...</span>
</pre>
<p>In the <strong>Foo</strong> class <em>displayFoo</em> function, &#8220;\n\t&#8221; is used for display formatting as well as using <em>println</em>. <em>println</em> and &#8216;\n&#8217; don&#8217;t produce the same result (on Windows, at least), so where &#8216;\n&#8217; is used in the display, &#8216;\n&#8217; should also be put into the expected string, and where <em>println</em> is used we use the line separator.</p>
<h3>Creating a hierarchical suite</h3>
<p>It is useful to create a suite that can run the tests in the <em>mi_foo</em> directory as well as the tests for code it uses (in this case the tests in <em>mi_foo/bar</em>). To do this we create a hierarchical test suite that includes the <strong>Foo</strong> tests and the suites of any directories below the <em>mi_foo</em> directory. This is done in a file in <em>mi_foo/tests</em> that is named <em>MIFooTestSuite.java</em>:</p>
<pre>
<span style="color: #3366ff;">... Initial comments omitted ...</span>
package mi_foo.tests;

// Import the sub tests
import mi_foo.bar.tests.MIFooBarTestSuite;

// The JUnit stuff for the suite
import org.junit.runner.RunWith;
import org.junit.runners.Suite;

//! A Suite class to handle the tests
@RunWith(Suite.class)
@Suite.SuiteClasses(
    {
      MIFooBarTestSuite.class,
      FooTests.class
    }
  )
public class MIFooTestSuite
{
}
</pre>
<p>This is similar to the earlier suite, except that we import the <strong>MIFooBarTestSuite</strong> and add that to the <em>SuiteClasses</em>. This suite will now run everything in the <em>mi_foo/bar/tests</em> suite as well as the tests in this directory.</p>
<h3>Adding a &#8216;run all&#8217; suite</h3>
<p>It is useful to be able to make a single call that will run all the tests. As it happens, we only have one module at the moment, so running <strong>MIFooTestSuite</strong> will do that, but as we add other modules we need a top-level class to run all suites for all modules. This suite is created in the top <em>src</em> directory in a file named <em>TestRunner.java</em>:</p>
<pre>
<span style="color: #3366ff;">... Initial comments omitted ...</span>
// Import the main test suites from the sub packages
import mi_foo.tests.MIFooTestSuite;

// The JUnit stuff for the suites
import org.junit.runner.RunWith;
import org.junit.runners.Suite;

//! A Suite class to handle all the tests
@RunWith(Suite.class)
@Suite.SuiteClasses(
    {
      MIFooTestSuite.class
    }
  )
public class TestRunner
{
}
</pre>
<p>Given the earlier suites, this code is simple enough. The <strong>MIFooTestSuite</strong> is imported and run by this suite. When other modules are created, their top-level suites will be included in this file and run. This is all we have to do as running <strong>MIFooTestSuite</strong> will run the tests in <em>mi_foo</em> and all the test suites below it.<br />
&nbsp;</p>
<h2>Building the tests</h2>
<p>Now we have all our test code in place, we want to be able to build it with <a href="http://www.scons.org" target="_blank" title="SCons build tool">SCons</a>. Only one small change is required to the <em>SConstruct</em> file to do this, and that change is to ensure the <strong>CLASSPATH</strong> variable is set from the system environment:</p>
<pre>
<span style="color: #3366ff;">...</span>
<span style="color: #808080;">
# On Windows, the path is incorrect, so change the path to
# the environment one. Even though windows is the problem,
# do it for all platforms
newpath=os.environ.get('PATH')
env.Append(ENV = { 'PATH' : newpath })</span>
<span style="color: #008000;">
# Set the classpath to the CLASSPATH environment variable
# for finding JUnit and maybe others
cpath=os.environ.get('CLASSPATH')
env.Append(JAVACLASSPATH = cpath)</span>
<span style="color: #808080;">
# On Windows the Jar builder isn't loaded properly, so do
# it explicitly
SCons.Tool.jar.generate(env)

# Build the lot with a single command!
env.Java(env['bdir'], env['sdir'])</span>
<span style="color: #3366ff;">...</span>
</pre>
<p>Now in the the <em>mi_java_scons</em> directory containing the <em>SConstruct</em> file we can type <strong>scons</strong> and the result should be something like:</p>
<pre>
> scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
javac -classpath :/usr/share/junit/junit.jar -d build →
  -sourcepath src src/TestRunner.java src/mi_foo/Foo.java →
  src/mi_foo/tests/FooTests.java →
  src/mi_foo/tests/MIFooTestSuite.java →
  src/mi_foo/bar/Bar.java src/mi_foo/bar/Raw.java →
  src/mi_foo/bar/tests/BarTests.java →
  src/mi_foo/bar/tests/MIFooBarTestSuite.java →
  src/mi_foo/bar/tests/RawTests.java
scons: done building targets.
</pre>
<p>&nbsp;<br />
<a name="runtests"></a></p>
<h2>Running the tests</h2>
<p>Once built the tests can be run by changing into the <em>build</em> directory and calling the appropriate class with the <em>org.junit.runner.JUnitCore</em> test runner. For example, to run all the tests, we simply use the <strong>TestRunner</strong> class:</p>
<pre>
> java org.junit.runner.JUnitCore TestRunner
JUnit version 4.10
.......
Time: 0.023

OK (7 tests)
</pre>
<p>If we want to just run the tests in the <strong>mi_foo</strong> package (actually the same as all tests at the moment), still in the <em>build</em> directory, type:</p>
<pre>
> java org.junit.runner.JUnitCore mi_foo.tests.MIFooTestSuite
JUnit version 4.10
.......
Time: 0.021

OK (7 tests)
</pre>
<p>To only run the tests in the <strong>mi_foo.bar</strong> package, again in the <em>build</em> directory, type:</p>
<pre>
> java org.junit.runner.JUnitCore mi_foo.bar.tests.MIFooBarTestSuite
JUnit version 4.10
.......
Time: 0.019

OK (6 tests)
</pre>
<p>Finally, to run the tests for a single class (the tests for the <strong>Bar</strong> class for example), inside the <em>build</em> directory, simply type:</p>
<pre>
> java org.junit.runner.JUnitCore mi_foo.bar.tests.BarTests
JUnit version 4.10
.....
Time: 0.014

OK (5 tests)
</pre>
<p>Any test or suite of tests can be easily run in a similar way.<br />
&nbsp;</p>
<h2>Summary</h2>
<p>This post covered how we added tests to our existing <strong>mi_java_scons</strong> code using <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a>. We covered the installation of <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> on both Windows and Linux, and how those installations were tested using a simple test example. The test example used both the pre-<a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> 4 method of extension of classes, and the <a href="http://www.junit.org/" target="_blank" title="JUnit testing framework">JUnit</a> 4 method of using annotations.</p>
<p>The annotation method was then used to add tests to the <strong>mi_java_scons</strong> code, creating tests for each class in the current code. Test suites were also created to collect together the tests at each level and enable them to be run with one call. A hierarchy of suites enabled one test suite to call another until, at the top-level, a single suite could be called to run all tests.</p>
<p>How this code was built using <a href="http://www.scons.org" target="_blank" title="SCons build tool">SCons</a> was then described, followed by how the tests could be run once built.</p>
<hr style="width: 50%" />
<dl>
<dt><strong>Revision 51</strong></dt>
<dd>Made some methods public due to a mistake in the initial draft of the classes</dd>
<dt><strong>Revision 52</strong></dt>
<dd>Added the class path to the <em>SConstruct</em> and renamed all the test classes from *Test.java to *Tests.java. Added tests for <strong>Bar</strong> and <strong>Raw</strong> classes</dd>
<dt><strong>Revision 53</strong></dt>
<dd>Changed line ending for expected string in <strong>RawTests</strong> to use the <em>System.getProperty(&#8220;line.separator&#8221;)</em> to work properly on Windows</dd>
<dt><strong>Revision 54</strong></dt>
<dd>Updated some <a href="http://www.doxygen.org/" target="_blank" title="Doxygen source code documentation generator tool">Doxygen</a> comments</dd>
<dt><strong>Revision 55</strong></dt>
<dd>Added test suites and the <strong>TestRunner</strong> so that multiple tests can be run with one call</dd>
<dt><strong>Revision 56</strong></dt>
<dd>Changed expected string in <strong>FooTests</strong> as &#8216;/n&#8217; and <em>System.getProperty(&#8220;line.separator&#8221;)</em> do not produce the same output</dd>
<dt><strong>Revision 57</strong></dt>
<dd>Changed the <a href="http://www.doxygen.org/" target="_blank" title="Doxygen source code documentation generator tool">Doxygen</a> version filter to use <strong>awk</strong> instead of <strong>sed</strong></dd>
<dt><strong>Revision 59</strong></dt>
<dd>Changed the <em>README</em> to correctly describe how to run the tests</dd>
<dt><strong>Revisions 58 and 60</strong></dt>
<dd>Changes to other, unrelated code in the <a href="http://www.mereidea.com/svn/testing/trunk/" target="_blank">Testing</a> repository</dd>
</dl>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=jLTiAfVZ-S0:_b-K9JJpDsA:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=jLTiAfVZ-S0:_b-K9JJpDsA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=jLTiAfVZ-S0:_b-K9JJpDsA:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=jLTiAfVZ-S0:_b-K9JJpDsA:I9og5sOYxJI"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=I9og5sOYxJI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/MereIdeaDevelopmentBlog/~4/jLTiAfVZ-S0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.mereidea.com/blog/2011/12/30/using-junit-for-testing-in-javascons-builds/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.mereidea.com/blog/2011/12/30/using-junit-for-testing-in-javascons-builds/</feedburner:origLink></item>
		<item>
		<title>Getting SCons Java builds to work under Windows</title>
		<link>http://feedproxy.google.com/~r/MereIdeaDevelopmentBlog/~3/otR5xMQBsi0/</link>
		<comments>http://www.mereidea.com/blog/2011/07/26/getting-scons-java-builds-to-work-under-windows/#comments</comments>
		<pubDate>Tue, 26 Jul 2011 22:50:01 +0000</pubDate>
		<dc:creator>chris</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[SCons]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Doxygen]]></category>
		<category><![CDATA[Jar]]></category>
		<category><![CDATA[javac]]></category>
		<category><![CDATA[JDK]]></category>

		<guid isPermaLink="false">http://www.mereidea.com/blog/?p=1410</guid>
		<description><![CDATA[Current repository revision: 50 (Testing) Code highlight key Grey: Code unchanged Red: Code removed Green: Code added Blue: Code not shown → Denotes the current line and the next are really one line The next blog post in this series building towards an Android development environment was supposed to be Using JUnit for testing in [...]]]></description>
			<content:encoded><![CDATA[<h2><strong>Current repository revision: 50</strong> (<a href="http://www.mereidea.com/svn/testing/trunk/" target="_blank">Testing</a>)</h2>
<blockquote><p><span style="text-decoration: underline;">Code highlight key</span></p>
<p><span style="color: #808080;">Grey: Code</span><span style="color: #888888;"> unchanged</span><br />
<span style="color: #ff0000;">Red: Code removed</span><br />
<span style="color: #008000;">Green: Code added</span><br />
<span style="color: #3366ff;">Blue: Code not shown</span></p>
<p><span style="color: #3366ff;"><span style="color: #000000;">→ Denotes the current line and the next are really one line</span></span></p>
</blockquote>
<p>The next blog post in this series building towards an <a href="http://www.android.com/" target="_blank" title="Android platform">Android</a> development environment was supposed to be <em>Using <a href="http://www.junit.org/" target="_blank" title="JUnit testing library">JUnit</a> for testing in <a href="http://www.java.com/" target="_blank" title="Java computing platform">Java</a> / <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> builds</em>. However, before getting onto the testing, a lot of code and a fully functional <em>SConstruct</em> file have been created. In doing so, a couple of problems have been solved, including fixing a problem with <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> building even the simplest <a href="http://www.java.com/" target="_blank" title="Java computing platform">Java</a> builds on Windows. It seems like enough has been done to include a post before the testing begins.</p>
<blockquote><p>
If you&#8217;re not interested in our code and <strong>SConstruct</strong> file, but just want to know how to fix the <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> <strong>Java</strong> and / or <strong>Jar</strong> builders on Windows, the answer is further down the post, <a href="#winfix" title="Fixing Java builds for Windows">here</a>.
</p></blockquote>
<p>&nbsp;</p>
<h2>Adding some code for testing</h2>
<p>The code being added to make up the <em>mi_java_scons</em> example is a <a href="http://www.java.com/" target="_blank" title="Java computing platform">Java</a> version of the <a href="http://www.mereidea.com/blog/example-code/" title="Example code">example code</a> in <em>mi_test_scons</em>. That code experimented with using <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> to create a C++ build environment. So far, the <em>foobar</em> library from that example code has been implemented (without the tests). In the <a href="http://www.java.com/" target="_blank" title="Java computing platform">Java</a> version, this package is called <em>mi_foo</em> instead of <em>foobar</em>. The structure of the code is as follows:</p>
<pre>
mi_java_scons/
+-- src/
|    +-- mi_foo/
|    |    +-- Foo.java
|    |    +-- bar/
|    |    |    +-- Bar.java
|    |    |    +-- Raw.java
|    |    |    +-- tests/
|    |    |    |    +-- BarTest.java
|    |    |    |    +-- RawTest.java
|    |    +-- tests/
|    |    |    +-- FooTest.java
|    +-- TestRunners.java
+-- docs/
|    +-- html_files/
|    |    +-- (various image and style files)
+-- Doxyfile
+-- intro.txt
+-- LICENSE.txt
+-- README.txt
+-- SConstruct
+-- versionfilter.sh
</pre>
<p>In the code, the <em>mi_foo</em> directory is the package <strong>mi_foo</strong>, with the <em>bar</em> directory having the package declared as <strong>mi_foo.bar</strong>. The <em>.java</em> files in the <em>tests</em> directories are, at present, empty, as is <em>TestRunners.java</em>.</p>
<p>The <em>docs</em> directory and the <em>Doxyfile</em>, <em>intro.txt</em> and <em>versionfilter.sh</em> files are all for <a href="http://www.doxygen.org/" title="Doxygen code documentation" target="_blank">Doxygen</a>. <em>README.txt</em> contains instructions on getting and building the code, and <em>LICENSE.txt</em> includes the GNU General Public License under which the code is licensed.</p>
<p>The <em>SConstruct</em> file is what does the building work, and will be described in detail below. This post will not contain details of the contents of the <em>.java</em> files as they are straightforward. To get the code and have a look, simply use <a href="http://subversion.apache.org/" title="Subversion version control" target="_blank">Subversion</a> to check out the code from the repository with:</p>
<pre>
svn checkout http://www.mereidea.com/svn/testing/trunk/mi_java_scons
</pre>
<p>Alternatively, you can browse the code at the <a href="http://websvn.mereidea.com" title="MereIdea WebSVN Repository">WebSVN repository</a>. More details can be seen in the <a href="http://www.doxygen.org/" title="Doxygen code documentation" target="_blank">Doxygen</a> documentation, which can be found <a href="http://www.mereidea.com/docs/mi_java_scons" title="mi_java_scons Doxygen documentation">here</a>.</p>
<blockquote><p><strong>Note:</strong> The instructions in the documentation for running the tests don&#8217;t yet work as no testing code has been written.</p>
</blockquote>
<p>&nbsp;</p>
<h2>The basic SConstruct file</h2>
<p>In the <a href="http://www.mereidea.com/blog/2011/07/12/java-and-scons-first-steps-to-android-development/" title="Java and SCons - First steps to Android development">previous post</a> a simple <em>SConstruct</em> file was created to build <a href="http://www.java.com/" target="_blank" title="Java computing platform">Java</a> files from within a <em>src</em> directory. The whole <em>SConstruct</em> file was:</p>
<pre>
Java('build','src')
</pre>
<p>This would actually have worked as the <em>SConstruct</em> file for the <strong>mi_java_scons</strong> project, but we wanted to offer choices, and enable the creation of <em>.jar</em> files for each package, so the <em>SConstruct</em> file, initially, looked like this:</p>
<pre>
<span style="color: #3366ff;"># ...License comment ommitted...</span>

# Set up allowable options (using options enables us to set up help →
and
# input arguments)

# Allow options to come from a file
optfile = ARGUMENTS.get('optfile','')
Help("""You can use the 'optfile' option to specify a file →
containing other
options (e.g.): optfile='myopts.py'
""")

vars = Variables(optfile)
vars.AddVariables(
    ('bdir', "Set to the build directory if you don't like 'build'", →
'build'),
    ('sdir', "Set to the source directory if you don't like 'src'", →
'src'),
    ('javaver', "Set to the Java version you are using. Defaults →
to '1.6'",
                '1.6'),
    BoolVariable('jar',  "Wrap the packages into .jar files",'false')
   )

# Create an environment containing all the options, and add help
env = Environment(variables = vars)
Help(vars.GenerateHelpText(env))

# Check for any unknown options
unknown = vars.UnknownVariables()
if unknown:
  print "You passed in the following unknown options:", unknown.keys()
  Exit(1)

# Set the java version to ensure correct dependencies
env.Replace(JAVAVERSION = env['javaver'])

# Build the lot with a single command!
env.Java(env['bdir'], env['sdir'])
</pre>
<blockquote><p>
<strong>Note:</strong> As we are now using <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> version 2.0.1, we decided to finally move from the long deprecated <strong>Options</strong>, <strong>AddOptions</strong> and <strong>BoolOption</strong> to the newer <strong>Variables</strong>, <strong>AddVariables</strong> and <strong>BoolVariable</strong>. Why the name of these methods were changed is not clear, but in a few versions of <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> (according to the <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> deprecation policy) our old scripts will no longer work. <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_sad.gif' alt=':-(' class='wp-smiley' />  I&#8217;m sure the folks at <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> had a good reason for breaking backwards compatibility and losing some consistency by renaming something which handles options from <strong>Options</strong> to <strong>Variables</strong>, especially when they tend to be constants! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' />
</p></blockquote>
<p>Even with the options added, the <em>SConscript</em> file is still short and easy to follow. Note that this doesn&#8217;t yet include the code to build the <em>.jar</em> files.</p>
<p>This script was run on a Linux based computer, and the result was:</p>
<pre>
> scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
javac -d build -sourcepath src src/TestRunners.java →
src/mi_foo/Foo.java src/mi_foo/tests/FooTest.java →
src/mi_foo/bar/Bar.java src/mi_foo/bar/Raw.java →
src/mi_foo/bar/tests/BarTest.java →
src/mi_foo/bar/tests/RawTest.java
scons: done building targets.
</pre>
<p>After the build, the <em>build</em> directory contained the directory <em>mi_foo</em>, which itself contained <em>Foo.class</em> and the directory <em>bar</em>. The <em>bar</em> directory contained the files <em>Bar.class</em> and <em>Raw.class</em>. This package structure is correct for running classes in the build directory, or simply using something similar to <strong>-classpath build</strong> when executing a class.</p>
<blockquote><p>
<strong>Note:</strong> Typing <strong>scons</strong> again without making code changes should tell you that &#8216;.&#8217; is up to date. However, typing <strong>scons</strong> with the code in its current state rebuilds the code. This is because the <strong>TestRunners.java</strong> file is empty (as are others), and so gets rebuilt as no <strong>.class</strong> file exists. This problem should go away when this code is written.
</p></blockquote>
<p>Now that the <em>SConstruct</em> file seems to work, the part to create the <em>.jar</em> files (if requested) can be added:</p>
<pre>
<span style="color: #3366ff;"># ...License comment ommitted...</span>

<span style="color: #008000;">import glob, re</span>
<span style="color: #808080;">
# Set up allowable options (using options enables us to set up help →
and
# input arguments)

# Allow options to come from a file
optfile = ARGUMENTS.get('optfile','')
Help("""You can use the 'optfile' option to specify a file →
containing other
options (e.g.): optfile='myopts.py'
""")

vars = Variables(optfile)
vars.AddVariables(
    ('bdir', "Set to the build directory if you don't like 'build'", →
'build'),
    ('sdir', "Set to the source directory if you don't like 'src'", →
'src'),
    ('javaver', "Set to the Java version you are using. Defaults →
to '1.6'",
                '1.6'),
    BoolVariable('jar',  "Wrap the packages into .jar files",'false')
   )

# Create an environment containing all the options, and add help
env = Environment(variables = vars)
Help(vars.GenerateHelpText(env))

# Check for any unknown options
unknown = vars.UnknownVariables()
if unknown:
  print "You passed in the following unknown options:", unknown.keys()
  Exit(1)

# Set the java version to ensure correct dependencies
env.Replace(JAVAVERSION = env['javaver'])

# Build the lot with a single command!
env.Java(env['bdir'], env['sdir'])</span>

<span style="color: #008000;"># If jars are requested
if env['jar']:
  # Get a name for each package
  packs = glob.glob('%s/mi_*' % env['sdir'])

  # We need to make sure Jar operates in the build directory
  env.Replace(JARCHDIR=env['bdir'])

  # Go through the packages and jar each
  for nxtp in packs:
    nxtp = re.sub('%s' % env['sdir'], '', nxtp)
    env.Jar(target = '%s/jars/%s.jar' % (env['bdir'], nxtp),
            source = '%s/%s' % (env['bdir'],nxtp))
</span>
</pre>
<p>This code simply uses <strong>glob</strong> to get a list of all directories in the <em>src</em> directory beginning with <em>mi_</em>. These directories will be individual packages, each of which will be turned into a <em>jar</em> file.</p>
<p>The list of directories is iterated through. For each directory name, <strong>re</strong> (the regular expression library) is used to remove the source directory name from the package directory using the <strong>sub</strong> function. <strong>os.path.basename</strong> could be used, but it&#8217;s possible that would return an empty string in some circumstances, so it was thought safer to use <strong>re.sub</strong>. The resulting string can be used to create the appropriate <em>.jar</em> file using the <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> <strong>Jar</strong> command.</p>
<p>Note that the code also sets the <strong>JARCHDIR</strong> environment variable to the build directory. If this is not done then the build directory name is included as part of the path in the <em>.jar</em> file, and the packages will not work properly.</p>
<p>Running <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> with the <strong>jar</strong> option set results in:</p>
<pre>
> scons jar=true
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
javac -d build -sourcepath src src/TestRunners.java →
src/mi_foo/Foo.java src/mi_foo/tests/FooTest.java →
src/mi_foo/bar/Bar.java src/mi_foo/bar/Raw.java →
src/mi_foo/bar/tests/BarTest.java →
src/mi_foo/bar/tests/RawTest.java
jar cf build/jars/mi_foo.jar -C build mi_foo
scons: done building targets.
</pre>
<p>and the file <em>mi_foo.jar</em> is created in the <em>build/jars/</em> directory.</p>
<p>&nbsp;<br />
<a name="winfix"></a></p>
<h2>Fixing the SCons Java build for Windows</h2>
<p>The code discussed above was committed to the <a href="http://www.mereidea.com/svn/testing/trunk/" target="_blank">Testing</a> repository under the name <em>mi_java_scons</em>. The code was then checked out under Windows and <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> was run. The result was:</p>
<pre>
> scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
javac -d build -sourcepath src src\TestRunners.java →
src\mi_foo\Foo.java src\mi_foo\bar\Bar.java →
src\mi_foo\bar\Raw.java →
src\mi_foo\bar\tests\BarTest.java src\mi_foo\bar\tests\RawTest.java →
src\mi_foo\tests\FooTest.java
'javac' is not recognized as an internal or external command,
operable program or batch file.
scons: *** [build\TestRunners.class] Error 1
scons: building terminated because of errors.
</pre>
<p>It seems that even though the <strong>javac</strong> command can be used at the command prompt, it cannot be found when called from <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a>. A snippet from the <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> environment during a run (produced using <strong>print env.Dump()</strong> in the <em>SConstruct</em> file) shows why:</p>
<pre>
<span style="color: #3366ff;">...</span>
<span style="color: #808080;">  'Dir': &lt;SCons.Defaults.Variable_Method_Caller object at 0x028E8430&gt;,
  'Dirs': &lt;SCons.Defaults.Variable_Method_Caller object at 0x028E8450&gt;,
  'ENV': { 'COMSPEC': 'C:\\Windows\\system32\\cmd.exe', </span>
           'PATH': u'C:\\Windows\\System32',
<span style="color: #808080;">           'PATHEXT': '.COM;.EXE;.BAT;.CMD',
           'SystemDrive': 'C:',
           'SystemRoot': 'C:\\Windows',
           'TEMP': 'C:\\Users\\chris\\AppData\\Local\\Temp',
           'TMP': 'C:\\Users\\chris\\AppData\\Local\\Temp'},
  'ESCAPE': &lt;function escape at 0x02A441F0&gt;,
  'F77': 'gfortran',</span>
<span style="color: #3366ff;">...</span>
</pre>
<p>The path is a minimal path only including the <em>System32</em> directory.</p>
<blockquote><p>
<strong>Note:</strong> Sometimes this path includes more than this &#8211; on one of our Windows boxes it includes lots of Visual Studio stuff.
</p></blockquote>
<p>What we do <strong>not</strong> want to do is have everybody specify their own path somehow (maybe through an option) when they run <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a>, or have it hard coded in the <em>SConstruct</em> file. Specifying the path for every run is a hassle, and hard coding the path is wrong, especially when the <em>SConstruct</em> file is being committed to a common repository. The solution is simply to import the system&#8217;s path, since typing <strong>javac</strong> at the command prompt works!</p>
<blockquote><p>
<strong>Note:</strong> If typing <strong>javac</strong> at the command prompt does not work, then you either need to reinstall the <strong><a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html" title="The Java Development Kit" target="_blank">JDK</a></strong> correctly, or add its directory to your path manually.
</p></blockquote>
<p>We can edit our <em>SConscript</em> file to include the lines:</p>
<pre>
<span style="color: #3366ff;">...</span>
<span style="color: #808080;">import glob, re</span><span style="color: #008000;">, os</span>
<span style="color: #3366ff;">...</span>
<span style="color: #808080;"># Set the java version to ensure correct dependencies
env.Replace(JAVAVERSION = env['javaver'])</span>

<span style="color: #008000;"># On Windows, the path is incorrect, so change the path to
# the environment one. Even though windows is the problem,
# do it for all platforms
newpath=os.environ.get('PATH')
env.Append(ENV = { 'PATH' : newpath })</span>

<span style="color: #808080;"># Build the lot with a single command!
env.Java(env['bdir'], env['sdir'])</span>
<span style="color: #3366ff;">...</span>
</pre>
<p>Now when run, all&#8217;s ok:</p>
<pre>
> scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
javac -d build -sourcepath src src\TestRunners.java →
src\mi_foo\Foo.java src\mi_foo\bar\Bar.java src\mi_foo\bar\Raw.java →
src\mi_foo\bar\tests\BarTest.java src\mi_foo\bar\tests\RawTest.java →
src\mi_foo\tests\FooTest.java
scons: done building targets.
</pre>
<p>But when run with the <em>.jar</em> file building option turned on:</p>
<pre>
> scons jar=true
scons: Reading SConscript files ...
AttributeError: 'SConsEnvironment' object has no attribute 'Jar':
  File "C:\code\mi_java_scons\SConstruct", line 62:
    env.Jar(target = '%s/jars/%s.jar' % (env['bdir'], nxtp),
</pre>
<p>Looking on the <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> forum, <a href="http://scons.tigris.org/ds/viewMessage.do?dsForumId=1272&#038;dsMessageId=806677" title="Jar builder not installed as a default builder" target="_blank">this post</a> was found that describes a problem with the <strong>Jar</strong> builder not being created in a <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> environment properly. The post mentions that the builder is not created if the <a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html" title="The Java Development Toolkit" target="_blank">JDK</a> <em>bin</em> directory is not included in the system path, but on our system, it is in the path, and still doesn&#8217;t seem to create the builder. However, a solution in the post was implemented: </p>
<pre>
<span style="color: #3366ff;">...</span>
<span style="color: #808080;">import glob, re, os</span><span style="color: #008000">, SCons.Tool.jar</span>
<span style="color: #3366ff;">...</span>
<span style="color: #808080;"># On Windows, the path is incorrect, so change the path to
# the environment one. Even though windows is the problem,
# do it for all platforms
newpath=os.environ.get('PATH')
env.Append(ENV = { 'PATH' : newpath })</span>

<span style="color: #008000;"># On Windows the Jar builder isn't loaded properly, so do
# it explicitly
SCons.Tool.jar.generate(env)</span>

<span style="color: #808080;"># Build the lot with a single command!
env.Java(env['bdir'], env['sdir'])</span>
<span style="color: #3366ff;">...</span>
</pre>
<p>This solution fixed the problem:</p>
<pre>
> scons jar=true
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
javac -d build -sourcepath src src\TestRunners.java →
src\mi_foo\Foo.java src\mi_foo\bar\Bar.java src\mi_foo\bar\Raw.java →
src\mi_foo\bar\tests\BarTest.java src\mi_foo\bar\tests\RawTest.java →
src\mi_foo\tests\FooTest.java
jar cf build\jars\mi_foo.jar -C build mi_foo
scons: done building targets.
</pre>
<p>Now that everything works, the final code can be committed and we can move on to include <a href="http://www.junit.org/" target="_blank" title="JUnit testing library">JUnit</a> tests, one post late&#8230; <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>&nbsp;</p>
<h2>Summary</h2>
<p>This post covered the initial setting up of the <em>mi_java_scons</em> test library. Some code was set up, and a <em>SConstruct</em> file was created that called the <strong>Java</strong> builder. Once this worked, extra <em>SConstruct</em> code was added to enable each package to be wrapped into a <em>.jar</em> file. This also worked nicely on a Linux based system, but when committed and checked out on a Windows machine the build was broken as the <strong>javac</strong> command could not be found by <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a>.</p>
<p>This was fixed by including the system path in the <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> environment, but the <strong>Jar</strong> builder still did not work. A fix for this was found in the <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> forum and implemented, explicitly including the <strong>Jar</strong> builder in the <em>SConstruct</em> file.</p>
<p>The code was then tested on both Windows and Linux, and worked the same on both.</p>
<p>If <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> wants to be able to claim that the build tool is cross-platform, then it should really be tested on multiple platforms. These problems with Windows showed up on even the simplest <a href="http://www.java.com/" target="_blank" title="Java computing platform">Java</a> builds, so should have shown up in any basic testing. This difficulty in even getting simple builds to work well under Windows is why, after testing <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> and blogging about it, we now prefer <a href="http://www.cmake.org" title="CMake cross-platform makefile generator" target="_blank">CMake</a> for C++ builds. Tools should be there to help, not be something that you have to spend time getting working before you can start using them properly! It&#8217;s a shame, because <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> seems as if it could be a nice build system.</p>
<p>There are still a couple of problems with the <em>SConstruct</em> that need to be addressed to do with the way <a href="http://www.scons.org/" target="_blank" title="SCons build tool">SCons</a> builds the <a href="http://www.java.com/" target="_blank" title="Java computing platform">Java</a> code, but we will leave that for the next post when we start adding the tests.</p>
<hr style="width: 50%;" />
<p><strong>Revision 43:</strong> the initial import of <em>mi_java_scons</em> code.</p>
<p><strong>Revision 44, 45, 46:</strong> maintenance to the repository &#8211; deleting accidentally committed binary files and adding the correct eol-style property to all <em>.java</em> files.</p>
<p><strong>Revision 47:</strong> added the <em>SConstruct</em> code to build <em>.jar</em> files.</p>
<p><strong>Revision 48:</strong> a final deletion of a binary file accidentally committed.</p>
<p><strong>Revision 49:</strong> added code to set up correct path on Windows so that <strong>javac</strong> can be found.</p>
<p><strong>Revision 50:</strong> changed various things as this blog post was written, including adding <em>SConstruct</em> code to explicitly add the <strong>Jar</strong> builder.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=otR5xMQBsi0:VCbmszTv8i8:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=otR5xMQBsi0:VCbmszTv8i8:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=otR5xMQBsi0:VCbmszTv8i8:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=otR5xMQBsi0:VCbmszTv8i8:I9og5sOYxJI"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=I9og5sOYxJI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/MereIdeaDevelopmentBlog/~4/otR5xMQBsi0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.mereidea.com/blog/2011/07/26/getting-scons-java-builds-to-work-under-windows/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.mereidea.com/blog/2011/07/26/getting-scons-java-builds-to-work-under-windows/</feedburner:origLink></item>
		<item>
		<title>Java and SCons – First steps to Android development</title>
		<link>http://feedproxy.google.com/~r/MereIdeaDevelopmentBlog/~3/v8oYpGFU43Q/</link>
		<comments>http://www.mereidea.com/blog/2011/07/12/java-and-scons-first-steps-to-android-development/#comments</comments>
		<pubDate>Tue, 12 Jul 2011 16:38:24 +0000</pubDate>
		<dc:creator>chris</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[SCons]]></category>
		<category><![CDATA[javac]]></category>
		<category><![CDATA[JDK]]></category>

		<guid isPermaLink="false">http://www.mereidea.com/blog/?p=1313</guid>
		<description><![CDATA[Current repository revision: 40 (Testing) Code highlight key Grey: Code unchanged Red: Code removed Green: Code added Blue: Code not shown → Denotes the current line and the next are really one line The next series of blog posts is going to highlight the path followed to reach the goal of using SCons to build [...]]]></description>
			<content:encoded><![CDATA[<h2><strong>Current repository revision: 40</strong> (<a href="http://www.mereidea.com/svn/testing/trunk/" target="_blank">Testing</a>)</h2>
<blockquote><p><span style="text-decoration: underline;">Code highlight key</span><br />
<span style="color: #808080;">Grey: Code</span><span style="color: #888888;"> unchanged</span><br />
<span style="color: #ff0000;">Red: Code removed</span><br />
<span style="color: #008000;">Green: Code added</span><br />
<span style="color: #3366ff;">Blue: Code not shown</span></p>
<p><span style="color: #3366ff;"><span style="color: #000000;">→ Denotes the current line and the next are really one line</span></span></p></blockquote>
<p>The next series of blog posts is going to highlight the path followed to reach the goal of using <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> to build a simple <a title="Android platform" href="http://www.android.com/" target="_blank">Android</a> application. The steps taken to achieve this are:</p>
<ol>
<li>Building <a title="Java computing platform" href="http://www.java.com" target="_blank">Java</a> with <a title="SCons build system" href="http://www.scons.org/" target="_blank">SCons</a></li>
<li>Using <a title="JUnit testing library" href="http://www.junit.org/" target="_blank">JUnit</a> for testing in <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a>/<a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> builds</li>
<li>Using <a title="Buildbot automated build/test cycle" href="http://trac.buildbot.net/" target="_blank">Buildbot</a> to run <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a>/<a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> compile/test cycles</li>
<li>Integrating <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a>/<a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> builds with <a title="Eclipse open development platform" href="http://www.eclipse.org/" target="_blank">Eclipse</a></li>
<li>Testing a GUI application with <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a>/<a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> and <a title="JUnit testing library" href="http://www.junit.org/" target="_blank">JUnit</a></li>
<li>Including the <a title="Android platform" href="http://www.android.com/" target="_blank">Android</a> API in a <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a>/<a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> build</li>
<li>Building an <a title="Android platform" href="http://www.android.com/" target="_blank">Android</a> app with <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a>/<a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a></li>
<li>Dual GUI (<a title="Android platform" href="http://www.android.com/" target="_blank">Android</a> and Swing) development</li>
</ol>
<p><a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a> and <a title="Android platform" href="http://www.android.com/" target="_blank">Android</a> are subjects that are new to us, so these blog posts will follow the learning process rather than being the advice of experts. As such we hope they will highlight and solve many of the problems people who are also coming to <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a> and <a title="Android platform" href="http://www.android.com/" target="_blank">Android</a> for the first time may encounter. Hopefully the series will help anyone wanting to start out.</p>
<p><a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> is to be used for this development as it is a build system we have worked with before (and blogged about) which handles <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a> simply (or so it seems at the moment) and saves time on learning a new build system like <a title="Apache's Ant Java build tool" href="http://ant.apache.org/" target="_blank">Ant</a> or <a title="Maven project management tool" href="http://maven.apache.org/" target="_blank">Maven</a>.</p>
<p>Now onto our first experience of building <a title="Java computing platform" href="http://www.java.com/">Java</a> with <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a>!</p>
<p>&nbsp;</p>
<h2>Hello, Java World!</h2>
<p>To create <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a> programs using <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> it is necessary to have <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a>, the <a title="The Java Development Kit" href="http://www.oracle.com/technetwork/java/javase/downloads/index.html" target="_blank">Java Development Kit</a> (<a title="The Java Development Kit" href="http://www.oracle.com/technetwork/java/javase/downloads/index.html" target="_blank">JDK</a>), <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> and <a title="Python programming language" href="http://www.python.org/" target="_blank">Python</a> installed. Other requirements will be covered when they are first used.</p>
<p>Most computers will already have <a title="Java computing platform" href="http://www.java.com" target="_blank">Java</a> installed. To check whether it is installed, and whether it&#8217;s the latest version, simply go to the <a title="Java computing platform" href="http://www.java.com" target="_blank">Java website</a> and click on the <em>Do I have Java?</em> link (ensuring <a title="Java computing platform" href="http://www.java.com" target="_blank">Java</a> is enabled in the browser!). From that page <a title="Java computing platform" href="http://www.java.com" target="_blank">Java</a> can be downloaded and installed. At the time of writing our installed version is <em>Version 6 Update 26</em>.</p>
<p>To program in <a title="Java computing platform" href="http://www.java.com" target="_blank">Java</a> the <a title="The Java Development Kit" href="http://www.oracle.com/technetwork/java/javase/downloads/index.html" target="_blank">JDK</a> also needs to be installed. This includes the core packages of interfaces and classes used in the <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a> language, as well as the <strong>javac</strong> <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a> compiler and other development tools.</p>
<p>Now, for the first program (and a test that the <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a> and <a title="The Java Development Kit" href="http://www.oracle.com/technetwork/java/javase/downloads/index.html" target="_blank">JDK</a> are correctly installed), the <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a> version of <em>Hello, World!</em> can be written. In a directory called <em>scons_test</em> a directory named <em>src</em> was created. The <em>src</em> directory is created as it will be used by <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> later. Inside this <em>src</em> directory, a file named <em>HelloWorld.java</em> was edited with a text editor to contain:</p>
<pre>/*
 * Java version of Hello, World!
 */
class HelloWorld
{
  public static void main(String args[])
  {
    System.out.println("Hello, Java World!");
  }
}</pre>
<p>At a command prompt, in the <em>src</em> directory containing the <em>HelloWorld.java</em> file, the code was compiled using the command:</p>
<pre>javac HelloWorld.java</pre>
<p>This created a class file <em>HelloWorld.class</em>. The program was run with the command:</p>
<pre>java HelloWorld</pre>
<p>And the output was:</p>
<pre>Hello, Java World!</pre>
<p>&nbsp;</p>
<h2>Hello, SCons World!</h2>
<p>Now we want to build our <em>Hello, Java World!</em> program using <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a>. To use <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> requires a <a title="Python programming language" href="http://www.python.org/" target="_blank">Python</a> installation. Whether or not <a title="Python programming language" href="http://www.python.org/" target="_blank">Python</a> is installed on a system can be checked by opening a command prompt or shell and typing:</p>
<pre>python --version</pre>
<p>This will respond with either the <a title="Python programming language" href="http://www.python.org/" target="_blank">Python</a> version or a <em>command not found</em> style error. If <a title="Python programming language" href="http://www.python.org/" target="_blank">Python</a> is not installed already, then it can be downloaded and installed from the <a title="Python programming language" href="http://www.python.org/" target="_blank">Python website</a>.</p>
<p>A <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> installation can be checked with:</p>
<pre>scons --version</pre>
<p>Again, this will either return a <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> version number (and some other information) or a <em>command not found</em> style error message. If <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> is not installed, then it can be downloaded and installed from the <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons website</a>. The version of <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> installed for this blog is 2.0.1.</p>
<p>Now we&#8217;re ready to build the <em>HelloWorld.java</em> code using <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a>. A small change was made to the code to change the message output to refer to <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> rather than <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a>, but this was not really necessary <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' />  :</p>
<pre><span style="color: #808080;">/*
 * Java version of Hello, World!
 */
class HelloWorld
{
  public static void main(String args[])
  {
    <span style="color: #ff0000;">System.out.println("Hello, Java World!");</span>
    <span style="color: #008000;">System.out.println("Hello, SCons World!");</span>
  }
}</span></pre>
<p>Also, the <em>HelloWorld.class</em> file created earlier in the <em>src</em> directory was deleted. Then, in the directory above <em>src</em> (the <em>scons_test</em> directory) a new text file named <em>SConstruct</em> was created and edited in a text editor. It only contains a single line:</p>
<pre>Java('build','src')</pre>
<p>A command prompt was opened in the directory containing the <em>SConstruct</em> file, and <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> was run with the command:</p>
<pre>scons</pre>
<p>The output should have been something like:</p>
<pre>scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
javac -d build -sourcepath src src/HelloWorld.java
scons: done building targets.</pre>
<p>However, running it on a SuSE Linux based system, on the first run our output was more like:</p>
<pre>scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
javac -d build -sourcepath src src/HelloWorld.java
javac: unrecognized option '-sourcepath'
src: file not recognized: Is a directory
collect2: ld returned 1 exit status
scons: *** [build/HelloWorld.class] Error 1
scons: building terminated because of errors.</pre>
<p>After a little investigation, the program <strong>javac</strong> turned out to be calling the <a title="The Gnu Compiler Collection" href="http://gcc.gnu.org/" target="_blank">Gnu</a> program <strong>gcj</strong> instead of the <a title="The Java Development Kit" href="http://www.oracle.com/technetwork/java/javase/downloads/index.html" target="_blank">JDK</a> version of <strong>javac</strong>. To fix this <strong>gcj</strong> was uninstalled, and the <a title="The Java Development Kit" href="http://www.oracle.com/technetwork/java/javase/downloads/index.html" target="_blank">JDK</a> installed properly. After this was done the <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a> run worked as above, and the <em>HelloWorld.class</em> file was generated in the automatically created <em>build</em> directory.</p>
<p>The newly compiled program was run by changing into the <em>build</em> directory and typing:</p>
<pre>java HelloWorld</pre>
<p>It could also be run by staying in the directory containing the <em>SConstruct</em> file and typing:</p>
<pre>java -classpath build HelloWorld</pre>
<p>Both will output:</p>
<pre>Hello, SCons World!</pre>
<p>Note that the directory containing the class has to be set using the <strong>-classpath</strong> option. Trying to just specify the directory as follows:</p>
<pre>java build/HelloWorld</pre>
<p>will not work, and will result in a long error something like:</p>
<pre>Exception in thread "main" java.lang.NoClassDefFoundError:
build/HelloWorld (wrong name: HelloWorld)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
        at java.securoty.SecureClassLoader.defineClass(
SecureClassLoader.java:142)
        at java.net.URLClassLoader.defineClass(
URLClassLoader.java:277)
        at java.net.URLClassLoader.access$000(URLClassLoader.java:73)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:212)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
        at sun.misc.Launcher$AppClassLoader.loadClass(
Launcher.java:294)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
Could not find the main class: build/HelloWorld. Program will exit</pre>
<p>&nbsp;</p>
<h2>Summary</h2>
<p>This post describes building <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a> with <a title="SCons build tool" href="http://www.scons.org/" target="_blank">SCons</a>. It shows a simple <em>Hello, World!</em> style example compiled both manually, and with a <em>SConstruct</em> file, and shows how to run the resulting class.</p>
<p>None of this code has been committed to the <a title="The Testing repository for MereIdea" href="http://www.mereidea.com/svn/testing/trunk/" target="_blank">MereIdea Testing Repository</a>, as it was only a simple example. The next post, however, will create <a title="Java computing platform" href="http://www.java.com/" target="_blank">Java</a> code that is the equivalent to the C++ <a title="The blog example code" href="http://www.mereidea.com/blog/example-code/">example code</a> and will include a couple of <em>packages</em>, archived into <em>jar</em> files, and some tests created using <a title="JUnit testing library" href="http://www.junit.org/" target="_blank">JUnit</a>, which will be stored in the repository for readers to check out.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=v8oYpGFU43Q:cpVEfVm32XU:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=v8oYpGFU43Q:cpVEfVm32XU:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=v8oYpGFU43Q:cpVEfVm32XU:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=v8oYpGFU43Q:cpVEfVm32XU:I9og5sOYxJI"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=I9og5sOYxJI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/MereIdeaDevelopmentBlog/~4/v8oYpGFU43Q" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.mereidea.com/blog/2011/07/12/java-and-scons-first-steps-to-android-development/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.mereidea.com/blog/2011/07/12/java-and-scons-first-steps-to-android-development/</feedburner:origLink></item>
		<item>
		<title>Buildbot slaves as a Windows service and adding a testing framework (UnitTest++)</title>
		<link>http://feedproxy.google.com/~r/MereIdeaDevelopmentBlog/~3/tw3L4Ukl6LI/</link>
		<comments>http://www.mereidea.com/blog/2010/09/02/buildbot-slaves-as-a-windows-service-and-adding-a-testing-framework-unittest/#comments</comments>
		<pubDate>Thu, 02 Sep 2010 18:26:55 +0000</pubDate>
		<dc:creator>chris</dc:creator>
				<category><![CDATA[Buildbot]]></category>
		<category><![CDATA[CMake]]></category>
		<category><![CDATA[Microsoft Visual Studio]]></category>
		<category><![CDATA[Subversion]]></category>

		<guid isPermaLink="false">http://www.mereidea.com/blog/?p=1185</guid>
		<description><![CDATA[Current repository revision: 12 (Main) Code highlight key Grey: Code unchanged Red: Code removed Green: Code added Blue: Code not shown → Denotes the current line and the next are really one line The mini-series of posts about setting up Buildbot is coming to an end with this post about starting a Windows Slave as [...]]]></description>
			<content:encoded><![CDATA[<h2><strong>Current repository revision: 12</strong> (<a href="http://www.mereidea.com/svn/main/trunk/">Main</a>)</h2>
<blockquote><p><span style="text-decoration: underline;">Code highlight key</span><br />
<span style="color: #808080;">Grey: Code</span><span style="color: #888888;"> unchanged</span><br />
<span style="color: #ff0000;">Red: Code removed</span><br />
<span style="color: #008000;">Green: Code added</span><br />
<span style="color: #3366ff;">Blue: Code not shown</span></p>
<p><span style="color: #3366ff;"><span style="color: #000000;">→ Denotes the current line and the next are really one line</span></span></p></blockquote>
<p>The mini-series of posts about setting up <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a> is coming to an end with this post about starting a Windows <em>Slave</em> as a service, adding the <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> testing framework to the code and running the tests with <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a>. Earlier posts had described how to <a title="Setting up a Buildbot master with CMake" href="http://www.mereidea.com/blog/2010/08/13/setting-up-a-buildbot-master-with-cmake/">set up a Buildbot master with CMake</a> and how to <a title="Setting up Buildbot slaves for CMake" href="http://www.mereidea.com/blog/2010/08/18/adding-code-and-creating-buildbot-slaves-for-cmake/">set up Buildbot slaves for CMake</a>.</p>
<hr style="width: 50%;" />
<h2>Starting the Buildbot Slave as a Windows Service</h2>
<blockquote><p><strong>Note:</strong> Before setting up the <em>Slave</em> to run as a service, make sure it works running from the command line. This ensures everything is set up right!</p></blockquote>
<p>.<br />
Setting up the <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a> <em>Slave</em> as a Service started with following the appropriate section (<strong>Windows Buildbot service setup</strong>) in <a title="Running Buildbot on Windows" href="http://buildbot.net/trac/wiki/RunningBuildbotOnWindows">this piece of documentation</a>. A couple of problems occurred while following this procedure, but these have been solved and the details are below.</p>
<p>Firstly, the script <em>buildbot_service.py</em> was checked for in the <a title="Python Programming Language" href="http://www.python.org/">Python</a> <em>Scripts</em> directory. It didn&#8217;t exist! This was because the script comes with the <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a> <em>Master</em> installation, but not the <em>Slave</em>. On Windows the <em>Master</em> was unnecessary, and so wasn&#8217;t installed. To save installing it now, the <em>buildbot_service.py</em> script was copied across from the Linux <em>Master</em> installer (from within the expanded <em>buildbot-0.8.1/contrib/windows</em> directory). Remember that <a title="Python for Windows Extensions" href="http://sourceforge.net/projects/pywin32/">PyWin32</a> has to be installed before adding as a service.</p>
<h3>Windows XP</h3>
<p>The first <em>Slave</em> is to run on a Windows XP machine, and this was set up with a single user (Chris). As this is the default user it should have administrator privileges. However, step 3 in the <a title="Running Buildbot on Windows" href="http://buildbot.net/trac/wiki/RunningBuildbotOnWindows">documentation</a> was followed, and the user Chris was added to the <strong>Log on as a service</strong> user.</p>
<p>The service was then installed, using the command:</p>
<pre>&gt; python C:\Python26\Scripts\buildbot_service.py →
--user BARRACUDA\Chris --password SomePassword --startup auto install</pre>
<p>This command reported success, so the next step was to ensure the user (Chris) had permissions on the registry key <em>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\BuildBot</em>. <strong>Regedit</strong> was fired up, the key was browsed to, and the <em>BuildBot</em> folder right clicked on. <strong>Permissions&#8230;</strong> was selected from the context menu, and user Chris added.</p>
<p>As the user Chris created the buildbot <em>Slave</em> configuration directory it was unnecessary to change permissions on the configuration folder. Now the buildslave was ready to start with the command:</p>
<pre>&gt; buildbot_service.py start "C:\code\Buildbot\slave"</pre>
<p>Unfortunately, that resulted in the error:</p>
<pre>Error starting service: The service did not start due to a logon →
failure</pre>
<p>It was time to look at what the script had actually installed in the Services dialog (available through <em>Control Panel | Administrative Tools | Services</em>). In the list that pops up, the <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a> service can be seen. Double-clicking on it brings up the properties, and everything looked sensible. Clicking the <strong>Start</strong> button brought up the same message as running at the command line did.</p>
<p>After much checking of permissions and retyping passwords, it was decided that the Administrator account should be tried instead of the user Chris, so the details were entered in the <strong>Log On</strong> tab:</p>
<p><img title="The Buildbot Service Log On Tab" src="http://www.mereidea.com/blog_ims/bbserv2.png" alt="The Buildbot Service Log On Tab" width="410" height="468" /></p>
<p>The service was then started again, but this time the service started and stopped immediately. To find out what had happened it was necessary to look in the event viewer (<em>Control Panel | Administrative Tools | Event Viewer</em>). Double-clicking on the latest Warning related to the Buildbot service showed the following details:</p>
<p><img title="The Event Viewer description of the Buildbot service warning" src="http://www.mereidea.com/blog_ims/ev1.png" alt="The Event Viewer description of the Buildbot service warning" width="404" height="455" /></p>
<p>There is an <em>ImportError</em> as the <em>buildbot_service.py</em> script tries to import <em>runner</em> from <em>buildbot.scripts</em>. This relates back to the initial problem of the missing <em>buildbot_service.py</em> script &#8211; only the <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a> <em>Slave</em> code is installed on the Windows box.</p>
<p>Looking through the scripts called when the <em>buildslave</em> command is executed, it seems the code that does the actual running is in the file <em>C:\Python26\Scripts\buildslave</em>. This script just has just three lines of actual code, so these were copied and pasted into the <em>buildbot_services.py</em> file in the <strong>_RunChild()</strong> function in place of the <em>Master</em> running code (the problematic import and a <strong>runner.run()</strong> command):</p>
<pre><span style="color: #888888;">def _RunChild():</span>
<span style="color: #3366ff;">...</span>
    <span style="color: #888888;"># Start the buildbot app</span>
    <span style="color: #ff0000;">from buildbot.scripts import runner
    runner.run()</span>
    _<span style="color: #008000;">_requires__ =  'buildbot-slave==0.8.1'
    import pkg_resources
    pkg_resources.run_script('buildbot-slave==0.8.1','buildslave')</span>
    <span style="color: #808080;">print "Service child process terminating normally."</span></pre>
<blockquote><p><strong>Note:</strong> Remember &#8211; this code change is only necessary if the Buildbot Master code hasn&#8217;t been installed. In fact, if you make this change and hope to start a Buildbot Master it&#8217;s unlikely to work!</p></blockquote>
<p>The service property dialog was then reopened, and the parameters normally passed to start the <em>Slave</em> were entered in the start parameters box:</p>
<p><img title="The Buildbot Service properties dialog" src="http://www.mereidea.com/blog_ims/bbserv1.png" alt="The Buildbot Service properties dialog" width="410" height="468" /></p>
<p>The service was then started again, and this time all was successful! Checking on the MereIdea <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a> web pages confirmed that the <em>Slave</em> was now connected to the <em>Master</em>. This could also be started with the command:</p>
<pre>&gt; buildbot_service.py start C:\code\Buildbot\slave</pre>
<p>All that remained now was to check whether the <strong>Builders</strong> would still work with the service, and whether the service restarted correctly when the computer was rebooted.</p>
<p>The build did not work due to something mentioned in an earlier post in reference to the <strong>setvcvars</strong> step of the Windows build; a different command shell is used for each step, so the work done by the <strong>setvcvars</strong> step is lost by the next step. The call to NMake fails because the <a title="Visual Studio Express" href="http://www.microsoft.com/express/">Visual Studio</a> paths are no longer set.</p>
<p>This was fixed by removing <strong>setvcvars</strong> as a standalone first step, and editing <em>setvcvars.bat</em> to set up the environment and call NMake:</p>
<pre><span style="color: #008000;">call</span> <span style="color: #999999;">"c:\Program Files\Microsoft Visual Studio →
10.0\VC\vcvarsall.bat" x86</span>
<span style="color: #008000;">nmake</span></pre>
<p>This file was renamed <em>run_nmake.bat</em>, and then the Windows <strong>Compile</strong> step in the <em>master.cfg</em> file (<em>wcomp</em>) was changed to use it and the original <strong>setvcvars</strong> step removed:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #999999;">svnbaseurl='http://svn.mereidea.com/main/'
svnup = source.SVN(mode='update',
                   baseURL=svnbaseurl,
                   defaultBranch='trunk')
svnco = source.SVN(mode='clobber',
                   baseURL=svnbaseurl,
                   defaultBranch='trunk')</span>
<span style="color: #ff0000;">setvcvars = shell.ShellCommand(command="setvcvars.bat")</span>
<span style="color: #999999;">confg = shell.Configure(command="cmake ../src")
wconfg = shell.Configure(command='cmake -G "NMake Makefiles" ../src')</span><span style="color: #999999;">
</span><span style="color: #999999;">comp = shell.Compile(command='make')</span><span style="color: #999999;">
</span><span style="color: #808080;"><span style="color: #999999;">wcomp = shell.Compile(command='</span><span style="color: #008000;">run_</span><span style="color: #999999;">nmake')</span></span>
<span style="color: #999999;">rtest = shell.Test(command='run_tests')</span>
<span style="color: #3366ff;">...</span>
<span style="color: #808080;"><span style="color: #999999;">fwchg = factory.B</span><span style="color: #999999;">uildFactory()</span></span>
<span style="color: #ff0000;">fwchg.addStep(setvcvars)</span>
<span style="color: #999999;">fwchg.addStep(svnup)
fwchg.addStep(wconfg)
fwchg.addStep(wcomp)
fwchg.addStep(rtest)</span>
<span style="color: #3366ff;">...</span>
<span style="color: #999999;">fwngt = factory.BuildFactory()</span>
<span style="color: #ff0000;">fwngt.addStep(setvcvars)</span>
<span style="color: #999999;">fwngt.addStep(svnco)
fwngt.addStep(wconfg)
fwngt.addStep(wcomp)
fwngt.addStep(rtest)</span>
<span style="color: #3366ff;">...</span></pre>
<p>A new build was forced, and was now successful! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  The computer was rebooted, and the service restarted successfully. The build still worked.</p>
<h3>Windows 7/Vista</h3>
<p>Most of the Windows 7 setup for a <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a> <em>Slave</em> as a service was the same as for Windows XP, except that the main steps need to be done in a command prompt that has been &#8216;Run As Administrator&#8217;.</p>
<p>It was also necessary to open the services dialog and reenter the user details in the Log On tab, then press <strong>Apply</strong> to set the permissions for the user to <em>Lon on as a service</em>. This gets automatically set when the user details are set. There is probably a way to set these permissions separately, but this works!</p>
<p>For Windows 7 the user was in the Administrators group, but when setting up the Vista service a non-administrative user was used. This still worked the same as Windows 7 (&#8216;Run As Administrator&#8217; shell needed to be used) except that the registry permissions needed granting on <em>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\BuildBot</em>, and the service had to be started (whether from the dialog or the command prompt) by the administrator user.</p>
<p>A final note on Window 7. There were problems with using <a href="http://www.microsoft.com/express/">Visual Studio</a> 2010 under Windows 7. This problem was due to <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org">CMake</a> and not <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a>. Therefore, the test for this service was done with  <a href="http://www.microsoft.com/express/">Visual Studio</a> 2008. There may be a blog post on using <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org">CMake</a> with Windows 7 and <a href="http://www.microsoft.com/express/">Visual Studio</a> 2010 later.</p>
<hr style="width: 50%;" />
<h2>Adding a testing framework</h2>
<p><a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> was chosen as the testing framework. This section shows the addition of this framework to the MereIdea code, building the tests with <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org">CMake</a><br />
and running them through <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a>.</p>
<p>The first step is to add the <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> code to the MereIdea code. It was decided to simply import the code and build it as part of the <em>mi_test</em> module, which will be built with the rest of the code, rather than expect everyone to get the code and build it. The code is small and easily compiled to a library using a couple of <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org">CMake</a> files. Including it also eliminates the main problem with using thirdparty code &#8211; if the <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a>code changes or becomes unavailable, it won&#8217;t require (possibly extensive) changes to the MereIdea code.</p>
<p>The first step, therefore, was to add a new module inside the <em>mi_main</em> source directory. The directory <em>mi_test</em> was created and another directory created inside it &#8211; <em>unittest++</em>. This will keep the <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> code separate from any other code added to the <em>mi_test</em> module. The <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> code was downloaded to a different directory using svn:</p>
<pre>svn co https://unittest-cpp.svn.sourceforge.net/svnroot/unittest-cpp →
unittest-cpp</pre>
<p>and the code files (including the <em>Posix</em>, <em>Win32</em> and <em>test</em> directories) were copied to the new <em>mi_test/unittest++</em> module. The <em>COPYING</em> and <em>README</em> files were also copied to the top-level <em>mi_test</em> directory.</p>
<p>The <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org">CMake</a> files were then added to the <em>mi_test</em> module. Firstly, the top-level <em>CMakeLists.txt</em> file was created. This simply declares the project name and adds the <em>unittest++</em> subdirectory:</p>
<pre># Give this module its own project name so we can
# refer to its build and source structures
project(MI_TEST)

# And build the unittest++ library
add_subdirectory(unittest++)</pre>
<p>Next the <em>CMakeLists.txt</em> file in the <em>unittest++</em> subdirectory was written. This adds the source files to build the library, and then descends into the <em>tests</em> directory:</p>
<pre># Add the source files here
set(MI_TEST_UNITTEST_PP_SOURCES
    AssertException.h
    Test.h
    Checks.h
    ....
    CurrentTest.h
    CheckMacros.h
    Config.h
    ExecuteTest.h
    TestMacros.h
    TestSuite.h
    TimeHelpers.h
    UnitTest++.h

    AssertException.cpp
    Test.cpp
    Checks.cpp
    ...
    CurrentTest.cpp
)

# Add the platform specific code
if (WIN32)
  set (MI_TEST_UNITTEST_PP_SOURCES ${MI_TEST_UNITTEST_PP_SOURCES}
       Win32/TimeHelpers.h
       Win32/TimeHelpers.cpp
  )
else ()
  set(MI_TEST_UNITTEST_PP_SOURCES ${MI_TEST_UNITTEST_PP_SOURCES}
      Posix/SignalTranslator.h
      Posix/TimeHelpers.h
      Posix/SignalTranslator.cpp
      Posix/TimeHelpers.cpp
  )
endif (WIN32)

# And add the library
add_library(miunittest++ ${MI_TEST_UNITTEST_PP_SOURCES})

# Put the library in a lib subdirectory
# Do for static, shared and module libraries
set_target_properties(miunittest++ PROPERTIES
                      ARCHIVE_OUTPUT_DIRECTORY ${MI_TEST_BINARY_DIR} →
/lib
                      LIBRARY_OUTPUT_DIRECTORY ${MI_TEST_BINARY_DIR} →
/lib
)

# Build the tests
add_subdirectory(tests)</pre>
<p>All the source files <strong>and</strong> header files are included in the <em>CMakeLists.txt</em> file. This is because, even though some of the header files aren&#8217;t needed for building the library, the header files won&#8217;t appear in any generated <a href="http://www.microsoft.com/express/">Visual Studio</a> projects if they are not included in the <em>CMakeLists.txt</em> file. The last new <em>CMakeLists.txt</em> file to be created is in the <em>mi_test/unittest++/tests</em> directory:</p>
<pre># Add the source files here
set(MI_TEST_UNITTEST_PP_TEST_SOURCES
    RecordingReporter.h
    ScopedCurrentTest.h

    Main.cpp
    TestAssertHandler.cpp
    TestChecks.cpp
    ...
    TestCurrentTest.cpp
    TestTestSuite.cpp
)

# Create the test executable
add_executable(unittest++_tests ${MI_TEST_UNITTEST_PP_TEST_SOURCES})

# Set the libraries required
target_link_libraries(unittest++_tests miunittest++)

# Add as a test (so we can use 'make test')
add_test(unittest++_tester unittest++_tests)</pre>
<p>Then the top-level source <em>CMakeLists.txt</em> file is edited to turn testing on (so <strong>add_test</strong> actually adds a test) and to include the <em>mi_test</em> module.</p>
<pre>...
include_directories(${MI_MAIN_SOURCE_DIR})

# Make sure tests are built
enable_testing()

# Now set the subdirectories to work through. Ensure these are done
# in the correct order
add_subdirectory(mi_test)
add_subdirectory(mi_hello)</pre>
<p>Now changing to the <em>build</em> directory in a Linux shell and typing:</p>
<pre>&gt; cmake ../src
&gt; make
&gt; make test</pre>
<p>will generate the appropriate make files, build the code, and run the tests which should pass 100%. Under Windows it would be:</p>
<pre>&gt; cmake -G "NMake Makefiles" ../src
&gt; nmake
&gt; nmake test</pre>
<p>On Windows the <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org">CMake</a> GUI could also be run to generate the <a href="http://www.microsoft.com/express/">Visual Studio</a> projects which can be built and run within the IDE.</p>
<hr style="width: 50%;" />
<h2>Changing the mi_hello test to use UnitTest++</h2>
<p>Now to change the <em>mi_hello</em> tests to use the <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> code. Before this is done, however, a small change to the <strong>mi_hello::Hello</strong> class is needed. Instead of sending the output to the console (<strong>std::cout</strong>) it needs capturing so that it can be tested. The header file becomes:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #999999;">#include &lt;string&gt;</span>
<span style="color: #008000;">#include &lt;ostream&gt;</span>

<span style="color: #3366ff;">...</span>
    <span style="color: #999999;">//! Display a message
    /*!</span>
         <span style="color: #ff0000;">Display a string passed in (or "Hello, World!")</span>
         <span style="color: #008000;">Send a string parameter to a stream</span>

         <span style="color: #999999;">\param disp_str The string to display</span> <span style="color: #ff0000;">(defaults to <span style="color: #000000;">→</span>
"Hello, World!")</span>
         <span style="color: #008000;">\param os The stream to send the string to</span>
    <span style="color: #999999;">*/</span>
    <span style="color: #999999;">v</span><span style="color: #999999;">oid display(const std::string &amp;disp_str</span><span style="color: #ff0000;"> = "Hello, World!"</span><span style="color: #008000;">, <span style="color: #000000;">→</span>
std::ostream &amp;os</span><span style="color: #999999;">) const;</span>
<span style="color: #3366ff;">.<span style="color: #3366ff;">.</span></span><span style="color: #3366ff;">.</span></pre>
<p>Note that the default for the display string (&#8220;Hello, World&#8221;) was removed as it will have to be entered anyway when changing the stream. As this class will only be used for testing, defaults don&#8217;t really make much sense. The cxx file is changed to match:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #ff0000;">#include &lt;iostream&gt;</span>
<span style="color: #3366ff;">...</span>
<span style="color: #808080;">void mi_hello::Hello::display(const std::string &amp;disp_str</span> →
<span style="color: #ff0000;">/*= "Hello, World!"*/</span><span style="color: #008000;">,</span>
                              <span style="color: #008000;">std::ostream &amp;os</span><span style="color: #999999;">) const</span>
<span style="color: #999999;">{</span>
  <span style="color: #ff0000;">std::cout &lt;&lt; disp_str &lt;&lt; std::endl;</span>
  <span style="color: #008000;">os &lt;&lt; disp_str;</span>
<span style="color: #999999;">}</span></pre>
<p>Finally the test can be changed to use the new <strong>mi_hello::Hello</strong> class and the <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> code (comments omitted):</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #999999;">#include &lt;mi_hello/hello.h&gt;</span>
<span style="color: #008000;">#include &lt;mi_test/unittest++/UnitTest++&gt;
#include &lt;sstream&gt;</span>

<span style="color: #008000;">struct MI_HelloFixture
{
  MI_HelloFixture() { }
  ~MI_HelloFixture() { }

  mi_hello::Hello hello_;
  std::string test_str_;
  std::stringstream str_;
};

TEST_FIXTURE(MI_HelloTestFixture, TestHello)
{
  test_str_ = "Hello, World!";
  hello_.display(test_str_, str_);
  CHECK_EQUAL(test_str_, str_.str());
}

TEST_FIXTURE(MI_HelloTestFixture, TestGoodbye)
{
  test_str_ = "Goodbye, World!";
  hello_.display(test_str_, str_);
  CHECK_EQUAL(test_str_, str_.str());
}
</span>
<span style="color: #ff0000;">int main()
{
  mi_hello::Hello h;
  h.display();
  h.display("Goodbye, World!");

  return 0;
}</span></pre>
<p>Now that the <strong>main()</strong> function has been removed from this file (as there could be several of these test files), a new file needs to be created to run the tests. This file is named <em>test_hello_main.cxx</em> and is created alongside <em>test_hello.cxx</em>. This new file simply contains:</p>
<pre>#include &lt;mi_test/unittest++/UnitTest++.h&gt;
#include &lt;mi_test/unittest++/TestReporterStdout.h&gt;

int main()
{
  return UnitTest::RunAllTests();
}</pre>
<p>Each <em>tests</em> directory in the libraries included in the MereIdea Main Projects will contain one or more test files (like <em>test_hello.cxx</em>) and one main file (like <em>test_hello_main.cxx</em>).</p>
<p>Finally, the <em>CMakeLists.txt</em> file in <em>mi_hello/tests</em> was edited for the new code:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #008000;">set(MI_HELLO_TEST_SOURCES
    test_hello_main.cxx
    test_hello.cxx
)</span>

<span style="color: #ff0000;">add_executable(test_hello test_hello.cxx)</span>
<span style="color: #008000;">add_executable(test_hello ${MI_HELLO_TEST_SOURCES})</span>

<span style="color: #999999;">target_link_libraries(test_hello</span> <span style="color: #008000;">miunittest++</span> <span style="color: #999999;">mihello)</span>

<span style="color: #008000;">add_test(test_hello_tester test_hello)</span></pre>
<p>Now the code can be built and tested from within the build directory with the familiar:</p>
<pre>&gt; cmake ../src
&gt; make
&gt; make test</pre>
<p>All tests should pass with some output similar to:</p>
<pre>Running tests...
Test project /home/user/code/mereidea/mi_main/build
     Start 1: unittest++_tester
1/2 Test #1: unittest++_tester ................   Passed    0.10 sec
     Start 2: test_hello_tester
2/2 Test #2: test_hello_tester ................   Passed    0.01 sec

100% tests passed, 0 tests failed out of 2

Total Test time (real) =   0.12 sec</pre>
<p>This is the output from <strong>CTest</strong> (part of <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org">CMake</a>), which is called when using <em>make</em> with the <em>test</em> target. It shows this summary by default, which will be enough for displaying in<br />
<a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a> as it will show which library test has the failure. It would then be up to the developer to go to that library&#8217;s test directory and type <em>ctest -V</em> or, with<br />
<a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org">CMake</a> version 2.8 and later, <em>ctest &#8211;output-on-failure</em>. This will give more information about the problem test.</p>
<hr style="width: 50%;" />
<h2>Updating Buildbot to use new tests</h2>
<p>The last step in the new testing framework is to update the factories in the<br />
<a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a> <em>Master</em> to call <em>make test</em>:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #ff0000;">rtest = shell.Test(command='mi_hello/tests/test_hello')</span>
<span style="color: #008000;">rtest = shell.Test(command='ctest')</span>
<span style="color: #ff0000;">wrtest = shell.Test(command='mi_hello\\tests\\test_hello')</span>
<span style="color: #3366ff;">...</span>
<span style="color: #ff0000;">fwchg.addStep(wrtest)</span>
<span style="color: #008000;">fwchg.addStep(rtest)</span>
<span style="color: #3366ff;">...</span>
<span style="color: #ff0000;">fwngt.addStep(wrtest)</span>
<span style="color: #008000;">fwngt.addStep(rtest)</span>
<span style="color: #3366ff;">...</span></pre>
<p>Note that the factory uses <em>ctest</em> instead of <em>make test</em>. The two are equivalent in the<br />
<a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org">CMake</a> world. Calling <em>ctest</em> means that the Windows and Linux commands can be identical. Otherwise <em>make test</em> would need to be called on Linux, and <em>nmake test</em> on Windows. Calling <em>nmake test</em> would also mean that another script would have to be created to set up the <a href="http://www.microsoft.com/express/">Visual Studio</a> environment before calling NMake.</p>
<hr style="width: 50%;" />
<h2>Fixing Visual Studio problems</h2>
<p>Once the new testing framework was committed, the <em>Master</em> configuration updated and the <em>Master</em> restarted, the code was built and tested using the Linux and Windows builders. Two problems arose in the <a href="http://www.microsoft.com/express/">Visual Studio</a> build: There were some warnings about <strong>strcpy</strong> being insecure and an unhandled exception occurred in the <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> test suite.</p>
<p>The unhandled exception was tackled first. This problem stemmed from the fact that <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org">CMake</a>, by default, uses C++ exceptions without structured exceptions (switch <strong>/EHsc</strong>). This means that C++ exceptions will be caught (using <strong>throw</strong>, <strong>try</strong> and <strong>catch</strong>), but system exceptions will not.</p>
<p>To ensure all exceptions are handled as a test failure by <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> the exception switch to the compiler has to be changed to <strong>/EHa</strong>. This is done for all compiled programs (any part could use the <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> code) by setting the <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org">CMake</a> variable <strong>CMAKE_CXX_FLAGS</strong> to include the corrected switches:</p>
<pre><span style="color: #999999;"># The project is MI_MAIN, so variables like ${MI_MAIN_SOURCE_DIR} and
# ${MI_MAIN_BINARY_DIR} can be used
project(MI_MAIN)</span>

<span style="color: #008000;"># Ensure the make flags are as we need (/EHa on Windows)
if (MSVC)
  set(CMAKE_CXX_FLAGS "/DWIN32 /D_WINDOWS /W3 /Zm1000 /EHa /GR"
      CACHE
      STRING
      "Flags used by the compiler during all build types."
      FORCE)
else()
  set(CMAKE_CXX_FLAGS "-Wall -pedantic -ansi"
      CACHE
      STRING
      "Flags used by the compiler during all build types."
      FORCE)
endif(MSVC)</span></pre>
<p>This is done in the top-level (<em>src</em>) <em>CMakeLists.txt</em> file. Note that, while compiler flags are being set, the flags for use with <strong>gcc</strong> are also set. If somebody uses another compiler, any necessary flags should be set here.</p>
<p>The final change is to fix the warnings produced by the Windows build. These warnings occur because <strong>strcpy()</strong> can be unsafe, and <strong>strcpy_s()</strong> should be used instead. However, <strong>strcpy_s()</strong> is not cross-platform, so it won&#8217;t be used here! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  Instead, it will just be assumed that the authors of <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> use <strong>strcpy()</strong> in a safe way and this warning can be ignored. It is possible to define <strong>_CRT_SECURE_NO_WARNINGS</strong> in the preprocessor switches, but this assumes that everyone who uses <strong>strcpy()</strong> anywhere in the code will use it safely. Instead, it&#8217;s better to make programmers leave an &#8220;I have thought about this and think it&#8217;s safe&#8221; message when using an unsafe function. This is done by expecting developers to disable the warning locally.</p>
<p>There are only three files in which the warning occurs &#8211; <em>AssertException.cpp</em> in the top-level of the <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> code, and <em>RecordingReporter.h</em> and <em>TestDeferredTestReporter.cpp</em> in the <em>tests</em> sub-directory. In those files any call to <strong>strcpy()</strong> is surrounded by <strong>#pragma warning()</strong> macros; one to turn the warning off, and another to turn it back on again. For example, in <em>AssertException.cpp</em>:</p>
<pre><span style="color: #999999;">AssertException::AssertException(char const* description, →
char const* filename, int lineNumber)
    : m_lineNumber(lineNumber)
{
	using namespace std;</span>
<span style="color: #008000;">#ifdef _MSC_VER</span>
<span style="color: #008000;">#  pragma warning( disable : 4996 )</span>
<span style="color: #008000;">#endif</span>
<span style="color: #999999;">    strcpy(m_description, description);
    strcpy(m_filename, filename);</span>
<span style="color: #008000;">#ifdef _MSC_VER</span>
<span style="color: #008000;">#  pragma warning( default : 4996 )</span>
<span style="color: #008000;">#endif</span>
<span style="color: #999999;">}</span></pre>
<p>With the exception solved and the warnings removed from the build, the code can now be committed and the results viewed in the <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a> Waterfall.</p>
<hr style="width: 50%;" />
<h2>Final note</h2>
<p>To start adding new <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a> <em>Slaves</em> to the installation, it was necessary to rename the <strong>Builders</strong> more sensibly as a builder is needed for each machine to add. In doing this, the builds were moved to new directories and were rebuilt from scratch. The nightly build on Windows failed in the configuration step because the <a href="http://www.microsoft.com/express/">Visual Studio</a> environment wasn&#8217;t set up when <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org">CMake</a> was called and the compiler couldn&#8217;t be found. This hadn&#8217;t shown up before because  <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org">CMake</a> was run in a directory in which it had been previously run successfully (when the buildslave was run in a shell with the correct environment &#8211; before changing to a service). To fix this problem and to ensure anything like it is caught earlier in future, the <em>master.cfg</em> was changed to run a batch file for running  <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org">CMake</a> on Windows (similar to the <em>run_nmake</em> script) and an extra step was added to remove the <em>CMakeCache.txt</em> file on all nightly builds (when the code should be clobbered). The <em>run_cmake.bat</em> script is simply:</p>
<pre>call "C:\Program File\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" &rarr;
x86
cmake -G "NMake Makefiles" ../src</pre>
<p>and the changes to the <em>master.cfg</em> file were:</p>
<pre>...
<span style="color: #999999;">confg = shell.Configure(command="cmake ../src")</span>
<span style="color: #ff0000;">wconfg = shell.Configure(command=['cmake','-GNMake Makefiles',→
'../src'])</span>
<span style="color: #008000;">wconfg = shell.Configure(command='run_cmake')</span>
<span style="color: #999999;">rtest = shell.Test(command='ctest')</span>
<span style="color: #008000;">rcmake = shell.ShellCommand(command='rm -f CMakeCache.txt')
wrcmake = shell.ShellCommand(command='del /F /Q CMakeCache.txt')</span>
...
<span style="color: #999999;">flngt = factory.BuildFactory()
flngt.addStep(svnco)</span>
<span style="color: #008000;">f</span><span style="color: #008000;">lngt.addStep(rcmake)</span>
<span style="color: #999999;">flngt.addStep(confg)</span>
...
<span style="color: #999999;">fwngt = factory.BuildFactory()
fwngt.addStep(svnco)</span>
<span style="color: #008000;">fwngt.addStep(wrcmake)</span>
<span style="color: #999999;">fwngt.addStep(wconfg)</span>
...</pre>
<hr style="width: 50%;" />
<h2>Summary</h2>
<p>This post covered two major topics; setting up a <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a> <em>Slave</em> as a service on Windows, and putting the Mere Idea Main Projects&#8217; tests into the <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> framework.</p>
<p>The <em>Slaves</em> were installed as services following a <a title="Running Buildbot on Windows" href="http://buildbot.net/trac/wiki/RunningBuildbotOnWindows">page</a> on the <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a> website. The instructions on that <a title="Running Buildbot on Windows" href="http://buildbot.net/trac/wiki/RunningBuildbotOnWindows">page</a> were mostly correct, but a couple of problems arose because the script to install the service (<em>buildbot_service.py</em>) isn&#8217;t included in the <em>Slave</em> installer, so had to be copied and modified from the one in the <em>Master</em> installer. Reading the  <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a> mailing list, a change to rectify this may soon be made.</p>
<p>The use of <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> in the code was straightforward &#8211; copying the code into the <strong>mi_main</strong> source code repository and changing the local tests to use it. The tests could then be run by using <em>make test</em> or <em>ctest</em> once the code was built.</p>
<p>The <a title="Buildbot Automated Compile/Test System" href="http://buildbot.net/trac">Buildbot</a> <strong>Builders</strong> were then changed to carry out the testing correctly.</p>
<hr style="width: 50%" />
<h3>Repository Changes since last post</h3>
<p><strong>Revision 3 and 4</strong> added <a href="http://www.doxygen.org/" title="Doxygen - C++ Documentation System">Doxygen</a> support for the Main Projects code. The result can be seen <a href="http://www.mereidea.com/docs/mi_main/" title="Doxygen code documentation for MereIdea Main Project">here</a>.</p>
<p><strong>Revision 5</strong> added a README file on how to download and build the code (also available in the <a href="http://www.mereidea.com/docs/mi_main/build_inst.html>documentation</a>).</p>
<p><strong>Revision 6</strong> change the way the license and README are built by <a href="http://www.doxygen.org/" title="Doxygen - C++ Documentation System">Doxygen</a>.</p>
<p><strong>Revision 7</strong> changed the <a href="http://www.doxygen.org/" title="Doxygen - C++ Documentation System">Doxygen</a> revision filter to correctly display the current repository revision and the last revision at which a particular file was changed.</p>
<p><strong>Revision 8</strong> added the <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> code and changed the &#8216;hello&#8217; test.</p>
<p><strong>Revision 9</strong> fixed a problem with using targets containing &#8216;++&#8217; in their name by changing them to &#8216;pp&#8217;. The problem occurred with NMake.</p>
<p><strong>Revision 10 and 11</strong> fixed the problems mentioned in this post regarding building the <a title="UnitTest++ testing framework" href="http://unittest-cpp.sourceforge.net/">UnitTest++</a> code under Windows (using /EHa and #pragma).</p>
<p><strong>Revision 12</strong> added support for MinGW as the code won&#8217;t compile with the -pedantic flag set, so turn it off if compiling with MinGW.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=tw3L4Ukl6LI:JkqDUQ8mXvE:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=tw3L4Ukl6LI:JkqDUQ8mXvE:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=tw3L4Ukl6LI:JkqDUQ8mXvE:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=tw3L4Ukl6LI:JkqDUQ8mXvE:I9og5sOYxJI"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=I9og5sOYxJI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/MereIdeaDevelopmentBlog/~4/tw3L4Ukl6LI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.mereidea.com/blog/2010/09/02/buildbot-slaves-as-a-windows-service-and-adding-a-testing-framework-unittest/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.mereidea.com/blog/2010/09/02/buildbot-slaves-as-a-windows-service-and-adding-a-testing-framework-unittest/</feedburner:origLink></item>
		<item>
		<title>Adding code and creating Buildbot Slaves for CMake</title>
		<link>http://feedproxy.google.com/~r/MereIdeaDevelopmentBlog/~3/n0_3wiha7lo/</link>
		<comments>http://www.mereidea.com/blog/2010/08/18/adding-code-and-creating-buildbot-slaves-for-cmake/#comments</comments>
		<pubDate>Wed, 18 Aug 2010 17:11:12 +0000</pubDate>
		<dc:creator>chris</dc:creator>
				<category><![CDATA[Buildbot]]></category>
		<category><![CDATA[CMake]]></category>
		<category><![CDATA[gcc]]></category>
		<category><![CDATA[Microsoft Visual Studio]]></category>
		<category><![CDATA[Subversion]]></category>

		<guid isPermaLink="false">http://www.mereidea.com/blog/?p=1080</guid>
		<description><![CDATA[Current repository revision: 2 (Main) Code highlight key Grey: Code unchanged Red: Code removed Green: Code added Blue: Code not shown → Denotes the current line and the next are really one line The last post described how to create a Buildbot Master for compiling the MereIdea libraries. CMake is to be used to create [...]]]></description>
			<content:encoded><![CDATA[<h2><strong>Current repository revision: 2</strong> (<a href="http://www.mereidea.com/svn/main/trunk/">Main</a>)</h2>
<blockquote><p><span style="text-decoration: underline;">Code highlight key</span><br />
<span style="color: #808080;">Grey: Code</span><span style="color: #888888;"> unchanged</span><br />
<span style="color: #ff0000;">Red: Code removed</span><br />
<span style="color: #008000;">Green: Code added</span><br />
<span style="color: #3366ff;">Blue: Code not shown</span></p>
<p><span style="color: #3366ff;"><span style="color: #000000;">→ Denotes the current line and the next are really one line</span></span></p></blockquote>
<p>The <a title="Setting up a Buildbot Master with CMake" href="http://www.mereidea.com/blog/2010/08/13/setting-up-a-buildbot-master-with-cmake/">last post</a> described how to create a <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> <em>Master</em> for compiling the MereIdea libraries. <strong><a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org/" target="_blank">CMake</a> </strong>is to be used to create the makefiles to build the code. This post will describe how some simple code was created and committed to the <em>Main</em> <strong><a title="Subversion Version Control Software" href="http://subversion.apache.org/" target="_blank">Subversion</a></strong> repository (<a title="The Main MereIdea Subversion repository" href="http://www.mereidea.com/svn/main/trunk/" target="_blank">http://www.mereidea.com/svn/main/trunk/</a>), including the initial <a title="CMake Cross-Platform Makefile Generator" href="http://www.cmake.org/" target="_blank">CMake</a> files. Then a Linux <em>Slave</em> will be set up to check out and build the code. This will lead to an iterative process to correct the <em>Builder</em> commands in the <em>Master</em> to work with the Linux platform, before moving onto Windows and setting up a Windows based <em>Slave</em>.</p>
<p>An out-of-source build is preferred, as it avoids cluttering up the source tree and accidental commits of binary files to the code repository. The general directory structure for such code will be:</p>
<pre>code/
+-- build/
+-- src/
|    +-- CMakeLists.txt
|    +-- mi_lib1/
|    |    +-- CMakeLists.txt
|    |    +-- foo.h
|    |    +-- foo.cxx
|    |    +-- bar/
|    |    |    +-- CMakeLists.txt
|    |    |    +-- bar.h
|    |    |    +-- bar.cxx
|    |    |    +-- tests/
|    |    |    |    +-- CMakeLists.txt
|    |    |    |    +-- test_bar.cxx
|    |    +-- tests/
|    |    |    +-- CMakeLists.txt
|    |    |    +-- test_foo.cxx
|    +--  mi_lib2/
|    +--  mi_lib3/</pre>
<p>Everything within the <em>src/</em> part of the tree (not including the <em>src/ </em>directory itself) will be committed to the repository.</p>
<hr style="width: 50%;" />
<h2>Initial Code Tree</h2>
<p>With this in mind, a <em>src/</em> directory was created and a library directory (<em>mi_hello</em>) was added inside <em>src/</em>. The <em>mi_hello </em>library will always contain simple code (pretty much a C++ &#8216;Hello, World&#8217;) that can be used to test the compiler. It will be the first library built (with the exception of <em>mi_test</em> that will set up the testing framework) with at least one test to be linked. The files in here should be amended if C++ features are used in the other libraries which may or may not work on some compilers. Having this small library means you can get quick feedback as to whether the rest of the libraries should build (though not guaranteed) with your system.</p>
<p>The initial code that will be used to test the <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac">Buildbot</a></strong> set up was created in the actual directory structure:</p>
<pre>code/
+-- src/
|    +-- CMakeLists.txt
|    +-- LICENSE.txt
|    +-- mi_hello/
|    |    +-- CMakeLists.txt
|    |    +-- hello.cxx
|    |    +-- hello.h
|    |    +-- tests/
|    |    |    +-- CMakeLists.txt
|    |    |    +-- test_hello.cxx</pre>
<p>You can check out the code for yourself from <a title="The MereIdea Main repository" href="http://svn.mereidea.com/main/trunk/" target="_blank"><strong>http://svn.mereidea.com/main/trunk/</strong></a> as revision 2, and you should get this structure. This can be done with:</p>
<pre>svn co -r 2 http://www.mereidea.com/svn/main/trunk src</pre>
<p>The <em>LICENSE.txt </em>file simply contains a copy of the GNU General Public License under which the code is available.</p>
<p>The files <em>hello.h </em>and <em>hello.cxx</em> contain the definition of a class (<strong>mi_hello::Hello</strong>) with a single method:</p>
<pre>void display(const std::string &amp;disp_str = "Hello, World!") const;</pre>
<p>This method simply displays the string passed to it, or <em>Hello,  World!</em> if no other string is passed. The test file <em>test_hello.cxx</em> contains a <strong>main()<em> </em><span style="font-weight: normal;">function to call the </span>display()</strong> method of the <strong>mi_hello::Hello</strong> class. The code for this is:</p>
<pre>#include &lt;mi_hello/hello.h&gt;
int main()
{
  mi_hello::Hello h;
  h.display();
  h.display("Goodbye, World!");

  return 0;
}</pre>
<p>This should compile to an executable and be run when the <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> <em>Slave</em> runs. Later this code will be placed in a proper testing framework, but until the <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> code is working correctly it&#8217;s not necessary to complicate things! The more things added at any testing stage, the more places things could go wrong. Putting things together one at a time makes it easier to locate where any problem is.</p>
<hr style="width: 50%;" />
<h2>Initial CMake files</h2>
<p>Now that there is some code to compile, the <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org/" target="_blank"><strong>CMake</strong></a> files to help compile it can be added. The first file to add is the top-level <em>CMakeLists.txt</em> file, which defines the project and lists the sub-directories in which further <em>CMakeLists.txt </em>files exist to recursively create the make system. The <em>CMakeLists.txt</em> top-level file in the <strong>MereIdea Main </strong>code base is initially written as follows:</p>
<pre># Set the minimum version to &gt;= 2.6
cmake_minimum_required(VERSION 2.6)

# The project is MI_MAIN, so variables like ${MI_MAIN_SOURCE_DIR} and
# ${MI_MAIN_BINARY_DIR} can be used
project(MI_MAIN)

# Set up the top-level as an include directory so we can use
# #include &lt;library_name/optional_sub_dirs/file.h&gt;
# in the source to include a file
include_directories( ${MI_MAIN_SOURCE_DIR} )

# Now set the subdirectories to work through. Ensure these are done
# in the correct order
add_subdirectory (mi_hello)</pre>
<p>This will set up the project, the include directory and call each library sub-directory to build in turn. The only library so far is the <em>mi_hello</em> compiler test library. To build the <em>mi_hello</em> library requires a <em>CMakeLists.txt</em> file in the <em>mi_hello</em> sub-directory:</p>
<pre># Give this library its own project name so we can
# refer to its build and source structures
project(MI_HELLO)

# Add the source files here
set (MI_HELLO_SOURCES
     hello.h
     hello.cxx
    )

# And add the library
add_library(mihello ${MI_HELLO_SOURCES})

# Put the library in a lib sub-directory
# Do for any shared, static and module libraries
set_target_properties(mihello PROPERTIES
                      ARCHIVE_OUTPUT_DIRECTORY →
${MI_HELLO_BINARY_DIR}/lib
                      LIBRARY_OUTPUT_DIRECTORY →
${MI_HELLO_BINARY_DIR}/lib
                     )

# And build the tests
add_subdirectory(tests)</pre>
<p>This builds the <em>mi_hello</em> library, places it in a <em>lib</em> directory and descends into the <em>tests</em> directory. The <em>tests</em> directory then has the final <em>CMakeLists.txt</em> file for this little test:</p>
<pre># Add each test as an executable
# This may change to add_test later when the testing
# framework is added
add_executable(test_hello test_hello.cxx)

# Set the libraries each executable needs to link against
target_link_libraries(test_hello mihello)</pre>
<p>Now the makefiles can be generated by changing directory to the <em>build</em> directory and typing:</p>
<pre>&gt; cmake ../src
-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /use/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: →
/home/mi_user/code/mereidea/mi_main/build</pre>
<p>You should see something similar to the above on a Linux box &#8211; Windows is covered later. For more control over the settings of the build, run <strong>ccmake</strong> instead of <strong>cmake</strong> and the <strong><a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org/" target="_blank">CMake</a> </strong>interface will appear enabling you to alter the settings, such as the build type. The libraries and tests can now be built by simply typing <em>make</em> at the command line. The <em>mi_hello</em> library will end up in <em>build/mi_hello/lib</em>, and the test executable will end up in <em>build/mi_hello/tests</em>. Change to this tests directory and type:</p>
<pre>&gt; ./test_hello
Hello, World!
Goodbye, World!</pre>
<p>It all appears to have worked! Now we can commit the code to the repository, and test with <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> installation.</p>
<hr style="width: 50%;" />
<h2>Committing code to Subversion Repository</h2>
<p><strong><span style="font-weight: normal;">The code is to be committed to the <a title="MereIdea Main Subversion repository" href="http://svn.mereidea.com/main/trunk/" target="_blank">MereIdea Main</a> repository. Before checking it in it is worth making a small note about <a title="Subversion Version Control System" href="http://subversion.apache.org/" target="_blank">Subversion</a> and cross platform code.</span></strong></p>
<p><strong><span style="font-weight: normal;">One of the annoyances of transferring text files (such as code files) from one platform to another has been the different line ending codes. For example, where Windows uses the carriage return and line feed ending (\r\n), Linux uses just the line feed (\n). <a title="Subversion Version Control System" href="http://subversion.apache.org/" target="_blank">Subversion</a>, however, can solve this problem for you to some extend by enabling you to set the <em>eol-style</em> property to <em>native</em> on the files being committed. The good thing is that, rather than having to set this property on every file you commit, you can set the <em>eol-style</em> property (and any other property) automatically for different types (or specific names) of files. This is set in the <a title="Subversion Version Control System" href="http://subversion.apache.org/" target="_blank">Subversion</a> <em>conf</em> file. On Linux this can be found in your home directory inside a sub-directory named <em>.subversion</em>, on Windows Vista it is in somewhere like <em>C:\Users\mi_user\AppData\Roaming\Subversion</em>, and on Windows XP it is somewhere like <em>C:\Documents And Settings\Chris\Application Data\Subversion. </em>Near the end of the configuration file is the line:</span></strong></p>
<pre># enable-auto-props = yes</pre>
<p>This is commented out by default, so simply remove the <em>#</em> to turn on the automatic properties. Then just set up the properties required on the specific file types:</p>
<pre>### Section for configuring automatic properties.
[auto-props]
### The format of the entries is:
###   file-name-pattern = propname[=value][;propname[=value]...]
### The file-name-pattern can contain wildcards (such as '*' and
### '?').  All entries which match will be applied to the file.
### Note that auto-props functionality must be enabled, which
### is typically done by setting the 'enable-auto-props' option.
*.c = svn:eol-style=native
*.cpp = svn:eol-style=native
*.cxx = svn:eol-style=native
*.h = svn:eol-style=native
*.txt = svn:eol-style=native
SConstruct = svn:eol-style=native
SConscript = svn:eol-style=native
SConfig = svn:eol-style=native
*.cmake = svn:eol-style=native
*.py = svn:eol-style=native
*.sh = svn:eol-style=native;svn:executable
Makefile = svn:eol-style=native
*.png = svn:mime-type=image/png
*.jpg = svn:mime-type=image/jpeg</pre>
<p>This is an example that includes <a title="SCons Cross Platform Build System" href="http://www.scons.org/" target="_blank">SCons</a> files used in other MereIdea projects. These changes only need to be made if you are planning to submit code to the repository.</p>
<p>Now the code can be committed easily with the command (assuming that the files are in a directory named <em>src</em>):</p>
<pre>&gt; svn import src http://www.mereidea.com/svn/main/trunk →
-m "Initial import"</pre>
<p>Then this can be checked out again (importing doesn&#8217;t alter the <em>src</em> tree to become a working directory) by removing the existing source directory and typing:</p>
<pre>&gt; svn co http://www.mereidea.com/svn/main/trunk src</pre>
<p>After checking it still builds and runs the code is now ready to set up with <a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a>.</p>
<h2>Setting up a Buildbot Slave</h2>
<p>After committing the code and waiting a few minutes (the <a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a> <em>Master</em> <em>SVNPoller</em> has a <strong>pollinterval</strong> of an hour) it can be seen that the <em>Master</em> knows it has to do some builds:</p>
<p><img title="The builders after an initial commit" src="http://www.mereidea.com/blog_ims/builders2.png" alt="The builders after an initial commit" width="429" height="231" /></p>
<p>It can also be seen that there are five nightly builds queued up as the <em>Master</em> was started a few days ago! Now the Linux <em>Slave</em> can be started and an iterative process of getting the <strong>Builder</strong> commands correct can begin.</p>
<p>Using the same Linux user as used to set up the <em>Master </em>(and after checking this user can check out and build the code)<em>, </em>a directory name <em>slave</em> is created alongside the one named <em>master</em>. This was configured as a <em>Slave</em> with the command:</p>
<pre>&gt; buildslave create-slave ~/Buildbot/slave/ www.mereidea.com:9989 →
oSUSE_11.2_x86_64_gcc password1</pre>
<p>This creates a directory (<em>info</em>) containing two configuration files that needed editing. These were <em>admin </em>which was edited to include an administrator name and email address for this <em>Slave</em>, and <em>host</em> in which some system information was entered (this can be seen at <a title="The details of the openSUSE build slave" href="http://builds.mereidea.com/buildslaves/oSUSE_11.2_x86_64_gcc" target="_blank">http://builds.mereidea.com/buildslaves/oSUSE_11.2_x86_64_gcc</a> &#8211; the details under the heading <em>Administrator</em> were entered in the <em>admin</em> file, and the details under <em>Slave Information</em> were entered in the <em>host</em> file). Then, inside the <em>slave</em> directory, the <em>Slave</em> can be started with:</p>
<pre>&gt; buildslave start</pre>
<p>This ran the pending builds on the new <em>Slave</em> and put the results on the web pages.</p>
<hr style="width: 50%;" />
<h2>Fixing the Linux Build</h2>
<p>The Linux build succeeded in checking out the code, but the <em>Configure</em> step of running <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org/" target="_blank">CMake</a> failed. This is because when the code is checked out from the repository it is put in a directory named <em>build</em>.  This can be fixed by amending the <em>source.SVN</em> by adding the <em>workdir</em> option:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #808080;">svnup = source.SVN(mode='update',
                   baseURL=svnbaseurl,
                   defaultBranch='trunk'</span><span style="color: #ff0000;">)</span><span style="color: #008000;">,
                   workdir='src')</span>
<span style="color: #808080;">svnco = source.SVN(mode='clobber',
                   baseURL=svnbaseurl,
                   defaultBranch='trunk'</span><span style="color: #ff0000;">)</span><span style="color: #008000;">,
                   workdir='src')</span>
<span style="color: #3366ff;">...</span></pre>
<p>This will change the default directory to be <em>src</em> for the SVN checkout, but the configure step will still use <em>build</em> by default. The <em>Master</em> is then restarted to take account of the change, and a build forced (<em>forceBuild</em> was set temporarily to <em>True</em> on the <em>WebStatus</em> &#8211; set this in the <em>authz_cfg</em> variable in the <em>master.cfg</em>).</p>
<p>The whole thing now compiles, but the test fails. That is because a place holder (<em>run_tests</em>) was given as the command to execute for the test phase. This was changed to:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #ff0000;">rtest = shell.Test(command='run_tests')</span>
<span style="color: #008000;">rtest = shell.Test(command='mi_hello/tests/test_hello')</span>
<span style="color: #3366ff;">...</span></pre>
<p>The Linux build is now clean, and the <em>Web Status</em> shows green for both the <em>Linux-change-build</em> and the <em>Linux-nightly-build</em>.</p>
<hr style="width: 50%;" />
<h2>Setting up the Windows Slave</h2>
<p>Now the Linux build works with the simple code it is time to set up a <em>Slave</em> on the Windows machine and fix any problems with it.</p>
<p>The first step was to install all of the code necessary for <a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a> to work. This was done by simply following the instructions on <a title="How to install Buildbot (or buildslave) on Windows" href="http://buildbot.net/trac/wiki/RunningBuildbotOnWindows" target="_blank">this page</a> with a couple of changes:</p>
<ul>
<li> Only the buildslave code is needed, so only <a title="Buildbot Slave code" href="http://downloads.sourceforge.net/buildbot/buildbot-slave-0.8.1.zip" target="_blank">buildbot-slave-0.8.1.zip</a> was downloaded and extracted.</li>
<li>The installed <a title="Python Programming Language" href="http://www.python.org/" target="_blank">Python</a> was 2.6.5, so in step 2 it was <em>C:\Python26</em> and <em>C:\Python26\scripts</em> that were added to the path variable</li>
<li>After installing <a title="Twisted Networking for Python" href="http://twistedmatrix.com/trac/" target="_blank">Twisted</a> and typing <em>trial &#8211;version</em> an <em>ImportError</em> occurred stating that the zope interface was missing. The <a title="Zope interface for Python" href="http://www.zope.org/Products/ZopeInterface" target="_blank">Zope</a> website didn&#8217;t seem to have an installer for <a title="Python Programming Language" href="http://www.python.org/" target="_blank">Python</a> 2.6, but luckily  a <a title="Python Programming Language" href="http://www.python.org/" target="_blank">Python</a> egg was available for 2.6 <a title="The zope egg for Python" href="http://pypi.python.org/pypi/zope.interface" target="_blank">here</a>.</li>
<li>As the <a title="Zope interface for Python" href="http://www.zope.org/Products/ZopeInterface" target="_blank">Zope</a> interface was in egg form, setuptool (or distribute) needed to be installed so that <em>easy_install</em> could be used with the egg. Distribute was found <a title="Distribute installation tools for Python" href="http://pypi.python.org/pypi/distribute" target="_blank">here</a> along with installation instructions.</li>
<li>Once the <a title="Zope interface for Python" href="http://www.zope.org/Products/ZopeInterface" target="_blank">Zope</a> egg was installed using <em>easy_install</em> then the command <em>trial &#8211;version</em> returned the <a title="Twisted Networking for Python" href="http://twistedmatrix.com/trac/" target="_blank">Twisted</a> version number.</li>
<li>It wasn&#8217;t necessary to alter the buildslave.bat file as detailed in step 9.</li>
</ul>
<p>Once the buildslave code was installed, it was time to try a checkout and build of the code on the command line prior to setting up the <em>Slave</em>. First a batch file was created to call the appropriate <em>vcvars</em> file. This file just had the single line:</p>
<pre>"c:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86</pre>
<p>in it and was named <em>setvcvars.bat</em> (the name in the <em>master.cfg</em> file). It was placed in the directory <em>C:\bin</em> which is on the executable path so it can be executed by the <em>Slave</em>. The binary <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org/" target="_blank">CMake</a> directory was also placed in the path variable.</p>
<p><a title="TortoiseSVN Windows SVN client" href="http://tortoisesvn.tigris.org/" target="_blank">TortoiseSVN</a> is usually used for handling <a title="Subversion Version Control System" href="http://subversion.apache.org/" target="_blank">Subversion</a> on MereIdea Windows boxes, but this doesn&#8217;t come with a command line client, so <a title="SlikSVN command line SVN client for Windows" href="http://www.sliksvn.com/en/download" target="_blank">SlikSVN</a> was installed. Now the test could begin with the sequence of commands the <em>Slave</em> would be asked to execute on the Windows machine:</p>
<pre>&gt; setvcvars.bat
Setting environment for using Microsoft Visual Studio 2010 x86 tools.
&gt; svn co http://www.mereidea.com/svn/main/trunk src
A    src\LICENSE.txt
A    src\mi_hello
...
&gt; cd build
&gt; cmake -G "NMake Makefiles" ../src
-- The C compiler identification is MSVC
-- The CXX compiler identification is MSVC
...
-- Configuring done
-- Generating done
-- Build files have been written to: C:/code/mi_main/build
&gt; nmake
...
&gt; mi_hello\tests\test_hello
Hello, World!
Goodbye, World!</pre>
<p>So the command set works fine! Two concerns are whether the commands are all run in the same environment (i.e. will the <em>setvcvars</em> call be persistent), and will the forward slashes in the <em>shell.Test</em> function be converted by <a title="Python Programming Language" href="http://www.python.org/" target="_blank">Python</a>?<br />
The only way to find out is to set up the <em>Slave</em> and see what happens. A directory was created to house the <em>Slave</em>, and then the same <em>buildslave</em> command was run as on Linux:</p>
<pre>&gt; buildslave create-slave C:\code\Buildbot\slave\ →
www.mereidea.com:9989 WinXP_VS2010 password2</pre>
<p>The created files in the <em>slave\info</em> sub-directory (<em>admin</em> and <em>host</em>) were edited as with the Linux <em>Slave</em>, and then the <em>Slave</em> was started by running:</p>
<pre>&gt; buildslave start</pre>
<p>in the <em>S</em><em>lave</em> directory.</p>
<blockquote><p><strong>Note: </strong><em>The command &#8216;buildslave start&#8217; on Windows never returns to the command prompt as it runs in the foreground.</em></p></blockquote>
<p>Once the slave was started, the queued builds were run. The result was that the configure step (<a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org/" target="_blank">CMake</a>) failed. When looking at the output from the configure it was seen that the <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org/" target="_blank">CMake</a> error produced was:</p>
<pre>CMake Error: Could not create named generator "NMake</pre>
<p>This shows that the command wasn&#8217;t being passed correctly to the command line. The <em>-G &#8220;NMake Makefiles&#8221;</em> was being split at the space after <em>NMake</em>. The first thing tried was to change the configure step in the <em>master.cfg</em> to escape the quotes:</p>
<pre><span style="color: #808080;">wconfg = shell.Configure(command='cmake -G </span><span style="color: #008000;">\</span><span style="color: #808080;">"NMake Makefiles</span><span style="color: #008000;">\</span><span style="color: #808080;">" →
../src')</span></pre>
<p>This made no difference, and using a double set of quotes (<em>&#8220;&#8221;NMake Makefiles&#8221;"</em>) only changed the error to:</p>
<pre>CMake Error: Could not create named generator ""NMake</pre>
<p>Next the command was split up into command and arguments:</p>
<pre><span style="color: #008000;"><span style="color: #3366ff;">...</span></span>
<span style="color: #008000;"><span style="color: #ff0000;">wconfg = shell.Configure(command='cmake -G "NMake Makefiles" ../src')</span></span>
<span style="color: #008000;">wconfg = shell.Configure(command=</span><span style="color: #008000;">['cmake', '-G "NMake Makefiles"', →
'../src']</span><span style="color: #008000;">)</span>
<span style="color: #3366ff;">...</span></pre>
<p>This changed the error to:</p>
<pre>CMake Error: Could not create named generator  "NMake Makefiles"</pre>
<p>So now it became apparent that the quotes were being passed to <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org/" target="_blank">CMake</a> as part of the argument. The quotes were removed altogether, and the error changed to:</p>
<pre>CMake Error: Could not create named generator  NMake Makefiles</pre>
<p>This looks correct now, so what was the problem? The answer is there are two spaces between <em>generator</em> and <em>NMake</em> in the error message! A space seems to be passed to <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org/" target="_blank">CMake</a> as part of the generator name! A final change was made to the <em>master.cfg</em> to fix this:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #ff0000;">wconfg = shell.Configure(command=['cmake', '-G NMake Makefiles', →
'../src'])</span>
<span style="color: #008000;">wconfg = shell.Configure(command=['cmake', '-GNMake Makefiles', →
'../src'])</span>
<span style="color: #3366ff;">...</span></pre>
<p>This seems strange, but fixes the problem. There must be a reason for this, and at least part of the blame must go to <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org/" target="_blank">CMake</a> for thinking that using generator names containing spaces was a good idea! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>When the <em>Master</em> was restarted, and the build was forced again, the configure and compile passed, but the test failed. The reason for this was simple, and was easily fixed. Windows paths used backslashes, so the test execution requires backslashes. The change was made to the <em>master.cfg</em>:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #808080;">rtest = shell.Test(command='mi_hello/tests/test_hello')</span>
<span style="color: #008000;">wrtest = shell.Test(command='mi_hello\\tests\\test_hello')</span>
<span style="color: #3366ff;">...</span>
<span style="color: #ff0000;">fwchg.addStep(rtest)</span>
<span style="color: #008000;">fwchg.addStep(wrtest)</span>
<span style="color: #3366ff;">...</span>
<span style="color: #ff0000;">fwngt.addStep(rtest)</span>
<span style="color: #008000;">fwngt.addStep(wrtest)</span>
<span style="color: #3366ff;">...</span></pre>
<p>This fixes the test, and all builders now work!</p>
<p><img title="All builders fully working!" src="http://www.mereidea.com/blog_ims/builders3.png" alt="All builders fully working!" width="435" height="200" /></p>
<hr style="width: 50%;" />
<h2>Summary</h2>
<p>This post describes the basics of setting up <a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a> <em>Slave</em>s on both Linux and Windows machines, using <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org/" target="_blank">CMake</a> as part of the build system. Some example code was created, along with the <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org/" target="_blank">CMake</a> files to generate the <em>Makefiles</em> necessary to build it. This was then committed to the MereIdea <a title="MereIdea Main Subversion repository" href="http://www.mereidea.com/svn/main/trunk/" target="_blank">Main</a> repository as Revision 2.</p>
<p>A <a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a> <em>Slave </em>was then created on the Linux box and errors in the build which occurred when it was started were fixed. The Windows <em>Slave </em>was then set up, which included installing all the necessary components, and errors resulting from this <em>Slave</em> were fixed.</p>
<p>There are still some outstanding issues to address in the next post:</p>
<ul>
<li>A proper testing framework needs to be set up and integrated into the configure/compile/test cycle.</li>
<li>As the Windows <em>Slave</em> never returns from running, the environment under which the <em>Slave</em> is run should be used for running subsequent commands. This means if the <em>Slave</em> was run under a Visual Studio 2010 command prompt <em>setvcvars</em> may not be needed.</li>
<li>The Windows <em>Slave</em> could be set up as a service so that it will be run in the background. This is likely to require the <em>setvcvars</em> step to remain.</li>
</ul>
<p>All of these issues will be covered in the next post, which should complete the initial set up of the <a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a> system.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=n0_3wiha7lo:5HvMTm8Qcnk:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=n0_3wiha7lo:5HvMTm8Qcnk:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=n0_3wiha7lo:5HvMTm8Qcnk:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=n0_3wiha7lo:5HvMTm8Qcnk:I9og5sOYxJI"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=I9og5sOYxJI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/MereIdeaDevelopmentBlog/~4/n0_3wiha7lo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.mereidea.com/blog/2010/08/18/adding-code-and-creating-buildbot-slaves-for-cmake/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.mereidea.com/blog/2010/08/18/adding-code-and-creating-buildbot-slaves-for-cmake/</feedburner:origLink></item>
		<item>
		<title>Setting up a Buildbot Master With CMake</title>
		<link>http://feedproxy.google.com/~r/MereIdeaDevelopmentBlog/~3/D8gOevAXWik/</link>
		<comments>http://www.mereidea.com/blog/2010/08/13/setting-up-a-buildbot-master-with-cmake/#comments</comments>
		<pubDate>Fri, 13 Aug 2010 12:43:40 +0000</pubDate>
		<dc:creator>chris</dc:creator>
				<category><![CDATA[Buildbot]]></category>
		<category><![CDATA[CMake]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.mereidea.com/blog/?p=1025</guid>
		<description><![CDATA[Code highlight key Grey: Code unchanged Red: Code removed Green: Code added Blue: Code not shown → Denotes the current line and the next are really one line It&#8217;s been a while since a new post graced this blog. Unfortunately (for the blog, at least), other work has got in the way and it is [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p><span style="text-decoration: underline;">Code highlight key</span><br />
<span style="color: #808080;">Grey: Code</span><span style="color: #888888;"> unchanged</span><br />
<span style="color: #ff0000;">Red: Code removed</span><br />
<span style="color: #008000;">Green: Code added</span><br />
<span style="color: #3366ff;">Blue: Code not shown</span></p>
<p><span style="color: #3366ff;"><span style="color: #000000;">→ Denotes the current line and the next are really one line</span><br />
</span></p></blockquote>
<p>It&#8217;s been a while since a new post graced this blog. Unfortunately (for the blog, at least), other work has got in the way and it is now it is necessary to start a new, year long project producing some software and an accompanying website. To write this new software <a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org" target="_blank"><strong>CMake</strong></a> is going to be used as the build system (actually, <strong><a title="Cross Platform Makefile Generator" href="http://www.cmake.org" target="_blank">CMake</a></strong> is only a Makefile/Project file generator, so the build system will be whatever is native to the operating system &#8211; <strong>make</strong> and <strong><a title="Microsoft Visual Studio (Express)" href="http://www.microsoft.com/express/" target="_blank">Visual Studi</a></strong><strong><a title="Microsoft Visual Studio (Express)" href="http://www.microsoft.com/express/">o</a></strong><strong> &#8211; </strong>and <strong>nmake -</strong> in this case).</p>
<p><strong><a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org" target="_blank">CMake</a></strong><strong> </strong>wasn&#8217;t chosen because it&#8217;s any better (or worse) than <a title="SCons Cross Platform Build System" href="http://www.scons.org" target="_blank"><strong>SCons</strong></a>, but because of familiarity with it and the need to create an actual project rather than experimenting with <a title="The MereIdea Example Code" href="http://www.mereide.com/blog/example-code">example code</a>.<strong> </strong>This, sadly, means setting the <strong><a title="SCons Cross Platform Build System" href="http://www.scons.org" target="_blank">SCons</a></strong> tests on one side for the moment.</p>
<p>It was hoped the new <strong><a title="SCons Cross Platform Build System" href="http://www.scons.org" target="_blank">SCons</a></strong><strong> 2.0</strong> series could be tested using the example code and different configurations on different platforms, but this work is now on hold. It is recommended, however, that anyone considering <strong><a title="SCons Cross Platform Build System" href="http://www.scons.org" target="_blank">SCons</a></strong> to try out the new <strong>2.0</strong> series.</p>
<p>When it is thought that some element of the new project is worth blogging about, then this blog will be updated, hopefully frequently. Updates are also available via the <a title="MereIdea @ Twitter" href="http://www.twitter.com/mereidea">Twitter</a> account.</p>
<p>One thing that was on the original path was the automated compile/test of the example code using <a title="The Buildbot automated compile/test software" href="http://buildbot.net/trac" target="_blank"><strong>Buildbot</strong></a>. Originally, this blog was to detail the integration of <strong><a title="SCons Cross Platform Build System" href="http://www.scons.org" target="_blank">SCons</a></strong> with <strong><a title="Buildbot The Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong>, but now will describe the integration of <strong><a title="CMake Cross Platform Makefile Generator" href="http://www.cmake.org" target="_blank">CMake</a></strong> instead. This first post in the mini-series describes the setting up and configuration of the <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong><strong> </strong><em>Master</em>.</p>
<p><strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> is made up of two main components. The <em>Master</em> which decides when things need to be built, and collates and displays the results via web pages; and the <em>Slave</em> which is an individual machine that performs the building of the code and sends the results of the build to the <em>Master</em>. There is one <em>Master</em> and one or more <em>Slaves</em>. The more <em>Slaves </em>on differently configured machines the better!</p>
<hr style="width: 50%;" />
<h2>Installation</h2>
<p>The <em>Master</em> is going to be set up on a machine running <a title="openSUSE Linux Operating System" href="http://www.openSUSE.org"><strong>openSUSE</strong></a> x86_64 version 11.2. <strong><a title="Buildbot Automated Compile/Test software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> is written in <strong><a title="Python Programming Language" href="http://www.python.org" target="_blank">Python</a></strong>, so you need to install <strong><a title="Python Programming Language" href="http://www.python.org" target="_blank">Python</a></strong> (usually included by the default <strong>SUSE</strong> installation) first. The <strong><a title="Python Programming Language" href="http://www.python.org" target="_blank">Python</a></strong> version installed is <strong>2.6.2</strong>. <a title="Twisted Python Networking" href="http://twistedmatrix.com/trac/"><strong>Twisted</strong></a> also needs to be installed for <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac">Buildbot</a></strong>&#8216;s networking functionality. This was installed through <em>YaST</em> (<strong>SUSE</strong>&#8216;s system admin client) which installed <strong><a title="Twisted Python Networking" href="http://twistedmatrix.com/trac/" target="_blank">Twisted</a></strong> version <strong>8.2.0</strong>. <strong><a title="Python Programming Language" href="http://www.python.org/" target="_blank">Python</a></strong> and <strong><a title="Twisted Python Networking" href="http://twistedmatrix.com/trac/" target="_blank">Twisted</a></strong><strong> </strong>are required in systems running either the <em>Master</em> or the <em>Slave</em>. There are some further pre-requisites for the machine running the <em>Master</em>.</p>
<p><strong><a title="SQLite Cross Platform Database Engine" href="http://www.sqlite.org/" target="_blank">sqlite3</a></strong> and <strong><a title="Python JSON encoder/decoder" href="http://code.google.com/p/simplejson/" target="_blank">simplejson</a></strong> are included with the <strong><a title="Python Programming Language" href="http://www.python.org/" target="_blank">Python</a></strong> version installed, so no further action was needed for this. <strong><a title="Jinja2 Template Language" href="http://jinja.pocoo.org/2/">Jinja2</a></strong>, however, needed to be downloaded and installed. Actually, it can be downloaded and installed using the command:</p>
<pre>&gt; easy_install Jinja2</pre>
<p>but instead the source was downloaded, decompressed and installed from the source <em>tar</em>. Once downloaded and decompressed, the installation can be done as <em>root </em>using the command:</p>
<pre>&gt; python setup.py install</pre>
<p>in the extracted directory. This, however, caused a problem as the <strong><a title="Python Programming Language" href="http://www.python.org/">Python</a></strong><strong> </strong><em>setuptools</em> module was not installed. A derivative of <em>setuptools</em> (<em>distribute</em>) was installed using the commands:</p>
<pre>&gt; curl -O http://python-distribute.org/distribute_setup.py
&gt; python distribute_setup.py</pre>
<p>Then <strong><a title="Jinja2 Template Language" href="http://jinja.pocoo.org/2/" target="_blank">Jinja2</a></strong> could be installed correctly using the command as above.</p>
<p>Now everything is ready to install <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong>. The version of <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> used is <strong>0.8.1</strong>. Both the <em>Master</em> and <em>Slave</em> tar.gz files were downloaded and decompressed. These were then installed by switching to each of the extracted directories and typing:</p>
<pre>&gt; python setup.py build
&gt; python setup.py install</pre>
<p>The installations can then be tested. Firstly to check the version numbers:</p>
<pre>&gt; buildbot --version
Buildbot version: 0.8.1
Twisted version: 8.2.0

&gt; buildslave --version
Buildslave version: 0.8.1
Twisted version: 8.2.0</pre>
<p>and then by running the tests:</p>
<pre>&gt; PYTHONPATH=. trial buildbot.test</pre>
<p>Unfortunately, running the above test suite showed two errors. These errors were both of type <em>ImportError </em>relating to the <em>mock</em> module missing from <strong><a title="Python Programming Language" href="http://www.python.org/">Python</a></strong>. The <em>mock</em> module was installed using:</p>
<pre>&gt; easy_install mock</pre>
<p>The tests could then be run without error for both the <em>Master</em> and <em>Slave</em> components:</p>
<pre>&gt; PYTHONPATH=. trial buildbot.test
&gt; PYTHONPATH=. trial buildslave.test</pre>
<hr style="width: 50%;" />
<h2>Creating the Master</h2>
<p>Finally the <em>Master</em> is ready to be set up.</p>
<p>Firstly a new user was created to act as the <em>Master</em> administrator. This was done through <em>YaST</em>, but could easily be done with <em>useradd</em>. This new user was then logged in. A new directory was created to house the <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac">Buildbot</a></strong><strong> </strong><em>Master </em>in the new user&#8217;s home directory:</p>
<pre>&gt; mkdir -p ~/Buildbot/master</pre>
<p><em><span style="font-style: normal;">This directory was then initialised as a new </span>Master</em> directory:</p>
<pre>&gt; buildbot create-master -r ~/Buildbot/master</pre>
<p>Now it&#8217;s ready for the configuration.</p>
<hr style="width: 50%;" />
<h2>Configuring the Master</h2>
<p>The <em>create-master</em> option to the <em>buildbot</em> command creates a set of files in the specified directory. One of these files (<em>master.cfg.sample) </em>is the configuration file which needs to be edited. First, a copy of the sample file is made:</p>
<pre>&gt; cp master.cfg.sample master.cfg</pre>
<p>This was then edited as follows (comments omitted):</p>
<pre><span style="color: #808080;">c = BuildmasterConfig = {}</span>

<span style="color: #808080;">c['db_url'] = "sqlite:///state.sqlite"</span>

<span style="color: #808080;">from buildbot.buildslave import BuildSlave</span>
<span style="color: #ff0000;">c['slaves'] = [BuildSlave("bot1name", "bot1passwd")]</span>
<span style="color: #008000;">c['slaves'] = [
                BuildSlave("oSUSE_11.2_x86_64_gcc","password1", →</span>
<span style="color: #008000;"> </span><span style="color: #008000;">max_builds=2),</span>
<span style="color: #008000;">                BuildSlave("WinXP_VS2010","password2",max_builds=2)
              ]</span>
<span style="color: #008000;"><span style="color: #808080;">c['slavePortnum'] = 9989</span></span></pre>
<p>This sets up the slaves on which the code will be built. More will be added to this list, but for now it will be tested with just these two. Obviously, the passwords need to be changed to something more secure! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  These passwords will be used later to set up the slaves. The <em>max_builds=2</em> also tells the <em>Master</em> not to send more than two concurrent builds to the <em>Slaves</em>. The <em>slavePortNum</em> is the port on which the <em>Master</em> will listen for <em>Slaves</em>, and so must match the port used when setting up a <em>Slave </em>later.</p>
<p>Now the method for detecting changes to the source code can be specified. As Subversion will be used for this project, SVNPoller is used to check for updates to the code:</p>
<pre><span style="color: #ff0000;">from buildbot.changes.pb import PBChangeSource</span>
<span style="color: #008000;">from buildbot.changes.svnpoller import SVNPoller</span>
<span style="color: #ff0000;">c['change_source'] = PBChangeSource()</span>
<span style="color: #008000;">source_svn_url='http://svn.mereidea.com/main/trunk'
svn_poller = SVNPoller( svnurl=source_svn_url,
                        pollinterval=3600, # seconds
                        histmax=10,
                        svnbin='/usr/local/bin/svn'
                      )
c['change_source'] = [ svn_poller ]</span></pre>
<p>So the <em>change_source </em>is changed from <em>PBChangeSource</em> to the <em>SVNPoller</em>. The <strong>main</strong> repository is one that can be read by anyone, but a username and password can be specified to the <em>SVNPoller</em> if necessary with the <em>svnuser</em> and <em>svnpasswd </em>options.</p>
<p>Next the <em>schedulers</em> can be defined. These tell the <em>Master</em> when to call the <em>Slaves</em> to build the code. Two types of <em>schedulers</em> are required &#8211; a change scheduler (of type <em>Scheduler</em>) called whenever the repository changes, and a nightly scheduler (of type <em>timed.Nightly</em>) to rebuild the code every morning at 03:30. The build for change scheduler will simply update the code and run a build, the nightly scheduler will check out a clean copy of the code and build from scratch. The configuration file is changed as follows:</p>
<pre><span style="color: #808080;">from buildbot.scheduler import Scheduler</span>
<span style="color: #008000;">from buildbot.schedulers import timed</span>
<span style="color: #808080;">c['schedulers'] = []</span>
<span style="color: #ff0000;">c['schedulers'].append(Scheduler(name="all", branch=None,
                                 treeStableTimer=2*60,
                                 builderNames=["buildbot-full"]))</span>
<span style="color: #008000;">c['schedulers'].append(Scheduler(name="change-build",branch=None,
                                 treeStableTimer=600,
                                 builderNames=[</span>
<span style="color: #008000;">                                        "Linux-change-build",</span>
<span style="color: #008000;">                                        "Win-change-build"</span>
<span style="color: #008000;">                                              ]
                                )
                       )
c['schedulers'].append(timed.Nightly(name="nightly-build",
                                     hour=3, minute=30,
                                     builderNames=[</span>
<span style="color: #008000;">                                            "Linux-nightly-build", </span>
<span style="color: #008000;">                                            "Win-nightly-build"</span>
<span style="color: #008000;">                                                  ]
                                    )
                      )</span></pre>
<p>This sets up the two required schedulers. The first (<em>change-build</em>) is triggered when the code is changed and detected with the <em>SVNPoller</em> set up earlier. Once triggered, it waits 10 minutes (<em>treeStableTimer=600</em>) to try to ensure all commits are complete before running the two builders &#8211; <em>Linux-change-build</em> and <em>Win-change-build</em>. The second (<em>nightly-build</em>) runs its builders &#8211; <em>Linux-nightly-build</em> and <em>Win-nightly-build</em> &#8211; at 3:30am (<em>hour=3, minute=30</em>).</p>
<p>This scheduler set up shows that four builders are going to be required &#8211; <em>Linux-change-build</em>, <em>Linux-nightly-build</em>, <em>Win-change-build</em>, and <em>Win-nightly-build</em>. The configuration is altered to include these builders as follows:</p>
<pre><span style="color: #ff0000;">cvsroot = ":pserver:anonymous@cvs.sourceforge.net:/cvsroot/buildbot"
cvsmodule = "buildbot"</span>
<span style="color: #808080;">from buildbot.process import factory</span>
<span style="color: #ff0000;">from buildbot.steps.source import CVS</span>
<span style="color: #ff0000;">from buildbot.steps.shell import Compile</span>
<span style="color: #008000;">from buildbot.steps import source, shell</span>
<span style="color: #ff0000;">from buildbot.steps.python_twisted import Trial</span>

<span style="color: #008000;">svnbaseurl='http://svn.mereidea.com/main/'
svnup = source.SVN(mode='update',
                   baseURL=svnbaseurl,
                   defaultBranch='trunk')
svnco = source.SVN(mode='clobber',
                   baseURL=svnbaseurl,
                   defaultBranch='trunk')</span>
<span style="color: #008000;">setvcvars = shell.ShellCommand(command="setvcvars.bat")
confg = shell.Configure(command="cmake ../src")
wconfg = shell.Configure(command='cmake -G "NMake Makefiles" ../src')
comp = shell.Compile(command='make')
wcomp = shell.Compile(command='nmake')
rtest = shell.Test(command='run_tests')</span>

<span style="color: #ff0000;">f1 = factory.BuildFactory()</span>
<span style="color: #ff0000;">f1.addStep(CVS(cvsroot=cvsroot, cvsmodule=cvsmodule, login="" →</span>
<span style="color: #ff0000;">mode="copy")) </span>
<span style="color: #ff0000;">f1.addStep(Compile(command=["python", "./setup.py", "build"])) </span>
<span style="color: #ff0000;">f1.addStep(Trial(testChanges=True, testpath="."))</span>

<span style="color: #008000;">flchg = factory.BuildFactory()
flchg.addStep(svnup)
flchg.addStep(confg)
flchg.addStep(comp)
flchg.addStep(rtest)

fwchg = factory.BuildFactory()</span>
<span style="color: #008000;">fwchg.addStep(setvcvars)
fwchg.addStep(svnup)
fwchg.addStep(wconfg)
fwchg.addStep(wcomp)
fwchg.addStep(rtest)

flngt = factory.BuildFactory()
flngt.addStep(svnco)
flngt.addStep(confg)
flngt.addStep(comp)
flngt.addStep(rtest)

fwngt = factory.BuildFactory()</span>
<span style="color: #008000;">fwngt.addStep(setvcvars)
fwngt.addStep(svnco)
fwngt.addStep(wconfg)
fwngt.addStep(wcomp)
fwngt.addStep(rtest)</span>

<span style="color: #ff0000;">b1 = {'name': "buildbot-full",
        'slavename': "bot1name",
        'builddir': "full",
        'factory': f1      }</span>

<span style="color: #008000;">blchg = {'name'="Linux-change-build",
         'slavename': "oSUSE_11.2_x86_64_gcc",
         'builddir': "linus_change_build",
         'factory': flchg
        }

bwchg = {'name'="Win-change-build",
         'slavename': "WinXP_VS2010",
         'builddir': "win_change_build",
         'factory': fwchg
        }

blngt = {'name'="Linux-nightly-build",
         'slavename': "oSUSE_11.2_x86_64_gcc",
         'builddir': "linux_nightly_build",
         'factory': flngt
        }

bwngt = {'name'="Win-nightly-build",
         'slavename': "WinXP_VS2010",
         'builddir': "win_nightly_build",
         'factory': fwngt
        }</span>

<span style="color: #ff0000;">c['builders'] = [b1]</span>
<span style="color: #008000;">c['builders'] = [blchg, bwchg, blngt, bwngt]</span></pre>
<p>The builders are set with the factories to execute a set of commands appropriate to the build and architecture. The commands in this post are really just place holders, as the correct commands to run this correctly on any machine can only be decided once a few tests runs have been tried. The commands may be correct, but it is unlikely! For example, the <em>setvcvars</em> step needs to be set up and checked for persistence for later commands (are all commands executed in the same shell environment?). Further slaves that match a builder&#8217;s commands can be added to the builder by changing <em>slavename</em> to <em>slavenames </em>and providing a list:</p>
<pre>'slavenames': ['WinXP_VS2010', 'Win7_VS2008'],</pre>
<p>The correct build commands will be worked out in the next blog post when <em>Slaves</em> will be set up, too.</p>
<p>The final bits of the master configuration are the status targets and the project identity. The status targets tell <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> where to display the status results of the builds. These can be as web pages, by email, by IRC, etc. For now the <strong>MereIdea </strong><strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> will just display as web pages, though later the emailer will probably be set. The defaults for this section of the configuration are, therefore, fine. The <em>http_port</em> of the <em>WebStatus</em> may be changed, but this must correspond to the port set in the project identity section.</p>
<p>The project identity section provides a description of the project and how the web display can be accessed from the outside world. Simply change to whatever you want:</p>
<pre><span style="color: #ff0000;">c['projectName'] = "Buildbot"</span>
<span style="color: #008000;">c['projectName'] = "MereIdea Projects"</span>
<span style="color: #ff0000;">c['projectURL'] = "http://buildbot.sourceforge.net"</span>
<span style="color: #008000;">c['projectURL'] = "http://www.mereidea.com"</span>
<span style="color: #ff0000;">c['buildbotURL'] = "http://localhost:8010"</span>
<span style="color: #008000;">c['buildbotURL'] = "http://builds.mereidea.com"</span></pre>
<p>These are pretty straightforward. If using the port number (8010) in the <em>buildbotURL</em>, this must match the <em>http_port </em>setting in the <em>WebStatus</em> of the status targets section of the configuration. In the above example, a redirect is set in the <strong>MereIdea<a title="Apache http server" href="http://httpd.apache.org/" target="_blank"> Apache</a></strong> web server to convert <em><a title="The MereIdea Buildbot Pages" href="http://builds.mereidea.com" target="_blank">http://builds.mereidea.com</a></em> to <em><a title="The MereIdea Buildbot Pages" href="http://www.mereidea.com:8010" target="_blank">http://www.mereidea.com:8010</a></em>.</p>
<p>That&#8217;s the configuration of the <em>Master</em> complete. Now it can be checked for validity with:</p>
<pre>&gt; buildbot checkconfig master.cfg</pre>
<p>This did produce a deprecation warning:</p>
<pre>/usr/lib64/python2.6/site-packages/twisted/mail/smtp.py:10: →
DeprecationWarning: the MimeWriter module is deprecated; →
use  the email package instead
  import MimeWriter, tempfile, rfc822
Config file is good!</pre>
<p>This warning comes from within the <strong><a title="Twisted Python Networking" href="http://twistedmatrix.com/trac/" target="_blank">Twisted</a></strong> code, so it&#8217;s just going to be ignored (possibly updating <strong><a title="Twisted Python Networking" href="http://twistedmatrix.com/trac/" target="_blank">Twisted</a></strong> would make it go away). Apart from that warning, <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> says the configuration is good! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  Finally, the <em>Master</em> can be started with the command (executed in the directory containing the master&#8217;s configuration):</p>
<pre>buildbot start</pre>
<p>Then it can be checked by using a web browser to look at the <em>buildbotURL</em>. For the configuration above this means browsing to <em><a title="The MereIdea Buildbot Pages" href="http://builds.mereidea.com" target="_blank">http://builds.mereidea.com</a></em>. The <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> welcome page should be displayed. Looking on the <em>Builders</em> page it can be seen that the four builders specified in the configuration file are set up, and that they are currently offline:</p>
<p><img title="Buildbot Builders Web Page" src="http://www.mereidea.com/blog_ims/builders.png" alt="Buildbot Builders Web Page" width="429" height="185" /></p>
<p>So it&#8217;s all working so far! However, when the <em>SVNPoller</em> tried to poll the svn server an error occurred:</p>
<pre>SVNPoller failed [Failure instance: Traceback (failure with →
no frames): &lt;class 'twisted.internet.utils. →
_UnexpectedErrorOutput'&gt;: got stderr: "svn: →
Repository moved temporarily to →
'http://www.mereidea.com/svn/main/trunk'; →
please relocate\n"
    ]</pre>
<p>It seems the redirection in the <strong><a title="Apache http Server" href="http://httpd.apache.org/" target="_blank">Apache</a></strong> server from <em><a title="The MereIdea SVN Repository" href="http://svn.mereidea.com/main" target="_blank">http://svn.mereidea.com</a></em> to <em><a title="The MereIdea SVN Repository" href="http://www.mereidea.com/svn/main" target="_blank">http://www.mereidea.com/svn</a></em> has caused problems with the <em>SVNPoller</em>. The problem was easily solved by changing the configuration file:</p>
<pre><span style="color: #808080;">from buildbot.changes.svnpoller import SVNPoller</span>
<span style="color: #ff0000;">source_svn_url='http://svn.mereidea.com/main/trunk'</span>
<span style="color: #008000;">source_svn_url='http://www.mereidea.com/svn/main/trunk'</span>
<span style="color: #3366ff;">...</span>
<span style="color: #ff0000;">svnbaseurl='http://svn.mereidea.com/main/'</span>
<span style="color: #008000;">svnbaseurl='http://www.mereidea.com/svn/main/'</span></pre>
<p>The <em>svnbaseurl</em> was also changed to reduce the chance of any further problems.</p>
<hr style="width: 50%;" />
<h2>Summary</h2>
<p>This post described setting up the <em>Master</em> side of the <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> automatic build/test cycle software. It involved installing the necessary <strong><a title="Python Programming Language" href="http://www.python.org/" target="_blank">Python</a></strong><strong> </strong>components, including <strong><a title="Twisted Python Networking" href="http://twistedmatrix.com/trac/" target="_blank">Twisted</a></strong> and <strong><a title="Jinja2 Template Language" href="http://jinja.pocoo.org/2/" target="_blank">Jinja2</a></strong>. Then the installation of <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> itself. A <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> user was created to administer and run the <em>Master</em> daemon. A directory (<em>~/Buildbot/master</em>) was created to house the <em>Master</em> server files and it was initialised using the <em>builbot</em> <em>create-master</em> command. This also created a <em>master.cfg.sample</em> file which was copied to <em>master.cfg</em> and edited as detailed in the post to create four builds &#8211; two each for two <em>Slaves</em>. These were Linux and Windows builds when the code base (SVN) changed, and Linux and Windows nightly builds. The <em>Master</em> was then started and checked by accessing the <em>WebStatus<strong> </strong><span style="font-style: normal;">through </span><a title="The MereIdea Buildbot Pages" href="http://builds.mereidea.com" target="_blank">http://builds.mereidea.com</a></em>.</p>
<p>The configuration of the <em>Master</em> is quite straightforward, though it seemed daunting when first looking at the documentation. This is because the documentation is written to cover all eventualities and give a thorough outline of all types of builders, schedulers, build factories, etc.. The documentation is good, but the easiest way to configure <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> (and the way used to devise the configuration for the MereIdea set up) is to work through the <em>master.cfg</em> file using the comments as a guide and the <strong><a title="Buildbot Automated Compile/Test Software" href="http://buildbot.net/trac" target="_blank">Buildbot</a></strong> documentation as a reference.</p>
<p>With the <em>Master</em> now running, the next step is to put some code in the SVN repository and set up the <em>Slaves</em>. A continuous (short <em>Periodic</em> build) will the be set up so that the commands to run on the <em>Slave</em> can be fine tuned until it builds correctly. First the Linux <em>Slave</em> will be set up, then the Windows <em>Slave</em>. This is the subject of the next post, coming soon!</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=D8gOevAXWik:soqR_x8so2M:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=D8gOevAXWik:soqR_x8so2M:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=D8gOevAXWik:soqR_x8so2M:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=D8gOevAXWik:soqR_x8so2M:I9og5sOYxJI"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=I9og5sOYxJI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/MereIdeaDevelopmentBlog/~4/D8gOevAXWik" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.mereidea.com/blog/2010/08/13/setting-up-a-buildbot-master-with-cmake/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.mereidea.com/blog/2010/08/13/setting-up-a-buildbot-master-with-cmake/</feedburner:origLink></item>
		<item>
		<title>SCons and Microsoft Visual Studio 2010 Express</title>
		<link>http://feedproxy.google.com/~r/MereIdeaDevelopmentBlog/~3/eI1sNs8GVoU/</link>
		<comments>http://www.mereidea.com/blog/2010/04/17/scons-and-microsoft-visual-studio-2010-express/#comments</comments>
		<pubDate>Fri, 16 Apr 2010 23:36:03 +0000</pubDate>
		<dc:creator>chris</dc:creator>
				<category><![CDATA[Microsoft Visual Studio]]></category>
		<category><![CDATA[SCons]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://www.mereidea.com/blog/?p=893</guid>
		<description><![CDATA[Current Repository Revision: 38 Code highlight key Grey: Code unchanged Red: Code removed Green: Code added Blue: Code not shown → Denotes the current line and the next are really one line On the 12th April 2010 Microsoft launched the first full (post Beta) version of their Visual Studio suite. This blog post covers testing [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Current Repository Revision: 38<br />
</strong></p>
<blockquote><p><span style="text-decoration: underline;">Code highlight key</span><br />
<span style="color: #808080;">Grey: Code</span><span style="color: #888888;"> unchanged</span><br />
<span style="color: #ff0000;">Red: Code removed</span><br />
<span style="color: #008000;">Green: Code added</span><br />
<span style="color: #3366ff;">Blue: Code not shown</span></p>
<p><span style="color: #3366ff;"><span style="color: #000000;">→ Denotes the current line and the next are really one line</span><br />
</span></p></blockquote>
<p>On the 12th April 2010 Microsoft launched the first full (post Beta) version of their Visual Studio suite. This blog post covers testing the Mere Idea <a title="Mere Idea Example Code" href="http://www.mereidea.com/blog/example-code">example code</a> with SCons using <a title="Visual Studio 2010 Express" href="http://www.microsoft.com/exPress/">Visual Studio 2010 Express</a>. It tests the code with a system where 2010 is installed alongside 2008 and 2005, and a system where 2010 is installed alongside 2008 only. A system where 2010 is the only Visual Studio version installed will be tested in the next post, but the fix in this post should work fine.</p>
<blockquote><p><strong>Note:</strong> A <a href="#summary">summary of the fix</a> in this post is given at the end. If you&#8217;re not interested in how this all applies to the organically grown example code and simply want to see how to get SCons working with MSVS 2010, then you could just <a href="#summary">read the summary</a> to see if that helps.</p></blockquote>
<p>A few weeks before the launch of the 2010 version of Visual Studio, SCons released version 1.3.0 which improved support MSVS 2008. This new version of SCons will also be installed on a new machine to see if it improves anything with regard to 2010. This, however, will be saved for the next post, as there were problems when <a href="#latest_scons">version 1.3.0 was tried</a>. Details of the problems are given at the <a href="#latest_scons">end of this post</a>.</p>
<blockquote><p><strong>Note:</strong> The correct version numbers for the Visual Studios 2005, 2008 and 2010 are 8.0, 9.0 and 10.0 respectively. These years and numbers are used interchangeably throughout this post. Also, when referring to Visual Studio 2010 Express in this post, it is referring to the C++ edition unless otherwise stated.</p></blockquote>
<p>Before starting on Visual Studio it is worth mentioning that two commits were made to the <a title="Mere Idea Example Code" href="http://www.mereidea.com/blog/example-code/">example code</a> since the last post. These were simple changes to set the <a title="Subversion website" href="http://subversion.tigris.org/">Subversion</a> eol-style property for the <em>SConfig</em> files. This property is normally set automatically due to settings in the Subversion config file. However, when adding the new <em>SConfig</em> files, the type was not added to the Subversion config file. The config file has now been updated, so future <em>SConfig</em> files should have the property correctly set to <strong>native</strong>. The two <em>SConfig</em> files were committed separately, so revisions 36 and 37 have this simple change. It&#8217;s not always easy to remember to keep all support files up-to-date! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Now back to Visual Studio 2010.</p>
<p>The first thing to try is to see whether the work done on the <a title="Mere Idea Example Code" href="http://www.mereidea.com/blog/example-code">example code</a> to get it to <a title="SCons with MSVS 2008" href="http://www.mereidea.com/blog/2009/04/13/scons-with-microsoft-visual-studio-express-2008/">work properly with Visual Studio 2008</a> simply worked with 2010. The latest revision was checked out on a machine with three Visual Studios installed &#8211; 2005, 2008 and 2010. The Microsoft Visual Studio 2010 Command Prompt (from <strong>Start | Programs | Microsoft Visual Studio 2010 Express | Visual Studio Command Prompt (2010)</strong> ) was opened (using this command prompt ensures that the environment is set up properly for compiling with 2010 &#8211; it&#8217;s essentially the same as starting a normal command prompt and running the appropriate <em>vcvars32.bat</em> batch file), and <strong> </strong>SCons was then run with the following result:</p>
<pre>&gt; scons
scons: Reading SConscript files ...
Checking for C++ header file string... no
ERROR: String header not present.
ERROR: Please ensure the Standard Library is installed and it
ERROR: contains &lt;string&gt;.
</pre>
<p>So there are problems simply checking for the header file <strong>string</strong>. To test for the <strong>string</strong> header, SCons compiles a small program that consists of:</p>
<pre>#include "string"
</pre>
<p>To test that this works as a compilable program the same code was compiled without SCons:</p>
<pre>&gt;cl test_string.cxx /c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version →
16.00.30319.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

test_10.cxx
C:\Program Files\Microsoft Visual Studio 10.0\VC\INCLUDE\ →
xlocale(323)  : warning
C4530: C++ exception handler used, but unwind semantics are →
not enabled. Specify
 /EHsc
</pre>
<p>The <strong>/c</strong> on the command is to ensure that no linking is attempted (there is no <strong>main()</strong>, so the linker would complain), and the warning about exception handlers comes from the C++ libraries. The <em>SConscript<strong> </strong></em>script adds <strong>/EHsc</strong> to the compiler flags when used properly to avoid this warning. The successful compilation, however, shows that the compiler can compile the code simply by starting the 2010 command prompt and using <strong>cl</strong>. The problem must lie in the way it is handled in SCons.</p>
<blockquote><p>A note on compiler versions in case of interest. The 9.0 (2008) and 10.0 (2010) version numbers refer only to the Visual Studio IDE. The same IDE version (with varied components) is used for all languages, so C++ and C# (for example) both use MSVS version 9.0 for 2008 and 10.0 for 2010. The language compilers have their own version numbers. The C++ compiler major version numbers are 15.00 in 2008 and 16.00 in 2010, whereas in C# the compiler major revisions are 3.5 in 2008 and 4.0 in 2010 (actually matching the .NET version number as they are developed together). The incremental linker major version numbers for C++ do match the Visual Studio numbers &#8211; 9.00 and 10.00 respectively.</p></blockquote>
<p>The version of SCons installed on the test computer is old (version 1.0.1). This is so the example code can be tested with as many versions of SCons as possible to enable users with legacy systems to use the solutions laid out in this blog. As other computers are available for testing with newer versions of SCons, this post will look for a solution that will work with all installed SCons versions (including 1.0.1).</p>
<p>To check whether it is the version of SCons that is causing the problem <a title="Microsoft Visual Studio 2010 Express" href="http://www.microsoft.com/exPress/">Visual Studio 2010 Express</a> was installed on a computer using SCons version 1.2.0. This computer had only MSVS 2008 already installed. SCons was run at the 2010 Command Prompt, and the same problem with the <strong>string</strong> configure test appeared.</p>
<p>A further version test was performed by temporarily installing the latest version of SCons (1.3.0) on the above computer (only temporarily, as 1.3.0 will be used later on a different computer, and 1.2.0 is needed on this one) and SCons was run again. Again the <strong>string</strong> configure test problem remains, showing that even the latest version of SCons has problems with multiple versions of Visual Studio installed (whether it has problems with just 2010 installed will be tested later).</p>
<p>Looking at the environment should give clues as to why the current code doesn&#8217;t work with the 2010 installation. The fix to make Visual Studio 2008 work with SCons was to use the 2008 Command Prompt (or run 2008&#8242;s <em>vcvars32.bat</em>) and copy the <strong>LIB</strong> and <strong>INCLUDE</strong> environment variables into the SCons environment. This code was put in the <em>SConstruct</em> file:</p>
<pre><span style="color: #888888;"># If the compiler is msvs and the LIB and INCLUDE environment
# variables in the os exist, use them instead of SCons version
# to fix MSVS 2008 Express problems
if (cmplr=='cl') and (not glob_env['vs2005']):
  envlib = os.environ.get('LIB')
  envincl = os.environ.get('INCLUDE')
  if envlib is not None:
    glob_env.Append(ENV = {'LIB' : envlib})
  if envincl is not None:
    glob_env.Append(ENV = {'INCLUDE' : envincl})</span>
</pre>
<p>As a quick recap from the <a title="SCons with MSVS 2008" href="http://www.mereidea.com/blog/2009/04/13/scons-with-microsoft-visual-studio-express-2008/">earlier post</a>, this code is necessary as SCons assumes the Microsoft SDK is below the Visual Studio directory, whereas from 2008 the SDK was in a separate directory. Hopefully this is fixed in SCons version 1.3.0.</p>
<p>So what happens when we start up the 2010 Command Prompt and run through this bit of code? The environment contains:</p>
<pre>'ENV': { 'INCLUDE': 'C:\\Program Files (x86)\\Microsoft Visual →
Studio 10.0\\VC\\INCLUDE;C:\\Program Files (x86)\\Microsoft SDKs\\ →
Windows\\v7.0A\\include;',
'LIB': 'C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\ →
VC\\LIB;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\ →
lib;',
'PATH': u'C:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\ →
Common7\\IDE;C:\\Program Files (x86)\\Microsoft Visual Studio →
9.0\\VC\\bin;C:\\Program Files (x86)\\Microsoft Visual Studio →
9.0\\Common7\\Tools;C:\\Program Files (x86)\\Microsoft Visual →
Studio 9.0\\Common7\\Tools\\bin;C:\\Windows\\Microsoft.NET\\ →
Framework\\v4.0.30319',
'PATHEXT': '.COM;.EXE;.BAT;.CMD',
'SYSTEMDRIVE': 'C:',
'SYSTEMROOT': 'C:\\Windows',
'SystemRoot': u'C:\\Windows',
'TEMP': 'C:\\Users\\chris\\AppData\\Local\\Temp',
'TMP': 'C:\\Users\\chris\\AppData\\Local\\Temp'},
</pre>
<p>You can see from this that the <strong>LIB</strong> and <strong>INCLUDE</strong> variables are correct, but the <strong>PATH</strong> variable still thinks it is using the Visual Studio 9.0 version. <strong>PATH </strong>could easily be added to the environment variables imported from the command prompt, but this includes lots of irrelevant paths, so it would be better to set this up correctly.</p>
<p>What compilers does the environment think are available? Again the environment dump can show this:</p>
<pre>'MSVS': { 'FRAMEWORKDIR': u'C:\\Windows\\Microsoft.NET\\Framework\\',
'FRAMEWORKVERSION': u'v4.0.30319',
'FRAMEWORKVERSIONS': [ u'v4.0.30319',
u'v3.5',
u'v3.0',
u'v2.0.50727',
u'v1.1.4322',
u'v1.0.3705'],
'PROJECTSUFFIX': '.vcproj',
'SOLUTIONSUFFIX': '.sln',
'SUITE': 'EXPRESS',
'SUITES': ['EXPRESS'],
'VERSION': '9.0Exp',
'VERSIONS': ['9.0Exp', '10.0Exp']},
</pre>
<p>SCons seems to think the best version installed is <strong>9.0Exp</strong>, but it recognises that <strong>10.0Exp</strong> is available. Note that on the computer with three MSVS versions installed, <strong>9.0Exp</strong> is still selected as the compiler:</p>
<pre>'VERSION': '9.0Exp',
'VERSIONS': [ '9.0Exp',
'9.0',
'8.0Exp',
'8.0',
'3.5',
'10.0Exp',
'10.0'],
</pre>
<p>Notice, also, that this computer has versions with and without the &#8216;Exp&#8217; on, even though only the Express editions are installed. This could be due to the old (1.0.1) version of SCons.</p>
<p>What would be good is if the Visual Studio version selected could be the one for which the Command Prompt was open. This would mean that opening a 2005 Command prompt would use 2005 (8.0), opening a 2008 Command Prompt would use 2008 (9.0), and opening a 2010 Command Prompt would use 2010 (10.0).</p>
<p>It would be nice if there was an MSVS_VER environment variable at the command prompt to help get the right version, but there isn&#8217;t. Instead something with the installation path in it can be used to get the version, as the installation path contains the version number in all MSVS versions (up to now). One environment variable that contains just the install path is <strong>DevEnvDir</strong>. This is set to the directory containing the development environment, and includes the string <em>Microsoft Visual Studio x<strong> </strong></em>(where <em>x</em> is the version number) in its value. Given this we can write some code to work out what the correct version is for the open command prompt.</p>
<p>As the MSVS version is going to be selected based on the command prompt, the &#8216;<strong>vs2005</strong>&#8216; option is no longer needed (just open a 2005 Command Prompt), but an &#8216;<strong>msver</strong>&#8216; option is provided in case there is a problem with auto detecting the version number. The new code (all added into the <em>SConstruct</em> file) is:</p>
<pre><span style="color: #999999;"><span style="color: #3366ff;">...</span>

opts = Options(optfile)
opts.AddOptions(
    ('bdir', "Set to the build directory if you don't like 'build'", <span style="color: #000000;">→</span>
'build'),
    ('sdir', "Set to the source directory if you don't like 'src'", <span style="color: #000000;">→</span>
'src'),
    ('smalllib',
 'Set to the number of object files below which a library is <span style="color: #000000;">→</span>
considered small',
     0),
    ('testname',
 "The name (or part of the path) of test(s) to run - sets <span style="color: #000000;">→</span>
'runtests=true'",
     ''),</span>
    <span style="color: #008000;">('msver',
 "Set the MSVS version number. E.g. for 2008 use 'msver=9.0', useful <span style="color: #000000;">→</span>
if not auto detected",
     ''),</span>
    <span style="color: #999999;">BoolOption('debug','Set to apply debugging flags','false'),
    BoolOption('forgive',
        'If set the compiler will be more forgiving of non-standard <span style="color: #000000;">→</span>
code',
        'false'),
    BoolOption('msvs',
        "Set to create Visual Studio projects only (no build <span style="color: #000000;">→</span>
performed)",
        'false'),
    BoolOption('runtests',
        "Set to run the tests once built",
        'false'),
    BoolOption('mingw',
        "Set to use MinGW instead of the compiler SCons chooses",
        'false')</span><span style="color: #ff0000;">,</span>
    <span style="color: #ff0000;">BoolOption('vs2005',
        "Set to use Visual Studio [Express] 2005 instead of a later <span style="color: #000000;">→</span>
compiler",
        'false')</span>
<span style="color: #808080;">)</span><span style="color: #3366ff;">

</span><span style="color: #999999;">glob_env = Environment(options = opts)

Help(opts.GenerateHelpText(glob_env))</span>

<span style="color: #999999;"># If </span><span style="color: #ff0000;">vs2005 or </span><span style="color: #999999;">mingw is set, get an environment with
# the appropriate tool instead</span>
<span style="color: #ff0000;">if glob_env['vs2005']:
 glob_env = Environment(options = opts,
 MSVS_VERSION = '8.0')
el</span><span style="color: #999999;">if glob_env['mingw']:
 glob_env = Environment(options = opts,
 tools = ['mingw'])</span>

<span style="color: #ff0000;"># Set up the correct path separator
# If your platform uses a different separator, or
# uses back slash instead of forward slash, ammend this
# bit
plat = string.lower(glob_env['PLATFORM'])
if plat == "win32":
  glob_env.Replace(pathsep = '\\')
else:
  glob_env.Replace(pathsep = '/')

# Add the src directory to the CPPPATH so we can use
# #include &lt;libname/subdir/class.h&gt;
glob_env.Append(CPPPATH='#%s' % glob_env['sdir'])</span>

<span style="color: #999999;"># Set up specific compiler options
<span style="color: #008000;">#</span></span><span style="color: #008000;"> If using msvs, set the correct version</span>
<span style="color: #999999;">#  dbgf will hold any debug flags
#  unff will hold any 'unforgiving' flags
#  genf will hold any general flags
cmplr = string.lower(glob_env['CC'])</span>

<span style="color: #999999;"># If the compiler is msvs and the LIB and INCLUDE environment
# variables in the os exist, use them instead of SCons version
# to fix MSVS 2008</span><span style="color: #008000;">/2010</span> <span style="color: #999999;">Express problems</span>
<span style="color: #999999;">if (cmplr=='cl')</span> <span style="color: #ff0000;">and (not glob_env['vs2005'])</span>:
 <span style="color: #008000;"> if (glob_env['msver']!=''):
    mver = glob_env['msver']
  else:
    # Select the right MSVS version number
    envdir = os.environ.get('DevEnvDir')
    chkstr = 'Microsoft Visual Studio'
    pos = envdir.find(chkstr)
    if pos == -1:
      # Warn we might not have the right compiler setup as we can't <span style="color: #000000;">→</span>
dicover the
      # correct version from the directory name
      print "WARNING: Unable to determine Visual Studio version - <span style="color: #000000;">→</span>
ensure you use"
      print "WARNING: the appropriate command prompt for your VS <span style="color: #000000;">→
</span>Version or use"
      print "WARNING: the msver option to set the version number."
      print "WARNING: Assuming default environment."
      mver=""
    else:
      # Try to get the version number
      pos=pos+len(chkstr)+1
      epos=envdir.find('\\',pos)
      mver = envdir[pos:epos]

  if (mver != ""):
    glob_env = Environment(options = opts,
                             MSVS_VERSION = mver)</span>

  <span style="color: #999999;"># Now set the LIB and INCLUDE variables
  envlib = os.environ.get('LIB')
  envincl = os.environ.get('INCLUDE')
  if envlib is not None:
    glob_env.Append(ENV = {'LIB' : envlib})
  if envincl is not None:
    glob_env.Append(ENV = {'INCLUDE' : envincl})</span>

<span style="color: #008000;"># Set up the correct path separator
# If your platform uses a different separator, or
# uses back slash instead of forward slash, ammend this
# bit
plat = string.lower(glob_env['PLATFORM'])
if plat == "win32":
  glob_env.Replace(pathsep = '\\')
else:
  glob_env.Replace(pathsep = '/')

# Add the src directory to the CPPPATH so we can use
# #include &lt;libname/subdir/class.h&gt;
glob_env.Append(CPPPATH='#%s' % glob_env['sdir'])</span>
<span style="color: #3366ff;">...</span></pre>
<p>The first thing in the code is to add the <strong>msver</strong> option to enable the user to set the compiler version if it isn&#8217;t auto detected. This should be entered as, for example, <strong>msver=9.0</strong> as an option to the <strong>scons</strong> command.<strong> </strong>The default is set to an empty string. The <strong>vs2005</strong> option is removed, as is the code to set up an environment specifically for MSVS 2005 (version 8.0).</p>
<p>A block of code that creates some environment settings is then removed. As the new code  to select the correct compiler creates a new environment it would destroy the work this block of code does. The code is actually added back at the end after the compiler selection.</p>
<p>The check for the <strong>vs2005</strong> option not being set is removed, and then a block of code is added that discovers the Visual Studio version. If the <strong>msver </strong>option is set, then that is used for the new environment. This just uses whatever the user enters &#8211; it could be checked to see if it&#8217;s a valid version number, but if something invalid is entered SCons just selects a compiler from the <strong>VERSIONS</strong> list. If <strong>msver</strong> is not set, the <em>SConstruct</em> script looks for the string <em>Microsoft Visual Studio</em> in the <strong>DevEnvDir</strong> command prompt environment variable. If the string isn&#8217;t found then a warning message is displayed and SCons is left to decide on the compiler, otherwise the version number is extracted from the <strong>DevEnvDir</strong> string.</p>
<p>If the version number is set to something, then a new SCons environment is created using that version number. This sets the compiler details correctly except for the <strong>LIB </strong>and <strong>INCLUDE </strong>paths, which still incorrectly assume the location of the Microsoft SDK to be under the Visual Studio directory. Therefore, the code to set them based on the command prompt environment is still included.</p>
<p>SCons can now be rerun with this new <em>SConstruct </em>file. Checking the SCons environment created it can be seen that the <strong>LIB</strong>, <strong>INCLUDE</strong> and <strong>PATH</strong> variables are now correct:</p>
<pre>'ENV': { 'INCLUDE': 'C:\\Program Files\\Microsoft Visual Studio →
10.0\\VC\\INCLUDE;C:\\Program Files\\Microsoft SDKs\\Windows\\v7.0A →
\\include;',
'LIB': 'C:\\Program Files\\Microsoft Visual Studio 10.0\\VC\\LIB; →
C:\\Program Files\\Microsoft SDKs\\Windows\\v7.0A\\lib;',
'PATH': u'C:\\Program Files\\Microsoft Visual Studio 10.0\\Common7 →
\\IDE;C:\\Program Files\\Microsoft Visual Studio 10.0\\VC\\bin; →
C:\\Program Files\\Microsoft Visual Studio 10.0\\Common7\\Tools; →
C:\\Program Files\\Microsoft Visual Studio 10.0\\Common7\\Tools →
\\bin;C:\\WINDOWS\\Microsoft.NET\\Framework\\v4.0.30319',
'PATHEXT': '.COM;.EXE;.BAT;.CMD',
'SYSTEMDRIVE': 'C:',
'SYSTEMROOT': 'C:\\WINDOWS',
'SystemRoot': u'C:\\WINDOWS',
'TEMP': 'C:\\DOCUME~1\\Chris\\LOCALS~1\\Temp',
'TMP': 'C:\\DOCUME~1\\Chris\\LOCALS~1\\Temp'},
</pre>
<p>As is the selected compiler version:</p>
<pre>'MSVS': { 'FRAMEWORKDIR': u'C:\\WINDOWS\\Microsoft.NET\\Framework\\',
'FRAMEWORKVERSION': u'v4.0.30319',
'FRAMEWORKVERSIONS': [ u'v4.0.30319',
 u'v3.5',
 u'v3.0',
 u'v2.0.50727',
 u'v1.1.4322',
 u'v1.0.3705'],
'PROJECTSUFFIX': '.vcproj',
'SOLUTIONSUFFIX': '.sln',
'SUITE': 'EXPRESS',
'SUITES': ['EXPRESS'],
'VCINSTALLDIR': u'C:\\Program Files\\Microsoft Visual Studio 10.0 →
\\VC\\',
'VERSION': '10.0',
'VERSIONS': [ '9.0Exp',
 '9.0',
 '8.0Exp',
 '8.0',
 '3.5',
 '10.0Exp',
 '10.0'],
'VSINSTALLDIR': u'C:\\Program Files\\Microsoft Visual Studio 10.0\\'},
</pre>
<p>And the code now builds using SCons 1.0.1 and 1.2.0. OK, so it gives hundreds of warnings from the MSVS 2010 includes (about unreferenced inline functions being removed), but this will be fixed in the next post! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<hr style="width: 50%;" /><a name="summary"></a></p>
<h2>Simple summary of fix</h2>
<p>For people who aren&#8217;t interested in using the example code, but are just looking for a quick way to fix problems with Visual Studio 2010, this is the section for you! This solution uses SCons versions 1.2.0 or earlier, as <a href="#latest_scons">some problems were found with version 1.3.0</a>, the fixes for which will be detailed in a later post.</p>
<p>This section will use simple <em>SConscript</em> file containing:</p>
<pre><span style="color: #008000;">Program('hello.c')</span>
</pre>
<p>The file <strong>hello.c</strong> contains:</p>
<pre><span style="color: #008000;">#include &lt;stdio.h&gt;
int main()
{
  printf("Hello World");
  return 0;
}</span>
</pre>
<p>This should be compilable simply by typing <strong>scons</strong> on the command prompt (note that you should open an MSVS 2010 Command Prompt for this), but doing so produces an error:</p>
<pre>&gt; scons -Q
cl /Fohello.obj /c hello.c /nologo
hello.c
link /nologo /OUT:hello.exe hello.obj
LINK : fatal error LNK1104: cannot open file 'kernel32.lib'
scons: *** [hello.exe] Error 1104
</pre>
<p>Looking at the environment, it can seen that (on the test system that also has MSVS 2008 installed) the compiler is assuming version 9.0Exp (MSVS 2008), but that version 10.0Exp is available:</p>
<pre>'MSVS': { 'FRAMEWORKDIR': u'C:\\Windows\\Microsoft.NET\\Framework\\',
 'FRAMEWORKVERSION': u'v4.0.30319',
<span style="color: #3366ff;">...</span>
 'VERSION': '9.0Exp',
 'VERSIONS': ['9.0Exp', '10.0Exp']},</pre>
<p>This can easily be changed by changing the <em>SConscript</em> file to explicitly use version 10.0:</p>
<pre><span style="color: #008000;">env=Environment(MSVS_VERSION = '10.0')</span>
<span style="color: #008000;">env.</span><span style="color: #999999;">Program('hello.c')</span>
</pre>
<p>Compiling with this <em>SConscript </em>file still produces the missing <em>kernel32.lib</em> problem. This problem is due to the fact that SCons (at least the versions 1.2.0 and earlier) assumes that the Microsoft SDK is in a directory below the Visual Studio installation directory, and the <strong>LIB</strong> and <strong>INCLUDE</strong> SCons environment variables are set accordingly. A solution to this is to get the correct <strong>LIB </strong>and <strong>INCLUDE </strong>directories from the command prompt. If you used a Visual Studio 2010 Command Prompt as suggested, or started a standard command prompt and ran <em>vcvars32.bat</em>, which is contained in the Visual Studio 2010 installation directory, then <strong>LIB </strong>and <strong>INCLUDE</strong> will be set up correctly. These can the be set in our SCons environment with the following change to the <em>SConscript</em> file:</p>
<pre><span style="color: #008000;">import os</span>

<span style="color: #999999;">env = Environment(MSVS_VERSION = '10.0')</span>

<span style="color: #008000;"># Set the LIB and INCLUDE variables
envlib = os.environ.get('LIB')
envincl = os.environ.get('INCLUDE')

# If they are set in the command environment,
# set them in the SCons environment
if envlib is not None:
 env.Append(ENV = {'LIB' : envlib})
if envincl is not None:
 env.Append(ENV = {'INCLUDE' : envincl})</span>

<span style="color: #999999;">env.Program('hello.c')</span></pre>
<p>Now we can run SCons again at the command line and&#8230;:</p>
<pre>&gt; scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
cl /Fohello.obj /c hello.c /nologo
hello.c
link /nologo /OUT:hello.exe hello.obj
scons: done building targets.

&gt; hello
Hello World
</pre>
<p>&#8230;hey presto! It works.</p>
<p>If you don&#8217;t have multiple versions of Visual Studio installed, you can just start with an empty environment by replacing <strong>env.Environment( MSVS_VERSION = &#8217;10.0&#8242;) </strong>with <strong>env.Environment()</strong>, but there is no harm in explicity stating the version to use.</p>
<blockquote><p><strong>Note: </strong>If you skipped it, the full version of the solution in this post actually gets the MSVS version to use automatically from the command prompt you opened, so opening a 2008 Command Prompt will use version 9.0, and opening a 2010 Command Prompt will use version 10.0.</p></blockquote>
<hr style="width: 50%;" /><a name="latest_scons"></a></p>
<h2>SCons 1.3.0 (and 1.3.0.d20100404)</h2>
<p>This section details problems encountered when trying to use SCons version 1.3.0 with even the simplest of<em> SConscript </em>files (<a href="#summary">as above</a>). The <em>SConscript </em>file has just <strong>Program(&#8216;hello.c&#8217;) </strong>in it, and <em>hello.c</em> was just Hello World. Running SCons gave the following error:</p>
<pre>&gt; scons -version
SCons by Steven Knight et al.:
 engine: v1.3.0.r4720, 2010/03/24 03:14:11, by jars on jars-desktop
Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, →
2010 The SCons Foundation
&gt; scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
cl /Fohello.obj /c hello.c /nologo
'cl' is not recognized as an internal or external command,
operable program or batch file.
scons: *** [hello.obj] Error 1
scons: building terminated because of errors.
</pre>
<p>This is a step backwards, as now even the compiler can&#8217;t be located. At least before it only failed to link. Looking at the default environment:</p>
<pre>'ENV': { 'COMSPEC': 'C:\\Windows\\system32\\cmd.exe',
 'PATH': u'C:\\Windows\\System32',
 'PATHEXT': '.COM;.EXE;.BAT;.CMD',
 'SystemDrive': 'C:',
 'SystemRoot': 'C:\\Windows',
 'TEMP': 'C:\\Users\\chris\\AppData\\Local\\Temp',
 'TMP': 'C:\\Users\\chris\\AppData\\Local\\Temp'},
<span style="color: #3366ff;">...</span>
'MSVC_VERSION': '10.0',
'MSVS': { 'PROJECTSUFFIX': '.vcproj', 'SOLUTIONSUFFIX': '.sln'},
<span style="color: #3366ff;">...</span>
'MSVS_VERSION': '10.0',
</pre>
<p>The environment (<strong>ENV</strong>) no longer has the <strong>LIB</strong> and <strong>INCLUDE</strong> variables, and the <strong>PATH</strong> has no reference to the MSVS paths. A new variable <strong>MSVC_VERSION</strong> is added which actually holds the Visual Studio version number (10.0) where you may expect the actual C++ version (16.00). At least the <strong>MSVS_VERSION </strong>is detected as 10.0!</p>
<p>As this version of SCons only claimed to fix Visual Studio 2008 problems, it was tested by setting the version number to 9.0 using <strong>env=Environment(MSVS_VERSION=&#8217;9.0)</strong> and <strong>env.Program(&#8216;hello.c&#8217;) </strong>in the <em>SConscript</em> file<strong> </strong>and using a Visual Studio 2008 Command Prompt. The (slightly cut down) result:</p>
<pre>scons: Reading SConscript files ...
scons: warning: MSVS_VERSION is deprecated: please use MSVC_VERSION →
instead
scons: warning: VC version 9.0 not installed.  C/C++ compilers are →
most likely not set correctly.
 Installed versions are: ['10.0', '9.0Exp']
scons: done reading SConscript files.
scons: Building targets ...
cl /Fohello.obj /c hello.c /nologo
'cl' is not recognized as an internal or external command,
operable program or batch file.
scons: building terminated because of errors.
</pre>
<p>This is strange for many reasons.In SCons version 1.2.0 specifying 10.0 matched with 10.0Exp, but in 1.3.0 specifying 9.0 doesn&#8217;t match with 9.0Exp. Only MSVS 10 Express is installed, but is shown as 10.0 rather than 10.0Exp. It appears <strong>MSVS_VERSION </strong>is deprecated in favour of <strong>MSVC_VERSION</strong>, but still expects the Visual Studio version number rather than the C++ version number, and it still can&#8217;t find the compiler!</p>
<p>Changing the environment to use 9.0Exp gives the following:</p>
<pre>ValueError: Unrecognized version 9.0Exp:
 File "SConstruct", line 3:
 env = Environment(MSVC_VERSION = '9.0Exp')
</pre>
<p>So 9.0 isn&#8217;t a valid version because it&#8217;s not in the list, but 9.0Exp is an unrecognised version and so can&#8217;t be used either!</p>
<p>The SCons 1.3.0 investigation will have to wait for the next post. When trying 1.3.0.d20100404 things got even worse, reporting <strong>NameError: global name &#8216;msvc_version_numeric&#8217; is not defined</strong> with even the simple one line <em>SConscript</em>!</p>
<p>The conclusion is to stick to SCons 1.2.0 and to use the fix detailed in this post to work with Visual Studio. It works with all versions so far!</p>
<hr style="width: 50%;" />The changes detailed in this post have been added to the <a title="Mere Idea Example Code" href="http://www.mereidea.com/blog/example-code/">example code</a> repository. The current repository revision is 38.</p>
<p>Revision 36 and 37 updated the Subversion end-of-line property to set it to native for <em>SConfig</em> files.</p>
<p>Revision 38 updated the <em>SConscript</em> file to automatically detect the Visual Studio version number from the open command prompt. It also enables the <a title="Mere Idea Example Code" href="http://www.mereidea.com/blog/example-code/">example code</a> to work with MSVS 2010.</p>
<p>The next post will have a look at SCons version 1.3.0 to see if the problems can be fixed for the simple code and then the <a title="Mere Idea Example Code" href="http://www.mereidea.com/blog/example-code/">example code</a>. It will also look at the warnings thrown by the MSVS 2010 header files to fix (or possibly ignore) them.</p>
<p>The promised SCons configure check post will come after that. Getting SCons to work with MSVS 2010 is taking a little more work than was hoped! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=eI1sNs8GVoU:iUU6uLXkkTY:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=eI1sNs8GVoU:iUU6uLXkkTY:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=eI1sNs8GVoU:iUU6uLXkkTY:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=eI1sNs8GVoU:iUU6uLXkkTY:I9og5sOYxJI"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=I9og5sOYxJI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/MereIdeaDevelopmentBlog/~4/eI1sNs8GVoU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.mereidea.com/blog/2010/04/17/scons-and-microsoft-visual-studio-2010-express/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.mereidea.com/blog/2010/04/17/scons-and-microsoft-visual-studio-2010-express/</feedburner:origLink></item>
		<item>
		<title>More SCons configure checks</title>
		<link>http://feedproxy.google.com/~r/MereIdeaDevelopmentBlog/~3/OXOkVPMRh04/</link>
		<comments>http://www.mereidea.com/blog/2010/03/17/more-scons-configure-checks/#comments</comments>
		<pubDate>Wed, 17 Mar 2010 16:41:08 +0000</pubDate>
		<dc:creator>chris</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[SCons]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Configure style checks]]></category>
		<category><![CDATA[STL Containers]]></category>

		<guid isPermaLink="false">http://www.mereidea.com/blog/?p=833</guid>
		<description><![CDATA[Current Repository Revision: 35 Code highlight key Grey: Code unchanged Red: Code removed Green: Code added Blue: Code not shown The first SCons configure style checks were added in the previous post. These simply checked for the existence of header files. This blog entry will add to the header checks by adding checks for functions. [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Current Repository Revision: 35<br />
</strong></p>
<blockquote><p><span style="text-decoration: underline;">Code highlight key</span><br />
<span style="color: #808080;">Grey: Code</span><span style="color: #888888;"> unchanged</span><br />
<span style="color: #ff0000;">Red: Code removed</span><br />
<span style="color: #008000;">Green: Code added</span><br />
<span style="color: #3366ff;">Blue: Code not shown</span></p></blockquote>
<p>The first SCons configure style checks were added in the previous post. These simply checked for the existence of header files. This blog entry will add to the header checks by adding checks for functions.</p>
<p>As well as the addition of the configure checks, code will be added to handle the players, keeping track of who&#8217;s turn it is to go next and who should start the next game.</p>
<p>To enable any game which uses squares with three possible states to use the <strong>Board</strong> class (e.g. Reversi, Draughts (Checkers), etc. or even ones using two states (filled/empty) such as solitaire) the <strong>Board</strong> class can be changed to use any number of squares. As the size of the <strong>Board</strong> isn&#8217;t known before hand, an <strong>std::vector</strong> will be used.</p>
<p>The choice to use a <strong>std::vector </strong>is a simple one. To do it without STL containers would require pointers (managed by the MereIdea code) to allocate the appropriate amount of memory. This management is simple, but it&#8217;s always better not to do it if there&#8217;s an alternative as it avoids memory errors that can be hard to debug.</p>
<p>When using an STL container, <strong>std::vector</strong> should always be considered first, as it is the most efficient in terms of space and speed of access (it is essentially a management wrapper around a C-style array). It is perfect for this purpose, so there is no need to look further.</p>
<p>The code of the<strong> Board</strong> class is changed to use the <strong>std::vector</strong> in place of the nine element array:</p>
<pre><span style="color: #888888;">/*!
   \file board.h</span>

   <span style="color: #ff0000;">\brief Maintain a board for a game of noughts and crosses</span>
   <span style="color: #008000;">\brief Maintain a board for a game using squares and up to →
three states</span>

   <span style="color: #888888;">\clog</span>
<span style="color: #3366ff;">...</span>

<span style="color: #888888;">#ifndef MI_TICTAC_CORE_BOARD_H_
#define MI_TICTAC_CORE_BOARD_H_</span>

<span style="color: #008000;">#include &lt;vector&gt;</span>

<span style="color: #888888;">#include &lt;tictac/core/piece.h&gt;</span>
<span style="color: #3366ff;">...
    <span style="color: #888888;">//! Construct an empty board
    /*!</span>
        <span style="color: #008000;">\param n_squares The number of squares on the board. These →
will be
                         numbered 1 to n_squares.</span>
      <span style="color: #888888;">  \post The board will be initialised with</span> <span style="color: #ff0000;">nine</span> <span style="color: #008000;">n_squares</span> <span style="color: #888888;">→
undefined squares</span>
    <span style="color: #808080;">*/</span>
    <span style="color: #ff0000;">Board(void);</span>
  <span style="color: #008000;">  Board(const unsigned int n_squares);
<span style="color: #3366ff;">...
    <span style="color: #808080;">void setSquare</span><span style="color: #808080;">(const unsigned int num,
                   const Piece pce);

</span>  </span></span></span><span style="color: #008000;">  //! Clear a single square on the board
    /*!
        \param num The number of the square to set
        \pre num contains a number in the range 1-n inclusive →
(where n is
                 the number of squares specified on contruction).
        \exception SquareOutOfRange thrown if num is not in the →
range 1-n
        \sa content(), setSquare()
    */
    void clearSquare(const unsigned int num);</span><span style="color: #3366ff;"><span style="color: #008000;"><span style="color: #3366ff;">

  <span style="color: #808080;">  //! Clear the board so that all squares are undefined
<span style="color: #3366ff;">...</span>
  private:
    //! The squares for the noughts and crosses board
    <span style="color: #ff0000;">Square board_m[9];</span>
    <span style="color: #008000;">std::vector&lt;Square&gt; board_m;</span>
  };
}
</span></span></span></span></pre>
<p>The changes to the <strong>Board</strong> class are straightforward. The constructor has an argument added for the number of squares needed for the board. There is no need for a default constructor as the board always has to have some size, and there is no sensible default size. There is no need to restrict the board to square numbers, either, as it isn&#8217;t known how the<strong> Board</strong> will be used.</p>
<p>The second change is the addition of the <strong>clearSquare()</strong> method. This is now necessary as a use of the <strong>Board</strong> could include the removal of pieces.</p>
<p>Finally, the actual internal data type for the <strong>Board</strong> is changed from and array of <strong>Square</strong> to a <strong>std::vector</strong> of <strong>Square</strong>.</p>
<p>These changes don&#8217;t require much of a change in the <em>board.cxx</em> file (as both the array and <strong>std::vector</strong> are accessed with the square brackets operator), so the code isn&#8217;t shown here. The <em>board_test.cxx</em> was also altered to correct for the changes and add new tests for the <strong>clearSquare()</strong> method.</p>
<p>As the <strong>Board</strong> has been changed to have board sizes other than 3&#215;3, it can be used for more than Noughts and Crosses (or tic-tac-toe). Therefore, it no longer makes sense to call the library <em>tictac</em>. The name was, therefore, changed to <em>bdgme</em>, for board game. This required changing all the namespaces from <strong>mi_tictac</strong> to <strong>mi_bdgme</strong>, changing the <strong>TictacException</strong> to <strong>BdGmeException</strong>, changing any comments containing <strong>tictac</strong> to <strong>bdgme</strong>, and changing the <em>SConscript</em> file in <em>mi_test_scons/src</em> to include the directory <em>bdgme</em> instead of <em>tictac</em>.</p>
<p>Finally, <em>svn</em> is used to move the <em>tictac</em> directory to <em>bdgme</em>:</p>
<pre>svn mv tictac bdgme</pre>
<p>Directory name changes should be done using <em>svn</em> otherwise the repository and working copies become out of sync and it is possible to end up with both <em>tictac</em> and <em>bdgme</em> directories active in the latest revision if you&#8217;re not careful!</p>
<p>The next step was to add a <strong>Player</strong> class. This class simply holds the name of a player and the <strong>Piece</strong> they are using. The class is declared as follows:</p>
<pre><span style="color: #008000;">class Player
{
public:
  Player(const std::string &amp;name = "anonymous",
         const Piece pce = "UNDEFINED_PIECE");
  ~Player(void);

  void setName(const std::string &amp;name);
  void setPiece(const Piece pce);

  const std::string &amp;name(void) const;
  const Piece &amp;piece(void) const; 

private:
  std::string name_m;
  Piece piece_m;
};</span></pre>
<p>The definition of the <strong>Player</strong> class is straightforward, and so is not shown. Some tests were also added in the file <em>src/bdgme/core/tests/player_test.cxx</em>.</p>
<p>To handle the number of players in the game, who&#8217;s turn it is next, and who&#8217;s turn it is to start the next game, a <strong>Players</strong> class is created. This is declared as follows:</p>
<pre><span style="color: #008000;">class Players
{
public:
  Players(const Player &amp;p);
  Players(const std::vector&lt;Player&gt; &amp;vp);
  ~Players(void);

  void addPlayer(const Player &amp;p);

  const Player &amp;startPlay(bool rnd=false);
  const Player &amp;currentPlayer(void) const;
  const Player &amp;nextPlayer(void);

  unsigned int nPlayers(void);

private:
  std::vector&lt;Player&gt; players_m;
  unsigned int current_player_m;
  unsigned int next_start_m;
};</span></pre>
<p>There is no default constructor for the <strong>Players</strong> class as we always need at least one player in any game! The player added through the single <strong>Player</strong> constructor becomes the first player, and other players remain in the order they are added through the <strong>addPlayer()</strong> method. By default the <strong>startPlay()</strong> method starts the next game from the next player in the <strong>std::vector</strong>. This behaviour can be changed by specifying <strong>true</strong> as the <em>rnd</em> argument of the <strong>startPlay()</strong> method. The player to start is then picked randomly from the number of players in the class. This could mean the same player being picked a number of times in a row. The standard C built-in <strong>rand() </strong>function is used. As their names suggest, <strong>currentPlayer() </strong>returns the current player taking their turn, <strong>nextPlayer()</strong> moves the <strong>current_player_m</strong> index by one (to point at the next <strong>Player</strong> in the <strong>std::vector</strong>) and returns that player, and <strong>nPlayers()</strong> returns the number of players held by the class. A set of tests were added in the file <em>src/bdgme/core/players_test.cxx</em>.</p>
<p>The definition of the constructors and the <strong>startPlay()</strong> functions are as follows:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #008000;">mi_bdgme::Players::Players(const Player &amp;p) :
current_player_m(0),
next_start_m(0)
{
  players_m.push_back(p);

  // Seed the random number generator based on time
  srand((unsigned int)time(0));
}
...
mi_bdgme::Players::Players(const std::vector&lt;Player&gt; &amp;vp) :
players_m(vp),
current_player_m(0),
next_start_m(0)
{
  // Seed the random number generator based on time
  srand((unsigned int)time(0));
}
...
const mi_bdgme::Player &amp;mi_bdgme::Players::startPlay(bool rnd
                                                     /*=false*/)
{
  unsigned int st;
  if (rnd)
  {
    st = rand() % players_m.size();
    next_start_m=st+1;
  }
  else
  {
    st = next_start_m++;
  }

  next_start_m = (next_start_m==players_m.size()) ? 0 : next_start_m;
  current_player_m = st;

  return players_m[st];
}
</span><span style="color: #3366ff;">...</span>
</pre>
<p>The adjusted <strong>Board</strong> and new <strong>Players</strong> classes include three new standard headers &#8211; <em>vector</em>, <em>cstdlib</em> and <em>ctime</em>. SCons configure style checks were added for these headers in <em>src/bdgme/core/SConfig</em>:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #008000;">if not conf.CheckCXXHeader('vector'):
  print "ERROR: Vector header not present."
  print "ERROR: Please ensure the Standard Library is installed and it"
  print "ERROR: contains &lt;vector&gt;."
  Exit(1)

# Check for C library headers
if not conf.CheckCXXHeader('cstdlib'):
  print "ERROR: The C standard library header not present."
  print "ERROR: Please ensure your C library is properly installed and"
  print "ERROR: contains &lt;cstdlib&gt;."
  Exit(1)

if not conf.CheckCXXHeader('ctime'):
  print "ERROR: The C time header not present."
  print "ERROR: Please ensure your C library is properly installed and"
  print "ERROR: contains &lt;ctime&gt;."
  Exit(1)</span>
<span style="color: #3366ff;">...</span>
</pre>
<p>The <strong>Players</strong> class also includes three standard functions &#8211; <em>srand, time </em>and <em>rand</em>. Configure checks can be added to the <em>SConfig </em>file for the existence of these functions using the <strong>CheckFunc</strong>() SCons function:</p>
<pre><span style="color: #3366ff;">...
</span><span style="color: #008000;"># Check for functions
if not conf.CheckFunc('srand'):
  print "ERROR: Function srand is not present."
  print "ERROR: Please ensure your C library is properly installed and"
  print "ERROR: contains srand()."
  Exit(1)

if not conf.CheckFunc('time'):
  print "ERROR: Function time is not present."
  print "ERROR: Please ensure your C library is properly installed and"
  print "ERROR: contains time()."
  Exit(1)

if not conf.CheckFunc('rand'):
  print "ERROR: Function rand is not present."
  print "ERROR: Please ensure your C library is properly installed and"
  print "ERROR: contains rand()."
  Exit(1)</span>

<span style="color: #999999;"># Finish up the configure environment
loc_env = conf.Finish()</span>
</pre>
<p>Now running SCons without the -Q switch will show the new set of checks:</p>
<pre>[mi_test_scons]$ scons
scons: Reading SConscript files ...
Checking for C++ header file string... yes
Checking for C++ header file exception... yes
Checking for C++ header file sstream... yes
Checking for C++ header file vector... yes
Checking for C++ header file cstdlib... yes
Checking for C++ header file ctime... yes
Checking for C function srand()... yes
Checking for C function time()... yes
Checking for C function rand()... yes
Checking for C++ header file iostream... yes
Checking for C++ header file string... yes
Checking for C++ header file vector... yes
Checking for C++ header file cstdlib... yes
Checking for C function srand()... yes
scons: done reading SConscript files.
scons: Building targets ...
</pre>
<p>There are repeated checks in this list. As SCons caches checks, it is hoped that these are not repeated (i.e. recompiled).</p>
<p>They exist because I like to have the checks local to the code (in this case in <em>core</em> and in <em>core/tests</em>). This is to make it easier to keep track of which checks are necessary. This is especially true in the case where, for example, a particular header file is removed from all files within a particular directory. The header check can be removed in the local <em>SConfig</em> file, but if other directories contain files including that header the necessary checks will remain. Having a global file would require searching of all directories to check if the header check is needed.</p>
<hr style="width: 50%;" />The changes detailed in this post have been added to the <a title="The Mere Idea Example Code" href="http://www.mereidea.com/blog/example-code">example code</a> repository. The current revision is 35.</p>
<p>Revision 33 included the changes to the <strong>Board</strong> class to enable any number of squares to be specified. It also changed the library name from <strong>tictac</strong> to <strong>bdgme</strong>.</p>
<p>Revision 34 added the Player and Players classes. It also changed the <strong>TictacException</strong> class to <strong>BdGmeException</strong>. This revision also added the extra header checks and the function checks detailed in this post.</p>
<p>Revision 35 Simply added a newline to the end of the <strong>Players</strong> class and added a check for the <strong>time</strong> function missed in the earlier revision.</p>
<p>So far only simple function checks have been added. In the next post a further function check will be created that will fail to find the function, and will use a replacement function which is locally written. Also some custom checks will be added.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=OXOkVPMRh04:bzPi-WIkjys:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=OXOkVPMRh04:bzPi-WIkjys:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=OXOkVPMRh04:bzPi-WIkjys:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=OXOkVPMRh04:bzPi-WIkjys:I9og5sOYxJI"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=I9og5sOYxJI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/MereIdeaDevelopmentBlog/~4/OXOkVPMRh04" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.mereidea.com/blog/2010/03/17/more-scons-configure-checks/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.mereidea.com/blog/2010/03/17/more-scons-configure-checks/</feedburner:origLink></item>
		<item>
		<title>Adding SCons configure style checks to new example code.</title>
		<link>http://feedproxy.google.com/~r/MereIdeaDevelopmentBlog/~3/9tYjLqvMecA/</link>
		<comments>http://www.mereidea.com/blog/2009/11/27/adding-scons-configure-style-checks-to-new-example-code/#comments</comments>
		<pubDate>Fri, 27 Nov 2009 17:52:57 +0000</pubDate>
		<dc:creator>chris</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Microsoft Visual Studio]]></category>
		<category><![CDATA[SCons]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Coding style]]></category>
		<category><![CDATA[Configure style checks]]></category>
		<category><![CDATA[Exceptions]]></category>

		<guid isPermaLink="false">http://www.mereidea.com/blog/?p=715</guid>
		<description><![CDATA[Current Repository Revision: 32 Code highlight key Grey: Code unchanged Red: Code removed Green: Code added Blue: Code not shown In the last post a new library (tictac) was added to the SCons test example code. Now it&#8217;s time to put some code into that library, and check that the SCons setup can handle the [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Current Repository Revision: 32<br />
</strong></p>
<blockquote><p><span style="text-decoration: underline;">Code highlight key</span><br />
<span style="color: #808080;">Grey: Code</span><span style="color: #888888;"> unchanged</span><br />
<span style="color: #ff0000;">Red: Code removed</span><br />
<span style="color: #008000;">Green: Code added</span><br />
<span style="color: #3366ff;">Blue: Code not shown</span></p></blockquote>
<p>In the last post a new library (<em>tictac</em>) was added to the SCons test <a href="http://www.mereidea.com/blog/example_code">example code</a>. Now it&#8217;s time to put some code into that library, and check that the SCons setup can handle the two libraries. The first <strong>configure</strong> style checks in the code  &#8211; checks for C++ headers &#8211; will also be added.</p>
<p>Firstly, a walk through the new code. The <em>tictac</em> library is to implement a set of classes and functions to support a game of Noughts and Crosses (or TicTacToe) for two players (one of which could be the computer, but, for the purposes of this test code, won&#8217;t be). All of the code added so far is in the <em>mi_test_scons/src/tictac/core </em>and its relevant <em>tests</em> directories.</p>
<p>We start by creating something to represent the contents of a square (or the <strong>Piece</strong>). This can be a nought, a cross or empty (undefined). The <strong>Piece </strong>is simply declared as an enumerated type as follows:</p>
<pre>enum Piece
{
  NOUGHT=0,
  UNDEFINED_PIECE,
  CROSS
};</pre>
<p>The enumerations are defined in what may seem an unusual order. This is so that bitwise operations can be performed to set/clear a defined/undefined bit, or to set the piece type. This enables a <strong>Square</strong> class to be created that acts as a kind of tri-state variable (actually it&#8217;s more of a quad-state variable as it could hold defined-cross, defined-nought, undefined-cross and undefined-nought) to represent each square on the <strong>Board</strong>.</p>
<p>The <strong>Square</strong> class is simply a collection of setters and getters for the contents of a single square. The content is stored as an <em>unsigned char</em>, which is manipulated inside the setters and getters to set it to any of the four states as mentioned above. An <em>unsigned char</em> is used as it is the smallest type, and only two bits are actually needed to store the <strong>Piece</strong>. The <strong>Piece</strong> type itself isn&#8217;t used because an enumerated type is (usually) the same size as an <em>int</em> (though I had a little bit of a mad moment when writing this and forgot why I hadn&#8217;t used the enumerated type &#8211; see revision 31, and it&#8217;s reverse merge (revision 32) in the <a href="http://www.mereidea.com/blog/example-code">example code</a> <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' />  ).</p>
<p>A bitfield could have been used with just two bits in it, but this would have to be aligned to the size of a <em>char </em>anyway, and its usually better to go for the simpler option. A further option would have been to simply use two <em>bool</em>s, but this would be at least double the size of the <em>unsigned char</em> type. This may all seem like a small saving, but if a library client wanted to create a board with millions of <strong>Square</strong>s, or if someone wants to include it in a small embedded system, it&#8217;ll turn out to be a huge saving! These things should always be considered. One of the <a href="http://www.mereidea.com/blog/test-systems">test systems</a> only has 64MB RAM. This helps keep things memory efficient! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>The <strong>Square</strong> has two setter methods (<strong>set</strong> with a <strong>Piece</strong> which changes the content to defined with that <strong>Piece</strong>, and <strong>set</strong> without a piece that simply changes the content to defined), one <strong>clear</strong> method (to set the content as undefined) and a <strong>content</strong> method that returns the current <strong>Piece</strong>. These use bit logic to change the content of the square.</p>
<p>The content of the <strong>Square</strong> should be one of the following values:<br />

<table id="wp-table-reloaded-id-1-no-1" class="wp-table-reloaded wp-table-reloaded-id-1">
<thead>
	<tr class="row-1 odd">
		<th class="column-1">Binary Value</th><th class="column-2">Meaning</th>
	</tr>
</thead>
<tbody>
	<tr class="row-2 even">
		<td class="column-1">00</td><td class="column-2">UNDEFINED_PIECE/NOUGHT</td>
	</tr>
	<tr class="row-3 odd">
		<td class="column-1">01</td><td class="column-2">Defined/NOUGHT</td>
	</tr>
	<tr class="row-4 even">
		<td class="column-1">10</td><td class="column-2">UNDEFINED_PIECE/CROSS</td>
	</tr>
	<tr class="row-5 odd">
		<td class="column-1">11</td><td class="column-2">Defined/CROSS</td>
	</tr>
</tbody>
</table>
</p>
<p>These values and the order we originally put the <strong>Piece</strong> enumerated type in makes the <strong>set</strong> with a <strong>Piece</strong> method easy to write:</p>
<pre>void mi_ticac::Square::set(const Piece pce)
{
  content_m = static_cast&lt;unsigned char&gt;(pce)+1;
}</pre>
<p>By adding one to the enumerated type, the content is set to the correct value:
<table id="wp-table-reloaded-id-2-no-1" class="wp-table-reloaded wp-table-reloaded-id-2">
<thead>
	<tr class="row-1 odd">
		<th class="column-1">Piece</th><th class="column-2">Binary value</th><th class="column-3">One added (binary)</th><th class="column-4">Final Meaning</th>
	</tr>
</thead>
<tbody>
	<tr class="row-2 even">
		<td class="column-1">NOUGHT</td><td class="column-2">00</td><td class="column-3">01</td><td class="column-4">Defined/NOUGHT</td>
	</tr>
	<tr class="row-3 odd">
		<td class="column-1">CROSS</td><td class="column-2">10</td><td class="column-3">11</td><td class="column-4">Defined/CROSS</td>
	</tr>
	<tr class="row-4 even">
		<td class="column-1">UNDEFINED_PIECE</td><td class="column-2">01</td><td class="column-3">10</td><td class="column-4">UNDEFINED_PIECE/CROSS</td>
	</tr>
</tbody>
</table>
</p>
<p>Similarly, the <strong>set</strong> method without a <strong>Piece</strong> can be easily written. All that is needed in this method is ensure the defined/undefined (or least significant) bit is set to 1:</p>
<pre>void mi_tictac::Square::set(void)
{
  content_m |= 1;
}</pre>
<p>Using the <em>OR</em> operation, the least significant bit (LSB) is always set to 1 (? | 1 = 1) and the rest of the bits in the <em>unsigned char</em> are left as they are ( ? | 0 = ?).</p>
<p>Finally for the setters, we want to clear (set to 0) the defined/undefined bit. This is done in the <strong>clear</strong> method:</p>
<pre>void mi_tictac::Square::clear(void)
{
  content_m &amp;=2;
}</pre>
<p>Using the <em>AND</em> operation with 2 (binary 10) will always clear the LSB (? &amp; 0 = 0), and leave bit 1 (the nought/cross bit) as whatever it was (? &amp; 1 = ?).</p>
<p>The last method in the <strong>Square</strong> is the getter &#8211; returning the <strong>Piece</strong> that the <strong>Square</strong> represents. This code isn&#8217;t just a case of returning the enumerated type, because it has to convert from the internal <em>unsigned char</em> type to the enumerated <strong>Piece</strong> type as an enumerated type cannot be set from another type. This makes sense because an enumerated type, by definition, is only permitted to hold certain values, and enabling it to be set from another type may break that rule &#8211; something which wouldn&#8217;t be known until runtime.</p>
<p>The code to convert is, again, pretty simple, and again it uses logical operators:</p>
<pre>mi_tictac::Piece mi_tictac::Square::content(void) const
{
  Piece cont = UNDEFINED_PIECE;

  if (content_m &amp; static_cast&lt;unsigned char&gt;(UNDEFINED_PIECE))
  {
    if (content_m &amp; static_cast&lt;unsigned char&gt;(CROSS))
      cont = CROSS;
    else
      cont = NOUGHT;
  }
  return cont;
}</pre>
<p>The logic is straightforward given what you already know from this post even if you didn&#8217;t know much about bitwise logic before! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>The next class in the <em>core</em> library is the collection of <strong>Square</strong>s &#8211; the <strong>Board</strong>. The <strong>Board</strong> class simply holds an array of nine <strong>Squares</strong>. An array is used rather than, for example, an STL container as the number of <strong>Squares</strong> required is known (though this may change in a later version!), and the array is the simplest, most-efficient way to represent the board. <strong>Board</strong> has three methods &#8211; one to set a single <strong>Square</strong>, one to get the contents of a single <strong>Square</strong>, and one to clear all squares (set them back to <strong>UNDEFINED_PIECE</strong>).</p>
<p>The methods <strong>setSquare</strong> and <strong>contents</strong> take an <em>unsigned short</em> to identify the square whose contents are to be set/returned. This number should be in the range 1-9 (this is better for humans than 0-8, even if it means the code having to subtract one for the array index). If the number is outside the range, a <strong>SquareOutOfRange</strong> exception is thrown.</p>
<p>An exception is used here because the way the code is to be called by clients of the library is unknown. With an exception, they can deal with it at the level of the call, turn it into an error code, allow it to pass to another level, or simply ignore it and let the program crash. In other places a return code may be used, but when something can be called from outside the library it seems a good case for an exception. This is a choice that should be made based on the situation, and always using exceptions, or always using return codes means the programmer is not thinking about what is best in a particular situation. Never think that one-way is the only way. There is no programmer&#8217;s panacea! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>A test was added for each class (in <em>src/titac/core/tests</em>) and the code compiled and tested on Linux and Windows. As the initial coding was done under Linux, there were some problems when building with MSVS.</p>
<p>Before moving on to the MSVS problems, a change was made to the way tests could be specified at the SCons command line. A new command line option, <em>testname</em> was added to enable the user to specify the name (or part of a name or path) of the test to run. This required a change to the <em>SConstruct</em> file:</p>
<pre><span style="color: #3366ff;">...</span>
    <span style="color: #999999;">('smalllib',
'Set to the number of object files below which a library →
small'.
     0),</span>
    <span style="color: #008000;">('testname',
"The name (or part of the path) of test(s) to run - sets →
'runtests=true'",
     ''),</span>
    <span style="color: #999999;">BoolOption('debug','Set to apply debug flags',false),</span>
<span style="color: #3366ff;">...</span></pre>
<p>A change was also necessary to the <em>mi_scons.py</em> file in the <strong>MI_BuildTestsAndExecute</strong> function to check for the test name in the test being run. It makes use of the fact the the Python string function <strong>find</strong> matches everything if it is trying to match an empty string:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #999999;">trgt = env.Program(nxttst)</span>

<span style="color: #999999;"># Now execute
if [env['runtests']</span><span style="color: #008000;"> or env['testname'] != ''</span><span style="color: #999999;">:</span>
  <span style="color: #999999;">cmnd = env['currbdir']+env['pathsep']+str(trgt[0])
  dmname = 'dummy_'+str(trgt[0])</span>
  <span style="color: #008000;">if cmnd.find(env['testname']) != -1:</span>
  <span style="color: #008000;">··</span><span style="color: #999999;">env.Command(dmname,trgt,cmnd)</span>
<span style="color: #3366ff;">...</span></pre>
<p>So, for example, you can make the following calls to SCons to run specific tests:</p>
<pre># Run all tests
scons -Q runtests=true

# Run any test with 'board' in its name
scons -Q testname="board"

# Run all tests in the 'core' directory or anything else with
# 'core' in it's name
scons -Q testname="core"</pre>
<p>Now to the MSVS problems. Firstly, there was a warning about structured exceptions (C4571). This is a warning that, when compiling with <em>/EHsc</em> structured exceptions will not be caught by <em>catch(&#8230;)</em>. This warning has now been disabled in the <em>SConstruct</em> file:</p>
<pre><span style="color: #008000;">#  /wd4571 - Disable this warning about not catching structured
             exceptions in catch(...) block due to many warnings
             for every catch(...) block.</span>
<span style="color: #808080;">#  /FC     - Display the full path of source files containing</span>
<span style="color: #3366ff;">...</span>
<span style="color: #808080;">genf = ' /Wall /EHsc /wd4820 /wd4710</span> <span style="color: #008000;">/wd4571</span> <span style="color: #808080;">/FC'</span>
<span style="color: #808080;">unff = ' /Za'</span></pre>
<p>Another set of warnings, which have been appearing since this blog began and had begun to get too many to ignore, regarded <em>argc </em>and <em>argv</em> being unreferenced variables when compiling with MSVS. All <em>argc </em>and <em>argv</em> parameters were removed from <strong>main</strong> functions to correct this warning.</p>
<blockquote><p><strong>NOTE: </strong>The above paragraph may suggest that compiler warnings are occasionally ignored. You should <strong>never </strong>ignore compiler warnings as they often point to real problems. The next MSVS warning is a prime example!</p></blockquote>
<p>MSVS also warned that the virtual function <strong>TictacException::what</strong> did not override a base class virtual member function (warning C4263). The reason for this was a missing <strong>const</strong> on the end of the function. This is an example of where a warning can point to a potential error, and also where using multiple compilers can help catch more problems than a single compiler (gcc gave no warning about this).</p>
<p>The final few warnings from MSVS were about type conversions. In the tests, <em>unsigned int</em> had been used for loop variables which call into the <strong>content</strong> function which takes an <em>unsigned short</em>. All loops were changed to <em>unsigned short</em>. A further conversion warning was given for the line that sets the square content by adding one to the <strong>Piece</strong> (<strong>Square::set</strong>). As the <strong>+1</strong> was outside the cast, there was a warning that an <em>unsigned char</em> was being set from an <em>int</em>. This was easily solved:</p>
<pre><span style="color: #888888;">void mi_tictac::Square::set(const Piece pce)
{
  // One is added to the value so that the value is set to →
defined (or in the case
  // of undefined, it will clear the defined bit).</span>
  <span style="color: #ff0000;">content_m = static_cast&lt;unsigned char&gt;(pce)+1;</span>
  <span style="color: #008000;">content_m = static_cast&lt;unsigned char&gt;(pce+1);</span>
<span style="color: #888888;">}</span></pre>
<p>Once the MSVS warnings were fixed the code was committed, and rebuilt under gcc. This added a new warning about the exception specification being different between the derived <strong>TictacException::what()</strong> and the base <strong>std::exception::what()</strong>. This is simply resolved by adding <strong>throw()</strong> to the end of the <strong>what()</strong> method. Exception specifications won&#8217;t be used in the code normally, but are added here simply to clear the warning. Normally a list of exceptions thrown are put in the Doxygen documentation.</p>
<p>Now the code compiled cleanly with both gcc and MSVS, and we can move on to adding the first configure style checks to the code.</p>
<p>As the <em>SConscript</em> files at each level are functionally the same for each library, it would be better not to have the configure style checks just in the <em>SConscript</em> files that needed them, and missing from the others. To avoid this inconsistency, a new type of file was created &#8211; <em>SConfig</em>. A <em>SConfig</em> file is created only in parts of the source tree where the configure style checks need to be carried out, while other parts of the tree don&#8217;t contain one (or could contain an empty one, but that&#8217;s not necessary). The <em>SConfig</em> file can then be checked for in each directory&#8217;s <em>SConscript</em> file, and included if necessary. This only requires a small change to all <em>SConscript</em> files in the source tree:</p>
<pre><span style="color: #888888;"># Set up the environment for this directory
loc_env.MI_SetDirectories()
srcf = loc_env.MI_GetSources()</span>

<span style="color: #008000;"># Make any configure style checks
if (os.path.isfile("%s/SConfig" % loc_env['currsdir'])):
  SConscript("SConfig", exports='loc_env')</span>

<span style="color: #888888;"># Store results
objs=[]</span></pre>
<p>This change (or something similar) is included in all Library Level, Subdirectory Level and Test Builder <em>SConscript</em> files. A small change was also made to the <em>SConstruct</em> file to export the <strong>os</strong> for use in the <em>SConscript</em> files:</p>
<pre><span style="color: #888888;">if not glob_env['forgive']:
  glob_env.Append(CCFLAGS = unff)</span>

<span style="color: #008000;"><span style="color: #888888;"># Allow others to use the environment and mi_scons module
Export('glob_env', 'mi_scons'</span>, 'os'<span style="color: #888888;">)</span></span>

<span style="color: #888888;"># Pass on to the next stage
BuildDir(glob_env['bdir'],glob_env['sdir'],duplicate=0)
SConscript('%s/Sconscript' % glob_env['bdir'])</span></pre>
<p>Finally the <em>SConfig</em> files can be added containing the configure style checks. To start with, some simple header file checks are made. The first <em>SConfig</em> file is placed in the <em>src/tictac/core</em> directory, and contains checks for the <strong>string</strong>, <strong>exception</strong>, and <strong>sstream</strong> headers used by files in that library:</p>
<pre><span style="color: #3366ff;"># Copyright message cut out
...</span>

# Import the environment
Import('loc_env')

# Set up the configure environment
conf = Configure(loc_env)

# Check for standard library headers
if not conf.CheckCXXHeader('string'):
  print "ERROR: String header not present."
  print "ERROR: Please ensure the Standard Library is installed →
and it"
  print "ERROR: contains &lt;string&gt;."
  Exit(1)

if not conf.CheckCXXHeader('exception'):
  print "ERROR: Exception header not present."
  print "ERROR: Please ensure the Standard Library is installed →
and it"
  print "ERROR: contains &lt;exception&gt;."
  Exit(1)

if not conf.CheckCXXHeader('sstream'):
  print "ERROR: String stream header not present."
  print "ERROR: Please ensure the Standard Library is installed →
and it"
  print "ERROR: contains &lt;sstream&gt;."
  Exit(1)

# Finish up the configure environment
loc_env = conf.Finish()</pre>
<p>The tests in <em>src/tictac/core/tests</em> also use the <strong>iostream</strong> header, so a <em>SConfig</em> file is also included in that directory:</p>
<pre><span style="color: #3366ff;"># Copyright message cut out
...</span>

# Import the environment
Import('loc_env')

# Set up the configure environment
conf = Configure(loc_env)

# Check for standard library headers
if not conf.CheckCXXHeader('iostream'):
  print "ERROR: Input/Output stream header not present."
  print "ERROR: Please ensure the Standard Library is installed →
and it"
  print "ERROR: contains &lt;iostream&gt;."
  Exit(1)
# Finish up the configure environment
loc_env = conf.Finish()</pre>
<p>Now running SCons without the -Q will show the configure checks (running with the -Q will only show any errors, not the list of checks):</p>
<pre>[mi_test_scons]$ scons
scons: Reading SConscript files ...
Checking for C++ header file string... yes
Checking for C++ header file exception... yes
Checking for C++ header file sstream... yes
Checking for C++ header file iostream... yes
scons: done reading SConscript files.
scons: Building targets ...
<span style="color: #3366ff;">...</span></pre>
<hr style="width: 50%;" />The changes detailed in this post have been added to the <a title="The example code and how to get it!" href="http://www.mereidea.com/blog/example-code">example code</a> repository. The current revision is 32.</p>
<p>Revision 23 added the initial <em>tictac</em> code, and also the <strong>testname</strong> variable to the <em>SConstruct</em> file and <em>mi_scons.py</em>.</p>
<p>Revision 24 corrected the Doxygen tag <em>exceptions</em> to <em>exception</em> (though the Doxygen manual claims that <em>exceptions </em>is a synonym for <em>exception</em>, Doxygen reported it as an unrecognised tag).</p>
<p>Revision 25 Simply added a link to the PDF version of the Doxygen documentation from the introductory page.</p>
<p>Revision 26 Fixed some errors in comments in <em>exceptions.h</em>.</p>
<p>Revision 27 Fixed the Windows problems outlined in this post.</p>
<p>Revision 28 Added the <strong>throw()</strong> exception specifier to the <strong>what()</strong> function in the TictacException to stop gcc warning.</p>
<p>Revision 29 Added the <em>SConfig</em> file in <em>src/tictac/core</em> and updated all the necessary <em>SConscript</em> files to check for and include and <em>SConfig</em> file.</p>
<p>Revision 30 Added the <em>SConfig</em> file to <em>src/tictac/core/tests</em>.</p>
<p>Revision 31 Was a mistake (mentioned in this post) where I forgot why I&#8217;d used <em>unsigned char</em> for the board rather than <strong>Piece</strong>.</p>
<p>Revision 32 Reverted to revision 30 to remove revision 31.</p>
<p>The next post will add to the <em>tictac</em> library, adding new checks for libraries and functions, and including replacement functions if necessary ones don&#8217;t exist. It was also noticed that the <strong>glob_env</strong>/<strong>loc_env</strong> change made in a <a title="Post describing the use of glob_env and loc_env." href="http://www.mereidea.com/blog/2009/10/02/adding-a-new-library-to-the-mere-idea-scons-example-code/">previous post</a> didn&#8217;t seem to work, as the <em>foobar</em> library wasn&#8217;t included when linking the <em>tictac</em> library. This will need further investigation in a later post.</p>
<p>When writing the code for <em>tictac </em>it seemed that a style guide may be useful in explaining why certain decision are made in the code. This guide will be created shortly, and will be organic &#8211; growing as thing are thought of which need to go in there. The guide will contain both coding and Doxygen stylings for the Mere Idea code base. <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=9tYjLqvMecA:cJETZI7-5c0:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=9tYjLqvMecA:cJETZI7-5c0:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=9tYjLqvMecA:cJETZI7-5c0:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=9tYjLqvMecA:cJETZI7-5c0:I9og5sOYxJI"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=I9og5sOYxJI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/MereIdeaDevelopmentBlog/~4/9tYjLqvMecA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.mereidea.com/blog/2009/11/27/adding-scons-configure-style-checks-to-new-example-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.mereidea.com/blog/2009/11/27/adding-scons-configure-style-checks-to-new-example-code/</feedburner:origLink></item>
		<item>
		<title>Adding a new library to the Mere Idea SCons example code</title>
		<link>http://feedproxy.google.com/~r/MereIdeaDevelopmentBlog/~3/eoxIfQW15Ss/</link>
		<comments>http://www.mereidea.com/blog/2009/10/02/adding-a-new-library-to-the-mere-idea-scons-example-code/#comments</comments>
		<pubDate>Fri, 02 Oct 2009 15:41:06 +0000</pubDate>
		<dc:creator>chris</dc:creator>
				<category><![CDATA[SCons]]></category>

		<guid isPermaLink="false">http://www.mereidea.com/blog/?p=610</guid>
		<description><![CDATA[Current Repository Revision: 22 Code highlight key Grey: Code unchanged Red: Code removed Green: Code added Blue: Code not shown Now it&#8217;s time to add a new library to the example code. This new library will be used for testing the SCons configure style options &#8211; checking for the existence of header files, libraries and [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Current Repository Revision: 22<br />
</strong></p>
<blockquote><p><span style="text-decoration: underline;">Code highlight key</span><br />
<span style="color: #808080;">Grey: Code</span><span style="color: #888888;"> unchanged</span><br />
<span style="color: #ff0000;">Red: Code removed</span><br />
<span style="color: #008000;">Green: Code added</span><br />
<span style="color: #3366ff;">Blue: Code not shown</span></p></blockquote>
<p>Now it&#8217;s time to add a new library to the <a title="The mi_test_scons example code." href="http://www.mereidea.com/blog/example-code" target="_self">example code</a>. This new library will be used for testing the SCons configure style options &#8211; checking for the existence of header files, libraries and functions.</p>
<p>The new library is called <em>tictac</em> and will contain a set of classes to implement a two player Noughts and Crosses (or Tic-Tac-Toe) game. It may be written in a very convoluted way to test some features of SCons, so if you really want to write a Noughts and Crosses game, don&#8217;t copy this! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>The first thing needed is a place for the new library in the <em>src</em> directory. A new directory (<em>tictac</em>) was created in <em>src</em>. Then, inside <em>tictac</em>, the necessary sub-directories are created:</p>
<pre>mkdir core tui lib tests</pre>
<p>The <em>tui</em> directory will include the interface code (tui &#8211; Textual User Interface, but will simply be some <strong>cout</strong>s and <strong>cin</strong>s) and the <em>core</em> directory will include any other code for the game play.</p>
<p>There will be no code at the top (<em>tictac</em>) level of this library (unlike <em>foobar</em> which has <em>foo.h</em> and <em>foo.cxx</em>), so the tests directory at this level may be unnecessary, or it may contain tests combining all other code. Either way, it&#8217;s worth creating now for consistency. Consistency is a significant battle to win in software development! Maintenance is so much easier if things are as you expect them to be.</p>
<p>Just to test the scripts for multiple levels, the directory <em>io</em> is also created inside the <em>tui</em> directory. <em>tests</em> directories also need to be created in all the sub-directories.</p>
<p>The next step is to set up the <em>SConscript</em> files so that these directories are included in the build. When creating the <strong>foobar</strong> library 5 types of <em>SConscript</em> files were set up that can be reused in new projects or libraries:</p>
<ul>
<li>Top-level (e.g. <em>mi_test_scons/src/SConscript</em>) that specify which libraries to build. There is one per project.</li>
<li>Library-level (e.g. <em>mi_test_scons/src/foobar/SConscript</em>) that collect all the object files together (through calls to sub-directories as well as compiling any code at the top-level of the library) and calls the library building <em>SConscript</em>, as well as calling the test building <em>SConscript</em> for any library-level tests. There is one per library.</li>
<li>Sub-directory (e.g. <em>mi_test_scons/src/foobar/bar/SConscript</em>) that compile the code for that sub-directory, and pass the details back to the library-level <em>SConscript</em>. These can also call any further sub-directory scripts, and build any sub-directory tests. There is one for every sub-directory to any number of levels.</li>
<li>Library builder (e.g. <em>mi_test_scons/src/foobar/lib/SConscript</em>) that build all of the object files gathered by the library-level <em>SConscript </em>and sub-directory <em>SConscript</em> files into the appropriate library. There is one per library.</li>
<li>Test builder (e.g. <em>mi_test_scons/src/foobar/tests/SConscript</em>) that simply build any tests. There is one per <em>tests </em>directory, whether at the library level or in sub-directories.</li>
</ul>
<p>These <em>SConscript</em> files need little change from project to project and library to library, so they can be just copied from one project or library to another. Once the development environment testing is over, all of the files useful for creating new projects and libraries will be put into a single Subversion module, but for now they&#8217;ll just be copied from library to library within <strong>mi_test_scons</strong>.</p>
<p>Firstly, the library-level <em>SConscript</em> is copied from <em>foobar</em> into the <em>tictac</em> directory and edited appropriately.</p>
<blockquote><p><strong>NOTE: </strong>One problem that always cropped up when using Makefiles was that people would just copy a working Makefile from one build to another, and it ended up with no one knowing how the Makefile worked. This made it difficult when something went wrong.</p>
<p>The SConscript files are short and simple, so you should read them carefully when copying them. That way you can check that you have edited everything that needs editing, and can ensure you understand them!</p></blockquote>
<p>Before copying the library-level <em>SConscript</em> file it was noticed that the code that sets up the <strong>LIBPATH</strong> variable could be moved out of the <em>SConscript</em> file and into the <em>mi_scons.py</em> file containing the Mere Idea utility functions. A new function <strong>MI_SetLibraryPath</strong> was added:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #008000;">def MI_SetLibraryPath(env):
  bloc = env['currbdir'].find(env['bdir'])
  if bloc==-1:</span>
    <span style="color: #3366ff;"># Comment omitted</span>
    <span style="color: #008000;">blibpath = '#lib'
  else:
    blibpath = '#'+env['currbdir'][bloc:]+env['pathsep']+'lib'

  env.Append(LIBPATH=[blibpath])</span>
<span style="color: #3366ff;">...</span></pre>
<p>and included in the environment:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #808080;">SConsEnvironment.MI_GetHeaders = MI_GetHeaders</span>
<span style="color: #008000;">SConsEnvironment.MI_SetLibraryPath = MI_SetLibraryPath</span>
<span style="color: #808080;">SConsEnvironment.MI_BuildObjects = MI_BuildObjects</span>
<span style="color: #3366ff;">...</span></pre>
<blockquote><p><strong>NOTE: </strong>When appending to the <strong>LIBPATH </strong>variable in the <em>mi_scons.py</em> version of this code, the square brackets are used to add the new path as a new list item. These were not used in the <em>SConscript</em> version, and would result in the new path just being concatenated to the existing <strong>LIBPATH </strong>string (e.g. it would end up with something like &#8216;/build/release/foobar/lib#build/release/tictac/lib&#8217; instead of two separate paths). This wasn&#8217;t apparent until the second library was added.</p></blockquote>
<p>The <strong>LIBPATH</strong> appending code was stripped from the <em>SConscript</em> file, and replaced with a call to the new function:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #808080;"># Only put the library together if we have object files
if len(objs)&gt;0:
  # Subdirectories may have changed the directories, so reset them
  env.MI_SetDirectories()</span>
  <span style="color: #008000;">env.MI_SetLibraryPath()</span>

  <span style="color: #ff0000;"># Then work out where we are for the library path
  bloc = env['currbdir'].find(env['bdir'])
  if bloc==-1:</span>
    <span style="color: #3366ff;"># Comment omitted</span>
  <span style="color: #ff0000;">  blibpath = '#lib'
  else
    blibpath = '#'+env['currbdir'][bloc:]+'/lib'

  env.Append(LIBPATH='%s' % blibpath)</span>
  <span style="color: #808080;">SConscript('lib/SConscript', exports='objs')</span>

<span style="color: #808080;">elif env['msvs']:</span>
<span style="color: #3366ff;">...</span></pre>
<p>This simplifies the <em>SConscript</em> file, which can now be copied to the directory <em>tictac</em>, and edited accordingly:</p>
<pre><span style="color: #808080;">#</span>
<span style="color: #ff0000;"># SConscript file for the foobar library</span>
<span style="color: #008000;"># SConscript file for the tictac library</span>
<span style="color: #808080;">#</span>
<span style="color: #3366ff;">...</span>

<span style="color: #808080;"># Enter any subdirectories here
subdirs = Split("""</span>
                <span style="color: #ff0000;">bar</span>
                <span style="color: #008000;">core
                tui</span>
                <span style="color: #808080;">""")</span>
<span style="color: #3366ff;">...</span></pre>
<p>Those are the only changes needed &#8211; the rest of the <em>SConscript</em> just works!</p>
<p>If you added the <em>tictac</em> directory name to the top-level <em>SConscript</em> file now, the code could be compiled, and the missing sub-directory <em>SConscript</em> files would be ignored (and SCons would warn you about them). You could then add the other <em>SConscript </em>files only when needed. However, it&#8217;s better to get all the setup out of the way, so we&#8217;ll continue to copy the appropriate <em>SConscript</em> files from the <em>foobar</em> directory.</p>
<p>The sub-directory <em>SConscript</em> file for the <em>core</em> directory needs only one change from the one in the <em>bar</em> directory of the foobar library &#8211; the first comment in the file:</p>
<pre><span style="color: #ff0000;"># SConscript file for foobar/bar</span>
<span style="color: #008000;"># SConscript file for tictac/core</span>
<span style="color: #3366ff;">...</span></pre>
<p>The one copied into <em>tui </em>(from <em>bar</em>), however, needs a further change, as <em>tui</em> has a sub-directory (<em>io)</em>:</p>
<pre><span style="color: #ff0000;"># SConscript file for foobar/bar</span>
<span style="color: #008000;"># SConscript file for tictac/tui</span>
<span style="color: #3366ff;">...</span>

<span style="color: #808080;"># Set up some local variables
subdirs = Split("""</span>
                <span style="color: #008000;">io</span>
                <span style="color: #808080;">""")</span>
<span style="color: #3366ff;">...</span></pre>
<p>The same <em>SConscript</em> file can be copied from <em>bar</em> into <em>io</em>, and the first comment changed:</p>
<pre><span style="color: #ff0000;"># SConscript file for foobar/bar</span>
<span style="color: #008000;"># SConscript file for tictac/tui/io</span>
<span style="color: #3366ff;">...
</span></pre>
<p>Now the test builder <em>SConscript</em> files can be copied over. First the <em>tictac/tests/SConscript</em> file can be copied from <em>foobar/tests/SConscript</em>. All that needs changing in this file is that ever present first comment:</p>
<pre><span style="color: #ff0000;"># SConscript file for foobar/tests</span>
<span style="color: #008000;"># SConscript file for tictac/tests</span>
<span style="color: #3366ff;">...</span></pre>
<p>In fact, the <em>SConscript </em>files for all test directories are identical, except for that first comment. Just change it to the path (starting with the library-level directory) to that test directory.</p>
<p>Finally, the library builder <em>SConscript</em> file can be set up. It is just copied from <em>foobar/lib</em> to <em>tictac/lib</em> and changed appropriately. When looking at the script it was noticed that the constant &#8216;foobar&#8217; was used in several places. To make it easier to change the library builder <em>SConscript</em> files a variable was added at the top of the library-level <em>SConscript</em> file (<em>src/tictac/SConscript) </em> to hold the library name, and this can be used throughout. It is exported to the library builder <em>SConscript</em> (and to any other <em>SConscript</em> file when needed). This keeps the required changes to a minimum. The <em>src/tictac/lib/SConscript</em> file, therefore, becomes:</p>
<pre><span style="color: #ff0000;"># Library building SConscript for foobar</span>
<span style="color: #008000;"># Library building SConscript for tictac</span>
<span style="color: #3366ff;">...</span>
<span style="color: #888888;">Import('*')</span>

<span style="color: #888888;">loc_env.MI_SetDirectories()</span>
<span style="color: #3366ff;">...</span>

<span style="color: #808080;">if env['msvs']:</span>
  <span style="color: #ff0000;">libname = env['LIBPREFIX']+"foobar"+env['LIBSUFFIX']</span>
<span style="color: #008000;">  libname = env['LIBPREFIX']+thislib+env['LIBSUFFIX']</span>
  <span style="color: #808080;">env.MI_BuildMSVSProject(srcf,incf,libname)
else:</span>
  <span style="color: #ff0000;">env.Library('foobar',objs)</span>
<span style="color: #008000;">  env.Library(thislib,objs)</span>

<span style="color: #808080;"># Add it to the link list</span>
<span style="color: #ff0000;">env.Prepend(LIBS='foobar')</span>
<span style="color: #008000;">env.Prepend(LIBS=[thislib])</span></pre>
<p>and the variable and exports are added to <em>src/tictac/SConscript</em>:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #808080;"># &lt;http://www.gnu.org/licenses/&gt;.</span>

<span style="color: #008000;"># Set up this library name
thislib = 'tictac'</span>

<span style="color: #808080;"># Enter any subdirectories here
subdirs = Split("""</span>
<span style="color: #3366ff;">...</span>

<span style="color: #808080;"># Only put the library together if we have object files
if len(objs)&gt;0:
  # Subdirectories may have changed the directories, so reset them,
  # and set the appropriate library path
  env.MI_SetDirectories()
  env.MI_SetLibraryPath()</span>

  <span style="color: #808080;">SConscript('lib/SConscript', exports=['objs'</span><span style="color: #008000;">, 'thislib'</span><span style="color: #808080;">])</span>

<span style="color: #808080;">elif env['msvs']:
  SConscript('lib/SConscript', exports=['objs'</span><span style="color: #008000;">, 'thislib'</span><span style="color: #808080;">])</span>
<span style="color: #3366ff;">...</span></pre>
<p>The changes to use the variable <strong>thislib</strong> were also made in the <em>src/foobar/SConscript </em>and <em>src/foobar/lib/SConscript</em> files.</p>
<blockquote><p><strong>NOTE: </strong>The square brackets have been added to the <strong>Prepend</strong> line to ensure the library is prepended correctly. This change has also been made to the SConscript file in the foobar/lib directory.</p></blockquote>
<p>Now the <em>tictac</em> directory can be added to the top-level <em>src</em> directory <em>SConscript </em>file:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #808080;"># Simply call the SConscript file for each module
mods = Split("""
             foobar</span>
             <span style="color: #008000;">tictac</span>
             <span style="color: #808080;">""")</span>
<span style="color: #3366ff;">...
</span></pre>
<p>When adding the libraries to the top-level <em>SConscript</em>, you should put the lowest level first to ensure correct ordering.</p>
<p>The libraries should now build without any warnings or errors. A couple of potential problems are noticeable, though. Firstly, an empty <strong>tictac</strong> library is built, and, secondly, it is included on the link line for any <strong>foobar</strong> based executable. For example, the link line for <strong>test_foo</strong> (using gcc) is:</p>
<pre>g++ -o build/release/foobar/tests/test_foo →
       build/release/foobar/tests/test_foo.o →
       -Lbuild/release/foobar/lib -Lsrc/foobar/lib →
       -Lbuild/release/tictac/lib -Lsrc/tictac/lib →
       -ltictac -lfoobar</pre>
<p>The <strong>foobar</strong> library represents a lower level library than <strong>tictac</strong>, so shouldn&#8217;t require <strong>tictac</strong> to build. Also, <strong>tictac</strong> shouldn&#8217;t be built if there is no code to put in it, as this is unnecessary work for the build.</p>
<p>The first of these problems stems from the fact that SCons&#8217; first job is to scan all the <em>SConscript</em> files. This sets all variables (including the libraries to link). To fix this, a different SCons environment will be set up for each library, with higher level libraries starting with the environment from the previous level and adding it&#8217;s own bits and pieces.</p>
<p>The first part of this is to change the name of <strong>env</strong> in the <em>SConstruct</em> file to <strong>glob_env</strong> (for global environment) so that variables that need to be passed from library to library can be stored. For example:</p>
<pre><span style="color: #008000;"><span style="color: #3366ff;">...</span>
glob_</span><span style="color: #888888;">env = Environment(options = opts)</span>

<span style="color: #888888;">Help(opts.GenerateHelpText(</span><span style="color: #008000;">glob_</span><span style="color: #888888;">env))</span>

<span style="color: #888888;"># If vs2005 or mingw is set, get and environment with
# the appropriate tool instead
if</span> <span style="color: #008000;">glob_</span><span style="color: #888888;">env['vs2005']:</span>
  <span style="color: #008000;">glob_</span><span style="color: #888888;">env = Environment(options = opts,
                         MSVS_VERSION = '8.0')</span>
<span style="color: #888888;">elif</span> <span style="color: #008000;">glob_</span><span style="color: #888888;">env['mingw']:</span>
  <span style="color: #008000;">glob_</span><span style="color: #888888;">env = Environment(options = opts,
                         tools = ['mingw']</span>
<span style="color: #3366ff;">...</span>
<span style="color: #808080;"># Allow others to use the environment
Export(</span><span style="color: #808080;">'</span><span style="color: #008000;">glob_</span><span style="color: #808080;">env')</span>
<span style="color: #3366ff;">...</span></pre>
<p>This name change is done throughout the <em>SConstruct</em> file, and it&#8217;s now <strong>glob_env<em> </em></strong>that is exported.</p>
<p>Next the <strong>glob_env</strong> variable needs to be cloned in each library to create <strong>loc_env</strong> (for local environment) in the library-level <em>SConscript</em> file. We then need to export the <strong>loc_env</strong> variable to each of the subsequent <em>SConscript</em> files. This is done as a &#8216;local&#8217; export through the <strong>SConscript </strong>functions rather than globally through the <strong>Export</strong> function. Finally, the <strong>glob_env</strong> environment is reset with a clone of the <strong>loc_env</strong> environment so that the next library up the chain can use the lower level libraries and their settings. For example, in <em>src/foobar/SConscript</em>:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #808080;"># Import the global variables
Import('*')</span>

<span style="color: #008000;"># Create a local environment for building this library
loc_env = glob_env.Clone()</span>
<span style="color: #3366ff;">...</span>
<span style="color: #808080;"># Build any subdirectories
for subdir in subdirs:</span>
  <span style="color: #808080;">if <span style="color: #008000;">loc_</span>env['msvs']:
    i, s = SConscript('%s/SConscript' % subdir<span style="color: #008000;">, exports='loc_env'</span>)
    incf = incf + i
    srcf = srcf + s
  else:
    o = SConscript('%s/SConscript' % subdir<span style="color: #008000;">, exports='loc_env'</span>)
    objs.append(o)

# Only put the library together if we have object files
if len(objs)&gt;0:
  # Subdirectories may have changed the directories, so reset them
  # and set the appropriate library path
  <span style="color: #008000;">loc_</span>env.MI_SetDirectories()
  <span style="color: #008000;">loc_</span>env.MI_SetLibraryPath()

  SConscript('lib/SConscript', exports=['objs', 'thislib' <span style="color: #000000;">→</span>
<span style="color: #008000;">, 'loc_env'</span>])

elif <span style="color: #008000;">loc_</span>env['msvs']:
  SConscript('lib/SConscript', exports=['incf', 'srcf', 'thislib'<span style="color: #008000;"> <span style="color: #000000;">→</span>
, 'loc_env'</span>])

# Now try to build and run any tests (this will normally be done in
# a testing framework, but here we are simply testing scons...)
SConscript('tests/SConscript'<span style="color: #008000;">, exports='loc_env'</span>)</span>

<span style="color: #008000;"># Put everything we've set up here back into the global environment
glob_env = loc_env.Clone()</span></pre>
<p>Similar changes have to be made in all <em>SConscript</em> files, ensuring all <strong>env</strong> references are changed to <strong>loc_env</strong>, and that the <strong>loc_env</strong> variable is exported through any call to the <strong>SConscript</strong> function. The calls to Clone are only needed in the library-level <em>SConscript</em> file, however, not the sub-directory ones.</p>
<p>As the environment is local, the same name can be used in all the libraries, so a new library can be added as described at the start of this post &#8211; by copying the files from an existing library, and making minimal changes. Remember, though &#8211; you should still ensure you understand the script you are copying! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Now for the second problem &#8211; stopping the empty library from being built. The problem lies in the lines:</p>
<pre>o = SConscript('%s/SConscript' % subdir, exports='loc_env')
objs.append(o)

# Only put the library together if we have object files
if len(objs)&gt;0:</pre>
<p>These exist in various <em>SConscript</em> files. In this code, even if <strong>o</strong> is empty it is still appended to the <strong>objs</strong> list. The <strong>objs</strong> variable then has a length of greater than zero, so the library is built.</p>
<p>To solve this is simply a matter of checking that the <strong>o</strong> variable isn&#8217;t empty before appending it to the <strong>objs</strong> variable in each <em>SConscript</em> file, and printing a warning if the <strong>objs</strong> list is empty:</p>
<pre><span style="color: #888888;">o = SConscript('%s/SConscript' % subdir, exports='loc_env')</span>
<span style="color: #008000;">if len(o)&gt;0:</span>
<span style="color: #008000;">··</span><span style="color: #888888;">objs.append(o)</span>

<span style="color: #888888;"># Only put the library together if we have object files
if len(objs)&gt;0:
  # Subdirectories may have changed the directories, so reset them
  # and set the appropriate library path
  loc_env.MI_SetDirectories()
  loc_env.MI_SetLibraryPath()

  SConscript('lib/SConscript', export=['objs', 'thislib', 'loc_env'])

elif loc_env['msvs']:
  SConscript('lib/SConscript', exports=['incf', 'srcf', 'thislib' <span style="color: #000000;">→</span>
,'loc_env'])
<span style="color: #008000;">else:
  print "WARNING:", thislib, "library is empty."</span>
</span></pre>
<p>After this change to all <em>SConscript</em> files the empty <strong>tictac</strong> library no longer builds. This means that, in the future, if developers begin to think that certain bits of code fit better into a different library, and move them about, and leave a library empty (believe me it does happen), then the library won&#8217;t be built, and any link line including it will fail! In fact, this can be taken a little further, and maybe a library with only a single object file (or maybe two) should be considered for refactoring, distributing it&#8217;s source files elsewhere. This can be done by adding a new command line option to the <em>SConstruct</em> file:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #808080;">opts.AddOptions(
    ('bdir', "Set to the build directory if you don't like 'build'", <span style="color: #000000;">→</span>
'build'),
    ('sdir', "Set to the source directory if you don't like 'src'", <span style="color: #000000;">→</span>
'src'),</span>
    <span style="color: #008000;">('smalllib',
 'Set to the number of object files below which a library is <span style="color: #000000;">→</span>
considered small',
     0),</span>
    <span style="color: #808080;">BoolOption('debug','Set to apply debugging flags','false'),</span>
<span style="color: #3366ff;">...</span></pre>
<p>and using this command line option to issue a warning in the library builder <em>SConscript</em> files:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #808080;">Import('*')

loc_env.MI_SetDirectories()</span>

<span style="color: #008000;">objs2 = list(mi_scons.MI_FlattenList(objs))</span>
<span style="color: #008000;">if len(objs2)&lt;=int(loc_env['smalllib']):
  print "WARNING: tictac only contains", len(objs2) ,"object files."</span>

<span style="color: #808080;">if loc_env['msvs']:</span>
<span style="color: #3366ff;">...</span></pre>
<p>You will notice that a new function was used in the above code &#8211; <strong>MI_FlattenList</strong>. This function is necessary to flatten the list to give the correct number of object files when using <strong>len()</strong>, as the list is nested when gathering object files from sub-directories. The new function is added to the <em>mi_scons.py</em> file:</p>
<pre><span style="color: #008000;"><span style="color: #3366ff;">...</span>
def MI_FlattenList(lst):
  for i in lst:
    if type(i) is list:
      for j in MI_FlattenList(i):
        yield j
    else:
      yield i
<span style="color: #3366ff;">...</span></span></pre>
<p>This function isn&#8217;t added to the SCons environment like the others (as it doesn&#8217;t need or use the environment, it isn&#8217;t appropriate to add it), so to make it available to all scripts we have to export the <strong>mi_scons</strong> module in the <em>SConstruct </em>file:</p>
<pre><span style="color: #3366ff;">...</span>
<span style="color: #808080;"># Allow others to use the environment</span> <span style="color: #008000;">and mi_scons module</span>
<span style="color: #808080;">Export('glob_env'</span><span style="color: #008000;">,'mi_scons'</span><span style="color: #808080;">)</span>
<span style="color: #3366ff;">...</span></pre>
<p>Now we can use the <strong>MI_FlattenList<em> </em></strong>function in the library scripts.</p>
<p>The final structure of the <em>tictac</em> tree is:</p>
<pre>src/
  +-- foobar/
  +-- SConscript
  +-- tictac/
  |     +-- core/
  |     |     +-- SConscript
  |     |     +-- tests/
  |     |     |     +-- SConscript
  |     +-- lib/
  |     |     +-- SConscript
  |     +-- SConscript
  |     +-- tests/
  |     |     +-- SConscript
  |     +-- tui/
  |     |     +-- io/
  |     |     |     +-- SConscript
  |     |     |     +-- tests/
  |     |     |     |     +-- SConscript
  |     |     +-- SConscript
  |     |     +-- tests/
  |     |     |     +-- SConscript</pre>
<hr style="width: 50%;" />
<p>The above changes have been added to the <a title="The example code, and how to get it!" href="http://www.mereidea.com/blog/example-code" target="_self">example code&#8217;s</a> repository as revision 22. Some of the added bits may need further tweaks once code is added to the <em>tictac</em> library. That&#8217;s the time we find out if this all works! <img src='http://www.mereidea.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=eoxIfQW15Ss:PdAHSE0KxV4:l6gmwiTKsz0"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=l6gmwiTKsz0" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=eoxIfQW15Ss:PdAHSE0KxV4:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=eoxIfQW15Ss:PdAHSE0KxV4:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?a=eoxIfQW15Ss:PdAHSE0KxV4:I9og5sOYxJI"><img src="http://feeds.feedburner.com/~ff/MereIdeaDevelopmentBlog?d=I9og5sOYxJI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/MereIdeaDevelopmentBlog/~4/eoxIfQW15Ss" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.mereidea.com/blog/2009/10/02/adding-a-new-library-to-the-mere-idea-scons-example-code/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.mereidea.com/blog/2009/10/02/adding-a-new-library-to-the-mere-idea-scons-example-code/</feedburner:origLink></item>
	</channel>
</rss>

