Почему при сборке консоли создается совершенно другой файл project.assets.json, чем при сборке VS IDE?

У нас есть большое решение с ~ 100 проектами, которые зависят от многих пакетов NuGet, некоторые из которых разрабатываются внутри компании. Этот вопрос касается основного веб-приложения, которое является центром этого решения.

Мы хотели бы рассмотреть возможность перехода на новые проекты в стиле SDK (там, где это возможно), и первым шагом был перенос ВСЕХ проектов в PackageReference. И тут случился главный облом - когда код построен

  • на консоли с msbuild получаем System.Buffers версию 4.0.3.0 в папке bin.
  • на консоли с devenv /build получаем System.Buffers версию 4.0.3.0 в папке bin.
  • в VS IDE (я использую 16.7.7) получаем System.Buffers версии 4.0.2.0 (!!!) в папке bin.

Я обнаружил, что разница обусловлена ​​разницей в файле project.assets.json, созданном для рассматриваемого веб-приложения.

Разница огромна, но абсолютное большинство различий заключается в том, что package.assets.json для сборки консоли содержит гораздо больше контента, чем для сборки IDE. И еще есть System.Buffers:

введите описание изображения здесь

Примечания:

  • Ни один проект явно не ссылается на System.Buffers
  • У нас есть процесс, который гарантирует, что все проекты будут ссылаться на одну и ту же версию конкретного пакета NuGet. Несоблюдение правил не способствует построению PR. Однако это не помогает с транзитивными зависимостями этих пакетов NuGet. Итак, мы вынуждены иметь дело с перенаправлением привязки сборки (даже если все наши проекты без подписи - не имеет значения, многие зависимости подписаны)
  • Код не изменился между сборками, была удалена только папка bin соответствующего веб-приложения.

Мой вопрос - кто нибудь сталкивался с этим? Меня совершенно сбивает с толку тот факт, что devenv /build создает точно такой же файл project.assets.json, что и msbuild.

Моя теория - это связано с эвристикой Fast Up To Date, используемой VS. Но я не хочу его отключать - это огромная экономия времени. Просто теория, слабая, потому что код в конце концов не изменился.

Моя методика заключалась в запуске следующего скрипта:

$FileItem = Get-Item .\bin\_PublishedWebsites\MyWebApp\bin\System.Buffers.dll

del $FileItem.Directory.FullName -r -Force
r.ps1 -NoPull -Main -NoValidateSolutions
[reflection.assemblyname]::GetAssemblyName($FileItem.FullName)
Copy-Item .\UI\MyWORKBits\obj\project.assets.json c:\temp\_msbuild.project.assets.json

del $FileItem.Directory.FullName -r -Force
devenv Main.sln /build
[reflection.assemblyname]::GetAssemblyName($FileItem.FullName)
Copy-Item .\UI\MyWORKBits\obj\project.assets.json c:\temp\_devenv.project.assets.json

del $FileItem.Directory.FullName -r -Force
$VSInstanceFinder = (Get-ToolFromNuGet VSInstanceFinder 1.0.20009.1).FullName
Add-Type -Path $VSInstanceFinder
$dte = [VSInstanceFinder2.Program]::Find((Get-Item .\Main.sln).FullName)
$dte.Solution.SolutionBuild.Build()
while (!(Test-Path $FileItem.FullName))
{
    Write-Host -NoNewline '.'
    Start-Sleep 10
}
[reflection.assemblyname]::GetAssemblyName($FileItem.FullName)
Copy-Item .\UI\MyWORKBits\obj\project.assets.json c:\temp\_ide.project.assets.json
bc c:\temp\_console.project.assets.json c:\temp\_devenv.project.assets.json
bc c:\temp\_console.project.assets.json c:\temp\_ide.project.assets.json

Минимальное воспроизведение

Я все еще работаю над минимальным воспроизведением, которое полностью осветило бы проблему. Однако, чтобы продемонстрировать, что project.assets.json отличается, хотя и невинно, достаточно небольшого проекта.

