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

com.kezong.fataar.RClassesTransform Maven / Gradle / Ivy

package com.kezong.fataar;

import com.android.build.api.transform.DirectoryInput;
import com.android.build.api.transform.Format;
import com.android.build.api.transform.QualifiedContent;
import com.android.build.api.transform.Status;
import com.android.build.api.transform.Transform;
import com.android.build.api.transform.TransformInput;
import com.android.build.api.transform.TransformInvocation;
import com.android.build.api.transform.TransformOutputProvider;
import com.android.build.gradle.internal.pipeline.TransformManager;
import com.google.common.collect.ImmutableSet;

import org.gradle.api.Project;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import kotlin.io.FilesKt;

/**
 * com.sdk.R
 * |-- com.lib1.R
 * |-- com.lib2.R
 * 

* rename com.lib1.R and com.lib2.R to com.sdk.R */ public class RClassesTransform extends Transform { private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private final ExecutorService executor = Executors.newFixedThreadPool(CPU_COUNT + 1); private final List> futures = new ArrayList<>(); private final Project project; private final Map targetPackageMap = new HashMap<>(); private final Map> libraryPackageMap = new HashMap<>(); public RClassesTransform(final Project project) { this.project = project; } /** * Different variants have different package names. * So targetPackageName must set after evaluate * @param variantName variant name * @param targetPackage main module's package name */ public void putTargetPackage(String variantName, String targetPackage) { targetPackageMap.put(variantName, targetPackage); } /** * library packages name must set after exploded task perform * @param variantName variant name * @param libraryPackages sub module's package name, read from AndroidManifest.xml */ public void putLibraryPackages(String variantName, Collection libraryPackages) { libraryPackageMap.put(variantName, libraryPackages); } @Override public String getName() { return "transformR"; } @Override public Set getInputTypes() { return TransformManager.CONTENT_CLASS; } @Override public Set getScopes() { return ImmutableSet.of(QualifiedContent.Scope.PROJECT); } @Override public boolean isIncremental() { return true; } @Override public void transform(TransformInvocation transformInvocation) throws InterruptedException, IOException { long startTime = System.currentTimeMillis(); Map transformTable = buildTransformTable(transformInvocation.getContext().getVariantName()); final boolean isIncremental = transformInvocation.isIncremental() && this.isIncremental(); final TransformOutputProvider outputProvider = transformInvocation.getOutputProvider(); if (!isIncremental) { outputProvider.deleteAll(); } final File outputDir = outputProvider.getContentLocation("classes", getOutputTypes(), getScopes(), Format.DIRECTORY); try { for (final TransformInput input : transformInvocation.getInputs()) { for (final DirectoryInput directoryInput : input.getDirectoryInputs()) { final File directoryFile = directoryInput.getFile(); final ClassPool classPool = new ClassPool(); classPool.insertClassPath(directoryFile.getAbsolutePath()); for (final File originalClassFile : getChangedClassesList(directoryInput)) { if (!originalClassFile.getPath().endsWith(".class")) { continue; // ignore anything that is not class file } Future submit = executor.submit(() -> { try { File relative = FilesKt.relativeTo(originalClassFile, directoryFile); String className = filePathToClassname(relative); final CtClass ctClass = classPool.get(className); if (transformTable != null) { ClassFile classFile = ctClass.getClassFile(); ConstPool constPool = classFile.getConstPool(); constPool.renameClass(transformTable); } ctClass.writeFile(outputDir.getAbsolutePath()); } catch (CannotCompileException | NotFoundException | IOException e) { e.printStackTrace(); } }); futures.add(submit); } } } } catch (NotFoundException e) { throw new RuntimeException(e); } for (Future future : futures) { try { future.get(); } catch (ExecutionException e) { e.printStackTrace(); } } futures.clear(); long endTime = System.currentTimeMillis(); project.getLogger().info("the task cost " + (endTime - startTime) + "ms"); } private Map buildTransformTable(String variantName) { String targetPackage = targetPackageMap.get(variantName); Collection libraryPackages = libraryPackageMap.get(variantName); if (targetPackage == null || libraryPackages == null) { return null; } final List resourceTypes = Arrays.asList("anim", "animator", "array", "attr", "bool", "color", "dimen", "drawable", "font", "fraction", "id", "integer", "interpolator", "layout", "menu", "mipmap", "navigation", "plurals", "raw", "string", "style", "styleable", "transition", "xml"); HashMap map = new HashMap<>(); for (String resource : resourceTypes) { String targetClass = targetPackageMap.get(variantName).replace(".", "/") + "/R$" + resource; for (String libraryPackage : libraryPackageMap.get(variantName)) { String fromClass = libraryPackage.replace(".", "/") + "/R$" + resource; map.put(fromClass, targetClass); } } return map; } private List getChangedClassesList(final DirectoryInput directoryInput) throws IOException { final Map changedFiles = directoryInput.getChangedFiles(); if (changedFiles.isEmpty()) { // we're in non incremental mode return Files.walk(directoryInput.getFile().toPath()) .filter(Files::isRegularFile) .map(Path::toFile) .collect(Collectors.toList()); } else { changedFiles.entrySet().stream() .filter(it -> it.getValue() == Status.REMOVED) .forEach(it -> it.getKey().delete()); return changedFiles.entrySet().stream() .filter(it -> it.getValue() == Status.ADDED || it.getValue() == Status.CHANGED) .map(Map.Entry::getKey) .filter(File::isFile) .collect(Collectors.toList()); } } private String filePathToClassname(File file) { // com/classify/module/a.class -> com.classify.module.a.class -> comify.module.a is not expected // so must be replace .class first return file.getPath().replace(".class", "") .replace("/", ".") .replace("\\", "."); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy