Windows10にVSCodeでLinuxのC++開発環境を構築

# はじめに
お仕事でRasberry Pi用のインターフェイスプログラムの作成がありました。もともとWindows用にDelphi5からC#（.NET 4.x）で移植したものがあり、Linux用でも動作するためC#（.NET 5.0） に修正してRasberry Piでも動作することが確認できました。
Bashからインターフェイスプログラムを呼び出すことが出来たのですが、C++でインターフェイスプログラムを呼び出すにはどうすればいいのか forkとexeclを使えば出来そうと分かったが、それにはLinux用開発環境を構築する必要がありました。

下記サイトを参考にVSCodeとWSLを開発環境を構築しました。

https://novnote.com/vscode-wsl-cpp-env/511/

# 環境構築
* Windows 10 Pro
* WSL(Ubuntu 18.04 LTS)
* Visual Studio Code 1.56.2

※WSL2(Ubuntu 20.04 LTS)でも方法は同じになります。

## WSLの追加作業
WSL(Ubuntu 18.04 LTS)にて、下記アプリケーションをインストールします。

* gcc、g++、makeなどのC/C++ソースコードをビルドするのに必要な実行ファイルのインストール
* デバッガgdbのインストール
* VSCodeのインストール

```bash 
sudo apt update;
sudo apt install build-essential -y;
sudo apt install gdb -y;
sudo apt install code;
```

build-essentialをインストールしたはずなのに「/usr/bin/g++: No such file or directory」のエラーになっていました。原因は「sudo apt update」をしていなかったためです。
教訓として、先に「sudo apt update」はしておきましょう。

## VSCodeに拡張機能を追加
Windows 10側のVSCodeに下記の拡張機能を追加します。

* Remote WSL
* C/C++
* C/C++ IntelliSense

# ビルドとデバッグの設定
サンプルとして、Hello Worldではつまらないのでフィボナッチ数列を求めるプログラムを作成します。

## プロジェクトフォルダ用意
Windows上に下記のフォルダを作成します。

```
C:\WorkSpace\LinuxFibonacci
```

## Remote WSL
VSCodeの左パネルからリモートエクスプローラーをクリックします。
WSL_TARGETSのUbuntu-18.04を選択し右クリックメニューから「Connect to WSL」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/50671843-8c25-dc8a-4a59-20048449e14c.png)

VSCodeがもう一つ起動してきます。これが、WSL内のVSCodeになります。
最初のVSCodeは閉じても構いません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/23b62cb2-8f00-09c1-5387-04d9bf867018.png)

## 対象プロジェクトフォルダーを開く
VSCodeからフォルダを開くをクリックします。
今回はWindowsフォルダ内の「C:\WorkSpace\LinuxFibonacci」にしたいので、「/mnt/c/WorkSpace\LinuxFibonacci」まで辿っていき、OKボタンをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/731be813-91f4-1abb-3e57-e1843fd74f3f.png)

## 作業開始
左下隅にてWSL:Ubuntu-18.04の接続が確認できます。これで作業の開始となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/17a37948-5d77-d948-31d7-8506873556f4.png)

## プログラム作成
```cpp:Fibonacci.cpp
#include<iostream>
using namespace std;
 
int main()
{
    int fib1 = 0, fib2 = 1, fib3 = 1;
 
    cout << "The Fibonacci Series is follows : " << endl << fib1 << " " << fib2 << " ";
    while (fib1 + fib2 < 1000)
    {
        fib3 = fib1 + fib2;
        fib1 = fib2;
        fib2 = fib3;
        cout << fib3 << " ";
    }
    cout << endl;
 
    return 0;
}
```

上記のソースコードを入力して、「Fibonacci.cpp」で保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/180dfb15-e4cf-9956-e6aa-9b74f3a93109.png)

# ビルドとデバッグの設定
適当な位置にブレイクポイントをセットして、メニューの「実行」から「デバッグの開始」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/cfa535d8-1b2a-faab-1f84-17129744476a.png)

launch.jsonがまだ生成されていない場合、環境の選択と構成の選択が選択肢が出てきます。
※一度生成されるとlaunch.jsonを削除しても勝手に自動生成されてしまい環境の選択が出来ないです、よく分からない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/9a4fe6b7-4139-4977-73d7-3219a7f35cd1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/ddf4dca9-d7f8-dadd-fa26-09ab9d550a2b.png)

すると、launch.jsonとtask.jsonが自動生成されます。

```json:launch.json
{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "g++ - アクティブ ファイルのビルドとデバッグ",
            "type": "cppdbg",
            "request": "launch",
            "program": "${fileDirname}/${fileBasenameNoExtension}",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${fileDirname}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "gdb の再フォーマットを有効にする",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "C/C++: g++ アクティブなファイルのビルド",
            "miDebuggerPath": "/usr/bin/gdb"
        }
    ]
}
```

```json:task.json
{
	"version": "2.0.0",
	"tasks": [
		{
			"type": "cppbuild",
			"label": "C/C++: g++ アクティブなファイルのビルド",
			"command": "/usr/bin/g++",
			"args": [
				"-g",
				"${file}",
				"-o",
				"${fileDirname}/${fileBasenameNoExtension}"
			],
			"options": {
				"cwd": "${fileDirname}"
			},
			"problemMatcher": [
				"$gcc"
			],
			"group": "build",
			"detail": "コンパイラ: /usr/bin/g++"
		}
	]
}
```

