GYP (Generate Your Projects) の入力フォーマットと基本的な使い方

GYP (Generate Your Projects) の基本的な使い方について紹介します。 GYP の導入からプロジェクトを生成してビルドするまでの大まかな流れについて知りたい方は前回の記事(ビルドオートメーションツールGYPを使おう)をご参照ください。

基本的な入力フォーマット

プロジェクトの生成に必要な入力ファイルは、編集しやすいプレーンテキストです。拡張子は .gyp を使用します。 .gyp ファイルの構文規則は Python のデータ構造 (Data Structures) を採用しています1。Python を使ったことがなくても、JSON に似ているので馴染みのある方ならきっとすぐに慣れるでしょう。 .gyp ファイルでは次の項目が使用できます:

  • 角括弧 ([]) とカンマ (,) で要素を区切ったリスト (List)
  • キー (Key) と値 (Value) のペアであるディクショナリ (Dictionary, 辞書または連想配列)
  • 文字列
  • 数値(整数と小数)
{
  'key': 'string-value',
  'list': [
    'element1',
    'element2',
    'element3',
  ],
  'numbers': [
    1,
    '2',
    '+3',
    '-4',
    '5.0',
  ],
}

JSON における文字列はダブルクオーテーションで括りますが、GYP ではシングルクオーテーションで括られた文字列も有効です。慣例的にはシングルクオーテーションがよく使われます。

{
  'key':'string-value',
}

数値は文字列と同じくシングルクオーテーションで括って指定します。符号なし整数はシングルクオーテーションを付けずに指定することができます。

{
  'numbers': [
    1,
    '2',
    '+3',
    '-4',
    '5.0',
  ],
}

また、GYP の入力フォーマットでは次のような末尾のカンマが許可されています:

[
  'The',
  'Marshmallow',
  'Times',
]

Note: JSON では末尾のカンマは許可されていませんが、GYP の入力フォーマットとして採用されている Python Data Structures では許可されています。 末尾のカンマが許可されていることで、リストの末尾に要素を追加したり、要素の順番を並べ替えることが容易にできます。 末尾のカンマの取り外しを気にする必要がなく、とても重宝します。

コメントの書き方

