38

In a C++ file, I have 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"

How can I do it?

12 Answers 12

38

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 configurations)

  2. Set the environment variable prior to calling MSBuild. Use the /D compiler option to define your macros

        cd c:\
        set ExternalCompilerOptions=/DFOO /DBAR
        msbuild
    

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

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

This works for me with Visual Studio 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.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the late answer :)
For setting the defines while building resource files, also set the Additional Options under Configuration Properties -> Resources -> Command Line
You save my day!
I don't know if I am missing something, but this solution didn't work for me (with vs2022). After having followed Step 1, setting the environment variable ExternalCompilerOptions had no effect in my case. I had to set a build property with the same name instead: >> msbuild .\MyProj.vcxproj /p:"Configuration=Release;PlatformTarget=x86;ExternalCompilerOptions=/DLOGGER" This worked fine!
15

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.
...

9 Comments

Is it possible to add MY_DEFINE=$(MyDefine) in projects properties?
@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.
Repeat this for other configurations and platforms.
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.
@omahena Yes, I wrote about this: <ClCompile> and/or <ResourceCompile>
|
13

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"

3 Comments

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?
You're right : I wrongly assumed that vcbuild.exe had the same set of parameters as the VCBuild task. Answer updated.
Good shot, <DefineConstants>$(DefineConstants);$(MyConstants)</DefineConstants>, that's what my want.
12

I think you want:

/p:DefineConstants=ACTIVATE

5 Comments

@Matt, I suggested the same answer and was told by two commenters that this doesn't work. So I deleted my answer so that others with more experience with MSBuild might offer ideas.
Oh and acemtp told me that he already tried this and it didn't work.
This works for me as well. I did put quotes around the list of constants i defined but that probably doesn't matter.
it seems like this overwrites constants defined in the .csproj-file
/p:DefineConstants doesn't work in C++. Seems to only work for C#.
11

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

1 Comment

The most convenient way
0

I wrote a cmd script for some build system and I succeeded to find a solution.

According to the OP'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

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

Comments

-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!

Comments

-1

For Visual Studio 2010 and up, see my answer here for a solution that doesn't requires any modification of the original project file.

Comments

-1

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

  • Create an empty file as part of the 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.

Comments

-1

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

What he suggested as 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.

Comments

-4

It should probably be:

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

1 Comment

No it's not #ifdef, it's a #if And anyway, it doesn't work with #ifdef too
-5

The answer to ponder is: you cannot

6 Comments

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++.
This accepted answer is so deep I can't even see it.
@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.
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.
Re "you cannot": Can you elaborate?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.