-
Notifications
You must be signed in to change notification settings - Fork 255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
design spec for version property in project reference #12348
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
# Allow users to define version ranges for ProjectReferences | ||
|
||
- [Martin Ruiz](https://github.com/martinrrm) | ||
- Start Date (2023-01-01) | ||
- [5556](https://github.com/NuGet/Home/issues/5556) | ||
|
||
# Summary | ||
|
||
Add a `Version` to `ProjectReference` tag in CSPROJ, to allow customers to specify the referenced project version in the `.nupkg` and `nuspec` files when doing a pack command. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about adding another As you can see in the below example, <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ClassLibrary2" Version="[1.0.0, 2.0.0)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ClassLibrary2\ClassLibrary2.csproj" />
</ItemGroup>
</Project> I didn't think about the pros/cons or possibilities of this approach. |
||
|
||
# Motivation | ||
|
||
When using `ProjectReference` there is no option to define a Version to the reference like in `PackageReference` and the version will always be defined as `>= ProjectVersion`, this results in customers manually modifying the nuspec file if they want to declare a different version than the project version. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you be able to add little bit more details on exactly what is the project version here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For project version I mean the value of About For example if ProjectA has a |
||
|
||
# Explanation | ||
|
||
Currently when adding a `ProjectReference` to a project, there is no property to specify which version(s) of it to be used when doing a pack. | ||
When doing a pack to a package with a `ProjectReference` it will always be added as a range, where the minimum version will be the ProjectReference version and with an open maximum version. | ||
|
||
``` | ||
<ItemGroup> | ||
<ProjectReference Include="../MyReferencedPackage/MyReferencedPackage.csproj" /> | ||
</ItemGroup> | ||
``` | ||
|
||
Add a `Version` property to `ProjectReference` and store that value in the assets file when doing a restore so we can retrieve that information when doing a `pack` command. | ||
|
||
|
||
## Considerations | ||
|
||
If there is no `Version` information then the behavior should be the current one. | ||
|
||
If the `Version` is not a range, it will be treated as one, like in `PackageReference`. `Version="2.0.0"` means `Version="[2.0.0, )"` | ||
|
||
Minimum version should not be open to users, and NuGet should always automatically fill in the min version from the project's version, this is for the protection of package consumers. | ||
|
||
--- | ||
## Option 1 - Version with VersionRange | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After doing more investigation (Nikolche also commented about this in #12348 (comment)), I think this option is not possible. I don't think we are aware of the project version during restore, meaning that we will have different information in the assets file and the .nupkg Example: MyReferencedPackage -> v.1.0.9
Will write this the assets file:
And this can cause confusion when pack resolves to another min version. |
||
|
||
### .CSPROJ file | ||
``` | ||
<ItemGroup> | ||
<PackageReference Include="Newtonsoft.Json" Version="[9.0.0, 13.0.2)" /> | ||
<ProjectReference Include="../MyReferencedPackage/MyReferencedPackage.csproj" Version="[1.0.0, 2.0.0)" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the minimum version should not be allowed to be specified on the ProjectReference, and NuGet should always automatically fill in the min version from the project's version. This might not be popular with customers who want this feature, but this is for the protection of package consumers, especially since NuGet only selected minimum version in its dependency resolution graph. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With this I think we can take the customer suggestion and do something like this:
With this in mind, you think saving this as a version range in the assets file (like the initial proposal) or change it to be different properties? Option 1 (I like this one more) :
Option 2:
I currently have an mvp for this in branch There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apparently I forgot to reply to this 😕
That's not valid MSBuild syntax. While project files are XML, MSBuild has a strict syntax, and diverging from it will require changes to MSBuild if we feel stongly enough that it's needed to support a customer scenario (in which case you'd also need to consider what potential bugs it could introduce to non-NuGet scenarios if the syntax was extended). Anyway, MSBuild items are basically NuGet's However, I think another scenario that some customers may want, is to limit the package dependency to the exact version of the project when it's being packed. VersionRange defines this as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @zivkan I like the idea of doing
What if we do something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As per our docs on version ranges,
Yes, that works with MSBuild's syntax. Something to keep in mind, although it's very unlikely, is that customers might have build custom build scripts, so if any customer already uses Customers are more likely to already be familiar with All this to say, I don't have an obvious answer/solution. Get more feedback from others. Perhaps my
How? What would the syntax be for "use the referenced project's version as the max" be? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. magic word maybe?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Today, the I don't love it though. It feels like the most completely way without misuse risk, but it's kind of ugly. |
||
</ItemGroup> | ||
``` | ||
|
||
### Explanation | ||
`Version` will be a version range, but the lower version will be replaced with the actual `Version` as an inclusive min version. | ||
|
||
We will log a warning saying that the min version should be equal to the `ProjectVersion` and it was replaced. | ||
|
||
## Option 2 - Version without defined min version | ||
### .CSPROJ file | ||
``` | ||
<ItemGroup> | ||
<PackageReference Include="Newtonsoft.Json" Version="[X, 13.0.2)"/> | ||
<ProjectReference Include="../MyReferencedPackage/MyReferencedPackage.csproj" Version="[1.0.0, 2.0.0)" /> | ||
</ItemGroup> | ||
``` | ||
|
||
### Explanation | ||
`Version` will be a version range, but the lower version will be defined by a 'X' and replaced with the actual `ProjectVersion` as an inclusive min version. | ||
|
||
### assets file | ||
``` | ||
"frameworks": { | ||
"net6.0": { | ||
"targetAlias": "net6.0", | ||
"projectReferences": { | ||
"C:\\Users\\mruizmares\\source\\repos\\ConsoleApp1\\MyReferencedPackage\\MyReferencedPackage.csproj": { | ||
"projectPath": "C:\\Users\\mruizmares\\source\\repos\\ConsoleApp1\\MyReferencedPackage\\MyReferencedPackage.csproj", | ||
"version": "[1.0.0, 2.0.0)" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will this change be compatible with old version tools? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tested doing a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My guess is that because it's additive it should work. |
||
} | ||
} | ||
} | ||
}, | ||
``` | ||
|
||
### nuspec file | ||
``` | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd"> | ||
<metadata> | ||
<id>ConsoleApp1</id> | ||
<version>1.2.4</version> | ||
<authors>ConsoleApp1</authors> | ||
<description>Package Description</description> | ||
<dependencies> | ||
<group targetFramework="net6.0"> | ||
<dependency id="MyReferencedPackage" version="[1.0.0, 2.0.0)" exclude="Build,Analyzers" /> | ||
<dependency id="Newtonsoft.Json" version="[9.0.0, 13.0.2)" exclude="Build,Analyzers" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May I know if there is any behavior change for package reference? Or will this change only impact project reference? Thanks! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right now, we consider ProjectReferences as packages when doing pack, so this code is shared. Since PackageReference's can handle ranges, I think the behavior is going to be the same. Currently we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to switch from legacy short string to short string? |
||
</group> | ||
</dependencies> | ||
</metadata> | ||
<files> | ||
<file src="C:\Users\mruizmares\source\repos\ConsoleApp1\ConsoleApp1\bin\Debug\net6.0\ConsoleApp1.runtimeconfig.json" target="lib\net6.0\ConsoleApp1.runtimeconfig.json" /> | ||
<file src="C:\Users\mruizmares\source\repos\ConsoleApp1\ConsoleApp1\bin\Debug\net6.0\ConsoleApp1.dll" target="lib\net6.0\ConsoleApp1.dll" /> | ||
</files> | ||
</package> | ||
``` | ||
|
||
### nupkg | ||
``` | ||
Metadata: | ||
id: ConsoleApp1 | ||
version: 1.2.4 | ||
authors: ConsoleApp1 | ||
description: Package Description | ||
Dependencies: | ||
net6.0: | ||
MyReferencedPackage: '>= 1.0.0 && < 2.0.0' | ||
Newtonsoft.Json: '>= 9.0.0 && < 13.0.2' | ||
|
||
Contents: | ||
- File: _rels/.rels | ||
- File: [Content_Types].xml | ||
- File: ConsoleApp1.nuspec | ||
- File: lib/net6.0/ConsoleApp1.dll | ||
- File: lib/net6.0/ConsoleApp1.runtimeconfig.json | ||
- File: package/services/metadata/core-properties/a638a18cb3b1449185ce67e16a13ebaf.psmdcp | ||
``` | ||
|
||
# Drawbacks | ||
|
||
I don't think there are drawbacks to this implementation when doing a `pack` command. For restore I'm not sure if adding a new propert to the assets file can affect the performance. | ||
|
||
# Rationale and alternatives | ||
|
||
## Option 3 - Separate variables for max project version. | ||
### .CSPROJ file | ||
``` | ||
<ItemGroup> | ||
<PackageReference Include="Newtonsoft.Json" MaxProjectVersion="3.0.2" IsMaxProjectVersionInclusive="true" /> | ||
<ProjectReference Include="../MyReferencedPackage/MyReferencedPackage.csproj" Version="[1.0.0, 2.0.0)" /> | ||
</ItemGroup> | ||
``` | ||
|
||
### assets file | ||
``` | ||
"frameworks": { | ||
"net6.0": { | ||
"targetAlias": "net6.0", | ||
"projectReferences": { | ||
"C:\\Users\\mruizmares\\source\\repos\\ConsoleApp1\\MyReferencedPackage\\MyReferencedPackage.csproj": { | ||
"projectPath": "C:\\Users\\mruizmares\\source\\repos\\ConsoleApp1\\MyReferencedPackage\\MyReferencedPackage.csproj", | ||
"MaxProjectVersion": "3.0.2" | ||
"IsMaxProjectVersionInclusive": "true" | ||
} | ||
} | ||
} | ||
}, | ||
``` | ||
|
||
### nuspec file | ||
``` | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd"> | ||
<metadata> | ||
<id>ConsoleApp1</id> | ||
<version>1.2.4</version> | ||
<authors>ConsoleApp1</authors> | ||
<description>Package Description</description> | ||
<dependencies> | ||
<group targetFramework="net6.0"> | ||
<dependency id="MyReferencedPackage" MaxProjectVersion="3.0.2" IsMaxProjectVersionInclusive="true" exclude="Build,Analyzers" /> | ||
<dependency id="Newtonsoft.Json" version="[9.0.0, 13.0.2)" exclude="Build,Analyzers" /> | ||
</group> | ||
</dependencies> | ||
</metadata> | ||
<files> | ||
<file src="C:\Users\mruizmares\source\repos\ConsoleApp1\ConsoleApp1\bin\Debug\net6.0\ConsoleApp1.runtimeconfig.json" target="lib\net6.0\ConsoleApp1.runtimeconfig.json" /> | ||
<file src="C:\Users\mruizmares\source\repos\ConsoleApp1\ConsoleApp1\bin\Debug\net6.0\ConsoleApp1.dll" target="lib\net6.0\ConsoleApp1.dll" /> | ||
</files> | ||
</package> | ||
``` | ||
|
||
### nupkg | ||
``` | ||
Metadata: | ||
id: ConsoleApp1 | ||
version: 1.2.4 | ||
authors: ConsoleApp1 | ||
description: Package Description | ||
Dependencies: | ||
net6.0: | ||
MyReferencedPackage: '>= 1.0.0 && <= 3.0.2' | ||
Newtonsoft.Json: '>= 9.0.0 && < 13.0.2' | ||
|
||
Contents: | ||
- File: _rels/.rels | ||
- File: [Content_Types].xml | ||
- File: ConsoleApp1.nuspec | ||
- File: lib/net6.0/ConsoleApp1.dll | ||
- File: lib/net6.0/ConsoleApp1.runtimeconfig.json | ||
- File: package/services/metadata/core-properties/a638a18cb3b1449185ce67e16a13ebaf.psmdcp | ||
``` | ||
|
||
# Unresolved Questions | ||
|
||
# Future Possibilities |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason, I think
Version
attribute doesn't belong toProjectReference
because it refers to the package version range. Looking at the schema forProjectReference
element https://github.com/dotnet/msbuild/blob/main/src/MSBuild/MSBuild/Microsoft.Build.CommonTypes.xsd#L643-L722, I thinkPackageVersion
might be more appropriate. The schema hasPackage
as sub element forProjectReference
but there are no comments to understand more about its usage. I noticed that there is alsoSpecificVersion
attribute forProjectReference
element to specify whether the exact version of the assembly should be used.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI - There is an MSBuild property
PackageVersion
allowing customers to specify package version in the csproj file if the project is packed as nupkg.https://learn.microsoft.com/nuget/create-packages/package-authoring-best-practices#package-metadata