2

I have a C# project which is built in a few different configurations. Some of the source files should be always included, and some only in certain configurations. So far I've been doing this with #if ... #endif around the entire files, but I was hoping to create a small extension to do this a nicer way.

I've created an extension that adds an item to files' context menus, but I can't find any way to set the Condition attribute on the item node in the project file.

I've looked at the Properties collection of the EnvDTE.ProjectItem interface, but can't see anything useful there (except BuildAction... I'll come back to that).

Then I tried getting an IVsBuildPropertyStorage on the item and calling SetItemAttribute(). This does add information to the project file, but as a child element like this:

<ItemGroup>
  <Compile Include="Program.cs">
    <Condition>%27%24%28Configuration%29%27==%27Debug%27</Condition>
  </Compile>
</ItemGroup>

when what I was trying to achieve was:

<ItemGroup>
  <Compile Include="Program.cs" Condition="'$(Configuration)'=='Debug'" />
</ItemGroup>

There's also an IVsBuildPropertyStorage.SetPropertyValue() but that adds a similar child element to a PropertyGroup section near the top, not to the item node.

I've looked at 'Project Subtypes/Flavors', but that looks like it's just going to get me another IVsBuildPropertyStorage, which doesn't seem to be useful. They do look capable of a lot of complex things, but documentation on the subject appears to be minimal and vague.

I've seen some posts describing how to use the MSBuild assemblies to directly load and manipulate the project file, but I'm not sure when is safe to do that without confusing Visual Studio and potentially losing changes, since VS prompts to reload when it detects changes to the project file.

As a last idea, I thought about manipulating the BuildAction property between Compile and None, but that sounds like it could be a lot of work for my extension to maintain correctly, keeping it in sync with every time the user switches configurations in the IDE for example.

Is there anyone with any experience with this kind of thing that has any advice to offer me, or should I give up hope and stick with manually adding #if directives everywhere?

Ashley
  • 2,108
  • 1
  • 14
  • 12
  • Possible approaches are, 1) separate project files for each configurations; 2) a core project with basic configuration based extension projects. They are much easier to manage compared to what you are attempting to achieve right now. – Lex Li Sep 12 '12 at 03:11
  • There's an argument for simply doing #ifs: most people know how it works. Or at least a lot more than who understand MSBuild. I love MSBuild hackery, but the last thing I'd want is to spend an hour trying to figure out why this file isn't being compiled, only to discover there's magic going on in the project file. – Jason Malinowski Sep 12 '12 at 05:14
  • @JasonMalinowski: The code in question is sets of partial classes, and the parts are in files named for example Class.cs, Class.Client.cs and Class.Server.cs, so it's not too confusing. It's just tedious having to add all the #ifs - there are a lot of files to do and to maintain. – Ashley Sep 12 '12 at 16:21

2 Answers2

0

You may like to explore the MSBuild option you mentioned.

You don't actually have to load the MSBuild project from file, because Visual Studio gives you a way of accessing the MSBuild project directly, i.e.:

string projectPath = projectItem.ContainingProject.FullName;
MsBuildProject project = ProjectCollection.GlobalProjectCollection.GetLoadedProjects(projectPath);
var compileItems = project.GetItems("Compile");

From there you can locate your specific items and potentially add the condition attribute, though I haven't tried this step myself (if this doesn't work, you might have to try modifying the project elements under the project.Xml property instead).

You can then call project.Save(), which shouldn't trigger the "Reload project?" dialog because of the way the MsBuild project instance is linked to the Visual Studio project hierarchy.

However, you may like to force Visual Studio to reload the project anyway, because if you switch build configurations (e.g. between Debug and Release), the MSBuild engine may not re-evaluate your item conditions during build. The code to do this programmatically can be found here:

How do I programmatically refresh/reload a VS project after modifying the underlying file?

Community
  • 1
  • 1
Dan Nolan
  • 4,733
  • 2
  • 25
  • 27
  • Thanks for this, I didn't know it was possible to access the project structure directly as loaded. I gave up on trying for the moment because I'd wasted too much time and needed to get some actual work done, but I'll give this suggestion a go sometime next week, and report back how it goes. – Ashley Sep 14 '12 at 20:18
  • Yeah, it's one of those things I only reach for if I can't access or update the properties within ProjectItem or IVsProject.Object. Not entirely ideal, but at least the behaviour is consistent across all project types (not always so with the other two). Let me know if you need any more help. – Dan Nolan Sep 15 '12 at 07:33
  • You could do what you wanted? I пще the same problem. Thx – ShurikEv Oct 02 '13 at 08:38
0

Unfortunately I never got the time to persue the original goal of creating an extension for doing this, however I did achieve what I needed using the suggestion by lex-li: using separate project files per configuration.

Since the project files can all reside in the same directory, it's easy to simply use the 'Include/Exclude from project' context menu item in the solution explorer to choose which files are included. There's also no need for file linking this way, which I'd tried before and found very time-consuming to manage.

Partial Methods are also worth looking at, if you have similar needs. They allow you to define the signature of a method in one place, but optionally implement it elsewhere. If you don't implement it, no call is generated by the compiler.

With respect to the original idea of the extension, I suspect the answer by Daniel Nolan was heading in the right direction, but unfortunately I didn't get to try it out.

Community
  • 1
  • 1
Ashley
  • 2,108
  • 1
  • 14
  • 12