Msbuild pack ignores nuget packages with targets and task - visual-studio

I have ProjectA and ProjectB
ProjectA
Is a dotnet standard project with output and exe file to be used as as tool.
This generates a nuget package on build, using property and property to mark the package as a tool.
It also is marked as to auto exclude from projects which installs the nuget package, when they them also generate a nuget package.
<Project Sdk="Microsoft.Net.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<AssemblyName>ProjectA</AssemblyName>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IsTool>true</IsTool>
<DevelopmentDependency>true</DevelopmentDependency>
</PropertyGroup>
<ItemGroup>
<None Include="build\ProjectA.props" Pack="True" PackagePath="build" />
<None Include="build\net461\ProjectA.targets" Pack="True" PackagePath="build" />
</ItemGroup>
<PropertyGroup>
</Project>
ProjectA.targets
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<GetPackageVersionDependsOn>MyCustomTask;$(GetPackageVersionDependsOn)</GetPackageVersionDependsOn>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
</PropertyGroup>
<Target Name="MyCustomTask">
<!-- Idealy i would use a custom task to set PackageVersion to something different. Like 5.0.99-alpha1+102435 -->
<PropertyGroup>
<PackageVersion>5.0.99-alpha1+102435</PackageVersion>
</PropertyGroup>
</Target>
</Project>
ProjectB
Installs a reference to the nuget package created by ProjectA.
<PropertyGroup>
<TargetFrameworks>net461</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ProjectA" Version="1.0.0" PrivateAssets="all" />
</ItemGroup>
When building ProjectB, the following files are genearted in obj/ folder:
.csproj.nuget.g.props
.csproj.nuget.g.targets
These files include import to ProjectA targets and props from the nuget package cache at:
%userprofile%.nuget\projecta\1.0.0\build\ProjectA.targets etc
Msbuild -t:pack ignores these imports when running, and hence the build ProjectB never sets the PackageVersion to 5.0.99-alpha1+102435 as i would expect.
Adding the above content of ProjectA.targets directly into ProjectB.csproj works.
Anyone have any suggestions what I'm missing or if the might be an issue?

If you use *.targets to pack the nuget project into a nuget package based on that file, you should make this file recognized along with csproj file.
Based on the description, it seems that you used that targets file to pack your nuget project into a version 5.0.99.
So you should make your csproj to work along with that file like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<!--the path of the *.targets file-->
<Import Project="xxx\xxx.targets"/>
</Project>
Update 1
clean nuget caches or delete all files under C:\Users\xxx\.nuget\packages) and msbuild -t:pack ProjectB.csproj to check it.
Update 1
Instead, modify Project B.csproj file:
use <TargetFramework>net461</TargetFramework> instead of <TargetFrameworks>net461</TargetFrameworks>.
The multi-platform framework seems to break the performance of the targets file, I'm also curious why this is happening. But this is the tip. You should use that.

Related

Why am I getting warning NU1507 with Central Package Management even with Package Source Mapping set?

I'm trying out the new Central Package Management feature in NuGet.
As stated in the documentation, if you use multiple NuGet sources (which I use), it is now required to configure the Package Source Mapping in a nuget.config file, otherwise you should get a NU1507 warning.
My simplified file tree is this:
MySolution.sln
Proj1.csproj
MyLibrary\ (this is an external repository with all my shared projects)
Directory.Packages.props
nuget.config
Proj2\ (an example library project)
Proj2.csproj
Here is the Proj1.csproj content:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<PropertyGroup>
<DefaultItemExcludes>$(DefaultItemExcludes);MyLibrary\**\*</DefaultItemExcludes>
</PropertyGroup>
</Project>
Here is Directory.Packages.props:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="JetBrains.Annotations" Version="2022.1.0" />
</ItemGroup>
</Project>
Here is nuget.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear/>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json"/>
<add key="Custom NuGet" value="https://nuget.mydomain.net/"/>
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*"/>
</packageSource>
<packageSource key="Custom NuGet">
<package pattern="MyNamespace.*"/>
</packageSource>
</packageSourceMapping>
</configuration>
and Proj2.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
In the official documentation the MyLibrary\nuget.config should be used when restoring the packages because it is in the parent directory of Proj2.csproj, and actually it works when I build the solution via dotnet build, but when I build it in Visual Studio I still get the NU1507 warning.
I know that I could work around the issue by copying the nuget.config file in the same directory as the solution file (the warning goes away), but I have hundreds of projects that include the MyLibrary directory and I'd like to keep all the configuration centralized.
Moreover it works as expected when it's built from the command line, so what am I doing wrong?
Everything seems like correct from your configure file.But MS doc has mentioned this note:Central package management is in active development. You can provide any feedback you may have at Nuget/Home
Here’re suggestions you can have a try to check :
1 check your visual studio version and this feature is available for Visual Studio 2022.17.2 and later.Because older tooling will ignore central package management configurations and features.See more information please refer to doc:CPM
2 If you are unable to onboard to Package Source Mapping but want to use central package management, can suppress the warning. Please refer to doc:
<PropertyGroup>
<NoWarn>$(NoWarn);NU1507</NoWarn>
</PropertyGroup>
3 use dotnet nuget list source to get package source list and check whether they are enabled.

