はじめに
第1弾では、Rust 製描画エンジンを Windows / macOS / Linux × x86_64 / ARM64 の6通りで実機ビルドする構成を紹介しました。今回はその中の ARM について、Windows ARM 機1台で Windows ARM64 と Linux ARM64 の両方を開発・検証できた話です。
Windows ARM 機は、まだそれなりの価格ですね。それでも購入を決めたのは、某プロジェクトで NVIDIA DGX Spark(ARM64 の Linux)でVsCode をつかい、Rustの開発を行っていましたが、ソースコード印刷や、MDで作成した仕様書をPDF作成を行うことがきっかけでした。
そのためには高価なDGXではなく、ARM64(Snapdragon X)実機が必要になり、まずは ARM Windowsを購入、そして WSL2 を利用しARM Linux の開発を開始しました。
今後のなかでおすすめしたのでは、やはりARM64でもメモリは最低32GBは必要だとおもいます。
1. 構成:Windows ARM 機1台で Windows と Linux ARM64 の両方を開発する
Windows ARM 機に WSL2 で Ubuntu を入れると、その Ubuntu も ARM64 で動きます。1台で Windows ARM64 と Linux ARM64 の両方を、クロスコンパイルなしに実機と同じ条件でビルド・検証できます。
- ソースはそれぞれに clone する:Windows 側と WSL の Ubuntu 側に別々に GitHub から clone します。別のマシンと同じ扱いで、作業前に Pull、作業後に Push です
- PowerShell 7 も両方に入れる:Ubuntu ARM64 には GitHub Releases の arm64 版 .deb があります。同じ .ps1 が Windows と Ubuntu の両方で動きます
- VS Code は Windows 側から WSL に接続する:WSL 拡張を入れると、Windows の VS Code の画面のまま Ubuntu 側のフォルダを開き、ターミナルも Ubuntu で動きます
- Ubuntu の GUI も開ける:WSL の Ubuntu ARM64 に xrdp を入れ、Windows のリモートデスクトップ接続からつなぐと、Ubuntu のデスクトップ画面がそのまま開きます。別の Linux 機を用意しなくても、同じマシンの中で Linux の画面を見ながら検証できます
2. Ubuntu ARM64 で C# のデスクトップアプリが動いた
ここが今回いちばん伝えたいことです。この WSL の Ubuntu ARM64 のデスクトップ上で、次の流れが最後まで通りました。
- C# で作ったデスクトップアプリが起動する
- そのアプリで HTML を保存する
- Ubuntu にインストールした Chrome で、保存した HTML の内容を確認する
- ファイルの保存まで問題なくできる
Windows ARM 機1台の中で、Linux ARM64 向けの C# アプリを「起動 → 出力 → ブラウザで確認 → 保存」まで実際に試せたことになります。ARM64 の Linux でここまで通して確認した例は、まだほとんど見かけません。これから ARM 向けにアプリを作る人には、「実機が Windows ARM 機1台あれば、Linux ARM64 の GUI アプリまで検証できる」というのは大きい点だと思います。
3. 開発を支えた PowerShell 7
Windows と WSL の Ubuntu で同じスクリプトを動かせたのは、両方に同じバージョンの PowerShell 7 を入れていたからです。ただ最初は、PowerShell を「Windows に入った Unix シェルのようなもの」だと思い込んでいました。bash 用に書かれた Git のコマンドを貼り付けて動かなかったことで、設計思想から別物だと気づきました。以下、その要点です。
3-1. Windows PowerShell 5.1 と PowerShell 7 は別物
Windows に最初から入っているのは Windows PowerShell 5.1 です。PowerShell 7 は別にインストールする別製品で、両方を並べて入れられます。
| 項目 | Windows PowerShell 5.1 | PowerShell 7 |
|---|---|---|
| 起動コマンド | powershell.exe |
pwsh |
| 対応 OS | Windows のみ | Windows / macOS / Linux |
| 土台 | .NET Framework | .NET(7.6 は .NET 10) |
| 入手 | Windows に標準で同梱 | 別途インストール |
ターミナルに「PowerShell」と表示されていても、どちらが動いているかは分かりません。次の1行で確認できます。
$PSVersionTable.PSVersion
メジャーバージョンが 5 なら Windows PowerShell、7 なら PowerShell 7 です。
3-2. 全マシンで同じバージョンにそろえる理由
第1弾では、skia-safe や .NET などのバージョンを全マシンでそろえる方針を取りました。シェルも同じです。
- スクリプトを書き分けなくて済む:ビルドやパッケージ作成の手順を PowerShell で書けば、Windows 用の .ps1 と Mac/Linux 用の .sh を二重に保守しなくて済みます
- 「このマシンだけ動かない」を減らせる:5.1 と 7 では、既定の文字コードや外部コマンドへの引数の渡し方が違います(後述)
- LTS にそろえておけば更新の間隔が長い:現在の LTS は PowerShell 7.6 です。2026年3月18日リリース、サポートは2028年11月14日までです(Microsoft Learn)
ひとつ前の LTS である 7.4 と、7.5 は、2026年11月10日でサポートが終わります。今から入れるなら 7.6 にそろえるのが素直です。
3-3. 各 OS への導入
| OS | 入れ方 | 備考 |
|---|---|---|
| Windows(x64 / ARM64) | winget install --id Microsoft.PowerShell --source winget |
5.1 はそのまま残り、並んで入る |
| macOS(Apple Silicon / Intel) | 公式の .pkg、または brew install powershell
|
--cask 版は 7.6 から使われなくなった |
| Linux(Ubuntu) | Microsoft のパッケージリポジトリを登録して apt、または .deb | ARM64 は GitHub Releases の .deb |
macOS の Homebrew には注意が必要です。以前の定番だった brew install --cask powershell は、7.6 から正しいバージョンが入らなくなっています。現在の Homebrew 版はコミュニティが管理し、ソースからビルドする形です(Microsoft Learn)。他のマシンと全く同じ Microsoft 製のバイナリにそろえたいなら、GitHub Releases の .pkg が確実です。
入れたら、全マシンで同じコマンドを実行してバージョンを比べます。
pwsh -NoProfile -Command '$PSVersionTable.PSVersion.ToString()'
VS Code を使う場合は、統合ターミナルの既定も PowerShell 7 にしておきます。Windows では「PowerShell」という名前のプロファイルが 5.1 を指していることがあるので、開いたターミナルで上のコマンドを一度打って確認します。
3-4. Unix シェルとの最大の違い:パイプを流れるのはオブジェクト
bash のパイプを流れるのは文字列です。PowerShell のパイプを流れるのは .NET のオブジェクトです。見た目は似ていても、ここが根本的に違います。
CPU 使用量の多いプロセス上位5件を表示する例で比べます。
# bash:文字列を列番号で切り出す
ps aux | sort -k3 -nr | head -5
# PowerShell:プロパティ名で並べ替えて選ぶ
Get-Process | Sort-Object CPU -Descending | Select-Object -First 5 Name, CPU
bash では「3列目が CPU」という出力の見た目に依存します。出力形式が OS やオプションで変わると壊れます。PowerShell では CPU という名前で値を取るので、表示の形に左右されません。
帳票の JSON を扱うときも、この違いが効きます。jq のような外部ツールを入れなくても、JSON をそのままオブジェクトとして読めます。
# データファイルから、使うテンプレート名と明細件数を取り出す
$d = Get-Content invoice.data.json -Raw | ConvertFrom-Json
$d.Parameters.TemplateFile
$d.Parameters.Data.Count
逆に言えば、grep や awk で文字列を削っていく bash の発想のまま PowerShell を使うと、かえって遠回りになります。「まずオブジェクトとして取り、プロパティで絞る」と考えるのがコツです。何のプロパティがあるかは Get-Member で調べられます。
Get-Process | Get-Member -MemberType Property
3-5. ハマりどころ
-
bash の書き方はそのまま動かない:環境変数を付けて実行する
NAME=1 commandは PowerShell では通らず、$env:NAME = '1'と別行で設定します。行の継続も\ではなくバッククォート(`)です -
bash 用の手順は Git Bash で実行する:
git filter-branchのように、中で sh のスクリプトを渡すコマンドは、無理に PowerShell 用に書き換えず Git Bash で動かすのが安全です。ネット上の Git の手順は、ほとんどが bash 前提で書かれています -
&&と||は 7 から:「成功したら次を実行」の&&は PowerShell 7 で使えますが、5.1 では構文エラーです。同じスクリプトがマシンによって動いたり動かなかったりする典型例です -
lsやcatの正体が OS で違う:Windows では PowerShell のコマンドの別名ですが、macOS / Linux の PowerShell 7 では別名がなく、OS のlsやcatが動きます。ls -laは Linux では動き、Windows ではエラーになります。共通スクリプトではGet-ChildItemのように正式名で書きます -
文字コードの既定が違う:5.1 の
>は UTF-16LE、Set-Contentはシステムの既定(日本語環境では Shift_JIS)で書きます。7 は BOM なし UTF-8 が既定です。5.1 で書き出した JSON を他の OS で読むと文字化けの原因になります -
外部コマンドへの引数の渡し方:二重引用符を含む引数を
gitなどの外部コマンドへ渡すとき、5.1 は引用符を落としてしまうことがありました。7.3 以降で改善されています。これも「全マシン 7 にそろえる」理由のひとつです
4. まとめ
- Windows ARM 機に WSL2 で Ubuntu を入れれば、Windows ARM64 と Linux ARM64 の両方を1台で開発・検証できる
- xrdp で Ubuntu の GUI を開き、C# のデスクトップアプリの起動から HTML 保存、Chrome での確認まで Linux ARM64 上で通せた
- それを支えたのが、全マシンにそろえた PowerShell 7。Unix シェルの移植ではなく、パイプをオブジェクトが流れる別物として使う
- bash 前提の手順は Git Bash、OS をまたぐ自分のスクリプトは PowerShell 7 と使い分ける