How to set SAFESEH linker option using MSBuild command line - c++

I am trying to build my C++ project created in VS2008 and upgraded to VS2013 using MSBuild.
I am referencing 3 third party dlls.
In the project settings I'm using /SAFESEH:NO. With this setting when I build my project in Visual Studio, it successfully builds.
An issue arises when I build the same project using MSBuild, error as follows: error LNK2026: module unsafe for SAFESEH image
Build command:
"C:\Program Files (x86)\MSBuild\12.0\bin\MSBuild.exe" TI.sln /t:ReBuild /p:configuration=Release /p:Platform="Win32" /p:ToolVersion="12.0" /p:SAFESEH="NO"
Please suggest me a solution

/SAFESEH:NO shown in the project settings is very different from passing an msbuild property named SAFESEH and set it to "NO": /SAFESEH is a linker command line option and there is no direct relation to msbuild properties. Instead in an msbuild project file the linker options are specified in the ItemDefinitionGroup called Link so if you open the file in a text editor you'll see something like
<ItemDefinitionGroup>
<Link>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link>
</ItemDefinitionGroup>
Now if your project doesn't build without SAFESEH truned off, I don't see why you wouldn't permanently configure it that way in the project settings. It also has the benefit it'll build from VS and command line without additional configuration.
If for some reason you cannot do that there are a couple of options. First one is basically the same as my answer to the question on how to set compiler options via the command line: create a file, say c:\nosafeseh.props, containing
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
<Link>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link>
</ItemDefinitionGroup>
</Project>
then put it to use by calling
msbuild TI.sln /p:ForceImportBeforeCppTargets=c:\nosafeseh.props
Another option is to 'translate' a property into the ItemDefinitionGroup entry. Open the project file using a text editor and add these lines, in the same location where VS would put the linker options, ususally right before the line with '':
<ItemDefinitionGroup>
<Link>
<ImageHasSafeExceptionHandlers Conditon="'$(SafeSeh)'!='">$(SafeSeh)</ImageHasSafeExceptionHandlers>
</Link>
</ItemDefinitionGroup>
This sets the value of ImageHasSafeExceptionHandlers to the value of the property named SafeSeh (this is an arbitrary name, you can select whatever you want), and only if that property is defined and has a value. So, to set it to false, you'd call
msbuild TI.sln /p:SafeSeh=False

Related

ResolveAssemblyReference cannot find dll and I cannot force it to look where it is

I have solution with n csharp projects and cpp project on top, this cpp provides interfaces and headers so those csharp ones can be used in other cpp solutions.
The build machine is configured to build csharp project with anyCPU architecture so it provides single assembly per build in Solution\bin\Release. For cpp the anyCpu is not available so I build project twice and store assemblies in Solution\bin\Release\x86 and x64 folders.
This is all to get it packaged in nuget as a single package with .targets file to ease consumption in other cpp projects.
Issue is that cpp project is looking for csharp asseblies using ResolveAssemblyReference and cannot find it, giving missleading message:
ResolveAssemblyReferences:
Primary reference "Implementation".
Could not find dependent files. Expected file "C:\Jenkins\Workspace\Solution\bin\Release\x86\Implementation.dll" does not exist.
Could not find dependent files. Expected file "C:\Jenkins\Workspace\Solution\bin\Release\x86\Implementation.dll" does not exist.
Resolved file path is "C:\Jenkins\Workspace\Solution\bin\Release\x86\Implementation.dll".
Reference found at search path location "".
I tried to alternate ResolveAssemblyReferences behaviour using command line properties, custom targets/properties, but without any luck. The parameters described in https://learn.microsoft.com/en-us/visualstudio/msbuild/resolveassemblyreference-task?view=vs-2017 seem to be computed during the build process and I cannot inject any value, which should be in this case something like $(OutDir)..
The one feasable solution seems to be copy c# dlls into each cpp folder, but I dont think it is the way to solve it properly.
Closes I got is by using /p:ReferencePath like below:
"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MsBuild\15.0\bin\MsBuild.exe" /p:BuildProjectReferences=false /p:Configuration=Release /p:DebugType=full /p:DebugSymbols=true /p:PlatformToolset=v120 /p:WindowsTargetPlatformVersion=8.1 /p:ForceImportBeforeCppTargets="C:\Jenkins\Workspace\Solution\Cpp.props" /p:OutDir="C:\Jenkins\Workspace\Solution\bin\Release\x86\" /p:Platform=Win32 /t:Build Interface\Interface.vcxproj /p:ReferencePath="C:\jenkins\workspace\Solution\bin\Release"
My custom Cpp.props does:
<Target Name="Output" BeforeTargets="ResolveAssemblyReferences">
<Message Text="AssemblySearchPaths: $(AssemblySearchPaths)" />
</Target>
and by adding /p:ReferencePath it got added to AssemblySearchPaths as second record, after {CandidateAssemblyFiles}; but it is still not finding those dlls

