35

In a C++ file, I have a code like this:

#if ACTIVATE
#   pragma message( "Activated" )
#else
#   pragma message( "Not Activated")
#endif

I want to set this ACTIVE define to 1 with the msbuild command line.

It tried this but it doesn't work:

msbuild /p:DefineConstants="ACTIVATE=1"

Any idea?

graham.reeds
  • 16,230
  • 17
  • 74
  • 137
acemtp
  • 2,971
  • 6
  • 34
  • 43

12 Answers12

37

Our solution was to use an environment variable with /D defines in it, combined with the Additional Options box in visual studio.

  1. In Visual Studio, add an environment variable macro, $(ExternalCompilerOptions), to the Additional Options under project options->C/C++->Command Line (remember both Debug and Release configs)
  2. Set the environment variable prior to calling msbuild. Use the /D compiler option to define your macros
    c:\> set ExternalCompilerOptions=/DFOO /DBAR 
    c:\> msbuild

Item #1 ends up looking like this in the vcxproj file:

    <ClCompile>
      <AdditionalOptions>$(ExternalCompilerOptions) ... </AdditionalOptions>
    </ClCompile>

This works for me with VS 2010. We drive msbuild from various build scripts, so the environment variable ugliness is hidden a bit. Note that I have not tested if this works when you need to set the define to specific value ( /DACTIVATE=1 ). I think it would work, but I'm concerned about having multiple '='s in there.

H^2

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
bigh_29
  • 2,529
  • 26
  • 22
14

If you need to define some constant (not just true/false), you can do it the following way:

On command line:

MSBuild /p:MyDefine=MyValue

In vcxproj file (in section <ClCompile>; and/or <ResourceCompile>, depending on where you need it):

<PreprocessorDefinitions>MY_DEFINE=$(MyDefine);$(PreprocessorDefinitions)</PreprocessorDefinitions>

Note that if you don't specify /p:MyDefine=MyValue in a call to MSBuild then empty string will be assigned to MY_DEFINE macro. If it's OK for you, that's it. If not, keep reading.

How to make a macro undefined if corresponding MSBuild parameter is not specified

To have MY_DEFINE macro undefined instead of empty string, you can use the following trick:

<ClCompile>
  ....
  <PreprocessorDefinitions>_DEBUG;_CONSOLE;OTHER_UNCONDITIONAL_MACROS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
  <PreprocessorDefinitions Condition="'$(MyDefine)'!=''">MY_DEFINE=$(MyDefine);%(PreprocessorDefinitions)</PreprocessorDefinitions>
  ....
</ClCompile>

First PreprocessorDefinitions defines unconditional macros. Second PreprocessorDefinitions additionally defines MY_DEFINE macro when MyDefine is not empty string. You can test this by placing the following piece of code into your cpp file:

#define STRINGIZE2(x) #x
#define STRINGIZE(x) STRINGIZE2(x)

#ifndef MY_DEFINE
#pragma message("MY_DEFINE is not defined.")
#else
#pragma message("MY_DEFINE is defined to: [" STRINGIZE(MY_DEFINE) "]")
#endif

and running:

> MSBuild SandBox.sln /p:Configuration=Debug /p:MyDefine=test /t:Rebuild
...
MY_DEFINE is defined to: [test]
...

> MSBuild SandBox.sln /p:Configuration=Debug /p:MyDefine= /t:Rebuild
...
MY_DEFINE is not defined.
...

> MSBuild SandBox.sln /p:Configuration=Debug /t:Rebuild
...
MY_DEFINE is not defined.
...
4LegsDrivenCat
  • 1,247
  • 1
  • 15
  • 24
  • Is it possible to add `MY_DEFINE=$(MyDefine)` in projects properties? – s4eed May 18 '18 at 10:48
  • 1
    @s4eed Yes, open project properties dialog, select specific Configuration (e.g. Debug) and Platform (e.g. Win32), go to C/C++ -> Preprocessor section, and add "MY_DEFINE=$(MyDefine);" in front of other definitions in Preprocessor Definitions field. For example, if you had "a=b;$(PreprocessorDefinitions)" in that field, then make it "MY_DEFINE=$(MyDefine);a=b;$(PreprocessorDefinitions)". Do the same in Resources -> General section if needed. – 4LegsDrivenCat Jul 19 '18 at 13:56
  • Repeat this for other configurations and platforms. – 4LegsDrivenCat Jul 19 '18 at 14:02
  • This seems cleaner than the top answer, which sets environment variables, and works on MSBuild 16.1.76. You may want to add `#ifndef` guards to the constants to silence warnings. – MakotoE Jul 02 '19 at 01:15
  • 1
    The challenge with this solution is that MY_DEFINE is always defined - at least to the empty string. This is difficult to test for. @MakotoE 's suggestion with #ifndef does not work. – Troels Blum Nov 13 '19 at 14:59
  • @TroelsBlum What we now do is use a build script to replace the version constants file with a generated one. – MakotoE Nov 13 '19 at 19:38
  • @TroelsBlum I've updated the answer, please try. I've just tested this with VS2019 and it works for me. – 4LegsDrivenCat Nov 13 '19 at 21:29
  • If you are using this method to version your executable it is easy to forget that the resource compiler is used to compile the resource file. So the `` needs the same conditional ``. – omahena Jul 25 '23 at 12:49
  • @omahena Yes, I wrote about this: and/or – 4LegsDrivenCat Jul 25 '23 at 13:57
14

