普段は WordPress のプラグイン等を開発しているので使用言語は PHP や JavaScript なのですが、最近、コンパイル言語で小さなツールを作りたいと思い調査開始 🧐
コンパイラ言語,,, やっぱり速いって魅力ですよね
PHPで時間のかかっているところの処理を置き換えしたいな、できれば今どきのモダンな機能を備えていて、読みやすく書きやすいシンプルな言語がいい
有名なところで、Go や Rust あたりですが、Rust は難しいと聞いていたので、まず Go を少しやってみたのですが、ちょっとしっくりこないというか…
他にないかと調べていると Nim, Zig, V 等いろいろありますね
まだあまり知られていないし、現在進行系で開発中だったりしているようですが、V言語が面白そう
V言語はまだ安定版に達してないようだけどシンプルで読みやすそうなのでちょっとやってみたいと思いDockerを使ったプログラミング環境を作ってみました
といっても既に公開されている記事を参考にちょっとカスタマイズしただけですが… 😅
VSCode による Dockerコンテナ内 V言語プログラミング環境
V言語についてはこのあたりを見てください
この記事は、V言語のチュートリアルではありません。私もこれから始めるので良い点、悪い点等まだ判断できていない状態です 😅
とりあえず学び始める準備として手軽に実行できる環境を作成したのでその紹介です
VSCode, WSL2, Docker for Desktop 等のインストールについては、ここでは説明しません。検索すれば沢山の記事が見つかると思いますので事前にインストールを済ませてからお試しください
ダウンロード
ダウンロードボタンをクリックするとパスワード入力ページが表示されるので、パスワード( v-docker )を入力してダウンロードして下さい
パスワードを入力すると自動的にダウンロードが実行されます
ファイル構成
v_docker_vsc.zip を解凍すると下記ファイル構成に展開されます
docker-compose.yml | v_docker_vsc コンポーズファイル本体 | ||
readme.txt readme-ja.txt | v_docker_vsc の説明書 | ||
v/ | |||
Dockerfile | V言語コンテナビルドファイル | ||
app/ | Vプログラム作成用の共有フォルダー | ||
hello.V | Vプログラムのサンプルファイル | ||
build/ | デバッグビルド用ディレクトリ | ||
.vscode/ | VSCode 用の設定ファイル | ||
launch.json | Vプログラムデバッグ用 gdb 定義と起動 | ||
tasks.json | Vプログラムのデバッガー起動前の gcc コンパイル定義 | ||
prettyprinter.py | Vプログラムのデバッグデータ(V配列等)の表示サポート用 | ||
.devcontainer/ | |||
devcontainer.json | コンテナ内で実行するリモート VSCode 用定義 | ||
docker-compose.yml | devcontainer の既存 docker-compose オーバーライド用 |
Docker イメージについて
docker-compose.yml ファイル
version: "3" services: v: build: context: './v/' tty: true volumes: - ./app:/workspace environment: - "PS1=[$(whoami):$(pwd)]$ " ports: - "8080:8080" - "5173:5173" working_dir: /workspace container_name: vlang
ファイルの永続化
./app:/workspace
Windows または WSL2 側の ./app と docker コンテナ内 /workspace を共有
vlangコンテナ Dockerfile ファイル
FROM debian:latest RUN apt-get update && \ apt-get install --no-install-recommends -y \ sudo git wget openssh-client libssl-dev libsqlite3-dev \ # V develop gcc make gdb valgrind strace binutils libc-dev libc6-dev \ # V UI libx11-dev libglfw3-dev libfreetype6-dev \ # Certs ca-certificates && \ # Get NodeJS setup script wget -qO- "http://deb.nodesource.com/setup_lts.x" | sh - && \ # Install NodeJS and clean up apt-get update && \ apt-get install --no-install-recommends -y \ nodejs && \ apt-get clean && rm -rf /var/cache/apt/archives/* && \ rm -rf /var/lib/apt/lists/* WORKDIR /opt/vlang ENV VVV /opt/vlang ENV PATH /opt/vlang:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin RUN mkdir -p /opt/vlang && \ ln -s /opt/vlang/v /usr/bin/v RUN git clone https://github.com/vlang/v /opt/vlang && \ make VFLAGS='-cc gcc' && \ v -version # Create user ARG USER_ID="1000" ARG GROUP_ID="1000" ARG USER_NAME="vlang" RUN groupadd --gid $GROUP_ID $USER_NAME \ && useradd --uid $USER_ID --gid $GROUP_ID -m $USER_NAME \ && echo "$USER_NAME ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/$USER_NAME \ && chmod 0440 /etc/sudoers.d/$USER_NAME RUN chown -R $USER_NAME:$USER_NAME /opt/vlang USER $USER_NAME
ベースイメージ: debian 最新版
V language : https://github.com/vlang/v から取得して make install
主なツール
gcc, make, gdb, python3, git, wget、node, …
ユーザー vlang 作成
root での作業を避けるためにユーザー vlang を登録します
また、V のインストール先は、ユーザー(vlang) が書き込み可能である必要があるので chown で vlang に変更
VSCode 用の構成ファイルについて
VSCode を使用して Docker コンテナ内のリモート環境でプログラムの作成とデバッグを行えるように devcontainer.json, launch.json, tasks.json を使用して構成します
devcontainer.json
コンテナ内で実行するリモート VSCode 用定義(コンテナ側にインストールする Extension 登録)
{ "name": "v_vscdev", "dockerComposeFile": ["../docker-compose.yml", "docker-compose.yml"], "service": "v", "workspaceFolder": "/workspace", // Configure tool-specific properties. "customizations": { // Configure properties specific to VS Code. "vscode": { "settings": { "terminal.integrated.shell.linux": "/bin/bash", "terminal.integrated.inheritEnv": false, }, "extensions": [ "vlanguage.vscode-vlang", "ms-vscode.cpptools-extension-pack" ] } }, // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Use 'postCreateCommand' to run commands after the container is created. // "postCreateCommand": "gcc -v", // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. "remoteUser": "vlang" }
VSCode 拡張機能
Windows / WSL side
- WSL (ms-vscode-remote.remote-wsl)
- Docker (ms-azuretools.vscode-docker)
- Dev Containers (ms-vscode-remote.remote-containers)
Windows / WSL and Dev Container side
- C/C++ Extension Pack (ms-vscode.cpptools-extension-pack)
- V language support for Visual Studio Code. (vlanguage.vscode-vlang)
launch.json
Vプログラムデバッグ用 gdb 定義と起動
{ "version": "0.2.0", "configurations": [ { "preLaunchTask": "Build V debug", "type": "cppdbg", "request": "launch", "name": "Vlang debug", "program": "${workspaceFolder}/build/${fileBasenameNoExtension}", "args": [], "stopAtEntry": false, "cwd": "${workspaceFolder}/build", "environment": [], "externalConsole": false, "MIMode": "gdb", "miDebuggerPath": "/usr/bin/gdb", "setupCommands": [ { "description": "Enable pretty-printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true }, { "text": "set charset UTF-8" }, { "description": "V pretty printer", "text": "source ${workspaceFolder}/.vscode/prettyprinter.py", "ignoreFailures": false } ] } ] }
tasks.json
Vプログラムのデバッガー起動前の gcc コンパイル定義
launch.json の preLaunchTask と label で関連付けることで自動的に呼び出される
{ "version": "2.0.0", "tasks": [ { "label": "Build V debug", "type": "shell", "command": "v", "args": [ //"-keepc", //"-cg", "-g", "-cc", "gcc", "${file}", "-o", "./build/${fileBasenameNoExtension}" ], "problemMatcher": ["$gcc"], "group": { "kind": "build", "isDefault": true } } ] }
ビルドオプションについては v help, v help build 等でお調べください
prettyprinter.py
Vプログラムのデバッグデータ(V配列等)の表示サポート用
launch.json の setupCommands 登録により実行される
import gdb class StringPrinter: def __init__(self, val): self.val = val def to_string(self): buffer = gdb.inferiors()[0].read_memory(self.val['str'], self.val['len']) return str(buffer, 'utf-8') # depending on the default encoding this must be changed class ArrayPrinter: def __init__(self, val, _type): self.val = val self.length = int(self.val['len']) self.mem = self.val['data'] self._type = _type[6:] def to_string(self): return f"{self._type} array:" def next_element(self): try: for i in range(self.length): ptr = self.mem.cast(gdb.lookup_type(self._type).pointer()) ptr += i yield str(i), str(ptr.dereference()) except gdb.error: return def children(self): return self.next_element() def display_hint(self): return "array" class MapPrinter: def __init__(self, val, _type): self.val = val self.length = int(self.val['len']) self.values = self.val['key_values']['values'] self.keys = self.val['key_values']['keys'] _, self.key_type, self.value_type = _type.split('_', 2) def to_string(self): return None # "Map:" def next_element(self): for i in range(self.length): key_ptr = self.keys.cast(gdb.lookup_type(self.key_type).pointer()) val_ptr = self.values.cast(gdb.lookup_type(self.value_type).pointer()) key_ptr += i val_ptr += i yield str(key_ptr.dereference()), str(val_ptr.dereference()) def children(self): return self.next_element() def display_hint(self): return None class UTF16StringPrinter: def __init__(self, val): self.val = val self.value = '' i = 100 while True: buffer = gdb.inferiors()[0].read_memory(self.val, 2) value = bytes(buffer).decode("utf-16-le") self.value += value if value == '\x00': break i -= 1 self.val += 1 if i == 0: break def to_string(self): return self.value def v_printer(val): _type = str(val.type) if _type == 'string': return StringPrinter(val) elif _type.startswith('Array_'): if _type.startswith('Array_fixed'): return elif _type.endswith('*'): return else: return ArrayPrinter(val, _type) elif _type.startswith('Map_'): return MapPrinter(val, _type) # elif _type == 'u16 *': # return UTF16StringPrinter(val) gdb.pretty_printers.append(v_printer)
GDB デバッグ時に配列等のデータをわかりやすく表示するためのスクリプトをGDBに設定します
https://github.com/lazalong/V-Hello
参考
使い方
VSCode を起動して F1 キークリック(View -> Command Palette…)から Dev Containers: Open Folder in Container… を選択して v_docker_vsc フォルダーを開きます
初回は Docker イメージの生成でちょっと時間がかかりますが、正常に起動したら新しくターミナルを開き ctrl+shift+@
v -v
や gdb -v
を実行してバージョンが表示されるか確認してください
V プログラムの開発環境が準備できたのでプログラムの作成とデバッグを行います
プログラムファイルを作成/更新したら V Extension 機能やターミナルからのコマンド入力によりコンパイルを行います
コンパイルエラーがなくなったら、デバッグ対象ファイルをエディタで開いたままデバッグ用のブレイクポインタ等を設定
VSCode を Run and Debug モード ctrl+shift+D にしてデバッグを実行します
デバッグが完了したら実行用プログラムをビルド(-prod)して生成
以上が Docker コンテナ内の V プログラミング手順です
WSL2 の Linux(ubuntu等)からの実行について
Windowsのフォルダーから実行して Docker クライアントとファイル共有するのが簡単で扱いやすいのですが、デメリットとして、Windows と Linux の異なるファイルシステム間でのファイル共有はファイルアクセスが遅くなるという影響があります。また、node を使用した npm の JavaScript 開発においてはファイルの変更を検出できずに Live reload 等が使えないということもあります。
これらのデメリットが気になるようでしたらひと手間かかりますが、ターミナルを使い WSL2 上の Ubuntu 等にログインして、 home ディレクトリのログインユーザー下に workspace 等の作業ディレクトリを作ってファイルを展開することをおすすめします。
ターミナルでLinux(Ubuntu等)へのログイン後は、作業用ディレクトリへ移り、そこから code .
と入力するだけでVSCodeが起動出来るので、以降の作業は Windows 上での操作と同じ感覚で行えると思います
このように VSCode では、簡単に WSL Linux 上で実行出来るようになっているのですが、Windowsで使っているエディタ等では WSL 上のファイル操作に対応していないことが多いです。このような場合にはネットワークドライブを割り当てることで対応します(wsl$Ubuntu-20.04 をドライブ名 Z: 等に割当てドライブ名を使用してアクセスすることが可能-但し、一部のファイル(SQLiteのDB等)はネットワークドライブでアクセスが制限されることもあるようです)
始めてみよう
準備が出来たので、とりあえずはいろいろ試してみるしかないですね
このあたりから始めるのが良さそうです
標準ライブラリ等についてはここですね
今回はここまで、まずははじめの一歩です 😄