All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.xored.maven.integrity.VerifyMojo Maven / Gradle / Ivy

Go to download

Maven plugin, which allows to verify the project's integrity by looking for potential modules, which might have been missed from the build.

The newest version!
package com.xored.maven.integrity;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.DirectoryScanner;
import org.codehaus.plexus.util.MatchPatterns;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Scans project directory structure in attempt to find missed (not included to the build) modules. If it finds any
 * missed modules, it marks the build failed.
 */
@Mojo(name = "verify-modules", defaultPhase = LifecyclePhase.VALIDATE, aggregator = true)
public class VerifyMojo extends AbstractMojo {

	private static final String DEFAULT_INCLUDE = "pom.xml";

	@Parameter(defaultValue = "${reactorProjects}", readonly = true)
	private List reactorProjects;

	@Parameter(defaultValue = "${project}", readonly = true)
	private MavenProject project;

	/**
	 * Specifies, which directories should be considered modules.
	 * Ant patterns, which are applied to paths, relative to the project root. It is implied,
	 * that each pattern starts with **/ and ends with /**. You should not specify this explicitly.
	 * If not specified, each directory, which contains pom.xml file, is a module.
	 * 

Exact module directory is determined by the first path element, which matches the pattern * (not counting the implied **/).

*/ @Parameter(property = "integrity.modules.includes") private String[] includes; /** * Specifies, which directories should not be considered modules, even if they meet includes criteria. * Ant patterns, which are applied to paths, relative to the project root. It is implied, * that each pattern starts and ends with **/. You should not specify this explicitly. *

Exact module directory is determined by the first path element, which matches the pattern * (not counting the implied **/).

