C++プロジェクトでNuGetを使ってみようよ！（Vol. 2：パッケージ作成編）

こんにちはー！ニアです。

今回は、[C++プロジェクトでNuGetを使ってみようよ！（Vol. 1：パッケージ導入編）](http://qiita.com/nia_tn1012/items/77c3c1b006f7c69d30ac) の続編で、C++用のNuGetパッケージを作成する基本的な方法について紹介していきます。

#1. C++用のNuGetパッケージを作成するために必要なもの
C++向けのNuGetパッケージに必要なものは以下の4点です。但し、プロパティシートは必要に応じて用意します。

1. **NuGetパッケージに入れたいライブラリ**
2. **Nuspecファイル**
3. **プロパティシート**
4. **nuget.exe**

* Nuspecファイルとは、パッケージの情報やパッケージに含めるファイルの場所などが定義されたXMLファイルで、拡張子は「.nuspec」です。
* プロパティシートとは、C++のプロジェクトの設定するためのXMLファイルで、拡張子は「.props」または「.targets」です。
* nuget.exeはNuGetパッケージを作成するために必要な実行ファイルです。[Installing NuGet](http://docs.nuget.org/consume/installing-nuget) の「Installing and Updating NuGet Client」→「4. Command-Line Utility」のところにあるリンク「Direct Download」からダウンロードすることができます。

#2. NuGetパッケージを作ってみよう

NuGetパッケージに入れたいライブラリを作成したら、NuGetパッケージに必要なファイルを作成していきましょう。

※この記事ではサンプルとして、以下のソースコードで作成したライブラリをNuGetパッケージにしていきます。

◆ ライブラリのソースコード

```cpp:Person.h
#pragma once
#include <string>

class Person {
private:
	// 名前
	std::string name;
	// 年齢
	int age;

public:
	// コンストラクター
	Person( std::string n, int a ) : name( n ), age( a ) {}

	// 名前、年齢の取得
	std::string Name() const { return name; }
	int Age() const { return age; }

	// 文字情報の取得
	std::string ToString() const;
};
```

```cpp:Person.cpp
#include "Person.h"
#include <sstream>

using namespace std;

string Person::ToString() const {
	ostringstream oss;

	oss << "Name : " << this->Name() << endl;
	oss << "Age : " << this->Age();

	return oss.str();
}
```

◆ 作成するライブラリ
>Person_d.lib
Person.lib

◆ ライブラリとリンクするためのヘッダーファイル

```cpp:NuGetTrial.h
#pragma once

#include "Person.h"

// ライブラリ
#if _DEBUG	// デバッグモードの時
#pragma comment( lib, "../lib/Person_d.lib" )
#else		// リリースモードの時
#pragma comment( lib, "../lib/Person.lib" )
#endif
```

##2.1. NuGetパッケージを作成するためのフォルダーを構成する
まずは、以下のような構成を持つフォルダーを作成します。

![n2-1.png](https://qiita-image-store.s3.amazonaws.com/0/78881/88c1b43e-44dd-b443-ff9c-0c1450ff3553.png)


1. ルートとなるフォルダーの中にnuget.exeを入れ、「**build**」（または「lib」）と名付けたフォルダーを作成します。
2. buildフォルダーの中に「**native**」と名付けたフォルダーを作成します。
3. nativeフォルダーの中にライブラリをいれます。

この記事のサンプルでは、nativeフォルダー中に「include」フォルダーと「lib」をフォルダーを作成し、includeフォルダーの中に「NuGetTrial.h」と「Persion.h」を入れ、libフォルダーに「Person_d.lib」と「Person.lib」を入れます。

![n2-2.png](https://qiita-image-store.s3.amazonaws.com/0/78881/6df25014-15b1-62a6-2e37-003d7f0a5585.png)


##2.2. Nuspecファイルを作成する
次に、Nuspecファイルを作成します。中身はXMLなので1から作ることもできますが、nuget.exeを使ってひな形を作成することができます。

コマンドプロンプトを起動し、ルートとなるフォルダーに移動して、以下のコマンドを実行します。

```bat:nuget.exeを使って、Nuspecファイルのひな形を作成するコマンド
nuget spec [Nuspecのファイル名]
```

※[Nuspecのファイル名]には、拡張子を除いたファイル名を入力します。省略した場合、「Package」という名前になります。

```xml:Nuspecファイルのひな形（[Nuspecのファイル名].nuspec）
<?xml version="1.0"?>
<package >
  <metadata>
    <id>[Nuspecのファイル名]</id>
    <version>1.0.0</version>
    <authors>[ユーザー名]</authors>
    <owners>[ユーザー名]</owners>
    <licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl>
    <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl>
    <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>Package description</description>
    <releaseNotes>Summary of changes made in this release of the package.</releaseNotes>
    <copyright>Copyright 2015</copyright>
    <tags>Tag1 Tag2</tags>
    <dependencies>
      <dependency id="SampleDependency" version="1.0" />
    </dependencies>
  </metadata>
</package>
```

### Nuspecで使用できる主な要素とその概要
>参考：Nuspec Reference | nuget
https://docs.nuget.org/create/nuspec-reference

※太字のノード名は必須の要素です。

|ノード名|概要|
|:---:|---|
|**package**|Nuspecファイルのルートノードです。|
|**metadata**|このノードの中にNuGetパッケージの情報を入れます。|

* metadataノード内

|ノード名|概要|
|:---:|---|
|**id**|NuGetパッケージを識別するたの一意的な名前を入力します。（注：パッケージのURLにも使用されるので、半角スペースやURLで使用不可能な文字を含めることはできません。）（例：Abc.Def）|
|**version**|バージョンを入力します。（例：1.2.3）|
|title|パッケージのタイトル名を入力します。省略した場合、**id**に入力した値を使用します。|
|**author**|パッケージに入れたライブラリの作者名をカンマで区切って入力します。（例：Alice, bob）|
|owner|NuGetパッケージの作成者名をカンマで区切って入力します。|
|**description**|パッケージの詳細な説明を入力します。|
|releaseNotes|リリースノート（改訂履歴）を入力します。|
|summary|パッケージの概要を入力します。省略した場合、**description**に入力した値を使用します。|
|language|言語（ロケール）を入力します。（例：ja-jp）|
|projectUrl|パッケージのプロジェクトのURLを入力します。|
|iconUrl|パッケージマネージャーに表示させるアイコンのURLを入力します。|
|licenseUrl|ライセンス文のURLを入力します。（注：requireLicenseAcceptanceの値をtrueに設定した場合、この要素は必須です。）|
|copyright|パッケージのコピーライトを入力します。（例：Copyright (C) 2014-2015 Chronoir.net）|
|requireLicenseAcceptance|パッケージのインストール前に、利用者にライセンスの同意をさせたい場合、trueに設定します。省略した場合、falseとなります。|
|dependencies|このノードの中に依存関係となるパッケージのノード（dependency）を入れます。dependencyノードのid属性にそのパッケージのid、version属性にそのパッケージの最小バージョンを入力します。（例：\<dependency id="dpd_id" version="1.0.1"\>）|
|tags|パッケージのタグを半角スペースで区切って入力します。パッケージマネージャーの検索などで、そのタグを使って絞り込むことができます。（例：C++ Test Lib）|

作成したNuspecファイルを以下のように編集します。

```xml:NuGetTrial.nuspec
<?xml version="1.0"?>
<package>
	<metadata>
		<id>NuGetTrial</id>
		<title>NuGetのテスト</title>
		<version>1.0.0</version>
		<authors>Nia Tomonaka</authors>
		<owners>Nia Tomonaka</owners>
		<requireLicenseAcceptance>false</requireLicenseAcceptance>
		<summary>NuGetのテスト（※ここには概要が入ります）</summary>
		<description>NuGetのテスト（※ここには詳細な説明が入ります）</description>
		<releaseNotes>Ver. 1.00: 初版リリース</releaseNotes>
		<copyright>Copyright(C) 2014-2015 Chronoir.net</copyright>
		<tags>C++ NuGetTest</tags>
		<language>ja-JP</language>
	</metadata>
</package>
```

![n2-3.png](https://qiita-image-store.s3.amazonaws.com/0/78881/a1f9b4a9-3faf-cf97-1ed9-e78c52d6b8ac.png)


##2.3. プロパティシートを作成する
C++用のNuGetパッケージをインストールすると、「[ソリューションのフォルダー]/packages/[NuGetパッケージ名]」のフォルダーにパッケージが展開されるので、ヘッダーをインクルードする時はそのフォルダーを経由する必要があります。

そこで、プロパティシートを使い、「追加のインクルードディレクトリ」や「追加のライブラリディレクトリ」にそのフォルダーを追加することで、ヘッダーファイルをインクルードしやすくなります。

```cpp:「追加のインクルードディレクトリ」に追加前
// 「[ソリューションのフォルダー]/[プロジェクトのフォルダー]」にあるソースファイルで、
// 「[ソリューションのフォルダー]/packages/NuGetTrial.1.0.0/build/native」フォルダーにある
// NuGetTrial.h をインクルードします。
#include "../packages/NuGetTrial.1.0.0/build/native/NuGetTrial.h"
```

↓

```cpp:「追加のインクルードディレクトリ」に追加後
// 「[ソリューションのフォルダー]/packages/[NuGetパッケージ名]/build/native」フォルダーを
// 追加のインクルードディレクトリに追加します。
#include "NuGetTrial.h"
```

まず、プロパティーシート（.props）ファイルを作成します。

```xml:NuGetTrial.props
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<ImportGroup Label="PropertySheets"/>
	<PropertyGroup>
	</PropertyGroup>
</Project>
```

次に、プロパティーシート（.targets）ファイルを作成します。

```xml:NuGetTrial.targets
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<ImportGroup Label="PropertySheets"/>
	<PropertyGroup>
	</PropertyGroup>
	<ItemDefinitionGroup>
		<!-- プロジェクトファイルの「構成プロパティ」→「C++」に該当します。 -->
		<ClCompile>
			<!-- マクロを「HAS_NUGETTRIAL」を定義します。 -->
			<PreprocessorDefinitions>HAS_NUGETTRIAL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
			<!-- 追加のインクルードディレクトリに追加するフォルダーを指定します。 -->
			<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)../../build/native/include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
		</ClCompile>
		<!-- プロジェクトファイルの「構成プロパティ」→「リンカー」に該当します。 -->
		<Link>
			<!-- 追加のライブラリディレクトリに追加するフォルダーを指定します。 -->
			<AdditionalLibraryDirectories>$(MSBuildThisFileDirectory)../../build/native/lib/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
		</Link>
	</ItemDefinitionGroup>
</Project>
```

「$(MSBuildThisFileDirectory)」は、それを書いたファイルのディレクトリを表します。

> 参考 : MSBuild の予約済みおよび既知のプロパティ | MSDN ライブラリ
https://msdn.microsoft.com/ja-jp/library/ms164309.aspx

.propsのファイルと.targetsのファイルの違いは、プロジェクトファイル（.vcxproj）にインポートするタイミングが異なることです。

>参考 : Deep Dive into NuGet Native (Part One) | Past We Saw
http://blog.tnzk.org/post/60429873815/deep-dive-into-nuget-native-part-one

```xml:プロジェクトファイル（.vcxproj）
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  
  <!-- プロパティシート（ .props ）はプロジェクトファイルの最上部にインポートされます。 -->
  <Import Project="..\packages\NuGetTrial.1.0.0\build\native\NuGetTrial.props" Condition="Exists('..\packages\NuGetTrial.1.0.0\build\native\NuGetTrial.props')" />
  
  <!-- 中略（プロジェクトの設定） -->
  
  <!-- プロパティシート（ .targets ）はプロジェクトファイルの最下部にインポートされます。 -->
  <ImportGroup Label="ExtensionTargets">
    <Import Project="..\packages\NuGetTrial.1.0.0\build\native\NuGetTrial.targets" Condition="Exists('..\packages\NuGetTrial.1.0.0\build\native\NuGetTrial.targets')" />
  </ImportGroup>
  
</Project>
```

プロパティシートを作成したら、nativeフォルダーの中に入れます。

![n2-4.png](https://qiita-image-store.s3.amazonaws.com/0/78881/f0811826-d9b8-5d4b-012f-ba0bce0f23f3.png)


##2.4. パッケージを作成する
パッケージに必要なファイルが揃ったら、いよいよパッケージを作成していきます。

コマンドプロンプトを起動し、ルートとなるフォルダーに移動して、以下のコマンドを実行します。

```bat:NuGetパッケージを作成するコマンド
nuget pack [Nuspecのファイル名].nuspec
```

※[Nuspecのファイル名]には、作成したNuspecファイルの名前を入れます。

パッケージ作成に成功すると、**[id].[version].nupkg** という名前のファイルができます。
※[id]はNuspecファイルで設定したid、[version]はNuspecファイルで設定したversionです。

```bat:例
nuget pack NuGetTrial.nuspec
```
>'NuGetTrial.nuspec' からパッケージをビルドしています。
パッケージ 'D:\Visual Studio\NuGet\Test\NuGetTest.1.0.0.nupkg' が正常に作成されました。

![n2-5.png](https://qiita-image-store.s3.amazonaws.com/0/78881/dca2dbb2-5c8f-12e0-d345-7d93df3e7668.png)


ちなみに、作成したNuGetパッケージはZIPファイル形式であり、中身はNuspecファイルのあるフォルダーの中身すべてとパッケージを展開するためのファイルが入っています。
![n2-b0.png](https://qiita-image-store.s3.amazonaws.com/0/78881/3cebaf9f-b494-b652-092b-bb7cd65bf07f.png)


#3. 作成したNuGetパッケージでテストしてみよう
作成したNuGetパッケージはNuGetギャラリーや社内サーバーなどへ自由にアップロードできますが、その前にローカル環境でテストしてみましょう。

まず、適当なフォルダーに作成したパッケージを入れます。
![n2-b1.png](https://qiita-image-store.s3.amazonaws.com/0/78881/b348312d-7b33-4a74-c8a3-e8b3b0179486.png)


次に、Visual Studioのパッケージマネージャーの設定を開き、先ほどのフォルダーをNuGetのパッケージリソースに追加します。

>参考 : ローカルフォルダをnugetのリポジトリにする | by Temarin_PITA
http://qiita.com/Temarin_PITA/items/293780adf571b802795c

![n2-b2.png](https://qiita-image-store.s3.amazonaws.com/0/78881/d45e0eae-05c0-dde9-cbf0-b2f6a2ce8bba.png)

![n2-b3.png](https://qiita-image-store.s3.amazonaws.com/0/78881/9060bf26-1733-b05d-2d1e-a6d728d469a9.png)


C++プロジェクトを作成し、NuGetパッケージマネージャーで、パッケージリソースを先ほど追加したリポジトリを選択し、作成したパッケージをインストールします（インストールの仕方は、[C++プロジェクトでNuGetを使ってみようよ！（Vol. 1：パッケージ導入編）](http://qiita.com/nia_tn1012/items/77c3c1b006f7c69d30ac) を参照）。

![n2-b4.png](https://qiita-image-store.s3.amazonaws.com/0/78881/4600c483-bc85-8aaa-c8af-1c59f2796004.png)

![n2-b4v14.png](https://qiita-image-store.s3.amazonaws.com/0/78881/879b2aec-1866-1c1d-ece8-868823013525.png)

パッケージをインストールしたら、ライブラリが正しく動作するかテストしてみましょう。

```cpp:Test.cpp
#include <iostream>
#include "NuGetTrial.h"		// 作成したNuGetパッケージにあるヘッダーファイル

using namespace std;

int main( int argc, char* argv[] ) {

	Person nia( "Nia", 22 );
	cout << nia.ToString() << endl;

    return 0;
}
```

**◆ 実行結果**
>Name : Nia
Age : 22

#4. おわりに
今回は、C++用のNuGetパッケージを作る基本的な方法を紹介しました。ぜひ、手元にある自作のライブラリのNuGetパッケージを作ってみてはいかがでしょう。

それでは、See you next!

# C++プロジェクトでNuGetを使ってみようよシリーズ一覧
* **[Vol. 1 : パッケージ導入編](http://qiita.com/nia_tn1012/items/77c3c1b006f7c69d30ac)**
* **Vol. 2 : パッケージ作成編**