SourceXrefPlugin.groovy
001 /*
002  * SPDX-License-Identifier: Apache-2.0
003  *
004  * Copyright 2018-2019 Andres Almiray.
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     http://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 package org.kordamp.gradle.plugin.sourcexref
019 
020 import groovy.transform.CompileDynamic
021 import groovy.transform.CompileStatic
022 import org.gradle.BuildAdapter
023 import org.gradle.api.Action
024 import org.gradle.api.DefaultTask
025 import org.gradle.api.Project
026 import org.gradle.api.Task
027 import org.gradle.api.file.FileCollection
028 import org.gradle.api.invocation.Gradle
029 import org.gradle.api.plugins.GroovyBasePlugin
030 import org.gradle.api.plugins.JavaBasePlugin
031 import org.gradle.api.tasks.TaskProvider
032 import org.gradle.api.tasks.bundling.Jar
033 import org.kordamp.gradle.plugin.AbstractKordampPlugin
034 import org.kordamp.gradle.plugin.base.BasePlugin
035 import org.kordamp.gradle.plugin.base.ProjectConfigurationExtension
036 import org.kordamp.gradle.plugin.base.plugins.SourceXref
037 
038 import static org.kordamp.gradle.PluginUtils.resolveEffectiveConfig
039 import static org.kordamp.gradle.StringUtils.isNotBlank
040 import static org.kordamp.gradle.plugin.base.BasePlugin.isRootProject
041 
042 /**
043  @author Andres Almiray
044  @since 0.7.0
045  */
046 @CompileStatic
047 class SourceXrefPlugin extends AbstractKordampPlugin {
048     static final String SOURCE_XREF_TASK_NAME = 'sourceXref'
049     static final String AGGREGATE_SOURCE_XREF_TASK_NAME = 'aggregateSourceXref'
050 
051     Project project
052 
053     void apply(Project project) {
054         this.project = project
055 
056         if (isRootProject(project)) {
057             project.childProjects.values().each {
058                 configureProject(it)
059             }
060         }
061         configureProject(project)
062     }
063 
064     static void applyIfMissing(Project project) {
065         if (!project.plugins.findPlugin(SourceXrefPlugin)) {
066             project.plugins.apply(SourceXrefPlugin)
067         }
068     }
069 
070     private void configureProject(Project project) {
071         if (hasBeenVisited(project)) {
072             return
073         }
074         setVisited(project, true)
075 
076         BasePlugin.applyIfMissing(project)
077 
078         project.afterEvaluate {
079             ProjectConfigurationExtension effectiveConfig = resolveEffectiveConfig(project)
080 
081             if (effectiveConfig.sourceXref.enabled) {
082                 project.plugins.withType(JavaBasePlugin) {
083                     TaskProvider<? extends Task> xrefTask = configureSourceXrefTask(project)
084                     if (xrefTask?.get()?.enabled) {
085                         effectiveConfig.sourceXref.projects() << project
086                         effectiveConfig.sourceXref.xrefTasks() << xrefTask
087                     else {
088                         effectiveConfig.sourceXref.enabled = false
089                     }
090                 }
091             }
092             setEnabled(effectiveConfig.sourceXref.enabled)
093         }
094 
095         if (isRootProject(project&& !project.childProjects.isEmpty()) {
096             TaskProvider<JxrTask> jxrTask = project.tasks.register(AGGREGATE_SOURCE_XREF_TASK_NAME, JxrTask,
097                 new Action<JxrTask>() {
098                     @Override
099                     void execute(JxrTask t) {
100                         t.group = 'Documentation'
101                         t.description = 'Generates an aggregate JXR report of the source code.'
102                         t.outputDirectory = project.file("${project.buildDir}/docs/source-xref")
103                         t.enabled = false
104                     }
105                 })
106 
107             TaskProvider<Jar> jxrJarTask = project.tasks.register(AGGREGATE_SOURCE_XREF_TASK_NAME + 'Jar', Jar,
108                 new Action<Jar>() {
109                     @Override
110                     void execute(Jar t) {
111                         t.dependsOn jxrTask
112                         t.group = 'Documentation'
113                         t.description = 'An archive of the JXR report the source code.'
114                         t.classifier = 'sources-jxr'
115                         t.from jxrTask.get().outputDirectory
116                         t.enabled = false
117                     }
118                 })
119 
120             project.gradle.addBuildListener(new BuildAdapter() {
121                 @Override
122                 void projectsEvaluated(Gradle gradle) {
123                     configureAggregateSourceXrefTask(project, jxrTask, jxrJarTask)
124                 }
125             })
126         }
127     }
128 
129     private TaskProvider<? extends Task> configureSourceXrefTask(Project project) {
130         ProjectConfigurationExtension effectiveConfig = resolveEffectiveConfig(project)
131         if (!effectiveConfig.sourceXref.enabled) {
132             return
133         }
134 
135         Task classesTask = project.tasks.findByName('classes')
136         if (!classesTask) {
137             return project.tasks.register(SOURCE_XREF_TASK_NAME, DefaultTask, new Action<DefaultTask>() {
138                 @Override
139                 void execute(DefaultTask t) {
140                     t.enabled = false
141                     t.group = 'Documentation'
142                     t.description = 'Generates a JXR report of the source code.'
143                 }
144             })
145         }
146 
147         TaskProvider<JxrTask> jxrTask = project.tasks.register(SOURCE_XREF_TASK_NAME, JxrTask,
148             new Action<JxrTask>() {
149                 @Override
150                 void execute(JxrTask t) {
151                     t.group = 'Documentation'
152                     t.description = 'Generates a JXR report of the source code.'
153                     t.outputDirectory = project.file("${project.buildDir}/docs/source-xref")
154                     t.sourceDirs = resolveSrcDirs(project)
155                 }
156             })
157 
158         configureTask(effectiveConfig.sourceXref, jxrTask)
159 
160         project.tasks.register(SOURCE_XREF_TASK_NAME + 'Jar', Jar,
161             new Action<Jar>() {
162                 @Override
163                 void execute(Jar t) {
164                     t.dependsOn jxrTask
165                     t.group = 'Documentation'
166                     t.description = 'An archive of the JXR report the source code.'
167                     t.classifier = 'sources-jxr'
168                     t.from jxrTask.get().outputDirectory
169                 }
170             })
171 
172         jxrTask
173     }
174 
175     private void configureAggregateSourceXrefTask(Project project, TaskProvider<JxrTask> jxrTask, TaskProvider<Jar> jxrJarTask) {
176         ProjectConfigurationExtension effectiveConfig = resolveEffectiveConfig(project)
177 
178         Set<Project> projects = new LinkedHashSet<>()
179         Set<TaskProvider<? extends Task>> xrefTasks = new LinkedHashSet<>()
180         FileCollection srcdirs = project.files()
181 
182         project.childProjects.values().each {
183             SourceXref e = resolveEffectiveConfig(it).sourceXref
184             if (!e.enabled || effectiveConfig.sourceXref.excludedProjects().intersect(e.projects())) return
185             projects.addAll(e.projects())
186             xrefTasks.addAll(e.xrefTasks())
187             srcdirs = project.files(srcdirs, e.xrefTasks().collect { ((JxrTaskit.get()).sourceDirs })
188         }
189 
190         jxrTask.configure(new Action<JxrTask>() {
191             @Override
192             void execute(JxrTask t) {
193                 t.dependsOn xrefTasks
194                 t.sourceDirs = srcdirs
195                 t.enabled = true
196             }
197         })
198         configureTask(effectiveConfig.sourceXref, jxrTask)
199 
200         jxrJarTask.configure(new Action<Jar>() {
201             @Override
202             void execute(Jar t) {
203                 t.enabled = true
204             }
205         })
206     }
207 
208     private TaskProvider<JxrTask> configureTask(SourceXref sourceXref, TaskProvider<JxrTask> jxrTask) {
209         jxrTask.configure(new Action<JxrTask>() {
210             @Override
211             void execute(JxrTask t) {
212                 if (isNotBlank(sourceXref.templateDir)) t.templateDir = sourceXref.templateDir
213                 if (isNotBlank(sourceXref.inputEncoding)) t.inputEncoding = sourceXref.inputEncoding
214                 if (isNotBlank(sourceXref.outputEncoding)) t.outputEncoding = sourceXref.outputEncoding
215                 if (isNotBlank(sourceXref.windowTitle)) t.windowTitle = sourceXref.windowTitle
216                 if (isNotBlank(sourceXref.docTitle)) t.docTitle = sourceXref.docTitle
217                 if (isNotBlank(sourceXref.bottom)) t.bottom = sourceXref.bottom
218                 if (isNotBlank(sourceXref.stylesheet)) t.stylesheet = sourceXref.stylesheet
219                 if (sourceXref.javaVersiont.javaVersion = sourceXref.javaVersion
220                 if (sourceXref.excludest.excludes.addAll(sourceXref.excludes)
221                 if (sourceXref.includest.includes.addAll(sourceXref.includes)
222             }
223         })
224 
225 
226         jxrTask
227     }
228 
229     private boolean hasJavaPlugin(Project project) {
230         project.plugins.hasPlugin(JavaBasePlugin)
231     }
232 
233     private boolean hasGroovyPlugin(Project project) {
234         project.plugins.hasPlugin(GroovyBasePlugin)
235     }
236 
237     @CompileDynamic
238     private FileCollection resolveSrcDirs(Project project) {
239         try {
240             if (project.sourceSets.main) {
241                 if (hasGroovyPlugin(project)) {
242                     return project.files(project.files(
243                         project.sourceSets.main.groovy.srcDirs,
244                         project.sourceSets.main.java.srcDirs).files.findAll file ->
245                         file.exists()
246                     })
247                 else if (hasJavaPlugin(project)) {
248                     return project.files(
249                         project.files(project.sourceSets.main.java.srcDirs).files.findAll file ->
250                             file.exists()
251                         })
252                 }
253             }
254         catch (Exception ignored) {
255             // ignore this project
256             return project.files()
257         }
258     }
259 }