|
1 | 1 | package com.github.jengelman.gradle.plugins.shadow |
2 | 2 |
|
3 | | -import com.github.jengelman.gradle.plugins.shadow.internal.JavaJarExec |
4 | 3 | import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar |
5 | 4 | import org.gradle.api.GradleException |
6 | 5 | import org.gradle.api.Plugin |
7 | 6 | import org.gradle.api.Project |
8 | | -import org.gradle.api.distribution.Distribution |
9 | 7 | import org.gradle.api.distribution.DistributionContainer |
10 | | -import org.gradle.api.file.CopySpec |
11 | 8 | import org.gradle.api.plugins.ApplicationPlugin |
12 | 9 | import org.gradle.api.plugins.JavaApplication |
13 | 10 | import org.gradle.api.plugins.JavaPluginExtension |
14 | | -import org.gradle.api.provider.Provider |
| 11 | +import org.gradle.api.tasks.JavaExec |
15 | 12 | import org.gradle.api.tasks.Sync |
16 | | -import org.gradle.api.tasks.TaskProvider |
17 | 13 | import org.gradle.api.tasks.application.CreateStartScripts |
18 | | -import org.gradle.jvm.toolchain.JavaLauncher |
19 | 14 | import org.gradle.jvm.toolchain.JavaToolchainService |
20 | 15 |
|
21 | | -class ShadowApplicationPlugin implements Plugin<Project> { |
| 16 | +/** |
| 17 | + * A {@link Plugin} which packages and runs a project as a Java Application using the shadowed jar. |
| 18 | + * |
| 19 | + * Modified from |
| 20 | + * <a href="https://github.com/gradle/gradle/blob/fdecc3c95828bb9a1c1bb6114483fe5b16f9159d/platforms/jvm/plugins-application/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java">org.gradle.api.plugins.ApplicationPlugin.java</a>. |
| 21 | + * |
| 22 | + * @see ApplicationPlugin |
| 23 | + */ |
| 24 | +abstract class ShadowApplicationPlugin implements Plugin<Project> { |
22 | 25 |
|
23 | 26 | public static final String SHADOW_RUN_TASK_NAME = 'runShadow' |
24 | 27 | public static final String SHADOW_SCRIPTS_TASK_NAME = 'startShadowScripts' |
25 | 28 | public static final String SHADOW_INSTALL_TASK_NAME = 'installShadowDist' |
26 | 29 |
|
27 | | - private Project project |
28 | | - private JavaApplication javaApplication |
| 30 | + private static final String DISTRIBUTION_NAME = ShadowBasePlugin.EXTENSION_NAME |
29 | 31 |
|
30 | 32 | @Override |
31 | 33 | void apply(Project project) { |
32 | | - this.project = project |
33 | | - this.javaApplication = project.extensions.getByType(JavaApplication) |
34 | | - |
35 | | - DistributionContainer distributions = project.extensions.getByName("distributions") as DistributionContainer |
36 | | - Distribution distribution = distributions.create("shadow") |
37 | | - |
38 | 34 | addRunTask(project) |
39 | 35 | addCreateScriptsTask(project) |
40 | | - |
41 | | - configureDistSpec(project, distribution.contents) |
42 | | - |
43 | | - configureJarMainClass(project) |
| 36 | + configureDistribution(project) |
| 37 | + configureShadowJarMainClass(project) |
44 | 38 | configureInstallTask(project) |
45 | 39 | } |
46 | 40 |
|
47 | | - protected void configureJarMainClass(Project project) { |
48 | | - def classNameProvider = javaApplication.mainClass |
49 | | - jar.configure { jar -> |
50 | | - jar.inputs.property('mainClassName', classNameProvider) |
51 | | - jar.doFirst { |
52 | | - jar.manifest.attributes 'Main-Class': classNameProvider.get() |
53 | | - } |
54 | | - } |
55 | | - } |
56 | | - |
57 | 41 | protected void addRunTask(Project project) { |
| 42 | + project.tasks.register(SHADOW_RUN_TASK_NAME, JavaExec) { task -> |
| 43 | + task.description = "Runs this project as a JVM application using the shadow jar" |
| 44 | + task.group = ApplicationPlugin.APPLICATION_GROUP |
58 | 45 |
|
59 | | - project.tasks.register(SHADOW_RUN_TASK_NAME, JavaJarExec) { run -> |
60 | | - def install = project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync) |
61 | | - run.dependsOn SHADOW_INSTALL_TASK_NAME |
62 | | - run.mainClass.set('-jar') |
63 | | - run.description = 'Runs this project as a JVM application using the shadow jar' |
64 | | - run.group = ApplicationPlugin.APPLICATION_GROUP |
65 | | - run.conventionMapping.jvmArgs = { javaApplication.applicationDefaultJvmArgs } |
66 | | - run.conventionMapping.jarFile = { |
67 | | - project.file("${install.get().destinationDir.path}/lib/${jar.get().archiveFile.get().asFile.name}") |
68 | | - } |
69 | | - configureJavaLauncher(run) |
70 | | - } |
71 | | - } |
| 46 | + task.classpath = project.files(project.tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME)) |
| 47 | + |
| 48 | + def applicationExtension = project.extensions.getByType(JavaApplication) |
| 49 | + def javaPluginExtension = project.extensions.getByType(JavaPluginExtension) |
| 50 | + def javaToolchainService = project.extensions.getByType(JavaToolchainService) |
| 51 | + |
| 52 | + task.mainModule.convention(applicationExtension.mainModule) |
| 53 | + task.mainClass.convention(applicationExtension.mainClass) |
| 54 | + task.jvmArguments.convention(project.provider { applicationExtension.applicationDefaultJvmArgs }) |
72 | 55 |
|
73 | | - private void configureJavaLauncher(JavaJarExec run) { |
74 | | - def toolchain = project.getExtensions().getByType(JavaPluginExtension.class).toolchain |
75 | | - JavaToolchainService service = project.getExtensions().getByType(JavaToolchainService.class) |
76 | | - Provider<JavaLauncher> defaultLauncher = service.launcherFor(toolchain) |
77 | | - run.getJavaLauncher().set(defaultLauncher) |
| 56 | + task.modularity.inferModulePath.convention(javaPluginExtension.modularity.inferModulePath) |
| 57 | + task.javaLauncher.convention(javaToolchainService.launcherFor(javaPluginExtension.toolchain)) |
| 58 | + } |
78 | 59 | } |
79 | 60 |
|
80 | 61 | protected void addCreateScriptsTask(Project project) { |
81 | | - project.tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts) { startScripts -> |
82 | | - startScripts.description = 'Creates OS specific scripts to run the project as a JVM application using the shadow jar' |
83 | | - startScripts.group = ApplicationPlugin.APPLICATION_GROUP |
84 | | - startScripts.classpath = project.files(jar) |
85 | | - startScripts.mainClass.set(javaApplication.mainClass) |
86 | | - startScripts.conventionMapping.applicationName = { javaApplication.applicationName } |
87 | | - startScripts.conventionMapping.outputDir = { new File(project.layout.buildDirectory.asFile.get(), 'scriptsShadow') } |
88 | | - startScripts.conventionMapping.defaultJvmOpts = { javaApplication.applicationDefaultJvmArgs } |
89 | | - startScripts.inputs.files project.objects.fileCollection().from { -> jar } |
| 62 | + project.tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts) { task -> |
| 63 | + task.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar" |
| 64 | + |
| 65 | + task.classpath = project.files(project.tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME)) |
| 66 | + |
| 67 | + def applicationExtension = project.extensions.getByType(JavaApplication) |
| 68 | + def javaPluginExtension = project.extensions.getByType(JavaPluginExtension) |
| 69 | + |
| 70 | + // TODO: replace usages of conventionMapping. |
| 71 | + task.mainModule.convention(applicationExtension.mainModule) |
| 72 | + task.mainClass.convention(applicationExtension.mainClass) |
| 73 | + task.conventionMapping.map("applicationName") { applicationExtension.applicationName } |
| 74 | + task.conventionMapping.map("outputDir") { |
| 75 | + project.layout.buildDirectory.dir("scriptsShadow").get().asFile |
| 76 | + } |
| 77 | + task.conventionMapping.map("executableDir") { applicationExtension.executableDir } |
| 78 | + task.conventionMapping.map("defaultJvmOpts") { applicationExtension.applicationDefaultJvmArgs } |
| 79 | + |
| 80 | + task.modularity.inferModulePath.convention(javaPluginExtension.modularity.inferModulePath) |
90 | 81 | } |
91 | 82 | } |
92 | 83 |
|
93 | 84 | protected void configureInstallTask(Project project) { |
94 | 85 | project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync).configure { task -> |
95 | | - task.doFirst { |
96 | | - if (task.destinationDir.directory) { |
97 | | - if (task.destinationDir.listFiles().size() != 0 && (!new File(task.destinationDir, 'lib').directory || !new File(task.destinationDir, 'bin').directory)) { |
98 | | - throw new GradleException("The specified installation directory '${task.destinationDir}' is neither empty nor does it contain an installation for '${javaApplication.applicationName}'.\n" + |
| 86 | + def applicationExtension = project.extensions.getByType(JavaApplication) |
| 87 | + def applicationName = project.provider { applicationExtension.applicationName } |
| 88 | + def executableDir = project.provider { applicationExtension.executableDir } |
| 89 | + |
| 90 | + task.doFirst("Check installation directory") { |
| 91 | + def destinationDir = task.destinationDir |
| 92 | + def children = destinationDir.list() |
| 93 | + if (children == null) { |
| 94 | + throw new IOException("Could not list directory ${destinationDir}") |
| 95 | + } |
| 96 | + if (children.length == 0) return |
| 97 | + if (!new File(destinationDir, "lib").isDirectory() || |
| 98 | + !new File(destinationDir, "bin").isDirectory() || |
| 99 | + !new File(destinationDir, executableDir.get()).isDirectory()) { |
| 100 | + throw new GradleException( |
| 101 | + "The specified installation directory '${destinationDir}' is neither empty nor does it contain an installation for '${applicationName.get()}'.\n" + |
99 | 102 | "If you really want to install to this directory, delete it and run the install task again.\n" + |
100 | 103 | "Alternatively, choose a different installation directory." |
101 | | - ) |
102 | | - } |
103 | | - } |
104 | | - } |
105 | | - task.doLast { |
106 | | - task.eachFile { |
107 | | - if (it.path == "bin/${javaApplication.applicationName}") { |
108 | | - it.mode = 0x755 |
109 | | - } |
| 104 | + ) |
110 | 105 | } |
111 | 106 | } |
112 | 107 | } |
113 | 108 | } |
114 | 109 |
|
115 | | - protected CopySpec configureDistSpec(Project project, CopySpec distSpec) { |
116 | | - def startScripts = project.tasks.named(SHADOW_SCRIPTS_TASK_NAME) |
117 | | - |
118 | | - distSpec.with { |
119 | | - from(project.file("src/dist")) |
120 | | - |
121 | | - into("lib") { |
122 | | - from(jar) |
123 | | - from(project.configurations.shadow) |
124 | | - } |
125 | | - into("bin") { |
126 | | - from(startScripts) |
127 | | - filePermissions { it.unix(493) } |
| 110 | + protected void configureDistribution(Project project) { |
| 111 | + def distributions = project.extensions.getByType(DistributionContainer) |
| 112 | + distributions.register(DISTRIBUTION_NAME) { dist -> |
| 113 | + def applicationExtension = project.extensions.getByType(JavaApplication) |
| 114 | + dist.distributionBaseName.convention( |
| 115 | + project.provider { |
| 116 | + // distributionBaseName defaults to `$project.name-$distribution.name`, applicationName |
| 117 | + // defaults to project.name |
| 118 | + // so we append the suffix to match the default distributionBaseName. Modified from |
| 119 | + // `ApplicationPlugin.configureDistribution()`. |
| 120 | + "${applicationExtension.applicationName}-${DISTRIBUTION_NAME}" |
| 121 | + } |
| 122 | + ) |
| 123 | + dist.contents { distSpec -> |
| 124 | + distSpec.from(project.file("src/dist")) |
| 125 | + distSpec.into("lib") { lib -> |
| 126 | + lib.from(project.tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME)) |
| 127 | + // Reflects the value of the `Class-Path` attribute in the JAR manifest. |
| 128 | + lib.from(project.configurations.named(ShadowBasePlugin.CONFIGURATION_NAME)) |
| 129 | + } |
| 130 | + // Defaults to bin dir. |
| 131 | + distSpec.into(project.provider { applicationExtension.executableDir }) { bin -> |
| 132 | + bin.from(project.tasks.named(SHADOW_SCRIPTS_TASK_NAME)) |
| 133 | + bin.filePermissions { permissions -> permissions.unix('rwxr-xr-x') } |
| 134 | + } |
| 135 | + distSpec.with(applicationExtension.applicationDistribution) |
128 | 136 | } |
129 | 137 | } |
130 | | - |
131 | | - distSpec |
132 | 138 | } |
133 | 139 |
|
134 | | - private TaskProvider<ShadowJar> getJar() { |
135 | | - project.tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME, ShadowJar) |
| 140 | + protected void configureShadowJarMainClass(Project project) { |
| 141 | + project.tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME, ShadowJar).configure { task -> |
| 142 | + def applicationExtension = project.extensions.getByType(JavaApplication) |
| 143 | + task.inputs.property('mainClassName', applicationExtension.mainClass) |
| 144 | + task.doFirst { |
| 145 | + task.manifest.attributes 'Main-Class': applicationExtension.mainClass.get() |
| 146 | + } |
| 147 | + } |
136 | 148 | } |
137 | 149 | } |
0 commit comments