web-dev-qa-db-ja.com

Visual Studio 2010&2008は、異なるフォルダーにある同じ名前のソースファイルを処理できませんか?

直接の質問:同じ名前の2つのファイルがある(ただし、ディレクトリが異なる)場合、Visual Studio 2005のみがこれを透過的に処理できるようです。 VS 2008&2010には、微調整が必​​要ですか?私の命名規則は別として、私は何か間違っていますか?

背景:

C++統計ライブラリーを開発しています... 2つのフォルダーがあります。

/ 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

クロスコンパイルをサポートする必要があります-これらの同じファイルをLinuxのライブラリにコンパイルするためにg ++/makeを使用しています。彼らはうまく働きます。

私は問題なくVisual Studio 2005を使用していましたが、Visual Studio 2008または2010にアップグレードする必要があります(現在、nVidiaのnsightツールに依存しています)。ただし、同じ名前のプロジェクトにファイルを追加すると、(別のディレクトリにある場合でも)問題が発生します。命名規則を変更するつもりですが、他の人がこの問題に遭遇してwell文書化された解決策を見つけたかどうか知りたいのですが?

さらに、2005プロジェクトから2010プロジェクトにアップグレードした場合、VS 2010 isは、異なるディレクトリにある同じ名前の2つのソースファイルを正しく処理できるように見えます。ただし、重複するファイルの1つを削除してからプロジェクトに再度追加すると、次の警告が表示されます。

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で有効なバグを発見しましたか?

57
M. Tibbits

@Hans Passantが正しい方向を指してくれて、ありがとう!!ファイルをリストする必要はありません。フォルダで十分です。次に、VS 2010リストの下部にある定義済みマクロを見ると、次のように表示されます。

%(RelativeDir)/一変量/

投稿されたように、問題は実際には私が取り組んでいるものの簡略化されたバージョンでした-単一のプロジェクト内のいくつかのレベルのフォルダーであり、いくつかの名前の競合があります。したがって、私は本当にそれを単に「修正」したいと思っていました...

ソリューションエクスプローラーでプロジェクトを右クリックした場合は、[C/C++]-> [出力ファイル]を選択し、[オブジェクトファイル名]ボックスに次のように入力します。

$(IntDir)/%(RelativeDir)/

ドロップダウンから(すべての構成、すべてのプラットフォーム)も選択したことに注意してください。これにより、ソースツリーを反映したディレクトリ階層内のすべてのファイルがコンパイルされます。 VS2010は、これらのディレクトリが存在しない場合は、それらを作成することからビルドを開始します。さらに、ディレクトリ名に空白が嫌いな人のために、このマクロはすべてのスペースを削除するので、使用するときに二重引用符をいじる必要はありません。

これは正確に私が欲しかったものです-ソースツリーをクリーンに保ちながら、MakefileがUbuntu側で機能する方法と同じです。

63
M. Tibbits

これはIDEで簡単に修正できます。フォルダ内の最初のファイルをクリックし、Shiftキーを押しながら最後のファイルをクリックして、すべてのファイルが選択されるようにします。右クリック、[プロパティ]、[C++]、[出力ファイル]。オブジェクトファイル名を$(IntDir)\から、たとえば$(IntDir)\Univariate\に変更します。厳密に必要なわけではありませんが、多変量ファイルグループに対して繰り返すことができます。

12
Hans Passant

そうです、VSはそれを処理できません。根本的な問題は、プロジェクト内の.objファイルごとに.cppファイルを生成し、それらがすべて同じフォルダーに配置されることです。したがって、たとえば、あなたのケースでは、複数の.cppファイルがAdaptive.objにコンパイルされることになります。

少なくともリンカは現在、警告を生成します。いつもそうであるとは限りませんでした。

ファイルが異なる中間ディレクトリパスを使用していることを確認することでこれを回避できるはずですが、これはすべきことを少しハックすることです可能。

もちろん、バグレポートや機能リクエストはいつでも Microsoft Connect に提出できます。

8
jalf

このスレッドの他のソリューションでは、RelativeDirベースの../ ..問題が発生し、各ソースファイルで手動で設定する必要があります。

言うまでもなく、彼らは/ MPを破壊します。 %(ObjectFileName)に正確な.objを指定するソリューションでは、CL.exeに渡される(特定の.objファイルにマップする)各.cppファイルに対して異なる/ Foが発生するため、Visual Studioはそれらをバッチ処理できません。同じコマンドライン(/ Foを含む)でいくつかの.cppファイルをバッチ処理しないと、/ MPは機能しません。

