Search code examples
c#visual-studiomsbuildbuild-processconditional-compilation

Modify DefineConstants for referenced project in MSBuild and Visual Studio


I have a solution with a windows phone class library project, BaseProj, and several WP-projects that references this project, project A, B etc. BaseProj has a LocationHelper-class.

Lets say that project A needs BaseProj to compile with the LocationHelper-class but project B should be built without the ID_CAP_LOCATION-capabillity, so the LocationHelper-class shouldn't be included when BaseProj builds.

I can achieve this with conditional compilation symbols, the question is: How can I get BaseProj to be built with the appropriate symbols depending on whether I'm building project A or B?

After some research I come up with the following solution, in BaseProj.csproj I added

<PropertyGroup Condition=" '$(TestProperty)'==true ">
  <DefineConstants>$(DefineConstants);TEST_SYMBOL</DefineConstants>
</PropertyGroup>

And in B.csproj a property in the ProjectReference:

<ProjectReference Include="..\BaseProj\BaseProj.csproj">
  <Project>{...}</Project>
  <Name>BaseProj</Name>
  <Properties>TestProperty=true</Properties>
</ProjectReference>

Thinking that TEST_SYMBOL should be added when building with B and not with A. This works beautifully when I build with MSBuild. Visual Studio on the other hand completely ignores this, meaning that I get different behavior when I debug and when I make a release build with my build script.

How can I get the behavior I want from MSBuild and Visual Studio?


Solution

  • I came up with a new solution that works both for MSBuild and Visual Studio.

    First, BaseProj is set up to build with the location api if the conditional compilation symbol INCLUDE_LOCATION_API is defined.

    Projects A and B is set up to build with a pre-build event that executes a powershell script, pre-build.ps1. (refer to this question).

    pre-build.ps1 will read the manifest of the current project to determine whether it has the location capability (ID_CAP_LOCATION), and if BaseProj Debug and Release configurations defines INCLUDE_LOCATION_API. If necessary, INCLUDE_LOCATION_API will be added or removed. Since this will modify the BaseProj.csproj, the build script will exit with an error code to prevent the build from complete.

    When I build from VS, if BaseProj has correct configurations, it will build normally. If it has the wrong configuration, BaseProj.csproj will be modified and the build fails, but then the next build will succeed.

    In my build script, I execute pre-build.ps1 once before the actual build, ensuring that the project has correct configuration. Then MSBuild is executed and project is built successfully.

    I Wouldn't consider this best practice, but it gets the job done and I don't need to think about what configurations should be used when switching between projects.