Msbuild: PackageReference is not resolved while it works in Visual Studio

Created on 16 Feb 2018  Â·  19Comments  Â·  Source: dotnet/msbuild

Steps to reproduce

  • Create a Empty Web Application Project in Visual Studio.
  • Make it really empty by removing the DotNetCompilerPlatform stuff
  • Add a Nuget package reference to Microsoft.AspNet.Mvc version 5.2.4
  • Add a Global.asax and its code behind with a using System.Web.Mvc
  • Compile in Visual Studio
  • Compile from the command line

Directory contents:

/
- Properties/
  - AssemblyInfo.cs
- Global.asax
- Global.asax.cs
- web.config
- Empty.WebHost.csproj

Empty.WebHost.csproj:

<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>
    </ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{080CBD85-3B74-4ECC-8389-47E594F376DE}</ProjectGuid>
    <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Empty.WebHost</RootNamespace>
    <AssemblyName>Empty.WebHost</AssemblyName>
    <TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
    <UseIISExpress>true</UseIISExpress>
    <Use64BitIISExpress />
    <IISExpressSSLPort />
    <IISExpressAnonymousAuthentication />
    <IISExpressWindowsAuthentication />
    <IISExpressUseClassicPipelineMode />
    <UseGlobalApplicationHostFile />
    <NuGetPackageImportStamp>
    </NuGetPackageImportStamp>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Web.DynamicData" />
    <Reference Include="System.Web.Entity" />
    <Reference Include="System.Web.ApplicationServices" />
    <Reference Include="System.ComponentModel.DataAnnotations" />
    <Reference Include="System" />
    <Reference Include="System.Data" />
    <Reference Include="System.Core" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Web.Extensions" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Drawing" />
    <Reference Include="System.Web" />
    <Reference Include="System.Xml" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Web.Services" />
    <Reference Include="System.EnterpriseServices" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNet.Mvc" Version="5.2.4" />
  </ItemGroup>
  <ItemGroup>
    <Content Include="Web.config" />
    <Content Include="Global.asax" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="Global.asax.cs">
      <DependentUpon>Global.asax</DependentUpon>
    </Compile>
  </ItemGroup>
  <PropertyGroup>
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
  </PropertyGroup>
  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
  <Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
  <ProjectExtensions>
    <VisualStudio>
      <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
        <WebProjectProperties>
          <UseIIS>True</UseIIS>
          <AutoAssignPort>True</AutoAssignPort>
          <DevelopmentServerPort>1983</DevelopmentServerPort>
          <DevelopmentServerVPath>/</DevelopmentServerVPath>
          <IISUrl>http://localhost:1983/</IISUrl>
          <NTLMAuthentication>False</NTLMAuthentication>
          <UseCustomServer>False</UseCustomServer>
          <CustomServerUrl>
          </CustomServerUrl>
          <SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
        </WebProjectProperties>
      </FlavorProperties>
    </VisualStudio>
  </ProjectExtensions>
</Project>

Global.asax:

<%@ Application Codebehind="Global.asax.cs" Inherits="Empty.Web" Language="C#" %>

Global.asax.cs:

using System.Web;
using System.Web.Mvc;

namespace Empty.Web
{
  public class MvcApplication : HttpApplication
  {
    protected void Application_Start()
    {
      AreaRegistration.RegisterAllAreas();
    }
  }
}

web.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
</configuration>

Properties\AssemblyInfo.cs:

using System.Reflection;

