How to get version of the installed nuget in MSBuild? - msbuild

I have to show the user version-specific error message (what features would not work based on currently installed nuget version).
Is there a way to detect the version being used of a specific nuget package through MSBuild?
I know a way to search the filesystem for the DLL and detect the version, but this doesn't seem clean solution. Is there something out of the box?

There is a target usable for customisations like this that was previously part of the build in 1.* but is still around for compatibility: ResolvePackageDependencies.
You can use it in msbuild like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.*" />
<PackageReference Include="DasMulli.Win32.ServiceUtils" Version="*" />
</ItemGroup>
<Target Name="PrintPackageReferences" DependsOnTargets="RunResolvePackageDependencies">
<Message Text="Dependencies:%0A #(PackageDefinitions->'%(Name), Version: %(Version)', '%0A ')" Importance="High" />
</Target>
</Project>
Which (at the time of writing) produces:
> dotnet msbuild -restore -t:PrintPackageReferences -nologo
Restore completed in 14.56 ms for C:\demos\testcons\testcons.csproj.
Dependencies:
DasMulli.Win32.ServiceUtils, Version: 1.2.0
Newtonsoft.Json, Version: 12.0.2

Related

How to update Microsoft.AspNetCore?

I am running 2.1.0-rc1-final but I noticed that 2.1.1 is now out. I am wondering how to update my code?
When I try to update it in the nuget ui manager it says "blocked by project"
Make sour you have a recent version of the .NET Core SDK installed (currently 2.1.301) and no explicit version set for the metapackage you are using:
<Project Sdk="Microsoft.NET.Sdk.Web">
…
<ItemGroup>
<!-- No Version needed -->
<PackageReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
</Project>

How to add <frameworkAssembly> into nuget package generated from csproj

I'm using VS2017 and the new csproj file format for creating nuget packages.
In my csproj file I have the following:
<ItemGroup>
<Reference Include="System.Net" />
</ItemGroup>
Which works fine (TargetFrameworks == net45) when you build it. But when I pack it in nuget package I want the target package to have it as
<frameworkAssemblies>
<frameworkAssembly assemblyName="System.Net" targetFramework="net45" />
</frameworkAssemblies>
How can I do that with this new tooling?
This is limitation of the current 1.0.* tooling. In the upcoming versions 1.1.* and 2.0.* versions of the ".NET SDK", this will be done automatically, with all <Reference> elements being added as framework assemblies to the resulting NuGet package (unless they are marked with Pack="false"). These changes will also be part of VS 2017 15.3 (not released yet at the time of writing). Note that i am talking about the tools (dotnet --version with SDK installed) version, not the .NET Core runtime versions.
There is a way to use the current preview packages of the pack targets, overriding the ones provided by the SDK - note that this is quite a hacky way and should be removed once you use the new 1.1 or 2.0 tooling.
<Project>
<PropertyGroup>
<NuGetBuildTasksPackTargets>junk-value-to-avoid-conflicts</NuGetBuildTasksPackTargets>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<!-- All your project's other content here -->
<ItemGroup>
<PackageReference Include="NuGet.Build.Tasks.Pack" Version="4.3.0-preview1-4045" PrivateAssets="All" />
</ItemGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>
Also see the related GitHub issue on the NuGet repo where the information for this workaround originated from.

.NET Core msbuild ProjectReference

I have a solution that contains a console application with a .csproj file like the this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
</Project>
I also have a library project that uses the console application to generate a heap of C# code that get compiled into the library, the library .csproj file looks like this.
<Project Sdk="Microsoft.NET.Sdk" InitialTargets="RunGenerator">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../generator/generator.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<Target Name="RunGenerator">
<Exec Command="dotnet run -p "../generator/generator.csproj" input output" />
</Target>
</Project>
This fails because the dependency analysis says that a netstandard1.4 assembly cannot reference a netcoreapp1.1 assembly. That is correct except that I am not referencing the assembly.
I can work around that issue by building the generator project like this:
<Project Sdk="Microsoft.NET.Sdk" InitialTargets="RunGenerator">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
</PropertyGroup>
<Target Name="RunGenerator">
<Exec Command="dotnet build "../generator/generator.csproj"" />
<Exec Command="dotnet run -p "../generator/generator.csproj" input output" />
</Target>
</Project>
The problem is that the generator project no longer takes part in the dependency analysis when these projects are built using the containing solution file and the explicit build of the generator project sometimes runs concurrently with another build of the same project initiated by the solution build and this results in errors because files are locked etc.
Is it possible to have a project dependency without checking the target framework?
Can anyone suggest a workaround?
Thanks.
Here are some MSBuild tips. You might need to combine a few of these ideas.
You can use your solution file to add an explicit project dependency. See https://learn.microsoft.com/en-us/visualstudio/ide/how-to-create-and-remove-project-dependencies (This question was originally asked here: Visual Studio 2010: How to enforce build order of projects in a solution?). Unfortunately, this is really hard to do if you don't have VS. The format is .sln files is kinda a nightmare.
To avoid the concurrent build issue, use the MSBuild task instead of the Exec task. See https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-task
<Target Name="CompileAnotherProject">
<MSBuild Projects="../generator/generator.csproj" Targets="Build" />
</Target>
dotnet-run invokes "dotnet build" automatically. This is actually problematic in concurrent builds. You can instead add a target to your generator.csproj that runs the app after it has been built. "dotnet filepath.dll" runs the compiled app without building it.
<Target Name="RunCodeGen" AfterTargets="Build">
<Exec Command="dotnet $(AssemblyName).dll input output"
WorkingDirectory="$(OutDir)" />
</Target>

