2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

apple/containerでEmbedded Swiftをビルドする

2
Posted at

はじめに

apple/container の 1.0.0 がリリースされました🎉
せっかくなので、apple/container 上で Embedded Swift をビルドしてみました

ホスト PC に Swift toolchain も Pico SDK も ARM GCC も入れずに、Raspberry Pi Pico (RP2040) 向けのファームウェアをビルドできます
VS Code Remote-SSH との組み合わせで、コンテナ内の sourcekit-lsp によるリアルタイム診断もできます

リポジトリはこちらです

ビルドする

1. コンテナを起動する

git clone https://github.com/fuziki/apple-container-embedded-swift.git
cd apple-container-embedded-swift

container system start
bash scripts/start-dev-container.sh

初回はイメージのビルドで数分かかります
完了すると ~/.ssh/config に追加するブロックが表示されるので追記します

2. VS Code で接続する

コマンドパレットから Remote-SSH: Connect to Host → pico-dev を選択します
フォルダは /work を開きます(リポジトリがマウントされています)
Swift 拡張(swiftlang.swift)のインストールを促されたらコンテナ側に入れます

3. Configure する

VS Code のターミナルで実行します

bash scripts/configure.sh

ウィンドウを再読み込みすると Main.swift のリアルタイム診断が有効になります

4. ビルドする

Cmd+Shift+B でビルドが走り、./out/swift-app.uf2 が生成されます

5. Pico に書き込む

BOOTSEL を押しながら USB 接続すると RPI-RP2 としてマウントされます
./out/swift-app.uf2 をドラッグすれば LED が点滅します

実装のポイント

Dockerfile

FROM swiftlang/swift:nightly-noble

RUN apt-get update && apt-get install -y --no-install-recommends \
        git ninja-build python3 build-essential \
        gcc-arm-none-eabi libnewlib-arm-none-eabi \
        openssh-server \
    && rm -rf /var/lib/apt/lists/*

RUN pip3 install --break-system-packages cmake

RUN git clone --depth 1 https://github.com/raspberrypi/pico-sdk.git /opt/pico-sdk \
    && cd /opt/pico-sdk && git submodule update --init
ENV PICO_SDK_PATH=/opt/pico-sdk

ベースイメージは nightly を使う

Embedded Swift は Development Snapshot(nightly)の toolchain でしか使えません
release イメージには含まれていないので swiftlang/swift:nightly-noble を使います

CMake は pip で入れる

Ubuntu の apt 版は 3.28 以下で、Embedded Swift のビルドには 3.29 以上が必要です
pip で最新版を入れることで回避しています

Pico SDK はイメージに焼き込む

ホストに clone せず、 /opt/pico-sdk に入れているので、ホスト側には何も残りません

container exec を SSH の ProxyCommand にする

apple/container は今のところ TCP ポートフォワーディングに制限があります
container exec を ProxyCommand にすることで、ポートを開けずに VS Code からコンテナに接続できます

Host pico-dev
    HostName pico-dev
    User root
    ProxyCommand container exec -i pico-dev /usr/sbin/sshd -i

sshd -i は inetd モードで、ポートのかわりに stdin/stdout を通信路として使います
コンテナを再作成しても IP が変わっても、この設定のまま繋がります

VS Code 拡張をホスト側にキャッシュする

コンテナを再作成するたびに拡張をインストールし直すのは手間です
/root/.vscode-server をホスト側にマウントしてキャッシュしています

--volume "$HOME/.cache/pico-dev/vscode-server:/root/.vscode-server"

BridgingHeaderLSP.h:sourcekit-lsp 向けのスタブヘッダ

Pico SDK のヘッダは ARM 向けクロスコンパイラを前提としていて、sourcekit-lsp の内部 clang ではパースエラーになります
そこで Swift から呼ぶ C の関数だけを宣言したスタブを用意して、診断はそちらで行います

// firmware/BridgingHeaderLSP.h
#pragma once
#include <stdint.h>
#include <stdbool.h>
typedef unsigned int uint;
void sleep_ms(uint32_t ms);
void gpio_init(uint gpio);
void gpio_set_dir(uint gpio, bool out);
void gpio_put(uint gpio, bool value);

実際のビルドは本物の SDK ヘッダを使うので、スタブとシグネチャが食い違えばビルドが落ちます

CMakeLists.txt:Embedded Swift に必要な設定

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_Swift_COMPILER_TARGET armv6m-none-none-eabi)

# Pico SDK のデフォルトオプションが Swift のコンパイルを邪魔するので退避する
set_target_properties(pico_standard_link PROPERTIES INTERFACE_COMPILE_OPTIONS "")
target_compile_options(pico_standard_link INTERFACE
    "$<$<COMPILE_LANGUAGE:C>:SHELL: -ffunction-sections -fdata-sections>")

# ARM GCC の暗黙インクルードパスを Swift コンパイラに渡す
set(SWIFT_INCLUDES)
foreach(dir ${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES})
    string(CONCAT SWIFT_INCLUDES ${SWIFT_INCLUDES} "-Xcc -I${dir} ")
endforeach()

target_compile_options(swift-blinky PUBLIC "$<$<COMPILE_LANGUAGE:Swift>:SHELL:
    -enable-experimental-feature Embedded
    -target armv6m-none-none-eabi
    -Xcc -mfloat-abi=soft -Xcc -fshort-enums
    -Xfrontend -function-sections
    -import-bridging-header ${CMAKE_CURRENT_LIST_DIR}/BridgingHeader.h
    ${SWIFT_INCLUDES} >")

-enable-experimental-feature Embedded で Embedded モードを有効化します

Pico SDK の C ヘッダを Swift に見せるには、ARM GCC の暗黙インクルードパスを -Xcc -I でひとつずつ渡す必要があります
CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES をループで取り出しているのはそのためです

CMAKE_EXPORT_COMPILE_COMMANDS ON で出力した compile_commands.json を sourcekit-lsp が読むことで、リアルタイム診断が実際のビルドと同じコンパイラ引数で動きます

Docker と比較して

apple/container は Dockerfile がそのまま使えるので、今回のイメージも Docker で書く場合と同じ書き方で済みました

個人的には動作も Docker Desktop より軽い印象を受けました

一方で TCP のポートフォワーディングはまだ弱く、今回 VS Code を container exec 経由で繋ぐひと手間が必要そうでした

今回のような単一コンテナでビルドする用途には十分でした

おわりに

apple/container を使って、ホスト PC に何もインストールせずに Embedded Swift のビルド環境を作れました
Dockerfile でビルド環境をコード化できるのは Docker と同じ感覚で良いですね

今回は Lチカで止まっていますが、次はもう少し実用的なファームウェアを書いてみたいと思います

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?