Какие бывают зависимости и как с ними бороться

Разработка на C# под linux
Зачем упаковывать пакеты самостоятельно
Идея: скачать все репозитории с C#-кодом
У меня новая мегаидея. А что если разделить все ебилды на два? Один будет nekiy.ebuild, а другой будет dev-dotnet/nekiy.ebuild
соответственно, первый будет заниматься сборкой из исходников, а второй - по обстоятельствам. Может вызовет первый и упакует в nupkg, а может готовый nupkg скачает с интернета
или можно категорию завести, например emerge -av =nuget-org/nunit-nupkg-3.6.4 для билдов, которые качают с интернета. Тогда можно будет на выбор ставить местной сборки или с nuget.otg
dev-dotnet/nekiy.ebuild, dev-dotnet/nekiy-nupkg.ebuild и nuget-org/nekiy-nupkg.ebuild
Зачем это нужно? Можно будет прописывать зависимости только от пакетов без суффакса -nupkg
сейчас если что, то прийдётся ставить пакет два раза - первый раз без USE=nupkg, а второй раз с ним

я предлагаю перестать использовать USE=nupkg, а сделать вместо этого дополниетльные пакеты с суффиксом -nupkg

почему надо сейчас два раза собирать?
честно говоря флаг то выглядит лучше, в чём бенефит отдельного пакета ну если представить, что за один раз соберётся вместе с nupkg?

ок. ты меня уговоил :) бенефит есть только для пакетов, которые собираются до сборки nuget и mono-packaging-tools. Нет смысла из-за этого малого количества пакетов разбивать вообще все пакеты в системе

PublicKeyToken

Он одинаковый для поставщика сборок или для сборки разной бинарно - тоже меняется?

HintPath и как с ним работать

https://github.com/jstangroome/NuGetReferenceHintPathRewrite
https://github.com/d-aguilera/ProjectReferencesConverter
https://github.com/RoryBecker/CR_ProjectReferenceDefaults

Reference item in .csproj msbuild script

https://msdn.microsoft.com/en-us/library/bb629388.aspx
Что такое FusionName?
Для чего нужно FusionName?
By default, when a new (non GAC) reference is added to a VS Project, it's Copy Local property is set to true and it's Specific Version property is set to false.

ProjectReference item in .csproj msbuild script

<ProjectReference Include="..\CRM.Business\CRM.Business.csproj">
  <Project>{4E950B82-167B-4A42-8ECA-2F7607A9746D}</Project>
  <Name>CRM.Business</Name>
</ProjectReference>

https://msdn.microsoft.com/en-us/library/bb629388.aspx
зачем нужно и как применяется Package?
Optional string. The path of the project file that is being referenced.
Project
Optional string. A GUID for the reference, in the form {12345678-1234-1234-1234-1234567891234}.
Name
Optional string. The display name of the reference.

NativeReference

https://msdn.microsoft.com/en-us/library/bb629388.aspx
Про них важно знать, чтобы пропатчить HintPath при обработке .gitmodules

if I add a ProjectReference Element to link to a native library, that I build errors deep inside of the MSBuild scripts.

Put this in the project just inside the </Project> tag
<Target Name="MyProjectReference">
  <ItemGroup>
    <ProjectReference Include="…what you had here…"/>
  </ItemGroup>
</Target>
Then, edit the root <Project> tag to add an attribute InitialTargets="MyProjectReference"
This makes it still work, but hides it from VS.

reference in Manifest

Build-time only dependency

https://blogs.msdn.microsoft.com/kirillosenkov/2015/04/04/how-to-have-a-project-reference-without-referencing-the-actual-binary/

Sometimes you want a project reference from project B to project A to indicate a build-time dependency,
but you don’t actually want assembly B to reference assembly A (maybe because it’s a runtime-only dependency or loaded using reflection).

set the ReferenceOutputAssembly metadata:
    <ProjectReference Include="..\ProjectA\ProjectA.csproj">
      <Project>{b402782f-de0a-41fa-b364-60612a786fb2}</Project>
      <Name>ProjectA</Name>
      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
    </ProjectReference>
This will indicate a dependency between projects to MSBuild, but won’t pass the output assembly of ProjectA as a reference to the compiler when compiling ProjectB.

