Browse Source

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
pull/214/head
Glyn Normington 12 years ago
parent
commit
00a86c3722
  1. 6
      build.gradle
  2. 85
      buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy

6
build.gradle

@ -8,6 +8,11 @@ buildscript { @@ -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) { @@ -954,6 +959,7 @@ configure(rootProject) {
"set GRADLE_OPTS=$gradleBatOpts %GRADLE_OPTS%\nset DEFAULT_JVM_OPTS=")
}
}
}
/*

85
buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy

@ -0,0 +1,85 @@ @@ -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<File, Set<String>> 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<String> getPackagesInDirectory(File dir) {
def pkgs = new HashSet<String>()
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<String> 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())
}
}
}
Loading…
Cancel
Save