V言語プログラミング環境

普段は WordPress のプラグイン等を開発しているので使用言語は PHP や JavaScript なのですが、最近、コンパイル言語で小さなツールを作りたいと思い調査開始 🧐

コンパイラ言語,,,  やっぱり速いって魅力ですよね

PHPで時間のかかっているところの処理を置き換えしたいな、できれば今どきのモダンな機能を備えていて、読みやすく書きやすいシンプルな言語がいい

有名なところで、Go や Rust あたりですが、Rust は難しいと聞いていたので、まず Go を少しやってみたのですが、ちょっとしっくりこないというか…

他にないかと調べていると Nim, Zig, V 等いろいろありますね
まだあまり知られていないし、現在進行系で開発中だったりしているようですが、V言語が面白そう

V言語はまだ安定版に達してないようだけどシンプルで読みやすそうなのでちょっとやってみたいと思いDockerを使ったプログラミング環境を作ってみました

といっても既に公開されている記事を参考にちょっとカスタマイズしただけですが… 😅

VSCode による Dockerコンテナ内 V言語プログラミング環境

V言語についてはこのあたりを見てください

GitHub – vlang/v: Simple, fast, safe, compiled language for developing maintainable software. Compiles itself in <1s with zero library dependencies. Supports automatic C => V translation. https://vlang.io
Simple, fast, safe, compiled language for developing maintainable software. Compiles itself in <1s with zero library dependencies. Supports automatic C => V translation. https://vlang.io – Gi…
GitHub - vlang/v: Simple, fast, safe, compiled language for developing maintainable software. Compiles itself in <1s with zero library dependencies. Supports automatic C => V translation. https://vlang.io

この記事は、V言語のチュートリアルではありません。私もこれから始めるので良い点、悪い点等まだ判断できていない状態です 😅

とりあえず学び始める準備として手軽に実行できる環境を作成したのでその紹介です

VSCode, WSL2, Docker for Desktop 等のインストールについては、ここでは説明しません。検索すれば沢山の記事が見つかると思いますので事前にインストールを済ませてからお試しください

ダウンロード

ダウンロードボタンをクリックするとパスワード入力ページが表示されるので、パスワード( v-docker )を入力してダウンロードして下さい
パスワードを入力すると自動的にダウンロードが実行されます

ファイル構成

v_docker_vsc.zip を解凍すると下記ファイル構成に展開されます

docker-compose.ymlv_docker_vsc コンポーズファイル本体
readme.txt
readme-ja.txt
v_docker_vsc の説明書
v/
DockerfileV言語コンテナビルドファイル
app/Vプログラム作成用の共有フォルダー
hello.VVプログラムのサンプルファイル
build/デバッグビルド用ディレクトリ
.vscode/VSCode 用の設定ファイル
launch.jsonVプログラムデバッグ用 gdb 定義と起動
tasks.json Vプログラムのデバッガー起動前の gcc コンパイル定義
prettyprinter.pyVプログラムのデバッグデータ(V配列等)の表示サポート用
.devcontainer/
devcontainer.jsonコンテナ内で実行するリモート VSCode 用定義
docker-compose.ymldevcontainer の既存 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

参考

Get Started with C++ and Windows Subsystem for Linux in Visual Studio Code
Configuring the C++ extension in Visual Studio Code to target g++ and GDB on WSL installation with Ubuntu
Get Started with C++ and Windows Subsystem for Linux in Visual Studio Code
Debug C++ in Visual Studio Code
How to debug C++ programs in Visual Studio Code.
Debug C++ in Visual Studio Code
GitHub – vlang/docker
Contribute to vlang/docker development by creating an account on GitHub.
GitHub - vlang/docker
GitHub – lazalong/V-Hello: Minimal V project using VS Code, mingw gcc and gdb
Minimal V project using VS Code, mingw gcc and gdb – GitHub – lazalong/V-Hello: Minimal V project using VS Code, mingw gcc and gdb
GitHub - lazalong/V-Hello: Minimal V project using VS Code, mingw gcc and gdb
GitHub – stereobooster/v-vscode-remote-development-template
Contribute to stereobooster/v-vscode-remote-development-template development by creating an account on GitHub.
GitHub - stereobooster/v-vscode-remote-development-template

使い方

VSCode を起動して F1 キークリック(View -> Command Palette…)から Dev Containers: Open Folder in Container… を選択して v_docker_vsc フォルダーを開きます

 
初回は Docker イメージの生成でちょっと時間がかかりますが、正常に起動したら新しくターミナルを開き ctrl+shift+@
v -vgdb -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等)はネットワークドライブでアクセスが制限されることもあるようです)

始めてみよう

準備が出来たので、とりあえずはいろいろ試してみるしかないですね

このあたりから始めるのが良さそうです

v/docs.md at master · vlang/v
Simple, fast, safe, compiled language for developing maintainable software. Compiles itself in <1s with zero library dependencies. Supports automatic C => V translation. https://vlang.io – v/…
v/docs.md at master · vlang/v
V プログラミング言語 – Qiita
はじめに The V Programming Language の 公式ドキュメント の和訳だよ 情報はすべて↑から コンパイルが速いこととC/C++との相互変換がウリの模様 (C/C++ 勢なので注目せざるを得ない 2019 年 …
V プログラミング言語 - Qiita
v_by_example/jp at master · v-community/v_by_example
Learn V by Example. Contribute to v-community/v_by_example development by creating an account on GitHub.
v_by_example/jp at master · v-community/v_by_example

標準ライブラリ等についてはここですね

Standard Library documentation
Standard Library API documentation

今回はここまで、まずははじめの一歩です 😄


まとめ記事紹介

go-to-top