How to Set a Conditional Compile Variable

How do I set a conditional compile variable?

The C# compiler csc.exe and the C# language itself do not expose any predefined constants for conditional compilation. Visual Studio only adds the DEBUG and TRACE values, which can be configured through the IDE. The IDE also lets you add your own arbitrary symbols, but since these are essentially fixed (invariant) values, the capability is of limited use.

More powerful custom options can set up by manually editing your .csproj project file. You can set up conditions here to selectively propagate conditional compilation symbols into C# based on the huge amount of environment and configuration information available in MSBuild (see here and here, but in principle, there can be no complete list, since disparate components arbitrarily contribute metadata ad-hoc).

Let's consider a working example. One case where it's useful to conditionally compile is if you want to write code that adapts to the whatever tools are discovered during the build. This way you can exploit the latest language features while still preserving the ability to compile on machines with older tooling which would, as expected, reject the alien syntax and/or keywords. For the particular case of C# 7.0 in Visual Studio 2017 we can modify the .csproj as follows:

.csproj file (excerpt):

Sample Image

You could also identify each of the older C# compilers as well, degrading gracefully along the way. The same goes for detecting the .NET Framework version (oft-requested on Stack Overflow [1]
[2]
[3]
[4]) and any other ambient build conditions. Such are left as exercises for the reader, but in case you want to copy/paste the highlighted lines from above, here is the text version. As an update over the screenshot, I added single-quotes to the conditional expression here (even though everything seemed to work without them)

<DefineConstants Condition="'$(VisualStudioVersion)'=='15'">CSHARP7</DefineConstants>
<!-- ... -->
<DefineConstants>DEBUG;TRACE;$(DefineConstants)</DefineConstants>
<!-- ... -->
<DefineConstants>TRACE;$(DefineConstants)</DefineConstants>

Anyway, in this manner you can now write conditional C# code using #if… #elif… #else… #endif. Continuing the example case, the code below uses new tuple syntax--only available in C# 7--to swap array elements. Incidentally, the tuple version is not only more concise and/or elegant; it also produces excellent CIL code:

#if CSHARP7
(rg[i], rg[j]) = (rg[j], rg[i]); // Swap elements: tuple syntax
#else
var t = rg[i]; // Swap elements: clunky
rg[i] = rg[j];
rg[j] = t;
#endif

Note that the Visual Studio IDE does correctly process your manual .csproj customizations in every regard. Given the .csproj I showed earlier, the IDE code editor properly recognizes and evaluates conditional compilation for the purposes of IntelliSense, refactoring, "dimming-out" inactive blocks of code, etc.

I also mentioned that MSBuild has a treasure trove of information available, of which $(VisualStudioVersion) was just one example. Unfortunately, there's no easy to find out which values are available and what values they might have at buildtime. A trick is to temporarily put a C++ project into your Visual Studio solution (if you don't already have one) alongside your C# project. If you right click the project properties for this .vcxproj and then look at (e.g.) "Additional Include Directories" on the C/C++ page, a dropdown will appear at the far right when you click to edit:

Sample Image

You'll get a dialog box with a "Macros" button which you can click to get a list of all the available MSBuild variables plus their expected values according to platform and configuration that are currently selected in the IDE. Don't overlook the well-known item metadata fields (prefixed with %) at the bottom of the list.

Sample Image

You can get an idea for how much stuff is here from the size of the scrollbar thumb in this screenshot. (They're listed alphabetically; I just scrolled to this part of the 'P' section, because it had minimal personal information.) It's important to note, however, that both the (available) variables and their values evolve over time during the course of the build, so you may find items in this list that aren't available to your .csproj at the time it's processed.


Another way to find out what property values are available during and throughout your build process is to set the MSBuild "output verbosity" to "Detailed", and then rebuild.

Sample Image

After the build finishes, examine the top of the build log in the Visual Studio Output Window, and you'll see a list of the available property names along with their initial values.

Custom conditional compilation symbols

You can define conditional compiling constants in the project properties

Sample Image

#if WINDOWS
System.Diagnostics.Debug.WriteLine("Windows"); // NOT printed!
#endif

#if ANDROID
System.Diagnostics.Debug.WriteLine("Android"); // Printed!
#endif

You can enter several symbols separated by semicolons. Don't set them true or false. The ones that are listed are true. The missing ones are automatically false.

If neither the one nor the other prints, then possibly the solution does not compile and you are running an old code. Try to rebuild the solution.

How do I conditionally compile c++ source files in Visual Studio 2019?

This solution works with VS2017 but I don't know of any reason why it wouldn't also work with VS2019.

You can "import" environment variables as preprocessor definitions. In your Visual Studio project's properties go to Configuration Properties -> C/C++ -> Preprocessor. Click in the Preprocessor Definitions field, hit the down arrow at the far right and select Edit.

Here, you can add preprocessor definitions that include environment variables. Each line represents a definition with the notation [name]=[value] which defines a preprocessor definition named [name] which will be substituted by [value]. Environment variables should be wrapped in a $() to be resolved. So, for example, to import the environment variable MY_ENV_VAR you would add the definition MY_ENV_VAR=$(MY_ENV_VAR). If MY_ENV_VAR had say 5 at the time of compilation, this definition would be equivalent to having a #define MY_ENV_VAR 5 available across the project.

In your source file, you can then wrap your code with a #if/#endif guard to conditionally compile that code. Using the same example, to only compile a source file if MY_ENV_VAR is exactly 1, you would write :

#if MY_ENV_VAR == 1

// Entire source file

#endif // #if MY_ENV_VAR == 1

Note that environment variables are loaded when Visual Studio launches. You may need to restart Visual Studio if you want recent changes to the environment variables to be visible.

Is it possible to conditionally compile / run code based on a macro OR a variable?

If the condition in the test is a compile-time constant, any half-decent optimizing compiler will remove the dead code completely from the object file.

So something as simple as this should work fine:

#ifdef REMOVECODE

#ifdef PROJECT1
#define CONDITION1 1
#else
#define CONDITION1 0
#endif

#else
#define CONDITION1 project == 1
#endif

...

if (CONDITION1)
{
...
}

Run your compiler with -S (or equivalent) and look at the generated assembly to confirm.



Related Topics



Leave a reply



Submit