If you set ReferenceOutputAssembly to false you will notice that MSBuild stops copying the assembly A into the output directory for assembly B. If you would still like to copy assembly A to the output of assembly B (without referencing it), use this:
    <ProjectReference Include="..\ProjectA\ProjectA.csproj">
      <Project>{b402782f-de0a-41fa-b364-60612a786fb2}</Project>
      <Name>ProjectA</Name>
      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
      <OutputItemType>Content</OutputItemType>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </ProjectReference>
This will produce an item of type Content in project B that includes the output assembly of project A, and the CopyToOutputDirectory metadata on that item will be set to Copy If Newer.

many people have asked how to also copy the .pdb file in addition to the .exe/.dll.
    <ProjectReference Include="..\ProjectA\ProjectA.csproj">
      <Project>{b402782f-de0a-41fa-b364-60612a786fb2}</Project>
      <Name>ProjectA</Name>
      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
      <OutputItemType>Content</OutputItemType>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <Targets>Build;DebugSymbolsProjectOutputGroup</Targets>
    </ProjectReference>

Need to check if this work on linux with .mdb instead of .pdb

Solution-level reference

You could use Visual Studio Configuration Manager dialog to declare the dependency between projects in the .sln file, but don’t.
Project("{C4ABA494-43D0-400A-9917-20E167A12CFD}") = "Foo", "...\Foo.vcxproj", "{5A42640E-9E0A-442B-8A40-AA91AD5444AC}"
    ProjectSection(ProjectDependencies) = postProject
        ...
        {2CE32AE0-B129-40BA-B06E-A628FA149AB3} = {2CE32AE0-B129-40BA-B06E-A628FA149AB3}
    EndProjectSection
EndProject
...
Project("{C4ABA494-43D0-400A-9917-20E167A12CFD}") = "baz", "...\baz.csproj", "{2CE32AE0-B129-40BA-B06E-A628FA149AB3}"
    ProjectSection(ProjectDependencies) = postProject
        ...
    EndProjectSection
EndProject
MsBuild is NOT using solution file dependencies.
Configuration Manager is a foreign concept in MSBuild, it’s a legacy feature that shouldn’t exist.
http://blogs.msdn.com/b/visualstudio/archive/2010/12/21/incorrect-solution-build-ordering-when-using-msbuild-exe.aspx
MsBuild is using project references inside the project files to create build order.

I do recommend project references in general, on the principle of “express the dependency in the place it applies” – you can look at the project and see that the dependency is correct, and you can include the projects in more than one solution easily.

when you build a solution with Visual Studio what it actually does internally is convert the solution file to an msbuild script that contains a line to build the projects:
<MSBuild Projects="my_project.csproj" Targets="Build" />

Circular References

In .Net framework class library there are some cases where such circular references exist
http://stackoverflow.com/questions/1316518/how-did-microsoft-create-assemblies-that-have-circular-references
AFAIK, Microsoft uses a special build system in order to create such circular references.
I've heard it can be done with something called "temporary metadata only" assemblies.
Microsoft uses internal tool which uses ILDASM to disassemble assemblies, strip all internal/private stuff and method bodies and recompile IL again (using ILASM) into what is called 'dehydrated assembly' or metadata assembly. This is done every time public interface of assembly is changed.
During the build, metadata assemblies are used instead of real ones. That way cycle is broken.
They are not strongly signed until after build (they are delay signed), so dehydrated assemblies are not signed.
"In my knowledge, this is not documented publicly."

but Visual Studio refuse to add (project) reference in such case.
Its pretty easy to do in Visual Studio as long as you don't use project references...

