VS CodeとWSLでopensource COBOLの開発とデバッグをした

# 概要
[前回の記事](https://qiita.com/nor51010/items/f11beed7a9a24bf3035e)でWSL環境上にopensource COBOL環境を作りました。
今回は、前回環境をベースにVisual Studio Code(以下VSCode)でopensource COBOLの開発とデバッグができるまでをやっていこうと思います。

## 環境
Windows 10
Visual Studio Code version 1.36.1

# VSCodeの導入

まずはインストールしますが、ダウンロードして実行するだけです。

インストール後、C/C++とCOBOLを扱うためプラグインを導入していきます。

- C/C++

拡張機能をC/C++で検索するとMS提供の開発環境があるので、こちらをいれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/b432bee7-9d38-c4a6-bcc6-b3b9010982a8.png)

- COBOL

COBOLのシンタックスに対応してくれる非常に有用なプラグインが公開されています。
COBOLで調べるとトップに出てきますので、こちらも導入します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/9185c3ce-b2a5-9b67-6a22-4de4dfa09a75.png)

# 開発環境セットアップ

VSCodeは非常にアップデートが早く、公式ドキュメントでも一部違いが吸収されていない部分があります。
しかし、ドキュメント自体はしっかりありますので、基本的には[公式ドキュメント](https://code.visualstudio.com/docs/cpp/config-wsl)を確認すれば問題なくできるかと思います。

ここではopensource COBOL向けにドキュメントを読み替えて構築してきます。

### Linux環境を構築

すでにWSL環境はできているとおもいますので、割愛します。
Linux環境上で、プロジェクトのワークスペースとするフォルダを作成していきます。

ubuntuシェルを立ち上げます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/ecb44619-0f1e-f14c-6127-ffbb3be9a86b.png)

フォルダを作ります。ここでは公式に則り`helloworld`プロジェクトとしてます。

```
mkdir projects
cd projects
mkdir helloworld
```

結果はこんな感じになります。

```
nor51010@MSPC182:~/projects/helloworld$ pwd
/home/nor51010/projects/helloworld
```

ついでにデバッグ用のGDBをインストールしていきます。
`build-essential`はopensource COBOLのインストール時に入れていますので、無視します。

```
sudo apt-get install gdb
```

ubuntuでは`wgereis`コマンドで導入できているか確認できます。

```
nor51010@MSPC182:~/projects/helloworld$ whereis gdb
gdb: /usr/bin/gdb /etc/gdb /usr/share/gdb /usr/share/man/man1/gdb.1.gz
```

### Windows上にワークスペースを作成する

WSLなので、LinuxもWindows上ではありますが、こちらでは普通にワークスペースを作っていきます。
ここも公式に則り、コマンドプロンプトで作成していきます。
コマンドプロンプトで以下コマンドをがしがし打っていきます。

```
mkdir projects
cd projects
mkdir helloworld
cd helloworld
code .
```

最後の`code .`でカレントディレクトリでVSCodeが起動します。

### コンパイラの設定

ここから、各種設定をしていきます。VSCodeでは、各種設定をJSONファイルに記載することで行います。

まず初めに、コンパイラ環境について設定していきます。

`ctrl + shift + p`でコマンドパレット（というらしいコマンド入力欄）を開いて、「C/C++: Editconfigurations (JSON)」を実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/dbea4163-a199-4ed6-a29f-6fb34d46eb44.png)

自動で`.vscode/c_cpp_properties.json`が作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/8217034c-f70b-bc6d-cd19-7fe3d486d09a.png)

ここで設定を変更し、以下のようにWSLのLinux環境に合わせていきます。
各設定の詳細については、公式ドキュメントを参照してください。

```json:c_cpp_properties.json
{
    "configurations": [
        {
            "name": "Win32",
            "defines": [
                "_DEBUG",
                "UNICODE",
                "_UNICODE"
            ],
            "compilerPath": "/usr/bin/g++",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "gcc-x64",
            "browse": {
                "path": [
                    "${workspaceFolder}"
                ],
                "limitSymbolsToIncludedHeaders": true,
                "databaseFilename": ""
            }
        }
    ],
    "version": 4
}
```

### ビルドタスクの作成

次はビルドタスクを作成していきます。俗にいうコンパイルシェルに近いものになります。
公式ではC/C++用になっているので、ここでCOBOL用のビルドパスも用意してしまいます。

再びコマンドパレットを開いて、次の画像のように選択していきます。
※公式ドキュメントとはコマンド名が異なっていました。

`Tasks: Configure Default Build Task`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/df71c7ab-22fd-4f49-d2aa-aa39aa11bee7.png)

`Create tasks,json file from template`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/be6714d3-b8e5-0396-8beb-8c1ac169d55f.png)

`Others`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/433da65c-6360-30da-c348-6f048e150653.png)

`.vscode/tasks.json`ができるので、設定は以下のようにしていきます。
今回はプログラムファイル名は固定で指定しています。
`"isDefault": true`としておくことで、ビルド実行時に自動で実行されるため、COBOL側を有効にしておきます。

