Visual Studio 2010和2008不能在不同的文件夹中处理名称相同的源文件?

时间:2022-02-18 20:49:30

Direct Question: If I have two files with the same name (but in different directories), it appears that only Visual Studio 2005 can handle this transparently?? VS 2008 & 2010 require a bunch of tweaking? Aside from my naming convention, am I doing something wrong?

直接问题:如果我有两个同名的文件(但是在不同的目录中),那么只有Visual Studio 2005才能透明地处理这个问题?VS 2008和2010需要一些调整?除了命名约定,我做错了什么吗?

Background:

背景:

I'm developing C++ statistical libraries... I have two folders:

我正在开发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

I need to support cross compilation -- I'm using g++/make to compile these same files into a library in Linux. They work just fine.

我需要支持交叉编译——我使用g++/make将这些相同的文件编译到Linux中的库中。他们工作的很好。

I had been using Visual Studio 2005 without issue, but I need to upgrade to Visual Studio 2008 or 2010 (currently drooling over nVidia's nsight tool). However, I'm having trouble if I add files to a project with the same name (even if they're in a different directory). I'm willing to change my naming convention, but I'm curious if others have encountered this problem and have found any well documented solutions??

我一直在毫无问题地使用Visual Studio 2005,但是我需要升级到Visual Studio 2008或2010(目前正在使用nVidia的nsight工具)。但是,如果我向同名的项目添加文件(即使它们位于不同的目录中),我就会遇到麻烦。我很愿意改变我的命名约定,但是我很好奇其他人是否遇到过这个问题,并且找到了任何好的文档化的解决方案?

I'm further boggled by the fact that if I upgrade from 2005 projects to 2010 projects, it appears that VS 2010 is able to correctly handle two source files with the same name in different directories; however, if I remove one of the duplicate files and then add it back to the project I am greeted by the following warning:

如果我从2005年的项目升级到2010年的项目,那么VS 2010似乎能够在不同的目录中正确地处理两个同名的源文件。但是,如果我删除一个重复的文件,然后将其添加到项目中,我将收到以下警告:

Distributions\Release\Adaptive.obj : warning LNK4042: object specified more than once; extras ignored

分布\释放\适应性。obj:警告LNK4042:对象指定不止一次;额外的忽视

Now I have the intermediate directory specified as $(ProjectName)\$(Configuration) -- I need to have my object files in a different location from my source tree. So I can see why it's copying the object files on top of each other, but when the projects are converted from 2005 to 2008 or 2010, a bunch of conditional compiles are added:

现在我将中间目录指定为$(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>

These are accessible from the Source file Properties page in C/C++ -> Output Files -> "Object File Name" & "XML Documentation File Name". But if I simply add the file directly (or remove and re-add them), VS doesn't complain until I try to compile, but also never adds the conditional directives -- So in order for things to work correctly, I have to add the conditional directives myself for every single configuration. Am I making a mistake / poor assumption or have I uncovered a valid bug in VS 2008 / 2010?

这些可以从C/ c++ ->输出文件的源文件属性页->“对象文件名”&“XML文档文件名”中访问。但是,如果我只是直接添加文件(或删除并重新添加它们),VS在尝试编译之前不会抱怨,但也不会添加条件指令——因此,为了让事情正确工作,我必须为每个配置自己添加条件指令。我是否做了一个错误/糟糕的假设,或者我在VS 2008 / 2010中发现了一个有效的错误?

8 个解决方案

#1


54  

So @Hans Passant pointed in the right direction, Thanks!! You don't have to list the file, a folder is sufficient. Then if you look in the defined macros at the bottom of the VS 2010 list, you'll see:

所以@Hans Passant指出了正确的方向,谢谢!你不需要列出文件,一个文件夹就足够了。然后,如果您查看VS 2010列表底部的已定义宏,您将看到:

%(RelativeDir)/ Univariate/

%(RelativeDir)/单变量

The problem, as posted, was actually a simplified version of what I'm working on -- a couple of levels of folders in a single project and there are a couple of name conflicts. Hence, I really wanted someway to just "fix" it...

问题是,正如所贴出的,实际上是我正在处理的一个简化版本——在一个项目中有两个级别的文件夹,并且有两个名称冲突。因此,我真的想要某种方法来“修复”它……

If you right click on the project in the solution explorer, choose C/C++ -> "Output Files" and type the following into the "Object File Name" box:

如果在解决方案资源管理器中右键单击项目,选择C/ c++ ->“输出文件”,在“对象文件名”框中输入以下内容:

$(IntDir)/%(RelativeDir)/

美元(IntDir)/ %(RelativeDir)/

Note that I also selected (All Configurations, All Platforms) from the drop downs. This will compile every file in a directory hierarchy which mirrors the source tree. VS2010 will begin the build by creating these directories if they don't exist. Further, for those who hate white space in their directory names, this macro does remove all spaces, so there is no need to play around with double quotes when using it.

注意,我还从下拉列表中选择了(所有配置、所有平台)。这将编译目录层次结构中的每个文件,该目录层次结构反映源树。VS2010将通过创建这些目录(如果它们不存在的话)开始构建。此外,对于那些在他们的目录名中讨厌空白的人,这个宏将删除所有空格,因此在使用时不需要使用双引号。

This is exactly what I wanted -- identical to the way my Makefiles work on the Ubuntu side, while still keeping the source tree clean.

这正是我想要的——与我的makefile在Ubuntu端工作的方式相同,同时保持源代码树的整洁。

#2


11  

This is easy to fix in the IDE. Click the first file in the folder, Shift+Click the last file so all of them are selected. Right-click, Properties, C++, Output Files. Change the Object File Name from $(IntDir)\ to, say, $(IntDir)\Univariate\. You can repeat for the Multivariate file group although that's not strictly necessary.

这很容易在IDE中修复。单击文件夹中的第一个文件,Shift+单击最后一个文件,这样所有文件都被选中。右键单击,属性,c++,输出文件。将对象文件名从$(IntDir)更改为$(IntDir)\Univariate\。您可以对多变量文件组进行重复,尽管这并不是绝对必要的。

#3


8  

You're right, VS can't handle that, and never could. The root problem is that it generates a .obj file for each .cpp file in the project, and they're all placed in the same folder. So you end up with multiple .cpp files compiling to Adaptive.obj in your case, for example.

你是对的,VS不能处理,也永远不能。根本问题是,它为项目中的每个.cpp文件生成一个.obj文件,它们都放在同一个文件夹中。因此,您最终将得到多个.cpp文件编译为Adaptive。比如obj。

At least the linker generates a warning for it now. That wasn't always the case.

至少链接器现在为它生成一个警告。情况并非总是如此。

You should be able to work around this by ensuring the files use different Intermediate Directory paths, but it is a bit of a hack around something that ought to be possible.

您应该能够通过确保文件使用不同的中间目录路径来解决这个问题,但是这是一种可能的方法。

Of course, you could always file a bug report or feature request on it on Microsoft Connect

当然,您总是可以在Microsoft Connect上提交bug报告或功能请求。

#4


5  

The other solutions in this thread suffer from the RelativeDir-based ../.. problems and having to set things manually on each source file.

这个线程中的其他解决方案都受到基于相对论的影响。问题和必须在每个源文件上手动设置东西。

Not to mention, they wreck /MP. Any solution which specifies an exact .obj for %(ObjectFileName) will result in a different /Fo for each .cpp file (to map it to a specific .obj file) passed to CL.exe and thus Visual Studio can't batch them. Without batching several .cpp files with identical commandlines (including /Fo) the /MP can't work.

更不用说,他们已经破产了。任何为%(ObjectFileName)指定精确的.obj的解决方案都会为传递给CL的每个.cpp文件(将其映射到特定的.obj文件)带来不同的/Fo。exe和Visual Studio不能批处理它们。如果没有使用相同的命令行(包括/Fo)的多个.cpp文件,那么/MP就不能工作。

Here's a new approach. This works on vs2010 through to vs2015 at least. Add this to your vcxproj in the <project>

这是一种新方法。这至少适用于vs2010到vs2015。在 中,将此添加到您的vcxproj中。

<!-- ================ 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 ================ -->

And then modify <project> so that it reads:

修改 ,使其为:

<Project InitialTargets="UNDUPOBJ" ...

The result will be something like myproj/src/a/x.cpp and myproj/src/b/x.cpp compiling to Debug/0/x.obj and Debug/1/x.obj. RelativeDirs arent employed and so aren't a problem.

结果就像myproj/src/a/x。cpp和myproj / src / b / x。cpp编译调试/ 0 / x。obj和调试/ 1 / x.obj。亲戚们没有工作,所以也不是问题。

Additionally, in this case, there will be only two different /Fo passed to CL.exe: Debug/0/ and Debug/1/. Consequently, no more than two batches will get issued to CL.exe, allowing the /MP to work more efficiently.

此外,在这种情况下,只有两个不同的/Fo传递给CL。exe:调试/ 0 /和调试/ 1 /。因此,给CL的批次不会超过两批。exe,允许/MP更有效地工作。

Other approaches would be basing the .obj subdirectories on the .cpp subdirectories, or making the .obj filename contain some memento of the original .cpp directory so that you can readily see a .cpp->.obj mapping, but those result in more /Fo and therefore less batching. Future work could dump a mapping file for quick reference, perhaps.

其他方法是将.obj子目录建立在.cpp子目录的基础上,或者使.obj文件名包含原始.cpp目录的一些内存,以便您可以很容易地看到.cpp->。obj映射,但是这会导致更多的/Fo,从而减少批处理。将来的工作可能会转储一个映射文件以供快速参考。

See this for more details on /MP and batching : MSVC10 /MP builds not multicore across folders in a project

请参阅此以获得更多关于/MP和批处理的详细信息:MSVC10 /MP在项目的文件夹中构建的不是多核。

I've tested this in production for quite a while on vs2010 and vs2015 on a variety of toolchains. It seems bulletproof, but there's always a chance it may interact badly with other msbuild customizations or exotic toolchains.

我在vs2010和vs2015中对各种工具链进行了相当长一段时间的生产测试。它似乎是防弹的,但它总有可能与其他msbuild定制或奇特的工具链发生严重的交互。

Starting in vs2015, if you get a warning "warning MSB8027: Two or more files with the name of X.cpp will produce outputs to the same location" then you can add this to your project or msbuild files:

从2015年的vs2015开始,如果你收到一个警告“MSB8027:两个或更多以X命名的文件。cpp将输出到相同的位置“然后您可以将此添加到您的项目或msbuild文件中:

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

See more at https://connect.microsoft.com/VisualStudio/feedback/details/797460/incorrect-warning-msb8027-reported-for-files-excluded-from-build and How to suppress specific MSBuild warning

请参阅https://connect.microsoft.com/visualstudio/feedback/details/797460/incorrect - wart-msb8027 - reporting -from-build以及如何抑制特定的MSBuild警告

#5


2  

use Configuration Properties > C/C++ > Ouptut Files> $(IntDir)\%(RelativeDir)\%(Filename)

使用配置属性> C/ c++ > Ouptut文件> $(IntDir)\%(RelativeDir)\%(文件名)

this will duplicate the source file structure under the debug directory and deposit the object file for each directory in a folder of the same name under the Debug directory

这将复制debug目录下的源文件结构,并将每个目录的对象文件保存在debug目录下同名文件夹中

#6


2  

Note that in my instance (Visual Studio 2013 with VS2010 platform toolset), using $(IntDir)\%(RelativeDir) doesn't work correctly, and it ignores the intermediate directory which results in linker errors when building multiple configurations because all of the object files for every configuration (ie, Debug and Release) are placed in the same folder. These go away if you clean the project when switching configurations.

注意,在我的实例(Visual Studio 2013 VS2010平台工具集),使用$(IntDir)\ %(RelativeDir)不能正常工作,它忽略了中间目录导致链接器错误在构建多个配置,因为所有的对象文件为每个配置(例如,调试和发布)被放置在相同的文件夹中。如果您在切换配置时清理项目,这些就会消失。

Error Example:

错误的例子:

MSVCRTD.lib(MSVCR100D.dll) : error LNK2005: _fclose already defined in LIBCMTD.lib(fclose.obj)

错误LNK2005: _fclose已经在LIBCMTD.lib中定义(fclose.obj)

Solution:

解决方案:

$(IntDir)\%(Directory)

$(IntDir)\ %(目录)

To get around this, I had to use $(IntDir)\%(Directory) which correctly placed all of the *.obj files under the intermediate directory and allowed building and linking multiple configurations without cleaning. The only downside is the entire (potentially long) folder hierarchy that your files are in will be completely re-created under the Debug/Release/etc folder.

为了解决这个问题,我必须使用$(IntDir)\%(目录)来正确地放置所有的*。obj文件在中间目录下,允许在不清理的情况下构建和链接多个配置。唯一的缺点是文件所在的整个(可能很长)文件夹层次结构将在Debug/Release/etc文件夹下被完全重新创建。

#7


1  

It also can be that you create a .cpp file in visual studio and later rename it to .h. Although the file is renamed visual studio still compiles it as a cpp file and therefore two obj files are created and linker warning shown.

也可以在visual studio中创建一个.cpp文件,然后将其重命名为.h。尽管这个文件被重新命名为visual studio,但是仍然将它编译为cpp文件,因此创建了两个obj文件,并显示了链接器警告。

#8


0  

The %(RelativeDir) solution only works for Visual Studo 2010.

%(相对论迪尔)解决方案只适用于Visual Studo 2010。

For Visual Studio 2008 you need to right click on each duplicate .cpp filename and choose "Properties". It may not be obvious but you can actually modify the "Configuration Properties -> C/C++ -> Ouptut Files" section for each individual file.

对于Visual Studio 2008,您需要右键单击每个复制的.cpp文件名并选择“Properties”。这可能不是很明显,但是您可以为每个单独的文件修改“配置属性-> C/ c++ -> Ouptut文件”一节。

Add a sub folder to the "Object File Name" setting of each of the duplicate .cpp files, note that .h files do not need to be modified as the .cpp file determines the .obj file output.

将子文件夹添加到每个复制的.cpp文件的“对象文件名”设置中,注意.h文件不需要修改,因为.cpp文件确定.obj文件输出。

For example, if your project has two conflicting files:

例如,如果您的项目有两个冲突的文件:

internal\example.cpp base\example.cpp

内部\例子。cpp基地\ example.cpp

You would set the "Object File Name" for each to:

将“对象文件名”设置为:

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

内部(IntDir)\ \美元(IntDir)\ \基地

You will need to do this for all configurations Release/Debug etc.

您将需要对所有配置、发布/调试等进行此操作。

#1


54  

So @Hans Passant pointed in the right direction, Thanks!! You don't have to list the file, a folder is sufficient. Then if you look in the defined macros at the bottom of the VS 2010 list, you'll see:

所以@Hans Passant指出了正确的方向,谢谢!你不需要列出文件,一个文件夹就足够了。然后,如果您查看VS 2010列表底部的已定义宏,您将看到:

%(RelativeDir)/ Univariate/

%(RelativeDir)/单变量

The problem, as posted, was actually a simplified version of what I'm working on -- a couple of levels of folders in a single project and there are a couple of name conflicts. Hence, I really wanted someway to just "fix" it...

问题是,正如所贴出的,实际上是我正在处理的一个简化版本——在一个项目中有两个级别的文件夹,并且有两个名称冲突。因此,我真的想要某种方法来“修复”它……

If you right click on the project in the solution explorer, choose C/C++ -> "Output Files" and type the following into the "Object File Name" box:

如果在解决方案资源管理器中右键单击项目,选择C/ c++ ->“输出文件”,在“对象文件名”框中输入以下内容:

$(IntDir)/%(RelativeDir)/

美元(IntDir)/ %(RelativeDir)/

Note that I also selected (All Configurations, All Platforms) from the drop downs. This will compile every file in a directory hierarchy which mirrors the source tree. VS2010 will begin the build by creating these directories if they don't exist. Further, for those who hate white space in their directory names, this macro does remove all spaces, so there is no need to play around with double quotes when using it.

注意,我还从下拉列表中选择了(所有配置、所有平台)。这将编译目录层次结构中的每个文件,该目录层次结构反映源树。VS2010将通过创建这些目录(如果它们不存在的话)开始构建。此外,对于那些在他们的目录名中讨厌空白的人,这个宏将删除所有空格,因此在使用时不需要使用双引号。

This is exactly what I wanted -- identical to the way my Makefiles work on the Ubuntu side, while still keeping the source tree clean.

这正是我想要的——与我的makefile在Ubuntu端工作的方式相同,同时保持源代码树的整洁。

#2


11  

This is easy to fix in the IDE. Click the first file in the folder, Shift+Click the last file so all of them are selected. Right-click, Properties, C++, Output Files. Change the Object File Name from $(IntDir)\ to, say, $(IntDir)\Univariate\. You can repeat for the Multivariate file group although that's not strictly necessary.

这很容易在IDE中修复。单击文件夹中的第一个文件,Shift+单击最后一个文件,这样所有文件都被选中。右键单击,属性,c++,输出文件。将对象文件名从$(IntDir)更改为$(IntDir)\Univariate\。您可以对多变量文件组进行重复,尽管这并不是绝对必要的。

#3


8  

You're right, VS can't handle that, and never could. The root problem is that it generates a .obj file for each .cpp file in the project, and they're all placed in the same folder. So you end up with multiple .cpp files compiling to Adaptive.obj in your case, for example.

你是对的,VS不能处理,也永远不能。根本问题是,它为项目中的每个.cpp文件生成一个.obj文件,它们都放在同一个文件夹中。因此,您最终将得到多个.cpp文件编译为Adaptive。比如obj。

At least the linker generates a warning for it now. That wasn't always the case.

至少链接器现在为它生成一个警告。情况并非总是如此。

You should be able to work around this by ensuring the files use different Intermediate Directory paths, but it is a bit of a hack around something that ought to be possible.

您应该能够通过确保文件使用不同的中间目录路径来解决这个问题,但是这是一种可能的方法。

Of course, you could always file a bug report or feature request on it on Microsoft Connect

当然,您总是可以在Microsoft Connect上提交bug报告或功能请求。

#4


5  

The other solutions in this thread suffer from the RelativeDir-based ../.. problems and having to set things manually on each source file.

这个线程中的其他解决方案都受到基于相对论的影响。问题和必须在每个源文件上手动设置东西。

Not to mention, they wreck /MP. Any solution which specifies an exact .obj for %(ObjectFileName) will result in a different /Fo for each .cpp file (to map it to a specific .obj file) passed to CL.exe and thus Visual Studio can't batch them. Without batching several .cpp files with identical commandlines (including /Fo) the /MP can't work.

更不用说,他们已经破产了。任何为%(ObjectFileName)指定精确的.obj的解决方案都会为传递给CL的每个.cpp文件(将其映射到特定的.obj文件)带来不同的/Fo。exe和Visual Studio不能批处理它们。如果没有使用相同的命令行(包括/Fo)的多个.cpp文件,那么/MP就不能工作。

Here's a new approach. This works on vs2010 through to vs2015 at least. Add this to your vcxproj in the <project>

这是一种新方法。这至少适用于vs2010到vs2015。在 中,将此添加到您的vcxproj中。

<!-- ================ 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 ================ -->

And then modify <project> so that it reads:

修改 ,使其为:

<Project InitialTargets="UNDUPOBJ" ...

The result will be something like myproj/src/a/x.cpp and myproj/src/b/x.cpp compiling to Debug/0/x.obj and Debug/1/x.obj. RelativeDirs arent employed and so aren't a problem.

结果就像myproj/src/a/x。cpp和myproj / src / b / x。cpp编译调试/ 0 / x。obj和调试/ 1 / x.obj。亲戚们没有工作,所以也不是问题。

Additionally, in this case, there will be only two different /Fo passed to CL.exe: Debug/0/ and Debug/1/. Consequently, no more than two batches will get issued to CL.exe, allowing the /MP to work more efficiently.

此外,在这种情况下,只有两个不同的/Fo传递给CL。exe:调试/ 0 /和调试/ 1 /。因此,给CL的批次不会超过两批。exe,允许/MP更有效地工作。

Other approaches would be basing the .obj subdirectories on the .cpp subdirectories, or making the .obj filename contain some memento of the original .cpp directory so that you can readily see a .cpp->.obj mapping, but those result in more /Fo and therefore less batching. Future work could dump a mapping file for quick reference, perhaps.

其他方法是将.obj子目录建立在.cpp子目录的基础上,或者使.obj文件名包含原始.cpp目录的一些内存,以便您可以很容易地看到.cpp->。obj映射,但是这会导致更多的/Fo,从而减少批处理。将来的工作可能会转储一个映射文件以供快速参考。

See this for more details on /MP and batching : MSVC10 /MP builds not multicore across folders in a project

请参阅此以获得更多关于/MP和批处理的详细信息:MSVC10 /MP在项目的文件夹中构建的不是多核。

I've tested this in production for quite a while on vs2010 and vs2015 on a variety of toolchains. It seems bulletproof, but there's always a chance it may interact badly with other msbuild customizations or exotic toolchains.

我在vs2010和vs2015中对各种工具链进行了相当长一段时间的生产测试。它似乎是防弹的,但它总有可能与其他msbuild定制或奇特的工具链发生严重的交互。

Starting in vs2015, if you get a warning "warning MSB8027: Two or more files with the name of X.cpp will produce outputs to the same location" then you can add this to your project or msbuild files:

从2015年的vs2015开始,如果你收到一个警告“MSB8027:两个或更多以X命名的文件。cpp将输出到相同的位置“然后您可以将此添加到您的项目或msbuild文件中:

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

See more at https://connect.microsoft.com/VisualStudio/feedback/details/797460/incorrect-warning-msb8027-reported-for-files-excluded-from-build and How to suppress specific MSBuild warning

请参阅https://connect.microsoft.com/visualstudio/feedback/details/797460/incorrect - wart-msb8027 - reporting -from-build以及如何抑制特定的MSBuild警告

#5


2  

use Configuration Properties > C/C++ > Ouptut Files> $(IntDir)\%(RelativeDir)\%(Filename)

使用配置属性> C/ c++ > Ouptut文件> $(IntDir)\%(RelativeDir)\%(文件名)

this will duplicate the source file structure under the debug directory and deposit the object file for each directory in a folder of the same name under the Debug directory

这将复制debug目录下的源文件结构,并将每个目录的对象文件保存在debug目录下同名文件夹中

#6


2  

Note that in my instance (Visual Studio 2013 with VS2010 platform toolset), using $(IntDir)\%(RelativeDir) doesn't work correctly, and it ignores the intermediate directory which results in linker errors when building multiple configurations because all of the object files for every configuration (ie, Debug and Release) are placed in the same folder. These go away if you clean the project when switching configurations.

注意,在我的实例(Visual Studio 2013 VS2010平台工具集),使用$(IntDir)\ %(RelativeDir)不能正常工作,它忽略了中间目录导致链接器错误在构建多个配置,因为所有的对象文件为每个配置(例如,调试和发布)被放置在相同的文件夹中。如果您在切换配置时清理项目,这些就会消失。

Error Example:

错误的例子:

MSVCRTD.lib(MSVCR100D.dll) : error LNK2005: _fclose already defined in LIBCMTD.lib(fclose.obj)

错误LNK2005: _fclose已经在LIBCMTD.lib中定义(fclose.obj)

Solution:

解决方案:

$(IntDir)\%(Directory)

$(IntDir)\ %(目录)

To get around this, I had to use $(IntDir)\%(Directory) which correctly placed all of the *.obj files under the intermediate directory and allowed building and linking multiple configurations without cleaning. The only downside is the entire (potentially long) folder hierarchy that your files are in will be completely re-created under the Debug/Release/etc folder.

为了解决这个问题,我必须使用$(IntDir)\%(目录)来正确地放置所有的*。obj文件在中间目录下,允许在不清理的情况下构建和链接多个配置。唯一的缺点是文件所在的整个(可能很长)文件夹层次结构将在Debug/Release/etc文件夹下被完全重新创建。

#7


1  

It also can be that you create a .cpp file in visual studio and later rename it to .h. Although the file is renamed visual studio still compiles it as a cpp file and therefore two obj files are created and linker warning shown.

也可以在visual studio中创建一个.cpp文件,然后将其重命名为.h。尽管这个文件被重新命名为visual studio,但是仍然将它编译为cpp文件,因此创建了两个obj文件,并显示了链接器警告。

#8


0  

The %(RelativeDir) solution only works for Visual Studo 2010.

%(相对论迪尔)解决方案只适用于Visual Studo 2010。

For Visual Studio 2008 you need to right click on each duplicate .cpp filename and choose "Properties". It may not be obvious but you can actually modify the "Configuration Properties -> C/C++ -> Ouptut Files" section for each individual file.

对于Visual Studio 2008,您需要右键单击每个复制的.cpp文件名并选择“Properties”。这可能不是很明显,但是您可以为每个单独的文件修改“配置属性-> C/ c++ -> Ouptut文件”一节。

Add a sub folder to the "Object File Name" setting of each of the duplicate .cpp files, note that .h files do not need to be modified as the .cpp file determines the .obj file output.

将子文件夹添加到每个复制的.cpp文件的“对象文件名”设置中,注意.h文件不需要修改,因为.cpp文件确定.obj文件输出。

For example, if your project has two conflicting files:

例如,如果您的项目有两个冲突的文件:

internal\example.cpp base\example.cpp

内部\例子。cpp基地\ example.cpp

You would set the "Object File Name" for each to:

将“对象文件名”设置为:

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

内部(IntDir)\ \美元(IntDir)\ \基地

You will need to do this for all configurations Release/Debug etc.

您将需要对所有配置、发布/调试等进行此操作。