OITA: Oika's Information Technological Activities

@oika 情報技術的活動日誌。

VisualStudio ビルド時のファイルコピー先をプロジェクト階層と違う場所にする

VisualStudio(というかMSBuildなんだけど)では、
ソースファイルでない任意のプロジェクトファイルについて
ソリューション エクスプローラーからビルドアクションを指定するときに
出力ディレクトリへのコピー有無を指定できる。

ただし、設定できるのはコピーするかどうかを決めるモードだけで、
コピー先のディレクトリを変えたりすることはできない。

例えば、以下のような構成で

Project Root\
    ├ ProjName.csproj
    ├ AClassFile.cs
    ├ bin\
    ├ obj\
    ├ Properties\
    └ img\
        └ image.jpg

image.jpgをプロジェクトに追加して、プロパティの
「出力ディレクトリにコピー」を「常にコピーする」にしてリリースビルドすると、
ファイルは bin\Release\img\image.jpg にコピーされる。

このコピー先をたとえば、 bin\Release\out\image.jpg にしたい場合。

ひとつの力技としては、プロジェクトのプロパティから
ビルドイベント>「ビルド後に実行するコマンド ライン」に

mkdir $(TargetDir)out  
copy $(ProjectDir)img\image.jpg $(TargetDir)out\  

のような感じで自前のコピー処理を入れてやるという手はある。

けど、もう少しシンプルな解決策がありました。

一度VisualStudioを閉じてから、.csprojをテキストエディタで開き、
image.jpgをインクルードしている部分を探す。

<ItemGroup>  
  <Content Include="img\image.jpg">  
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>  
  </Content>  
</ItemGroup>  

これに以下の1行を追加。

<ItemGroup>  
  <Content Include="img\image.jpg">  
    <Link>out\image.jpg</Link>  
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>  
  </Content>  
</ItemGroup>  

これだけで期待通りの動きになる。

Link要素は本来、既存の項目をリンクとしてプロジェクトに追加する際に使用されるもので
要素の値は追加先のディレクトリパスだ。
なので上の書き方だと、 img\image.jpg に配置されているファイルを
一旦リンクとして out\image.jpg に追加し、それがビルド時にコピーされるので
出力先が bin\Release\out\image.jpg になる。

ただ欠点は、VisualStudioのソリューション エクスプローラーで見ても
こんな書き方になっていることがわからないことかな。

build-copy

上のように img\image.jpg のファイルはプロジェクトに含まれていないように見える。
そして(VS2013以降だけ?)outフォルダがプロジェクトディレクトリ内にも勝手に生成されるが、
中身は空のままなので、なんだこのフォルダは??ってなるかも。