はじめに
こんにちは.だいみょーじんです.
この記事は,自作OSアドベントカレンダー2025の20日目の記事として公開したものです.
WasabiOSとは
WasabiOSは,hikaliumさんの自作OSのひとつです.
WasabiOSはその上でd0iasmさんによるSaBAというブラウザが動かせるという特徴を持っています.
WasabiOSとSaBAは,それぞれ作って学ぶOSのしくみおよび作って学ぶブラウザのしくみとして書籍化されており,低レイヤから高レイヤまで一気通貫で学べる優れた教材です.
今回はDockerを使ったWasabiOSおよびSaBAのビルドの自動化,およびQEMUにおける実行の自動化を行いました.
自動化スクリプトはこちらからご覧ください.
WasabiOSのビルド
以下の手順でSaBAを含むWasabiOSのディレクトリツリーmntおよびそれを圧縮したwasabi.zipを得ます.
/somewhere $ git clone https://github.com/TaiseiIto/wasabi.git
/somewhere $ cd wasabi
/somewhere/wasabi $ make build
/somewhere/wasabi $ ls wasabi.zip
wasabi.zip
/somewhere/wasabi $ find mnt
mnt
mnt/uname
mnt/httpget
mnt/sheet
mnt/dig
mnt/saba
mnt/init.txt
mnt/window2
mnt/paint
mnt/hello0
mnt/README.md
mnt/window0
mnt/EFI
mnt/EFI/BOOT
mnt/EFI/BOOT/BOOTX64.EFI
mnt/window1
mnt/hello1
mnt/loop
mnt/rev
mntをUSBにコピーし,そのUSBから起動すれば実機でWasabiOSを動かせます.
大まかなビルドの流れは以下のとおりです.
- WasabiOSをビルドできる環境をDockerで構築
- Dockerコンテナ内でWasabiOSをビルド
- ビルドされたディレクトリツリー
mntをwasabi.zipに圧縮 -
mntおよびwasabi.zipをDockerコンテナからホストにコピー
WasabiOSをビルドするコマンドはmake buildです.
では,Makefileの中身を見てみましょう.
REPOSITORY=$(shell git config --get remote.origin.url) # リモートリポジトリのURL
PRODUCT=$(shell echo $(REPOSITORY) | awk -F '[./]' '{print $$(NF-1)}') # リポジトリ名'wasabi'
IMAGE=$(shell echo $(PRODUCT) | awk '{print tolower($$0)}') # Dockerイメージ名'wasabi'
CONTAINER=$(IMAGE) # Dockerコンテナ名'wasabi'
VNC_PORT=5905
.PHONY: build
build:
./build.sh $(IMAGE) $(CONTAINER) $(VNC_PORT) # WasabiOSをビルド
IMAGEとCONTAINERはそれぞれDockerのイメージ名とコンテナ名で,リポジトリ名wasabiと同じになるはずです.
VNC_PORTはWasabiOSの実行で使用しますが,ビルド時は使わないので無視してください.
そして,build.shにDockerイメージ名とDockerコンテナ名を渡しています.
では,build.shを見てみましょう.
#!/bin/bash
image=$1 # Dockerイメージ名
container=$2 # Dockerコンテナ名
vnc_port=$3
# Build a docker image.
if [ -z "$(docker images --format {{.Repository}} | grep -x $image)" ]; then
docker build --build-arg vnc_port=$vnc_port -t $image . # Dockerイメージを作成
fi
# Create a docker container.
if [ -z "$(docker ps -a --format {{.Names}} | grep -x $container)" ]; then
docker create -i -t -p $vnc_port:$vnc_port --privileged --name $container $image /bin/bash # Dockerコンテナを作成
fi
# Start the docker container.
if [ -z "$(docker ps --format {{.Names}} | grep -x $container)" ]; then
docker start $container
docker exec --workdir /root/saba $container ./build_saba.sh # Dockerコンテナ内でSaBAをビルド
docker exec --workdir /root/wasabi $container ./build_wasabi.sh # Dockerコンテナ内でWasabiOSをビルド
docker stop $container
fi
# Extract WasabiOS
if [ ! -e wasabi.zip ]; then
docker cp $container:/root/wasabi/wasabi.zip wasabi.zip # wasabi.zipをホストにコピー
fi
if [ ! -d mnt ]; then
docker cp $container:/root/wasabi/mnt mnt # mntをホストにコピー
fi
WasabiOSをビルドできる環境をDockerイメージとして構築し,それをコンテナ化します.
build_saba.shがコンテナ内でSaBAをビルドし,build_wasabi.shがコンテナ内でWasabiOSをビルドします.
要するにコンテナ外のbuild.shがコンテナ内のbuild_saba.shおよびbuild_wasabi.shを呼び出してビルドを依頼する形になっています.
その後SaBAを含むWasabiOSのビルド済みディレクトリツリーmntと,それを圧縮したwasabi.zipを,コンテナ内からコンテナ外にコピーします.
次に,環境を構築する手順を記載しているDockerfileを見てみましょう.
FROM ubuntu:22.04
# Don't ask stdin anything to install software automatically.
ENV DEBIAN_FRONTEND=noninteractive
# Install softwares.
RUN apt-get update && apt-get upgrade -y && apt-get install -y bison
RUN apt-get update && apt-get upgrade -y && apt-get install -y build-essential
RUN apt-get update && apt-get upgrade -y && apt-get install -y clang
RUN apt-get update && apt-get upgrade -y && apt-get install -y curl
RUN apt-get update && apt-get upgrade -y && apt-get install -y dejagnu
RUN apt-get update && apt-get upgrade -y && apt-get install -y dosfstools
RUN apt-get update && apt-get upgrade -y && apt-get install -y flex
RUN apt-get update && apt-get upgrade -y && apt-get install -y git
RUN apt-get update && apt-get upgrade -y && apt-get install -y gnupg
RUN apt-get update && apt-get upgrade -y && apt-get install -y iasl
RUN apt-get update && apt-get upgrade -y && apt-get install -y libexpat-dev
RUN apt-get update && apt-get upgrade -y && apt-get install -y libgcrypt20-dev
RUN apt-get update && apt-get upgrade -y && apt-get install -y libglib2.0-dev
RUN apt-get update && apt-get upgrade -y && apt-get install -y libgmp-dev
RUN apt-get update && apt-get upgrade -y && apt-get install -y libmpfr-dev
RUN apt-get update && apt-get upgrade -y && apt-get install -y libpixman-1-dev
RUN apt-get update && apt-get upgrade -y && apt-get install -y libslirp-dev
RUN apt-get update && apt-get upgrade -y && apt-get install -y lld
RUN apt-get update && apt-get upgrade -y && apt-get install -y nasm
RUN apt-get update && apt-get upgrade -y && apt-get install -y netcat-openbsd
RUN apt-get update && apt-get upgrade -y && apt-get install -y ninja-build
RUN apt-get update && apt-get upgrade -y && apt-get install -y pkg-config
RUN apt-get update && apt-get upgrade -y && apt-get install -y python3
RUN apt-get update && apt-get upgrade -y && apt-get install -y python3-venv
RUN apt-get update && apt-get upgrade -y && apt-get install -y texinfo
RUN apt-get update && apt-get upgrade -y && apt-get install -y tmux
RUN apt-get update && apt-get upgrade -y && apt-get install -y tzdata
RUN apt-get update && apt-get upgrade -y && apt-get install -y uuid-dev
RUN apt-get update && apt-get upgrade -y && apt-get install -y vim
RUN apt-get update && apt-get upgrade -y && apt-get install -y wget
RUN apt-get update && apt-get upgrade -y && apt-get install -y zip
# Install Rust.
RUN curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh -s -- -y
# Install QEMU.
WORKDIR /root
RUN git clone --branch v8.0.0 --depth 1 --recursive --shallow-submodules --single-branch https://gitlab.com/qemu-project/qemu.git
WORKDIR qemu
RUN ./configure --enable-gcrypt --enable-slirp --target-list=x86_64-softmmu CFLAGS="-O0 -g -fno-inline" CXXFLAGS="-O0 -g -fno-inline"
RUN make
RUN make install
WORKDIR roms/edk2
RUN ./OvmfPkg/build.sh -a X64
# Install WasabiOS.
WORKDIR /root
RUN git clone https://github.com/hikalium/wasabi.git
WORKDIR wasabi
RUN git checkout 8e23542da41be26f37d52f2be1b728c06c53fffa
COPY build_wasabi.sh build_wasabi.sh
COPY run_wasabi.sh run_wasabi.sh
# Install saba.
WORKDIR /root
RUN git clone https://github.com/d0iasm/saba.git
WORKDIR saba
RUN git checkout 29b43505168a2be021509bd25061342a4fd30004
COPY build_saba.sh build_saba.sh
# Expose VNC port.
ARG vnc_port
EXPOSE $vnc_port
SaBAおよびWasabiOSはRustで書かれているので,Rustをはじめ,必要なソフトウェアをインストールしています.
その後,WasabiOSとSaBAそれぞれについて,クローン,特定のコミットへのチェックアウト,ビルドスクリプトのイメージ内への配置を行います.
次にDockerコンテナ内でSaBAをビルドするbuild_saba.shを見てみましょう.
#!/bin/bash
source /root/.cargo/env # Cargoの環境を読み込む
make # SaBAをビルドする
Cargoの環境を読み込んでmakeしているだけです.
次にDockerコンテナ内でWasabiOSをビルドするbuild_wasabi.shを見てみましょう.
#!/bin/bash
source /root/.cargo/env # Cargoの環境を読み込む
make # WasabiOSをビルドする
make run_deps # WasabiOSのディレクトリツリーをmntに作成する
cp target/x86_64-unknown-uefi/debug/os.efi mnt/EFI/BOOT/BOOTX64.EFI # WasabiOSをmnt以下にコピーする
cp /root/saba/target/x86_64-unknown-none/release/saba mnt/saba # SaBAをmnt以下にコピーする
zip -r wasabi.zip mnt # ディレクトリツリーを圧縮
Cargoの環境を読み込んでWasabiOSをビルドし,mntとしてディレクトリツリーを作成してSaBAも取り込み,wasabi.zipに圧縮しています.
GitHub Actionsによるビルド
1コマンドでWasabiOSをビルドできるようになったので,次はGitHub Actionsを使って,ゼロコマンドでビルド済みのWasabiOSを取得できるようにします.
build.ymlを見てみましょう.
name: build
on:
push:
branches:
- main
jobs:
build:
name: build
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@main
- name: Build Wasabi
run: make build
- name: Upload Wasabi
uses: actions/upload-artifact@main
with:
name: WasabiOS
path: mnt
mainブランチにpushされた時にGitHub Actionsでmake buildを実行し,生成されたディレクトリツリーmntをアップロードしています.
これでGitHubからSaBAを含むビルド済みのWasabiOSをダウンロードできるようになりました.
WasabiOSの実行
ビルドを実行できたので,次はDockerコンテナ内のQEMU上でWasabiOSの実行を自動化してみましょう.
ありがたいことに,WasabiOSのリポジトリでは既にmake runでQEMU上での実行をサポートしているので,それを呼び出せばよいです.
またMakefileを見ると,VNCで5905番ポートにパスワードwasabiでアクセスできるっぽいです.
PORT_OFFSET_VNC?=5
VNC_PASSWORD?=wasabi
なので,MakefileにVNCポート番号を書いておいて,それをbuild.shに渡します.
VNC_PORT=5905
.PHONY: build
build:
./build.sh $(IMAGE) $(CONTAINER) $(VNC_PORT)
build.shはさらにDockerfileにVNCポート番号を渡します.
vnc_port=$3
# Build a docker image.
if [ -z "$(docker images --format {{.Repository}} | grep -x $image)" ]; then
docker build --build-arg vnc_port=$vnc_port -t $image .
fi
DockerfileはVNCポートを外部に公開します.
# Expose VNC port.
ARG vnc_port
EXPOSE $vnc_port
Dockerイメージができると,今度はbuild.shでDockerコンテナ作成時にホストのVNCポートとコンテナのVNCポートを直結します.
# Create a docker container.
if [ -z "$(docker ps -a --format {{.Names}} | grep -x $container)" ]; then
docker create -i -t -p $vnc_port:$vnc_port --privileged --name $container $image /bin/bash
fi
これでホスト側のVNCポートとDockerコンテナ側のVNCポートを直結できました.
次に,make runコマンドでDockerコンテナ内のQEMU上でWasabiOSを起動できるように,Makefileにrunコマンドを記述します.
.PHONY: run
run: build
./run.sh $(CONTAINER)
run.shは,Dockerコンテナ内のrun_wasabi.shを呼び出します.
#!/bin/bash
container=$1
docker stop $container
docker start $container
docker exec --workdir /root/wasabi $container ./run_wasabi.sh
run_wasabi.shは,DockerfileでDockerイメージ内にあらかじめコピーされています.
COPY run_wasabi.sh run_wasabi.sh
run_wasabi.shは,WasabiOSのMakefileを呼び出し,QEMU上でliumOSを起動します.
#!/bin/bash
source /root/.cargo/env # Cargoの環境を読み込む
make vnc > vnc.log 2>&1 & # VNCのプロキシを起動
make run WITH_APP_BIN=/root/saba/target/x86_64-unknown-none/release/saba # SaBAを含めてWasabiOSを起動
これでWasabiOSの実行を自動化できます.
以下のコマンドにより,WasabiOSが起動します.
/somewhere $ git clone https://github.com/TaiseiIto/wasabi.git
/somewhere $ cd wasabi
/somewhere/wasabi $ make run
そして,VNC viewerでlocalhost:5905にアクセスし,パスワードaでWasabiOSを操作できるようになります.
WasabiOSの画面でsabaというコマンドを実行するとブラウザが立ち上がり,アドレスバーにURLを入力するとページが表示されます.
まとめ
- hikalium氏のWasabiOSおよびd0iasm氏のSaBAのビルドを自動化
- Dockerを使うことで,1コマンドで環境構築からビルドまでできるように
- GitHub Actionsを使うことで,GitHubからSaBAを含むビルド済みのWasabiOSをダウンロードできるように
- VNC通信によりDockerコンテナ内のQEMU上で動くWasabiOSを操作
