Skip to content

Migration Aspect¶ ⧉

To nicely support language evolution, migrations automatically upgrade client code to use the latest version of a language or multiple languages.

MPS documentation

General

Migrations are necessary as part of languages that change over time. Don't create migrations when you are still in the development phase and don't need to execute them. When using the Refactoring menu in a language, deselect Write migration script, and Write refactoring log to not create migrations.

How do I search for instances of a deprecated concept?

How do I remove migrations?

I want to migrate an MPS project that's many years old. How do you do that?

First of all, make sure that you don't skip too many MPS versions; otherwise, the migration won't succeed. For old projects, you have to edit the module files manually because the structure slightly changed. Open the .mpl files in a text editor and change all occurrences of <language id="%ID" fqName="%FQNAME" version="%VERSION" /> into <language slang="l:%ID:%FQNAME" version="%VERSION" />.

The next step is to start the migration assistant. It will fail because it can't find migrations for some language versions. For example, jetbrains.mps.lang.editor only has a migration starting with version 7. You have to find all occurrences of this language in your project with a text editor and change the attribute version to 7. Then run the migration assistant again and hope that everything works. Some concepts cannot be migrated automatically, such as the substitute and transformation menus. There will also be other issues that have to be fixed manually.

I want to replace node A with another node B where A and B share the same super-concept.

Use the class RefactoringRuntime ⧉. This class can be used to set/unset properties or replace nodes with other ones. Example: RefactoringRuntime.replaceWithNewConcept(old, concept/ComponentConfigRef/)

Are there any best practices for feature branches and language migrations?

How can you detect that migrations are running?

I have code that needs to know if migrations are currently executed, for example, model listeners that update the model when a change by the user appears.

I would like to detect when migrations are executed to prevent this code from executing in the middle of a migration to prevent manipulating the model in an incomplete state during the migration.

PersistenceRegistry.getInstance().isFastSearch() returns false while migrations are running. You can check it in the model lister:

1
2
3
4
5
6
model listener for MyNode { 
    child added in role values (instance, child)->void {
    // don't run as part of migrations.
    if(!PersistenceRegistry.getInstance().isFastSearch()) { return; }
    }
}
contributed by: @abstraktor

Assuming a migration for language A replaces objects of concept C_old with objects of concept C_new. Further, a concept C_B of language B references an object of type Cold. Therefore, you added a new reference of type C_new and marked the old one as deprecated. But how does this migration find the C_new object for the C_old reference?

Read Defining language migrations | MPS documentation ⧉ and look for produces annotation data. This mechanism also ensures the correct order of dependent migrations.

How can you find all references to a specified root node and change these references so they point to another root node?

Search through the methods in RefactoringRuntime ⧉.

Example with finders:

1
2
3
4
5
6
7
SearchResults<node<>> results = execute finders(NodeUsages, node, unspecified scope, <default>); 

foreach result in results.getSearchResults() {
node<> resultObject = result.getObject();
sequence<reference> references = resultObject.references.where({~it => it.target :eq: node; });
references.forEach({~it => resultObject/.setReferenceTarget(it.link, reuseact); });
}

What are module versions, and how/why can other modules depend on modules with different versions than the current module version?

You start with language and module version 0. Reasons why the language version of a language updates:

  • A new migration was added manually.
  • You've executed the action "Correct Language Version."
  • You've set it manually in the module properties.
  • A concept was moved to a different language through the refactoring menu, and a migration script was written.

Reasons why the module version of a language updates:

  • You've set it manually in the module properties.
  • A concept/property/link was moved to a different language through the refactoring menu, and a refactoring log was written.

Reasons why both versions don't update:

  • The language was renamed.
  • Nodes/models are moved to a different solution.

The module version of a solution can't change automatically. I also don't know why you would change it manually. Devkits don't even have the option to change the module version. A good example of a language with different language and module versions is jetbrains.mps.lang.feedback.problem.scopes (language: 0, module: 1) because it only has a refactoring step but no other migrations.

Troubleshooting

Migrations are not only necessary from one language version to another but also for newer MPS versions. Issues can arise because MPS or the IntelliJ platform changes or an even more drastic change occurs like a new Java version. Still, migrating early is a good idea to not fall behind to many MPS versions. New MPS versions not only mean new features but also many bug fixes. While this topic is only marginally related to the migration aspect, it is mentioned here.

When creating root nodes as part of language migration via java model.add root(someRoot), it throws the exception UnsupportedOperationException.

Make sure that the affected model is not non-editable.


Last update: July 18, 2023

Comments