Open70

[WIP] .NET MAUI で Linux 向けにビルドしたい!

Lemon73Lemon73

.NET MAUI とは

.NET MAUI は C# と XAML により GUI クロスプラットフォームアプリケーションを開発できるフレームワークです。

Linux 版

しかし、登場した当時は Linux 版はコミュニティーによる開発扱いで、現在はもはや Linux の存在は公式ドキュメントから削除されています。

現在のコミュニティーによる開発は、ほとんど停滞しており、その開発の情報はほとんど存在しません。

この資料の目的

この資料は .NET MAUI を Linux でなんとか利用できないか試み、情報をある程度まとめたものです。

Lemon73Lemon73

参考になるページ

リポジトリ

下2つは、#66などで maui-linux にマージしています。このリポジトリの保持者である jsuarezruiz さんも今はあんまり MAUI Linux の開発に関与していないようです。

コミュニティー

本家の wiki でも強い要望があるにも関わらず、MS やコミュニティーによる開発はあまり進んでいません。(というか、興味がある人は多いけど、開発資料が少なすぎて途中参入が難しいんだとおもう)

(まじで情報がない)

Lemon73Lemon73

ビルドとかの方針

これは指針であり、まだ成功はしていません。

A 案

Windows や macOS 上でのビルドと同様に行う (多分無理)

-> Linux 上では maui-android 以外使えないので、ビルドエラーが大量に出る

B 案

CI/CD を参考にする

-> gtk のワークロードがインストールできない

C 案

CI/CD をそのまま動かす (一番希望がある)

-> GitHub Actions をローカルで動かせる act でできるかも (現状若干エラーが出てるけど頑張れば行けるか?)
-> B 案と同じで、gtk のワークロードのインストールができない

D 案

自作する (無理)

-> Qt (Qt.NET (古い)) で作るとか (ライセンス的 (GPL/LGPL) に微妙な気がする)

結論

とりあえず C 案で進めます。B 案でもほぼ同じな気がしますが… (資料作るときは B 案のほうが act とか入れなくてもいいので、簡単になりそう)

Lemon73Lemon73

雑に URL を貼る

CI/CD はバージョンがかなり古いので、すべて v4 に上げるのを推奨するよ

Lemon73Lemon73

ぶっちゃけ、あと gtkのワークロードさえインストールできれば動きそうな雰囲気ある

あと、多分これ (GTK ランタイム) は必要かも

sudo apt install libgtk-3-dev
sudo apt install libgtk-3-0
Lemon73Lemon73

一応 act の使い方をちょっと書いておく

  1. gh (GitHub CLI) をインストール
# 参考: https://github.com/cli/cli/blob/trunk/docs/install_linux.md
(type -p wget >/dev/null || (sudo apt update && sudo apt-get install wget -y)) \
	&& sudo mkdir -p -m 755 /etc/apt/keyrings \
	&& wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \
	&& sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
	&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
	&& sudo apt update \
	&& sudo apt install gh -y
  1. act をインストール
# 参考: https://nektosact.com/installation/gh.html
gh extension install https://github.com/nektos/gh-act
  1. Docker をインストール (よくわからないけど apt で全部入れた (?) 多分 docker.io があればいいのかな?)
  2. Docker 起動
sudo systemctl start docker
  1. ユーザーを docker グループに追加 (docker はスーパーユーザーが必要)
sudo usermod -aG docker $USER
newgrp docker # 変更を適応
  1. リポジトリをクローンして、その中に移動する
git clone <リポジトリの URL>
cd <クローンしたディレクトリ>
  1. 動かす
# 場所は、リポジトリのルートで実行 (ルートじゃなくても動くかも?)
gh act push
# または gh act pull_request かな?
Lemon73Lemon73
# 参考: https://github.com/HavenDV/Gtk
curl -sSL https://raw.githubusercontent.com/HavenDV/Gtk/main/scripts/workload-install.sh | sudo bash

これやって、0.2.0はありません的なエラーが出たから、他の方法を試した。
(インストールできてないから dotnet workload list には入らないけど、dotnet workload search には入る)

