TeamCity - MSBuild unable to locate NuGet references - msbuild

Attempting to build a C# project which has numerous references to assemblies in NuGet packages fails in TeamCity but works fine in Visual Studio.
Found in the log;
For SearchPath "{HintPathFromItem}".
[13:48:15][ResolveAssemblyReference]
Considered "..\packages\AspNetMvc.4.0.20126.16343\lib\net40\System.Web.Mvc.dll", but it didn't exist.
The reference in the project file is;
<Reference Include="System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\AspNetMvc.4.0.20126.16343\lib\net40\System.Web.Mvc.dll</HintPath>
</Reference>
Any ideas? It seems like it's not starting from the correct directory so can't resolve "../packages" which exists one level above the .csproj file.

I know this has been answered, but maybe someone else has had the same problem I did.
My hint paths in my project file were incorrectly pointing to packages and changing it to ..packages fixed it for me.
So changing it from this:
<Reference Include="Newtonsoft.Json">
<HintPath>packages\Newtonsoft.Json.5.0.5\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
To this:
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.5.0.5\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
Fixed it.

I had restructured my projects since installing the NuGet packages so even though '../packages' was correct for the main project, it wasn't for the other projects which had been moved.
Uninstalling and re-installing the NuGet packages writes the paths correctly or more straightforward, doing a find and replace on the paths in each .csproj file.

My understanding at this point is based on the information here: http://youtrack.jetbrains.com/issue/TW-20525
But I am just diving into both TeamCity and NuGet at the same time (coming from CruiseControl.NET)
So what I did for the time being is to add a "NuGet Installer" build step before my vs.net solution build step and everything worked great.

You either need to add the packages directory to source control or enable nuget to automatically download packages (its a feature in the right click menu of nuget 1.6)
See http://docs.nuget.org/docs/workflows/using-nuget-without-committing-packages

Sorry for resurrecting this old post, but in addition to the above excellent points (Tjaart, SeeNoWeevil, Luke), you might also want to check the property CopyLocal=true for the references that you nuget'ed down.
For myself, this has often been the one tiny oversight that leads to exactly the error that the OP was mentioning.

You can highlight the file in Solution Explorer and got to Properties, and verify that Copy to Output Directory is set to True

Ensure that the .dll and .pdb files are included in source control (or have been downloaded).
For TFS (not TeamCity), by default, .pdb files and .dll files are excluded. So double-check that all files for each package sub-directory have been included, not just the nuget .xml file.
Background: I came to this question with the same thought as some other posters - that the relative reference in the .csproj file might be incorrect. After using a path in the .csproj file to ensure that the reference was absolute with regards to the project...
<HintPath>$(MSBuildProjectDirectory)\..\.nuget\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll</HintPath>
..only to get the same error, I double-checked that the required files existed. I also did an MSBuild build on my local machine (as opposed to Visual Studio build), and it worked. Further investigation on the build server revealed that the specified files did not exist, even though the directory, and .nuget package .xml files did.

Related

msbuild refuses to copy unsigned dll if it is of lower version than demanded by one of the project dependencies - is it by design?

