From adaefcc08d8f691c67a797e1332013b4b1e2b48f Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Tue, 22 Aug 2023 16:29:50 +0200 Subject: [PATCH 01/43] chore: added new commit scopes --- commitlint.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/commitlint.config.js b/commitlint.config.js index 67e9bc5cbf..3953f5a0c5 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -5,6 +5,9 @@ module.exports = { 2, "always", [ + "bulk-model-sync-gradle", + "bulk-model-sync-lib", + "bulk-model-sync-solution", "deps", "light-model-client", "model-server-lib", @@ -15,7 +18,6 @@ module.exports = { "model-client", "model-datastructure", "model-server", - "model-sync-lib", "modelql", "mps-model-adapters", "mps-model-server", From 8cc2dc7e1c6fb888b6c4afd0c0b356e9ee15e76e Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Tue, 22 Aug 2023 16:32:55 +0200 Subject: [PATCH 02/43] feat(mps-model-adapters): implemented more adapters --- .../org/modelix/model/mpsadapters/MPSArea.kt | 24 ++++--- .../modelix/model/mpsadapters/MPSChildLink.kt | 2 + .../modelix/model/mpsadapters/MPSConcept.kt | 11 ++- .../modelix/model/mpsadapters/MPSLanguage.kt | 36 ++++++++++ .../mpsadapters/MPSLanguageRepository.kt | 50 ++++++++++++++ .../model/mpsadapters/MPSModelAsNode.kt | 18 +++-- .../model/mpsadapters/MPSModuleAsNode.kt | 11 +-- .../org/modelix/model/mpsadapters/MPSNode.kt | 69 ++++++++++++++++--- .../model/mpsadapters/MPSNodeReference.kt | 21 ++++++ .../model/mpsadapters/MPSRepositoryAsNode.kt | 25 ++++--- 10 files changed, 220 insertions(+), 47 deletions(-) create mode 100644 mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguage.kt create mode 100644 mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSArea.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSArea.kt index 61d39caa54..be9457d3a2 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSArea.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSArea.kt @@ -13,6 +13,7 @@ */ package org.modelix.model.mpsadapters +import jetbrains.mps.smodel.GlobalModelAccess import org.jetbrains.mps.openapi.module.SRepository import org.modelix.model.api.IBranch import org.modelix.model.api.IConcept @@ -23,13 +24,14 @@ import org.modelix.model.area.IArea import org.modelix.model.area.IAreaListener import org.modelix.model.area.IAreaReference -data class MPSArea(val repository: SRepository) : IArea { +data class MPSArea(val repository: SRepository) : IArea, IAreaReference { override fun getRoot(): INode { return MPSRepositoryAsNode(repository) } + @Deprecated("use ILanguageRepository.resolveConcept") override fun resolveConcept(ref: IConceptReference): IConcept? { - TODO("Not yet implemented") + return MPSLanguageRepository(repository).resolveConcept(ref.getUID()) } override fun resolveNode(ref: INodeReference): INode? { @@ -49,11 +51,11 @@ data class MPSArea(val repository: SRepository) : IArea { } override fun getReference(): IAreaReference { - TODO("Not yet implemented") + return this } override fun resolveArea(ref: IAreaReference): IArea? { - TODO("Not yet implemented") + return takeIf { ref == it } } override fun executeRead(f: () -> T): T { @@ -61,11 +63,17 @@ data class MPSArea(val repository: SRepository) : IArea { repository.modelAccess.runReadAction { result = f() } - return result as T + return result!! } override fun executeWrite(f: () -> T): T { - TODO("Not yet implemented") + var result: T? = null + if (repository.modelAccess is GlobalModelAccess) { + repository.modelAccess.runWriteAction { result = f() } + } else { + repository.modelAccess.executeCommand { result = f() } + } + return result!! } override fun canRead(): Boolean { @@ -77,10 +85,10 @@ data class MPSArea(val repository: SRepository) : IArea { } override fun addListener(l: IAreaListener) { - TODO("Not yet implemented") + throw UnsupportedOperationException("Not implemented") } override fun removeListener(l: IAreaListener) { - TODO("Not yet implemented") + throw UnsupportedOperationException("Not implemented") } } diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSChildLink.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSChildLink.kt index c271f87fa1..6ec8d6cbef 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSChildLink.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSChildLink.kt @@ -22,6 +22,8 @@ data class MPSChildLink(val link: SContainmentLinkAdapter) : IChildLink { constructor(link: SContainmentLink) : this(link as SContainmentLinkAdapter) override val isMultiple: Boolean get() = link.isMultiple + + @Deprecated("use .targetConcept", ReplaceWith("targetConcept")) override val childConcept: IConcept get() = targetConcept override val targetConcept: IConcept diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSConcept.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSConcept.kt index 1bdf7673d5..52ece7cbc6 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSConcept.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSConcept.kt @@ -23,19 +23,18 @@ import org.jetbrains.mps.openapi.language.SInterfaceConcept import org.modelix.model.api.ConceptReference import org.modelix.model.api.IChildLink import org.modelix.model.api.IConcept -import org.modelix.model.api.IConceptReference import org.modelix.model.api.ILanguage import org.modelix.model.api.IProperty import org.modelix.model.api.IReferenceLink data class MPSConcept(val concept: SAbstractConceptAdapter) : IConcept { constructor(concept: SAbstractConcept) : this(concept as SAbstractConceptAdapter) - override fun getReference(): IConceptReference { + override fun getReference(): ConceptReference { return ConceptReference(getUID()) } - override val language: ILanguage? - get() = TODO("Not yet implemented") + override val language: ILanguage + get() = MPSLanguage(concept.language) override fun getUID(): String { val id: SConceptId = when (concept) { @@ -72,8 +71,8 @@ data class MPSConcept(val concept: SAbstractConceptAdapter) : IConcept { }.map { MPSConcept(it) } } - override fun isExactly(other: IConcept?): Boolean { - val otherMpsConcept = other as? MPSConcept ?: return false + override fun isExactly(concept: IConcept?): Boolean { + val otherMpsConcept = concept as? MPSConcept ?: return false return this.concept == otherMpsConcept.concept } diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguage.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguage.kt new file mode 100644 index 0000000000..2fd0a1e8bd --- /dev/null +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguage.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.mpsadapters + +import jetbrains.mps.smodel.adapter.ids.MetaIdHelper +import org.jetbrains.mps.openapi.language.SLanguage +import org.modelix.model.api.IConcept +import org.modelix.model.api.ILanguage + +data class MPSLanguage(val language: SLanguage) : ILanguage { + override fun getUID(): String { + return MetaIdHelper.getLanguage(language).serialize() + } + + override fun getName(): String { + return language.qualifiedName + } + + override fun getConcepts(): List { + return language.concepts.map { MPSConcept(it) } + } +} diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt new file mode 100644 index 0000000000..91a08921b3 --- /dev/null +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.mpsadapters + +import jetbrains.mps.smodel.adapter.ids.SConceptId +import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory +import jetbrains.mps.smodel.language.ConceptRegistry +import jetbrains.mps.smodel.language.LanguageRegistry +import jetbrains.mps.smodel.runtime.illegal.IllegalConceptDescriptor +import org.jetbrains.mps.openapi.module.SRepository +import org.modelix.model.api.IConcept +import org.modelix.model.api.ILanguageRepository + +class MPSLanguageRepository(private val repository: SRepository) : ILanguageRepository { + override fun resolveConcept(uid: String): IConcept? { + if (uid.startsWith("mps:")) return null + + val conceptId = try { + SConceptId.deserialize(uid.substring(4)) + } catch (e: Exception) { return null } ?: return null + + val conceptDescriptor = ConceptRegistry.getInstance().getConceptDescriptor(conceptId) + + if (conceptDescriptor is IllegalConceptDescriptor) return null + + return MPSConcept(MetaAdapterFactory.getAbstractConcept(conceptDescriptor)) + } + + override fun getAllConcepts(): List { + val result = mutableListOf() + LanguageRegistry.getInstance(repository).withAvailableLanguages { language -> + result.addAll(language.identity.concepts.map { MPSConcept(it) }.toList()) + } + return result + } +} diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt index 3390cb4572..37014cf912 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt @@ -22,18 +22,20 @@ import org.modelix.model.api.INode import org.modelix.model.api.INodeReference import org.modelix.model.api.IProperty import org.modelix.model.api.IReferenceLink -import org.modelix.model.api.SerializedNodeReference +import org.modelix.model.api.NodeReference +import org.modelix.model.api.NullChildLink import org.modelix.model.area.IArea +import org.modelix.model.data.NodeData data class MPSModelAsNode(val model: SModel) : IDeprecatedNodeDefaults { override fun getArea(): IArea { - TODO("Not yet implemented") + return MPSArea(model.repository) } override val isValid: Boolean get() = TODO("Not yet implemented") override val reference: INodeReference - get() = SerializedNodeReference("mps-model:" + model.reference.toString()) + get() = NodeReference("mps-model:" + model.reference.toString()) override val concept: IConcept get() = RepositoryLanguage.Model override val parent: INode @@ -50,12 +52,14 @@ data class MPSModelAsNode(val model: SModel) : IDeprecatedNodeDefaults { TODO("Not yet implemented") } - override fun getContainmentLink(): IChildLink? { - TODO("Not yet implemented") + override fun getContainmentLink(): IChildLink { + return RepositoryLanguage.Module.models } override fun getChildren(link: IChildLink): Iterable { - return if (link.getUID().endsWith(RepositoryLanguage.Model.rootNodes.getUID()) || + return if (link is NullChildLink) { + emptyList() + } else if (link.getUID().endsWith(RepositoryLanguage.Model.rootNodes.getUID()) || link.getUID().contains("rootNodes") || link.getSimpleName() == "rootNodes" ) { @@ -99,6 +103,8 @@ data class MPSModelAsNode(val model: SModel) : IDeprecatedNodeDefaults { property.getSimpleName() == "name" ) { model.name.value + } else if (property.getSimpleName() == NodeData.idPropertyKey) { + model.modelId.toString() } else { null } diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleAsNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleAsNode.kt index d15063cdaa..4101cafeaa 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleAsNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleAsNode.kt @@ -15,6 +15,7 @@ package org.modelix.model.mpsadapters import jetbrains.mps.project.ProjectBase import jetbrains.mps.project.ProjectManager +import jetbrains.mps.smodel.MPSModuleRepository import org.jetbrains.mps.openapi.module.SModule import org.modelix.model.api.IChildLink import org.modelix.model.api.IConcept @@ -24,18 +25,18 @@ import org.modelix.model.api.INode import org.modelix.model.api.INodeReference import org.modelix.model.api.IProperty import org.modelix.model.api.IReferenceLink -import org.modelix.model.api.SerializedNodeReference +import org.modelix.model.api.NodeReference import org.modelix.model.area.IArea data class MPSModuleAsNode(val module: SModule) : IDeprecatedNodeDefaults { override fun getArea(): IArea { - TODO("Not yet implemented") + return MPSArea(module.repository ?: MPSModuleRepository.getInstance()) } override val isValid: Boolean get() = TODO("Not yet implemented") override val reference: INodeReference - get() = SerializedNodeReference("mps-module:" + module.moduleReference.toString()) + get() = NodeReference("mps-module:" + module.moduleReference.toString()) override val concept: IConcept get() = RepositoryLanguage.Module override val parent: INode? @@ -52,8 +53,8 @@ data class MPSModuleAsNode(val module: SModule) : IDeprecatedNodeDefaults { TODO("Not yet implemented") } - override fun getContainmentLink(): IChildLink? { - TODO("Not yet implemented") + override fun getContainmentLink(): IChildLink { + return RepositoryLanguage.Repository.modules } override fun getChildren(link: IChildLink): Iterable { diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNode.kt index e3dd6099a8..ec758b9031 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNode.kt @@ -13,7 +13,9 @@ */ package org.modelix.model.mpsadapters +import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations import jetbrains.mps.smodel.MPSModuleRepository +import jetbrains.mps.smodel.adapter.MetaAdapterByDeclaration import org.jetbrains.mps.openapi.model.SNode import org.modelix.model.api.ConceptReference import org.modelix.model.api.IChildLink @@ -24,8 +26,9 @@ import org.modelix.model.api.INode import org.modelix.model.api.INodeReference import org.modelix.model.api.IProperty import org.modelix.model.api.IReferenceLink -import org.modelix.model.api.SerializedNodeReference +import org.modelix.model.api.resolveIn import org.modelix.model.area.IArea +import org.modelix.model.data.NodeData data class MPSNode(val node: SNode) : IDeprecatedNodeDefaults { override fun getArea(): IArea { @@ -35,7 +38,7 @@ data class MPSNode(val node: SNode) : IDeprecatedNodeDefaults { override val isValid: Boolean get() = true override val reference: INodeReference - get() = SerializedNodeReference("mps-node:${node.reference}") // MPSNodeReference(node.reference) + get() = MPSNodeReference(node.reference) override val concept: IConcept get() = MPSConcept(node.concept) override val parent: INode? @@ -49,7 +52,8 @@ data class MPSNode(val node: SNode) : IDeprecatedNodeDefaults { get() = node.children.map { MPSNode(it) } override fun removeChild(child: INode) { - TODO("Not yet implemented") + require(child is MPSNode) { "child must be an MPSNode" } + node.removeChild(child.node) } override fun getPropertyLinks(): List { @@ -60,8 +64,8 @@ data class MPSNode(val node: SNode) : IDeprecatedNodeDefaults { return node.references.map { MPSReferenceLink(it.link) } } - override fun getContainmentLink(): IChildLink? { - return node.containmentLink?.let { MPSChildLink(it) } + override fun getContainmentLink(): IChildLink { + return node.containmentLink?.let { MPSChildLink(it) } ?: RepositoryLanguage.Model.rootNodes } override fun getChildren(link: IChildLink): Iterable { @@ -74,15 +78,52 @@ data class MPSNode(val node: SNode) : IDeprecatedNodeDefaults { } override fun moveChild(role: IChildLink, index: Int, child: INode) { - TODO("Not yet implemented") + require(role is MPSChildLink) { "role must be an MPSChildLink" } + + val link = role.link + val children = node.getChildren(link).toList() + require(index <= children.size) { "index out of bounds: $index > ${children.size}" } + + require(child is MPSNode) + val sChild = child.node + SNodeOperations.deleteNode(sChild) + + if (index == -1 || index == children.size) { + node.addChild(link, sChild) + } else { + node.insertChildBefore(link, sChild, children[index]) + } } override fun addNewChild(role: IChildLink, index: Int, concept: IConcept?): INode { - TODO("Not yet implemented") + require(role is MPSChildLink) { "role must be an MPSChildLink" } + + val link = role.link + val children = node.getChildren(link).toList() + require(index <= children.size) { "index out of bounds: $index > ${children.size}" } + + val targetConcept = if (concept is MPSConcept) concept.concept else link.targetConcept + val instantiatableConcept = MetaAdapterByDeclaration.asInstanceConcept(targetConcept) + + val model = node.model + val newChild = if (model == null) { + jetbrains.mps.smodel.SNode(instantiatableConcept) + } else { + model.createNode(instantiatableConcept) + } + + if (index == -1 || index == children.size) { + node.addChild(link, newChild) + } else { + node.insertChildBefore(link, newChild, children[index]) + } + return MPSNode(newChild) } override fun addNewChild(role: IChildLink, index: Int, concept: IConceptReference?): INode { - TODO("Not yet implemented") + val repo = checkNotNull(node.model?.repository) + val targetConcept = concept?.let { MPSLanguageRepository(repo).resolveConcept(it.getUID()) } + return addNewChild(role, index, targetConcept) } override fun getReferenceTarget(link: IReferenceLink): INode? { @@ -91,11 +132,13 @@ data class MPSNode(val node: SNode) : IDeprecatedNodeDefaults { } override fun setReferenceTarget(link: IReferenceLink, target: INode?) { - TODO("Not yet implemented") + val ref = node.references.first { MPSReferenceLink(it.link).getUID() == link.getUID() } + val targetNode = target?.let { getArea().resolveNode(it.reference) } as MPSNode + node.setReferenceTarget(ref.link, targetNode.node) } override fun setReferenceTarget(role: IReferenceLink, target: INodeReference?) { - TODO("Not yet implemented") + setReferenceTarget(role, target?.resolveIn(getArea())) } override fun getReferenceTargetRef(role: IReferenceLink): INodeReference? { @@ -104,11 +147,15 @@ data class MPSNode(val node: SNode) : IDeprecatedNodeDefaults { } override fun getPropertyValue(property: IProperty): String? { + if (property.getSimpleName() == NodeData.idPropertyKey) { + return node.nodeId.toString() + } val mpsProperty = node.properties.firstOrNull { MPSProperty(it).getUID() == property.getUID() } ?: return null return node.getProperty(mpsProperty) } override fun setPropertyValue(property: IProperty, value: String?) { - TODO("Not yet implemented") + val mpsProperty = node.properties.first { MPSProperty(it).getUID() == property.getUID() } + node.setProperty(mpsProperty, value) } } diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNodeReference.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNodeReference.kt index 24ad12a5d2..dd31c449d6 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNodeReference.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNodeReference.kt @@ -16,10 +16,18 @@ package org.modelix.model.mpsadapters import jetbrains.mps.smodel.SNodePointer import org.jetbrains.mps.openapi.model.SNodeReference import org.modelix.model.api.INodeReference +import org.modelix.model.api.INodeReferenceSerializer +import org.modelix.model.api.INodeReferenceSerializerEx +import kotlin.reflect.KClass data class MPSNodeReference(val ref: SNodeReference) : INodeReference { companion object { + + init { + INodeReferenceSerializer.register(MPSNodeReferenceSerializer) + } + fun tryConvert(ref: INodeReference): MPSNodeReference? { if (ref is MPSNodeReference) return ref val serialized = ref.serialize() @@ -37,3 +45,16 @@ fun INodeReference.toMPSNodeReference(): MPSNodeReference { return MPSNodeReference.tryConvert(this) ?: throw IllegalArgumentException("Not an MPS node reference: $this") } + +object MPSNodeReferenceSerializer : INodeReferenceSerializerEx { + override val prefix = "mps" + override val supportedReferenceClasses: Set> = setOf(MPSNodeReference::class) + + override fun serialize(ref: INodeReference): String { + return (ref as MPSNodeReference).ref.nodeId.toString() + } + + override fun deserialize(serialized: String): INodeReference { + TODO("Not yet implemented") + } +} diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt index 8ca97210d3..9702402825 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt @@ -22,7 +22,8 @@ import org.modelix.model.api.INode import org.modelix.model.api.INodeReference import org.modelix.model.api.IProperty import org.modelix.model.api.IReferenceLink -import org.modelix.model.api.SerializedNodeReference +import org.modelix.model.api.NodeReference +import org.modelix.model.api.NullChildLink import org.modelix.model.area.IArea data class MPSRepositoryAsNode(val repository: SRepository) : IDeprecatedNodeDefaults { @@ -33,7 +34,7 @@ data class MPSRepositoryAsNode(val repository: SRepository) : IDeprecatedNodeDef override val isValid: Boolean get() = TODO("Not yet implemented") override val reference: INodeReference - get() = SerializedNodeReference("mps-repository") + get() = NodeReference("mps-repository") override val concept: IConcept get() = RepositoryLanguage.Repository override val parent: INode? @@ -51,11 +52,13 @@ data class MPSRepositoryAsNode(val repository: SRepository) : IDeprecatedNodeDef } override fun getContainmentLink(): IChildLink? { - TODO("Not yet implemented") + return null } override fun getChildren(link: IChildLink): Iterable { - return if (link.getUID().endsWith("0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618902/474657388638618903") || + return if (link is NullChildLink) { + return emptyList() + } else if (link.getUID().endsWith("0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618902/474657388638618903") || link.getUID().contains("modules") || link.getSimpleName() == "modules" ) { @@ -78,23 +81,23 @@ data class MPSRepositoryAsNode(val repository: SRepository) : IDeprecatedNodeDef } override fun getReferenceTarget(link: IReferenceLink): INode? { - TODO("Not yet implemented") + return null } override fun setReferenceTarget(link: IReferenceLink, target: INode?) { - TODO("Not yet implemented") + return } override fun setReferenceTarget(role: IReferenceLink, target: INodeReference?) { - TODO("Not yet implemented") + return } override fun getReferenceTargetRef(role: IReferenceLink): INodeReference? { - TODO("Not yet implemented") + return null } override fun getPropertyValue(property: IProperty): String? { - TODO("Not yet implemented") + return null } override fun setPropertyValue(property: IProperty, value: String?) { @@ -102,10 +105,10 @@ data class MPSRepositoryAsNode(val repository: SRepository) : IDeprecatedNodeDef } override fun getPropertyLinks(): List { - TODO("Not yet implemented") + return emptyList() } override fun getReferenceLinks(): List { - TODO("Not yet implemented") + return emptyList() } } From 7a7b5d6818fb3aa2430d9209acef6b3c64fc417b Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Tue, 22 Aug 2023 16:34:37 +0200 Subject: [PATCH 03/43] feat(bulk-model-sync-gradle): new model sync gradle plugin --- model-sync-gradle/.gitignore | 43 ++++ model-sync-gradle/build.gradle.kts | 40 ++++ .../sync/gradle/ModelSyncGradlePlugin.kt | 216 ++++++++++++++++++ .../gradle/config/ModelSyncGradleSettings.kt | 150 ++++++++++++ .../gradle/tasks/ExportFromModelServer.kt | 75 ++++++ .../model/sync/gradle/tasks/ExportFromMps.kt | 44 ++++ .../gradle/tasks/GenerateAntScriptForMps.kt | 125 ++++++++++ .../gradle/tasks/ImportIntoModelServer.kt | 59 +++++ .../model/sync/gradle/tasks/ImportIntoMps.kt | 44 ++++ .../sync/gradle/tasks/ValidateSyncSettings.kt | 92 ++++++++ settings.gradle.kts | 1 + 11 files changed, 889 insertions(+) create mode 100644 model-sync-gradle/.gitignore create mode 100644 model-sync-gradle/build.gradle.kts create mode 100644 model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt create mode 100644 model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/config/ModelSyncGradleSettings.kt create mode 100644 model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromModelServer.kt create mode 100644 model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromMps.kt create mode 100644 model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/GenerateAntScriptForMps.kt create mode 100644 model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoModelServer.kt create mode 100644 model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoMps.kt create mode 100644 model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ValidateSyncSettings.kt diff --git a/model-sync-gradle/.gitignore b/model-sync-gradle/.gitignore new file mode 100644 index 0000000000..e4c1c0ee94 --- /dev/null +++ b/model-sync-gradle/.gitignore @@ -0,0 +1,43 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store +/src/main/resources/modelix.core.version.properties diff --git a/model-sync-gradle/build.gradle.kts b/model-sync-gradle/build.gradle.kts new file mode 100644 index 0000000000..d54cdaeabd --- /dev/null +++ b/model-sync-gradle/build.gradle.kts @@ -0,0 +1,40 @@ +plugins { + kotlin("jvm") + `java-gradle-plugin` +} + +repositories { + mavenCentral() + mavenLocal() +} + +dependencies { + implementation(project(":model-client", "jvmRuntimeElements")) + implementation(project(":model-sync-lib")) + implementation(libs.ktor.client.core) + implementation(libs.ktor.client.cio) +} + +kotlin { + jvmToolchain(11) +} + +gradlePlugin { + val modelSync by plugins.creating { + id = "org.modelix.model-sync" + implementationClass = "org.modelix.model.sync.gradle.ModelSyncGradlePlugin" + } +} + +val writeVersionFile by tasks.registering { + val propertiesFile = projectDir.resolve("src/main/resources/modelix.core.version.properties") + propertiesFile.parentFile.mkdirs() + propertiesFile.writeText( + """ + modelix.core.version=$version + """.trimIndent(), + ) +} +tasks.named("processResources") { + dependsOn(writeVersionFile) +} diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt new file mode 100644 index 0000000000..95b361bb87 --- /dev/null +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt @@ -0,0 +1,216 @@ +package org.modelix.model.sync.gradle + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.tasks.Sync +import org.gradle.api.tasks.TaskProvider +import org.modelix.model.sync.gradle.config.LocalSource +import org.modelix.model.sync.gradle.config.LocalTarget +import org.modelix.model.sync.gradle.config.ModelSyncGradleSettings +import org.modelix.model.sync.gradle.config.ServerSource +import org.modelix.model.sync.gradle.config.ServerTarget +import org.modelix.model.sync.gradle.config.SyncDirection +import org.modelix.model.sync.gradle.tasks.ExportFromModelServer +import org.modelix.model.sync.gradle.tasks.ExportFromMps +import org.modelix.model.sync.gradle.tasks.GenerateAntScriptForMps +import org.modelix.model.sync.gradle.tasks.ImportIntoModelServer +import org.modelix.model.sync.gradle.tasks.ImportIntoMps +import org.modelix.model.sync.gradle.tasks.ValidateSyncSettings +import java.io.File +import java.net.URL +import java.util.Enumeration +import java.util.Properties + +class ModelSyncGradlePlugin : Plugin { + + private lateinit var settings: ModelSyncGradleSettings + + override fun apply(project: Project) { + settings = project.extensions.create("modelSync", ModelSyncGradleSettings::class.java) + getBaseDir(project).mkdirs() + + project.afterEvaluate { + val validateSyncSettings = project.tasks.register("validateSyncSettings", ValidateSyncSettings::class.java) { + it.settings.set(settings) + } + val modelixCoreVersion = readModelixCoreVersion() ?: throw RuntimeException("modelix.core version not found") + val antDependencies = project.configurations.create("model-sync-ant-dependencies") + project.dependencies.add(antDependencies.name, "org.apache.ant:ant-junit:1.10.12") + + val mpsDependencies = project.configurations.create("modelSyncMpsDependencies") + project.dependencies.add(mpsDependencies.name, "org.modelix:model-sync-mps:$modelixCoreVersion") + + val copyMpsDependencies = project.tasks.register("copyMpsDependencies", Sync::class.java) { sync -> + sync.dependsOn(validateSyncSettings) + val src = mpsDependencies.resolve().map { project.zipTree(it) } + val target = getDependenciesDir(project).apply { mkdirs() } + sync.from(src) + sync.into(target) + } + + settings.syncDirections.forEach { + registerTasksForSyncDirection(it, project, copyMpsDependencies) + } + } + } + + private fun registerTasksForSyncDirection( + syncDirection: SyncDirection, + project: Project, + previousTask: TaskProvider<*>, + ) { + val baseDir = project.buildDir.resolve("model-sync").apply { mkdirs() } + val jsonDir = baseDir.resolve(syncDirection.name).apply { mkdir() } + val sourceTask = when (syncDirection.source) { + is LocalSource -> registerTasksForLocalSource(syncDirection, project, previousTask, jsonDir) + is ServerSource -> registerTasksForServerSource(syncDirection, project, previousTask, jsonDir) + else -> previousTask + } + + when (syncDirection.target) { + is LocalTarget -> registerTasksForLocalTarget(syncDirection, project, sourceTask, jsonDir) + is ServerTarget -> registerTasksForServerTarget(syncDirection, project, sourceTask, jsonDir) + } + } + + private fun registerTasksForServerSource( + syncDirection: SyncDirection, + project: Project, + previousTask: TaskProvider<*>, + jsonDir: File, + ): TaskProvider<*> { + val serverSource = syncDirection.source as ServerSource + + val name = "${syncDirection.name}ExportFromModelServer" + val exportFromModelServer = project.tasks.register(name, ExportFromModelServer::class.java) { + it.dependsOn(previousTask) + it.outputDir.set(jsonDir) + it.url.set(serverSource.url) + it.repositoryId.set(serverSource.repositoryId) + it.branchName.set(serverSource.branchName) + it.revision.set(serverSource.revision) + } + return exportFromModelServer + } + + private fun registerTasksForLocalSource( + syncDirection: SyncDirection, + project: Project, + previousTask: TaskProvider<*>, + jsonDir: File, + ): TaskProvider<*> { + val localSource = syncDirection.source as LocalSource + val antScript = jsonDir.resolve("build.xml") + val generateAntScript = project.tasks.register( + "${syncDirection.name}GenerateAntScriptForExport", + GenerateAntScriptForMps::class.java, + ) { + it.dependsOn(previousTask) + it.mpsHomePath.set(localSource.mpsHome?.absolutePath) + it.mpsHeapSize.set(localSource.mpsHeapSize) + it.repositoryPath.set(localSource.repositoryDir?.absolutePath) + it.antScriptFile.set(antScript) + it.mpsDependenciesPath.set(getDependenciesDir(project).absolutePath) + it.jsonDirPath.set(jsonDir.absolutePath) + it.exportFlag.set(true) + it.includedModules.set(syncDirection.includedModules) + } + + val exportFromMps = project.tasks.register("${syncDirection.name}ExportFromMps", ExportFromMps::class.java) { + it.dependsOn(generateAntScript) + it.outputs.cacheIf { false } + it.workingDir = jsonDir + it.classpath = project.getAntDependencies() + it.mainClass.set("org.apache.tools.ant.launch.Launcher") + it.mpsHome.set(localSource.mpsHome) + it.antScript.set(antScript) + it.jsonDir.set(jsonDir) + } + return exportFromMps + } + + private fun Project.getAntDependencies() = configurations.getByName("model-sync-ant-dependencies") + + private fun registerTasksForServerTarget( + syncDirection: SyncDirection, + project: Project, + previousTask: TaskProvider<*>, + jsonDir: File, + ) { + val importTaskName = "${syncDirection.name}ImportIntoModelServer" + val importIntoModelServer = project.tasks.register(importTaskName, ImportIntoModelServer::class.java) { + it.dependsOn(previousTask) + it.inputDir.set(jsonDir) + val serverTarget = syncDirection.target as ServerTarget + + it.url.set(serverTarget.url) + it.repositoryId.set(serverTarget.repositoryId) + it.branchName.set(serverTarget.branchName) + } + + project.tasks.register("runSync${syncDirection.name.replaceFirstChar { it.uppercaseChar() }}") { + it.dependsOn(importIntoModelServer) + it.group = "modelix" + } + } + + private fun registerTasksForLocalTarget( + syncDirection: SyncDirection, + project: Project, + previousTask: TaskProvider<*>, + jsonDir: File, + ) { + val localTarget = syncDirection.target as LocalTarget + + val antScript = jsonDir.resolve("build.xml") + + val antDependencies = project.configurations.create("model-import-ant-dependencies") + project.dependencies.add(antDependencies.name, "org.apache.ant:ant-junit:1.10.12") + + val generateAntScriptName = "${syncDirection.name}generateAntScriptForImport" + val generateAntScript = project.tasks.register(generateAntScriptName, GenerateAntScriptForMps::class.java) { + it.dependsOn(previousTask) + it.mpsHomePath.set(localTarget.mpsHome?.absolutePath) + it.mpsHeapSize.set(localTarget.mpsHeapSize) + it.repositoryPath.set(localTarget.repositoryDir?.absolutePath) + it.antScriptFile.set(antScript) + it.mpsDependenciesPath.set(getDependenciesDir(project).absolutePath) + it.jsonDirPath.set(jsonDir.absolutePath) + it.exportFlag.set(false) + it.includedModules.set(emptyList()) + } + + val importName = "${syncDirection.name}ImportIntoMps" + val importIntoMps = project.tasks.register(importName, ImportIntoMps::class.java) { + it.dependsOn(generateAntScript) + it.workingDir = jsonDir + it.classpath = project.getAntDependencies() + it.mainClass.set("org.apache.tools.ant.launch.Launcher") + it.jsonDir.set(jsonDir) + it.antScript.set(jsonDir.resolve("build.xml")) + it.mpsHome.set(localTarget.mpsHome) + } + + project.tasks.register("runSync${syncDirection.name.replaceFirstChar { it.uppercaseChar() }}") { + it.dependsOn(importIntoMps) + it.group = "modelix" + } + } + + private fun getBaseDir(project: Project): File { + return project.buildDir.resolve("model-sync") + } + + private fun getDependenciesDir(project: Project): File { + return getBaseDir(project).resolve("dependencies") + } + + private fun readModelixCoreVersion(): String? { + val resources: Enumeration? = javaClass.classLoader.getResources("modelix.core.version.properties") + while (resources != null && resources.hasMoreElements()) { + val properties = resources.nextElement().openStream().use { Properties().apply { load(it) } } + return properties.getProperty("modelix.core.version") + } + return null + } +} diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/config/ModelSyncGradleSettings.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/config/ModelSyncGradleSettings.kt new file mode 100644 index 0000000000..7d781b0743 --- /dev/null +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/config/ModelSyncGradleSettings.kt @@ -0,0 +1,150 @@ +package org.modelix.model.sync.gradle.config + +import org.gradle.api.Action +import java.io.File + +open class ModelSyncGradleSettings { + internal val syncDirections = mutableListOf() + + fun direction(name: String, action: Action) { + val syncDirection = SyncDirection(name) + action.execute(syncDirection) + syncDirections.add(syncDirection) + } +} + +data class SyncDirection( + internal val name: String, + internal var source: SyncEndPoint? = null, + internal var target: SyncEndPoint? = null, + internal val includedModules: MutableList = mutableListOf(), +) { + fun fromModelServer(action: Action) { + val endpoint = ServerSource() + action.execute(endpoint) + source = endpoint + } + + fun fromLocal(action: Action) { + val endpoint = LocalSource() + action.execute(endpoint) + source = endpoint + } + + fun toModelServer(action: Action) { + val endpoint = ServerTarget() + action.execute(endpoint) + target = endpoint + } + + fun toLocal(action: Action) { + val endpoint = LocalTarget() + action.execute(endpoint) + target = endpoint + } + + fun includeModule(module: String) { + includedModules.add(module) + } +} + +interface SyncEndPoint { + fun getValidationErrors(): String +} + +sealed interface LocalEndpoint : SyncEndPoint { + var mpsHome: File? + var mpsHeapSize: String + var repositoryDir: File? + + override fun getValidationErrors(): String { + return buildString { + if (mpsHome == null) { + appendUndefinedLocalFieldError("mpsHome") + } + if (repositoryDir == null) { + appendUndefinedLocalFieldError("repositoryDir") + } + } + } +} + +data class LocalSource( + override var mpsHome: File? = null, + override var mpsHeapSize: String = "2g", + override var repositoryDir: File? = null, +) : LocalEndpoint + +data class LocalTarget( + override var mpsHome: File? = null, + override var mpsHeapSize: String = "2g", + override var repositoryDir: File? = null, +) : LocalEndpoint + +sealed interface ServerEndpoint : SyncEndPoint { + var url: String? + var repositoryId: String? + var branchName: String? + + override fun getValidationErrors(): String { + return buildString { + if (url == null) { + appendUndefinedServerFieldError("url") + } + } + } +} + +data class ServerSource( + override var url: String? = null, + override var repositoryId: String? = null, + override var branchName: String? = null, + var revision: String? = null, +) : ServerEndpoint { + override fun getValidationErrors(): String { + return buildString { + append(super.getValidationErrors()) + if (revision == null) { + if (repositoryId == null && branchName == null) { + appendLine("Invalid server source. Please either specify a revision or repositoryId and branchName.") + } else if (repositoryId == null) { + appendUndefinedServerFieldError("repositoryId") + } else if (branchName == null) { + appendUndefinedServerFieldError("branchName") + } + } + } + } +} + +data class ServerTarget( + override var url: String? = null, + override var repositoryId: String? = null, + override var branchName: String? = null, +) : ServerEndpoint { + override fun getValidationErrors(): String { + return buildString { + append(super.getValidationErrors()) + + if (repositoryId == null) { + appendUndefinedServerFieldError("repositoryId") + } + + if (branchName == null) { + appendUndefinedServerFieldError("branchName") + } + } + } +} + +private fun StringBuilder.appendUndefinedLocalFieldError(fieldName: String) { + appendUndefinedFieldError(fieldName, "LocalEndpoint") +} + +private fun StringBuilder.appendUndefinedServerFieldError(fieldName: String) { + appendUndefinedFieldError(fieldName, "ServerEndpoint") +} + +private fun StringBuilder.appendUndefinedFieldError(fieldName: String, block: String) { + appendLine("Undefined '$fieldName' in '$block'.") +} diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromModelServer.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromModelServer.kt new file mode 100644 index 0000000000..c7521a79e8 --- /dev/null +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromModelServer.kt @@ -0,0 +1,75 @@ +package org.modelix.model.sync.gradle.tasks + +import kotlinx.coroutines.runBlocking +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import org.modelix.model.ModelFacade +import org.modelix.model.api.IBranch +import org.modelix.model.api.PBranch +import org.modelix.model.api.getRootNode +import org.modelix.model.client2.IModelClientV2 +import org.modelix.model.client2.ModelClientV2 +import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder +import org.modelix.model.client2.getReplicatedModel +import org.modelix.model.lazy.RepositoryId +import org.modelix.model.sync.ModelExporter +import javax.inject.Inject + +abstract class ExportFromModelServer @Inject constructor(of: ObjectFactory) : DefaultTask() { + + @Input + val url: Property = of.property(String::class.java) + + @Input + @Optional + val repositoryId: Property = of.property(String::class.java) + + @Input + @Optional + val branchName: Property = of.property(String::class.java) + + @Input + @Optional + val revision: Property = of.property(String::class.java) + + @OutputDirectory + val outputDir: DirectoryProperty = of.directoryProperty() + + @TaskAction + fun export() { + val client = ModelClientV2PlatformSpecificBuilder().url(url.get()).build().apply { runBlocking { init() } } + val branch = if (revision.isPresent) { + getBranchByRevision(client) + } else { + getBranchByRepoIdAndBranch(client) + } + + branch.runRead { + val root = branch.getRootNode() + println("Got root node: $root") + val outputFile = outputDir.get().asFile.resolve("exported-repo.json") + ModelExporter(root).export(outputFile) + } + } + + private fun getBranchByRepoIdAndBranch(client: ModelClientV2): IBranch { + val repoId = RepositoryId(repositoryId.get()) + val branchRef = ModelFacade.createBranchReference(repoId, branchName.get()) + + val branch = runBlocking { + client.getReplicatedModel(branchRef).start() + } + return branch + } + + private fun getBranchByRevision(client: IModelClientV2): IBranch { + val version = runBlocking { client.loadVersion(revision.get(), null) } + return PBranch(version.getTree(), client.getIdGenerator()) + } +} diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromMps.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromMps.kt new file mode 100644 index 0000000000..e8de5cfc78 --- /dev/null +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromMps.kt @@ -0,0 +1,44 @@ +package org.modelix.model.sync.gradle.tasks + +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.JavaExec +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction +import javax.inject.Inject + +abstract class ExportFromMps @Inject constructor(of: ObjectFactory) : JavaExec() { + + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + val mpsHome: DirectoryProperty = of.directoryProperty() + + @InputFile + @PathSensitive(PathSensitivity.RELATIVE) + val antScript: RegularFileProperty = of.fileProperty() + + @OutputDirectory + val jsonDir: DirectoryProperty = of.directoryProperty() + + @TaskAction + override fun exec() { + val jsonDir = jsonDir.get().asFile + val mpsHome = mpsHome.get().asFile + val antVariables = listOf( + "mps.home" to mpsHome.absolutePath, + "mps_home" to mpsHome.absolutePath, + "build.dir" to jsonDir.absolutePath, + ).map { (key, value) -> "-D$key=$value" } + + args("-v") + args(antVariables) + args("-buildfile", antScript.get()) + args("export-modules") + super.exec() + } +} diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/GenerateAntScriptForMps.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/GenerateAntScriptForMps.kt new file mode 100644 index 0000000000..3ad8892ffe --- /dev/null +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/GenerateAntScriptForMps.kt @@ -0,0 +1,125 @@ +package org.modelix.model.sync.gradle.tasks + +import org.gradle.api.DefaultTask +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import java.io.File +import java.util.Properties +import javax.inject.Inject + +@CacheableTask +abstract class GenerateAntScriptForMps @Inject constructor(of: ObjectFactory) : DefaultTask() { + + @Input + val mpsHomePath: Property = of.property(String::class.java) + + @Input + val mpsDependenciesPath: Property = of.property(String::class.java) + + @Input + val mpsHeapSize: Property = of.property(String::class.java) + + @Input + val repositoryPath: Property = of.property(String::class.java) + + @Input + val jsonDirPath: Property = of.property(String::class.java) + + @Input + val exportFlag: Property = of.property(Boolean::class.javaObjectType) + + @get:OutputFile + val antScriptFile: RegularFileProperty = of.fileProperty() + + @Input + val includedModules: ListProperty = of.listProperty(String::class.java) + + @TaskAction + fun generate() { + val isExport = exportFlag.get() + + val antLibs = getAntLibs(File(mpsHomePath.get())) + antScriptFile.get().asFile.parentFile.mkdirs() + antScriptFile.get().asFile.writeText( + """ + + + + + + + + + + + + ${antLibs.joinToString("\n ") { + """""" + }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.trimIndent(), + ) + } + + private fun getAntLibs(mpsHome: File): List { + val mpsVersion = getMpsVersion(mpsHome) + return if (mpsVersion < "2021.2") { + listOf("lib/ant/lib/ant-mps.jar", "lib/log4j.jar", "lib/jdom.jar") + } else if (mpsVersion < "2021.3") { + listOf("lib/ant/lib/ant-mps.jar", "lib/util.jar") + } else { + listOf("lib/ant/lib/ant-mps.jar", "lib/util.jar", "lib/3rd-party-rt.jar") + } + } + + private fun getMpsVersion(mpsHome: File): String { + val buildPropertiesFile = mpsHome.resolve("build.properties") + require(buildPropertiesFile.exists()) { "MPS build.properties file not found: ${buildPropertiesFile.absolutePath}" } + val buildProperties = Properties() + buildPropertiesFile.inputStream().use { buildProperties.load(it) } + + return listOfNotNull( + buildProperties["mpsBootstrapCore.version.major"], + buildProperties["mpsBootstrapCore.version.minor"], + buildProperties["mpsBootstrapCore.version.eap"], + ) + .map { it.toString().trim('.') } + .filter { it.isNotEmpty() } + .joinToString(".") + } +} diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoModelServer.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoModelServer.kt new file mode 100644 index 0000000000..90ff5e89d4 --- /dev/null +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoModelServer.kt @@ -0,0 +1,59 @@ +package org.modelix.model.sync.gradle.tasks + +import kotlinx.coroutines.runBlocking +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction +import org.modelix.model.ModelFacade +import org.modelix.model.api.getRootNode +import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder +import org.modelix.model.client2.getReplicatedModel +import org.modelix.model.lazy.RepositoryId +import org.modelix.model.sync.ModelImporter +import org.modelix.model.sync.import +import javax.inject.Inject + +abstract class ImportIntoModelServer @Inject constructor(of: ObjectFactory) : DefaultTask() { + + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + val inputDir: DirectoryProperty = of.directoryProperty() + + @Input + val repositoryId: Property = of.property(String::class.java) + + @Input + val branchName: Property = of.property(String::class.java) + + @Input + val url: Property = of.property(String::class.java) + + @TaskAction + fun import() { + val inputDir = inputDir.get().asFile + val repoId = RepositoryId(repositoryId.get()) + + val branchRef = ModelFacade.createBranchReference(repoId, branchName.get()) + val client = ModelClientV2PlatformSpecificBuilder().url(url.get()).build() + val branch = runBlocking { + client.init() + client.getReplicatedModel(branchRef).start() + } + + val file = inputDir.listFiles()?.first { it.extension == "json" } ?: error("json file not found") + + branch.runWrite { + val rootNode = branch.getRootNode() + println("Got root node: $rootNode") + println("Importing...") + ModelImporter(branch.getRootNode()).import(file) + println("Import finished") + } + } +} diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoMps.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoMps.kt new file mode 100644 index 0000000000..c2f0487558 --- /dev/null +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoMps.kt @@ -0,0 +1,44 @@ +package org.modelix.model.sync.gradle.tasks + +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.JavaExec +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction +import javax.inject.Inject + +abstract class ImportIntoMps @Inject constructor(of: ObjectFactory) : JavaExec() { + + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + val jsonDir: DirectoryProperty = of.directoryProperty() + + @InputFile + @PathSensitive(PathSensitivity.RELATIVE) + val antScript: RegularFileProperty = of.fileProperty() + + @OutputDirectory + val mpsHome: DirectoryProperty = of.directoryProperty() + + @TaskAction + override fun exec() { + val jsonDir = jsonDir.get().asFile + val mpsHome = mpsHome.get().asFile + val antVariables = listOf( + "mps.home" to mpsHome.absolutePath, + "mps_home" to mpsHome.absolutePath, + "build.dir" to jsonDir.absolutePath, + ).map { (key, value) -> "-D$key=$value" } + + args("-v") + args(antVariables) + args("-buildfile", antScript.get()) + args("import-modules") + super.exec() + } +} diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ValidateSyncSettings.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ValidateSyncSettings.kt new file mode 100644 index 0000000000..17e82b3553 --- /dev/null +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ValidateSyncSettings.kt @@ -0,0 +1,92 @@ +package org.modelix.model.sync.gradle.tasks + +import org.gradle.api.DefaultTask +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction +import org.modelix.model.sync.gradle.config.LocalSource +import org.modelix.model.sync.gradle.config.LocalTarget +import org.modelix.model.sync.gradle.config.ModelSyncGradleSettings +import org.modelix.model.sync.gradle.config.ServerSource +import org.modelix.model.sync.gradle.config.ServerTarget +import org.modelix.model.sync.gradle.config.SyncDirection +import javax.inject.Inject + +@CacheableTask +abstract class ValidateSyncSettings @Inject constructor(of: ObjectFactory) : DefaultTask() { + + @Input + val settings: Property = of.property(ModelSyncGradleSettings::class.java) + + private val errorMsgBuilder = StringBuilder() + + @TaskAction + fun validate() { + errorMsgBuilder.clear() + val settings = settings.get() + + check(settings.syncDirections.isNotEmpty()) { "No sync direction defined. Add one via direction {}." } + val duplicateNames = settings.syncDirections.groupBy { it.name } + .filter { (_, list) -> list.size > 1 } + .map { it.key } + .toSet() + + if (duplicateNames.isNotEmpty()) { + error("Duplicate sync directions: ${duplicateNames.joinToString(separator = ", ", prefix = "'", postfix = "'")}") + } + + settings.syncDirections.forEach { validateSyncDirection(it) } + + val errorMsg = errorMsgBuilder.toString() + if (errorMsg.isNotEmpty()) { + error("Invalid Model Sync Settings \n${errorMsg.prependIndent(" ")}") + } + } + + private fun validateSyncDirection(direction: SyncDirection) { + val syncDirectionErrors = buildString { + if (direction.name.isEmpty()) { + appendLine("Undefined name.") + } + + if (direction.source == null) { + appendLine("Undefined source.") + } + if (direction.target == null) { + appendLine("Undefined target.") + } + + if (direction.source == null || direction.target == null) { + return@buildString + } + + if (direction.source is LocalSource && direction.target !is ServerTarget) { + appendLine("Invalid target for local source. Currently only server targets are allowed for local sources.") + return@buildString + } + + if (direction.source is ServerSource && direction.target !is LocalTarget) { + appendLine("Invalid target for server source. Currently only local targets are allowed for server sources.") + return@buildString + } + + val sourceErrors = direction.source?.getValidationErrors() ?: "" + if (sourceErrors.isNotEmpty()) { + appendLine() + appendLine(sourceErrors) + } + + val targetErrors = direction.target?.getValidationErrors() ?: "" + if (targetErrors.isNotEmpty()) { + appendLine(targetErrors) + } + } + + if (syncDirectionErrors.isNotEmpty()) { + errorMsgBuilder.appendLine("Invalid sync direction '${direction.name}':") + errorMsgBuilder.append(syncDirectionErrors.prependIndent(" ")) + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 923711c17b..55d6cc217e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -25,6 +25,7 @@ include("model-datastructure") include("model-server") include("model-server-api") include("model-server-lib") +include("model-sync-gradle") include("model-sync-lib") include("modelql-client") include("modelql-core") From 3f89de316d427d2b23797272f2a5441fd12da1d0 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Tue, 22 Aug 2023 16:36:09 +0200 Subject: [PATCH 04/43] feat(bulk-model-sync-solution): model sync mps solution --- .../gradle/tasks/GenerateAntScriptForMps.kt | 4 +- .../kotlin/org/modelix/model/sync/Util.kt | 24 + .../modelix/model/sync/PlatformSpecific.kt | 13 +- model-sync-mps/.mps/.gitignore | 3 + model-sync-mps/.mps/.name | 1 + model-sync-mps/.mps/migration.xml | 6 + model-sync-mps/.mps/modules.xml | 8 + model-sync-mps/.mps/vcs.xml | 6 + model-sync-mps/build.gradle.kts | 40 ++ .../org.modelix.model.sync.mps/.gitignore | 4 + .../models/org.modelix.model.sync.mps.mps | 615 ++++++++++++++++++ .../org.modelix.model.sync.mps.msd | 70 ++ settings.gradle.kts | 1 + 13 files changed, 792 insertions(+), 3 deletions(-) create mode 100644 model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/Util.kt create mode 100644 model-sync-mps/.mps/.gitignore create mode 100644 model-sync-mps/.mps/.name create mode 100644 model-sync-mps/.mps/migration.xml create mode 100644 model-sync-mps/.mps/modules.xml create mode 100644 model-sync-mps/.mps/vcs.xml create mode 100644 model-sync-mps/build.gradle.kts create mode 100644 model-sync-mps/solutions/org.modelix.model.sync.mps/.gitignore create mode 100644 model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps create mode 100644 model-sync-mps/solutions/org.modelix.model.sync.mps/org.modelix.model.sync.mps.msd diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/GenerateAntScriptForMps.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/GenerateAntScriptForMps.kt index 3ad8892ffe..4fd7ec274d 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/GenerateAntScriptForMps.kt +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/GenerateAntScriptForMps.kt @@ -64,7 +64,7 @@ abstract class GenerateAntScriptForMps @Inject constructor(of: ObjectFactory) : }} - + @@ -75,7 +75,7 @@ abstract class GenerateAntScriptForMps @Inject constructor(of: ObjectFactory) : - + diff --git a/model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/Util.kt b/model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/Util.kt new file mode 100644 index 0000000000..a56b17e493 --- /dev/null +++ b/model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/Util.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.sync + +import org.modelix.model.data.ModelData +import org.modelix.model.data.NodeData + +fun mergeModelData(vararg models: ModelData): ModelData { + return ModelData(root = NodeData(children = models.map { it.root })) +} diff --git a/model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/PlatformSpecific.kt b/model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/PlatformSpecific.kt index 8560fd5119..d0e1dd275a 100644 --- a/model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/PlatformSpecific.kt +++ b/model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/PlatformSpecific.kt @@ -3,6 +3,12 @@ package org.modelix.model.sync import org.modelix.model.data.ModelData import java.io.File +// import is a reserved keyword in java +@Deprecated("Use importFile instead", ReplaceWith("importFile(jsonFile)")) +fun ModelImporter.import(jsonFile: File) { + this.importFile(jsonFile) +} + /** * Incrementally updates the root of the receiver [ModelImporter] * based on the [ModelData] specification contained in the given file. @@ -11,10 +17,15 @@ import java.io.File * * @throws IllegalArgumentException if the file is not a json file or the file does not exist. */ -fun ModelImporter.import(jsonFile: File) { +fun ModelImporter.importFile(jsonFile: File) { require(jsonFile.exists()) require(jsonFile.extension == "json") val data = ModelData.fromJson(jsonFile.readText()) import(data) } + +fun ModelImporter.importFilesAsRootChildren(vararg files: File) { + val models = files.map { ModelData.fromJson(it.readText()) } + import(mergeModelData(*models.toTypedArray())) +} diff --git a/model-sync-mps/.mps/.gitignore b/model-sync-mps/.mps/.gitignore new file mode 100644 index 0000000000..26d33521af --- /dev/null +++ b/model-sync-mps/.mps/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/model-sync-mps/.mps/.name b/model-sync-mps/.mps/.name new file mode 100644 index 0000000000..25ea1e6d93 --- /dev/null +++ b/model-sync-mps/.mps/.name @@ -0,0 +1 @@ +org.modelix.model.sync.mps diff --git a/model-sync-mps/.mps/migration.xml b/model-sync-mps/.mps/migration.xml new file mode 100644 index 0000000000..d512ce4547 --- /dev/null +++ b/model-sync-mps/.mps/migration.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/model-sync-mps/.mps/modules.xml b/model-sync-mps/.mps/modules.xml new file mode 100644 index 0000000000..3a51249c45 --- /dev/null +++ b/model-sync-mps/.mps/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/model-sync-mps/.mps/vcs.xml b/model-sync-mps/.mps/vcs.xml new file mode 100644 index 0000000000..54e4b961ee --- /dev/null +++ b/model-sync-mps/.mps/vcs.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/model-sync-mps/build.gradle.kts b/model-sync-mps/build.gradle.kts new file mode 100644 index 0000000000..0bfbec4050 --- /dev/null +++ b/model-sync-mps/build.gradle.kts @@ -0,0 +1,40 @@ +import org.modelix.gradle.mpsbuild.MPSBuildSettings + +plugins { + base + alias(libs.plugins.modelix.mps.buildtools) +} + +val generatorLibs: Configuration by configurations.creating + +dependencies { + generatorLibs(project(":model-sync-lib")) + generatorLibs(project(":mps-model-adapters")) +} + +val copyLibs by tasks.registering(Sync::class) { + from(generatorLibs) + into(projectDir.resolve("solutions/org.modelix.model.sync.mps/lib").apply { mkdirs() }) + rename { fileName -> + generatorLibs.resolvedConfiguration.resolvedArtifacts + .find { it.file.name == fileName }?.let { + if (it.classifier == null) { + "${it.name}.${it.extension}" + } else { + "${it.name}-${it.classifier}.${it.extension}" + } + } + ?: fileName + } +} + +extensions.configure { + dependsOn(copyLibs) + mpsVersion("2021.1.4") + search(".") + disableParentPublication() + + publication("model-sync-mps") { + module("org.modelix.model.sync.mps") + } +} diff --git a/model-sync-mps/solutions/org.modelix.model.sync.mps/.gitignore b/model-sync-mps/solutions/org.modelix.model.sync.mps/.gitignore new file mode 100644 index 0000000000..570819bb1d --- /dev/null +++ b/model-sync-mps/solutions/org.modelix.model.sync.mps/.gitignore @@ -0,0 +1,4 @@ +/lib +/source_gen +/source_gen.caches +/classes_gen diff --git a/model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps b/model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps new file mode 100644 index 0000000000..e061541d2d --- /dev/null +++ b/model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps @@ -0,0 +1,615 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/model-sync-mps/solutions/org.modelix.model.sync.mps/org.modelix.model.sync.mps.msd b/model-sync-mps/solutions/org.modelix.model.sync.mps/org.modelix.model.sync.mps.msd new file mode 100644 index 0000000000..6caeac03e8 --- /dev/null +++ b/model-sync-mps/solutions/org.modelix.model.sync.mps/org.modelix.model.sync.mps.msd @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 8865b7a8-5271-43d3-884c-6fd1d9cfdd34(MPS.OpenAPI) + 742f6602-5a2f-4313-aa6e-ae1cd4ffdc61(MPS.Platform) + 498d89d2-c2e9-11e2-ad49-6cf049e62fe5(MPS.IDEA) + 6354ebe7-c22a-4a0f-ac54-50b52ab9b065(JDK) + 2d3c70e9-aab2-4870-8d8d-6036800e4103(jetbrains.mps.kernel) + 9b80526e-f0bf-4992-bdf5-cee39c1833f3(collections.runtime) + + + + + + + + + + + + + + + + + + + + + + diff --git a/settings.gradle.kts b/settings.gradle.kts index 55d6cc217e..74b48f9c01 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -27,6 +27,7 @@ include("model-server-api") include("model-server-lib") include("model-sync-gradle") include("model-sync-lib") +include("model-sync-mps") include("modelql-client") include("modelql-core") include("modelql-html") From 1d989063bd54f54c375bb04ae3b5e9a178ef1606 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Tue, 22 Aug 2023 16:37:28 +0200 Subject: [PATCH 05/43] test(bulk-model-sync-gradle): add plugin test for ci --- .github/workflows/build.yaml | 4 + .../gradle/wrapper/gradle-wrapper.jar | 1 + bulk-model-sync-gradle-test/gradlew | 1 + bulk-model-sync-gradle-test/gradlew.bat | 1 + .../gradle/wrapper/gradle-wrapper.jar | 1 + .../graph-lang-api/gradlew | 1 + .../graph-lang-api/gradlew.bat | 1 + .../gradle/wrapper/gradle-wrapper.jar | Bin 61608 -> 42 bytes model-api-gen-gradle-test/gradlew | 245 +----------------- model-api-gen-gradle-test/gradlew.bat | 93 +------ model-sync-gradle-test/.gitignore | 49 ++++ model-sync-gradle-test/build.gradle.kts | 109 ++++++++ model-sync-gradle-test/ci.sh | 35 +++ model-sync-gradle-test/gradle.properties | 1 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61574 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + model-sync-gradle-test/gradlew | 244 +++++++++++++++++ model-sync-gradle-test/gradlew.bat | 92 +++++++ .../graph-lang-api/.gitignore | 1 + .../graph-lang-api/build.gradle.kts | 68 +++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61574 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + model-sync-gradle-test/graph-lang-api/gradlew | 244 +++++++++++++++++ .../graph-lang-api/gradlew.bat | 92 +++++++ .../graph-lang-api/settings.gradle.kts | 29 +++ model-sync-gradle-test/settings.gradle.kts | 30 +++ .../model/sync/gradle/test/PullTest.kt | 16 ++ .../model/sync/gradle/test/PushTest.kt | 47 ++++ .../test-repo/.mps/.gitignore | 3 + model-sync-gradle-test/test-repo/.mps/.name | 1 + .../test-repo/.mps/migration.xml | 6 + .../test-repo/.mps/modules.xml | 9 + model-sync-gradle-test/test-repo/.mps/vcs.xml | 6 + .../languages/GraphLang/GraphLang.mpl | 102 ++++++++ .../GraphLang/models/GraphLang.behavior.mps | 11 + .../models/GraphLang.constraints.mps | 18 ++ .../GraphLang/models/GraphLang.editor.mps | 132 ++++++++++ .../GraphLang/models/GraphLang.structure.mps | 84 ++++++ .../GraphLang/models/GraphLang.typesystem.mps | 10 + .../solutions/GraphSolution/GraphSolution.msd | 21 ++ .../models/GraphSolution.example.mps | 59 +++++ 41 files changed, 1543 insertions(+), 336 deletions(-) create mode 120000 bulk-model-sync-gradle-test/gradle/wrapper/gradle-wrapper.jar create mode 120000 bulk-model-sync-gradle-test/gradlew create mode 120000 bulk-model-sync-gradle-test/gradlew.bat create mode 120000 bulk-model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.jar create mode 120000 bulk-model-sync-gradle-test/graph-lang-api/gradlew create mode 120000 bulk-model-sync-gradle-test/graph-lang-api/gradlew.bat mode change 100644 => 120000 model-api-gen-gradle-test/gradle/wrapper/gradle-wrapper.jar mode change 100755 => 120000 model-api-gen-gradle-test/gradlew mode change 100644 => 120000 model-api-gen-gradle-test/gradlew.bat create mode 100644 model-sync-gradle-test/.gitignore create mode 100644 model-sync-gradle-test/build.gradle.kts create mode 100755 model-sync-gradle-test/ci.sh create mode 100644 model-sync-gradle-test/gradle.properties create mode 100644 model-sync-gradle-test/gradle/wrapper/gradle-wrapper.jar create mode 100644 model-sync-gradle-test/gradle/wrapper/gradle-wrapper.properties create mode 100755 model-sync-gradle-test/gradlew create mode 100644 model-sync-gradle-test/gradlew.bat create mode 100644 model-sync-gradle-test/graph-lang-api/.gitignore create mode 100644 model-sync-gradle-test/graph-lang-api/build.gradle.kts create mode 100644 model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.jar create mode 100644 model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.properties create mode 100755 model-sync-gradle-test/graph-lang-api/gradlew create mode 100644 model-sync-gradle-test/graph-lang-api/gradlew.bat create mode 100644 model-sync-gradle-test/graph-lang-api/settings.gradle.kts create mode 100644 model-sync-gradle-test/settings.gradle.kts create mode 100644 model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt create mode 100644 model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt create mode 100644 model-sync-gradle-test/test-repo/.mps/.gitignore create mode 100644 model-sync-gradle-test/test-repo/.mps/.name create mode 100644 model-sync-gradle-test/test-repo/.mps/migration.xml create mode 100644 model-sync-gradle-test/test-repo/.mps/modules.xml create mode 100644 model-sync-gradle-test/test-repo/.mps/vcs.xml create mode 100644 model-sync-gradle-test/test-repo/languages/GraphLang/GraphLang.mpl create mode 100644 model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.behavior.mps create mode 100644 model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.constraints.mps create mode 100644 model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.editor.mps create mode 100644 model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.structure.mps create mode 100644 model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.typesystem.mps create mode 100644 model-sync-gradle-test/test-repo/solutions/GraphSolution/GraphSolution.msd create mode 100644 model-sync-gradle-test/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d7ffcd0857..1db68b0aa9 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -34,6 +34,10 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: model-api-gen-gradle-test/ci.sh + - name: Test Model Sync Gradle Plugin + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: model-sync-gradle-test/ci.sh - name: Archive test report uses: actions/upload-artifact@v3 if: always() diff --git a/bulk-model-sync-gradle-test/gradle/wrapper/gradle-wrapper.jar b/bulk-model-sync-gradle-test/gradle/wrapper/gradle-wrapper.jar new file mode 120000 index 0000000000..b4214b888a --- /dev/null +++ b/bulk-model-sync-gradle-test/gradle/wrapper/gradle-wrapper.jar @@ -0,0 +1 @@ +../../../gradle/wrapper/gradle-wrapper.jar \ No newline at end of file diff --git a/bulk-model-sync-gradle-test/gradlew b/bulk-model-sync-gradle-test/gradlew new file mode 120000 index 0000000000..502f5a2d3e --- /dev/null +++ b/bulk-model-sync-gradle-test/gradlew @@ -0,0 +1 @@ +../gradlew \ No newline at end of file diff --git a/bulk-model-sync-gradle-test/gradlew.bat b/bulk-model-sync-gradle-test/gradlew.bat new file mode 120000 index 0000000000..2840132887 --- /dev/null +++ b/bulk-model-sync-gradle-test/gradlew.bat @@ -0,0 +1 @@ +../gradlew.bat \ No newline at end of file diff --git a/bulk-model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.jar b/bulk-model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.jar new file mode 120000 index 0000000000..75a6c2799b --- /dev/null +++ b/bulk-model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.jar @@ -0,0 +1 @@ +../../../../gradle/wrapper/gradle-wrapper.jar \ No newline at end of file diff --git a/bulk-model-sync-gradle-test/graph-lang-api/gradlew b/bulk-model-sync-gradle-test/graph-lang-api/gradlew new file mode 120000 index 0000000000..343e0d2caa --- /dev/null +++ b/bulk-model-sync-gradle-test/graph-lang-api/gradlew @@ -0,0 +1 @@ +../../gradlew \ No newline at end of file diff --git a/bulk-model-sync-gradle-test/graph-lang-api/gradlew.bat b/bulk-model-sync-gradle-test/graph-lang-api/gradlew.bat new file mode 120000 index 0000000000..cb5a946451 --- /dev/null +++ b/bulk-model-sync-gradle-test/graph-lang-api/gradlew.bat @@ -0,0 +1 @@ +../../gradlew.bat \ No newline at end of file diff --git a/model-api-gen-gradle-test/gradle/wrapper/gradle-wrapper.jar b/model-api-gen-gradle-test/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index ccebba7710deaf9f98673a68957ea02138b60d0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61608 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 diff --git a/model-api-gen-gradle-test/gradle/wrapper/gradle-wrapper.jar b/model-api-gen-gradle-test/gradle/wrapper/gradle-wrapper.jar new file mode 120000 index 0000000000..b4214b888a --- /dev/null +++ b/model-api-gen-gradle-test/gradle/wrapper/gradle-wrapper.jar @@ -0,0 +1 @@ +../../../gradle/wrapper/gradle-wrapper.jar \ No newline at end of file diff --git a/model-api-gen-gradle-test/gradlew b/model-api-gen-gradle-test/gradlew deleted file mode 100755 index 79a61d421c..0000000000 --- a/model-api-gen-gradle-test/gradlew +++ /dev/null @@ -1,244 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/model-api-gen-gradle-test/gradlew b/model-api-gen-gradle-test/gradlew new file mode 120000 index 0000000000..502f5a2d3e --- /dev/null +++ b/model-api-gen-gradle-test/gradlew @@ -0,0 +1 @@ +../gradlew \ No newline at end of file diff --git a/model-api-gen-gradle-test/gradlew.bat b/model-api-gen-gradle-test/gradlew.bat deleted file mode 100644 index 93e3f59f13..0000000000 --- a/model-api-gen-gradle-test/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/model-api-gen-gradle-test/gradlew.bat b/model-api-gen-gradle-test/gradlew.bat new file mode 120000 index 0000000000..2840132887 --- /dev/null +++ b/model-api-gen-gradle-test/gradlew.bat @@ -0,0 +1 @@ +../gradlew.bat \ No newline at end of file diff --git a/model-sync-gradle-test/.gitignore b/model-sync-gradle-test/.gitignore new file mode 100644 index 0000000000..5985ed9f9d --- /dev/null +++ b/model-sync-gradle-test/.gitignore @@ -0,0 +1,49 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store +/test-repo/languages/GraphLang/classes_gen/ +/test-repo/languages/GraphLang/generator/ +/test-repo/languages/GraphLang/source_gen/ +/test-repo/languages/GraphLang/source_gen.caches/ +/test-repo/solutions/GraphSolution/source_gen/ +/test-repo/solutions/GraphSolution/classes_gen/ +/test-repo/solutions/GraphSolution/source_gen.caches/ diff --git a/model-sync-gradle-test/build.gradle.kts b/model-sync-gradle-test/build.gradle.kts new file mode 100644 index 0000000000..5e739cb36a --- /dev/null +++ b/model-sync-gradle-test/build.gradle.kts @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import GraphLang.L_GraphLang +import org.modelix.model.server.Main + +buildscript { + val modelixCoreVersion: String = file("../version.txt").readText() + dependencies { + classpath("org.modelix:model-server:$modelixCoreVersion") + classpath("org.modelix:graph-lang-api:$modelixCoreVersion") + } +} + +plugins { + kotlin("jvm") + id("org.modelix.model-sync") +} + +val modelixCoreVersion: String = file("../version.txt").readText() + +version = modelixCoreVersion + +repositories { + mavenLocal() + maven { url = uri("https://repo.maven.apache.org/maven2") } + maven { url = uri("https://plugins.gradle.org/m2/") } + mavenCentral() + maven { url = uri("https://artifacts.itemis.cloud/repository/maven-mps/") } +} + +val mps by configurations.creating +val mpsDir = buildDir.resolve("mps").apply { mkdirs() } +val kotlinGenDir = buildDir.resolve("metamodel/kotlin").apply { mkdirs() } + +dependencies { + mps("com.jetbrains:mps:2021.1.4") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.2") + implementation("org.modelix:model-server:$modelixCoreVersion") + implementation("org.modelix:model-api-gen-runtime:$modelixCoreVersion") + testImplementation("org.modelix:model-client:$modelixCoreVersion") + testImplementation("org.modelix:model-sync-lib:$modelixCoreVersion") + testImplementation("org.modelix.mps:model-adapters:$modelixCoreVersion") + testImplementation("org.modelix:graph-lang-api:$modelixCoreVersion") + testImplementation(kotlin("test")) +} + +tasks.test { + useJUnitPlatform() +} + +kotlin { + jvmToolchain(11) +} + +tasks.register("runModelServer", JavaExec::class) { + group = "modelix" + classpath = sourceSets["main"].runtimeClasspath + mainClass.set("org.modelix.model.server.Main") + args("-inmemory") +} + +val resolveMps by tasks.registering(Copy::class) { + from(mps.resolve().map { zipTree(it) }) + into(mpsDir) +} + +val repoDir = projectDir.resolve("test-repo") + +modelSync { + direction("testPush") { + registerLanguage(L_GraphLang) + includeModule("GraphSolution") + fromLocal { + mpsHome = mpsDir + mpsHeapSize = "4g" + repositoryDir = repoDir + } + toModelServer { + url = "http://0.0.0.0:${Main.DEFAULT_PORT}/v2" + repositoryId = "ci-test" + branchName = "master" + } + } + direction("testPull") { + fromModelServer { + url = "http://0.0.0.0:${Main.DEFAULT_PORT}/v2" + repositoryId = "ci-test" + branchName = "master" + } + toLocal { + mpsHome = mpsDir + repositoryDir = repoDir + } + } +} diff --git a/model-sync-gradle-test/ci.sh b/model-sync-gradle-test/ci.sh new file mode 100755 index 0000000000..7358b0bd97 --- /dev/null +++ b/model-sync-gradle-test/ci.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +set -e +( + cd "$(dirname "$0")" + + ( + cd graph-lang-api + ./gradlew publishToMavenLocal + ) + + ./gradlew assemble --console=plain + + trap cleanup INT TERM EXIT + cleanup () { + kill "${MODEL_SERVER_PID}" + exit + } + + ./gradlew runModelServer --console=plain & + MODEL_SERVER_PID=$! + sleep 5 + + #CI needs more time + if [ "${CI}" = "true" ]; then + sleep 15 + fi + + curl -X POST http://127.0.0.1:28101/v2/repositories/ci-test/init + + ./gradlew runSyncTestPush --console=plain --stacktrace + ./gradlew test --tests 'PushTest' + #./gradlew runSyncTestPull --console=plain --stacktrace + #./gradlew test --tests 'PullTest' +) diff --git a/model-sync-gradle-test/gradle.properties b/model-sync-gradle-test/gradle.properties new file mode 100644 index 0000000000..7fc6f1ff27 --- /dev/null +++ b/model-sync-gradle-test/gradle.properties @@ -0,0 +1 @@ +kotlin.code.style=official diff --git a/model-sync-gradle-test/gradle/wrapper/gradle-wrapper.jar b/model-sync-gradle-test/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..943f0cbfa754578e88a3dae77fce6e3dea56edbf GIT binary patch literal 61574 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 ScNgau(U?ep-K_E5zy1%ZQTdPn literal 0 HcmV?d00001 diff --git a/model-sync-gradle-test/gradle/wrapper/gradle-wrapper.properties b/model-sync-gradle-test/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..17a8ddce2d --- /dev/null +++ b/model-sync-gradle-test/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/model-sync-gradle-test/gradlew b/model-sync-gradle-test/gradlew new file mode 100755 index 0000000000..79a61d421c --- /dev/null +++ b/model-sync-gradle-test/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/model-sync-gradle-test/gradlew.bat b/model-sync-gradle-test/gradlew.bat new file mode 100644 index 0000000000..93e3f59f13 --- /dev/null +++ b/model-sync-gradle-test/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/model-sync-gradle-test/graph-lang-api/.gitignore b/model-sync-gradle-test/graph-lang-api/.gitignore new file mode 100644 index 0000000000..85de9cf933 --- /dev/null +++ b/model-sync-gradle-test/graph-lang-api/.gitignore @@ -0,0 +1 @@ +src diff --git a/model-sync-gradle-test/graph-lang-api/build.gradle.kts b/model-sync-gradle-test/graph-lang-api/build.gradle.kts new file mode 100644 index 0000000000..87312a3cfe --- /dev/null +++ b/model-sync-gradle-test/graph-lang-api/build.gradle.kts @@ -0,0 +1,68 @@ +plugins { + id("org.modelix.model-api-gen") + `maven-publish` + kotlin("jvm") version "1.9.0" +} + +repositories { + mavenLocal() + gradlePluginPortal() + maven { url = uri("https://artifacts.itemis.cloud/repository/maven-mps/") } + mavenCentral() +} + +val modelixCoreVersion = file("../../version.txt").readText() + +version = modelixCoreVersion + +val mps by configurations.creating +val kotlinGenDir = buildDir.resolve("metamodel/kotlin").apply { mkdirs() } + +dependencies { + mps("com.jetbrains:mps:2021.1.4") + api("org.modelix:model-api-gen-runtime:$modelixCoreVersion") +} + +val copyGeneratedApiToSrc by tasks.registering(Sync::class) { + dependsOn(tasks.named("generateMetaModelSources")) + from(kotlinGenDir) + into("src/main/kotlin") +} + +val mpsDir = buildDir.resolve("mps").apply { mkdirs() } + +val resolveMps by tasks.registering(Copy::class) { + from(mps.resolve().map { zipTree(it) }) + into(mpsDir) +} + +val repoDir = projectDir.resolve("test-repo") + +val copyMetamodelToMpsHome by tasks.registering(Copy::class) { + from(file(projectDir.resolve("../test-repo/languages"))) + into(file(mpsDir.resolve("languages").apply { mkdirs() })) +} + +metamodel { + dependsOn(resolveMps) + dependsOn(copyMetamodelToMpsHome) + mpsHome = mpsDir + kotlinDir = kotlinGenDir + includeLanguage("GraphLang") +} + +publishing { + publications { + create("maven") { + groupId = "org.modelix" + from(components["kotlin"]) + } + } + repositories { + mavenLocal() + } +} + +tasks.named("compileKotlin") { + dependsOn(copyGeneratedApiToSrc) +} diff --git a/model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.jar b/model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..943f0cbfa754578e88a3dae77fce6e3dea56edbf GIT binary patch literal 61574 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 ScNgau(U?ep-K_E5zy1%ZQTdPn literal 0 HcmV?d00001 diff --git a/model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.properties b/model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..17a8ddce2d --- /dev/null +++ b/model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/model-sync-gradle-test/graph-lang-api/gradlew b/model-sync-gradle-test/graph-lang-api/gradlew new file mode 100755 index 0000000000..79a61d421c --- /dev/null +++ b/model-sync-gradle-test/graph-lang-api/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/model-sync-gradle-test/graph-lang-api/gradlew.bat b/model-sync-gradle-test/graph-lang-api/gradlew.bat new file mode 100644 index 0000000000..93e3f59f13 --- /dev/null +++ b/model-sync-gradle-test/graph-lang-api/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/model-sync-gradle-test/graph-lang-api/settings.gradle.kts b/model-sync-gradle-test/graph-lang-api/settings.gradle.kts new file mode 100644 index 0000000000..2a6b565964 --- /dev/null +++ b/model-sync-gradle-test/graph-lang-api/settings.gradle.kts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pluginManagement { + val modelixCoreVersion: String = file("../../version.txt").readText() + plugins { + id("org.modelix.model-sync") version modelixCoreVersion + id("org.modelix.model-api-gen") version modelixCoreVersion + } + repositories { + mavenLocal() + gradlePluginPortal() + maven { url = uri("https://artifacts.itemis.cloud/repository/maven-mps/") } + mavenCentral() + } +} diff --git a/model-sync-gradle-test/settings.gradle.kts b/model-sync-gradle-test/settings.gradle.kts new file mode 100644 index 0000000000..ec53a134c7 --- /dev/null +++ b/model-sync-gradle-test/settings.gradle.kts @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pluginManagement { + val modelixCoreVersion: String = file("../version.txt").readText() + plugins { + id("org.modelix.model-sync") version modelixCoreVersion + id("org.modelix.model-api-gen") version modelixCoreVersion + kotlin("jvm") version "1.9.0" + } + repositories { + mavenLocal() + gradlePluginPortal() + maven { url = uri("https://artifacts.itemis.cloud/repository/maven-mps/") } + mavenCentral() + } +} diff --git a/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt b/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt new file mode 100644 index 0000000000..fb2f592f34 --- /dev/null +++ b/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt @@ -0,0 +1,16 @@ +package org.modelix.model.sync.gradle.test + +import org.junit.jupiter.api.Test +import org.modelix.model.data.ModelData +import java.io.File + +class PullTest { + + @Test + fun `nodes were synced to local`() { + val inputJson = File("build/model-sync/testPull").listFiles() + ?.first { it.exists() && it.extension == "json" } ?: throw RuntimeException("input json not found") + + val inputRoot = ModelData.fromJson(inputJson.readText()).root + } +} diff --git a/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt b/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt new file mode 100644 index 0000000000..9198b63d6f --- /dev/null +++ b/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt @@ -0,0 +1,47 @@ +package org.modelix.model.sync.gradle.test + +import GraphLang.L_GraphLang +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.Test +import org.modelix.model.ModelFacade +import org.modelix.model.api.ILanguageRepository +import org.modelix.model.api.getRootNode +import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder +import org.modelix.model.client2.getReplicatedModel +import org.modelix.model.data.ModelData +import org.modelix.model.lazy.RepositoryId +import org.modelix.model.mpsadapters.RepositoryLanguage +import org.modelix.model.server.Main +import org.modelix.model.sync.asExported +import java.io.File +import kotlin.test.assertEquals + +class PushTest { + + @Test + fun `nodes were synced to server`() { + val inputJson = File("build/model-sync/testPush").listFiles() + ?.first { it.exists() && it.extension == "json" } ?: throw RuntimeException("input json not found") + + val inputRoot = ModelData.fromJson(inputJson.readText()).root + + ILanguageRepository.default.registerLanguage(L_GraphLang) + ILanguageRepository.default.registerLanguage(RepositoryLanguage) + + val repoId = RepositoryId("ci-test") + val branchName = "master" + val url = "http://0.0.0.0:${Main.DEFAULT_PORT}/v2" + val branchRef = ModelFacade.createBranchReference(repoId, branchName) + val client = ModelClientV2PlatformSpecificBuilder().url(url).build() + + val branch = runBlocking { + client.init() + client.getReplicatedModel(branchRef).start() + } + branch.runRead { + assertEquals(inputRoot, branch.getRootNode().asExported()) + } + + // TODO Prepare next stage + } +} diff --git a/model-sync-gradle-test/test-repo/.mps/.gitignore b/model-sync-gradle-test/test-repo/.mps/.gitignore new file mode 100644 index 0000000000..26d33521af --- /dev/null +++ b/model-sync-gradle-test/test-repo/.mps/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/model-sync-gradle-test/test-repo/.mps/.name b/model-sync-gradle-test/test-repo/.mps/.name new file mode 100644 index 0000000000..4f5582dc75 --- /dev/null +++ b/model-sync-gradle-test/test-repo/.mps/.name @@ -0,0 +1 @@ +Graph diff --git a/model-sync-gradle-test/test-repo/.mps/migration.xml b/model-sync-gradle-test/test-repo/.mps/migration.xml new file mode 100644 index 0000000000..d512ce4547 --- /dev/null +++ b/model-sync-gradle-test/test-repo/.mps/migration.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/model-sync-gradle-test/test-repo/.mps/modules.xml b/model-sync-gradle-test/test-repo/.mps/modules.xml new file mode 100644 index 0000000000..a2d5259147 --- /dev/null +++ b/model-sync-gradle-test/test-repo/.mps/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/model-sync-gradle-test/test-repo/.mps/vcs.xml b/model-sync-gradle-test/test-repo/.mps/vcs.xml new file mode 100644 index 0000000000..2926018e29 --- /dev/null +++ b/model-sync-gradle-test/test-repo/.mps/vcs.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/model-sync-gradle-test/test-repo/languages/GraphLang/GraphLang.mpl b/model-sync-gradle-test/test-repo/languages/GraphLang/GraphLang.mpl new file mode 100644 index 0000000000..14d188766b --- /dev/null +++ b/model-sync-gradle-test/test-repo/languages/GraphLang/GraphLang.mpl @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.behavior.mps b/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.behavior.mps new file mode 100644 index 0000000000..b70e7137d5 --- /dev/null +++ b/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.behavior.mps @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.constraints.mps b/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.constraints.mps new file mode 100644 index 0000000000..8182541cca --- /dev/null +++ b/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.constraints.mps @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.editor.mps b/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.editor.mps new file mode 100644 index 0000000000..21f81d0157 --- /dev/null +++ b/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.editor.mps @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.structure.mps b/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.structure.mps new file mode 100644 index 0000000000..b1ea87789e --- /dev/null +++ b/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.structure.mps @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.typesystem.mps b/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.typesystem.mps new file mode 100644 index 0000000000..f245ee5007 --- /dev/null +++ b/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.typesystem.mps @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/model-sync-gradle-test/test-repo/solutions/GraphSolution/GraphSolution.msd b/model-sync-gradle-test/test-repo/solutions/GraphSolution/GraphSolution.msd new file mode 100644 index 0000000000..5dca9fc5b7 --- /dev/null +++ b/model-sync-gradle-test/test-repo/solutions/GraphSolution/GraphSolution.msd @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/model-sync-gradle-test/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps b/model-sync-gradle-test/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps new file mode 100644 index 0000000000..7209589d6d --- /dev/null +++ b/model-sync-gradle-test/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 3f7326971e00a40c9c60ed74f8032c39355fdfe7 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Fri, 25 Aug 2023 15:05:44 +0200 Subject: [PATCH 06/43] build(mps-model-adapters): fix dependencies --- gradle/libs.versions.toml | 1 + mps-model-adapters/build.gradle.kts | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c9670101a1..69131d5b2a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -71,6 +71,7 @@ vavr = { group = "io.vavr", name = "vavr", version = "0.10.4" } apache-commons-lang = { group = "org.apache.commons", name = "commons-lang3", version = "3.13.0" } apache-commons-io = { group = "commons-io", name = "commons-io", version = "2.13.0" } apache-commons-collections = { group = "org.apache.commons", name = "commons-collections4", version = "4.4" } +trove = { group = "trove", name = "trove", version = "1.0.2"} trove4j = { group = "net.sf.trove4j", name = "trove4j", version = "3.0.3" } ignite-core = { group = "org.apache.ignite", name = "ignite-core", version.ref = "ignite" } diff --git a/mps-model-adapters/build.gradle.kts b/mps-model-adapters/build.gradle.kts index 0a7aed6643..42ad722e83 100644 --- a/mps-model-adapters/build.gradle.kts +++ b/mps-model-adapters/build.gradle.kts @@ -8,9 +8,10 @@ val mpsVersion = project.findProperty("mps.version")?.toString().takeIf { !it.is dependencies { api(project(":model-api")) - compileOnly("com.jetbrains:mps-openapi:$mpsVersion") - compileOnly("com.jetbrains:mps-core:$mpsVersion") - compileOnly("com.jetbrains:mps-environment:$mpsVersion") + implementation("com.jetbrains:mps-openapi:$mpsVersion") + implementation("com.jetbrains:mps-core:$mpsVersion") + implementation("com.jetbrains:mps-environment:$mpsVersion") + implementation(libs.trove) implementation(kotlin("stdlib")) implementation(libs.kotlin.logging) From 4466a55f99d61d6842dc0f993d814fc7de2d6aa7 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Fri, 25 Aug 2023 15:44:17 +0200 Subject: [PATCH 07/43] fix(mps-model-adapters): fix init of RepositoryLanguage --- .../model/mpsadapters/RepositoryLanguage.kt | 91 ++++++++++--------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/RepositoryLanguage.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/RepositoryLanguage.kt index b5f79d9ac0..08fc737ca5 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/RepositoryLanguage.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/RepositoryLanguage.kt @@ -18,6 +18,9 @@ import jetbrains.mps.smodel.adapter.structure.concept.SInterfaceConceptAdapterBy import org.modelix.model.api.SimpleChildLink import org.modelix.model.api.SimpleConcept import org.modelix.model.api.SimpleLanguage +import org.modelix.model.mpsadapters.RepositoryLanguage.BaseConcept +import org.modelix.model.mpsadapters.RepositoryLanguage.INamedConcept +import org.modelix.model.mpsadapters.RepositoryLanguage.addConcept object RepositoryLanguage : SimpleLanguage("org.modelix.model.repositoryconcepts", uid = "mps:0a7577d1-d4e5-431d-98b1-fae38f9aee80") { val BaseConcept = MPSConcept(SConceptAdapterById.deserialize("c:ceab5195-25ea-4f22-9b92-103b95ca8c0c/1133920641626:jetbrains.mps.lang.core.structure.BaseConcept")) @@ -25,48 +28,52 @@ object RepositoryLanguage : SimpleLanguage("org.modelix.model.repositoryconcepts val NamePropertyUID = "ceab5195-25ea-4f22-9b92-103b95ca8c0c/1169194658468/1169194664001" val VirtualPackagePropertyUID = "ceab5195-25ea-4f22-9b92-103b95ca8c0c/1133920641626/1193676396447" - object Model : SimpleConcept( - conceptName = "Model", - uid = "mps:0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618892", - directSuperConcepts = listOf(BaseConcept, INamedConcept), - ) { - init { addConcept(this) } - val rootNodes = SimpleChildLink( - simpleName = "rootNodes", - isMultiple = true, - isOptional = true, - targetConcept = BaseConcept, - uid = "0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618892/474657388638618900", - ) - } + val Model = org.modelix.model.mpsadapters.Model + val Module = org.modelix.model.mpsadapters.Module + val Repository = org.modelix.model.mpsadapters.Repository +} + +object Model : SimpleConcept( + conceptName = "Model", + uid = "mps:0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618892", + directSuperConcepts = listOf(BaseConcept, INamedConcept), +) { + init { addConcept(this) } + val rootNodes = SimpleChildLink( + simpleName = "rootNodes", + isMultiple = true, + isOptional = true, + targetConcept = BaseConcept, + uid = "0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618892/474657388638618900", + ) +} - object Module : SimpleConcept( - conceptName = "Module", - uid = "mps:0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618895", - directSuperConcepts = listOf(BaseConcept, INamedConcept), - ) { - init { addConcept(this) } - val models = SimpleChildLink( - simpleName = "models", - isMultiple = true, - isOptional = true, - targetConcept = Model, - uid = "0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618895/474657388638618898", - ) - } +object Module : SimpleConcept( + conceptName = "Module", + uid = "mps:0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618895", + directSuperConcepts = listOf(BaseConcept, INamedConcept), +) { + init { addConcept(this) } + val models = SimpleChildLink( + simpleName = "models", + isMultiple = true, + isOptional = true, + targetConcept = Model, + uid = "0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618895/474657388638618898", + ) +} - object Repository : SimpleConcept( - conceptName = "Repository", - uid = "mps:0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618902", - directSuperConcepts = listOf(BaseConcept), - ) { - init { addConcept(this) } - val modules = SimpleChildLink( - simpleName = "modules", - isMultiple = true, - isOptional = true, - targetConcept = Module, - uid = "0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618902/474657388638618903", - ) - } +object Repository : SimpleConcept( + conceptName = "Repository", + uid = "mps:0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618902", + directSuperConcepts = listOf(BaseConcept), +) { + init { addConcept(this) } + val modules = SimpleChildLink( + simpleName = "modules", + isMultiple = true, + isOptional = true, + targetConcept = Module, + uid = "0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618902/474657388638618903", + ) } From 951ed819ec74561aa39eadd37590db55cf824188 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Fri, 25 Aug 2023 16:11:39 +0200 Subject: [PATCH 08/43] feat(bulk-model-sync-gradle): ability to register languages for sync --- model-sync-gradle/build.gradle.kts | 1 + .../model/sync/gradle/ModelSyncGradlePlugin.kt | 1 + .../sync/gradle/config/ModelSyncGradleSettings.kt | 11 +++++++++-- .../sync/gradle/tasks/ImportIntoModelServer.kt | 14 ++++++++++++-- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/model-sync-gradle/build.gradle.kts b/model-sync-gradle/build.gradle.kts index d54cdaeabd..3f16779e14 100644 --- a/model-sync-gradle/build.gradle.kts +++ b/model-sync-gradle/build.gradle.kts @@ -11,6 +11,7 @@ repositories { dependencies { implementation(project(":model-client", "jvmRuntimeElements")) implementation(project(":model-sync-lib")) + implementation(project(":mps-model-adapters")) implementation(libs.ktor.client.core) implementation(libs.ktor.client.cio) } diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt index 95b361bb87..5a0ce85d57 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt @@ -141,6 +141,7 @@ class ModelSyncGradlePlugin : Plugin { val importIntoModelServer = project.tasks.register(importTaskName, ImportIntoModelServer::class.java) { it.dependsOn(previousTask) it.inputDir.set(jsonDir) + it.registeredLanguages.set(syncDirection.registeredLanguages) val serverTarget = syncDirection.target as ServerTarget it.url.set(serverTarget.url) diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/config/ModelSyncGradleSettings.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/config/ModelSyncGradleSettings.kt index 7d781b0743..8d644550cf 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/config/ModelSyncGradleSettings.kt +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/config/ModelSyncGradleSettings.kt @@ -1,6 +1,8 @@ package org.modelix.model.sync.gradle.config import org.gradle.api.Action +import org.modelix.model.api.ILanguage +import org.modelix.model.mpsadapters.RepositoryLanguage import java.io.File open class ModelSyncGradleSettings { @@ -17,7 +19,8 @@ data class SyncDirection( internal val name: String, internal var source: SyncEndPoint? = null, internal var target: SyncEndPoint? = null, - internal val includedModules: MutableList = mutableListOf(), + internal val includedModules: Set = mutableSetOf(), + internal val registeredLanguages: Set = mutableSetOf(RepositoryLanguage), ) { fun fromModelServer(action: Action) { val endpoint = ServerSource() @@ -44,7 +47,11 @@ data class SyncDirection( } fun includeModule(module: String) { - includedModules.add(module) + (includedModules as MutableSet).add(module) + } + + fun registerLanguage(language: ILanguage) { + (registeredLanguages as MutableSet).add(language) } } diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoModelServer.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoModelServer.kt index 90ff5e89d4..de4f07273f 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoModelServer.kt +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoModelServer.kt @@ -5,18 +5,21 @@ import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction import org.modelix.model.ModelFacade +import org.modelix.model.api.ILanguage +import org.modelix.model.api.ILanguageRepository import org.modelix.model.api.getRootNode import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder import org.modelix.model.client2.getReplicatedModel import org.modelix.model.lazy.RepositoryId import org.modelix.model.sync.ModelImporter -import org.modelix.model.sync.import +import org.modelix.model.sync.importFile import javax.inject.Inject abstract class ImportIntoModelServer @Inject constructor(of: ObjectFactory) : DefaultTask() { @@ -34,8 +37,15 @@ abstract class ImportIntoModelServer @Inject constructor(of: ObjectFactory) : De @Input val url: Property = of.property(String::class.java) + @Input + val registeredLanguages: SetProperty = of.setProperty(ILanguage::class.java) + @TaskAction fun import() { + registeredLanguages.get().forEach { + ILanguageRepository.default.registerLanguage(it) + } + val inputDir = inputDir.get().asFile val repoId = RepositoryId(repositoryId.get()) @@ -52,7 +62,7 @@ abstract class ImportIntoModelServer @Inject constructor(of: ObjectFactory) : De val rootNode = branch.getRootNode() println("Got root node: $rootNode") println("Importing...") - ModelImporter(branch.getRootNode()).import(file) + ModelImporter(branch.getRootNode()).importFile(file) println("Import finished") } } From ccb79e6dedd8c0b03cd6f9ed992811ddd59fdd97 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Fri, 25 Aug 2023 17:35:08 +0200 Subject: [PATCH 09/43] feat(bulk-model-sync-gradle): ability to define task dependencies --- model-sync-gradle-test/build.gradle.kts | 9 ++- model-sync-gradle-test/ci.sh | 6 +- .../model/sync/gradle/test/PullTest.kt | 68 +++++++++++++++++-- .../model/sync/gradle/test/PushTest.kt | 39 +++++++++-- .../sync/gradle/ModelSyncGradlePlugin.kt | 3 + .../gradle/config/ModelSyncGradleSettings.kt | 5 ++ 6 files changed, 116 insertions(+), 14 deletions(-) diff --git a/model-sync-gradle-test/build.gradle.kts b/model-sync-gradle-test/build.gradle.kts index 5e739cb36a..4a2ca3555a 100644 --- a/model-sync-gradle-test/build.gradle.kts +++ b/model-sync-gradle-test/build.gradle.kts @@ -78,9 +78,16 @@ val resolveMps by tasks.registering(Copy::class) { into(mpsDir) } -val repoDir = projectDir.resolve("test-repo") +val repoDir = buildDir.resolve("test-repo") + +val copyTestRepo by tasks.registering(Sync::class) { + from(projectDir.resolve("test-repo")) + into(repoDir) +} modelSync { + dependsOn(resolveMps) + dependsOn(copyTestRepo) direction("testPush") { registerLanguage(L_GraphLang) includeModule("GraphSolution") diff --git a/model-sync-gradle-test/ci.sh b/model-sync-gradle-test/ci.sh index 7358b0bd97..c1cee0ef29 100755 --- a/model-sync-gradle-test/ci.sh +++ b/model-sync-gradle-test/ci.sh @@ -6,7 +6,7 @@ set -e ( cd graph-lang-api - ./gradlew publishToMavenLocal + ./gradlew publishToMavenLocal --console=plain ) ./gradlew assemble --console=plain @@ -30,6 +30,6 @@ set -e ./gradlew runSyncTestPush --console=plain --stacktrace ./gradlew test --tests 'PushTest' - #./gradlew runSyncTestPull --console=plain --stacktrace - #./gradlew test --tests 'PullTest' + ./gradlew runSyncTestPull --console=plain --stacktrace + ./gradlew test --tests 'PullTest' ) diff --git a/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt b/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt index fb2f592f34..2884c55a8b 100644 --- a/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt +++ b/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt @@ -1,16 +1,76 @@ package org.modelix.model.sync.gradle.test import org.junit.jupiter.api.Test -import org.modelix.model.data.ModelData import java.io.File +import kotlin.test.assertEquals class PullTest { @Test fun `nodes were synced to local`() { - val inputJson = File("build/model-sync/testPull").listFiles() - ?.first { it.exists() && it.extension == "json" } ?: throw RuntimeException("input json not found") + val localModelFile = File("build/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps") + val expected = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.trimIndent() - val inputRoot = ModelData.fromJson(inputJson.readText()).root + assertEquals(expected, localModelFile.readText()) } } diff --git a/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt b/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt index 9198b63d6f..35a97fa6e7 100644 --- a/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt +++ b/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt @@ -1,14 +1,23 @@ package org.modelix.model.sync.gradle.test import GraphLang.L_GraphLang +import GraphLang.N_Node +import GraphLang._C_UntypedImpl_Node +import jetbrains.mps.lang.core.L_jetbrains_mps_lang_core import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Test +import org.modelix.metamodel.TypedLanguagesRegistry +import org.modelix.metamodel.typed import org.modelix.model.ModelFacade +import org.modelix.model.api.ConceptReference +import org.modelix.model.api.IBranch import org.modelix.model.api.ILanguageRepository +import org.modelix.model.api.getDescendants import org.modelix.model.api.getRootNode import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder import org.modelix.model.client2.getReplicatedModel import org.modelix.model.data.ModelData +import org.modelix.model.data.NodeData import org.modelix.model.lazy.RepositoryId import org.modelix.model.mpsadapters.RepositoryLanguage import org.modelix.model.server.Main @@ -20,12 +29,14 @@ class PushTest { @Test fun `nodes were synced to server`() { - val inputJson = File("build/model-sync/testPush").listFiles() - ?.first { it.exists() && it.extension == "json" } ?: throw RuntimeException("input json not found") + val inputDir = File("build/model-sync/testPush") + val files = inputDir.listFiles()?.filter { it.extension == "json" } ?: error("no json files found") - val inputRoot = ModelData.fromJson(inputJson.readText()).root + val modules = files.map { ModelData.fromJson(it.readText()) } + val inputModel = ModelData(root = NodeData(children = modules.map { it.root })) - ILanguageRepository.default.registerLanguage(L_GraphLang) + TypedLanguagesRegistry.register(L_GraphLang) + TypedLanguagesRegistry.register(L_jetbrains_mps_lang_core) ILanguageRepository.default.registerLanguage(RepositoryLanguage) val repoId = RepositoryId("ci-test") @@ -39,9 +50,25 @@ class PushTest { client.getReplicatedModel(branchRef).start() } branch.runRead { - assertEquals(inputRoot, branch.getRootNode().asExported()) + branch.getRootNode().allChildren.forEachIndexed { index, child -> + assertEquals(inputModel.root.children[index], child.asExported()) + } } - // TODO Prepare next stage + applyChangesForPullTest(branch) + } + + private fun applyChangesForPullTest(branch: IBranch) { + branch.runWrite { + val graphNodes = branch.getRootNode() + .getDescendants(false) + .filter { it.getConceptReference() == ConceptReference(_C_UntypedImpl_Node.getUID()) } + .map { it.typed() } + .toList() + + graphNodes[0].name = "X" + graphNodes[1].name = "Y" + graphNodes[2].name = "Z" + } } } diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt index 5a0ce85d57..56d9183de4 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt @@ -31,6 +31,9 @@ class ModelSyncGradlePlugin : Plugin { project.afterEvaluate { val validateSyncSettings = project.tasks.register("validateSyncSettings", ValidateSyncSettings::class.java) { + settings.taskDependencies.forEach { dependency -> + it.dependsOn(dependency) + } it.settings.set(settings) } val modelixCoreVersion = readModelixCoreVersion() ?: throw RuntimeException("modelix.core version not found") diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/config/ModelSyncGradleSettings.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/config/ModelSyncGradleSettings.kt index 8d644550cf..95b8b29f79 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/config/ModelSyncGradleSettings.kt +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/config/ModelSyncGradleSettings.kt @@ -7,12 +7,17 @@ import java.io.File open class ModelSyncGradleSettings { internal val syncDirections = mutableListOf() + internal val taskDependencies = mutableListOf() fun direction(name: String, action: Action) { val syncDirection = SyncDirection(name) action.execute(syncDirection) syncDirections.add(syncDirection) } + + fun dependsOn(task: Any) { + taskDependencies.add(task) + } } data class SyncDirection( From 186d4f895bc9994e2849dd804cdd29862894cdd1 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Tue, 29 Aug 2023 10:31:10 +0200 Subject: [PATCH 10/43] fix(bulk-model-sync-gradle): bugfixes --- .../model/sync/gradle/test/PushTest.kt | 2 + .../sync/gradle/ModelSyncGradlePlugin.kt | 2 +- .../gradle/tasks/ExportFromModelServer.kt | 9 +- .../gradle/tasks/ImportIntoModelServer.kt | 6 +- .../models/org.modelix.model.sync.mps.mps | 168 ++++++++++-------- 5 files changed, 103 insertions(+), 84 deletions(-) diff --git a/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt b/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt index 35a97fa6e7..8a9e07b9da 100644 --- a/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt +++ b/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt @@ -21,6 +21,7 @@ import org.modelix.model.data.NodeData import org.modelix.model.lazy.RepositoryId import org.modelix.model.mpsadapters.RepositoryLanguage import org.modelix.model.server.Main +import org.modelix.model.sleep import org.modelix.model.sync.asExported import java.io.File import kotlin.test.assertEquals @@ -70,5 +71,6 @@ class PushTest { graphNodes[1].name = "Y" graphNodes[2].name = "Z" } + sleep(5000) // wait for changes to be sent to server } } diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt index 56d9183de4..44750c7a02 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt @@ -171,7 +171,7 @@ class ModelSyncGradlePlugin : Plugin { val antDependencies = project.configurations.create("model-import-ant-dependencies") project.dependencies.add(antDependencies.name, "org.apache.ant:ant-junit:1.10.12") - val generateAntScriptName = "${syncDirection.name}generateAntScriptForImport" + val generateAntScriptName = "${syncDirection.name}GenerateAntScriptForImport" val generateAntScript = project.tasks.register(generateAntScriptName, GenerateAntScriptForMps::class.java) { it.dependsOn(previousTask) it.mpsHomePath.set(localTarget.mpsHome?.absolutePath) diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromModelServer.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromModelServer.kt index c7521a79e8..bc9b97c7d0 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromModelServer.kt +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromModelServer.kt @@ -11,6 +11,7 @@ import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import org.modelix.model.ModelFacade import org.modelix.model.api.IBranch +import org.modelix.model.api.IProperty import org.modelix.model.api.PBranch import org.modelix.model.api.getRootNode import org.modelix.model.client2.IModelClientV2 @@ -18,6 +19,7 @@ import org.modelix.model.client2.ModelClientV2 import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder import org.modelix.model.client2.getReplicatedModel import org.modelix.model.lazy.RepositoryId +import org.modelix.model.mpsadapters.RepositoryLanguage import org.modelix.model.sync.ModelExporter import javax.inject.Inject @@ -53,8 +55,11 @@ abstract class ExportFromModelServer @Inject constructor(of: ObjectFactory) : De branch.runRead { val root = branch.getRootNode() println("Got root node: $root") - val outputFile = outputDir.get().asFile.resolve("exported-repo.json") - ModelExporter(root).export(outputFile) + val outputDir = outputDir.get().asFile + root.allChildren.forEach { + val outputFile = outputDir.resolve("${it.getPropertyValue(IProperty.fromName(RepositoryLanguage.NamePropertyUID))}.json") + ModelExporter(it).export(outputFile) + } } } diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoModelServer.kt b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoModelServer.kt index de4f07273f..dd15f42f28 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoModelServer.kt +++ b/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoModelServer.kt @@ -19,7 +19,7 @@ import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder import org.modelix.model.client2.getReplicatedModel import org.modelix.model.lazy.RepositoryId import org.modelix.model.sync.ModelImporter -import org.modelix.model.sync.importFile +import org.modelix.model.sync.importFilesAsRootChildren import javax.inject.Inject abstract class ImportIntoModelServer @Inject constructor(of: ObjectFactory) : DefaultTask() { @@ -56,13 +56,13 @@ abstract class ImportIntoModelServer @Inject constructor(of: ObjectFactory) : De client.getReplicatedModel(branchRef).start() } - val file = inputDir.listFiles()?.first { it.extension == "json" } ?: error("json file not found") + val files = inputDir.listFiles()?.filter { it.extension == "json" } ?: error("no json files found") branch.runWrite { val rootNode = branch.getRootNode() println("Got root node: $rootNode") println("Importing...") - ModelImporter(branch.getRootNode()).importFile(file) + ModelImporter(branch.getRootNode()).importFilesAsRootChildren(*files.toTypedArray()) println("Import finished") } } diff --git a/model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps b/model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps index e061541d2d..9549c40651 100644 --- a/model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps +++ b/model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps @@ -59,7 +59,6 @@ - @@ -118,11 +117,9 @@ - - @@ -376,22 +373,6 @@ - - - - - - - - - - - - - - - - @@ -407,22 +388,6 @@ - - - - - - - - - - - - - - - - @@ -439,60 +404,107 @@ - - - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + - - - + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 147e3a5494fa4a3b8a727577baff19202c7195c5 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Thu, 31 Aug 2023 12:23:05 +0200 Subject: [PATCH 11/43] fix(bulk-model-sync-lib): fixed jvm name conflicting with java keyword --- .../kotlin/org/modelix/model/sync/ModelImporter.kt | 2 ++ .../kotlin/org/modelix/model/sync/PlatformSpecific.kt | 9 ++------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/ModelImporter.kt b/model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/ModelImporter.kt index 11a87098d3..413fe2e550 100644 --- a/model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/ModelImporter.kt +++ b/model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/ModelImporter.kt @@ -8,6 +8,7 @@ import org.modelix.model.api.getDescendants import org.modelix.model.api.remove import org.modelix.model.data.ModelData import org.modelix.model.data.NodeData +import kotlin.jvm.JvmName /** * A ModelImporter updates an existing [INode] and its subtree based on a [ModelData] specification. @@ -30,6 +31,7 @@ class ModelImporter(private val root: INode) { * * @param data the model specification */ + @JvmName("importData") fun import(data: ModelData) { originalIdToExisting.clear() postponedReferences.clear() diff --git a/model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/PlatformSpecific.kt b/model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/PlatformSpecific.kt index d0e1dd275a..6b1743661a 100644 --- a/model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/PlatformSpecific.kt +++ b/model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/PlatformSpecific.kt @@ -3,12 +3,6 @@ package org.modelix.model.sync import org.modelix.model.data.ModelData import java.io.File -// import is a reserved keyword in java -@Deprecated("Use importFile instead", ReplaceWith("importFile(jsonFile)")) -fun ModelImporter.import(jsonFile: File) { - this.importFile(jsonFile) -} - /** * Incrementally updates the root of the receiver [ModelImporter] * based on the [ModelData] specification contained in the given file. @@ -17,7 +11,8 @@ fun ModelImporter.import(jsonFile: File) { * * @throws IllegalArgumentException if the file is not a json file or the file does not exist. */ -fun ModelImporter.importFile(jsonFile: File) { +@JvmName("importFile") +fun ModelImporter.import(jsonFile: File) { require(jsonFile.exists()) require(jsonFile.extension == "json") From 26b4451c2f4e3890a9eea9410a0722c7e95ec1d1 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Fri, 1 Sep 2023 10:43:45 +0200 Subject: [PATCH 12/43] build: align mps versions --- model-sync-gradle-test/build.gradle.kts | 2 ++ mps-model-adapters/build.gradle.kts | 2 +- mps-model-server-plugin/build.gradle.kts | 10 ++++++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/model-sync-gradle-test/build.gradle.kts b/model-sync-gradle-test/build.gradle.kts index 4a2ca3555a..040a97b4ae 100644 --- a/model-sync-gradle-test/build.gradle.kts +++ b/model-sync-gradle-test/build.gradle.kts @@ -15,6 +15,7 @@ */ import GraphLang.L_GraphLang +import jetbrains.mps.lang.core.L_jetbrains_mps_lang_core import org.modelix.model.server.Main buildscript { @@ -90,6 +91,7 @@ modelSync { dependsOn(copyTestRepo) direction("testPush") { registerLanguage(L_GraphLang) + registerLanguage(L_jetbrains_mps_lang_core) includeModule("GraphSolution") fromLocal { mpsHome = mpsDir diff --git a/mps-model-adapters/build.gradle.kts b/mps-model-adapters/build.gradle.kts index 42ad722e83..71950a07cb 100644 --- a/mps-model-adapters/build.gradle.kts +++ b/mps-model-adapters/build.gradle.kts @@ -3,7 +3,7 @@ plugins { `maven-publish` } -val mpsVersion = project.findProperty("mps.version")?.toString().takeIf { !it.isNullOrBlank() } ?: "2020.3.6" +val mpsVersion = project.findProperty("mps.version")?.toString().takeIf { !it.isNullOrBlank() } ?: "2021.1.4" dependencies { api(project(":model-api")) diff --git a/mps-model-server-plugin/build.gradle.kts b/mps-model-server-plugin/build.gradle.kts index 424ab8e3f8..7e779d6394 100644 --- a/mps-model-server-plugin/build.gradle.kts +++ b/mps-model-server-plugin/build.gradle.kts @@ -3,13 +3,15 @@ plugins { id("org.jetbrains.intellij") version "1.15.0" } +val mpsVersion = "2021.1.4" + dependencies { implementation(project(":model-server-lib")) implementation(project(":mps-model-adapters")) - compileOnly("com.jetbrains:mps-openapi:2021.1.4") - compileOnly("com.jetbrains:mps-core:2021.1.4") - compileOnly("com.jetbrains:mps-environment:2021.1.4") - compileOnly("com.jetbrains:mps-platform:2021.1.4") + compileOnly("com.jetbrains:mps-openapi:$mpsVersion") + compileOnly("com.jetbrains:mps-core:$mpsVersion") + compileOnly("com.jetbrains:mps-environment:$mpsVersion") + compileOnly("com.jetbrains:mps-platform:$mpsVersion") } // Configure Gradle IntelliJ Plugin From 668a31e5aa20b7ec8c919112b9627af962f0a123 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Mon, 4 Sep 2023 14:39:46 +0200 Subject: [PATCH 13/43] fix(model-datastructure): catch exceptions in checkRoleId --- .../commonMain/kotlin/org/modelix/model/lazy/CLTree.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/model-datastructure/src/commonMain/kotlin/org/modelix/model/lazy/CLTree.kt b/model-datastructure/src/commonMain/kotlin/org/modelix/model/lazy/CLTree.kt index 0ec941f8d4..a289502c47 100644 --- a/model-datastructure/src/commonMain/kotlin/org/modelix/model/lazy/CLTree.kt +++ b/model-datastructure/src/commonMain/kotlin/org/modelix/model/lazy/CLTree.kt @@ -48,7 +48,7 @@ class CLTree : ITree, IBulkTree { constructor(hash: String?, store: IDeserializingKeyValueStore) : this(if (hash == null) null else store.get(hash) { CPTree.deserialize(it) }, store) constructor(store: IDeserializingKeyValueStore) : this(null as CPTree?, store) constructor(data: CPTree?, store_: IDeserializingKeyValueStore) : this(data, null as RepositoryId?, store_) - private constructor(data: CPTree?, repositoryId_: RepositoryId?, store_: IDeserializingKeyValueStore, useRoleIds: Boolean = false) { + constructor(data: CPTree?, repositoryId_: RepositoryId?, store_: IDeserializingKeyValueStore, useRoleIds: Boolean = false) { var repositoryId = repositoryId_ var store = store_ if (data == null) { @@ -580,8 +580,10 @@ class CLTree : ITree, IBulkTree { private fun checkPropertyRoleId(nodeId: Long, role: String?) = checkRoleId(nodeId, role) { it.getAllProperties() } private fun checkRoleId(nodeId: Long, role: String?, rolesGetter: (IConcept) -> Iterable) { if (role != null && usesRoleIds()) { - val concept = getConceptReference(nodeId)?.tryResolve() - if (concept != null && rolesGetter(concept).any { it.getSimpleName() == role }) { + val isKnownRoleName = getConceptReference(nodeId)?.tryResolve()?.let { concept -> + runCatching { rolesGetter(concept).any { it.getSimpleName() == role } }.getOrNull() + } ?: false + if (isKnownRoleName) { throw IllegalArgumentException("A role UID is expected, but a name was provided: $role") } } From 24a5f2f0b1f4f4966cf0abbf661e2adc64c16f72 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Mon, 4 Sep 2023 14:47:05 +0200 Subject: [PATCH 14/43] fix(model-server): new parameter for using roleIds --- .../src/main/kotlin/org/modelix/model/server/CmdLineArgs.kt | 3 +++ model-server/src/main/kotlin/org/modelix/model/server/Main.kt | 2 +- .../org/modelix/model/server/handlers/RepositoriesManager.kt | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/model-server/src/main/kotlin/org/modelix/model/server/CmdLineArgs.kt b/model-server/src/main/kotlin/org/modelix/model/server/CmdLineArgs.kt index 78ba35c552..c4ba3faf61 100644 --- a/model-server/src/main/kotlin/org/modelix/model/server/CmdLineArgs.kt +++ b/model-server/src/main/kotlin/org/modelix/model/server/CmdLineArgs.kt @@ -40,6 +40,9 @@ internal class CmdLineArgs { ) var schemaInit = false + @Parameter(names = ["-useroleids"], description = "Use IDs for roles instead of simpleNames. Required for bulk sync.", converter = BooleanConverter::class) + var useRoleIds = false + @Parameter(names = ["-h", "--help"], help = true) var help = false } diff --git a/model-server/src/main/kotlin/org/modelix/model/server/Main.kt b/model-server/src/main/kotlin/org/modelix/model/server/Main.kt index 5e63b571fa..ba2d7878fa 100644 --- a/model-server/src/main/kotlin/org/modelix/model/server/Main.kt +++ b/model-server/src/main/kotlin/org/modelix/model/server/Main.kt @@ -151,7 +151,7 @@ object Main { } val jsonModelServer = DeprecatedLightModelServer(localModelClient) - val repositoriesManager = RepositoriesManager(localModelClient) + val repositoriesManager = RepositoriesManager(localModelClient, cmdLineArgs.useRoleIds) val repositoryOverview = RepositoryOverview(repositoriesManager) val historyHandler = HistoryHandler(localModelClient, repositoriesManager) val contentExplorer = ContentExplorer(localModelClient, repositoriesManager) diff --git a/model-server/src/main/kotlin/org/modelix/model/server/handlers/RepositoriesManager.kt b/model-server/src/main/kotlin/org/modelix/model/server/handlers/RepositoriesManager.kt index 99bd4d2b67..fdf0181174 100644 --- a/model-server/src/main/kotlin/org/modelix/model/server/handlers/RepositoriesManager.kt +++ b/model-server/src/main/kotlin/org/modelix/model/server/handlers/RepositoriesManager.kt @@ -30,7 +30,7 @@ import org.modelix.model.server.store.IStoreClient import org.modelix.model.server.store.LocalModelClient import org.modelix.model.server.store.pollEntry -class RepositoriesManager(val client: LocalModelClient) { +class RepositoriesManager(val client: LocalModelClient, val useRoleIds: Boolean = false) { init { migrateLegacyRepositoriesList() } @@ -57,7 +57,7 @@ class RepositoriesManager(val client: LocalModelClient) { id = client.idGenerator.generate(), time = Clock.System.now().epochSeconds.toString(), author = userName, - tree = CLTree(client.storeCache), + tree = CLTree(null, null, client.storeCache, useRoleIds = useRoleIds), baseVersion = null, operations = emptyArray(), ) From 4cd5141c933d2674f44ef9e08325277ab895e86e Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Mon, 4 Sep 2023 16:37:58 +0200 Subject: [PATCH 15/43] fix(bulk-model-sync-gradle): fix test import failing --- model-sync-gradle-test/build.gradle.kts | 1 + .../model/sync/gradle/test/PullTest.kt | 6 +- .../models/org.modelix.model.sync.mps.mps | 255 ++++++++++++------ .../model/mpsadapters/MPSModelAsNode.kt | 2 +- .../model/mpsadapters/MPSModuleAsNode.kt | 2 +- 5 files changed, 175 insertions(+), 91 deletions(-) diff --git a/model-sync-gradle-test/build.gradle.kts b/model-sync-gradle-test/build.gradle.kts index 040a97b4ae..c8470739fe 100644 --- a/model-sync-gradle-test/build.gradle.kts +++ b/model-sync-gradle-test/build.gradle.kts @@ -72,6 +72,7 @@ tasks.register("runModelServer", JavaExec::class) { classpath = sourceSets["main"].runtimeClasspath mainClass.set("org.modelix.model.server.Main") args("-inmemory") + args("-useroleids") } val resolveMps by tasks.registering(Copy::class) { diff --git a/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt b/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt index 2884c55a8b..ff932c4a79 100644 --- a/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt +++ b/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt @@ -42,11 +42,11 @@ class PullTest { - + - + @@ -71,6 +71,6 @@ class PullTest { """.trimIndent() - assertEquals(expected, localModelFile.readText()) + assertEquals(expected, localModelFile.readText().trim()) } } diff --git a/model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps b/model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps index 9549c40651..1251721395 100644 --- a/model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps +++ b/model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps @@ -14,6 +14,7 @@ + @@ -388,72 +389,140 @@ - - - - - + + + + + + + + + - + - + + + + + - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + - - - + + + - - + + @@ -462,53 +531,67 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt index 37014cf912..8d9af632c2 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt @@ -111,7 +111,7 @@ data class MPSModelAsNode(val model: SModel) : IDeprecatedNodeDefaults { } override fun setPropertyValue(property: IProperty, value: String?) { - TODO("Not yet implemented") + // TODO("Not yet implemented") } override fun getPropertyLinks(): List { diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleAsNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleAsNode.kt index 4101cafeaa..0267308a93 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleAsNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleAsNode.kt @@ -117,7 +117,7 @@ data class MPSModuleAsNode(val module: SModule) : IDeprecatedNodeDefaults { } override fun setPropertyValue(property: IProperty, value: String?) { - TODO("Not yet implemented") + // TODO("Not yet implemented") } override fun getPropertyLinks(): List { From ed5fc5025c78e26946f25a842f3e4b12c11beebd Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Mon, 4 Sep 2023 16:39:34 +0200 Subject: [PATCH 16/43] docs(bulk-model-sync-gradle): add docs for bulk sync plugin --- .../modules/core/images/model-sync-gradle.png | Bin 0 -> 90422 bytes .../component-model-api-gen-gradle.adoc | 6 +- .../component-model-sync-gradle.adoc | 142 ++++++++++++++++++ .../modules/core/partials/nav-reference.adoc | 1 + 4 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 docs/global/modules/core/images/model-sync-gradle.png create mode 100644 docs/global/modules/core/pages/reference/component-model-sync-gradle.adoc diff --git a/docs/global/modules/core/images/model-sync-gradle.png b/docs/global/modules/core/images/model-sync-gradle.png new file mode 100644 index 0000000000000000000000000000000000000000..a1407070e444f8b81535846c33f711792889feac GIT binary patch literal 90422 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

;K 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%0DxREDNcLZDc%1(i^3MR0Xw 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 +|Defines a new sync direction with the given name. +|=== + +=== SyncDirection configuration +[%header, cols="1,1,2"] +|=== +|method +|parameter type +|description + +|`fromLocal` +|Action +|Defines a local source (MPS). + +|`toModelServer` +|Action +|Defines a model-server target. + +|`fromModelServer` +|Action +|Defines a model-server source. + +|`toLocal` +|Action +|Defines a local target (MPS). + +|`registerLanguage` +|ILanguage +|Registers the given language and all of its concepts for the synchronisation process. + +|`includeModule` +|String +|Includes the module specified by the given fully qualified name in the synchronisation process. +|=== + +=== LocalSource/-Target configuration +[%header, cols="1,1,2"] +|=== +|setting +|type +|description + +|`mpsHome` +|File +|Location of the MPS to be used for the sync. + +|`mpsHeapSize` +|String +|MPS heap size specified as a String, e.g. "2g" for 2GB (default: "2g") + +|`repositoryDir` +|File +|Directory in which the modules are stored. +|=== + +=== ServerSource/-Target configuration +[%header, cols="1,1,2"] +|=== +|setting +|type +|description + +|`url` +|String +|URL of the model-server API endpoint. + +|`repositoryId` +|String +|Id of the target/source model-server repository. + +|`branchName` +|String +|Name of the target/source model-server branch. + +|`revision` +|String +|Source model-server revision. Can be used instead of `repositoryId` and `branchName`. Only available in ServerSource. + +|=== + +== Example + +[source,kotlin] +-- +modelSync { + dependsOn(someOtherTask) + direction("pushToMyServer") { + registerLanguage(L_MyGeneratedLanguage) + includeModule("MySolution") + fromLocal { + mpsHome = buildDir.resolve("mps") + mpsHeapSize = "4g" + repositoryDir = projectDir.resolve("my-repo") + } + toModelServer { + url = "http://0.0.0.0:28101/v2" + repositoryId = "my-repo" + branchName = "dev" + } + } +} +-- + +Generated gradle task to perform synchronization: `runSyncPushToMyServer`. diff --git a/docs/global/modules/core/partials/nav-reference.adoc b/docs/global/modules/core/partials/nav-reference.adoc index 47b9aa713b..fab38b48b2 100644 --- a/docs/global/modules/core/partials/nav-reference.adoc +++ b/docs/global/modules/core/partials/nav-reference.adoc @@ -3,5 +3,6 @@ * xref:core:reference/component-metamodel-export.adoc[] * xref:core:reference/component-model-api-gen.adoc[] * xref:core:reference/component-model-api-gen-gradle.adoc[] +* xref:core:reference/component-model-sync-gradle.adoc[] * xref:core:reference/component-light-model-client.adoc[] * xref:core:reference/component-mps-model-server-plugin.adoc[] From cf55c86761e418113efd51e2fbc4991c2b663594 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Tue, 5 Sep 2023 08:41:42 +0200 Subject: [PATCH 17/43] ci(bulk-model-sync-gradle): fix ci specific problems --- model-sync-gradle-test/build.gradle.kts | 6 +++- model-sync-gradle-test/ci.sh | 41 +++++++++++++------------ 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/model-sync-gradle-test/build.gradle.kts b/model-sync-gradle-test/build.gradle.kts index c8470739fe..d4260725bb 100644 --- a/model-sync-gradle-test/build.gradle.kts +++ b/model-sync-gradle-test/build.gradle.kts @@ -69,6 +69,10 @@ kotlin { tasks.register("runModelServer", JavaExec::class) { group = "modelix" + + description = "Launches a model-server instance to be used as a target for the test. " + + "This task will block and is intended to be run in a separate process, apart from the actual test execution." + classpath = sourceSets["main"].runtimeClasspath mainClass.set("org.modelix.model.server.Main") args("-inmemory") @@ -96,7 +100,7 @@ modelSync { includeModule("GraphSolution") fromLocal { mpsHome = mpsDir - mpsHeapSize = "4g" + mpsHeapSize = "2g" repositoryDir = repoDir } toModelServer { diff --git a/model-sync-gradle-test/ci.sh b/model-sync-gradle-test/ci.sh index c1cee0ef29..45b247e7de 100755 --- a/model-sync-gradle-test/ci.sh +++ b/model-sync-gradle-test/ci.sh @@ -1,35 +1,36 @@ #!/bin/sh set -e -( - cd "$(dirname "$0")" - ( - cd graph-lang-api - ./gradlew publishToMavenLocal --console=plain - ) +cd "$(dirname "$0")" - ./gradlew assemble --console=plain +( + cd graph-lang-api + ./gradlew publishToMavenLocal --console=plain +) +./gradlew assemble --console=plain + +if [ "${CI}" != "true" ]; then trap cleanup INT TERM EXIT cleanup () { kill "${MODEL_SERVER_PID}" exit } +fi - ./gradlew runModelServer --console=plain & - MODEL_SERVER_PID=$! - sleep 5 +./gradlew runModelServer --console=plain & +MODEL_SERVER_PID=$! +sleep 5 - #CI needs more time - if [ "${CI}" = "true" ]; then - sleep 15 - fi +#CI needs more time +if [ "${CI}" = "true" ]; then + sleep 10 +fi - curl -X POST http://127.0.0.1:28101/v2/repositories/ci-test/init +curl -X POST http://127.0.0.1:28101/v2/repositories/ci-test/init - ./gradlew runSyncTestPush --console=plain --stacktrace - ./gradlew test --tests 'PushTest' - ./gradlew runSyncTestPull --console=plain --stacktrace - ./gradlew test --tests 'PullTest' -) +./gradlew runSyncTestPush --console=plain --stacktrace +./gradlew test --tests 'PushTest' +./gradlew runSyncTestPull --console=plain --stacktrace +./gradlew test --tests 'PullTest' From f0849d2a97cab54336ac96fd53bae45c32fc8ef2 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Wed, 6 Sep 2023 11:43:38 +0200 Subject: [PATCH 18/43] refactor: rename components BREAKING CHANGE: renamed model-sync-lib to bulk-model-sync-lib and updated package names --- .github/workflows/build.yaml | 4 +- .../.gitignore | 0 .../build.gradle.kts | 4 +- .../ci.sh | 0 .../gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.properties | 0 .../graph-lang-api/.gitignore | 0 .../graph-lang-api/build.gradle.kts | 0 .../gradle/wrapper/gradle-wrapper.properties | 0 .../graph-lang-api/settings.gradle.kts | 1 - .../settings.gradle.kts | 2 +- .../model/sync/gradle/test/PullTest.kt | 0 .../model/sync/gradle/test/PushTest.kt | 2 +- .../test-repo/.mps/.gitignore | 0 .../test-repo/.mps/.name | 0 .../test-repo/.mps/migration.xml | 0 .../test-repo/.mps/modules.xml | 0 .../test-repo/.mps/vcs.xml | 0 .../languages/GraphLang/GraphLang.mpl | 0 .../GraphLang/models/GraphLang.behavior.mps | 0 .../models/GraphLang.constraints.mps | 0 .../GraphLang/models/GraphLang.editor.mps | 0 .../GraphLang/models/GraphLang.structure.mps | 0 .../GraphLang/models/GraphLang.typesystem.mps | 0 .../solutions/GraphSolution/GraphSolution.msd | 0 .../models/GraphSolution.example.mps | 0 .../.gitignore | 0 .../build.gradle.kts | 6 +- .../bulk}/gradle/ModelSyncGradlePlugin.kt | 44 +++- .../gradle/config/ModelSyncGradleSettings.kt | 18 +- .../gradle/tasks/ExportFromModelServer.kt | 20 +- .../sync/bulk}/gradle/tasks/ExportFromMps.kt | 2 +- .../gradle/tasks/GenerateAntScriptForMps.kt | 26 +- .../gradle/tasks/ImportIntoModelServer.kt | 22 +- .../sync/bulk}/gradle/tasks/ImportIntoMps.kt | 2 +- .../gradle/tasks/ValidateSyncSettings.kt | 30 ++- .../.gitignore | 0 .../build.gradle.kts | 0 .../modelix/model/sync/bulk}/ModelExporter.kt | 18 +- .../modelix/model/sync/bulk}/ModelImporter.kt | 18 +- .../org/modelix/model/sync/bulk}/Util.kt | 2 +- .../src/commonTest/resources/model.json | 0 .../src/commonTest/resources/newmodel.json | 0 .../modelix/model/sync/bulk/ModelExporter.kt | 29 +++ .../modelix/model/sync/bulk/ModelExporter.kt | 35 +++ .../model/sync/bulk}/PlatformSpecific.kt | 18 +- .../model/sync/bulk}/ModelExporterTest.kt | 18 +- .../model/sync/bulk}/ModelImporterTest.kt | 18 +- .../modelix/model/sync/bulk}/SyncTestUtil.kt | 18 +- .../.mps/.gitignore | 0 bulk-model-sync-solution/.mps/.name | 1 + .../.mps/migration.xml | 0 .../.mps/modules.xml | 2 +- .../.mps/vcs.xml | 0 .../build.gradle.kts | 10 +- .../.gitignore | 0 .../org.modelix.mps.model.sync.bulk.mps | 28 +- .../org.modelix.mps.model.sync.bulk.msd | 9 +- .../gradle/wrapper/gradle-wrapper.jar | Bin 61574 -> 0 bytes model-sync-gradle-test/gradlew | 244 ------------------ model-sync-gradle-test/gradlew.bat | 92 ------- .../gradle/wrapper/gradle-wrapper.jar | Bin 61574 -> 0 bytes model-sync-gradle-test/graph-lang-api/gradlew | 244 ------------------ .../graph-lang-api/gradlew.bat | 92 ------- .../org/modelix/model/sync/ModelExporter.kt | 13 - .../org/modelix/model/sync/ModelExporter.kt | 19 -- model-sync-mps/.mps/.name | 1 - settings.gradle.kts | 6 +- 68 files changed, 334 insertions(+), 784 deletions(-) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/.gitignore (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/build.gradle.kts (96%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/ci.sh (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/gradle.properties (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/gradle/wrapper/gradle-wrapper.properties (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/graph-lang-api/.gitignore (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/graph-lang-api/build.gradle.kts (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/graph-lang-api/gradle/wrapper/gradle-wrapper.properties (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/graph-lang-api/settings.gradle.kts (93%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/settings.gradle.kts (93%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt (98%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/test-repo/.mps/.gitignore (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/test-repo/.mps/.name (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/test-repo/.mps/migration.xml (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/test-repo/.mps/modules.xml (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/test-repo/.mps/vcs.xml (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/test-repo/languages/GraphLang/GraphLang.mpl (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/test-repo/languages/GraphLang/models/GraphLang.behavior.mps (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/test-repo/languages/GraphLang/models/GraphLang.constraints.mps (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/test-repo/languages/GraphLang/models/GraphLang.editor.mps (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/test-repo/languages/GraphLang/models/GraphLang.structure.mps (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/test-repo/languages/GraphLang/models/GraphLang.typesystem.mps (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/test-repo/solutions/GraphSolution/GraphSolution.msd (100%) rename {model-sync-gradle-test => bulk-model-sync-gradle-test}/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps (100%) rename {model-sync-gradle => bulk-model-sync-gradle}/.gitignore (100%) rename {model-sync-gradle => bulk-model-sync-gradle}/build.gradle.kts (81%) rename {model-sync-gradle/src/main/kotlin/org/modelix/model/sync => bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk}/gradle/ModelSyncGradlePlugin.kt (85%) rename {model-sync-gradle/src/main/kotlin/org/modelix/model/sync => bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk}/gradle/config/ModelSyncGradleSettings.kt (88%) rename {model-sync-gradle/src/main/kotlin/org/modelix/model/sync => bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk}/gradle/tasks/ExportFromModelServer.kt (80%) rename {model-sync-gradle/src/main/kotlin/org/modelix/model/sync => bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk}/gradle/tasks/ExportFromMps.kt (96%) rename {model-sync-gradle/src/main/kotlin/org/modelix/model/sync => bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk}/gradle/tasks/GenerateAntScriptForMps.kt (80%) rename {model-sync-gradle/src/main/kotlin/org/modelix/model/sync => bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk}/gradle/tasks/ImportIntoModelServer.kt (75%) rename {model-sync-gradle/src/main/kotlin/org/modelix/model/sync => bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk}/gradle/tasks/ImportIntoMps.kt (96%) rename {model-sync-gradle/src/main/kotlin/org/modelix/model/sync => bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk}/gradle/tasks/ValidateSyncSettings.kt (75%) rename {model-sync-lib => bulk-model-sync-lib}/.gitignore (100%) rename {model-sync-lib => bulk-model-sync-lib}/build.gradle.kts (100%) rename {model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync => bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk}/ModelExporter.kt (61%) rename {model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync => bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk}/ModelImporter.kt (91%) rename {model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync => bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk}/Util.kt (95%) rename {model-sync-lib => bulk-model-sync-lib}/src/commonTest/resources/model.json (100%) rename {model-sync-lib => bulk-model-sync-lib}/src/commonTest/resources/newmodel.json (100%) create mode 100644 bulk-model-sync-lib/src/jsMain/kotlin/org/modelix/model/sync/bulk/ModelExporter.kt create mode 100644 bulk-model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/bulk/ModelExporter.kt rename {model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync => bulk-model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/bulk}/PlatformSpecific.kt (55%) rename {model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync => bulk-model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/bulk}/ModelExporterTest.kt (69%) rename {model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync => bulk-model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/bulk}/ModelImporterTest.kt (91%) rename {model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync => bulk-model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/bulk}/SyncTestUtil.kt (88%) rename {model-sync-mps => bulk-model-sync-solution}/.mps/.gitignore (100%) create mode 100644 bulk-model-sync-solution/.mps/.name rename {model-sync-mps => bulk-model-sync-solution}/.mps/migration.xml (100%) rename {model-sync-mps => bulk-model-sync-solution}/.mps/modules.xml (55%) rename {model-sync-mps => bulk-model-sync-solution}/.mps/vcs.xml (100%) rename {model-sync-mps => bulk-model-sync-solution}/build.gradle.kts (75%) rename {model-sync-mps/solutions/org.modelix.model.sync.mps => bulk-model-sync-solution/solutions/org.modelix.mps.model.sync.bulk}/.gitignore (100%) rename model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps => bulk-model-sync-solution/solutions/org.modelix.mps.model.sync.bulk/models/org.modelix.mps.model.sync.bulk.mps (97%) rename model-sync-mps/solutions/org.modelix.model.sync.mps/org.modelix.model.sync.mps.msd => bulk-model-sync-solution/solutions/org.modelix.mps.model.sync.bulk/org.modelix.mps.model.sync.bulk.msd (88%) delete mode 100644 model-sync-gradle-test/gradle/wrapper/gradle-wrapper.jar delete mode 100755 model-sync-gradle-test/gradlew delete mode 100644 model-sync-gradle-test/gradlew.bat delete mode 100644 model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.jar delete mode 100755 model-sync-gradle-test/graph-lang-api/gradlew delete mode 100644 model-sync-gradle-test/graph-lang-api/gradlew.bat delete mode 100644 model-sync-lib/src/jsMain/kotlin/org/modelix/model/sync/ModelExporter.kt delete mode 100644 model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/ModelExporter.kt delete mode 100644 model-sync-mps/.mps/.name diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 1db68b0aa9..726537cce3 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -34,10 +34,10 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: model-api-gen-gradle-test/ci.sh - - name: Test Model Sync Gradle Plugin + - name: Test Bulk Model Sync Gradle Plugin env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: model-sync-gradle-test/ci.sh + run: bulk-model-sync-gradle-test/ci.sh - name: Archive test report uses: actions/upload-artifact@v3 if: always() diff --git a/model-sync-gradle-test/.gitignore b/bulk-model-sync-gradle-test/.gitignore similarity index 100% rename from model-sync-gradle-test/.gitignore rename to bulk-model-sync-gradle-test/.gitignore diff --git a/model-sync-gradle-test/build.gradle.kts b/bulk-model-sync-gradle-test/build.gradle.kts similarity index 96% rename from model-sync-gradle-test/build.gradle.kts rename to bulk-model-sync-gradle-test/build.gradle.kts index d4260725bb..fb2e50a058 100644 --- a/model-sync-gradle-test/build.gradle.kts +++ b/bulk-model-sync-gradle-test/build.gradle.kts @@ -28,7 +28,7 @@ buildscript { plugins { kotlin("jvm") - id("org.modelix.model-sync") + id("org.modelix.bulk-model-sync") } val modelixCoreVersion: String = file("../version.txt").readText() @@ -53,7 +53,7 @@ dependencies { implementation("org.modelix:model-server:$modelixCoreVersion") implementation("org.modelix:model-api-gen-runtime:$modelixCoreVersion") testImplementation("org.modelix:model-client:$modelixCoreVersion") - testImplementation("org.modelix:model-sync-lib:$modelixCoreVersion") + testImplementation("org.modelix:bulk-model-sync-lib:$modelixCoreVersion") testImplementation("org.modelix.mps:model-adapters:$modelixCoreVersion") testImplementation("org.modelix:graph-lang-api:$modelixCoreVersion") testImplementation(kotlin("test")) diff --git a/model-sync-gradle-test/ci.sh b/bulk-model-sync-gradle-test/ci.sh similarity index 100% rename from model-sync-gradle-test/ci.sh rename to bulk-model-sync-gradle-test/ci.sh diff --git a/model-sync-gradle-test/gradle.properties b/bulk-model-sync-gradle-test/gradle.properties similarity index 100% rename from model-sync-gradle-test/gradle.properties rename to bulk-model-sync-gradle-test/gradle.properties diff --git a/model-sync-gradle-test/gradle/wrapper/gradle-wrapper.properties b/bulk-model-sync-gradle-test/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from model-sync-gradle-test/gradle/wrapper/gradle-wrapper.properties rename to bulk-model-sync-gradle-test/gradle/wrapper/gradle-wrapper.properties diff --git a/model-sync-gradle-test/graph-lang-api/.gitignore b/bulk-model-sync-gradle-test/graph-lang-api/.gitignore similarity index 100% rename from model-sync-gradle-test/graph-lang-api/.gitignore rename to bulk-model-sync-gradle-test/graph-lang-api/.gitignore diff --git a/model-sync-gradle-test/graph-lang-api/build.gradle.kts b/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts similarity index 100% rename from model-sync-gradle-test/graph-lang-api/build.gradle.kts rename to bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts diff --git a/model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.properties b/bulk-model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.properties rename to bulk-model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.properties diff --git a/model-sync-gradle-test/graph-lang-api/settings.gradle.kts b/bulk-model-sync-gradle-test/graph-lang-api/settings.gradle.kts similarity index 93% rename from model-sync-gradle-test/graph-lang-api/settings.gradle.kts rename to bulk-model-sync-gradle-test/graph-lang-api/settings.gradle.kts index 2a6b565964..13c41738ce 100644 --- a/model-sync-gradle-test/graph-lang-api/settings.gradle.kts +++ b/bulk-model-sync-gradle-test/graph-lang-api/settings.gradle.kts @@ -17,7 +17,6 @@ pluginManagement { val modelixCoreVersion: String = file("../../version.txt").readText() plugins { - id("org.modelix.model-sync") version modelixCoreVersion id("org.modelix.model-api-gen") version modelixCoreVersion } repositories { diff --git a/model-sync-gradle-test/settings.gradle.kts b/bulk-model-sync-gradle-test/settings.gradle.kts similarity index 93% rename from model-sync-gradle-test/settings.gradle.kts rename to bulk-model-sync-gradle-test/settings.gradle.kts index ec53a134c7..00c06f01a9 100644 --- a/model-sync-gradle-test/settings.gradle.kts +++ b/bulk-model-sync-gradle-test/settings.gradle.kts @@ -17,7 +17,7 @@ pluginManagement { val modelixCoreVersion: String = file("../version.txt").readText() plugins { - id("org.modelix.model-sync") version modelixCoreVersion + id("org.modelix.bulk-model-sync") version modelixCoreVersion id("org.modelix.model-api-gen") version modelixCoreVersion kotlin("jvm") version "1.9.0" } diff --git a/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt similarity index 100% rename from model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt rename to bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt diff --git a/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt similarity index 98% rename from model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt rename to bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt index 8a9e07b9da..dbdaaffff2 100644 --- a/model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt +++ b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt @@ -22,7 +22,7 @@ import org.modelix.model.lazy.RepositoryId import org.modelix.model.mpsadapters.RepositoryLanguage import org.modelix.model.server.Main import org.modelix.model.sleep -import org.modelix.model.sync.asExported +import org.modelix.model.sync.bulk.asExported import java.io.File import kotlin.test.assertEquals diff --git a/model-sync-gradle-test/test-repo/.mps/.gitignore b/bulk-model-sync-gradle-test/test-repo/.mps/.gitignore similarity index 100% rename from model-sync-gradle-test/test-repo/.mps/.gitignore rename to bulk-model-sync-gradle-test/test-repo/.mps/.gitignore diff --git a/model-sync-gradle-test/test-repo/.mps/.name b/bulk-model-sync-gradle-test/test-repo/.mps/.name similarity index 100% rename from model-sync-gradle-test/test-repo/.mps/.name rename to bulk-model-sync-gradle-test/test-repo/.mps/.name diff --git a/model-sync-gradle-test/test-repo/.mps/migration.xml b/bulk-model-sync-gradle-test/test-repo/.mps/migration.xml similarity index 100% rename from model-sync-gradle-test/test-repo/.mps/migration.xml rename to bulk-model-sync-gradle-test/test-repo/.mps/migration.xml diff --git a/model-sync-gradle-test/test-repo/.mps/modules.xml b/bulk-model-sync-gradle-test/test-repo/.mps/modules.xml similarity index 100% rename from model-sync-gradle-test/test-repo/.mps/modules.xml rename to bulk-model-sync-gradle-test/test-repo/.mps/modules.xml diff --git a/model-sync-gradle-test/test-repo/.mps/vcs.xml b/bulk-model-sync-gradle-test/test-repo/.mps/vcs.xml similarity index 100% rename from model-sync-gradle-test/test-repo/.mps/vcs.xml rename to bulk-model-sync-gradle-test/test-repo/.mps/vcs.xml diff --git a/model-sync-gradle-test/test-repo/languages/GraphLang/GraphLang.mpl b/bulk-model-sync-gradle-test/test-repo/languages/GraphLang/GraphLang.mpl similarity index 100% rename from model-sync-gradle-test/test-repo/languages/GraphLang/GraphLang.mpl rename to bulk-model-sync-gradle-test/test-repo/languages/GraphLang/GraphLang.mpl diff --git a/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.behavior.mps b/bulk-model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.behavior.mps similarity index 100% rename from model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.behavior.mps rename to bulk-model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.behavior.mps diff --git a/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.constraints.mps b/bulk-model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.constraints.mps similarity index 100% rename from model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.constraints.mps rename to bulk-model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.constraints.mps diff --git a/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.editor.mps b/bulk-model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.editor.mps similarity index 100% rename from model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.editor.mps rename to bulk-model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.editor.mps diff --git a/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.structure.mps b/bulk-model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.structure.mps similarity index 100% rename from model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.structure.mps rename to bulk-model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.structure.mps diff --git a/model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.typesystem.mps b/bulk-model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.typesystem.mps similarity index 100% rename from model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.typesystem.mps rename to bulk-model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.typesystem.mps diff --git a/model-sync-gradle-test/test-repo/solutions/GraphSolution/GraphSolution.msd b/bulk-model-sync-gradle-test/test-repo/solutions/GraphSolution/GraphSolution.msd similarity index 100% rename from model-sync-gradle-test/test-repo/solutions/GraphSolution/GraphSolution.msd rename to bulk-model-sync-gradle-test/test-repo/solutions/GraphSolution/GraphSolution.msd diff --git a/model-sync-gradle-test/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps b/bulk-model-sync-gradle-test/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps similarity index 100% rename from model-sync-gradle-test/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps rename to bulk-model-sync-gradle-test/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps diff --git a/model-sync-gradle/.gitignore b/bulk-model-sync-gradle/.gitignore similarity index 100% rename from model-sync-gradle/.gitignore rename to bulk-model-sync-gradle/.gitignore diff --git a/model-sync-gradle/build.gradle.kts b/bulk-model-sync-gradle/build.gradle.kts similarity index 81% rename from model-sync-gradle/build.gradle.kts rename to bulk-model-sync-gradle/build.gradle.kts index 3f16779e14..3fc1c48626 100644 --- a/model-sync-gradle/build.gradle.kts +++ b/bulk-model-sync-gradle/build.gradle.kts @@ -10,7 +10,7 @@ repositories { dependencies { implementation(project(":model-client", "jvmRuntimeElements")) - implementation(project(":model-sync-lib")) + implementation(project(":bulk-model-sync-lib")) implementation(project(":mps-model-adapters")) implementation(libs.ktor.client.core) implementation(libs.ktor.client.cio) @@ -22,8 +22,8 @@ kotlin { gradlePlugin { val modelSync by plugins.creating { - id = "org.modelix.model-sync" - implementationClass = "org.modelix.model.sync.gradle.ModelSyncGradlePlugin" + id = "org.modelix.bulk-model-sync" + implementationClass = "org.modelix.model.sync.bulk.gradle.ModelSyncGradlePlugin" } } diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt similarity index 85% rename from model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt rename to bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt index 44750c7a02..8f10b9dd47 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/ModelSyncGradlePlugin.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt @@ -1,21 +1,37 @@ -package org.modelix.model.sync.gradle +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.sync.bulk.gradle import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.tasks.Sync import org.gradle.api.tasks.TaskProvider -import org.modelix.model.sync.gradle.config.LocalSource -import org.modelix.model.sync.gradle.config.LocalTarget -import org.modelix.model.sync.gradle.config.ModelSyncGradleSettings -import org.modelix.model.sync.gradle.config.ServerSource -import org.modelix.model.sync.gradle.config.ServerTarget -import org.modelix.model.sync.gradle.config.SyncDirection -import org.modelix.model.sync.gradle.tasks.ExportFromModelServer -import org.modelix.model.sync.gradle.tasks.ExportFromMps -import org.modelix.model.sync.gradle.tasks.GenerateAntScriptForMps -import org.modelix.model.sync.gradle.tasks.ImportIntoModelServer -import org.modelix.model.sync.gradle.tasks.ImportIntoMps -import org.modelix.model.sync.gradle.tasks.ValidateSyncSettings +import org.modelix.model.sync.bulk.gradle.config.LocalSource +import org.modelix.model.sync.bulk.gradle.config.LocalTarget +import org.modelix.model.sync.bulk.gradle.config.ModelSyncGradleSettings +import org.modelix.model.sync.bulk.gradle.config.ServerSource +import org.modelix.model.sync.bulk.gradle.config.ServerTarget +import org.modelix.model.sync.bulk.gradle.config.SyncDirection +import org.modelix.model.sync.bulk.gradle.tasks.ExportFromModelServer +import org.modelix.model.sync.bulk.gradle.tasks.ExportFromMps +import org.modelix.model.sync.bulk.gradle.tasks.GenerateAntScriptForMps +import org.modelix.model.sync.bulk.gradle.tasks.ImportIntoModelServer +import org.modelix.model.sync.bulk.gradle.tasks.ImportIntoMps +import org.modelix.model.sync.bulk.gradle.tasks.ValidateSyncSettings import java.io.File import java.net.URL import java.util.Enumeration @@ -41,7 +57,7 @@ class ModelSyncGradlePlugin : Plugin { project.dependencies.add(antDependencies.name, "org.apache.ant:ant-junit:1.10.12") val mpsDependencies = project.configurations.create("modelSyncMpsDependencies") - project.dependencies.add(mpsDependencies.name, "org.modelix:model-sync-mps:$modelixCoreVersion") + project.dependencies.add(mpsDependencies.name, "org.modelix.mps:bulk-model-sync-solution:$modelixCoreVersion") val copyMpsDependencies = project.tasks.register("copyMpsDependencies", Sync::class.java) { sync -> sync.dependsOn(validateSyncSettings) diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/config/ModelSyncGradleSettings.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt similarity index 88% rename from model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/config/ModelSyncGradleSettings.kt rename to bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt index 95b8b29f79..bdc2c66d7f 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/config/ModelSyncGradleSettings.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt @@ -1,4 +1,20 @@ -package org.modelix.model.sync.gradle.config +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.sync.bulk.gradle.config import org.gradle.api.Action import org.modelix.model.api.ILanguage diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromModelServer.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromModelServer.kt similarity index 80% rename from model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromModelServer.kt rename to bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromModelServer.kt index bc9b97c7d0..59dc683ed4 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromModelServer.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromModelServer.kt @@ -1,4 +1,20 @@ -package org.modelix.model.sync.gradle.tasks +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.sync.bulk.gradle.tasks import kotlinx.coroutines.runBlocking import org.gradle.api.DefaultTask @@ -20,7 +36,7 @@ import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder import org.modelix.model.client2.getReplicatedModel import org.modelix.model.lazy.RepositoryId import org.modelix.model.mpsadapters.RepositoryLanguage -import org.modelix.model.sync.ModelExporter +import org.modelix.model.sync.bulk.ModelExporter import javax.inject.Inject abstract class ExportFromModelServer @Inject constructor(of: ObjectFactory) : DefaultTask() { diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromMps.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromMps.kt similarity index 96% rename from model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromMps.kt rename to bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromMps.kt index e8de5cfc78..6ddac006b5 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ExportFromMps.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromMps.kt @@ -1,4 +1,4 @@ -package org.modelix.model.sync.gradle.tasks +package org.modelix.model.sync.bulk.gradle.tasks import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/GenerateAntScriptForMps.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/GenerateAntScriptForMps.kt similarity index 80% rename from model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/GenerateAntScriptForMps.kt rename to bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/GenerateAntScriptForMps.kt index 4fd7ec274d..d2deab9e6e 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/GenerateAntScriptForMps.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/GenerateAntScriptForMps.kt @@ -1,4 +1,20 @@ -package org.modelix.model.sync.gradle.tasks +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.sync.bulk.gradle.tasks import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty @@ -77,13 +93,13 @@ abstract class GenerateAntScriptForMps @Inject constructor(of: ObjectFactory) : - + - - - + + + diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoModelServer.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt similarity index 75% rename from model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoModelServer.kt rename to bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt index dd15f42f28..ac7d6596b8 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoModelServer.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt @@ -1,4 +1,20 @@ -package org.modelix.model.sync.gradle.tasks +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.sync.bulk.gradle.tasks import kotlinx.coroutines.runBlocking import org.gradle.api.DefaultTask @@ -18,8 +34,8 @@ import org.modelix.model.api.getRootNode import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder import org.modelix.model.client2.getReplicatedModel import org.modelix.model.lazy.RepositoryId -import org.modelix.model.sync.ModelImporter -import org.modelix.model.sync.importFilesAsRootChildren +import org.modelix.model.sync.bulk.ModelImporter +import org.modelix.model.sync.bulk.importFilesAsRootChildren import javax.inject.Inject abstract class ImportIntoModelServer @Inject constructor(of: ObjectFactory) : DefaultTask() { diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoMps.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoMps.kt similarity index 96% rename from model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoMps.kt rename to bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoMps.kt index c2f0487558..d1dceeb744 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ImportIntoMps.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoMps.kt @@ -1,4 +1,4 @@ -package org.modelix.model.sync.gradle.tasks +package org.modelix.model.sync.bulk.gradle.tasks import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty diff --git a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ValidateSyncSettings.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ValidateSyncSettings.kt similarity index 75% rename from model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ValidateSyncSettings.kt rename to bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ValidateSyncSettings.kt index 17e82b3553..6f215ba4ee 100644 --- a/model-sync-gradle/src/main/kotlin/org/modelix/model/sync/gradle/tasks/ValidateSyncSettings.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ValidateSyncSettings.kt @@ -1,4 +1,20 @@ -package org.modelix.model.sync.gradle.tasks +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.sync.bulk.gradle.tasks import org.gradle.api.DefaultTask import org.gradle.api.model.ObjectFactory @@ -6,12 +22,12 @@ import org.gradle.api.provider.Property import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.Input import org.gradle.api.tasks.TaskAction -import org.modelix.model.sync.gradle.config.LocalSource -import org.modelix.model.sync.gradle.config.LocalTarget -import org.modelix.model.sync.gradle.config.ModelSyncGradleSettings -import org.modelix.model.sync.gradle.config.ServerSource -import org.modelix.model.sync.gradle.config.ServerTarget -import org.modelix.model.sync.gradle.config.SyncDirection +import org.modelix.model.sync.bulk.gradle.config.LocalSource +import org.modelix.model.sync.bulk.gradle.config.LocalTarget +import org.modelix.model.sync.bulk.gradle.config.ModelSyncGradleSettings +import org.modelix.model.sync.bulk.gradle.config.ServerSource +import org.modelix.model.sync.bulk.gradle.config.ServerTarget +import org.modelix.model.sync.bulk.gradle.config.SyncDirection import javax.inject.Inject @CacheableTask diff --git a/model-sync-lib/.gitignore b/bulk-model-sync-lib/.gitignore similarity index 100% rename from model-sync-lib/.gitignore rename to bulk-model-sync-lib/.gitignore diff --git a/model-sync-lib/build.gradle.kts b/bulk-model-sync-lib/build.gradle.kts similarity index 100% rename from model-sync-lib/build.gradle.kts rename to bulk-model-sync-lib/build.gradle.kts diff --git a/model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/ModelExporter.kt b/bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk/ModelExporter.kt similarity index 61% rename from model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/ModelExporter.kt rename to bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk/ModelExporter.kt index 46b8eac24f..eccd63f029 100644 --- a/model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/ModelExporter.kt +++ b/bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk/ModelExporter.kt @@ -1,4 +1,20 @@ -package org.modelix.model.sync +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.sync.bulk import org.modelix.model.api.INode import org.modelix.model.data.NodeData diff --git a/model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/ModelImporter.kt b/bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk/ModelImporter.kt similarity index 91% rename from model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/ModelImporter.kt rename to bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk/ModelImporter.kt index 413fe2e550..03c088bdce 100644 --- a/model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/ModelImporter.kt +++ b/bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk/ModelImporter.kt @@ -1,4 +1,20 @@ -package org.modelix.model.sync +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.sync.bulk import org.modelix.model.api.ConceptReference import org.modelix.model.api.INode diff --git a/model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/Util.kt b/bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk/Util.kt similarity index 95% rename from model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/Util.kt rename to bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk/Util.kt index a56b17e493..1f3d354aef 100644 --- a/model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/Util.kt +++ b/bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk/Util.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.modelix.model.sync +package org.modelix.model.sync.bulk import org.modelix.model.data.ModelData import org.modelix.model.data.NodeData diff --git a/model-sync-lib/src/commonTest/resources/model.json b/bulk-model-sync-lib/src/commonTest/resources/model.json similarity index 100% rename from model-sync-lib/src/commonTest/resources/model.json rename to bulk-model-sync-lib/src/commonTest/resources/model.json diff --git a/model-sync-lib/src/commonTest/resources/newmodel.json b/bulk-model-sync-lib/src/commonTest/resources/newmodel.json similarity index 100% rename from model-sync-lib/src/commonTest/resources/newmodel.json rename to bulk-model-sync-lib/src/commonTest/resources/newmodel.json diff --git a/bulk-model-sync-lib/src/jsMain/kotlin/org/modelix/model/sync/bulk/ModelExporter.kt b/bulk-model-sync-lib/src/jsMain/kotlin/org/modelix/model/sync/bulk/ModelExporter.kt new file mode 100644 index 0000000000..1bdd985e4d --- /dev/null +++ b/bulk-model-sync-lib/src/jsMain/kotlin/org/modelix/model/sync/bulk/ModelExporter.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.sync.bulk + +import org.modelix.model.api.INode + +actual class ModelExporter actual constructor(private val root: INode) { + + /** + * Triggers a bulk export of this ModelExporter's root node and its (in-)direct children. + * + * @return exported node + */ + fun export() = root.asExported() +} diff --git a/bulk-model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/bulk/ModelExporter.kt b/bulk-model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/bulk/ModelExporter.kt new file mode 100644 index 0000000000..410f28213f --- /dev/null +++ b/bulk-model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/bulk/ModelExporter.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.sync.bulk + +import org.modelix.model.api.INode +import org.modelix.model.data.ModelData +import java.io.File + +actual class ModelExporter actual constructor(private val root: INode) { + + /** + * Triggers a bulk export of this ModelExporter's root node and its (in-)direct children into the specified file. + * + * @param outputFile target file of the export + */ + fun export(outputFile: File) { + val modelData = ModelData(root = root.asExported()) + outputFile.parentFile.mkdirs() + outputFile.writeText(modelData.toJson()) + } +} diff --git a/model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/PlatformSpecific.kt b/bulk-model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/bulk/PlatformSpecific.kt similarity index 55% rename from model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/PlatformSpecific.kt rename to bulk-model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/bulk/PlatformSpecific.kt index 6b1743661a..51c2bc5d08 100644 --- a/model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/PlatformSpecific.kt +++ b/bulk-model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/bulk/PlatformSpecific.kt @@ -1,4 +1,20 @@ -package org.modelix.model.sync +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.sync.bulk import org.modelix.model.data.ModelData import java.io.File diff --git a/model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/ModelExporterTest.kt b/bulk-model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/bulk/ModelExporterTest.kt similarity index 69% rename from model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/ModelExporterTest.kt rename to bulk-model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/bulk/ModelExporterTest.kt index 8b16395d48..d5301382e2 100644 --- a/model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/ModelExporterTest.kt +++ b/bulk-model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/bulk/ModelExporterTest.kt @@ -1,4 +1,20 @@ -package org.modelix.model.sync +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.sync.bulk import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeAll diff --git a/model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/ModelImporterTest.kt b/bulk-model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/bulk/ModelImporterTest.kt similarity index 91% rename from model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/ModelImporterTest.kt rename to bulk-model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/bulk/ModelImporterTest.kt index 2dd4830f85..a5cc3d3fe5 100644 --- a/model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/ModelImporterTest.kt +++ b/bulk-model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/bulk/ModelImporterTest.kt @@ -1,4 +1,20 @@ -package org.modelix.model.sync +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.sync.bulk import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test diff --git a/model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/SyncTestUtil.kt b/bulk-model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/bulk/SyncTestUtil.kt similarity index 88% rename from model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/SyncTestUtil.kt rename to bulk-model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/bulk/SyncTestUtil.kt index f201ff6c01..f280cf8e92 100644 --- a/model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/SyncTestUtil.kt +++ b/bulk-model-sync-lib/src/jvmTest/kotlin/org/modelix/model/sync/bulk/SyncTestUtil.kt @@ -1,4 +1,20 @@ -package org.modelix.model.sync +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.sync.bulk import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json diff --git a/model-sync-mps/.mps/.gitignore b/bulk-model-sync-solution/.mps/.gitignore similarity index 100% rename from model-sync-mps/.mps/.gitignore rename to bulk-model-sync-solution/.mps/.gitignore diff --git a/bulk-model-sync-solution/.mps/.name b/bulk-model-sync-solution/.mps/.name new file mode 100644 index 0000000000..bd284d4a6a --- /dev/null +++ b/bulk-model-sync-solution/.mps/.name @@ -0,0 +1 @@ +org.modelix.mps.model.sync.bulk diff --git a/model-sync-mps/.mps/migration.xml b/bulk-model-sync-solution/.mps/migration.xml similarity index 100% rename from model-sync-mps/.mps/migration.xml rename to bulk-model-sync-solution/.mps/migration.xml diff --git a/model-sync-mps/.mps/modules.xml b/bulk-model-sync-solution/.mps/modules.xml similarity index 55% rename from model-sync-mps/.mps/modules.xml rename to bulk-model-sync-solution/.mps/modules.xml index 3a51249c45..2a8c28483d 100644 --- a/model-sync-mps/.mps/modules.xml +++ b/bulk-model-sync-solution/.mps/modules.xml @@ -2,7 +2,7 @@ - + diff --git a/model-sync-mps/.mps/vcs.xml b/bulk-model-sync-solution/.mps/vcs.xml similarity index 100% rename from model-sync-mps/.mps/vcs.xml rename to bulk-model-sync-solution/.mps/vcs.xml diff --git a/model-sync-mps/build.gradle.kts b/bulk-model-sync-solution/build.gradle.kts similarity index 75% rename from model-sync-mps/build.gradle.kts rename to bulk-model-sync-solution/build.gradle.kts index 0bfbec4050..dc6104bea5 100644 --- a/model-sync-mps/build.gradle.kts +++ b/bulk-model-sync-solution/build.gradle.kts @@ -5,16 +5,18 @@ plugins { alias(libs.plugins.modelix.mps.buildtools) } +group = "org.modelix.mps" + val generatorLibs: Configuration by configurations.creating dependencies { - generatorLibs(project(":model-sync-lib")) + "generatorLibs"(project(":bulk-model-sync-lib")) generatorLibs(project(":mps-model-adapters")) } val copyLibs by tasks.registering(Sync::class) { from(generatorLibs) - into(projectDir.resolve("solutions/org.modelix.model.sync.mps/lib").apply { mkdirs() }) + into(projectDir.resolve("solutions/org.modelix.mps.model.sync.bulk/lib").apply { mkdirs() }) rename { fileName -> generatorLibs.resolvedConfiguration.resolvedArtifacts .find { it.file.name == fileName }?.let { @@ -34,7 +36,7 @@ extensions.configure { search(".") disableParentPublication() - publication("model-sync-mps") { - module("org.modelix.model.sync.mps") + publication("bulk-model-sync-solution") { + module("org.modelix.mps.model.sync.bulk") } } diff --git a/model-sync-mps/solutions/org.modelix.model.sync.mps/.gitignore b/bulk-model-sync-solution/solutions/org.modelix.mps.model.sync.bulk/.gitignore similarity index 100% rename from model-sync-mps/solutions/org.modelix.model.sync.mps/.gitignore rename to bulk-model-sync-solution/solutions/org.modelix.mps.model.sync.bulk/.gitignore diff --git a/model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps b/bulk-model-sync-solution/solutions/org.modelix.mps.model.sync.bulk/models/org.modelix.mps.model.sync.bulk.mps similarity index 97% rename from model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps rename to bulk-model-sync-solution/solutions/org.modelix.mps.model.sync.bulk/models/org.modelix.mps.model.sync.bulk.mps index 1251721395..a4a490c5fb 100644 --- a/model-sync-mps/solutions/org.modelix.model.sync.mps/models/org.modelix.model.sync.mps.mps +++ b/bulk-model-sync-solution/solutions/org.modelix.mps.model.sync.bulk/models/org.modelix.mps.model.sync.bulk.mps @@ -1,5 +1,5 @@ - + @@ -7,7 +7,6 @@ - @@ -15,6 +14,7 @@ + @@ -153,7 +153,7 @@ - + @@ -182,7 +182,7 @@ - + @@ -232,11 +232,11 @@ - + - + @@ -259,7 +259,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -384,7 +384,7 @@ - + @@ -487,11 +487,11 @@ - + - + @@ -506,8 +506,8 @@ - - + + @@ -619,7 +619,7 @@ - + diff --git a/model-sync-mps/solutions/org.modelix.model.sync.mps/org.modelix.model.sync.mps.msd b/bulk-model-sync-solution/solutions/org.modelix.mps.model.sync.bulk/org.modelix.mps.model.sync.bulk.msd similarity index 88% rename from model-sync-mps/solutions/org.modelix.model.sync.mps/org.modelix.model.sync.mps.msd rename to bulk-model-sync-solution/solutions/org.modelix.mps.model.sync.bulk/org.modelix.mps.model.sync.bulk.msd index 6caeac03e8..b20a1fdd69 100644 --- a/model-sync-mps/solutions/org.modelix.model.sync.mps/org.modelix.model.sync.mps.msd +++ b/bulk-model-sync-solution/solutions/org.modelix.mps.model.sync.bulk/org.modelix.mps.model.sync.bulk.msd @@ -1,5 +1,5 @@ - + @@ -15,7 +15,6 @@ - @@ -34,9 +33,9 @@ - + @@ -44,8 +43,6 @@ 742f6602-5a2f-4313-aa6e-ae1cd4ffdc61(MPS.Platform) 498d89d2-c2e9-11e2-ad49-6cf049e62fe5(MPS.IDEA) 6354ebe7-c22a-4a0f-ac54-50b52ab9b065(JDK) - 2d3c70e9-aab2-4870-8d8d-6036800e4103(jetbrains.mps.kernel) - 9b80526e-f0bf-4992-bdf5-cee39c1833f3(collections.runtime) @@ -65,6 +62,6 @@ - + diff --git a/model-sync-gradle-test/gradle/wrapper/gradle-wrapper.jar b/model-sync-gradle-test/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 943f0cbfa754578e88a3dae77fce6e3dea56edbf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61574 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 ScNgau(U?ep-K_E5zy1%ZQTdPn diff --git a/model-sync-gradle-test/gradlew b/model-sync-gradle-test/gradlew deleted file mode 100755 index 79a61d421c..0000000000 --- a/model-sync-gradle-test/gradlew +++ /dev/null @@ -1,244 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/model-sync-gradle-test/gradlew.bat b/model-sync-gradle-test/gradlew.bat deleted file mode 100644 index 93e3f59f13..0000000000 --- a/model-sync-gradle-test/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.jar b/model-sync-gradle-test/graph-lang-api/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 943f0cbfa754578e88a3dae77fce6e3dea56edbf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61574 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 ScNgau(U?ep-K_E5zy1%ZQTdPn diff --git a/model-sync-gradle-test/graph-lang-api/gradlew b/model-sync-gradle-test/graph-lang-api/gradlew deleted file mode 100755 index 79a61d421c..0000000000 --- a/model-sync-gradle-test/graph-lang-api/gradlew +++ /dev/null @@ -1,244 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/model-sync-gradle-test/graph-lang-api/gradlew.bat b/model-sync-gradle-test/graph-lang-api/gradlew.bat deleted file mode 100644 index 93e3f59f13..0000000000 --- a/model-sync-gradle-test/graph-lang-api/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/model-sync-lib/src/jsMain/kotlin/org/modelix/model/sync/ModelExporter.kt b/model-sync-lib/src/jsMain/kotlin/org/modelix/model/sync/ModelExporter.kt deleted file mode 100644 index db1aa28c2c..0000000000 --- a/model-sync-lib/src/jsMain/kotlin/org/modelix/model/sync/ModelExporter.kt +++ /dev/null @@ -1,13 +0,0 @@ -package org.modelix.model.sync - -import org.modelix.model.api.INode - -actual class ModelExporter actual constructor(private val root: INode) { - - /** - * Triggers a bulk export of this ModelExporter's root node and its (in-)direct children. - * - * @return exported node - */ - fun export() = root.asExported() -} diff --git a/model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/ModelExporter.kt b/model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/ModelExporter.kt deleted file mode 100644 index 1a052ae250..0000000000 --- a/model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/ModelExporter.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.modelix.model.sync - -import org.modelix.model.api.INode -import org.modelix.model.data.ModelData -import java.io.File - -actual class ModelExporter actual constructor(private val root: INode) { - - /** - * Triggers a bulk export of this ModelExporter's root node and its (in-)direct children into the specified file. - * - * @param outputFile target file of the export - */ - fun export(outputFile: File) { - val modelData = ModelData(root = root.asExported()) - outputFile.parentFile.mkdirs() - outputFile.writeText(modelData.toJson()) - } -} diff --git a/model-sync-mps/.mps/.name b/model-sync-mps/.mps/.name deleted file mode 100644 index 25ea1e6d93..0000000000 --- a/model-sync-mps/.mps/.name +++ /dev/null @@ -1 +0,0 @@ -org.modelix.model.sync.mps diff --git a/settings.gradle.kts b/settings.gradle.kts index 74b48f9c01..cac7035365 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,6 +13,9 @@ plugins { rootProject.name = "modelix.core" include("authorization") +include("bulk-model-sync-gradle") +include("bulk-model-sync-lib") +include("bulk-model-sync-solution") include("kotlin-utils") include("light-model-client") include("metamodel-export") @@ -25,9 +28,6 @@ include("model-datastructure") include("model-server") include("model-server-api") include("model-server-lib") -include("model-sync-gradle") -include("model-sync-lib") -include("model-sync-mps") include("modelql-client") include("modelql-core") include("modelql-html") From 8279625db578804a2739a055357059931e023234 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Wed, 6 Sep 2023 14:29:49 +0200 Subject: [PATCH 19/43] docs(bulk-model-sync-gradle): updated docs after renaming --- .../images/bulk-model-sync-gradle.overview.png | Bin 0 -> 53601 bytes .../modules/core/images/model-sync-gradle.png | Bin 90422 -> 0 bytes ...oc => component-bulk-model-sync-gradle.adoc} | 13 ++++++++----- .../modules/core/partials/nav-reference.adoc | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 docs/global/modules/core/images/bulk-model-sync-gradle.overview.png delete mode 100644 docs/global/modules/core/images/model-sync-gradle.png rename docs/global/modules/core/pages/reference/{component-model-sync-gradle.adoc => component-bulk-model-sync-gradle.adoc} (83%) diff --git a/docs/global/modules/core/images/bulk-model-sync-gradle.overview.png b/docs/global/modules/core/images/bulk-model-sync-gradle.overview.png new file mode 100644 index 0000000000000000000000000000000000000000..278864238589a7c1170a21654c2bb3c6d2442785 GIT binary patch literal 53601 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

;K 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%0DxREDNcLZDc%1(i^3MR0Xw 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 Date: Thu, 7 Sep 2023 10:21:43 +0200 Subject: [PATCH 20/43] test(bulk-model-sync-gradle): use build dir as sourceset instead of copying --- .../graph-lang-api/.gitignore | 1 - .../graph-lang-api/build.gradle.kts | 20 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) delete mode 100644 bulk-model-sync-gradle-test/graph-lang-api/.gitignore diff --git a/bulk-model-sync-gradle-test/graph-lang-api/.gitignore b/bulk-model-sync-gradle-test/graph-lang-api/.gitignore deleted file mode 100644 index 85de9cf933..0000000000 --- a/bulk-model-sync-gradle-test/graph-lang-api/.gitignore +++ /dev/null @@ -1 +0,0 @@ -src diff --git a/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts b/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts index 87312a3cfe..69dec0e800 100644 --- a/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts +++ b/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts @@ -15,7 +15,7 @@ val modelixCoreVersion = file("../../version.txt").readText() version = modelixCoreVersion -val mps by configurations.creating +val mps: Configuration by configurations.creating val kotlinGenDir = buildDir.resolve("metamodel/kotlin").apply { mkdirs() } dependencies { @@ -23,12 +23,6 @@ dependencies { api("org.modelix:model-api-gen-runtime:$modelixCoreVersion") } -val copyGeneratedApiToSrc by tasks.registering(Sync::class) { - dependsOn(tasks.named("generateMetaModelSources")) - from(kotlinGenDir) - into("src/main/kotlin") -} - val mpsDir = buildDir.resolve("mps").apply { mkdirs() } val resolveMps by tasks.registering(Copy::class) { @@ -43,6 +37,12 @@ val copyMetamodelToMpsHome by tasks.registering(Copy::class) { into(file(mpsDir.resolve("languages").apply { mkdirs() })) } +kotlin { + sourceSets.named("main") { + kotlin.srcDir(kotlinGenDir) + } +} + metamodel { dependsOn(resolveMps) dependsOn(copyMetamodelToMpsHome) @@ -63,6 +63,10 @@ publishing { } } +tasks.named("processResources") { + dependsOn("generateMetaModelSources") +} + tasks.named("compileKotlin") { - dependsOn(copyGeneratedApiToSrc) + dependsOn("generateMetaModelSources") } From 305f9f80f754601835a1381d9d5dd6cbc5cf48ae Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Thu, 7 Sep 2023 10:22:01 +0200 Subject: [PATCH 21/43] test(bulk-model-sync-gradle): use assertContentEquals instead of looping --- .../kotlin/org/modelix/model/sync/gradle/test/PushTest.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt index dbdaaffff2..773556cf9f 100644 --- a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt +++ b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt @@ -24,7 +24,7 @@ import org.modelix.model.server.Main import org.modelix.model.sleep import org.modelix.model.sync.bulk.asExported import java.io.File -import kotlin.test.assertEquals +import kotlin.test.assertContentEquals class PushTest { @@ -51,9 +51,7 @@ class PushTest { client.getReplicatedModel(branchRef).start() } branch.runRead { - branch.getRootNode().allChildren.forEachIndexed { index, child -> - assertEquals(inputModel.root.children[index], child.asExported()) - } + assertContentEquals(inputModel.root.children, branch.getRootNode().allChildren.map { it.asExported() }) } applyChangesForPullTest(branch) @@ -71,6 +69,6 @@ class PushTest { graphNodes[1].name = "Y" graphNodes[2].name = "Z" } - sleep(5000) // wait for changes to be sent to server + sleep(5000) // changes are pushed asynchronously to the server. wait for the propagation } } From e472371cbe567632b9ec993b81e7fd25bfa0a932 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Thu, 7 Sep 2023 10:23:07 +0200 Subject: [PATCH 22/43] refactor(bulk-model-sync-gradle): cleanup --- .../sync/bulk/gradle/ModelSyncGradlePlugin.kt | 8 +- .../gradle/config/ModelSyncGradleSettings.kt | 91 ++++++++++--------- .../gradle/tasks/ExportFromModelServer.kt | 5 +- .../gradle/tasks/ImportIntoModelServer.kt | 9 +- .../bulk/gradle/tasks/ValidateSyncSettings.kt | 21 ++--- 5 files changed, 71 insertions(+), 63 deletions(-) diff --git a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt index 8f10b9dd47..a3432b449c 100644 --- a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt @@ -33,8 +33,6 @@ import org.modelix.model.sync.bulk.gradle.tasks.ImportIntoModelServer import org.modelix.model.sync.bulk.gradle.tasks.ImportIntoMps import org.modelix.model.sync.bulk.gradle.tasks.ValidateSyncSettings import java.io.File -import java.net.URL -import java.util.Enumeration import java.util.Properties class ModelSyncGradlePlugin : Plugin { @@ -83,7 +81,7 @@ class ModelSyncGradlePlugin : Plugin { val sourceTask = when (syncDirection.source) { is LocalSource -> registerTasksForLocalSource(syncDirection, project, previousTask, jsonDir) is ServerSource -> registerTasksForServerSource(syncDirection, project, previousTask, jsonDir) - else -> previousTask + else -> error("Unknown sync direction source") } when (syncDirection.target) { @@ -226,8 +224,8 @@ class ModelSyncGradlePlugin : Plugin { } private fun readModelixCoreVersion(): String? { - val resources: Enumeration? = javaClass.classLoader.getResources("modelix.core.version.properties") - while (resources != null && resources.hasMoreElements()) { + val resources = javaClass.classLoader.getResources("modelix.core.version.properties") ?: return null + if (resources.hasMoreElements()) { val properties = resources.nextElement().openStream().use { Properties().apply { load(it) } } return properties.getProperty("modelix.core.version") } diff --git a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt index bdc2c66d7f..254ab1f305 100644 --- a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt @@ -77,7 +77,7 @@ data class SyncDirection( } interface SyncEndPoint { - fun getValidationErrors(): String + fun getValidationErrors(): List } sealed interface LocalEndpoint : SyncEndPoint { @@ -85,15 +85,15 @@ sealed interface LocalEndpoint : SyncEndPoint { var mpsHeapSize: String var repositoryDir: File? - override fun getValidationErrors(): String { - return buildString { - if (mpsHome == null) { - appendUndefinedLocalFieldError("mpsHome") - } - if (repositoryDir == null) { - appendUndefinedLocalFieldError("repositoryDir") - } + override fun getValidationErrors(): List { + val errors = mutableListOf() + if (mpsHome == null) { + errors.addUndefinedLocalFieldError("mpsHome") } + if (repositoryDir == null) { + errors.addUndefinedLocalFieldError("repositoryDir") + } + return errors } } @@ -114,12 +114,12 @@ sealed interface ServerEndpoint : SyncEndPoint { var repositoryId: String? var branchName: String? - override fun getValidationErrors(): String { - return buildString { - if (url == null) { - appendUndefinedServerFieldError("url") - } + override fun getValidationErrors(): List { + val errors = mutableListOf() + if (url == null) { + errors.addUndefinedServerFieldError("url") } + return errors } } @@ -129,19 +129,28 @@ data class ServerSource( override var branchName: String? = null, var revision: String? = null, ) : ServerEndpoint { - override fun getValidationErrors(): String { - return buildString { - append(super.getValidationErrors()) - if (revision == null) { - if (repositoryId == null && branchName == null) { - appendLine("Invalid server source. Please either specify a revision or repositoryId and branchName.") - } else if (repositoryId == null) { - appendUndefinedServerFieldError("repositoryId") - } else if (branchName == null) { - appendUndefinedServerFieldError("branchName") - } - } + override fun getValidationErrors(): List { + val errors = mutableListOf() + errors.addAll(super.getValidationErrors()) + if (revision != null) { + // If a revision is specified, repo and branch are not required + return errors + } + + if (repositoryId == null && branchName == null) { + // Give hint is configuration is completely off + errors.add("Invalid server source. Please either specify a revision or repositoryId and branchName.") + return errors } + + // Configuration is incomplete + if (repositoryId == null) { + errors.addUndefinedServerFieldError("repositoryId") + } else if (branchName == null) { + errors.addUndefinedServerFieldError("branchName") + } + + return errors } } @@ -150,29 +159,29 @@ data class ServerTarget( override var repositoryId: String? = null, override var branchName: String? = null, ) : ServerEndpoint { - override fun getValidationErrors(): String { - return buildString { - append(super.getValidationErrors()) + override fun getValidationErrors(): List { + val errors = mutableListOf() + errors.addAll(super.getValidationErrors()) - if (repositoryId == null) { - appendUndefinedServerFieldError("repositoryId") - } + if (repositoryId == null) { + errors.addUndefinedServerFieldError("repositoryId") + } - if (branchName == null) { - appendUndefinedServerFieldError("branchName") - } + if (branchName == null) { + errors.addUndefinedServerFieldError("branchName") } + return errors } } -private fun StringBuilder.appendUndefinedLocalFieldError(fieldName: String) { - appendUndefinedFieldError(fieldName, "LocalEndpoint") +private fun MutableList.addUndefinedLocalFieldError(fieldName: String) { + addUndefinedFieldError(fieldName, "LocalEndpoint") } -private fun StringBuilder.appendUndefinedServerFieldError(fieldName: String) { - appendUndefinedFieldError(fieldName, "ServerEndpoint") +private fun MutableList.addUndefinedServerFieldError(fieldName: String) { + addUndefinedFieldError(fieldName, "ServerEndpoint") } -private fun StringBuilder.appendUndefinedFieldError(fieldName: String, block: String) { - appendLine("Undefined '$fieldName' in '$block'.") +private fun MutableList.addUndefinedFieldError(fieldName: String, block: String) { + add("Undefined '$fieldName' in '$block'.") } diff --git a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromModelServer.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromModelServer.kt index 59dc683ed4..2783a13450 100644 --- a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromModelServer.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromModelServer.kt @@ -70,10 +70,11 @@ abstract class ExportFromModelServer @Inject constructor(of: ObjectFactory) : De branch.runRead { val root = branch.getRootNode() - println("Got root node: $root") + logger.info("Got root node: {}", root) val outputDir = outputDir.get().asFile root.allChildren.forEach { - val outputFile = outputDir.resolve("${it.getPropertyValue(IProperty.fromName(RepositoryLanguage.NamePropertyUID))}.json") + val nameRole = IProperty.fromName(RepositoryLanguage.NamePropertyUID) + val outputFile = outputDir.resolve("${it.getPropertyValue(nameRole)}.json") ModelExporter(it).export(outputFile) } } diff --git a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt index ac7d6596b8..99bee4cddc 100644 --- a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt @@ -72,14 +72,15 @@ abstract class ImportIntoModelServer @Inject constructor(of: ObjectFactory) : De client.getReplicatedModel(branchRef).start() } - val files = inputDir.listFiles()?.filter { it.extension == "json" } ?: error("no json files found") + val files = inputDir.listFiles()?.filter { it.extension == "json" } + if (files.isNullOrEmpty()) error("no json files found") branch.runWrite { val rootNode = branch.getRootNode() - println("Got root node: $rootNode") - println("Importing...") + logger.info("Got root node: {}", rootNode) + logger.info("Importing...") ModelImporter(branch.getRootNode()).importFilesAsRootChildren(*files.toTypedArray()) - println("Import finished") + logger.info("Import finished") } } } diff --git a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ValidateSyncSettings.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ValidateSyncSettings.kt index 6f215ba4ee..f23b8a459b 100644 --- a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ValidateSyncSettings.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ValidateSyncSettings.kt @@ -30,6 +30,11 @@ import org.modelix.model.sync.bulk.gradle.config.ServerTarget import org.modelix.model.sync.bulk.gradle.config.SyncDirection import javax.inject.Inject +/** + * Instead of throwing exceptions for single configuration errors, + * this task collects all configuration errors and puts them into a single exception, + * so the user can see all steps that must be taken at a glance. + */ @CacheableTask abstract class ValidateSyncSettings @Inject constructor(of: ObjectFactory) : DefaultTask() { @@ -67,14 +72,8 @@ abstract class ValidateSyncSettings @Inject constructor(of: ObjectFactory) : Def appendLine("Undefined name.") } - if (direction.source == null) { - appendLine("Undefined source.") - } - if (direction.target == null) { - appendLine("Undefined target.") - } - if (direction.source == null || direction.target == null) { + appendLine("Both source and target have to be defined.") return@buildString } @@ -88,15 +87,15 @@ abstract class ValidateSyncSettings @Inject constructor(of: ObjectFactory) : Def return@buildString } - val sourceErrors = direction.source?.getValidationErrors() ?: "" + val sourceErrors = direction.source?.getValidationErrors() ?: emptyList() if (sourceErrors.isNotEmpty()) { appendLine() - appendLine(sourceErrors) + appendLine(sourceErrors.joinToString(separator = "\n") { it }) } - val targetErrors = direction.target?.getValidationErrors() ?: "" + val targetErrors = direction.target?.getValidationErrors() ?: emptyList() if (targetErrors.isNotEmpty()) { - appendLine(targetErrors) + appendLine(targetErrors.joinToString(separator = "\n") { it }) } } From d95a05660d4280e6dadfaedb59cefef7dfb8cebb Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Thu, 7 Sep 2023 12:05:09 +0200 Subject: [PATCH 23/43] test(bulk-model-sync-gradle): only test for modified nodes --- .../model/sync/gradle/test/PullTest.kt | 84 +++++-------------- 1 file changed, 20 insertions(+), 64 deletions(-) diff --git a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt index ff932c4a79..22b315d259 100644 --- a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt +++ b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt @@ -2,75 +2,31 @@ package org.modelix.model.sync.gradle.test import org.junit.jupiter.api.Test import java.io.File -import kotlin.test.assertEquals class PullTest { @Test fun `nodes were synced to local`() { - val localModelFile = File("build/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps") - val expected = """ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - """.trimIndent() + val localModel = File("build/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps").readText() - assertEquals(expected, localModelFile.readText().trim()) + val expectedNodesRegex = """ + |\s* + |\s* + |\s* + |\s* + |\s* + |\s* + |\s* + |\s* + |\s* + |\s* + |\s* + |\s* + |\s* + |\s* + |\s* + """.trimMargin().toRegex() + + assert(expectedNodesRegex.containsMatchIn(localModel)) } } From 266dfbb7156b408d502ee55fe559c6c7eb6b58cc Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Fri, 8 Sep 2023 11:59:28 +0200 Subject: [PATCH 24/43] test(bulk-model-sync-gradle): bump mps version to 2021.2.5 MPS <=2021.1.4 can't handle cycles in filesystems while collecting jars. This causes the ci test to fail. --- bulk-model-sync-gradle-test/build.gradle.kts | 2 +- bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts | 2 +- bulk-model-sync-gradle-test/test-repo/.mps/migration.xml | 2 ++ .../test-repo/languages/GraphLang/GraphLang.mpl | 4 ++-- .../languages/GraphLang/models/GraphLang.behavior.mps | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/bulk-model-sync-gradle-test/build.gradle.kts b/bulk-model-sync-gradle-test/build.gradle.kts index fb2e50a058..a763e510d0 100644 --- a/bulk-model-sync-gradle-test/build.gradle.kts +++ b/bulk-model-sync-gradle-test/build.gradle.kts @@ -48,7 +48,7 @@ val mpsDir = buildDir.resolve("mps").apply { mkdirs() } val kotlinGenDir = buildDir.resolve("metamodel/kotlin").apply { mkdirs() } dependencies { - mps("com.jetbrains:mps:2021.1.4") + mps("com.jetbrains:mps:2021.2.5") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.2") implementation("org.modelix:model-server:$modelixCoreVersion") implementation("org.modelix:model-api-gen-runtime:$modelixCoreVersion") diff --git a/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts b/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts index 69dec0e800..ed16589d73 100644 --- a/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts +++ b/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts @@ -19,7 +19,7 @@ val mps: Configuration by configurations.creating val kotlinGenDir = buildDir.resolve("metamodel/kotlin").apply { mkdirs() } dependencies { - mps("com.jetbrains:mps:2021.1.4") + mps("com.jetbrains:mps:2021.2.5") api("org.modelix:model-api-gen-runtime:$modelixCoreVersion") } diff --git a/bulk-model-sync-gradle-test/test-repo/.mps/migration.xml b/bulk-model-sync-gradle-test/test-repo/.mps/migration.xml index d512ce4547..1ed589a824 100644 --- a/bulk-model-sync-gradle-test/test-repo/.mps/migration.xml +++ b/bulk-model-sync-gradle-test/test-repo/.mps/migration.xml @@ -1,6 +1,8 @@ + + diff --git a/bulk-model-sync-gradle-test/test-repo/languages/GraphLang/GraphLang.mpl b/bulk-model-sync-gradle-test/test-repo/languages/GraphLang/GraphLang.mpl index 14d188766b..bcbd9c3324 100644 --- a/bulk-model-sync-gradle-test/test-repo/languages/GraphLang/GraphLang.mpl +++ b/bulk-model-sync-gradle-test/test-repo/languages/GraphLang/GraphLang.mpl @@ -38,7 +38,7 @@ - + @@ -83,7 +83,7 @@ - + diff --git a/bulk-model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.behavior.mps b/bulk-model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.behavior.mps index b70e7137d5..e83f329ca0 100644 --- a/bulk-model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.behavior.mps +++ b/bulk-model-sync-gradle-test/test-repo/languages/GraphLang/models/GraphLang.behavior.mps @@ -2,7 +2,7 @@ - + From 06d65b1fc39da393ad364fd62c53f7f7f94afc14 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Fri, 8 Sep 2023 16:23:00 +0200 Subject: [PATCH 25/43] ci: parallelized plugin tests --- .github/workflows/build.yaml | 56 ++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 726537cce3..075f510658 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -28,21 +28,61 @@ jobs: arguments: | --build-cache build + -PciBuild=true + - name: Archive test report + uses: actions/upload-artifact@v3 + if: always() + with: + name: test-report + path: | + */build/test-results + */build/reports + + test-model-api-gen-gradle: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '11' + - name: Assemble + uses: gradle/gradle-build-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + arguments: | + --build-cache + assemble publishToMavenLocal -PciBuild=true - name: Test Model API Generator Gradle Plugin env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: model-api-gen-gradle-test/ci.sh + + test-bulk-model-sync-gradle: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '11' + - name: Assemble + uses: gradle/gradle-build-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + arguments: | + --build-cache + assemble + publishToMavenLocal + -PciBuild=true + - name: Test Bulk Model Sync Gradle Plugin env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: bulk-model-sync-gradle-test/ci.sh - - name: Archive test report - uses: actions/upload-artifact@v3 - if: always() - with: - name: test-report - path: | - */build/test-results - */build/reports From a9dcab004159a19b7f75778b5208122b642b0f12 Mon Sep 17 00:00:00 2001 From: Sascha Lisson Date: Tue, 12 Sep 2023 15:40:30 +0200 Subject: [PATCH 26/43] fix(mps-model-adapters): reverted dependency on MPS to compileOnly --- build.gradle.kts | 17 ++- .../model/sync/gradle/test/PushTest.kt | 3 - .../gradle/config/ModelSyncGradleSettings.kt | 3 +- .../gradle/tasks/ExportFromModelServer.kt | 8 +- .../modelix/model/sync/bulk/ModelExporter.kt | 2 +- .../org.modelix.mps.model.sync.bulk.mps | 29 ++-- .../metamodel/TypedLanguagesRegistry.kt | 2 + .../org/modelix/model/api/BuiltinLanguages.kt | 143 ++++++++++++++++++ .../modelix/model/api/ILanguageRepository.kt | 25 ++- .../org/modelix/model/api/PNodeAdapter.kt | 12 ++ .../org/modelix/model/api/SimpleProperty.kt | 14 +- .../modelix/model/api/SimpleReferenceLink.kt | 11 +- mps-model-adapters/build.gradle.kts | 6 +- .../mpsadapters/MPSLanguageRepository.kt | 2 + .../model/mpsadapters/RepositoryLanguage.kt | 1 + 15 files changed, 240 insertions(+), 38 deletions(-) create mode 100644 model-api/src/commonMain/kotlin/org/modelix/model/api/BuiltinLanguages.kt diff --git a/build.gradle.kts b/build.gradle.kts index b1b3082ea5..5ac30acf52 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,6 +17,8 @@ import kotlinx.html.unsafe import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.DokkaBaseConfiguration import org.jetbrains.dokka.gradle.DokkaTaskPartial +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper import org.semver.Version buildscript { @@ -76,12 +78,25 @@ subprojects { version.set("0.50.0") } + val kotlinApiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_6 tasks.withType().all { if (!name.lowercase().contains("test")) { kotlinOptions { jvmTarget = "11" freeCompilerArgs += listOf("-Xjvm-default=all-compatibility") - apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_6.version + apiVersion = kotlinApiVersion.version + } + } + } + + plugins.withType { + project.extensions.configure { + sourceSets.all { + if (!name.lowercase().contains("test")) { + languageSettings { + apiVersion = kotlinApiVersion.version + } + } } } } diff --git a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt index 773556cf9f..aa713e0d2d 100644 --- a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt +++ b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt @@ -11,7 +11,6 @@ import org.modelix.metamodel.typed import org.modelix.model.ModelFacade import org.modelix.model.api.ConceptReference import org.modelix.model.api.IBranch -import org.modelix.model.api.ILanguageRepository import org.modelix.model.api.getDescendants import org.modelix.model.api.getRootNode import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder @@ -19,7 +18,6 @@ import org.modelix.model.client2.getReplicatedModel import org.modelix.model.data.ModelData import org.modelix.model.data.NodeData import org.modelix.model.lazy.RepositoryId -import org.modelix.model.mpsadapters.RepositoryLanguage import org.modelix.model.server.Main import org.modelix.model.sleep import org.modelix.model.sync.bulk.asExported @@ -38,7 +36,6 @@ class PushTest { TypedLanguagesRegistry.register(L_GraphLang) TypedLanguagesRegistry.register(L_jetbrains_mps_lang_core) - ILanguageRepository.default.registerLanguage(RepositoryLanguage) val repoId = RepositoryId("ci-test") val branchName = "master" diff --git a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt index 254ab1f305..1dbe08ddcc 100644 --- a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt @@ -18,7 +18,6 @@ package org.modelix.model.sync.bulk.gradle.config import org.gradle.api.Action import org.modelix.model.api.ILanguage -import org.modelix.model.mpsadapters.RepositoryLanguage import java.io.File open class ModelSyncGradleSettings { @@ -41,7 +40,7 @@ data class SyncDirection( internal var source: SyncEndPoint? = null, internal var target: SyncEndPoint? = null, internal val includedModules: Set = mutableSetOf(), - internal val registeredLanguages: Set = mutableSetOf(RepositoryLanguage), + internal val registeredLanguages: Set = mutableSetOf(), ) { fun fromModelServer(action: Action) { val endpoint = ServerSource() diff --git a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromModelServer.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromModelServer.kt index 2783a13450..6869c4b4f5 100644 --- a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromModelServer.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromModelServer.kt @@ -26,8 +26,8 @@ import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import org.modelix.model.ModelFacade +import org.modelix.model.api.BuiltinLanguages import org.modelix.model.api.IBranch -import org.modelix.model.api.IProperty import org.modelix.model.api.PBranch import org.modelix.model.api.getRootNode import org.modelix.model.client2.IModelClientV2 @@ -35,7 +35,6 @@ import org.modelix.model.client2.ModelClientV2 import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder import org.modelix.model.client2.getReplicatedModel import org.modelix.model.lazy.RepositoryId -import org.modelix.model.mpsadapters.RepositoryLanguage import org.modelix.model.sync.bulk.ModelExporter import javax.inject.Inject @@ -73,8 +72,9 @@ abstract class ExportFromModelServer @Inject constructor(of: ObjectFactory) : De logger.info("Got root node: {}", root) val outputDir = outputDir.get().asFile root.allChildren.forEach { - val nameRole = IProperty.fromName(RepositoryLanguage.NamePropertyUID) - val outputFile = outputDir.resolve("${it.getPropertyValue(nameRole)}.json") + val nameRole = BuiltinLanguages.jetbrains_mps_lang_core.INamedConcept.name + val fileName = it.getPropertyValue(nameRole) + val outputFile = outputDir.resolve("$fileName.json") ModelExporter(it).export(outputFile) } } diff --git a/bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk/ModelExporter.kt b/bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk/ModelExporter.kt index eccd63f029..19cd88feff 100644 --- a/bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk/ModelExporter.kt +++ b/bulk-model-sync-lib/src/commonMain/kotlin/org/modelix/model/sync/bulk/ModelExporter.kt @@ -33,7 +33,7 @@ fun INode.asExported(): NodeData { val idKey = NodeData.idPropertyKey return NodeData( id = getPropertyValue(idKey) ?: reference.serialize(), - concept = concept?.getUID(), + concept = getConceptReference()?.getUID(), role = roleInParent, properties = getPropertyRoles().associateWithNotNull { getPropertyValue(it) }.filterKeys { it != idKey }, references = getReferenceRoles().associateWithNotNull { diff --git a/bulk-model-sync-solution/solutions/org.modelix.mps.model.sync.bulk/models/org.modelix.mps.model.sync.bulk.mps b/bulk-model-sync-solution/solutions/org.modelix.mps.model.sync.bulk/models/org.modelix.mps.model.sync.bulk.mps index a4a490c5fb..614863c2d3 100644 --- a/bulk-model-sync-solution/solutions/org.modelix.mps.model.sync.bulk/models/org.modelix.mps.model.sync.bulk.mps +++ b/bulk-model-sync-solution/solutions/org.modelix.mps.model.sync.bulk/models/org.modelix.mps.model.sync.bulk.mps @@ -12,7 +12,6 @@ - @@ -214,11 +213,11 @@ - + - + @@ -292,7 +291,7 @@ - + @@ -325,7 +324,7 @@ - + @@ -342,7 +341,7 @@ - + @@ -393,7 +392,7 @@ - + @@ -405,7 +404,7 @@ - + @@ -413,7 +412,7 @@ - + @@ -423,7 +422,7 @@ - + @@ -440,7 +439,7 @@ - + @@ -472,7 +471,7 @@ - + @@ -564,11 +563,11 @@ - + - + @@ -582,7 +581,7 @@ - + diff --git a/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/TypedLanguagesRegistry.kt b/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/TypedLanguagesRegistry.kt index 748e253cb4..d4be88ea9e 100644 --- a/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/TypedLanguagesRegistry.kt +++ b/model-api-gen-runtime/src/commonMain/kotlin/org/modelix/metamodel/TypedLanguagesRegistry.kt @@ -45,6 +45,8 @@ object TypedLanguagesRegistry : ILanguageRepository { ?: return UnknownConceptInstance(node) return concept.wrap(node) } + + override fun getPriority(): Int = 2000 } fun INode.typed(nodeClass: KClass): NodeT = nodeClass.cast(TypedLanguagesRegistry.wrapNode(this)) diff --git a/model-api/src/commonMain/kotlin/org/modelix/model/api/BuiltinLanguages.kt b/model-api/src/commonMain/kotlin/org/modelix/model/api/BuiltinLanguages.kt new file mode 100644 index 0000000000..ef5e570483 --- /dev/null +++ b/model-api/src/commonMain/kotlin/org/modelix/model/api/BuiltinLanguages.kt @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.api + +import kotlin.reflect.KProperty + +object BuiltinLanguages { + @Suppress("ClassName") + object jetbrains_mps_lang_core : SimpleLanguage(name = "jetbrains.mps.lang.core", uid = "ceab5195-25ea-4f22-9b92-103b95ca8c0c") { + object BaseConcept : SimpleConcept(conceptName = "BaseConcept", is_abstract = true, uid = "mps:ceab5195-25ea-4f22-9b92-103b95ca8c0c/1133920641626") { + init { addConcept(this) } + val virtualPackage by property("ceab5195-25ea-4f22-9b92-103b95ca8c0c/1133920641626/1193676396447") + val smodelAttribute by childLink("ceab5195-25ea-4f22-9b92-103b95ca8c0c/1133920641626/5169995583184591170").multiple().optional().type { Attribute } + } + object Attribute : SimpleConcept( + conceptName = "Attribute", + is_abstract = true, + uid = "mps:ceab5195-25ea-4f22-9b92-103b95ca8c0c/5169995583184591161", + directSuperConcepts = listOf(BaseConcept), + ) { + init { addConcept(this) } + } + object NodeAttribute : SimpleConcept( + conceptName = "NodeAttribute", + is_abstract = true, + uid = "mps:ceab5195-25ea-4f22-9b92-103b95ca8c0c/3364660638048049748", + directSuperConcepts = listOf(Attribute), + ) { + init { addConcept(this) } + } + object INamedConcept : SimpleConcept(conceptName = "INamedConcept") { + init { addConcept(this) } + val name by property("ceab5195-25ea-4f22-9b92-103b95ca8c0c/1169194658468/1169194664001") + } + } + + /** + * These concepts are originally defined in + * https://github.com/JetBrains/MPS-extensions/blob/5d96c3e69192f8902cf9aa7d846d05ccfb65253d/code/model-api/org.modelix.model.repositoryconcepts/models/org.modelix.model.repositoryconcepts.structure.mps , + * but to get rid of that dependency, they are redefined here, with their original IDs to stay compatible. + */ + object MPSRepositoryConcepts : SimpleLanguage("org.modelix.model.repositoryconcepts", uid = "mps:0a7577d1-d4e5-431d-98b1-fae38f9aee80") { + + object Model : SimpleConcept( + conceptName = "Model", + uid = "mps:0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618892", + directSuperConcepts = listOf(jetbrains_mps_lang_core.BaseConcept, jetbrains_mps_lang_core.INamedConcept), + ) { + init { addConcept(this) } + val rootNodes = SimpleChildLink( + simpleName = "rootNodes", + isMultiple = true, + isOptional = true, + targetConcept = jetbrains_mps_lang_core.BaseConcept, + uid = "0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618892/474657388638618900", + ) + } + + object Module : SimpleConcept( + conceptName = "Module", + uid = "mps:0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618895", + directSuperConcepts = listOf(jetbrains_mps_lang_core.BaseConcept, jetbrains_mps_lang_core.INamedConcept), + ) { + init { addConcept(this) } + val models = SimpleChildLink( + simpleName = "models", + isMultiple = true, + isOptional = true, + targetConcept = Model, + uid = "0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618895/474657388638618898", + ) + } + + object Repository : SimpleConcept( + conceptName = "Repository", + uid = "mps:0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618902", + directSuperConcepts = listOf(jetbrains_mps_lang_core.BaseConcept), + ) { + init { addConcept(this) } + val modules = SimpleChildLink( + simpleName = "modules", + isMultiple = true, + isOptional = true, + targetConcept = Module, + uid = "0a7577d1-d4e5-431d-98b1-fae38f9aee80/474657388638618902/474657388638618903", + ) + } + } + + fun getAllLanguages() = listOf( + jetbrains_mps_lang_core, + MPSRepositoryConcepts, + ) +} + +private fun SimpleConcept.property(uid: String) = object { + private lateinit var name: String + private lateinit var owner: SimpleConcept + private val instance: IProperty by lazy { + SimpleProperty(name, uid = uid).also { owner.addProperty(it) } + } + operator fun getValue(ownerConcept: SimpleConcept, kotlinProperty: KProperty<*>): IProperty { + this.owner = ownerConcept + this.name = kotlinProperty.name + return instance + } +} + +private fun SimpleConcept.childLink(uid: String) = object { + private lateinit var name: String + private lateinit var owner: SimpleConcept + private var multiple: Boolean = true + private var optional: Boolean = true + private lateinit var targetConcept: () -> IConcept + private val instance: IChildLink by lazy { + SimpleChildLink(simpleName = name, uid = uid, isMultiple = multiple, isOptional = optional, targetConcept = targetConcept()).also { owner.addChildLink(it) } + } + operator fun getValue(ownerConcept: SimpleConcept, kotlinProperty: KProperty<*>): IChildLink { + this.owner = ownerConcept + this.name = kotlinProperty.name + return instance + } + + fun mandatory() = this.also { this.optional = false } + fun optional() = this.also { this.optional = true } + fun single() = this.also { this.multiple = false } + fun multiple() = this.also { this.multiple = true } + fun type(targetConcept: () -> IConcept) = also { this.targetConcept = targetConcept } +} diff --git a/model-api/src/commonMain/kotlin/org/modelix/model/api/ILanguageRepository.kt b/model-api/src/commonMain/kotlin/org/modelix/model/api/ILanguageRepository.kt index 9283417d3b..f1f4ef6f06 100644 --- a/model-api/src/commonMain/kotlin/org/modelix/model/api/ILanguageRepository.kt +++ b/model-api/src/commonMain/kotlin/org/modelix/model/api/ILanguageRepository.kt @@ -30,11 +30,18 @@ interface ILanguageRepository { * @throws RuntimeException if multiple concepts were found for the given reference */ fun tryResolveConcept(ref: IConceptReference): IConcept? { - val concepts = repositories.mapNotNull { it.resolveConcept(ref.getUID()) } + val concepts = repositories.map { it to it.resolveConcept(ref.getUID()) }.filterSecondNotNull() return when (concepts.size) { 0 -> null - 1 -> concepts.first() - else -> throw RuntimeException("Multiple concepts found for $ref: $concepts") + 1 -> concepts.first().second + else -> + concepts + .groupBy { it.first.getPriority() } + .maxByOrNull { it.key }!! + .value + .singleOrNull() + ?.second + ?: throw RuntimeException("Multiple concepts found for $ref: $concepts") } } @@ -95,6 +102,12 @@ interface ILanguageRepository { * @return list of all concepts */ fun getAllConcepts(): List + + /** + * If multiple repositories can provide information for the same concept UID, the IConcept from the repository with + * the highest priority is used. + */ + fun getPriority(): Int = 0 } /** @@ -161,4 +174,10 @@ object DefaultLanguageRepository : ILanguageRepository { override fun getAllConcepts(): List { return concepts.values.toList() } + + override fun getPriority(): Int = 0 + + init { + BuiltinLanguages.getAllLanguages().forEach { registerLanguage(it) } + } } diff --git a/model-api/src/commonMain/kotlin/org/modelix/model/api/PNodeAdapter.kt b/model-api/src/commonMain/kotlin/org/modelix/model/api/PNodeAdapter.kt index 2dc3f27d8a..7a17605b42 100644 --- a/model-api/src/commonMain/kotlin/org/modelix/model/api/PNodeAdapter.kt +++ b/model-api/src/commonMain/kotlin/org/modelix/model/api/PNodeAdapter.kt @@ -64,6 +64,18 @@ open class PNodeAdapter(val nodeId: Long, val branch: IBranch) : INode, INodeEx return PNodeAdapter(branch.writeTransaction.addNewChild(nodeId, role, index, concept), branch) } + override fun addNewChild(role: String?, index: Int, concept: IConceptReference?): INode { + return PNodeAdapter(branch.writeTransaction.addNewChild(nodeId, role, index, concept), branch) + } + + override fun addNewChild(role: IChildLink, index: Int, concept: IConcept?): INode { + return PNodeAdapter(branch.writeTransaction.addNewChild(nodeId, role.key(this), index, concept), branch) + } + + override fun addNewChild(role: IChildLink, index: Int, concept: IConceptReference?): INode { + return PNodeAdapter(branch.writeTransaction.addNewChild(nodeId, role.key(this), index, concept), branch) + } + override val allChildren: Iterable get() { notifyAccess() diff --git a/model-api/src/commonMain/kotlin/org/modelix/model/api/SimpleProperty.kt b/model-api/src/commonMain/kotlin/org/modelix/model/api/SimpleProperty.kt index 908c9f59a9..4d4773d757 100644 --- a/model-api/src/commonMain/kotlin/org/modelix/model/api/SimpleProperty.kt +++ b/model-api/src/commonMain/kotlin/org/modelix/model/api/SimpleProperty.kt @@ -13,14 +13,22 @@ */ package org.modelix.model.api -class SimpleProperty(private val simpleName: String, override val isOptional: Boolean = true) : IProperty { +import kotlin.jvm.JvmOverloads + +class SimpleProperty +@JvmOverloads constructor( + private val simpleName: String, + override val isOptional: Boolean = true, + private val uid: String? = null, +) : IProperty { var owner: SimpleConcept? = null override fun getConcept(): IConcept = owner!! override fun getUID(): String { - val o = owner - return (if (o == null) simpleName else o.getUID() + "." + simpleName) + return uid + ?: owner?.let { it.getUID() + "." + simpleName } + ?: simpleName } override fun getSimpleName(): String = simpleName diff --git a/model-api/src/commonMain/kotlin/org/modelix/model/api/SimpleReferenceLink.kt b/model-api/src/commonMain/kotlin/org/modelix/model/api/SimpleReferenceLink.kt index ff0fc0d6d3..e595cb6f94 100644 --- a/model-api/src/commonMain/kotlin/org/modelix/model/api/SimpleReferenceLink.kt +++ b/model-api/src/commonMain/kotlin/org/modelix/model/api/SimpleReferenceLink.kt @@ -13,18 +13,23 @@ */ package org.modelix.model.api -class SimpleReferenceLink( +import kotlin.jvm.JvmOverloads + +class SimpleReferenceLink +@JvmOverloads constructor( private val simpleName: String, override val isOptional: Boolean, override var targetConcept: IConcept, + private val uid: String? = null, ) : IReferenceLink { var owner: SimpleConcept? = null override fun getConcept(): IConcept = owner!! override fun getUID(): String { - val o = owner - return (if (o == null) simpleName else o.getUID() + "." + simpleName) + return uid + ?: owner?.let { it.getUID() + "." + simpleName } + ?: simpleName } override fun getSimpleName(): String = simpleName diff --git a/mps-model-adapters/build.gradle.kts b/mps-model-adapters/build.gradle.kts index 71950a07cb..a765db517f 100644 --- a/mps-model-adapters/build.gradle.kts +++ b/mps-model-adapters/build.gradle.kts @@ -8,9 +8,9 @@ val mpsVersion = project.findProperty("mps.version")?.toString().takeIf { !it.is dependencies { api(project(":model-api")) - implementation("com.jetbrains:mps-openapi:$mpsVersion") - implementation("com.jetbrains:mps-core:$mpsVersion") - implementation("com.jetbrains:mps-environment:$mpsVersion") + compileOnly("com.jetbrains:mps-openapi:$mpsVersion") + compileOnly("com.jetbrains:mps-core:$mpsVersion") + compileOnly("com.jetbrains:mps-environment:$mpsVersion") implementation(libs.trove) implementation(kotlin("stdlib")) diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt index 91a08921b3..0daef5d70a 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt @@ -47,4 +47,6 @@ class MPSLanguageRepository(private val repository: SRepository) : ILanguageRepo } return result } + + override fun getPriority(): Int = 1000 } diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/RepositoryLanguage.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/RepositoryLanguage.kt index 08fc737ca5..56074752f0 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/RepositoryLanguage.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/RepositoryLanguage.kt @@ -22,6 +22,7 @@ import org.modelix.model.mpsadapters.RepositoryLanguage.BaseConcept import org.modelix.model.mpsadapters.RepositoryLanguage.INamedConcept import org.modelix.model.mpsadapters.RepositoryLanguage.addConcept +@Deprecated("use org.modelix.model.api.BuiltinLanguages.MPSRepositoryConcepts", ReplaceWith("org.modelix.model.api.BuiltinLanguages.MPSRepositoryConcepts")) object RepositoryLanguage : SimpleLanguage("org.modelix.model.repositoryconcepts", uid = "mps:0a7577d1-d4e5-431d-98b1-fae38f9aee80") { val BaseConcept = MPSConcept(SConceptAdapterById.deserialize("c:ceab5195-25ea-4f22-9b92-103b95ca8c0c/1133920641626:jetbrains.mps.lang.core.structure.BaseConcept")) val INamedConcept = MPSConcept(SInterfaceConceptAdapterById.deserialize("i:ceab5195-25ea-4f22-9b92-103b95ca8c0c/1169194658468:jetbrains.mps.lang.core.structure.INamedConcept")) From 4f3f02e5270e6548a6f6959eec58fffdc5052971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20Li=C3=9Fon?= Date: Tue, 12 Sep 2023 17:14:41 +0200 Subject: [PATCH 27/43] fix(mps-model-adapters): some write access implementations ignored incorrect usage --- .../org/modelix/model/mpsadapters/MPSModelAsNode.kt | 4 +++- .../modelix/model/mpsadapters/MPSNodeReference.kt | 2 +- .../modelix/model/mpsadapters/MPSRepositoryAsNode.kt | 12 +++++++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt index 8d9af632c2..a6518e5f16 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt @@ -111,7 +111,9 @@ data class MPSModelAsNode(val model: SModel) : IDeprecatedNodeDefaults { } override fun setPropertyValue(property: IProperty, value: String?) { - // TODO("Not yet implemented") + if (getPropertyValue(property) != value) { + throw UnsupportedOperationException("Property $property of $concept is read-only") + } } override fun getPropertyLinks(): List { diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNodeReference.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNodeReference.kt index dd31c449d6..5ccf54791d 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNodeReference.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSNodeReference.kt @@ -55,6 +55,6 @@ object MPSNodeReferenceSerializer : INodeReferenceSerializerEx { } override fun deserialize(serialized: String): INodeReference { - TODO("Not yet implemented") + return MPSNodeReference(SNodePointer.deserialize(serialized)) } } diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt index 9702402825..023ea840d9 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt @@ -85,11 +85,15 @@ data class MPSRepositoryAsNode(val repository: SRepository) : IDeprecatedNodeDef } override fun setReferenceTarget(link: IReferenceLink, target: INode?) { - return + if (target != null) { + throw IllegalArgumentException("$concept doesn't contain a reference link $link") + } } override fun setReferenceTarget(role: IReferenceLink, target: INodeReference?) { - return + if (target != null) { + throw IllegalArgumentException("$concept doesn't contain a reference link $role") + } } override fun getReferenceTargetRef(role: IReferenceLink): INodeReference? { @@ -101,7 +105,9 @@ data class MPSRepositoryAsNode(val repository: SRepository) : IDeprecatedNodeDef } override fun setPropertyValue(property: IProperty, value: String?) { - TODO("Not yet implemented") + if (value != null) { + throw IllegalArgumentException("$concept doesn't contain a property $property") + } } override fun getPropertyLinks(): List { From 5eab7a34606c615e51bf929f66b116e89f40b806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20Li=C3=9Fon?= Date: Tue, 12 Sep 2023 17:15:51 +0200 Subject: [PATCH 28/43] fix(mps-model-adapters): MPSLanguageRepository never resolved any concepts --- .../org/modelix/model/mpsadapters/MPSLanguageRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt index 0daef5d70a..335282c6be 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt @@ -27,7 +27,7 @@ import org.modelix.model.api.ILanguageRepository class MPSLanguageRepository(private val repository: SRepository) : ILanguageRepository { override fun resolveConcept(uid: String): IConcept? { - if (uid.startsWith("mps:")) return null + if (!uid.startsWith("mps:")) return null val conceptId = try { SConceptId.deserialize(uid.substring(4)) From 08dcf5bcfe34f3602ff39d8ba00afc5e4d1288f3 Mon Sep 17 00:00:00 2001 From: Sascha Lisson Date: Tue, 12 Sep 2023 17:24:25 +0200 Subject: [PATCH 29/43] chore: use registration helper in bulk-model-sync-gradle-test --- bulk-model-sync-gradle-test/build.gradle.kts | 5 +---- bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/bulk-model-sync-gradle-test/build.gradle.kts b/bulk-model-sync-gradle-test/build.gradle.kts index a763e510d0..16f0d58133 100644 --- a/bulk-model-sync-gradle-test/build.gradle.kts +++ b/bulk-model-sync-gradle-test/build.gradle.kts @@ -14,8 +14,6 @@ * limitations under the License. */ -import GraphLang.L_GraphLang -import jetbrains.mps.lang.core.L_jetbrains_mps_lang_core import org.modelix.model.server.Main buildscript { @@ -95,8 +93,7 @@ modelSync { dependsOn(resolveMps) dependsOn(copyTestRepo) direction("testPush") { - registerLanguage(L_GraphLang) - registerLanguage(L_jetbrains_mps_lang_core) + org.modelix.model.sync.gradle.test.GraphLanguagesHelper.registerAll() includeModule("GraphSolution") fromLocal { mpsHome = mpsDir diff --git a/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts b/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts index ed16589d73..1b6bdd1a31 100644 --- a/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts +++ b/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts @@ -49,6 +49,7 @@ metamodel { mpsHome = mpsDir kotlinDir = kotlinGenDir includeLanguage("GraphLang") + registrationHelperName = "org.modelix.model.sync.gradle.test.GraphLanguagesHelper" } publishing { From 6af6477d2b893c0f5e850dfc1ee3ac4ccd8e66ca Mon Sep 17 00:00:00 2001 From: Sascha Lisson Date: Tue, 12 Sep 2023 17:29:43 +0200 Subject: [PATCH 30/43] chore: ensure bulk-model-sync-gradle-test uses the correct Kotlin version --- bulk-model-sync-gradle-test/build.gradle.kts | 2 +- .../graph-lang-api/build.gradle.kts | 2 +- .../graph-lang-api/settings.gradle.kts | 13 +++++++++++++ bulk-model-sync-gradle-test/settings.gradle.kts | 14 +++++++++++++- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/bulk-model-sync-gradle-test/build.gradle.kts b/bulk-model-sync-gradle-test/build.gradle.kts index 16f0d58133..ad4d12d20a 100644 --- a/bulk-model-sync-gradle-test/build.gradle.kts +++ b/bulk-model-sync-gradle-test/build.gradle.kts @@ -25,7 +25,7 @@ buildscript { } plugins { - kotlin("jvm") + alias(libs.plugins.kotlin.jvm) id("org.modelix.bulk-model-sync") } diff --git a/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts b/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts index 1b6bdd1a31..06f57fe18b 100644 --- a/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts +++ b/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts @@ -1,7 +1,7 @@ plugins { id("org.modelix.model-api-gen") `maven-publish` - kotlin("jvm") version "1.9.0" + alias(libs.plugins.kotlin.jvm) } repositories { diff --git a/bulk-model-sync-gradle-test/graph-lang-api/settings.gradle.kts b/bulk-model-sync-gradle-test/graph-lang-api/settings.gradle.kts index 13c41738ce..24c457a2c9 100644 --- a/bulk-model-sync-gradle-test/graph-lang-api/settings.gradle.kts +++ b/bulk-model-sync-gradle-test/graph-lang-api/settings.gradle.kts @@ -25,4 +25,17 @@ pluginManagement { maven { url = uri("https://artifacts.itemis.cloud/repository/maven-mps/") } mavenCentral() } + dependencyResolutionManagement { + repositories { + mavenLocal() + gradlePluginPortal() + maven { url = uri("https://artifacts.itemis.cloud/repository/maven-mps/") } + mavenCentral() + } + versionCatalogs { + create("libs") { + from("org.modelix:core-version-catalog:$modelixCoreVersion") + } + } + } } diff --git a/bulk-model-sync-gradle-test/settings.gradle.kts b/bulk-model-sync-gradle-test/settings.gradle.kts index 00c06f01a9..23aebaebff 100644 --- a/bulk-model-sync-gradle-test/settings.gradle.kts +++ b/bulk-model-sync-gradle-test/settings.gradle.kts @@ -19,7 +19,6 @@ pluginManagement { plugins { id("org.modelix.bulk-model-sync") version modelixCoreVersion id("org.modelix.model-api-gen") version modelixCoreVersion - kotlin("jvm") version "1.9.0" } repositories { mavenLocal() @@ -27,4 +26,17 @@ pluginManagement { maven { url = uri("https://artifacts.itemis.cloud/repository/maven-mps/") } mavenCentral() } + dependencyResolutionManagement { + repositories { + mavenLocal() + gradlePluginPortal() + maven { url = uri("https://artifacts.itemis.cloud/repository/maven-mps/") } + mavenCentral() + } + versionCatalogs { + create("libs") { + from("org.modelix:core-version-catalog:$modelixCoreVersion") + } + } + } } From 984e3fe85a99e2564457f25d59bb0e9b65a4c405 Mon Sep 17 00:00:00 2001 From: Sascha Lisson Date: Tue, 12 Sep 2023 18:31:23 +0200 Subject: [PATCH 31/43] fix(model-server): removed -useroleids flag The /init endpoint now has a parameter instead. ID based roles should be the default for new repositories anyway and the parameter is not expected to be ever used. --- bulk-model-sync-gradle-test/build.gradle.kts | 1 - .../pages/reference/component-bulk-model-sync-gradle.adoc | 2 -- .../src/main/kotlin/org/modelix/model/server/CmdLineArgs.kt | 3 --- model-server/src/main/kotlin/org/modelix/model/server/Main.kt | 2 +- .../modelix/model/server/handlers/ModelReplicationServer.kt | 3 ++- .../org/modelix/model/server/handlers/RepositoriesManager.kt | 4 ++-- 6 files changed, 5 insertions(+), 10 deletions(-) diff --git a/bulk-model-sync-gradle-test/build.gradle.kts b/bulk-model-sync-gradle-test/build.gradle.kts index ad4d12d20a..40e0fbc522 100644 --- a/bulk-model-sync-gradle-test/build.gradle.kts +++ b/bulk-model-sync-gradle-test/build.gradle.kts @@ -74,7 +74,6 @@ tasks.register("runModelServer", JavaExec::class) { classpath = sourceSets["main"].runtimeClasspath mainClass.set("org.modelix.model.server.Main") args("-inmemory") - args("-useroleids") } val resolveMps by tasks.registering(Copy::class) { diff --git a/docs/global/modules/core/pages/reference/component-bulk-model-sync-gradle.adoc b/docs/global/modules/core/pages/reference/component-bulk-model-sync-gradle.adoc index 17460ea6dd..199c26b3fd 100644 --- a/docs/global/modules/core/pages/reference/component-bulk-model-sync-gradle.adoc +++ b/docs/global/modules/core/pages/reference/component-bulk-model-sync-gradle.adoc @@ -12,8 +12,6 @@ https://github.com/modelix/modelix.core[Repository^] | https://github.com/modeli The `bulk-model-sync` Gradle plugin synchronizes models between `model-api` endpoints. A common example would be synchronizing a local MPS project to a `model-server`. -IMPORTANT: For correct synchronization the used model-server is required to use IDs for roles. This can be achieved by starting the model-server with the `-useroleids` flag. - .Bulk Synchronization between MPS and model-server image::bulk-model-sync-gradle.overview.png[Bulk Synchronization between MPS and model-server] diff --git a/model-server/src/main/kotlin/org/modelix/model/server/CmdLineArgs.kt b/model-server/src/main/kotlin/org/modelix/model/server/CmdLineArgs.kt index c4ba3faf61..78ba35c552 100644 --- a/model-server/src/main/kotlin/org/modelix/model/server/CmdLineArgs.kt +++ b/model-server/src/main/kotlin/org/modelix/model/server/CmdLineArgs.kt @@ -40,9 +40,6 @@ internal class CmdLineArgs { ) var schemaInit = false - @Parameter(names = ["-useroleids"], description = "Use IDs for roles instead of simpleNames. Required for bulk sync.", converter = BooleanConverter::class) - var useRoleIds = false - @Parameter(names = ["-h", "--help"], help = true) var help = false } diff --git a/model-server/src/main/kotlin/org/modelix/model/server/Main.kt b/model-server/src/main/kotlin/org/modelix/model/server/Main.kt index ba2d7878fa..5e63b571fa 100644 --- a/model-server/src/main/kotlin/org/modelix/model/server/Main.kt +++ b/model-server/src/main/kotlin/org/modelix/model/server/Main.kt @@ -151,7 +151,7 @@ object Main { } val jsonModelServer = DeprecatedLightModelServer(localModelClient) - val repositoriesManager = RepositoriesManager(localModelClient, cmdLineArgs.useRoleIds) + val repositoriesManager = RepositoriesManager(localModelClient) val repositoryOverview = RepositoryOverview(repositoriesManager) val historyHandler = HistoryHandler(localModelClient, repositoriesManager) val contentExplorer = ContentExplorer(localModelClient, repositoriesManager) diff --git a/model-server/src/main/kotlin/org/modelix/model/server/handlers/ModelReplicationServer.kt b/model-server/src/main/kotlin/org/modelix/model/server/handlers/ModelReplicationServer.kt index b705d242f4..f482646cad 100644 --- a/model-server/src/main/kotlin/org/modelix/model/server/handlers/ModelReplicationServer.kt +++ b/model-server/src/main/kotlin/org/modelix/model/server/handlers/ModelReplicationServer.kt @@ -103,7 +103,8 @@ class ModelReplicationServer(val repositoriesManager: RepositoriesManager) { fun ApplicationCall.repositoryId() = RepositoryId(parameters["repository"]!!) fun PipelineContext.repositoryId() = call.repositoryId() post("init") { - val initialVersion = repositoriesManager.createRepository(repositoryId(), call.getUserName()) + val useRoleIds = call.request.queryParameters["useRoleIds"] != "false" + val initialVersion = repositoriesManager.createRepository(repositoryId(), call.getUserName(), useRoleIds) call.respondDelta(initialVersion.getContentHash(), null) } route("branches") { diff --git a/model-server/src/main/kotlin/org/modelix/model/server/handlers/RepositoriesManager.kt b/model-server/src/main/kotlin/org/modelix/model/server/handlers/RepositoriesManager.kt index fdf0181174..3ef6fd80e9 100644 --- a/model-server/src/main/kotlin/org/modelix/model/server/handlers/RepositoriesManager.kt +++ b/model-server/src/main/kotlin/org/modelix/model/server/handlers/RepositoriesManager.kt @@ -30,7 +30,7 @@ import org.modelix.model.server.store.IStoreClient import org.modelix.model.server.store.LocalModelClient import org.modelix.model.server.store.pollEntry -class RepositoriesManager(val client: LocalModelClient, val useRoleIds: Boolean = false) { +class RepositoriesManager(val client: LocalModelClient) { init { migrateLegacyRepositoriesList() } @@ -45,7 +45,7 @@ class RepositoriesManager(val client: LocalModelClient, val useRoleIds: Boolean return store[REPOSITORIES_LIST_KEY]?.lines()?.map { RepositoryId(it) }?.toSet() ?: emptySet() } - fun createRepository(repositoryId: RepositoryId, userName: String?): CLVersion { + fun createRepository(repositoryId: RepositoryId, userName: String?, useRoleIds: Boolean = true): CLVersion { var initialVersion: CLVersion? = null store.runTransaction { val masterBranch = repositoryId.getBranchReference() From 75ea8589fe71a86091f4bac86a0814b51aa5cebf Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Mon, 11 Sep 2023 16:58:06 +0200 Subject: [PATCH 32/43] chore(bulk-model-sync-gradle): use wildcards in git ignore --- bulk-model-sync-gradle-test/.gitignore | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/bulk-model-sync-gradle-test/.gitignore b/bulk-model-sync-gradle-test/.gitignore index 5985ed9f9d..b5d1cc08ef 100644 --- a/bulk-model-sync-gradle-test/.gitignore +++ b/bulk-model-sync-gradle-test/.gitignore @@ -40,10 +40,7 @@ bin/ ### Mac OS ### .DS_Store -/test-repo/languages/GraphLang/classes_gen/ -/test-repo/languages/GraphLang/generator/ -/test-repo/languages/GraphLang/source_gen/ -/test-repo/languages/GraphLang/source_gen.caches/ -/test-repo/solutions/GraphSolution/source_gen/ -/test-repo/solutions/GraphSolution/classes_gen/ -/test-repo/solutions/GraphSolution/source_gen.caches/ +/test-repo/**/**/classes_gen/ +/test-repo/**/**/generator/ +/test-repo/**/**/source_gen/ +/test-repo/**/**/source_gen.caches/ From fd27ed0baeb72e5e63601b193db017edc2cf1ae0 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Mon, 11 Sep 2023 17:01:46 +0200 Subject: [PATCH 33/43] docs(bulk-model-sync-gradle): minor fixes --- .../pages/reference/component-bulk-model-sync-gradle.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/global/modules/core/pages/reference/component-bulk-model-sync-gradle.adoc b/docs/global/modules/core/pages/reference/component-bulk-model-sync-gradle.adoc index 199c26b3fd..20379321d6 100644 --- a/docs/global/modules/core/pages/reference/component-bulk-model-sync-gradle.adoc +++ b/docs/global/modules/core/pages/reference/component-bulk-model-sync-gradle.adoc @@ -15,7 +15,7 @@ A common example would be synchronizing a local MPS project to a `model-server`. .Bulk Synchronization between MPS and model-server image::bulk-model-sync-gradle.overview.png[Bulk Synchronization between MPS and model-server] -It allows the definition of sync directions by specifying source and target endpoint. +The plugin allows the definition of sync directions inside of a `modelSync` block by specifying source and target endpoint. Based on these directions, Gradle tasks will be generated, which can be run to trigger the corresponding synchronization. Internally, the node data will be bulk exported from the source endpoint and stored as JSON files. @@ -140,4 +140,4 @@ modelSync { } -- -Generated gradle task to perform synchronization: `runSyncPushToMyServer`. +Generated Gradle task to perform synchronization: `runSyncPushToMyServer`. From 7850727a3d5e5e5eda54f364df799fda9c647511 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Mon, 11 Sep 2023 17:10:52 +0200 Subject: [PATCH 34/43] refactor(bulk-model-sync-gradle): fix typo in SyncEndpoint --- .../sync/bulk/gradle/config/ModelSyncGradleSettings.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt index 1dbe08ddcc..fca8ecc1f2 100644 --- a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt @@ -37,8 +37,8 @@ open class ModelSyncGradleSettings { data class SyncDirection( internal val name: String, - internal var source: SyncEndPoint? = null, - internal var target: SyncEndPoint? = null, + internal var source: SyncEndpoint? = null, + internal var target: SyncEndpoint? = null, internal val includedModules: Set = mutableSetOf(), internal val registeredLanguages: Set = mutableSetOf(), ) { @@ -75,11 +75,11 @@ data class SyncDirection( } } -interface SyncEndPoint { +interface SyncEndpoint { fun getValidationErrors(): List } -sealed interface LocalEndpoint : SyncEndPoint { +sealed interface LocalEndpoint : SyncEndpoint { var mpsHome: File? var mpsHeapSize: String var repositoryDir: File? @@ -108,7 +108,7 @@ data class LocalTarget( override var repositoryDir: File? = null, ) : LocalEndpoint -sealed interface ServerEndpoint : SyncEndPoint { +sealed interface ServerEndpoint : SyncEndpoint { var url: String? var repositoryId: String? var branchName: String? From f83ef634b6a1d12c7bcfd4ce554a75e863a083a1 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Mon, 11 Sep 2023 17:17:03 +0200 Subject: [PATCH 35/43] chore(bulk-model-sync-solution): fix inconsistent notation --- bulk-model-sync-solution/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bulk-model-sync-solution/build.gradle.kts b/bulk-model-sync-solution/build.gradle.kts index dc6104bea5..52bd9f6082 100644 --- a/bulk-model-sync-solution/build.gradle.kts +++ b/bulk-model-sync-solution/build.gradle.kts @@ -10,7 +10,7 @@ group = "org.modelix.mps" val generatorLibs: Configuration by configurations.creating dependencies { - "generatorLibs"(project(":bulk-model-sync-lib")) + generatorLibs(project(":bulk-model-sync-lib")) generatorLibs(project(":mps-model-adapters")) } From 989c77a3af108d31287e2832efcf82ccf643ebd6 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Wed, 13 Sep 2023 12:15:13 +0200 Subject: [PATCH 36/43] refactor(bulk-model-sync-gradle): deduplicate antDependencies config name --- .../model/sync/bulk/gradle/ModelSyncGradlePlugin.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt index a3432b449c..2039737c36 100644 --- a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt @@ -39,6 +39,8 @@ class ModelSyncGradlePlugin : Plugin { private lateinit var settings: ModelSyncGradleSettings + private val antDependenciesConfigName = "model-sync-ant-dependencies" + override fun apply(project: Project) { settings = project.extensions.create("modelSync", ModelSyncGradleSettings::class.java) getBaseDir(project).mkdirs() @@ -50,8 +52,9 @@ class ModelSyncGradlePlugin : Plugin { } it.settings.set(settings) } - val modelixCoreVersion = readModelixCoreVersion() ?: throw RuntimeException("modelix.core version not found") - val antDependencies = project.configurations.create("model-sync-ant-dependencies") + val modelixCoreVersion = readModelixCoreVersion() + ?: throw RuntimeException("modelix.core version not found. Try running the writeVersionFile task.") + val antDependencies = project.configurations.create(antDependenciesConfigName) project.dependencies.add(antDependencies.name, "org.apache.ant:ant-junit:1.10.12") val mpsDependencies = project.configurations.create("modelSyncMpsDependencies") @@ -146,7 +149,7 @@ class ModelSyncGradlePlugin : Plugin { return exportFromMps } - private fun Project.getAntDependencies() = configurations.getByName("model-sync-ant-dependencies") + private fun Project.getAntDependencies() = configurations.getByName(antDependenciesConfigName) private fun registerTasksForServerTarget( syncDirection: SyncDirection, From 80a78222c67086142a508bbe66997cceebfe13af Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Wed, 13 Sep 2023 13:02:38 +0200 Subject: [PATCH 37/43] fix(bulk-model-sync-lib): remove restriction on file extension --- .../kotlin/org/modelix/model/sync/bulk/PlatformSpecific.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/bulk-model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/bulk/PlatformSpecific.kt b/bulk-model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/bulk/PlatformSpecific.kt index 51c2bc5d08..a218fb68ad 100644 --- a/bulk-model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/bulk/PlatformSpecific.kt +++ b/bulk-model-sync-lib/src/jvmMain/kotlin/org/modelix/model/sync/bulk/PlatformSpecific.kt @@ -30,7 +30,6 @@ import java.io.File @JvmName("importFile") fun ModelImporter.import(jsonFile: File) { require(jsonFile.exists()) - require(jsonFile.extension == "json") val data = ModelData.fromJson(jsonFile.readText()) import(data) From 69be9bd9762b8874c34d41cb746abcafe6642e9a Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Wed, 13 Sep 2023 13:06:08 +0200 Subject: [PATCH 38/43] docs(mps-model-adapters): clarify implementation is not a stub --- .../org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt index 023ea840d9..12a3b9522b 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt @@ -111,10 +111,10 @@ data class MPSRepositoryAsNode(val repository: SRepository) : IDeprecatedNodeDef } override fun getPropertyLinks(): List { - return emptyList() + return emptyList() // A repository has no properties } override fun getReferenceLinks(): List { - return emptyList() + return emptyList() // A repository has no references } } From 7de1fbdd0c2e7ebb650054461dad5d44cba5ae79 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Wed, 13 Sep 2023 13:18:11 +0200 Subject: [PATCH 39/43] chore(mps-model-adapters): clarify exception case --- .../org/modelix/model/mpsadapters/MPSLanguageRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt index 335282c6be..82081168bd 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSLanguageRepository.kt @@ -31,7 +31,7 @@ class MPSLanguageRepository(private val repository: SRepository) : ILanguageRepo val conceptId = try { SConceptId.deserialize(uid.substring(4)) - } catch (e: Exception) { return null } ?: return null + } catch (e: NumberFormatException) { return null } ?: return null // in case the id cannot be parsed val conceptDescriptor = ConceptRegistry.getInstance().getConceptDescriptor(conceptId) From e463ff54b9cdbbe515aa6d880fc211d582ae1b3f Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Wed, 13 Sep 2023 13:33:27 +0200 Subject: [PATCH 40/43] refactor(mps-model-adapters): improve readability for client init --- .../model/sync/bulk/gradle/tasks/ExportFromModelServer.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromModelServer.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromModelServer.kt index 6869c4b4f5..1f76faceef 100644 --- a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromModelServer.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ExportFromModelServer.kt @@ -60,7 +60,12 @@ abstract class ExportFromModelServer @Inject constructor(of: ObjectFactory) : De @TaskAction fun export() { - val client = ModelClientV2PlatformSpecificBuilder().url(url.get()).build().apply { runBlocking { init() } } + val client = ModelClientV2PlatformSpecificBuilder() + .url(url.get()) + .build() + + runBlocking { client.init() } + val branch = if (revision.isPresent) { getBranchByRevision(client) } else { From ff43d7ee1dd6923c6e87f3ea537749df14a5a7e1 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Wed, 13 Sep 2023 15:49:06 +0200 Subject: [PATCH 41/43] fix(mps-model-adapters): use built-in languages instead of repository lang --- .../kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt | 7 ++++--- .../org/modelix/model/mpsadapters/MPSModuleAsNode.kt | 7 ++++--- .../org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt | 3 ++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt index a6518e5f16..803ea16f37 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelAsNode.kt @@ -14,6 +14,7 @@ package org.modelix.model.mpsadapters import org.jetbrains.mps.openapi.model.SModel +import org.modelix.model.api.BuiltinLanguages import org.modelix.model.api.IChildLink import org.modelix.model.api.IConcept import org.modelix.model.api.IConceptReference @@ -53,13 +54,13 @@ data class MPSModelAsNode(val model: SModel) : IDeprecatedNodeDefaults { } override fun getContainmentLink(): IChildLink { - return RepositoryLanguage.Module.models + return BuiltinLanguages.MPSRepositoryConcepts.Module.models } override fun getChildren(link: IChildLink): Iterable { return if (link is NullChildLink) { emptyList() - } else if (link.getUID().endsWith(RepositoryLanguage.Model.rootNodes.getUID()) || + } else if (link.getUID().endsWith(BuiltinLanguages.MPSRepositoryConcepts.Model.rootNodes.getUID()) || link.getUID().contains("rootNodes") || link.getSimpleName() == "rootNodes" ) { @@ -98,7 +99,7 @@ data class MPSModelAsNode(val model: SModel) : IDeprecatedNodeDefaults { } override fun getPropertyValue(property: IProperty): String? { - return if (property.getUID().endsWith(RepositoryLanguage.NamePropertyUID) || + return if (property.getUID().endsWith(BuiltinLanguages.jetbrains_mps_lang_core.INamedConcept.name.getUID()) || property.getUID().contains("name") || property.getSimpleName() == "name" ) { diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleAsNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleAsNode.kt index 0267308a93..57fa0032d2 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleAsNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleAsNode.kt @@ -17,6 +17,7 @@ import jetbrains.mps.project.ProjectBase import jetbrains.mps.project.ProjectManager import jetbrains.mps.smodel.MPSModuleRepository import org.jetbrains.mps.openapi.module.SModule +import org.modelix.model.api.BuiltinLanguages import org.modelix.model.api.IChildLink import org.modelix.model.api.IConcept import org.modelix.model.api.IConceptReference @@ -38,7 +39,7 @@ data class MPSModuleAsNode(val module: SModule) : IDeprecatedNodeDefaults { override val reference: INodeReference get() = NodeReference("mps-module:" + module.moduleReference.toString()) override val concept: IConcept - get() = RepositoryLanguage.Module + get() = BuiltinLanguages.MPSRepositoryConcepts.Module override val parent: INode? get() = module.repository?.let { MPSRepositoryAsNode(it) } @@ -97,12 +98,12 @@ data class MPSModuleAsNode(val module: SModule) : IDeprecatedNodeDefaults { } override fun getPropertyValue(property: IProperty): String? { - return if (property.getUID().endsWith(RepositoryLanguage.NamePropertyUID) || + return if (property.getUID().endsWith(BuiltinLanguages.jetbrains_mps_lang_core.INamedConcept.name.getUID()) || property.getUID().contains("name") || property.getSimpleName() == "name" ) { module.moduleName - } else if (property.getUID().endsWith(RepositoryLanguage.VirtualPackagePropertyUID) || + } else if (property.getUID().endsWith(BuiltinLanguages.jetbrains_mps_lang_core.BaseConcept.virtualPackage.getUID()) || property.getUID().contains("virtualPackage") || property.getSimpleName() == "virtualPackage" ) { diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt index 12a3b9522b..d86d0606c1 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt @@ -14,6 +14,7 @@ package org.modelix.model.mpsadapters import org.jetbrains.mps.openapi.module.SRepository +import org.modelix.model.api.BuiltinLanguages import org.modelix.model.api.IChildLink import org.modelix.model.api.IConcept import org.modelix.model.api.IConceptReference @@ -36,7 +37,7 @@ data class MPSRepositoryAsNode(val repository: SRepository) : IDeprecatedNodeDef override val reference: INodeReference get() = NodeReference("mps-repository") override val concept: IConcept - get() = RepositoryLanguage.Repository + get() = BuiltinLanguages.MPSRepositoryConcepts.Repository override val parent: INode? get() = null From df8227beecb5d749e5be367dcd139aaf7fe3dfcc Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Wed, 13 Sep 2023 15:57:15 +0200 Subject: [PATCH 42/43] test(bulk-model-sync-gradle): align package names --- bulk-model-sync-gradle-test/build.gradle.kts | 2 +- bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts | 2 +- .../org/modelix/model/sync/{ => bulk}/gradle/test/PullTest.kt | 2 +- .../org/modelix/model/sync/{ => bulk}/gradle/test/PushTest.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/{ => bulk}/gradle/test/PullTest.kt (95%) rename bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/{ => bulk}/gradle/test/PushTest.kt (98%) diff --git a/bulk-model-sync-gradle-test/build.gradle.kts b/bulk-model-sync-gradle-test/build.gradle.kts index 40e0fbc522..fe53146980 100644 --- a/bulk-model-sync-gradle-test/build.gradle.kts +++ b/bulk-model-sync-gradle-test/build.gradle.kts @@ -92,7 +92,7 @@ modelSync { dependsOn(resolveMps) dependsOn(copyTestRepo) direction("testPush") { - org.modelix.model.sync.gradle.test.GraphLanguagesHelper.registerAll() + org.modelix.model.sync.bulk.gradle.test.GraphLanguagesHelper.registerAll() includeModule("GraphSolution") fromLocal { mpsHome = mpsDir diff --git a/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts b/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts index 06f57fe18b..88952c9424 100644 --- a/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts +++ b/bulk-model-sync-gradle-test/graph-lang-api/build.gradle.kts @@ -49,7 +49,7 @@ metamodel { mpsHome = mpsDir kotlinDir = kotlinGenDir includeLanguage("GraphLang") - registrationHelperName = "org.modelix.model.sync.gradle.test.GraphLanguagesHelper" + registrationHelperName = "org.modelix.model.sync.bulk.gradle.test.GraphLanguagesHelper" } publishing { diff --git a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PullTest.kt similarity index 95% rename from bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt rename to bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PullTest.kt index 22b315d259..a0192a5004 100644 --- a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PullTest.kt +++ b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PullTest.kt @@ -1,4 +1,4 @@ -package org.modelix.model.sync.gradle.test +package org.modelix.model.sync.bulk.gradle.test import org.junit.jupiter.api.Test import java.io.File diff --git a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PushTest.kt similarity index 98% rename from bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt rename to bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PushTest.kt index aa713e0d2d..e784a5b7ce 100644 --- a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/gradle/test/PushTest.kt +++ b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PushTest.kt @@ -1,4 +1,4 @@ -package org.modelix.model.sync.gradle.test +package org.modelix.model.sync.bulk.gradle.test import GraphLang.L_GraphLang import GraphLang.N_Node From eba5b1d8f9da929ecd7c9224ac4cea0b35aaf660 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Wed, 13 Sep 2023 16:32:49 +0200 Subject: [PATCH 43/43] test(bulk-model-sync-gradle): use xpath based testing --- bulk-model-sync-gradle-test/build.gradle.kts | 1 + .../model/sync/bulk/gradle/test/PullTest.kt | 42 +++++++++++-------- gradle/libs.versions.toml | 1 + 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/bulk-model-sync-gradle-test/build.gradle.kts b/bulk-model-sync-gradle-test/build.gradle.kts index fe53146980..26d41379db 100644 --- a/bulk-model-sync-gradle-test/build.gradle.kts +++ b/bulk-model-sync-gradle-test/build.gradle.kts @@ -55,6 +55,7 @@ dependencies { testImplementation("org.modelix.mps:model-adapters:$modelixCoreVersion") testImplementation("org.modelix:graph-lang-api:$modelixCoreVersion") testImplementation(kotlin("test")) + testImplementation(libs.xmlunit.core) } tasks.test { diff --git a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PullTest.kt b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PullTest.kt index a0192a5004..34c7a2fb93 100644 --- a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PullTest.kt +++ b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PullTest.kt @@ -1,32 +1,38 @@ +/* + * Copyright (c) 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.modelix.model.sync.bulk.gradle.test import org.junit.jupiter.api.Test +import org.xmlunit.builder.Input +import org.xmlunit.xpath.JAXPXPathEngine import java.io.File +import kotlin.test.assertContentEquals class PullTest { @Test fun `nodes were synced to local`() { val localModel = File("build/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps").readText() + val source = Input.fromString(localModel).build() + val properties = JAXPXPathEngine().selectNodes("model/node/node[@concept='1DmExO']/property", source) - val expectedNodesRegex = """ - |\s* - |\s* - |\s* - |\s* - |\s* - |\s* - |\s* - |\s* - |\s* - |\s* - |\s* - |\s* - |\s* - |\s* - |\s* - """.trimMargin().toRegex() + val actual = properties.map { it.attributes.getNamedItem("value").nodeValue } + val expected = listOf("X", "Y", "Z", "D", "E") - assert(expectedNodesRegex.containsMatchIn(localModel)) + assertContentEquals(expected, actual) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 69131d5b2a..e6a68825b8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -83,6 +83,7 @@ postgresql = { group = "org.postgresql", name = "postgresql", version = "42.6.0" jcommander = { group = "com.beust", name = "jcommander", version = "1.82" } cucumber-java = { group = "io.cucumber", name = "cucumber-java", version = "7.14.0" } junit = { group = "junit", name = "junit", version = "4.13.2" } +xmlunit-core = { group = "org.xmlunit", name = "xmlunit-core", version = "2.9.1"} apache-cxf-sse = { group = "org.apache.cxf", name = "cxf-rt-rs-sse", version.ref = "apacheCxf" } apache-cxf-client = { group = "org.apache.cxf", name = "cxf-rt-rs-client", version.ref = "apacheCxf" }