Channels ▼
RSS

Design

A Build System for Complex Projects: Part 5


Removing a File

Removing a file is just as easy: You simply remove unnecessary files from the file system, run ibs, and watch the removed files disappear from all the build files.

Adding a New Library

The H team was proud of its software engineering acumen and shared their divide-and-conquer approach with the W team responsible for the world library. The W team got excited and wanted to pursue a similar approach. However Isaac (the development manager) wanted to go even further. He noticed that "hello" and "world" share the letters "o" and "l" and proposed a new reusable letters library that wil provide functions for getting important letters. This library can be used by the "hello" and "world" libraries to get all the letters they need.

The U team (responsible for developing the utils library) was assigned the task of creating the letters library. The library consisted two files: letters.cpp and letters.hpp. Each letter needed for the hello world application got its own function. Here is the code for letters.cpp (letters.hpp contains the function prototypes):


#include "letters.hpp"

std::string get_h() { return "h"; }
std::string get_e() { return "e"; }
std::string get_l() { return "l"; }
std::string get_o() { return "o"; }
std::string get_w() { return "w"; }
std::string get_r() { return "r"; }
std::string get_d() { return "d"; }

The H and W teams modified the getHello() and getWorld() methods to use the new letters library. The H team also got rid of the helpers.cpp and helpers.hpp files that were no longer needed. Here is the code for world.cpp file, which implements the getWorld() method:


#include "world.hpp"
#include <hw/letters/letters.hpp>
std::string WorldProvider::getWorld()
{
  return get_w() + get_o() + 
         get_r() + get_l() + get_d();
}

This is a great example of code reuse and the code base is now very flexible. For example, if the project stakeholders decided that all the "o" letters in the system should be uppercase, only the get_o() function of the letters library will have to change and all the libraries and applications using it will just need to relink against it.

What kind of changes to the build files are needed to add a new library? First all the build files necessary to build the library itself, then all the dynamic libraries or executables that depend on it (directly or indirectly) must link against it. In addition, you want to update the workspace file so the new library shows up in the IDE and can be built and debugged in the IDE. Of course, you want to do all that for all the platforms you support. That's a lot of work and it's easy to miss a step or misspell a file here and there. Just figuring out what test programs and applications need to link against the new library is pretty labor intensive. Luckily, for Isaac and his development team ibs can do all that automatically. The single act of placing the letters library under the src\hw directory is enough to tell ibs everything it needs to know. Let's see what ibs did on Windows this time:

  • Created the letters.vcproj file in the hw/letters directory.
  • Added the letters project to the hello_world solution under the hw folder (see Figure_1)
  • Figured out by following the #include trail that the "hello" and "world" libraries use "letters" and hence any program that uses either "hello" or "world" depend on "letters" and will link against it automatically. Currently, that's the hello_world application itself and the testHello and testWorld test programs.

[Click image to view at full size]
Figure 1

Here are the relevant changes to the hello_world.sln file:


Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "letters", "hw\letters\letters.vcproj", "{C27369BC-2E11-4571-B524-2F0279F202BD}"
EndProject

Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hello", "hw\hello\hello.vcproj", "{23B8D8A1-8E84-462B-BF90-58E1F07D267D}"
	ProjectSection(ProjectDependencies) = postProject
	     {C27369BC-2E11-4571-B524-2F0279F202BD} = {C27369BC-2E11-4571-B524-2F0279F202BD}
	EndProjectSection
EndProject

Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "world", "hw\world\world.vcproj", "{2A5E91EE-8A54-4594-A28E-3185F5F8602C}"
      ProjectSection(ProjectDependencies) = postProject
        {C27369BC-2E11-4571-B524-2F0279F202BD} = {C27369BC-2E11-4571-B524-2F0279F202BD}
	EndProjectSection
EndProject

	{C27369BC-2E11-4571-B524-2F0279F202BD}.Debug|Win32.ActiveCfg = Debug|Win32
	{C27369BC-2E11-4571-B524-2F0279F202BD}.Debug|Win32.Build.0 = Debug|Win32
	{C27369BC-2E11-4571-B524-2F0279F202BD}.Release|Win32.ActiveCfg = Release|Win32
	{C27369BC-2E11-4571-B524-2F0279F202BD}.Release|Win32.Build.0 = Release|Win32

  {C27369BC-2E11-4571-B524-2F0279F202BD} = {0276cb28-8c64-46ae-9e52-3363bb4dcbd8}

Adding a New Test

The U team did a good job with the letters library and to adhere to the development standard, it added a test program too -- not TDD (Test Driven Development), but better than no tests at all. The U team created a directory called testLetters under src/test and put the following main.cpp file in it:


#include <hw/utils/base.hpp>
#include <hw/letters/letters.hpp>
#include <iostream>

int main(int argc, char** argv) 
{
  CHECK(get_h() == std::string("h")); 
  CHECK(get_e() == std::string("e")); 
  CHECK(get_l() == std::string("l")); 
  CHECK(get_o() == std::string("o")); 
  CHECK(get_w() == std::string("w")); 
  CHECK(get_r() == std::string("r")); 
  CHECK(get_d() == std::string("d")); 
  
  return 0;
}

After invoking ibs, the new testLetters project became part of the solution and the U team ran the test successfully.

Adding a New Application

The "Hello World - Enterprise Platinum Edition" was a great success and became a killer app overnight. However, some big players weren't satisified with the security of the system and demanded an encrypted version of hello world. Isaac (the development manager) decided that this called for a separate application to keep the original hello_world application nimble and user-friendly. The new application was to be called "Hello Secret World" and print an encrypted version of the string "hello world!". Furthermore, it will not use any of of intensive infrastructure built for the original "Hello World" system. A special no-name clandestine team was recruited to implement it. After a lot of deliberation, the no-name team decided to implement the ultimate encryption algorithm -- ROT13. In addition, the team demonstrated a nice usage of the standard transform() algorithm to apply the ROT13 encryption.


#include <iostream>
#include <algorithm>

char ROT13(char c)
{
  if (c >= 'a' && c < 'n')
    return char(int(c) + 13);
  else if (c > 'm' && c <= 'z')
    return char(int(c) - 13);
  else
    return c;
}
int main(int argc, char** argv) 
{
  std::string s("hello, world!");
  // Apply the secret ROT13 algorithm
  std::transform(s.begin(), s.end(), s.begin(), ROT13);
  std::cout <<s.c_str() << std::endl;
  return 0;
}

Again, ibs took care of integrating the new application. The unnamed team just had to put its hello_secret_world application under src/apps.

Extending the Build System

To this point, Bob hasn't make an appearence in this article and it is a good sign. The developers, including the new unnamed team, were able to use ibs effectively without any help from Bob. But, the success of the "hello world" product family brought new demands. Upper management decided that they want to package the "hello world" functionality as a platform and let other developer enjoy "hello world" (for a small fee of course). Isaac conducted a thorough market analysis and concluded that Ruby is the way to go. He summoned Bob and asked him to extend ibs, so it will be possible to provide Ruby bindings for the "hello" and "world" libraries.

Bob started to research the subject, soon discovering that Ruby depends on the gcc toolchain to produce its bindings. It's possible on Windows to generate an NMAKE file for Visual Studio, but Bob decided that he would first take a shot of building a Ruby binding for the Mac OS X only.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video