Interacting with standard MSBuild infrastructure: <CleanDependsOn> - visual-studio-2010

I read about using these mechanisms in Brian Kretzler’s book and this example posted by Adam Badura, in particular.
Following Badura’s example (which he notes “Cleaning/Rebuilding does remove the file as expected as well.”) I have:
<PropertyGroup>
⋮
<CleanDependsOn>QtClean;$(CleanDependsOn)</CleanDependsOn> <!-- doesn't work -->
⋮
</PropertyGroup>
at top-level (direct child of the document root element).
When I use the IDE menu to Clean Solution, the QtClean Target is not performed. The Message task I included within it does not appear, and the expected effect of the RemoveDir task is not observed (nor are any error messages).
Why would this not work?

This should run using the latest VS2017 feature enhancement Directory.Build.Targets.
Insert the following in a file with the name Directory.Build.Targets at the root folder in trunk of your repository. MSBuild, while loading your .sln, will automatically load your customized Directory.Build.Targets file.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Inject a custom target into Clean by extending CleanDependsOn -->
<PropertyGroup>
<CleanDependsOn> $(CleanDependsOn); CustomAfterClean </CleanDependsOn>
</PropertyGroup>
<Target Name="CustomAfterClean" Condition="$(ProjectName) == 'XXXMyProjectXXX'" >
<!--- my custom clean up -->
</Target>
</Project>

I've learned that <CleanDependsOn> doesn't work because it's overwritten (not appended to) by Microsoft.Common.Targets, which is pulled in at the bottom of a project file. Thus, it cannot be extended by statements within the meaty center of the project file or property sheets as normally included. The “extension targets” are included at the end of the project file, after the normal common targets.
However, the <CppCleanDependsOn> property is extended (not overwritten without including the previous value) everywhere it is used.
One general answer to “Why would this not work?” is that global variables are evil. You have to understand the temporal proximity of the variable (“property”), as it may get changed again before it is read, or read before you are setting it.

Related

Non-standard configuration names in C++ projects break NuGet linking

I wanted to use two configurations in my Visual Studio 2019 C++ project, lets say I wanted to rename Debug to Debug-A and add a new configuration called Debug-B based on Debug-A.
Debug-A and Debug-B differ only by one define symbol, lets say one has SYMBOL_A and another has SYMBOL_B.
Currently, I don't care about Release and anything other than x64.
It works corretly until I add a NuGet package (for example fmt). Then, when I try to compile, I get undefined symbol linker errors. Just like fmtd.lib was not included, if I include it manually (or change the project configuration name to Debug), the issue is gone.
I know the reason why NuGet includes it if my project configuration is named Debug. Look at the nuget package targets file (packages/fmt.7.0.1/build/fmt.targets) - lib files are hardcoded to $(Configuration) being either Debug or Release.
As far as I know, all NuGet C++ packages are built this way.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- ... -->
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<Link>
<AdditionalDependencies>fmtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib>
<AdditionalDependencies>fmtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<Link>
<AdditionalDependencies>fmt.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib>
<AdditionalDependencies>fmt.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<!-- ... -->
</Project>
I know I can manually link the libs, but fmt was just an example, I use a lot of packages and manually linking will become an issue quite fast.
Is there any way to use the quick selection of preprocessor symbols provided by active solution configuration (the toolbar dropdown) and still be able to use NuGet properly? For example parsing the nuget files with fake $(Configuration) variable. Using $(Platform) (x86, x64 etc.) is impossible, library include path is depending on it's hardcoded value too.
Sample project with this bug.
If, as you mentioned, the NuGet Packages’ lib files are hardcoded, then we may need to find solutions from other sides, for example .vcxproj file or MSBuild.
I didn’t find any directly properties/parameters/ways which meet your requirements. But, is it possible to keep the Debug name, I mean, leave it named Debug not Debug-A or Debug-B, and then switch to use different Configurations by using other methods. Imagine there is a Debug configuration file(DebugB)(maybe DebugB related things are set in this file) excluded in the project and the project currently use another Debug configuration(DebugA), and during the build, the project will exactly use DebugA configuration. To switch, do something, or add a code line in .vcxproj file to include the file which contains DebugB configuration, and then let the DebugB configuration cover the DebugA configuration.
So, for covering the properties/items of .vcproj file. Perhaps customize build works.
Hope above could give you a little help.

Automatically change the version of only assembly which has code changes in Dot Net