How do you specify common package versions in a common.props file?

We've got a simple common.props file that is basically doing what Directory.Build.props appears to do. It specifies various settings and is then imported to the csproj files that need it.
Here's the basics of the MyProject.common.props file:
<Project>
<PropertyGroup>
// some settings
</PropertyGroup>
<PropertyGroup>
<LangVersion>8.0</LangVersion>
//some more settings...
<MyNewVariable>3.1.22</MyNewVariable>
</PropertyGroup>
</Project>
Here's the basics of the csproj:
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\folderA\MyProject.common.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="$(MyNewVariable)" />
</ItemGroup>
</Project>
When I use this approach I get the error:
Project dependency Microsoft.Extensions.Hosting.WindowsServices does
not contain an inclusive lower bound. Include a lower bound in the
dependency version to ensure consistent restore results.
However, if I just input 3.1.22 where the variable is it works fine. This suggests to me that the variable is coming through empty or not being picked up. If I remove the variable altogether and leave the Version blank I get the same error. I also double checked the path to the file by misspelling the import and I get a different error, so I know it can at least find the common.props file.

build project without runtimes folder but included nuget packages in nopcommerce 4.40(.net 5 and VS 2019)

I'm developing plugin for nopcommerce,
I'm using VS 2019 and nopcommerce 4.40.4(.net 5)
I should use a nuget package in my plugin,
If I set CopyLocalLockFileAssemblies to true, when I build my project, it created runtimes folder, which is about 65 MB,
If I set CopyLocalLockFileAssemblies to false, it does not create runtimes folder, but, the dll of nuget package which I should use, not included in the build folder,
would you please help me about this?
Note: set copy local to no, make no difference when I change for Nop.Services which I use in the project
this is my csproj and my the package is > SmsIrRestful.NetCore :
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<OutputPath>..\..\Presentation\Nop.Web\Plugins\AttributeStockSMS</OutputPath>
<OutDir>$(OutputPath)</OutDir>
<!--Set this parameter to true to get the dlls copied from the NuGet cache to the output of your project.
You need to set this parameter to true if your plugin has a nuget package
to ensure that the dlls copied from the NuGet cache to the output of your project-->
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<ItemGroup>
<ClearPluginAssemblies Include="$(MSBuildProjectDirectory)\..\..\Build\ClearPluginAssemblies.proj" />
</ItemGroup>
<ItemGroup>
<None Remove="plugin.json" />
</ItemGroup>
<ItemGroup>
<Content Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="SmsIrRestful.NetCore" Version="1.1.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Libraries\Nop.Services\Nop.Services.csproj">
<Private>false</Private>
</ProjectReference>
</ItemGroup>
<Target Name="NopTarget" AfterTargets="Build">
<!-- Delete unnecessary libraries from plugins path -->
<MSBuild Projects="#(ClearPluginAssemblies)" Properties="PluginPath=$(MSBuildProjectDirectory)\$(OutDir)" Targets="NopClear" />
</Target>
</Project>
Instead of using NuGet reference, include the dll file of that NuGet package. For example, You are going to use TaxJar library then follow these steps.
Added NuGet reference.
Right click on NuGet package and go to properties.
Copy path value from property values as below.
Go to that path in file explorer.
Find dll file(s) from there, copy-paste into your plugin folder and add refence.
Repeat same procedures for dependent packages also (if any).
Mark as Copy Local to Yes from properties.

Overwrite targets post build event of another targets file

