Visual Studio's default mechanism of defining custom compilation symbols clobbers any other pre-defined compilation symbols - by rmiesen

Status : 

  By Design<br /><br />
		The product team believes this item works according to its intended design.<br /><br />
		A more detailed explanation for the resolution of this particular item may have been provided in the comments section.


1
0
Sign in
to vote
ID 596694 Comments
Status Closed Workarounds
Type Bug Repros 0
Opened 9/9/2010 9:20:30 PM
Access Restriction Public

Description

I attempted to define a custom compiler symbol inside a .csproj file directly to include a custom compilation symbol if certain condition is met. In debugging my application to find out why this custom compilation symbol wasn't being defined even though my syntax was ok, I came to the conclusion that this bug was caused by how Visual Studio adds in custom compilation symbols: by not chaining them onto the list of pre-existing compilation symbols.

I've attached a sample project with comments in the .csproj file that illustrates this issue.
Sign in to post a comment.
Posted by ohnobinki on 7/19/2013 at 1:35 PM
I agree with Microsoft here. VS does a pretty good job of editing the .csproj file by not moving elements around. So as long as you make sure your conditional DefineConstants setting is after the Build Configuration-based <PropertyGroup/>s and coded right, you’ll get the behavior you should.

Considering http://stackoverflow.com/a/3682270/429091 links here, it also matters what *time* you set DefineConstants. A conditionally-defined constant which informs the source code that it is being compiled on a particular version of the .net framework should probably *not* be defined so that Visual Studio sees it. It should not be in a top-level PropertyGroup. Instead, you should set DefineConstants when a build is actually going on. For example:

<Target Name="MyDefineFrameworkConstants" BeforeTargets="BeforeBuild">
    <PropertyGroup>
     <MyTargetFrameworkVersionDouble>$(TargetFrameworkVersion.Trim('v'))</MyTargetFrameworkVersionDouble>
     <DefineConstants Condition="$(MyTargetFrameworkVersionDouble) >= 4.0">$(DefineConstants);NET40</DefineConstants>
     <DefineConstants Condition="$(MyTargetFrameworkVersionDouble) >= 4.5">$(DefineConstants);NET45</DefineConstants>
    </PropertyGroup>
    <Error Condition="'$(MyTargetFrameworkVersionDouble)' == '' Or $(MyTargetFrameworkVersionDouble) < 3.5" Text=".net Framework 3.5 or greater required." />
</Target>

It doesn’t make sense to even allow VS’s Build Property page to see NET40 or NET45 itself because these constants are dynamically calculated from the TargetFrameworkVersion. The user has access to TargetFrameworkVersion through VS’s UI directly already. VS’s Build Property/Configuration constants configuration tab should only hold constants that change other things about compilation.
Posted by Microsoft on 10/11/2010 at 1:55 PM
What you are trying to do should work just fine. You just need to know how to do it.

So, in Visual Studio, if you right-click on you C# project file, then choose the Build tab, you will see a text box labeled "Conditional compilation symbols:". If you enter symbols to be defined in this box (for example: Foo;Bar=2), and you choose DEBUG or TRACE checkbox's beneath it, then that Configuration|Platform will contain a definition like:
<DefineConstants>TRACE;DEBUG;Foo;Bar=2</DefineConstants>

Now, if you want to edit the configuration by hand (an advanced scenario) then you need to know how to do this. But, if we stay inside Visual Studio, we can reduce this to an intermediate level scenario. MSBuild properties can be expanded with the $(MyProperty) syntax; where MyProperty is the property you want.

So, if you wanted to have whatever previously defined symbols were defined plus the above, you could enter "Foo;Bar=2;$(DefineConstants)" into the conditional box above. If my previous definition was <DefineConstants>MyConstant</DefineConstants>, then you would see the value:
DefineConstants=TRACE;DEBUG;Foo;Bar=2;MyConstant
during the build.

