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

com.github.dingxin.maven.plugins.proguard.ProguardMojo Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 com.github.dingxin.maven.plugins.proguard;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

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 org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Java;

/**
 * Maven Plugin for ProGuard
 * 
 * @author dingxin
 */
@Mojo(name = "proguard", defaultPhase = LifecyclePhase.PACKAGE, requiresProject = true, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE)
public class ProguardMojo extends AbstractMojo {

	/**
	 * Disables the plugin execution
	 */
	@Parameter(property = "maven.proguard.skip", defaultValue = "false")
	private boolean skip;

	/**
	 * ProGuard options
	 */
	@Parameter
	private String[] options;

	/**
	 * ProGuard configuration file
	 */
	@Parameter(defaultValue = "proguard.conf")
	private File configFile;

	/**
	 * Directory containing the input and generated JAR.
	 */
	@Parameter(property = "project.build.directory")
	private File targetDirectory;

	/**
	 * Specifies the input jar name (or war, apk) of the application to be
	 * processed.
	 *
	 * You may specify a classes directory, e.g. 'classes'.
	 */
	@Parameter
	private String injar;

	/**
	 * Specifies the name of the output jar (or war, apk)
	 */
	@Parameter
	private String outjar;

	/**
	 * ProGuard Filters for the input jar
	 * 
	 * e.g. !module-info.class,!META-INF/maven/**
	 */
	@Parameter(defaultValue = "!module-info.class,!META-INF/maven/**")
	private String inFilter;

	/**
	 * ProGuard Filters for the output jar
	 * 
	 * e.g. !META-INF/maven/**
	 */
	@Parameter(defaultValue = "!META-INF/maven/**")
	private String outFilter;

	/**
	 * Add dependency jars to -libraryjars arguments
	 */
	@Parameter(defaultValue = "true")
	private boolean includeDependency;

	/**
	 * Add dependency jars to -injars arguments
	 */
	@Parameter(defaultValue = "false")
	private boolean includeDependencyInjar;

	/**
	 * ProGuard Filters for dependency jars
	 * 
	 * e.g. !module-info.class
	 */
	@Parameter(defaultValue = "!module-info.class,!META-INF/**")
	private String dependencyFilter;

	/**
	 * Additional -libraryjars
	 * 
	 * e.g. ${java.home}/jmods/java.base.jmod(!**.jar;!module-info.class)
	 * 
	 * e.g. ${java.home}/lib/rt.jar
	 */
	@Parameter
	private List libs;

	/**
	 * The Maven project reference where the plugin is currently being executed.
	 */
	@Parameter(property = "project", readonly = true)
	private MavenProject mavenProject;

	/**
	 * The plugin dependencies.
	 */
	@Parameter(property = "plugin.artifacts", readonly = true)
	private List pluginArtifacts;

	@Override
	public void execute() throws MojoExecutionException, MojoFailureException {
		if (skip) {
			getLog().info("Proguard is skipped.");
			return;
		}

		setDefaultJarName();

		List args = getArgs();
		execProguard(args);
	}

	private void execProguard(List args) throws MojoExecutionException, MojoFailureException {
		getLog().info("Execute ProGuard: " + args.toString());

		File proguardJar = getProguardJar();

		Project antProject = new Project();
		antProject.setName(this.mavenProject.getName());
		antProject.init();

		DefaultLogger antLogger = new DefaultLogger();
		antLogger.setOutputPrintStream(System.out);
		antLogger.setErrorPrintStream(System.err);
		antLogger.setMessageOutputLevel(Project.MSG_INFO);

		antProject.addBuildListener(antLogger);
		antProject.setBaseDir(this.mavenProject.getBasedir());

		Java java = new Java();
		java.setProject(antProject);
		java.setTaskName("proguard");
		java.createClasspath().setLocation(proguardJar);
		java.setClassname("proguard.ProGuard");
		java.setFailonerror(true);
		java.setFork(true);

		for (String arg : args) {
			java.createArg().setValue(arg);
		}

		int result = java.executeJava();
		if (result != 0) {
			throw new MojoExecutionException("ProGuard failed (result=" + result + ")");
		}
		//remove classes/classes when injar and outjar are directory
		File outjarFile = new File(targetDirectory, outjar);
		if(outjarFile.isDirectory()) {
			File classesDir = new File(targetDirectory, outjar + "/classes/classes");
			File classesFile = classesDir.listFiles()[0];
			classesFile.renameTo(new File(targetDirectory, outjar + "/" + classesFile.getName()));
			classesDir.getParentFile().delete();
		}
	}

