From 00a86c37222d92b1477f2480b7bdfaebd12c2d1a Mon Sep 17 00:00:00 2001 From: Glyn Normington Date: Thu, 3 Jan 2013 14:50:45 +0000 Subject: [PATCH 1/5] Detect split packages at build time Split packages are a well-known anti-pattern for OSGi and a blocker for Eclipse Virgo (which prevents split packages being accessed via its Import-Library construct). Split packages are also unhelpful with a traditional linear classpath as a split package name does not uniquely identify the Spring framework JAR from which it came, thus complicating problem diagnosis and maintenance. Juergen Hoeller supports this position in the following comment in SPR-9990: >FWIW, I generally find split packages a bad practice, even without >OSGi in the mix. For the Spring Framework codebase, I consider a >split-package arrangement a design accident that we want to detect >in any case - and that we're willing to fix if it happened. > >I'm actually equally concerned about the source perspective: After >all, we want a package to be comprehensible from a single glance >at the project, not requiring the developer to jump into several >source modules to understand the overall layout of a package. Split packages have crept into Spring framework twice in recent months - see SPR-9811 and SPR-9988. Currently, they are only detected once the Spring framework has been converted to OSGi bundles and these bundles have been tested with Eclipse Virgo. This commit adds a build-time check for split packages to the Spring framework build. Issue: SPR-9990 Conflicts: build.gradle --- build.gradle | 6 ++ .../build/gradle/SplitPackageDetector.groovy | 85 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy diff --git a/build.gradle b/build.gradle index 8c0a1b42452..b9a5e2e8385 100644 --- a/build.gradle +++ b/build.gradle @@ -8,6 +8,11 @@ buildscript { } } +configure(rootProject) { + def splitFound = new org.springframework.build.gradle.SplitPackageDetector('.', logger).diagnoseSplitPackages(); + assert !splitFound // see error log messages for details of split packages +} + configure(allprojects) { project -> group = "org.springframework" version = qualifyVersionIfNecessary(version) @@ -954,6 +959,7 @@ configure(rootProject) { "set GRADLE_OPTS=$gradleBatOpts %GRADLE_OPTS%\nset DEFAULT_JVM_OPTS=") } } + } /* diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy new file mode 100644 index 00000000000..b17f859de1c --- /dev/null +++ b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy @@ -0,0 +1,85 @@ +/* + * Copyright 2013 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.springframework.build.gradle + +class SplitPackageDetector { + + private static final String HIDDEN_DIRECTORY_PREFIX = "." + + private static final String JAVA_FILE_SUFFIX = ".java" + + private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" + + private final Map> pkgMap = [:] + + private final logger + + SplitPackageDetector(baseDir, logger) { + this.logger = logger + dirList(baseDir).each { File dir -> + def packages = getPackagesInDirectory(dir) + if (!packages.isEmpty()) { + pkgMap.put(dir, packages) + } + } + } + + private File[] dirList(String dir) { + dirList(new File(dir)) + } + + private File[] dirList(File dir) { + dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) + } + + private Set getPackagesInDirectory(File dir) { + def pkgs = new HashSet() + addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") + return pkgs; + } + + boolean diagnoseSplitPackages() { + def splitFound = false; + def dirs = pkgMap.keySet().toArray() + def numDirs = dirs.length + for (int i = 0; i < numDirs - 1; i++) { + for (int j = i + 1; j < numDirs - 1; j++) { + def di = dirs[i] + def pi = new HashSet(pkgMap.get(di)) + def dj = dirs[j] + def pj = pkgMap.get(dj) + pi.retainAll(pj) + if (!pi.isEmpty()) { + logger.error("Packages $pi are split between directories '$di' and '$dj'") + splitFound = true + } + } + } + return splitFound + } + + private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { + def scanDir = new File(dir, pkg) + def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) + if (javaFiles != null && javaFiles.length != 0) { + packages.add(pkg) + } + dirList(scanDir).each { File subDir -> + addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) + } + } +} \ No newline at end of file From a27a3be76c5812a97f7439187838c7bbaedaff97 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Thu, 3 Jan 2013 13:04:51 -0600 Subject: [PATCH 2/5] Example SplitPackageDetectorPlugin --- build.gradle | 7 +- .../build/gradle/SplitPackageDetector.groovy | 85 ------------ .../gradle/SplitPackageDetectorPlugin.groovy | 125 ++++++++++++++++++ 3 files changed, 130 insertions(+), 87 deletions(-) delete mode 100644 buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy create mode 100644 buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy diff --git a/build.gradle b/build.gradle index b9a5e2e8385..6ab6f444de9 100644 --- a/build.gradle +++ b/build.gradle @@ -9,8 +9,11 @@ buildscript { } configure(rootProject) { - def splitFound = new org.springframework.build.gradle.SplitPackageDetector('.', logger).diagnoseSplitPackages(); - assert !splitFound // see error log messages for details of split packages + apply plugin: org.springframework.build.gradle.SplitPackageDetectorPlugin + + diagnoseSplitPackages { + inputDir = project.projectDir + } } configure(allprojects) { project -> diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy deleted file mode 100644 index b17f859de1c..00000000000 --- a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2013 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.springframework.build.gradle - -class SplitPackageDetector { - - private static final String HIDDEN_DIRECTORY_PREFIX = "." - - private static final String JAVA_FILE_SUFFIX = ".java" - - private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" - - private final Map> pkgMap = [:] - - private final logger - - SplitPackageDetector(baseDir, logger) { - this.logger = logger - dirList(baseDir).each { File dir -> - def packages = getPackagesInDirectory(dir) - if (!packages.isEmpty()) { - pkgMap.put(dir, packages) - } - } - } - - private File[] dirList(String dir) { - dirList(new File(dir)) - } - - private File[] dirList(File dir) { - dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) - } - - private Set getPackagesInDirectory(File dir) { - def pkgs = new HashSet() - addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") - return pkgs; - } - - boolean diagnoseSplitPackages() { - def splitFound = false; - def dirs = pkgMap.keySet().toArray() - def numDirs = dirs.length - for (int i = 0; i < numDirs - 1; i++) { - for (int j = i + 1; j < numDirs - 1; j++) { - def di = dirs[i] - def pi = new HashSet(pkgMap.get(di)) - def dj = dirs[j] - def pj = pkgMap.get(dj) - pi.retainAll(pj) - if (!pi.isEmpty()) { - logger.error("Packages $pi are split between directories '$di' and '$dj'") - splitFound = true - } - } - } - return splitFound - } - - private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { - def scanDir = new File(dir, pkg) - def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) - if (javaFiles != null && javaFiles.length != 0) { - packages.add(pkg) - } - dirList(scanDir).each { File subDir -> - addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) - } - } -} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy new file mode 100644 index 00000000000..ed7e4212237 --- /dev/null +++ b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy @@ -0,0 +1,125 @@ +/* + * Copyright 2002-2012 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.springframework.build.gradle + +import java.io.File; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.gradle.api.* +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.ProjectDependency; +import org.gradle.api.artifacts.maven.Conf2ScopeMapping +import org.gradle.api.plugins.MavenPlugin +import org.gradle.api.tasks.* +import org.gradle.plugins.ide.eclipse.EclipsePlugin +import org.gradle.plugins.ide.eclipse.model.EclipseClasspath; +import org.gradle.plugins.ide.idea.IdeaPlugin +import org.gradle.api.invocation.* + + +class SplitPackageDetectorPlugin implements Plugin { + public void apply(Project project) { + Task diagnoseSplitPackages = project.tasks.add('diagnoseSplitPackages', SplitPackageDetectorTask.class) + diagnoseSplitPackages.setDescription('Detects split packages') + //project.tasks.findByName('build').dependsOn(diagnoseSplitPackages) + } +} + +public class SplitPackageDetectorTask extends DefaultTask { + @InputDirectory + File inputDir + + @TaskAction + public final void diagnoseSplitPackages() { + def projects = project.subprojects.findAll { it.plugins.findPlugin(org.springframework.build.gradle.MergePlugin) }.findAll { it.merge.into } + projects.each { p -> + println ' > The project directory '+ p.projectDir + ' will merge into ' + p.merge.into.projectDir + } + def splitFound = new org.springframework.build.gradle.SplitPackageDetector(inputDir.absolutePath, project.logger).diagnoseSplitPackages(); + assert !splitFound + } +} + +class SplitPackageDetector { + + private static final String HIDDEN_DIRECTORY_PREFIX = "." + + private static final String JAVA_FILE_SUFFIX = ".java" + + private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" + + private final Map> pkgMap = [:] + + private final logger + + SplitPackageDetector(baseDir, logger) { + this.logger = logger + dirList(baseDir).each { File dir -> + def packages = getPackagesInDirectory(dir) + if (!packages.isEmpty()) { + pkgMap.put(dir, packages) + } + } + } + + private File[] dirList(String dir) { + dirList(new File(dir)) + } + + private File[] dirList(File dir) { + dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) + } + + private Set getPackagesInDirectory(File dir) { + def pkgs = new HashSet() + addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") + return pkgs; + } + + boolean diagnoseSplitPackages() { + def splitFound = false; + def dirs = pkgMap.keySet().toArray() + def numDirs = dirs.length + for (int i = 0; i < numDirs - 1; i++) { + for (int j = i + 1; j < numDirs - 1; j++) { + def di = dirs[i] + def pi = new HashSet(pkgMap.get(di)) + def dj = dirs[j] + def pj = pkgMap.get(dj) + pi.retainAll(pj) + if (!pi.isEmpty()) { + logger.error("Packages $pi are split between directories '$di' and '$dj'") + splitFound = true + } + } + } + return splitFound + } + + private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { + def scanDir = new File(dir, pkg) + def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) + if (javaFiles != null && javaFiles.length != 0) { + packages.add(pkg) + } + dirList(scanDir).each { File subDir -> + addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) + } + } +} \ No newline at end of file From 2df08bdfbdb4efc9da46866eb1a3f6411a045e9e Mon Sep 17 00:00:00 2001 From: Glyn Normington Date: Fri, 4 Jan 2013 11:34:16 +0000 Subject: [PATCH 3/5] Rework split package detection code Allow packages to be split across projects which will be merged into a single JAR file. Make split package detection a dependency of the 'check' task. This is idiomatic gradle as well as allowing the 'test' task (another dependency of 'check') to be executed without split packages being detected. Omit the project spring-instructment-tomcat from the check on the basis of SPR-10150. Issues: SPR-9990, SPR-10150 Conflicts: build.gradle --- build.gradle | 19 +- .../gradle/SplitPackageDetectorPlugin.groovy | 192 +++++++++--------- 2 files changed, 108 insertions(+), 103 deletions(-) diff --git a/build.gradle b/build.gradle index 6ab6f444de9..697cdbea53d 100644 --- a/build.gradle +++ b/build.gradle @@ -8,14 +8,6 @@ buildscript { } } -configure(rootProject) { - apply plugin: org.springframework.build.gradle.SplitPackageDetectorPlugin - - diagnoseSplitPackages { - inputDir = project.projectDir - } -} - configure(allprojects) { project -> group = "org.springframework" version = qualifyVersionIfNecessary(version) @@ -761,13 +753,18 @@ configure(rootProject) { apply plugin: "docbook-reference" apply plugin: "groovy" apply from: "${gradleScriptDir}/jdiff.gradle" + apply plugin: org.springframework.build.gradle.SplitPackageDetectorPlugin - reference { + reference { sourceDir = file("src/reference/docbook") pdfFilename = "spring-framework-reference.pdf" } - // don"t publish the default jar for the root project + diagnoseSplitPackages { + projectsToScan = project.subprojects - project(":spring-instrument-tomcat") // SPR-10150 + } + + // don"t publish the default jar for the root project configurations.archives.artifacts.clear() dependencies { // for integration tests @@ -791,6 +788,8 @@ configure(rootProject) { testCompile("hsqldb:hsqldb:${hsqldbVersion}") } + check.dependsOn diagnoseSplitPackages + task api(type: Javadoc) { group = "Documentation" description = "Generates aggregated Javadoc API documentation." diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy index ed7e4212237..01c36fad7a2 100644 --- a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy +++ b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2013 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. @@ -16,110 +16,116 @@ package org.springframework.build.gradle -import java.io.File; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.gradle.api.* +import org.gradle.api.DefaultTask +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.ProjectDependency; +import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.artifacts.maven.Conf2ScopeMapping import org.gradle.api.plugins.MavenPlugin -import org.gradle.api.tasks.* +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction import org.gradle.plugins.ide.eclipse.EclipsePlugin -import org.gradle.plugins.ide.eclipse.model.EclipseClasspath; +import org.gradle.plugins.ide.eclipse.model.EclipseClasspath import org.gradle.plugins.ide.idea.IdeaPlugin -import org.gradle.api.invocation.* - class SplitPackageDetectorPlugin implements Plugin { - public void apply(Project project) { - Task diagnoseSplitPackages = project.tasks.add('diagnoseSplitPackages', SplitPackageDetectorTask.class) - diagnoseSplitPackages.setDescription('Detects split packages') - //project.tasks.findByName('build').dependsOn(diagnoseSplitPackages) - } + public void apply(Project project) { + Task diagnoseSplitPackages = project.tasks.add('diagnoseSplitPackages', SplitPackageDetectorTask.class) + diagnoseSplitPackages.setDescription('Detects packages which will be split across JARs') + } } public class SplitPackageDetectorTask extends DefaultTask { - @InputDirectory - File inputDir - - @TaskAction - public final void diagnoseSplitPackages() { - def projects = project.subprojects.findAll { it.plugins.findPlugin(org.springframework.build.gradle.MergePlugin) }.findAll { it.merge.into } - projects.each { p -> - println ' > The project directory '+ p.projectDir + ' will merge into ' + p.merge.into.projectDir - } - def splitFound = new org.springframework.build.gradle.SplitPackageDetector(inputDir.absolutePath, project.logger).diagnoseSplitPackages(); - assert !splitFound - } + @Input + Set projectsToScan + + @TaskAction + public final void diagnoseSplitPackages() { + def Map mergeMap = [:] + def projects = projectsToScan.findAll { it.plugins.findPlugin(org.springframework.build.gradle.MergePlugin) }.findAll { it.merge.into } + projects.each { p -> + mergeMap.put(p, p.merge.into) + } + def splitFound = new org.springframework.build.gradle.SplitPackageDetector(projectsToScan, mergeMap, project.logger).diagnoseSplitPackages(); + assert !splitFound // see error log messages for details of split packages + } } class SplitPackageDetector { - private static final String HIDDEN_DIRECTORY_PREFIX = "." - - private static final String JAVA_FILE_SUFFIX = ".java" - - private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" - - private final Map> pkgMap = [:] - - private final logger - - SplitPackageDetector(baseDir, logger) { - this.logger = logger - dirList(baseDir).each { File dir -> - def packages = getPackagesInDirectory(dir) - if (!packages.isEmpty()) { - pkgMap.put(dir, packages) - } - } - } - - private File[] dirList(String dir) { - dirList(new File(dir)) - } - - private File[] dirList(File dir) { - dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) - } - - private Set getPackagesInDirectory(File dir) { - def pkgs = new HashSet() - addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") - return pkgs; - } - - boolean diagnoseSplitPackages() { - def splitFound = false; - def dirs = pkgMap.keySet().toArray() - def numDirs = dirs.length - for (int i = 0; i < numDirs - 1; i++) { - for (int j = i + 1; j < numDirs - 1; j++) { - def di = dirs[i] - def pi = new HashSet(pkgMap.get(di)) - def dj = dirs[j] - def pj = pkgMap.get(dj) - pi.retainAll(pj) - if (!pi.isEmpty()) { - logger.error("Packages $pi are split between directories '$di' and '$dj'") - splitFound = true - } - } - } - return splitFound - } - - private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { - def scanDir = new File(dir, pkg) - def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) - if (javaFiles != null && javaFiles.length != 0) { - packages.add(pkg) - } - dirList(scanDir).each { File subDir -> - addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) - } - } + private static final String HIDDEN_DIRECTORY_PREFIX = "." + + private static final String JAVA_FILE_SUFFIX = ".java" + + private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" + + private static final String PACKAGE_SEPARATOR = "." + + private final Map mergeMap + + private final Map> pkgMap = [:] + + private final logger + + SplitPackageDetector(projectsToScan, mergeMap, logger) { + this.mergeMap = mergeMap + this.logger = logger + projectsToScan.each { Project p -> + def dir = p.projectDir + def packages = getPackagesInDirectory(dir) + if (!packages.isEmpty()) { + pkgMap.put(p, packages) + } + } + } + + private File[] dirList(String dir) { + dirList(new File(dir)) + } + + private File[] dirList(File dir) { + dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) + } + + private Set getPackagesInDirectory(File dir) { + def pkgs = new HashSet() + addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") + return pkgs; + } + + boolean diagnoseSplitPackages() { + def splitFound = false; + def projs = pkgMap.keySet().toArray() + def numProjects = projs.length + for (int i = 0; i < numProjects - 1; i++) { + for (int j = i + 1; j < numProjects - 1; j++) { + def pi = projs[i] + def pkgi = new HashSet(pkgMap.get(pi)) + def pj = projs[j] + def pkgj = pkgMap.get(pj) + pkgi.retainAll(pkgj) + if (!pkgi.isEmpty() && mergeMap.get(pi) != pj && mergeMap.get(pj) != pi) { + pkgi.each { pkg -> + def readablePkg = pkg.substring(1).replaceAll(File.separator, PACKAGE_SEPARATOR) + logger.error("Package '$readablePkg' is split between $pi and $pj") + } + splitFound = true + } + } + } + return splitFound + } + + private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { + def scanDir = new File(dir, pkg) + def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) + if (javaFiles != null && javaFiles.length != 0) { + packages.add(pkg) + } + dirList(scanDir).each { File subDir -> + addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) + } + } } \ No newline at end of file From b8f408ed5faae1bf266331063bc2ea575962bead Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Tue, 8 Jan 2013 17:02:43 +0100 Subject: [PATCH 4/5] Replace space indentation with tabs Issue: SPR-9990 --- build.gradle | 14 +- .../gradle/SplitPackageDetectorPlugin.groovy | 182 +++++++++--------- 2 files changed, 98 insertions(+), 98 deletions(-) diff --git a/build.gradle b/build.gradle index 697cdbea53d..f67e2740c0c 100644 --- a/build.gradle +++ b/build.gradle @@ -753,18 +753,18 @@ configure(rootProject) { apply plugin: "docbook-reference" apply plugin: "groovy" apply from: "${gradleScriptDir}/jdiff.gradle" - apply plugin: org.springframework.build.gradle.SplitPackageDetectorPlugin + apply plugin: org.springframework.build.gradle.SplitPackageDetectorPlugin - reference { + reference { sourceDir = file("src/reference/docbook") pdfFilename = "spring-framework-reference.pdf" } - diagnoseSplitPackages { - projectsToScan = project.subprojects - project(":spring-instrument-tomcat") // SPR-10150 - } + diagnoseSplitPackages { + projectsToScan = project.subprojects - project(":spring-instrument-tomcat") // SPR-10150 + } - // don"t publish the default jar for the root project + // don"t publish the default jar for the root project configurations.archives.artifacts.clear() dependencies { // for integration tests @@ -788,7 +788,7 @@ configure(rootProject) { testCompile("hsqldb:hsqldb:${hsqldbVersion}") } - check.dependsOn diagnoseSplitPackages + check.dependsOn diagnoseSplitPackages task api(type: Javadoc) { group = "Documentation" diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy index 01c36fad7a2..eddfec385c4 100644 --- a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy +++ b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy @@ -31,101 +31,101 @@ import org.gradle.plugins.ide.eclipse.model.EclipseClasspath import org.gradle.plugins.ide.idea.IdeaPlugin class SplitPackageDetectorPlugin implements Plugin { - public void apply(Project project) { - Task diagnoseSplitPackages = project.tasks.add('diagnoseSplitPackages', SplitPackageDetectorTask.class) - diagnoseSplitPackages.setDescription('Detects packages which will be split across JARs') - } + public void apply(Project project) { + Task diagnoseSplitPackages = project.tasks.add('diagnoseSplitPackages', SplitPackageDetectorTask.class) + diagnoseSplitPackages.setDescription('Detects packages which will be split across JARs') + } } public class SplitPackageDetectorTask extends DefaultTask { - @Input - Set projectsToScan - - @TaskAction - public final void diagnoseSplitPackages() { - def Map mergeMap = [:] - def projects = projectsToScan.findAll { it.plugins.findPlugin(org.springframework.build.gradle.MergePlugin) }.findAll { it.merge.into } - projects.each { p -> - mergeMap.put(p, p.merge.into) - } - def splitFound = new org.springframework.build.gradle.SplitPackageDetector(projectsToScan, mergeMap, project.logger).diagnoseSplitPackages(); - assert !splitFound // see error log messages for details of split packages - } + @Input + Set projectsToScan + + @TaskAction + public final void diagnoseSplitPackages() { + def Map mergeMap = [:] + def projects = projectsToScan.findAll { it.plugins.findPlugin(org.springframework.build.gradle.MergePlugin) }.findAll { it.merge.into } + projects.each { p -> + mergeMap.put(p, p.merge.into) + } + def splitFound = new org.springframework.build.gradle.SplitPackageDetector(projectsToScan, mergeMap, project.logger).diagnoseSplitPackages(); + assert !splitFound // see error log messages for details of split packages + } } class SplitPackageDetector { - private static final String HIDDEN_DIRECTORY_PREFIX = "." - - private static final String JAVA_FILE_SUFFIX = ".java" - - private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" - - private static final String PACKAGE_SEPARATOR = "." - - private final Map mergeMap - - private final Map> pkgMap = [:] - - private final logger - - SplitPackageDetector(projectsToScan, mergeMap, logger) { - this.mergeMap = mergeMap - this.logger = logger - projectsToScan.each { Project p -> - def dir = p.projectDir - def packages = getPackagesInDirectory(dir) - if (!packages.isEmpty()) { - pkgMap.put(p, packages) - } - } - } - - private File[] dirList(String dir) { - dirList(new File(dir)) - } - - private File[] dirList(File dir) { - dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) - } - - private Set getPackagesInDirectory(File dir) { - def pkgs = new HashSet() - addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") - return pkgs; - } - - boolean diagnoseSplitPackages() { - def splitFound = false; - def projs = pkgMap.keySet().toArray() - def numProjects = projs.length - for (int i = 0; i < numProjects - 1; i++) { - for (int j = i + 1; j < numProjects - 1; j++) { - def pi = projs[i] - def pkgi = new HashSet(pkgMap.get(pi)) - def pj = projs[j] - def pkgj = pkgMap.get(pj) - pkgi.retainAll(pkgj) - if (!pkgi.isEmpty() && mergeMap.get(pi) != pj && mergeMap.get(pj) != pi) { - pkgi.each { pkg -> - def readablePkg = pkg.substring(1).replaceAll(File.separator, PACKAGE_SEPARATOR) - logger.error("Package '$readablePkg' is split between $pi and $pj") - } - splitFound = true - } - } - } - return splitFound - } - - private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { - def scanDir = new File(dir, pkg) - def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) - if (javaFiles != null && javaFiles.length != 0) { - packages.add(pkg) - } - dirList(scanDir).each { File subDir -> - addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) - } - } -} \ No newline at end of file + private static final String HIDDEN_DIRECTORY_PREFIX = "." + + private static final String JAVA_FILE_SUFFIX = ".java" + + private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" + + private static final String PACKAGE_SEPARATOR = "." + + private final Map mergeMap + + private final Map> pkgMap = [:] + + private final logger + + SplitPackageDetector(projectsToScan, mergeMap, logger) { + this.mergeMap = mergeMap + this.logger = logger + projectsToScan.each { Project p -> + def dir = p.projectDir + def packages = getPackagesInDirectory(dir) + if (!packages.isEmpty()) { + pkgMap.put(p, packages) + } + } + } + + private File[] dirList(String dir) { + dirList(new File(dir)) + } + + private File[] dirList(File dir) { + dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) + } + + private Set getPackagesInDirectory(File dir) { + def pkgs = new HashSet() + addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") + return pkgs; + } + + boolean diagnoseSplitPackages() { + def splitFound = false; + def projs = pkgMap.keySet().toArray() + def numProjects = projs.length + for (int i = 0; i < numProjects - 1; i++) { + for (int j = i + 1; j < numProjects - 1; j++) { + def pi = projs[i] + def pkgi = new HashSet(pkgMap.get(pi)) + def pj = projs[j] + def pkgj = pkgMap.get(pj) + pkgi.retainAll(pkgj) + if (!pkgi.isEmpty() && mergeMap.get(pi) != pj && mergeMap.get(pj) != pi) { + pkgi.each { pkg -> + def readablePkg = pkg.substring(1).replaceAll(File.separator, PACKAGE_SEPARATOR) + logger.error("Package '$readablePkg' is split between $pi and $pj") + } + splitFound = true + } + } + } + return splitFound + } + + private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { + def scanDir = new File(dir, pkg) + def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) + if (javaFiles != null && javaFiles.length != 0) { + packages.add(pkg) + } + dirList(scanDir).each { File subDir -> + addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) + } + } +} From 654c07db344310d8582c433dc643b0a256c8cc7e Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Fri, 11 Jan 2013 15:54:11 +0100 Subject: [PATCH 5/5] Refactor detect-split-packages Gradle plugin - Use conventional plugin naming, i.e. "detect-split-packages" instead of applying plugin based on fully-qualified class name - Rename "diagnose" => "detect" consistently throughout plugin, task and method names and generally refactor naming throughout to follow "detect split packages" phrasing - Add Javadoc to DetectSplitPackagesPlugin - Improve error reporting when split packages are detected Upon detecting one or more split packages, `detectSplitPackages` now fails idiomatically, throwing a GradleException to signal task failure (as opposed to the previous approach of using an assert assertion), and the output reads as follows: $ gradle detectSplitPackages [...] :buildSrc:build UP-TO-DATE :detectSplitPackages FAILED FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':detectSplitPackages'. > The following split package(s) have been detected: - org.springframework.beans (split across spring-beans and spring-orm) - org.springframework.core.env (split across spring-context and spring-core) - DetectSplitPackagesTask now automatically attaches itself to `check` task lifecycle if the enclosing project contains a `check` task - DetectSplitPackagesTask adds itself to the 'Verification' task group, ensuring that it shows up correctly in `gradle tasks` task listings - packagesToScan now defaults to all subprojects; users may then customize this by removing individual subprojects from the collection Issue: SPR-9990 --- build.gradle | 8 +- .../gradle/DetectSplitPackagesPlugin.groovy | 157 ++++++++++++++++++ .../gradle/SplitPackageDetectorPlugin.groovy | 131 --------------- .../detect-split-packages.properties | 1 + 4 files changed, 161 insertions(+), 136 deletions(-) create mode 100644 buildSrc/src/main/groovy/org/springframework/build/gradle/DetectSplitPackagesPlugin.groovy delete mode 100644 buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy create mode 100644 buildSrc/src/main/resources/META-INF/gradle-plugins/detect-split-packages.properties diff --git a/build.gradle b/build.gradle index f67e2740c0c..11e9ed8becd 100644 --- a/build.gradle +++ b/build.gradle @@ -752,16 +752,16 @@ configure(rootProject) { apply plugin: "docbook-reference" apply plugin: "groovy" + apply plugin: "detect-split-packages" apply from: "${gradleScriptDir}/jdiff.gradle" - apply plugin: org.springframework.build.gradle.SplitPackageDetectorPlugin reference { sourceDir = file("src/reference/docbook") pdfFilename = "spring-framework-reference.pdf" } - diagnoseSplitPackages { - projectsToScan = project.subprojects - project(":spring-instrument-tomcat") // SPR-10150 + detectSplitPackages { + projectsToScan -= project(":spring-instrument-tomcat") } // don"t publish the default jar for the root project @@ -788,8 +788,6 @@ configure(rootProject) { testCompile("hsqldb:hsqldb:${hsqldbVersion}") } - check.dependsOn diagnoseSplitPackages - task api(type: Javadoc) { group = "Documentation" description = "Generates aggregated Javadoc API documentation." diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/DetectSplitPackagesPlugin.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/DetectSplitPackagesPlugin.groovy new file mode 100644 index 00000000000..8b73878694e --- /dev/null +++ b/buildSrc/src/main/groovy/org/springframework/build/gradle/DetectSplitPackagesPlugin.groovy @@ -0,0 +1,157 @@ +/* + * Copyright 2002-2013 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.springframework.build.gradle + +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction + +/** + * Gradle plugin that detects identically named, non-empty packages split across multiple + * subprojects, e.g. "org.springframework.context.annotation" existing in both spring-core + * and spring-aspects. Adds a 'detectSplitPackages' task to the current project's task + * collection. If the project already contains a 'check' task (i.e. is a typical Gradle + * project with the "java" plugin applied), the 'check' task will be updated to depend on + * the execution of 'detectSplitPackages'. + * + * By default, all subprojects will be scanned. Use the 'projectsToScan' task property to + * modify this value. Example usage: + * + * apply plugin: 'detect-split-packages // typically applied to root project + * + * detectSplitPackages { + * packagesToScan -= project(":spring-xyz") // scan every project but spring-xyz + * } + * + * @author Rob Winch + * @author Glyn Normington + * @author Chris Beams + */ +public class DetectSplitPackagesPlugin implements Plugin { + public void apply(Project project) { + def tasks = project.tasks + Task detectSplitPackages = tasks.add('detectSplitPackages', DetectSplitPackagesTask.class) + if (tasks.asMap.containsKey('check')) { + tasks.getByName('check').dependsOn detectSplitPackages + } + } +} + +public class DetectSplitPackagesTask extends DefaultTask { + + private static final String JAVA_FILE_SUFFIX = ".java" + private static final String PACKAGE_SEPARATOR = "." + private static final String HIDDEN_DIRECTORY_PREFIX = "." + + @Input + Set projectsToScan = project.subprojects + + public DetectSplitPackagesTask() { + this.group = 'Verification' + this.description = 'Detects packages split across two or more subprojects.' + } + + @TaskAction + public void detectSplitPackages() { + def splitPackages = doDetectSplitPackages() + if (!splitPackages.isEmpty()) { + def message = "The following split package(s) have been detected:\n" + splitPackages.each { pkg, mod -> + message += " - ${pkg} (split across ${mod[0].name} and ${mod[1].name})\n" + } + throw new GradleException(message) + } + } + + private Map> doDetectSplitPackages() { + def splitPackages = [:] + def mergedProjects = findMergedProjects() + def packagesByProject = mapPackagesByProject() + + def projects = packagesByProject.keySet().toArray() + def nProjects = projects.length + + for (int i = 0; i < nProjects - 1; i++) { + for (int j = i + 1; j < nProjects - 1; j++) { + def prj_i = projects[i] + def prj_j = projects[j] + + def pkgs_i = new HashSet(packagesByProject.get(prj_i)) + def pkgs_j = packagesByProject.get(prj_j) + pkgs_i.retainAll(pkgs_j) + + if (!pkgs_i.isEmpty() + && mergedProjects.get(prj_i) != prj_j + && mergedProjects.get(prj_j) != prj_i) { + pkgs_i.each { pkg -> + def readablePkg = pkg.substring(1).replaceAll(File.separator, PACKAGE_SEPARATOR) + splitPackages[readablePkg] = [prj_i, prj_j] + } + } + } + } + return splitPackages; + } + + private Map> mapPackagesByProject() { + def packagesByProject = [:] + this.projectsToScan.each { Project p -> + def packages = new HashSet() + p.sourceSets.main.java.srcDirs.each { File dir -> + findPackages(packages, dir, "") + } + if (!packages.isEmpty()) { + packagesByProject.put(p, packages) + } + } + return packagesByProject; + } + + private Map findMergedProjects() { + def mergedProjects = [:] + this.projectsToScan.findAll { p -> + p.plugins.findPlugin(MergePlugin) + }.findAll { p -> + p.merge.into + }.each { p -> + mergedProjects.put(p, p.merge.into) + } + return mergedProjects + } + + private static void findPackages(Set packages, File dir, String packagePath) { + def scanDir = new File(dir, packagePath) + def File[] javaFiles = scanDir.listFiles({ file -> + !file.isDirectory() && file.name.endsWith(JAVA_FILE_SUFFIX) + } as FileFilter) + + if (javaFiles != null && javaFiles.length != 0) { + packages.add(packagePath) + } + + scanDir.listFiles({ File file -> + file.isDirectory() && !file.name.startsWith(HIDDEN_DIRECTORY_PREFIX) + } as FileFilter).each { File subDir -> + findPackages(packages, dir, packagePath + File.separator + subDir.name) + } + } +} + diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy deleted file mode 100644 index eddfec385c4..00000000000 --- a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2013 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.springframework.build.gradle - -import org.gradle.api.DefaultTask -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.ProjectDependency -import org.gradle.api.artifacts.maven.Conf2ScopeMapping -import org.gradle.api.plugins.MavenPlugin -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.TaskAction -import org.gradle.plugins.ide.eclipse.EclipsePlugin -import org.gradle.plugins.ide.eclipse.model.EclipseClasspath -import org.gradle.plugins.ide.idea.IdeaPlugin - -class SplitPackageDetectorPlugin implements Plugin { - public void apply(Project project) { - Task diagnoseSplitPackages = project.tasks.add('diagnoseSplitPackages', SplitPackageDetectorTask.class) - diagnoseSplitPackages.setDescription('Detects packages which will be split across JARs') - } -} - -public class SplitPackageDetectorTask extends DefaultTask { - @Input - Set projectsToScan - - @TaskAction - public final void diagnoseSplitPackages() { - def Map mergeMap = [:] - def projects = projectsToScan.findAll { it.plugins.findPlugin(org.springframework.build.gradle.MergePlugin) }.findAll { it.merge.into } - projects.each { p -> - mergeMap.put(p, p.merge.into) - } - def splitFound = new org.springframework.build.gradle.SplitPackageDetector(projectsToScan, mergeMap, project.logger).diagnoseSplitPackages(); - assert !splitFound // see error log messages for details of split packages - } -} - -class SplitPackageDetector { - - private static final String HIDDEN_DIRECTORY_PREFIX = "." - - private static final String JAVA_FILE_SUFFIX = ".java" - - private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" - - private static final String PACKAGE_SEPARATOR = "." - - private final Map mergeMap - - private final Map> pkgMap = [:] - - private final logger - - SplitPackageDetector(projectsToScan, mergeMap, logger) { - this.mergeMap = mergeMap - this.logger = logger - projectsToScan.each { Project p -> - def dir = p.projectDir - def packages = getPackagesInDirectory(dir) - if (!packages.isEmpty()) { - pkgMap.put(p, packages) - } - } - } - - private File[] dirList(String dir) { - dirList(new File(dir)) - } - - private File[] dirList(File dir) { - dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) - } - - private Set getPackagesInDirectory(File dir) { - def pkgs = new HashSet() - addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") - return pkgs; - } - - boolean diagnoseSplitPackages() { - def splitFound = false; - def projs = pkgMap.keySet().toArray() - def numProjects = projs.length - for (int i = 0; i < numProjects - 1; i++) { - for (int j = i + 1; j < numProjects - 1; j++) { - def pi = projs[i] - def pkgi = new HashSet(pkgMap.get(pi)) - def pj = projs[j] - def pkgj = pkgMap.get(pj) - pkgi.retainAll(pkgj) - if (!pkgi.isEmpty() && mergeMap.get(pi) != pj && mergeMap.get(pj) != pi) { - pkgi.each { pkg -> - def readablePkg = pkg.substring(1).replaceAll(File.separator, PACKAGE_SEPARATOR) - logger.error("Package '$readablePkg' is split between $pi and $pj") - } - splitFound = true - } - } - } - return splitFound - } - - private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { - def scanDir = new File(dir, pkg) - def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) - if (javaFiles != null && javaFiles.length != 0) { - packages.add(pkg) - } - dirList(scanDir).each { File subDir -> - addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) - } - } -} diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/detect-split-packages.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/detect-split-packages.properties new file mode 100644 index 00000000000..10b1e4e981d --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/detect-split-packages.properties @@ -0,0 +1 @@ +implementation-class=org.springframework.build.gradle.DetectSplitPackagesPlugin