Ich habe eine große C # -Lösungsdatei (~ 100 Projekte) und versuche, die Erstellungszeiten zu verbessern. Ich denke, dass "Copy Local" in vielen Fällen für uns verschwenderisch ist, aber ich wundere mich über Best Practices.
In unserer .sln haben wir Anwendung A in Abhängigkeit von Baugruppe B, die von Baugruppe C abhängt. In unserem Fall gibt es Dutzende von "B" und eine Handvoll von "C". Da diese alle in der .sln enthalten sind, verwenden wir Projektreferenzen. Alle Assemblys sind derzeit in $ (SolutionDir) / Debug (oder Release) integriert.
Standardmäßig markiert Visual Studio diese Projektreferenzen als "Lokal kopieren", was dazu führt, dass jedes "C" für jedes "B", das erstellt wird, einmal in $ (SolutionDir) / Debug kopiert wird. Das scheint verschwenderisch. Was kann schief gehen, wenn ich "Lokal kopieren" ausschalte? Was machen andere Leute mit großen Systemen?
NACHVERFOLGEN:
Viele Antworten deuten darauf hin, dass der Build in kleinere SLN-Dateien aufgeteilt wird. Im obigen Beispiel würde ich zuerst die Foundation-Klassen "C" erstellen, gefolgt vom Großteil der Module "B" und dann einige Anwendungen. " EIN". In diesem Modell muss ich nicht-projektbezogene Verweise auf C von B haben. Das Problem, auf das ich dort stoße, ist, dass "Debug" oder "Release" in den Hinweispfad eingebrannt wird und ich meine Release-Builds von "B" erstelle. gegen Debug-Builds von "C".
Wie gehen Sie mit diesem Problem um, wenn Sie den Aufbau in mehrere SLN-Dateien aufteilen?
quelle
<Private>True</Private>
in einem csproj?.sln
in kleinere bricht jedoch die Berechnung der automatischen Interdependenz von<ProjectReference/>
s durch VS. Ich bin selbst von mehreren kleineren.sln
s zu einem großen.sln
gewechselt, nur weil VS auf diese Weise weniger Probleme verursacht. Vielleicht geht das Follow-up also von einer nicht unbedingt besten Lösung für die ursprüngliche Frage aus? ;-)Antworten:
In einem früheren Projekt habe ich mit einer großen Lösung mit Projektreferenzen gearbeitet und bin auch auf ein Leistungsproblem gestoßen. Die Lösung war dreifach:
Setzen Sie die Eigenschaft Copy Local immer auf false und erzwingen Sie dies über einen benutzerdefinierten msbuild-Schritt
Stellen Sie das Ausgabeverzeichnis für jedes Projekt auf dasselbe Verzeichnis ein (vorzugsweise relativ zu $ (SolutionDir).
Die Standard-cs-Ziele, die mit dem Framework ausgeliefert werden, berechnen die Referenzsätze, die in das Ausgabeverzeichnis des aktuell erstellten Projekts kopiert werden sollen. Da dies die Berechnung eines transitiven Abschlusses unter der Beziehung 'Referenzen' erfordert, kann dies werden SEHR kostspielig werden. Meine Problemumgehung bestand darin, das
GetCopyToOutputDirectoryItems
Ziel in einer gemeinsamen Zieldatei (z. B.Common.targets
) neu zu definieren, die nach dem Import von in jedes Projekt importiert wirdMicrosoft.CSharp.targets
. Das Ergebnis in jeder Projektdatei sieht folgendermaßen aus:Dies reduzierte unsere Erstellungszeit zu einem bestimmten Zeitpunkt von einigen Stunden (hauptsächlich aufgrund von Speicherbeschränkungen) auf einige Minuten.
Die Neudefinition
GetCopyToOutputDirectoryItems
kann durch Kopieren der Zeilen 2.438–2.450 und 2.474–2.524 vonC:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets
nach erstellt werdenCommon.targets
.Der Vollständigkeit halber lautet die resultierende Zieldefinition dann:
Mit dieser Problemumgehung fand ich es praktikabel, bis zu> 120 Projekte in einer Lösung zu haben. Dies hat den Hauptvorteil, dass die Erstellungsreihenfolge der Projekte weiterhin von VS bestimmt werden kann, anstatt dies manuell durch Aufteilen Ihrer Lösung zu tun .
quelle
Ich werde Ihnen vorschlagen, die Artikel von Patric Smacchia zu diesem Thema zu lesen:
Sie können diesen Artikel auch lesen , um die Anzahl Ihrer Projekte zu verringern und die Kompilierungszeit zu verbessern.
quelle
Ich schlage vor, copy local = false für fast alle Projekte zu verwenden, mit Ausnahme des Projekts, das sich oben im Abhängigkeitsbaum befindet. Und für alle Referenzen in der obersten Menge kopieren Sie local = true. Ich sehe viele Leute, die vorschlagen, ein Ausgabeverzeichnis zu teilen. Ich denke, das ist eine schreckliche Idee, die auf Erfahrung basiert. Wenn Ihr Startprojekt Verweise auf eine DLL enthält, auf die jedes andere Projekt verweist, tritt irgendwann eine Verletzung von access \ shared auf, selbst wenn copy local = false für alles ist und Ihr Build fehlschlägt. Dieses Problem ist sehr ärgerlich und schwer zu finden. Ich empfehle dringend, sich von einem Shard-Ausgabeverzeichnis fernzuhalten und anstatt das Projekt oben in der Abhängigkeitskette zu haben, die erforderlichen Assemblys in den entsprechenden Ordner zu schreiben. Wenn Sie kein Projekt oben haben, dann würde ich eine Kopie nach dem Bau vorschlagen, um alles an den richtigen Ort zu bringen. Außerdem würde ich versuchen, die Leichtigkeit des Debuggens im Auge zu behalten. Alle exe-Projekte, die ich noch hinterlasse, sind copy local = true, damit das F5-Debugging funktioniert.
quelle
Du hast Recht. CopyLocal wird Ihre Build-Zeiten absolut zunichte machen. Wenn Sie einen großen Quellbaum haben, sollten Sie CopyLocal deaktivieren. Leider ist es nicht so einfach, es sauber zu deaktivieren. Ich habe diese genaue Frage beantwortet zu deaktivieren Copylocal bei Wie kann ich Copylocal (Private) Einstellung für Referenzen in .NET von MSBUILD außer Kraft setzen . Hör zu. Sowie Best Practices für große Lösungen in Visual Studio (2008).
Hier sind einige weitere Informationen zu CopyLocal, wie ich es sehe.
CopyLocal wurde wirklich implementiert, um das lokale Debuggen zu unterstützen. Wenn Sie Ihre Anwendung für das Packen und Bereitstellen vorbereiten, sollten Sie Ihre Projekte in demselben Ausgabeordner erstellen und sicherstellen, dass Sie alle erforderlichen Referenzen haben.
Ich habe im Artikel MSBuild: Best Practices zum Erstellen zuverlässiger Builds, Teil 2, darüber geschrieben, wie mit dem Erstellen großer Quellbäume umgegangen werden soll .
quelle
Meiner Meinung nach ist eine Lösung mit 100 Projekten ein großer Fehler. Sie könnten Ihre Lösung wahrscheinlich in gültige logische kleine Einheiten aufteilen und so sowohl die Wartung als auch die Erstellung vereinfachen.
quelle
Ich bin überrascht, dass niemand die Verwendung von Hardlinks erwähnt hat. Anstatt die Dateien zu kopieren, wird ein Hardlink zur Originaldatei erstellt. Dies spart Speicherplatz und beschleunigt die Erstellung erheblich. Dies kann in der Befehlszeile mit den folgenden Eigenschaften aktiviert werden:
/ p: CreateHardLinksForAdditionalFilesIfPossible = true; CreateHardLinksForCopyAdditionalFilesIfPossible = true; CreateHardLinksForCopyFilesToOutputDirectoryIfPossible = true; CreateHardLinksForCopyLocalIfPossible = true;
Sie können dies auch einer zentralen Importdatei hinzufügen, damit alle Ihre Projekte diesen Vorteil nutzen können.
quelle
Wenn Sie die Abhängigkeitsstruktur über Projektreferenzen oder über Abhängigkeiten auf Lösungsebene definiert haben, können Sie "Lokal kopieren" sicher deaktivieren. Ich würde sogar sagen, dass dies eine bewährte Methode ist, da Sie MSBuild 3.5 verwenden können, um Ihren Build parallel auszuführen ( via / maxcpucount), ohne dass verschiedene Prozesse übereinander stolpern, wenn versucht wird, referenzierte Assemblys zu kopieren.
quelle
Unsere "Best Practice" besteht darin, bei vielen Projekten Lösungen zu vermeiden. Wir haben ein Verzeichnis mit dem Namen "Matrix" mit aktuellen Versionen von Assemblys, und alle Referenzen stammen aus diesem Verzeichnis. Wenn Sie ein Projekt ändern und "Jetzt ist die Änderung abgeschlossen" sagen können, können Sie die Baugruppe in das Verzeichnis "Matrix" kopieren. Alle Projekte, die von dieser Assembly abhängen, haben also die aktuelle (= neueste) Version.
Wenn Sie nur wenige Projekte in Lösung haben, ist der Erstellungsprozess viel schneller.
Sie können den Schritt "Baugruppe in Matrixverzeichnis kopieren" mithilfe von Visual Studio-Makros oder mit "Menü -> Tools -> externe Tools ..." automatisieren.
quelle
Sie müssen die CopyLocal-Werte nicht ändern. Sie müssen lediglich ein gemeinsames $ (OutputPath) für alle Projekte in der Lösung vordefinieren und $ (UseCommonOutputDirectory) auf true setzen. Siehe dies: http://blogs.msdn.com/b/kirillosenkov/archive/2015/04/04/using-a-common-intermediate-and-output-directory-for-your-solution.aspx
quelle
Wenn Sie CopyLocal = false festlegen, wird die Erstellungszeit verkürzt, es können jedoch während der Bereitstellung unterschiedliche Probleme auftreten.
Es gibt viele Szenarien, in denen Copy Local auf True gesetzt werden muss, z
Die möglichen Probleme, die in SO-Fragen beschrieben werden:
" Wann sollte copy-local auf true gesetzt werden und wann nicht? ",
" Fehlermeldung 'Einer oder mehrere der angeforderten Typen können nicht geladen werden. Rufen Sie die LoaderExceptions-Eigenschaft ab, um weitere Informationen zu erhalten.' "
Und aaron-Stainback ‚s Antwort auf diese Frage.
Meine Erfahrung mit dem Setzen von CopyLocal = false war NICHT erfolgreich. Siehe meinen Blog-Beitrag "NICHT ändern" Lokale Projektreferenzen kopieren auf "falsch", es sei denn, Sie verstehen Teilsequenzen. "
Die Zeit zur Lösung der Probleme übergewichtet die Vorteile der Einstellung von copyLocal = false.
quelle
CopyLocal=False
wird sicherlich einige Probleme verursachen, aber es gibt Lösungen für diese. Außerdem sollten Sie die Formatierung Ihres Blogs korrigieren, da es kaum lesbar ist. Dort zu sagen, dass "Ich wurde von <zufälliger Berater> von <zufälliger Firma> vor möglichen Fehlern während der Bereitstellung gewarnt", ist kein Argument. Sie müssen sich entwickeln.Ich neige dazu, in ein gemeinsames Verzeichnis zu erstellen (z. B. .. \ bin), damit ich kleine Testlösungen erstellen kann.
quelle
Sie können versuchen, einen Ordner zu verwenden, in den alle Assemblys kopiert werden, die von Projekten gemeinsam genutzt werden. Erstellen Sie dann eine DEVPATH-Umgebungsvariable und legen Sie sie fest
<developmentMode developerInstallation="true" />
in der Datei machine.config auf der Workstation jedes Entwicklers fest. Sie müssen lediglich eine neue Version in Ihren Ordner kopieren, auf die die Variable DEVPATH verweist.
Teilen Sie Ihre Lösung nach Möglichkeit auch in einige kleinere Lösungen auf.
quelle
Das ist vielleicht nicht die beste Übung, aber so arbeite ich.
Ich habe festgestellt, dass Managed C ++ alle Binärdateien in $ (SolutionDir) / 'DebugOrRelease' speichert. Also habe ich auch alle meine C # -Projekte dort abgeladen. Ich habe auch die Option "Lokal kopieren" aller Verweise auf Projekte in der Lösung deaktiviert. Ich hatte eine spürbare Verbesserung der Bauzeit in meiner kleinen 10-Projektlösung. Diese Lösung ist eine Mischung aus C # -, verwalteten C ++ -, nativen C ++ -, C # -Webdienst- und Installationsprojekten.
Vielleicht ist etwas kaputt, aber da ich nur so arbeite, merke ich es nicht.
Es wäre interessant herauszufinden, was ich kaputt mache.
quelle
Normalerweise müssen Sie Local nur kopieren, wenn Sie möchten, dass Ihr Projekt die DLL verwendet, die sich in Ihrem Bin befindet, und nicht die, die sich an einem anderen Ort befindet (GAC, andere Projekte usw.).
Ich würde eher mit den anderen Leuten übereinstimmen, dass Sie auch versuchen sollten, wenn möglich, diese Lösung aufzubrechen.
Sie können Configuration Manager auch verwenden, um innerhalb dieser einen Lösung unterschiedliche Build-Konfigurationen vorzunehmen, mit denen nur bestimmte Projektgruppen erstellt werden.
Es erscheint seltsam, wenn sich alle 100 Projekte aufeinander verlassen. Sie sollten also in der Lage sein, sie entweder aufzubrechen oder Configuration Manager zu verwenden, um sich selbst zu helfen.
quelle
Sie können Ihre Projektreferenzen auf die Debug-Versionen der DLLs verweisen lassen. Als in Ihrem msbuild-Skript können Sie das festlegen
/p:Configuration=Release
, sodass Sie eine Release-Version Ihrer Anwendung und aller Satelliten-Assemblys haben.quelle
Wenn Sie einen zentralen Ort zum Verweisen auf eine DLL mit copy local false haben möchten, schlägt dies ohne den GAC fehl, sofern Sie dies nicht tun.
http://nbaked.wordpress.com/2010/03/28/gac-alternative/
quelle
Wenn die Referenz nicht im GAC enthalten ist, müssen wir Copy Local auf true setzen, damit die Anwendung funktioniert. Wenn wir sicher sind, dass die Referenz im GAC vorinstalliert wird, kann sie auf false gesetzt werden.
quelle
Nun, ich weiß sicherlich nicht, wie die Probleme funktionieren, aber ich hatte Kontakt zu einer Build-Lösung, die sich selbst half, so dass alle erstellten Dateien mit Hilfe symbolischer Links auf eine Ramdisk gelegt wurden .
c: \ Lösungsordner \ obj -> Ramdisk r: \ Lösungsordner \ obj \
Sie können dem Visual Studio auch zusätzlich mitteilen, welches temporäre Verzeichnis es für den Build verwenden kann.
Eigentlich war das nicht alles, was es tat. Aber es hat mein Verständnis von Leistung wirklich getroffen.
100% Prozessorauslastung und ein riesiges Projekt in weniger als 3 Minuten mit allen Abhängigkeiten.
quelle