## タスクの構成の再生成
tasks.jsonが生成されていない場合、下記の方法で同じものを生成できます。

メニューの「ターミナル」から「タスクの構成」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/32667d05-9f3b-06fa-3930-3defc1fbf457.png)

Linux用なので「C++:g++ アクティブなファイルのビルド」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/b2bf223b-742c-fbbe-38a5-84083b70c712.png)

すると、task.jsonが自動生成されますので、保存します。

## launch.jsonの修正
「デバッグの開始」で下記エラー「launch:program (パス名)/build/Debug/outDebug does not exist」が出るようでしたら、launch.jsonを修正します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/b834268e-214f-1f34-198c-e2218f225889.png)

### 修正前

```json:launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "C/C++ Runner: Debug Session",
      "type": "cppdbg",
      "request": "launch",
      "args": [],
      "stopAtEntry": false,
      "cwd": "/mnt/c/WorkSpace/LinuxFibonacci",
      "environment": [],
      "program": "/mnt/c/WorkSpace/LinuxFibonacci/build/Debug/outDebug",
      "internalConsoleOptions": "openOnSessionStart",
      "MIMode": "gdb",
      "miDebuggerPath": "/usr/bin/gdb",
      "externalConsole": false
    }
  ]
}
```

### 修正後
"cwd"と"program"の内容を変更、"preLaunchTask"を追加しています。

```json:修正箇所
"cwd": "${fileDirname}",
"program": "${fileDirname}/${fileBasenameNoExtension}",
"preLaunchTask": "C/C++: g++ アクティブなファイルのビルド",
```

```json:launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "C/C++ Runner: Debug Session",
      "type": "cppdbg",
      "request": "launch",
      "args": [],
      "stopAtEntry": false,
      "cwd": "${fileDirname}",
      "environment": [],
      "program": "${fileDirname}/${fileBasenameNoExtension}",
      "internalConsoleOptions": "openOnSessionStart",
      "MIMode": "gdb",
      "preLaunchTask": "C/C++: g++ アクティブなファイルのビルド",
      "miDebuggerPath": "/usr/bin/gdb",
      "externalConsole": false
    }
  ]
}
```

## フォルダ構成
「Fibonacci」は実行ファイルになります。Linuxでは実行ファイルでも「exe」などの拡張子が付きません。
実行ファイルは、“./(実行ファイル名)“と先頭に”./“を付けることでBashなどで実行できるようになる。
先頭に”./“を付けないと、「command not found」エラーとなります。

```
C:\WorkSpace\LinuxFibonacci
│  Fibonacci
│  Fibonacci.cpp
│
└─.vscode
    launch.json
    tasks.json
    c_cpp_properties.json
    settings.json
```

# 実際にビルド・デバッグを試す
ようやく環境が整ったので、ビルド・デバッグの動作を確認してみます。

## ビルド実施
メニューの「実行」の「デバッグの開始」をクリックすればビルドとデバッグが開始されます。
また、メニューの「ターミナル」の「ビルドタスクの実行」をクリックすればビルドのみが行われます。

```
> Executing task: C/C++: cpp アクティブなファイルのビルド <

ビルドを開始しています...
/usr/bin/cpp -g /mnt/c/WorkSpace/LinuxFibonacci/Fibonacci.cpp -o /mnt/c/WorkSpace/LinuxFibonacci/Fibonacci
ビルドが正常に完了しました。

ターミナルはタスクで再利用されます、閉じるには任意のキーを押してください。
```

## デバッグ実施
左側に変数の値が出力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/7d279c4d-d9f6-6c30-d297-cef7b1582661.png)

## 結果
ターミナルに結果が出力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/756ed33c-476d-4462-0b08-2c0f94f3a488.png)

```bash:結果
The Fibonacchi Series is follows :
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
```

# 外部プロセス実行プログラム
C++でインターフェイスプログラムを呼び出すプログラムになります。
Windowsでは CreateProcessとWaitForSingleObjectを使用するが、Linuxにはないため forkとexeclとwaitpidを使用する。

Clientフォルダに「Test」という実行ファイルを引数2つ(-u,HOGE)を渡して実行する。

```cpp:main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
 
int main()
{
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        exit(-1);
    }
    else if (pid == 0) {
        // 子プロセスで別プログラムを実行
        execl("./Client/Test", "./Client/Test", "-u", "HOGE", NULL);
        exit(-1);
    }
 
    // 親プロセス
    int status;
    //子プロセスの終了待ち
    pid_t r = waitpid(pid, &status, 0); 
    if (r < 0) {
        perror("waitpid");
        exit(-1);
    }
    if (WIFEXITED(status)) {
        // 子プロセスが正常終了の場合
        printf("child exit-code=%d\n", WEXITSTATUS(status));
    }
    else {
        printf("child status=%04x\n", status);
    }
 
    return 0;
}
```

# 最後に
WSL(Linux)用の実行ファイルの生成とデバッグができるようになります。
でも、今回本当にやりたいのは、Rasberry Pi用(CPU:Arm)の実行ファイルの生成とデバッグなんですよね。
~~まだ、実現できていないのですが、出来るようになったら記事を作成します。~~

https://qiita.com/yaju/items/bb91ea2bf84aac92243c