$(OutDir) is not set to project Output directory

I have a C++/CLI dll project in Visual Studio 2013 where I'm trying to change the output directory. I've set the "Output Directory" setting in Project properties > General to $(SolutionDir)Stage\$(Configuration)\bin$(PlatformArchitecture)\. I can see that in the vcxproj file this is reflected to the OutDir:
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)Stage\$(Configuration)\bin$(PlatformArchitecture)\</OutDir>
</PropertyGroup>
However, it does not seem to actually be used. Under Linker > General > Output File the default value is $(OutDir)$(TargetName)$(TargetExt), but the expanded value of $(OutDir) seen under Command Line does not reflect the changes I've made, rather it seems to have the value $(SolutionDir)Stage\$(ConfigurationName) ($(ConfigurationName) is eg "Debug_x64"). This is wierd, since that is not present in the vcxproj-file...
What do I need to do to make the change in Output Directory affect $(OutDir)? I have also noticed that there is a macro $(OutDirWasSpecified), which has the value false...
I believe $(OutDir) is typically set in file:
C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Platforms\x64\Microsoft.Cpp.x64.default.props
Or alternately, if on a 32-bit platform:
...\Win32\Microsoft.Cpp.Win32.default.props
Typically, you'll find in your project file:
MyProject.vcxproj
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
and if you find the Microsoft.Cpp.Default.props file, it will have:
<!-- Allow platforms to define the defaults first -->
<Import Condition="Exists('$(VCTargetsPath)\Platforms\$(Platform)\Microsoft.Cpp.$(Platform).default.props')" Project="$(VCTargetsPath)\Platforms\$(Platform)\Microsoft.Cpp.$(Platform).default.props"/>
Which is where $OutDir gets brought in.
It seems likely that this include-chain is somehow broken, or the original definition of $(OutDir) got erased/modified in the file Microsoft.Cpp.x64.default.props.
For what it's worth, my Visual Studio 2013 configuration is a tad different. I have a root folder C:\Program Files (x86)\MSBuild\Microsoft.Cpp, beneath which are two subfolders, C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110 and C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120.

Can I pass a preprocessor definition to the resource compiler through the command line?

I'm currently trying to switch between a few different default Icons in a Visual C++ .rc file using #ifdef tags.
The builds switching the #define value are being created through command line using MSBuild.
The difficulty I have been running into is that using Visual Studio 2010, in order to pass a preprocessor definition to the resource compiler you must define it in the project settings (Config Properties -> Resources -> General).
This makes it difficult to use an #ifdef tag because using this method it will always be defined in the resource compiler.
I would love to define it to a value, so that I might use a preprocessor #if SOMEVALUE == 4 might work, but Cannot seem to find a way to pass a Preprocessor definition + value to MSBuild via the command line.
Does anyone know a way to pass a preprocessor definition directly through to the resource compiler or a way to define a value for a preprocessor definition via commandline for msbuild?
Yes, this can be done.
Try using environment variables to pass values into your build process.
In your project properties add ;$(CMDLINE_DEFINES) to the end of your resource preprocessor definitions. (Be sure to pick the right configuration.)
Then when you use MSBuild from the command line type (or add to a batch file)...
C:\Projects\SomeProject> set CMDLINE_DEFINES=SOMETEST=42
C:\Projects\SomeProject> MSBuild SomeProject.vcproj
A batch file may look like:
#echo off
SET CMDLINE_DEFINES=%1
MSBUILD SomeProject.vcproj
Using this batch file, whatever you pass on the commandline will be passed on to the build process as your preprocessor macro(s).
See the answer to this, with the additional step of setting up ResourceCompile options, i.e. edit your project file in a text editor to include elements like this:
<ItemDefinitionGroup>
<ClCompile>
<AdditionalOptions>/DERROR_LOG_LEVEL=5 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<ResourceCompile>
<AdditionalOptions>/DERROR_LOG_LEVEL=5 %(AdditionalOptions)</AdditionalOptions>
</ResourceCompile>
</ItemDefinitionGroup>
The difficulty I have been running into is that using Visual Studio 2010, in order to pass a preprocessor definition to the resource compiler you must define it in the project settings (Config Properties -> Resources -> General).
You got the hard part. Just enter the define as TESTAPPLE=15 and it will effectively #define TESTAPPLE 15 for the entire project.
Although this is an old question, but for any reference now, the following steps work with me:
Pass the variable = underlying value in msbuild command line using /p, e.g.
msbuild my_solution.sln /p:ENVSOMEVALUE="4"
In the project file my_proj.vcxproj, define the variable as a preprocessor definition for the resource compiler, and assign it to the variable passed from msbuild, e.g. add the following section inside <Project></Project>:
<ItemDefinitionGroup>
<ResourceCompile>
<PreprocessorDefinitions>
SOMEVALUE=$(ENVSOMEVALUE);
%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
in the .rc file, use the code the way you like:
#if SOMEVALUE == 4
You might need to set a default value for the definition if not passed from msbuild (e.g. building from Visual Studio IDE), in this case you might use the condition manually in the project file, e.g.
<ItemDefinitionGroup Condition="'$(ENVSOMEVALUE)'!=''">
<ResourceCompile>
<PreprocessorDefinitions>
SOMEVALUE=$(ENVSOMEVALUE);
%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(ENVSOMEVALUE)'==''">
<ResourceCompile>
<PreprocessorDefinitions>
SOMEVALUE=1; <!-- Default Value -->
%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>

