Wie hat Microsoft Assemblys mit Zirkelverweisen erstellt?

107

In der .NET BCL gibt es Zirkelverweise zwischen:

  • System.dll und System.Xml.dll
  • System.dll und System.Configuration.dll
  • System.Xml.dll und System.Configuration.dll

Hier ist ein Screenshot von .NET Reflector, der zeigt, was ich meine:

Geben Sie hier die Bildbeschreibung ein

Wie Microsoft diese Assemblys erstellt hat, ist mir ein Rätsel. Ist ein spezieller Kompilierungsprozess erforderlich, um dies zu ermöglichen? Ich stelle mir vor, dass hier etwas Interessantes los ist.

Drew Noakes
quelle
2
Sehr gute Frage. Ich habe mir nie die Zeit genommen, dies zu überprüfen, aber ich bin gespannt auf die Antwort. In der Tat scheint Dykam einen vernünftigen geliefert zu haben.
Noldorin
3
Warum werden diese DLLs nicht zu einer zusammengeführt, wenn sie alle einander benötigen? Gibt es dafür einen praktischen Grund?
Andreas Petersson
1
Interessante Frage ... Ich würde gerne Eric Lipperts Antwort auf diese Frage erfahren! Und wie Andreas sagte, frage ich mich, warum sie nicht alles in dieselbe Versammlung gebracht haben ...
Thomas Levesque
Wenn eine Baugruppe aktualisiert werden muss, müssen sie die anderen nicht berühren. Das ist der einzige Grund, den ich sehe. Interessante Frage
Atmocreations
2
Schauen Sie sich diese Präsentation an (Asmmeta-Dateien): msakademik.net/academicdays2005/Serge_Lidin.ppt
Mehrdad

Antworten:

58

Ich kann nur sagen, wie das Mono-Projekt das macht. Der Satz ist recht einfach, obwohl er ein Code-Chaos verursacht.

Sie kompilieren zuerst System.Configuration.dll, ohne dass der Teil den Verweis auf System.Xml.dll benötigt. Danach kompilieren sie System.Xml.dll auf normale Weise. Jetzt kommt die Magie. Sie kompilieren System.configuration.dll neu, wobei der Teil den Verweis auf System.Xml.dll benötigt. Jetzt gibt es eine erfolgreiche Zusammenstellung mit dem Zirkelverweis.

Zusamenfassend:

  • A wird kompiliert, ohne dass der Code B und den Verweis auf B benötigt.
  • B wird kompiliert.
  • A wird neu kompiliert.
Dykam
quelle
1
Es wird von Visual Studio blockiert, kann jedoch direkt über den Befehlszeilen-Compiler (csc.exe) ausgeführt werden. Siehe meine Antwort.
Alfred Myers
14
Ich weiß. Das Haupt-Build-System von Mono ist nicht Visual Studio. Vermutlich ist Microsoft auch nicht.
Dykam
35

RBarryYoung und Dykam sind auf etwas. Microsoft verwendet ein internes Tool, das ILDASM verwendet, um Assemblys zu zerlegen, alle internen / privaten Inhalte und Methodenkörper zu entfernen und IL (mithilfe von ILASM) erneut in eine sogenannte "dehydrierte Assembly" oder Metadatenassemblierung zu kompilieren. Dies erfolgt jedes Mal, wenn die öffentliche Schnittstelle der Assembly geändert wird.

Während des Builds werden Metadaten-Assemblys anstelle von realen verwendet. Auf diese Weise wird der Zyklus unterbrochen.

Srdjan Jovcic
quelle
1
Interessante Antwort, haben Sie Links?
Henk Holterman
Ich versuche, einen externen Verweis auf das Tool zu finden. Ich glaube nicht, dass es außerhalb von Microsoft veröffentlicht wird, aber das Konzept ist einfach: Disassemble-Strip Internals-Remontemble.
Srdjan Jovcic
Einverstanden - interessante Antwort. Einige Links, um dies zu sichern, wären gut.
Drew Noakes
Ja, so wird es tatsächlich gemacht (aus persönlicher Erfahrung).
Pavel Minaev
1
Sie werden erst nach dem Build stark signiert (sie werden verzögert signiert), sodass dehydrierte Assemblys nicht signiert werden.
Srdjan Jovcic
26

Es kann so gemacht werden, wie Dykam es beschrieben hat, aber Visual Studio hindert Sie daran.

Sie müssen den Befehlszeilen-Compiler csc.exe direkt verwenden.

  1. csc / target: Bibliothek ClassA.cs

  2. csc / target: library ClassB.cs /reference:ClassA.dll

  3. csc / target: Bibliothek ClassA.cs ClassC.cs /reference:ClassB.dll


//ClassA.cs
namespace CircularA {
    public class ClassA {
    }
}


//ClassB.cs
using CircularA;
namespace CircularB {
    public class ClassB : ClassA  {
    }
}


//ClassC.cs
namespace CircularA {
    class ClassC : ClassB {
    }
}
Alfred Myers
quelle
Sie können dies auch in Visual Studio tun, obwohl es auch ziemlich schwierig ist. Die grundlegende Methode besteht darin, # if's zu verwenden und die Referenz mit dem Lösungs-Explorer zu entfernen. Dies wird im dritten Schritt umgekehrt. Eine andere Art, an die ich denke, ist eine dritte Projektdatei, die dieselben Dateien, aber unterschiedliche Referenzen enthält. Dies würde funktionieren, da Sie die Erstellungsreihenfolge angeben können.
Dykam
Soweit ich weiß, kann ich es hier nicht testen.
Dykam
Das würde ich wirklich gerne sehen. Nach dem, was ich hier experimentiert habe, stoppt Sie die IDE, sobald Sie versuchen, eine Referenz hinzuzufügen.
Alfred Myers
Ich weiß. Aber ein drittes Projekt hat nicht diese Referenz UND das Symbol #if und wird von der zweiten referenziert, auf die die erste verweist. Kein Zyklus. Der dritte verwendet jedoch den Code des ersten und gibt ihn an den ersten Montageort aus. Eine Baugruppe kann leicht durch eine andere mit denselben Spezifikationen ersetzt werden. Ich denke jedoch, dass eine starke Benennung bei dieser Methode ein Problem verursachen kann.
Dykam
Es ist ein bisschen wie Srdjans Antwort, obwohl eine andere Methode.
Dykam
18

In Visual Studio ist dies ziemlich einfach, solange Sie keine Projektreferenzen verwenden ... Versuchen Sie Folgendes:

  1. Visual Studio öffnen
  2. Erstellen Sie 2 Klassenbibliotheksprojekte "ClassLibrary1" & "ClassLibrary2".
  3. Bauen
  4. Fügen Sie in ClassLibrary1 einen Verweis auf ClassLibrary2 hinzu, indem Sie zu der in Schritt 3 erstellten DLL navigieren.
  5. Fügen Sie in ClassLibrary2 einen Verweis auf ClassLibrary1 hinzu, indem Sie zu der in Schritt 3 erstellten DLL navigieren.
  6. Erneut erstellen (Hinweis: Wenn Sie in beiden Projekten Änderungen vornehmen, müssen Sie zweimal erstellen, um beide Referenzen "frisch" zu machen.)

So machst du es also. Aber im Ernst ... Mach das NIEMALS in einem echten Projekt! Wenn Sie dies tun, bringt Ihnen der Weihnachtsmann dieses Jahr keine Geschenke.

JohannesH
quelle
1
Die einzige Ausnahme ist, wenn es zwischen dem 26. und 31. Dezember ist und die Geschenke bereits gesichert sind
Jesse Hufstetler
6

Ich denke, dies könnte erreicht werden, indem man mit einem azyklischen Satz von Baugruppen beginnt und ILMerge verwendet, um die kleineren Baugruppen dann zu logisch verwandten Gruppen zusammenzufassen.

Steve Gilham
quelle
4

Nun, ich habe es noch nie unter Windows gemacht, aber ich habe es in vielen Compile-Link-RTL-Umgebungen gemacht, die als praktische Vorläufer dafür dienten. Was Sie tun, ist, zuerst Stub- "Ziele" ohne Querverweise zu erstellen, dann zu verknüpfen, dann die Zirkelverweise hinzuzufügen und dann erneut zu verknüpfen. Die Linker kümmern sich im Allgemeinen nicht um kreisförmige Verweise oder das Verfolgen von Verweisketten, sondern nur darum, dass sie jede Referenz für sich auflösen können.

Wenn Sie also zwei Bibliotheken haben, A und B, die aufeinander verweisen müssen, versuchen Sie Folgendes:

  1. Link A ohne Verweise auf B.
  2. Verknüpfe B mit Verweisen auf A.
  3. Link A, Hinzufügen der Refs zu B.

Dykam macht einen guten Punkt, es ist kompilieren, nicht in .Net verlinken, aber das Prinzip bleibt das gleiche: Machen Sie Ihre Quellen mit Querverweisen, mit ihren exportierten Einstiegspunkten, aber mit allen bis auf einen, die ihre eigenen Verweise auf die anderen haben aus. Baue sie so. Entfernen Sie dann die externen Referenzen und erstellen Sie sie neu. Dies sollte auch ohne spezielle Tools funktionieren. Tatsächlich hat dieser Ansatz auf jedem Betriebssystem funktioniert, auf dem ich es jemals ausprobiert habe (ungefähr 6 davon). Obwohl es offensichtlich etwas ist, das es automatisiert, wäre es eine große Hilfe.

RBarryYoung
quelle
Der Satz ist richtig. In der .NET-Welt erfolgt die Verknüpfung jedoch dynamisch und ist kein Problem. Dies ist der Kompilierungsschritt, in dem diese Lösung benötigt wird.
Dykam
Tut mir leid, Sie wieder zu reparieren: P. Die Referenzierung (Verknüpfung) zur Kompilierungszeit erfolgt jedoch in der .Net-Welt, die alles ist, was von dieser spezifischen ECMA-Spezifikation abgeleitet ist. Also Mono, dotGnu und .Net. Nicht Windows selbst.
Dykam
1

Ein möglicher Ansatz besteht darin, die bedingte Kompilierung (#if) zu verwenden, um zuerst eine System.dll zu kompilieren, die nicht von diesen anderen Assemblys abhängt, dann die anderen Assemblys zu kompilieren und schließlich System.dll neu zu kompilieren, um die Teile abhängig von Xml und Aufbau.

Daniel
quelle
1
Leider ist es Ihnen nicht möglich, eine Versammlung unter bestimmten Bedingungen zu referenzieren (ich wünschte, es wäre möglich, es würde in einem meiner Projekte wirklich helfen ...)
Thomas Levesque
1
Bedingte Verweise können einfach durch Bearbeiten der .csproj-Datei durchgeführt werden. Fügen Sie dem Element <Reference> einfach ein Condition-Attribut hinzu.
Daniel
0

Technisch ist es möglich, dass diese überhaupt nicht kompiliert und von Hand zusammengesetzt wurden. Immerhin handelt es sich um Bibliotheken auf niedriger Ebene.

tylermac
quelle
Nicht wirklich. Es gibt nicht viele Low-Level-Sachen, nur einfache. Warum hast du gedacht, es wäre ein niedriges Niveau? Die Laufzeit und Corlib ist niedrig. Verhältnismäßig. Immer noch einfaches C oder C ++, dachte die JIT enthält Low-Level-Zeug.
Dykam