.gyp ファイルではコメントを書くことができます。シャープ記号(#)から行末まで有効です。

{
  # comment out
}

ディクショナリのキーワード一覧

.gyp ファイルではディクショナリのキーとして多くのキーワード(予約語)が出てきます。以下にその例を挙げます。

  • 'conditions'
  • 'variables'
  • 'includes'
  • 'targets'
  • 'targets_default'
  • 'configurations'
  • 'default_configuration'

次のコードはディクショナリを用いた GYP の例です:

{
  'conditions': [
    ['OS == "win"', {
      # windows settings
    }],
  ],
 'variables': [
    {"variable_name%": "variable-value"},
  ],
  'includes': [
    'common.gypi',
  ],
  'targets_default': {
    'default_configuration': 'Release',
    'configurations': {
      'Debug': {
      },
      'Release': {
      },
    }
  },
  'targets': {
  },
}

ディクショナリの各セクションについて紹介します。

条件分岐 (conditions)

GYP では条件分岐の制御を行えます。'conditions' セクションに条件分岐を書きます。

{
  'conditions': [
    ['OS == "win"', {
      # windows settings
    }], # OS == "win"
    ['OS == "mac"', {
      # osx settings
    }], # OS == "mac"
  ],
}

条件式には andor といった Python の論理演算子を使用することができます。

{
  'conditions': [
    ['OS == "mac" and target_arch == "x64"', {
      # mac osx (64-bit)
    }],
    ['OS == "linux" or OS == "android"', {
      # linux or android
    }],
  ],
}

変数 (variables)

GYP には大まかに 3 つの変数があります。1 つ目は GYP によって事前に定義されている定義済み変数です。定義済み変数は大文字のスネークケース (例 CAPITAL_LETTERS) の命名規則が適用されています。もっともよく使う定義済み変数に OS が挙げられます。 2 つ目に、ユーザ定義変数があります。これは GYP を利用するユーザが定義する変数です。慣例的には、小文字のスネークケース (例 lowercase_letters) の命名規則が使われています。 3 つ目は 自動変数 です。これは、任意のディクショナリ内の、文字列を値に持つキーを参照できる変数です。キーと同じ名前にアンダースコア (_) の接頭辞がついた変数名になります。 それぞれの変数について見ていきましょう。

定義済み変数

OS のようにすでに定義された変数を GYP の各ジェネレータモジュールが提供しています。 OSlinux, mac, win のように各プラットフォーム OS を示します。もちろん、これらは完全ではありません。 GYP のジェネレータが列挙するオペレーティングシステム名は linux, mac, win などに限ります。 そこで、GYP ユーザは定義済み変数を任意の値で置き換えることができます。 次のように、GYP のコマンドラインで -DOS=os-nameオプションを使います:

gyp example.gyp --depth=. -f gypd -DOS=monaos

gypd

上記の例で使用している -f gypd オプションについて紹介しておきましょう。 gypd は、GYP のデバッグ出力を行うジェネレータモジュールです。 .gyp ファイルと指定されたオプションを入力として GYP で処理し、その結果を.gypdファイルに出力します。 .gypd ファイルは .gyp ファイルと同じフォーマットのプレーンテキストです。そのまま GYP の入力として使用することができます。

Note: gypd モジュールを使用した場合、定義済み変数である OS が未定義となります。 そのため、-DOS=monaos のようにユーザが任意の値を指定する必要があります。 -DOS=mac-DOS=win を指定することも可能です。

DEPTH

例の中で出てきたもう1つのオプション --depthDEPTH 値を設定するオプションです。 DEPTH.gyp ファイルから任意のディレクトリまでの相対パスを示します。 後ほど DEPTH について紹介します。

ユーザ定義変数

変数を定義するときは 'variables' セクションで行います。

{
  'variables': {
    'variable_name': 'string-value',
    'number1': 1,
    'number2': '2',
    'number3': '3.0',
    'string_list': [
      'string-element1',
      'string-element2',
    ],
  },
}

変数にデフォルト値を設定する場合は、変数名にパーセント記号 (%) の接尾辞をつけます。

{
  'variables': {
    'renderer%': 'direct3d',
  },
}

デフォルト値が設定された変数は、gyp コマンドを実行する際に任意で -D オプションをつけることで、値を上書きすることができます。変数 renderer の値を 'opengl' に上書きする場合は、次のように -D renderer=opengl をコマンドライン引数に渡します。

gyp example.gyp --depth=. -f gypd -D renderer=opengl

-D オプションで指定しなかった場合、renderer にはデフォルト値の direct3d が 割り当てられます。 また、コマンドラインから変数の値を受け取ることもできます。変数の値に '<(identifier)' を指定します。変数名に接尾辞としてパーセント記号 (%) をつけることに注意してください。

{
  'variables': {
    'renderer%': '<(renderer)',
  },
}

変数を上書きするときと同じく -D オプションで値を指定します。

gyp example.gyp --depth=. -f gypd -D renderer=direct3d

Note: コマンドラインからの入力を示す '<(identifier)' を変数の定義に用いた場合、 gyp コマンドを実行する際に必ず -D オプションを付けて変数値を指定する必要があります。

ここまでの説明をふまえて、レンダリングエンジンのビルドを例にユーザ定義変数の使い方をまとめてみます。 これからビルドするレンダリングエンジンは、グラフィックス API に OpenGL を採用しています。 例外的に Windows プラットフォームのみグラフィックス API として Direct3D も利用できます。 Windows 向けのビルドに限り、デフォルトのレンダラを Direct3D とし、任意でレンダラを OpenGL か Direct3D のどちらか選択できるようにしましょう。 .gyp ファイルは次のようになります。

{
  'variables': {
    'conditions': [
      ['OS != "win"', {
        'renderer': 'opengl',
      }],
      ['OS == "win"', {
        'renderer%': 'direct3d',
      }],
    ],
  },
  'targets': [
    {
      'target_name': 'my_rendering_engine',
      'type': 'shared_library',
      'conditions': [
        ['renderer == "direct3d"', {
          'sources': [
            'src/Direct3DRenderer.cpp',
          ],
        }],
        ['renderer == "opengl"', {
          'sources': [
             'src/OpenGLRenderer.cpp',
          ],
        }],
      ],
    },
  ], # targets
}

この設定ファイルでは次の gyp コマンドとオプションが有効です。

gyp example.gyp --depth=. -f gypd -DOS=mac
gyp example.gyp --depth=. -f gypd -DOS=linux
gyp example.gyp --depth=. -f gypd -DOS=win
gyp example.gyp --depth=. -f gypd -DOS=win -Drenderer=opengl
gyp example.gyp --depth=. -f gypd -DOS=win -Drenderer=direct3d

自動変数

後ほど出てくる targets セクションを例に紹介します。'type': 'executable' のようにディクショナリの中で、文字列を値に持つキーがあります。 'type' の値を自動変数 _type として参照することが出来ます。キーと同じ名前にアンダースコア (_) の接頭辞を付けることに気をつけてください。

{
  'targets': [
    {
      'type': 'executable',
      'conditions': [
        ['_type == "executable"', {
          # _type == "executable"
        }],
        ['_type == "static_library"', {
          # _type == "static_library"
        }],
      ],
    },
  ], # targets
}

自動変数が有効に働くのは同じスコープ内になります。

数学定数を設定する

ここまでで記事の半分まで到達しました。少し長くなりましたので、ちょっとだけ休憩ということで寄り道しましょう。GYP は Python で作られたビルドツールです。変数を定義する際に Python のコードを実行することができます。試しに円周率 pi やネイピア数 e を定義してみましょう:

{
  'variables': {
    'pi': 'import math; print math.pi',
    'e': 'import math; print math.e',
  },
  'conditions': [
    ['pi > e', {
      # OK
    }],
  ],
}

(こんなこともできるなんてすごいぞ GYP !!)

共通の設定ファイルを読み込む (includes)

複数のソフトウェア、モジュール、ライブラリが混在するプロジェクトでは複数の GYP ファイルを扱うことがほとんどです。2 つ以上の GYP ファイルの中で、複数箇所に同じ設定内容を記述することがあります。そのような場合、コピーする手間を省くために includes を使用します。 includes を使うことで、共通の設定をまとめ、必要なビルドーターゲットごとに設定を取り込むことができます。共通の設定を記述した common.gypi を読み込む例です:

{
  'includes': ['common.gypi']
}

例えば、common.gypi を次のように記述しておくと、includes で取り込んだすべてのプロジェクト(ビルドーターゲット)に対して、Visual Studio C++ コンパイラの警告レベルを 4 (/W4) に一括して設定することができます。

# common.gypi
{
  'target_defaults': {
    'msvs_settings': {
      'VCCLCompilerTool': {
        'WarningLevel': '4', # /W4
      },
    }
  }, # target_defaults
}

こうした includes によって取り込まれる設定ファイルには、慣例的に .gypi (gyp include) 拡張子が使われています。中身は通常の .gyp ファイルと変わりません。また、例で取り上げた「共通の設定を記述した .gypi ファイル」には 'common.gypi' の名前がよく使われています。

ビルドターゲット・ソースファイルの指定 (targets)

ビルドターゲットを設定します。ビルドの対象となるソースコード、インクルードディレクトリのパス、依存するプロジェクト、出力形式(実行ファイルやスタティックライブラリ、ダイナミックリンクライブラリなど)を指定することが可能です。

{
  'targets': [
    {
      'target_name': 'my_flight_game',
      'type': 'executable',
      'include_dirs': [
        '../include',
      ],
      'sources': [
        '../src/Game.cpp',
        '../src/Entity.cpp',
        '../src/Context.cpp',
      ],
    },
  ], # targets
}

'target_name'

ビルドターゲットの名前です。GYP 内でプロジェクトを指定するときに用いることがあります。

'product_name'

ビルドによって出力されるファイルの名前を指定します。これは出力された実行可能形式やライブラリのファイル名に用いられます。 スタティックライブラリ (.a, .lib) や 共有ライブラリ (.dylib, .dll)の場合、lib のプリフィクスが付きます。 例を示します。

出力形式 出力ファイル名 (例)
executable (Win32) product-name.exe
executable (Cocoa) product-name.app
static_libarary(Win32) libproduct-name.lib
shared_libarary(Win32) libproduct-name.dll
shared_libarary(Linux) libproduct-name.so
static_libarary(Linux, OS X) libproduct-name.a
shared_libarary(OS X) libproduct-name.dylib
framework (shared_library + mac_bundle) product-name.framework

'type'

ビルドの出力形式です。以下に指定できる形式を列挙します。

  • executable
  • shared_library
  • static_library
  • none2

Note: OS X 用の .framework ファイルや Cocoa アプリケーション (.app) をビルドするためには mac_bundle の値を指定する必要があります。

'include_dirs'

インクルードディレクトリのパスを指定します。

'sources'

ビルドするソースコードを指定します。通常はビルドに必要なインプリメントファイル(.cpp, .cc など)のみでかまいません。 生成されるプロジェクトファイルにヘッダファイルを含めたい場合は、ヘッダファイルも追加するといいでしょう。

{
  'targets': [
    {
      'sources': [
        '../include/trivial/Game.hpp',
        '../include/trivial/Entity.hpp',
        '../include/trivial/Context.hpp',
        '../src/Game.cpp',
        '../src/Entity.cpp',
        '../src/Context.cpp',
      ],
    },
  ], # targets
}

'defines'

プリプロセッサの定義です。

{
  defines': [
    'DEBUG',
    '_WIN32_WINNT=0x0600',
  ]
}