multiple build rules using vc2010 and msbuild

I'm managing a C++ project in VS2010 and want to have ALL .cpp files run through an external tool before going to the C++ compiler. All signs seem to indicate this is possible. See, for example, here.
Since this will happen over multiple projects, it makes sense to put this functionality in a property sheet and then just importing this property sheet everywhere. Before I touched the property sheet, it looked like this in its entirety:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
<ExtensionsToDeleteOnClean>...</ExtensionsToDeleteOnClean>
<CustomBuildBeforeTargets>ClCompile</CustomBuildBeforeTargets>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>...</AdditionalIncludeDirectories>
<ForcedIncludeFiles>%(ForcedIncludeFiles)</ForcedIncludeFiles>
</ClCompile>
<Link>
<AdditionalDependencies>...</AdditionalDependencies>
</Link>
<Outputs>...</Outputs>
</CustomBuildStep>
</ItemDefinitionGroup>
</Project>
As per the above linked document, I have added the following lines:
<Project>
<PropertyGroup>
...
<CustomBuildBeforeTargets>ClCompile</CustomBuildBeforeTargets>
</PropertyGroup>
...
<ItemGroup>
<CustomBuild Include="*.cpp">
<Message>Running Custom Build Step</Message>
<Command>dummy</Command>
<Outputs>dummy</Outputs>
</CustomBuild>
</ItemGroup>
</Project>
This appears to have no effect, and my custom build tool never runs before ClCompile. I have tried various ways of moving things around and renaming tags (the Xml editor complains that CustomBuild isn't even valid according to the schema, for example), but nothing works.
What am I doing wrong?
Although this is a very old question, I will attempt to answer it.
The Include attribute expects to find *.cpp files in the Project directory. If there are no .cpp files in the Project directory, then this CustomBuild "Task" will never kick in.
What the OP could have done was
<CustomBuild Include="**\*.cpp">
<Message>Custombuild kicking in</Message>
<Command>echo %(Identity)</Command>
<Outputs>dummy</Outputs>
</CustomBuild>
instead of
<CustomBuild Include="*.cpp">
...
and he would have performed the CustomBuild action on all the .cpp files in all the subdirectories of the project directory, and not just the .cpp files waiting to be compiled.
If the .cpp files are not availble under the Project directory (can happen when such a project directory structure is used) then the OP must explicitly point to the right "root" directory.
Alternatively you can save yourself wildcard usage by simply running an Exec task on every included ClCompile.
<Target Name="ProcessClCompileFiles" BeforeTargets="ClCompile" Condition="'#(ClCompile)'!=''">
<Message Text="== Starting processing cpp files ==" Importance="High"/>
<Exec Command="echo processing %(ClCompile.filename)%(ClCompile.extension) & ////YOUR COMMAND HERE////" />
</Target>
As a note. This requires the ClCompile itemgroup to be initialised before this Target. if you were to create a target that dynamically adds ClCompile, you'd have to ensure that this happens before this Target is called.
Another note is that these Exec tasks will always run if there are any items in the ClCompile itemgroup. If you have a condition where it should skip them (eg, already processed), use Condition="A=B"
For easy reference, here's how to validate it hasn't created a file and if it created a file; if the file is out of date:
Condition="!Exists('$(OutDir)%(Filename)%(Extension)') OR ($([System.DateTime]::Parse('%(ClCompile.ModifiedTime)').Ticks) > $([System.IO.File]::GetLastWriteTime('$(OutDir)%(Filename)%(Extension)').Ticks))"

MSBuild: Custom.After.Microsoft.Common.targets for native C++ projects in VS2010

I've read about the use of "Custom.Before.Microsoft.Common.targets" and "Custom.After.Microsoft.Common.targets" in order to execute a custom target before/after every project build and I would like to use this technique in order to change version info while building on our TeamCity build server.
The problem is that although it works for C# projects, it doesn't seem to work for native C++ projects.
After some digging around in the Microsoft.Cpp.targets file I found out that for native C++ projects this seems to be implemented through setting $(ForceImportBeforeCppTargets) and $(ForceImportAfterCppTargets).
I can't seem to find a single piece of information on the web about this technique for native C++ apps though, so I'm asking if I'm looking in the right direction or not.
Any help is appreciated.
For VC++ projects it is a bit different. You define a file to be imported either at the beginning or at the end of the project. To use this approach you need to define values for the properties ForceImportBeforeCppTargets or ForceImportAfterCppTargets. For example if you want a file to be included at the beginning of the project you can pass in the value at the command line. For example I just created a dummy VC++ project named CppTets01. Then I created the two sample files below.
Before.proj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CustomTargetInBefore" AfterTargets="Build">
<Message Text="From CustomTargetInBefore" Importance="high"/>
</Target>
</Project>
After.proj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CustomTargetInAfter" AfterTargets="Build">
<Message Text="From CustomTargetInAfter" Importance="high"/>
</Target>
</Project>
Then I executed the following command:
msbuild CppTest01.vcxproj
/p:ForceImportBeforeCppTargets="C:\Temp\_NET\ThrowAway\CppTest01\CppTest01\Before.proj";
ForceImportAfterCppTargets="C:\Temp\_NET\ThrowAway\CppTest01\CppTest01\After.proj"
The result was
C:\Temp_NET\ThrowAway\CppTest01\CppTest01>msbuild CppTest01.vcxproj /p:ForceImportBeforeCppTargets="C:\Temp_NET\ThrowAway\CppTest01\C
ppTest01\Before.proj";ForceImportAfterCppTargets="C:\Temp_NET\ThrowAway\CppTest01\CppTest01\After.proj"
Microsoft (R) Build Engine Version 4.0.30319.1
[Microsoft .NET Framework, Version 4.0.30319.1]
Copyright (C) Microsoft Corporation 2007. All rights reserved.
Build started 10/18/2010 8:32:55 AM.
Project "C:\Temp\_NET\ThrowAway\CppTest01\CppTest01\CppTest01.vcxproj" on node 1 (default targets).
InitializeBuildStatus:
Creating "Debug\CppTest01.unsuccessfulbuild" because "AlwaysCreate" was specified.
ClCompile:
All outputs are up-to-date.
All outputs are up-to-date.
ManifestResourceCompile:
All outputs are up-to-date.
Link:
All outputs are up-to-date.
Manifest:
All outputs are up-to-date.
FinalizeBuildStatus:
Deleting file "Debug\CppTest01.unsuccessfulbuild".
Touching "Debug\CppTest01.lastbuildstate".
CustomTargetInBefore:
From CustomTargetInBefore
CustomTargetInAfter:
From CustomTargetInAfter
Done Building Project "C:\Temp\_NET\ThrowAway\CppTest01\CppTest01\CppTest01.vcxproj" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.21
As you can see from the output the targets were successfully injected into the build process. If you want to relate this back to Custom.Before.Microsoft.Common.targets and Custom.Before.Microsoft.Common.targets then you should know that the technique used there is a bit different. Specifically if you create those files they are automatically imported into every C#/VB.NET project. In this case you have to set this property. You really have two options here:
You can set this property as an environment variable
You can use another technique, ImportBefore & ImportAfter which is specific to VC++
For #1 let me explain a bit. In MSBuild when you access a property with the syntax $(PropName) then if a property with the name PropName doesn't exist MSBuild will look up in the environment variables to see if such a value exists, if it does then that value is returned. So if you have a build server in which you want to include a file for each VC++ build, then just create those properties as environment variables. Now for the other technique.
ImportBefore/ImportAfter
In VC++ a new concept is introduced. In Microsoft.Cpp.Win32.targets you can see the declaration at the top of the .targets file.
<Import Project="$(VCTargetsPath)\Platforms\Win32\ImportBefore\*.targets"
Condition="Exists('$(VCTargetsPath)\Platforms\Win32\ImportBefore')" />
Then there is one towards the bottom
<Import Project="$(VCTargetsPath)\Platforms\Win32\ImportAfter\*.targets"
Condition="Exists('$(VCTargetsPath)\Platforms\Win32\ImportAfter')" />
A similar import declaration exists for the other target platforms as well. Take a look at the files at %ProgramFiles32%\MSBuild\Microsoft.Cpp\v4.0\Platforms\ for the specific names.
With this technique if you want a file to be imported then simply create a file that ends with .targets and place it into the appropriate folder. The advantage of this is that it will be imported into every VC++ build for that platform, and that you can create many different files. The drawback is that you have to place them in those specific folders. That's the main difference between both techniques. With this first technique you can specify the file location via property and its not automatically included for every build, but for the second approach it is but you cannot change the location
You can also add project content into one of *.props files from directory %LOCALAPPDATA%\Microsoft\MSBuild\v4.0\
It make same effect.