Есть десктоп решение (4.6 и net standard 2.0), в котором буквально три уровня зависимостей - шаредная сборка (ядро), UI сборка и плагин (точнее их несколько, но они одинаковые с точки зрения зависимостей).
UI сборка сделана зависимой от ядра, обычный билд успешно копирует сборки когда это нужно, исключая к сожалению нативные зависимости. У плагинов всё ещё печальне, т.к. они зависят от ядра, но никто не должен о них знать, стандартными зависимостями не удается пользоваться.
Сейчас сборка гарантируется частично beforebuild событием, но т.к. несвязанные проекты могут компилироваться в разном порядке, иногда приходится тупо два раза билдить. Плюс, сейчас оно сделано с использованием xcopy, а хочется кроссплатформенную сборку.
Насколько я понял, есть вариант с targets файлами, но по ним не нашел никакой толковой справки, кроме например статьи https://rsdn.org/article/devtools/msbuild-05.xml#EVGAE
Плюс, есть разные вещи типа Cake (C# Make), но фактически это отдельный инструмент, которым придётся учиться пользоваться, плюс непонятно опять, насколько эти решения кроссплатформенные.
В целом - нужно какое то популярное решение, по которому есть хорошие доки и которое умеет в типовые решения (чтобы для билда проекта не пришлось писать вручную запуск msbuild) без костылей.
Примерная схема, как это сейчас выглядит. Сплошные линии - явные зависимости, пунктирные - которые подразумеваются, и которые приходится на костылях копировать из разных папок в разные папки.
В итоге, msbuild закрыл все мои задачи, хоть местами и смотрится неудобно.
Для плагинов написан простой отдельный таргет:
<Project>
<!-- Copy plugin dll and pdb files to shared folder after compile -->
<Target Name="CopyAfterCompile" AfterTargets="CopyFilesToOutputDirectory">
<ItemGroup>
<SourceFiles Include="$(TargetPath)" />
<SourceFiles Include="$(TargetDir)$(TargetName).pdb" />
</ItemGroup>
<Copy SourceFiles="@(SourceFiles)"
DestinationFiles="@(SourceFiles->'$(ProjectDir)\..\Bin\$(ConfigurationName)\$(TargetFramework)\%(RecursiveDir)%(Filename)%(Extension)')" />
</Target>
</Project>
Такой таргет потом легко подключается к проекту плагина по имени файла, как <Import Project="..\Sites.props"/> и автоматически копирует сборку в нужную папку.
ПС: отдельно стоит заметить, что я тут ссылаюсь на TargetFramework, но он работает только для мультитаргетных проектов, например мои сделаны под net461 и netstandard2.0
Приложения в netstandard я собираю как self-contained, и тут добавление сборок подцепилось только к событию "публикации", несмотря на кучу других советов в гугле. Выглядит примерно так:
<!-- Copy native libs - skiasharp and sqlite interops -->
<Target Name="CopyNativeLibraries" AfterTargets="Publish">
<PropertyGroup>
<UserRoot>$(userprofile)</UserRoot>
<UserRoot Condition="'$(userprofile)' == ''">$(HOME)</UserRoot>
</PropertyGroup>
<ItemGroup>
<Library Include="$(UserRoot)\.nuget\packages\skiasharp\1.57.1\runtimes\osx\native\libSkiaSharp.dylib" />
<Library Include="$(UserRoot)\.nuget\packages\skiasharp\1.57.1\runtimes\win7-x64\native\libSkiaSharp.dll" />
</ItemGroup>
<Copy SourceFiles="@(Library)"
DestinationFiles="@(Library->'$([System.IO.Path]::GetFullPath('$(PublishDir)'))\%(RecursiveDir)%(Filename)%(Extension)')" />
</Target>
На самом деле, разработчик skiasharp уже исправил публикацию своих сборок, но оно пока не вышло в релиз. А вот для sqlite интеропов обновление уже вышло, поэтому тут осталась только skia.
ПС: переменная UserRoot пытается определить папку, в которой находится нугет. Оно не совсем кроссплатформ, но Linux и Windows в текущей реализации работают.
В целом, для анализа порядка выполнения таргетов и анализа того, что кругом происходит, достаточно запустить msbuild -v:diag с билдом или публикацией. В логе будут записаны и все обработанные таргеты и их порядок. Большой плюс msbuild по сравнению с тем же Cake, который я вспоминал в вопросе - у него уже есть куча вычисленных переменных и ими просто надо пользоваться. Более того, у него и жизненный цикл вполне логичный (по логам) и накидать свой таргет, завязанный на существующий - довольно легко.
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости