@@ -61,6 +61,12 @@ open class GenerateTask: DefaultTask() {
6161
6262 var classpath: FileCollection ? = null
6363
64+ val dirGenerator = GenerateDirSpec ()
65+
66+ fun dirGenerator (closure : Closure <Any ?>? ) {
67+ ConfigureUtil .configure(closure, dirGenerator)
68+ }
69+
6470 @Input
6571 internal var container: NamedDomainObjectContainer <GenerateSpec >? = null
6672
@@ -70,17 +76,44 @@ open class GenerateTask: DefaultTask() {
7076 container?.all { spec: GenerateSpec ->
7177 val specClasspath = spec.classpath
7278 if (specClasspath == null || specClasspath.isEmpty) {
73- generateSpec (spec, joinedLoader)
79+ generateFile (spec, joinedLoader)
7480 } else {
7581 URLClassLoader (combinedClasspath(spec.classpath)).use { classLoader ->
76- generateSpec(spec, classLoader)
82+ generateFile(spec, classLoader)
83+ }
84+ }
85+ }
86+
87+ if (dirGenerator.generator!= null ) {
88+ val outDir = if (dirGenerator.outputDir == null ) project.file(outputDir) else File (project.file(outputDir),dirGenerator.outputDir)
89+ outDir.mkdirs() // ensure the output directory exists
90+ if (dirGenerator.classpath!= null ) {
91+ URLClassLoader (combinedClasspath(dirGenerator.classpath)). use {
92+ generateDir(outDir, it)
7793 }
94+ } else {
95+ generateDir(outDir, joinedLoader)
7896 }
7997 }
98+
8099 }
81100 }
82101
83- private fun generateSpec (spec : GenerateSpec , classLoader : ClassLoader ) {
102+ private fun generateDir (outDir : File , classLoader : ClassLoader ) {
103+ val generatorClass = classLoader.loadClass(dirGenerator.generator)
104+ val m = generatorClass.getGenerateDirMethod(dirGenerator.input)
105+
106+ val generatorInst = if (Modifier .isStatic(m.modifiers)) null else generatorClass.newInstance()
107+ if (m.parameterCount== 1 ) {
108+ m.invoke(generatorInst, outDir)
109+ } else {
110+ m.invoke(generatorInst, outDir, dirGenerator.input)
111+ }
112+
113+ }
114+
115+
116+ private fun generateFile (spec : GenerateSpec , classLoader : ClassLoader ) {
84117 if (spec.output != null ) {
85118 val outFile = File (project.file(outputDir), spec.output)
86119
@@ -92,12 +125,13 @@ open class GenerateTask: DefaultTask() {
92125 if (spec.generator != null ) {
93126 val gname = spec.generator
94127 val generatorClass = classLoader.loadClass(gname)
95- val generatorInst = generatorClass.newInstance()
96128 if (! outFile.isFile) {
97129 outFile.parentFile.mkdirs()
98130 outFile.createNewFile()
99131 }
132+
100133 val m = generatorClass.getGenerateMethod(spec.input)
134+ val generatorInst = if (Modifier .isStatic(m.modifiers)) null else generatorClass.newInstance()
101135 outFile.writer().use { writer ->
102136 if (m.parameterCount == 2 ) {
103137 m.invoke(generatorInst, writer, spec.input)
@@ -120,7 +154,29 @@ open class GenerateTask: DefaultTask() {
120154 .filter { Appendable ::class .java.isAssignableFrom(it.parameterTypes[0 ]) && it.parameterTypes[0 ].isAssignableFrom(Writer ::class .java) }
121155 .filter { if (it.parameterCount== 1 ) true else it.parameterTypes[1 ].isInstance(input) }
122156 .singleOrNull() ? : throw NoSuchMethodError (" Generators must have a unique public method \" doGenerate(Appendable|Writer, " +
123- " [Object])\" where the second parameter is optional iff the input is null" )
157+ " [Object])\" where the second parameter is optional iff the input is null. If not a static method," +
158+ " the class must have a noArg constructor" )
159+ }
160+
161+ private fun Class <* >.getGenerateDirMethod (input : Any? ):Method {
162+ return methods.asSequence()
163+ .filter { it.name== " doGenerate" }
164+ .filter { Modifier .isPublic(it.modifiers) }
165+ .filter { if (input== null ) it.parameterCount in 1 .. 2 else it.parameterCount== 2 }
166+ .let {
167+ val candidates = it.toList()
168+ candidates.filter { (File ::class .java== it.parameterTypes[0 ].apply { project.logger.debug(" Rejecting $it as the first parameter is not a file" ) }) }
169+ .filter { if (it.parameterCount== 1 ) true else it.parameterTypes[1 ].isInstance(input) }
170+ .singleOrNull() ? : throw NoSuchMethodError ("""
171+ Generators must have a unique public method "doGenerate(File, [Object])"
172+ where the second parameter is optional iff the input is null. If not a static
173+ method, the class must have a noArg constructor.
174+
175+ Candidates were:
176+ ${candidates.joinToString(" \n " ) { it.toString() }}
177+ """ .trimIndent() )
178+
179+ }
124180 }
125181
126182 private fun combinedClasspath (others : FileCollection ? ): Array <out URL >? {
@@ -140,7 +196,7 @@ public const val OUTPUT_SOURCE_SET = "generatedSources"
140196public const val DEFAULT_GEN_DIR = " gen"
141197
142198interface GenerateImpl {
143- fun doGenerate (output : Writer , input : Iterable <out File >? )
199+ fun doGenerate (output : Writer , input : Iterable <File >? )
144200}
145201
146202class GenerateSpec (val name : String ) {
@@ -150,7 +206,14 @@ class GenerateSpec(val name: String) {
150206 var input: Any? = null
151207}
152208
153- open class GenerateSourceSet (val name : String , val generate : NamedDomainObjectContainer <GenerateSpec >) {
209+ class GenerateDirSpec () {
210+ var input: Any? = null
211+ var generator: String? = null
212+ var classpath: FileCollection ? = null
213+ var outputDir: String? = null
214+ }
215+
216+ open class GenerateSourceSet (val generate : NamedDomainObjectContainer <GenerateSpec >) {
154217
155218 fun generate (configureClosure : Closure <Any ?>? ): GenerateSourceSet {
156219 ConfigureUtil .configure(configureClosure, generate)
@@ -161,11 +224,10 @@ open class GenerateSourceSet(val name:String, val generate: NamedDomainObjectCon
161224class CodegenPlugin : Plugin <Project > {
162225
163226 override fun apply (project : Project ) {
164- val javaBasePlugin = project.plugins.apply (JavaBasePlugin ::class .java)
165- project.plugins.apply (JavaPlugin ::class .java)
227+ project.plugins.apply (JavaBasePlugin ::class .java)
166228
167229 val sourceSetsToSkip = mutableSetOf<String >(" generators" )
168- val sourceSets = project.sourceSets.all {sourceSet ->
230+ project.sourceSets.all {sourceSet ->
169231 if (! sourceSetsToSkip.contains(sourceSet.name)) {
170232 if (sourceSet.name.endsWith(" enerators" )) {
171233 project.logger.error(" Generators sourceSet (${sourceSet.name} ) not registered in ${sourceSetsToSkip} " )
@@ -196,7 +258,7 @@ class CodegenPlugin : Plugin<Project> {
196258
197259 val generateExt = project.container(GenerateSpec ::class .java)
198260 if (sourceSet is HasConvention ) {
199- sourceSet.convention.plugins.put(" net.devrieze.gradlecodegen" , GenerateSourceSet (" generate " , generateExt))
261+ sourceSet.convention.plugins.put(" net.devrieze.gradlecodegen" , GenerateSourceSet (generateExt))
200262 } else {
201263 project.extensions.add(generateTaskName, generateExt)
202264 }
0 commit comments