ぴーさんログ

だいたいXamarin.Formsのブログ

.NET for iOS / AndroidのSDKバージョンを固定する

.NET for iOS / Android、およびMAUI SDKのバージョンを制御する方法というテーマで調べた事をつらつら書いていきます。

問題

  • .NET 6以降のiOS/Android SDK管理は、基本的に最新へ上げ続ける事を要求する。
  • ついでに、Xamarin.Formsが.NET MAUIになってバージョンを下げられなくなった。

特に.NET for iOS SDKのバージョンが上がると、Xcodeのバージョンを上げないとビルドすらできなくなる場合もあり大変困ります。

主な動機

  • ビルドパイプラインのために毎回同じ環境を一から構築できるようにする。
  • セットアップ時期の違うマシンで開発環境を揃える。
  • バグのあるバージョンを避けてSDKをダウングレードする。

SDK? Workload?

さて、今回扱うSDKは、XamarinでいうとXamarin.iOS / Android SDKに当たるものです。 つまり、C#のコードをiOS/Androidアプリにするためのビルドツールや、ネイティブAPIC#ラッパーの事です。 ネイティブのAndroid SDKXcodeは含みません。(簡単のためC#前提で進めます)

.NET 6以降でこれらのSDKWorkload という仕組みでインストールされます。 Visual StudioSDKの更新サイクルを分離して、個別にアップデート出来るようにするのが大きな目的です。

dotnet workload コマンドが用意されており、例えばMAUI SDKをインストールする場合は dotnet workload install maui を実行します。 dpotnet install では常に最新のWorkloadがインストールされ、 dpotnet update でインストール済みのWorkloadを全て最新に更新します。バージョン指定するオプションはありません。

いくら何でも強気すぎますね!?

ついでに、Xamarin.FormsはただのライブラリだったのでNuGetで任意のバージョンをインストールできましたが、後継の.NET MAUIは.NET SDKの一部になってしまい開発環境で特定のバージョンを使うことになりました。 (複数のプロジェクトで違うバージョンで開発したい場合に困りますね)

アンドキュメントな方法でバージョンを指定する

実は workload installworkload update には --from-rollback-file という隠しパラメータが存在していて、所定のフォーマットのjsonファイルを与えるとバージョン指定が出来ます。

ロールバックファイルの中身はこんな感じ。

{
  "microsoft.net.sdk.maui": "7.0.52",
}

コマンド実行する時はこんな感じになります。

workload install maui --from-rollback-file rollback.json

先行事例に倣って"microsoft.net.sdk.maui"と指定しましたが、実験したところ代わりにWorkload IDの"maui"を使っても大丈夫でした。

参考

ドキュメント化しないと明言されているissue

Add workload install --from-rollback-file documentation · Issue #28226 · dotnet/docs

MAUIのバージョンを固定したいと相談しているissue

How do I Pin a Maui App to a specific version? · Issue #8985 · dotnet/maui

ロールバックファイルで指定する内容

ロールバックファイルで指定するバージョンをいくつにしたら良いか?

大抵の場合は安定環境にインストールされているメモして、それを利用するのが良さそうです。

WindowsVisual Studioをインストールした環境で dotnet workload list を実行すると以下のような結果となりました。

インストール済みワークロードの ID      マニフェストのバージョン           インストール ソース
----------------------------------------------------------------
maui-android            7.0.52/7.0.100         VS 17.4.33213.308
android                 33.0.4/7.0.100         VS 17.4.33213.308
maui-windows            7.0.52/7.0.100         VS 17.4.33213.308
maui-maccatalyst        7.0.52/7.0.100         VS 17.4.33213.308
maccatalyst             16.1.1477/7.0.100      VS 17.4.33213.308
maui-ios                7.0.52/7.0.100         VS 17.4.33213.308
ios                     16.1.1477/7.0.100      VS 17.4.33213.308

消せないWorkload

WindowsVisual Studioをインストールしている環境では関連するWorkloadをアンインストールできなくなるようです。

「インストールソース」で頭に"VS"が付いてるものをアンインストールしようとすると、見つからない(実際にはインストールされている)扱いとなるので特別扱いされます。

"ロールバックファイルで指定する内容"の状態からmauiiosのWorkloadをインストールするとこのようになります。

インストール済みワークロードの ID      マニフェストのバージョン           インストール ソース
-----------------------------------------------------------------------------
ios                     16.2.1024/7.0.100      SDK 7.0.100, VS 17.4.33213.308
maui                    7.0.59/7.0.100         SDK 7.0.100

この後、mauiiosのWorkloadをアンインストールできますが、mauiのそのままリストから消えるのに対し、iosの方は「インストールソース」の"SDK 7.0.100"部分だけ消えてWorkload自体は残ります。

CIなどを考える場合、WindowsではVisual Studioをインストールせずに.NET SDK、Workloadだけを直接インストールしたビルド専用マシンにするのが良さそうです。

Workload Manifest

WorkloadのIDやバージョンはWorkload Manifestに定義されています。 ManifestファイルはNuGetで配布されていて、dotnetコマンドをこれを自動的に取得して有効なIDか、新しいバージョンがあるかなどを判別しているようです。

nuget.orgで"Microsoft.NET.Sdk"と検索するとそれらしいのが沢山ヒットします。

命名規則は次のようになっているようで……

Microsoft.NET.Sdk.<プラットフォーム>.Manifest-<.NET SDKバージョン>

.NET 7.0.1xx系で利用可能なiOS開発用SDKの場合は Microsoft.NET.Sdk.iOS.Manifest-7.0.100 という具合ですね。

中身には WorkloadManifest.json という名前でマニフェストファイルの実体が入っています。

おわり

  • ビルド専用: ロールバックファイルを利用すれば固定SDKバージョンで再現性のある環境構築が出来そう。
  • 開発用(Windows): Visual StudioのせいでSDKバージョンをコントロール出来ないので、最新に追従し続ける事になりそう。
  • 開発用(Mac): ロールバックファイルでバージョン指定できるが、VS for Macが利用しているXamarin SDKとバージョンが乖離していくと問題が発生しそう。(どうやらデバッグ実行でiOSシミュレータ起動したりはXamarin SDK側の機能っぽい)
  • Xamarin用の開発環境と.NET 6+用の開発環境は分けた方が安全そう。