MergePlugin.groovy 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /*
  2. * Copyright 2002-2015 the original author or 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. * https://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. package io.spring.gradle.convention
  17. import org.gradle.api.*
  18. import org.gradle.api.artifacts.Configuration
  19. import org.gradle.api.artifacts.ProjectDependency;
  20. import org.gradle.api.artifacts.maven.Conf2ScopeMapping
  21. import org.gradle.api.plugins.MavenPlugin
  22. import org.gradle.plugins.ide.eclipse.EclipsePlugin
  23. import org.gradle.plugins.ide.idea.IdeaPlugin
  24. import org.gradle.api.invocation.*
  25. /**
  26. * Gradle plugin that allows projects to merged together. Primarily developed to
  27. * allow Spring to support multiple incompatible versions of third-party
  28. * dependencies (for example Hibernate v3 and v4).
  29. * <p>
  30. * The 'merge' extension should be used to define how projects are merged, for example:
  31. * <pre class="code">
  32. * configure(subprojects) {
  33. * apply plugin: MergePlugin
  34. * }
  35. *
  36. * project("myproject") {
  37. * }
  38. *
  39. * project("myproject-extra") {
  40. * merge.into = project("myproject")
  41. * }
  42. * </pre>
  43. * <p>
  44. * This plugin adds two new configurations:
  45. * <ul>
  46. * <li>merging - Contains the projects being merged into this project<li>
  47. * <li>runtimeMerge - Contains all dependencies that are merge projects. These are used
  48. * to allow an IDE to reference merge projects.</li>
  49. * <ul>
  50. *
  51. * @author Rob Winch
  52. * @author Phillip Webb
  53. */
  54. class MergePlugin implements Plugin<Project> {
  55. private static boolean attachedProjectsEvaluated;
  56. public void apply(Project project) {
  57. project.plugins.apply(MavenPlugin)
  58. project.plugins.apply(EclipsePlugin)
  59. project.plugins.apply(IdeaPlugin)
  60. MergeModel model = project.extensions.create("merge", MergeModel)
  61. model.project = project
  62. project.configurations.create("merging")
  63. Configuration runtimeMerge = project.configurations.create("runtimeMerge")
  64. // Ensure the IDE can reference merged projects
  65. project.eclipse.classpath.plusConfigurations += [ runtimeMerge ]
  66. project.idea.module.scopes.PROVIDED.plus += [ runtimeMerge ]
  67. // Hook to perform the actual merge logic
  68. project.afterEvaluate{
  69. if (it.merge.into != null) {
  70. setup(it)
  71. }
  72. setupIdeDependencies(it)
  73. }
  74. // Hook to build runtimeMerge dependencies
  75. if (!attachedProjectsEvaluated) {
  76. project.gradle.projectsEvaluated{
  77. postProcessProjects(it)
  78. }
  79. attachedProjectsEvaluated = true;
  80. }
  81. }
  82. private void setup(Project project) {
  83. project.merge.into.dependencies.add("merging", project)
  84. project.dependencies.add("provided", project.merge.into.sourceSets.main.output)
  85. project.dependencies.add("runtimeMerge", project.merge.into)
  86. setupTaskDependencies(project)
  87. setupMaven(project)
  88. }
  89. private void setupTaskDependencies(Project project) {
  90. // invoking a task will invoke the task with the same name on 'into' project
  91. ["sourcesJar", "jar", "javadocJar", "javadoc", "install", "artifactoryPublish", "signArchives", "uploadArchives"].each {
  92. def task = project.tasks.findByPath(it)
  93. if (task) {
  94. task.enabled = false
  95. task.dependsOn(project.merge.into.tasks.findByPath(it))
  96. }
  97. }
  98. // update 'into' project artifacts to contain the source artifact contents
  99. project.merge.into.sourcesJar.from(project.sourcesJar.source)
  100. project.merge.into.jar.from(project.sourceSets.main.output)
  101. project.merge.into.javadoc {
  102. source += project.javadoc.source
  103. classpath += project.javadoc.classpath
  104. }
  105. }
  106. private void setupIdeDependencies(Project project) {
  107. project.configurations.each { c ->
  108. c.dependencies.findAll( { it instanceof org.gradle.api.artifacts.ProjectDependency } ).each { d ->
  109. if(d.dependencyProject.hasProperty("merge")) {
  110. d.dependencyProject.merge.from.each { from ->
  111. project.dependencies.add("runtimeMerge", from)
  112. }
  113. }
  114. }
  115. }
  116. }
  117. private void setupMaven(Project project) {
  118. project.configurations.each { configuration ->
  119. Conf2ScopeMapping mapping = project.conf2ScopeMappings.getMapping([configuration])
  120. if (mapping.scope) {
  121. Configuration intoConfiguration = project.merge.into.configurations.create(
  122. project.name + "-" + configuration.name)
  123. configuration.excludeRules.each {
  124. configuration.exclude([
  125. (ExcludeRule.GROUP_KEY) : it.group,
  126. (ExcludeRule.MODULE_KEY) : it.module])
  127. }
  128. configuration.dependencies.each {
  129. def intoCompile = project.merge.into.configurations.getByName("compile")
  130. // Protect against changing a compile scope dependency (SPR-10218)
  131. if (!intoCompile.dependencies.contains(it)) {
  132. intoConfiguration.dependencies.add(it)
  133. }
  134. }
  135. def index = project.parent.childProjects.findIndexOf {p -> p.getValue() == project}
  136. project.merge.into.install.repositories.mavenInstaller.pom.scopeMappings.addMapping(
  137. mapping.priority + 100 + index, intoConfiguration, mapping.scope)
  138. }
  139. }
  140. }
  141. private postProcessProjects(Gradle gradle) {
  142. gradle.allprojects(new Action<Project>() {
  143. public void execute(Project project) {
  144. if(!project.hasProperty("merge")) {
  145. return
  146. }
  147. project.configurations.getByName("runtime")?.allDependencies?.withType(ProjectDependency)?.each{
  148. Configuration dependsOnMergedFrom = it.dependencyProject.configurations.getByName("merging");
  149. dependsOnMergedFrom.dependencies.each{ dep ->
  150. project.dependencies.add("runtimeMerge", dep.dependencyProject)
  151. }
  152. }
  153. }
  154. });
  155. }
  156. }
  157. class MergeModel {
  158. Project project;
  159. Project into;
  160. List<Project> from = [];
  161. public void setInto(Project into) {
  162. this.into = into;
  163. into.merge.from.add(project);
  164. }
  165. }