/
JavaPlugin.java
502 lines (425 loc) · 22.3 KB
/
JavaPlugin.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.api.plugins;
import org.gradle.api.Action;
import org.gradle.api.Incubating;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.artifacts.ConfigurationPublications;
import org.gradle.api.artifacts.ConfigurationVariant;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.type.ArtifactTypeDefinition;
import org.gradle.api.attributes.Usage;
import org.gradle.api.file.FileCollection;
import org.gradle.api.internal.artifacts.ArtifactAttributes;
import org.gradle.api.internal.artifacts.publish.AbstractPublishArtifact;
import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact;
import org.gradle.api.internal.component.BuildableJavaComponent;
import org.gradle.api.internal.component.ComponentRegistry;
import org.gradle.api.internal.java.JavaLibrary;
import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet;
import org.gradle.api.internal.project.ProjectInternal;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.bundling.Jar;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.api.tasks.javadoc.Javadoc;
import org.gradle.api.tasks.testing.Test;
import org.gradle.internal.cleanup.BuildOutputCleanupRegistry;
import org.gradle.language.jvm.tasks.ProcessResources;
import javax.annotation.Nullable;
import javax.inject.Inject;
import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.concurrent.Callable;
import static org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE;
/**
* <p>A {@link Plugin} which compiles and tests Java source, and assembles it into a JAR file.</p>
*/
public class JavaPlugin implements Plugin<ProjectInternal> {
/**
* The name of the task that processes resources.
*/
public static final String PROCESS_RESOURCES_TASK_NAME = "processResources";
/**
* The name of the lifecycle task which outcome is that all the classes of a component are generated.
*/
public static final String CLASSES_TASK_NAME = "classes";
/**
* The name of the task which compiles Java sources.
*/
public static final String COMPILE_JAVA_TASK_NAME = "compileJava";
/**
* The name of the task which processes the test resources.
*/
public static final String PROCESS_TEST_RESOURCES_TASK_NAME = "processTestResources";
/**
* The name of the lifecycle task which outcome is that all test classes of a component are generated.
*/
public static final String TEST_CLASSES_TASK_NAME = "testClasses";
/**
* The name of the task which compiles the test Java sources.
*/
public static final String COMPILE_TEST_JAVA_TASK_NAME = "compileTestJava";
/**
* The name of the task which triggers execution of tests.
*/
public static final String TEST_TASK_NAME = "test";
/**
* The name of the task which generates the component main jar.
*/
public static final String JAR_TASK_NAME = "jar";
/**
* The name of the task which generates the component javadoc.
*/
public static final String JAVADOC_TASK_NAME = "javadoc";
/**
* The name of the API configuration, where dependencies exported by a component at compile time should
* be declared.
* @since 3.4
*/
@Incubating
public static final String API_CONFIGURATION_NAME = "api";
/**
* The name of the implementation configuration, where dependencies that are only used internally by
* a component should be declared.
* @since 3.4
*/
@Incubating
public static final String IMPLEMENTATION_CONFIGURATION_NAME = "implementation";
/**
* The name of the configuration used by consumers to get the API elements of a component, that is to say
* the dependencies which are required to compile against that component.
*
* @since 3.4
*/
@Incubating
public static final String API_ELEMENTS_CONFIGURATION_NAME = "apiElements";
/**
* The name of the configuration that is used to declare API or implementation dependencies. This configuration
* is deprecated.
*
* @deprecated Users should prefer {@link #API_CONFIGURATION_NAME} or {@link #IMPLEMENTATION_CONFIGURATION_NAME}.
*/
public static final String COMPILE_CONFIGURATION_NAME = "compile";
/**
* The name of the configuration that is used to declare dependencies which are only required to compile a component,
* but not at runtime.
*/
public static final String COMPILE_ONLY_CONFIGURATION_NAME = "compileOnly";
/**
* The name of the "runtime" configuration. This configuration is deprecated and doesn't represent a correct view of
* the runtime dependencies of a component.
*
* @deprecated Consumers should use {@link #RUNTIME_ELEMENTS_CONFIGURATION_NAME} instead.
*/
public static final String RUNTIME_CONFIGURATION_NAME = "runtime";
/**
* The name of the runtime only dependencies configuration, used to declare dependencies
* that should only be found at runtime.
* @since 3.4
*/
@Incubating
public static final String RUNTIME_ONLY_CONFIGURATION_NAME = "runtimeOnly";
/**
* The name of the runtime classpath configuration, used by a component to query its own runtime classpath.
* @since 3.4
*/
@Incubating
public static final String RUNTIME_CLASSPATH_CONFIGURATION_NAME = "runtimeClasspath";
/**
* The name of the runtime elements configuration, that should be used by consumers
* to query the runtime dependencies of a component.
* @since 3.4
*/
@Incubating
public static final String RUNTIME_ELEMENTS_CONFIGURATION_NAME = "runtimeElements";
/**
* The name of the compile classpath configuration.
* @since 3.4
*/
@Incubating
public static final String COMPILE_CLASSPATH_CONFIGURATION_NAME = "compileClasspath";
public static final String TEST_COMPILE_CONFIGURATION_NAME = "testCompile";
/**
* The name of the test implementation dependencies configuration.
* @since 3.4
*/
@Incubating
public static final String TEST_IMPLEMENTATION_CONFIGURATION_NAME = "testImplementation";
/**
* The name of the configuration that should be used to declare dependencies which are only required
* to compile the tests, but not when running them.
*/
public static final String TEST_COMPILE_ONLY_CONFIGURATION_NAME = "testCompileOnly";
/**
* The name of the configuration that represents the component runtime classpath. This configuration doesn't
* represent the exact runtime dependencies and therefore is deprecated.
*
* @deprecated Use {@link #TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME} instead.
*/
public static final String TEST_RUNTIME_CONFIGURATION_NAME = "testRuntime";
/**
* The name of the test runtime only dependencies configuration.
* @since 3.4
*/
@Incubating
public static final String TEST_RUNTIME_ONLY_CONFIGURATION_NAME = "testRuntimeOnly";
/**
* The name of the test compile classpath configuration.
* @since 3.4
*/
@Incubating
public static final String TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME = "testCompileClasspath";
/**
* The name of the test runtime classpath configuration.
* @since 3.4
*/
@Incubating
public static final String TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME = "testRuntimeClasspath";
/**
* Represents the "jar" format of a variant of a Java component.
* @since 3.5
*/
@Incubating
public static final String JAR_TYPE = ArtifactTypeDefinition.JAR_TYPE;
private final ObjectFactory objectFactory;
@Inject
public JavaPlugin(ObjectFactory objectFactory) {
this.objectFactory = objectFactory;
}
public void apply(ProjectInternal project) {
project.getPluginManager().apply(JavaBasePlugin.class);
JavaPluginConvention javaConvention = project.getConvention().getPlugin(JavaPluginConvention.class);
project.getServices().get(ComponentRegistry.class).setMainComponent(new BuildableJavaComponentImpl(javaConvention));
BuildOutputCleanupRegistry buildOutputCleanupRegistry = project.getServices().get(BuildOutputCleanupRegistry.class);
configureSourceSets(javaConvention, buildOutputCleanupRegistry);
configureConfigurations(project);
configureJavaDoc(javaConvention);
configureTest(project, javaConvention);
configureArchivesAndComponent(project, javaConvention);
configureBuild(project);
}
private void configureSourceSets(JavaPluginConvention pluginConvention, final BuildOutputCleanupRegistry buildOutputCleanupRegistry) {
Project project = pluginConvention.getProject();
SourceSet main = pluginConvention.getSourceSets().create(SourceSet.MAIN_SOURCE_SET_NAME);
SourceSet test = pluginConvention.getSourceSets().create(SourceSet.TEST_SOURCE_SET_NAME);
test.setCompileClasspath(project.files(main.getOutput(), project.getConfigurations().getByName(TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME)));
test.setRuntimeClasspath(project.files(test.getOutput(), main.getOutput(), project.getConfigurations().getByName(TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME)));
// Register the project's source set output directories
pluginConvention.getSourceSets().all(new Action<SourceSet>() {
@Override
public void execute(SourceSet sourceSet) {
buildOutputCleanupRegistry.registerOutputs(sourceSet.getOutput());
}
});
}
private void configureJavaDoc(JavaPluginConvention pluginConvention) {
Project project = pluginConvention.getProject();
SourceSet mainSourceSet = pluginConvention.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
Javadoc javadoc = project.getTasks().create(JAVADOC_TASK_NAME, Javadoc.class);
javadoc.setDescription("Generates Javadoc API documentation for the main source code.");
javadoc.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP);
javadoc.setClasspath(mainSourceSet.getOutput().plus(mainSourceSet.getCompileClasspath()));
javadoc.setSource(mainSourceSet.getAllJava());
addDependsOnTaskInOtherProjects(javadoc, true, JAVADOC_TASK_NAME, COMPILE_CONFIGURATION_NAME);
}
private void configureArchivesAndComponent(Project project, JavaPluginConvention pluginConvention) {
Jar jar = project.getTasks().create(JAR_TASK_NAME, Jar.class);
jar.setDescription("Assembles a jar archive containing the main classes.");
jar.setGroup(BasePlugin.BUILD_GROUP);
jar.from(pluginConvention.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput());
ArchivePublishArtifact jarArtifact = new ArchivePublishArtifact(jar);
Configuration apiElementConfiguration = project.getConfigurations().getByName(API_ELEMENTS_CONFIGURATION_NAME);
Configuration runtimeConfiguration = project.getConfigurations().getByName(RUNTIME_CONFIGURATION_NAME);
Configuration runtimeElementsConfiguration = project.getConfigurations().getByName(RUNTIME_ELEMENTS_CONFIGURATION_NAME);
project.getExtensions().getByType(DefaultArtifactPublicationSet.class).addCandidate(jarArtifact);
JavaCompile javaCompile = (JavaCompile) project.getTasks().getByPath(COMPILE_JAVA_TASK_NAME);
ProcessResources processResources = (ProcessResources) project.getTasks().getByPath(PROCESS_RESOURCES_TASK_NAME);
addJar(apiElementConfiguration, jarArtifact);
addJar(runtimeConfiguration, jarArtifact);
addRuntimeVariants(runtimeElementsConfiguration, jarArtifact, javaCompile, processResources);
project.getComponents().add(new JavaLibrary(project.getConfigurations(), jarArtifact));
}
private void addJar(Configuration configuration, ArchivePublishArtifact jarArtifact) {
ConfigurationPublications publications = configuration.getOutgoing();
// Configure an implicit variant
publications.getArtifacts().add(jarArtifact);
publications.getAttributes().attribute(ArtifactAttributes.ARTIFACT_FORMAT, JavaPlugin.JAR_TYPE);
}
private void addRuntimeVariants(Configuration configuration, ArchivePublishArtifact jarArtifact, final JavaCompile javaCompile, final ProcessResources processResources) {
ConfigurationPublications publications = configuration.getOutgoing();
// Configure an implicit variant
publications.getArtifacts().add(jarArtifact);
publications.getAttributes().attribute(ArtifactAttributes.ARTIFACT_FORMAT, JavaPlugin.JAR_TYPE);
// Define some additional variants
NamedDomainObjectContainer<ConfigurationVariant> runtimeVariants = publications.getVariants();
ConfigurationVariant classesVariant = runtimeVariants.create("classes");
classesVariant.getAttributes().attribute(USAGE_ATTRIBUTE, objectFactory.named(Usage.class, Usage.JAVA_RUNTIME_CLASSES));
classesVariant.artifact(new IntermediateJavaArtifact(ArtifactTypeDefinition.JVM_CLASS_DIRECTORY, javaCompile) {
@Override
public File getFile() {
return javaCompile.getDestinationDir();
}
});
ConfigurationVariant resourcesVariant = runtimeVariants.create("resources");
resourcesVariant.getAttributes().attribute(USAGE_ATTRIBUTE, objectFactory.named(Usage.class, Usage.JAVA_RUNTIME_RESOURCES));
resourcesVariant.artifact(new IntermediateJavaArtifact(ArtifactTypeDefinition.JVM_RESOURCES_DIRECTORY, processResources) {
@Override
public File getFile() {
return processResources.getDestinationDir();
}
});
}
private void configureBuild(Project project) {
addDependsOnTaskInOtherProjects(project.getTasks().getByName(JavaBasePlugin.BUILD_NEEDED_TASK_NAME), true,
JavaBasePlugin.BUILD_NEEDED_TASK_NAME, TEST_RUNTIME_CONFIGURATION_NAME);
addDependsOnTaskInOtherProjects(project.getTasks().getByName(JavaBasePlugin.BUILD_DEPENDENTS_TASK_NAME), false,
JavaBasePlugin.BUILD_DEPENDENTS_TASK_NAME, TEST_RUNTIME_CONFIGURATION_NAME);
}
private void configureTest(final Project project, final JavaPluginConvention pluginConvention) {
project.getTasks().withType(Test.class, new Action<Test>() {
public void execute(final Test test) {
test.getConventionMapping().map("testClassesDirs", new Callable<Object>() {
public Object call() throws Exception {
return pluginConvention.getSourceSets().getByName(SourceSet.TEST_SOURCE_SET_NAME).getOutput().getClassesDirs();
}
});
test.getConventionMapping().map("classpath", new Callable<Object>() {
public Object call() throws Exception {
return pluginConvention.getSourceSets().getByName(SourceSet.TEST_SOURCE_SET_NAME).getRuntimeClasspath();
}
});
}
});
Test test = project.getTasks().create(TEST_TASK_NAME, Test.class);
project.getTasks().getByName(JavaBasePlugin.CHECK_TASK_NAME).dependsOn(test);
test.setDescription("Runs the unit tests.");
test.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
}
private void configureConfigurations(Project project) {
ConfigurationContainer configurations = project.getConfigurations();
Configuration defaultConfiguration = configurations.getByName(Dependency.DEFAULT_CONFIGURATION);
Configuration compileConfiguration = configurations.getByName(COMPILE_CONFIGURATION_NAME);
Configuration implementationConfiguration = configurations.getByName(IMPLEMENTATION_CONFIGURATION_NAME);
Configuration runtimeConfiguration = configurations.getByName(RUNTIME_CONFIGURATION_NAME);
Configuration runtimeOnlyConfiguration = configurations.getByName(RUNTIME_ONLY_CONFIGURATION_NAME);
Configuration compileTestsConfiguration = configurations.getByName(TEST_COMPILE_CONFIGURATION_NAME);
Configuration testImplementationConfiguration = configurations.getByName(TEST_IMPLEMENTATION_CONFIGURATION_NAME);
Configuration testRuntimeConfiguration = configurations.getByName(TEST_RUNTIME_CONFIGURATION_NAME);
Configuration testRuntimeOnlyConfiguration = configurations.getByName(TEST_RUNTIME_ONLY_CONFIGURATION_NAME);
compileTestsConfiguration.extendsFrom(compileConfiguration);
testImplementationConfiguration.extendsFrom(implementationConfiguration);
testRuntimeConfiguration.extendsFrom(runtimeConfiguration);
testRuntimeOnlyConfiguration.extendsFrom(runtimeOnlyConfiguration);
Configuration apiElementsConfiguration = configurations.maybeCreate(API_ELEMENTS_CONFIGURATION_NAME);
apiElementsConfiguration.setVisible(false);
apiElementsConfiguration.setDescription("API elements for main.");
apiElementsConfiguration.setCanBeResolved(false);
apiElementsConfiguration.setCanBeConsumed(true);
apiElementsConfiguration.getAttributes().attribute(USAGE_ATTRIBUTE, objectFactory.named(Usage.class, Usage.JAVA_API));
apiElementsConfiguration.extendsFrom(runtimeConfiguration);
Configuration runtimeElementsConfiguration = configurations.maybeCreate(RUNTIME_ELEMENTS_CONFIGURATION_NAME);
runtimeElementsConfiguration.setVisible(false);
runtimeElementsConfiguration.setCanBeConsumed(true);
runtimeElementsConfiguration.setCanBeResolved(false);
runtimeElementsConfiguration.setDescription("Elements of runtime for main.");
runtimeElementsConfiguration.getAttributes().attribute(USAGE_ATTRIBUTE, objectFactory.named(Usage.class, Usage.JAVA_RUNTIME_JARS));
runtimeElementsConfiguration.extendsFrom(implementationConfiguration, runtimeOnlyConfiguration, runtimeConfiguration);
defaultConfiguration.extendsFrom(runtimeElementsConfiguration);
}
/**
* Adds a dependency on tasks with the specified name in other projects. The other projects are determined from
* project lib dependencies using the specified configuration name. These may be projects this project depends on or
* projects that depend on this project based on the useDependOn argument.
*
* @param task Task to add dependencies to
* @param useDependedOn if true, add tasks from projects this project depends on, otherwise use projects that depend on this one.
* @param otherProjectTaskName name of task in other projects
* @param configurationName name of configuration to use to find the other projects
*/
private void addDependsOnTaskInOtherProjects(final Task task, boolean useDependedOn, String otherProjectTaskName,
String configurationName) {
Project project = task.getProject();
final Configuration configuration = project.getConfigurations().getByName(configurationName);
task.dependsOn(configuration.getTaskDependencyFromProjectDependency(useDependedOn, otherProjectTaskName));
}
/**
* This is only used by buildSrc to add to the buildscript classpath.
*/
private static class BuildableJavaComponentImpl implements BuildableJavaComponent {
private final JavaPluginConvention convention;
public BuildableJavaComponentImpl(JavaPluginConvention convention) {
this.convention = convention;
}
public Collection<String> getRebuildTasks() {
return Arrays.asList(BasePlugin.CLEAN_TASK_NAME, JavaBasePlugin.BUILD_TASK_NAME);
}
public Collection<String> getBuildTasks() {
return Arrays.asList(JavaBasePlugin.BUILD_TASK_NAME);
}
public FileCollection getRuntimeClasspath() {
ProjectInternal project = convention.getProject();
SourceSet mainSourceSet = convention.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
FileCollection runtimeClasspath = mainSourceSet.getRuntimeClasspath();
FileCollection gradleApi = project.getConfigurations().detachedConfiguration(project.getDependencies().gradleApi(), project.getDependencies().localGroovy());
Configuration runtimeElements = project.getConfigurations().getByName(mainSourceSet.getRuntimeElementsConfigurationName());
FileCollection mainSourceSetArtifact = runtimeElements.getOutgoing().getArtifacts().getFiles();
return mainSourceSetArtifact.plus(runtimeClasspath.minus(mainSourceSet.getOutput()).minus(gradleApi));
}
public Configuration getCompileDependencies() {
return convention.getProject().getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME);
}
}
/**
* A custom artifact type which allows the getFile call to be done lazily only when the
* artifact is actually needed.
*/
abstract static class IntermediateJavaArtifact extends AbstractPublishArtifact {
private final String type;
IntermediateJavaArtifact(String type, Task task) {
super(task);
this.type = type;
}
@Override
public String getName() {
return getFile().getName();
}
@Override
public String getExtension() {
return "";
}
@Override
public String getType() {
return type;
}
@Nullable
@Override
public String getClassifier() {
return null;
}
@Override
public Date getDate() {
return null;
}
}
}