VSCodeでProjectのビルドっぽいことをしてみた

# はじめに
筆者は普段Androidアプリをkotlinで書いています（on AndroidStudio）
週末プログラミングで、普段使っていないVSCode上でkotlinを書こうかなという試みです。
この記事を書くにあたって、VSCodeの導入やKotlinのStand aloneなコンパイラの導入などをまとめ記事を見ながら一通りやったのですが、
大体の記事が Hello world! を出すところで紹介が終わっていることに（後から）気が付きました。

実装して・・・クラスを分割して・・・を楽しんだ後、さぁビルドしようと思ったときに、
はて、VSCode上でProject単位でまとめてビルドまでやってくれんのかなー・・・？と。
もちろんそこまでやってくれるはずもなく、それっぽいまとめ記事も出てこなかったので、
今回まとめてみようと思った次第です。

# 大前提
正直ガッツリ実装環境を作る！というなら統合開発環境でやるか、gradleとか入れたほうが早いような気がします。
なので参考程度に御覧ください・・・。

VSCodeの導入やKotlinのコンパイラの導入はすでに終わっているものとします。
今回の環境はラズパイ上にVSCodeやKotlinを入れています。
おおよそ他の方が既に書かれている記事の通りで導入できます。

# Project & Sources

## Project
今回ビルドしようとしたのは下記のようなファイルたち。
ディレクトリ構造はわりと適当です。

```
-- TestApp/
      ├-- src/com/nonameplayer/testapp
      |                           ├-- Main.kt
      |                           ├-- Factory.kt
      |                           └-- Person.kt
      └-- build/Main.jar                           <- ビルド結果
```

## Sources
人のリストを出力するプログラムを複数ファイルに渡って書いてみました。
実際こんな感じでファイルを分けて書くよね・・・というイメージで見てもらえれば。
（中身については本質的ではないので触れないでください。。。）

```kotlin:Main.kt
package com.nonameplayer.testapp

import com.nonameplayer.testapp.Factory

fun main() {
    Factory.create().forEach {
        it.print()
    }
}

```

```kotlin:Factory.kt
package com.nonameplayer.testapp

import com.nonameplayer.testapp.Person

object Factory {
    fun create(): List<Person> {
        return mutableListOf<Person>().apply {
            add(
                Person("Sato", 28)
            )
            add(
                Person("Tanaka", 25)
            )
        }
    }
}
```

```kotlin:Person.kt
package com.nonameplayer.testapp

class Person(private val name: String, private val age: Int) {
    fun print() {
        println("Name: $name, Age: $age")
    }
}

```

# Pre-defined command
VSCode には Code Runner というプラグインを導入することで、簡易的にプログラムを実行できるようになっています。
`Ctrl+Alt+N` がビルドコマンドとして設定されていて、下記のコマンドが走るようです。
> cd "/home/pi/kotlin/Testapp/src/com/nonameplayer/testapp" && kotlinc Main.kt -include-runtime -d Main.jar && java -jar Main.jar

`Main.kt` の階層までcd、`Main.kt`をビルド、できたjarを実行という内容なのですが、、、
（当たり前ですが）`Main.kt` 単体を  でビルドしても、`Factory` が参照できないので当然通りません。
> Main.kt:3:31: error: unresolved reference: Factory

上記の `Factory.kt`, `Person.kt` も一緒にビルドしてあげれば `Main.kt` から `Factory.kt` を参照できてビルドが通るようになります。
コマンド的には次のようなものがほしいです。

```bash:expected
cd "/home/pi/kotlin/Testapp/src/com/nonameplayer/testapp" && kotlinc Main.kt Factory.kt Person.kt -include-runtime -d Main.jar && java -jar Main.jar
```

`Ctrl+Alt+N` を実行したときにカレントのディレクトリにあるファイルをビルドするようなコマンドに書き換えてしまうこともできるのですが、
汎用性が著しく落ちるので、`tasks.json` を変更してビルドコマンドを作ってみたいと思います。

# tasks.json
VSCodeのメニューの Terminal > Configure tasks... から テンプレート > Other を選択して `tasks.json` を作ります。
defaultでは次のような中身になっていますが、これを変更していきます。

```json:tasks.json(変更前)
{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "label": "echo",
      "type": "shell",
      "command": "echo Hello"
    }
  ]
}
```

```json:tasks.json(変更後)
{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "type": "shell",
            "command": "kotlinc",
            "args": [
                "./src/com/nonameplayer/testapp/*.kt",
                "-include-runtime",
                "-d",
                "./build/Main.jar"
            ],
            "presentation": {
                "reveal": "always"
            },
            "group": "build"
        },
        {
            "label": "executeJar",
            "type": "shell",
            "command": "java",
            "args": [
                "-jar",
                "./build/Main.jar"
            ],
            "presentation": {
                "reveal": "always"
            },
            "problemMatcher": [],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "dependsOn": [ "build" ]
        }
    ]
}
```

`build` と `executeJar` という2つのタスクを作っていて、`executeJar` が `build` に依存する ( `"dependsOn": [ "build" ]` ) 設定にしています。
また `executeJar` の方をデフォルト（ `"isDefault": true` ）で実行されるタスクにしています。
こうすることで、タスク実行のコマンドで `executeJar` が実行され、依存している `build` も同時に走らせることができます。
そして肝心のビルドファイルの指定ですが、`"./src/com/nonameplayer/testapp/*.kt"`と指定していて、`Main.kt` と同じディレクトリにいれば同時にビルドしてくれるように設定しています。

# いざ実行
タスクの実行は `Ctrl+Shift+B` で出来るようで、実行するとターミナルが起動し、タスクが実行されます。

```bash:result
> Executing task: kotlinc ./src/com/nonameplayer/testapp/*.kt -include-runtime -d ./build/Main.jar <

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.intellij.util.ReflectionUtil to method java.util.ResourceBundle.setParent(java.util.ResourceBundle)
WARNING: Please consider reporting this to the maintainers of com.intellij.util.ReflectionUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

Terminal will be reused by tasks, press any key to close it.

> Executing task: java -jar ./build/Main.jar <

Name: Sato, Age: 28
Name: Tanaka, Age: 25

Terminal will be reused by tasks, press any key to close it.
```

# おわりに
オブジェクト指向プログラミングをやると、どうしてもファイルが複数に渡ってしまい、一つのクラス（ファイル）のみのビルドだけでは実現できないことがしばしばあります。
単体ファイルのビルドだけで実行したいからと言って1つのファイルに全部まとめてしまうのはイケてないし、まとめてビルドするためにgradleなどの仕組みを勉強して導入するのは手間・・・
ということで、今回は複数のクラス（ファイル）からなるプログラムを簡易的にまとめてビルドして動かせるような環境（ `tasks.json` ）を作ってみました。
ただし、ディレクトリ構造がややこしくなればなるほど `tasks.json` で指定するプログラムのファイルが増えるため、あまりおすすめはできません。

`Hello world!` 程度では終わらないけど、統合開発環境や便利なgradleなどのコンパイラを導入してまではガッツリやらない！という中間的なレベルでは使えるのではと。

# 参考
https://qiita.com/ShortArrow/items/dc0c8cacd696154510f1
https://qiita.com/__cooper/items/9517addaaf1e77215848
https://code.visualstudio.com/docs/editor/tasks