c++ - ruta - ¿Visual Studio 2010 y 2008 no puede manejar archivos fuente con nombres idénticos en diferentes carpetas?
obtener ruta de archivo con openfiledialog c# (8)
Así que @ Hans Passant señaló en la dirección correcta, ¡¡¡Gracias !! No es necesario que liste el archivo, una carpeta es suficiente. Luego, si observa las macros definidas en la parte inferior de la lista de VS 2010, verá:
% (RelativeDir) / Univariate /
El problema, tal como se publicó, era en realidad una versión simplificada de lo que estoy trabajando: un par de niveles de carpetas en un solo proyecto y hay un par de conflictos de nombres. Por lo tanto, realmente quería de alguna manera simplemente "arreglarlo" ...
Si hace clic derecho en el proyecto en el explorador de soluciones, elija C / C ++ -> "Archivos de salida" y escriba lo siguiente en el cuadro "Nombre de archivo de objeto":
$ (IntDir) /% (RelativeDir) /
Tenga en cuenta que también seleccioné (Todas las configuraciones, Todas las plataformas) de los menús desplegables. Esto compilará todos los archivos en una jerarquía de directorios que refleje el árbol fuente. VS2010 comenzará la compilación creando estos directorios si no existen. Además, para aquellos que odian el espacio en blanco en sus nombres de directorio, esta macro elimina todos los espacios, por lo que no es necesario jugar con comillas dobles al usarlo.
Esto es exactamente lo que quería, idéntico a la forma en que mis Makefiles funcionan en el lado de Ubuntu, al tiempo que mantengo limpio el árbol fuente.
Pregunta directa: si tengo dos archivos con el mismo nombre (pero en diferentes directorios), parece que solo Visual Studio 2005 puede manejar esto de forma transparente. VS 2008 y 2010 requieren un montón de ajustes? Aparte de mi convención de nombres, ¿estoy haciendo algo mal?
Fondo:
Estoy desarrollando bibliotecas estadísticas C ++ ... Tengo dos carpetas:
/ 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
Necesito apoyar la compilación cruzada. Estoy usando g ++ / make para compilar estos mismos archivos en una biblioteca en Linux. Ellos funcionan bien.
He estado usando Visual Studio 2005 sin problemas, pero necesito actualizar a Visual Studio 2008 o 2010 (actualmente babeando sobre la herramienta nVis de nVidia). Sin embargo, estoy teniendo problemas si agrego archivos a un proyecto con el mismo nombre (incluso si están en un directorio diferente). Estoy dispuesto a cambiar mi convención de nombres, pero tengo curiosidad por saber si otros han encontrado este problema y han encontrado alguna solución bien documentada.
Estoy más confundido por el hecho de que si actualizo desde proyectos de 2005 a proyectos de 2010, parece que VS 2010 es capaz de manejar correctamente dos archivos de origen con el mismo nombre en diferentes directorios; Sin embargo, si elimino uno de los archivos duplicados y luego lo vuelvo a agregar al proyecto, me da la siguiente advertencia:
Distributions / Release / Adaptive.obj: warning LNK4042: objeto especificado más de una vez; extras ignorados
Ahora tengo el directorio intermedio especificado como $ (ProjectName) / $ (Configuración) - Necesito tener mis archivos de objeto en una ubicación diferente de mi árbol fuente. Así que puedo ver por qué está copiando los archivos objeto uno encima del otro, pero cuando los proyectos se convierten de 2005 a 2008 o 2010, se agregan un montón de compilaciones condicionales:
<ObjectFileName Condition="''$(Configuration)|$(Platform)''==''Release|x64''">$(IntDir)%(Filename)1.obj</ObjectFileName>
<XMLDocumentationFileName Condition="''$(Configuration)|$(Platform)''==''Release|x64''">$(IntDir)%(Filename)1.xdc</XMLDocumentationFileName>
Se puede acceder a ellos desde la página de propiedades del archivo de origen en C / C ++ -> Archivos de salida -> "Nombre de archivo de objeto" y "Nombre de archivo de documentación XML". Pero si simplemente agrego el archivo directamente (o lo elimino y vuelvo a agregar), VS no se queja hasta que intente compilar, pero tampoco agrega las directivas condicionales. Entonces, para que las cosas funcionen correctamente, tengo que agregue las directivas condicionales para cada configuración individual. ¿Estoy cometiendo un error / asunción pobre o he descubierto un error válido en VS 2008/2010?
Esto es fácil de arreglar en el IDE. Haga clic en el primer archivo de la carpeta, mantenga presionada la tecla Mayús y haga clic en el último archivo para que todos estén seleccionados. Haga clic derecho, Propiedades, C ++, Archivos de salida. Cambie el nombre del archivo de objeto de $(IntDir)/
a, por ejemplo, $(IntDir)/Univariate/
. Puede repetir para el grupo de archivos multivariante, aunque eso no es estrictamente necesario.
La solución% (RelativeDir) solo funciona para Visual Studo 2010.
Para Visual Studio 2008, debe hacer clic con el botón derecho en cada nombre de archivo .cpp duplicado y seleccionar "Propiedades". Puede que no sea obvio, pero puede modificar la sección "Propiedades de configuración -> C / C ++ -> Ouptut Files" para cada archivo individual.
Agregue una subcarpeta a la configuración de "Nombre de archivo de objeto" de cada uno de los archivos .cpp duplicados, tenga en cuenta que los archivos .h no necesitan modificarse ya que el archivo .cpp determina el resultado del archivo .obj.
Por ejemplo, si su proyecto tiene dos archivos en conflicto:
internal / example.cpp base / example.cpp
Debería establecer el "Nombre del archivo de objeto" para cada uno de ellos:
$ (IntDir) / internal / $ (IntDir) / base /
Tendrá que hacer esto para todas las configuraciones Release / Debug, etc.
Las otras soluciones en este hilo sufren los problemas relativos a RelativeDir ../.. y tener que establecer cosas manualmente en cada archivo fuente.
Por no mencionar, naufragio / MP. Cualquier solución que especifique un .obj exacto para% (ObjectFileName) dará como resultado un archivo diferente / Fo para cada archivo .cpp (para asignarlo a un archivo .obj específico) transferido a CL.exe y, por lo tanto, Visual Studio no podrá procesarlos en lote. . Sin lotes de varios archivos .cpp con líneas de comando idénticas (incluyendo / Fo), / MP no puede funcionar.
Aquí hay un nuevo enfoque. Esto funciona en vs2010 hasta vs2015 al menos. Agregue esto a su vcxproj en el <project>
<!-- ================ UNDUPOBJ ================ -->
<!-- relevant topics -->
<!-- https://.com/questions/3729515/visual-studio-2010-2008-cant-handle-source-files-with-identical-names-in-diff/26935613 -->
<!-- https://.com/questions/7033855/msvc10-mp-builds-not-multicore-across-folders-in-a-project -->
<!-- https://.com/questions/18304911/how-can-one-modify-an-itemdefinitiongroup-from-an-msbuild-target -->
<!-- other maybe related info -->
<!-- https://.com/questions/841913/modify-msbuild-itemgroup-metadata -->
<UsingTask TaskName="UNDUPOBJ_TASK" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)/Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<OutputDir ParameterType="System.String" Required="true" />
<ItemList ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<OutputItemList ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
</ParameterGroup>
<Task>
<Code><![CDATA[
//general outline: for each item (in ClCompile) assign it to a subdirectory of $(IntDir) by allocating subdirectories 0,1,2, etc., as needed to prevent duplicate filenames from clobbering each other
//this minimizes the number of batches that need to be run, since each subdirectory will necessarily be in a distinct batch due to /Fo specifying that output subdirectory
var assignmentMap = new Dictionary<string,int>();
HashSet<string> neededDirectories = new HashSet<string>();
foreach( var item in ItemList )
{
//solve bug e.g. 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];
]]></Code>
</Task>
</UsingTask>
<Target Name="UNDUPOBJ">
<!-- see topics for discussion on why we need to do some loopy copying stuff here -->
<ItemGroup>
<ClCompileCopy Include="@(ClCompile)"/>
<ClCompile Remove="@(ClCompile)"/>
</ItemGroup>
<UNDUPOBJ_TASK OutputDir="$(IntDir)" ItemList="@(ClCompileCopy)" OutputItemList="@(ClCompile)">
<Output ItemName="ClCompile" TaskParameter="OutputItemList"/>
</UNDUPOBJ_TASK>
</Target>
<!-- ================ UNDUPOBJ ================ -->
Y luego modifica <project> para que se lea:
<Project InitialTargets="UNDUPOBJ" ...
El resultado será algo como myproj / src / a / x.cpp y myproj / src / b / x.cpp compilando para Debug / 0 / x.obj y Debug / 1 / x.obj. RelativeDirs no son empleados, por lo que no son un problema.
Además, en este caso, solo habrá dos / Fo pasados a CL.exe: Debug / 0 / y Debug / 1 /. En consecuencia, no se emitirán más de dos lotes a CL.exe, lo que permite que / MP funcione de manera más eficiente.
Otros enfoques serían basar los subdirectorios .obj en los subdirectorios .cpp, o hacer que el nombre de archivo .obj contenga algún recuerdo del directorio original .cpp para que pueda ver fácilmente un mapeo .cpp -> .obj, pero esos resultados en más / Fo y, por lo tanto, menos lotes. El trabajo futuro podría volcar un archivo de mapeo para una referencia rápida, tal vez.
Consulte esto para obtener más detalles sobre / MP y procesamiento por lotes: MSVC10 / MP compila no multinúcleo en las carpetas de un proyecto
He probado esto en producción durante bastante tiempo en vs2010 y vs2015 en una variedad de cadenas de herramientas. Parece a prueba de balas, pero siempre existe la posibilidad de que interactúe mal con otras personalizaciones de msbuild o cadenas de herramientas exóticas.
A partir de vs2015, si aparece una advertencia "warning MSB8027: dos o más archivos con el nombre de X.cpp producirán resultados en la misma ubicación", puede agregar esto a su proyecto o a los archivos de msbuild:
<PropertyGroup Label="Globals"><IgnoreWarnCompileDuplicatedFilename>true</IgnoreWarnCompileDuplicatedFilename></PropertyGroup>
Consulte más en https://connect.microsoft.com/VisualStudio/feedback/details/797460/incorrect-warning-msb8027-reported-for-files-excluded-from-build y cómo suprimir una advertencia específica de MSBuild.
También puede ser que usted cree un archivo .cpp en Visual Studio y luego lo cambie a .h. Aunque el archivo se renombró, visual studio aún lo compila como un archivo cpp y, por lo tanto, se crean dos archivos obj y se muestra la advertencia del enlazador.
Tenga en cuenta que en mi instancia (Visual Studio 2013 con el conjunto de herramientas de la plataforma VS2010), usar $ (IntDir) /% (RelativeDir) no funciona correctamente e ignora el directorio intermedio que da como resultado errores del enlazador cuando se crean múltiples configuraciones porque todas las los archivos de objeto para cada configuración (es decir, Depuración y Versión) se colocan en la misma carpeta. Estos desaparecen si limpia el proyecto al cambiar las configuraciones.
Ejemplo de error:
MSVCRTD.lib (MSVCR100D.dll): error LNK2005: _fclose ya definido en LIBCMTD.lib (fclose.obj)
Solución:
$ (IntDir) /% (Directorio)
Para evitar esto, tuve que usar $ (IntDir) /% (Directorio) que colocó correctamente todos los archivos * .obj en el directorio intermedio y permitió construir y vincular varias configuraciones sin limpiar. El único inconveniente es que toda la jerarquía de carpetas (potencialmente larga) en la que se encuentran sus archivos se volverá a crear completamente en la carpeta Debug / Release / etc.
Tienes razón, VS no puede manejar eso, y nunca pudo. El problema de raíz es que genera un archivo .obj
para cada archivo .cpp
en el proyecto, y todos se colocan en la misma carpeta. Así que terminas con múltiples archivos .cpp
compilando en Adaptive.obj
en tu caso, por ejemplo.
Al menos el enlazador genera una advertencia para él ahora. Ese no fue siempre el caso.
Debería poder evitar esto asegurando que los archivos utilicen diferentes rutas de Directorio Intermedio, pero es un truco de algo que debería ser posible.
Por supuesto, siempre puede presentar un informe de error o una solicitud de función en él en Microsoft Connect.
use Propiedades de configuración> C / C ++> Archivos Ouptut> $ (IntDir) /% (RelativeDir) /% (Filename)
esto duplicará la estructura del archivo fuente bajo el directorio de depuración y depositará el archivo objeto para cada directorio en una carpeta del mismo nombre bajo el directorio Debug