VS 2015 "Build Dependencies -> Build Customization" always triggers PreBuild and PostBuild - c++

I have a VS 2015 C++ project with both PreBuild and PostBuild steps.
In addition I have a Custom Target added to the project by "Build Dependencies -> Build Customization". The Custom Target runs a Perl script which runs nmake building files with Intel Compiler. The custom target always runs. Specifically the Perl script always runs while nmake checks for changes and prevents building if input files have not changed.
Invoking the custom target causes the PreBuild and PostBuild to run even if the custom target did not produce and new output (it ran but did nothing but checks).
I want to prevent PreBuild and PostBuild to run if my Custom Target didn't produce any new output. So far I didn't find a way to do this.
Another option is to prevent the custom target from running if sources have not changed. Unfortunately the files built by the Intel compiler are marked as "Exclude From Build" and thus do not trigger the custom target. I tried to define Input & Output for the task run by the custom target with no luck.
Any help will be highly appreciated!

Make sure your custom targets have an Inputs and Outputs attribute which properly describes which files will be used as input and which one were the resulting output. MsBuild will use the timestamp on these files to decide whether you actually changed anything. The timestamp on these files must be older than the file that would be generated as output from the target, that's how MsBuild decides.
Example:
<Target Name="Custom"
Inputs="#(CSFile)"
Outputs="hello.exe">
<Csc
Sources="#(CSFile)"
OutputAssembly="hello.exe"/>
</Target>
See also:
https://msdn.microsoft.com/en-us/library/ms171483.aspx
You can use transforms to map input to output if there is a logical relationship between the two:
<Target Name="Convert"
Inputs="#(TXTFile)"
Outputs="#(TXTFile->'%(Filename).content')">
<GenerateContentFiles
Sources = "#(TXTFile)">
<Output TaskParameter = "OutputContentFiles"
ItemName = "ContentFiles"/>
</GenerateContentFiles>
</Target>
https://msdn.microsoft.com/en-us/library/ms171483.aspx
Do not rely on BeforeTargets and AfterTargets and never rely on PreBuildEvent, as that target itself doesn't have any inputs or outputs and thus always triggers, they're quite old constructs, stemming from the 2003 era, instead override BuildDependsOn and inject your target in the chain.
Example:
<PropertyGroup>
<BuildDependsOn>
Convert;
$(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>
See:
https://blogs.msdn.microsoft.com/msbuild/2006/02/10/how-to-add-custom-process-at-specific-points-during-build-method-2/

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.

How do I put a condition on msbuild built-in targets like Build/Rebuild?

I am working differentially building a huge monolithic solution that includes about 80 projects. In my build pipeline right now I include a step to build the entire solution. But what I'd like to do is to build the solution but provide conditions as msbuild arguments so that I can exclude some of the projects that might not have any changes associated with them. I already have scripts to go through my commits and realize what changed and which projects need to be built.
I just need a way to send that info to MSBuild so that it does not build all projects everytime. I tried building projects separately but that takes a whole lot more time than just building the solution together.
So, I'm looking for any solutions out there through which I can specify to MSBuild that skip a specific project would help a lot. Thanks much!
I already have scripts to go through my commits and realize what
changed and which projects need to be built.
Since I could get clearly know that which script are you using to realize what changed and which projects need to be built. I am assuming that you are using MSbuildTarget script which in the xx.csproj to do these judgement.
=If I did not have misunderstanding, you can get help from this similar issue (See ilya's answer).
See this document and you'll find the build action is performed by these three targets, BeforeBuild,CoreBuild and AfterBuild. So assuming you have a target to go through my commits and realize what changed and if a project need to be built, you can add script like below to xx.csproj:
<PropertyGroup>
<BuildWrapperDependsOn>$(BuildDependsOn)</BuildWrapperDependsOn>
<BuildDependsOn>CheckIfBuildIsNeeded;BuildWrapper</BuildDependsOn>
</PropertyGroup>
<Target Name="CheckIfBuildIsNeeded">
<!-- Execute command here that checks if proceed with the build and sets the exit code -->
<Exec Command="exit /b 1" WorkingDirectory="$(SourcesPath)" IgnoreExitCode="true">
<Output TaskParameter="ExitCode" PropertyName="ExecExitCode"/>
</Exec>
<Message Text="Exit Code: $(ExecExitCode)" Importance="high" />
<PropertyGroup Condition="'$(ExecExitCode)' == '1'">
<DoBuild>false</DoBuild>
</PropertyGroup>
</Target>
<Target Name="BuildWrapper" Condition=" '$(DoBuild)' != 'false' " DependsOnTargets="$(BuildWrapperDependsOn)" Returns="$(TargetPath)" />
Above is the script from ilys, and hope my description can help you understand it. With this script, when we start a build target, it will firstly run the targets it depends on, so it will run the CheckIfBuildIsNeeded target and BuildWrapper target. And only when the DoBuild property is true, the BuildWrapper will actually execute. And since buildwrapper depends on original $(BuildDependsOn), it will continue the real build process.
The total logic is: Run CheckIfBuildIsNeeded script and output value to indicates whether need to build=>Try to Run BuildWrapper=>IF need to build, then run the real build success(BeforeBuild, Corebuild,Afterbuild), if the value is false, finish the build process. So I think you can do some little changes to this script then it can work for your situation. (Not sure what your script looks like, I can't complete it for you)
And since you have many projects, you don't need to add this script to every project manually. You can create a Directory.Build.props file, copy the script into it, and place the file in solution folder, then it will work for all projects in the solution.

Setting Code Analysis settings in TFSBuild.proj

I am trying to set/override some settings in our TEST installation of TFS with regards to forcing Code Analysis and assosicated settings during the build process (regardless of the setting sin the project file)
We currently use in our TEST TFS installation:
Visual Studio 2012 Ultimate on our developer machines AND build server
Have TFS 2012 installed on one server (application and data layer)
Have TFS 2012 build service (controller and agent) installed on another server
We can compile sample .net 4.5 projects (class libraries (DLLs), web applications etc) as expected. This is solely to do with overriding associated Code Analysis settings (hopefully).
Scenario 1 - In our sample applications on our developer machines when you select the project settings (right click -> properties in solution explorer), go to the Code Analysis tab if I turn on the "Enable Code Analysis on build" and select a Rule set from the drop down is performs as exepcted, hence it will generate some warnings. This technical adds <RunCodeAnalysis>false</RunCodeAnalysis> to the *.csproj file if opened up in notepad. If the build is executed to compile the sample project/solution then Code Analysis is performed as expected. I do NOT want to do this on every project because a developer could turn it off (although I am looking to have check-in policies and/or private/gated checkins as well to force this anyway).
Scenario 2 - I can disable the "Enable Code Analysis on Build" checkbox and force code analysis in our TFSBuild.proj file (we (will) use the default upgradetemplate.xaml as our process definition because we will be upgrading from TFS 2008 on our LIVE TFS installation) by having:
<RunCodeAnalysis>Always</RunCodeAnalysis>
This works and this is how we will force (lessons still to be learned :-)) Code Analysis on our builds.
The problem then comes when setting other assosicated Code Analysis settings. For example which default rule set(s) to apply/use or treat CA warnings as errors. Some of these settings can be set either in VS or all of them by editting *.csproj in notepad. If i edit the *.csproj then these values are used in the build as expected (as well as locally on the developer machine). This is not ideal as I want to do it centrally in TFSBuild.proj without having to edit every project file. I believe I can use settings such as in my TFSbuild.proj file:
<PropertyGroup>
<RunCodeAnalysis>Always</RunCodeAnalysis>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
</PropertyGroup>
But they don't appear to work or I am putting them in the wrong place? How do I fix/use them correctly?
FYI i build my solutions in TFSBuild.proj by:
<Project DefaultTargets="DesktopBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets" />
<ItemGroup>
<SolutionToBuild Include="/some folder/some solution.sln" />
<ConfigurationToBuild Include="Debug|Any CPU">
<FlavorToBuild>Debug</FlavorToBuild>
<PlatformToBuild>Any CPU</PlatformToBuild>
</ConfigurationToBuild>
</ItemGroup>
</Project>
On the build server I did find reference to the target file for Code Analysis at c:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\CodeAnalysis but I dont want to change the default behaviour on the build server (although it does work when I do). The condition for example for CodeAnalysisTreatWarningsAsErrors must be getting evaluated as false. Its like my values are not read from TFSBuild.proj but are from the .csproj file.
Any questions feel free to ask and thanks in advance
I had what I think is a similar problem with Cruise Control not compiling using the CODE_ANALYSIS compilation symbol, even if "Enable Code Analysis on Build (defines CODE_ANALYSIS constant)" was checked in VS.Net.
It looks like whether it is check or not, CODE_ANALYSIS is actually not explicitly added to the list of compilation symbols in the csproj (even if it appears in the text box "Conditional compilation symbols"), only <RunCodeAnalysis>true</RunCodeAnalysis> is added.
When compiling through VS.Net, the CODE_ANALYSIS is automatically added, but not when using MSBuild, which is what Cruise Control uses.
I eventually changed in VS.Net the "Conditional compilation symbols" from "CODE_ANALYSIS;MySymbol" to "MySymbol;CODE_ANALYSIS". Doing that forced CODE_ANALYSIS to also appear in the csproj.
I remember having a similar problem - but not having the time to investigate it, I worked around it by calling FxCop directly using the exec task. I'll just give you the highlights, omitting the specification of some properties, I hope the names are clear.
I created an ItemGroup of the output dlls, FilesToAnalyze, and fed it to FxCop in a way similar to:
<PropertyGroup>
<FxCopErrorLinePattern>: error</FxCopErrorLinePattern>
<FxCopCommand>"$(FxCopPath)" /gac /rule:"$(FxCopRules)" /ruleset:="$(FxCopRuleSet)" #(FilesToAnalyze->'/file:"%(identity)"', ' ') /out:$(FullFxCopLog) /console | Find "$(FxCopErrorLinePattern)" > "$(FxCopLogFile)"</FxCopCommand>
</PropertyGroup>
<Exec Command="$(FxCopCommand)"
ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="FxCopExitCode"/>
</Exec>
<ReadLinesFromFile File="$(FxCopLogFile)">
<Output TaskParameter="Lines" ItemName="AllErrorLines"/>
</ReadLinesFromFile>
I could then determine the number of errors in the output using an extensionpack task:
<MSBuild.ExtensionPack.Framework.MsBuildHelper TaskAction="GetItemCount" InputItems1="#(AllErrorLines)">
<Output TaskParameter="ItemCount" PropertyName="FxErrorCount"/>
</MSBuild.ExtensionPack.Framework.MsBuildHelper>
and create a failing build step for each error:
<BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
Id="$(FxCopStep)"
Status="Failed"
Message="FxCop Failed: $(FxErrorCount) errors."/>
<BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
Status="Failed"
Message="%(AllErrorLines.Identity)"/>
By doing code analysis on the build server this way, we also avoided having to configure each project separately. We isolated all this in a separate .targets file, so adding code analysis to a solution was a matter of importing that file, and perhaps adjusting the behavior by setting appropriate properties.

How can I make a custom MSBuild Target's execution dependent on a file's time stamp?

The C++ project I'm working on (which I converted from VS2008 to VS2010) used to use several vcbuild .rules files to specify custom build rules. These .rules files had a specific "AdditionalDependencies" property for the CustomBuildRule node which specified a list of files that the should be taken into account when working out if the target needs rebuilding or not. These "AdditionalDependencies" were faithfully carried over into the corresponding .props file during the VS2010 conversion.
The .targets file associated with the custom build rule does add these AdditionalDependencies to the Inputs property of the Target node. This ensures that the target gets executed in case any of the files listed in the dependencies doesn't exist, but it does not execute the target if one of the dependencies is newer than the target's output. It's also not quite logically correct as not all of the files are actually inputs, several of them refer to executables that might be used during the target's build. As such, they might be checked into version control and will be present, but a newer version of the file needs to trigger a rebuild of the affected target.
The MSDN documentation for the Target node shows a Condition property which should work fine for my requirements, but the conditions supported by this property don't appear go past the 'Exists' test that is already being performed.
Is there a condition that I can use which will compare two files' time stamps (or ideally, the time stamp of the files currently listed in AdditionalDependencies against the Target's output files) and thus allow me to trigger a make-like "rebuild this target if it is out of date these dependencies"?
Please take a closer look to the target Output property:
"MSBuild can compare the timestamps of the input files with the timestamps of the output files and determine whether to skip, build, or partially rebuild a target. In the following example, if any file in the #(CSFile) item list is newer than the hello.exe file, MSBuild will run the target; otherwise it will be skipped:"
<Target Name="Build"
Inputs="#(CSFile)"
Outputs="hello.exe">
<Csc
Sources="#(CSFile)"
OutputAssembly="hello.exe"/>
</Target>
The original article and more information about incremental builds with MSBuild can be found here.

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.