NuGetの設定を別プロジェクトへ引き継ぐ方法 (Windows版Caffeを例に)

概要

  • Visual C++でNuGetパッケージの設定を別プロジェクトへ引き継ぐ方法の紹介です。
  • プロジェクトの参照をする際、NuGetパッケージの情報を記載したプロパティシートを読み込むだけで簡単に参照先のNuGetパッケージを利用できるようになります。
  • Visual Studio 2013 でCaffe(libcaffe)のNuGetパッケージも含めて参照したサンプルコードを公開しました。

はじめに

この記事はVisual Studio Advent Calendar 2016の22日目の記事です。

Visual C++プロジェクトの設定に関して、GUI操作だけでなく各種設定用XMLファイル(.vcxprojや.props)を直接編集できる方を対象とした内容です。

NuGetとは.NETなどMicrosoft開発プラットフォームのライブラリ管理ツールです。Visual Studio 2012からは標準でインストールされています。

NuGetの基本的な使い方は下記をご参照ください。

qiita.com

ここで、NuGetを用いて利用する外部ライブラリをNuGetパッケージと呼ぶことにします。

Visual C++でNuGetパッケージをインストールする際、GUI操作でインストールすると、NuGetパッケージの設定がそのプロジェクトにしか適用されません。 それゆえ、Windows版Caffeのように、複数のNuGetパッケージを利用するものは、それを参照するすべてのプロジェクトにNuGetパッケージをインストールしなければならなくなり、面倒です。

そこで本記事は、Windows版Caffeを例に、様々なVisual C++プロジェクトで個々にGUI操作によるインストールをせずに特定のNuGetパッケージを利用する方法を紹介します。 具体的には、プロパティシートへ特定のNuGetパッケージを利用するための設定を行い、そのプロパティシートをプロジェクトに取り込むことで利用できるようにします。

なお、この方法は筆者がなんとなく思いついたものですが、他にもっと便利な方法でこの問題を解決できる方法があれば教えていただきたいです。

Windows版Caffe

Caffeは有名なディープラーニングフレームワークです。 Windows版Caffeのライブラリプロジェクト"libcaffe"を用いて、"libcaffe"を別プロジェクト"classification"で参照する場合を例に説明します。

下記のように説明用のVisual C++ソリューションは、boostやOpenCVなど16個のNuGetパッケージを用いるCaffeのライブラリプロジェクト"libcaffe"と、"libcaffe"を参照する"classification"プロジェクトが定義されているとします。

projects

overview

このとき、"classification"に対して"caffelib"が依存しているNuGetパッケージをGUIインストールせずに利用できるようにします。

そもそも、NuGetパッケージをインストールするとは?

GUI操作でNuGetパッケージをインストールすると、下記のことが自動的に行われます。

(1) インストール対象プロジェクトのプロジェクトディレクトリ$(ProjectDir)にファイル"packages.config" がなければ、"packages.config"が作られる。

(2) "packages.config" がプロジェエクトに追加される。

packages.config

(3) "packages.config"にインストールしたパッケージの情報(id, version,targetFramework)が書き込まれる。

packages.config

(4) インストールしたNuGetパッケージのプロパティシート(.props)を読み込む記述がプロジェクトファイル(.vcxproj)に追記される(あれば)。

(5) インストールしたNuGetパッケージのtargetファイル(.targets)を読み込む記述がプロジェクトファイル(.vcxproj)に追記される。

(6) 所定の場所(デフォルトでは$(SolutionDir)/packages)の下にnugetのパッケージがダウンロードされる。

上記のうち、

  • 参照先("caffelib")プロジェクトに必要なものは(1), (3)。

  • 参照元と参照先の双方のプロジェクトに必要なものは(4), (5)。

  • 必要でないものは(2),(6)。 (2)はVisual Studio上で表示するための設定。(6)は(1), (3), (4), (5)の設定があればビルド時に自動的にダウンロードされる。

