diff --git a/cli/src/integrationTest/kotlin/com/specificlanguages/mops/cli/MakeIntegrationTest.kt b/cli/src/integrationTest/kotlin/com/specificlanguages/mops/cli/MakeIntegrationTest.kt index c7e76eb0..dc9f3f71 100644 --- a/cli/src/integrationTest/kotlin/com/specificlanguages/mops/cli/MakeIntegrationTest.kt +++ b/cli/src/integrationTest/kotlin/com/specificlanguages/mops/cli/MakeIntegrationTest.kt @@ -7,6 +7,7 @@ import com.specificlanguages.mops.protocol.ProtocolJson import org.junit.jupiter.api.extension.RegisterExtension import org.junit.jupiter.api.parallel.ResourceLock import kotlin.test.Test +import kotlin.test.assertContains import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -29,6 +30,21 @@ class MakeIntegrationTest { ) } + @Test + fun `make module leaves out a solution created with the not-generated preset`() { + val project = daemons.project("mps-json", copy = "not-generated") + val created = project.run("create", "solution", "example.analyzed") + assertEquals(0, created.exitCode, created.output) + val model = project.run("create", "model", "example.analyzed.main", "--module", "example.analyzed") + assertEquals(0, model.exitCode, model.output) + + val result = project.run("make", "module", "example.analyzed") + + assertEquals(0, result.exitCode, result.output) + assertEquals("nothing to generate", result.stdout.trim(), result.output) + assertContains(result.stderr, "warning\tnot generating the models of example.analyzed") + } + companion object { @JvmField @RegisterExtension diff --git a/cli/src/main/kotlin/com/specificlanguages/mops/cli/make/makeHelp.kt b/cli/src/main/kotlin/com/specificlanguages/mops/cli/make/makeHelp.kt index 5a772cb6..04aa83fd 100644 --- a/cli/src/main/kotlin/com/specificlanguages/mops/cli/make/makeHelp.kt +++ b/cli/src/main/kotlin/com/specificlanguages/mops/cli/make/makeHelp.kt @@ -6,10 +6,11 @@ internal const val MAKE_OUTPUT_HELP = "a one-line action report on stdout: `made module(s): success`, `made module(s): FAILED ( " + "error(s))`, or `nothing to generate`. The command exits non-zero when the make reports errors." -/** What a make deletes, for the help footers of both make commands. */ +/** What a make generates and deletes, for the help footers of both make commands. */ internal const val MAKE_CLEAN_HELP = "The make generates every model of the made modules and deletes files in their output folders that it no " + - "longer generates." + "longer generates. It leaves out the models of modules without generation-target facets, such as " + + "solutions created with the `not-generated` usage preset, and names each such module in a warning." /** How the generation options behave, for the help footers of both make commands. */ internal const val MAKE_GENERATION_HELP = diff --git a/daemon/src/main/kotlin/com/specificlanguages/mops/daemon/ProjectMake.kt b/daemon/src/main/kotlin/com/specificlanguages/mops/daemon/ProjectMake.kt index cf8a1883..3aada442 100644 --- a/daemon/src/main/kotlin/com/specificlanguages/mops/daemon/ProjectMake.kt +++ b/daemon/src/main/kotlin/com/specificlanguages/mops/daemon/ProjectMake.kt @@ -5,13 +5,16 @@ import com.specificlanguages.mops.daemon.core.MpsRequestException import com.specificlanguages.mops.protocol.MakeGenerationOptions import com.specificlanguages.mops.protocol.MakeMessageJson import com.specificlanguages.mops.protocol.MakeMessageKind +import com.specificlanguages.mops.protocol.MakeMessageLevel import com.specificlanguages.mops.protocol.MakeOutcome import com.specificlanguages.mops.protocol.MakeResponse +import jetbrains.mps.generator.GenerationFacade import jetbrains.mps.generator.GenerationSettingsProvider import jetbrains.mps.generator.IModifiableGenerationSettings import jetbrains.mps.make.MakeSession import jetbrains.mps.make.dependencies.MakeSequence import jetbrains.mps.make.resources.IResource +import jetbrains.mps.make.script.IResult import jetbrains.mps.make.script.IScriptController import jetbrains.mps.make.service.CoreMakeTask import jetbrains.mps.messages.IMessage @@ -21,6 +24,7 @@ import jetbrains.mps.progress.EmptyProgressMonitor import jetbrains.mps.project.AbstractModule import jetbrains.mps.project.Project import jetbrains.mps.project.dependency.GlobalModuleDependenciesManager +import jetbrains.mps.project.facets.GenerationTargetFacet import jetbrains.mps.smodel.Language import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory import jetbrains.mps.smodel.resources.ModelsToResources @@ -36,8 +40,9 @@ import org.jetbrains.mps.openapi.util.ProgressMonitor * The make framework builds exactly the modules whose models it is handed — it never pulls in a dependency that is not * in the set, it only orders what it is given. So to make un-made dependencies too, [makeModules] first expands the * requested modules to their transitive compile-dependency closure ([GlobalModuleDependenciesManager]); already-built - * library dependencies drop out because [ModelsToResources] keeps only generatable models. Every model handed to the - * make is generated; nothing skips unmodified models. + * library dependencies drop out because [ModelsToResources] keeps only generatable models. Models of modules without + * generation-target facets are left out too ([makeSet]). Every model handed to the make is generated; nothing skips + * unmodified models. * * Model collection runs inside short read actions; the make call runs outside any model action, on the calling thread, * because the make framework acquires its own model locks. @@ -95,43 +100,58 @@ class ProjectMake(private val project: Project) { monitor: ProgressMonitor, ): MakeResponse { val options = makeGenerationOptions(generationSettings, generation) - // resources() reads the model graph and keeps only generatable models, grouped one MResource per module. - // But "generatable" is not the same as "writable": MPS ships its library languages and runtime solutions as - // *source* models inside `-src.jar` files, and those models report themselves generatable, so they survive - // ModelsToResources — yet writing their generation output into the jar fails ("Write for jar files is not - // supported"). We only ever intend to make project sources, so drop models of read-only (packaged) modules - // first. See https://github.com/specificlanguages/mps-api-research/blob/main/make-generation.md. - val resources: List = project.modelAccess.computeReadAction> { - ModelsToResources(models.filter { it.isInWritableModule() }, /* cleanMake = */ true).resources().toList() - } - - if (resources.isEmpty()) { - return MakeResponse(MakeOutcome.NOTHING_TO_GENERATE, moduleCount = 0, messages = emptyList()) + val makeSet = project.modelAccess.computeReadAction { makeSet(models) } + val handler = CollectingMessageHandler() + makeSet.leftOutModules.mapTo(handler.messages) { + MakeMessageJson(MakeMessageKind.WARNING, it.notGeneratedMessage()) } - val handler = CollectingMessageHandler() // MakeSession's own clean flag only names the IDE's make script; the resources above carry the clean make. val session = MakeSession(project, handler, /* cleanMake = */ false) + // The make sequence leaves out a module whose models use no language and import no language or devkit, so it + // can have no step even when there are resources. + val sequence = MakeSequence(makeSet.resources, null, session) + if (sequence.steps() == 0) { + return MakeResponse(MakeOutcome.NOTHING_TO_GENERATE, 0, handler.messages.reportedAt(generation.messages)) + } + // Reimplements BuildMakeService.doMake using classes from mps-core: BuildMakeService itself ships only in // lib/mpsant/mps-tool.jar, which is not on the daemon classpath. A null script lets MPS build the correct // per-cluster script; a null-controller default is IScriptController.Stub2. See the mps-api-research notes. val controller: IScriptController = makeScriptController(session, options) - val task = CoreMakeTask(MakeSequence(resources, null, session), controller, handler) + val task = CoreMakeTask(sequence, controller, handler) val result = withMakeGenerationSettings(generationSettings, generation) { task.run(monitor) task.getResult() } - // MPS's make result is not reliable on its own — it can report success while errors were reported — so a run - // counts as successful only when the result is successful AND no error message was seen. - val succeeded = result != null && result.isSucessful && !handler.errorOccurred - return MakeResponse( - outcome = if (succeeded) MakeOutcome.SUCCESS else MakeOutcome.FAILED, - moduleCount = resources.size, - messages = handler.messages.filter { generation.messages.reports(it.kind) }, - ) + return makeResponse(result, handler.messages, makeSet.resources.size, generation.messages) + } + + /** + * The make resources for [models], grouped one per module, and the modules whose generatable models are left out + * because they have nowhere to write generated files. Must run inside a read action. + * + * Models of read-only modules are left out: MPS ships its library languages and runtime solutions as *source* + * models inside `-src.jar` files, and those models report themselves generatable, yet writing their generation + * output into the jar fails ("Write for jar files is not supported"). Models whose module has no generation-target + * facets are left out too: text generation has no output location for them, which fails the make on MPS before + * 2025.3. See https://github.com/specificlanguages/mps-api-research/blob/main/make-generation.md. + */ + private fun makeSet(models: List): MakeSet { + val writable = models.filter { it.isInWritableModule() } + val (withTarget, withoutTarget) = writable.partition { GenerationTargetFacet.find(it) != null } + val leftOutModules = withoutTarget.filter(GenerationFacade::canGenerate).mapNotNull { it.module }.distinct() + // resources() keeps only generatable models. + val resources = ModelsToResources(withTarget, /* cleanMake = */ true).resources().toList() + return MakeSet(resources, leftOutModules) } + private class MakeSet(val resources: List, val leftOutModules: List) + + private fun SModule.notGeneratedMessage(): String = + "not generating the models of $moduleName: the module has no generation-target facets" + /** * The modules to make for a request over [requested]: the requested modules, their module-level compile-dependency * closure, and — because MPS treats a used language as an already-built dependency and never adds its source module @@ -168,8 +188,7 @@ class ProjectMake(private val project: Project) { } // A model belongs to a writable module when its owning module is not read-only. Read-only modules are the deployed, - // jar-packaged libraries MPS ships pre-built; they must never be regenerated. Models with no resolvable module are - // kept — they are not the packaged-library case this guards against. + // jar-packaged libraries MPS ships pre-built; they must never be regenerated. private fun SModel.isInWritableModule(): Boolean = (module as? AbstractModule)?.isReadOnly != true @@ -188,13 +207,11 @@ class ProjectMake(private val project: Project) { } /** - * Collects the messages MPS reports during the make and records whether any error was seen. A message with no kind - * is treated as an error, matching how MPS's own headless tooling handles it. + * Collects the messages MPS reports during the make. A message with no kind is treated as an error, matching how + * MPS's own headless tooling handles it. */ private class CollectingMessageHandler : IMessageHandler { val messages = mutableListOf() - var errorOccurred = false - private set override fun handle(msg: IMessage) { val kind = when (msg.kind) { @@ -202,9 +219,43 @@ class ProjectMake(private val project: Project) { MessageKind.WARNING -> MakeMessageKind.WARNING else -> MakeMessageKind.ERROR } - if (kind == MakeMessageKind.ERROR) errorOccurred = true messages.add(MakeMessageJson(kind, msg.text.orEmpty())) } } } +/** + * The response for a make that ran with [result] and reported [messages], keeping the messages that [level] reports. + * + * MPS's make result is not reliable on its own — it can report success while errors were reported — so a make + * succeeds only when [result] is successful and no error message was seen. A failed make always carries an error: when + * MPS reported none, the response says so with the reason from [result], if any. + */ +internal fun makeResponse( + result: IResult?, + messages: List, + moduleCount: Int, + level: MakeMessageLevel?, +): MakeResponse { + val hasError = messages.any { it.kind == MakeMessageKind.ERROR } + val succeeded = result != null && result.isSucessful && !hasError + val reported = messages.reportedAt(level) + return MakeResponse( + outcome = if (succeeded) MakeOutcome.SUCCESS else MakeOutcome.FAILED, + moduleCount = moduleCount, + messages = if (succeeded || hasError) reported else reported + unexplainedFailure(result), + ) +} + +private fun List.reportedAt(level: MakeMessageLevel?): List = + filter { level.reports(it.kind) } + +private fun unexplainedFailure(result: IResult?): MakeMessageJson { + val reason = result?.message().orEmpty().ifBlank { null } + val text = if (reason == null) { + "make failed without reporting an error" + } else { + "make failed without reporting an error: $reason" + } + return MakeMessageJson(MakeMessageKind.ERROR, text) +} diff --git a/daemon/src/test/kotlin/com/specificlanguages/mops/daemon/MakeResponseTest.kt b/daemon/src/test/kotlin/com/specificlanguages/mops/daemon/MakeResponseTest.kt new file mode 100644 index 00000000..9553cf18 --- /dev/null +++ b/daemon/src/test/kotlin/com/specificlanguages/mops/daemon/MakeResponseTest.kt @@ -0,0 +1,47 @@ +package com.specificlanguages.mops.daemon + +import com.specificlanguages.mops.protocol.MakeMessageJson +import com.specificlanguages.mops.protocol.MakeMessageKind +import com.specificlanguages.mops.protocol.MakeMessageLevel +import com.specificlanguages.mops.protocol.MakeOutcome +import com.specificlanguages.mops.protocol.MakeResponse +import jetbrains.mps.make.script.IResult +import kotlin.test.Test +import kotlin.test.assertEquals + +/** The outcome and messages of a make, from the MPS make result and the messages the make reported. */ +class MakeResponseTest { + private val warning = MakeMessageJson(MakeMessageKind.WARNING, "a warning") + private val error = MakeMessageJson(MakeMessageKind.ERROR, "an error") + private val info = MakeMessageJson(MakeMessageKind.INFO, "an info") + + @Test + fun `a successful result without errors succeeds with the reported messages`() { + val response = makeResponse(IResult.SUCCESS(null), listOf(info, warning), moduleCount = 2, level = null) + + assertEquals(MakeResponse(MakeOutcome.SUCCESS, 2, listOf(warning)), response) + } + + @Test + fun `a successful result with an error fails with that error`() { + val response = makeResponse(IResult.SUCCESS(null), listOf(warning, error), moduleCount = 1, level = null) + + assertEquals(MakeResponse(MakeOutcome.FAILED, 1, listOf(warning, error)), response) + } + + @Test + fun `a failed result without an error message fails with the reason from the result`() { + val response = makeResponse(IResult.FAILURE("not started", null), listOf(warning), moduleCount = 1, level = null) + + val reason = MakeMessageJson(MakeMessageKind.ERROR, "make failed without reporting an error: not started") + assertEquals(MakeResponse(MakeOutcome.FAILED, 1, listOf(warning, reason)), response) + } + + @Test + fun `a missing result fails with an error even at the error message level`() { + val response = makeResponse(null, listOf(warning), moduleCount = 1, level = MakeMessageLevel.ERROR) + + val reason = MakeMessageJson(MakeMessageKind.ERROR, "make failed without reporting an error") + assertEquals(MakeResponse(MakeOutcome.FAILED, 1, listOf(reason)), response) + } +} diff --git a/daemon/src/test/kotlin/com/specificlanguages/mops/daemon/MakeSemanticsTest.kt b/daemon/src/test/kotlin/com/specificlanguages/mops/daemon/MakeSemanticsTest.kt index d6cad295..ce0c3b06 100644 --- a/daemon/src/test/kotlin/com/specificlanguages/mops/daemon/MakeSemanticsTest.kt +++ b/daemon/src/test/kotlin/com/specificlanguages/mops/daemon/MakeSemanticsTest.kt @@ -2,22 +2,35 @@ package com.specificlanguages.mops.daemon import com.specificlanguages.mops.daemon.core.MpsErrorCode import com.specificlanguages.mops.daemon.core.MpsRequestException +import com.specificlanguages.mops.protocol.CreateModelRequest +import com.specificlanguages.mops.protocol.CreateSolutionRequest +import com.specificlanguages.mops.protocol.EditBatch +import com.specificlanguages.mops.protocol.EditOperation import com.specificlanguages.mops.protocol.MakeGenerationOptions +import com.specificlanguages.mops.protocol.MakeModulesRequest import com.specificlanguages.mops.protocol.MakeMessageKind import com.specificlanguages.mops.protocol.MakeOutcome +import com.specificlanguages.mops.protocol.MakeResponse +import com.specificlanguages.mops.protocol.ModelEditRequest +import com.specificlanguages.mops.protocol.ModelDestination +import com.specificlanguages.mops.protocol.MpsNodePropertyJson +import com.specificlanguages.mops.protocol.SolutionUsagePreset import java.nio.file.Path +import kotlin.io.path.exists import kotlin.io.path.readText import kotlin.io.path.writeText import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith +import kotlin.test.assertFalse +import kotlin.test.assertIs import kotlin.test.assertTrue /** * Drives the real make against fixture-project copies (make writes generated files, so the shared read-only project * cannot be used). Asserts that making a solution automatically makes the language it depends on, that a whole-project - * make succeeds, that a module whose closure reaches jar-packaged library sources still makes, and that a broken model - * fails with reported errors. + * make succeeds, that a module whose closure reaches jar-packaged library sources still makes, that a broken model + * fails with reported errors, and that modules and models with nothing to generate are left out of the make. */ class MakeSemanticsTest { @Test @@ -108,6 +121,61 @@ class MakeSemanticsTest { model.writeText(broken) } + // The tests below send each step as its own daemon request, as the CLI does. + @Test + fun `a solution with no generation-target facets makes without generating its models`() { + val (response, solutionDir) = SharedMpsEnvironment.withOpenProjectCopy { project, projectPath -> + val daemon = DomainRequestHandler(projectPath, SharedMpsEnvironment.mpsAccess(project)) + createSolutionWithModel(daemon, "example.analyzed", SolutionUsagePreset.NOT_GENERATED) + val addClass = EditOperation.AddRoot( + model = ModelDestination("example.analyzed.main"), + concept = CLASS_CONCEPT, + properties = listOf(MpsNodePropertyJson(name = "name", value = "Analyzed")), + ) + daemon.handleDomainRequest(ModelEditRequest("", EditBatch(listOf(addClass)))) + daemon.make("example.analyzed") to projectPath.resolve("solutions/example.analyzed") + } + + assertEquals(MakeOutcome.NOTHING_TO_GENERATE, response.outcome, "got ${response.messages}") + assertTrue( + response.messages.any { it.kind == MakeMessageKind.WARNING && it.text.contains("example.analyzed") }, + "expected a warning naming the module left out; got ${response.messages}", + ) + assertFalse(solutionDir.resolve("source_gen").exists(), "the solution's models must not be generated") + } + + @Test + fun `a solution with no generation-target facets and an empty model has nothing to generate`() { + val response = SharedMpsEnvironment.withOpenProjectCopy { project, projectPath -> + val daemon = DomainRequestHandler(projectPath, SharedMpsEnvironment.mpsAccess(project)) + createSolutionWithModel(daemon, "example.analyzed", SolutionUsagePreset.NOT_GENERATED) + daemon.make("example.analyzed") + } + + assertEquals(MakeOutcome.NOTHING_TO_GENERATE, response.outcome, "got ${response.messages}") + } + + @Test + fun `a generated solution whose only model is empty has nothing to generate`() { + val response = SharedMpsEnvironment.withOpenProjectCopy { project, projectPath -> + val daemon = DomainRequestHandler(projectPath, SharedMpsEnvironment.mpsAccess(project)) + createSolutionWithModel(daemon, "example.empty", SolutionUsagePreset.JAVA) + daemon.make("example.empty") + } + + assertEquals(MakeOutcome.NOTHING_TO_GENERATE, response.outcome, "got ${response.messages}") + } + + private fun createSolutionWithModel(daemon: DomainRequestHandler, name: String, preset: SolutionUsagePreset) { + daemon.handleDomainRequest(CreateSolutionRequest("", name, usagePreset = preset)) + daemon.handleDomainRequest(CreateModelRequest("", "$name.main", name)) + } + + private fun DomainRequestHandler.make( + module: String, + generation: MakeGenerationOptions = MakeGenerationOptions(), + ): MakeResponse = assertIs(handleDomainRequest(MakeModulesRequest("", listOf(module), generation))) + @Test fun `an unknown module name fails with a target-not-found error`() { val failure = assertFailsWith { @@ -122,5 +190,6 @@ class MakeSemanticsTest { private companion object { const val SANDBOX = "base-language-sandbox" const val EXPRESSION_LANGUAGE = "expression-language" + const val CLASS_CONCEPT = "jetbrains.mps.baseLanguage.structure.ClassConcept" } } diff --git a/docs/reference/make.md b/docs/reference/make.md index 39dbf766..890e23b1 100644 --- a/docs/reference/make.md +++ b/docs/reference/make.md @@ -54,7 +54,9 @@ module(s): success`, `made module(s): FAILED ( error(s))`, or `nothing to generate`. The command exits non-zero when the make reports errors. The make generates every model of the made modules and deletes files in their -output folders that it no longer generates. +output folders that it no longer generates. It leaves out the models of modules +without generation-target facets, such as solutions created with the +`not-generated` usage preset, and names each such module in a warning. The generation options apply to this make only; without them the make uses the daemon's MPS generation settings. With `--threads`, MPS generates in parallel @@ -100,7 +102,9 @@ module(s): success`, `made module(s): FAILED ( error(s))`, or `nothing to generate`. The command exits non-zero when the make reports errors. The make generates every model of the made modules and deletes files in their -output folders that it no longer generates. +output folders that it no longer generates. It leaves out the models of modules +without generation-target facets, such as solutions created with the +`not-generated` usage preset, and names each such module in a warning. The generation options apply to this make only; without them the make uses the daemon's MPS generation settings. With `--threads`, MPS generates in parallel diff --git a/examples/pages/editing.creation.md b/examples/pages/editing.creation.md index e715c4e7..bed08446 100644 --- a/examples/pages/editing.creation.md +++ b/examples/pages/editing.creation.md @@ -3,7 +3,7 @@ {{table:commands|Command}} Solution presets configure facets, not dependencies, imported languages, devkits, or contents. Supported presets: -`not-generated` (default, no facets), `text` (plain-text generation), `java` (Java compiled and loaded by MPS), -`java-tests` (`java` plus the tests facet), and `java-mps-plugin` (Java extending MPS as a plugin). The creation report -and the `--dry-run` plan print the preset applied and the facets it produced; `--json` carries them as `usagePreset` and -`facets`. +`not-generated` (default, no facets, so make leaves its models out), `text` (plain-text generation), `java` (Java +compiled and loaded by MPS), `java-tests` (`java` plus the tests facet), and `java-mps-plugin` (Java extending MPS as a +plugin). The creation report and the `--dry-run` plan print the preset applied and the facets it produced; `--json` +carries them as `usagePreset` and `facets`. diff --git a/protocol/src/main/kotlin/com/specificlanguages/mops/protocol/MakeOutcome.kt b/protocol/src/main/kotlin/com/specificlanguages/mops/protocol/MakeOutcome.kt index ba4d23e4..16e578d1 100644 --- a/protocol/src/main/kotlin/com/specificlanguages/mops/protocol/MakeOutcome.kt +++ b/protocol/src/main/kotlin/com/specificlanguages/mops/protocol/MakeOutcome.kt @@ -13,9 +13,12 @@ enum class MakeOutcome { /** The make finished and reported no errors. */ SUCCESS, - /** The make result was a failure, or at least one error message was reported. */ + /** + * The make result was a failure, or at least one error message was reported. A failed make always carries at least + * one error message. + */ FAILED, - /** The make set held no generatable model, so nothing ran. */ + /** The make set held no model to generate, so nothing ran. */ NOTHING_TO_GENERATE, } diff --git a/protocol/src/main/kotlin/com/specificlanguages/mops/protocol/daemonResponses.kt b/protocol/src/main/kotlin/com/specificlanguages/mops/protocol/daemonResponses.kt index 090e6de9..82e6c1ba 100644 --- a/protocol/src/main/kotlin/com/specificlanguages/mops/protocol/daemonResponses.kt +++ b/protocol/src/main/kotlin/com/specificlanguages/mops/protocol/daemonResponses.kt @@ -182,9 +182,9 @@ data class ModuleDiagnosticResponse( /** * Successful response carrying the result of a make run: its [outcome], how many generatable modules were in the make - * set (the requested modules plus their generatable dependency closure), and the error and warning [messages] MPS - * reported. On [MakeOutcome.FAILED] the [messages] carry the errors; on [MakeOutcome.NOTHING_TO_GENERATE] the make set - * held no generatable model. + * set (the requested modules plus their generatable dependency closure), and the make [messages] at the requested level. + * On [MakeOutcome.FAILED] the [messages] carry at least one error; on [MakeOutcome.NOTHING_TO_GENERATE] the make set + * held no model to generate. */ @Serializable @SerialName("make-result") diff --git a/skills/mops/references/editing.creation.md b/skills/mops/references/editing.creation.md index 7abdf7af..e308061e 100644 --- a/skills/mops/references/editing.creation.md +++ b/skills/mops/references/editing.creation.md @@ -13,7 +13,7 @@ | Preview creation | `mops create solution sample.solution --usage-preset java --dry-run` | Solution presets configure facets, not dependencies, imported languages, devkits, or contents. Supported presets: -`not-generated` (default, no facets), `text` (plain-text generation), `java` (Java compiled and loaded by MPS), -`java-tests` (`java` plus the tests facet), and `java-mps-plugin` (Java extending MPS as a plugin). The creation report -and the `--dry-run` plan print the preset applied and the facets it produced; `--json` carries them as `usagePreset` and -`facets`. +`not-generated` (default, no facets, so make leaves its models out), `text` (plain-text generation), `java` (Java +compiled and loaded by MPS), `java-tests` (`java` plus the tests facet), and `java-mps-plugin` (Java extending MPS as a +plugin). The creation report and the `--dry-run` plan print the preset applied and the facets it produced; `--json` +carries them as `usagePreset` and `facets`.