-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathSolutionProducer.kt
181 lines (154 loc) · 8.46 KB
/
SolutionProducer.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
@file:Suppress("removal")
package org.modelix.model.mpsadapters
import jetbrains.mps.extapi.persistence.FileBasedModelRoot
import jetbrains.mps.ide.project.ProjectHelper
import jetbrains.mps.persistence.DefaultModelRoot
import jetbrains.mps.project.AbstractModule
import jetbrains.mps.project.DevKit
import jetbrains.mps.project.MPSExtentions
import jetbrains.mps.project.MPSProject
import jetbrains.mps.project.ModuleId
import jetbrains.mps.project.Solution
import jetbrains.mps.project.structure.modules.DevkitDescriptor
import jetbrains.mps.project.structure.modules.GeneratorDescriptor
import jetbrains.mps.project.structure.modules.LanguageDescriptor
import jetbrains.mps.project.structure.modules.SolutionDescriptor
import jetbrains.mps.smodel.GeneralModuleFactory
import jetbrains.mps.smodel.Generator
import jetbrains.mps.smodel.Language
import jetbrains.mps.vfs.IFile
class SolutionProducer(private val myProject: MPSProject) {
fun create(name: String, id: ModuleId): Solution {
val basePath = checkNotNull(ProjectHelper.toIdeaProject(myProject).getBasePath()) { "Project has no base path: $myProject" }
val projectBaseDir = myProject.getFileSystem().getFile(basePath)
val solutionBaseDir = projectBaseDir.findChild("solutions").findChild(name)
return create(name, id, solutionBaseDir)
}
fun create(namespace: String, id: ModuleId, moduleDir: IFile): Solution {
val descriptorFile = moduleDir.findChild(namespace + MPSExtentions.DOT_SOLUTION)
val descriptor: SolutionDescriptor = createSolutionDescriptor(namespace, id, descriptorFile)
val module = GeneralModuleFactory().instantiate(descriptor, descriptorFile) as Solution
myProject.addModule(module)
module.save()
return module
}
private fun createSolutionDescriptor(namespace: String, id: ModuleId, descriptorFile: IFile): SolutionDescriptor {
val descriptor = SolutionDescriptor()
// using outputPath instead of outputRoot for backwards compatibility
// descriptor.outputRoot = "\${module}/source_gen"
descriptor.outputPath = descriptorFile.parent!!.findChild("source_gen").path
descriptor.namespace = namespace
descriptor.id = id
val moduleLocation = descriptorFile.parent
val modelsDir = moduleLocation!!.findChild(Solution.SOLUTION_MODELS)
check(!(modelsDir.exists() && modelsDir.children?.isNotEmpty() == true)) {
"Trying to create a solution in an existing solution's directory: $moduleLocation"
}
modelsDir.mkdirs()
descriptor.modelRootDescriptors.add(DefaultModelRoot.createDescriptor(modelsDir.parent!!, modelsDir))
descriptor.outputPath = descriptorFile.parent!!.findChild("source_gen").path
return descriptor
}
}
class LanguageProducer(private val myProject: MPSProject) {
fun create(name: String, id: ModuleId): Language {
val basePath = checkNotNull(ProjectHelper.toIdeaProject(myProject).getBasePath()) { "Project has no base path: $myProject" }
val projectBaseDir = myProject.getFileSystem().getFile(basePath)
val solutionBaseDir = projectBaseDir.findChild("languages").findChild(name)
return create(name, id, solutionBaseDir)
}
fun create(namespace: String, id: ModuleId, moduleDir: IFile): Language {
val descriptorFile = moduleDir.findChild(namespace + MPSExtentions.DOT_LANGUAGE)
val descriptor = createDescriptor(namespace, id, descriptorFile)
val module = GeneralModuleFactory().instantiate(descriptor, descriptorFile) as Language
myProject.addModule(module)
module.save()
return module
}
private fun createDescriptor(namespace: String, id: ModuleId, descriptorFile: IFile): LanguageDescriptor {
val descriptor = LanguageDescriptor()
// using genPath instead of outputRoot for backwards compatibility
// descriptor.outputRoot = "\${module}/source_gen"
descriptor.genPath = descriptorFile.parent!!.findChild("source_gen").path
descriptor.namespace = namespace
descriptor.id = id
val moduleLocation = descriptorFile.parent
val modelsDir = moduleLocation!!.findChild(Language.LANGUAGE_MODELS)
check(!(modelsDir.exists() && modelsDir.children?.isNotEmpty() == true)) {
"Trying to create a language in an existing language's directory: $moduleLocation"
}
modelsDir.mkdirs()
descriptor.modelRootDescriptors.add(DefaultModelRoot.createDescriptor(modelsDir.parent!!, modelsDir))
return descriptor
}
}
class GeneratorProducer(private val myProject: MPSProject) {
fun create(language: Language, name: String, id: ModuleId, alias: String?): Generator {
val basePath = checkNotNull(ProjectHelper.toIdeaProject(myProject).getBasePath()) { "Project has no base path: $myProject" }
val projectBaseDir = myProject.fileSystem.getFile(basePath)
val solutionBaseDir = projectBaseDir.findChild("languages").findChild(language.moduleName!!)
return create(language, name, id, alias, solutionBaseDir)
}
fun create(language: Language, namespace: String, id: ModuleId, alias: String?, languageModuleDir: IFile): Generator {
val siblingDirs = language.generators.mapNotNull { it.getGeneratorLocation() }.toSet()
val generatorLocation: IFile = findEmptyGeneratorDir(languageModuleDir, siblingDirs)
generatorLocation.mkdirs()
val descriptor = createDescriptor(namespace, id, alias, generatorLocation, null)
descriptor.sourceLanguage = language.moduleReference
language.moduleDescriptor.generators.add(descriptor)
language.setModuleDescriptor(language.moduleDescriptor) // instantiate generator module
language.save()
return language.generators.first { it.moduleReference.moduleId == id }
}
private fun findEmptyGeneratorDir(languageModuleDir: IFile, siblingDirs: Set<IFile>): IFile {
var folderName = "generator"
var cnt = 1
var newChild: IFile?
do {
newChild = languageModuleDir.findChild(folderName)
folderName = "generator" + cnt++
} while (siblingDirs.contains(newChild) || newChild.exists() && (!newChild.isDirectory || !newChild.children!!.isEmpty()))
return newChild
}
private fun createDescriptor(namespace: String, id: ModuleId, alias: String?, generatorModuleLocation: IFile, templateModelsLocation: IFile?): GeneratorDescriptor {
val descriptor = GeneratorDescriptor()
// using outputPath instead of outputRoot for backwards compatibility
// descriptor.outputRoot = "\${module}/${generatorModuleLocation.name}/source_gen"
descriptor.outputPath = generatorModuleLocation.findChild(AbstractModule.CLASSES_GEN).path
descriptor.namespace = namespace
descriptor.id = id
descriptor.alias = alias ?: "main"
val modelRoot = if (templateModelsLocation == null) {
DefaultModelRoot.createDescriptor(generatorModuleLocation, generatorModuleLocation.findChild("templates"))
} else {
DefaultModelRoot.createSingleFolderDescriptor(templateModelsLocation)
}
descriptor.modelRootDescriptors.add(modelRoot)
return descriptor
}
}
fun Generator.getGeneratorLocation(): IFile? {
return modelRoots.filterIsInstance<FileBasedModelRoot>().mapNotNull { it.contentDirectory }.firstOrNull()
}
class DevkitProducer(private val myProject: MPSProject) {
fun create(name: String, id: ModuleId): DevKit {
val basePath = checkNotNull(ProjectHelper.toIdeaProject(myProject).getBasePath()) { "Project has no base path: $myProject" }
val projectBaseDir = myProject.getFileSystem().getFile(basePath)
val solutionBaseDir = projectBaseDir.findChild("devkits").findChild(name)
return create(name, id, solutionBaseDir)
}
fun create(namespace: String, id: ModuleId, moduleDir: IFile): DevKit {
val descriptorFile = moduleDir.findChild(namespace + MPSExtentions.DOT_DEVKIT)
val descriptor = createDescriptor(namespace, id, descriptorFile)
val module = GeneralModuleFactory().instantiate(descriptor, descriptorFile) as DevKit
myProject.addModule(module)
module.save()
return module
}
private fun createDescriptor(namespace: String, id: ModuleId, descriptorFile: IFile): DevkitDescriptor {
val descriptor = DevkitDescriptor()
descriptor.namespace = namespace
descriptor.id = id
return descriptor
}
}