🧭

Goの競プロ環境構築【VSCode,devcontainer,acc,oj】

2023/11/25に公開

やりたいこと

  • 競技プログラミング用のGoの環境をdevcontainerで構築したい
  • atcoder-cliで問題とテストケースを取得したい
  • online-judge-toolsを使ってテストをしたい

記事を書こうと思った理由

  • pip3 install online-judge-toolserror: externally-managed-environmentエラーでうまくいかなった
  • バインドマウントでdevcontainerのフォルダが上書きされて詰まった
  • せっかくいろんなサイトで調べて解決できたのでgo + devcontainer + acc + oj + venvの環境構築を流れで残しておきたかった

環境構築

Dockerfileを作成

  • 元となるイメージを指定
    AtCoderのGoのバージョンが1.20.6なのでそれに合わせる
Dockerfile
FROM golang:1.20.6
  • カレントディレクトリを変更
Dockerfile
WORKDIR /go/src
  • パッケージをインストール
    • nodejs
    • npm
    • python3
    • python3-pip
    • python3.11-venv
Dockerfile
RUN apt update && apt install -y nodejs npm python3 python3-pip python3.11-venv
  • venvのセットアップ
Dockerfile
RUN python3 -m venv venv
  • online-judge-toolsのインストール
Dockerfile
RUN . venv/bin/activate && pip3 install online-judge-tools
  • venvのpythonを使用するようにパスを通す
Dockerfile
ENV PATH /go/src/venv/bin:$PATH
  • atcoder-cliのインストール
Dockerfile
RUN npm install -g atcoder-cli
  • toolsをインストール
Dockerfile
RUN go install -v golang.org/x/tools/gopls@latest
  • atcoder-cliの設定変更
    問題を全てダウンロードする設定
Dockerfile
RUN acc config default-task-choice all

docker-composeを作成

  • バージョン指定
docker-compose.yaml
version: '3.9'
  • コンテナ名設定
docker-compose.yaml
services:
  atcoder:
    container_name: atcoder-golang
  • 使用するDockerfileを指定
docker-compose.yaml
services:
  atcoder:
    build:
      dockerfile: ./Dockerfile
  • コンテナ永続化
docker-compose.yaml
services:
  atcoder:
    tty: true
  • ボリュームマウント
    作成されたvenvフォルダがバインドマウントによって上書きされてしまうので、ボリュームマウントとして設定する
docker-compose.yaml
services:
  atcoder:
    volumes:
      - type: volume
        source: venv
        target: /go/src/venv

volumes:
  venv:
  • バインドマウント
docker-compose.yaml
services:
  atcoder:
    volumes:
      - type: bind
        source: ./src
        target: /go/src

devcontainerを起動

  • command + shift + pでコマンドパレットを開いてDev Containers: ReOpen in Containerを実行

  • .devcontainer/devcontainer.jsonが自動で作成されるはずなので書き換える

    • workspaceFolder: デフォルトで開くルートフォルダを指定
    • vscode.settings: devcontainer側のsettings.jsonに追記したい内容
devcontainer.json
{
	"name": "atcoder",
	"dockerComposeFile": [
		"../docker-compose.yaml",
		"docker-compose.yml"
	],
	"service": "atcoder",
	"workspaceFolder": "/go/src",
	"customizations": {
		"vscode": {
			"settings": {
				"[go]": {
					"editor.defaultFormatter": "golang.go",
					"editor.formatOnSave": true
				}
			}
		}
	}
}

Makefileを作成

  • ログイン用コマンド作成
Makefile
acc_login:
	acc login

oj_login:
	oj login https://atcoder.jp/

login: acc_login oj_login

タスクを作成

  • command + shift + pでコマンドパレットを開いてTasks: Configure Taskを実行

  • テンプレートから tasks.json を生成を実行

  • Othersを実行

  • .vscode/tasks.jsonが自動で作成されるはずなので編集する

    • download: contextで指定したコンテストの問題を取得する
    tasks.json
    {
        "tasks": [
    	{
    	    "label": "download",
    	    "type": "shell",
    	    "command": "bash ${workspaceFolder}/cmd/download.sh"
    	},
        ]
    }
    
    cmd/download.sh
    #! /bin/bash
    
    dir=$(cat ./contest)
    WORKING_DIR="/go/src"
    PROBLEM_DIR="${WORKING_DIR}/${dir}"
    TEMPLATE="${WORKING_DIR}/templates/main.go"
    
    cd $WORKING_DIR
    
    acc new $dir
    
    PROBLEMS="${PROBLEM_DIR}/*"
    for DIRPATH in $PROBLEMS; do
        if [ ! -d $DIRPATH ]; then
    	continue
        fi
    
        cp -n $TEMPLATE "${DIRPATH}/main.go"
    done
    
    • test: 開いているmain.goをテストする
    tasks.json
    {
        "tasks": [
    	{
    	    "label": "test",
    	    "type": "shell",
    	    "command": "oj t -d ${fileDirname}/tests -c 'go run ${file}'"
    	},
        ]
    }
    
    • submit: 開いているmain.goをAtCoderに提出する
    tasks.json
    {
        "tasks": [
    	{
    	    "label": "submit",
    	    "type": "shell",
    	    "command": "cd ${fileDirname} && acc submit main.go"
    	},
        ]
    }
    

VSCodeの拡張

https://marketplace.visualstudio.com/items?itemName=golang.Go
https://marketplace.visualstudio.com/items?itemName=SanaAjani.taskrunnercode

  • アンインストールの横にある歯車からdevcontainer.json に追加を選択すると.devcontainer/devcontainer.jsonに追記されdevcontainer起動時に自動でインストールされる
devcontainer.json
{
	...
	"customizations": {
		"vscode": {
			"extensions": [
				"golang.go",
				"SanaAjani.taskrunnercode"
			],
			...
		}
	}
}

初期設定

テンプレートファイルを作成

templates/main.go
package main

func main() {

}

使い方

ログイン

make login

コンテストを指定

contest
abc001

問題とテストケースのダウンロード

  • task runnerからdownloadコマンドを実行
  • ルート直下に問題とテストケースがダウンロードされ、テンプレートファイルがコピーされる
src/
 ├ abc001
     ├ a
       ├ main.go
       └ tests
     ├ b
       ├ main.go
       └ tests
     └ c
       ├ main.go
       └ tests

問題を解く

  • main.goを編集

テストする

  • task runnerからtestコマンドを実行

提出する

  • task runnerからsubmitコマンドを実行

おわりに

Dockerの本を読んで実際に作りたくなったので勉強もかねて環境構築しましたが結構詰まりました。
トライアンドエラーでやってたので不要な記述とかありそうですね。。。
一旦満足したのでちょこちょこコンテストにも参加してみたいなと思います。

参考

Discussion