How to override compile command of Visual Studio 2017 Community [duplicate] - c++

This question already has answers here:
How to use Clang compiler with MSBuild?
(3 answers)
Closed 5 years ago.
I would like to override the default Visual Studio C++ compiler with a simple shell script. What I want is to capture the arguments, such as file name, and create some statistics. However I want to override the compilation process completely - that is, I want to call the original compilation from my shell script.
I googled but all I found was how to have pre-build and post-build scripts that execute within a project. That's not what I want.
I want to change this globally. How can I do it?

For a standard C++ project file compilation is done by invoking the MsBuild Target named ClCompile. Note there's also an MsBuild Item named ClCompile which lists the actual C++ source files used, this can be readily seen by opening your .vcxproj in a text editor. Consequently this ClCompile Item is used in the ClCompile Target, where it gets passed to the CL Task which in turn will invoke cl.exe, the actual compiler executable. This code for this can be found in the Microsoft.CppCommon.targets file for the toolset you use, for a default install of VS2017 community on a 64bit machine that is C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\VC\VCTargets\Microsoft.CppCommon.targets.
Any of those 3 could be overridden with a custom version however as you figured already just replacing cl.exe on disk isn't the best idea.
But CL can use any executable simply by overriding the CLToolExe and CLToolPath properties. Practically: open your .vcxproj file and add
<PropertyGroup>
<CLToolExe>mycl.exe</CLToolExe>
<CLToolPath>c:\path\to\mycompilerstub\</CLToolPath>
</PropertyGroup>
all the way at the end, after the line importing Microsoft.Cpp.targets; mycl.exe will be called instead of cl.exe. If you want the same effect globally on your machine, you'll put that PropertyGroup in a seperate msbuild file and save it in for example C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\VC\VCTargets\Platforms\x64\ImportAfter\MyCustomImport.targets. Any targets file in that directory will be imported automatically.
As an alternative you could override the ClCompile target or the CL task. That's more involved though, e.g. for ClCompile you'd start by copying the whole implementation found in Microsoft.CppCommon.targets and then add whatever logic you need. Advantage is you have direct access to e.g. source files etc without having to parse a command line. For example this would override ClCompile and print source files and pass them to a custom executable:
<Target Name="ClCompile"
Condition="'#(ClCompile)' != ''"
DependsOnTargets="SelectClCompile">
<Message Text="All the sources = #(ClCompile)"/>
<Exec Command="mycustom.exe #(ClCompile)" />
... <!--rest of implementation copied from Microsoft.CppCommon.targets goes here-->
</Target>
Again, this needs to be put at the end of your project file or in the ImportAfter directory for global overriding.

Related

Where exactly is macro $(VCTargetsPath) defined?

We had a discussion before.
https://social.msdn.microsoft.com/Forums/vstudio/en-US/e04e7791-c0c4-4598-b900-310878f5af45/how-can-i-locate-and-change-the-vctargetspath-variable?forum=msbuild
After hours searching .props files, all the $(VCTargetsPath) in .prop and .targets are deleted, but Visual Studio can still recognize this macro/variable correctly.
By modifing Microsoft.Cpp.ToolsetLocation.props, $(VCTargetsPath) can be modified and in consequence VS can no longer create new project.
So, where exactly this macro defined?
I believed that this macro is not and shouldn't be hardcoded.
Where exactly is macro $(VCTargetsPath) defined?
Actually, VCTargetsPath property is defined in the MSBuild system props or targets files under MSBuild folder(C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild). And the files are nested and related to each other. Therefore, the values of various properties are referred to each other in the level-by-level files, and the value of VCTargetsPath is no exception.
==========================================================================
MSBuild is actually equivalent to soft coding. Once MSBuild is installed, it exists as a bunch of dlls and various established props and targets files. You can modify its properties or extend its functions in various ways in the props or targets file. The MSBuild itself is an open source build tool. It is not as unchangeable as you think.
==========================================================================
The VCTargetsPath is actually defined in the msbuild props file called Microsoft.Cpp.ToolsetLocation.props as you said.
It's just that the various targets and props files in MSBuild are nested and interrelated. VCTargetsPath is set as the value of _VCTargetsPathForToolset property while _VCTargetsPathForToolset is defined in another associated targets or props file.
In general, use <import projects="xxxx\xxx.props or targets" /> to embed another file in the current targets or props file.
And there are so many files in MSBuild that if you want to search layer by layer until you find the final specific value, it might seem a little complicated.
Besides, VCTargetsPath is the path of some tools used by MSBuild to compile c++ projects.
The default value in VS2019 is C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160.
You can write a custom target in the xxx.vcxproj file to output its value.
1) write this in xxx.vcxproj file:
<Target Name="test123" AfterTargets="Build">
<Message Importance="high" Text="$(VCTargetsPath)"></Message>
</Target>
2) Then rebuild your project and you can see this:
It is the MSBuild system defined path. If you change it easily, it will cause the project to fail to compile, unless you have a set of self-defined system similar to MSBuild to build C++ projects, and then put it in new path, let the new path overwrite VCTargetsPath property.
In addition, I am curious why you want to find the lowest VCTargetPath value. If you want to know the principle of it, the above answer explains it.
And if you want to overwrite its value, you can just create an environment system variable called VCTargetsPath directly, and then set its value to a new one.
Restart VS to enable such new value. It is much easier than modifying the targets file.

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

