Read a nuget package library version in the target assembly when installed - visual-studio

How to find the installing package library version in the target assembly?
Assume that:
We have NugetLib and ConsumerApp (which will install NugetLib).
I am using .targets file in NugetLib named the same, and it will run on the target when installed.
Inside NugetLib.targets file:
<Target Name="Main" AfterTargets="Build">
<Message Text="$(NugetLibVersion)" Importance="high" />
</Target>
Currently I don't have the correct value of NugetLibVersion, and it needs to be set somehow.
Using $(PackageVersion)" will not help, cause it doesn't read and provide NugetLib version, it will provide ConsumerApp version (the target assembly, which will always be 1.0.0.0).
If you know a way achieving this including setting some special properties, or even by using text files, or any other own or tricky methods are all welcome and appreciated.

If your project is new sdk style format, you can just add this custom target to get the nuget version:
<Target Name="PrintPackageReferences" AfterTargets="Build">
<Message Text="Dependencies:%0A #(PackageReference->'%(Identity), Version: %(Version)', '%0A ')" Importance="High" />
<CreateProperty Condition="'%(PackageReference.Identity)'=='NugetLib'" Value="%(PackageReference.Version)">
<Output TaskParameter="Value" PropertyName="NugetLibVersion" />
</CreateProperty>
<Message Importance="high" Text="$(NugetLibVersion)"></Message>
</Target>

Related

How to mark files of a folder as embedded resource with wildcards with the new project format?

My scenario is simple.
I have test project where i want all files within a folder to be marked as embedded resource by default. To prevent someone from doing mistakes here i want this to be automatic through wildcards
I looked at this question, which looked very promising.
MSBuild: Include a custom resource file as embedded resource
However that does not seem to work with the new csproj format. Does anyone know what i should be doing different for it to work with the new format?
My current code is this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="Shouldly" Version="3.0.2" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.2.1" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="3.2.1" />
</ItemGroup>
<Target Name="BeforeBuild">
<CreateItem Include="TestContent\*.cs">
<Output ItemName="EmbeddedResource" TaskParameter="Include" />
</CreateItem>
</Target>
</Project>
You can try this script:
<Target Name="MyCustomStep" BeforeTargets="BeforeBuild">
<CreateItem Include="TestContent\*.cs">
<Output ItemName="EmbeddedResource" TaskParameter="Include" />
</CreateItem>
</Target>
There exists difference between the BeforeBuild Target in old and new csproj format. (Or maybe the difference between .net core and .net framewrok, not sure about this point)
Some discoveries when I set the msbuild verbosity to Detailed:
1.For projects that target .net framework using the old csproj format:
The BeforeBuild target will exactly execute the CreateItem Task. So it works for old-format project files.
2.For projects that target .net core using new sdk format:
The BeforeBuild target seems not to execute the task as what we expected.
After defining the Custom target which executes before the BeforeBuild target, it works in my machine:

use post build events with Fody.Costura installed

Once I added Fody.Costura to my project, my post build event that was copying the resulting assembly into a different location started failing with access denied message. That makes sense since Costura uses MSBuild to embed the assemblies. Is there a way to force my post builds to execute after Costura is finished? Example of a post build command:
copy /Y "$(TargetPath)" "%ALLUSERSPROFILE%\Autodesk\Revit\Addins\2019\HOK-Addin.bundle\Contents"
Basically the solution to my own question is the following.
<Target Name="CopyFiles" AfterTargets="AfterBuild;NonWinFodyTarget">
<Message Text="Signing file..." Importance="high" />
<Exec Command=""C:\Program Files (x86)\Windows Kits\10\bin\10.0.17134.0\x64\signtool.exe" sign /c "Code Signing - DTM" /v "$(TargetPath)"" />
<Message Text="Copy files..." Importance="high" />
<Message Text="$(TargetPath) > $(ALLUSERSPROFILE)\Autodesk\Revit\Addins\$(Configuration)\HOK-Addin.bundle\Contents" Importance="high" />
<Message Text="$(TargetDir)$(TargetName).addin > $(ALLUSERSPROFILE)\Autodesk\Revit\Addins\$(Configuration)" Importance="high" />
<Copy SourceFiles="$(TargetPath)" DestinationFolder="$(ALLUSERSPROFILE)\Autodesk\Revit\Addins\$(Configuration)\HOK-Addin.bundle\Contents" ContinueOnError="true" />
<Copy SourceFiles="$(TargetDir)$(TargetName).addin" DestinationFolder="$(ALLUSERSPROFILE)\Autodesk\Revit\Addins\$(Configuration)" ContinueOnError="true" />
</Target>
What I did, was to replace the standatd Post Build Command that runs Command Line routines, with a MSBuild Target and a Task.Giving it flags to run after Build is finished and Fody is done merging assemblies resolves my issue.
What also helps is the fact that Tasks have flags like ContinueOnError="true" that allow the task to keep trying until the file is available (if that was the issue) as opposed to command line utilities that would just fail.
Cheers!

