.NET 6 + VB でも NativeAOT がしたい！

# はじめに
これは、[Visual Basic Advent Calendar 2021](https://qiita.com/advent-calendar/2021/vb)の9日目の記事となります。

別のところで C#用のNativeAOTの記事を書きました。Qiitaでは2つのAdvent Calendarのリンクができません。

https://qiita.com/yaju/items/d5a771f3f00672aa19ff

# NativeAOTとは
ILを完全にネイティブコードに落とし込むオープンソースプロジェクトです。ILからの変換なので、C#以外にVisual BasicやF#でも可能です。
2021年以降は、NativeAOTリポジトリ
https://github.com/dotnet/runtimelab/tree/feature/NativeAOT
※将来的にはメインプロジェクトに統合される予定

# NativeAOTの準備
## C++ ビルドツール
マイクロソフト C++ ビルドツールのインストール
https://visualstudio.microsoft.com/ja/visual-cpp-build-tools/

## C++ によるデスクトップ開発
Visual Studio Installerを起動します。変更ボタンをクリックします。
ワークロードタブに「C++によるデスクトップ開発」にチェックを入れ、「インストール」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/8584324c-dc04-0914-731b-a720a73f4791.png)

## ビルド
HelloWorldのサンプルが下記サイトにあります。
https://github.com/dotnet/runtimelab/tree/feature/NativeAOT/samples/HelloWorld

HelloWorldのままではつまらないので、今回はフィボナッチ数列にしてみます。

### プロジェクト生成
NativeAOTは、ILからの変換なので、C#以外にVisual BasicやF#でも可能です。
今回は、Visual Basicを指定します。
自分はC:\WorkSpaceフォルダにいつもプロジェクトを生成しています。

```bat
> cd C:\WorkSpace
> dotnet new console -o FibonacciVB -lang VB
> cd FibonacciVB
```

### プロジェクトにNativeAOTを追加
```bat
> dotnet new nugetconfig
テンプレート "NuGet Config" が正常に作成されました。
```
nuget.configファイルが追加されます。

### nuget.configの編集

```xml:nuget.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <!--To inherit the global NuGet package sources remove the <clear/> line below -->
    <clear />
    <add key="nuget" value="https://api.nuget.org/v3/index.json" />
  </packageSources>
</configuration>
```
packageSources要素の<clear/>より下を下記の置き換えます。

```xml
<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
```

最終結果

```xml:nuget.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <!--To inherit the global NuGet package sources remove the <clear/> line below -->
    <clear />
    <add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
  </packageSources>
</configuration>
```

### ILCompilerの追加
```bat
> dotnet add package Microsoft.DotNet.ILCompiler -v 7.0.0-*
```

FibonacciVB.vbproj にItemGroup要素に`Microsoft.DotNet.ILCompiler`が追加されました。

```xml:FibonacciVB.vbproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <RootNamespace>FibonacciVB</RootNamespace>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="7.0.0-*" />
  </ItemGroup>

</Project>
```

### プログラムの修正
HelloWorldのままなのでプログラムを書き換えます。

```vb:Program.vb
Imports System

Module Program
    Sub Main(args As String())
        Console.WriteLine("Hello World!")
    End Sub
End Module
```

フィボナッチ数列にプログラムを書き換えます。
※C# 10.0 のように、namespace、Main、Import Systemを記述しないスクリプトライクのようには記述できません。

```vb:Program.vb
Imports System

Module VBModule
 
    Sub Main(args As String())
        Fibonacci_Iterative(10)
    End Sub
    
    Sub Fibonacci_Iterative(ByVal len As Integer)
        Dim a, b, c As Integer
        a = 0 : b  = 1 : c  = 0
        Console.Write("{0} {1}", a, b)

        For i As Integer = 2 To len - 1
            c = a + b
            Console.Write(" {0}", c)
            a = b
            b = c
        Next
    End Sub
  
End Module
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/3e5e8599-8b1f-5e6e-f346-015c79bd01ad.png)

# NativeAOTによる発行
下記は発行のコマンドの説明になります。

```bat
> dotnet publish -r <RID> -c <Configuration>
```

RIDには、`win-x64` or `linux-x64` or `osx-x64` を指定します。
残念ながら`linux-arm64`がまだありませんので、Raspberry Pi 4用には出来ない。
Configurationには、 `debug` or `release` を指定します。

では、win-x64のreleaseで発行します。

```bat
> dotnet publish -r win-x64 -c release

FibonacciVB -> C:\WorkSpace\FibonacciVB\bin\release\net6.0\win-x64\publish\
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/9b681346-cc88-e724-6a9f-fdc6e17d680a.png)


FibonacciVB.exe でサイズが 4.18MiB あります。自分はWindows Terminal上でPowershellを使用しているので、先頭に"./"を付けて実行します。

```bat
> cd bin\release\net6.0\win-x64\native
> ./FibonacciVB.exe

0 1 1 2 3 5 8 13 21 34
```

## イメージサイズ縮小
現在、FibonacciVB.exe でサイズが 4.18MiB あります。
PropertyGroup要素に幾つかオプションを追加してみます。
今回は実行速度より小さいコードサイズを優先にします。

```xml:FibonacciVB.vbproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <RootNamespace>FibonacciVB</RootNamespace>
    <TargetFramework>net6.0</TargetFramework>

    <!-- https://github.com/dotnet/corert/blob/master/Documentation/using-corert/optimizing-corert.md -->
    <IlcOptimizationPreference>Size</IlcOptimizationPreference>
    <RootAllApplicationAssemblies>false</RootAllApplicationAssemblies>
    <IlcGenerateCompleteTypeMetadata>false</IlcGenerateCompleteTypeMetadata>
    <IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
    <IlcDisableReflection>true</IlcDisableReflection>
    <IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>
    <IlcDisableUnhandledExceptionExperience>false</IlcDisableUnhandledExceptionExperience>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="7.0.0-*" />
  </ItemGroup>

</Project>
```

## 再発行
```bat
> cd C:\WorkSpace\FibonacciVB
> dotnet publish -r win-x64 -c release
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/3b2987d4-e29a-9198-d092-bc7f5c095ca0.png)

FibonacciVB.exe のサイズが 4.18 MiB → 1.13 MiB まで小さくなりました。
実行結果も問題ありません。

```bat
> cd bin\release\net6.0\win-x64\native
> ./FibonacciVB.exe

0 1 1 2 3 5 8 13 21 34
```

## 究極に小さく
下記サイトではあらゆるテクニックを駆使して、64MiBから8176バイトまで小さくしています。
[Building a self-contained game in C# under 8 kilobytes](https://medium.com/@MStrehovsky/building-a-self-contained-game-in-c-under-8-kilobytes-74c3cf60ea04)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/2fa5efff-b054-ae99-aa27-94d2d335b293.png)

## bflatの紹介
単純なアプリケーションの場合は bflat を使用すると良い結果が得られる可能性があります。
[How to compile a .NET application to native code? - stackoverflow](https://stackoverflow.com/questions/45702/how-to-compile-a-net-application-to-native-code)

bflatは、.NET実行可能ファイルを生成する「公式」C＃コンパイラである。最も単純なアプリであっても、サイズが2MBから3MBの実行可能ファイルを生成します。
https://github.com/MichalStrehovsky/bflat

# その他
Redditにて、NativeAOTの.NET 7での計画のスレッドがあります。
[NativeAOT .NET 7 Plans](https://www.reddit.com/r/dotnet/comments/qncfuq/nativeaot_net_7_plans/)
[Is Native AOT on the roadmap?](https://www.reddit.com/r/dotnet/comments/od35v9/is_native_aot_on_the_roadmap/)

WinFormsとNativeAOTについて、.NET 5の時なので.NET 6で対応されているかも。
[Again WinForms and NativeAOT](https://codevision.medium.com/again-winforms-and-nativeaot-fd068425cc13)

# 最後に
無事に、.NET 6 + VB でも NativeAOTによりネイティブプログラムにすることが出来ました。