Portability Analyzerを使ってライブラリの.NET Standard準拠を調べよう

Visual Studio 2017のリリースを迎え(るにあたり)、(技術的には、直接的にそこに関連付いているわけではないけれど)「.NET Standard」というキーワードが強く聞こえてくるようになった気がしています。

そうそう、先日、 Xamarin Studio も .NET Standard Library 対応しましたね。

blog.xamarin.com

ということで、今回は既存資産のライブラリ実装が、.NET Standardに準拠しているかどうか調べることができる「Portability Analyzer」というものを使ってみたいと思います。

Githubリポジトリは↓↓↓

github.com

ちなみにGithubからソース取らなくても、以下のGUI操作だけで使えます。

※以下はVisual Studio 2015を使います。明日か明後日にVS2017版に差し替えるかも・・・

.NET Portability Analyzerをインストール

※2017.3.8追記
2017/3/8現在のVisual Studio 2017では「拡張機能と更新プログラム」からだと「.NET Portable Analyzer」が出てこないみたいです。
以下から.vsix ファイルをダウンロードしてダブルクリックでインストールするとVS2017でも使えるようになります。
でも、VS2017が不安定になる可能性があります、と警告文が出るので自己責任でご利用ください(正式対応じゃないってことですね)。私の環境では、特に何かが変になることなく動いていました。

.NET Portability Analyzer - Visual Studio Marketplace


Visual Studio 2015を起動。
メニュー「ツール → 拡張機能と更新プログラム」を選択。

f:id:daigo-knowlbo:20170307122708p:plain

「オンライン → .NET Portable Analyzer」を選択してインストール

f:id:daigo-knowlbo:20170307122719p:plain

VSの再起動を促されます。

調査対象のライブラリプロジェクトを開く

調査対象のライブラリプロジェクトをVisual Studioで開きます。
今回は以下のような「OmnipotentLibrary」というライブラリプロジェクトを調査対象としました。

f:id:daigo-knowlbo:20170307123000p:plain

プロジェクトに含まれるMyLogger.csは以下のような、Reflectionを使ったキビシイ実装です。

// MyLogger.cs
using System;
using System.Security;
using System.Reflection;
using System.Diagnostics;

namespace OmnipotentLibrary
{
  public class MyLogger
  {
    /// <summary>
    /// メソッドの開始時に呼び出します。
    /// </summary>
    /// <returns></returns>
    [DynamicSecurityMethod]
    public static DateTime Start()
    {
      DateTime now = DateTime.Now;

      const int callerFrameIndex = 1;
      StackFrame callerFrame = new StackFrame(callerFrameIndex);
      MethodBase callerMethod = callerFrame.GetMethod();
      Debug.WriteLine(string.Format("start {0}()", callerMethod.Name));

      return now;
    }

    /// <summary>
    /// メソッドの終了時に呼び出します。
    /// </summary>
    /// <param name="start"></param>
    [DynamicSecurityMethod]
    public static void End(DateTime start)
    {
      DateTime now = DateTime.Now;

      TimeSpan ts = now - start;

      const int callerFrameIndex = 1;
      StackFrame callerFrame = new StackFrame(callerFrameIndex);
      MethodBase callerMethod = callerFrame.GetMethod();
      Debug.WriteLine(string.Format("end {0}(): {1}ms", callerMethod.Name, ts.TotalMilliseconds));
    }
  }
}

namespace System.Security
{
  [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
  internal sealed class DynamicSecurityMethodAttribute : Attribute
  {
  }
}

Portability Analyzerの設定を行う

ソリューションエクスプローラでプロジェクトを選択、マウス右ボタンクリック、メニュー「Prtable Analyzer Settings」を選択します。

f:id:daigo-knowlbo:20170307134814p:plain

表示された以下のウィンドウで「Target Platforms」を選択します。
「Target Platforms」とは、アナライズ対象のプラットフォームです。調査対象のプロジェクト(OmnipotentLibrary)が、「対象のプラットフォームに準拠しているか?」調べる対象になります。

f:id:daigo-knowlbo:20170307134827p:plain

ここでは、.NET Core 1.0 / 1.1 / 2.0、.NET Framework 4.5 / 4.5.1 / 4.5.2 / 4.6 / 4.6.1 / 4.6.2、.NET Standard 1.0 / 1.1 / 1.2 / 1.3 / 1.4 / 1.5 / 1.6 / 2.0、Xamarin Android 1.0.0、Xamarin.iOS 1.0.0.0に準拠しているか?を、アナライズする設定としました。

Portability Analyzerを実行する

ソリューションエクスプローラでプロジェクトを選択、マウス右ボタンクリック、メニュー「Analyzer Azzembly Portability」を選択します。

f:id:daigo-knowlbo:20170307135554p:plain

アナライズが完了すると、「Portability Analyze Results」ウィンドウに結果が表示されます。

f:id:daigo-knowlbo:20170307135652p:plain

「Open Report」をクリックすると結果のExcelファイルが起動されます。

結果を分析する

アナライズ結果のExcelは、2シート構成です。

1シート目は結果のサマリーになっています。
横長なので折り返し加工していますが、以下のような感じです。

f:id:daigo-knowlbo:20170307140356p:plain

表のヘッダ(青い部分)が「ターゲットプラットフォーム」、その下の数値が「分析結果のスコア」です。
スコア100(緑)が準拠OK、スコア100未満(オレンジ)が準拠NG、となります。
このライブラリはReflection系を使用しているので、.NET Core 1.0 / 1.1 や .NET Standard 1.xなどでは結果NGとなりました。

2つ目のシートには、スコアが100未満であった原因の詳細がリストされています。

f:id:daigo-knowlbo:20170307140524p:plain

T:System.Diagnostics.StackFrame とか T:System.Runtime.InteropServices.GuidAttributeを使ってるのが悪いよー、Supported: 2.0+じゃないと使えないよー、っていう結果が分かります。

まとめ

ということで

Let’s head to .NET Standard!

Introducing .NET Standard | .NET Blog

docs.microsoft.com