We have big projects with a lot of package references (20+) with a lot of branches. Today, merging changes to package references is very hard, because we have to edit to very big files (.csproj and packages.config, and merge conflicts are common.
I propose that when project.json is joined with new new csproj format, that there is a possibility to store package references as files, instead of part of .csproj.
You would have a "packages" folder in your project path. It has a subfolder for each target platform. Each subfolder contains files with extensions "pref". The name of each pref file is "PackageName.pref" and the contents is the package version.
The .csproj would look something like this, including each pref file like this
<Project>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net45:net46</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="packages\**\*.pref" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
This means that to add a package, we just add a file in a folder, and it would just appear in the project. To upgrade the version, we just edit the .pref file. This would make merging a lot easier, and the history of each .pref file would be very clean.
Perhaps a general question about cps, but is this something a target can do? like you'd have a custom target that runs early in the pipeline, and resolved pref files into "regular" project references. (not sure if this works at runtime in vs though, but I'm not a cps expert)
I've started implementing this and several other methods of specifying references over at https://github.com/aL3891/CustomPackageReferences if you're interested. early days but the concept works at least :)
I'm curious, were these merge conflicts a problem with project.json also?
It is actually a C#/VB project system question as we own/control how package references work for managed code.
While @aL3891's approach will work outside Visual Studio - due to our current design inside Visual Studio, where we don't run MSBuild code to gather <PackageReference> items, it's not going to work.
Another approach is to do this advantage of the fact that you can easily share data between projects using targets files and you can import arbitrary targets using a wildcard (note I'm using the new syntax, and haven't tested this - so expect things to not work without fixing it for current public builds):
NewtonSoft.Json.pref:
<Project>
<ItemGroup>
<PackageReference Include="NewtonSoft.Json" Version="5.0.0.0" />
</ItemGroup>
</Project>
MyProject.csproj
<Project>
<Import Project="packages\**\*.pref" />
</Project>
Note, to differ between multiple frameworks, you could can use conditions:
<Project>
<ItemGroup Condition="'$(TargetFramework)'=='net45'">
<PackageReference Include="NewtonSoft.Json" Version="5.0.0.0" />
</ItemGroup>
</Project>
Or use framework-specific directories:
<Project>
<Import Project="packages\$(TargetFramework)\**\*.pref"/>
</Project>
Note, I'm going to treat this as a feature request to actually make @aL3891's approach work inside Visual Studio - this would open up the most flexibility and let you store package references in arbitrary files with whatever syntax you wanted.
Also, sorry for the late reply - I missed this while on holidays.
Happy to see _any_ effort made towards mending the divide here and allowing developers to work in their desired/preferred formats, @davkean. 馃憤 Much appreciated. Thanks again for getting the ball rolling, @aL3891. I am starting to learn the power of simple POC projects to start the inspirations. :)
@davkean Thanks for the detailed reply!
Yes, my approach is a little rough inside VS, but it does work (kind of) if you run dotnet restore from the cli, then the lock files gets written and VS will pick up the changes. it does however require you to unload/reload the project, so not exactly smooth :D
it would be really cool to see VS support this kind of approach! Should I open another issue for this though so as to not highjack this one? (or perhaps there is one already?)
-edit-
Actually, what if VS only listened for changes to the lock file? It could then invoke msbuild /t:restore internally, and when pick up the change to the lock file? I'm sorry if i'm over simplifying, I havent dug super deep into the Roslyn project system code :)
Going to close this out - we support this concept built-in via imports as per above. Since above, however, we also run the CollectPackageReferences target which means that you can run a target to convert your format of the package references into a PackageReference. This is very similar to what Paket does.