mpulse.weaving.plugin.MPulsePlugin.groovy Maven / Gradle / Ivy
package mpulse.weaving.plugin
import com.android.build.gradle.AppPlugin
import com.android.build.gradle.LibraryPlugin
import com.android.build.gradle.api.ApplicationVariant
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.artifacts.Configuration
import java.nio.file.Files
import java.nio.file.FileSystemException
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import org.slf4j.Logger
import javax.imageio.ImageIO
class MPulsePlugin implements Plugin {
@Override
void apply(Project project) {
def hasApp = project.plugins.withType(AppPlugin)
def hasLib = project.plugins.withType(LibraryPlugin)
if (!hasApp && !hasLib) {
throw new IllegalStateException("'android' or 'android-library' plugin required.")
}
final def log = project.logger
final def variants
if (hasApp) {
variants = project.android.applicationVariants
} else {
variants = project.android.libraryVariants
}
project.dependencies {
compile 'org.aspectj:aspectjrt:1.8.9'
}
// Will be set in doLast;
Set configurations;
Set dependencies;
def depFiles;
variants.all { variant ->
log.info("variant -----> " + variant)
JavaCompile javaCompile = variant.javaCompile
javaCompile.doFirst {
log.info("MPulsePlugin: Before processResources, Making necessary changes to Activities")
javaCompile.classpath.asPath.tokenize(File.pathSeparator).each { fileName ->
//TODO: Test on Windows
if (fileName.contains('/build/intermediates/exploded-aar/') && fileName.endsWith('/classes.jar')) {
weaveFile(log, fileName, javaCompile.classpath.asPath, project.android.bootClasspath.join(File.pathSeparator), false);
}
}
}
javaCompile.doLast {
configurations = project.getConfigurations();
//resolve returns the files of all the dependencies it used
dependencies = configurations.compile.resolve();
depFiles = handleDependencies(log, dependencies);
log.info("MPulsePlugin: Weaving after java compilation: javaCompile.classpath:" + javaCompile.classpath.asPath)
try {
boolean success = weave(log, javaCompile.destinationDir.toString(), javaCompile.classpath.asPath, project.android.bootClasspath.join(File.pathSeparator), javaCompile.destinationDir.toString(), true);
if (!success) {
throw new Exception("MPulsePlugin: Unable to instrument Jar file: " + javaCompile.destinationDir.toString(), nul);
}
}
catch(all)
{
log.debug("MPulsePlugin: Exception during weaving. Skipping " + javaCompile.destinationDir.toString(), all);
}
log.info("MPulsePlugin: Weaving Dependency packages");
depFiles.each { fileName ->
weaveFile(log, fileName, javaCompile.classpath.asPath, project.android.bootClasspath.join(File.pathSeparator), true);
}
}
}
// Once the build has finished get Gradle Instance and clean up after us...
// See: https://docs.gradle.org/current/javadoc/org/gradle/api/invocation/Gradle.html#buildFinished(groovy.lang.Closure)
//
// This is fine since we are really the last thing happening
// in the build even if it fails
project.getGradle().buildFinished { buildResult ->
log.info("MPulsePlugin: All outputs have been done. Restoring original packages");
restoreOriginals(log, depFiles);
}
}
private boolean weave(Logger log, String inPath, String classPath, String bootClassPath, String outJar)
{
String[] args = [
"-showWeaveInfo",
"-Xlint:adviceDidNotMatch=ignore,cantFindType=ignore",
"-1.7",
"-inpath", inPath,
"-aspectpath", classPath,
"-outjar", outJar,
"-classpath", classPath,
"-bootclasspath", bootClassPath
]
return weave(args, log)
}
private boolean weave(Logger log, String inPath, String classPath, String bootClassPath, String destinationDir, Boolean isDir)
{
String[] args = [
"-showWeaveInfo",
"-Xlint:adviceDidNotMatch=ignore,cantFindType=ignore",
"-1.7",
"-inpath", inPath,
"-aspectpath", classPath,
"-d", destinationDir,
"-classpath", classPath,
"-bootclasspath", bootClassPath
]
return weave(args, log)
}
private boolean weave(String[] args, Logger log)
{
MessageHandler handler = new MessageHandler(true);
new Main().run(args, handler);
for (IMessage message : handler.getMessages(null, true)) {
switch (message.getKind()) {
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message, message.thrown
return false;
case IMessage.WARNING:
log.warn message.message, message.thrown
break;
case IMessage.INFO:
log.info message.message, message.thrown
break;
case IMessage.DEBUG:
log.debug message.message, message.thrown
break;
}
}
return true;
}
private restoreOriginals (log, depFiles)
{
depFiles.each { fileName ->
String absolutePathStr = fileName;
String absolutePathOriginalStr = absolutePathStr.replace(".jar", "_orig.jar");
String absolutePathWeavedStr = absolutePathStr.replace(".jar", "_weaved.jar");
Path absolutePath = Paths.get(absolutePathStr);
Path absolutePathOriginal = Paths.get(absolutePathOriginalStr);
Path absolutePathWeaved = Paths.get(absolutePathWeavedStr);
log.info("MPulsePlugin: Removing weaved file " + absolutePathWeavedStr);
Files.deleteIfExists(absolutePathWeaved);
if(new File(absolutePathOriginalStr).exists())
{
log.info("MPulsePlugin: Copying original copy from: " + absolutePathOriginalStr + " back to " + absolutePathStr);
Files.copy(absolutePathOriginal, absolutePath, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
}
else
{
log.info("MPulsePlugin: Backup for file " + absolutePathStr + " not found.");
}
log.info("MPulsePlugin: Deleting backup copy of original: " + absolutePathOriginalStr);
Files.deleteIfExists(absolutePathOriginal);
}
}
private weaveFile(log, fileName, classPath, bootClassPath, keepOriginal)
{
String absolutePathStr = fileName;
String absolutePathOriginalStr = absolutePathStr.replace(".jar", "_orig.jar");
String absolutePathWeavedStr = absolutePathStr.replace(".jar", "_weaved.jar");
try {
Path absolutePath = Paths.get(absolutePathStr);
Path absolutePathOriginal = Paths.get(absolutePathOriginalStr);
log.info("MPulsePlugin: Copying " + absolutePathStr + " onto " + absolutePathOriginalStr);
Files.copy(absolutePath, absolutePathOriginal, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
log.info("MPulsePlugin: Weaving " + absolutePathOriginalStr + " to create " + absolutePathWeavedStr);
boolean success = weave(log, absolutePathStr, classPath, bootClassPath, absolutePathWeavedStr);
if (!success) {
throw new Exception("MPulsePlugin: Unable to instrument Jar file: " + absolutePathStr, nul);
}
Path absolutePathWeaved = Paths.get(absolutePathWeavedStr);
log.info("MPulsePlugin: Copying " + absolutePathWeavedStr + " back onto " + absolutePathStr);
Files.copy(absolutePathWeaved, absolutePath, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
}
catch (FileSystemException e)
{
// Since we failed to replace the original file, delete the backup
// This prevents an error when we unnecessarily restore later
log.debug("MPulsePlugin: Exception during weaving due to a FileSystemException. Skipping " + absolutePathStr, e);
Files.deleteIfExists(Paths.get(absolutePathOriginalStr));
}
catch(Exception e)
{
log.debug("MPulsePlugin: Exception during weaving. Skipping " + absolutePathStr, e);
}
finally {
if (!keepOriginal) {
//Delete classes_weaved.jar
Files.deleteIfExists(Paths.get(absolutePathWeavedStr))
//Delete classes_original.jar
Files.deleteIfExists(Paths.get(absolutePathOriginalStr))
}
}
}
private handleDependencies(log, dependencies) {
try {
def depFiles = [];
dependencies.each { depFile ->
String fileName = depFile.getAbsolutePath();
log.info("MPulsePlugin: Found file: " + depFile.getAbsolutePath());
// We'll ignore all Packages matching
// aspectjrt, mpulse, or the android
// support libraries.
if(!fileName.matches(~/.*aspectjrt-[0-9\.]*.jar$/) &&
!fileName.matches(~/.*mpulse-[0-9\.]*.jar$/) &&
!fileName.matches(~/.*.aar$/) &&
!fileName.matches(~/.*com\/android\/support\/support-annotations.*/) &&
// corelib_v302 hard-coded to avoid VF Ghana build issue
!fileName.matches(~/.*corelib_v302.*/) &&
!fileName.matches(~/.*adobeMobileLibrary.*/) &&
!fileName.matches(~/.*org.apache.httpcomponents.*/)) {
log.info("MPulsePlugin: Found file to be woven: " + fileName);
depFiles.push(fileName);
}
}
return depFiles;
}
catch (Exception exception) {
log.error(exception, exception);
return [];
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy