Commit 88cbda74 authored by Daniel Bluhm's avatar Daniel Bluhm

Add basic maven plugin for dependency scraping

Plugin searches through dependency jars for files matching filtering
configurable criteria.
Signed-off-by: Daniel Bluhm's avatarDaniel Bluhm <bluhmdj@ornl.gov>
parent 8859332e
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes>
<attribute name="test" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.eclipse.ice.dev.dependencyscraper</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>org.eclipse.ice.dev</artifactId>
<groupId>org.eclipse.ice</groupId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.ice.dev.dependencyscraper</artifactId>
<packaging>maven-plugin</packaging>
<name>org.eclipse.ice.dev.dependencyscraper Maven Plugin</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<prerequisites>
<maven>${maven.version}</maven>
</prerequisites>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.release>11</maven.compiler.release>
<maven.version>3.3.9</maven.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>${maven.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>${maven.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<version>${maven.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-compat</artifactId>
<version>${maven.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.6.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-testing</groupId>
<artifactId>maven-plugin-testing-harness</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_maven-plugin_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.6.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<plugin>
<artifactId>maven-invoker-plugin</artifactId>
<version>3.1.0</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<!-- <goalPrefix>maven-archetype-plugin</goalPrefix> -->
<skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
</configuration>
<executions>
<execution>
<id>mojo-descriptor</id>
<goals>
<goal>descriptor</goal>
</goals>
</execution>
<execution>
<id>help-goal</id>
<goals>
<goal>helpmojo</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>run-its</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-invoker-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<debug>true</debug>
<cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
<pomIncludes>
<pomInclude>*/pom.xml</pomInclude>
</pomIncludes>
<postBuildHookScript>verify</postBuildHookScript>
<localRepositoryPath>${project.build.directory}/local-repo</localRepositoryPath>
<settingsFile>src/it/settings.xml</settingsFile>
<goals>
<goal>clean</goal>
<goal>test-compile</goal>
</goals>
</configuration>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>install</goal>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<profiles>
<profile>
<id>it-repo</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>local.central</id>
<url>@localRepositoryUrl@</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>local.central</id>
<url>@localRepositoryUrl@</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</settings>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.ice.it</groupId>
<artifactId>simple-it</artifactId>
<version>1.0-SNAPSHOT</version>
<description>A simple IT verifying the basic use case.</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>@project.groupId@</groupId>
<artifactId>@project.artifactId@</artifactId>
<version>@project.version@</version>
<executions>
<execution>
<id>touch</id>
<phase>validate</phase>
<goals>
<goal>touch</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
File touchFile = new File( basedir, "target/touch.txt" );
assert touchFile.isFile()
/*******************************************************************************
* Copyright (c) 2020- UT-Battelle, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Daniel Bluhm - Initial implementation
*******************************************************************************/
package org.org.eclipse.ice.dev.dependencyscraper;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.maven.artifact.Artifact;
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.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
/**
* Maven plugin used to search dependencies for files matching filtering rules
* and copy to output directory.
*/
@Mojo(
name = "scrape",
defaultPhase = LifecyclePhase.PROCESS_SOURCES,
requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME
)
public class MyMojo extends AbstractMojo {
/**
* Current project. Used to get handle to dependencies.
*/
@Parameter(
defaultValue = "${project}",
readonly = true,
required = true
)
private MavenProject project;
/**
* Directory to output scraped files.
*/
@Parameter(
property = "outputDirectory",
defaultValue = "frontend/src",
required = false
)
private File outputDirectory;
@Parameter(
property = "sourceDirectory",
defaultValue = "frontend",
required = false
)
private String sourceDirectory;
/**
* List of files to include. May contain wildcards, i.e. <code>*</code> or
* <code>**{@literal /}*.js</code>.
*/
@Parameter(
property = "includes",
required = true
)
private List<String> includes;
/**
* Filter for whether a ZipEntry begins with the sourceDirectory.
* @param file to test
* @return if ZipEntry path begins with sourceDirectory.
*/
private boolean startsWithSourceDirectory(ZipEntry file) {
return file.getName()
.toLowerCase(Locale.ENGLISH)
.startsWith(sourceDirectory.toLowerCase(Locale.ENGLISH));
}
/**
* Filter for whether a ZipEntry should be included.
* @param file to test
* @return if it should be included
*/
private boolean shouldInclude(ZipEntry file) {
return includes.stream()
.anyMatch(
include -> FilenameUtils.wildcardMatch(file.getName(), include)
);
}
/**
* Copy file from jar to output directory.
* @param jar from which file will be copied
* @param fileInJar file to copy
* @throws MojoFailureException if file could not be copied, inadequate
* permissions, etc.
*/
private void copyFileInJarToOutput(
JarFile jar, ZipEntry fileInJar
) throws MojoFailureException {
String fullPath = fileInJar.getName();
// Drop sourceDirectory from file path
String relativePath = fullPath
.substring(fullPath.toLowerCase(Locale.ENGLISH)
.indexOf(sourceDirectory.toLowerCase(Locale.ENGLISH))
+ sourceDirectory.length());
// Determine output path
File target = new File(outputDirectory, relativePath);
try {
if (target.exists()) {
// Replace target if contents differ
File tempFile = File.createTempFile(fullPath, null);
FileUtils.copyInputStreamToFile(
jar.getInputStream(fileInJar), tempFile
);
if (!FileUtils.contentEquals(tempFile, target)) {
FileUtils.forceDelete(target);
FileUtils.moveFile(tempFile, target);
} else {
tempFile.delete();
}
} else {
FileUtils.copyInputStreamToFile(
jar.getInputStream(fileInJar),
target
);
}
} catch (IOException e) {
throw new MojoFailureException(
"Failed to copy file " + fileInJar.getName()
);
}
}
/**
* Collect Jar files from dependencies and search through them for files
* matching the parameters of the plugin. Copy matching files into output
* directory specified by parameters.
*/
public void execute() throws MojoExecutionException, MojoFailureException {
Set<File> jarFiles = project.getArtifacts().stream()
.filter(artifact -> "jar".equals(artifact.getType()))
.map(Artifact::getFile).collect(Collectors.toSet());
for (File jar : jarFiles) {
try (JarFile jarFile = new JarFile(jar, false)) {
Set<ZipEntry> toCopy = jarFile.stream()
.filter(file -> !file.isDirectory())
.filter(this::startsWithSourceDirectory)
.filter(this::shouldInclude)
.collect(Collectors.toSet());
for (ZipEntry file : toCopy) {
getLog().info(String.format("Copying %s", file.getName()));
copyFileInJarToOutput(jarFile, file);
}
} catch (IOException e) {
throw new MojoFailureException(
String.format("Failed to open jar file %s!", jar.toString())
);
}
}
}
}
\ No newline at end of file
package org.org.eclipse.ice.dev.dependencyscraper;
import org.apache.maven.plugin.testing.MojoRule;
import org.apache.maven.plugin.testing.WithoutMojo;
import org.junit.Rule;
import static org.junit.Assert.*;
import org.junit.Test;
import java.io.File;
public class MyMojoTest
{
@Rule
public MojoRule rule = new MojoRule() {
@Override
protected void before() throws Throwable { }
@Override
protected void after() { }
};
/**
* @throws Exception if any
*/
@Test
public void testSomething() throws Exception {
File pom = new File("target/test-classes/project-to-test/");
assertNotNull(pom);
assertTrue(pom.exists());
MyMojo myMojo = (MyMojo) rule.lookupConfiguredMojo(pom, "scrape");
assertNotNull(myMojo);
myMojo.execute();
// File outputDirectory = (File) rule.getVariableValueFromObject(myMojo, "outputDirectory");
// assertNotNull(outputDirectory);
// assertTrue(outputDirectory.exists());
//
// File touch = new File(outputDirectory, "touch.txt");
// assertTrue(touch.exists());
}
/** Do not need the MojoRule. */
@WithoutMojo
@Test
public void testSomethingWhichDoesNotNeedTheMojoAndProbablyShouldBeExtractedIntoANewClassOfItsOwn()
{
assertTrue(true);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.ice</groupId>
<artifactId>org.eclipse.ice.dev.dependencyscraper</artifactId>
<version>3.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Test MyMojo</name>
<build>
<plugins>
<plugin>
<artifactId>maven-my-plugin</artifactId>
<configuration>
<sourceDirectory>META-INF</sourceDirectory>
<includes>
<include>*</include>