I'm writing a VSIX package to allow the user to bulk-edit the OutputPath property of all the active configurations of projects in the currently loaded solution (see the incredibly annoying step #4 here).
I ran into a very specific problem: when setting the property to a value containing macros (e.g. "$(SolutionDir)\bin\Debug"
the value written into the .csproj is escaped as follows:
<OutputPath>%24%28SolutionDir%29\bin\Debug\</OutputPath>
Which, rather than letting MSBuild expand the macro, creates an actual physical folder named $(SolutionDir)
. I'd like to somehow bypass this escaping.
The MSDN documentation is unsurprisingly lacking in that area.
My initial code is as follows:
private void MenuItemCallback(object sender, EventArgs e)
{
SolutionWideOutputDialogWindow dialog = new SolutionWideOutputDialogWindow();
dialog.ShowModal();
if (!dialog.DialogResult.HasValue || !dialog.DialogResult.Value)
{
return;
}
string requestedOutputPath = dialog.outputPathTextBox.Text;
Solution2 solution = _dte2.Solution as Solution2;
if (solution == null)
{
return;
}
Projects projects = solution.Projects;
foreach (Project project in projects)
{
Property outputPath = project.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath");
outputPath.Value = requestedOutputPath;
project.Save();
}
}
Greatly appreciate anyone's help.
Here's what I ended up doing:
The problem I was trying to solve is not repeating myself (D.R.Y.) and specifying a solution-wide output directory (in a solution with a lot of projects) - that is, when compiling a solution, all projects would have their output directory set to something like $(SolutionDir)bin\Debug
or $(SolutionDir)bin\Release
. It is worth mentioning that some projects are included across repositories and in more than one solution.
At first, I created an MSBuild file (a <Project>
XML - called it MySolution.sln.targets
). In it, I defined a <PropertyGroup>
that overrode the <OutputPath>
property to:
$(SolutionDir)bin\$(Platform)\$(Configuration)
Then I added the following import to all the relevant projects, before the build targets import:
<Import Project="$(SolutionPath).targets" />
That way, each solution has an accompanying .targets
file defining such things that I want to be solution-wide.
This worked well, but then I encountered the following problem: The abovementioned $(Platform)
and $(Configuration)
macros refer to the project's properties, not the solution-wide ones. What would happen if my solution's Debug/Any CPU configuration still built some very specific project in its Release configuration? As far as I'm aware, after thoroughly examining the documentation, no such macros are exported that have a solution-wide granularity.
I found ceztko's Visual Studio extension which made Visual Studio export exactly the macros that I was looking for - but after some experimentation and fiddling, I discovered that this extension set them too late - only upon building the solution. This caused issues with Visual Studio's Incremental Build feature - It kept thinking that the projects were out of date because it was looking in the wrong place - it wasn't aware of the variables, but MSBuild.exe was.
I started fiddling with the IVsUpdateSolutionEvents
interface, tracing when each method is called - and then discovered that IVsUpdateSolutionEvents.OnActiveProjectCfgChange
is called twice when opening a 1-project solution in a fresh Visual Studio, or when changing the solution's configuration from Debug to Release. Further fiddling revealed that if I set the project to compile in Release in both solution configurations, this method now got called once instead of twice when changing solution configurations.
I forked the extension's repository and amended the issue by moving the macro-setting logic to the above-mentioned method. You can find it here.
Disclaimer: this may not interact so well with batch-build operations from the IDE, and requires you to export these properties yourself when building from MSBuild.exe's command-line.
Good luck in your travels.
Visual Studio will unfortunately escape special characters when editing from project properties.
To fix this, edit your .csproj file directly in a text editor.
For example, change:
<OutputPath>%24%28SolutionDir%29\bin\Debug\</OutputPath>
to:
<OutputPath>$(SolutionDir)\bin\Debug\</OutputPath>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With