1 /*
<lambda>null2  * Copyright (C) 2021 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.hilt.android.plugin.task
18 
19 import dagger.hilt.android.plugin.root.AggregatedElementProxyGenerator
20 import dagger.hilt.android.plugin.root.ComponentTreeDepsGenerator
21 import dagger.hilt.android.plugin.root.ProcessedRootSentinelGenerator
22 import dagger.hilt.processor.internal.root.ir.AggregatedRootIrValidator
23 import dagger.hilt.processor.internal.root.ir.ComponentTreeDepsIrCreator
24 import javax.inject.Inject
25 import org.gradle.api.DefaultTask
26 import org.gradle.api.file.ConfigurableFileCollection
27 import org.gradle.api.file.DirectoryProperty
28 import org.gradle.api.provider.Property
29 import org.gradle.api.tasks.CacheableTask
30 import org.gradle.api.tasks.Classpath
31 import org.gradle.api.tasks.Input
32 import org.gradle.api.tasks.Optional
33 import org.gradle.api.tasks.OutputDirectory
34 import org.gradle.api.tasks.TaskAction
35 import org.gradle.work.InputChanges
36 import org.gradle.workers.WorkAction
37 import org.gradle.workers.WorkParameters
38 import org.gradle.workers.WorkerExecutor
39 import org.objectweb.asm.Opcodes
40 import org.slf4j.LoggerFactory
41 
42 /**
43  * Aggregates Hilt component dependencies from the compile classpath and outputs Java sources
44  * with shareable component trees.
45  *
46  * The [compileClasspath] input is expected to contain jars or classes transformed by
47  * [dagger.hilt.android.plugin.util.AggregatedPackagesTransform].
48  */
49 @CacheableTask
50 abstract class AggregateDepsTask @Inject constructor(
51   private val workerExecutor: WorkerExecutor
52 ) : DefaultTask() {
53 
54   // TODO(danysantiago): Make @Incremental and try to use @CompileClasspath
55   @get:Classpath
56   abstract val compileClasspath: ConfigurableFileCollection
57 
58   @get:Input
59   @get:Optional
60   abstract val asmApiVersion: Property<Int>
61 
62   @get:OutputDirectory
63   abstract val outputDir: DirectoryProperty
64 
65   @get:Input
66   abstract val testEnvironment: Property<Boolean>
67 
68   @get:Input
69   abstract val crossCompilationRootValidationDisabled: Property<Boolean>
70 
71   @TaskAction
72   internal fun taskAction(@Suppress("UNUSED_PARAMETER") inputs: InputChanges) {
73     workerExecutor.noIsolation().submit(WorkerAction::class.java) {
74       it.compileClasspath.from(compileClasspath)
75       it.asmApiVersion.set(asmApiVersion)
76       it.outputDir.set(outputDir)
77       it.testEnvironment.set(testEnvironment)
78       it.crossCompilationRootValidationDisabled.set(crossCompilationRootValidationDisabled)
79     }
80   }
81 
82   internal interface Parameters : WorkParameters {
83     val compileClasspath: ConfigurableFileCollection
84     val asmApiVersion: Property<Int>
85     val outputDir: DirectoryProperty
86     val testEnvironment: Property<Boolean>
87     val crossCompilationRootValidationDisabled: Property<Boolean>
88   }
89 
90   abstract class WorkerAction : WorkAction<Parameters> {
91     override fun execute() {
92       // Logger is not an injectable service yet: https://github.com/gradle/gradle/issues/16991
93       val logger = LoggerFactory.getLogger(AggregateDepsTask::class.java)
94       val aggregator = Aggregator.from(
95         logger = logger,
96         asmApiVersion = parameters.asmApiVersion.getOrNull() ?: Opcodes.ASM7,
97         input = parameters.compileClasspath
98       )
99       val rootsToProcess = AggregatedRootIrValidator.rootsToProcess(
100         isCrossCompilationRootValidationDisabled =
101           parameters.crossCompilationRootValidationDisabled.get(),
102         processedRoots = aggregator.processedRoots,
103         aggregatedRoots = aggregator.aggregatedRoots
104       )
105       if (rootsToProcess.isEmpty()) {
106         return
107       }
108       val componentTrees = ComponentTreeDepsIrCreator.components(
109         isTest = parameters.testEnvironment.get(),
110         isSharedTestComponentsEnabled = true,
111         aggregatedRoots = rootsToProcess,
112         defineComponentDeps = aggregator.defineComponentDeps,
113         aliasOfDeps = aggregator.aliasOfDeps,
114         aggregatedDeps = aggregator.aggregatedDeps,
115         aggregatedUninstallModulesDeps = aggregator.uninstallModulesDeps,
116         aggregatedEarlyEntryPointDeps = aggregator.earlyEntryPointDeps,
117       )
118       ComponentTreeDepsGenerator(
119         proxies = aggregator.allAggregatedDepProxies.associate { it.value to it.fqName },
120         outputDir = parameters.outputDir.get().asFile
121       ).let { generator ->
122         componentTrees.forEach { generator.generate(it) }
123       }
124       AggregatedElementProxyGenerator(parameters.outputDir.get().asFile).let { generator ->
125         (aggregator.allAggregatedDepProxies - aggregator.aggregatedDepProxies).forEach {
126           generator.generate(it)
127         }
128       }
129       ProcessedRootSentinelGenerator(parameters.outputDir.get().asFile).let { generator ->
130         rootsToProcess.map { it.root }.forEach { generator.generate(it) }
131       }
132     }
133   }
134 }
135