Friday, January 14, 2011

Nested ItemGroups in MSBUILD

First something about what I want to achieve. In our setup we're using boost C++ library quite intensely and therefore we would like the code and the build process to be part of our TFS setup. We don't use the binary libraries out of the box because for example in Visual Studio 2005 and 2008 a release build was built by default with checked iterators (macro _SECURE_SCL = 1) and our code was build without checked iterators. Boost only supported the Visual studio defaults, so we have a long tradition of building our own boost libraries. The default checked iterator policy was changed back in Visual Studio 2010 to disabled in release mode, but we maintain both flavours.
Since Visual studio 2010 we started using TFS and TFS build. The custom MSBUILD project to produce the boost binaries had the following structure

<ItemGroup>
  <BoostLib Include="regex"/>
  <BoostLib Include="thread"/>
  <BoostLib Include="filesystem"/>
  <BoostLib Include="date_time"/>
  <BoostLib Include="random"/>
</ItemGroup>
<ItemGroup>
  <BuildPlatform Include="Win32">
     <AddressModel>32</AddressModel>
  </BuildPlatform>
  <BuildPlatform Include="x64">
    <AddressModel>64</AddressModel>
  </BuildPlatform>
</ItemGroup>
<ItemGroup>
  <BuildVariant Include="Debug">
    <CxxFlag>\D_SECURE_SCL=1</CxxFlag>
    <Variant>debug</Variant>
  </BuildVariant>
  <BuildVariant Include="Release">
    <CxxFlag>\D_SECURE_SCL=1</CxxFlag>
    <Variant>release</Variant>
  </BuildVariant>
  <BuildVariant Include="ReleaseSecure0">
    <CxxFlag>\D_SECURE_SCL=0</CxxFlag>
     <Variant>release</Variant>
  </BuildVariant>
</ItemGroup>
<PropertyGroup>
  <_CommonCmdLine>$(ProjectDir)..\bjam.exe toolset=msvc-10.0 link=static threading=multi runtime-link=static --cxxflags=/MP</_CommonCmdLine>
</PropertyGroup>
This gave me maximum flexibility: I can add or remove libraries, build variants or address models. But now I had to call bjam for each combination. And this is not trivial in MSBUILD.

I have found one reference to combining or nesting Items from ItemsGroups, but sadly this doesn't seem to work for me. If I attach the follwoning target, as suggested in the blog


<Target Name="Build">
    <PropertyGroup>
      <CurrentVariant>%(BuildVariant.CxxFlag)</CurrentVariant>
    </PropertyGroup>
    <Message Text="VariantPlatform $(CurrentVariant) : %(BuildPlatform.FileName)"/>
</Target>

The output I get reads

So the property CurrentVariant gets all the subsequent BuildVariant values and in my Message task I'm stuck with the last value.

I will not bother you with all the combinations and tricks I have tried but in the end I experimented with CreateItem and came up with the idea to batch over one type item while adding AddionalMetadata from another type. Something like this



<Target Name="Build">
   <CreateItem
      Include="@(BuildVariant)"
      AdditionalMetadata="Platform=%(BuildPlatform.FileName);AddressModel=%(BuildPlatform.AddressModel)">
      <Output
        TaskParameter="Include"
        ItemName="VariantPlatform"/>
     </CreateItem>
    <Message Text="VariantPlatform %(VariantPlatform.Platform), %(VariantPlatform.FileName), %(VariantPlatform.AddressModel)"/>
</Target>

This gave the following result
This was exactly what I was after: a combination of the two properties!


For completeness here is the rest of the  MSBUILD projects which builds the boost binaries