I have the following situation:
All the involved dlls are unsigned
All the projects in the solution depend on version 1.0.21221.1 of Shared.dll
Some NuGet dependencies of some projects in the solution depend on version 1.0.21237.1 of the same dll.
When the web application is built (let us name it Api) it is expected to copy Shared.dll from $(OutDir) to the $(OutDir)_PublishedWebsites\Api\bin folder. The Shared.dll found in $(OutDir) has the version 1.0.21221.1.
The Shared.dll is NOT copied and the web application fails to run.
Here is the evidence from the binary log:
Exhibit A - The conflict of versions:
Exhibit B - ResolveAssemblyReference instructs NOT to copy Shared.dll:
I understand that msbuild does not like the idea of conflicting versions, but NOT copying the dll produces a downright bug, because the application fails to start.
I understand one can resolve it by adding an assembly binding redirect. But I thought it was unnecessary for unsigned assemblies. Am I understanding wrong or am I missing something?
EDIT 1
Here are my answers to the questions posted in the comments:
(Unfortunately I was asked to obfuscate some keywords, I do not know why)
How exactly does the Api project reference Shared.dll?
As we can see in the exhibit B Shared.dll is a transitive dependency of Api. Indeed, Api depends on Xyz.BusinessApi like this:
<Reference Include="Xyz.BusinessAPI" />
Now that DLL depends on Shared.dll through the respective NuGet dependency, here is a snippet from the project.assets.json file of Xyz.BusinessAPI:
What other projects reference Shared.dll and how?
There are a lot of projects referencing it as a NuGet package at version 1.0.21221.1. The problem is that some projects also reference two other NuGet packages which in turn depend on the version 1.0.21237.1 of the same NuGet package. This is indicated in the RAR output - see the exhibit A.
I would like to emphasize - no project references Shared.dll as a raw dll, only either as NuGet package or indirectly through other NuGets or projects or project dlls. Project dll is a dll of a project from a previously built solution - we do not allow project references to other solutions, so if a project is built in a previous solution, then it would be referenced as DLL in subsequent solutions.
What is the mechanism used to copy from OutDir to _PublishedWebsites\api\bin?
This is the standard web application publishing target _CopyFilesMarkedCopyLocal from C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VisualStudio\v16.0\WebApplications\Microsoft.WebApplication.targets:
<!-- copy any referenced assemblies to _PublishedWebsites\app\bin folder -->
<Copy SourceFiles="#(ReferenceCopyLocalPaths)"
DestinationFiles="#(ReferenceCopyLocalPaths->'$(WebProjectOutputDir)\bin\%(DestinationSubDirectory)%(Filename)%(Extension)')"
SkipUnchangedFiles="true"
Retries="$(CopyRetryCount)"
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/>
How does Shared.dll end up in OutDir?
All of our code is built into the shared bin directory - we set OutDir to the same value for all the projects. Thus all the project binaries and their dependencies, including Shared.dll first end up there.
Are there any double-writes in the binlog?
Yes, but I do not think they are relevant:
The short answer is that your projects indirectly depend on two versions of Shared.dll: 1.0.21221.1 and 1.0.21237.1.
RAR (the MSBuild ResolveAssemblyReference task) inspected all the references of all .dlls and found these two versions. It reported a conflict:
Pay attention how it reported the found file path in square brackets for Shared.dll 21221 and reported [] (meaning no file of such version was found) for 21237.
It's useful to search for these using There was a conflict under($rar) or $warning under($rar).
Now, the OutDir only contains the Shared.dll 21221, so the Shared.dll with version 21237 couldn't be found anywhere.
The trick is to search for Shared.dll under($rar project(api.csproj)) You will find the relevant messages from RAR:
Considered "C:\Xyz\61\bin.link\Shared.dll",
but its name "Shared, Version=1.0.21221.1, Culture=neutral, PublicKeyToken=null"
didn't match the expected name "Shared, Version=1.0.21237.1, Culture=neutral, PublicKeyToken=null".
So, it saw a conflict, decided to unify on a later version (21237), but didn't find the file of that version. So the reference of 21221 was not CopyLocal because it didn't unify on that version, and the reference to 21237 was not resolved because a file of that version was not found.
To resolve this, I recommend adding an explicit reference to Shared.dll of the version 21237 (either via NuGet or via GeneratePathProperty metadata on the package reference: https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#generatepathproperty). If you use GeneratePathProperty you can then reference the .dll directly using $(PkgFoo_Bar)\lib\net472\Shared.dll or similar. Also add a binding redirect from 21221 to 21237 to resolve the conflict. Once the correct version (21237) will be in your OutDir, it will get copied to output correctly.
Hope this clarifies.

How does msbuild resolve assembly from a multi-framework nuget package?

I have a solution with a console app ConsoleApp-net461 that references a library ClassLibrary-net452.
Both projects reference a multi-framework nuget package NuGetPackage-net452-net461.
When I build, I end up with the nuget package's net461 dll in the output folder. At runtime, I am getting errors like:
System.TypeLoadException: Inheritance security rules violated by type: 'ClassA'.
Derived types must either match the security accessibility of the base type or be less accessible.
I am suspecting this is because my ClassLibrary-net452 ends up having a dependency on the nuget package dll with the net461 framework.
Is this normal behavior? What are the best ways to ensure the net452 dll is resolved from the nuget package? BindingRedirects don't seem to let you choose a target framework.
Not 100% sure this will work, but might be worth trying:
(Relies on using SDK csproj format, this link might work for packages.config, but I know less about the area.)
It looks like what you want is something like this. However the dotnet/SDK team haven't got round to implementing it yet.
The suggested work around is:
- Grab the dll path from your package reference
- Tell the package reference to not copy compile assets (dlls).
- Use the dll path to generate a similar path for a non-package reference that redirects to the Net452 version of the package.
Explanatory links here and here.
Summary is that your csproj should contain something like this:
<ItemGroup>
<PackageReference Include="*MyPackageName*" ExcludeAssets="Compile" GeneratePathProperty="true">
<Version>x.y.z</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Reference Include="*NameYouWantToGiveThisReference(suggest dll name)*">
<HintPath>$(Pkg*MyPackageName*)lib\net452\*DLLname*.dll</HintPath>
</Reference>
Everything surrounded by ** needs to be named according to whatever your projects are actually called.
The two MyPackageNames must match up (with the caveat that all "."s in the first one should be replaced with "_" in the second one).

Why Msbuild does not copy Newtonsoft.Json.dll referenced by Microsoft.AspNet.WebApi.Client?

My web projectA references my projectB that references Microsoft.AspNet.WebApi.Client that references Newtonsoft.Json assembly. It is not referenced anywhere else. The problem was that on my deployment server Newtonsoft.Json was not copied to bin directory once run as
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe .\src\path.to.Web.projectA.csproj /t:Rebuild /p:Configuration=Release
And it eventually broke with this exception:
FileNotFoundException: Could not load file or assembly
'Newtonsoft.Json, Version=7.0.0.0, Culture=neutral,
PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The
system cannot find the file specified.
I've resolved it by adding a stub code to projectB:
string json = JsonConvert.SerializeObject(new Something());
Can someone describe why it was not copied before?
Note. These questions do not seem to answer the question or out of date or I miss something?
MSBuild does not copy directly referenced dlls to bin folder
msbuild not copying referenced assembly
MSBuild doesn't copy references (DLL files) if using project dependencies in solution
http://blog.alexyakunin.com/2009/09/making-msbuild-visual-studio-to.html
Copying a DLL's dependencies in Visual Studio
I made a comment under Alex's blog post,
It does not work for me when the indirect dependency comes from NuGet packages (Microsoft.AspNet.WebApi.Client and Newtonsoft.Json). But the tip described in http://www.paraesthesia.com/archive/2014/05/09/recursively-copying-indirect-project-dependencies-in-msbuild.aspx/ solves the issue. I think it uses a more suitable indirect dependency detection approach.
If you follow that solution, then there is no need to use the stub at all.
About why the dll is not copied over, it has been narrowed down to an MSBuild issue (or more as there are too many reports related), which Microsoft claims won't fix on Connect,
https://connect.microsoft.com/VisualStudio/feedback/details/797034/msbuild-handles-direct-and-transitive-references-differently-for-unused-assembly-references

How to use YUI Compressor.Net MSBuild Task 2.3.0.0

I have tried searching the web for a solution to this problem but it seems very unclear. It sounds like people are seeing this but the solutions don't seem to work for me.
I have downloaded and installed YUICompressor.NET.MSBuild 2.3.0.0 from NuGet into my project.
I have created an MSBuild target file that closely resembles the example file included and exists as part of my .csproj file so I can run it in the AfterBuild step.
I am getting the following error everytime I try to build my VS project:
The "JavaScriptCompressorTask" task could not be loaded from the assembly SolutionDir\packages\YUICompressor.NET.MSBuild.2.3.0.0\lib\NET20\Yahoo.Yui.Compressor.Build.MsBuild.dll.
Could not load file or assembly 'Yahoo.Yui.Compressor, Version=2.3.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
Confirm that the declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask.
My folder structure is as follows:
\Solution
\packages
YUICompressor.NET.MSBuild.2.3.0.0\lib\NET20\
\build
ProjectFile.csproj
In the csproj I have:
<UsingTask TaskName="CssCompressorTask" AssemblyFile="packages\YUICompressor.NET.MSBuild.2.3.0.0\lib\NET20\Yahoo.Yui.Compressor.Build.MsBuild.dll" />
<UsingTask TaskName="JavaScriptCompressorTask" AssemblyFile="packages\YUICompressor.NET.MSBuild.2.3.0.0\lib\NET20\Yahoo.Yui.Compressor.Build.MsBuild.dll" />
<Target Name="AfterBuild">
<ItemGroup>
<JavaScriptFiles Include="someFile.js" />
</ItemGroup>
<JavaScriptCompressorTask SourceFiles="#(JavaScriptFiles)" OutputFile="build\combined.js" DeleteSourceFiles="false" CompressionType="None" ObfuscateJavaScript="false" PreserveAllSemicolons="true" />
<JavaScriptCompressorTask SourceFiles="#(JavaScriptFiles)" OutputFile="build\combined.min.js" DeleteSourceFiles="false" CompressionType="Standard" />
</Target>
Am I missing something? Can anyone help me? Thank you.
Did you try to download version 2.3.0.0 directly from codeplex? Then make sure that the Yahoo.Yui.Compressor.Build.MsBuild.dll and Yahoo.Yui.Compressor.dll reside in the same directory. Like this I was able to make it work.
It's been a while since i've played around with the AfterBuild .. but how this works is that the the msbuild program is ran from some directory .. and therefore looks for those assembly files RELATIVE to where the msbuild is being executed from.
I'm not sure if this means the msbuild is being run from C:\program files(x86)\Microsoft Visual Studio\<whatever...>
TAKE NOTE: it's the folder/path (aka execution path) where visual studio is running the msbuild command from ... NOT where msbuild exists.
So therefore, it can't find the assemblies.
Try putting in the full path to the assemblies (just to see if that works).
eg. C:\Projects\Solution\packages\YUICompressor.NET.MSBuild.2.3.0.0\lib\NET20\Yahoo.Yui.Compressor.Build.MsBuild.dll
If it's running it from the \solution\bin directory, then you're in luck! cause then you can do ..\packages\YUICompressor.NET.MSBuild.2.3.0.0\lib\NET20\Yahoo.Yui.Compressor.Build.MsBuild.dll' (the..means: from thisbindirectory, go up one level tosolutionthen down intopackagesand then down intoYUIComp`... etc.
So the answer to your question is this: Find the location Visual Studio is running the msbuild command from, during an AfterBuild. Maybe put in some code in there to say 'write to file => current path i'm in :P'

MSBuild doesn't pick up references of the referenced project

I bumped into a strange situation with MSBuild just now. There's a solution which has three projects: LibX, LibY and Exe. Exe references LibX. LibX in its turn references LibY, has some content files, and also references to a third-party library (several pre-built assemblies installed in both GAC and local lib folder). The third-party library is marked as "Copy Local" ("private") and appears in the output of the LibX project, as the LibY's output and LibX's content files do. Now, Exe project's output has LibX project output, content files of the LibX project, LibY project output (coming from LibX), but NO third-party library's assemblies.
Now I worked this around by referencing the third-party library directly in Exe project, but I don't feel this is a "right" solution.
Anyone had this problem before?
There is a difference in behavior when building with MSBuild (i.e. command line, TFS Build and other tools) compared to building with Visual Studio. The secondary references are not included in the references variable sent into MSBuild compile tasks.
There are several extension points provided by MSBuild to change how references are to be resolved. I have successfully used AfterResolveReference to fix this issue for some of my projects - I have posted more info about the background on my blog.
The workaround is to add the following code into you vbproj or csproj files
<Target Name="AfterResolveReferences">
<!-- Redefine referencepath to add dependencyies-->
<ItemGroup>
<ReferencePath Include="#(ReferenceDependencyPaths)">
</ReferencePath>
</ItemGroup>
</Target>
Microsoft has stated that this is a won't fix on Connect
You can actually go into the Microsoft.CSharp.targets or Microsoft.VisualBasic.targets file (located in the framework directory, usually C:\Windows\Microsoft.NET\Framework\v3.5) and modify the csc or vbc task parameters to include additional reference dependencies. In the file (VB targets, line 166; C# targets, line 164) change:\
References="#(ReferencePath)"
to
References="#(ReferencePath);#(ReferenceDependencyPaths)"
This might cause other issues depending on how complicated things are and it may play tricks with the Visual Studio inproc compiler, but it's the only way to do it in MSBuild that I've found.
josant's answer almost worked for me; I kept getting an error in Visual Studio when I tried that:
A problem occurred while trying to set the "References" parameter for the IDE's in-process compiler. Error HRESULT E_FAIL has been returned from a call to a COM component
The solution to my problem was to put a condition on the ItemGroup, like this:
<Target Name="AfterResolveReferences">
<!-- Redefine referencepath to add dependencies-->
<ItemGroup Condition=" '$(BuildingInsideVisualStudio)' != 'true' ">
<ReferencePath Include="#(ReferenceDependencyPaths)"></ReferencePath>
</ItemGroup>
</Target>
That caused Visual Studio to ignore the reference change completely, and the build works fine locally and on the build server.
Yes, I've had that problem, too. Though I'd love to say otherwise, I believe you must include all transitive dependencies as references in your build file.
I've combined Alex Yakunin's solution with one that will also copy native dll's.
The AfterResolveReferences method fails if you've got a directed graph not a tree with a "trying to deploy different copies of the dll" error. (cf. How to configure msbuild/MSVC to deploy dependent files of dependent assemblies)