How-To Set and Get Build Version in TFS SQL Project

I'm using TFS in Visual Studio 2013 & SSDT to create various SQL Database scripts. i.e. I'm doing all my SQL DB development via VS not SSMS.
Want I'm now trying to achieve is to generate/retreive a version number from an external text file when the project is built/published, based on the functionality posted here:
http://www.codeproject.com/Articles/468855/Working-with-MSBuild-Part-2
So I've added the following to the MyProject.sqlproj xml file:
<PropertyGroup>
<WorkingFolder>C:\Source Control\MISTP\Main\DB\SSMS\MyProject</WorkingFolder>
</PropertyGroup>
<Target Name="GetVersion">
<Message Text="GetVersion: Reading version number from VersionInfo.txt" />
<Attrib Files="$(WorkingFolder)\VersionInfo.txt" Normal="true" />
<Version VersionFile="$(WorkingFolder)\Build\VersionInfo.txt">
<Output TaskParameter="Major" PropertyName="Major" />
<Output TaskParameter="Minor" PropertyName="Minor" />
<Output TaskParameter="Build" PropertyName="Build" />
<Output TaskParameter="Revision" PropertyName="Revision" />
</Version>
<Message Text="GetVersion: $(Major).$(Minor).$(Build).$(Revision)" />
</Target>
<Target Name="SetVersion" DependsOnTargets="GetVersion">
<Message Text="SetVersionInfo: Updating Versions in all files" />
<CreateItem Include="$(WorkingFolder)\**\*.*">
<Output TaskParameter="Include" ItemName="Files"/>
</CreateItem>
<Attrib Files="#(Files)" Normal="true" />
<FileUpdate Files="#(Files)" Regex="FileVersionAttribute\("(\d+)\.(\d+)\.(\d+)\.(\d+)"\)" ReplacementText="FileVersionAttribute("$(Major).$(Minor).$(Build).$(Revision)")" />
<FileUpdate Files="#(Files)" Regex="FileVersion\("(\d+)\.(\d+)\.(\d+)\.(\d+)"\)" ReplacementText=" FileVersion ("$(Major).$(Minor).$(Build).$(Revision)")" />
<FileUpdate Files="#(Files)" Regex="FileVersion\("(\d+)\.(\d+)\.(\d+)\.(\d+)"\)" ReplacementText="FileVersion("$(Major).$(Minor).$(Build).$(Revision)")" />
</Target>
I have a VersionInfo.txt file located in:
C:\Source Control\MISTP\Main\DB\SSMS\MyProject\Build
Which simply contains the string: 1.2.3.4
However, this doesn't seem to actually do anything when I Build and/or Publish the project within VS. What am I missing?!
I'm new to MSBuild, but the syntax appears correct - and is largely lifted from the codeproject article - and the path to the file are ok.
It feels like the xml is not being executed, but I'm assuming that it's very presence in the .sqlproj file will result in it being executed.
Thanks
The target isn't triggered during the build. Update "SetVersion" target as following:
<Target Name="SetVersion" DependsOnTargets="GetVersion" AfterTargets="PostBuildEvent">

How can I create a cycle of compiling my project with incrementing of some option?

