A opção de nível --output
de solução não é mais válida para comandos relacionados à compilação
No SDK 7.0.200, houve uma alteração para não aceitar mais a --output
/-o
opção ao usar um arquivo de solução com os seguintes comandos:
build
clean
pack
publish
store
test
vstest
Isso ocorre porque a semântica da OutputPath
propriedade, que é controlada pela --output
/-o
opção, não está bem definida para soluções. Os projetos construídos desta forma terão seus resultados colocados no mesmo diretório, o que é inconsistente e levou a uma série de problemas relatados pelo usuário.
Essa alteração foi reduzida para um nível de aviso de gravidade no SDK 7.0.201 e pack
foi removida da lista de comandos afetados.
Versão introduzida
SDK do .NET 7.0.200, reduzido a um aviso somente no SDK 7.0.201.
Comportamento anterior
Anteriormente, se você especificasse --output
/-o
ao usar um arquivo de solução, a saída para todos os projetos criados seria colocada no diretório especificado em uma ordem indefinida e inconsistente.
Novo comportamento
A dotnet
CLI errará se a --output
/-o
opção for usada com um arquivo de solução. A partir do SDK 7.0.201, um aviso será emitido e, no caso de dotnet pack
nenhum aviso ou erro, será gerado.
Tipo de mudança de rutura
Essa alteração de quebra pode exigir modificações para criar scripts e pipelines de integração contínua. Como resultado, afeta a compatibilidade binária e de origem.
Razão para a alteração
Essa mudança foi feita porque a semântica da OutputPath
propriedade, que é controlada pela --output
/-o
opção, não está bem definida para soluções. Os projetos construídos desta forma terão seus resultados colocados no mesmo diretório, o que é inconsistente e levou a uma série de problemas relatados pelo usuário.
Quando uma solução é construída com a --output
opção, a propriedade é definida com o mesmo valor para todos os projetos, o OutputPath
que significa que todos os projetos terão sua saída colocada no mesmo diretório. Dependendo da complexidade dos projetos na solução, resultados diferentes e inconsistentes podem ocorrer. Vamos dar uma olhada em alguns exemplos de diferentes formas de solução e como elas são afetadas por um compartilhamento OutputPath
.
Projeto único, TargetFramework único
Imagine uma solução que contém um único projeto destinado a um único TargetFramework
, net7.0
. Nesse caso, fornecer a --output
opção é equivalente a definir a OutputPath
propriedade no arquivo de projeto. Durante uma compilação (ou outros comandos, mas vamos definir o escopo da discussão para construir por enquanto), todas as saídas para o projeto serão colocadas no diretório especificado.
Projeto único, vários TargetFrameworks
Agora imagine uma solução que contém um único projeto com vários TargetFrameworks
, net6.0
e net7.0
. Devido ao multi-targeting, o projeto será construído duas vezes, uma para cada TargetFramework
. Para cada uma dessas compilações 'internas', as OutputPath
serão definidas com o mesmo valor e, portanto, as saídas para cada uma das compilações internas serão colocadas no mesmo diretório. Isso significa que qualquer compilação concluída por último substituirá as saídas da outra compilação e, em um sistema de compilação paralela como o MSBuild opera por padrão, 'last' é indeterminado.
Biblioteca => Console => Teste, TargetFramework único
Agora imagine uma solução que contém um projeto de biblioteca, um projeto de console que faz referência ao projeto de biblioteca e um projeto de teste que faz referência ao projeto de console. Todos estes projetos visam um único TargetFramework
, net7.0
. Nesse caso, o projeto de biblioteca será criado primeiro e, em seguida, o projeto de console será construído. O projeto de teste será construído por último e fará referência ao projeto de console. Para cada projeto construído, as saídas de cada compilação serão copiadas para o diretório especificado pelo OutputPath
, e, portanto, o diretório final conterá ativos de todos os três projetos. Isso funciona para testes, mas para publicação pode resultar em ativos de teste sendo enviados para a produção.
Biblioteca => Console => Teste, vários TargetFrameworks
Agora pegue a mesma cadeia de projetos e adicione uma net6.0
TargetFramework
compilação a eles, além da net7.0
compilação. O mesmo problema que a compilação de projeto único e com vários destinos ocorre - cópia inconsistente de ativos específicos do TFM para o diretório especificado.
Várias aplicações
Até agora, temos analisado cenários com um gráfico de dependência linear - mas muitas soluções podem conter vários aplicativos relacionados. Isso significa que vários aplicativos podem ser criados simultaneamente para a mesma pasta de saída. Se os aplicativos incluírem um arquivo de dependência com o mesmo nome, a compilação poderá falhar intermitentemente quando vários projetos tentarem gravar nesse arquivo no caminho de saída simultaneamente.
Se vários aplicativos dependerem de versões diferentes de um arquivo, mesmo que a compilação seja bem-sucedida, qual versão do arquivo é copiada para o caminho de saída pode ser não determinística. Isso pode acontecer quando os projetos dependem (possivelmente transitivamente) de diferentes versões de um pacote NuGet. Dentro de um único projeto, o NuGet ajuda a garantir que suas dependências (incluindo quaisquer dependências transitivas por meio de pacotes NuGet e/ou referências de projeto) sejam unificadas para a mesma versão. Como a unificação é feita dentro do contexto de um único projeto e seus projetos dependentes, isso significa que é possível resolver diferentes versões de um pacote quando dois projetos de nível superior separados são construídos. Se o projeto que depende da versão superior copiar a dependência por último, então geralmente os aplicativos serão executados com êxito. No entanto, se a versão inferior for copiada por último, o aplicativo que foi compilado em relação à versão superior não conseguirá carregar o assembly em tempo de execução. Como a versão copiada pode ser não determinística, isso pode levar a compilações esporádicas e não confiáveis, onde é muito difícil diagnosticar o problema.
Outros exemplos
Para obter mais exemplos de como esse erro subjacente se apresenta na prática, consulte a discussão em dotnet/sdk#15607.
Ação recomendada
A recomendação geral é executar a ação que você executou anteriormente sem a --output
/-o
opção e, em seguida, mover a saída para o local desejado após a conclusão do comando. Também é possível realizar a ação em um projeto específico e ainda aplicar a --output
/-o
opção, pois isso tem semântica mais bem definida.
Se você quiser manter o comportamento existente exatamente, então você pode usar o --property
sinalizador para definir uma propriedade MSBuild para o diretório desejado. A propriedade a ser usada varia de acordo com o comando:
Comando | Property | Exemplo |
---|---|---|
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 obter melhores resultados, o DESIRED_PATH deve ser um caminho absoluto. Os caminhos relativos serão 'ancorados' (ou seja, tornados absolutos) de maneiras que você pode não esperar, e podem não funcionar da mesma forma com todos os comandos.