La opción --output de nivel de solución ya no es válida para los comandos relacionados con la compilación.

En el SDK 7.0.200, se ha producido un cambio para que ya no se acepte la opción --output/-o cuando se usa un archivo de solución con los comandos siguientes:

  • build
  • clean
  • pack
  • publish
  • store
  • test
  • vstest

Esto se debe a que la semántica de la propiedad OutputPath, que está controlada por la opción --output/-o, no está bien definida para las soluciones. Los proyectos compilados de esta manera tendrán su salida colocada en el mismo directorio, que es incoherente y ha llevado a una serie de problemas notificados por el usuario.

Este cambio se redujo a un nivel de advertencia de gravedad en el SDK 7.0.201 y se quitó pack de la lista de comandos afectados.

Versión introducida

SDK de .NET 7.0.200, reducido a una advertencia solo en el SDK 7.0.201.

Comportamiento anterior

Anteriormente, si especificaba --output/-o al usar un archivo de solución, la salida de todos los proyectos compilados se colocaba en el directorio especificado en un orden indefinido e incoherente.

Comportamiento nuevo

La CLI de dotnet producirá un error si la opción --output/-o se usa con un archivo de solución. A partir del SDK 7.0.201, se emitirá en su lugar una advertencia y, en caso de dotnet pack, no se generará ninguna advertencia ni error.

Tipo de cambio importante

Este cambio importante puede requerir modificaciones en los scripts de compilación y las canalizaciones de integración continua. Como consecuencia, afecta a la compatibilidad con los archivos binarios y de origen.

Motivo del cambio

Este cambio se realizó porque la semántica de la propiedad OutputPath, que está controlada por la opción --output/-o, no está bien definida para las soluciones. Los proyectos compilados de esta manera tendrán su salida colocada en el mismo directorio, que es incoherente y ha llevado a una serie de problemas notificados por el usuario.

Cuando se compila una solución con la opción --output, la propiedad OutputPath se establece en el mismo valor para todos los proyectos, lo que significa que todos los proyectos tendrán su salida colocada en el mismo directorio. En función de la complejidad de los proyectos de la solución, pueden producirse resultados diferentes e incoherentes. Echemos un vistazo a algunos ejemplos de formas de solución diferentes y cómo se ven afectados por un objeto OutputPath compartido.

Proyecto único, valor TargetFramework único

Imagine una solución que contiene un único proyecto destinado a un único valor TargetFramework, net7.0. En este caso, proporcionar la opción --output equivale a establecer la propiedad OutputPath del archivo del proyecto. Durante una compilación (u otros comandos, pero vamos a limitar por ahora el debate a la compilación), todas las salidas del proyecto se colocarán en el directorio especificado.

Proyecto único, varios valores TargetFramework

Ahora imagine una solución que contiene un único proyecto con varios valores TargetFrameworks, net6.0 y net7.0. Como hay varios destinos, el proyecto se compilará dos veces, una por cada valor TargetFramework. Para cada una de estas compilaciones "internas", el objeto OutputPath se establecerá en el mismo valor, por lo que las salidas de cada una de las compilaciones internas se colocarán en el mismo directorio. Esto significa que cualquier compilación que se realice en último lugar sobrescribirá los resultados de la otra compilación, y en un sistema de compilación en paralelo como MSBuild funciona de forma predeterminada, "último" es indeterminado.

Biblioteca => Consola => Prueba, único valor TargetFramework

Ahora imagine una solución que contiene un proyecto de biblioteca, un proyecto de consola que hace referencia al proyecto de biblioteca y un proyecto de prueba que hace referencia al proyecto de consola. Todos estos proyectos tienen como destino un único valor TargetFramework, net7.0. En este caso, el proyecto de biblioteca se compilará primero y, luego, se compilará el proyecto de consola. El proyecto de prueba se compilará en último lugar y hará referencia al proyecto de consola. Para cada proyecto compilado, las salidas de cada compilación se copiarán en el directorio especificado por OutputPath, por lo que el directorio final contendrá recursos de los tres proyectos. Este sistema funciona para las pruebas, pero para la publicación puede dar lugar a que los recursos de prueba se envíen a producción.

Biblioteca = Consola =>> Prueba, varios valores TargetFramework

Ahora tome la misma cadena de proyectos y agrégueles una compilación net6.0TargetFramework además de la compilación net7.0. Se produce el mismo problema que en la compilación de varios destinos de un solo proyecto: copia incoherente de los recursos específicos de TFM en el directorio especificado.

Varias aplicaciones

Hasta ahora hemos estado examinando escenarios con un gráfico de dependencias lineales, pero muchas soluciones pueden contener varias aplicaciones relacionadas. Esto significa que se pueden compilar varias aplicaciones simultáneamente en la misma carpeta de salida. Si las aplicaciones incluyen un archivo de dependencias con el mismo nombre, la compilación puede producir un error intermitente cuando varios proyectos intentan escribir a la vez en ese archivo en la ruta de acceso de salida.

Si varias aplicaciones dependen de versiones diferentes de un archivo, incluso si la compilación se realiza correctamente, la versión del archivo se copie en la ruta de acceso de salida puede ser no determinista. Esto puede ocurrir cuando los proyectos dependen (posiblemente de forma transitiva) de diferentes versiones de un paquete NuGet. Dentro de un único proyecto, NuGet ayuda a garantizar que sus dependencias (incluidas las dependencias transitivas mediante paquetes NuGet o referencias de proyecto) se unifican a la misma versión. Dado que la unificación se realiza dentro del contexto de un único proyecto y sus proyectos dependientes, esto significa que es posible resolver diferentes versiones de un paquete cuando se compilan dos proyectos de nivel superior distintos. Si el proyecto que depende de la versión superior copia la dependencia en último lugar, las aplicaciones se siguen ejecutando correctamente. Sin embargo, si la versión inferior se copia en último lugar, la aplicación que se compiló con la versión superior no podrá cargar el ensamblado en tiempo de ejecución. Dado que la versión que se copia puede ser no determinista, esto puede dar lugar a compilaciones esporádicas y poco confiables en las que es muy difícil diagnosticar el problema.

Otros ejemplos

Para ver más ejemplos de cómo se presenta este error subyacente en la práctica, consulte el debate sobre dotnet/sdk#15607.

La recomendación general es realizar la acción que escogió anteriormente sin la opción --output/-o y, luego, mover la salida a la ubicación deseada una vez completado el comando. También es posible realizar la acción en un proyecto específico y seguir aplicando la opción --output/-o, ya que tiene una semántica mejor definida.

Si quiere mantener exactamente el comportamiento existente, puede usar la marca --property para establecer una propiedad de MSBuild en el directorio deseado. La propiedad que se va a usar varía en función del comando:

Get-Help Propiedad Ejemplo
build OutputPath dotnet build --property:OutputPath=DESIRED_PATH
clean OutputPath dotnet clean --property:OutputPath=DESIRED_PATH
pack PackageOutputPath dotnet pack --property:PackageOutputPath=DESIRED_PATH
publish PublishDir dotnet publish --property:PublishDir=DESIRED_PATH
store OutputPath dotnet store --property:OutputPath=DESIRED_PATH
test TestResultsDirectory dotnet test --property:OutputPath=DESIRED_PATH

NOTA Para obtener los mejores resultados, DESIRED_PATH debe ser una ruta de acceso absoluta. Las rutas de acceso relativas se "anclarán" (es decir, se harán absolutas) de formas inesperadas y puede que no funcionen igual con todos los comandos.