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
- MPS, Feature Branches and Language Migrations: DOs and DON’Ts ⧉ (languageengineering.io's blog)
- Preparing migrations with Git rebase ⧉ (Specific Languages' blog)
- Checking for pending migrations ⧉ (Specific Languages' blog)
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?
- Migrations: Searching for instances ⧉ (Specific Languages' blog)
How do I remove migrations?
- Removing migrations ⧉ (Specific Languages' blog)
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:
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:
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.
- The migration frustration ⧉ (Specific Languages' blog)
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.