One possible approach is to use conditional compilation (#if) to first compile a System.dll that doesn't depend on those other assemblies, then compile the other assemblies, and at last recompile System.dll to include the parts depending on Xml and Configuration.
Conditional references can be easily done by editing the .csproj file. Just add a Condition attribute to the <Reference> element.

as part of the cleanup of .NET Core, there will be no more circular references.

Conditional references

https://whathecode.wordpress.com/2012/10/07/conditional-project-or-library-reference-in-visual-studio/

Conditional project or library reference in Visual Studio

project uses two of my other open source libraries, located in separate repositories. Adding these projects to the solution and referencing them as project references has the advantage of easier debugging and facilitates making changes to them. However, I do not want to force anyone interested in making changes to the main project to having to download the other repositories as well. Therefore I opted to use DLL references. This has one major downside. Whenever I do make changes to one of the dependent libraries, I need to manually copy the newly compiled DLLs to the main project.

Wouldn’t it be easy to use two separate solution files, one with project references, and one with DLL references?
(почему бы второй solution не создавать автоматически во время роллинга? Патчить .csproj-файлы всё равно прийдётся. Но можно патчить просто, а можно вставлять эти условные блоки. Тогда работоспособность локальных ссылок сохранится, но не ясно, в каком сценарии такая способность нужна...)

You’ll encounter is project references aren’t stored in the .sln file but in the .csproj file, which makes sense really.
Since the .csproj file is shared by both solutions, we’ll have to conditionally reference either our DLLs or our projects, depending on which solution the project is opened in. This is possible using MSBuild, but you will need to create a new project configuration. Setting the configuration of the project differently in one solution from the other allows you to differentiate between the two of them. The following is part of a .csproj file which conditionally loads a reference by checking whether the project configuration is set to ‘Debug With Project References’.

  <Choose>
    <When Condition="'$(Configuration)' == 'Debug With Project References'">
      <ItemGroup>
        <ProjectReference Include="..\SomeProject\SomeProject.csproj">
          <Project>{6CA7AB2C-2D8D-422A-9FD4-2992BE62720A}</Project>
          <Name>SomeProject</Name>
        </ProjectReference>
      </ItemGroup>
    </When>
    <Otherwise>
      <ItemGroup>
        <Reference Include="SomeProject">
          <HintPath>..\Libraries\SomeProject.dll</HintPath>
        </Reference>
      </ItemGroup>
    </Otherwise>
  </Choose>

You could very well stop here, but I wanted to resolve another issue as well. Unless you follow a really strict folder structure, and force everyone else who wants to open your solution to do the same, the path used to reference the project can vary between people. Remember we are using separate repositories here, and the referenced projects aren’t included in the repository of the main solution, so relative paths don’t necessarily work. It is possible to specify the paths in an external ‘configuration’ file, and importing it into the .csproj file. Additionally, as a fallback when the project can’t be found at the given path, it is also useful to load the DLL instead. This way you can choose to load one or more, but not necessarily all references as a project reference.

The configuration file (ProjectReferences.txt):

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup Condition="$(Configuration) == 'Debug With Project References'">
	<SomeProject>..\SomeProject</SomeProject>
  </PropertyGroup>
</Project>

Segment from the .csproj file:

  <Import Project="..\ProjectReferences.txt" />
  <Choose>
    <When Condition="Exists($(SomeProject))">
      <ItemGroup>
        <ProjectReference Include="$(SomeProject)\SomeProject.csproj">
          <Project>{6CA7AB2C-2D8D-422A-9FD4-2992BE62720A}</Project>
          <Name>SomeProject</Name>
        </ProjectReference>
      </ItemGroup>
    </When>
    <Otherwise>
      <ItemGroup>
        <Reference Include="SomeProject">
          <HintPath>..\Libraries\SomeProject.dll</HintPath>
        </Reference>
      </ItemGroup>
    </Otherwise>
  </Choose>

Notice the project configuration check now occurs in the configuration file. The ‘$(SomeProject)’ property is only set when using the ‘Debug With Project References’ configuration, thus in all other scenarios the DLL will be loaded instead since the ‘Exists()‘ check will fail.

One last issue remains. We still need to manually copy the latest compiled DLLs to the ‘Libraries’ folder when changes were made for the solution which uses them to work as expected. We can exploit the fact that we now have the relevant project paths available in the configuration file. Using a post build event you can call a script which parses the XML data, and copies the DLLs to the correct location. This script should be called conditionally, only for the solution which includes the project references, and ideally also only for a Release build. I used a small Ruby script which offers a lot more flexibility than a Batch script.

The post build event:

if "$(SolutionName)" == "SomeSolution With Project References" if "$(ConfigurationName)" == "Release" ruby $(SolutionDir)UpdateLibraryDlls.rb $(SolutionDir)