🐙

言語/ツールのコンパイルをしてみた。

に公開
  • ruby
  • C
  • go
  • python
  • rust
  • nginx
  • node

実行環境

  • dockerコンテナ

今回は、簡素なDockerfileを用いて学習。
使用するOSはubuntu。

目的

いつも私たちが何気なく使っているコンテナ。
私たちが使ってるイメージ、中身がどうなってるか知りたくない?という衝動で始めました。
ということで、さっそく下記のコンパイル用のイメージを作成。

各言語コンパイル

rubyコンパイル

FROM ubuntu:24.04

# Rubyメジャー・マイナーバージョン
ENV RUBY_SERIES=3.4
# Rubyバージョン
ENV RUBY_VERSION=3.4.3

# 必要なパッケージのインストール
RUN apt-get update && apt-get install -y \
  git \
  build-essential \
  autoconf \
  bison \
  libssl-dev \
  libreadline-dev \
  zlib1g-dev \
  libyaml-dev \
  libffi-dev \
  wget \
  tar \
  rustc \
  && rm -r /var/lib/apt/lists/*

# Rubyソースの取得ビルド
WORKDIR /usr/src
RUN wget https://cache.ruby-lang.org/pub/ruby/${RUBY_SERIES}/ruby-${RUBY_VERSION}.tar.gz && \
  tar -xvzf ruby-${RUBY_VERSION}.tar.gz && \
  cd ruby-${RUBY_VERSION} && \
  ./configure --prefix=/opt/ruby-${RUBY_VERSION} --enable-shared --disable-install-doc --enable-yjit && \
  make -j$(nproc) && \
  make install

# PATHを通す
ENV PATH="/opt/ruby-${RUBY_VERSION}/bin:$PATH"

# 確認
CMD ["ruby", "-v"]

Cコンパイル

FROM ubuntu:24.04

RUN apt-get update && apt-get install -y \
  build-essential \
  && rm -rf /var/lib/apt/lists/*

CMD ["gcc", "--version"]

goコンパイル

FROM ubuntu:24.04

RUN apt-get update && apt-get install -y \
  wget \
  tar \
  build-essential \
  && rm -rf /var/lib/apt/lists/*

ENV GO_VERSION=1.22.3

WORKDIR /usr/src

RUN wget https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz && \
  tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz && \
  rm go${GO_VERSION}.linux-amd64.tar.gz

ENV PATH="/usr/local/go/bin:$PATH"

CMD ["go", "version"]

pythonコンパイル

FROM ubuntu:24.04

RUN apt-get update && apt-get install -y \
  build-essential \
  libssl-dev \
  zlib1g-dev \
  libncurses5-dev \
  libsqlite3-dev \
  libreadline-dev \
  libffi-dev \
  wget \
  && rm -rf /var/lib/apt/lists/*

# Pythonビルド
ENV PYTHON_VERSION=3.12.3
WORKDIR /usr/src
RUN wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tgz && \
  tar -xzf Python-${PYTHON_VERSION}.tgz && \
  cd Python-${PYTHON_VERSION} && \
  ./configure --enable-optimizations && \
  make -j$(nproc) && \
  make altinstall

ENV PATH="usr/local/bin:$PATH"
CMD ["/usr/local/bin/python3.12", "--version"]

rustコンパイル

FROM ubuntu:24.04

RUN apt-get update && apt-get install -y \
  curl \
  ca-certificates \
  && rm -rf /var/lib/apt/lists/*

ENV RUST_VERSION=1.77.2
RUN curl -O https://static.rust-lang.org/dist/rust-${RUST_VERSION}-x86_64-unknown-linux-gnu.tar.gz && \
  tar -xzf rust-${RUST_VERSION}-x86_64-unknown-linux-gnu.tar.gz && \
  cd rust-${RUST_VERSION}-x86_64-unknown-linux-gnu && \
  ./install.sh --prefix=/usr/local

# パスを通す(通常 /usr/local/bin に入るが念のため)
ENV PATH="/usr/local/bin:$PATH"

CMD ["rustc", "--version"]

nginxコンパイル

FROM ubuntu:24.04

RUN apt-get update && apt-get install -y \
  build-essential \
  libpcre3-dev \
  zlib1g-dev \
  libssl-dev \
  curl \
  && rm -rf /var/lib/apt/lists/*

WORKDIR /usr/src

ENV NGINX_VERSION=1.26.0

RUN curl -O http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
  tar -xzf nginx-${NGINX_VERSION}.tar.gz && \
  cd nginx-${NGINX_VERSION} && \
  ./configure --prefix=/usr/local/nginx \
  --with-http_ssl_module && \
  make -j$(nproc) && \
  make install

ENV PATH="/usr/local/nginx/sbin:$PATH"

CMD ["nginx", "-v"]

nodeコンパイル

FROM ubuntu:24.04

ENV NODE_VERSION=v22.15.1

RUN apt-get update && apt-get install -y \
  curl \
  tar \
  ca-certificates \
  && rm -rf /var/lib/apt/lists/*

WORKDIR /usr/src

RUN curl -fsSLO https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-x64.tar.gz \
  && tar -xzf node-${NODE_VERSION}-linux-x64.tar.gz \
  && mv node-${NODE_VERSION}-linux-x64 /opt/nodejs \
  && ln -s /opt/nodejs/bin/node /usr/local/bin/node \
  && ln -s /opt/nodejs/bin/npm /usr/local/bin/npm

# 動作確認
CMD ["node", "--version"]

学んだこと

各言語の形式

言語 実行方式 コンパイルツール コンパイルタイミング
ruby バイトコード解釈実行 Ruby本体(MakefileはRuby自体のビルド用) 実行時に自動コンパイル
python バイトコード解釈実行 Python自体(バイトコード生成機能) 実行時に自動コンパイル
C 事前に機械語へコンパイル gcc等 実行前に明示的にコンパイル
Go 事前に機械語へコンパイル Goツールチェーン内 実行前に明示的にコンパイル

(rustはいいや。。。)

makeコマンドの一連の流れ

makeコマンドは言語のコードファイルをビルドするためのツール。
./configureによって、makefileを作成。
make -j$(nproc)によって、指定プロセッサによるmakefile実行。
installという命名だが、単にファイルをこぴっているだけ。(ややこし)

よくaptやrbenv, nvmで楽々インストール!している中ではこうなってたのか!という感情発見

地道に、curlやwgetで引っ張って、パイプでスクリプト実行したり、ファイルを解凍して地道にmakeコマンドでビルドしているのか。という感情を得ました。

FHSの概念

ubuntuのファイル構成、どうなってるんだろ。とだれもが思うはず。
このあたりの理解が深まった感覚はあります。
/optと、usr/local, usr/src 皆さんお答えできますか?
ノック練習をしてみるといいです。

おまけ

GoやSwift、Rustは自己ホスティング化というのを使っているらしい。
ここがとんでもなく難しいらしく、私は事故しそうなのでそっと閉じておきました。
あと、依存関係回り、難しすぎて、コツコツ日々学習が必要な項目だと思いました。

Discussion