c# .net visual-studio msbuild copy-local

c# - ¿Es "Copy Local" transitivo para referencias de proyectos?



.net visual-studio (2)

Wrt. el engañado propuesto: ya que este aquí queston sugiere lo contrario de la pregunta vinculada , me gustaría pensar que no es una tontería.

Primero , leí ¿Cuál es la mejor práctica para "Copiar local" y con las referencias del proyecto? (también esto ) y tendré que probar esto de todos modos, pero obtener comentarios generales sobre esto parece necesario ya que los documentos sobre este tema son horribles y solo estoy en VS2010 y tal vez cambiaron algo en las versiones más nuevas que serán bueno saber.

En segundo lugar , solo me interesan las referencias de proyectos para esta pregunta, ya que he leído que las reuniones del GAC se manejan de manera diferente y el GAC es irrelevante para mi problema.

Tercero , después de leer el truco sugerido, pero más que la buena respuesta aquí por @Albireo, también parece que es importante diferenciar las dependencias de archivos , donde la dependencia hace referencia a un archivo ensamblador dll y dependencias de proyecto (es decir, lo que estoy preguntando about), donde la dependencia hace referencia a un proyecto e implícitamente al archivo de salida de ese proyecto.

De todos modos, esta es la situación, algo peculiar, creo, pero aún así:

  • 2 proyectos ejecutables C #
  • n proyectos de ensamblaje C # dll
  • Los 2 ejecutables tienen directorios de salida diferentes, ya que se implementarán por separado y de esa manera también estarán separados en la máquina del desarrollador.
  • Los 2 ejecutables tienen dependencias en algunos de los ensamblados de DLL (que pueden depender el uno del otro)
  • Hay tres directorios de salida:
    • /x1 para el proyecto ejecutable 1
    • /x2 para el proyecto ejecutable 2
    • /lib para todos los ensamblajes dll

Los ensamblados de DLL tienen Copy Local establecido en false para sus referencias de proyecto, ya que todos se compilan en el mismo directorio de salida.

Los 2 proyectos ejecutables han configurado Copy Local en true para todas las referencias de proyecto de ensamblado DLL a las que hacen referencia directamente, de modo que las DLL se copiarán en /x1 /x2 respectivamente.

La pregunta ahora es wrt. a archivos DLL a los que un proyecto ejecutable no hace referencia directa, sino transitoriamente a través de un ensamblado al que se hace referencia: ¿Se copiarán en la carpeta de salida del ejecutable los ensamblados, que solo se referencian transitivamente a través de otro ensamblaje , cuando se configura "Copiar local" en cierto en la primera asamblea?

Ejemplo:

  • x1.csproj (egOutput = x1/one.exe )
    • Referencia: dlA.csproj (p. Ej. Output = lib/a.dll ) con Copy Local = *true*
    • (sin referencia directa en b.dll)
  • dlA.csproj (por ejemplo, Output = lib/a.dll )
    • Referencia: dlB.csproj (por ejemplo, Output = lib/b.dll ) con Copy Local = **false**
    • (sin referencia directa en c.dll)
  • dlC.csproj (por ejemplo, Output = lib/c.dll )
    • (sin más referencias relevantes)

Por lo tanto, tenemos una dependencia lógica de one.exe -> a.dll -> b.dll -> c.dll , donde solo se a.dll con el directorio de salida de one.exe . ¿Los otros dos dlls también se copiarán en el directorio de salida? ¿Está esto documentado en alguna parte?

Y, sí, lo intenté. Y, sí, parece funcionar, pero todavía no lo he trabajado lo suficiente y, de todos modos, tal vez haya algo más que me haya pasado por alto. (Y también está la pregunta wrt. Cualquier documento oficial).


Es muy sencillo, no tiene nada que ver con Copy Local. MSBuild busca en los metadatos de un ensamblaje para ver cuáles son las dependencias de un ensamblaje. Entonces, puede ejecutar ildasm.exe en el ensamblado y hacer doble clic en el Manifiesto. Asegúrese de probar esto para obtener una idea. Verá las directivas .assembly . Insertado por el compilador cuando construyó el ensamblado, solo se enumerarán los ensamblados a los que se hace referencia que usó realmente en su código.

