1d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis/*
2ba381a314edcd57963ed1ac5910595e04faf29ccFilip Pavlis * Copyright 2018 The Android Open Source Project
3d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis *
4d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis * Licensed under the Apache License, Version 2.0 (the "License");
5d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis * you may not use this file except in compliance with the License.
6d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis * You may obtain a copy of the License at
7d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis *
8ba381a314edcd57963ed1ac5910595e04faf29ccFilip Pavlis *      http://www.apache.org/licenses/LICENSE-2.0
9d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis *
10d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis * Unless required by applicable law or agreed to in writing, software
11d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis * distributed under the License is distributed on an "AS IS" BASIS,
12d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis * See the License for the specific language governing permissions and
14ba381a314edcd57963ed1ac5910595e04faf29ccFilip Pavlis * limitations under the License.
15d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis */
16d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis
17ba381a314edcd57963ed1ac5910595e04faf29ccFilip Pavlispackage com.android.tools.build.jetifier.plugin.gradle
18d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis
19ba381a314edcd57963ed1ac5910595e04faf29ccFilip Pavlisimport com.android.tools.build.jetifier.core.config.ConfigParser
20ba381a314edcd57963ed1ac5910595e04faf29ccFilip Pavlisimport com.android.tools.build.jetifier.processor.FileMapping
21d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlisimport org.gradle.api.DefaultTask
22d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlisimport org.gradle.api.Project
23d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlisimport org.gradle.api.file.FileCollection
24d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlisimport org.gradle.api.tasks.InputFiles
25d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlisimport org.gradle.api.tasks.OutputDirectory
26d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlisimport org.gradle.api.tasks.TaskAction
27d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlisimport java.io.File
28d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis
29d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis/**
30d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis * Task that processes given file collections using Jetifier.
31d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis *
32d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis * This task also utilizes Gradle caching so it's run only when needed.
33d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis *
34d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis * Example usage in Gradle:
35d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis * dependencies {
36d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis *   compile jetifier.process('groupId:artifactId:1.0')
37d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis * }
38d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis */
39d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlisopen class JetifyLibsTask : DefaultTask() {
40d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis
41d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    companion object {
42d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis        const val TASK_NAME = "jetifyLibs"
43d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis        const val GROUP_ID = "Pre-build"
44d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis        const val DESCRIPTION = "Rewrites input libraries to run with jetpack"
45d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis
46d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis        const val OUTPUT_DIR_APPENDIX = "jetifier"
47d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis
4849f7010e84f8819274924817443b26fc3a117e22Filip Pavlis        fun resolveTask(project: Project): JetifyLibsTask {
49d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis            val task = project.tasks.findByName(TASK_NAME) as? JetifyLibsTask
50d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis            if (task != null) {
51d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis                return task
52d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis            }
53d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis            return project.tasks.create(TASK_NAME, JetifyLibsTask::class.java)
54d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis        }
55d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    }
56d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis
57d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    private val outputDir = File(project.buildDir, OUTPUT_DIR_APPENDIX)
58d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis
5949f7010e84f8819274924817443b26fc3a117e22Filip Pavlis    private val filesToProcess = mutableSetOf<FileMapping>()
60d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis
61d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    override fun getGroup() = GROUP_ID
62d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis
63d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    override fun getDescription() = DESCRIPTION
64d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis
65d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    /**
66d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis     * Adds individual files collection to be processed by Jetifier.
67d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis     *
68d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis     * See [JetifierExtension] for details on how to use this.
69d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis     */
7049f7010e84f8819274924817443b26fc3a117e22Filip Pavlis    fun addFilesToProcess(files: FileCollection): FileCollection {
7149f7010e84f8819274924817443b26fc3a117e22Filip Pavlis        return project.files(files.map { addFile(it).to }.toList())
7249f7010e84f8819274924817443b26fc3a117e22Filip Pavlis    }
7349f7010e84f8819274924817443b26fc3a117e22Filip Pavlis
7449f7010e84f8819274924817443b26fc3a117e22Filip Pavlis    private fun addFile(file: File): FileMapping {
7549f7010e84f8819274924817443b26fc3a117e22Filip Pavlis        val mappingMaybe = filesToProcess.firstOrNull { it.from == file }
7649f7010e84f8819274924817443b26fc3a117e22Filip Pavlis        if (mappingMaybe != null) {
7749f7010e84f8819274924817443b26fc3a117e22Filip Pavlis            return mappingMaybe
7849f7010e84f8819274924817443b26fc3a117e22Filip Pavlis        }
7949f7010e84f8819274924817443b26fc3a117e22Filip Pavlis
8049f7010e84f8819274924817443b26fc3a117e22Filip Pavlis        val newFile = File(outputDir, file.hashCode().toString() + "_" + file.name)
8149f7010e84f8819274924817443b26fc3a117e22Filip Pavlis        val mapping = FileMapping(file, newFile)
8249f7010e84f8819274924817443b26fc3a117e22Filip Pavlis        filesToProcess.add(mapping)
8349f7010e84f8819274924817443b26fc3a117e22Filip Pavlis        return mapping
84d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    }
85d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis
86d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    /**
87d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis     * Used by Gradle to figure out whether this task should be re-run. If the result of this method
88d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis     * is different then the task is re-run.
89d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis     */
90d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    @InputFiles
9149f7010e84f8819274924817443b26fc3a117e22Filip Pavlis    fun getInputFiles(): FileCollection {
9249f7010e84f8819274924817443b26fc3a117e22Filip Pavlis        return project.files(filesToProcess.map { it.from }.toList())
93d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    }
94d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis
95d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    /**
96d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis     * Used by Gradle to figure out whether this task should be re-run and if other tasks that are
97d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis     * relying on files from this directory should be re-run. Actually not having this and only
98d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis     * having [InputFiles] annotation would disable the whole incremental mechanism for this task
99d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis     * and lead to constant re-runs.
100d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis     */
101d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    @OutputDirectory
10249f7010e84f8819274924817443b26fc3a117e22Filip Pavlis    fun getOutputDir(): File {
103d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis        return outputDir
104d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    }
105d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis
106d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    @TaskAction
107d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    @Throws(Exception::class)
108d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    fun run() {
109d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis        val config = ConfigParser.loadConfigOrFail(TasksCommon.configFilePath)
110d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis
111d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis        // Process the files using Jetifier
11249f7010e84f8819274924817443b26fc3a117e22Filip Pavlis        TasksCommon.processFiles(config, filesToProcess, project.logger)
113d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis    }
114d7b0788f3bf58fc26a936c2426ef1214ffb3a180Filip Pavlis}