'dependencies'

依存しているプロジェクトを指定します。たとえば、ライブラリとアプリケーションの 2 つのビルドターゲットが存在するときに、アプリケーション側でライブラリを必要とするときは次のように 'dependencies' にターゲット名を指定します。

{
  'targets': [
    {
      'target_name': 'trivial_library',
      'type': 'static_library',
    },
    {
      'target_name': 'trivial_application',
      'type': executable,
      'dependencies': [
        'trivial_library',
      ],
    },
  ], # targets
}

他の .gyp ファイルに記述されたビルドターゲットを指定することもできます:

{
  'dependencies': [
    'trivial.gyp:*',
  ],
}

明示的に任意のビルドターゲットのみを指定する場合は次のように記述します:

{
  'dependencies': [
    'trivial.gyp:trivial_library',
  ],
}

複数ターゲットの指定

Windows デスクトップ向けに開発している人にわかりやすく説明すると、ビルドターゲット 1 つにつき、Visual Studio のプロジェクトファイル(.vcxproj) が対応付けられます。また 'targets' とあるように複数のターゲットを列挙できます。

{
  'targets': [
    {
      'target_name': 'trivial_library',
      'type': 'static_library',
      # ...
    },
    {
      'target_name': 'trivial_engine',
      'type': 'shared_library',
      'dependencies': ['trivial_library']
      # ...
    },
    {
      'target_name': 'trivial_application',
      'type': 'executable',
      'dependencies': ['trivial_engine']
      # ...
    },
  ], # targets
}

これは、1 つのアプリケーションで複数のプロジェクトファイルを使用する場合を想定しています。例えば、内製フレームワークのスタティックライブラリや、エンジンやプラグインの機能を提供するダイナミックリンクライブラリ (DLL) など、いくつものビルドターゲットが存在する場合に有用です。

ビルドターゲット間で共通の設定を記述する (target_defaults)

次のように 'targets' 内で重複した記述がある場合を考えてみましょう。

{
  'targets': [
    {
      'target_name': 'trivial_library',
      'xcode_settings': {
        'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
        'CLANG_CXX_LANGUAGE_STANDARD': 'c++0x',
      },
    },
    {
      'target_name': 'trivial_engine',
      'xcode_settings': {
        'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
        'CLANG_CXX_LANGUAGE_STANDARD': 'c++0x',
      },
    },
    {
      'target_name': 'trivial_application',
      'xcode_settings': {
        'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
        'CLANG_CXX_LANGUAGE_STANDARD': 'c++0x',
      },
    },
  }, # targets
}

'targets' で列挙しているターゲット間において、共通の設定を行っている場合は 'target_defaults' を使うと便利です。

{
  'target_defaults': {
    'xcode_settings': {
      'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
      'CLANG_CXX_LANGUAGE_STANDARD': 'c++0x',
    },
  }, # target_defaults
  'targets': {
    {'target_name': 'trivial_library'},
    {'target_name': 'trivial_engine'},
    {'target_name': 'trivial_application'},
  ], # targets
}

