-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Resolve package references to projects #1151
Comments
This is possible to some degree today. If you have a Some caveats:
@CesarBS got this working for the ASP.NET benchmarks repo: aspnet/Benchmarks#190 |
@dsplaisted It's such a bad experience though. We need something comparable to what |
Getting this feature back would be awesome! With |
@davidfowl I completly agree to you. The developer experience on dnx with project.json was great. especially in the case of debugging into a nuget package from own code. You had just to clone the git repository and set one path in global json. Had to make not a single Change to my project. I couldn't be easier. I can't understand why Microsoft has thrown away such a great feature when all this was already a release candidate and very close to a final release. |
I also created a uservoice to restore this or similar tooling within VS. |
I've been crying out for this for years, https://lernajs.io/ does something very similar (functionality, not implementation obviously) in the node.js world and it works really well. |
For anyone who is looking to try the conditional reference solution accepted in this SO question to workaround this issue, note that conditional references aren't currently supported when doing nuget restores NuGet/Home#4996. Restoring nuget packages from VS GUI seems to work fine though. |
Here is a manual process that works for me:
file: Directory.Build.targets
project csproj snippet
|
Is there a way to replace a package reference with a project reference when the package is referenced both directly and transitively? I'm not finding a way to suppress the transitive DLL reference, so the compiler finds every type both places and nothing compiles. |
You can't for a transitive package reference. You have to lift it to a direct package reference and add ExcludeAssets=All. https://docs.microsoft.com/en-us/nuget/schema/msbuild-targets#replacing-one-library-from-a-restore-graph |
I think that would have done the trick except that this is an old csproj due to needing winforms designers to work, so necessary package references are not transitively referenced through the replacement project reference. |
@natemcmaster you could do something similar to what I do to automatically treat packagereferences as projectreferences. Create a Directory.Build.targets in the root your repositories and add something like this: <!--?xml version="1.0" encoding="utf-8"?-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<!-- Keep the identity of the packagereference -->
<ExtendedPackageReference Include="@(PackageReference)">
<PackageName>%(Identity)</PackageName>
</ExtendedPackageReference>
<!-- Find all package references that are expected to be available as projects -> My convetion: they start with TomSun. -->
<ExtendedPackageReference2 Include="@(ExtendedPackageReference -> StartsWith('TomSun.') )">
<IsTomSunPackage>%(Identity)</IsTomSunPackage>
</ExtendedPackageReference2>
<!-- Filter them by mapping them to another itemGroup using the WithMetadataValue item function -->
<TomSunPackageTemp Include="@(ExtendedPackageReference2 -> WithMetadataValue('IsTomSunPackage', True) )"/>
<!-- Map back to the orginal packagereference identity -->
<TomSunPackage Include="@(TomSunPackageTemp -> '%(PackageName)' )"/>
<!-- Remove the TomSun. prefix -->
<TomSunRepository Include="@(TomSunPackage -> Replace('TomSun.','') )"/>
<!-- Reference them as the projects: My convention here is that the project is located in [RepositoryNameWithoutTomSun]\[PackageName]\[PackageName].csproj -->
<ProjectReference Include="@(TomSunRepository -> '..\..\%(Identity)\TomSun.%(Identity)\TomSun.%(Identity).csproj' )"></ProjectReference>
<!-- Remove the package references that are now referenced as projects -->
<PackageReference Remove="@(TomSunPackage)"/>
</ItemGroup>
<!-- Some logging ... -->
<Target Name="LoggingTarget" BeforeTargets="Build">
<Message Text="@(TomSunPackage)" Importance="high"></Message>
</Target>
</Project>
The result is, that you can keep your packagereferences without any switch/case logic within the projects itself. They will be only converted to project references, based on conventions, when the Directory.Build.targets file is available in some parent directory. In my case the file is in the parent directory of all git repositories and is only available on my dev machine. I'm quite sure that you could extract such a behavior also into a build-nuget-package. Of course your convention is different, but maybe this gives you an idea how u could solve it in your case. What is missing in this example is the check, whether the project is in the current solution. That check is technical possible, but i haven't take over the logic yet from my old target framework that was written for classic MSBuild project files. Could be that this can be done easier now (starting from VS 2017) with targets and a task written in C#. But at least up to VS2015 it was necessary to do that transformation outside of the targets, otherwise the VS UI didn't show the references correctly. |
@TFTomSun your solution helped me alot! You might consider to add In VS2017 it showed me all the packages in the UI. Doing this in the first item was enough for me:
|
Hi, my current workaround is the following, that checks the solution file. <PropertyGroup>
<SolutionContent Condition=" '$(SolutionPath)' != '' and Exists('$(SolutionPath)') ">$([System.IO.File]::ReadAllText($(SolutionPath)))</SolutionContent>
</PropertyGroup>
<ItemGroup Condition="'$(SolutionContent)' != ''">
<MyPackageReferences Include="@(PackageReference)">
<IsInSln>$(SolutionContent.Contains('$([System.String]::Copy('"%(Identity)"'))'))</IsInSln>
<ProjectPath>%(ProjectPath)</ProjectPath>
</MyPackageReferences>
<ToUseProjectRef Include="@(MyPackageReferences -> WithMetadataValue('IsInSln', 'True') )"
Exclude="@(MyPackageReferences -> WithMetadataValue('ProjectPath', '') )">
<PackageName>%(Identity)</PackageName>
<ProjectPath>%(ProjectPath)</ProjectPath>
</ToUseProjectRef>
<ProjectReference Include="@(ToUseProjectRef -> '%(ProjectPath)' )" />
<PackageReference Remove="@(ToUseProjectRef)"/>
</ItemGroup> And add the ProjectPath to package element: <PackageReference Include="GInfoNET.Libs.ErrorHandler" Version="1.0.0">
<ProjectPath>..\..\ErrorHandler\GInfoNET.Libs.ErrorHandler.csproj</ProjectPath>
</PackageReference> I tried to get the project path dynamically from solution file, but without success :( <ToUseProjectRef Include="@(MyPackageReferences -> WithMetadataValue('IsInSln', 'True') )"
Exclude="@(MyPackageReferences -> WithMetadataValue('ProjectPath', '') )">
<PackageName>%(Identity)</PackageName>
<ProjectPathStartIndex>$([MSBuild]::Add(3, $(SolutionContent.IndexOf(',', %(ProjectNameIndex)))))</ProjectPathStartIndex>
<ProjectPathEndIndex>$(SolutionContent.IndexOf('"', %(ProjectPathStartIndex)))</ProjectPathEndIndex>
<ProjectPath>$(SolutionContent.Substring(%(ProjectPathStartIndex), $([MSBuild]::Subtract(%(ProjectPathEndIndex), %(ProjectPathStartIndex)))))</ProjectPath>
</ToUseProjectRef> |
@NitashEU @ManniManfred My current solution determines the project path automatically using regex. It's now completely generic/common and easy to reuse. <PropertyGroup>
<ReplacePackageReferences>true</ReplacePackageReferences>
</PropertyGroup>
<Choose>
<When Condition="$(ReplacePackageReferences) AND '$(SolutionPath)' != '' AND '$(SolutionPath)' != '*undefined*' AND Exists('$(SolutionPath)')">
<PropertyGroup>
<SolutionFileContent>$([System.IO.File]::ReadAllText($(SolutionPath)))</SolutionFileContent>
<SmartSolutionDir>$([System.IO.Path]::GetDirectoryName( $(SolutionPath) ))</SmartSolutionDir>
<RegexPattern>(?<="[PackageName]", ")(.*)(?=", ")</RegexPattern>
</PropertyGroup>
<ItemGroup>
<!-- Keep the identity of the packagereference -->
<SmartPackageReference Include="@(PackageReference)">
<PackageName>%(Identity)</PackageName>
<InSolution>$(SolutionFileContent.Contains('\%(Identity).csproj'))</InSolution>
</SmartPackageReference>
<!-- Filter them by mapping them to another itemGroup using the WithMetadataValue item function -->
<PackageInSolution Include="@(SmartPackageReference -> WithMetadataValue('InSolution', True) )">
<Pattern>$(RegexPattern.Replace('[PackageName]','%(PackageName)') )</Pattern>
<SmartPath>$([System.Text.RegularExpressions.Regex]::Match( '$(SolutionFileContent)', '%(Pattern)' ))</SmartPath>
</PackageInSolution>
<ProjectReference Include="@(PackageInSolution -> '$(SmartSolutionDir)\%(SmartPath)' )"/>
<!-- Remove the package references that are now referenced as projects -->
<PackageReference Remove="@(PackageInSolution -> '%(PackageName)' )"/>
</ItemGroup>
</When>
</Choose>
@ManniManfred |
@TFTomSun thanks for your solution |
Does anyone know if the https://github.com/dotnet/sourcelink project is meant to lend a hand with improving the experience here...? |
@tmitchel2 Not really. SourceLink allows you to embed the location of source files into the PDB, which is particularly useful when the link is to a public URL (such as a specific commit to a file on GitHub). Essentially, SourceLink provides the ability for IDE's such as Visual Studio to get the file for the code you are debugging into, but it does not load the project in your solution and allow you to modify it which is what this issue is about. |
@tmitchel2 to be a bit more specific, project references don't only ensure a nice debugging experience which u can indeed have with packagereferences too....but also think about refactorings over multiple projects...that wouldn't be possible with package references.therefore automatically replacing package references with project references when the source code of the referenced project is pulled still makes sense. |
Hi, I'm looking for a way to also include the .props and the .targets file of the "transfered" project. ...
<!-- Filter them by mapping them to another itemGroup using the WithMetadataValue item function -->
<PackageInSolution Include="@(SmartPackageReference -> WithMetadataValue('InSolution', True) )">
<Pattern>$(RegexPattern.Replace('[PackageName]','%(PackageName)') )</Pattern>
<SmartPath>$([System.Text.RegularExpressions.Regex]::Match( '$(SolutionFileContent)', '%(Pattern)' ))</SmartPath>
<ProjectDir>$(SmartSolutionDir)\$([System.IO.Path]::GetDirectoryName(%(SmartPath)))</ProjectDir>
<PropsToImport Condition="Exists('%(ProjectDir)\build\%(PackageName).props')">%(ProjectDir)\build\%(PackageName).props</PropsToImport>
<TargetsToImport Condition="Exists('%(ProjectDir)\build\%(PackageName).targets')">%(ProjectDir)\build\%(PackageName).targets</TargetsToImport>
</PackageInSolution>
<ProjectReference Include="@(PackageInSolution -> '$(SmartSolutionDir)\%(SmartPath)' )"/>
<!-- Remove the package references that are now referenced as projects -->
<PackageReference Remove="@(PackageInSolution -> '%(PackageName)' )"/>
</ItemGroup>
<PropertyGroup>
<AllPropsToImport>@(PackageInSolution->'%(PropsToImport)')</AllPropsToImport>
<AllTargetsToImport>@(PackageInSolution->'%(TargetsToImport)')</AllTargetsToImport>
</PropertyGroup>
</When>
</Choose>
<Import Project="$(AllPropsToImport)" Condition="'$(AllPropsToImport)' != ''" />
<Import Project="$(AllTargetsToImport)" Condition="'$(AllTargetsToImport)' != ''" /> but without success. The import statement failed. |
@ManniManfred |
@TFTomSun There are several passes of MSBuild evaluation. The first pass evaluates properties and also follows imports. So an import condition can depend on properties which were previously defined. However, items are evaluated in a subsequent phase, so imports can't depend on items. There is some documentation on this under "Property and item evaluation order" here: https://docs.microsoft.com/en-us/visualstudio/msbuild/comparing-properties-and-items The source code which implements this is here: https://github.com/Microsoft/msbuild/blob/master/src/Build/Evaluation/Evaluator.cs |
…0191203.5 (dotnet#1151) - Microsoft.AspNetCore.Analyzers - 5.0.0-alpha1.19603.5 - Microsoft.AspNetCore.Mvc.Api.Analyzers - 5.0.0-alpha1.19603.5 - Microsoft.AspNetCore.Mvc.Analyzers - 5.0.0-alpha1.19603.5 - Microsoft.AspNetCore.Components.Analyzers - 5.0.0-alpha1.19603.5
It seems that Visual Studio 16.8 has regressed the experience of some of these discussed solutions, as the Manage NuGet window is considering the 'project version number' rather than the 'package version number' when resolving the installed version against the package feed. It seems to come from a belief that projects and packages need to be treated the same in most cases. This completely misses the point that projects are completely out of scope when managing installed NuGet packages and so VS isn't currently honouring this: |
Please any workaround for this. can you support switch between ProjectReference and PackageReference. Can you guys please vote to support that feature from following link: I wish you support that feature |
Edited, Now Please what steps should I do during I still get nupkg version not Project Reference DLL, Should I modify csproj after add nuget package? or clean all references before apply script? Please any help for that. |
@softwarekamal just to clarify: The purpose of the script is being able to specify only PackageReferences in the csprojs and have an automatic resolution to ProjectReferences when a project with the specified package name exists in the current solution. The script can be either
important: The script is not intended to be copied into the csproj directly. The purpose is to keep the csprojs clean. |
Hello TFTomSun, Would you mind to look I upload Solution *.rar https://anonfiles.com/h6e529Jdx4/TwoSolution_rar Thank you much dear |
@softwarekamal I don't download and unpack files from unknown sources. But anyways, it looks like your projects are not using the sdk style. I'd suggest to migrate them to sdk style first. Afterwards replace the targetframework net5.0-windows with net48, if you want to keep building your project against the classic .NET Framework (4.8) |
Hello TFTomSun, When you navigate to FooProject.cs you will find Thank you much for great information dear. |
@softwarekamal you can keep building your project against net48 after the sdk migration. Please follow the steps mentioned in the link above to migrate to sdk style. Otherwise the given solution to dynamically switch between project and packagereferences probably won't work for you. At least I never tested it with the old style project format. |
@nagilson I don't see anything having happened to this issue recently warranting a close by the bot, maybe needs to be double checked? your tag changes seems to be the last thing that happened and they don't look like a reason to autoclose? |
Yeah, that seems like a bug with the bot. Thanks for pointing it out, reopened this. Hopefully it does not try to close it again 🙄 |
Thanks for creating this issue! We believe this issue is related to NuGet tooling, which is maintained by the NuGet team. Thus, we closed this one and encourage you to raise this issue in the NuGet repository instead. Don’t forget to check out NuGet’s contributing guide before submitting an issue! If you believe this issue was closed out of error, please comment to let us know. Happy Coding! |
Thanks for creating this issue! We believe this issue is related to NuGet tooling, which is maintained by the NuGet team. Thus, we closed this one and encourage you to raise this issue in the NuGet repository instead. Don’t forget to check out NuGet’s contributing guide before submitting an issue! If you believe this issue was closed out of error, please comment to let us know. Happy Coding! |
I think this issue is at the cross intersection between NuGet and Dotnet Sdk, |
Removing the NuGet label since their bot is a bit zealous :) |
Thanks, don’t forget to re-open the issue |
I think we are in fact asking for a |
In project.json, dependencies could be either a project or a package reference. NuGet and compilation would resolve the package vs project based on some conventions (like folder name) and the global.json file. Futhermore, VS would automatically add new projects to the solution explorer when the were discovered.
In MSBuild, there is no equivalent feature. References are either ProjectReference or PackageReference. Swapping a package reference to a project is not always straight forward. In cases where the package is a transitive reference from other PackageReferences, you have to keep the PackageReference and set ExcludeAssets=All in order to avoid conflicts between ProjectReference and PackageReference. (See https://docs.microsoft.com/en-us/nuget/schema/msbuild-targets#replacing-one-library-from-a-restore-graph). It also requires manually adding new projects to the sln file so they appear in VS.
It would be nice to provide a way to resolve PackageReference to projects without requiring changes to csproj.
Some data on this:
cc @davidfowl @dsplaisted
The text was updated successfully, but these errors were encountered: