Azure Functions なプロジェクトのビルドで Azure Pipelines を使っていたところ、手元ではビルド出来ていたコードが Azure Pipelines だとビルドエラーになってしまったので、最適な解決策を調べていました。
具体的には C# 7.1 以降のコードが Azure Pipelines の Hosted Agent だとビルド出来ません。以下のように default の型推論をつかったコードを書くとエラーになります。
string test = default;
実際に Azure Pipelines でビルドさせると、この機能は C# 7.0 では使えないので 7.1 以降のバージョンを使うように、というエラーが出ます。
そしてよく見ると分かるように、MSBuild のバージョンが 15.x です。
原因としては Azure Pipelines の Hosted Agent に Visual Studio 2017 向けの .NET Core SDK 2.2.105 がインストールされているので、MSBuild のバージョンが古いというオチです。
ちなみに Visual Studio 2019 や Ubuntu の Hosted Agent を使っても同じ結果です。
開発環境とビルド環境で使える言語バージョンが異なっているのは避けたいので、以下の 3 つの方法のどれかを使って回避することにします。
- csproj で LangVersion を latest に設定する
- 最新の .NET Core SDK を手動でインストールする
- Container jobs で最新バージョンの .NET Core SDK Docker Image を使う
設定や使い方を実際に動かした例を交えながら紹介します。割とこの問題には悩まされたので、自分用のメモを兼ねて YAML 定義も残しておきます。
LangVersion を設定
C# 7.x がデフォルトでコンパイルできるようになったのは MSBuild 16.x 系なので、Hosted Agent が使っている MSBuild 15.x 系では LangVersion
で指定しない限り C# 7.0 までしか有効化されていません。
csproj で LangVersion
に latest
を指定すると最新のバージョンが使えます。
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp2.1</TargetFramework> <AzureFunctionsVersion>v2</AzureFunctionsVersion> <!-- ↓ を追加 --> <LangVersion>latest</LangVersion> </PropertyGroup> </Project>
これで MSBuild 15.x でもC# 7.1 以降のコードがビルド出来るようになりますが、プロジェクトを弄るのは正直あまりやりたくないです。
Visual Studio 2019 では設定なしで使えるので、開発環境側に合わせておきたい気持ちが大きいです。
最新の .NET Core SDK をインストール
そもそもの問題としては、Visual Studio 2019 な Hosted Agent に 2019 向けの .NET Core SDK が入っていないことなので、明示的に最新の SDK をインストールするようにしても解決します。インストールされている .NET Core SDK が古いのは別の問題です。
ちなみに Hosted Agent の .NET Core SDK は 4 月あたりから更新されていないです。いくつも Issue が上がっていますが、正直なところ最新の SDK が Agent 更新の度に入るようになるのには時間がかかりそうです。
trigger: - master pool: vmImage: 'windows-2019' variables: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true NUGET_XMLDOC_MODE: skip steps: - task: UseDotNet@2 inputs: packageType: 'sdk' version: '2.2.x' - task: DotNetCoreCLI@2 inputs: command: 'build' projects: '**/*.csproj' arguments: '-c Release'
UseDotNet@2
を使って 2.2.x
系で最新の SDK をインストールするようにしています。
これでパイプラインを実行すると、正常にビルド出来るようになります。新しい MSBuild を使うと NuGet パッケージの復元が早くなる気がします。少なくともログは少なくなって見やすいです。
そのままでは .NET Core SDK インストール直後にパッケージキャッシュの展開が走ってしまって、パッケージの復元で異常に時間がかかるようになるので、DOTNET_SKIP_FIRST_TIME_EXPERIENCE
を環境変数に追加して展開しないようにします。これで処理時間が多少は短縮できます。
この辺りは他の CI SaaS でも共通の方法なので、以前 CircleCI でも同じ設定を使い高速化しました。
Ubuntu のイメージには最初からスキップする設定が入っているようですが、何故か Windows のイメージには入ってませんでした。とても不具合くさいです。
Container jobs で最新の .NET Core SDK を使う
タスクを使って .NET Core SDK をインストールする以外に、公式提供されている SDK の Docker Image と Container jobs を使って、最新の SDK でビルドする方法もあります。
こっちの方がタスクはシンプルに保てますが、Docker Image のサイズと必要なプラットフォームと相談という感じです。Windows が必要な場合は時間的に厳しいです。
trigger: - master pool: vmImage: 'ubuntu-latest' container: 'mcr.microsoft.com/dotnet/core/sdk:2.2' steps: - task: DotNetCoreCLI@2 inputs: command: 'build' projects: '**/*.csproj' arguments: '-c Release'
Ubuntu の場合は下手に .NET Core SDK をインストールするよりも、処理時間を短縮できそうです。
最新の .NET Core SDK 2.2.x な Docker Image を使うので、もちろん正常にビルドが行えます。
Docker Image にはパッケージキャッシュが含まれているので、手動で .NET Core SDK をインストールするのに比べて、NuGet パッケージの復元にかかる時間が少なくて済みます。
本来なら Hosted Agent に最新の SDK がインストールされていて、そのままの状態で MSBuild 16.x 系を使ったビルドが行えるのが理想ですが、暫くは各自対策する必要がありそうです。