Substituting a package (package redirection)

Topics: General
Oct 7, 2013 at 5:36 PM
How can I substitute one package for a a different package?

As a concrete example I ran in to a bug with Microsoft.Bcl.Build. The fix is a small change to the targets file.

After making the change I can create a new package, but at this point I run in to a problem. If I give the new package its own ID (e.g., Custom.Bcl.Build) then this cause problems with packages that depend on Microsoft.Bcl.Build,.

However, if I use Microsoft.Bcl.Build as the ID this will solve the dependency problem, but causes problems because I do not manage that package. So now I have to be careful that no one tries to fetch this package from the official repository and only uses my repository.

Installing the modified version directly from a nupack file, or altering the package after installing could work, but this breaks package restore (and requires that the package is checked in to source control). It also causes problems with updates, because the update will download the original package, not the customized package.

Ideally I want to be able to edit a file such as packages/redirects.config and specify that any request for Microsoft.Bcl.Build is substituted for Custom.Bcl.Build, and that Custom.Bcl.Build satisfies the dependency on Microsoft.Bcl.Build.

This is simple as long as the version numbers match 1 to 1 between the two packages, though could be more complex if they do not match. Perhaps using a system similar to binding redirects, where a specific version or range from the original package is redirected to a specific version or range in the substitute package (e.g., allowing me to say that Microsoft.Bcl.Build 3.5 can be substituted for Custom.Bcl.Build [3.5,3.6).
Developer
Oct 7, 2013 at 5:41 PM
Change and rehost the package locally (network share or local NuGet.Server instance) and add the local source as the first source. NuGet would locate the locally hosted package first. There isn't a mechanism for package Id substitution as yet in NuGet.
Oct 8, 2013 at 12:12 PM
That was my general idea. Though as I said has 2 main flaws.

It wouldn't work with an open source project (people would keep downloading the wrong package) and even in a closed environment, if you forget to set up the package source (or change it in the drop down) you could fetch the wrong package by mistake.

If using the same package ID like that I think the safest way would be to alter your packages.config to set allowedVersions to the version of your custom package (to avoid accidentally upgrading it). After that add the package directly to the project from a nupack file instead of using a feed, and add that package in to version control so that it will not be fetched using package restore.

Though this is quite awkward and does require that you're always careful when updating packages not to overwrite the custom package with the original package.
This also makes using forks of projects more difficult than I think it should be (with the assumption that the fork is remaining binary compatible with the original).
Oct 9, 2013 at 3:32 PM
Edited Oct 9, 2013 at 3:33 PM
The fix is a small change to the targets file.
In the case where it is a targets file fix I think what you need to do is probably not use nuget to solve the problem - instead just check your modified targets file into your source tree and modify your .csproj to reference that targets file instead of the Bcl one.

Or, refactor it as an additional targets file to include, instead of a modification to the existing one.
Oct 9, 2013 at 5:36 PM
Curious, what was the bug that you ran with Microsoft.Bcl.build? Wanted to see if others would also run into it and it would be worth to contact the owner and resolve it in the package itself?
Oct 12, 2013 at 5:20 PM
deepakverma wrote:
Curious, what was the bug that you ran with Microsoft.Bcl.build? Wanted to see if others would also run into it and it would be worth to contact the owner and resolve it in the package itself?
The connect issue is filed here https://connect.microsoft.com/VisualStudio/feedback/details/793340/microsoft-bcl-build-1-0-7-cannot-load-packages-config-when-building-within-visual-studio-2010

It seemed the only place to file bugs for Microsoft.Bcl.Build. Was originally closed because I reported it against VS2010 (didn't have VS2012 to check if its still relevent) but it seems the bug has been re-opened as some other people have run in to it.

Though one of the people reporting back seem to have an alternative workaround that is interesting, it doesn't require altering the targets file and instead just adds a dummy additional property.
Oct 12, 2013 at 5:28 PM
tilovell wrote:
The fix is a small change to the targets file.
In the case where it is a targets file fix I think what you need to do is probably not use nuget to solve the problem - instead just check your modified targets file into your source tree and modify your .csproj to reference that targets file instead of the Bcl one.

Or, refactor it as an additional targets file to include, instead of a modification to the existing one.
Yeah, but that is still awkward, as installing any other project that depends on Microsoft.Bcl.Build goes and adds the broken package. It's not such a problem if the package your installing is at the end of the dependency chain, as you can just add it manually rather than using nuget. What makes this awkward is the broken package I wanted to substitute is a dependency to the one I want to install.

Like you said, as a targets file I can still just alter my csproj file afterwards to point to a different version, but this is just one specific case, the problem itself is wider than that, e.g. what if you had a customized version of leveldb to support something extra and you wanted to use your custom version instead.

At the simplest implementation, rather than worrying about the complexity of substitution, a system by which I could tell nuget to ignore a specific package by ID would work. So that I'm specifically telling nuget not to worry about that dependency, I've handled it myself. I would want to set this in the packages directory either as part of the repository.config, or a separate file along side it so that all the projects in the solution share it.

That would change nuget so that ignoring dependencies is no longer all or nothing, and would let me handle the substitution of specific packages by hand. This would be a lot simpler to implement than substitution as well. At that point I could just create a package called My.Bcl.Build and install that (so I can still use nuget to update it) and tell nuget to ignore Microsoft.Bcl.Build.