MPS 2024.1 ヘルプ

マイグレーション

言語が公開され、ユーザーがそれを使い始めた後、言語の作者は言語定義へのさらなる変更に注意しなければなりません。特に、概念を削除したり、プロパティ、子、概念への参照を追加および削除すると、前の言語バージョンと次の言語バージョンの間に互換性がなくなります。次の言語バージョンに更新すると、言語のユーザーに影響があります。自分のモデルが言語定義と一致しなくなり、適切なエラーがモデルから報告されるためです。

MPS はプロジェクトで使用されている言語のバージョンを追跡し、言語の使用箇所を最新のバージョンにアップグレードするための自動移行を提供します。言語設計者は、メンテナンス " 移行 " コードを作成してユーザーコードに対して自動的に実行し、言語定義に加えられた変更に準拠するようにユーザーコードを変更することができます。これは言語の移行と呼ばれます。

完全な言語の移行ストーリーには、いくつかの側面があります。

  • 言語設計者は、ユーザーコードを移行するためのスクリプトを作成し、言語とバンドルすることができます。

  • MPS はクライアントコードで使用されている言語バージョンを自動的に追跡します

  • MPS は、ユーザーのプロジェクトがすべての言語変更で最新であることを制御します

  • MPS は必要に応じて必要な移行を実行します

MPS では、2 種類の移行方法があります。

  • 言語の移行 - 次のバージョンの言語定義に準拠するようにプロジェクトをアップグレードするマイグレーション。各言語の移行は、言語定義のバージョンに関連付けられています。

  • プロジェクトのマイグレーション - これらは言語の使用箇所によって引き起こされるのではなく、代わりにそれら自身が実行されるべき条件を定義します。これらの移行は常にプロジェクト全体に適用されます。

言語バージョン

言語は、モジュール定義(.mpl)ファイルにバージョン番号を格納します。この数字は、言語の「マイグレーション」の側面で新しいマイグレーションが作成されたときに増加します。

言語を使用するモジュールには、モジュール(.msd、.mpl)ファイル内の使用されている各言語参照に関連付けられたバージョン番号が含まれています。これらはモジュールによって使用される言語バージョンを表します。このモジュールに対して対応する移行を実行して新しい言語バージョンに移行すると、番号が変わります。

言語のバージョン番号は、言語の Properties ダイアログで手動で表示および変更できます。

Migrationx1003.png

利用可能な 2 つの番号があることに注意してください。

  • 言語バージョン - 言語の構造が変わるたびに更新されます

  • モジュールバージョン - モジュール内のノードへの参照が移行されるたびに更新されます。ソースのあるモジュール(ノードの移動など)で移行を実行する場合は、移行が必要です。移行は、参照または依存するモジュールで実行されます。モジュールバージョンはそれを追跡します。

移行アシスタント

現在開いているプロジェクト内のモジュールが、現在存在しているものよりも古い言語のバージョンを参照していることを MPS が検出すると、移行アシスタントが実行されます。プロジェクトを最新バージョンの言語に更新するためにマイグレーションを実行するかどうかをユーザーにプロンプトします。

Migration1003.png

実行される移行の詳細リストがユーザーに提示されます。

Migration1.png

移行前チェック - 移行が開始されると、MPS はプロジェクトの有効性と移行への適合性をチェックします。これには、問題の言語のバージョンギャップをカバーするために必要なすべての移行スクリプトが利用可能かどうかの確認、使用されているすべてのライブラリがすでに移行されているかどうかの確認、プロジェクト内の壊れた参照の検出と報告、言語が全体的かどうかの確認が含まれます。問題の中には、以降の移行の実行をブロックするものもあれば、ユーザーが明示的に続行を許可できるものもあります。

ユーザーが移行を開始すると、プロジェクトは完全に移行されます。移行を妨げる問題がある場合、移行されていないコードのリストとともに問題のリストがユーザーに提示されます。

更新前チェック - メインメニュー | 移行 | Run Pre-Update Check メニュー項目は、MPS バージョンを更新する前に、プロジェクトに対して実行することをお勧めするアクションをトリガーします。これを古い MPS インスタンスで実行して、新しいバージョンの MPS で問題が発生する可能性がある、移行されていない残り物がないことを確認します。つまり、プロジェクトで使用されている言語のすべての移行スクリプトのすべての check() メソッドを呼び出します。アクションは、これらのケースで考えられるすべての問題を修正しようとします。

  • 古い言語バージョンを使用して作成された新しいノードは、移行されていないブランチから移行されたブランチにマージされました。

  • 一部のノードは完全に移行されませんでした。これは通常、移行が複雑すぎるために移行を断念し、代わりにこれらのノードを手動で移行することを推奨したためです。

再実行可能な移行を実行する - メインメニュー | 移行 | 移行 | 再実行可能な移行の実行メニュー項目は、利用可能なすべての再実行可能な移行を実行するアクションをトリガーします。再実行可能な移行は、すでに移行されたコードで実行しても安全であると宣言されているため、このアクションは移行する必要があるコードのみを移行し、最新のコードには影響しません。

ベースライン MPS バージョン

新しいプロジェクトが作成されると、そのプロジェクトの「ベースライン」MPS バージョンが保存されます。プロジェクトのベースラインバージョンより前の MPS バージョンからのプロジェクトの移行は適用されません。これにより、作成者はプロジェクトの移行に指定された希望の「ベースライン」バージョンを柔軟に装備できるようになります。これは、プロジェクト移行のインターフェース「getBaselineVersion():int」メソッドで指定できます。どのプロジェクトでも、ユーザーは移行メニューからプロジェクトの移行を手動で実行して適用できます。

プロジェクト内でベースラインバージョンが検出されない場合、2018.1 がそのベースラインバージョンとみなされます。このような場合、移行ポップアップは次のメッセージをユーザーに通知します。

MigProjectBase1.png

言語の移行を定義する

移行は、言語定義の移行の側面移行クラスとして定義されます。移行クラスは、jetbrains.mps.lang.migration 言語で定義された MigrationScript コンセプトのノードです。

Migration1001.png

言語の番号と移行

  • 各移行スクリプトの名前には番号が付いています

  • 各移行スクリプトは from version プロパティを定義します

新しい移行スクリプトが作成されると、言語バージョンが 1 増加し、移行の fromVersion フィールドが言語バージョンの古い値に設定されます。これで、作成された移行スクリプトが古いバージョンから新しいバージョンへの移行を実行すると言うことができます。

言語の番号と移行のヒントとコツ

  • 移行を見逃すことはできません。言語にバージョン X とバージョン Y からの移行が含まれている場合は、X と Y の間の各バージョンの移行も含まれているはずです。一部のバージョンで移行が見つからない場合、これはバージョン X から移行できないことを意味します。そのような言語の生成はエラーで終わるでしょう。

  • 言語のすべての移行を保存する必要はありません。一部の言語が「公開」されていて、古い移行の一部を削除する必要がある場合は、削除することもできます。残りのマイグレーションの from バージョンは、範囲 A..B を形成するはずです。ここで、A は任意の古いバージョンで、B = <現在のバージョン> - 1 です。

  • マイグレーションが誤って作成され、公開されていない(つまり、自分のプロジェクトで実行したユーザーがいない)場合は、自由に削除できます。移行を削除した後、言語のコンテキストメニューから言語のバージョンを修正を実行します - このアクションにより、言語のバージョンを最後の移行のバージョンと同期させることができます。これをするとき十分気をつける

Migration1002.png

移行の仕組み

Migrationx1002.png

マイグレーションによって提供される可能性があるいくつかのオプションの要素があります。

  • 実行後 - 移行スクリプト間に順序付けの制約を設ける

  • アノテーションデータを生成します - このスクリプトによって生成され、後の移行スクリプトによって消費される可能性のある移行データを保持するために使用される ConceptDeclaration を指定します。

  • アノテーションデータが必要 - 以前の移行スクリプトによって生成された移行データを表すために使用される ConceptDeclaration を指定します。また、この移行スクリプト内でデータを表す論理名をデータに付けます。

  • データを生成する (非推奨) - 移行データを転送する従来の変種は、ノードアノテーションの代わりに外部ファイルを使用します。

  • データが必要です (非推奨) - 移行データの転送の従来の変種。

  • description - スクリプトのわかりやすい説明文

  • execute メソッド - 各移行は、ユーザーモデルの実際のモデル変換を実行する execute() メソッドを定義します。 このメソッドは、ユーザーモジュールをパラメーターとして受け取り、必要なアノテーションデータセクションで定義された要素を参照する場合があります。

  • isRerunnable - 再実行可能であると宣言する移行は、以前にこの移行ですでに移行されている可能性のあるプロジェクトに対して、安全に繰り返し実行できます。

  • チェック方法 - 各移行では、プロジェクト内のすべてのノードで移行が完全に実行されたことを確認する check() メソッドを定義できます。このメソッドは、移行の execute() が終了した直後に実行され、手動の更新前チェックアクション ( メインメニュー | 移行 | 更新前チェックの実行 ) の一部としても実行されます。check() メソッドは、移行に失敗した一連の NotMigratedNode インスタンスを返します。

データ本番と消費

移行スクリプト間でデータを渡す機能は、移行プロセスを分割するのに役立ちます。たとえば、ある移行スクリプトはノードを古い概念から新しい概念に移行し、次の移行スクリプトは元のノードへのすべての参照を移行して新しいノードを指すようにします。これが機能するためには、最初のスクリプトが古いノードと新しいノードの ID を保存し、生成されたデータとしてマッピングを公開する必要があります。2 番目の移行スクリプトは、必要なデータとしてデータを消費します。古いノードへの参照を更新する必要があるたびに、データは新しいノードの ID を見つけるために使用されます。技術的には、データの生成とは、データが関連付けられている場所に十分近いノードに、データを含む特別な属性をアタッチすることです。モデル全体に関連しているためにアノテーションを配置する特定の場所がない場合、データノードは現在のモデルの新しいルートとしてアタッチされます。

データを含むノードを生成する移行スクリプトは、そのようなノードの概念を宣言し、putData () 構造を使用して、そのような各アノテーションをモデルに挿入する必要があります。

producedata.png

データを含むノードは、データが生成されたモジュールに応じて、他のモジュールで実行されている他の移行スクリプトによって取得できます。

requiredata.png

移行スクリプトの順序

アノテーションデータ必要とし、アノテーションデータセクションを生成することによって表現される移行スクリプト間の暗黙の依存関係は、移行スクリプトの正しい順序付けに注意を払うでしょう。スクリプトがいくつかのモジュールを移行するとき、このモジュールとそのすべての依存関係のために保存されたデータを使うことができるため、消費するスクリプトはモジュールのすべての依存関係で必要なすべてのプロデューサーを走らせた後に初めてモジュールの移行を始めます。これらの依存関係を明示的に表現する必要はありません。

ただし、(依存関係を考慮せずに) 同じモジュールに対して他のスクリプトが実行された後でのみ、あるスクリプトを実行する必要がある場合、そのような順序制約は、execute after セクションを通じて表現できます。たとえば、あるプロパティがある概念から、別の言語で宣言されているそのスーパーコンセプトに移動された場合、その移行は 2 つの移行スクリプトで表現できます。最初のスクリプトはサブコンセプトに適用され、古い非推奨プロパティから新しいプロパティにプロパティ値をコピーします。2 番目のスクリプトはスーパーコンセプトに適用でき、サブコンセプトのインスタンスではないスーパーコンセプトのインスタンスに対して、何らかのデフォルト値を使用して新しいプロパティを開始します。そして、2 番目のスクリプトが、移動されたプロパティの値に応じて他の初期化を実行すると仮定します。2 番目のスクリプトは最初のスクリプトの後でのみ、すべてのモジュールで実行する必要があります。

移行を定義するための言語

jetbrains.mps.lang.migration 言語は、移行スクリプトに固有のすべての概念を定義します。あなたの移行を定義するときは、jetbrains.mps.lang.smodel と一緒 BaseLanguage を使用することができますし、 モデルを操作するための .query 言語。 ofType <model> 構造は、渡された SModule に含まれるモデルを取得するために特に役立つ場合があります。

sequence<SModel> models = m.getModels();&nbsp; models.ofType<model>.selectMany({~model => model.nodes(BaseDocComment); }).forEach({~node => ... });

通常の移行では、最初に移行アスペクトモデルを移行から除外し、次に移行が必要なノードをスキャンします。新しいノードが作成され、古いノードの値と子で初期化されます。その後、古いノードが新しいノードに置き換えられます。新しいノードの ID を古いノードの ID の値に設定すると、ターゲットを失うことなくこのノードへの参照を移行できます。

void execute(SModule m) { sequence<model> models = ((sequence<model>) m.getModels()).where({~it => !it.isAspectModel(migration); }); models.selectMany({~m => m.nodes(OldComponent); }).forEach({~oldNode => node<NewComponent> newNode = <NEW component $( oldNode.name )$ {>; *( oldNode.member )* ((SNode) newNode/).setId(((SNode) oldNode/).getNodeId()); oldNode.replace with(newNode); }); }

注: この例は、「移行」サンプルプロジェクトから取得されています。サンプル移行では、新しいノードのインスタンス化と、古いノードのプロパティと子を新しいノードにコピーする操作を容易にするために、引用符と逆引用符が使用されています。新しいノードの ID は、「SNode.setId()」メソッドを使用して明示的に設定されます。ノードの SNode インスタンスを取得するには、「セマンティックダウンキャスト」(別名「/」) 演算子を使用する必要があります。

概略図

  1. 変換は何らかのノードに適用されます。その結果、古いノードへの参照(No で呼び出し)と新しいノード(Nn)への参照があります。

  2. No の子孫の ID は自動的に保持されます。もし変換された子孫ノードが変換後の出力ノードの子孫である場合、すでに同じ ID を持ちます。

  3. No:MPS の ID は、No が出力ノードの子孫であるかどうかを決定します。

    1. もしそうなら、すでに No を指している参照のターゲットを持っています。(これは「折り返し」の場合です - 変換の結果として、ノードは別のノードに「折り返されます」。)

    2. いいえの場合、Nn は No の ID を取得します。(これは、ノードの概念を変更した場合ですが、古いノードは新しいものと意味的に同じです。)

  4. 包含モデルでは、No は Nn に置き換えられます。

コンセプト交換

言語設計者が言語の概念を削除して、おそらく新しい概念に置き換えることにした場合、言語の概念定義をすぐに言語から削除しないでください。代わりに、この概念を最初に非推奨にし、ユーザーコードを非推奨の概念から移行するための移行スクリプトを提供する必要があります。

廃止予定のコンセプトは、廃止予定のコンセプトの後のバージョンでは完全に削除できます(ただし、削除する必要はありません)。廃止予定のコンセプトを参照している移行スクリプトも削除されました。

プロジェクト移行の定義

プロジェクトの移行は通常、言語開発者ではなく、MPS チームがモデルファイル形式の変更、モジュールの依存関係システム、その他のプロジェクト全体のことを記述するために使用します。

プロジェクトの移行はプロジェクト全体に対して実行されるため、プロジェクトの一部が変更されたときに移行がどのように機能するかを考えるのは MPS 開発者の責任です。例: ユーザーは VCS からプロジェクトを更新できます。この場合、プロジェクトが一度移行されたことを知るだけでは不十分な場合があります。更新されたモジュールは、引き続き移行する必要がある場合があります。

MPS は、プロジェクトの移行が実行される順序を保証しないため、基本的に、相互に依存するプロジェクトの移行を作成することはできません。

それでも、ユーザーは独自のプロジェクト移行を作成できます。プロジェクトの移行に特別な言語はないため、基本的に Java/BaseLanguage クラスとして記述され、plugin.xml を介して提供されます。さらに、すでに MPS プラグインがあり、そこにプロジェクトの移行を記述していると仮定します。

新しいプロジェクト移行の追加 - バリアント 1

これは、2 つの可能なアプローチのうち、より新しく、より単純なものです。

  • プラグインソリューションを作成し、「Java」タブのプロパティで「MPS への拡張機能の提供」を有効にします。それに「プラグイン」モデルを追加し、jetbrains.mps.lang.standalone 言語をモデルにインポートします。

  • モデルに StandalonePluginDescriptor を作成します。

  • ProjectMigration インターフェースを実装する移行用のクラスを作成します。ほとんどの場合、BaseProjectMigration クラスから継承すると便利です。

  • 新しい移行に貢献する ApplicationPlugin を作成します。init() メソッドは、ProjectMigrationsRegistry.getInstance()。addProjectMigration() メソッドを使用して移行を登録する必要があります。dispose() メソッドは、ProjectMigrationsRegistry.getInstance()。removeProjectMigration() メソッドを使用して移行の登録を解除する必要があります。

新しいプロジェクト移行の追加 - バリアント 2

  • ProjectMigration インターフェースを実装する移行用のクラスを作成します。ほとんどの場合、BaseProjectMigration クラスから継承すると便利です。

  • 新しい移行に貢献する ApplicationComponent を作成します。plugin.xml に登録することを忘れないでください

  • ProjectMigrationsRegistry.addProjectMigration() メソッドを使用して、作成された ApplicationComponent からのすべてのプロジェクト移行を提供します

プロジェクト移行からのデータの保存

プロジェクトの移行では、MigrationProperties プロジェクトコンポーネントを使用してデータを永続化できます。永続化されたデータはプロジェクトの .mps フォルダーに保存されるため、VCS を介してプロジェクトの開発者間で共有されます。

マルチブランチ

複数のブランチを使用するプロジェクトの移行には、さらにいくつかの課題があります。詳細については、分岐を伴う移行の使用を参照してください。

移行 Ant タスク

ant スクリプトからプロジェクト内のすべての移行を実行する ant タスクがあります。このタスクは、移行の自動テストと、プロジェクトが移行されたかどうかの確認に使用できます。

このタスクでは MPS ホームパスを以下のように設定する必要があります

  1. mpshome タスク属性を定義しますか、

  2. mps_home 環境プロパティの定義または

  3. mps.home 環境プロパティの定義 - これが推奨される方法です

ホームパスは、build.txt ファイルを含むフォルダーへのパスです。例: Mac OS ではこれは "/Contents/" で終わります

リポジトリの内容は <repository> タグを使用して指定できます。

Repositoryx1.png

プロジェクトをマイグレーションするためにプラグインが必要な場合は、これを <migrate> ant タスクで指定できます。対応するプラグインは、その依存関係とともに有効になります。

MigratePlugins.png

The task supports multiple project specifications (you can migrate several projects at once). Either use nested <project path="” /> elements or a regular Ant ’ s <dirset> to enumerate project locations for the task. 移行タスクには次の 3 つの属性を使用できます。

  • makeDistribModules - 移行自体を実行する前に、MPS ディストリビューションモジュールをコンパイルして再ロードする必要があるかどうかを示します。

  • autoPluginDiscovery - タスクがインストールされたプラグインを自動的に検出してロードするかどうかを示します。プラグインがタスクの一部として明示的に指定されている場合は、false に設定します。

  • haltOnDependencyError - 依存関係の一部が移行されていないことが移行プロセスで検出されると、デフォルトで移行が停止します。この属性により、ant タスクはそのような場合に移行を続行できるようになります。移行されていない依存関係が検出されるとすぐに移行プロセスを停止するのがデフォルトの動作です。

migrationtaskattrs.png

サンプル

移行を定義する方法の例については、MPS にバンドルされている移行サンプルプロジェクトを確認してください。相互に接続された 2 つの単純な言語を移行するための移行スクリプトが表示されます。そのうちの 1 つはデータを使用して 2 つの移行スクリプト間で移行されたノードに関する情報を渡しますが、もう 1 つはノード ID の操作に依存します。

ローカル履歴ビューでの移行による変更

移行はローカル履歴機能と連携します。

lh1.png

移行を実行した後、各移行によってプロジェクトに加えられたすべての変更を確認することができます。プロジェクトのフォルダー、モジュール、モデルのローカル履歴ビューを開き、任意の 2 つの変更を選択し、Ctrl + D を押して違いを確認します。

lh2.png
lh3.png

ローカル履歴ビューと差分ダイアログから、変更または変更のグループを元に戻すこともできます。

IntelliJ IDEA の移行アシスタント

IntelliJ IDEA プラグインは言語の移行も実行できます。MPS 自体と同様に、移行アシスタントは IDEA プロジェクトのモデルを、現在インストールされている使用言語のバージョンと一致するように更新します。

IDEA-Migration1.png

非推奨コードの発見

非推奨は、ある言語の次のバージョンのいずれかで要素が削除されることを言語のユーザーに示すための推奨メカニズムです。MPS は、ユーザーが廃止予定のコードを排除するのに役立ついくつかの便利なファインダーを提供します。廃止予定の使用箇所の検索は廃止予定の要素のすべての使用箇所を見つけることができます。検出された使用箇所のレポートでは、コード削除の予定バージョンごとにエントリがグループ化されています。これにより、その深刻度を認識し、排除することを優先することが容易になります。

Migx.png