In VS2022, how do I specify a conditional compilation symbol for a build configuration? - conditional-compilation

In VS2017, I had several different build configurations that built an application in different ways. One configuration would produce the default application. Another build configuration would produce the application with more features, etc.
This was done in the source code with #if FEATURE blocks. FEATURE was defined in the Conditional compilation symbols for a project's build configuration.
Now, I ported the code to Visual Studio 2022. It appears that the Conditional compilation symbols are now part of the project and not part of the build configuration. So I have to define FEATURE for the project and not the build configuration.
I've used #if FEATURE to put in attributes to classes and methods, so I can't replace this with a simple if statement in the source code.
I don't want to change the project settings every time I need to build the different applications.
What is the workaround for being able to build a project with different compilation symbols easily?

Realise this is a year old now, but I've been looking at conditional compilation symbols this morning.
First, have a look at this: https://learn.microsoft.com/en-gb/dotnet/csharp/language-reference/compiler-options/language
The <DefineConstants> section deals with conditional compilation.
Edit the .csproj file directly, and in any applicable property group you can define constants that you can reference in code. e.g.
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DefineConstants>MYDEBUGCONSTANT</DefineConstants>
</PropertyGroup>
Then in code you can use:
#if MYDEBUGCONSTANT
// Some debug code
#endif
I had some issues with the conditions on the property groups, VS2022 got a little confused when two of the property groups applied at the same time. Once I sorted that everything worked as expected.
Hope that helps?

Related

How to override csproj.user for msbuild invocation?

My colleagues and I have user specific settings in csproj.user files. They are not checked into the repository. I would like for the build server to use its own set of csproj.user files, overriding certain properties, leaving the "base" project configuration at a decent developer default. But from the looks of it there is no such option in the msbuild command-line for doing that.
Is there really no way, other than copy csproj.user-files to where it'll be picked up by subsequent msbuild invocations?
While writing I realize I'm too much of a prude about these things and should just copy as a step prior build. Still posting in case someone knows a better way, for instance a way that does not modify the source tree.
Passing properties to the MSBuild command line overrides properties in the solution, including dependent projects. Here omitting debug information in build server, otherwise generated for release build to improve profiling:
msbuild MySolution.sln /p:DebugType=none ...
This does not work should I want different properties for different projects. Building projects individually should work nicely though.
Finally, passing arguments on command line can get messy, so to get a more "settings file"-like experience one may instead use #file arguments and MSBuild response files.

Include file to VS2017 c++ project by condition

I am trying to build the c/c++ project in VS2017
[https://git.postgresql.org/gitweb/?p=psqlodbc.git;a=blob;f=winbuild/psqlodbc.vcxproj;h=c54c93007c07c2b13bbea4ede14a6ee0e11fdf5a;hb=c54c93007c07c2b13bbea4ede14a6ee0e11fdf5a][1]
There are conditions in the project file
<ClCompile Include="$(srcPath)odbcapi30.c" />\r
<ClCompile Condition="'$(ANSI_VERSION)'=='no'" Include="$(srcPath)odbcapi30w.c" />\r
<ClCompile Condition="'$(ANSI_VERSION)'=='no'" Include="$(srcPath)odbcapiw.c" />\r
I have created
Unicode Debug/Release
ANSI Debug/Release
configurattions
and in the project properties->c\c++->Preprocessor I have added the ANSI_VERSION=no for Unicode and ANSI_VERSION=yes for ANSI.
But for any Platform/Configuration I see these files in the Solution Explorer and they are compiled by VS2017. How to include these files into project when condition is true only?
The condition requires that ANSI_VERSION is a MSBuild property. These are different from the C++ Preprocessor definitions (which are inputs used when compiling a file but not used by MSBUILD when testing for which files to compile - strictly speaking its used by the pre-processor but its part of the compile step from an msbuild point of view)
You can set the ANSI_VERSION as an MSBUILD property in your project file:-
For example:-
<PropertyGroup>
<ANSI_VERSION>no</ANSI_VERSION>
<ANSI_VERSION Condition="'$(Configuration)' == 'ANSI_DEBUG'">yes</ANSI_VERSION>
<ANSI_VERSION Condition="'$(Configuration)' == 'ANSI_RELEASE'">yes</ANSI_VERSION>
</PropertyGroup>
The above defaults ANSI_VERSION to no and overrides to yes when condition is met, but you could just as well test each possible configuration in turn instead if you prefer.
The conditions could also be combined into a single condition with an or if you prefer.
Personally I'd use true/false rather than yes/no. With true false you can just test the property as a Boolean rather than compare to string (although maybe this also works with yes/no - but I haven't tried that)
Edit in response to question:
The above conditionally excludes the files from the build, excluding them from the display is a little different as it would require the UI to re-parse the projects to update the list of files. You may find things works better for you to create a filter in the project for these files (i.e. right click on project in solution view and use Add->New Filter). Then conditionally use the ExcludeFromBuild setting to control which configurations actually compile them instead of making the CLCompile include conditional, something like:-
<ClCompile Include="SomeFile.cpp">
<ExcludedFromBuild Condition="'$(Configuration)'=='Debug'">true</ExcludedFromBuild>
</ClCompile>

