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.
Related
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.
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.
I have installed AsyncUsageAnalyzers for my common project (which is a NuGet package), and I want the rules to be enforced in all of my packages/applications that depends on it.
I have changed the severity of the UseConfigureAwait rule, and a .ruleset has been created.
I have read on https://stackoverflow.com/a/20271758/2663813 that I needed to have a NuGet package with a given structure. That assertion has been confirmed here https://stackoverflow.com/a/46115423/2663813
What I have now:
build/Common.ruleset
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="My common analyzer rules" Description="The rules that are enforced on all of my projects" ToolsVersion="10.0">
<Rules AnalyzerId="AsyncUsageAnalyzers" RuleNamespace="AsyncUsageAnalyzers">
<Rule Id="UseConfigureAwait" Action="Error" />
</Rules>
</RuleSet>
build/My.Package.Id.targets
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)Common.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project>
In my .csproj
<Import Project="build\My.Package.Id.targets" />
My question is : Now that I've done that and that the rules are correctly applied on my project, how do I add the files under build/ inside the NuGet package, inside the build/ folder? I know that I could do that with a .nuspec file as mentionned in the links, but I'd like to do that with the new .csproj syntax.
Sorry for posting too quickly, I just found the solution right under my eyes:
In the page https://learn.microsoft.com/en-us/dotnet/core/tools/csproj#nuget-metadata-properties , in the comments, MarkPflug gives the translation, and quotes
https://learn.microsoft.com/en-us/nuget/schema/msbuild-targets#pack-scenarios for reference.
In my case, I need to use PackagePath as follows (Content would also work, but as Martin Ullrich pointed out, None is a better choice here):
<ItemGroup>
<None Include="build\**" Pack="True" PackagePath="build\" />
</ItemGroup>
I have this situation:
I have a .proj file in project directory:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Include="PQExtensionTest.pq">
<SubType>Code</SubType>
</Compile>
<Content Include="PQExtensionTest.query.pq">
<SubType>Code</SubType>
</Content>
</ItemGroup>
<!-- <Import Project="..\Directory.Build.props" /> -->
<Import Project="$(aProperty)add.targets" />
</Project>
In the solution directory (..\ from project directory) I have file Directory.Build.props:
<Project DefaultTargets="BuildExtension" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<aProperty>$(MSBuildProjectDirectory)/Subdir/</aProperty>
</PropertyGroup>
</Project>
In the project directory I have subdirectory "Subdir", where there is file add.targets, which contains all the targets I need (do not show it's contains here because it is not relevant to the problem).
So all above has this folder structure:
Solution directory
Directory.Build.props
Project Directory
Project.mproj
Subdir
add.targets
Preparing all the above, I expected that aProperty will be initiated before the import and the import of add.targets will happen without problem. But I get error that imported project is not found, and I see in error message that MSBuild tries to import from project directory, and not from subdirectory Subdir.
If I uncomment this row:
<Import Project="..\Directory.Build.props" />
all works fine.
The only reasonable explanation for me of such behavior is that aProperty is empty at the moment of importing, because explicit import happens before implicit one.
Is there any way to force MSBuild to inexplicitly import Directory.Build.props before any other imports, while work in Visual Studio?
"While in Visual Studio"
For C# and VB language project, we don't need to import
Directory.Build.props manually or force it before other imports.
When creating a new project(C# or VB) in VS, open its proj file we can find the format is like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
...
</PropertyGroup>
<ItemGroup>
...
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
Every time when creating new C# or VB project, the top line within the <Project>node is Import project="Microsoft.Common.props", and we can find the sentence from this document:
When MSBuild runs, Microsoft.Common.props searches your directory structure for the Directory.Build.props file (and Microsoft.Common.targets looks for Directory.Build.targets). If it finds one, it imports the property.
So in visual studio, we don't need to force it before other imports.Its always called after import Microsoft.Common.props, and since the import Microsoft.Common.props is always first line of project node by default, the Directory.Build.Targets is always implicitly imported right after the Microsoft.Common.props and before others.
Note: This feature only supports C# and VB, cause only these two kinds of projects will import the Microsoft.Common.Props in proj file.
And for other kinds of projects, just like your .mproj or
.vcxproj(C++), this feature(Directory.Build.props) is not supported
yet.
So the Directory.Build.Targets or .props is the same as any custom .props. It doesn't make difference between Directory.Build.Targets and anyName.props.
In this way,to read the value in it we have to use import project to call it manually. And that's why the build can't succeed until you uncomment the row:<Import Project="..\Directory.Build.props" />
The way to import properties from 'Directory.Build.props' file from nested folder structure is given below:
Refer: https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019
Note that:
1. These property file definition works from MSBuild tools version 15.0
2. You need to be aware of where to place this import: At the beginning of the file or at the end of the file. Generally it is good to place at the end as nested properties will be visible to parent properties.
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.