VScode + gcc でDXライブラリの環境構築をしてみた話

はじめに

 前回の記事でも述べた通り今年の4月からゲームプログラマとして働くことになりました。内定先によるとゲーム業界ではC++が標準となっているらしい(パフォーマンスの向上を計りやすいから?)のですが、それに対して僕がプログラムでモノを作った経験というとUnityでテキトーにC#スクリプトを打ち込んだり、MATLABで解析のプログラムをゴリ押しで書いたりしたくらいで、内定を頂いた時点ですら「ポインタ? なにそれ美味しいの?」とか「俺は雰囲気でビルドをやっている」とかそんな感じの知識しかありませんでした。

 そこで、C++の練習用にDXライブラリを用いて簡単なゲームを作ることにしたのですが、せっかくだからビルド関連についてもちょっぴり詳しくなろうという野望を持ち始めたので、gccを用いてVScode上で開発環境を構築することにしました。

 というわけで、実際に取り組んでみたところ、ビルドに関する知識が中途半端な上に外部ライブラリを使用した上でとなると、調べ方が悪かったのか分かりやすい手順が中々得られず、環境構築まで結構時間がかかったので、整理用のメモも兼ねて自分でチュートリアルを作ることにしました。チュートリアルと言っても、プログラムについては新参者もいいところって感じですので、もし詳しい人がこの記事をご覧になった場合、補足や要修正点があればご指摘を頂けると非常にありがたいです。

 この記事を読むにあたって、ビルドについての大まかな知識(コンパイルとかリンクとかって手順があるのを知っている程度)を要求しますので、もし全く分からんって人が居たらこちらの記事に目を通してください。

proengineer.internous.co.jp

目次


準備

 まずは以下の3点をダウンロードおよびインストールしましょう。

Visual Studio Code

f:id:SzShow:20200110000727p:plain  Visual Studio Codeは今回1.41.1を使用しました。今回の記事においてVScodeがしていることと言えば外部exeファイルにコマンドを送る程度ですので、バージョンの違いとかは恐らくあまり問題にならないのかなと思います。まだ持っていない人はここからインストールしておいてください。

MinGW-w64

f:id:SzShow:20200111154131p:plain  MinGW(Minimalist GNU for Windows)についてはあまり知らなかったのでこの記事書く時に調べたのですが、これはGNUプロジェクトより開発されたツールチェーン(プログラミング用ツールの集まりみたいなもの)で、これに"-w64"が付いたのが64ビット対応版となっているっぽいです。では、gcc以外にどういったものが含まれているのかと言うと、ちょっと調べて分かったのがWindows APIと言ったWindowsの機能をプログラム上で使うためのインターフェイスに関するヘッダファイルでした。つまり、コンパイラ単独では使えない機能についてもMinGWごと入れれば一々いろんなものを扱わずに済む感じのモノです。たぶん……

 MinGW-w64のインストールはこのサイトに従って行いました。途中でちょっと怪しいサイトに飛ぶけど変なバナーとか踏まなきゃ恐らく大丈夫なはず……。それと、僕はインストールの際の設定も先程のサイトのスクリーションショットと全く同じ設定にしました。

 上のスクショのようにgcc -vと打ってコマンドプロンプト上からgccを開けることを確認すればOKです!

DXライブラリ

f:id:SzShow:20200110001651p:plain  DXライブラリは作者様のホームページより『DXライブラリ WindowsGnu C++( MinGW )用(Ver3.21b)をダウンロードする(zip圧縮形式(約97.4MB))』をクリックしてzipファイルを落とせば良いのですが、中を見ると外部ライブラリを使用した上でビルドする際に必要な『プロジェクトに追加スべきファイル_GCC(MinGW)用』の中身もバージョンで分かれていますので、コマンドラインで導入したgccのバージョンを確認(gcc -v)してそれに対応するものを選びましょう。僕の場合だと『8_1_0_i686_w64_posix_dwarf_rt_v6_rev0』を選びました。必要なファイルが選び終わったら、C:\直下などパスを選びやすいところにおいておくと良いでしょう(上の図を参照)。

 以上3点の準備が終われば準備完了です!


VScodeでの設定

 方針としては、コードを書いている時に文法の誤りを示したり、目的の関数・変数名などをサジェストしてくれるIntellisenseを設定するためにc_cpp_properties.json、ビルドの設定をするためにtask.jsonデバッグの設定をするためにdebug.jsonを構築していく方針となります。

 僕がこの辺について調べるときに以下の記事が非常に役に立ちました。この記事では後者の記事にあるjsonファイルをベースにして弄っていくスタイルを取るので、この記事で説明していない部分についてはこの2つを参考にしてください。

qiita.com

code.visualstudio.com

Intellisenseの設定

 コンパイラのインクルードフォルダにプラスして、先程Cドライブ直下に入れたdxlibのフォルダを"includePath"に追加してください。その次に、マルチバイト文字セットを適用するために"UNICODE"と"UNICODE"を消して"MBCS"および"MBCS"を"defines"に追加してください。dxライブラリはマルチバイト文字セットの使用を前提にしているため、MBCSをインクルードしないと、charからTcharへのキャストが上手く行かず(バイト数が揃わないため)にIntellisenseがエラーを送ってしまいます。

 僕の環境では最終的にc_cpp_properties.jsonは以下のようになりました。

