Tengo un gran archivo de solución de C # (~ 100 proyectos), y estoy tratando de mejorar los tiempos de compilación. Creo que "Copiar local" es un desperdicio en muchos casos para nosotros, pero me pregunto sobre las mejores prácticas.
En nuestro .sln, tenemos la aplicación A que depende del conjunto B, que depende del conjunto C. En nuestro caso, hay docenas de "B" y un puñado de "C". Como todos están incluidos en el archivo .sln, estamos utilizando referencias de proyectos. Todos los ensamblados actualmente se integran en $ (SolutionDir) / Debug (o Release).
De forma predeterminada, Visual Studio marca estas referencias de proyecto como "Copiar local", lo que hace que cada "C" se copie en $ (SolutionDir) / Debug una vez por cada "B" que se genera. Esto parece un desperdicio. ¿Qué puede salir mal si solo apago "Copiar local"? ¿Qué hacen otras personas con sistemas grandes?
SEGUIMIENTO:
Muchas respuestas sugieren dividir la compilación en archivos .sln más pequeños ... En el ejemplo anterior, construiría primero las clases básicas "C", seguidas por la mayor parte de los módulos "B", y luego algunas aplicaciones ". UNA". En este modelo, necesito tener referencias no relacionadas con el proyecto a C desde B. El problema con el que me encuentro allí es que "Debug" o "Release" se intercalan en el camino de la pista y termino construyendo mis versiones de lanzamiento de "B" contra las versiones de depuración de "C".
Para aquellos de ustedes que dividen la compilación en múltiples archivos .sln, ¿cómo manejan este problema?
fuente
<Private>True</Private>
en un csproj?.sln
en pequeños rompe el cálculo de interdependencia automática de VS de<ProjectReference/>
s. Yo mismo me mudé de varios pequeños.sln
más a uno solo.sln
solo porque VS causa menos problemas de esa manera ... Entonces, ¿tal vez el seguimiento está asumiendo una solución no necesariamente mejor para la pregunta original? ;-)Respuestas:
En un proyecto anterior trabajé con una gran solución con referencias de proyectos y también me topé con un problema de rendimiento. La solución fue triple:
Establezca siempre la propiedad Copiar local en falso y aplique esto mediante un paso personalizado de msbuild
Establezca el directorio de salida para cada proyecto en el mismo directorio (preferiblemente en relación con $ (SolutionDir)
Los objetivos de cs predeterminados que se envían con el marco calculan el conjunto de referencias que se copiarán en el directorio de salida del proyecto que se está creando actualmente. Como esto requiere calcular un cierre transitivo bajo la relación 'Referencias', esto puede ser MUY costoso. Mi solución para esto fue redefinir el
GetCopyToOutputDirectoryItems
objetivo en un archivo de objetivos comunes (p. Ej.Common.targets
) Que se importa en cada proyecto después de la importación delMicrosoft.CSharp.targets
. Como resultado, cada archivo de proyecto se verá así:Esto redujo nuestro tiempo de construcción en un momento dado de un par de horas (principalmente debido a restricciones de memoria), a un par de minutos.
El redefinido
GetCopyToOutputDirectoryItems
se puede crear copiando las líneas 2,438–2,450 y 2,474–2,524 deC:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets
enCommon.targets
.Para completar, la definición de objetivo resultante se convierte en:
Con esta solución en su lugar, me pareció factible tener hasta> 120 proyectos en una solución, esto tiene el beneficio principal de que VS puede determinar el orden de construcción de los proyectos en lugar de hacerlo a mano dividiendo su solución .
fuente
Le sugiero que lea los artículos de Patric Smacchia sobre ese tema:
También puede leer este artículo para ayudarlo a reducir el número de proyectos y mejorar el tiempo de compilación.
fuente
Sugiero tener una copia local = false para casi todos los proyectos, excepto el que está en la parte superior del árbol de dependencias. Y para todas las referencias en el conjunto superior, copie local = true. Veo a muchas personas sugiriendo compartir un directorio de salida; Creo que esta es una idea horrible basada en la experiencia. Si su proyecto de inicio contiene referencias a un archivo DLL que cualquier otro proyecto tiene una referencia a usted, en algún momento experimentará una violación de acceso / uso compartido, incluso si copia local = false en todo y su compilación fallará. Este problema es muy molesto y difícil de localizar. Sugiero completamente que se mantenga alejado de un directorio de salida de fragmentos y, en lugar de tener el proyecto en la parte superior de la cadena de dependencia, escriba los ensamblados necesarios en la carpeta correspondiente. Si no tiene un proyecto en la "parte superior", entonces sugeriría una copia posterior a la compilación para que todo esté en el lugar correcto. Además, trataría de tener en cuenta la facilidad de depuración. Cualquier proyecto exe que todavía deje copy local = true para que la experiencia de depuración F5 funcione.
fuente
Estás en lo correcto. CopyLocal matará absolutamente tus tiempos de construcción. Si tiene un árbol de origen grande, debe deshabilitar CopyLocal. Desafortunadamente, no es tan fácil como debería ser deshabilitarlo limpiamente. He respondido esta pregunta exacta acerca de deshabilitar CopyLocal en Cómo anulo la configuración de CopyLocal (Privado) para referencias en .NET desde MSBUILD . Echale un vistazo. Tanto como mejores prácticas para grandes soluciones en Visual Studio (2008).
Aquí hay más información sobre CopyLocal tal como la veo.
CopyLocal se implementó realmente para admitir la depuración local. Cuando prepare su aplicación para el empaquetado y la implementación, debe compilar sus proyectos en la misma carpeta de salida y asegurarse de tener todas las referencias que necesita allí.
Escribí sobre cómo lidiar con la construcción de grandes árboles de origen en el artículo MSBuild: Mejores prácticas para crear compilaciones confiables, Parte 2 .
fuente
En mi opinión, tener una solución con 100 proyectos es un GRAN error. Probablemente podría dividir su solución en pequeñas unidades lógicas válidas, lo que simplificaría tanto el mantenimiento como las compilaciones.
fuente
Me sorprende que nadie haya mencionado el uso de enlaces duros. En lugar de copiar los archivos, crea un enlace rígido al archivo original. Esto ahorra espacio en el disco y acelera enormemente la compilación. Esto se puede habilitar en la línea de comando con las siguientes propiedades:
/ p: CreateHardLinksForAdditionalFilesIfPossible = true; CreateHardLinksForCopyAdditionalFilesIfPossible = true; CreateHardLinksForCopyFilesToOutputDirectoryIfPossible = true; CreateHardLinksForCopyLocalIfPossible = true; CreateHardLinksInterPub
También puede agregar esto a un archivo de importación central para que todos sus proyectos también puedan obtener este beneficio.
fuente
Si definió la estructura de dependencia a través de referencias de proyecto o dependencias de nivel de solución, es seguro activar "Copiar local". Incluso diría que es una práctica recomendada, ya que eso le permitirá usar MSBuild 3.5 para ejecutar su compilación en paralelo ( via / maxcpucount) sin procesos diferentes que se tropiecen entre sí al intentar copiar ensamblados a los que se hace referencia.
fuente
nuestra "mejor práctica" es evitar soluciones con muchos proyectos. Tenemos un directorio llamado "matriz" con versiones actuales de ensamblajes, y todas las referencias son de este directorio. Si cambia algún proyecto y puede decir "ahora el cambio está completo", puede copiar el ensamblaje en el directorio "matriz". Por lo tanto, todos los proyectos que dependan de este ensamblado tendrán la versión actual (= última).
Si tiene pocos proyectos en solución, el proceso de compilación es mucho más rápido.
Puede automatizar el paso "copiar ensamblado al directorio de matriz" utilizando macros de Visual Studio o con "menú -> herramientas -> herramientas externas ...".
fuente
No necesita cambiar los valores de CopyLocal. Todo lo que necesita hacer es predefinir un $ (OutputPath) común para todos los proyectos en la solución y preestablecer $ (UseCommonOutputDirectory) en verdadero. Vea esto: http://blogs.msdn.com/b/kirillosenkov/archive/2015/04/04/using-a-common-intermediate-and-output-directory-for-your-solution.aspx
fuente
Establecer CopyLocal = false reducirá el tiempo de compilación, pero puede causar diferentes problemas durante la implementación.
Hay muchos escenarios, cuando necesita que Copiar Local 'se deje en Verdadero, por ejemplo
Los posibles problemas que se describen en las preguntas SO
"¿ Cuándo se debe establecer copy-local en true y cuándo no? ",
" Mensaje de error 'No se puede cargar uno o más de los tipos solicitados. Recupere la propiedad LoaderExceptions para obtener más información'. "
y la respuesta de aaron-stainback a esta pregunta.
Mi experiencia con la configuración de CopyLocal = false NO fue exitosa. Consulte la publicación de mi blog "NO cambiar" las referencias del proyecto Copiar local ”a falso, a menos que comprenda las subsecuencias".
El momento de resolver los problemas sobrepesa los beneficios de configurar copyLocal = false.
fuente
CopyLocal=False
seguramente causará algunos problemas, pero hay soluciones para esos. Además, debe corregir el formato de su blog, apenas es legible, y decir que "me advirtió <asesor aleatorio> de <empresa aleatoria> sobre posibles errores durante las implementaciones" no es un argumento. Necesitas desarrollarte.Tiendo a construir en un directorio común (por ejemplo, \ bin), por lo que puedo crear pequeñas soluciones de prueba.
fuente
Puede intentar usar una carpeta donde se copiarán todos los ensamblajes que se comparten entre proyectos, luego haga una variable de entorno DEVPATH y establezca
<developmentMode developerInstallation="true" />
en el archivo machine.config en la estación de trabajo de cada desarrollador. Lo único que debe hacer es copiar cualquier versión nueva en su carpeta donde apunte la variable DEVPATH.
Divida también su solución en pocas soluciones más pequeñas si es posible.
fuente
Puede que esta no sea la mejor práctica, pero así es como trabajo.
Noté que Managed C ++ volca todos sus binarios en $ (SolutionDir) / 'DebugOrRelease'. Así que dejé todos mis proyectos de C # allí también. También desactivé la "Copia local" de todas las referencias a proyectos en la solución. Tuve una notable mejora en el tiempo de construcción en mi pequeña solución de 10 proyectos. Esta solución es una mezcla de C #, C ++ administrado, C ++ nativo, servicio web C # y proyectos de instalación.
Tal vez algo está roto, pero como esta es la única forma en que trabajo, no lo noto.
Sería interesante descubrir lo que estoy rompiendo.
fuente
Por lo general, solo necesita Copiar Local si desea que su proyecto use la DLL que está en su Bin frente a lo que está en otro lugar (el GAC, otros proyectos, etc.)
Tendería a estar de acuerdo con las otras personas en que también debería intentar, si es posible, romper esa solución.
También puede usar el Administrador de configuración para crear diferentes configuraciones de compilación dentro de esa solución que solo creará conjuntos de proyectos dados.
Parecería extraño si los 100 proyectos dependieran el uno del otro, por lo que deberías poder dividirlo o usar Configuration Manager para ayudarte.
fuente
Puede hacer que sus referencias de proyectos apunten a las versiones de depuración de los dlls. Que en su secuencia de comandos msbuild, puede configurar el
/p:Configuration=Release
, por lo que tendrá una versión de lanzamiento de su aplicación y todos los conjuntos de satélites.fuente
Si desea tener un lugar central para hacer referencia a un archivo DLL utilizando copy local false, fallará sin el GAC a menos que haga esto.
http://nbaked.wordpress.com/2010/03/28/gac-alternative/
fuente
Si la referencia no está contenida en el GAC, debemos establecer la Copia local en verdadero para que la aplicación funcione, si estamos seguros de que la referencia estará preinstalada en el GAC, entonces se puede establecer en falso.
fuente
Bueno, ciertamente no sé cómo funcionan los problemas, pero tuve contacto con una solución de compilación que se ayudó de tal manera que todos los archivos creados se pusieron en un disco RAM con la ayuda de enlaces simbólicos .
c: \ carpeta de soluciones \ obj -> ramdisk r: \ carpeta de soluciones \ obj \
También puede indicarle al estudio visual qué directorio temporal puede usar para la compilación.
En realidad, eso no fue todo lo que hizo. Pero realmente me impactó mi comprensión del rendimiento.
100% de uso del procesador y un gran proyecto en menos de 3 minutos con todas las dependencias.
fuente