From 00a86c37222d92b1477f2480b7bdfaebd12c2d1a Mon Sep 17 00:00:00 2001 From: Glyn Normington Date: Thu, 3 Jan 2013 14:50:45 +0000 Subject: [PATCH] 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 8c0a1b42452c..b9a5e2e83850 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 000000000000..b17f859de1c1 --- /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