One of the challenges of cross-platform development is managing your build environment. With so many platforms these days using the combination of Microsoft Visual Studio and an emulator to facilitate rapid development and frequent cross-compilation using a separate build chain, you may frequently find your emulation and target build environments don’t have the same file lists or dependencies. This is because you have to update both the Microsoft Visual Studio environment and the target hardware environment using something like a make file or an Ant script. The repercussions of this process are obvious: bizarre run-time problems or broken builds that result in the gnashing of teeth for everyone on the development team.
Tired of this pain in our organization and fresh from switching from Microsoft to Ant to automate builds, we decided to use Ant’s built-in XSL processor to convert from Microsoft’s XML vocabulary to Ant’s XML vocabulary on-the-fly as part of a pre-build step. This eliminates the need for file dependency lists for the target platform entirely. In essence, this delegates the responsibility for maintaining the build dependencies to Microsoft Visual Studio alone, giving the development team a single place in which to add or remove files. This article shows you how to do this for the Slideshow application in the QUALCOMM BREW development book Software Development for the QUALCOMM BREW Platform You can download the source code here. Remember that the same strategy can be used for any remote target environment if you’re leveraging Ant for your device builds
Understanding the Vocabulary of a Microsoft Developer Studio Project File
With the advent of Microsoft Visual Studio C++ for .NET (the seventh major release of Microsoft Visual Studio), the tool chain now stores project files as XML documents, making it easy for developers to manage and process build dependencies. Listing 1 shows a typical .vcproj project file.
Most of the tags are self-explanatory. Each build configuration (release, debug, or custom configurations) is contained within the tag; this tag specifies both the tools used to build a specific configuration, as well as build options (include paths and compile-time definitions) and the target directory. There’s also the tag, which contains a list of files grouped in the project in tags, reflecting the organization of the vcproj in Microsoft Visual Studio’s Solution Explorer. Within the tag someplace?depending on the organization of your Solution Explorer window?are tags, one for each of the files that comprise your project.
Transforming the VCPROJ to Ant’s XML Vocabulary Using XSL
Ant supports XML style transformations through the use of the integrated Xalan processor. To convert the .vcproj to an Ant build script, you must:
- Define build property values for each of the environment variables.
- Transform the list of source files from the .vcproj file to the Ant build script vocabulary, reconciling the .vcproj‘s format for relative paths as appropriate.
- Create a top-level Ant project.
- Insert a task directive to invoke the target compiler.
In addition to these tasks, the transformation must take into account the specific build tools?we use an internally built Ant task called armcc that invokes the ARM RealView Developer Suite (ARM RVDS) command-line tools akin to ccTask for Ant. You may find that you can use ccTask, or you can write your own Ant task. You may also be able to get what you need by using Ant’s exec task alone to invoke specific compiler tools.
Listing 2 shows the transformation used to manage an incoming .vcproj such as the one you saw in Listing 1. Like most XSL documents, it’s pretty dense, so it may be helpful to walk through it step by step.
It opens with the typical preamble for an XSL document, pointing to the World Wide Web Consortium’s schema for XSL. The next two lines specify information about the output of the transformation, namely, that the output is indented XML (making it easier to read as you debug the transformation) and that white space is to be preserved when parsing the source’s XML for the incoming .vcproj vocabulary.
The next block, consisting of two declarations, defines variables to contain the name of the project and the name of the target output. These are taken directly from the incoming .vcproj file; the project.name variable is assigned the value of the Name property of the VisualStudioProject tag in the .vcproj file, while the target.name variable is assigned a value giving a unique name for the resulting Ant script (after the transformation) based on the project name specified in the .vcproj file.
There are four template directives within the XSL document: one to match the top-level Microsoft Visual Studio Project (corresponding to the VisualStudioProject tag in .vcproj), one to match the build configuration (corresponding to the tag in .vcproj), and two to match the tag in the .vcproj, which actually constructs the bulk of the transformed build script.
The first template directive, matching the VisualStudioProject tag, transforms the VisualStudioProject tag in a .vcproj file to an Ant project tag. As it does this, it includes the Ant task definition for the armcc task, as well as specifies the name for the target, based on the value of the target.name variable created in the variable definitions. Finally, it applies all subsequent templates to the contents of the VisualStudioProject tag in the source .vcproj file.
The second template pulls the configuration settings out of .vcproj‘s Configuration tag, selecting the tag with the attribute named Debug|Win32, (the default named debug build configuration in our environment). The settings it captures are the desired output directory as well as the include paths for the project. Capturing and transforming these values is a little tricky: here’s a close look at the include directory, cleaned up some from the initial listing for readability:
This snippet sets the Ant build property armcc.includedirs to be the value of the value of the AdditionalIncludeDirectories attribute of the Tool tag in the incoming .vcproj file, and converts the Microsoft Visual Studio semicolon-delimited list into a comma-delimited list, which is required by Ant.
If the responsibility of the first template is to produce the Ant project, and the second template’s is to harvest and prepare the ouput directory and include directory list, the third template performs the bulk of the work in the transformation. Matching the tag within the Debug build configuration of the .vcproj, it converts the list of C files to an Ant filelist tag. In converting the .vcproj entry to an Ant filelist tag, it:
- Generates build-time status output by inserting Ant echo tags indicating the name of the project, the resource build step, and the source build steps.
- Creates an Ant filelist tag containing the list of files, giving this tag the and id armcc-Slideshow.files.source.
- Generates a call to the armcc task passing it the id of the Ant filelist along with compile-time defines, the include path, and the output directory.
The last step is optional?you can just as easily pull compile-time defines from the .vcproj. It’s a little easier to do it this way (even if it means that tweaking in Microsoft Developer Studio must be carried over to the style sheet) because some defines for the environment (such as -DAEE_SIMULATOR) differ between the emulation and target hardware. Another point to note is an artifact of the armcc task: it doesn’t selectively rebuild object files, but rather performs a clean build on each invocation; this ensures that the build server is always generating a truly fresh build at the expense of some CPU cycles.
The final template directive is the simplest; it must merely map from the RelativePath attribute of each of the .vcproj file’s tags to an Ant file tag and its name attribute.
Running the ARM Build with Ant
The beauty of this trick is that it does not change how you invoke Ant; once you write and debug the transformation, you need only include it in your Ant build script. The one we used is shown in Listing 3.
The Ant build script begins by defining the target name and properties for the target name, such as the input vcproj (set as the Ant property SlideShow.vcproj.file) along with the input and output directory. The magic comes with the line:
This invokes Ant’s XSL processor, converting the input vcproj SlideShow.vcproj to the resulting file SlideShow-armcc.xml using the style sheet vcproj2ant_2.xsl, shown in Listing 2.
After performing the transformation, the Ant script in Listing 3 outputs the base directory, project directory, and output directory file and name, and then invokes itself using the newly created Ant script SlideShow-armcc.xml. You can see the Ant-created result of the transformation in Listing 4.
Running this script with Ant produces a full build?individual object files linked into the target elf and then converted to a BREW module, as you see in Listing 5.
The strategy presented here is applicable for any XML-based build environment, because it’s always possible to use XSL to convert from one XML vocabulary to another. The method can apply not just to file management in a software product, but to all aspects of build configuration management supported by Microsoft Developer Studio (including multiple targets based on compile-time options, different tool chain choices, and the like) given a sufficient investment in the transformation document.