マイグレーション
言語が公開され、ユーザーがそれを使い始めた後、言語の作者は言語定義へのさらなる変更に注意しなければなりません。特に、概念を削除したり、プロパティ、子、概念への参照を追加および削除すると、前の言語バージョンと次の言語バージョンの間に互換性がなくなります。次の言語バージョンに更新すると、言語のユーザーに影響があります。自分のモデルが言語定義と一致しなくなり、適切なエラーがモデルから報告されるためです。
MPS はプロジェクトで使用されている言語のバージョンを追跡し、言語の使用箇所を最新のバージョンにアップグレードするための自動移行を提供します。言語設計者は、メンテナンス " 移行 " コードを作成してユーザーコードに対して自動的に実行し、言語定義に加えられた変更に準拠するようにユーザーコードを変更することができます。これは言語の移行と呼ばれます。
完全な言語の移行ストーリーには、いくつかの側面があります。
言語設計者は、ユーザーコードを移行するためのスクリプトを作成し、言語とバンドルすることができます。
MPS はクライアントコードで使用されている言語バージョンを自動的に追跡します
MPS は、ユーザーのプロジェクトがすべての言語変更で最新であることを制御します
MPS は必要に応じて必要な移行を実行します
MPS では、2 種類の移行方法があります。
言語の移行 - 次のバージョンの言語定義に準拠するようにプロジェクトをアップグレードするマイグレーション。各言語の移行は、言語定義のバージョンに関連付けられています。
プロジェクトのマイグレーション - これらは言語の使用箇所によって引き起こされるのではなく、代わりにそれら自身が実行されるべき条件を定義します。これらの移行は常にプロジェクト全体に適用されます。
言語バージョン
各言語は、モジュール定義ファイル(.mpl)にバージョン番号を保存します。この番号は、言語の「マイグレーション」アスペクトで新しいマイグレーションが作成されるたびに増加します。
言語を使用するモジュールには、モジュール(.msd、.mpl)ファイル内の使用されている各言語参照に関連付けられたバージョン番号が含まれています。これらはモジュールによって使用される言語バージョンを表します。このモジュールに対して対応する移行を実行して新しい言語バージョンに移行すると、番号が変わります。
言語のバージョン番号は、言語の Properties ダイアログで手動で表示および変更できます。

利用可能な 2 つの番号があることに注意してください。
言語バージョン - 言語の構造が変わるたびに更新されます
モジュールバージョン - モジュール内のノードへの参照が移行されるたびに更新されます。ソースのあるモジュール(ノードの移動など)で移行を実行する場合は、移行が必要です。移行は、参照または依存するモジュールで実行されます。モジュールバージョンはそれを追跡します。
移行アシスタント
現在開いているプロジェクト内のモジュールが、現在存在しているものよりも古い言語のバージョンを参照していることを MPS が検出すると、移行アシスタントが実行されます。プロジェクトを最新バージョンの言語に更新するためにマイグレーションを実行するかどうかをユーザーにプロンプトします。

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

移住前チェック - 移行が開始されると、MPS はプロジェクトの有効性と移行への適合性をチェックします。これには、問題の言語のバージョンギャップをカバーするために必要なすべての移行スクリプトが利用可能かどうかの確認、使用されているすべてのライブラリがすでに移行されているかどうかの確認、プロジェクト内の壊れた参照の検出と報告、言語が全体的かどうかの確認が含まれます。問題の中には、以降の移行の実行をブロックするものもあれば、ユーザーが明示的に続行を許可できるものもあります。
ユーザーが移行を開始すると、プロジェクトは完全に移行されます。移行を妨げる問題が発生した場合は、問題の一覧と未移行コードの一覧がユーザーに表示されます。
更新前チェック - メインメニュー|移行|更新前チェックの実行メニュー項目は、MPS バージョンを更新する前にプロジェクトに対して実行することが推奨されるアクションをトリガーします。このアクションは、古い MPS インスタンスで実行して、新しいバージョンの MPS で問題となる可能性のある未移行の残骸がないことを確認します。具体的には、プロジェクトで使用されている言語のすべての移行スクリプトのすべての check() メソッドを呼び出します。このアクションは、以下のケースで発生する可能性のあるすべての問題を修正しようとします。
古い言語バージョンを使用して作成された新しいノードは、移行されていないブランチから移行されたブランチにマージされました。
一部のノードは完全に移行されませんでした。これは通常、移行プロセスがそれらのノードを複雑すぎると判断し、代わりに手動移行を推奨したためですが、結局手動移行は実行されませんでした。
再実行可能な移行を実行する - メインメニュー | 移行 | 移行 | 再実行可能な移行の実行メニュー項目は、利用可能なすべての再実行可能な移行を実行するアクションをトリガーします。再実行可能な移行は、すでに移行されたコードで実行しても安全であると宣言されているため、このアクションは移行する必要があるコードのみを移行し、最新のコードには影響しません。
ベースライン MPS バージョン
新しいプロジェクトが作成されると、そのプロジェクトの「ベースライン」MPS バージョンが保存されます。プロジェクトのベースラインバージョンより前の MPS バージョンからのプロジェクトの移行は適用されません。これにより、作成者はプロジェクトの移行に指定された希望の「ベースライン」バージョンを柔軟に装備できるようになります。これは、プロジェクト移行のインターフェース「getBaselineVersion():int」メソッドで指定できます。どのプロジェクトでも、ユーザーは移行メニューからプロジェクトの移行を手動で実行して適用できます。
プロジェクト内でベースラインバージョンが検出されない場合、2018.1 がそのベースラインバージョンとみなされます。このような場合、移行ポップアップは次のメッセージをユーザーに通知します。

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

