Ok, so I have a library I've written that I think will be generally useful to people writing .NET apps and Silverlight apps. It was created as a .NET 3.5 C# class library, with the idea that after I finished it I would create a version for Silverlight (it only needs a little tweaking to go from one to the other). This question is about how to create that version.
The brute force approach would be to just copy the code over and create a separate project, but I don't want two code bases. What I want is to have SILVERLIGHT defined and have a configuration that builds against the right version of the framework.
The default project file pulls in the default targets in an <import> element, like this:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
To make it a Silverlight build most of what is needed is to do this:
<Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight\v2.0\Microsoft.Silverlight.CSharp.targets" />
How to do that? After some reading I realized that Conditions work on the import element. Aha! So I can do this:
<Import Condition="'$(ConfigurationName)'=='Debug'" Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
And so on for the other configuration names. Perfect! Except it didn't work. After some more reading I discovered that when MSBuild is hosted in Visual Studio it doesn't evaluate conditions on the import element. It does for Item and PropertyGroups... but not Import. The docs state that it evaluates import using the defaults at load time and doesn't change it thereafter. Two workarounds are suggested, first to place the conditional imports in the target files; and second to "write code in a Choose element".
Well, I don't want to edit the standard target files for this library, and while the Choose element is neat and all it doesn't accept an Import element as a child (not sure what they meant by suggesting that).
So I thought I would try this: I created two files dn35.targets and sl20.targets, each containing the correct Import element. I put an <import Project="current.targets" /> in the project file. I then created a batch file as follows:
echo off
IF /i %1=="Debug" (copy /y %2\dn35.targets %2\current.targets)
IF /i %1=="Release" (copy /y %2\dn35.targets %2\current.targets)
IF /i %1=="Silverlight Debug" (copy /y %2\sl20.targets %2\current.targets)
IF /i %1=="Silverlight Release" (copy /y %2\sl20.targets %2\current.targets)
echo on
I created a prebuild step that called the batch file with the configuration name and project directory, and tested it in and out of the VS ide. It worked great...
...except the changes were ignored. I could see the step fire off and the contents of current.targets change, but apparently it was already loaded and evaluated and the IDE doesn't load it again.
Curiously, I can create two entirely separate project files under, say, MyLib.sl20 and MyLib.dn35 and copy the correct one to MyLib.csproj and it works exactly as I want it to... but the IDE then pops a message saying that the project file has been changed outside the environment. I have eight assemblies in this library and I don't find this to be an elegant solution either, especially since I might change other things in the project file and then have to remember to migrate them to both versions.
I know I can make this all work if I ditch the hosted MSBuild and have the IDE execute the command line build, since it will evaluate conditional imports. I may have to do that, but it really seems like this should be easier to do.
Anyone have any ideas?
The brute force approach would be to just copy the code over and create a separate project, but I don't want two code bases. What I want is to have SILVERLIGHT defined and have a configuration that builds against the right version of the framework.
The default project file pulls in the default targets in an <import> element, like this:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
To make it a Silverlight build most of what is needed is to do this:
<Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight\v2.0\Microsoft.Silverlight.CSharp.targets" />
How to do that? After some reading I realized that Conditions work on the import element. Aha! So I can do this:
<Import Condition="'$(ConfigurationName)'=='Debug'" Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
And so on for the other configuration names. Perfect! Except it didn't work. After some more reading I discovered that when MSBuild is hosted in Visual Studio it doesn't evaluate conditions on the import element. It does for Item and PropertyGroups... but not Import. The docs state that it evaluates import using the defaults at load time and doesn't change it thereafter. Two workarounds are suggested, first to place the conditional imports in the target files; and second to "write code in a Choose element".
Well, I don't want to edit the standard target files for this library, and while the Choose element is neat and all it doesn't accept an Import element as a child (not sure what they meant by suggesting that).
So I thought I would try this: I created two files dn35.targets and sl20.targets, each containing the correct Import element. I put an <import Project="current.targets" /> in the project file. I then created a batch file as follows:
echo off
IF /i %1=="Debug" (copy /y %2\dn35.targets %2\current.targets)
IF /i %1=="Release" (copy /y %2\dn35.targets %2\current.targets)
IF /i %1=="Silverlight Debug" (copy /y %2\sl20.targets %2\current.targets)
IF /i %1=="Silverlight Release" (copy /y %2\sl20.targets %2\current.targets)
echo on
I created a prebuild step that called the batch file with the configuration name and project directory, and tested it in and out of the VS ide. It worked great...
...except the changes were ignored. I could see the step fire off and the contents of current.targets change, but apparently it was already loaded and evaluated and the IDE doesn't load it again.
Curiously, I can create two entirely separate project files under, say, MyLib.sl20 and MyLib.dn35 and copy the correct one to MyLib.csproj and it works exactly as I want it to... but the IDE then pops a message saying that the project file has been changed outside the environment. I have eight assemblies in this library and I don't find this to be an elegant solution either, especially since I might change other things in the project file and then have to remember to migrate them to both versions.
I know I can make this all work if I ditch the hosted MSBuild and have the IDE execute the command line build, since it will evaluate conditional imports. I may have to do that, but it really seems like this should be easier to do.
Anyone have any ideas?
