Second step: Fixing the HintPath of referenced assemblies
All distributed assemblies from the NuGet package are added as references to the project. NuGet sets the
HintPath of the reference with relative path (relative to the
packages directory). When using a shared repository as described in previous post this brings some problems.
Problem with CI builds
The shared repository is stored somewhere on your machine and all project references added by NuGet should point there. You usually don’t clone your development machine directory structure on your CI server. The CI build will fail because the assemblies were not found (but they are there in the directory).
Problem with package restore
Even NuGet package restore feature won’t help because it only downloads the missing packages and places them by default under
packages directory in solution directory. But it does not change the
HintPaths of referenced assemblies. You can’t event set the MSBuild project property
AdditionalLibPath because you don’t know all the directories where the package DLLs are placed beforehand (of course I can create a build task).
The solution to this is very easy, I found this discussion where a build property is used in
HintPath. I had to extend it a little to support both local and CI builds and turn the package restore on.
<PackagesDirectory>path to shared packages directory</PackagesDirectory>
This setting covers both features – local development and CI build. Value of
NuGetPackagesDirectory property will be set to the path to the shared directory (verified by
Condition="Exists('$(PackagesDirectory)')") on local machine. On CI server this directory might or might not exist. If it does not exist it will be set to a
packages directory under solution folder (used by package restore).
And the reference to NuGet package DLL
The branch and changset details are available here.
I’ll start with be summary BenPhegan posted to a thread I started on NuGet CodePlex site.
- Developers are historically used to having a local common library location for all their binary dependencies (ie all hintpaths reference “d:\common” or something similar).
- Often this library is controlled centrally and xcopy/robocopied to all developers machines to provide commonality of build capability, generally the central copy come from a CI build system.
- There are a lot of different projects that a developer works on with a lot of dependencies, and it is seen as efficient to have a single copy of these dependencies on disk.
- Project files are included from multiple solutions at arbitrary locations on disk, resulting in mismatched relative hintpaths when using NuGet.
The problem (not the only one) with shared packages directory is that every project that ever loaded a NuGet package creates a record in
repositories.config file. You end up with this file containing lots of these. Also some packages directories might get deleted when a package is removed.
First step: Solution-level repositories.config file
My first approach to an “enterprise” NuGet was to get rid of the
repositories.config file that is located under packages directory. All the
packages.config files can be discovered by iterating through all projects in the solution:
const string fileName = "packages.config";
foreach (var project in solutionManager.GetProjects())
var projectItem = project.FindProjectItem(fileName);
if(projectItem == null)
// we have the packages.config file here
But then I thought about it and got the idea that I can make
repositories.config file local to a solution, i.e. put it in solution scope. And the best place to put NuGet specific file is the
.nuget directory. The entries in config file are added with path relative to solution folder.
Now there’s one
repositories.config file for every solution containing the records for every available
packages.config. Package files in shared directory can be still deleted when the package in repository is no longer needed (this feature can be turned off and shared repository must be cleared manually).
Vote for it on NuGet!
I tried to avoid big changes in NuGet codebase and I was slowly getting to understand how NuGet internals works. Next step was to force NuGet to use the shared packages repository. I changed some classes (get the settings from registry) to find out the only thing I have to do was to add a node to
C:\Users\(your name)\AppData\Roaming\NuGet\NuGet.config configuration file. Don’t forget to remove any
repositoryPath from a
Nuget.config file under your solution directory if you have one.
<add key="repositoryPath" value="path to your repository"/>
The branch and changset details can be viewed on CodePlex here.
The description of how to build (locally and CI) a solution with project(s) referencing assemblies in a shared local directory will follow.