1c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar/* 2c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * Copyright 2018 The Android Open Source Project 3c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * 4c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 5c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * you may not use this file except in compliance with the License. 6c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * You may obtain a copy of the License at 7c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * 8c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 9c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * 10c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * Unless required by applicable law or agreed to in writing, software 11c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 12c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * See the License for the specific language governing permissions and 14c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * limitations under the License. 15c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar */ 16526389b5eb93f99eaf4dba0b0c75b0b7df9a0f65Aurimas Liutikaspackage androidx.build.license 17c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar 18c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyarimport org.gradle.api.DefaultTask 19c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyarimport org.gradle.api.GradleException 20c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyarimport org.gradle.api.Project 21c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyarimport org.gradle.api.artifacts.ExternalDependency 22c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyarimport org.gradle.api.plugins.ExtraPropertiesExtension 23c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyarimport org.gradle.api.tasks.TaskAction 24c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyarimport java.io.File 25c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar 26c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar/** 27c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * This task creates a configuration for the project that has all of its external dependencies 28c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * and then ensures that those dependencies: 29c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * a) come from prebuilts 30c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar * b) has a license file. 31c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar */ 32c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyaropen class CheckExternalDependencyLicensesTask : DefaultTask() { 33c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar @Suppress("unused") 34c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar @TaskAction 35c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar fun checkDependencies() { 36c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar val supportRoot = (project.rootProject.property("ext") as ExtraPropertiesExtension) 37c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar .get("supportRootFolder") as File 38c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar val prebuiltsRoot = File(supportRoot, "../../prebuilts").canonicalFile 39c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar 40c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar val checkerConfig = project.configurations.getByName(CONFIG) 41c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar 42c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar project 43c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar .configurations 44c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar .flatMap { 45c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar it.allDependencies 46c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar .filterIsInstance(ExternalDependency::class.java) 47c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar .filterNot { 48c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar it.group?.startsWith("com.android") == true 49c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 50c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar .filterNot { 51c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar it.group?.startsWith("android.arch") == true 52c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 53bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viverette .filterNot { 54bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viverette it.group?.startsWith("androidx") == true 55bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viverette } 56c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 57c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar .forEach { 58c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar checkerConfig.dependencies.add(it) 59c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 60c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar val missingLicenses = checkerConfig.resolve().filter { 61c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar findLicenseFile(it.canonicalFile, prebuiltsRoot) == null 62c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 63c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar if (missingLicenses.isNotEmpty()) { 64c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar val suggestions = missingLicenses.joinToString("\n") { 65c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar "$it does not have a license file. It should probably live in " + 66c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar "${it.parentFile.parentFile}" 67c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 68c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar throw GradleException(""" 69c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar Any external library referenced in the support library 70c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar build must have a LICENSE or NOTICE file next to it in the prebuilts. 71c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar The following libraries are missing it: 72c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar $suggestions 73c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar """.trimIndent()) 74c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 75c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 76c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar 77c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar private fun findLicenseFile(dependency: File, prebuiltsRoot: File): File? { 78c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar if (!dependency.absolutePath.startsWith(prebuiltsRoot.absolutePath)) { 79c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar throw GradleException("prebuilts should come from prebuilts folder. $dependency is" + 80c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar " not there") 81c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 82c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar fun recurse(folder: File): File? { 83c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar if (folder == prebuiltsRoot) { 84c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar return null 85c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 86c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar if (!folder.isDirectory) { 87c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar return recurse(folder.parentFile) 88c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 89c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar 90c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar val found = folder.listFiles().firstOrNull { 91c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar it.name.toUpperCase().startsWith("NOTICE") 92c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar || it.name.toUpperCase().startsWith("LICENSE") 93c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 94c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar return found ?: recurse(folder.parentFile) 95c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 96c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar return recurse(dependency) 97c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 98c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar 99c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar companion object { 100c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar private const val CONFIG = "allExternalDependencies" 101c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar const val ROOT_TASK_NAME = "checkExternalLicenses" 102c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar private const val PER_PROJECT_TASK_NAME = ROOT_TASK_NAME 103c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar fun configure(project: Project) { 104c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar val task = project.tasks.create(PER_PROJECT_TASK_NAME, 105c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar CheckExternalDependencyLicensesTask::class.java) 106c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar project.configurations.create(CONFIG) 107c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar val rootTask = project.rootProject.tasks.findByName(ROOT_TASK_NAME) 108c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar ?: project.rootProject.tasks.create(ROOT_TASK_NAME) 109c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar rootTask.dependsOn(task) 110c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 111c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar } 112c62cb5335923b041b7cd64cc879f9b0b889f457cYigit Boyar}