*/ @Parameter(property = "integrity.modules.excludes") private String[] excludes; /** * Allows to do case-sensitive search. Use with caution, since this may make builds platform-dependent. */ @Parameter(alias = "case-sensitive", property = "integrity.modules.case-sensitive", defaultValue = "false") private boolean isCaseSensitive; private String[] nativeIncludes; private String[] nativeExcludes; private String[] extendedIncludes; private String[] extendedExcludes; private MatchPatterns[] includesPatterns; private MatchPatterns[] extendedIncludesPatterns; private Set relativeKnownPaths; private String[] targetDirExcludes; private Set missedPaths = new HashSet(); @Override public void execute() throws MojoExecutionException, MojoFailureException { // TODO: is there a better way to execute this mojo only once? if (reactorProjects.isEmpty() || !project.getBasedir().equals(reactorProjects.get(0).getBasedir())) { return; } if (null == includes || 0 == includes.length) { includes = new String[]{DEFAULT_INCLUDE}; } nativeIncludes = PathUtils.toNative(includes); nativeExcludes = PathUtils.toNative(excludes); extendedIncludes = extendPatterns(nativeIncludes, false); extendedExcludes = extendPatterns(nativeExcludes, false); includesPatterns = createMatchPatterns(nativeIncludes); extendedIncludesPatterns = createMatchPatterns(extendedIncludes); Set knownPaths = getKnownPaths(); File root = getRoot(knownPaths); relativeKnownPaths = PathUtils.relativizePaths(root, knownPaths); Set knownTargetPaths = getKnownTargetPaths(); targetDirExcludes = extendPatterns(PathUtils.relativizePaths(root, knownTargetPaths).toArray(new String[0]), true); getLog().info("Detected project root: " + root); getLog().info("Searching for missed modules..."); if (getLog().isDebugEnabled()) { getLog().debug("Found known paths: \n" + finePrint(relativeKnownPaths)); getLog().debug(" Includes: " + Arrays.toString(nativeIncludes)); getLog().debug(" Excludes: " + Arrays.toString(nativeExcludes)); getLog().debug(" Targets: " + Arrays.toString(targetDirExcludes)); } visitDir(root); if (missedPaths.isEmpty()) { getLog().info(" no missed modules found"); } else { String[] missed = PathUtils.toUnix(missedPaths.toArray(new String[0])); Arrays.sort(missed); throw new MojoFailureException(null, getShortFailureMsg(), getLongFailureMsg(missed)); } } private static String getShortFailureMsg() { // for some reason Maven repeats this message 2 times. It looks ugly, so I set it empty return ""; } private static String getLongFailureMsg(String[] missed) { return "Directories below look like modules, but they are not in the build. " + "If they are indeed modules, add them to the build. " + "If they're not modules, modify include/exclude criteria so, that they are not recognized as modules:\n" + finePrint(Arrays.asList(missed)); } private static MatchPatterns[] createMatchPatterns(String[] patterns) { MatchPatterns[] ret = new MatchPatterns[patterns.length]; for (int i = 0; i < patterns.length; ++i) { ret[i] = MatchPatterns.from(patterns[i]); } return ret; } private static String[] extendPatterns(String[] patterns, boolean tailOnly) throws MojoFailureException { if (null == patterns) { return null; } String[] ret = new String[patterns.length]; for (int i = 0; i < patterns.length; ++i) { ret[i] = extendPattern(patterns[i], tailOnly); } return ret; } private static String extendPattern(String pattern, boolean tailOnly) throws MojoFailureException { final String separator = File.separator; final String prefix = "**" + separator; final String suffix = separator + "**"; String ret = pattern; if (ret.startsWith(separator)) { throw new MojoFailureException("Includes/excludes pattern must not start with " + separator + ": " + pattern); } if (ret.endsWith(separator)) { throw new MojoFailureException("Includes/excludes pattern must not end with " + separator + ": " + pattern); } if (ret.startsWith(prefix)) { throw new MojoFailureException("Includes/excludes pattern must not start with " + prefix + ": " + pattern); } if (tailOnly) { return ret + suffix; } else { return prefix + ret + suffix; } } private static String finePrint(Iterable strings) { StringBuilder ret = new StringBuilder(); for (String s : strings) { ret.append("* "); ret.append(s); ret.append("\n"); } return ret.toString(); } private static File getRoot(Set paths) { return new File(PathUtils.getCommonPath(paths.toArray(new String[0]))); } private Set getKnownPaths() { Set ret = new HashSet(); for (MavenProject p : reactorProjects) { ret.add(PathUtils.toNative(p.getBasedir().getAbsolutePath())); } return ret; } private Set getKnownTargetPaths() { Set ret = new HashSet(); for (MavenProject p : reactorProjects) { ret.add(PathUtils.toNative(p.getBuild().getDirectory())); } return ret; } private void visitDir(File dir) { for (File k : getFilesToProcess(dir)) { process(dir, k); } } private List getFilesToProcess(File dir) { DirectoryScanner scanner = new DirectoryScanner(); scanner.setBasedir(dir); scanner.setIncludes(extendedIncludes); List allExcludes = new ArrayList(); allExcludes.addAll(Arrays.asList(extendedExcludes)); allExcludes.addAll(Arrays.asList(targetDirExcludes)); scanner.setExcludes(allExcludes.toArray(new String[0])); scanner.setCaseSensitive(isCaseSensitive); scanner.scan(); List ret = new ArrayList(); for (String n : scanner.getIncludedFiles()) { ret.add(new File(dir, n)); } return ret; } private void process(File dir, File f) { String p = PathUtils.toNative(f.getAbsolutePath()); String rel = PathUtils.relativizePath(dir, p); String module = rel; for (int i = 0; i < extendedIncludesPatterns.length; ++i) { MatchPatterns mp = extendedIncludesPatterns[i]; if (mp.matches(rel, isCaseSensitive)) { module = getUnmatchedPrefix(rel, includesPatterns[i]); break; } } if (!relativeKnownPaths.contains(module)) { missedPaths.add(module); } } private String getUnmatchedPrefix(String path, MatchPatterns p) { final String separator = File.separator; int i = -1; do { i = path.indexOf(separator, i + 1); if (p.matches(path.substring(i + 1), isCaseSensitive)) { return -1 == i ? "" : path.substring(0, i); } } while (i > -1); return path; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy