はじめに
こんにちは.だいみょーじんです.
この記事は,自作OSアドベントカレンダー2025の19日目の記事として公開したものです.
liumOSとは
liumOSは,hikaliumさんの自作OSのひとつで,NVDIMM対応や,DNSサーバへの問い合わせやpingなどのネットワーク関連の機能を持っていることが特徴です.
今回はliumOSのビルドの自動化およびDockerとQEMUによる実行の自動化を行いました.
自動化スクリプトはこちらからご覧ください.
liumOSのビルド
以下の手順でliumOSのディレクトリツリーmntおよびそれを圧縮したliumos.zipを得ます.
/somewhere $ git clone https://github.com/TaiseiIto/liumos.git
/somewhere $ cd liumos
/somewhere/liumos $ make build
/somewhere/liumos $ ls liumos.zip
liumos.zip
/somewhere/liumos $ find mnt
mnt
mnt/lowlayers.mid
mnt/Hallelujah.mid
mnt/sleep-prep.mid
mnt/EFI
mnt/EFI/BOOT
mnt/EFI/BOOT/BOOTX64.EFI
mnt/2014-000.mid
mnt/rikoten.mid
mnt/liumos.ppm
mnt/logo.ppm
mnt/liumos.png
mnt/100.mid
mntをUSBにコピーし,そのUSBから起動すれば実機でliumOSを動かせます.
大まかなビルドの流れは以下のとおりです.
- liumOSをビルドできる環境をDockerで構築
- Dockerコンテナ内でliumOSをビルド
- ビルドされたディレクトリツリー
mntをliumos.zipに圧縮 -
mntおよびliumos.zipをDockerコンテナからホストにコピー
liumOSをビルドするコマンドはmake buildです.
では,Makefileの中身を見てみましょう.
REPOSITORY=$(shell git config --get remote.origin.url) # リモートリポジトリのURL
PRODUCT=$(shell echo $(REPOSITORY) | awk -F '[./]' '{print $$(NF-1)}') # リポジトリ名'liumos'
IMAGE=$(shell echo $(PRODUCT) | awk '{print tolower($$0)}') # Dockerイメージ名'liumos'
CONTAINER=$(IMAGE) # Dockerコンテナ名'liumos'
VNC_PORT=5905
.PHONY: build
build:
./build.sh $(IMAGE) $(CONTAINER) $(VNC_PORT) # liumOSをビルド
IMAGEとCONTAINERはそれぞれDockerのイメージ名とコンテナ名で,リポジトリ名liumosと同じになるはずです.
VNC_PORTはliumOSの実行で使用しますが,ビルド時には使わないので無視してください.
そして,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}} $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}} --filter name=^$container\$)" ]; then
docker create -i -t -p $vnc_port:$vnc_port --privileged --name $container $image /bin/bash # Dockerコンテナを作成
docker start $container
docker exec --workdir /root/liumos $container ./build_on_container.sh # Dockerコンテナ内でliumOSをビルド
docker stop $container
fi
# Extract liumOS.
if [ ! -e liumos.zip ]; then
docker cp $container:/root/liumos/liumos.zip liumos.zip # liumos.zipをホストにコピー
fi
if [ ! -d mnt ]; then
docker cp $container:/root/liumos/mnt mnt # mntをホストにコピー
fi
liumOSをビルドできる環境をDockerイメージとして構築し,それをコンテナ化し,コンテナ内でbuild_on_container.shを実行しています.
これがコンテナ内でliumOSをビルドするスクリプトで,要するにコンテナ外のbuild.shがコンテナ内のbuild_on_container.shを呼び出してビルドを依頼する形になっています.
その後ビルドされたliumOSのディレクトリツリーmntと,それを圧縮したliumos.zipを,コンテナ内からコンテナ外にコピーします.
次に,liumOSをビルドできる環境を構築する手順を記載しているDockerfileを見てみましょう.
FROM ubuntu:20.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 clang-8
RUN apt-get update && apt-get upgrade -y && apt-get install -y clang-format
RUN apt-get update && apt-get upgrade -y && apt-get install -y cmake
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 git
RUN apt-get update && apt-get upgrade -y && apt-get install -y libc++-8-dev
RUN apt-get update && apt-get upgrade -y && apt-get install -y libc++abi-8-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 lld-8
RUN apt-get update && apt-get upgrade -y && apt-get install -y netcat
RUN apt-get update && apt-get upgrade -y && apt-get install -y qemu-system-x86
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.
# Rust 1.65.0 is released on 3 November, 2022
RUN curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain=nightly-2022-11-03-x86_64-unknown-linux-gnu
# Build liumOS.
WORKDIR /root
RUN git clone https://github.com/hikalium/liumos.git # liumOSをクローン
WORKDIR /root/liumos
RUN git checkout dd619eb19c0c04504228494e8af65945ad31aa9b # 特定のコミットにチェックアウト
COPY build_on_container.sh build_on_container.sh # ビルドスクリプトを配置
COPY run_on_container.sh run_on_container.sh
# Expose VNC port.
ARG vnc_port
EXPOSE $vnc_port
liumOSはRustで書かれているので,Rustをはじめ,必要なソフトウェアをインストールしています.
その後,liumOSをクローンし,特定のコミットにチェックアウトし,ビルドスクリプトbuild_on_container.shをイメージ内に配置して環境構築は終わりです.
次にliumOSの自動ビルドの中核部分であるbuild_on_container.shを見てみましょう.
#!/bin/bash
source /root/.cargo/env
# Rust 1.65.0 is released on 3 November, 2022
rustup toolchain install nightly-2022-11-03-x86_64-unknown-linux-gnu
rustup default nightly-2022-11-03-x86_64-unknown-linux-gnu
rustup component add rust-src --toolchain nightly-2022-11-03-x86_64-unknown-linux-gnu
make tools # ツールを準備
make prebuilt # ライブラリを準備
make # liumOSをビルド
make files # ディレクトリツリーmntを作成
zip -r liumos.zip mnt # ディレクトリツリーを圧縮
Rustのツールチェーンはnightly-2022-11-03-x86_64-unknown-linux-gnuにしています.
続いてliumOSのREADMEに従い,make toolsでツールを準備,make prebuiltでライブラリを準備,makeでliumOSをビルドします.
最後にmake filesでディレクトリツリーmntを作成し,liumos.zipに圧縮します.
GitHub Actionsによるビルド
1コマンドでliumOSをビルドできるようになったので,次はGitHub Actionsを使って,ゼロコマンドでビルド済みのliumOSを取得できるようにします.
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 liumOS
run: make build
- name: Upload liumOS
uses: actions/upload-artifact@main
with:
name: liumOS
path: mnt
mainブランチにpushされた時にGitHub Actionsでmake buildを実行し,生成されたディレクトリツリーmntをアップロードしています.
これでGitHubからビルド済みのliumOSをダウンロードできるようになりました.
liumOSの実行
ビルドを自動化できたので,次はDockerコンテナ内のQEMU上でliumOSの実行を自動化してみましょう.
ありがたいことに,liumOSのリポジトリでは既にmake runでQEMU上での実行をサポートしているので,それを呼び出せばよいです.
またqemu.mkを見ると,VNCで5905番ポートにパスワードaでアクセスできるっぽいです.
PROJECT_ROOT:=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))
OVMF?=${PROJECT_ROOT}/ovmf/bios64.bin
QEMU?=qemu-system-x86_64
VNC_PASSWORD?=a # VNCパスワードはa
PORT_MONITOR?=2222
PORT_COM1?=1234
PORT_COM2?=1235
# PORT_VNC=N => port 5900 + N
PORT_OFFSET_VNC?=5 # VNCポート番号は5905
PORT_GDB?=3333
なので,MakefileにVNCポート番号を書いておいて,それをbuild.shに渡します.
VNC_PORT=5905
.PHONY: build
build:
./build.sh $(IMAGE) $(CONTAINER) $(VNC_PORT) # VNCポート番号をbuild.shに渡す
build.shはさらにDockerfileにVNCポート番号を渡します.
vnc_port=$3
# Build a docker image
if [ -z "$(docker images --format {{.Repository}} $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}} --filter name=^$container\$)" ]; then
docker create -i -t -p $vnc_port:$vnc_port --privileged --name $container $image /bin/bash
docker start $container
docker exec --workdir /root/liumos $container ./build_on_container.sh
docker stop $container
fi
これでホスト側のVNCポートとDockerコンテナ側のVNCポートを直結できました.
次に,make runでDockerコンテナ内のQEMU上でliumOSを起動できるように,Makefileにrunコマンドを記述します.
.PHONY: run
run: build
./run.sh $(CONTAINER)
run.shは,Dockerコンテナ内のrun_on_container.shを呼び出します.
#!/bin/bash
container=$1
docker stop $container
docker start $container
docker exec --workdir /root/liumos $container ./run_on_container.sh
run_on_container.shは,DockerfileでDockerイメージ内にあらかじめコピーされています.
COPY run_on_container.sh run_on_container.sh
run_on_container.shは,liumOSのMakefileを呼び出し,QEMU上でliumOSを起動します.
#!/bin/bash
source /root/.cargo/env
make run GUI=n GDB=n
これでliumOSの実行を自動化できます.
以下のコマンドにより,liumOSが起動します.
/somewhere $ git clone https://github.com/TaiseiIto/liumos.git
/somewhere $ cd liumos
/somewhere/liumos $ make run
そして,VNC viewerでlocalhost:5905にアクセスし,パスワードaでliumOSを操作できるようになります.
まとめ
- hikalium氏のliumOSのビルドを自動化
- Dockerを使うことで,1コマンドで環境構築からビルドまでできるように
- GitHub Actionsを使うことで,GitHubからビルド済みのliumOSをダウンロードできるように
- VNC通信によりDockerコンテナ内のQEMU上で動くliumOSを操作
