Visual Studio 2010 e 2008 non possono gestire file di origine con nomi identici in cartelle diverse?

Domanda diretta: se ho due file con lo stesso nome (ma in diverse directory), sembra che solo Visual Studio 2005 possa gestirlo in modo trasparente ?? VS 2008 e 2010 richiedono un sacco di ritocchi? A parte la mia convenzione sui nomi, sto facendo qualcosa di sbagliato?

Sfondo:

Sto sviluppando librerie statistiche C ++ … Ho due cartelle:

/ Univariate Normal.cpp Normal.h Beta.cpp Beta.h Adaptive.cpp Adaptive.h / Multivariate Normal.cpp Normal.h Beta.cpp Beta.h Adaptive.cpp Adaptive.h 

Devo supportare la compilazione incrociata – sto usando g ++ / make per compilare questi stessi file in una libreria in Linux. Funzionano bene.

Stavo usando Visual Studio 2005 senza problemi, ma ho bisogno di aggiornare a Visual Studio 2008 o 2010 (attualmente sbavando sullo strumento di nsidia di nVidia). Tuttavia, ho problemi se aggiungo file a un progetto con lo stesso nome (anche se sono in una directory diversa). Sono disposto a cambiare la mia convenzione di denominazione, ma sono curioso di sapere se altri hanno riscontrato questo problema e hanno trovato soluzioni ben documentate ??

Sono ancora più sconvolto dal fatto che, se eseguo l’aggiornamento dai progetti del 2005 ai progetti del 2010, sembra che VS 2010 sia in grado di gestire correttamente due file sorgenti con lo stesso nome in diverse directory; tuttavia, se rimuovo uno dei file duplicati e poi lo aggiungo al progetto, sono accolto dal seguente avviso:

Distribuzioni \ Release \ Adaptive.obj: avviso LNK4042: object specificato più di una volta; extra ignorati

Ora ho la directory intermedia specificata come $ (ProjectName) \ $ (Configuration) – Ho bisogno di avere i miei file object in una posizione diversa dalla mia struttura dei sorgenti. Quindi capisco perché copi i file object uno sopra l’altro, ma quando i progetti vengono convertiti dal 2005 al 2008 o 2010, vengono aggiunte alcune combinazioni condizionali:

 $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc 

Questi sono accessibili dalla pagina delle proprietà del file di origine in C / C ++ -> File di output -> “Nome file object” e “Nome file di documentazione XML”. Ma se aggiungo semplicemente il file direttamente (o rimuoverlo e riaggiungerlo), VS non si lamenta finché non provo a compilare, ma non aggiunge mai le direttive condizionali – Quindi, per far funzionare le cose correttamente, devo aggiungere le direttive condizionali per ogni singola configurazione. Sto facendo un errore / scarsa ipotesi o ho scoperto un bug valido in VS 2008/2010?

Quindi @Hans Passant ha puntato nella giusta direzione, grazie !! Non è necessario elencare il file, una cartella è sufficiente. Quindi, se guardi le macro definite nella parte inferiore dell’elenco VS 2010, vedrai:

% (RelativeDir) / Univariate /

Il problema, come pubblicato, era in realtà una versione semplificata di ciò su cui sto lavorando: un paio di livelli di cartelle in un singolo progetto e ci sono un paio di conflitti di nomi. Quindi, volevo davvero solo “sistemarlo” …

Se fai clic destro sul progetto in solution explorer, scegli C / C ++ -> “File di output” e inserisci quanto segue nella casella “Nome file object”:

$ (IntDir) /% (RelativeDir) /

Nota che ho anche selezionato (Tutte le configurazioni, Tutte le piattaforms) dal menu a discesa. Questo compila ogni file in una gerarchia di directory che rispecchia l’albero dei sorgenti. VS2010 inizierà la creazione creando queste directory se non esistono. Inoltre, per coloro che odiano lo spazio bianco nei loro nomi di directory, questa macro rimuove tutti gli spazi, quindi non è necessario giocare con virgolette quando lo si utilizza.

Questo è esattamente quello che volevo – identico al modo in cui i miei Makefile funzionano su Ubuntu, mantenendo comunque pulito l’albero dei sorgenti.

Questo è facile da risolvere nell’IDE. Fare clic sul primo file nella cartella, Maiusc + Fare clic sull’ultimo file in modo che siano tutti selezionati. Clic con il tasto destro, Proprietà, C ++, File di output. Modificare il nome del file object da $(IntDir)\ a, ad esempio $(IntDir)\Univariate\ . È ansible ripetere per il gruppo di file multivariato, anche se non è strettamente necessario.

Hai ragione, VS non può gestirlo e mai potrebbe. Il problema di root è che genera un file .obj per ogni file .cpp nel progetto, e sono tutti posizionati nella stessa cartella. Ad esempio, si finisce con la compilazione di più file .cpp in Adaptive.obj nel tuo caso.

Almeno il linker genera un avviso per ora. Non è sempre stato così.

Dovresti essere in grado di aggirare questo problema assicurando che i file utilizzino percorsi di directory intermedie differenti, ma è un po ‘un trucco per qualcosa che dovrebbe essere ansible.

Naturalmente, è sempre ansible presentare una segnalazione di bug o una richiesta di funzionalità su di esso in Microsoft Connect

Le altre soluzioni in questo thread soffrono dei problemi ../ .. RelativeDir e devono impostare le cose manualmente su ogni file sorgente.

Per non parlare, si distruggono / MP. Qualsiasi soluzione che specifica un .obj esatto per% (ObjectFileName) genererà un diverso / Fo per ogni file .cpp (per mapparlo in un file obj specifico) passato a CL.exe e quindi Visual Studio non può eseguirli in batch . Senza eseguire il batch di diversi file .cpp con linee di comando identiche (incluso / Fo), / MP non può funzionare.

Ecco un nuovo approccio. Funziona su vs2010 fino a vs2015 almeno. Aggiungi questo al tuo vcxproj nel

               (); HashSet neededDirectories = new HashSet(); foreach( var item in ItemList ) { //solve bug eg Checkbox.cpp vs CheckBox.cpp var filename = item.GetMetadata("Filename").ToUpperInvariant(); //assign reused filenames to increasing numbers //assign previously unused filenames to 0 int assignment = 0; if(assignmentMap.TryGetValue(filename, out assignment)) assignmentMap[filename] = ++assignment; else assignmentMap[filename] = 0; var thisFileOutdir = Path.Combine(OutputDir,assignment.ToString()) + "/"; //take care it ends in / so /Fo knows it's a directory and not a filename item.SetMetadata( "ObjectFileName", thisFileOutdir ); } foreach(var needed in neededDirectories) System.IO.Directory.CreateDirectory(needed); OutputItemList = ItemList; ItemList = new Microsoft.Build.Framework.ITaskItem[0]; ]]>              

E poi modifica in modo che legga:

  

Il risultato sarà qualcosa come myproj / src / a / x.cpp e myproj / src / b / x.cpp che compila su Debug / 0 / x.obj e Debug / 1 / x.obj. RelativeDirs non sono impiegati e quindi non sono un problema.

Inoltre, in questo caso, ci saranno solo due diversi / Fo passati a CL.exe: Debug / 0 / e Debug / 1 /. Di conseguenza, non verranno emessi più di due batch su CL.exe, consentendo a / MP di funzionare in modo più efficiente.

Altri approcci sarebbero basando le sottodirectory .obj sulle sottodirectory .cpp, o facendo in modo che il nome del file .obj contenga qualche memento della directory .cpp originale in modo tale che sia ansible vedere facilmente una mapping .cpp ->. Obj, ma questi risulteranno in più / Fo e quindi meno lotti. Il lavoro futuro potrebbe scaricare un file di mapping per una rapida consultazione, forse.

Vedi questo per maggiori dettagli su / MP e batch: MSVC10 / MP non crea multicore su cartelle in un progetto

Ho provato questo in produzione per un po 'su vs2010 e vs2015 su una varietà di toolchain. Sembra a prova di proiettile, ma c'è sempre la possibilità che possa interagire male con altre personalizzazioni di msbuild o toolchain esotici.

A partire da vs2015, se viene visualizzato un avviso "avviso MSB8027: due o più file con il nome di X.cpp produrrà output nella stessa posizione", quindi è ansible aggiungerlo ai file di progetto o di msbuild:

 true 

Maggiori informazioni su https://connect.microsoft.com/VisualStudio/feedback/details/797460/incorrect-warning-msb8027-reported-for-files-excluded-from-build e Come sopprimere l'avviso specifico di MSBuild

utilizzare Proprietà di configurazione> C / C ++> File Ouptut> $ (IntDir) \% (RelativeDir) \% (Nome file)

questo duplicherà la struttura del file sorgente sotto la directory di debug e depositerà il file object per ogni directory in una cartella con lo stesso nome nella directory Debug

Si noti che nella mia istanza (Visual Studio 2013 con il set di strumenti della piattaforma VS2010), l’utilizzo di $ (IntDir) \% (RelativeDir) non funziona correttamente e ignora la directory intermedia che genera errori del linker durante la creazione di più configurazioni perché tutte le i file object per ogni configurazione (es. Debug e Release) sono collocati nella stessa cartella. Queste vanno via se si pulisce il progetto quando si cambiano le configurazioni.

Esempio di errore:

MSVCRTD.lib (MSVCR100D.dll): errore LNK2005: _fclose già definito in LIBCMTD.lib (fclose.obj)

Soluzione:

$ (IntDir) \% (Directory)

Per aggirare questo problema, ho dovuto usare $ (IntDir) \% (Directory) che ha posizionato correttamente tutti i file * .obj nella directory intermedia e consentito di creare e colbind più configurazioni senza eseguire la pulizia. L’unico svantaggio è l’intera (potenzialmente lunga) gerarchia di cartelle in cui i tuoi file saranno completamente ricreati nella cartella Debug / Release / etc.

Può anche essere che tu crei un file .cpp in Visual Studio e lo rinomini in seguito a .h. Sebbene il file venga rinominato Visual Studio lo compila ancora come un file cpp e quindi vengono creati due file obj e viene visualizzato l’avviso del linker.

La soluzione% (RelativeDir) funziona solo per Visual Studo 2010.

Per Visual Studio 2008 è necessario fare clic con il tasto destro del mouse su ciascun nome file duplicato .cpp e scegliere “Proprietà”. Potrebbe non essere ovvio ma puoi effettivamente modificare la sezione “Proprietà di configurazione -> C / C ++ -> File Ouptut” per ogni singolo file.

Aggiungi una sottocartella all’impostazione “Nome file object” di ciascuno dei file duplicati .cpp, nota che i file .h non devono essere modificati poiché il file .cpp determina l’output del file .obj.

Ad esempio, se il tuo progetto ha due file in conflitto:

interno \ example.cpp base \ example.cpp

Dovresti impostare il “Nome file object” per ognuno di:

$ (IntDir) \ internal \ $ (IntDir) \ base \

Dovrai farlo per tutte le configurazioni Release / Debug, ecc.