For the full on advanced scenario (which above would include editing the project file to the MyConstant #define) you can edit your MSBuild project file directly. (standard disclaimers apply :) In this case, you could add your additional properties below the PropertyGroups defined by Visual Studio. (as eluded to in the previous response). It can basically go at the bottom of the project file. You would add something like the following:
<PropertyGroup>
     <DefineConstants>$(DefineConstants);MyConstant</DefineConstants>
</PropertyGroup>

This would have the effect of adding your constant "MyConstant" onto the end of the list. This line is equivalent to an imperative style language saying something like:
DefineConstants = DefineConstants + ";MyConstant"

So, if you want to stay inside the Visual Studio box (base scenarios), then you should use the Build tab to define your additional constants.

If you want to use the advanced scenario, and edit your build scripts, you need to understand how Visual Studio works and work in concert with it. And you need to have an understanding of the MSBuild language. (Hence the reason it is an advanced scenario.) And in this case, there are many ways that you could attach your own defined properties into the Visual Studio project file. (I have only shown one above.) But, it would be up to you to make sure that you do it in a way that works for all of the Visual Studio cases.

This is how we designed this. But, there are two different parts.

First, MSBuild, which does not really care how the project file is formed. You can create your own project file by hand, and you can have them do whatever you want in them. It is a declaritive style language for build.

Second, there is the Visual Studio part. In this case, Visual Studio defines a set of targets and a pre-defined project file layout and interaction model, etc. The Visual Studio aspects are very targeted to provide a build experience that works out of the box. So, as long as you stay on the path, we can reasonably say that everything "just works".

But, when you start mixing the two parts (which you can; its just an advanced scenario), then you have to understand how all those parts work.

I hope that this clarifies the earlier response.

Thanks,

Chuck England
Visual Studio Pro
Program Manager - MSBuild
Posted by rmiesen on 9/13/2010 at 10:28 PM
Chuck, thank you for your response.

In my sample application, I added the <DefineConstants> XML attribute how I did to illustrate how VS adds conditional compilation symbols to a project. For example, opening the Project editor dialog in Visual Studio 2010 and adding a custom symbol will cause any other definitions not added towards the end of the MSBuild file to be clobbered. Here is an excerpt from my sample file that illustrates what I'm talking about.

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
    <PlatformTarget>x86</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
     <!--This value of DefineConstants is generated by Visual Studio 2010 and will clobber any conditional compilation symbols defined before it, irrespective of how you may have defined them in the MSBuild file.-->
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
</PropertyGroup>

I'm sorry my sample MSBuild file didn't make that fully clear. I guess the real question is: should the custom compilation symbols a user adds through Visual Studio 2010 clobber all preceding definitions of custom compilation symbols or should previous definitions be preserved? My thoughts are that Visual Studio 2010 should respect the intent of the programmer and append any custom compilation symbols defined through Visual Studio 2010's project editing interface to the previously-defined custom compilation symbols.

Thank you again for your time and I hope you will re-evaluate what issue I am raising: that Visual Studio 2010 adds custom compilation symbols defined through the project editing dialog in a destructive manner.
Posted by Microsoft on 9/13/2010 at 2:54 PM
Right, sorry about that. I see the project attached. :)

In looking at the file, I see you have two lines. The first one:
    <DefineConstants>$(DefineConstants);ITS_CLOBBERING_TIME</DefineConstants>
works because it says take the value of DefineContants and add on "ITS_CLOBBERING_TIME" to it.

The second one: (which is commented out in the file)
    <DefineConstants>ITS_CLOBBERING_TIME</DefineConstants>
says, overwrite the value of DefineConstants with the new value "ITS_CLOBBERING_TIME".

This is the correct semantic for these lines in the MSBuild language. These statements are analgous to:
    DefineConstants = DefineConstants + "ITS_CLOBBERING_TIME"
and:
    DefineConstants = "ITS_CLOBBERING_TIME"

So, if you want to add a constant, you need to use the first syntax. If you want overwrite, then you need to use the second syntax.

There are also some interesting rules to how MSBuild process properties, items, etc. We first process all properties from top to bottom through the projecct file, and all imports in order. So, it would not matter where you put this (except in a target, which is only executed when the target runs), you would still see the same results.

This is the design of how the MSBuild language works. As such, I am resolving as "By Design".

Chuck England
Visual Studio Platform
Program Manager - MSBuild



Posted by Microsoft on 9/13/2010 at 2:15 PM
Thanks for taking the time to send us this issue.

Can you please provide us with a sample? What compiler option are you specifying, and how are you adding it?

If you could create a small example C# console application that exemplifies the scenario, and can zip and attach, that would be most helpful to understanding the issue here.

Thanks,

Chuck England
Visual Studio Pro
Program Manager - MSBuild
Posted by Microsoft on 9/10/2010 at 2:28 AM
Thank you for reporting the issue.
We are routing this issue to the appropriate group within the Visual Studio Product Team for triage and resolution.These specialized experts will follow-up with your issue.