ソースコード構造解析ツール『Understand』は、大規模で複雑なプログラムを素早く解析するための多彩な機能を搭載しています。 アーキテクチャから個々のクラスや関数まで、あらゆるレベルでソースコードを解析し、プログラムの制御フローや構造、クラス継承、関数や変数の関係など、さまざまな角度から見える化します。

車載ソリューションの記事紹介

テクマトリックスでは車載ソフトウェア開発向けのソリューションを提案しています。
ツールチェーンとしてUnderstandが含まれていますので、ご関心のある方は「こちらの記事」をご覧ください。

2020/08/31

UnderstandをGit連携して、2つのリビジョンを比較する

以前、「Understand 5.1 の新機能・改善点 - 差分グラフィカルビュー/差分レポートの追加」において、プロジェクトを比較する機能としてCompareグラフやChangedEntities Reportをご紹介しました。
これに対してお客様より、「プロジェクトの比較設定がもっと簡単になると嬉しい」という声をいただきました。

今回は、Understandのユーザーツール機能を使用して、gitリポジトリの2つのリビジョンを指定することで、各リビジョンを自動的に解析し、比較する方法をご案内します。
本手順により任意のSHA番号を2つ指定いただくだけで、簡単にリビジョン間の解析差分を確認いただくことが可能となります。

これにより、例えば以下のようなユースケースでご利用いただくことが考えられます。

ユースケース

  • 開発者のユースケース
    ローカルリポジトリからdevelopブランチにコミットする前に、開発者自身が差分を確認する。その後プルリクエストを出す際に、自身の確認結果や確認したいポイントなどの補足として、CompareグラフをGithubなどのホスティングサービス上のコメント欄に張り付けて、レビュアーに共有する。
  • レビュアーのユースケース
    プルリクエスト時にレビュアーが該当差分をUnderstandを使用して確認する。
    コードレビューの証跡や指摘事項の記録として、Compareグラフにペイント等で確認箇所、指摘箇所に○印を付け、コメントに張り付けることで記録を残す。
今回は分析するサンプルコードとして、
「C⾔語のオープンソースコード︓cgit」のリポジトリを使⽤することにします。

目次

  • 前提条件
  • 事前準備
    • 準備1:batファイルの配置
    • 準備2:ユーザーツールの設定
    • 準備3:gitリポジトリの準備
    • 準備4:比較するSHA番号の確認
  • 実施手順:SHA番号を2つ指定し、リビジョンの比較を行う。
  • 結果の確認
  • 発展的な使い方の例
  • 各設定ファイルの例

前提条件

  1. 動作確認済環境

    • Windows 10 64bit
    • Understand 5.1 (Build 1022)
    • 有効なUnderstandライセンス(APIライセンスは不要)
    • Gitインストール済
    • 環境変数[Path]に以下が追加済
      (git及びUnderstandのインストール先に合わせて変えてください。)
        • C:\Program Files\SciTools\bin\pc-win64
        • C:\Program Files\Git\cmd
  2. 解析対象

    事前準備

    1. batファイルの配置

      1. ワークスペースを作成します。
        Compareディレクトリを作成し、Compareディレクトリ直下にreposディレクトリを作成します。
        ○パスの例
        • C:\work\Compare
        • C:\work\Compare\repos
      2. 本ブログ最後尾に記載の各batファイルをCompareディレクトリ直下に配置します。
        ○batファイルの配置パスの例
        • C:\work\Compare\CompareGitSHA.bat
        ○配置するbatファイル
        • ShowLog.bat
        • CompareGitSHA.bat
        • SetGitRepo.bat
        なお、batファイル内でUnderstandプロジェクトの解析を行う際に言語の指定を行っております。今回はC/C++言語のFuzzyモードでの解析となります。
    2.  ユーザーツールの設定

      1. 本ブログ最後尾に記載の scitools.iniを一時ファイルとして任意のディレクトリに保存します。
      2. ユーザーツール
        1. UnderstandのGUIを起動し、[メニューバー]-[ツール]-[オプション]-[ユーザーツール]を選択します。
        2. [ユーザーツール]の[インポート]ボタンを押し、保存したiniファイルを指定します。
        3. [ユーザーツールのインポート]画面において、[すべて]ボタンを押し、すべてのチェックボックスが選択された状態にして[インポート]ボタンを押します。
          ユーザーツールのインポート
        4. ユーザーツール内で、以下計7箇所を修正します。
          • [git\log]の設定
            git\logの設定
            • [コマンド(C)]:[batファイルの配置]項で配置した[ShowLog.bat]ファイルのパスを指定します。
            • [初期ディレクトリ]:Compareディレクトリ直下の[repos]ディレクトリを指定します。
              初期ディレクトリパスの例
              C:\work\Compare\repos
          • [git\setRepo]の設定
            git\setRepoの設定

            • [コマンド(C)]:[batファイルの配置]項で配置した[SetGitRepo.bat]ファイルのパスを指定します。
            • [初期ディレクトリ]:Compareディレクトリ直下の[repos]ディレクトリを指定します。
              初期ディレクトリパスの例
              C:\work\Compare\repos
          • [git\undCompare]の設定
            git\undCompareの設定

            • [コマンド(C)]:[batファイルの配置]項で配置した[CompareGitSHA.bat]ファイルのパスを指定します。
            • [パラメーター(P)]:defaultValueの文字列をお好みに合わせて変更頂くことでデフォルト値を設定できます。
            • [初期ディレクトリ]:Compareディレクトリ直下の[repos]ディレクトリを指定します。
              初期ディレクトリパスの例
              C:\work\Compare\repos
        5.  [OK]ボタンを押します。これでユーザーツールの設定は完了です。
          ※Understand設定ファイル(C:\Users\(ユーザー名)\AppData\Roaming\SciTools\Understand.ini)を削除されますと、本ユーザーツールの設定は削除されます。
          予め[ユーザーツールのエクスポート]より設定内容をバックアップしておくことをお勧めいたします。
      3. gitリポジトリの準備

        1. Understand(GUI)を起動した状態にします。
          ※後にオープンエラーが発生することを防ぐため、プロジェクトは開かないでください。 
          ユーザーツールから[git]-[setRepo]を選択
        2. メニューバーから[ツール]-[ユーザーツール]-[git]-[setRepo]を選択します。
          setRepoのダイアログ
          repoURL欄にgitの対象repositoryのURLを入力し、OKボタンを押します。
          例:https://git.zx2c4.com/cgit
          この手順により[repos]ディレクトリ直下にプロジェクトのディレクトリ(今回の例では[cgit]ディレクトリ)が作成され、その配下に.gitディレクトリやソースコード等がcloneの結果として生成されます。
          (参考)本ブログではあくまで同一Gitリポジトリ内のリビジョン比較を行う手順を説明しています。ただし、本手順を再度行うことで別のGitリポジトリを並行して取り扱うことも可能です。詳しくは本ブログ後方に記載の「発展的な使い方の例」をご確認ください。
      4. 比較するSHA番号の確認

        1. Understand(GUI)を起動した状態にします。
          ※後にオープンエラーが発生することを防ぐため、プロジェクトは開かないでください。
          ユーザーツールから[git]-[log]を選択
           
        2. メニューバーから[ツール]-[ユーザーツール]-[git]-[log]を押します。
          logのダイアログ
        3. ここでは[準備3]で準備したプロジェクトのディレクトリ([.git]ディレクトリの親ディレクトリ)を.git_ParentDir欄に入力し、OKボタンを押します。
          ※.gitディレクトリは隠しディレクトリです。
          (例).git_ParentDirに入力する値
          C:\work\Compare\repos\cgit
          (参考).gitディレクトリのパス
          C:\work\Compare\repos\cgit\.git
        4. Understand上の[コマンドウィンドウ]にgit logの履歴が表示されます。 
          比較したいリビジョン2つのSHA番号を確認します。
        Understand上の[コマンドウィンドウ]にgitのlogが表示された状態

      実施手順

      1. Understand(GUI)を起動した状態にします。
        ※後にオープンエラーが発生することを防ぐため、プロジェクトは開かないでください。
        ユーザーツールから[git]-[undCompare]を選択
      2. メニューバーから[ツール]-[ユーザーツール]-[git]-[undCompare]を押します。
        undCompareのダイアログ
      3. 表示された画面にて各値を指定し、OKボタンを押します。
        • .git_ParentDir:プロジェクトのディレクトリ([準備4]を参照)
        • SHA_A:比較元(通常は新ver)
        • SHA_B:比較先(通常は旧ver)
          ※gitのSHA番号は先頭数文字(7文字程度)を指定するだけでリビジョンを指定可能です。
          [cgitお試し用SHA番号]
          新Rev:5e49023
          旧Rev:c4d23d02e
          ※HEADやHEAD~等のエイリアスを使用することも可能です。
          ※ただし、SHA_Bを先にチェックアウト後、SHA_Aをチェックアウトとなります。SHA_Bの解析の際に、ローカルのリビジョン(HEAD)が変更される場合があるため、SHA_Aを指定する際はご注意ください。

      4. 以下のような実行ログが出力され、新しいUnderstandの画面が立ち上がり、その画面でA.udbが自動的に開けば成功です。
        実行ログ

      結果の確認

      • Compare系グラフ

      自動的に新しいUnderstandのGUIが起動し、生成された比較元プロジェクト(A.udb)が開かれました。 このとき、A.udbには「比較先プロジェクト」が自動設定されています(後述「比較先プロジェクト」も参照)。エンティティフィルターなどで関数を選び、ButterflyCompareグラフを確認していただくと、差分が表示されていることが分かります。 以前のブログ「Understand 5.1 の新機能・改善点 - 差分グラフィカルビュー/差分レポートの追加」でご紹介している「Object References Compare」や「Changed Entities Report」に関しても同様に、表示することが可能になっています。
      Butterfly Compareグラフの表示(cgitプロジェクトのエンティティ[about_fn])

      • ソースコードの比較

      [ツール]-[比較]-[ファイル/フォルダの比較]から表示されるダイアログにおいて、Compareディレクトリ配下に自動作成された[A]ディレクトリと[B]ディレクトリを指定の上[比較]ボタンを押すと、2つのリビジョンのコード差分を確認いただくことも可能です。
      [ツール]-[比較]-[ファイル/フォルダの比較]から表示されるダイアログ
      2つのリビジョンのコード差分の表⽰

      • 比較先プロジェクト

      メニューバーから[プロジェクト]-[プロジェクトの設定]-[比較]の[比較プロジェクト]欄をご確認いただくと、比較先プロジェクトとしてB.udbが自動的に指定されている(相対パスにて指定済)ことをご確認いただけます。
      [プロジェクト設定]-[比較]-[比較プロジェクト]の設定

      ぜひお試しください。 以降は発展的な使い方の例となります。


      発展的な使い方の例

      ここでは本設定をより便利に使用するための例(多くはカスタマイズが必要)をご紹介いたします。

      • 2つ目以降のリポジトリ

      開発者によっては、複数のプロジェクトにアサインされ、複数のリポジトリを並行して同じマシン環境で取り扱うことが必要な場合があるかと思います。
      そのような場合は、[準備3 gitリポジトリの準備]を再度行うことで2つ目以降のリポジトリをgit cloneし、該当リポジトリ内の2つのSHA番号について同様に取り扱うことが可能です。(undCompare実行時は.git_ParentDirで選択するリポジトリを変更してください。)
        • お試し用リポジトリ及びSHA番号
          https://github.com/apache/httpd.git
          新Rev:e1673c079052
          旧Rev:679b3024e825
        • [リポジトリの位置関係]
          [Compare]-[repos]-[cgit]-[.git]
          [Compare]-[repos]-[httpd]-[.git]

      cgitとhttpdのリポジトリを並行して取り扱った状態

      • 他ツールとの連携やAPI利用

      本稿の手順によりUnderstandでの解析結果(udb)をSHA番号の指定のみで自動的に得ることが可能となっております。
      CompareGitSHA.batに追記していただく、もしくは本稿を参考に新たにユーザーツールを作成いただくことで、コマンドライン経由で他ツールとの連携が可能となります。 すなわちSHA番号を指定するだけでUnderstand以外のツールとの連携をした使い方が可能となります。
      (参考)連携ツールや連携処理の例
        • Lattix
        • UnderstandAPIを使用したスクリプト(APIライセンスが必要です。)
        • コンパイルスクリプトの呼び出し及びテストスクリプトの呼び出し
        • 特定のURLに対しPOSTするbatを作成し、チケット発行または発行画面をブラウザで開く処理を実行
          例:wget (Redmineの新しいチケットを発行する画面のURLパラメータ込みのURL)
        • URLをwgetするbatによるJenkinsジョブの実行

      • C/C++のStrictモードやその他の言語

        • C/C++のStrictモード
          以前とあるお客様と本稿の内容についてお話しをしたところ、Strictモードでの設定が可能かと、質問を頂きました。 実現するにはやや難易度が高い内容になりますが、 本ブログに記載しているCompareGitSHA.batファイルに関し、A.udb及びB.udbの分析時にanalyzeを実施する前に以下のような設定を加えていただくことで、Strictモードでの解析が可能となります。
          ただし、プロジェクトやリビジョン毎のマクロやインクルードディレクトリの設定が必要になります。マクロやインクルードパス等の設定はテキストファイルに記載しインポートすることが可能です。gitの各プロジェクトにてこのテキストファイルを一緒に管理いただく形での実現になるかと考えられます。

          (例)und settings -C++UseStrict on fastgrep.udb
          コマンドライン処理に関する詳細は[Understand 5 ユーザーズ ガイド & リファレンス マニュアル]第13章をご参照ください。

        • その他の言語
          また、C/C++以外のいくつかの主要言語(C#やJAVAを含む)についてもCompareグラフを表示することが可能です。
          CompareGitSHA.batの内容を修正いただくことで同様にご使用いただくことが可能です。 ユーザーツールの設定の詳細は[Understand 5 ユーザーズ ガイド & リファレンス マニュアル]第12章をご参照ください。

           カスタム例:
          ユーザーツールに"パラメータ"にプロンプト引数を追加して、bat側で言語別に処理を分ける。
          →ユーザーツールのプロンプト引数に以下を追記(batファイル側の修正も必要です。)
           [$PromptForSelect "Language=C++Fuzzy;JAVA;C#"]

      $PromptForSelectの設定例
      プロンプト引数に$PromptForSelectを使用した場合の例

      各設定ファイルの例

      [CompareGitSHA.batファイルの中身]
      rd /s /q ..\A
      rd /s /q ..\B
      mkdir ..\A
      mkdir ..\B
      if ""%1""=="""" goto END
      if ""%2""=="""" goto END
      if ""%3""=="""" goto END
      cd %1
      if %ERRORLEVEL%==1 goto END2
      del *.udb
      und create -db B.udb -languages c++
      git checkout %3
      und add . B.udb
      und settings -AddMode Relative B.udb
      und analyze -all -db B.udb
      xcopy .\* ..\..\B\ /E
      del *.udb
      und create -db A.udb -languages c++
      git checkout %2
      und add . A.udb
      und settings -AddMode Relative A.udb
      und analyze -all -db A.udb
      und settings -ComparisonProjectPath ..\B\B.udb -db A.udb
      xcopy .\* ..\..\A\ /E
      understand ..\..\A\A.udb
      goto END
      :END2
      echo notExsit
      :END
      echo Finish

      [SetGitRepo.batファイルの中⾝]
      if ""%1""=="""" goto END
      git clone %1
      :END
      echo Finish

      [ShowLog.batファイルの中⾝]
      if ""%1""=="""" goto END
      cd %1
      git log
      :END
      echo Finish

      [scitools.iniファイルの中⾝](コピーされる際は行の折り返し箇所に改⾏が⼊らないようご注意ください)
      [commandcapture]
      usertools\storedtools\git%5Clog\command=C:\\work\\Compare\\ShowLog.bat
      usertools\storedtools\git%5Clog\parameters=$PromptForDir .git_ParentDir
      usertools\storedtools\git%5Clog\iconfile=
      usertools\storedtools\git%5Clog\commanddir=C:\\work\\Compare\\repos
      usertools\storedtools\git%5Clog\input=0
      usertools\storedtools\git%5Clog\output=1
      usertools\storedtools\git%5Clog\uperlscript=false
      usertools\storedtools\git%5Clog\addpopup=false
      usertools\storedtools\git%5Clog\addmenu=true
      usertools\storedtools\git%5Clog\addtoolbar=false
      usertools\storedtools\git%5Clog\runsave=false
      usertools\storedtools\git%5Clog\runparsechanged=false
      usertools\storedtools\git%5Clog\runparseall=false
      usertools\storedtools\git%5Clog\runrescan=false
      usertools\storedtools\git%5Clog\runBefore=false
      usertools\storedtools\git%5Clog\runafter=false
      usertools\storedtools\git%5CsetRepo\command=C:\\work\\Compare\\SetGitRepo.bat
      usertools\storedtools\git%5CsetRepo\parameters=$PromptForText repoURL
      usertools\storedtools\git%5CsetRepo\iconfile=
      usertools\storedtools\git%5CsetRepo\commanddir=C:\\work\\Compare\\repos
      usertools\storedtools\git%5CsetRepo\input=0
      usertools\storedtools\git%5CsetRepo\output=1
      usertools\storedtools\git%5CsetRepo\uperlscript=false
      usertools\storedtools\git%5CsetRepo\addpopup=false
      usertools\storedtools\git%5CsetRepo\addmenu=true
      usertools\storedtools\git%5CsetRepo\addtoolbar=false
      usertools\storedtools\git%5CsetRepo\runsave=false
      usertools\storedtools\git%5CsetRepo\runparsechanged=false
      usertools\storedtools\git%5CsetRepo\runparseall=false
      usertools\storedtools\git%5CsetRepo\runrescan=false
      usertools\storedtools\git%5CsetRepo\runBefore=false
      usertools\storedtools\git%5CsetRepo\runafter=false
      usertools\storedtools\git%5CundCompare\command=C:\\work\\Compare\\CompareGitSHA.bat
      usertools\storedtools\git%5CundCompare\parameters=$PromptForDir .git_ParentDir=defaultValue $PromptForText  SHA_A(usually_new) $PromptForText SHA_B(usually_old)
      usertools\storedtools\git%5CundCompare\iconfile=
      usertools\storedtools\git%5CundCompare\commanddir=C:\\work\\Compare\\repos
      usertools\storedtools\git%5CundCompare\input=0
      usertools\storedtools\git%5CundCompare\output=1
      usertools\storedtools\git%5CundCompare\uperlscript=false
      usertools\storedtools\git%5CundCompare\addpopup=false
      usertools\storedtools\git%5CundCompare\addmenu=true
      usertools\storedtools\git%5CundCompare\addtoolbar=false
      usertools\storedtools\git%5CundCompare\runsave=false
      usertools\storedtools\git%5CundCompare\runparsechanged=false
      usertools\storedtools\git%5CundCompare\runparseall=false
      usertools\storedtools\git%5CundCompare\runrescan=false
      usertools\storedtools\git%5CundCompare\runBefore=false
      usertools\storedtools\git%5CundCompare\runafter=false
      usertools\storedtools\names=git\\log, git\\setRepo, git\\undCompare