У нас есть большое решение с ~ 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=""$(MSBuildBinPath)\msbuild.exe" /t:Restore /v:q /nologo /m $(MSBuildProjectFullPath)" />
</Target>
</Project>
Мне все еще нужно оценить влияние этого изменения на сборку. В конце концов, эта команда повторяется для каждого проекта, от которого она зависит. И каждый проект будет это называть.
К счастью, похоже, это НЕ нарушает эвристику быстрого обновления Visual Studio, поэтому я немного поработаю с ней, чтобы посмотреть, нет ли в ней неожиданных сюрпризов.
ИЗМЕНИТЬ 2
К сожалению, создание project.assets.json на этапе предварительной сборки не является решением. Судя по всему, этот файл служит входом в сборку. Следующий простой сценарий показывает поведение:
- Сборка в msbuild
- Установите
DisableFastUpToDateCheck = true
, чтобы отключить эвристику быстрого обновления. - Открыть VS
- Сборка в VS с двоичным ведением журнала (используйте инструменты Project System для создания журналов)
Изучение журналов показывает, что проекты фактически перекомпилированы, поскольку файлы project.assets.json
регенерируются перед каждой сборкой.
Фигово.
dotnet
CLI? - person Pavel Anikhouski   schedule 28.10.2020msbuild
. Кроме того, если ваша VS IDE установила другие сторонние расширения или инструменты, она будет взаимодействовать с процессом сборки проекта.devenv /build
иmsbuild
не будут перезагружать сторонние пакеты. И вы должны проверить, является ли это главной проблемой. Возможно, вы могли бы использовать devev / safemode, чтобы запустить VS и протестировать. - person Mr Qian   schedule 29.10.2020PackageReference
. Я протестирую проблему на других проектах и найду все возможные советы. - person Mr Qian   schedule 02.11.2020