1import static org.gradle.api.artifacts.type.ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE; 2 3import com.android.build.gradle.internal.dependency.ExtractAarTransform; 4import com.google.common.base.Joiner; 5import java.io.File; 6import java.util.ArrayList; 7import java.util.List; 8import java.util.concurrent.atomic.AtomicReference; 9import javax.inject.Inject; 10import org.gradle.api.Action; 11import org.gradle.api.Plugin; 12import org.gradle.api.Project; 13import org.gradle.api.Task; 14import org.gradle.api.artifacts.transform.TransformOutputs; 15import org.gradle.api.file.FileCollection; 16import org.gradle.api.tasks.compile.JavaCompile; 17import org.jetbrains.annotations.NotNull; 18 19/** 20 * Resolve aar dependencies into jars for non-Android projects. 21 */ 22public class AarDepsPlugin implements Plugin<Project> { 23 @Override 24 public void apply(Project project) { 25 project 26 .getDependencies() 27 .registerTransform( 28 ClassesJarExtractor.class, 29 reg -> { 30 reg.getParameters().getProjectName().set(project.getName()); 31 reg.getFrom().attribute(ARTIFACT_TYPE_ATTRIBUTE, "aar"); 32 reg.getTo().attribute(ARTIFACT_TYPE_ATTRIBUTE, "jar"); 33 }); 34 35 project.afterEvaluate( 36 p -> 37 project 38 .getConfigurations() 39 .forEach( 40 c -> { 41 // I suspect we're meant to use the org.gradle.usage attribute, but this 42 // works. 43 if (c.getName().endsWith("Classpath")) { 44 c.attributes( 45 cfgAttrs -> cfgAttrs.attribute(ARTIFACT_TYPE_ATTRIBUTE, "jar")); 46 } 47 })); 48 49 // warn if any AARs do make it through somehow; there must be a gradle configuration 50 // that isn't matched above. 51 //noinspection Convert2Lambda 52 project 53 .getTasks() 54 .withType(JavaCompile.class) 55 .all( 56 // the following Action<Task needs to remain an anonymous subclass or gradle's 57 // incremental compile breaks (run `gradlew -i classes` twice to see impact): 58 t -> t.doFirst(new Action<Task>() { 59 @Override 60 public void execute(Task task) { 61 List<File> aarFiles = AarDepsPlugin.this.findAarFiles(t.getClasspath()); 62 if (!aarFiles.isEmpty()) { 63 throw new IllegalStateException( 64 "AARs on classpath: " + Joiner.on("\n ").join(aarFiles)); 65 } 66 } 67 })); 68 } 69 70 private List<File> findAarFiles(FileCollection files) { 71 List<File> bad = new ArrayList<>(); 72 for (File file : files.getFiles()) { 73 if (file.getName().toLowerCase().endsWith(".aar")) { 74 bad.add(file); 75 } 76 } 77 return bad; 78 } 79 80 public static abstract class ClassesJarExtractor extends ExtractAarTransform { 81 @Inject 82 public ClassesJarExtractor() { 83 } 84 85 @Override 86 public void transform(@NotNull TransformOutputs outputs) { 87 AtomicReference<File> classesJarFile = new AtomicReference<>(); 88 AtomicReference<File> outJarFile = new AtomicReference<>(); 89 super.transform(new TransformOutputs() { 90 // This is the one that ExtractAarTransform calls. 91 @Override 92 public File dir(Object o) { 93 // ExtractAarTransform needs a place to extract the AAR. We don't really need to 94 // register this as an output, but it'd be tricky to avoid it. 95 File dir = outputs.dir(o); 96 97 // Also, register our jar file. Its name needs to be quasi-unique or 98 // IntelliJ Gradle/Android plugins get confused. 99 classesJarFile.set(new File(new File(dir, "jars"), "classes.jar")); 100 outJarFile.set(new File(new File(dir, "jars"), o + ".jar")); 101 outputs.file(o + "/jars/" + o + ".jar"); 102 return outputs.dir(o); 103 } 104 105 @Override 106 public File file(Object o) { 107 throw new IllegalStateException("shouldn't be called"); 108 } 109 }); 110 111 classesJarFile.get().renameTo(outJarFile.get()); 112 } 113 } 114} 115