I wrote a C# template for creating of the .Net extensions for AutoCAD. Before, for each AutoCAD version it is was necessary to point the individual referenses set, the output directory, the target .Net Framework Platform, etc. Exist many versions of AutoCAD: AutoCAD 2009, 2010, ..., 2015. Now my template do it instead of me. My csproj-file has the CAD_Year property:
<PropertyGroup>
<CAD_Year>2013</CAD_Year>
<Min_Year>2009</Min_Year>
<Max_Year>2015</Max_Year>
</PropertyGroup>
When I change CAD_Year value (manually edit this option in the csproj-file) - all settings of my project do change too according target AutoCAD version. It works fine.
But I need to compile my code for all versions of AutoCAD always... It is inconvenient to change the CAD_Year every time for this... :(((
How can I create the cycle of compiling my project for the versions Min_Year, ..., Max_Year when I press the Rebuild Solution menu item?
Thank you, #stijn. I will mark your answer as a solution. Here I create an "answer" for the code highlighting. My current code works:
<!-- Redefine the CoreClean target, otherwise MSBuild will remove all results
of building except for the last. -->
<Target Name="CoreClean">
<ItemGroup>
<AllFiles Include="$(OutputPath)\*.*" />
</ItemGroup>
<Copy SourceFiles="#(AllFiles)" DestinationFolder="$(OutputPath)\temp" />
</Target>
<Target Name="BatchRebuild">
<ItemGroup>
<CADYearsItem Include="$(BuildFor)" />
</ItemGroup>
<Msbuild Projects="$(MsBuildThisFile)" Targets="Rebuild" Properties="CAD_Year_Platform=%(CADYearsItem.Identity)" />
<ItemGroup>
<AllFilesBack Include="$(OutputPath)\temp\*.*" />
</ItemGroup>
<Move SourceFiles="#(AllFilesBack)" DestinationFolder="$(OutputPath)" />
<!-- Doesn't work for Debug. The $(OutputPath)\temp\ will not removed.
But it work for Release.-->
<RemoveDir Directories="$(OutputPath)\temp\" />
</Target>
I see, the RemoveDir task doesn't work for the Debug for me, but it is not a big problem. Now my template is complete, and I will do refactoring of this. Thank you very much!
If you add this to your project file:
<ItemGroup>
<CADYears Include="2013;2014;2015"/>
</ItemGroup>
<Target Name="BatchRebuild">
<Msbuild Projects="$(MsBuildThisFile)" Targets="Rebuild" Properties="CAD_Year=%(CADYears.Identity)"/>
</Target>
and call
msbuild <path_to_projectfile> /t:BatchRebuild
on the commandline, it will build path_to_projectfile 3 times each with a different CAD_Year property.
To get this invoked by VS is trickier since you need to override the Rebuild target, but this for instance works for VS2013 (Actualrebuild target was copied from the Rebuild target in C:\Program Files (x86)\MSBuild\12.0\Bin\Microsoft.Common.CurrentVersion.targets):
<ItemGroup>
<CADYears Include="2013;2014;2015"/>
</ItemGroup>
<Target Name="ActualRebuild"
Condition=" '$(_InvalidConfigurationWarning)' != 'true' "
DependsOnTargets="$(RebuildDependsOn)"
Returns="$(TargetPath)"/>
<Target Name="BatchRebuild">
<Msbuild Projects="$(MsBuildThisFile)" Targets="ActualRebuild" Properties="CAD_Year=%(CADYears.Identity)"/>
</Target>
<Target Name="Rebuild">
<Msbuild Projects="$(MsBuildThisFile)" Targets="BatchRebuild"/>
</Target>
Edit
Since the template system in VS tries to copies ItemGroups it finds in the project root (which seems like a bug to me, or at the least a very annoying feature) you can work around that by using a property and converting it into an item when needed:
<PropertyGroup>
<CADYears>2013;2014;2015<CADYears/>
</PropertyGroup>
<Target Name="BatchRebuild">
<ItemGroup>
<CADYearsItem Include="$(CADYears)"/>
</ItemGroup>
<Msbuild Projects="$(MsBuildThisFile)" Targets="Rebuild" Properties="CAD_Year=%(CADYearsItem .Identity)"/>
</Target>
Note: in the project you posted in the link you are invoking the Rebuild target in the Afterbuild target. I didn't try it, but that will almost certainly lead to infinite recursion. So you should stick to a solution like posted above with a seperate target.

Visual Studio 2010 custom output path for references

I have VS2010 project with several third-party references. Is there any way to automatically output these references to $(OutputPath)\Libraries instead of just $(OutputPath)?
Right now I have a custom AfterBuild target which looks like this,
<Target Name="AfterBuild">
<ItemGroup>
<LibFiles Include="$(SolutionDir)\lib\dotnetzip-1.9\Release\Ionic.Zip.dll" />
<LibFiles Include="$(SolutionDir)\lib\ninject-2.2.0.0\Ninject.dll" />
<LibFiles Include="$(SolutionDir)\lib\nlog-2.0.0.2000\NLog.dll" />
<LibFiles Include="$(SolutionDir)\lib\nlog-2.0.0.2000\NLog.Extended.dll" />
</ItemGroup>
<Copy SourceFiles="#(LibFiles)" DestinationFolder="$(OutputPath)\Libraries" />
</Target>
However this gets tiring since I have to manually add references to #(LibFiles) when adding a reference in VS.
Is there an easier way?
Try to do it this way:
<ItemGroup>
<LibFiles Include="$(SolutionDir)\lib\**\*.dll" />
</ItemGroup>
<Target Name="AfterBuild" Inputs="#(LibFiles)">
<Copy SourceFiles="#(LibFiles)" DestinationFolder="$(OutputPath)\Libraries" />
</Target>
Pros:
you don't have to modify AfterBuild target everytime you add new
reference into your projects
libraries are copied only once or if datetime of any of files in
#(LibFiles) is changed (after update)
Cons:
you will have more dlls in Libraries folder, I guess. But you can filter them using Exclude="$(SolutionDir)\lib\**\Debug\*.dll" for example

Resources