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  *     https://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.tasks.TaskProvider
030 import org.gradle.api.tasks.bundling.Jar
031 import org.kordamp.gradle.plugin.AbstractKordampPlugin
032 import org.kordamp.gradle.plugin.base.BasePlugin
033 import org.kordamp.gradle.plugin.base.ProjectConfigurationExtension
034 import org.kordamp.gradle.plugin.base.plugins.SourceXref
035 
036 import static org.kordamp.gradle.PluginUtils.resolveEffectiveConfig
037 import static org.kordamp.gradle.StringUtils.isNotBlank
038 import static org.kordamp.gradle.plugin.base.BasePlugin.isRootProject
039 
040 /**
041  @author Andres Almiray
042  @since 0.7.0
043  */
044 @CompileStatic
045 class SourceXrefPlugin extends AbstractKordampPlugin {
046     static final String SOURCE_XREF_TASK_NAME = 'sourceXref'
047     static final String AGGREGATE_SOURCE_XREF_TASK_NAME = 'aggregateSourceXref'
048 
049     Project project
050 
051     void apply(Project project) {
052         this.project = project
053 
054         if (isRootProject(project)) {
055             project.childProjects.values().each {
056                 configureProject(it)
057             }
058         }
059         configureProject(project)
060     }
061 
062     static void applyIfMissing(Project project) {
063         if (!project.plugins.findPlugin(SourceXrefPlugin)) {
064             project.pluginManager.apply(SourceXrefPlugin)
065         }
066     }
067 
068     private void configureProject(Project project) {
069         if (hasBeenVisited(project)) {
070             return
071         }
072         setVisited(project, true)
073 
074         BasePlugin.applyIfMissing(project)
075 
076         project.afterEvaluate {
077             ProjectConfigurationExtension effectiveConfig = resolveEffectiveConfig(project)
078 
079             if (effectiveConfig.sourceXref.enabled) {
080                 project.pluginManager.withPlugin('java-base') {
081                     TaskProvider<? extends Task> xrefTask = configureSourceXrefTask(project)
082                     if (xrefTask?.get()?.enabled) {
083                         effectiveConfig.sourceXref.projects() << project
084                         effectiveConfig.sourceXref.xrefTasks() << xrefTask
085                     else {
086                         effectiveConfig.sourceXref.enabled = false
087                     }
088                 }
089             }
090             setEnabled(effectiveConfig.sourceXref.enabled)
091         }
092 
093         if (isRootProject(project&& !project.childProjects.isEmpty()) {
094             TaskProvider<JxrTask> jxrTask = project.tasks.register(AGGREGATE_SOURCE_XREF_TASK_NAME, JxrTask,
095                 new Action<JxrTask>() {
096                     @Override
097                     void execute(JxrTask t) {
098                         t.group = 'Documentation'
099                         t.description = 'Generates an aggregate JXR report of the source code.'
100                         t.outputDirectory = project.file("${project.buildDir}/docs/source-xref")
101                         t.enabled = false
102                     }
103                 })
104 
105             TaskProvider<Jar> jxrJarTask = project.tasks.register(AGGREGATE_SOURCE_XREF_TASK_NAME + 'Jar', Jar,
106                 new Action<Jar>() {
107                     @Override
108                     void execute(Jar t) {
109                         t.dependsOn jxrTask
110                         t.group = 'Documentation'
111                         t.description = 'An archive of the JXR report the source code.'
112                         t.classifier = 'sources-jxr'
113                         t.from jxrTask.get().outputDirectory
114                         t.enabled = false
115                     }
116                 })
117 
118             project.gradle.addBuildListener(new BuildAdapter() {
119                 @Override
120                 void projectsEvaluated(Gradle gradle) {
121                     configureAggregateSourceXrefTask(project, jxrTask, jxrJarTask)
122                 }
123             })
124         }
125     }
126 
127     private TaskProvider<? extends Task> configureSourceXrefTask(Project project) {
128         ProjectConfigurationExtension effectiveConfig = resolveEffectiveConfig(project)
129         if (!effectiveConfig.sourceXref.enabled) {
130             return
131         }
132 
133         Task classesTask = project.tasks.findByName('classes')
134         if (!classesTask) {
135             return project.tasks.register(SOURCE_XREF_TASK_NAME, DefaultTask, new Action<DefaultTask>() {
136                 @Override
137                 void execute(DefaultTask t) {
138                     t.enabled = false
139                     t.group = 'Documentation'
140                     t.description = 'Generates a JXR report of the source code.'
141                 }
142             })
143         }
144 
145         TaskProvider<JxrTask> jxrTask = project.tasks.register(SOURCE_XREF_TASK_NAME, JxrTask,
146             new Action<JxrTask>() {
147                 @Override
148                 void execute(JxrTask t) {
149                     t.group = 'Documentation'
150                     t.description = 'Generates a JXR report of the source code.'
151                     t.outputDirectory = project.file("${project.buildDir}/docs/source-xref")
152                     t.sourceDirs = resolveSrcDirs(project)
153                 }
154             })
155 
156         configureTask(effectiveConfig.sourceXref, jxrTask)
157 
158         project.tasks.register(SOURCE_XREF_TASK_NAME + 'Jar', Jar,
159             new Action<Jar>() {
160                 @Override
161                 void execute(Jar t) {
162                     t.dependsOn jxrTask
163                     t.group = 'Documentation'
164                     t.description = 'An archive of the JXR report the source code.'
165                     t.classifier = 'sources-jxr'
166                     t.from jxrTask.get().outputDirectory
167                 }
168             })
169 
170         jxrTask
171     }
172 
173     private void configureAggregateSourceXrefTask(Project project, TaskProvider<JxrTask> jxrTask, TaskProvider<Jar> jxrJarTask) {
174         ProjectConfigurationExtension effectiveConfig = resolveEffectiveConfig(project)
175 
176         Set<Project> projects = new LinkedHashSet<>()
177         Set<TaskProvider<? extends Task>> xrefTasks = new LinkedHashSet<>()
178         FileCollection srcdirs = project.files()
179 
180         project.childProjects.values().each {
181             SourceXref e = resolveEffectiveConfig(it).sourceXref
182             if (!e.enabled || effectiveConfig.sourceXref.excludedProjects().intersect(e.projects())) return
183             projects.addAll(e.projects())
184             xrefTasks.addAll(e.xrefTasks())
185             srcdirs = project.files(srcdirs, e.xrefTasks().collect { ((JxrTaskit.get()).sourceDirs })
186         }
187 
188         jxrTask.configure(new Action<JxrTask>() {
189             @Override
190             void execute(JxrTask t) {
191                 t.dependsOn xrefTasks
192                 t.sourceDirs = srcdirs
193                 t.enabled = true
194             }
195         })
196         configureTask(effectiveConfig.sourceXref, jxrTask)
197 
198         jxrJarTask.configure(new Action<Jar>() {
199             @Override
200             void execute(Jar t) {
201                 t.enabled = true
202             }
203         })
204     }
205 
206     private TaskProvider<JxrTask> configureTask(SourceXref sourceXref, TaskProvider<JxrTask> jxrTask) {
207         jxrTask.configure(new Action<JxrTask>() {
208             @Override
209             void execute(JxrTask t) {
210                 if (isNotBlank(sourceXref.templateDir)) t.templateDir = sourceXref.templateDir
211                 if (isNotBlank(sourceXref.inputEncoding)) t.inputEncoding = sourceXref.inputEncoding
212                 if (isNotBlank(sourceXref.outputEncoding)) t.outputEncoding = sourceXref.outputEncoding
213                 if (isNotBlank(sourceXref.windowTitle)) t.windowTitle = sourceXref.windowTitle
214                 if (isNotBlank(sourceXref.docTitle)) t.docTitle = sourceXref.docTitle
215                 if (isNotBlank(sourceXref.bottom)) t.bottom = sourceXref.bottom
216                 if (isNotBlank(sourceXref.stylesheet)) t.stylesheet = sourceXref.stylesheet
217                 if (sourceXref.javaVersiont.javaVersion = sourceXref.javaVersion
218                 if (sourceXref.excludest.excludes.addAll(sourceXref.excludes)
219                 if (sourceXref.includest.includes.addAll(sourceXref.includes)
220             }
221         })
222 
223 
224         jxrTask
225     }
226 
227     private boolean hasJavaPlugin(Project project) {
228         project.pluginManager.hasPlugin('java-base')
229     }
230 
231     private boolean hasGroovyPlugin(Project project) {
232         project.pluginManager.hasPlugin('groovy-base')
233     }
234 
235     @CompileDynamic
236     private FileCollection resolveSrcDirs(Project project) {
237         try {
238             if (project.sourceSets.main) {
239                 if (hasGroovyPlugin(project)) {
240                     return project.files(project.files(
241                         project.sourceSets.main.groovy.srcDirs,
242                         project.sourceSets.main.java.srcDirs).files.findAll file ->
243                         file.exists()
244                     })
245                 else if (hasJavaPlugin(project)) {
246                     return project.files(
247                         project.files(project.sourceSets.main.java.srcDirs).files.findAll file ->
248                             file.exists()
249                         })
250                 }
251             }
252         catch (Exception ignored) {
253             // ignore this project
254             return project.files()
255         }
256     }
257 }