Last time I introduced ClearCase and how it helps maintaining large applications. Basically, it allows you to build a huge system piece by piece while still being able to track the dependencies between the components.
ClearCase tracks dependencies by monitoring the build process. In order to do that you need to employ a specific tool, called omake, which does some magic based on makefiles.
I’ll talk about the magic later. For now, let me focus on makefiles and how they relate to builds.
My explanation of makefiles will be short and not comprehensive. There is an abundance of information about the topic on the Internet.
I already mentioned that building software merely consists of translation of scripts in domain specific languages into the commands the targeted computer system can understand.
A makefile describes these translations and their dependencies. It will also include all steps necessary to setup and tear down the environment for each translation step, like creating directories, copying files and maintening meta-data such as registry settings.
To control the individual pieces, makefiles consist of targets and rules. Targets are made by executing the associated rule.
There is also a way to specify a total order on the targets. For each target a makefile lists the targets it depends on. Usually targets are filenames, which enables checking dependencies by means of time, that is, a target needs to be made if it’s older than any of its dependencies (or doesn’t exist at all).
In every makefile there is a primary target, commonly called ALL. Making this target involves making a significant number of dependencies to finally build the software described by this makefile.
Makefiles support parameters, so various parameter configurations can be used to make different builds. Usually there is a build with rich integrated debugging information but no optimization and the opposite, a release build, with optimizations and externalized debugging information.
Makefile development is not an easy task, so many of the development tools available today spare you from the chores of doing it.
One such tool is Visual Studio, which in its current incarnation does not even support generation of makefiles anymore. It organizes targets with specific build rules as projects and maintains their dependencies by organizing them in solutions. If there are additional steps necessary, custom scripts can be specified that run before or after a target project is build.
There are at some issues with this approach that might require you to use makefiles, though.
Setting up a translation for Visual Studio is anything but easy. In 99% of the cases, you will go with the translations already provided by Microsoft, e.g. for Crystal Reports, C++, C#, Visual Basic, IDL and resource files, or you will download 3rd party extensions, such as Together (UML modeling) or Active Reports (report generation).
But there is the 1% of parameters, options or compiler capabilities that Visual Studio doesn’t support. Or, you might choose to use a different compiler for your release builds, for instance to make your bits really fly.
The support of multiple configurations in Visual Studio is limited to the scope of a solution. So, if your configurations have different dependencies, you need to include them as projects and set their parameters appropriately.
This may not always be possible or it may cause solutions to grow quickly. If your projects have custom build steps, the dependencies have to be always rebuilt. These rebuilds increase build time in large projects prohibitively.
So for a componentized system of a certain size, makefiles are the only choice. The next version of Visual Studio will use a more extensible build system to address these issues.
ClearCase is focused around building components individually using makefiles. This causes some friction when trying to build .NET components in ClearCase environments with Visual Studio.