
com.github.wvengen.maven.proguard.ProGuardMojo Maven / Gradle / Ivy
Show all versions of proguard-maven-plugin
/**
* Pyx4me framework
* Copyright (C) 2006-2008 pyx4j.com.
*
* 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.
*
* @author vlads
* @version $Id$
*/
package com.github.wvengen.maven.proguard;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.maven.archiver.MavenArchiveConfiguration;
import org.apache.maven.archiver.MavenArchiver;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Java;
import org.codehaus.plexus.archiver.jar.JarArchiver;
/**
* Runs ProGuard as part of the build.
*
* @goal proguard
* @phase package
* @description Create small jar files using ProGuard
* @requiresDependencyResolution compile
* @threadSafe
*/
public class ProGuardMojo extends AbstractMojo {
/**
* Set this to 'true' to bypass ProGuard processing entirely.
*
* @parameter property="proguard.skip"
*/
private boolean skip;
/**
* Recursively reads configuration options from the given file.
*
* @parameter default-value="${basedir}/proguard.conf"
*/
private File proguardInclude;
/**
* Select specific ProGuard version from plugin dependencies.
*
* @parameter
*/
private String proguardVersion;
/**
* To run DexGuard instead of ProGuard, set this to 'true'.
*
* @parameter default-value="false"
*/
private boolean useDexGuard;
/**
* ProGuard configuration options
*
* @parameter
*/
private String[] options;
/**
* Specifies whether to obfuscate the input class files.
*
* @parameter default-value="true"
*/
private boolean obfuscate;
/**
* Specifies that project compile dependencies be added as {@code -libraryjars} to ProGuard arguments. Dependency itself is
* not included in resulting jar unless you set {@link #includeDependencyInjar} to 'true'.
*
* @parameter default-value="true"
*/
private boolean includeDependency;
/**
* @parameter expression="${project.build.directory}/tempLibraryjars"
* @readonly
*/
private File tempLibraryjarsDir;
/**
* Specifies to copy all the {@code -libraryjars} dependencies into a temporary directory and pass that directory
* as the only {@code -libraryjars} argument to ProGuard.
*
* @parameter default-value="false"
*/
private boolean putLibraryJarsInTempDir;
/**
* Use this parameter if your command line arguments become too long and execution fails.
*
* If this parameter is 'true', the configuration is passed to the ProGuard process through a file, instead of through
* command line arguments. This bypasses the operating system restrictions on the length of the command line arguments.
*
* @parameter default-value="false"
*/
private boolean generateTemporaryConfigurationFile;
/**
* @parameter expression="${project.build.directory}/generated-proguard.conf"
* @readonly
*/
private File temporaryConfigurationFile;
/**
* Specifies that project compile dependencies should be added as {@code -injars}.
*
* @parameter default-value="false"
*/
private boolean includeDependencyInjar;
/**
* Bundle project dependency to resulting jar. Specifies list of artifact inclusions.
*
* @parameter
*/
private Assembly assembly;
/**
* Additional {@code -libraryjars} e.g. ${java.home}/lib/rt.jar
. Project compile dependency are added automatically,
* see {@link #exclusions}.
*
* @parameter
*/
private List libs;
/**
* List of dependency exclusions
*
* @parameter
*/
private List exclusions;
/**
* Specifies the input jar name (or wars, ears, zips) of the application to be
* processed.
*
* You may specify a classes directory e.g. 'classes'. This way the plugin will process
* the classes instead of the jar. You would need to bind the execution to phase 'compile'
* or 'process-classes' in this case.
*
* @parameter expression="${project.build.finalName}.jar"
* @required
*/
protected String injar;
/**
* Set this to 'true' to bypass ProGuard processing when injar does not exists.
*
* @parameter default-value="false"
*/
private boolean injarNotExistsSkip;
/**
* Apply ProGuard classpathentry filters to input jar. e.g. !**.gif,!**/tests/**
*
* @parameter
*/
protected String inFilter;
/**
* Apply ProGuard classpathentry filters to all input lib jars. e.g. {@code !META-INF/**,!META-INF/versions/9/**.class}
*
* @parameter
*/
protected String inLibsFilter;
/**
* Specifies the names of the output jars. If not set, the input jar is overwritten.
*
*
If {@link #attach} is 'true' the value is ignored and the name is constructed based on classifier.
*
* @parameter
*/
protected String outjar;
/**
* Apply ProGuard classpathentry filters to output jar. e.g. !**.gif,!**/tests/**
*
* @parameter
*/
protected String outFilter;
/**
* Specifies whether to attach the created artifact to the project.
*
* @parameter default-value="false"
*/
private boolean attach;
/**
* Determines if {@link #attach} also attaches the {@link #mappingFileName} file.
*
* @parameter default-value="false"
*/
private boolean attachMap;
/**
* Determines if {@link #attach} also attaches the {@link #seedFileName} file.
*
* @parameter default-value="false"
*/
private boolean attachSeed;
/**
* Specifies attach artifact type.
*
* @parameter default-value="jar"
*/
private String attachArtifactType;
/**
* Specifies attach artifact Classifier, ignored if {@link #attach} is 'false'.
*
* @parameter default-value="small"
*/
private String attachArtifactClassifier;
/**
* Whether to append the {@link #attachArtifactClassifier} to the artifact final name.
*
* @parameter default-value="true"
*/
private boolean appendClassifier;
/**
* Whether to include {@code META-INF/MANIFEST.MF} file
*
* @parameter default-value="false"
*/
private boolean addManifest;
/**
* Whether to include {@code META-INF/maven/**} Maven descriptor.
*
* @parameter default-value="false"
*/
private boolean addMavenDescriptor;
/**
* Directory containing the input and generated JAR.
*
* @parameter property="project.build.directory"
* @required
*/
protected File outputDirectory;
/**
* The Maven project reference where the plugin is currently being executed. The default value is populated from
* Maven.
*
* @parameter property="project"
* @readonly
* @required
*/
protected MavenProject mavenProject;
/**
* The plugin dependencies.
*
* @parameter property="plugin.artifacts"
* @required
* @readonly
*/
protected List pluginArtifacts;
/**
* @component
*/
private MavenProjectHelper projectHelper;
/**
* The Jar archiver.
*
* @component role="org.codehaus.plexus.archiver.Archiver" roleHint="jar"
* @required
*/
private JarArchiver jarArchiver;
/**
* The Maven archive configuration to use. Only if {@link #assembly} is used.
*
* @parameter
*/
protected MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
/**
* The max memory the forked Java process should use, e.g. 256m
*
* @parameter
*/
protected String maxMemory;
/**
* ProGuard main class name.
*
* @parameter default-value="proguard.ProGuard"
*/
protected String proguardMainClass = "proguard.ProGuard";
/**
* Sets the name of the ProGuard mapping file.
*
* @parameter default-value="proguard_map.txt"
*/
protected String mappingFileName = "proguard_map.txt";
/**
* Sets the name of the ProGuard seed file.
*
* @parameter default-value="proguard_seed.txt"
*/
protected String seedFileName = "proguard_seeds.txt";
/**
* Sets the name of the ProGuard {@code -applymapping} file.
*
* @parameter
*/
protected File applyMappingFile;
/**
* Specifies whether to enable
* incremental obfuscation
*
* @parameter default-value="false"
*/
private boolean incremental;
/**
* The ProGuard jar to use. Useful for using beta versions of
* ProGuard that aren't yet on Maven Central.
*
* @parameter
*/
protected File proguardJar;
/**
* If the plugin should be silent.
*
* @parameter default-value="false"
*/
private boolean silent;
/**
* Bind ProGuard output to Maven plugin logging.
*
* @parameter default-value="false"
*/
private boolean bindToMavenLogging;
private Log log;
/**
* ProGuard filter which excludes the {@code MANIFEST.MF} file
*/
private static final String MANIFEST_FILTER = "!META-INF/MANIFEST.MF";
/**
* ProGuard filter which excludes the Maven descriptors in the {@code META-INF/maven/} directory
*/
private static final String MAVEN_DESCRIPTORS_FILTER = "!META-INF/maven/**";
/**
* ProGuard docs: Names with special characters like spaces and parentheses must be quoted with single or double
* quotes.
*/
private String fileNameToString(String fileName) {
return "'" + fileName + "'";
}
private String fileToString(File file) {
return fileNameToString(file.toString());
}
/**
* Creates a ProGuard classpath filter string.
*/
private String createFilterString(List names) {
if (names.isEmpty()) {
return "";
}
return "(" + String.join(",", names) + ")";
}
private String createFilterString(String... names) {
return createFilterString(Arrays.asList(names));
}
private String libFileToStringWithInLibsFilter(File file) {
return libFileToStringWithInLibsFilter(file.toString());
}
private String libFileToStringWithInLibsFilter(String file) {
StringBuilder filter = new StringBuilder(fileNameToString(file));
if ((inLibsFilter != null)) {
filter.append(createFilterString(inLibsFilter));
}
return filter.toString();
}
private boolean useArtifactClassifier() {
return appendClassifier && ((attachArtifactClassifier != null) && (attachArtifactClassifier.length() > 0));
}
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
log = getLog();
if (skip) {
log.info("Bypass ProGuard processing because \"proguard.skip=true\"");
return;
}
boolean mainIsJar = mavenProject.getPackaging().equals("jar");
File inJarFile = new File(outputDirectory, injar);
if (!inJarFile.exists()) {
if (injarNotExistsSkip) {
log.info("Bypass ProGuard processing because \"injar\" dos not exist");
return;
} else if (mainIsJar) {
throw new MojoFailureException("Can't find file " + inJarFile);
}
}
if (!outputDirectory.exists()) {
if (!outputDirectory.mkdirs()) {
throw new MojoFailureException("Can't create " + outputDirectory);
}
}
File outJarFile;
boolean sameArtifact;
if (attach) {
outjar = nameNoType(injar);
if (useArtifactClassifier()) {
outjar += "-" + attachArtifactClassifier;
}
outjar += "." + attachArtifactType;
}
if ((outjar != null) && (!outjar.equals(injar))) {
sameArtifact = false;
outJarFile = (new File(outputDirectory, outjar)).getAbsoluteFile();
if (outJarFile.exists()) {
if (!deleteFileOrDirectory(outJarFile)) {
throw new MojoFailureException("Can't delete " + outJarFile);
}
}
} else {
sameArtifact = true;
outJarFile = inJarFile.getAbsoluteFile();
File baseFile;
if (inJarFile.isDirectory()) {
baseFile = new File(outputDirectory, nameNoType(injar) + "_proguard_base");
} else {
baseFile = new File(outputDirectory, nameNoType(injar) + "_proguard_base.jar");
}
if (baseFile.exists()) {
if (!deleteFileOrDirectory(baseFile)) {
throw new MojoFailureException("Can't delete " + baseFile);
}
}
if (inJarFile.exists()) {
if (!inJarFile.renameTo(baseFile)) {
throw new MojoFailureException("Can't rename " + inJarFile);
}
}
inJarFile = baseFile;
}
ArrayList args = new ArrayList();
ArrayList libraryJars = new ArrayList();
if (log.isDebugEnabled()) {
@SuppressWarnings("unchecked")
List dependancy = mavenProject.getCompileArtifacts();
for (Artifact artifact : dependancy) {
log.debug("--- compile artifact " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
+ artifact.getType() + ":" + artifact.getClassifier() + " Scope:" + artifact.getScope());
}
@SuppressWarnings("unchecked")
final Set artifacts = mavenProject.getArtifacts();
for (Artifact artifact : artifacts) {
log.debug("--- artifact " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
+ artifact.getType() + ":" + artifact.getClassifier() + " Scope:" + artifact.getScope());
}
@SuppressWarnings("unchecked")
final List dependencies = mavenProject.getDependencies();
for (Dependency artifact : dependencies) {
log.debug("--- dependency " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
+ artifact.getType() + ":" + artifact.getClassifier() + " Scope:" + artifact.getScope());
}
}
Set inPath = new HashSet();
Map injars = new HashMap();
Map libraryjars = new HashMap();
boolean hasInclusionLibrary = false;
if (assembly != null && assembly.inclusions != null) {
for (Inclusion inc : assembly.inclusions) {
for (Artifact artifact : getDependencies(inc, mavenProject)) {
if (inc.library) {
if (!injars.containsKey(artifact)) {
libraryjars.put(artifact, inc);
}
} else {
injars.put(artifact, inc);
if (libraryjars.containsKey(artifact)) {
libraryjars.remove(artifact);
}
}
}
}
for (Entry entry : injars.entrySet()) {
log.info("--- ADD injars:" + entry.getKey().getArtifactId());
File file = getClasspathElement(entry.getKey(), mavenProject);
inPath.add(file.toString());
StringBuilder filter = new StringBuilder(fileToString(file));
List filterList = new ArrayList<>();
if (!addManifest) {
filterList.add(MANIFEST_FILTER);
}
if (!addMavenDescriptor) {
filterList.add(MAVEN_DESCRIPTORS_FILTER);
}
if (entry.getValue().filter != null) {
filterList.add(entry.getValue().filter);
}
filter.append(createFilterString(filterList));
args.add("-injars");
args.add(filter.toString());
}
for (Entry entry : libraryjars.entrySet()) {
log.info("--- ADD libraryjars:" + entry.getKey().getArtifactId());
File file = getClasspathElement(entry.getKey(), mavenProject);
hasInclusionLibrary = true;
inPath.add(file.toString());
if (putLibraryJarsInTempDir) {
libraryJars.add(file);
} else {
args.add("-libraryjars");
args.add(libFileToStringWithInLibsFilter(file));
}
}
}
if (inJarFile.exists()) {
args.add("-injars");
StringBuilder filter = new StringBuilder(fileToString(inJarFile));
if ((inFilter != null) || (!addMavenDescriptor)) {
List filterList = new ArrayList<>();
if (!addMavenDescriptor) {
filterList.add(MAVEN_DESCRIPTORS_FILTER);
}
if (inFilter != null) {
filterList.add(inFilter);
}
filter.append(createFilterString(filterList));
}
args.add(filter.toString());
}
if (includeDependency) {
List dependencyInjarFilterList = new ArrayList<>();
if (!addManifest) {
dependencyInjarFilterList.add(MANIFEST_FILTER);
}
if (!addMavenDescriptor) {
dependencyInjarFilterList.add(MAVEN_DESCRIPTORS_FILTER);
}
if (inFilter != null) {
dependencyInjarFilterList.add(inFilter);
}
String dependencyInjarFilter = createFilterString(dependencyInjarFilterList);
@SuppressWarnings("unchecked")
List dependency = this.mavenProject.getCompileArtifacts();
for (Artifact artifact : dependency) {
// dependency filter
if (isExclusion(artifact)) {
continue;
}
File file = getClasspathElement(artifact, mavenProject);
if (inPath.contains(file.toString())) {
log.debug("--- ignore library since one in injar:" + artifact.getArtifactId());
continue;
}
if (includeDependencyInjar) {
log.debug("--- ADD library as injars:" + artifact.getArtifactId());
args.add("-injars");
args.add(fileToString(file) + dependencyInjarFilter);
} else {
log.debug("--- ADD libraryjars:" + artifact.getArtifactId());
if (putLibraryJarsInTempDir) {
libraryJars.add(file);
} else {
args.add("-libraryjars");
args.add(libFileToStringWithInLibsFilter(file));
}
}
}
}
if (args.contains("-injars")) {
args.add("-outjars");
StringBuilder filter = new StringBuilder(fileToString(outJarFile));
if (outFilter != null) {
filter.append(createFilterString(outFilter));
}
args.add(filter.toString());
}
if (!obfuscate) {
args.add("-dontobfuscate");
}
if (proguardInclude != null) {
if (proguardInclude.exists()) {
args.add("-include");
args.add(fileToString(proguardInclude));
log.debug("proguardInclude " + proguardInclude);
} else {
log.debug("proguardInclude config does not exists " + proguardInclude);
}
}
if (libs != null) {
for (String lib : libs) {
if (putLibraryJarsInTempDir) {
libraryJars.add(new File(lib));
} else {
args.add("-libraryjars");
args.add(libFileToStringWithInLibsFilter(lib));
}
}
}
if (!libraryJars.isEmpty()) {
log.debug("Copy libraryJars to temporary directory");
log.debug("Temporary directory: " + tempLibraryjarsDir);
if (tempLibraryjarsDir.exists()) {
try {
FileUtils.deleteDirectory(tempLibraryjarsDir);
} catch (IOException ignored) {
throw new MojoFailureException("Deleting failed libraryJars directory", ignored);
}
}
tempLibraryjarsDir.mkdir();
if (!tempLibraryjarsDir.exists()) {
throw new MojoFailureException(
"Can't create temporary libraryJars directory: " + tempLibraryjarsDir.getAbsolutePath());
}
// Use this subdirectory for all libraries that are files, and not directories themselves
File commonDir = new File(tempLibraryjarsDir, "0");
commonDir.mkdir();
int directoryIndex = 1;
for (File libraryJar : libraryJars) {
try {
log.debug("Copying library: " + libraryJar);
if (libraryJar.isFile()) {
FileUtils.copyFileToDirectory(libraryJar, commonDir);
} else {
File subDir = new File(tempLibraryjarsDir, String.valueOf(directoryIndex));
FileUtils.copyDirectory(libraryJar, subDir);
args.add("-libraryjars");
args.add(libFileToStringWithInLibsFilter(subDir));
}
} catch (IOException e) {
throw new MojoFailureException("Can't copy to temporary libraryJars directory", e);
}
directoryIndex++;
}
args.add("-libraryjars");
args.add(libFileToStringWithInLibsFilter(commonDir));
}
File mappingFile = new File(outputDirectory, mappingFileName);
args.add("-printmapping");
args.add(fileToString(mappingFile.getAbsoluteFile()));
args.add("-printseeds");
args.add(fileToString((new File(outputDirectory, seedFileName).getAbsoluteFile())));
if (incremental && applyMappingFile == null) {
throw new MojoFailureException("applyMappingFile is required if incremental is true");
}
if (applyMappingFile != null && (!incremental || applyMappingFile.exists())) {
args.add("-applymapping");
args.add(fileToString(applyMappingFile.getAbsoluteFile()));
}
if (log.isDebugEnabled()) {
args.add("-verbose");
}
if (options != null) {
Collections.addAll(args, options);
}
if(generateTemporaryConfigurationFile) {
log.info("building config file");
StringBuilder stringBuilder = new StringBuilder();
for (String arg : args) {
if (arg.startsWith("-")) {
stringBuilder.append("\n");
} else {
stringBuilder.append(" ");
}
stringBuilder.append(arg);
}
try (FileWriter writer = new FileWriter(temporaryConfigurationFile);) {
IOUtils.write(stringBuilder.toString(), writer);
} catch (IOException e) {
throw new MojoFailureException("cannot write to temporary configuration file " + temporaryConfigurationFile, e);
}
args = new ArrayList();
args.add("-include");
args.add(fileToString(temporaryConfigurationFile));
}
log.info("execute ProGuard " + args.toString());
proguardMain(getProguardJars(this), args, this);
if (!libraryJars.isEmpty()) {
deleteFileOrDirectory(tempLibraryjarsDir);
}
if ((assembly != null) && (hasInclusionLibrary)) {
log.info("creating assembly");
File baseFile = new File(outputDirectory, nameNoType(injar) + "_proguard_result.jar");
if (baseFile.exists()) {
if (!baseFile.delete()) {
throw new MojoFailureException("Can't delete " + baseFile);
}
}
File archiverFile = outJarFile.getAbsoluteFile();
if (!outJarFile.renameTo(baseFile)) {
throw new MojoFailureException("Can't rename " + outJarFile);
}
MavenArchiver archiver = new MavenArchiver();
archiver.setArchiver(jarArchiver);
archiver.setOutputFile(archiverFile);
archive.setAddMavenDescriptor(addMavenDescriptor);
try {
jarArchiver.addArchivedFileSet(baseFile);
for (Entry entry : libraryjars.entrySet()) {
File file;
file = getClasspathElement(entry.getKey(), mavenProject);
if (file.isDirectory()) {
getLog().info("merge project: " + entry.getKey() + " " + file);
jarArchiver.addDirectory(file);
} else {
getLog().info("merge artifact: " + entry.getKey());
// Respect filter if set
String filter = entry.getValue().filter;
if(filter == null) {
jarArchiver.addArchivedFileSet(file);
} else {
// Filter elements must be separated int two lists
List includes = new ArrayList();
List excludes = new ArrayList();
// Elements starting with ! should be excluded while others should be included
for(String element : filter.split(",")) {
if(element.startsWith("!")) {
excludes.add(element.substring(1));
}else {
includes.add(element);
}
}
// Null is important on empty includes otherwise nothing gets included
jarArchiver.addArchivedFileSet(file,
(includes.isEmpty() ? null : includes.toArray(new String[0])),
(excludes.isEmpty() ? null : excludes.toArray(new String[0])));
}
}
}
archiver.createArchive(mavenProject, archive);
} catch (Exception e) {
throw new MojoExecutionException("Unable to create jar", e);
}
}
if (incremental) {
log.info("Merging mapping file into " + applyMappingFile);
try {
FileInputStream mappingFileIn = new FileInputStream(mappingFile);
try {
applyMappingFile.getParentFile().mkdirs();
FileOutputStream mappingFileOut = new FileOutputStream(applyMappingFile, true);
try {
IOUtils.copy(mappingFileIn, mappingFileOut);
} finally {
mappingFileOut.close();
}
} finally {
mappingFileIn.close();
}
} catch (IOException e) {
throw new MojoExecutionException("Unable to merge mapping file", e);
}
}
if (attach) {
if (!sameArtifact) {
final String classifier;
if (useArtifactClassifier()) {
classifier = attachArtifactClassifier;
} else {
classifier = null;
}
projectHelper.attachArtifact(mavenProject, attachArtifactType, classifier, outJarFile);
}
final String mainClassifier = useArtifactClassifier() ? attachArtifactClassifier : null;
final File buildOutput = new File(mavenProject.getBuild().getDirectory());
if (attachMap) {
attachTextFile(new File(buildOutput, mappingFileName), mainClassifier, "map");
}
if (attachSeed) {
attachTextFile(new File(buildOutput, seedFileName), mainClassifier, "seed");
}
}
}
private void attachTextFile(File theFile, String mainClassifier, String suffix) {
final String classifier = (null == mainClassifier ? "" : mainClassifier + "-") + suffix;
log.info("Attempting to attach " + suffix + " artifact");
if (theFile.exists()) {
if (theFile.isFile()) {
projectHelper.attachArtifact(mavenProject, "txt", classifier, theFile);
} else {
log.warn("Cannot attach file because it is not a file: " + theFile);
}
} else {
log.warn("Cannot attach file because it does not exist: " + theFile);
}
}
private Set getAllPluginArtifactDependencies(ProGuardMojo mojo) throws MojoExecutionException {
Set files = new HashSet<>(getProguardJars(mojo));
for (Artifact artifact : mojo.pluginArtifacts) {
files.add(artifact.getFile().getAbsoluteFile());
files.addAll(getChildArtifacts(artifact));
}
return files;
}
private Set getChildArtifacts(Artifact artifact) {
Set files = new HashSet<>();
for (Object child : artifact.getDependencyTrail()) {
if (child instanceof Artifact) {
files.add(((Artifact) child).getFile().getAbsoluteFile());
files.addAll(getChildArtifacts((Artifact) child));
}
}
return files;
}
private List getProguardJars(ProGuardMojo mojo) throws MojoExecutionException {
if (proguardJar != null) {
if (proguardJar.exists()) {
if (proguardJar.isFile()) {
return Collections.singletonList(proguardJar);
} else {
mojo.getLog().warn("proguard jar (" + proguardJar + ") is not a file");
throw new MojoExecutionException("proguard jar (" + proguardJar + ") is not a file");
}
} else {
mojo.getLog().warn("proguard jar (" + proguardJar + ") does not exist");
throw new MojoExecutionException("proguard jar (" + proguardJar + ") does not exist");
}
}
List proguardArtifacts = new ArrayList();
int proguardArtifactDistance = -1;
// This should be solved in Maven 2.1
//Starting in v. 7.0.0., proguard got split up in proguard-base and proguard-core,
//both of which need to be on the classpath.
for (Artifact artifact : mojo.pluginArtifacts) {
mojo.getLog().debug("pluginArtifact: " + artifact.getFile());
final String artifactId = artifact.getArtifactId();
if (artifactId.startsWith((useDexGuard ? "dexguard" : "proguard"))
&& !artifactId.startsWith("proguard-maven-plugin")) {
int distance = artifact.getDependencyTrail().size();
mojo.getLog().debug("proguard DependencyTrail: " + distance);
// Skip dependency if proguardVersion is defined and dependency does not match given version
if ((mojo.proguardVersion != null) && (!mojo.proguardVersion.equals(artifact.getVersion()))) {
continue;
}
/*
* Check if artifact has been defined twice - eg. no proguardVersion given but dependency for proguard
* defined in plugin config
*/
for (Artifact existingArtifact : proguardArtifacts) {
if(existingArtifact.getArtifactId().equals(artifactId)) {
mojo.getLog().warn("Dependency for proguard defined twice! This may lead to unexpected results: "
+ existingArtifact.getArtifactId() + ":" + existingArtifact.getVersion()
+ " | "
+ artifactId + ":" + artifact.getVersion());
break;
}
}
if ((mojo.proguardVersion != null) && (mojo.proguardVersion.equals(artifact.getVersion()))) {
proguardArtifacts.add(artifact);
} else if (proguardArtifactDistance == -1) {
proguardArtifacts.add(artifact);
proguardArtifactDistance = distance;
} else if (distance <= proguardArtifactDistance) {
Iterator it = proguardArtifacts.iterator();
while (it.hasNext()) {
Artifact art = it.next();
if (distance < art.getDependencyTrail().size())
it.remove();
}
proguardArtifacts.add(artifact);
proguardArtifactDistance = distance;
}
}
}
if (!proguardArtifacts.isEmpty()) {
List resList = new ArrayList(proguardArtifacts.size());
for (Artifact p : proguardArtifacts) {
mojo.getLog().debug("proguardArtifact: " + p.getFile());
resList.add(p.getFile().getAbsoluteFile());
}
return resList;
}
mojo.getLog().info((useDexGuard ? "dexguard" : "proguard") + " jar not found in pluginArtifacts");
ClassLoader cl;
cl = mojo.getClass().getClassLoader();
// cl = Thread.currentThread().getContextClassLoader();
String classResource = "/" + mojo.proguardMainClass.replace('.', '/') + ".class";
URL url = cl.getResource(classResource);
if (url == null) {
throw new MojoExecutionException(
"Obfuscation failed ProGuard (" + mojo.proguardMainClass + ") not found in classpath");
}
String proguardJar = url.toExternalForm();
if (proguardJar.startsWith("jar:file:")) {
proguardJar = proguardJar.substring("jar:file:".length());
proguardJar = proguardJar.substring(0, proguardJar.indexOf('!'));
} else {
throw new MojoExecutionException("Unrecognized location (" + proguardJar + ") in classpath");
}
return Collections.singletonList(new File(proguardJar));
}
private void proguardMain(Collection proguardJars, List argsList, ProGuardMojo mojo)
throws MojoExecutionException {
Java java = new Java();
Project antProject = new Project();
antProject.setName(mojo.mavenProject.getName());
antProject.init();
DefaultLogger antLogger;
if (bindToMavenLogging) {
antLogger = new MavenloggingBinder(mojo.log);
} else {
antLogger = new DefaultLogger();
antLogger.setOutputPrintStream(System.out);
antLogger.setErrorPrintStream(System.err);
}
int logLevel = mojo.log.isDebugEnabled() ? Project.MSG_DEBUG : Project.MSG_INFO;
antLogger.setMessageOutputLevel(silent ? Project.MSG_ERR : logLevel);
antProject.addBuildListener(antLogger);
antProject.setBaseDir(mojo.mavenProject.getBasedir());
java.setProject(antProject);
java.setTaskName("proguard");
mojo.getLog().info("proguard jar: " + proguardJars);
Set allDependencyFiles = getAllPluginArtifactDependencies(mojo);
for (File p : allDependencyFiles)
java.createClasspath().createPathElement().setLocation(p);
// java.createClasspath().setPath(System.getProperty("java.class.path"));
java.setClassname(mojo.proguardMainClass);
java.setFailonerror(true);
java.setFork(true);
// get the maxMemory setting
if (mojo.maxMemory != null) {
java.setMaxmemory(mojo.maxMemory);
}
for (String arg : argsList) {
java.createArg().setValue(arg);
}
int result = java.executeJava();
if (result != 0) {
throw new MojoExecutionException("Obfuscation failed (result=" + result + ")");
}
}
private String nameNoType(String fileName) {
int extStart = fileName.lastIndexOf('.');
if (extStart == -1) {
return fileName;
}
return fileName.substring(0, extStart);
}
private boolean deleteFileOrDirectory(File path) throws MojoFailureException {
if (path.isDirectory()) {
File[] files = path.listFiles();
if (null != files) {
for (File file : files) {
if (file.isDirectory()) {
if (!deleteFileOrDirectory(file)) {
throw new MojoFailureException("Can't delete dir " + file);
}
} else {
if (!file.delete()) {
throw new MojoFailureException("Can't delete file " + file);
}
}
}
}
return path.delete();
} else {
return path.delete();
}
}
private Set getDependencies(final Inclusion inc, MavenProject mavenProject)
throws MojoExecutionException {
@SuppressWarnings("unchecked")
Set dependencies = mavenProject.getArtifacts();
Set result = new HashSet();
for (Artifact artifact : dependencies) {
if (inc.match(artifact)) {
result.add(artifact);
}
}
if (result.isEmpty()) {
log.warn(String.format("No artifact found : %s:%s", inc.artifactId, inc.groupId));
}
return result;
}
private boolean isExclusion(Artifact artifact) {
if (exclusions == null) {
return false;
}
for (Exclusion excl : exclusions) {
if (excl.match(artifact)) {
return true;
}
}
return false;
}
private File getClasspathElement(Artifact artifact, MavenProject mavenProject) throws MojoExecutionException {
if (artifact.getClassifier() != null) {
return artifact.getFile();
}
String refId = artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion();
MavenProject project = (MavenProject) mavenProject.getProjectReferences().get(refId);
if (project == null) {
refId = artifact.getGroupId() + ":" + artifact.getArtifactId();
project = (MavenProject) mavenProject.getProjectReferences().get(refId);
}
if (project != null) {
File file = new File(project.getBuild().getOutputDirectory());
log.debug("Found directory: " + file.getAbsolutePath());
return file;
} else {
File file = artifact.getFile();
log.debug("Found file: " + file.getAbsolutePath());
if ((file == null) || (!file.exists())) {
throw new MojoExecutionException("Dependency Resolution Required " + artifact);
}
return file;
}
}
}