これが新しいアプローチです。これは、少なくともvs2010からvs2015まで機能します。これを<project>のvcxprojに追加します

<!-- ================ UNDUPOBJ ================ -->
<!-- relevant topics -->
<!-- https://stackoverflow.com/questions/3729515/visual-studio-2010-2008-cant-handle-source-files-with-identical-names-in-diff/26935613 -->
<!-- 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>を次のように変更します。

<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は2つだけです。Debug/ 0 /とDebug/1 /です。その結果、CL.exeに発行されるバッチは2つまでになり、/ MPがより効率的に機能します。

他のアプローチとしては、.objサブディレクトリを.cppサブディレクトリに基づいて作成するか、.objファイル名に元の.cppディレクトリのメモを含めて、.cpp->。objマッピングを簡単に確認できるようにします。/Foなので、バッチ処理が少なくなります。将来の作業では、おそらくクイックリファレンス用にマッピングファイルをダンプする可能性があります。

/ MPとバッチ処理の詳細については、これを参照してください。 MSVC10/MPビルドは、プロジェクト内のフォルダー間でマルチコアではありません

私はこれを、vs2010とvs2015のさまざまなツールチェーンで運用環境でかなり長い間テストしてきました。防弾のように見えますが、他のmsbuildのカスタマイズやエキゾチックなツールチェーンとうまく相互作用しない可能性は常にあります。

Vs2015以降、「MSB8027の警告:X.cppという名前の2つ以上のファイルが同じ場所に出力を生成する」という警告が表示された場合は、これをプロジェクトまたは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警告を抑制する方法

6
zeromus

私のインスタンス(VS2010プラットフォームツールセットを備えたVisual Studio 2013)では、$(IntDir)\%(RelativeDir)を使用しても正しく機能せず、複数の構成をビルドするときにリンカーエラーが発生する中間ディレクトリが無視されることに注意してください。すべての構成(つまり、デバッグとリリース)のオブジェクトファイルは、同じフォルダーに配置されます。構成を切り替えるときにプロジェクトをクリーンアップすると、これらはなくなります。

エラーの例:

MSVCRTD.lib(MSVCR100D.dll):エラーLNK2005:_fcloseはすでにLIBCMTD.lib(fclose.obj)で定​​義されています

ソリューション:

$(IntDir)\%(Directory)

これを回避するために、すべての* .objファイルを中間ディレクトリの下に正しく配置し、クリーニングなしで複数の構成を構築およびリンクできる$(IntDir)\%(Directory)を使用する必要がありました。唯一の欠点は、ファイルが含まれている(潜在的に長い)フォルダー階層全体が、Debug/Release/etcフォルダーの下に完全に再作成されることです。

4
syplex

[構成プロパティ]> [C/C++]> [出力ファイル]> $(IntDir)\%(RelativeDir)\%(Filename)を使用します

これにより、デバッグディレクトリの下のソースファイル構造が複製され、各ディレクトリのオブジェクトファイルがDebugディレクトリの下の同じ名前のフォルダに配置されます。

3
Christoph

また、Visual Studioで.cppファイルを作成し、後で.hに名前を変更することもできます。ファイルの名前は変更されますが、Visual Studioはそれをcppファイルとしてコンパイルするため、2つのobjファイルが作成され、リンカーの警告が表示されます。

1
Povilas

%(RelativeDir)ソリューションはVisual Studo 2010でのみ機能します。

Visual Studio 2008の場合、重複する.cppファイル名をそれぞれ右クリックして、[プロパティ]を選択する必要があります。明白ではないかもしれませんが、実際には、個々のファイルごとに「構成プロパティ-> C/C++->出力ファイル」セクションを変更できます。

重複する各.cppファイルの「オブジェクトファイル名」設定にサブフォルダを追加します。cppファイルが.objファイルの出力を決定するため、.hファイルを変更する必要がないことに注意してください。

たとえば、プロジェクトに2つの競合するファイルがある場合:

internal\example.cpp base\example.cpp

それぞれに「オブジェクトファイル名」を次のように設定します。

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

すべての構成のリリース/デバッグなどでこれを行う必要があります。

0
user3700221