	private void setDefaultJarName() {
		String type = mavenProject.getPackaging();
		getLog().debug("Package Type: " + type);

		if ("jar".equals(type)) {
			if (injar == null) {
				injar = mavenProject.getBuild().getFinalName() + ".jar";
			}
			if (outjar == null) {
				outjar = injar;
			}
		} else if ("war".equals(type)) {
			if (injar == null) {
				injar = "classes";
			}
			if (outjar == null) {
				outjar = injar;
			}
		}

		getLog().debug("injar " + injar);
		getLog().debug("outjar " + outjar);
	}

	private File getInjarFile() throws MojoFailureException {
		File injarFile = new File(targetDirectory, injar);
		if (!injarFile.exists()) {
			throw new MojoFailureException("Can't find file " + injarFile);
		}

		File tempInjarFile;
		if (injarFile.isDirectory()) {
			tempInjarFile = new File(targetDirectory, nameNoExt(injar) + "_proguard_base");
		} else {
			tempInjarFile = new File(targetDirectory, nameNoExt(injar) + "_proguard_base.jar");
		}
		if (tempInjarFile.exists() && !deleteFile(tempInjarFile)) {
			throw new MojoFailureException("Can't delete " + tempInjarFile);
		}
		if (!injarFile.renameTo(tempInjarFile)) {
			throw new MojoFailureException("Can't rename " + injarFile);
		}
		injarFile = tempInjarFile;

		getLog().debug("injarFile " + injarFile);

		return injarFile;
	}

	private File getOutjarFile() throws MojoFailureException {
		File outjarFile = new File(targetDirectory, outjar);

		if (outjarFile.exists() && !deleteFile(outjarFile)) {
			throw new MojoFailureException("Can't delete " + outjarFile);
		}

		return outjarFile;
	}

	private File getProguardJar() throws MojoFailureException {
		for (Artifact artifact : this.pluginArtifacts) {
			getLog().debug("Plugin Artifact: " + artifact.getFile());
			if (artifact.getArtifactId().equals("proguard-base")) {
				getLog().debug("Proguard Artifact: " + artifact.getFile());
				return artifact.getFile().getAbsoluteFile();
			}
		}
		throw new MojoFailureException("ProGuard not found");
	}

	private List getArgs() throws MojoFailureException {
		List args = new ArrayList<>();
		argForConfigFile(args);
		argForInjar(args);
		argForOutjar(args);
		argForLibs(args);
		argForDependency(args);
		argForOptions(args);

		return args;
	}

	private void argForConfigFile(List args) {
		if (configFile != null && configFile.exists()) {
			args.add("-include");
			args.add(configFile.getAbsolutePath());
			getLog().debug("ProGuard Configuration File: " + configFile);
		}
	}

	private void argForInjar(List args) throws MojoFailureException {
		args.add("-injars");

		String classpath = getInjarFile().getAbsolutePath();
		String filter = "";
		if (inFilter != null) {
			filter = "(" + inFilter + ")";
		}

		args.add(classpath + filter);
	}

	private void argForOutjar(List args) throws MojoFailureException {
		args.add("-outjars");

		String classpath = getOutjarFile().getAbsolutePath();
		String filter = "";
		if (outFilter != null) {
			filter = "(" + outFilter + ")";
		}

		args.add(classpath + filter);
	}

	private void argForLibs(List args) {
		if (libs != null) {
			for (String lib : libs) {
				args.add("-libraryjars");
				args.add(lib);
			}
		}
	}

	private void argForDependency(List args) {
		if (!includeDependency) {
			return;
		}

		String option = "-libraryjars";
		if (includeDependencyInjar) {
			option = "-injars";
		}

		String filter = "";
		if (dependencyFilter != null) {
			filter = "(" + dependencyFilter + ")";
		}

		Set artifacts = this.mavenProject.getArtifacts();
		for (Artifact artifact : artifacts) {
			String classpath = artifact.getFile().getAbsolutePath();
			args.add(option);
			args.add(classpath + filter);
		}
	}

	private void argForOptions(List args) {
		if (options != null) {
			Collections.addAll(args, options);
		} else {
			// Default options
			args.add("-dontoptimize");// 不要优化
			args.add("-keepattributes *Annotation*");// 避免混淆注解
			args.add("-keepattributes Signature");// 避免混淆泛型
			args.add("-keepattributes InnerClasses");// 避免混淆内部类
			args.add("-keepclassmembers class * { @** *; }");// 避免混淆带注解的成员
			args.add("-keepparameternames");// 避免混淆被保护的方法的参数类型和参数名
			args.add("-keep public class * { public protected *; }");// 避免混淆公开的成员
		}
	}

	private String nameNoExt(String fileName) {
		int dotIndex = fileName.lastIndexOf('.');
		if (dotIndex == -1) {
			return fileName;
		} else {
			return fileName.substring(0, dotIndex);
		}
	}

	private boolean deleteFile(File file) {
		if (file.isDirectory()) {
			for (File subFile : file.listFiles()) {
				deleteFile(subFile);
			}
		}
		return file.delete();
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy