Это один из редких случаев, когда двоичный журнал бесполезен, потому что он создается msbuild, то есть слишком поздно. Нужно включить диагностическую сборку в Visual Studio, потому что первая строка будет из эвристики. Здесь указана причина, по которой проект построен. Все остальные журналы диагностики могут быть отброшены, но первая строка бесценна. Это привело меня к следующему решению:
Включение эвристики
Я добавил следующие цели в файл целей, который мы импортируем из каждого проекта. Те, кому не нужна обратная совместимость с MSBuild 14, могут добавить его в Directory.Build.Targets:
<Target Name="SatisfyVisualStudioFastUpToDateCheckHeuristic" AfterTargets="AfterBuild" Condition="'$(OutDir)' != '' And '$(GatedCheckIn)' != True">
<ItemGroup>
<Files Include="$(TargetFileName).config" Condition="'$(AppConfig)' != '' And Exists('$(AppConfig)')" />
<Files Include="$(TargetName).pdb" />
<Files Include="$(TargetName)$(TargetExt)" />
<Files Include="@(IntermediateAssembly->'%(Filename)%(Extension)')" />
<Files Include="$(_SGenDllName)" Condition="'$(_SGenDllCreated)'=='true'" />
<Files Include="@(ReferenceCopyLocalPaths->'%(DestinationSubDirectory)%(Filename)%(Extension)')" />
<Files Include="@(_SourceItemsToCopyToOutputDirectory->'%(TargetPath)')" />
</ItemGroup>
<MakeDir Directories="$(OutputPath)\%(Files.RelativeDir)" />
<Touch Files="@(Files->'$(OutputPath)%(Identity)')" AlwaysCreate="true" ContinueOnError="true"/>
</Target>
<Target Name="CleanFakeOutputFiles" AfterTargets="AfterClean" Condition="'$(OutDir)' != '' And '$(GatedCheckIn)' != True">
<RemoveDir Directories="$(OutputPath)" />
</Target>
Эта цель создает поддельные суррогатные выходные файлы нулевой длины в месте, ожидаемом эвристикой.
Последующее действие 1
Это может фактически привести к сбою сборки, если есть проекты, ссылающиеся на другие проекты как библиотеки DLL, а не как ссылки на проекты. Например, если проект Алиса ссылается на проект Боба так:
<Reference Include="Bob, Version=1.0.0.0, Culture=neutral, PublicKeyToken=..., processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Bob\bin\$(Configuration)\Bob.dll</HintPath>
</Reference>
Проблема в HintPath
. Когда мы строим с общим бином и без поддельных суррогатных выходов, HintPath
игнорируется, потому что это не приводит к существующему файлу. Таким образом, Bob.dll будет найден в общей папке bin и загружен оттуда. Но когда в bin\$(Configuration)
есть подделки для удовлетворения эвристики, HintPath
возвращает существующий файл, поэтому поиск Bob.dll заканчивается с подделкой нулевой длины. Очевидно, компиляция не удалась. Исправление заключается в том, чтобы либо изменить его на соответствующую ссылку проекта, либо исключить HintPath
со всеми метаданными:
<Reference Include="Bob" />
Нужно ссылаться на Bob.dll так же, как мы ссылаемся на системные зависимости от GAC.
Последующее действие 2
На этом этапе должен сработать код и работает эвристика. За исключением случаев, он может сказать, что не может найти какую-то зависимость, например System.IO.dll
. В этом случае необходимо проверить, выполняются ли следующие два условия:
- Проект ссылается на
System.IO.dll
из некоторого каталога, например пакетов NuGet. System.IO.dll
находится вGAC и лучше подходит для целевой структуры.
В моем случае у меня было несколько проектов, ссылающихся на System.IO*
, System.Runtime*
, System.Security*
и System.ValueTuple
от NuGet, тогда как эти dll существуютв GAC благодаря наличию среды выполнения .NET 4.7.2 в системе. Эвристик просматривает эти ссылки в HintPath
и ожидает найти их в папке bin\$(Configuration)
.
Однако сборка фактически берет их из GAC, и библиотеки DLL, на которые ссылается GAC, по умолчанию не копируются в целевой лоток. Следовательно, нет поддельных суррогатов, удовлетворяющих эвристике.
Мое решение состояло в том, чтобы заменить все эти ссылки NuGet соответствующими ссылками из GAC.
РЕДАКТИРОВАТЬ 1
Проблемы 1
Побочным эффектом последующего действия 1 является то, что больше не легко вернуться к локальным папкам bin, поскольку изменениек проекту ссылки dll не обратно совместимы.
Можно сохранить метаданные HintPath
, но все они должны указывать на каталог общего хранилища, в этом случае он по-прежнему несовместим со старым способом.
Выпуск 2
Visual Studio перезаписывает .vs\config\applicationhost.config
каждый раз при загрузке проекта веб-приложения. Что означает:
- Когда решение открыто
- Когда проект выгружается, затем перезагружается
Это большой облом, потому что часть общегоПодход bin заключается в обновлении путей в .vs\config\applicationhost.config
с помощью сценариев. Но такое поведение VS делает его основной неприятностью.