C++ projects (and solutions) are not (yet ?) integrated in the MSBuild environment. As part of the build process, the VCBuild task is called, which is just a wrapper around vcbuild.exe.

You could :

  • create a specific configuration for your solution where ACTIVATE=1 would be defined, and compile it with devenv.exe (with the /ProjectConfig switch).
  • create your own target file to wrap your own call to the VCBuild task (see the Override parameter)...
  • use vcbuild.exe instead of msbuild.exe. (vcbuild.exe does not seem to have the equivalent of an Override parameter).

Note that your solution would not work for C# projects either unless you tweaked your project files a bit. For reference, here is how I would do this :

  • Add the following code before the call to <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> :
<PropertyGroup Condition=" '$(MyConstants)' != '' ">
  <DefineConstants>$(DefineConstants);$(MyConstants)</DefineConstants>
</PropertyGroup>
  • Call MSBuild like this :
msbuild /p:MyConstants="ACTIVATE=1"
Mac
  • 8,191
  • 4
  • 40
  • 51
  • I can use vcbuild.exe instead of msbuild.exe but the question is the same. How to set a specific preprocessor in the vcbuild command line? – acemtp Oct 03 '08 at 12:31
  • You're right : I wrongly assumed that vcbuild.exe had the same set of parameters as the VCBuild task. Answer updated. – Mac Oct 03 '08 at 12:49
  • Good shot, `$(DefineConstants);$(MyConstants)`, that's what my want. – JasonMing Sep 29 '14 at 03:31
12

I think you want:

/p:DefineConstants=ACTIVATE
Matt Howells
  • 40,310
  • 20
  • 83
  • 102
9

Use the CL environment variable to define preprocessor macros

Before calling MSBUILD, simply set the environment variable 'CL' with '/D' options like so:

set CL=/DACTIVATE to define ACTIVATE

You can use the '#' symbol to replace '=' sign

set CL=/DACTIVATE#1 will define ACTIVATE=1

Then make the call to MSBUILD

More documentation on the CL Environment Variables can be found at: https://msdn.microsoft.com/en-us/library/kezkeayy(v=vs.140).aspx

Jay
  • 323
  • 3
  • 8
0

Maybe it is a bad idea to answer such old question, but recently I googled a similar problem and found this topic. I wrote a cmd script for some build system and I was succeed to find a solution. I leave it here for future generations (:

According to @acemtp's problem, my solution would look like this:

@echo off

:: it is considered that Visual Studio tools are in the PATH
if "%1"=="USE_ACTIVATE_MACRO" (
    :: if parameter USE_ACTIVATE_MACRO is passed to script
    :: the macro ACTIVATE will be defined for the project
    set CL=/DACTIVATE#1
)
call msbuild /t:Rebuild /p:Configuration=Release

UPD: I tried to use set CL=/DACTIVATE=1 and it also worked, but the official documentation recommends to use number sign

Lex Sergeev
  • 231
  • 1
  • 3
  • 17
-1

For VS2010 and up, see my answer here for a solution that requires no modification of the original project file.

Community
  • 1
  • 1
stijn
  • 34,664
  • 13
  • 111
  • 163
-1

As @bigh_29 has mentioned, using environment variables to define or undefine a preprocessor.

What he suggested the way to undefine a preprocessor is actually /UACTIVATE.

This way, any preprocessor matching ACTIVATE will be negated and compiler wouldn't go through your #if ACTIVATE #endif enclosure.

Anthony
  • 51
  • 5
-1

I needed to do this too - needed to be able to build two different versions of my app and wanted to be able to script the build using VCBUILD. VCBUILD does have the /override command line switch, but I am not sure it can be used to modify #define symbols that can then be tested using #if conditional compilation.

The solution I cam up with was to write a simple utility to create a header file that #defined the symbol based on the state of an environment variable and run the utility from a pre-build step. Prior to each execution of the VCBUILD step the script sets the environment variable and "touches" a file in the app to ensure that the prebuild step is executed.

Yes, it is an ugly hack, but it was the best I could come up with!

Bruce Ikin
  • 885
  • 4
  • 6
-1

I got pretty fed up with trying to do this without modifying solution or project files so I came up with the following:

  • Create empty file as part of build e.g. feature_flag.h
  • Replace #if FEATURE_FLAG with #if !__has_include("feature_flag.h")
  • Remove feature_flag.h at the end of the build

It's not using #define but it does use the preprocessor which was what I needed.

tschumann
  • 2,776
  • 3
  • 26
  • 42
-2

It should probably be:

#ifdef ACTIVATE
#   pragma message( "Activated" )
#else
#   pragma message( "Not Activated")
#endif
Robert Deml
  • 12,390
  • 20
  • 65
  • 92
-5

The answer is : YOU CANNOT

acemtp
  • 2,971
  • 6
  • 34
  • 43
  • Shrug, perhaps it doesn't in C++ but Matt Howells answer did work for me with a C# project. Can't think of any reason it wouldn't work in C++. – blak3r Oct 28 '09 at 21:06
  • This accepted answer is so deep I can't even see it. – Sergio Basurco Oct 09 '15 at 07:56
  • @blak3r Matts answer does not work in C++ and this question is specifically about C++. And there seem to be no way to do it with only adding flags on the command line without additional changes either to the project file or another external file. – Zitrax Aug 16 '17 at 11:03
  • Not sure why you get the down votes. The question was for C++ and the trick with '/p:DefineConstants' doesn't work for vcxproj for me either. – gast128 Mar 20 '19 at 12:27