# 参考: https://matrix.to/#/!secXICyMCTWkttVBVC:matrix.org/$uX3kcJ_AITkkFvqne3wkFJUL4_77o_FpfpGos6RphzI?via=matrix.org
git clone https://github.com/GtkSharp/GtkSharp.git
cd GtkSharp
sudo dotnet tool restore
sudo dotnet cake --BuildTarget=InstallWorkload 

これやって、

# (これは元からインストールしてたっぽいから関係ない気がするけど)
sudo apt-get install libgtk-3-0

これやった後に、もう一度 sudo dotnet workload install gtk したら、なぜかインストールできた

$ dotnet workload list

インストール済みワークロードの ID      マニフェストのバージョン        インストール ソース 
-------------------------------------------------------
gtk                     0.2.0/8.0.400       SDK 8.0.400
maui-android            8.0.82/8.0.100      SDK 8.0.400
wasi-experimental       8.0.8/8.0.100       SDK 8.0.400

`dotnet workload search` を使用して追加ワークロードを検出し、インストールします。
Lemon73Lemon73

(なんか gtk が入った後も、結構苦難している)

Lemon73Lemon73

色んな方法を試しすぎてて、再現性が低くなりそう (あとでちゃんとした資料を書くつもり (これが本来の目的だし))

Lemon73Lemon73
# 参考: https://github.com/jsuarezruiz/maui-linux/issues/92#issuecomment-2311842074
dotnet cake --target=dotnet-pack --workloads=global --gtk
dotnet new install ./artifacts/*.nupkg

を試して、

# (Nuget.config)
- <!-- <add key="local" value="artifacts" /> -->
+ <add key="local" value="artifacts" />

にした。

この後にビルドをすると、Gtk あたりのエラーは解消されたっぽい。

だけど、次のエラーが発生した。

Lemon73Lemon73

System.Text.Json 8.0.0 が致命的な脆弱性があるというエラーが発生するので、バージョンを上げる。

eng/Versions.props
# $(MicrosoftNETCoreAppRefPackageVersion) は8.0.0になってる
- <SystemTextJsonPackageVersion>$(MicrosoftNETCoreAppRefPackageVersion)</SystemTextJsonPackageVersion>
+ <SystemTextJsonPackageVersion>8.0.4</SystemTextJsonPackageVersion>

ついでに、ビルドターゲットの出力形式を変更する

Maui.Controls.Sample.csproj
- <!-- <OutputType Condition="$(TargetFramework.Contains('-'))">Exe</OutputType> -->
+ <OutputType>Exe</OutputType>
Lemon73Lemon73

規定に関しても設定しなきゃいけないらしい

launchSettings.json
{
  "profiles": {
    "Windows Machine": {
      "commandName": "MsixPackage",
      "nativeDebugging": true
+   },
+   "Linux Machne": {
+     "commandName": "Project",
+     "nativeDebugging": true
    }
  }
}

あと、Main() が必要らしい (Windows とかは /Platforms/Windows/App.xaml.cs とかを参照にしてるっぽい)

MauiProgram.cs
public static void Main()
{
  CreateMauiApp();
}
Lemon73Lemon73
dotnet run --project src/Controls/samples/Controls.Sample/Maui.Controls.Sample.csproj --framework net8.0

まだエラーは続く…

Unhandled exception. Microsoft.Maui.ApplicationModel.NotImplementedInReferenceAssemblyException: This functionality is not implemented in the portable version of this assembly. You should reference the NuGet package from your main application project in order to reference the platform-specific implementation.
   at Microsoft.Maui.ApplicationModel.AppInfoImplementation.get_PackagingModel() in /home/lemon73/Downloads/maui-linux/src/Essentials/src/AppInfo/AppInfo.netstandard.cs:line 19
   at Microsoft.Maui.ApplicationModel.AppInfo.get_PackagingModel() in /home/lemon73/Downloads/maui-linux/src/Essentials/src/AppInfo/AppInfo.shared.cs:line 106
   at Maui.Controls.Sample.MauiProgram.<>c.<CreateMauiApp>b__4_3(IEssentialsBuilder essentials) in /home/lemon73/Downloads/maui-linux/src/Controls/samples/Controls.Sample/MauiProgram.cs:line 191
   at Microsoft.Maui.Hosting.EssentialsExtensions.EssentialsRegistration.RegisterEssentialsOptions(IEssentialsBuilder essentials) in /home/lemon73/Downloads/maui-linux/src/Core/src/Hosting/EssentialsMauiAppBuilderExtensions.cs:line 110
   at Microsoft.Maui.Hosting.EssentialsExtensions.EssentialsInitializer.Initialize(IServiceProvider services) in /home/lemon73/Downloads/maui-linux/src/Core/src/Hosting/EssentialsMauiAppBuilderExtensions.cs:line 131
   at Microsoft.Maui.MauiContextExtensions.InitializeAppServices(MauiApp mauiApp) in /home/lemon73/Downloads/maui-linux/src/Core/src/MauiContextExtensions.cs:line 84
   at Microsoft.Maui.Hosting.MauiAppBuilder.Build() in /home/lemon73/Downloads/maui-linux/src/Core/src/Hosting/MauiAppBuilder.cs:line 159
   at Maui.Controls.Sample.MauiProgram.CreateMauiApp() in /home/lemon73/Downloads/maui-linux/src/Controls/samples/Controls.Sample/MauiProgram.cs:line 304
   at Maui.Controls.Sample.MauiProgram.Main() in /home/lemon73/Downloads/maui-linux/src/Controls/samples/Controls.Sample/MauiProgram.cs:line 48

Essentials でエラーが発生してるっぽいけど…なにこれ…

Lemon73Lemon73

よくわからないので、一旦 Microsoft.Maui-vscode.sln のビルドを目指すことに。

Lemon73Lemon73
$ dotnet run --project  Microsoft.Maui-vscode.sln 
# (エラー文)
/usr/share/dotnet/sdk/8.0.401/NuGet.targets(174,5): error : 無効なフレームワーク識別子 ''。 [/home/lemon73/Downloads/maui-linux/Microsoft.Maui-vscode.sln]

ビルドに失敗しました。ビルド エラーを修正して、もう一度実行してください。
Lemon73Lemon73

global.json

{
  "sdk": {
    "version": "8.0.401"
  }
}

を追加するも、特に効果はなし。

Lemon73Lemon73

調節したら、別のエラーになった…
結局よくわからない

困難を極める

Lemon73Lemon73

ついに動きました。詳しくは後で書きます。

おそらく、src/Controls/samples/Controls.Sample.Gtk/Controls.Sample.Gtk.csproj は動くので、

dotnet run --project src/Controls/samples/Controls.Sample.Gtk/Controls.Sample.Gtk.csproj --framework net8.0-gtk にします。

(画像の調整の必要あり (後で書く予定))

でも、UI が崩壊しているね… (おそらく仕様)

Lemon73Lemon73

UI が崩壊しているのは、自分の Linux (KDE neon) 内で規定している GTK のテーマカラーが影響しているんじゃないかと思い、テーマカラーを上書きする設定にしたら、予想通り直った。

GTK_THEME=Adwaita:light dotnet run --project src/Controls/samples/Controls.Sample.Gtk/Controls.Sample.Gtk.csproj --framework net8.0-gtk

(GTK_THEME=Adwaita:light を追加)

これで、一通り開発できるわね。既存のアプリケーションへの適応はできなさそうだけど、あとで試してみるかも。(Essential あたりがほぼ実装されてない気がする)

Lemon73Lemon73

xaml での記述は無理かも?だけど、C# ではなんとか記述できるかも

とりあえず、コードの簡略化を進めますよん

Lemon73Lemon73

現状 Mali で作業しているけど、maui-linux のほうが新しいデータが入ってていいかも

Lemon73Lemon73

なんか、maui-linux でビルドしようとすると Graphics とか Essential でエラーが出るねぇ
あと、ios のワークロードが必要的な (Visual Studio なら動かせる?)

やっぱり Mali で動かすことにしよう。(ちょっと古いけど)

Lemon73Lemon73

とりあえず Nuget (追記: 一時的なアップロードだし、GitHub Packages のほうがいいか) にあげてみようかな。ライセンス (MIT License) 的には大丈夫だし。

Lemon73Lemon73

というか GitHub で現在の状況を公開したほうがいいか…?
面倒だからやってないけど

Lemon73Lemon73

とりあえず Nuget (追記: 一時的なアップロードだし、GitHub Packages のほうがいいか) にあげてみようかな。ライセンス (MIT License) 的には大丈夫だし。

ということで、とりあえず Core, Controls.Core, Compatibility の3つを以下のように編集しました。

.csproj
  <PropertyGroup>
    ...
-   <IsPackable>false</IsPackable>
+   <!-- <IsPackable>false</IsPackable> -->
    ...
+   <PackageId>Lemon73.Microsoft.Maui.Controls</PackageId>
+   <Version>0.0.1</Version>
+   <Authors>Lemon73 and others</Authors>
+   <Company>Lemon's Resting Area and others</Company>
  </PropertyGroup>

で Nuget 形式にパックする

dotnet pack src/Compatibility/Core/src/Compatibility.csproj
dotnet pack src/Core/src/Core.csproj
dotnet pack src/Controls/src/Core/Controls.Core.csproj

それを GitHub Packages で頒布

dotnet nuget push "artifacts/*.nupkg" --api-key (API キー) --source "github" --skip-duplicate

で頒布したのが以下のもの (あとで消すと思います。なので将来的にリンク切れするかも)

(なぜかバージョン指定がうまく行っていない気がする)


参考

Lemon73Lemon73

あとで、実際に利用できるか試してみます。

(なぜかバージョン指定がうまく行っていない気がする)

あと、なぜか .net6 になってるので、.net8 でビルドしたい

Lemon73Lemon73

あとで、実際に利用できるか試してみます。

依存関係的にこの3つだけでは動かないっぽい (もしかしたら全部ビルドする必要あるかも)

あと、なぜか .net6 になってるので、.net8 でビルドしたい

これは eng/Version.props を変えれば良さそう (-dev がついているのはよくわからない)

Lemon73Lemon73

やっぱり、Mali は基本的に .net6 ベースだから、.net8 ベースの maui-linux を動かしたいな

Lemon73Lemon73

とりあえず Nuget (追記: 一時的なアップロードだし、GitHub Packages のほうがいいか) にあげてみようかな。

やっぱり、Mali は基本的に .net6 ベースだから、.net8 ベースの maui-linux を動かしたいな

Related: https://github.com/jsuarezruiz/maui-linux/issues/94

この Issue の完了に目標を変更しよう

Lemon73Lemon73

とりあえず Nuget (追記: 一時的なアップロードだし、GitHub Packages のほうがいいか) にあげてみようかな。

やっぱり、Mali は基本的に .net6 ベースだから、.net8 ベースの maui-linux を動かしたいな

Related: https://github.com/jsuarezruiz/maui-linux/issues/94

この Issue の完了に目標を変更しよう

https://github.com/jsuarezruiz/maui-linux/blob/main-linux/eng/pipelines/common/pack.yml これ (Azure Devops 向け) を GitHub Actions 用に調節すれば Pack 部分ができる。あとは、我らが Alice Console (謎の自作構文) の https://github.com/AliceNovel/AliceConsole/blob/main/.github/workflows/nuget.yml を参考にして、Nuget.org にコミットごと+タグごとにリリースさせればいい。

Lemon73Lemon73

現状 Mali で作業しているけど、maui-linux のほうが新しいデータが入ってていいかも

というか GitHub で現在の状況を公開したほうがいいか…?
面倒だからやってないけど

とりあえず Nuget (追記: 一時的なアップロードだし、GitHub Packages のほうがいいか) にあげてみようかな。

やっぱり、Mali は基本的に .net6 ベースだから、.net8 ベースの maui-linux を動かしたいな

Related: https://github.com/jsuarezruiz/maui-linux/issues/94

この Issue の完了に目標を変更しよう

https://github.com/jsuarezruiz/maui-linux/blob/main-linux/eng/pipelines/common/pack.yml これ (Azure Devops 向け) を GitHub Actions 用に調節すれば Pack 部分ができる。あとは、我らが Alice Console (謎の自作構文) の https://github.com/AliceNovel/AliceConsole/blob/main/.github/workflows/nuget.yml を参考にして、Nuget.org にコミットごと+タグごとにリリースさせればいい。

ってことで、maui-linux (Lemon73 Fork) ですわ。

Lemon73Lemon73

やっぱこれか?

https://matrix.to/#/!secXICyMCTWkttVBVC:matrix.org/$uX3kcJ_AITkkFvqne3wkFJUL4_77o_FpfpGos6RphzI?via=matrix.org

# install dotnet 6
sudo apt-get update; \
sudo apt-get install -y apt-transport-https && \
sudo apt-get update && \
sudo apt-get install -y dotnet-sdk-6.0

# add keys
wget https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb

# install gtksharp with dotnet 6 support
git clone https://github.com/trungnt2910/GtkSharp.git
cd GtkSharp
sudo dotnet tool restore
sudo dotnet cake --BuildTarget=InstallWorkload 

# temporary hack for making the templates visible in dotnet new
sudo cp /usr/share/dotnet/template-packs/* /usr/share/dotnet/templates/6.0.5

# install gtk
sudo apt-get install libgtk-3-0

# test gtk template
cd ..
mkdir gtksharptest
cd gtksharptest
dotnet new gtk
dotnet run
Lemon73Lemon73

これを GitHub Actions 上で試してみるか。

git clone https://github.com/trungnt2910/GtkSharp.git
dotnet cake --BuildTarget=InstallWorkload

PowerShell 上で動いているっぽいので。

だめだー

Lemon73Lemon73

実機で動かしてみた感じ

  1. https://github.com/jsuarezruiz/maui-linux/blob/main/.github/workflows/build-gtk.yml (Linux 向けを Windows 向けに調整して動かす) -> だめ
  2. https://github.com/GtkSharp/GtkSharp/blob/develop/.github/workflows/main.yml (ビルドして動かす) -> だめ
  3. https://www.nuget.org/packages/Gtk.Sdk (スクリプト) -> だめ
  4. https://github.com/GtkSharp/GtkSharp/wiki/Installing-Gtk-on-Windows (msys2 使う) -> 未検証
  5. (https://www.nuget.org/packages/Gtk4DotNet -> Linux 専用?)

つまり、今までやっていたことは、CI/CD だからエラーになっているというわけではなく、実機でもだめっぽい。

可能なら、GitHub Actions の Windows 上で WSL を動かしたい。それなら Linux のコマンドが使えるから、1の方法ができるはず。(とはいえ、インストールに時間がかかって、CI/CD がかなり遅くなりそうだから非推奨かな…)

Lemon73Lemon73

可能なら、GitHub Actions の Windows 上で WSL を動かしたい。それなら Linux のコマンドが使えるから、1の方法ができるはず。(とはいえ、インストールに時間がかかって、CI/CD がかなり遅くなりそうだから非推奨かな…)

https://github.com/marketplace/actions/setup-wsl

あるじゃん!!

明日試してみる。WSL でも無理そうなら素直に MSYS2 頑張るか

Lemon73Lemon73

WSL 厳しそう。msys2 を実機でやって、ci にするか

Lemon73Lemon73

どうせ無理だと思うけど、gtk なしで一旦ビルドできるか試してみる?

最終目的はビルドして .nupkg を nuget.org に上げることなので、その過程については特にこだわりはないし

Lemon73Lemon73

何故かパスが通せない。から、逆に dotnet をコピーするか...?

でも、さすがに速度が遅くなりそう

Lemon73Lemon73

ちなみに、MAUI の開発で必須な Microsoft.Maui.ControlsMicrosoft.Maui.Controls.Compatibility の依存関係は以下

Lemon73Lemon73

どうやら Windows でもすべてのターゲット向けのコンパイルは厳しいらしい

https://github.com/jsuarezruiz/maui-linux/blob/c005e3aff8cf64c792f54f7b06b1f9e4ee310f78/.github/DEVELOPMENT.md#available-solutions

(.NET MAUI、クロスコンパイルが弱すぎる)

=> Linux でのビルドに切り替えてみる?

あと、dotnet build ./Microsoft.Maui.BuildTasks.slnf が必要かもしれない。

https://github.com/dotnet/maui/blob/ad6f752f70ce97846fc3e0e4becd0815153fcb1f/.github/DEVELOPMENT.md#building-the-build-tasks

Lemon73Lemon73

あとで、実際に利用できるか試してみます。

依存関係的にこの3つだけでは動かないっぽい (もしかしたら全部ビルドする必要あるかも)

あと、なぜか .net6 になってるので、.net8 でビルドしたい

これは eng/Version.props を変えれば良さそう (-dev がついているのはよくわからない)

-devDirectory.Build.props 内の <PackageVersion> で規定されているからっぽい。

(今の作業とは特に関係ないけど)

Lemon73Lemon73

現在のエラー:

/usr/share/dotnet/sdk/8.0.200/Sdks/NuGet.Build.Tasks.Pack/buildCrossTargeting/NuGet.Build.Tasks.Pack.targets(110,5):
error MSB4018:
System.ArgumentException:
PackageVersion string specified '8.0.40-ci.net8.+sha.59194fc-azdo.' is invalid.
[/home/runner/work/maui-linux/maui-linux/src/Compatibility/Core/src/Compatibility.csproj]

8.0.40-ci.net8 に関しては GitInfo.txt に記載がある。が、これがどこで利用されているのかがよくわからない。

Lemon73Lemon73

現在のエラー:

/usr/share/dotnet/sdk/8.0.200/Sdks/NuGet.Build.Tasks.Pack/buildCrossTargeting/NuGet.Build.Tasks.Pack.targets(110,5):
error MSB4018:
System.ArgumentException:
PackageVersion string specified '8.0.40-ci.net8.+sha.59194fc-azdo.' is invalid.
[/home/runner/work/maui-linux/maui-linux/src/Compatibility/Core/src/Compatibility.csproj]

見つけた

https://github.com/Lemon73-Computing/maui-linux/blob/c005e3aff8cf64c792f54f7b06b1f9e4ee310f78/eng/Versions.targets#L67-L68

$(BUILD_BUILDID) が指定されていないため文字が挿入されず、バージョンの末尾が azdo. のように . で終わってエラーになっている。

Lemon73Lemon73

あとは、maui-linux チームと連絡して調整ってかんじかな。

そういえば nuget package がちゃんと動くかわからないので、後で検証するか (github actions の artifacts にでも入れようかな?)

Lemon73Lemon73

kztao 氏の CI が洗練されているので、それを upstream に merge してもらうといいかも

kztao さんの内容を cherry pick して、自分の変更と合流させた。(これのせいで若干 commit が分かりづらい気がする…)

とりあえず PR: https://github.com/kztao/maui-linux/pull/1

文章とかがめちゃくちゃだけど、ご愛嬌…

返答をお待ちいたしますわ。多分 kztao さんも CI/CD が正常に動いていなくて困っていると思うから、情報共有したいのです。

Lemon73Lemon73

ということで、

@kztao さんからの PR (Nuget.org に、ベータ版の Packages をアップロードする CI/CD の追加)
https://github.com/jsuarezruiz/maui-linux/pull/95

@lemon73 (私) による PR (バージョンアップ)
https://github.com/jsuarezruiz/maui-linux/pull/96
(.NET 9 は STS なので、今回はバージョンアップしない。したほうがいい?[1])

が提出されましたわ。

Review を待ちます

脚注
  1. .NET 7 (STS) のときはバージョンアップされていたような気がする… ↩︎

Lemon73Lemon73

@lemon73 (私) による PR (バージョンアップ)

Review を待ちます

リポジトリの保持者に Review (Approve) されたのに Merge されない謎

Lemon73Lemon73

@kztao さんからの PR (Nuget.org に、ベータ版の Packages をアップロードする CI/CD の追加)

何故か Close する @kztao 氏