Si MSBuild puede encontrar dicho ensamblaje en el mismo directorio, lo copiará automáticamente. Si no, omitirá la copia en silencio .

A partir de esto, puede deducir los modos de falla. No puede copiar archivos DLL no administrados, no aparecen en los metadatos. No puede copiar ensamblajes de los que tenga una dependencia indirecta a través de Assembly.Load / From (), tampoco aparecen en los metadatos. No puede copiar ensamblajes que aún no se han creado, un problema de compilación. Y no puede copiar ensamblajes cuya propiedad Copy Local establezca en False. Que normalmente es solo una opción válida si el ensamblaje está presente en el GAC, no se requiere copia.

Para esos casos, necesita ayuda, XCOPY en un evento posterior a la construcción hace el trabajo.


también parece que es importante diferenciar dependencias de archivos, donde la dependencia hace referencia a un archivo ensamblador dll y dependencias de proyecto (es decir, de lo que estoy preguntando), donde la dependencia hace referencia a un proyecto e implícitamente al archivo de salida de ese proyecto.

No, realmente no.

A MSBuild realmente no le importa si la referencia apunta a otro proyecto en la solución o a una DLL.

Si ProjectA depende de ProjectB para compilar ProjectA ProjectB debe estar ya construido (y actualizado), MSBuild extraerá su DLL (no su código C #) y lo vinculará a ProjectA .

Agregar una referencia de proyecto en lugar de una DLL es "azúcar sintáctica" para su conveniencia: de esta forma, MSBuild sabe que debe elegir el resultado del proyecto referenciado, cualquiera que sea el resultado.

De lo contrario, tendrá que precompilar manualmente la dependencia, encontrar su DLL y vincularla al proyecto, repitiendo el proceso cada vez que cambie la configuración de la compilación, mueva o cambie el nombre de las cosas. No es realmente práctico.

¿Los otros dos dlls también se copiarán en el directorio de salida?

Si se utiliza cualquier tipo de elemento de una dependencia directamente del proyecto donde se hace referencia al ensamblado, esa referencia se copiará.

Un ejemplo podría ser este diseño de solución:

  • Mi solución
    • MySolution.ConsoleApplication
    • MySolution.FirstDependency
    • MySolution.SecondDependency
    • MySolution.ThirdDependency
    • MySolution.FourthDependency

Con esta cadena de dependencia:

  • MySolution.ConsoleApplication
    • MySolution.FirstDependency
      • MySolution.SecondDependency
        • MySolution.ThirdDependency
        • MySolution.FourthDependency

Si construye esta solución, notará que en el directorio de salida MySolution.ConsoleApplication estarán los archivos DLL para MySolution.FirstDependency , MySolution.SecondDependency y MySolution.ThirdDependency pero no para MySolution.FourthDependency .

¿Por que es esto entonces? Cuando MSBuild construye MySolution.SecondDependency se da cuenta de que hay una dependencia declarada en MySolution.FourthDependency , pero como no puede encontrar ningún uso de ningún tipo de elemento de MySolution.FourthDependency en el código MySolution.SecondDependency , decide realizar una cierta "optimización" y omite el ensamblado MySolution.FourthDependency de la salida.

Este mismo problema me mordió en el pasado cuando añadí a través de NuGet AutoMapper a una "profunda dependencia": al agregar AutoMapper se agregan dos referencias de ensamblaje, AutoMapper y AutoMapper.Net4 , donde el segundo ensamblaje se carga por el primero a través de la reflexión cuando necesita funcionar cierto tipo de acción en los nuevos objetos de colección introducidos por .NET Framework 4. Dado que el segundo ensamblaje se carga mediante reflexión, MSBuild piensa que no se utiliza y no se molesta en copiarlo.

Entonces, , se copiarán siempre y cuando los uses directamente y no a través del reflejo.

¿Está esto documentado en alguna parte?

Este comportamiento parece ser una "característica" de MSBuild. Logré encontrar una publicación en el blog de algunos usuarios de Microsoft cuando experimenté este problema, pero no puedo encontrarlo de nuevo en este momento.