-
Notifications
You must be signed in to change notification settings - Fork 252
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
Allow an upper limit Version for ProjectReference references in nupkg from dotnet pack
to support semver
#5556
Comments
@rohit21agrawal Can you please take a peek and add appropriate labels/milestone? |
This is something that I would like to see happen as well. Currently, PackageReferences support the full NuGet interval notation, but ProjectReferences don't. Because of this, the only way to have packages with the intended version range is to fall back to specifying a manually created nuspec, negating most of the benefits of the new project-based packaging. |
@emgarten do you think restore can start putting these ranges into the assets file? |
@rohit21agrawal If #4790 (comment) is considered, would it still make sense to be looking to add these to the assets file? This issue and #4790 are big pain points for me! |
aah yes, i forgot about #4790 . let me tackle that first, we'll design this when we are done with that (or while doing that if it makes more sense). |
This is relevant for those developing frameworks. If I could specify semver for pack, I could publish an entire framework worth of libraries in a single command.
where I'm probably oversimplifying this, but this is what I'm after right now. |
@rohit21agrawal Now that #4790 is done and this doesn't appear to have been part of it like you suggested it might be, do you have any idea on when this might be prioritized? |
@nkolev92 this is the issue I was discussing with you this morning, would love your thoughts on this. |
In a similar, but different vein: On some solutions with several shared components which are developed somewhat independently and reused among various other solutions, in development we use floating versions (e.g. "2.0.100.*") against a local packages directory and internal NuGet server (local packages increment from 5000 up, build server goes from 0-4999) when restoring packages to ensure we use the latest local build first, otherwise the latest from the build server. Build chains use the explicit version from their build chain dependencies when restoring so they aren't particularly impacted by this design; this is primarily employed in the developer experience. We have found that it would be really nice to be able to use the same floating version logic in from PackageReference Restore transitively for these projects via the Pack-generated nuspec dependencies (even if "take the latest version" was a feature that projects needed to opt-in to). In order to ensure we restore latest we must specify every transitive reference explicitly, which then make them all non-transitive dependencies. The generated nuspecs always resolve to the exact version that was resolved prior to build and the other supported version range functionality always takes the lowest matching version which is the exact opposite of what we want for development purposes against our own packages. As an aside, it seems with older project formats Visual Studio 2017 still complains about using MSBuild variables for PackageReference versions, which is how we currently control a lot of this in a shared manner. This is just an added inconvenience as we have to rely on other tools to call nuget/msbuild restore via "command line" even while operating within the IDE. This issue appears to go away when all projects in a solution use the new format and we are able to handle most of this issue as long as we always build every solution that generates nupkgs in dependency order, even if it hasn't changed (otherwise some projects/packages report version conflicts). |
We'd find this issue very useful too. We version all the projects in a solution independently and we'd like to be able to specify both a lower version and especially an upper version for project references else it doesn't work well with semantic versioning If not that, then a way of picking up local projects in the solution via a ProjectReference would be good |
Is there currently any way to specify exact version |
@jainaashish @nkolev92 @dsplaisted @nguerrera @livarcocc @Pilchie - we should likely discuss this request in a Monday sync. |
For us the ideal config would be just a flag to use SemVer from the current version and then the referencing project wouldn't have to change if the package major version gets bumped. So something like:
would cause it to pick the current version from the referenced project and pick the next major version as the exclusive upper limit |
We have a similar problem for our versioning: we are building multiple packages using different builds for the same repo. Projects are referencing each other using ProjectReference, and our build system set the third part of the version number based on azure devops BuildID. We a trying to respect SemVer, in the sense that we update the minor & major version number when breaking or non trivial changes are detected. By default, the pack command generate dependencies using the three parts of the version (>=1.0.0). The problem in our case is that the "1.0.0" version do not exists by itself, only 1.0.x where x is an arbitrary build number. When building projects, nuget complains about non existing version (warning) : "NU1603: xxx 1.9.52870 depends on yyyy (>= 1.8.0) but yyyy 1.8.0 was not found. An approximate best match of yyyy 1.8.52292 was resolved." Example:
When editing a nuspec file by hand, I am able to define a package dependency as >=1.0.*. I tried overriding the GetPackageVersionDependsOn target to force the PackageVersion to "1.0", but a padding zero is added in the final nuspec (I think related to the parsing used). We can't define a floating PackageVersion, because it can't be parsed as a valid version by PackTask and raise an error. I think the solution proposed in #7213 could be used in our case. Or instead of customizing the ProjectReference element for each project with a ProjectReference, maybe we could allow the dependency project to define how the dependency version range should be generated ? By allowing the PackageVersion to be a range instead of only a version number, or creating a new Property used to define the version range ? Allowing the PackageVersion to be a range does not seems to be a good idea, as it's also used when building the project itself. Adding a new property PackageDependencyVersion (defaulting to PackageVersion when not set), and updating the parsing to first try parsing the value as a range, then as a version could allow this usage. Also, defining a new Property in the dependency project can allow this property to use other properties from the project itself (Version, PackageVersion, etc.) to generate its value: Perhaps both solution could be set: first using a new ProjectReference metadata, then using the referenced project properties? Ultimately, what we want is the possibility to define the dependency version range as "1.8.*" instead of "1.8.0" in the project (ProjectB in my case). Edit: I just created a small POC with one solution. |
Any update on this? |
@tibel helped out with a change that would enable some extra customization at pack time even if it does not address the core problem here. |
This is a great work-around for now. |
I was trying to modify this so I could add a property called MajorVersion=true with the outcome of |
Voting up. 6 years and we still lack this functionality.
czw., 24 sie 2023 o 08:36 jonatansver ***@***.***> napisał(a):
… Building on @nkolev92 <https://github.com/nkolev92> and @madelson
<https://github.com/madelson>'s approach and a bit of MSBuild item
trickery, I was able to 'extend' ProjectReference with two attributes,
PackageVersion and ExactVersion:
<Project Sdk="Microsoft.NET.Sdk" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<ItemGroup>
<ProjectReference Include="..\MyOtherProject1\MyOtherProject1.csproj" PackageVersion="[1.1.0, 2.0.0)" />
<ProjectReference Include="..\MyOtherProject2\MyOtherProject.2csproj" ExactVersion="true" />
</ItemGroup>
...
<Target Name="UseExplicitPackageVersions" BeforeTargets="GenerateNuspec">
<ItemGroup>
<_ProjectReferenceWithExplicitPackageVersion Include="@(ProjectReference->'%(FullPath)')" Condition="'%(ProjectReference.PackageVersion)' != ''" />
<_ProjectReferenceWithExactPackageVersion Include="@(ProjectReference->'%(FullPath)')" Condition="'%(ProjectReference.ExactVersion)' == 'true'" />
<_ProjectReferenceWithReassignedVersion Include="@(_ProjectReferencesWithVersions)" Condition="'%(Identity)' != '' And '@(_ProjectReferenceWithExplicitPackageVersion)' == '@(_ProjectReferencesWithVersions)'">
<ProjectVersion>@(_ProjectReferenceWithExplicitPackageVersion->'%(PackageVersion)')</ProjectVersion>
</_ProjectReferenceWithReassignedVersion>
<_ProjectReferenceWithReassignedVersion Include="@(_ProjectReferencesWithVersions)" Condition="'%(Identity)' != '' And '@(_ProjectReferenceWithExactPackageVersion)' == '@(_ProjectReferencesWithVersions)'">
<ProjectVersion>[@(_ProjectReferencesWithVersions->'%(ProjectVersion)')]</ProjectVersion>
</_ProjectReferenceWithReassignedVersion>
<_ProjectReferencesWithVersions Remove="@(_ProjectReferenceWithReassignedVersion)" />
<_ProjectReferencesWithVersions Include="@(_ProjectReferenceWithReassignedVersion)" />
</ItemGroup>
</Target>
...
</Project>
This useful target can be put into Directory.Build.targets to cover all
projects in your solution.
I was trying to modify this so I could add a property called
MajorVersion=true with the outcome of
[{CurrentVersion},{NextMajorVersion) but with no success.
Wandering if someone managed to achieve that?
—
Reply to this email directly, view it on GitHub
<#5556 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACHV4TYMRBAV56CPRPHA4ELXW3Y5BANCNFSM4DSMN2KQ>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
I've successfully limited any packaged project references to the next major version (e.g. <!-- Use version range on project references (to limit on major version in generated packages) -->
<Target Name="_GetProjectReferenceVersionRanges" AfterTargets="_GetProjectReferenceVersions">
<ItemGroup>
<_ProjectReferencesWithVersions Condition="'%(ProjectVersion)' != ''">
<ProjectVersion>[%(ProjectVersion), $([MSBuild]::Add($([System.Text.RegularExpressions.Regex]::Match('%(ProjectVersion)', '^\d+').Value), 1)))</ProjectVersion>
</_ProjectReferencesWithVersions>
</ItemGroup>
</Target> This is heavily inspired by other examples posted here, but doesn't require adding additional attributes to the Since the above example relies on the private |
Closed https://developercommunity.visualstudio.com/t/When-a-multi-targeting-sdk-style-project/10444225 as a duplicate of this issue. |
Having an upperbound would be very useful |
The workaround in #5556 (comment) works a charm, the generated NuGet package now has a version range. However when actually using the generated package, there still is a runtime dependency on the latest version of the project reference. This means I end up with the runtime error:
Obviously the Looking through the build process, the workaround updates the NuGet dependency versions, but the generated Anyone knows a fix or workaround that does not involve me adding manual binding redirects like the "good old" net framework days? |
I gave all the inputs some more love and added some more operation modes. I also added a duplication check to ensure they get not mixed in a way that would result in multiple ones to be added. This allows all combinations as described by https://learn.microsoft.com/en-us/nuget/concepts/package-versioning?tabs=semver20sort#version-ranges
<Project Sdk="Microsoft.NET.Sdk" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<ItemGroup>
<!-- This uses MyOtherProject1 with [1.1.0, 2.0.0) -->
<ProjectReference Include="..\MyOtherProject1\MyOtherProject1.csproj" PackageVersion="[1.1.0, 2.0.0)" />
<!-- This uses MyOtherProject2 with [x] where x is the project version -->
<ProjectReference Include="..\MyOtherProject2\MyOtherProject2.csproj" ExactVersion="true" />
<!-- This uses MyOtherProject3 with [1,x] where x is the project version -->
<ProjectReference Include="..\MyOtherProject3\MyOtherProject3.csproj" LimitedMinimumVersion="1" MinimumExcluded="false" />
<!-- This uses MyOtherProject4 with [x,2) where x is the project version -->
<ProjectReference Include="..\MyOtherProject4\MyOtherProject4.csproj" LimitedMaximumVersion="2" MaximumExcluded="true" />
<!-- This uses MyOtherProject5 with (,x] where x is the project version -->
<ProjectReference Include="..\MyOtherProject5\MyOtherProject5.csproj" MinimumExcluded="true" />
<!-- This uses MyOtherProject6 with [x,) where x is the project version -->
<ProjectReference Include="..\MyOtherProject6\MyOtherProject6.csproj" MaximumExcluded="true" />
</ItemGroup>
...
<UsingTask TaskName="CheckForDuplicateItems" AssemblyFile="$(MicrosoftNETBuildTasksAssembly)" />
<Target Name="UseExplicitPackageVersions" BeforeTargets="GenerateNuspec">
<ItemGroup>
<!-- Support for LimitedMinimumVersion attribute -->
<_ProjectReferenceWithLimitedMinimumPackageVersion Include="@(ProjectReference->'%(FullPath)')" Condition="'%(ProjectReference.LimitedMinimumVersion)' != '' OR '%(ProjectReference.MinimumExcluded)' == 'true'" />
<_ProjectReferenceWithReassignedVersion Include="@(_ProjectReferencesWithVersions)" Condition="'%(Identity)' != '' And '@(_ProjectReferenceWithLimitedMinimumPackageVersion)' == '@(_ProjectReferencesWithVersions)'">
<ProjectVersion>@(_ProjectReferenceWithLimitedMinimumPackageVersion->'%(LimitedMinimumVersion)'),@(_ProjectReferencesWithVersions->'%(ProjectVersion)')</ProjectVersion>
<MinimumExcluded>@(_ProjectReferenceWithLimitedMinimumPackageVersion->'%(MinimumExcluded)')</MinimumExcluded>
</_ProjectReferenceWithReassignedVersion>
<!-- Support for LimitedMaximumVersion attribute -->
<_ProjectReferenceWithLimitedMaximumPackageVersion Include="@(ProjectReference->'%(FullPath)')" Condition="'%(ProjectReference.LimitedMaximumVersion)' != '' OR '%(ProjectReference.MaximumExcluded)' == 'true'" />
<_ProjectReferenceWithReassignedVersion Include="@(_ProjectReferencesWithVersions)" Condition="'%(Identity)' != '' And '@(_ProjectReferenceWithLimitedMaximumPackageVersion)' == '@(_ProjectReferencesWithVersions)'">
<ProjectVersion>@(_ProjectReferencesWithVersions->'%(ProjectVersion)'),@(_ProjectReferenceWithLimitedMaximumPackageVersion->'%(LimitedMaximumVersion)')</ProjectVersion>
<MaximumExcluded>@(_ProjectReferenceWithLimitedMaximumPackageVersion->'%(MaximumExcluded)')</MaximumExcluded>
</_ProjectReferenceWithReassignedVersion>
<!-- Apply MinimumExcluded attributes -->
<_ProjectReferenceWithReassignedVersion Update="@(_ProjectReferenceWithReassignedVersion)">
<ProjectVersion Condition="'%(_ProjectReferenceWithReassignedVersion.MinimumExcluded)' == 'true'">(%(ProjectVersion)</ProjectVersion>
<ProjectVersion Condition="'%(_ProjectReferenceWithReassignedVersion.MinimumExcluded)' != 'true'">[%(ProjectVersion)</ProjectVersion>
</_ProjectReferenceWithReassignedVersion>
<!-- Apply MaximumExcluded attributes -->
<_ProjectReferenceWithReassignedVersion Update="@(_ProjectReferenceWithReassignedVersion)">
<ProjectVersion Condition="'%(_ProjectReferenceWithReassignedVersion.MaximumExcluded)' == 'true'">%(ProjectVersion))</ProjectVersion>
<ProjectVersion Condition="'%(_ProjectReferenceWithReassignedVersion.MaximumExcluded)' != 'true'">%(ProjectVersion)]</ProjectVersion>
</_ProjectReferenceWithReassignedVersion>
<!-- Support for ExactVersion attribute -->
<_ProjectReferenceWithExactPackageVersion Include="@(ProjectReference->'%(FullPath)')" Condition="'%(ProjectReference.ExactVersion)' == 'true'" />
<_ProjectReferenceWithReassignedVersion Include="@(_ProjectReferencesWithVersions)" Condition="'%(Identity)' != '' And '@(_ProjectReferenceWithExactPackageVersion)' == '@(_ProjectReferencesWithVersions)'">
<ProjectVersion>[@(_ProjectReferencesWithVersions->'%(ProjectVersion)')]</ProjectVersion>
</_ProjectReferenceWithReassignedVersion>
<!-- Support for PackageVersion attribute -->
<_ProjectReferenceWithExplicitPackageVersion Include="@(ProjectReference->'%(FullPath)')" Condition="'%(ProjectReference.PackageVersion)' != ''" />
<_ProjectReferenceWithReassignedVersion Include="@(_ProjectReferencesWithVersions)" Condition="'%(Identity)' != '' And '@(_ProjectReferenceWithExplicitPackageVersion)' == '@(_ProjectReferencesWithVersions)'">
<ProjectVersion>@(_ProjectReferenceWithExplicitPackageVersion->'%(PackageVersion)')</ProjectVersion>
</_ProjectReferenceWithReassignedVersion>
</ItemGroup>
<CheckForDuplicateItems
Items="@(_ProjectReferenceWithExplicitPackageVersion)"
ItemName="_ProjectReferenceWithExplicitPackageVersion"
DefaultItemsEnabled="false"
DefaultItemsOfThisTypeEnabled=""
PropertyNameToDisableDefaultItems="None"
MoreInformationLink="None"
ContinueOnError="false">
</CheckForDuplicateItems>
<ItemGroup>
<!-- Replace all reassigned versions -->
<_ProjectReferencesWithVersions Remove="@(_ProjectReferenceWithReassignedVersion)" />
<_ProjectReferencesWithVersions Include="@(_ProjectReferenceWithReassignedVersion)" />
</ItemGroup>
</Target>
...
</Project> |
@Falco20019's solution gave me an even simpler and more elegant idea which does not need multiple attributes or duplicate checking: <Project Sdk="Microsoft.NET.Sdk" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<ItemGroup>
<!-- This uses MyOtherProject1 with [1.1.0, 2.0.0) -->
<ProjectReference Include="..\MyOtherProject1\MyOtherProject1.csproj" PackageVersion="[1.1.0, 2.0.0)" />
<!-- This uses MyOtherProject2 with [x] where x is the project version -->
<ProjectReference Include="..\MyOtherProject2\MyOtherProject2.csproj" PackageVersion="[~]" />
<!-- This uses MyOtherProject3 with [1,x] where x is the project version -->
<ProjectReference Include="..\MyOtherProject3\MyOtherProject3.csproj" PackageVersion="[1,~]" />
<!-- This uses MyOtherProject4 with [x,2) where x is the project version -->
<ProjectReference Include="..\MyOtherProject4\MyOtherProject4.csproj" PackageVersion="[~,2)" />
<!-- This uses MyOtherProject5 with (,x] where x is the project version -->
<ProjectReference Include="..\MyOtherProject5\MyOtherProject5.csproj" PackageVersion="(,~]" />
<!-- This uses MyOtherProject6 with [x,) where x is the project version -->
<!-- (note that in this case PackageVersion attribute is superfluous
as this is the default behavior of GenerateNuspec) -->
<ProjectReference Include="..\MyOtherProject6\MyOtherProject6.csproj" PackageVersion="[~,)" />
</ItemGroup>
<Target Name="UseExplicitPackageVersions" BeforeTargets="GenerateNuspec">
<ItemGroup>
<_ProjectReferencesWithVersions Condition="'%(FullPath)' != ''">
<PackageVersion>@(ProjectReference->'%(PackageVersion)')</PackageVersion>
</_ProjectReferencesWithVersions>
<_ProjectReferencesWithVersions Condition="'%(Identity)' != '' And '%(PackageVersion)' != ''">
<ProjectVersion>$([System.String]::new('%(PackageVersion)').Replace('~',%(ProjectVersion)))</ProjectVersion>
</_ProjectReferencesWithVersions>
</ItemGroup>
</Target>
...
</Project> I use |
@atykhyy I am completely baffled how @(ProjectReference->'%(PackageVersion)') even works, as it breaks my understanding of batching, but I tested it, and it seems to correctly apply the metadata of ProjectReference to the correct _ProjectReferencesWithVersions item. |
@namerril: The fragment <_ProjectReferencesWithVersions Condition="'%(FullPath)' != ''">
<PackageVersion>@(ProjectReference->'%(PackageVersion)')</PackageVersion>
</_ProjectReferencesWithVersions> batches |
Is it possible to use this workaround with project references conditional on the target framework as well? I tried to get it to work with one of the previously send build targets but |
@JTeeuwissen It is possible, but it is a bit more involved, because the <Target Name="ReturnProjectReferences" Returns="@(ProjectReference)" />
<Target Name="UseExplicitPackageVersions" BeforeTargets="GenerateNuspec">
<!-- collect per-TFM project reference items -->
<MSBuild Projects="$(MSBuildProjectFullPath)" Targets="ReturnProjectReferences"
Properties="TargetFramework=%(_TargetFramework.Identity)">
<Output TaskParameter="TargetOutputs" ItemName="_MergedProjectReferences" />
</MSBuild>
<!-- select project reference items which specify package versions and clean up duplicates -->
<ItemGroup>
<_MergedPackageVersions Include="@(_MergedProjectReferences)"
Condition="'%(_MergedProjectReferences.PackageVersion)' != ''"
KeepMetadata="PackageVersion" KeepDuplicates="false" />
</ItemGroup>
<ItemGroup>
<_ProjectReferencesWithVersions Condition="'%(FullPath)' != ''">
<PackageVersion>@(_MergedPackageVersions->'%(PackageVersion)')</PackageVersion>
</_ProjectReferencesWithVersions>
<_ProjectReferencesWithVersions Condition="'%(Identity)' != '' And '%(PackageVersion)' != ''">
<ProjectVersion>$([System.String]::new('%(PackageVersion)').Replace('~',%(ProjectVersion)))</ProjectVersion>
</_ProjectReferencesWithVersions>
</ItemGroup>
</Target> Note that this will cause |
The approaches you all provided work very good for stable versions, but given the fact that I publish preview (x.y.z-alpha.1 when published from a feature branch, x.y.z-beta.1 when published from the develop branch and x.y.z when published on the main branch) versions as well. Is there anything I could change to also support prerelease versions here? |
According to https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu1103 and https://learn.microsoft.com/en-us/nuget/concepts/dependency-resolution#floating-versions you would need to use |
With that version spec you proposed I always get the following issue: |
@michaelhahn-doka |
But that would mean, that I only support prerelease but no stable versions right because of the missing *? |
No, this should allow for stable versions to be used as well. |
Right. I don't think it is possible to say "use only beta versions in this range of numerical versions" with SemVer. (Mathematically speaking, SemVer version strings are defined to be completely ordered.) This means that separately versioned 'lineages' of packages like that must have a different set of package names: |
From @csMACnz on July 10, 2017 10:20
It would be good to be able to support Semantic Versioning from a csproj
ProjectReference
, like you can with PackageReference. To do this, upper version limits in nuget packages help a lot. You cannot currently do this with ProjectReferences anddotnet pack
.Steps to reproduce
1.2.3
)<ProjectReference Include="..\MyReferencedPackage\MyReferencedPackage.csproj" />
Expected behavior
project version in nupkg has a version range (e.g.
MyReferencedPackage (≥1.2.3 && < 2.0.0)
)Actual behavior
project version in nupkg has the built packages version (e.g.
MyReferencedPackage (≥1.2.3)
)I'm not too worried on the implementation detail of what the xml should look like, but as a for instance:
To produce the reference from the example above of
MyReferencedPackage (≥1.2.3 && < 2.0.0)
(Or if Inclusive is true, then
MyReferencedPackage (≥1.2.3 && ≤ 2.0.0)
)Copied from original issue: dotnet/cli#7113
The text was updated successfully, but these errors were encountered: