MPS 2026.1 ヘルプ

ハウツー MPS Make フレームワークへの統合

ファセットを構築

概要

基本的に他のビルドシステムやメイクシステムと同様に、MPS の make は一連のステップ(ターゲット)を実行してアーティファクトを作成します。必要な make ステップの全体的な順序は、各ビルドターゲットに指定された相対的な優先順位から導き出されます(ターゲット A は B より前に実行される必要があり、B は C より前に実行される必要があるため、全体的な順序は A、B、C となります)。

完全なビルドプロセスでは、モデルのテキストへの生成、これらのモデルのコンパイル、サーバーへのデプロイ、graphviz ソースファイルからの .png ファイルの生成など、いくつかの問題に対処できます。MPS では、このようなさまざまなビルドアスペクトはビルドファセットで実装されます。ファセットは、共通の懸念事項に対処するターゲットの集合です。

facet10.png

ファセット内のターゲットは設定パラメーターを交換できます。例: 全体的な make プロセスの早い段階で実行するように宣言されているターゲットは、設定パラメーターを収集し、第 2 のファセットに渡します。このファセット内パラメーター交換を実現するためのメカニズムは、プロパティと呼ばれます。さらに、ターゲットは、make プロセス中にクエリを使用してユーザーから情報を取得できます。

facet11.png

全体的な make プロセスは、パイプとフィルターのパターンに沿って編成されています。ターゲットはフィルターとして機能し、配信されているデータストリームを処理します。ターゲット間を流れるデータはリソースと呼ばれます。さまざまな種類のリソースがあり、すべて異なる Java インターフェースおよびタプルとして表されます。

facet12.png
  • MResource には、ユーザーが作成した MPS モデルが含まれています。これらのモデルは、プロジェクトのソリューションと言語に含まれています。

  • GResource は、出力モデルを含む生成プロセスの結果、つまり生成が完了した後のモデルの最終状態を表します。これらは一時的なモデルであり、一時的なモデルを保存ビルドオプションを使用してインスペクションできます。

  • TResource は text-gen の結果を表します

  • CResource は、Java クラスのコレクションを表します

  • DResource は、モデルに対するデルタ変更のコレクションを表します (IDelta)

  • TextGenOutcomeResource は、textgen によって生成されたテキストファイルを表します

ビルドターゲットはインターフェースを指定します。パイプとフィルターのパターンに従って、インターフェースは make ターゲットに出入りするデータの種類を記述します。これは、前述のリソースタイプと、ターゲットがこれらのリソースに適用する処理の種類によって指定されます。以下の 4 つの処理ポリシーが定義されています。

  • transform がデフォルトです。このポリシーは、入力リソースタイプのインスタンスを消費し、出力リソースタイプのインスタンスを生成します。(たとえば、MResources を消費して TResources を生成する可能性があります。)

  • 宣言する入力を消費しますが、出力は生成しません。* produce は何も消費しませんが、出力を生成します

  • パススルーはいかなるリソースにもアクセスせず、生産も消費もしません。

メイク処理はモデル生成よりも粒度が粗いことに注意してください。つまり、すべてのモデルジェネレーターを実行するファセットが 1 つだけ存在します。MPS 生成プロセスに(モデル生成の前後に何かを行うのではなく)追加のターゲットを「挿入」する必要がある場合は、generate ファセットのリファクタリングが必要です。これは本稿の範囲外です。

サンプルファセットの構築

MPS 用の C 言語ベース言語を構築する mbeddr.com プロジェクトの一環として、実際の C コンパイラーを MPS のビルドプロセスに統合する必要があります。具体的には、C 言語ベース言語で記述されたプログラムには、Makefile を生成する機能が含まれています。この Makefile は、Makefile とそれに対応するすべての .c ファイルおよび .h ファイルが生成された後、つまり MPS の make プロセスの最後に実行される必要があります。

これを行うために、2 つのターゲットを持つ make ファセットを構築しました。1 つ目は、入力モデルをインスペクションし、textgen の後に Makefile を含む可能性のあるディレクトリの絶対パスを収集します。次に、2 番目のターゲットは、このディレクトリに Makefile というファイルが実際にあるかどうかを確認し、そこで make を実行します。上記の概要で説明したように、2 つのターゲットはプロパティを介してディレクトリを交換します。

最初のターゲット: ディレクトリの収集