```json:tasks.json
{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "windows": {
        "options": {
            "shell": {
                "executable": "c:\\Windows\\System32\\bash.exe",
                "args": ["-c"]
            }
        }
    },
    "tasks": [
        {
            "label": "build hello world on WSL",
            "type": "shell",
            "command": "g++",
            "args": [
                "-g",
                "-o",
                "/home/nor51010/projects/helloworld/helloworld.out",
                "helloworld.cpp"
            ]
        },
        {
            "label": "build cobol hello world on WSL",
            "type": "shell",
            "command": "cobc",
            "args": [
                "-g",
                "--save-temp=/home/nor51010/projects/helloworld/",
                "-o",
                "/home/nor51010/projects/helloworld/HELLO.so",
                "HELLO.cbl"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        }
    ]
}
```

C/C++用の`build hello world on WSL`と、COBOL用に`build cobol hello world on WSL`を追加しています。
違いとしては、コンパイルに使うコマンドを`cobc`に変更したこと、デバッグで追いかけるソースコードを表示するため、`--save-temp`オプションを有効にしています。
パスで指定している`/home/nor51010`の部分は各自のユーザー名設定に読み替えてください。

### デバッグ用の設定

最後にデバッガを起動する設定を行っていきます。
ここはメニューから**Debug > Add Configuration...**を選択します。
選択肢がでてくるので`C++ (GDB/LLDB). `を選択すると、`.vscode/launch.json`が作成されます。
今回も同様にC/C++とCOBOL用の設定を行います。

設定ファイルは以下のようにします。

```json: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) Launch",
            "type": "cppdbg",
            "request": "launch",
            "program": "/home/nor51010/projects/helloworld/helloworld.out",
            "args": [""],
            "stopAtEntry": true,
            "cwd": "/home/nor51010/projects/helloworld/",
            "environment": [],
            "externalConsole": true,
            "MIMode": "gdb",
            "windows": {
                "MIMode": "gdb",
                "miDebuggerPath": "/usr/bin/gdb",
                "setupCommands": [
                    {
                        "description": "Enable pretty-printing for gdb",
                        "text": "-enable-pretty-printing",
                        "ignoreFailures": true
                    }
                ]
            },
            "pipeTransport": {
                "pipeCwd": "",
                "pipeProgram": "c:\\windows\\sysnative\\bash.exe",
                "pipeArgs": ["-c"],
                "debuggerPath": "/usr/bin/gdb"
            },
            "sourceFileMap": {
                "/mnt/c": "${env:systemdrive}/",
                "/usr": "C:\\Users\\nor51010\\AppData\\Local\\Packages\\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\\LocalState\\rootfs\\usr\\"
            }
        },
        {
            "name": "(gdb) cobol Launch",
            "type": "cppdbg",
            "request": "launch",
            "program": "/usr/local/bin/cobcrun",
            "args": ["HELLO"],
            "stopAtEntry": true,
            "cwd": "/home/nor51010/projects/helloworld/",
            "environment": [],
            "externalConsole": true,
            "MIMode": "gdb",
            "windows": {
                "MIMode": "gdb",
                "miDebuggerPath": "/usr/bin/gdb",
                "setupCommands": [
                    {
                        "description": "Enable pretty-printing for gdb",
                        "text": "-enable-pretty-printing",
                        "ignoreFailures": true
                    },
                    {
                        "description": "args",
                        "text": "--args",
                        "ignoreFailures": true
                    }
                ]
            },
            "pipeTransport": {
                "pipeCwd": "",
                "pipeProgram": "c:\\windows\\sysnative\\bash.exe",
                "pipeArgs": ["-c"],
                "debuggerPath": "/usr/bin/gdb"
            },
            "sourceFileMap": {
                "/mnt/c": "${env:systemdrive}/",
                "/usr": "C:\\Users\\nor510101\\AppData\\Local\\Packages\\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\\LocalState\\rootfs\\usr\\"
            }
        }
    ]
}
```

`"sourceFileMap"."/usr"`では、WSL環境のフォルダをWindows上から見えるパスで設定します。このパスの取得方法については後ほど。

COBOL側の設定では、`"program"`に`cobcrun`を、`"args"`にアプリケーション名を指定しています。
ビルドタスクにて、`-x`を指定してビルドしている場合は必要ありませんが、SOファイルを作成して、`cobcrun`から起動する場合は上記のようにする必要があります。

ここまでで環境構築は完了です。

# C++の開発

### C++コードの追加

まずはC++のコードを作ってみます。メニューの**File > New File**から`helloworld.cpp`を作成します。このとき、間違えて`.vscode`の下に作るのではなく、ワークスペース直下に作るようにします。

ファイルの内容は公式通りにします。

