Visual Studio 2010 & 2008 не может обрабатывать исходные файлы с одинаковыми именами в разных папках? - PullRequest
53 голосов
/ 16 сентября 2010

Прямой вопрос: Если у меня есть два файла с одинаковым именем (но в разных каталогах), кажется, что только Visual Studio 2005 может обрабатывать это прозрачно ??VS 2008 и 2010 требуют кучу настроек?Помимо соглашения об именах, я что-то не так делаю?

Справочная информация:

Я занимаюсь разработкой статистических библиотек C ++ ... У меня есть две папки:

/ 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

Мне нужна поддержка кросс-компиляции - я использую g ++ / make для компиляции этих же файлов в библиотеку Linux.Они работают просто отлично.

Я использовал Visual Studio 2005 без проблем, но мне нужно перейти на Visual Studio 2008 или 2010 (в настоящее время слюни на инструмент nSight от nVidia).Однако у меня возникают проблемы, если я добавляю файлы в проект с тем же именем (даже если они находятся в другом каталоге).Я готов изменить свое соглашение об именах, но мне любопытно, сталкивались ли другие с этой проблемой и нашли ли какие-либо хорошо документированные решения ??

Меня еще больше поражает тот факт, чточто если я обновлюсь с проектов 2005 года до проектов 2010 года, то окажется, что VS 2010 способен правильно обрабатывать два исходных файла с одинаковыми именами в разных каталогах;однако, если я удаляю один из дубликатов файлов и затем добавляю его обратно в проект, меня приветствует следующее предупреждение:

Distributions \ Release \ Adaptive.obj: предупреждение LNK4042: объект указан более одного раза;дополнительные функции игнорируются

Теперь у меня есть промежуточный каталог, указанный как $ (ProjectName) \ $ (Configuration) - мне нужно, чтобы мои объектные файлы находились в другом месте, чем исходное дерево.Поэтому я могу понять, почему он копирует объектные файлы друг на друга, но когда проекты преобразуются с 2005 на 2008 или 2010 год, добавляется куча условных компиляций:

<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(IntDir)%(Filename)1.obj</ObjectFileName>
<XMLDocumentationFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(IntDir)%(Filename)1.xdc</XMLDocumentationFileName>

Они доступны изстраница Свойства исходного файла в C / C ++ -> Выходные файлы -> «Имя файла объекта» и «Имя файла документации XML».Но если я просто добавляю файл напрямую (или удаляю и повторно добавляю его), VS не будет жаловаться, пока я не попытаюсь скомпилировать, но также никогда не добавит условные директивы - поэтому, чтобы все работало правильно, я должендобавить условные директивы самостоятельно для каждой конфигурации.Я делаю ошибку / плохое предположение или я обнаружил допустимую ошибку в VS 2008/2010?

Ответы [ 8 ]

60 голосов
/ 17 сентября 2010

Так @ Ханс Пассант указал в правильном направлении, спасибо !!Вам не нужно перечислять файл, достаточно папки.Затем, если вы посмотрите в определенные макросы внизу списка VS 2010, вы увидите:

% (RelativeDir) / Univariate /

Проблема, как было опубликовано, на самом деле былаупрощенная версия того, над чем я работаю - пара уровней папок в одном проекте и несколько конфликтов имен.Следовательно, я действительно хотел как-то просто «исправить» это ...

Если вы щелкнете правой кнопкой мыши по проекту в обозревателе решений, выберите C / C ++ -> «Выходные файлы» и введите следующее в «Поле "Имя файла объекта":

$ (IntDir) /% (RelativeDir) /

Обратите внимание, что я также выбрал (Все конфигурации, Все платформы) из раскрывающихся списков.Это скомпилирует каждый файл в иерархии каталогов, которая отражает исходное дерево.VS2010 начнет сборку с создания этих каталогов, если они не существуют.Кроме того, для тех, кто ненавидит пробелы в именах каталогов, этот макрос удаляет все пробелы, поэтому при его использовании не нужно возиться с двойными кавычками.

Это точно то, что я хотел - идентично тому, как мои Makefile работают на стороне Ubuntu, при этом сохраняя дерево исходного кода чистым.

12 голосов
/ 16 сентября 2010

Это легко исправить в IDE.Щелкните первый файл в папке, Shift + щелкните последний файл, чтобы все они были выбраны.Щелкните правой кнопкой мыши, Свойства, C ++, Выходные файлы.Измените имя файла объекта с $(IntDir)\ на, скажем, $(IntDir)\Univariate\.Вы можете повторить для группы файлов с несколькими переменными, хотя в этом нет особой необходимости.

8 голосов
/ 16 сентября 2010

Вы правы, В.С. не может справиться с этим, и никогда не мог. Основная проблема в том, что он генерирует файл .obj для каждого файла .cpp в проекте, и все они находятся в одной папке. Таким образом, вы получите несколько .cpp файлов, компилируемых в Adaptive.obj в вашем случае, например.

По крайней мере, компоновщик генерирует предупреждение для него сейчас. Это было не всегда так.

Вы можете обойти это, убедившись, что файлы используют разные пути к промежуточным каталогам, но это что-то вроде хака вокруг того, что должно быть возможным.

Конечно, вы всегда можете подать отчет об ошибке или запрос функции на Microsoft Connect

6 голосов
/ 14 ноября 2014

Другие решения в этом потоке страдают от проблем на основе RelativeDir ../ .. и необходимости вручную настраивать каждый исходный файл.

Не говоря уже о том, что они разрушены / MP.Любое решение, в котором указан точный .obj для% (ObjectFileName), приводит к тому, что для каждого файла .cpp (для сопоставления его с конкретным файлом .obj) передается отдельный файл / Fo, поэтому Visual Studio не может их пакетировать,Без пакетирования нескольких файлов .cpp с одинаковыми командными строками (включая / Fo) / MP не может работать.

Вот новый подход.Это работает с vs2010 до vs2015 по крайней мере.Добавьте это к вашему vcxproj в

<!-- ================ UNDUPOBJ ================ -->
<!-- relevant topics -->
<!-- /2826459/visual-studio-2010-2008-ne-mozhet-obrabatyvat-ishodnye-faily-s-odinakovymi-imenami-v-raznyh-papkah -->
<!-- https://stackoverflow.com/questions/7033855/msvc10-mp-builds-not-multicore-across-folders-in-a-project -->
<!-- https://stackoverflow.com/questions/18304911/how-can-one-modify-an-itemdefinitiongroup-from-an-msbuild-target -->
<!-- other maybe related info -->
<!-- https://stackoverflow.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 stackoverflow 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 ================ -->

, а затем измените так, чтобы оно показывало:

<Project InitialTargets="UNDUPOBJ" ...

В результате будет что-то вроде myproj / src / a/x.cpp и myproj / src / b / x.cpp компилируются в Debug / 0 / x.obj и Debug / 1 / x.obj.RelativeDirs не используются и поэтому не являются проблемой.

Кроме того, в этом случае в CL.exe будут переданы только два разных / Fo: Debug / 0 / и Debug / 1 /.Следовательно, для CL.exe будет выдано не более двух пакетов, что позволит / MP работать более эффективно.

Другими подходами было бы основание подкаталогов .obj на подкаталогах .cpp или создание .obj.имя файла содержит некоторое имя оригинального каталога .cpp, так что вы можете легко увидеть отображение .cpp ->. obj, но это приводит к большему количеству / Fo и, следовательно, меньшему количеству пакетирования.Дальнейшая работа может дать дамп файла сопоставления для быстрого ознакомления, возможно.

См. Это для получения дополнительной информации о / MP и пакетной обработке: MSVC10 / MP строит не многоядерные файлы в разных папках в проекте

Я довольно долго тестировал это на vs2010 и vs2015 на различных наборах инструментов.Это кажется пуленепробиваемым, но всегда есть вероятность, что он может плохо взаимодействовать с другими настройками msbuild или экзотическими цепочками инструментов.

Начиная с vs2015, если вы получаете предупреждение "предупреждение MSB8027: два или более файлов с именем X.cpp выдаст выходные данные в ту же папку ", затем вы можете добавить это к вашему проекту или файлам msbuild:

<PropertyGroup Label="Globals"><IgnoreWarnCompileDuplicatedFilename>true</IgnoreWarnCompileDuplicatedFilename></PropertyGroup>

Подробнее см. https://connect.microsoft.com/VisualStudio/feedback/details/797460/incorrect-warning-msb8027-reported-for-files-excluded-from-build и Как отключить определенное предупреждение MSBuild

4 голосов
/ 04 декабря 2014

Обратите внимание, что в моем случае (Visual Studio 2013 с набором инструментов платформы VS2010) использование $ (IntDir) \% (RelativeDir) работает некорректно и игнорирует промежуточный каталог, что приводит к ошибкам компоновщика при построении нескольких конфигураций, посколькувсе объектные файлы для каждой конфигурации (например, Debug и Release) помещаются в одну и ту же папку.Они исчезают, если вы очищаете проект при переключении конфигураций.

Пример ошибки:

MSVCRTD.lib (MSVCR100D.dll): ошибка LNK2005: _fclose ужеопределено в LIBCMTD.lib (fclose.obj)

Решение:

$ (IntDir) \% (Directory)

Чтобы обойти это, мне пришлось использовать $ (IntDir) \% (Directory), который правильно помещал все файлы * .obj в промежуточный каталог и позволял создавать и связывать несколько конфигураций без очистки.Единственный недостаток - вся (потенциально длинная) иерархия папок, в которой находятся ваши файлы, будет полностью воссоздана в папке Debug / Release / etc.

3 голосов
/ 09 мая 2013

используйте Свойства конфигурации> C / C ++> Выходные файлы> $ (IntDir) \% (RelativeDir) \% (Имя файла)

это дублирует структуру исходного файла в каталоге отладки и помещает объектный файл для каждого каталога в папку с таким же именем в каталоге Debug

1 голос
/ 25 сентября 2011

Также может быть, что вы создаете файл .cpp в visual studio, а затем переименовываете его в .h. Хотя файл переименован, Visual Studio все равно компилирует его как файл cpp, и поэтому создаются два файла obj и отображается предупреждение компоновщика.

0 голосов
/ 14 апреля 2015

Решение% (RelativeDir) работает только для Visual Studo 2010.

Для Visual Studio 2008 необходимо щелкнуть правой кнопкой мыши каждое дублированное имя файла .cpp и выбрать «Свойства».Это может быть неочевидно, но вы можете изменить раздел «Свойства конфигурации -> C / C ++ -> Выходные файлы» для каждого отдельного файла.

Добавить подпапку к настройке «Имя файла объекта» для каждого файла.из дубликатов файлов .cpp, обратите внимание, что файлы .h не нужно изменять, поскольку файл .cpp определяет вывод файла .obj.

Например, если в вашем проекте есть два конфликтующих файла:

internal \ example.cpp base \ example.cpp

Для каждого имени файла объекта вы должны установить:

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

Это необходимо сделать для всех конфигураций выпуска / отладки и т. д.

...