VSTS Continuous Integration: NuGet package are missing on this computer

I am using Visual Studio 2015, i have a solution with 2 different web applications. There exists One Solution and two projects under it. The files related to nuget are distributed as:
Packages Folder is in the Solution Directory
Package.config in each project directory
There is no nuget folder: nuget.config and nuget.exe in my solution.
My Project build and run fine in Visual Studio, but I am facing a problem when using VSTS Continuous Integration with Build Solution Step (which use a build definition that maps to one project) it gives:
This project references NuGet package(s) that are missing on this computer. Use
NuGet Package Restore to download them. For more information, see The missing file is
..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props.
This is MyProject.csproj file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props" Condition="Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" />
<Import Project="..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>
</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{BBD26A49-D1F4-4391-9D60-2469433DEE0D}</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>AdminUI</RootNamespace>
<AssemblyName>AdminUI</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<MvcBuildViews>false</MvcBuildViews>
<UseIISExpress>true</UseIISExpress>
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
<UseGlobalApplicationHostFile />
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
I noticed that it only tries to import the package that generated the error, but it doesn't explicitly import other packages, but it include them as references:
<Reference Include="WebGrease">
<Private>True</Private>
<HintPath>..\packages\WebGrease.1.5.2\lib\WebGrease.dll</HintPath>
</Reference>
<Reference Include="Antlr3.Runtime">
<Private>True</Private>
<HintPath>..\packages\Antlr.3.4.1.9004\lib\Antlr3.Runtime.dll</HintPath>
</Reference>
This also the end of the myProject.csproj file:
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props'))" />
</Target>
I am wondering why it gives this error, taking into consideration that this package is already installed in the previous step "Nugget Install". Why it can't find it? i have spent a lot of time on this issue!
Before you add the build task, you can add nuget install or restore task. This will restore/install the nuget packages on the build agent.
Actually, this worked for me:
Adding the mapping (repository configuration) to the packages folder in the solution
Repository Configuration
Nuget Install Config
Then, it worked like a charm.
Ensure the Package.config file you've mentioned exists in source control.
It may be that everything builds successfully on your machine because Package.config exists there but not in source control for when the continuous integration agent tries to download and build the project.

Get NuGet package folder in MSBuild

I want to call executable tools like NUnit which I manage via NuGet in MSBuild:
<Target Name="Test">
<CreateItem Include="$(BuildCompileDirectory)\*.Tests.*dll">
<Output TaskParameter="Include" ItemName="TestAssemblies" />
</CreateItem>
<NUnit
Assemblies="#(TestAssemblies)"
ToolPath="$(PackagesDirectory)\NUnit.2.5.10.11092\tools"
WorkingDirectory="$(BuildCompileDirectory)"
OutputXmlFile="$(BuildDirectory)\$(SolutionName).Tests.xml" />
</Target>
The problem is that the folder of a NuGet packages is containing the version number of the package. For instance nunit-console.exe is in the folder packages\NUnit.2.5.10.11092\tools. If I update the NUnit package this path will change and I have to update my MSBuild script. That isn't acceptable.
MSBuild doesn't allow Wildcards in directories, so this isn't working:
ToolPath="$(PackagesDirectory)\NUnit.*\tools"
How can I call tools in MSBuild without having to update my build script whenever I update a NuGet package?
You can use MSBuild Transforms to get the relative directory of a specific tool:
<ItemGroup>
<NunitPackage Include="$(PackagesDirectory)\NUnit.*\tools\nunit-console.exe"/>
</ItemGroup>
<Target Name="Test">
<CreateItem Include="$(BuildCompileDirectory)\*.Tests.*dll">
<Output TaskParameter="Include" ItemName="TestAssemblies" />
</CreateItem>
<NUnit
Assemblies="#(TestAssemblies)"
ToolPath="#(NunitPackage->'%(relativedir)')"
WorkingDirectory="$(BuildCompileDirectory)"
OutputXmlFile="$(BuildDirectory)\$(SolutionName).Tests.xml" />
</Target>
The comment of Mike Rosoft links to Patrik Svensson his blog post and it helped me as follows:
Add GeneratePathProperty="true" to PackageReference of the NuGet package you want to have the location of.
Use it as $(PkgPackage_Name) whereby the dots are replaced by an underscore. Note the Pkg prefix.
This example forces Nswag to use the 32 bit dotnet.exe by overwriting the existing NSwagExe_Net60 property. This was necessary on an x86 project.
<PropertyGroup>
<NSwagExe_Net60>"$(MSBuildProgramFiles32)\dotnet\dotnet.exe" "$(PkgNSwag_MSBuild)\tools\Net60\dotnet-nswag.dll"</NSwagExe_Net60>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NSwag.MSBuild" Version="13.16.1" GeneratePathProperty="true">
</ItemGroup>