Setting an MSBuild property to a file version - msbuild

I'm installing a toolset and I use the $(Registry:Path#Value) syntax to read the path to the compiler executable and set a property containing this value. However, in order to properly set up the search paths that the executable needs, I need to read the file version of this executable. I've seen examples of using a Target to do this and putting a Task in the target that invokes GetAssemblyVersion, but I want something more like a property function, because I want to initialize this before a build has even been run. The user should be able to go into the Project Settings and see the value I've determined as part of their include path. So what I'd really like is something like this:
<Project>
<PropertyGroup>
<CompilerExe>$(Registry:Path#Value)</CompilerExe>
<CompilerVersion>$(GetFileVersion:$(CompilerExe))</CompilerVersion>
</PropertyGroup>
</Project>
Obviously the syntax above doesn't work, but is there any way I can do something like this?

Related

Why is it called response file

MBuild can use response files to save and run commands. But why is it called response file? What is it responding to?
(Also in an MsBuild file the task elements are called Target. What is 'target' refering too?)
A target represents a collection of things you want to do. In an msbuild file, it is represented by an xml element that can have various child xml elements called tasks.
Conceptually it looks like this:
<Target Name="Foo">
<Task />
<AnotherTask />
</Target>
The target you want to execute can be passed in as a command line parameter to msbuild. There are other ways to execute the target of your choice, but you will need to read the docs for that:
https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild?view=vs-2019
In other build systems, a target can be called a goal.
Note:
Some build systems use a very rigid convention, where files have to be in certain places. MSBuild is not like that. It relies on configuration, where you can configure it any way you like. The only convention's really are the xml syntax and schema that you have to follow.
As for the response file name. Who knows and who cares anyways? It's just an extra place to put more command line parameters. I don't rely on it, and neither should you. If you know what you are doing you can stick all that stuff in a proper msbuild xml file and just invoke msbuild to kick off a build.

How to force msbuild to ignore warning MSB8012

I know this question was discussed hundreds of times, but I still cannot find solution. Maybe something was changed in msbuild and I am not aware. The problem is that I get the following warning
warning MSB8012: TargetPath(d:\src\output\Techd.dll) does not match the Linker's OutputFile
property value (d:\src\output\Debug32\bin\Techd.dll). This may cause your project to build
incorrectly. To correct this, please make sure that $(OutDir), $(TargetName) and
$(TargetExt) property values match the value specified in %(Link.OutputFile).
I change the outdir by property
msbuild.wxw /p:OutDir="my_out_dir"
I cannot change projects properties and I am not allowed to modify msbuild target files (Microsoft.CppBuild.targets). So don't have any idea hot to force msbuild to ignore this warning or to ignore the $OutDir variable's change.
You should be able to silence MSBuild warnings using a command line parameter:
msbuild.exe /nowarn:MSB8012
In an upcoming update to MSBuild/VS2017 (15.3), you can specify properties to control MSBuild warnings in project files as a property, so that it also affects builds in VS:
<MSBuildWarningsAsMessages>MSB8012</MSBuildWarningsAsMessages>

Target not running when using BeforeTargets="Build" on Build Server

I have a custom .targets file which I import into my C# MVC web application's project file. I've added custom targets to this like so:
<Target Name="CopyFiles" BeforeTargets="Build"></Target>
This works fine when building under Visual Studio, but when I use TeamCity to build it, the target never gets run, and I can't work out why.
If I change my target to use BeforeTargets="Compile" then it runs. Alternatively, if I add an additional target with the name Build to the .targets file
<Target Name="Build" />
then it will run, but doing so overrides the existing Build target and thus my application doesn't build. I can't quite make out the logic to this - it doesn't make sense. I'm using the Compile target for now, but if someone could explain why trying to execute it before the Build task doesn't work I'd really appreciate it.
'Build' is a special built-in target, so doesn't really work the same way as most other targets. It definitely can't be safely overridden.
The most relevant documentation is here: https://msdn.microsoft.com/en-us/library/ms366724.aspx
If you want something to run before build, the standard approach (as recommend by the comments in a newly-created .csproj file) is to override the BeforeBuild target (as documented above).
However, this isn't the most robust solution. As noted in the documentation above:
Overriding predefined targets is an easy way to extend the build process, but, because MSBuild evaluates the definition of targets sequentially, there is no way to prevent another project that imports your project from overriding the targets you already have overridden.
It's better (and only slightly more complex), to override the BuildDependsOn property and extend the default value of this property to include the target you want to run (this is also documented in the link above).
Another approach would be to leave BeforeBuild empty and use BeforeTargets="BeforeBuild", which feels a bit odd but is quite simple and will still work even if the BeforeBuild target gets overridden.
As to why BeforeTargets="Build" doesn't work, I can't find a reference for this in the documentation, but I think it's to do with its special nature. It doesn't work the same as ordinary targets and it's probably better not to think of it as a target at all.

Build depends on AfterBuild Microsoft.Common.targets

I'm trying to figure out when the different targets are run. But I'm a little confused when it comes to the AfterBuild target, it's comment is "Redefine this target in your project in order to run tasks just after Build". But when I look at what Build depends on I see:
<BuildDependsOn>
BeforeBuild;
CoreBuild;
AfterBuild
</BuildDependsOn>
Dos not this mean that the Build target runs after "AfterBuild" or am I missing something here? I'm new to Build so maybe I have missed something trivial.
You should do some further research in the same file (just do a search on BuildDependsOn in a text editor): you'll see the Build target itself is just a stub that looks something like this:
<Target
Name="Build"
DependsOnTargets="$(BuildDependsOn)"/>
So when one calls msbuild /t:Build, msbuild looks up the build target and sees it has a DependsOnTargets property whith the value BeforeBuild;CoreBuild;AfterBuild (note that is a list). Since DependsOnTargets is always executed before the target itself, all targets listed therein executed first in the order listed. Only then the Build target itself is executed (so yes, that effectively happens after AfterBuild). But the Build target itself actually doesn't do anything: compiling etc all happens in CoreBuild so by the time it's invoked everything is done already.
This might seem odd at first, but it's actually a very expandable way to make targets depends on each other and define the order in which they run. (there's DependsOn, but also BeforeTargets and AfterTargets) So suppose you want a target that for clarity effectively runs after Build, you can use the same principle:
<Target Name="MyTarget" AfterTargets="Build">
...
</Target>
Note this is actually the preferred way: in large projects it's not reliable to override AfterBuild since you don't know if somebody else also did it already, and overriding it in multiple places results in only the last one found to be called.