I am creating patch for my application to deploy small changes to customer. In my application I have 100 .CSProject. Out of 100 library I made code changes in class library A, B, C and Library D is calling to A,B and C library. So Is there any way when I build my application then It should change the version of only A,B,C and D library which have changes. Manually I can change but I need any automatic way.
So Is there any way when I build my application then It should change
the version of only A,B,C and D library which have changes. Manually I
can change but I need any automatic way.
Actually, Directory Build.targets file can probably realize it. When you create a file called Directory.Build.targets, it can search and act on all projects in the current folder and subordinates. See this document about its search scope. Directory Build.targets is pretty much like Directory Build.props. And Directory Build.targets executes after build while Directory Build.props executes before the build starts.
In your situation, you want to overwrite property Version, you should use Directory Build.targets.
Then when you're building a project, it automatically puts content in the scope of the project.
Solution
1) create a file called Directory Build.targets file under your solution or upper level directory of 100 projects.
2) add these into Directory Build.targets file:
<Target Name="change_property" AfterTargets="CoreCompile">
<PropertyGroup>
<Version>xxxxx</Version>
</PropertyGroup>
</Target>
3) Then you can set all changed projects' version to the same version that you wish.
And this function makes the same property changes for all the changed projects.
You should Note that it has a flaw
But you should not modify other related projects(code changes or add new item, picture or anything else) If you change unrelated projects, this feature also changes the version of unrelated projects. This is a special feature of CoreCompile target.
Suggestion
Therefore, you should pay attention not to modifying other projects or you could use Alt+A to select all projects-->unload project and then reload required projects and modify them in case any other unrelated projects are modified.
Or try to put these required projects into a new folder called Modified Projects under the solution folder(which exists xxx.sln file) and then put Directory Build.targets into Modified Projects folder.
-----------------Use other methods like this to avoid this situation.
Update 1
1) If your projects are in the same solution folder, then you can create a file called Directory.Build.targets.
It will work on all items in the current folder and its secondary directory.
And add my sample code in this file:
<Project>
<Target Name="change_property" AfterTargets="CoreCompile">
<PropertyGroup>
<Version>xxxxx</Version>
</PropertyGroup>
</Target>
</Project>
And then you can modify your nuget projects, when you finishing your related projects, you can build your solution or use MSBuild.exe to build xxx.sln and this file will be executed automatically and will change the version of modified nuget projects.
Hope it could help you and any other feedback will be expected.
Update 2
Please try to change to use in Directory.Build.targets and it will work under the whole solution to apply to every verison number.
<Project>
<Target Name="change_property" AfterTargets="CoreCompile">
<PropertyGroup>
<Version>xxxxx</Version>
</PropertyGroup>
</Target>
</Project>
<PropertyGroup>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
And make sure that <GeneratePackageOnBuild>true</GeneratePackageOnBuild> in Directory.Build.targets. Otherwise it will fail. So please remove this node in xxx.csorj file and add <GeneratePackageOnBuild>true</GeneratePackageOnBuild> in Directory.Build.targets.

How to set linker's OutputFile (in .vcxproj) from external list