DEPTH

DEPTH.gyp ファイルから任意のディレクトリまでの相対パスを示します。 .gyp ファイルを処理する時に、この DEPTH の値を使って依存するファイルのパスを求めます。 例を示します:

project-root/
 |
 |-- build/
 |    |-- common.gypi
 |    |-- MyFramework.gyp
 |
 |-- test
      |-- TestApp/
      |    |-- TestApp.gyp
      |
      |-- Sample/
      |-- UnitTest/
# build/MyFramework.gyp
{
  'targets': [
    {
      'target_name': 'MyFrameworkRuntime', # (1)
      'type': 'static_library',
      # ...
    }
  ],
}
# test/TestApp/TestApp.gyp
{
  'targets': [
    {
      'target_name': 'TestApp',
      'type': 'executable',
      'dependencies': [
        '<(DEPTH)/MyFramework.gyp:MyFrameworkRuntime', # (2)
      ],
      # ...
    }
  ],
}

.gyp ファイルの中でDEPTH の値を取得するには <(DEPTH) と記述します。 上記の例では (1) のビルドターゲットを (2) のようにして依存するターゲットとして指定しています。

DEPTH--depthオプションを使ってコマンドラインから設定することができます。 例えば、project-root ディレクトリから次のコマンドを実行することができます:

cd project-root
gyp test/TestApp/TestApp.gyp --depth=./build -f gypd -DOS=monaos

最後に

今回は入力フォーマットと基本的な使い方に焦点をあて紹介しました。 すべての機能を紹介することはできませんでしたが、GYP にはまだまだ頻繁に使用する機能があります。 例えば、Visual Studio 特有のオプションや Xcode 向けの設定といった各開発プラットフォーム向けの設定を記述できる機能が提供されています。 以下に該当するキーワードを挙げます。

  • 'msvs_settings'
  • 'xcode_settings'
  • 'make_global_settings'

GYP は Chromium プロジェクトのために開発されたツールとはいえ、多くのケース、多くのプロジェクトに対応できる機能を備えています。 紹介できなかった「ビルド設定の継承」や「'type': noneの使用どころ」、「OS X 向けアプリケーションのバンドル (mac_bundle) 」などその他の機能については、次回あらためて紹介します。お楽しみに!!

追記
GYP の具体的な使い方に関して、次の記事を書きました。ご参考になりましたら幸いです。

参考文献


  1. https://code.google.com/p/gyp/wiki/GypLanguageSpecification#Overview に "The .gyp file syntax is a Python data structure." とある。 

  2. none については日を改めて紹介します。 

Leave a Reply