Sharing violation when multiple projects reference library in msbuild

I have run into a problem with msbuild. I have a three tiers of project files. The lowest create libraries, some of which are language neutral, and some of which are language specific. The next tier builds a project that combines the language neutral libraries with the libraries for a specific language into a product. The final tier builds the product multiple times for the different languages.
The problem that I am having is that the language neutral library is being built multiple times, and when I invoke msbuild with the /m flag I get sharing violations during the build, since multiple threads end up building the language neutral project. I have been told that if I use the RemoveProperties parameter on the MSBuild task so that all of the properties match, msbuild will only build the project once. I have tried to do this, but have not been able to get the project to only build once. I have looked at a diagnostic log and created a custom logger to try and figure out which properties to add to the RemoveProperties parameter.
My question is how does msbuild decide that two invocations of the same project file are the same, and should only be built once. Based on the info passed to the OnProjectStarted event of my custom logger, all of the Global properties are the same. Is there something else that I need to make match?

Target not running when using BeforeTargets="Build" on Build Server

I have a custom .targets file which I import into my C# MVC web application's project file. I've added custom targets to this like so:
<Target Name="CopyFiles" BeforeTargets="Build"></Target>
This works fine when building under Visual Studio, but when I use TeamCity to build it, the target never gets run, and I can't work out why.
If I change my target to use BeforeTargets="Compile" then it runs. Alternatively, if I add an additional target with the name Build to the .targets file
<Target Name="Build" />
then it will run, but doing so overrides the existing Build target and thus my application doesn't build. I can't quite make out the logic to this - it doesn't make sense. I'm using the Compile target for now, but if someone could explain why trying to execute it before the Build task doesn't work I'd really appreciate it.
'Build' is a special built-in target, so doesn't really work the same way as most other targets. It definitely can't be safely overridden.
The most relevant documentation is here: https://msdn.microsoft.com/en-us/library/ms366724.aspx
If you want something to run before build, the standard approach (as recommend by the comments in a newly-created .csproj file) is to override the BeforeBuild target (as documented above).
However, this isn't the most robust solution. As noted in the documentation above:
Overriding predefined targets is an easy way to extend the build process, but, because MSBuild evaluates the definition of targets sequentially, there is no way to prevent another project that imports your project from overriding the targets you already have overridden.
It's better (and only slightly more complex), to override the BuildDependsOn property and extend the default value of this property to include the target you want to run (this is also documented in the link above).
Another approach would be to leave BeforeBuild empty and use BeforeTargets="BeforeBuild", which feels a bit odd but is quite simple and will still work even if the BeforeBuild target gets overridden.
As to why BeforeTargets="Build" doesn't work, I can't find a reference for this in the documentation, but I think it's to do with its special nature. It doesn't work the same as ordinary targets and it's probably better not to think of it as a target at all.

Compile symbol not used when building via MSBUILD?

I have a project where I have added a compile symbol (MYSYMBOL) in the project properties. When building from VS it works just fine and the application acts accordingly.
Other projects has various symbols defined this way.
When building from MSBUILD, MYSYBOL is for some strange reason ignored and the application acts as if it was never set.
I tried adding /p:"DefineConstants=MYSYMBOL" to the msbuild call, but then the build fails as it seems to replace the other symbols of each project with this one alone, so one assembly e.g. requires MYSYMBOL + OTHERSYMBOL but now only gets MYSYMBOL.
This is strange since it actually does use OTHERSYMBOL for those projects that have it, when building via msbuild.
Does anyone have a clue what could be causing MYSYMBOL on the project properties not to be used when building via msbuild?
Turned out that the build script has stated a set of compiler symbols on its own, overwriting the ones in the separate project settings which in turn caused this issue.
As far as I understand, you can't add one or more compiler symbols since anything you state replaced the symbols of the projects.
Ended up having to pass every symbol every project in the entire solution uses.
Too bad :(
You will want to add a new symbol to the list of DefineConstants (as opposed to overriding the entire list of DefineConstants). This will do what you want:
<PropertyGroup>
<DefineConstants>MYSYMBOL;$(DefineConstants)</DefineConstants>
</PropertyGroup>