I have a bunch of C++ projects (.vcxproj) for VS2017, which produce dynamic libraries (DLL), which are handled like "plugins" (i.e. they are loaded dynamically by the application).
Occasionally I want to change the produced DLLs names based on some list in text file (the format may be changed):
ProjName1;DLL1
ProjName2;DLL2
...
The first column specifies the project name ($(ProjectName)) and the second is for desired DLL name ($(TargetName)).
I want this file to be parsed at build-time, so each read DLL name goes into $(OutputFile) of the appropriate project.
For instance, ProjName1.vcxproj will have following fragment (at build time):
<Link>
<OutputFile>$(OutDir)DLL1$(TargetExt)</OutputFile>
</Link>
I guess, certain capabilities of msbuild can be utilized for this (e.g., ReadLinesFromFile task and FindInList task), but I'm not sure how to put these pieces together).
There are some examples of such automation to consider, though I doubt they are relevant to .vcxproj format:
MSBuild ReadLinesFromFile all text on one line
Read text file and split every line in MSBuild
Used ReadLinesFromFile to make version in separate file
The straightforward approach is to modify content of each .vcxproj-file to match the list (either manually, or with some script).
Are there any other options?
Update 1: I've managed to implement a partial solution, which is based on a separate file with DLL name (module name) for each project (refer to Property Functions for more hints).
With property sheets it was easy in my case to 'inject' the fix into all projects:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<PropertyGroup Condition="exists('$(MSBuildProjectDirectory)\_MODULE.name')">
<ModuleName>$([System.IO.File]::ReadAllText('$(MSBuildProjectDirectory)\_MODULE.name'))</ModuleName>
<TargetName>$(ModuleName)</TargetName>
</PropertyGroup>
...
</Project>
Though it works quite good, the approach doesn't meet my needs perfectly - I'd like to have all module names in one file.
After some investigations and tryouts I've finally managed to make a working solution for my case. Basically it is built on top of my previous partial solution by using magic powers of Regex. The hint how to handle regex in my context was found in another answer to similar question.
I've also tuned the list format a bit to my taste:
DLL1=ProjName1
DLL2=ProjName2
...
I've placed the changes in the property sheet, so they can be easily picked up by all dependent projects:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<PropertyGroup Condition="exists('$(MSBuildThisFileDirectory)..\_MODULES.txt')">
<ModulesNames>$([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)..\_MODULES.txt'))</ModulesNames>
<ModuleName>$([System.Text.RegularExpressions.Regex]::Match($(ModulesNames), '\W?(\w*)\W?=\W?$(MSBuildProjectName)\W?').get_Groups().get_Item(1))</ModuleName>
<TargetName>$(ModuleName)</TargetName>
</PropertyGroup>
...
</Project>
If the project isn't listed in _MODULES.txt, its $(TargetName) isn't affected.
If you see $(ModuleName) in the project settings, then everything is tuned correctly:
The solution seems to work fine except for one minor drawback: the build system (VS2017 v.15.9.15) is quite "lazy" in detecting changed in _MODULES.txt while the solution is open, so you'll have to close it and reopen. And chances are high the rebuild will be needed after you change the list.

T4 precompilation on build?

Used MS Studio Community 2015 with SP2.
I need to recompile T4 on the build.
I do add to the project :
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v14.0\TextTemplating\Microsoft.TextTemplating.targets" />
and
<PropertyGroup>
<TransformOnBuild>true</TransformOnBuild>
<TransformOutOfDateOnly>false</TransformOutOfDateOnly>
This force to recompile T4's.
But compilation have a problem - instead of using CustomToolNamespace given for specific T4 are used common RootNamespace. Result are a complete disaster.
I paly with location of CustomToolNamespace in the project file, but without positive result.
Point me where to look - still try to play with *.csproj or start look for debugging Microsoft.TextTemplating.targets?
Or simply generate 'tempalaterecompilation.bat' and run it on pre-build? I very much dislikes this way.
I do check a Microsoft.TextTemplating.targets.
It's contain a definition for namespace
<PropertyGroup>
<!-- Unless another namespace has been specified, use the project namespace as the
default namespace from pre-processed files. -->
<PreprocessTemplateDefaultNamespace Condition=" $(PreprocessTemplateDefaultNamespace)=='' ">$(RootNamespace)</PreprocessTemplateDefaultNamespace>
</PropertyGroup>
So, RootNamespace are used intentionally.
What I need to change to get target to use a CustomToolNamespace defined for a template?
I do find temporary solution for a problem.
There are few places from which Namespace can be picked up.
One of them - ClassNamespace​ in Content section. If this are specified given value will be used as a Namespace.
But this is work-around and I still look for solution using *.targets.

Visual Studio 2010 Beta 2 myTODO Error message

I have downloaded the myTODO Azure sample application and when trying to run it locally I am receiving the following very puzzling error message.
The item "..\MyTodo.WebUx\MyTodo.WebUx.csproj" in item list
"ProjectReferenceWithConfiguration" does not define a value for metadata "Configuration".
In order to use this metadata, either qualify it by specifying %(ProjectReferenceWithConfiguration.Configuration),
or ensure that all items in this list define a value for this metadata.
C:\Program Files (x86)\MSBuild\Microsoft\Cloud Service\1.0\Visual Studio 10.0\Microsoft.CloudService.targets
Below is an extractfrom the MyToDo.WebUX.csproj file, what do I change?
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
I had an issue today where MSBuild was giving me an almost identical error:
The item "xxx.csproj" in item list "ProjectReferenceWithConfiguration" does not define a value for metadata "Name".
Fortunately, I had another Azure service that was compiling correctly, so I was able to search out the differences. It turned out that in the solution that was working, MyService.ccproj (the Azure Service project) was compiling before xxx.csproj. In the solution that was failing, xxx.csproj was compiling first. When I specified the dependencies such that xxx.csproj compiled after MyService.ccproj, it started working.
I'm not sure exactly what's going on here, or what it is about xxx.csproj that makes it so it has to come after. If I figure it out I'll update, but maybe this will help you.
UPDATE: It seems like maybe Azure does not like it if you specify anything besides an Azure Role project as a dependency on the service project. I made it so MyWebRole.csproj was dependent on xxx.csproj, instead of MyService.ccproj being dependent on xxx.csproj, and it worked.
To get the web project to convert, remove the following from the element: "{603c0e0b-db56-11dc-be95-000d561079b0};"
That sample is out of date though ... you can use it to look at the tests project, but to get a more up to date vs2008 version of the majority of that project's code, try downloading the source to this lab: DeployingApplicationsinWindowsAzure.
The Source link is in the upper right corner. If you look at the end folder for the 3 exercise you will find a more up to date version of this project ... however I don't think the complete solution is there so you'll have to compare it to the one you linked to above.
... I haven't compared the projects myself so I don't know what is missing between the lab version and the one on code gallery.

Resources