Tfs2010 Build Number and Assembly File Versions & MSBuild targets

I read John Robbins' article TFS 2010 Build Number and Assembly File Versions: Completely In Sync with Only MSBuild 4.0, and I'm wondering about the best way to go about integrating this.
The download for the article has two files, one is a targets file and one is a proj file.
The targets file has a number of tasks to scrape out a build number based on the Tfs build number (the same one used for the builds) and write that number out to some location (call it BuildNumberFile) for consumption by other proj files.
The proj file is very simple. It just imports the aforementioned targets file, and then declares a target with name "All" while also declaring DefaultTargets on the Project element to be All as well.
<Project ToolsVersion="4.0" DefaultTargets="All" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- The two required properties so the Wintellect.TFSBuildNumber tasks knows your major and minor values.-->
<TFSMajorBuildNumber>3</TFSMajorBuildNumber>
<TFSMinorBuildNumber>1</TFSMinorBuildNumber>
</PropertyGroup>
<Import Project="Wintellect.TFSBuildNumber.targets"/>
<!-- Just ask for the version information files you need. These are here to show all the diffent ones in
Wintellect.TFSBuildNumber.Targets. You can change the names -->
<Target Name="All"
DependsOnTargets="WriteSharedCSharpAssemblyVersionFile;
WriteSharedVBAssemblyVersionFile;
WriteSharedCPPCLIAssemblyVersionFile;
WriteSharedCPPAssemblyVersionFile;
WriteSharedWiXAssemblyVersionFile;
WriteSharedTextAssemblyVersionFile;"/>
</Project>
I have two questions about this:
I'm still learning MSBuild. If the name of the target isn't specified elsewhere in the targets, is the target executed? How do I ensure that this target is run?
Are the csproj files supposed to declare an Include item for the location where BuildNumberFile is, even though it doesn't exist until compiletime?
Do ItemGroups and Include have a DependsOnTargets or something that allows them make sure the file exists before they build?
Are the entire contents of the csproj file using this supposed to be wrapped in a target that expresses DependsOnTargets for BuildNumberFile?
Thanks!
I think I've got this figured out, but two people promoted my question so I'll answer it here:
You can ensure that a target is run by expressing a dependency on it from another target. Microsoft.Common.targets exposes two targets--BeforeBuild and AfterBuild--expressly for the purpose of being overridden for customizability. I found the easiest way to do this was <Target Name="BeforeBuild" DependsOnTargets="WriteSharedCSharpAssemblyVersionFile" /> where WriteSharedCSharpAssemblyVersionFile is the target declared in the download from the link in the original post. Also, if you're new to MSBuild, this BeforeBuild target must be declared after the Microsoft.CSharp.targets is imported, but the default csproj template guides you in doing this.
The WriteSharedCSharpAssemblyVersionFile target should indeed write the file to some central location, since when building a solution, all targets are executed only once. All projects should reference the file from that location even if it doesn't exist, since by the time compilation happens (or more importantly, by the time references are resolved), the BeforeBuild target will have run and the file will be in place.
In my structure, I have these versioning files in a folder directly underneath the branch root folder. Furthermore, since the file being built is generated, I have it build to the output directory. It seems a little strange to be referencing things from the output, but it preserves the invariant of having all build products in one place so that the output directory can be blown away as a means of performing a clean.
In MSBuild items constitute inputs into the system (usually files) so it's weird to think of them depending on targets. After some learning this question doesn't make a lot of sense. In any case, the answer is no.
The entire contents of the file should indeed not be all in one target--all that is required is to import the Wintellect.TFSBuildNumber.targets file at the beginning of your csproj file, and declare BeforeBuild's dependency on WriteSharedCSharpAssemblyVersionFile at the end.
Hope this helps!