ファセットは、言語定義の plugins の側面に存在します。FacetDeclaration のインスタンスを作成できるように、プラグインモデルに {{jetbrains.mps.make.facets} 言語が含まれていることを確認してください。モデル uses がファセットを宣言する言語である場合、ファセットはモデルの作成プロセスの一部として実行されます。

このファセットは runMake と呼ばれます。これは TextGenGenerate に依存しています。これらの 2 つのファセットへの依存関係を指定することで、それらのファセット内のターゲットに対するターゲットの優先順位を宣言できるようになります。

facet runMake extends <none> Required: TextGen, Generate

最初のターゲットは collectPaths と呼ばれます。入力モデルと連絡を取るために、{{transform IMResource-> IMResource} として指定されています。ファセットは、優先順位として after configurebefore generate を指定します。後者は明らかです。なぜなら、モデルがテキストに生成される前にモデルを取得したいからです。前者の優先順位は、基本的に、make プロセスが初期化された後にこのターゲットを実行することを示しています (言い換えれば、「最初に」何かをしたい場合は、これら 2 つの優先順位を使用してください。)

target collectPathes overrides <none> { resources policy: transform IMResource -> IMResource Dependencies: after configure before generate

次に、make ファイルを含むモジュールに関する情報と生成されたコードが存在するディレクトリへのパスを格納するために使用するプロパティ pathes を宣言します。

Properies: list<[string, string]> pathes;

それでは、ターゲットの実装コードを見ていきましょう。基本的な構造は以下のとおりです。まず、pathes リストを初期化します。次に、入力(リソースの集合)を反復処理し、各入力に対して何らかの処理を行います(詳細は後述)。その後、output ステートメントを使用して入力データを出力します。つまり、ターゲットに入力されたものをそのまま出力します。最後に success ステートメントを使用して、このターゲットを正常に終了します(success を最後に使用するのは省略可能です。これはデフォルトです)。何らかの問題が発生した場合は、failure ステートメントを使用してターゲットを不成功に終了させることができます。

(input)->void { pathes = new linkedlist<[string, string]>; input.forEach({~inpt => (module, models) res = ((module, models)) inpt; // do stuff. See below. }); output input; success; }

実際の処理は、MPS データ構造に対する単純な Java プログラミングです。

res.models.forEach({~model => string path = res.module.getFacet(GenerationTargetFacet.class).getOutputLocation() + "/" + model.getLongName().replaceAll("\\.", "/"); string locationInfo = res.module.getModuleFqName() + "/" + model.getLongName(); pathes.add([path, locationInfo]); });

モジュールの GenerationTargetFacet ファセットの getOutputLocation メソッドを使用して、特定のモジュールがそのコードを生成するパスを取得します (これはモデルプロパティでユーザーが構成できます)。次に、モデルのドット名を取得し、ドットをスラッシュに置き換えます。これは、そのモジュールでモデルの生成されたファイルが最終的に配置される場所であるためです (これを確認するには、サンプル MPS プロジェクトを調べましょう)。次に、モジュールの名前とモデルの名前をスラッシュで区切って保存し、2 番目のターゲット (変数 locationInfo}). We add the two strings to the {{pathes コレクションを介して) のログメッセージを改善します。この pathes プロパティは、ファセットの 2 番目のターゲットによってクエリされます。

2 番目のターゲット: 実行

こちらはリソースを扱う必要がないため、pass through ポリシーを使用します。必要な入力はすべて、前述の collectPaths ターゲットのプロパティから取得できます。この 2 番目のターゲットは after collectPaths}, {{after textGenbefore reconcile を実行します。collectPaths}, since it uses the property data populated by it. It has to run after {{textGen}, otherwise the make files aren't there yet. And it has to run before {{reconcile}, because basically everything has to run before {{reconcile の後に実行する必要があることは明らかです。

target callMake overrides <none> resources policy: pass through Dependencies: after collectPathes after textGen before reconcile

それでは、実装コードを見ていきましょう。まず、collectPathes.pathes プロパティから、実際に Makefile を含むエントリをすべて取得します。該当するエントリが見つからない場合は、success を返します。

sequence<[string, string]> modelDirectoriesWithMakefile = collectPathes.pathes. where({~it => new File(it[0] + "/Makefile").exists(); }); if (modelDirectoriesWithMakefile.isEmpty) { success; }

次に、プログレスインジケータ言語を使用して、make ファイルを含むディレクトリがあるのと同じ数のワークユニットでプログレスバーを設定します。

begin work "run make" covering ALL units of total work left, expecting modelDirectoriesWithMakefile.size units;

次に、{{modelDirectoriesWithMakefile} コレクションのすべてのエントリを繰り返し処理します。ループでは、進行状況インジケーターを進めてから、Java 標準 API を使用して make ファイルを実行します。

foreach dirInfoTuple in modelDirectoriesWithMakefile { try { advance 1 units of "run make" with comment "running make for " + dirInfoTuple[1]; Process process = Runtime.getRuntime(). exec("make", new string[0], new File(dirInfoTuple[0])); if (process.waitFor() > 0) { error "make failed with exit code " + process.exitValue() + " for " + dirInfoTuple[1]; } else { info "make finished successfully for " + dirInfoTuple[1]; } } catch (Exception ex) { error ex.getMessage(), ex; } }

ターゲットをまとめるには、finish ステートメントを使用してプログレスバーをクリーンアップします。

finish "run make";
2026 年 5 月 06 日