インタプリタ言語開発環境をDockerで構築する[Windows]

5 min read読了の目安(約5000字

長期休みだし、Rubyみたいな解析木実行型のインタプリタ言語をのぞいてみたいと思ったのでそのための環境構築をしました。

Windows環境なので、Docke@tweet

ツイートページのURLを指定してください ツイートページのURLを指定してください ツイートページのURLを指定してください

rでlinux環境を作り処理を実行できるようにします。

作業場所

C:\Users\{user_name}\Documents\docker_environment\workspace\sunabaで作業を行い、Dockerfile等はここに置くことで実際のコードと分けて管理しようと思います。
コードはC:\Users\{user_name}\Documents\docker_environment\workspace\sunaba\workに置き、ここをホストとコンテナとの共有フォルダとしようと思います。

C:.
├─Documents
│  ├─docker_environment
│  │  └─workspace
│  │      └─sunaba
│  │          ├─docker_build.bat
│  │          ├─docker_start.bat
│  │          ├─docker_stop.bat
│  │          ├─Dockerfile
│  │          └─work
│  │              ├─.gitignore
│  │              ├─Makefile
│  │              ├─sunaba.c
│  │              └─test.sh

参考リンク

違いは上記の内容にyaccとlexをインストールしてbatchファイルを追加しているだけです

Dockerのインストール

Docker Desktop for WindowsをDocker Hubからダウンロードしてインストールしてください。
起動してチュートリアルをポチポチできればDockerのインストールができていることを確認できるので楽です

Dockerfileの作成

Dockerfileを以下のように作成し、C:\Users\{user_name}\Documents\docker_environment\workspace\sunaba'に'Dockerfileという名前で保存してください。

[1]

FROM ubuntu:latest
RUN apt update
RUN DEBIAN_FRONTEND=noninteractive apt install -y gcc make git binutils libc6-dev gdb sudo bison flex
RUN adduser --disabled-password --gecos '' user
RUN echo 'user ALL=(root) NOPASSWD:ALL' > /etc/sudoers.d/user
RUN mkdir /work
USER user
WORKDIR /home/user

Dockerイメージの作成

'make_compilerという名前でDockerイメージを作成するバッチファイルを作成します。 以下の内容をC:\Users{user_name}\Documents\docker_environment\workspace\sunaba'に'docker_build.bat`という名前で保存してください。

@REM docker初回起動時
docker build -t make_compiler C:/Users/{user_name}/Documents/docker_environment/workspace/sunaba/

@REMはコメントです。@REMがある行は削除しても処理に変化はありません。

コンテナ作成用バッチ

compile_testという名前でコンテナを作成するバッチファイルを作成します。
作成するファイル名はdocker_start.batとしてC:\Users\{user_name}\Documents\docker_environment\workspace\sunabaに保存してください

@REM インタラクティブに使用したい場合
@REM docker run --rm -it -v C:/Users/202169/{user_name}/docker_environment/workspace/sunaba/work:/work -w /work --name compile_test make_compiler

@REM コンテナの中でmakeを実行してソースファイルtestをコンパイル
docker run --rm -v C:/Users/{user_name}/Documents/docker_environment/workspace/sunaba/work:/work  -w /work --name compile_test make_compiler make test

docker run --rm -v C:/Users/{user_name}/Documents/docker_environment/workspace/sunaba/work:/work  -w /work --name compile_test make_compiler make clean

僕はコンテナを使用するたびに新しく作成したい派なので、--rmオプションでコマンドが終了するとコンテナも終了するようになっています。
また、-v <source>:<dest>オプションでホストの環境のパス<source>を、Dockerの中で<dest>として共有するようにしています。
最後にmake testとして、後述するテスト処理をするようにしています。

コンテナをインタラクティブに使用した時のコンテナ停止バッチ

コンテナをインタラクティブに使用して作業したい場合のためにコンテナ停止バッチもついでなので作っておきます。

作成するファイル名はdocker_stop.batとしてC:\Users\{user_name}\Documents\docker_environment\workspace\sunabaに保存してください
compile_testという名前でコンテナが作成されていると想定しています。

@REM dockerを止める時
docker stop compile_test
docker rm compile_test

環境構築できたかの確認

ここまでを行い、docker_build.batを実行すれば、環境構築は完了しているので
動作確認を行いたいと思います。

実行する処理は低レイヤを知りたい人のためのCコンパイラ作成入門: ステップ1:整数1個をコンパイルする言語の作成 コンパイラ本体の作成の内容です。

処理ファイルの作成

sunnaba.cという名前でC:\Users\{user_name}\Documents\docker_environment\workspace\sunaba\workに以下の内容を保存してください

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    if (argc != 2) {
        fprintf(stderr, "引数の個数が正しくありません\n");
        return 1;
    }

    printf(".intel_syntax noprefix\n");
    printf(".global main\n");
    printf("main:\n");
    printf("    mov rax, %d\n", atoi(argv[1]));
    printf("    ret\n");
    return 0;
}

テスト処理用のシェルスクリプト

自動テスト用のシェルスクリプトをtest.shという名前でC:\Users\{user_name}\Documents\docker_environment\workspace\sunaba\workに以下の内容を保存してください

#!/bin/bash
assert() {
    expected="$1"
    input="$2"

    ./sunaba "$input" > tmp.s
    cc -o tmp tmp.s
    ./tmp
    actual="$?"

    if [ "$actual" = "$expected" ]; then
        echo "$input => $actual"
    else
        echo "$input => $expected expected, but got $actual"
        exit 1
    fi
}
assert 0 0
assert 42 42

echo OK

Makefileの作成

ここまでの処理を自動実行するMakefileをMakefileという名前でC:\Users\{user_name}\Documents\docker_environment\workspace\sunaba\workに以下の内容を保存してください

CFLAFS=-std=c11 -g -static

mcc:sunaba.c

test:sunaba
	./test.sh

clean:
	rm -f sunaba *.o *~ tmp*

.PHONY: test clean

gitで管理することを考え、.gitignoreC:\Users\{user_name}\Documents\docker_environment\workspace\sunaba\workに作成します。

*~
*.o
tmp*
a.out
sunaba

最後に、docker_start.batを実行して

0 => 0
42 => 42
OK

と表示されたら完了です。

脚注
  1. {user_name}はそれぞれのPC環境の設定によって変わります。 ↩︎