Help us understand the problem. What is going on with this article?

C++プロジェクトでNuGetを使ってみようよ!(Vol. 2:パッケージ作成編)

More than 1 year has passed since last update.

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

今回は、C++プロジェクトでNuGetを使ってみようよ!(Vol. 1:パッケージ導入編) の続編で、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 の「Installing and Updating NuGet Client」→「4. Command-Line Utility」のところにあるリンク「Direct Download」からダウンロードすることができます。

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

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

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

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

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;
};
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

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

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

  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

2.2. Nuspecファイルを作成する

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

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

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

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

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ファイルを以下のように編集します。

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

2.3. プロパティシートを作成する

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

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

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

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

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

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)ファイルを作成します。

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

プロジェクトファイル(.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

2.4. パッケージを作成する

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

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

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

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

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

nuget pack NuGetTrial.nuspec

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

n2-5.png

ちなみに、作成したNuGetパッケージはZIPファイル形式であり、中身はNuspecファイルのあるフォルダーの中身すべてとパッケージを展開するためのファイルが入っています。
n2-b0.png

3. 作成したNuGetパッケージでテストしてみよう

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

まず、適当なフォルダーに作成したパッケージを入れます。
n2-b1.png

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

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

n2-b2.png

n2-b3.png

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

n2-b4.png

n2-b4v14.png

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

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を使ってみようよシリーズ一覧

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away