Archive site from 2010 and earlier

MSBUILD issue with .rptproj projects

If you've ever run into the issue with building solution files that have references to .rptproj projects you'll know about this error message:
error MSB4041: The default XML namespace of the project must be the MSBuild XML namespace. If the project is authored in the MSBuild 2003 format, please add xmlns="http://schemas.microsoft.com/developer/msbuild/2003" to the <Project> element. If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format.

I've looked around quite a bit for a solution, and most of the solutions are to create a new configuration and uncheck the build option for that configuration, and then using that configuration in msbuild. I did find one suggestion that made sense, but used a batch file and an xslt processor to generate an msbuild project file and then transform it.

Since we use powershell extensively at work, I decided to tailor one suggestion to my environment:

In a nutshell, I create the msbuild xml project file (.proj) by running:
[void](msbuild $solution /t:ValidateSolutionConfiguration)

I use the ValidateSolutionConfiguration target because that target generates the msbuild project with the least work.
Also, before I run the line above, I first set the environment variable MSBuildEmitSolution so that the msbuild project is actually generated.
$env:msbuildemitsolution=1
and then clear it afterward:
rm env:\msbuildemitsolution

Now that I have the xml .proj file for the solution, I can:

  1. read the project file into an in memory xml object
  2. find all the targets that build a project with a file name matching the pattern: ".rptproj"
  3. find all the targets that invoke those targets
  4. and remove the offending targets from the list of targets to build (confused yet?)
  5. write the in memory xml back out to the project file
  6. and finally invoke msbuild on the project file

Full Code

To cut to the chase, here are the three functions I wrote in powershell to do the heavy lifting:


function generate-msbuild {
  param($solution)
  $env:msbuildemitsolution=1
  [void](msbuild $solution /t:ValidateSolutionConfiguration)
  rm env:\msbuildemitsolution
  $ret = "{0}.proj" -f $solution
  if(test-path $ret){return resolve-path $ret}
}

function exclude-project {
  param($msproj,$exclude)
  $msproj = resolve-path $msproj
  $proj = [xml](gc $msproj)
  $proj.Project.Target |
    ?{$_.MSBuild -and 0 -lt ($_.MSBuild|?{$_.Projects -match $exclude}).Count} |
    %{
      $targetName=$_.Name
      $proj.Project.Target|?{$_.CallTarget}|%{
        $_.CallTarget.Targets =
          [string]::Join(';',(
            $_.CallTarget.Targets.Split(';')|
            ?{-not ($_ -eq $targetName)}
          ));
      }
    }
  $proj.Save($msproj)
}

function Build-SolutionSafe {
  param(
    [string] $solution,
    [string] $type='Rebuild',
    [string] $config = $configuration
  )
  $msproj = generate-msbuild $solution
  exclude-project $msproj ".rptproj"
  msbuild $msproj /t:$type /p:Configuration=$config
}


          


to use, just call the Build-SolutionSafe function.

Code demonstrating this lesson