LicensingPlugin.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.licensing
019 
020 import com.hierynomus.gradle.license.LicenseReportingPlugin
021 import com.hierynomus.gradle.license.tasks.LicenseCheck
022 import com.hierynomus.gradle.license.tasks.LicenseFormat
023 import groovy.transform.CompileDynamic
024 import groovy.transform.CompileStatic
025 import nl.javadude.gradle.plugins.license.DownloadLicenses
026 import nl.javadude.gradle.plugins.license.DownloadLicensesExtension
027 import nl.javadude.gradle.plugins.license.LicenseExtension
028 import nl.javadude.gradle.plugins.license.LicenseMetadata
029 import org.gradle.BuildAdapter
030 import org.gradle.api.Action
031 import org.gradle.api.Project
032 import org.gradle.api.invocation.Gradle
033 import org.gradle.api.tasks.TaskProvider
034 import org.kordamp.gradle.plugin.AbstractKordampPlugin
035 import org.kordamp.gradle.plugin.base.BasePlugin
036 import org.kordamp.gradle.plugin.base.ProjectConfigurationExtension
037 import org.kordamp.gradle.plugin.base.model.License
038 import org.kordamp.gradle.plugin.base.model.LicenseId
039 
040 import static org.kordamp.gradle.PluginUtils.resolveEffectiveConfig
041 import static org.kordamp.gradle.plugin.base.BasePlugin.isRootProject
042 
043 /**
044  @author Andres Almiray
045  @since 0.2.0
046  */
047 @CompileStatic
048 class LicensingPlugin extends AbstractKordampPlugin {
049     private static final LicenseMetadata LICENSE_APACHE_TWO = new LicenseMetadata('The Apache Software License, Version 2.0', LicenseId.APACHE_2_0.url())
050     private static final LicenseMetadata LICENSE_EPL1 = new LicenseMetadata('Eclipse Public License v1.0', LicenseId.EPL_1_0.url())
051     private static final LicenseMetadata LICENSE_EPL2 = new LicenseMetadata('Eclipse Public License v2.0', LicenseId.EPL_2_0.url())
052     private static final LicenseMetadata LICENSE_LGPL21 = new LicenseMetadata('GNU Lesser General Public License v2.1 or later', LicenseId.LGPL_2_1_OR_LATER.url())
053     private static final LicenseMetadata LICENSE_MIT = new LicenseMetadata('MIT License', LicenseId.MIT.url())
054     private static final LicenseMetadata LICENSE_BSD2 = new LicenseMetadata('BS2-Clause FreeBSD License', LicenseId.BSD_2_CLAUSE_FREEBSD.url())
055     private static final LicenseMetadata LICENSE_BSD3 = new LicenseMetadata('BS3-Clause "New" or "Revised" License', LicenseId.BSD_3_CLAUSE.url())
056 
057     private static final Map<LicenseId, LicenseMetadata> LICENSES_MAP = [
058         (LicenseId.APACHE_2_0)          : LICENSE_APACHE_TWO,
059         (LicenseId.EPL_1_0)             : LICENSE_EPL1,
060         (LicenseId.EPL_2_0)             : LICENSE_EPL2,
061         (LicenseId.LGPL_2_1_OR_LATER)   : LICENSE_LGPL21,
062         (LicenseId.MIT)                 : LICENSE_MIT,
063         (LicenseId.BSD_2_CLAUSE_FREEBSD): LICENSE_BSD2,
064         (LicenseId.BSD_3_CLAUSE)        : LICENSE_BSD3
065     ]
066 
067     private static final Map<LicenseMetadata, List<String>> DEFAULT_ALIASES = [
068         (LICENSE_APACHE_TWO)['The Apache Software License, Version 2.0',
069                                'The Apache Software License, version 2.0',
070                                'Apache Software License - Version 2.0',
071                                'Apache Software License - version 2.0',
072                                'the Apache License, ASL Version 2.0',
073                                'The Apache License, Version 2.0',
074                                'The Apache License Version 2.0',
075                                'Apache License, Version 2.0',
076                                'Apache License, version 2.0',
077                                'Apache License Version 2.0',
078                                'Apache License version 2.0',
079                                'The Apache License 2.0',
080                                'Apache 2.0 License',
081                                'Apache License 2.0',
082                                'Apache 2.0',
083                                'Apache-2.0',
084                                'Apache 2'],
085         (LICENSE_EPL1)      ['Eclipse Public License - Version 1.0',
086                                'Eclipse Public License v1.0',
087                                'Eclipse Public License 1.0',
088                                'Eclipse Public License',
089                                'EPL v1.0',
090                                'EP1.0',
091                                'EPL-1.0'],
092         (LICENSE_EPL2)      ['Eclipse Public License v2.0',
093                                'Eclipse Public License 2.0',
094                                'EPL v2.0',
095                                'EP2.0',
096                                'EPL-2.0'],
097         (LICENSE_LGPL21)    ['GNU Library General Public License v2.1 or later',
098                                'GNU Lesser General Public License v2.1 or later',
099                                'GNU Lesser General Public License, Version 2.1',
100                                'LGPL 2.1',
101                                'LGPL-2.1'],
102         (LICENSE_MIT)       ['The MIT License',
103                                'The MIT license',
104                                'MIT License',
105                                'MIT license',
106                                'MIT',],
107         (LICENSE_BSD2)      ['BS2-Clause FreeBSD License',
108                                'The BSD License',
109                                'The BSD license'],
110         (LICENSE_BSD3)      ['BS3-Clause "New" or "Revised" License',
111                                '3-Clause BSD License',
112                                '3-Clause BSD license',
113                                'Revised BSD License',
114                                'Revised BSD license',
115                                'BSD Revised License',
116                                'BSD Revised license',
117                                'New BSD License',
118                                'New BSD license',
119                                'BSD New License',
120                                'BSD New license',
121                                'BS3-Clause',
122                                'BS3-clause']
123     ]
124 
125     Project project
126 
127     void apply(Project project) {
128         this.project = project
129 
130         if (isRootProject(project)) {
131             if (project.childProjects.size()) {
132                 project.childProjects.values().each {
133                     configureProject(it)
134                 }
135             }
136             configureProject(project)
137         else {
138             configureProject(project)
139         }
140     }
141 
142     static void applyIfMissing(Project project) {
143         if (!project.plugins.findPlugin(LicensingPlugin)) {
144             project.pluginManager.apply(LicensingPlugin)
145         }
146     }
147 
148     private void configureProject(Project project) {
149         if (hasBeenVisited(project)) {
150             return
151         }
152         setVisited(project, true)
153 
154         BasePlugin.applyIfMissing(project)
155 
156         if (!project.plugins.findPlugin(LicenseReportingPlugin)) {
157             project.pluginManager.apply(LicenseReportingPlugin)
158         }
159         if (!project.plugins.findPlugin(nl.javadude.gradle.plugins.license.LicensePlugin)) {
160             project.pluginManager.apply(nl.javadude.gradle.plugins.license.LicensePlugin)
161         }
162 
163         preConfigureDownloadLicensesExtension(project)
164 
165         TaskProvider<LicenseCheck> licenseGradle = project.tasks.register('licenseGradle', LicenseCheck, new Action<LicenseCheck>() {
166             @Override
167             @CompileDynamic
168             void execute(LicenseCheck t) {
169                 t.description = 'Scanning license on Gradle files'
170                 t.source = project.fileTree(project.projectDir) {
171                     include('**/*.gradle')
172                     include('**/*.gradle.kts')
173                 }
174             }
175         })
176         project.tasks.findByName('license').dependsOn(licenseGradle)
177 
178         TaskProvider<LicenseFormat> licenseFormatGradle = project.tasks.register('licenseFormatGradle', LicenseFormat, new Action<LicenseFormat>() {
179             @Override
180             @CompileDynamic
181             void execute(LicenseFormat t) {
182                 t.description = 'Scanning license on Gradle files'
183                 t.source = project.fileTree(project.projectDir) {
184                     include('**/*.gradle')
185                     include('**/*.gradle.kts')
186                 }
187             }
188         })
189         project.tasks.findByName('licenseFormat').dependsOn(licenseFormatGradle)
190 
191         project.afterEvaluate {
192             configureLicenseExtension(project)
193             postConfigureDownloadLicensesExtension(project)
194         }
195 
196         if (isRootProject(project&& !project.childProjects.isEmpty()) {
197             TaskProvider<AggregateLicenseReportTask> task = project.tasks.register('aggregateLicenseReport', AggregateLicenseReportTask,
198                 new Action<AggregateLicenseReportTask>() {
199                     @Override
200                     void execute(AggregateLicenseReportTask t) {
201                         t.enabled = false
202                         t.group = 'Reporting'
203                         t.description = 'Generates an aggregate license report.'
204                     }
205                 })
206 
207             project.gradle.addBuildListener(new BuildAdapter() {
208                 @Override
209                 void projectsEvaluated(Gradle gradle) {
210                     configureAggregateLicenseReportTask(project, task)
211                 }
212             })
213         }
214     }
215 
216     @CompileDynamic
217     private void configureLicenseExtension(Project project) {
218         ProjectConfigurationExtension effectiveConfig = resolveEffectiveConfig(project)
219         setEnabled(effectiveConfig.licensing.enabled)
220 
221         if (!enabled || effectiveConfig.licensing.empty) {
222             project.tasks.withType(LicenseCheck).each it.enabled = false }
223             project.tasks.withType(LicenseFormat).each it.enabled = false }
224             return
225         }
226 
227         License lic = effectiveConfig.licensing.allLicenses()[0]
228         if (effectiveConfig.licensing.allLicenses().size() 1) {
229             lic = effectiveConfig.licensing.allLicenses().find {
230                 it.primary
231             ?: effectiveConfig.licensing.allLicenses()[0]
232         }
233 
234         LicenseExtension licenseExtension = project.extensions.findByType(LicenseExtension)
235 
236         licenseExtension.header = project.rootProject.file('gradle/LICENSE_HEADER')
237         licenseExtension.strictCheck = true
238         licenseExtension.mapping {
239             java = 'SLASHSTAR_STYLE'
240             groovy = 'SLASHSTAR_STYLE'
241             kt = 'SLASHSTAR_STYLE'
242             scala = 'SLASHSTAR_STYLE'
243             gradle = 'SLASHSTAR_STYLE'
244             kts = 'SLASHSTAR_STYLE'
245         }
246         licenseExtension.ext.project = project.name
247         licenseExtension.ext {
248             projectName = effectiveConfig.info.name
249             copyrightYear = effectiveConfig.info.copyrightYear
250             author = effectiveConfig.info.getAuthors().join(', ')
251             license = lic.licenseId?.spdx()
252         }
253         licenseExtension.exclude '**/*.png'
254         licenseExtension.exclude '**/*.gif'
255         licenseExtension.exclude '**/*.jpg'
256         licenseExtension.exclude '**/*.jpeg'
257         licenseExtension.exclude 'META-INF/services/*'
258     }
259 
260     private void preConfigureDownloadLicensesExtension(Project project) {
261         DownloadLicensesExtension extension = project.extensions.findByType(DownloadLicensesExtension)
262         extension.aliases = new LinkedHashMap<>(DEFAULT_ALIASES)
263     }
264 
265     @CompileDynamic
266     private void postConfigureDownloadLicensesExtension(Project project) {
267         ProjectConfigurationExtension effectiveConfig = resolveEffectiveConfig(project)
268 
269         Map<Object, List<Object>> defaultAliases = new LinkedHashMap<>(DEFAULT_ALIASES)
270         effectiveConfig.licensing.licenses.licenses.each { license ->
271             if (license.licenseId && license.aliases) {
272                 LicenseMetadata licenseMetadata = LICENSES_MAP.get(license.licenseId)
273                 if (!licenseMetadata) {
274                     licenseMetadata = new LicenseMetadata(license.name, license.url)
275                     LICENSES_MAP.put(license.licenseId, licenseMetadata)
276                 }
277 
278                 List<Object> aliases = defaultAliases.get(licenseMetadata)
279                 if (!aliases) {
280                     aliases = []
281                     defaultAliases.put(licenseMetadata, aliases)
282                 }
283                 List<String> combined = aliases + license.aliases
284                 combined = combined.unique() - aliases
285                 aliases.addAll(combined)
286             }
287         }
288 
289         DownloadLicensesExtension extension = project.extensions.findByType(DownloadLicensesExtension)
290         extension.aliases = new LinkedHashMap<>(defaultAliases)
291 
292         project.tasks.withType(DownloadLicenses) { DownloadLicenses task ->
293             task.aliases = defaultAliases
294         }
295     }
296 
297     private void configureAggregateLicenseReportTask(Project project, TaskProvider<AggregateLicenseReportTask> task) {
298         ProjectConfigurationExtension effectiveConfig = resolveEffectiveConfig(project)
299         if (!effectiveConfig.licensing.enabled) {
300             return
301         }
302 
303         Set<DownloadLicenses> tasks = new LinkedHashSet<>()
304         project.subprojects.each { prj ->
305             tasks.addAll(prj.tasks.withType(DownloadLicenses))
306         }
307 
308         task.configure(new Action<AggregateLicenseReportTask>() {
309             @Override
310             void execute(AggregateLicenseReportTask t) {
311                 t.dependsOn tasks
312                 t.enabled = true
313             }
314         })
315     }
316 }