```c++:helloworld.cpp
 #include <iostream>
 #include <vector>
 #include <string>

 using namespace std;

 int main()
 {

     vector<string> msg {"Hello", "C++", "World", "from", "VS Code!", "and the C++ extension!"};

     for (const string& word : msg)
     {
         cout << word << " ";
     }
     cout << endl;
 }
```

### /usrの取得

先ほど作ったプログラムコードで、`vector<string> msg`をマウスでドラッグして選択したあと、F12キーを押して定義へと移動します。（または右クリックからの**Go to Definition**）
すると、**OPEN EDITORS**に定義されている箇所のヘッダーファイル等が表示されます。
これを右クリックし、**Copy Path**を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/edcc05da-24b8-11dd-578d-529ee73994d0.png)

結果として以下のようなパスがとれます。

```
C:\Users\nor510101\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\rootfs\usr\include\c++\7\bits\stl_vector.h
```

ここの`...\usr\`までが必要な部分です。JSONに転記する際には、`\`をエスケープするのを忘れないようにしましょう。

### C++コードのコンパイル

設定したビルドタスクを呼び出します。今回はデフォルトタスクではないため、以下の順番で呼び出します。
コマンドパレットを呼び出します。

`Tasks: Run Task`を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/5a9134c1-431a-62fa-bcf2-8bb446a47c10.png)

`build hello world on WSL`を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/42d62c64-710b-b456-157e-4f4f06de6347.png)

実行するとTERMINALが表示されて結果が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/5e9d7434-844c-4347-ed67-30d4ce9b5fc0.png)

正常に終わっていると、Linux側のコンソールからもファイルが確認できます。

```
nor51010@MSPC182:~/projects/helloworld$ ls
helloworld.out
```

### C++コードのデバッグ

それでは実際にデバッグしていきます。
左側から虫マークへ移動し、起動するデバッグの種類を上部で選択、実行します。
今回はC/C++のため、`(gdb) Launch`を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/e4de7d35-a886-64b2-abd1-ef3728ebbae8.png)

緑の右三角を押すとデバッグが開始されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/541b7e23-1db4-bb14-2ab4-2fddd3adcdb6.png)

あとはブレークポイントを仕掛けたり、値を確認してみたり、ステップ実行したり、いつも通りのデバッグをおこなうことができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/e2b61176-115d-e7d4-5d24-be3aff3b077d.png)

# COBOLの開発

前置き（？）が非常に長くなりましたが、本番のopensource COBOLの開発に移ります。

### COBOLコードの追加

C/C++の時と同様に、メニューの**File > New File**から`HELLO.cbl`を追加していきます。

```cobol:HELLO.cbl
       IDENTIFICATION              DIVISION.
       PROGRAM-ID.                 HELLO.
       DATA                        DIVISION.
       WORKING-STORAGE             SECTION.
       PROCEDURE                   DIVISION.
       MAIN-RTN.
           DISPLAY "HELLO WORLD!".
       MAIN-EXT.
           STOP RUN.

```

プラグインのおかげで、ちゃんとハイライト等がされてたり、OUTLINEが表示されたりいい感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/bc91c857-a48f-483f-0894-097219f26701.png)

### COBOLコードのコンパイル

今回はデフォルトタスクとして追加していたため、パレットから`Tasks: Run Build Task`を選択するだけでビルドが実行されます。(または`ctrl + shift + B`)

問題なくビルドが実行され
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/c524dd61-a51f-d085-fe04-3c4cb9309a5e.png)

Linux上にもファイルができてます。

```
nor51010@MSPC182:~/projects/helloworld$ ls
HELLO.so  helloworld.out
```

また、VSCode上で、`HELLO.c`などの一時ファイルが残っていることが確認できます。（詳しくは次で）

### COBOLコードのデバッグ

お待ちかねのCOBOLコードのデバッグではありますが、残念ながら、opensource COBOLを直接GDBでデバッグすることはできません。
そのため、先ほど生成したCソースである`HELLO.c`をデバッグの対象とします。

HELLO.cを開いて入口あたりでブレークポイントを追加しておきます。
Cのコメントを参考に、DISPLAYのとこでブレークポイントを仕掛けてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/9648680f-73b0-09dd-6567-5955e0ca0f8f.png)

この状態でデバッグを開始していきます。C/C++のときと同様に、虫マークから、今度は`(gdb) cobol Launch`を選択し、実行していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/2574b394-23fb-70d3-c015-1e47b744852b.png)

起動時に一時停止しますが、`cobcrun`がないので、さっさと先にすすめます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/5b36ca77-ad0b-5f57-2042-34290b6977fd.png)

すると、HELLO.cの指定したポイントでとまっていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439579/bf0c7a9f-7883-07f4-3f7e-7f01e839de76.png)

このようにして、COBOLプログラム（からできたC）のデバッグができるようになりました。

# 次回予告

とりあえず今回は環境構築までは行えたので、次回はCOBOLプログラムをGDBでデバッグする際、自分がよく行うことをまとめていこうと思います。
具体的には、

- COBOL変数の参照
- 呼び出し階層の確認
- etc..

こうご期待