{
    "configurations": [
        {
            "name": "Win32",
            "includePath": [
                "${workspaceFolder}/**",
                "C:/8_1_0_i686_w64_posix_dwarf_rt_v6_rev0",
                "C:/Program Files (x86)/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/include"
            ],
            "defines": [
                "_DEBUG",
                "MBCS",
                "_MBCS"
            ],
            "windowsSdkVersion": "10.0.18362.0",
            "compilerPath": "C:/Program Files (x86)/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/bin/gcc.exe",
            "cStandard": "c11",
            "cppStandard": "c++14",
            "intelliSenseMode": "gcc-x86",
            "compilerArgs": [
                "-DDX_GCC_COMPILE",
                "-DDX_NON_INLINE_ASM"
            ]
        }
    ],
    "version": 4
}

ビルドの設定

 方針としてはtask経由でg++.exeを呼び出し、コンパイルとリンクを別々にやってもらう方針となります。どうして、別々にやらなければならないかというと、DXライブラリのホームページにあるこの質問スレッドに書いてあるのですが、コンパイルとリンクを一括でやってしまうと、別のcppファイルで定義した関数・メソッドの定義の参照にエラーが出てきてしまいます(僕の環境でも確認されました)。

 なので、最初に"compile"と"link"という2つのラベルを持つタスクを作った後、この2つのタスクを順番に動かしていくためにもう1つ"build"とラベル付けされたタスクを作り、その上で上記2つのタスクが順番に実行されるようにします。そうするためにはdepends onに先ほど作った2つのタスクのラベルを"compile", "link"の順番で入れた上でdependsOrderをsequenceにすればOKです(コマンドを呼ぶ必要はなし)。

 で、"compile"と"link"ではどうすれば良いのかと言いますと、まず"compile"では以下の目的を達成するようにオプションを付けておきます。

 オプションを付ける際には以下のリファレンスが参考になりました。

www.asahi-net.or.jp

 続いて、リンカの設定では

  • コンパイルによって生成されたオブジェクトファイル(cppでのファイル名+.o)の指定
  • -mwindowsの指定(これだけは何なのか全く分かりませんでした)
  • ライブラリフォルダのパスの指定(-L + パス名)
  • DXライブラリの使い方のページより指定された.aファイルの指定(-l + ファイル名)
  • 出力ファイル名の指定(-o + ファイル名.exe)

が達成されるようにオプションを付けておきます。

 以上に従ってオプションを付けて出来上がったtasks.jsonがこちらになります。

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "dependsOn": [
                "compile",
                "link"
            ],
            "dependsOrder": "sequence"
        },
        {
            "label": "compile",
            "type": "shell",
            "command": "g++.exe",
            "args": [
                "-g",
                "-c",
                "main.cpp",
                "TextWriter.cpp",
                "TextWindow.cpp",
                "-I/Program Files (x86)/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/include",
                "-I/8_1_0_i686_w64_posix_dwarf_rt_v6_rev0",
                "-I",
                ".",
                "-DDX_GCC_COMPILE",
                "-DDX_NON_INLINE_ASM",
         ],
            "problemMatcher": [
                "$gcc"
            ]
        },
        {
            "label": "link",
            "type": "shell",
            "command": "g++",
            "args": [
                "main.o",
                "TextWriter.o",
                "TextWindow.o",
                "-mwindows",
                "-L/Program Files (x86)/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/lib",
                "-L/8_1_0_i686_w64_posix_dwarf_rt_v6_rev0",
                "-lDxLib",
                "-lDxUseCLib",
                "-lDxDrawFunc",
                "-ljpeg",
                "-lpng",
                "-lzlib",
                "-ltiff",
                "-ltheora_static",
                "-lvorbis_static",
                "-lvorbisfile_static",
                "-logg_static",
                "-lbulletdynamics",
                "-lbulletcollision",
                "-lbulletmath",
                "-lopusfile",
                "-lopus",
                "-lsilk_common",
                "-lcelt",
                "-o",
                "test.exe"
            ],
            "problemMatcher": [
                "$gcc"
            ]
        }
    ]
}

デバッグの設定

 ビルドによって生成されたexeを開いていきます。これについては上記のvscodeチュートリアルほぼそのままの設定で大丈夫です。デバッグ用の実行ファイル"gdb.exe"のパスをmiDebuggerPathに通し、ビルドで生成した実行ファイルのフルパスを"program"に記入してください。

 僕の場合、ビルドとデバッグを一括でやりたかったので、その場合は"preLaunchTask"で先程作った"build"ラベルを指定しておきましょう。

 すると、launch.jsonは以下のようになります。

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(gdb) 起動",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/test.exe",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": true,
            "MIMode": "gdb",
            "miDebuggerPath": "C:/Program Files (x86)/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/bin/gdb.exe",
            "setupCommands": [
                {
                    "description": "gdb の再フォーマットを有効にする",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "build"
        }
    ]
}

結果

f:id:SzShow:20200111163630p:plain

 早速動かしてみましょう。まずは上の図のようにして先程作ったlaunchを動かしていきます(文字が汚いのは気にしないでね)。

f:id:SzShow:20200111163713p:plain

 すると、最初に画面下のような文字が並ぶと思いますが、これらはまさにtask.jsonで打ったg++.exeへのコマンドおよびそのオプションです。これらが無事に通ると、今度はlaunch.jsonで指定したexeファイル(ビルドで生成されたexe)が動きます(なんか出ているのは色々と試している途中のものなので、これも気にしないでね)。

f:id:SzShow:20200111164113p:plain

 更にブレークポイントを置いてやると、ちゃんと動作が止まり、スコープ内変数およびその中身も見れました。

最後に

 以上、VScode + gcc上でDXライブラリを用いた環境構築をするまでの話でした。最後まで読んでくださった方はありがとうございます。この記事が一人でも僕と同じところで躓いている人の助けになれれば嬉しいです。