Build a Visual Studio C++ project using the Command Line

I''ve written this batch script to build a Visual Studio C++ project using the Command Line:
pushd C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\
VsDevCmd.bat
pushd F:\Master_Copy2\embedded\IFV-170\visualC12
Msbuild.exe DSP1_Emu.vcxproj
The script changes to C:\Program Files (x86)\ ... directory, runs the VsDevCmd.bat, and then don't complete the commands, and I don't know what's wrong.
Note: When I run these commands one by one in a Command window it runs properly and builds the project.
Edit: #roalz pointed out that I should use Call with the batch file VsDevCmd.bat, and this solved the problem.
I think you need to use the call batch command to call VsDevCmd.bat from inside your batch file.
"The CALL command will launch a new batch file context along with any specified parameters. When the end of the second batch file is reached (or if EXIT is used), control will return to just after the initial CALL statement."
Please see the reference here
Another suggestion is to enclose paths containing spaces inside double quotes, i.e.
"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\"

MSBuild - Cannot open include file (despite listed in the INCLUDE list )

I am probably missing something obvious - but I have been stuck for a while on this issue. I am compiling a Visual Studio project on the command line using MSBuild. Basically like this:
CALL vcvars32.bat
MSBuild myproject.sln /m /t:rebuild /p:Configuration=Release /verbosity:m
But this gives me an error: fatal error C1083: Cannot open include file 'winsock.h': No such file or directory
But if I check the environment variable INCLUDEafter the vcvars32.bat call the directory containing 'winsock.h' is in the list - so I definitely have this file in the SDK.
In addition if I change verbosity of MSBuild to detailed I can see the full compile command used. If I copy that and run it in the console the same cpp file compiles without any problem.
Any idea whats different inside the MSBuild context ?
Solved it, adding /p:useenv=true make MSBuild use the INCLUDE environment variable.
( Still a bit unsure why that had to be done though, since I can't recall having needed that earlier for command line builds. )

How to customize C++ compiler error message(s) in MSBuild?

Is it possible to influence how exactly MSBuild formats and reports output of CL.EXE that is run as part of building a C++ project?
They actually differ when run from command-line and from Visual Studio:
Command-line: file(line): error message [project]
Visual Studio: file(line): error message
It seems like a minor detail, however I really, really need to display the [project] also from inside Visual Studio builds as I'm triggering builds of prerequisites and do not want to mix their errors with errors of the parent project.
I've tried faking $(BuildingInsideVisualStudio) and observing whichever properties are being passed to MSBuild but to no avail and cannot get the command-line error message style into VS. One way of forcing it is using an <Exec> task instead of <MSBuild> but spawning a separate build process is undesirable in almost every way imaginable. Is there any way to convince it peacefully? Even when $(BuildingInsideVisualStudio) is overridden to true or false, it doesn't seem to affect the style of the error output. Strange. Maybe Visual Studio intercepts and reformats the message?
Thanks for any help.
MSBuild provides very good extensibility. Place any *.targets file under $(VCTargetsPath)\Platforms\$(Platform)\ImportBefore\ with custom targets to be loaded before the standard processing.
Create a file named custom.cpp.win32.targets under:
C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\Platforms\Win32\ImportBefore\
Copy the following into custom.cpp.win32.targets. This will print the full path of the project before calling cl.exe:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BeforeClCompileTargets>
BeforeClCompileCustom;
$(BeforeClCompileTargets);
</BeforeClCompileTargets>
</PropertyGroup>
<Target Name="BeforeClCompileCustom">
<Message Importance="high"
Text="Building '$(ProjectName)' - $(Platform)|$(Configuration) [$(MSBuildProjectFullPath)]" />
</Target>