I have a NuGet package that adds to my C# projects a targets file with that content for the post build event.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="ThisIsMyTarget" AfterTargets="Build">
<Exec Command="ThisIsMyCommand.exe"/>
</Target>
</Project>
In less than 1% of the projects, I don't need this command, I need another command to be executed. Is it possible with the targets files to suspend a target from another targets file?
Something like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="ThisIsMyRareTarget" Overwrite="ThisIsMyTarget" AfterTargets="Build">
<Exec Command="ThisIsMyRareCommand.exe"/>
</Target>
</Project>
I don't want to split my NuGet package only for the 1% of the project.
All the credit goes to #Perry Quian-MSFT. He had right concept.
I have created 2 targets files / NuGet packages.
The standard package using this targets file.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="ThisIsMyTarget" AfterTargets="Build" Condition="'$(DontUseStandardCommand)'!='true'">
<Exec Command="ThisIsMyCommand.exe"/>
</Target>
</Project>
The rare case targets file looks like this.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="ThisIsMyRareTarget" AfterTargets="Build">
<Exec Command="ThisIsMyRareCommand.exe"/>
</Target>
<PropertyGroup>
<DontUseStandardCommand>true</DontUseStandardCommand>
</PropertyGroup>
</Project>
This is working fine in my setup, also independent of the order of the import targets files in the project file.
Overwrite targets post build event of another targets file
Actually, if you want to use multiple target files into nuget package and then switch between different target files depending on your needs, you cannot get what you want.
And as this document said, when you want to add a target file into a project by nuget, you should make sure the name of the target file is the same as the name of the nuget package so that it will work.
So you can only use one target file named <package_id>.targets and set a condidtion in it to distinguish which project environment to use.
Solution
1) please put these all in your <package_id>.targets file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Flag></Flag>
</PropertyGroup>
<Target Name="ThisIsMyTarget" AfterTargets="Build" Condition="'$(Flag)'!='true'">
<Exec Command="ThisIsMyCommand.exe"/>
</Target>
<Target Name="ThisIsMyRareTarget" AfterTargets="Build" Condition="'$(Flag)'=='true'">
<Exec Command="ThisIsMyRareCommand.exe"/>
</Target>
</Project>
2) then you pack your nuget project and when you install your nuget in other projects(less than 1% of the projects), you should define the property Flag to true on the end of the xxx.csproj file like this:
And less than 1% projects can use ThisIsMyRareTarget.exe. In other 99% projects, you should not define Flag property in xxx.csproj and it will automatic capture ThisIsMyCommand.exe.

PackageReference condition is ignored

In my VS 2017 project I reference docfx.console package and I want it to be used only when certain condition is met. But the package gets used for all builds.
Here is a part of my project. I want docfx.console to be used when configuration is Installer/AnyCPU and VS is building net40 flavor.
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net40;netstandard1.3;netstandard2.0</TargetFrameworks>
<!-- ... -->
<Configurations>Debug;Release;Installer</Configurations>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)'=='net40' ">
<!-- ... -->
<PackageReference Include="docfx.console" Version="2.30.0" Condition="'$(Configuration)|$(Platform)'=='Installer|AnyCPU'" />
</ItemGroup>
<!-- ... -->
</Project>
Is there a way to use docfx.console in Installer build for net40 only?
To summarize, even with the condition "false", the package will be imported.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp3.0;netcoreapp2.2;net472</TargetFrameworks>
<Platforms>x64;x86</Platforms>
</PropertyGroup>
<ItemGroup Condition="false">
<PackageReference Include="MyPackage" Version="1.0.0" />
</ItemGroup>
</Project>
We found that we can work around this issue by putting the packagereference in a different file, and making the import of the file conditional.
Separate file: packagerefs.targets
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="MyPackage" Version="1.0.0" />
</ItemGroup>
</Project>
Project file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp3.0;netcoreapp2.2;net472</TargetFrameworks>
<Platforms>x64;x86</Platforms>
</PropertyGroup>
<Import Project="packagerefs.targets" Condition="false" />
</Project>
Even i was looking for referencing nuget packages on condition based (load only when expected constant is set in DefineConstants). Though #Luke Schoen Solution worked for me, I could make it work without the external targets file.
Solution is to include your PackageReference using Choose > When
Make sure to have this block after your PropertyGroup which has DefineConstants.
<Choose>
<When Condition="$(DefineConstants.Contains('My_CONST'))">
<ItemGroup>
<PackageReference Include="MyPackage">
<Version>1.0.6</Version>
</PackageReference>
</ItemGroup>
</When> </Choose>
PackageReference condition is ignored
This is an known issue about the new style csproj PackageReference to work with content/Tools files in a nuget package.
In the package docfx.console, it looks like docfx.console has "content", "build" and "tools" without .NET code in it, just random files:
In this case, when we install this nuget package, nuget does not do anything. So it seems gets used for all builds. That because:
NuGet packages that work with Packages.config, don't always work in
transitive NuGet environments (projects using Project.json or
PackageReferences). Packages that work in transitive NuGet
environments must use "contentFiles" instead of "content" -- you can
have both, if a package would like to work in both environments. Also,
install.ps1/uninstall.ps1 doesn't execute in transitive NuGet
environments -- however, init.ps1 will work in both Packages.config
and transitive environments.
At this moment, there is not a perfect solution, so the issue 4837 is still open.
To resolve this issue, the NuGet docfx.console package needs to be changed to use contentFiles and define targets and would typically reference a tool by using $(MSBuildThisFileDirectory)..\tools\MyTool.exe. If you put this PackageName.targets file into the a build directory, it will be automatically included into the project referencing the NuGet package.
Hope this helps.
In my case I had also same kind of problem - but root cause was that some of msbuild properties were not defined when nuget package building was performed - in particular $(SolutionName) was not defined. Condition still gets evaluated, only it's returning true for some reason. (You can test this by putting Condition="false" - it will be omitted).
Solution for me was to check if property is defined, for example like this:
<ItemGroup Condition="'$(SolutionName)' != '' and $(SolutionName.Contains('SolutionCustomTag'))">
<Reference Include="...">
Te first statement '$(SolutionName)' != '' and - tests that property is defined.

Resources