4
votes

We have a large (~800 individual projects) system that we are migrating from an old build system up to Visual Studio 2010. Over the last few weeks, we have manually created Visual Studio project files (.vcxproj format) for each of the projects and we are able to build the entire system from the command line using only MSBuild.exe (WIN!!). Because of the large number of projects that needed to be converted, it was more efficient to create the project files by hand than to create them using the VS project wizard, since our previous build system was not using Visual Studio project files.

For maintenance and to adhere to DRY, we have the majority of the build configuration (compiler/linker switches, include paths, etc.) refactored into common .targets and .props files. Since the set of configurations is the same for every project, we also put the <ItemGroup> containing the <ProjectConfiguration> items in the common .props file and everything works fine for our unattended nightly build.

Unfortunately, the Visual Studio 2010 IDE (RTM version) is not able to load these projects. When I attempt to load the project, I get an error saying "Project [Foo] does not contain any configurations." If I manually copy the <ItemGroup> from our .props file into any of the projects, the IDE is able to load the project.

While searching I found this issue on MS Connect, but it is marked "Closed as External" with a reply from MS that the issue is being investigated for the next public release of Visual Studio. Manually adding the exact same <ItemGroup> element to ~800 projects is not an acceptable workaround. Since the projects build outside of VS and run perfectly, I have to assume that the issue is in the way Visual Studio is parsing/loading the project files.

Has anyone been able to find a workaround for this issue? Does anyone have information about when/if this will be fixed in Visual Studio?

3

3 Answers

3
votes

I've created a workaround for this problem. This only works on VS2010, and not on previous versions, since it requires MSBuild 4.

First, create a file named Configure.xslt with the following contents.

<?xml version="1.0" encoding="utf-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msbuild="http://schemas.microsoft.com/developer/msbuild/2003">
  <xsl:output version="1.0" encoding="UTF-8" indent="yes" />
  <xsl:template match="/msbuild:Project">
    <xsl:copy>
      <xsl:copy-of select="@*" />
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>
  <xsl:template match="*">
    <xsl:copy-of select="." />
  </xsl:template>
  <xsl:template match="msbuild:ItemGroup[@Label = 'ProjectConfigurations']">
    <xsl:copy-of select="document('Default.props')/msbuild:Project/msbuild:ItemGroup[@Label = 'ProjectConfigurations']" />
  </xsl:template>
</xsl:transform>

Now, create a file named Default.props with the following contents.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" InitialTargets="Configure" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- This must come first, or else VS IDE's property sheets will choke. -->
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

  <!-- The following configurations will be pasted into the importing project file on demand. -->
  <ItemGroup Label="ProjectConfigurations">
    <ProjectConfiguration Include="Debug|Win32">
      <Configuration>Debug</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|Win32">
      <Configuration>Release</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
  </ItemGroup>

  <!-- XLST task which manipulates the project file and performs an in-place replacement. -->
  <Target Name="Configure" Condition="Exists('$(MSBuildThisFileDirectory)Configure.xslt')" Inputs="$(MSBuildProjectFile);$(MSBuildThisFileFullPath);$(MSBuildThisFileDirectory)Configure.xslt" Outputs="$(MSBuildProjectFile);$(OutDir)">
    <Message Text="Configuring $(MSBuildProjectFile)..." />
    <XslTransformation XslInputPath="$(MSBuildThisFileDirectory)Configure.xslt" XmlInputPaths="$(MSBuildProjectFile)" OutputPaths="$(MSBuildProjectFile).tmp" />
    <Move SourceFiles="$(MSBuildProjectFile).tmp" DestinationFiles="$(MSBuildProjectFile)" />
    <MakeDir Directories="$(OutDir)" />
  </Target>

  <!-- The remaining MSBuild imports. -->
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>

This file is your separate "property sheet" containing all of the common properties for your projects, including configurations. Customize it to your needs. The important parts are the <Target> node (which creates a new target named Configure) and the InitialTargets attribute in the <Project> node (which tells MSBuild to execute the Configure target initially).

After that, move both Configure.xslt and Default.props to a directory of your choice.

Finally, use the following template in all your projects.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- Change the directory below according to the path containing the above files. -->
  <Import Project="Directory\Default.props" />
  <!-- The ItemGroup below will be effectively replaced by the same ItemGroup inside Default.props. -->
  <ItemGroup Label="ProjectConfigurations" />
</Project>

From the VS prompt, run an initial msbuild to populate your file with configurations, or else VS IDE won't open your project (because of the same bug which motivated this workaround in the first place). Alternatively, your template might contain any dummy configuration, just to enable the IDE to initially open the project -- the effect is the same.

Known caveats: You can't create per-project configurations using the IDE (because the IDE will forcibly group any configurations found under the ProjectConfigurations label). You can create them directly in the project file's XML data, and, as soon as you don't label them ProjectConfigurations, they won't be replaced or deleted.

1
votes

This is possible on VS2012 however you must remember the order in which you import / initialise variables is very important.

You will need the to be very early on in the project file, the first item really. I think that configuration may be required for system .props files to work correctly and that is why you get that error. I had the same error as yourself on VS2012 till I hacked my VS proj.

0
votes

Answered in this question: Is it possible to move configuration definitions into a property sheet with MSBuild?

Here is the connect issue that states this didn't make it into RTM: https://connect.microsoft.com/VisualStudio/feedback/details/514008/vcxproj-doesnt-examine-imports-to-find-project-configurations-beta-2

I've found no workaround other than generating the projects. One trick is to put a sentinal value in the project file, say ${CommonConfigurations}, and just use a string property function to replace it, generating entirely new project files for use in a VS solution. I'd also be generating the solution file in this case too, so you don't have to maintain the list of projects in two places.