Opzione --output
a livello di soluzione non più valida per i comandi correlati alla compilazione
Nell'SDK 7.0.200 è stata apportata una modifica per non accettare più l'opzione --output
/-o
quando si usa un file di soluzione con i comandi seguenti:
build
clean
pack
publish
store
test
vstest
Ciò è dovuto al fatto che la semantica della proprietà OutputPath
, controllata dall'opzione --output
/-o
, non è ben definita per le soluzioni. I progetti compilati in questo modo avranno l'output inserito nella stessa directory, che è incoerente e ha causato una serie di problemi segnalati dall'utente.
Questa modifica è stata ridotta a un livello di gravità di avviso nell'SDK 7.0.201 e pack
è stata rimossa dall'elenco dei comandi interessati.
Versione introdotta
.NET 7.0.200 SDK, ridotto a un avviso solo nell'SDK 7.0.201.
Comportamento precedente
In precedenza, se si specificava --output
/-o
quando si utilizzava un file di soluzione, l'output di tutti i progetti creati veniva collocato nella directory specificata in un ordine non definito e non coerente.
Nuovo comportamento
L'interfaccia della riga di comando dotnet
genera un errore se l'opzione --output
/-o
viene usata con un file di soluzione. A partire dalla versione 7.0.201 SDK, verrà generato un avviso e, in caso di dotnet pack
non verrà generato alcun avviso o errore.
Tipo di modifica che causa un'interruzione
Questa modifica che causa un'interruzione può richiedere modifiche per compilare script e pipeline di integrazione continua. Di conseguenza, influisce sulla compatibilità sia binaria che di origine.
Motivo della modifica
Questa modifica è stata apportata perché la semantica della proprietà OutputPath
, controllata dall'opzione --output
/-o
, non è ben definita per le soluzioni. I progetti compilati in questo modo avranno l'output inserito nella stessa directory, che è incoerente e ha causato una serie di problemi segnalati dall'utente.
Quando una soluzione viene compilata con l'opzione --output
, la proprietà OutputPath
viene impostata sullo stesso valore per tutti i progetti, il che significa che l'output di tutti i progetti verrà inserito nella stessa directory. A seconda della complessità dei progetti nella soluzione, possono verificarsi risultati diversi e incoerenti. Verranno ora esaminati alcuni esempi di forme di soluzione diverse e il modo in cui sono interessati da un oggetto OutputPath
condiviso.
Singolo progetto, singolo TargetFramework
Si supponga di creare una soluzione contenente un singolo progetto destinato a un singolo oggetto TargetFramework
, net7.0
. In questo caso, specificare l'opzione --output
equivale a impostare la proprietà OutputPath
nel file di progetto. Durante una compilazione (o altri comandi, ma si esaminerà ora la discussione della compilazione), tutti gli output per il progetto verranno inseriti nella directory specificata.
Progetto singolo, più TargetFrameworks
Si supponga ora di creare una soluzione contenente un singolo progetto con più oggetti TargetFrameworks
, net6.0
e net7.0
. A causa del multitargeting, il progetto verrà compilato due volte, una volta per ogni TargetFramework
. Per ognuna di queste compilazioni "interne" l'oggetto OutputPath
verrà impostato sullo stesso valore e quindi gli output per ognuna delle compilazioni interne verranno inseriti nella stessa directory. Ciò significa che la compilazione completata per ultima sovrascriverà gli output dell'altra compilazione e in un sistema di compilazione parallelo come quello in cui opera MSBuild per impostazione predefinita, "ultimo" è indeterminato.
Libreria => Console => Test, singolo TargetFramework
Si immagini ora una soluzione contenente un progetto di libreria, un progetto console che fa riferimento al progetto di libreria e un progetto di test che fa riferimento al progetto console. Tutti questi progetti hanno come destinazione un singolo oggetto TargetFramework
, net7.0
. In questo caso, il progetto di libreria verrà compilato per primo e quindi verrà compilato il progetto console. Il progetto di test verrà compilato per ultimo e farà riferimento al progetto console. Per ogni progetto compilato, gli output di ogni compilazione verranno copiati nella directory specificata da OutputPath
e quindi la directory finale conterrà gli asset di tutti e tre i progetti. Questa operazione funziona per i test, ma per la pubblicazione può comportare l'invio di asset di test all'ambiente di produzione.
Libreria => Console => Test, più TargetFrameworks
Prendere ora la stessa catena di progetti e aggiungere una compilazione net6.0
TargetFramework
a tali progetti oltre alla compilazione net7.0
. Si verifica lo stesso problema della compilazione a progetto singolo e con più destinazioni, ovvero la copia incoerente di asset specifici di TFM nella directory specificata.
Più app
Finora sono stati illustrati gli scenari con un grafico delle dipendenze lineare, ma molte soluzioni possono contenere più applicazioni correlate. Ciò significa che più app possono essere compilate simultaneamente nella stessa cartella di output. Se le app includono un file di dipendenza con lo stesso nome, la compilazione potrebbe non riuscire in modo intermittente quando più progetti tentano di scrivere in tale file nel percorso di output contemporaneamente.
Se più app dipendono da versioni diverse di un file, anche se la compilazione ha esito positivo, quale versione del file viene copiata nel percorso di output potrebbe non essere deterministica. Ciò può verificarsi quando i progetti dipendono (possibilmente transitivamente) da versioni diverse di un pacchetto NuGet. All'interno di un singolo progetto, NuGet garantisce che le relative dipendenze (incluse eventuali dipendenze transitive tramite pacchetti NuGet e/o riferimenti al progetto) siano unificate alla stessa versione. Poiché l'unificazione viene eseguita all'interno del contesto di un singolo progetto e dei relativi progetti dipendenti, ciò significa che è possibile risolvere versioni diverse di un pacchetto quando vengono compilati due progetti di primo livello separati. Se il progetto che dipende dalla versione successiva copia l'ultima dipendenza, spesso le app verranno eseguite correttamente. Tuttavia, se la versione precedente viene copiata per ultima, l'app compilata con la versione successiva non riuscirà a caricare l'assembly in fase di esecuzione. Poiché la versione copiata può essere non deterministica, ciò può causare compilazioni sporadiche e inaffidabili in cui è molto difficile diagnosticare il problema.
Altri esempi
Per altri esempi di come questo errore sottostante si presenta nella pratica, vedere la discussione su dotnet/sdk#15607.
Azione consigliata
Il consiglioo generale consiste nell'eseguire l'azione eseguita in precedenza senza l'opzione --output
/-o
, e poi spostare l'output nella posizione desiderata dopo il completamento del comando. È anche possibile eseguire l'azione in un progetto specifico e applicare comunque l'opzione --output
/-o
, in quanto ha una semantica più definita.
Se si vuole mantenere esattamente il comportamento esistente, è possibile usare il flag --property
per impostare una proprietà MSBuild sulla directory desiderata. La proprietà da usare varia in base al comando:
Comando | Proprietà | Esempio |
---|---|---|
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 Per ottenere risultati ottimali, il DESIRED_PATH deve essere un percorso assoluto. I percorsi relativi saranno "ancorati" (ad esempio resi assoluti) in modi che potrebbero non essere previsti e potrebbero non funzionare allo stesso modo con tutti i comandi.