stackh34p
stackh34p

Reputation: 9009

Can I have global preprocessor definitions in C# library?

In C# you can have conditional compilation by using macros similar to the C/C++ syntax. This would enable the following to happen:

#define MYMACRO
....
#if MYMACRO
//some C# code logic 
#else
//some other C# code logic

I need to define some macros in a dedicated file in a C# library project, and I need these macros to be visible inside the entire library, once defined. The problem is that the above code works only for a single file.

Another way I know to work around this, is to add the macros to the build command. This would take care of defining the macros for the entire .dll and I will have the #if - #else checks working wherever I want inside the library. The issues with this approach is that I want to be able to maintain the macros easily. Having them in a file inside the project will be perfect. I'd like to have some comments inside too, so that I will know what each macro is doing. This will not be applicable if I have to pass the macros as build parameters. Another reason is being able to turn a macro on/off by simply commenting it and examining the behavior.

Is there a decent way to achieve my requirement? I'd prefer not to deal with any build automation tools like MSBuild, NAnt or anything like this, still if no other way is possible I'd appreciate an advice which one you consider a better choice.

Upvotes: 6

Views: 8577

Answers (5)

Foad S. Farimani
Foad S. Farimani

Reputation: 14006

Although it is not recommended to modify the .csproj file directly, you can add:

<PropertyGroup>
  <DefineConstants>MYMACRO;TRACE</DefineConstants>
</PropertyGroup>

using an external editor, if you wan to, and then reload the Project .csproj or the Solution .sln in Visual Studio devenv.exe.

Alternatively if you prefer CLI, and/or don't want to directly mess with the .csproj file you can add the directives as command line arguments:

msbuild.exe ProjectFile.csproj /p:DefineConstants="MYMACRO;TRACE"

P.S. More info here.

Upvotes: 1

Nayana Adassuriya
Nayana Adassuriya

Reputation: 24766

Using GUI

  1. Open the project in Visual Studio
  2. Right-Click on the project file in the solution explorer go to properties
  3. Go to Build tab and Make sure you select the All Configurations in the configuration drop down
  4. Make sure selected the All Platforms in Platform drop-down
  5. Type the Preprocessor Definitions you want in the Conditional Compilation Symbols text box separated by semicolon

enter image description here

To the Project file

  1. Open the project file in a text editor
  2. Copy and paste this code to end of existing PropertyGroup

    <PropertyGroup Condition="'$(VariableName)'=='VarableValue'"> <DefineConstants>PDEF1;PDEF2;PDEF3</DefineConstants> </PropertyGroup>

  3. If you not required to add a condition, delete the Condition="'$(VariableName)'=='VarableValue'" part

  4. Save the project file and open from Visual Studio

From: https://codeketchup.blogspot.sg/2018/04/how-to-add-project-level-preprocessor.html

Upvotes: 1

Jason Williams
Jason Williams

Reputation: 57902

I'd also advise putting the macros in the project settings (csproj file) as @Hans Passant suggests.

If you need the defines documented, you could add a documentation file to the solution explaining what the settings mean.

If there aren't too many variants, you could define a new project configuration for each one. That will allow you to pre-configure the necessary list of #defines for each variant, and then simply switch between them from the configuration combo box in the toolbar. If you want to temporarily disable one option, you could duplicate the current configuration and remove the #define, then delete the config later when you've tested it.

The next option I can suggest to make it "easier" (by combining the settings and docs into a single file as you've suggested) would be to use a simple text file (settings + comments) to configure the project, and spend 15 minutes writing a quick c# app to read this file and write the settings it contains into the .csproj file - it's just XML so should be a trivial app to write. You'd be able to easily tweak this file and run your updater app to chnage the project settings. If it's something you will do often, spend 30 minutes on it and add a UI with checkboxes to choose the settings more easily.

The concept you're describing sounds rather odd, though. The point of a library is usually that you have one standardised lump of code that can be shared by many clients, so changing these sort of defines to reconfigure the whole library a lot is not something that I'd expect to need to do very often. Perhaps you have good reasons, but it may be worth reviewing why you need to solve this #define problem.

(e.g. If you have lots of customers who need different variants of the "library", the best approach will be to use configurations (described above) to allow you to build all needed variants in a batch build. If you are just trying out lots of different algorithms/techniques then can you redesign chunks of the library so that you can restrict the impact of most #defines to just to a single .cs file so they no longer need to be global? Perhaps the library shouldn't be in a single dll, or a plug-in architecture is needed to allow you to pick and choose the "modules" that are included within the library)

Upvotes: 2

svick
svick

Reputation: 244767

C# “preprocessor” directives don't work the same as C preprocessor directives. The most important difference for you is that there is no equivalent of #include. It's not needed under normal circumstances, because C# doesn't have (or need) header files. I don't think what you want is possible, unless you somehow create your own preprocessor or read the file with #defines and make them into parameters of msbuild.

But I think it would be easier for you to use more object-oriented approach: encapsulate the different approaches into classes and use them. To specify which one of them to use, you could use dependency injection. That means you would have to ship a DI library along with your library, but I think that's a price worth paying.

Also, this approach would alleviate a problem with conditional compilation: specifying different set of symbols may break the build in unexpected ways.

Upvotes: 2

Hans Passant
Hans Passant

Reputation: 941407

You #define them for an entire project with Project + Properties, Build tab, "Conditional compilation symbols" setting. This sets the <DefineConstants> element in the project file. You override this property with msbuild by giving it the /property:DefineConstants="MYMACRO" command line option.

Upvotes: 5

Related Questions