Condividi tramite


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.0TargetFramework 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.

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.