<Target Name="ComposeItems" BeforeTargets="Build;Clean;Rebuild">
   <CreateItem
     Include="@(BuildVariant)"
     AdditionalMetadata="Platform=%(BuildPlatform.FileName);AddressModel=%(BuildPlatform.AddressModel)">
       <Output
         TaskParameter="Include"
          ItemName="VariantPlatform"/>
   </CreateItem>
   <Message Text="VariantPlatform %(VariantPlatform.Platform) : %(VariantPlatform.FileName) ? %(VariantPlatform.AddressModel)"/>
   <CreateItem
     Include="@(VariantPlatform)"
     AdditionalMetadata="Lib=%(BoostLib.FileName)">
        <Output
         TaskParameter="Include"
         ItemName="Tobuild2"/>
       </CreateItem>
    <CreateItem
       Include="@(Tobuild2)"
       AdditionalMetadata="IntermDir=$(INTERMEDIATEROOT)\Boost_$(Boostver)\%(Tobuild2.Platform)\%(Tobuild2.FileName); OutDir=$(OUTPUT)%(Tobuild2.Platform)\%(Tobuild2.FileName)">
       <Output
       TaskParameter="Include"
       ItemName="Tobuild"/>
       </CreateItem>
         <Message Text="BUILD %(Tobuild.Lib) %(Tobuild.Platform) : %(Tobuild.FileName) ? %(Tobuild.AddressModel) %(ToBuild.StageDir)"/>
     </Target>
     <Target Name="Build">
     <Exec WorkingDirectory="$(ProjectDir)boost_$(BOOSTVER)"
Command='$(_CommonCmdLine) variant=%(Tobuild.Variant) cxxflags=%(Tobuild.CxxFlag) "--stagedir=%(ToBuild.OutDir)" "--build-dir=%(ToBuild.IntermDir)" --with-%(ToBuild.Lib) stage'/>
     </Target>
     <Target Name="Clean">
     <Exec WorkingDirectory="$(ProjectDir)boost_$(BOOSTVER)"
Command='$(_CommonCmdLine) --clean variant=%(Tobuild.Variant) cxxflags=%(Tobuild.CxxFlag) "--stagedir=%(ToBuild.OutDir)" "--build-dir=%(ToBuild.IntermDir)" --with-%(ToBuild.Lib) stage'/>
     </Target>
     <Target Name="Rebuild">
     <Exec WorkingDirectory="$(ProjectDir)boost_$(BOOSTVER)"
     Command='$(_CommonCmdLine) -a variant=%(Tobuild.Variant) cxxflags=%(Tobuild.CxxFlag) "--stagedir=%(ToBuild.OutDir)" "--build-dir=%(ToBuild.IntermDir)" --with-%(ToBuild.Lib) stage'/>
   </Target>
Happy programming!

Wednesday, December 15, 2010

Command Line ready for VS2010

At times you want to do stuff from the command line with visual studio. The first action is often to run vsvars32.bat which injects all the environment variables the visual studio tools need. You might consider doing that globally, but this turns out to be a very bad idea if you work with different version of Visual Studio.
Because like any real programmer I hate doing repetitive stuff, here's what I came up with. Put a shortcut to the command line window on desktop and open properties. The actual program called is found in Target

C:\Windows\System32\cmd.exe

As it turns out cmd has a nice set of command line switches as you can see here (for windows XP but things still work the same way in Windows7). So I altered the properties of my shortcut to

C:\Windows\System32\cmd.exe /k "%VS100COMNTOOLS%vsvars32.bat"

Which gives you the following result



If you set the 'Start in' to something meaningful for you, you're ready for take-off with no effort!!!

Friday, May 7, 2010

Visual Studio 2010 NoStepInto

Two things have changed with the NoStepInto feature of the native C++ debugger in VS2010 compared to VS2008 (for a general intro see here):
  • You don't have to put your rules in HKLM anymore: you might as well put them in HKCU (more precise in HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0_Config\NativeDE\StepOver), which is of course much better
  • If you put =NoStepInto things will no longer work: just leave it off.
Some examples (as entered in RegEdit)
  • Name:10, Value: boost\:\:shared_ptr.*
  • Name:20, Value: std\:\:.*
Happy programming!