Common.csproj

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>8.0.30703</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{04455D86-A7A4-41E5-B3ED-B0BC65EAFDFD}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AssemblyName>xyz.Common</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <OutputPath>Bin\Debug\</OutputPath>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <OutputPath>Bin\Release\</OutputPath>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="Dummy.cs" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Antlr">
      <Version>3.5.0.2</Version>
    </PackageReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

dummy.cs

using Antlr;

Создадим его в VS IDE и проверим файл project.assets.json:

C:\work\vsbug [master]> dir .\obj\project.assets.json | sls projectName

obj\project.assets.json:76:      "projectName": "Common",


C:\work\vsbug [master]>

Таким образом, имя проекта отображается как Обычный. Теперь давайте построим консоль:

C:\work\vsbug [master]> msbuild /restore .\Common.csproj /v:q
Microsoft (R) Build Engine version 16.7.0+b89cb5fde for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.

C:\work\vsbug [master]> dir .\obj\project.assets.json | sls projectName

obj\project.assets.json:76:      "projectName": "xyz.Common",


C:\work\vsbug [master]>

На этот раз имя проекта - xyz.Common.

Насколько я понимаю, это мягкая разница. Но этого не должно быть изначально, и это может быть симптомом проблемы, с которой мы столкнулись с нашим большим решением.

Обратите внимание, что использование проекта в стиле SDK устраняет разницу, но я не могу использовать его в нашем большом решении.

devenv /safemode, похоже, вообще не восстанавливает какие-либо пакеты NuGet, поэтому код не компилируется, и я не мог понять, как заставить его работать. В настоящее время совершенно бесполезен.

Я попробую сделать минимальный репро с другим набором пакетов, но у меня уже есть вопрос - почему у проекта другое название?

ИЗМЕНИТЬ 1

Есть предложение запустить msbuild.exe -t:restore с этапа предварительной сборки. Поскольку в моем решении 127 проектов C # (плюс один служебный проект), изменение каждого из них нецелесообразно. Вместо этого я добавил в Directory.Build.targets следующую цель:

<Project>
  <Target Name="MSBuildRestore" BeforeTargets="BeforeBuild" Condition="'$(BuildingInsideVisualStudio)' == True">
    <Exec Command="&quot;$(MSBuildBinPath)\msbuild.exe&quot; /t:Restore /v:q /nologo /m $(MSBuildProjectFullPath)" />
  </Target>
</Project>

Мне все еще нужно оценить влияние этого изменения на сборку. В конце концов, эта команда повторяется для каждого проекта, от которого она зависит. И каждый проект будет это называть.

К счастью, похоже, это НЕ нарушает эвристику быстрого обновления Visual Studio, поэтому я немного поработаю с ней, чтобы посмотреть, нет ли в ней неожиданных сюрпризов.

ИЗМЕНИТЬ 2

К сожалению, создание project.assets.json на этапе предварительной сборки не является решением. Судя по всему, этот файл служит входом в сборку. Следующий простой сценарий показывает поведение:

  1. Сборка в msbuild
  2. Установите DisableFastUpToDateCheck = true, чтобы отключить эвристику быстрого обновления.
  3. Открыть VS
  4. Сборка в VS с двоичным ведением журнала (используйте инструменты Project System для создания журналов)

Изучение журналов показывает, что проекты фактически перекомпилированы, поскольку файлы project.assets.json регенерируются перед каждой сборкой.

Фигово.


