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

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.

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 set up this visual studio (2015) custom build step (tool?). Basically I want a pre-preprocessor step that modifies header files (c++)

I want a run a build step that looks at a .h file, adds some code based on some external params, and hands the resulting file to the preprocessor.
I see the "Custom Build Step" in the project properties. It seems to need an output file. I just want to forward the results to the preprocessor.
It seems like the custom build step wants to do a 1-time process, not per-file or by file type.
The problem is that I don't know how to send my external executable the file currently being processed (eg, "HelloWorld.cpp"). $(InputName) and %(Filename) are blank and docs say it's deprecated. How do I send the filename to my external executable?
But even if I get that working, I don't want to set this per-file. I want all header files to go through this process.
Any ideas?
I've looked at:
https://msdn.microsoft.com/en-us/library/dd293663.aspx?f=255&MSPPError=-2147217396
https://msdn.microsoft.com/en-us/library/hefydhhy(v=vs.90).aspx
https://msdn.microsoft.com/en-us/library/ff770593(v=vs.140).aspx
working on a debug, x64 config on windows.
First of all, No, you cannot modify a file and pass along the results to the next stage (that I could see). I'd need some sort of Program Transformation System.
So I need an intermediate file. That file has to be added to the project, even if it gets overwritten by your code generator. I can associate c++ header files with a custom build tool, and they will all get called one-by-one in the stage of the build specified in the Custom Build Step. The custom build tool will modify the intermediate file(s), and all is well.
The VS 2015 name for the current file being processed is %(Filename). In older versions it has been $(ProjectName) and $(InputName).

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

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/

NMake Optional Dependencies

We’re currently upgrading our archaic build system from a bunch of batch scripts to a makefile system using NMake. It’s challenging as we use a custom intermediate language that ends up getting translated to C++ where some of our translators can generate 10’s of files what have a common parts in the file names. The other challenging thing is we use a bunch of CSV files to configure our interfaces and these files get passed through to our configuration tools which generate more source code files. Right now I am focusing on creating the simple rules for our configuration files but can’t seem to figure out a way associate a dependency with a rule if the dependency exists. I tried to use $(wildcard xxx.csv) but found out that this command doesn’t exist for NMake like it does for GNU Make.
So how can I create my rule so that it executes and runs my commands if I have two dependency csv files that will always exists and a third csv file that will exist only when my project calls for it?
[..] will exist only when my project calls for it?
This is a bit unclear. Assuming that there is a command that - depending on some external circumstances - might generate that third csv file, you could use a "stamp file" (I think they call it "pseudo target" in NMAKE):
stamp:
command_that_might_generate_csv3
touch stamp # updates timestamp of "stamp" (or creates it)
target: csv1 csv2 stamp
command_using_all_of csv1 csv2 csv3

TFS 2015 builds : Is it possible to use Variables in Repository mappings?

When creating a vNext build on TFS 2015 you can define variables, which are then used in build steps, and can also be used as environment variables in scripts the build runs.
The build I am working on runs scripts that pulls files from mapped locations, so it would be great if I could define a variable and use it in a mapping so that for example, if I update a reference in the project the build is building, I can simply update the variable with the new location and have the repository mappings and scripts all pull correctly from the new location without having to make the change in multiple places.
I have tried doing this by setting up the variable and mapping as follows,
But this generates an error when you try to save the build complaining that there are two '$' characters in the mapping. Is there way to do this or is this not currently possible?
This has been causing me havok for quite a while as well.
For starters, there is a uservoice request for this feature. You can add your votes and input here to get Microsoft to allow this feature: https://visualstudio.uservoice.com/forums/330519-team-services/suggestions/14131002-allow-variables-in-repository-variables-and-trigg
Second, we've developed a workaround that gets us most of the way there. It's not perfect, but it might be useful to you if you're comfortable with the tradeoffs or can work around the deficiencies.
Start by turning off the "Label Sources" option of the build and mapping the Server Path field to you base build. You'll want to add a custom variable to the Build Definition to tell the build instance what TFS location to pull from. For example, we have a base project and then multiple branches from the project, so our source is structured like this
$\Team Project\Project1
$\Team Project\Project1Branch1
$\Team Project\Project1Branch2
$\Team Project\Project1Branch3
and we create a variable named "Branch" that we can set to "Branch1", "Branch2", and so forth.
When we want to build the base project, we leave the Branch variable blank when launching the build. For branch builds, we set it to the name of the branch we want to build.
Then our build steps look like this
Remap Workspace Folder to Branch Folder
Get Files for Specified Branch - We have to do this manually after
remapping our workspace
Compile the Source in the Specified Branch
Publish Build Artifacts from the Specified Branch
Label the Code of the Specified Branch Manually
The Remap task runs the command
tf workfold "$/Team Project/Project1$(Branch)" "$(build.sourcesDirectory)\$(Build.DefinitionName)$(Branch)"
The Manual Get task runs the following command
get /recursive /noprompt "$/Team Project/Project1$(Branch)"
The build uses the Branch variable to point to the correct location of the solution file for the specified branch
$(build.sourcesDirectory)\$(Build.DefinitionName)$(Branch)\SolutionFile.sln
The Publish Artifacts task uses the Branch variable in both the Contents field and the Path field
Example in Contents
**\$(Build.DefinitionName)$(Branch)\bin
The Label Code task uses the following command
tf label "$(build.buildNumber)" "$/Team Project/Project1$(Branch)" /recursive
The downside of this setup is that you don't capture Associated Changes and Work Items to your subsidiary branches as the Server Path field is always set to the main location. This may not be an issue if you always merge from your branches to your main location prior to launching a build meant to go to production. What you can do to compensate for this really depends on your use case.
With some tweaking, you could use this same format to specify full paths as well if you needed to.
It's impossible. Just as the error message mentioned: there are two '$' characters in the mapping. Which means your application's path shouldn't vary from build to build.
Mappings on the Repository page are used to specify source control
folder which contains projects that need to be built in the build
definition. You can set it via clicking the Ellipsis (...) button,
however, you can't include variables in the mapping path.
There is a similar question: Variables in TFS Mappings on Visual Studio Online Team Builds