提案のNuGetパッケージの利用方法

参照先("caffelib")プロジェクトへの設定

$(ProjectDir)/packages.config を作成してその中にNuGetパッケージの情報を記載しておきます(上記(1),(3)に対応)。

packages.config

プロパティシートの作成

プロパティシートを作成して、NuGetパッケージ内の.propsや.targetsを読み込ませます(上記(4),(5)に対応)。 なお筆者は、GUIで一度NuGetパッケージをインストールした後に、.vcxprojへ書き込まれた内容を.propsへ移すことでプロパティシートの記述を行いました。 この時ユーザマクロでバージョン等を指定しておくと、変更に柔軟になると思います。

サンプルプロパティシート: CaffeBuildSettimgs.props

  <PropertyGroup Label="UserMacros">
    <NugetDir>$(SolutionDir)\packages\</NugetDir>
    <BoostVersion>1.59.0.0</BoostVersion>
    <GflagsVersion>2.1.2.1</GflagsVersion>
...
  </PropertyGroup>

...

  <ImportGroup Label="PropertySheets">
    <Import Project="$(NugetDir)\glog.$(GlogVersion)\build\native\glog.props" Condition="Exists('$(NugetDir)\glog.$(GlogVersion)\build\native\glog.props')" />
    <Import Project="$(NugetDir)\gflags.$(GflagsVersion)\build\native\gflags.props" Condition="Exists('$(NugetDir)\gflags.$(GflagsVersion)\build\native\gflags.props')" />
   ...
  </ImportGroup>

...


 <ImportGroup Label="ExtensionTargets">
    <Import Project="$(NugetDir)\OpenCV.$(OpenCVVersion)\build\native\OpenCV.targets" Condition="Exists('$(NugetDir)\OpenCV.$(OpenCVVersion)\build\native\OpenCV.targets')" />
    <Import Project="$(NugetDir)\boost.$(BoostVersion)\build\native\boost.targets" Condition="Exists('$(NugetDir)\boost.$(BoostVersion)\build\native\boost.targets')" />
    <Import Project="$(NugetDir)\boost_date_time-vc$(PlatformToolsetVersion).$(BoostVersion)\build\native\boost_date_time-vc$(PlatformToolsetVersion).targets" Condition="Exists('$(NugetDir)\boost_date_time-vc$(PlatformToolsetVersion).$(BoostVersion)\build\native\boost_date_time-vc$(PlatformToolsetVersion).targets')" />
  ...
  </ImportGroup>

参照先("caffelib")と参照元("classification")のプロジェクトへの設定

プロジェクトへ先ほど作成したプロパティシートを適用します。

おまけ:NuGetパッケージのダウンロードディレクトリ変更

$(SolutionDir)/nuget.configを作成して下記のようにrepositoryPathを設定すれば、NuGetパッケージのダウンロード先を変更することができます。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="nuget.org" value="https://www.nuget.org/api/v2/" />
  </packageSources>
  <repositoryPath>../NuGetPackages</repositoryPath>
</configuration>

nuget.config

Visual Studio 2013のサンプルプログラム

github.com

おわりに

NuGetを用いると様々なライブラリが簡単に利用・管理できるので便利だなと思っていたのですが、依存ライブラリが多い場合に毎度GUIでポチポチするのも怠かったので、いい方法がないかなと思っていました。 紹介したような方法でNuGetパッケージ読み込みをプロパティシートへ移しておけば、Caffeなどの依存ライブラリが多いプロジェクトを参照したプロジェクトを簡単に作成できるようになります。 なお、NuGetでインストールしたライブラリの別バージョンがインストールされていた場合(例えばOpenCVなど)、NuGetの方が優先されます(出力先にdllがコピーされるため)。コードを他人と共有するときなどは、NuGetでバージョン管理しておくと便利です。

Visual Studioにはまだまだ知らない使い方があると思うので、もっと使いこなせるようになりたいなぁ。