[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Command line

msbuild .\Empty.WebHost.csproj /t:restore,Build

Expected behavior

The build should succeed in Visual Studio.
The build should succeed on the command line.

Actual behavior

The build succeeds in Visual Studio.
The build fails on the command line.

Build FAILED.

"P:\cy\apps\cam\src\Core\Empty.WebHost\Empty.WebHost.csproj" (restore;Build target) (1) ->
(CoreCompile target) ->
  Global.asax.cs(2,18): error CS0234: The type or namespace name 'Mvc' does not exist in the namespace 'System.Web' (are you missing an assembly reference?) [ P:\cy\apps\cam\src\Core\Empty.WebHost\Empty.WebHost.csproj]

    0 Warning(s)
    1 Error(s)

Environment data

msbuild /version output:

Microsoft (R) Build Engine version 15.5.180.51428 for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.

15.5.180.51428

OS info:
Windows 10
Visual Studio 2017 15.5.6

Most helpful comment

In my case the root cause of this issue was old Nuget.exe version.
Old version used old MsBuild v14 instead on v15 and it causes build issues after nuget restore.
Try to update it with nuget.exe update -self

Or try to change ToolsVersion="12.0" to ToolsVersion="15.0" in csproj files

All 19 comments

When add an additional msbuild project like the following the build succeeds: So what is the difference?

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="CompileCode" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<ItemGroup>
    <WebProjects Include=".\Empty.WebHost.csproj"/>
  </ItemGroup>

  <Target Name="CompileCode">
    <MSBuild
      Projects="@(WebProjects)"
      StopOnFirstFailure="true"
      Targets="restore;Build"
    />
  </Target>
</Project>

In my case the root cause of this issue was old Nuget.exe version.
Old version used old MsBuild v14 instead on v15 and it causes build issues after nuget restore.
Try to update it with nuget.exe update -self

Or try to change ToolsVersion="12.0" to ToolsVersion="15.0" in csproj files

@ludwo Is there a way to see which version of nuget MSBuild is using? I can't seem to find anything in the restore logs or the package management logs in Visual Studio.

@ChristopherHaws I used nuget.exe as standalone app in my msbuild script in Exec task.
<Exec Command="nuget.exe restore %(Solution.FullPath)" />
I don't remember why I did it this way (issue in the past?). Today I switched back to Restore target.
Restore target in msbuild/vs2017 is using Nuget API internaly, I guess, because there is no nuget.exe file on my PC.

My build stopped working after some refactoring on our solution/project files.
When I used old nuget.exe to restore packages I had the same issue as reported by @lennybacon.
In VS2017 build was working, but failed on the command line.
I cleaned up my repository with git clean -X -d -f command to verify every attempt to resolve this issue.
After some time I noticed in the command line log that my old nuget.exe is using old msbuild version v14 from %ProgramFiles(x86)%\MsBuild instead of v15 from VS2017 folder.
So I tried to update nuget.exe to the latest version with nuget.exe update -self and bingo...

Later today I switched to the Restore target and I removed nuget.exe from my script. It is working fine.

My advice/guess with ToolsVersion is based on the @lennybacon 's last post where ToolsVersion is different in the csproj file - 15.0 vs 12.0.

@ludwo @ChristopherHaws I changed the ToolsVersion to 15.0, updated Nuget to 4.6.2.5055 and used msbuild from the latest patched Visual Studio 2017 installation (15.7.3). Still the same behavior :-(

lennybacon, I am facing same issue, can you please let me know how you resolved it if you were able to?

@ChandraTerli Just create a file named build.proj and write some xml (as in my comment above) that uses the <MsBuild/> task and points to the project you want to build. Then call msbuild.exe .\build.proj from the command line. This worked for me.

Thank you! for the response lennybacon, finally I was able to resolve it by downloading latest version of nuget on on build server and reference it in the nuget path of build definition.

/t:Restore;Build does not work reliably. Instead, specify msbuild.exe /restore, which runs the restore operation in a separate phase and ensures that the build operates with the latest restored build logic.

Adding a separate project that uses <MSBuild with Targets="Restore;Build" will also not be reliable.

Running nuget.exe _within_ MSBuild also will not work.

The core problems with all of these approaches are the same: MSBuild tries very hard to avoid loading projects more than it needs to. That means that if you modify the project (or create new imports, such as by restoring NuGet packages) after the project has already started building, the rest of that build will not see the updated build logic, because it's already read from disk.

The /restore command-line argument fixes this by doing the /t:Restore in a separate phase, allowing the rest of the build (as specified on the command line) to pick up the latest logic.

@rainersigwald Is there a bug/feature request to make this work more reliably? or will this just be "by design" it really hurts in systems where the <MSBuild tag was used. Near as I can tell the only work around for that would be to have a stand alone <Exec task that performs the restore before hand?

I'm not sure I understand the question @aolszowka. Can you elaborate? Specifically, I don't see what the MSBuild task and Exec have to do with the scenario.

Hi @rainersigwald so we've got a lot of scripts that look something like this:

  <Target Name="BuildTIMSNET" DependsOnTargets="CleanOutputFolders;CopyDependencies;GetTIMSNETReferencePaths">
    <Message Text="Building TIMSNET" Importance="high"/>
    <ItemGroup>
      <TIMSNETSolutionFiles Include="$(DotNetCode)\CUBO.sln"/>

      <!--Build Debug-->
      <ProjectsToBuild Include="@(TIMSNETSolutionFiles)" Condition="$(CUBuildType)=='Local'">
        <AdditionalProperties>
          Configuration=Debug;ReferencePath=@(TIMSNETReferencePaths);OutputPath=$(TIMSNETDebugOutputFolder)
        </AdditionalProperties>
      </ProjectsToBuild>
      <!--Build Release-->
      <ProjectsToBuild Include="@(TIMSNETSolutionFiles)" Condition="$(CUBuildType)=='Development' OR $(CUBuildType)=='Deployment'">
        <AdditionalProperties>
          Configuration=Release;ReferencePath=@(TIMSNETReferencePaths);OutputPath=$(TIMSNETReleaseOutputFolder)
        </AdditionalProperties>
      </ProjectsToBuild>
    </ItemGroup>

    <MSBuild
      Projects="@(ProjectsToBuild)"
      Properties="PostBuildEvent="
      Targets="Build"
      BuildInParallel="true"
      StopOnFirstFailure="True"
      UseResultsCache="True"
      UnloadProjectsOnCompletion="True"
      />
  </Target>

Based on your comments above:

Adding a separate project that uses

And

Instead, specify msbuild.exe /restore, which runs the restore operation in a separate phase and ensures that the build operates with the latest restored build logic.

I gather the only way to make this work properly is to do the following (Leveraging the <Exec task):

  <Target Name="BuildTIMSNET" DependsOnTargets="CleanOutputFolders;CopyDependencies;GetTIMSNETReferencePaths">
    <Message Text="Building TIMSNET" Importance="high"/>
    <ItemGroup>
      <TIMSNETSolutionFiles Include="$(DotNetCode)\CUBO.sln"/>

      <!--Build Debug-->
      <ProjectsToBuild Include="@(TIMSNETSolutionFiles)" Condition="$(CUBuildType)=='Local'">
        <AdditionalProperties>
          Configuration=Debug;ReferencePath=@(TIMSNETReferencePaths);OutputPath=$(TIMSNETDebugOutputFolder)
        </AdditionalProperties>
      </ProjectsToBuild>
      <!--Build Release-->
      <ProjectsToBuild Include="@(TIMSNETSolutionFiles)" Condition="$(CUBuildType)=='Development' OR $(CUBuildType)=='Deployment'">
        <AdditionalProperties>
          Configuration=Release;ReferencePath=@(TIMSNETReferencePaths);OutputPath=$(TIMSNETReleaseOutputFolder)
        </AdditionalProperties>
      </ProjectsToBuild>
    </ItemGroup>

    <Exec Command="msbuild.exe /restore %(ProjectsToBuild.Identity)" />

    <MSBuild
      Projects="@(ProjectsToBuild)"
      Properties="PostBuildEvent="
      Targets="Build"
      BuildInParallel="true"
      StopOnFirstFailure="True"
      UseResultsCache="True"
      UnloadProjectsOnCompletion="True"
      />
  </Target>

Is that correct or am I missing something?

Ideally the Targets would have just been Restore;Build but as you mentioned:

The core problems with all of these approaches are the same: MSBuild tries very hard to avoid loading projects more than it needs to. That means that if you modify the project (or create new imports, such as by restoring NuGet packages) after the project has already started building, the rest of that build will not see the updated build logic, because it's already read from disk.

Perhaps having an additional argument to the <MSBuild would be the solution to perform this restore?

Perhaps having an additional argument to the <MSBuild would be the solution to perform this restore?

That's tracked by #2811, but to set expectations it's definitely very hard and possibly too hard, which is why it isn't done already.

Is it possible for you to have a Restore target in the file that defines BuildTIMSNET that does something like

<Target Name="Restore">
    <MSBuild
      Projects="@(ProjectsToBuild)"
      Properties="PostBuildEvent="
      Targets="Restore"
      BuildInParallel="true"
      />
</Target>

?

Also, msbuild.exe /restore runs Restore and then the default target. If you want to do the exec-MSBuild approach for a subtree, you should call msbuild.exe /t:Restore to run _just_ the restore operation and do the build with the MSBuild task.

It is possible for us to use the Restore Target like you mentioned and if that will work is a much more desirable solution than <Exec since we will get much better logging out of it.

Thank you for taking the time to answer this support based question and for linking the "long term solution" (even if it is difficult). I know this type of stuff takes time to do and I really appreciate it.

It is possible for us to use the Restore Target like you mentioned and if that will work is a much more desirable solution than <Exec since we will get much better logging out of it.

Great! That's what we envisioned as the best approach when we were designing this: Restore is a single operation for a whole build, but it can be customized by customizing the Restore target in the entry-point project. That way, msbuild /restore can be extended to work for most scenarios.

Closing this issue as we already have a separate issue to consider support restore to run along other targets and it also seems like folks are unblocked.

@livarcocc Could you please link the separate issue with its ID?

It was linked above by rainer:

That's tracked by #2811, but to set expectations it's definitely very hard and possibly too hard, which is why it isn't done already.

The /restore command-line argument fixes this by doing the /t:Restore in a separate phase, allowing the rest of the build (as specified on the command line) to pick up the latest logic.

Thank you @rainersigwald !!! All I had to do was add the /restore switch to my msbuild.exe command. This all started when we switched from packages.config to PackageReference, which has been quite a painful, timely experience.

C:\MyProject>msbuild.exe /restore MyProject.sln ….

Thanks again for knowing this secret.

Was this page helpful?
0 / 5 - 0 ratings