msbuild <project> /t:Restore;Build fails when referencing netstandard2.0

Created on 20 Aug 2017  路  7Comments  路  Source: dotnet/msbuild

Using Visual Studio 2017 15.3 (final) and DotNet Core 2 SDK.

msbuild Project.csproj /t:Restore;Build

produces lots of these kinds of errors:

File.cs(109,59): error CS0012: The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neut
ral, PublicKeyToken=cc7b13ffcd2ddd51'. [C:....Project.csproj]

However, running this works:

msbuild Project.csproj /t:Restore
msbuild Project.csproj /Build

The former does still work when referencing earlier netstandard with the same tooling.

Most helpful comment

Note that calling Restore and Build targets in the same call is unsupported since restore will create files that need to be there doing project evaluation - but the project already has evaluated with those assets missing. That's the reason it has to be called separately.

There's a PR out (https://github.com/Microsoft/msbuild/pull/2414) introducing a /restore flag that would allow to do a restore + build in a single command line call (by causing a re-evaluation for the actual build).

The same rules apply when using the <MSBuild> task. The workaround would be to pass in a different set of global properties to force a re-evaluation after the restore has been completed:

<MSBuild Projects="$(ProjectFile)" Targets="Restore" Properties="_NeverMind=Me" />
<MSBuild Projects="$(ProjectFile)" Targets="Build" />

(I'd prefer passing properties to other calls like Configuration=Release or VSTestLogger=trx).

That's what also what makes my build scripts work.

All 7 comments

Also, the same problem occurs using the MSBuild task. However, unlike running it from the command line, doing the following doesn't work:

<MSBuild Projects="$(ProjectFile)" Targets="Restore" />
<MSBuild Projects="$(ProjectFile)" Targets="Build" />

Note that calling Restore and Build targets in the same call is unsupported since restore will create files that need to be there doing project evaluation - but the project already has evaluated with those assets missing. That's the reason it has to be called separately.

There's a PR out (https://github.com/Microsoft/msbuild/pull/2414) introducing a /restore flag that would allow to do a restore + build in a single command line call (by causing a re-evaluation for the actual build).

The same rules apply when using the <MSBuild> task. The workaround would be to pass in a different set of global properties to force a re-evaluation after the restore has been completed:

<MSBuild Projects="$(ProjectFile)" Targets="Restore" Properties="_NeverMind=Me" />
<MSBuild Projects="$(ProjectFile)" Targets="Build" />

(I'd prefer passing properties to other calls like Configuration=Release or VSTestLogger=trx).

That's what also what makes my build scripts work.

@dasMulli Your workaround of using different properties for each MSBuild task worked perfectly!

Note one danger of using different global properties; this causes reevaluation, which will work if the NuGet-restore-generated files do not exist for the restore call. If they exist, but are out of date and get updated by Restore, the second build will use the old one because the XML gets cached.

The only workaround for that is a separate msbuild.exe process.

That's the issue that's blocking #2414 at the moment.

I have updated https://github.com/Microsoft/msbuild/pull/2414 so that imports are reloaded from disk. If the change goes in soon, it'll go out with the next release.

You're right this doesn't work:

<MSBuild Projects="$(ProjectFile)" Targets="Restore" />
<MSBuild Projects="$(ProjectFile)" Targets="Build" />

But this works for me (notice the extra restore in the second entry):

<MSBuild Projects="$(ProjectFile)" Targets="Restore" />
<MSBuild Projects="$(ProjectFile)" Targets="Restore;Build" />

This oddly doesn't work:

<MSBuild Projects="$(ProjectFile)" Targets="Restore;Restore;Build" />

I suggest making use of msbuild 15.5's /restore parameter (linked above) by modifying ci build projects to having a Restore and a Build target:

so e.g.

<Project DefaultTargets="Build">
  <ItemGroup>
    <CiBuildProject Include="**/*.csproj" />
  </ItemGroup>  
  <Target Name="Restore">
    <MSBuild Projects="@(CiBuildProject)" Targets="Restore" />
  </Target>
  <Target Name="Build">
    <MSBuild Projects="@(CiBuildProject)" Targets="Build" />
  </Target>
</Project>

and then invoke it using one of:

> dotnet build ci.proj
> dotnet msbuild /restore ci.proj
> msbuild /restore ci.proj
Was this page helpful?
0 / 5 - 0 ratings