T4 precompilation on build? - t4

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.

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.

What does the Keyword tag mean in a *.vcxproj file?

I see the following in many *.vcxproj files
<PropertyGroup Label="Globals">
<ProjectGuid>{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>yadayada</RootNamespace>
</PropertyGroup>
What does the <Keyword>Win32Proj</Keyword> segment mean? Does it affect any behavior during the build?
In general, where can I find documentation on the tags in a project file?
<Keyword>Win32Proj</Keyword> tells Visual Studio which of the Windows-specific dependencies you are going to use.
It depends on type of the project selected during its creation.
For already present project you may view this setting in the Project Properties --> General.
Now, up to your question.
Does it affect any behavior during the build?
Win32Proj means that AdditionalIncludeDirectories will contain paths to WinAPI libraries. If it were MFCProj, then paths to MFC headers would have been present there in addition to WinAPI. For instance, see in this project file
As for the documentation, try to check this article from MSDN blog, it explains the meaning of some of the tags. And here are the guidelines for working with project properties.
Note that those properties are supposed to be viewed and edited from the UI, so a structure of a real *.vcxproj file may not seem to you extremely friendly or human-readable.

Interacting with standard MSBuild infrastructure: <CleanDependsOn>

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.

inject version to DLLs during build process

this is my situation:
I have VS2010 solution with X projects included.
Wix project that can create msi from all compiled artifacts.
I have build machine \ Jenkins that first compile (MSBuild .Net 4) all the solution, then compile the wix to package it to msi.
What\how can I inject to all artifacts\dlls the number of the product (e.g 11.2.0.4789) - as simple as possible?
Is there and command line arguments that can be passed while compiling the solution?
There are tools, such as several extensions for MSBuild, that do version stamping but each assumes a particular workflow. You might find one that works for you but a DIY method would help you evaluate them, even if it isn't your final solution.
You can add a property to the MSBuild command-line like this:
msbuild /p:VersionStamp=11.2.0.4789
Note: I assume you are going to parameterize the Jenkins build in some way or generate the number during a preceding build step. Here is a simulation of that:
echo 11.2.0.4789 >version.txt
set /p version=reading from pipe <version.txt
msbuild /p:VersionStamp=%version%
Now, the work is in getting each project to use it. That would depend on the project type and where you want VersionStamp to appear.
For a csproj, you might want to use it as the AssemblyVersion. The simplest way is to move the attribute to a cs file by itself and rewrite it every time. I would leave a comment in AssemblyInfo.cs as a clue to where it now comes from. You can include the cs file in your project either dynamically or permanently. I prefer dynamically since it is effectively an intermediate file for the build. So, in your .csproj add the following in a text editor (e.g. Visual Studio. Unload and Edit project):
<Target Name="BeforeBuild">
<PropertyGroup>
<AssemblyVersionPath>$(IntermediateOutputDir)AssemblyVersion.cs</AssemblyVersionPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(AssemblyVersionPath)" />
</ItemGroup>
<WriteLinesToFile
File='$(AssemblyVersionPath)'
Overwrite="true"
Condition="'$(ProductVersion)' != ''"
Lines='using System.Reflection%3b;
[assembly: AssemblyVersion("$(VersionStamp)")]' />
</Target>
This is sufficient but a more thorough solution would include adding the file to a list so it is cleaned with other files and only writing the file if the version changed to prevent unnecessary rebuilds, etc.
Use a similar technique for other project types.

Creating a VS 2010 Project with only content files

I have some content files that I would like to share between a number of projects in Visual Studio.
I have put these files in their own project, set the build action to "Content", and the copy to output directory to "Copy if newer". I would like all these files to be copied to the bin/debug directory of the projects that reference them.
I can get it to work by including a reference to the "contents" project in each of the projects that need the files, but that requires that a minimal assembly be generated (3K). I assume there is a way, using MSBuild, to make this all work without creating the empty assembly?
Thanks to everone who took the time to make a suggestion about how to solve this problem.
It turns out that if I want my compiled content files to be treated like content files (in that they get copied to the output directory of any other project that references my project), I need to create a target which runs before GetCopyToOutputDirectoryItems, and add the full path of the compiled content files to the AllItemsFullPathWithTargetPath ItemGroup. MSBuild calls GetCopyToOutputDirectoryItems for projects on which the current project depends, and uses the resulting file list to determine the files that are copied along with the assembly.dll. Here is the XML from my .csproj, just in case someone else has a similar problem.
I have a custom task called "ZipDictionary", and I accumulate all the files that I am going to compile in an ItemGroup called DictionaryCompile. My target, "FixGetCopyToOutputDirectoryItems" is executed before "GetCopyToOutputDirectoryItems". I don't do the actual compilation there, since this target can be called multiple times by referencing projects, and it would hurt performance. The target does some transforms to get the post-compilation file names, and then returns the full paths to all the files, since relative paths will not work when copy is called from the referencing project.
<ItemGroup>
<DictionaryCompile Include="Dictionaries\it-IT.dic">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</DictionaryCompile>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<UsingTask TaskName="ZipDictionary" AssemblyFile="..\LogicTree.DictionaryCompiler\bin\Debug\LogicTree.DictionaryCompiler.dll"/>
<Target Name="BeforeCompile">
<Message Text="Files #(DictionaryCompile)" Importance="high" />
<ZipDictionary DictionaryFiles="#(DictionaryCompile)" OutputDirectory="$(OutputPath)">
<Output TaskParameter="OutputFiles" ItemName="DictionaryOutputFiles" />
</ZipDictionary>
</Target>
<Target Name="FixGetCopyToOutputDirectoryItems" BeforeTargets="GetCopyToOutputDirectoryItems">
<ItemGroup>
<_DictionaryCompile Include="#(DictionaryCompile->'$(OutputPath)Dictionaries\%(FileName).ltdic')" />
</ItemGroup>
<AssignTargetPath Files="#(_DictionaryCompile)" RootFolder="$(MSBuildProjectDirectory)\$(OutputPath)">
<Output TaskParameter="AssignedFiles" ItemName="_DictionaryCompileWithTargetPath" />
</AssignTargetPath>
<ItemGroup>
<AllItemsFullPathWithTargetPath Include="#(_DictionaryCompileWithTargetPath->'%(FullPath)')" Condition="'%(_DictionaryCompileWithTargetPath.CopyToOutputDirectory)'=='Always' or '%(_DictionaryCompileWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'" />
<_SourceItemsToCopyToOutputDirectoryAlways Include="#(_DictionaryCompileWithTargetPath->'%(FullPath)')" Condition="'%(_DictionaryCompileWithTargetPath.CopyToOutputDirectory)'=='Always'" />
<_SourceItemsToCopyToOutputDirectory Include="#(_DictionaryCompileWithTargetPath->'%(FullPath)')" Condition="'%(_DictionaryCompileWithTargetPath.CopyToOutputDirectory)'=='PreserveNewest'" />
</ItemGroup>
</Target>
A better possible solution would be to
place a common directory in the solution dir and place your common content files there.
in VS, in each project that should share this content, right-click add existing item, browse to the desired item(s), select, click the down-arrow on the add button and select add as link. In the project, you will notice the files are added with a 'shortcut' overlay.
In the project, select the newly added links and right-click->properties and select Build Action: content, Copy To Output Directory: Copy Always.
This is a simple solution to the problem given.
I use this technique for things like SQL scripts and partial config files (using configSource) with great success. This allows me to make changes to these files in a single location with the assurance that they will be propigated throughout the solution.
A more robust solution would be to create a project with embedded resources. This requires a bit more work to manage the content on the receiving end but may be worth it in the long run as having a bunch of loose artifacts flying about can become problematic.
Hope that helps.
A similar solution like the one Sky suggested can be found in my answer to "Is there a way to automatically include content files into asp.net project file?".
It allows to share your content but you must not touch the folder or its content inside VS because this breaks the recursive path.
This approach works best for auto-generated content - you don't have to bother about including new content files to your solution.
And of course you can reuse this in multiple solutions/projects.
We do something similar where we have "...ReleaseBuilds" that reference dlls and content we require for specific projects. Compiling copies everything to the bin debug folder and indeed creates the empty assembly.
Within Visual Studio we have a post-build event in the "...RealeaseBuild" (in project properties) that copies/deletes or run batch files to make sure we have all the files (configs, services etc etc) required and to delete the empty assembly.
HTH

Resources