言語の番号と移行
各移行スクリプトの名前には番号が付いています
各移行スクリプトは from version プロパティを定義します
新しい移行スクリプトが作成されると、言語バージョンが 1 増加し、移行の fromVersion フィールドが言語バージョンの古い値に設定されます。これで、作成された移行スクリプトが古いバージョンから新しいバージョンへの移行を実行すると言うことができます。
言語の番号と移行のヒントとコツ
移行処理が「漏れる」ことは許されません。ある言語にバージョン X とバージョン Y からの移行処理が含まれている場合、X と Y の間の各バージョンについても移行処理が含まれている必要があります。いずれかのバージョンに対応する移行処理が見つからない場合、どのユーザーもバージョン X から移行できないことを意味します。このような言語を生成すると、エラーが発生します。
言語のすべての移行を保存する必要はありません。一部の言語が「公開」されていて、古い移行の一部を削除する必要がある場合は、削除することもできます。残りのマイグレーションの from バージョンは、範囲 A..B を形成するはずです。ここで、A は任意の古いバージョンで、B = <現在のバージョン> - 1 です。
マイグレーションが誤って作成され、公開されていない場合(つまり、どのユーザーも自分のプロジェクトで実行していない場合)、自由に削除できます。マイグレーションを削除した後、言語のコンテキストメニューから「言語バージョンの修正」を実行してください。この操作により、言語のバージョンを最新のマイグレーションのバージョンと同期できます。この操作を行う際は、十分気をつけるを使用してください。

移行の仕組み

マイグレーションによって提供される可能性があるいくつかのオプションの要素があります。
実行後 - 移行スクリプト間に順序付けの制約を設ける
アノテーションデータを生成します - このスクリプトによって生成され、後の移行スクリプトによって消費される可能性のある移行データを保持するために使用される ConceptDeclaration を指定します。
アノテーションデータが必要 - 以前の移行スクリプトによって生成された移行データを表すために使用される ConceptDeclaration を指定します。また、この移行スクリプト内でデータを表す論理名をデータに付けます。
データを生成する (非推奨) - 移行データを転送する従来の変種は、ノードアノテーションの代わりに外部ファイルを使用します。
データが必要です (非推奨) - 移行データを転送する従来の方法。
description - スクリプトのわかりやすい説明文
execute メソッド - 各移行は、ユーザーモデルの実際のモデル変換を実行する execute() メソッドを定義します。 このメソッドは、ユーザーモジュールをパラメーターとして受け取り、必要なアノテーションデータセクションで定義された要素を参照する場合があります。
isRerunnable - 再実行可能であると宣言されたマイグレーションは、以前にこのマイグレーションですでにマイグレーションされたプロジェクトに対しても、安全に繰り返し実行できます。
チェック方法 - 各移行では、プロジェクト内のすべてのノードで移行が完全に実行されたことを確認する check() メソッドを定義できます。このメソッドは、移行の execute() が終了した直後に実行され、手動の更新前チェックアクション ( メインメニュー | 移行 | 更新前チェックの実行 ) の一部としても実行されます。check() メソッドは、移行に失敗した一連の NotMigratedNode インスタンスを返します。
データ本番と消費
移行スクリプト間でデータを渡す機能は、移行プロセスを分割するのに役立ちます。たとえば、ある移行スクリプトはノードを古い概念から新しい概念に移行し、次の移行スクリプトは元のノードへのすべての参照を移行して新しいノードを指すようにします。これが機能するためには、最初のスクリプトが古いノードと新しいノードの ID を保存し、生成されたデータとしてマッピングを公開する必要があります。2 番目の移行スクリプトは、必要なデータとしてデータを消費します。古いノードへの参照を更新する必要があるたびに、データは新しいノードの ID を見つけるために使用されます。技術的には、データの生成とは、データが関連付けられている場所に十分近いノードに、データを含む特別な属性をアタッチすることです。モデル全体に関連しているためにアノテーションを配置する特定の場所がない場合、データノードは現在のモデルの新しいルートとしてアタッチされます。
データを含むノードを生成する移行スクリプトは、そのようなノードの概念を宣言し、putData () 構造を使用して、そのような各アノテーションをモデルに挿入する必要があります。

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

移行スクリプトの順序
マイグレーションスクリプト間の暗黙的な依存関係は、requires アノテーションデータと produces アノテーションデータセクションを通して表現されるため、マイグレーションスクリプトの適切な順序付けが保証されます。スクリプトがモジュールをマイグレーションする場合、そのモジュールとそのすべての依存関係に保存されているデータを使用できます。そのため、コンシューマースクリプトは、モジュールのすべての依存関係に対して必要なすべてのプロデューサーを実行した後でなければ、モジュールのマイグレーションを開始しません。これらの依存関係を明示的に表現する必要はありません。
ただし、依存関係を考慮せずに、同じモジュールに対して他のスクリプトが実行された後にのみ、あるスクリプトを実行する必要がある場合には、実行後のセクションを使用して、そのような順序制約を表現できます。たとえば、あるプロパティが、別の言語で宣言されているスーパーコンセプトに移動された場合、移行は 2 つの移行スクリプトで表現できます。最初のスクリプトはサブコンセプトに適用され、古い非推奨プロパティから新しいプロパティにプロパティ値をコピーします。2 番目のスクリプトはスーパーコンセプトに適用され、サブコンセプトのインスタンスではないスーパーコンセプトのインスタンスに対して、新しいプロパティをデフォルト値で初期化します。そして、2 番目のスクリプトが、移動されたプロパティの値に依存する他の初期化を行うと仮定します。2 番目のスクリプトは、最初のスクリプトの後にのみ、すべてのモジュールで実行される必要があります。
移行を定義するための言語
jetbrains.mps.lang.migration 言語は、マイグレーションスクリプトに特有のすべての概念を定義します。マイグレーションを定義する際には、BaseLanguage を jetbrains.mps.lang.smodel および jetbrains.mps.lang.smodel.query 言語と組み合わせて使用することで、モデルを操作できます。特に、ofType<model> 構文は、渡された SModule に含まれるモデルを取得する際に役立ちます。
一般的な移行では、まず移行対象から移行アスペクトモデルを除外し、次に移行が必要なノードをスキャンします。新しいノードが作成され、古いノードの値と子ノードで初期化されます。その後、古いノードは新しいノードに置き換えられます。新しいノードの ID を古いノードの ID と同じ値に設定することで、このノードへの参照がターゲットを失うことなく移行できるようになります。
注: この例は、「移行」サンプルプロジェクトから取得されています。サンプル移行では、新しいノードのインスタンス化と、古いノードのプロパティと子を新しいノードにコピーする操作を容易にするために、引用符と逆引用符が使用されています。新しいノードの ID は、「SNode.setId()」メソッドを使用して明示的に設定されます。ノードの SNode インスタンスを取得するには、「セマンティックダウンキャスト」(別名「/」) 演算子を使用する必要があります。
概略図
変換は何らかのノードに適用されます。その結果、古いノードへの参照(No で呼び出し)と新しいノード(Nn)への参照があります。
No の子孫の ID は自動的に保持されます。もし変換された子孫ノードが変換後の出力ノードの子孫である場合、すでに同じ ID を持ちます。
No:MPS の ID は、No が出力ノードの子孫であるかどうかを決定します。
もしそうなら、すでに No を指している参照のターゲットを持っています。(これは「折り返し」の場合です - 変換の結果として、ノードは別のノードに「折り返されます」。)
いいえの場合、Nn は No の ID を取得します。(これは、ノードの概念を変更した場合ですが、古いノードは新しいものと意味的に同じです。)
包含モデルでは、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 ホームパスを以下のように設定する必要があります
mpshome タスク属性を定義しますか、
mps_home 環境プロパティの定義または
mps.home 環境プロパティの定義 - これが推奨される方法です
ホームパスは、build.txt ファイルを含むフォルダーへのパスです。例: macOS の場合、これは "/Contents/" で終わります。

タスクは複数のプロジェクト仕様をサポートします (複数のプロジェクトを一度に移行できます)。ネストされた <project path="” /> 要素または通常の Ant の <dirset> を使用して、タスクのプロジェクトの場所を列挙します。
リポジトリの内容は、<repository> タグを使用して指定できます。移行タスクには次の 3 つの属性を使用できます。
makeDistribModules - MPS ディストリビューションモジュールを、移行処理自体を実行する前にコンパイルして再ロードする必要があるかどうかを示します。
autoPluginDiscovery - タスクがインストール済みのプラグインを自動的に検出してロードするかどうかを示します。タスクの一部としてプラグインを明示的に指定する場合は、false に設定してください。
haltOnDependencyError - 依存関係の一部が移行されていないことが移行プロセスで検出されると、デフォルトで移行が停止します。この属性により、ant タスクはそのような場合に移行を続行できるようになります。移行されていない依存関係が検出されるとすぐに移行プロセスを停止するのがデフォルトの動作です。
haltOnPreCheckFailure - 移行プロセスが、実際の移行が開始される前に実行されるモデルチェッカーによって報告されたエラーを検出した場合、このフラグは移行プロセスを停止するかどうかを制御します。
force - true の場合、.allow-pending-migrations ファイルが存在する場合でも移行を実行できるようになります。これは、自動移行を実行してはならないことを示します。
プロジェクトをマイグレーションするためにプラグインが必要な場合は、これを <migrate> ant タスクで指定できます。対応するプラグインは、その依存関係とともに有効になります。

サンプル
マイグレーションの定義方法の例については、MPS に同梱されているマイグレーションサンプルプロジェクトを参照してください。そこには、相互に関連する 2 つのシンプルな言語をマイグレーションするためのマイグレーションスクリプトが含まれています。一方のスクリプトは、データを使用してマイグレーションされたノードに関する情報を 2 つのマイグレーションスクリプト間で受け渡し、もう一方のスクリプトはノード ID の操作に依存しています。
ローカル履歴ビューでの移行による変更
移行はローカル履歴機能と連携します。

マイグレーションを実行した後、各マイグレーションによってプロジェクトに加えられたすべての変更を確認できます。プロジェクトのフォルダー、モジュール、モデルのローカル履歴ビューを開き、任意の 2 つの変更を選択して Ctrl + D を押すと、差分が表示されます。


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

移行が適用されたときの移行されたノードのログ記録
MPS プラグインモデルは、自動マイグレーション中にマイグレーションの変更を追跡できるようになりました。これにより、どのマイグレーションスクリプトがモデルに特定の変更をもたらしたかを簡単に分析できます。ModuleRuntime.Activator#contribute(ActivatorContext) から ActivatorContext#extension() を使用して MigrationListener を登録すると、マイグレーション中の変更をログに記録できるようになります。
関連ページ:
分岐を伴う移行の使用
分岐を伴うマイグレーションの使用:言語の進化をうまくサポートするために、移行ではクライアントコードを自動的にアップグレードして最新バージョンの言語または複数の言語を使用します。マイグレーションの資料では、MPS でマイグレーションを作成して実行するプロセスについて説明しています。簡単に言うと、言語の変更に準拠するようにモデルを変更するために、言語の作成者は移行を提供できます。MPS はプロジェクトで使用されている言語バージョンを自動的に追跡し、必要な移行を適用するようにエンドユーザーに要求しま...
TextGen
TextGen 言語アスペクト:導入 TextGen 言語の側面は、モデルからテキストへの変換を定義します。モデルを直接テキスト形式に変換する必要があるたびに便利です。この言語には、テキストを印刷し、ノードをテキスト値に変換し、出力に適切なレイアウトを与えるための構造が含まれています。TextGen アスペクトでは、バイナリ形式での出力も可能です。これは、たとえば、jetbrains.mps.lang.resources 言語のアイコンのイメージを生成するために活用されます。このメカニズムにより、直接...