person mark    schedule 28.10.2020    source источник
comment
Что произойдет, если вы воспользуетесь dotnet CLI?   -  person Pavel Anikhouski    schedule 28.10.2020
comment
Я не могу. В коде используется устаревший стиль проекта. Не проекты в стиле SDK.   -  person mark    schedule 28.10.2020
comment
Во-первых, вы должны убедиться, что вы указали ту же конфигурацию и платформу для процесса сборки в _1 _, _ 2_, msbuild. Кроме того, если ваша VS IDE установила другие сторонние расширения или инструменты, она будет взаимодействовать с процессом сборки проекта. devenv /build и msbuild не будут перезагружать сторонние пакеты. И вы должны проверить, является ли это главной проблемой. Возможно, вы могли бы использовать devev / safemode, чтобы запустить VS и протестировать.   -  person Mr Qian    schedule 29.10.2020
comment
Был бы чрезвычайно полезен образец, который мы могли бы изучить: stackoverflow.com/help/minimal-reproducible-example   -  person zivkan    schedule 29.10.2020
comment
@zivkan - над этим работаю.   -  person mark    schedule 29.10.2020
comment
Я также сталкиваюсь с той же проблемой в проекте консоли net framework по умолчанию с PackageReference. Я протестирую проблему на других проектах и ​​найду все возможные советы.   -  person Mr Qian    schedule 02.11.2020


Ответы (1)


Исследования

Кажется, это проблема между восстановлением VS IDE и восстановлением msbuild, и я также сталкивался с этой проблемой во многих проектах и ​​PCS. Так что пока это серьезная проблема, и спасибо, что указали на эту проблему.

================================

projects.assert.json при восстановлении VS IDE:

введите описание изображения здесь

================================

projects.assert.json при восстановлении сборки msbuild:

введите описание изображения здесь

Я сообщил о этой проблеме на на нашем форуме DC, и вы можете проголосовать за него и добавить комментарии, если я не описал проблему подробно, чтобы она привлекла больше внимания Microsoft. И я надеюсь, что команда даст вам удовлетворительный ответ.

Предложение

Поскольку процесс может занять некоторое время, в качестве предложения, чтобы устранить разницу, вы можете попробовать эту функцию:

вам нужно только добавить командную строку в событие предварительной сборки вашего проекта в VS IDE:

1) щелкните правой кнопкой мыши свой проект Свойства - ›События сборки -› напишите это в команде события предварительной сборки строка:

msbuild $(MSBuildProjectFullPath) -t:restore

2) Затем удалите папки bin и obj, перестройте свой проект в VS IDE.

Предпосылка состоит в том, что вы должны скопировать C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe в переменную системной среды PATH.

Если мое предложение вас не устраивает, просто проигнорируйте его.

person Mr Qian    schedule 03.11.2020
comment
Проблема гораздо глубже, потому что различия, которые я наблюдаю в нашем большом решении, более обширны. Не только имена, но и отсутствующие библиотеки в VS IDE создали project.assets.json. Это сложно воспроизвести на небольшом сэмпле, и у меня не так много времени, чтобы с ним поиграться. Что касается вашего предложения - должны ли мы удалять корзину и объект перед каждой сборкой? - person mark; 03.11.2020
comment
Спасибо за ваш отзыв. Возможно, это просто упрощенное представление этого другого поведения. И это довольно странно, потому что я тоже столкнулся с этим на своей стороне. Надеюсь, команда поможет нам решить проблему. Кроме того, когда вы используете мою команду, вам не нужно беспокоиться о -----_ 1_. - person Mr Qian; 04.11.2020
comment
Поскольку независимо от того, создаете ли вы проект в VS IDE или _2 _, _ 3_ будет выполняться каждый раз без пропуска, project.assert.json и другие восстановленные файлы всегда будут генерироваться msbuild, всегда перезаписываются при восстановлении msbuild. Таким образом, вам не нужно удалять папки bin и obj. - person Mr Qian; 04.11.2020
comment
Мне понадобится время, чтобы оценить ваше предложение. - person mark; 04.11.2020
comment
Это любезно с вашей стороны :) - person Mr Qian; 05.11.2020
comment
См. ИЗМЕНИТЬ 1. Я пока приму ваш ответ, хотя он не совсем отвечает на вопрос - почему это происходит? - person mark; 10.11.2020
comment
Я вынужден понизить его значение по сравнению с принятым ответом. Оказывается, в VS реализация этого обходного пути приводит к полной перестройке кода из-за Input file "C:\xyz\git\UI\Platform\obj\project.assets.json" is newer than output file "obj\Debug\xyz.Web.Platform.dll". - person mark; 20.11.2020