Skip to content

Commit

Permalink
Merge pull request #47 from bytedance/yangzhiqian/master
Browse files Browse the repository at this point in the history
merge internal:optimize hook proguard、improve compatibility、add checkIncrementalInDebug、fix npe cased by refer-checker
  • Loading branch information
yangzhiqian authored Jul 27, 2020
2 parents a028267 + 37a0642 commit 4fa5445
Show file tree
Hide file tree
Showing 45 changed files with 554 additions and 311 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# Change Log
### Version 0.1.8
- improve compatibility
- optimize hook proguard
- add checkIncrementalInDebug
- fix npe cased by refer-checker
-
### Version 0.1.7
- Optimize incremental build and fix bugs
- Fix memory leak in plugins
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,34 @@ package com.ss.android.ugc.bytex.gradletoolkit
*/

import com.android.build.gradle.tasks.MergeResources
import com.android.ide.common.resources.ResourceSet
import java.io.File

//todo fix reflection
fun MergeResources.resourceSetList(): List<File> {
val resourceSets = try {
resourceSetList1()
} catch (e: Exception) {
resourceSetList2()
}
return resourceSets.flatMap { it.sourceFiles }.toSet().toList()
return resourceSets.flatMap { it!!.javaClass.getMethod("getSourceFiles").invoke(it) as List<File> }.toSet().toList()
}


fun MergeResources.resourceSetList1(): Iterable<ResourceSet> {
private fun MergeResources.resourceSetList1(): Iterable<*> {

val computeResourceSetListMethod = MergeResources::class.java.declaredMethods
.find { it.name == "computeResourceSetList" && it.parameterCount == 0 }!!

val oldIsAccessible = computeResourceSetListMethod.isAccessible
try {
computeResourceSetListMethod.isAccessible = true
return computeResourceSetListMethod.invoke(this) as Iterable<ResourceSet>
return computeResourceSetListMethod.invoke(this) as Iterable<*>
} finally {
computeResourceSetListMethod.isAccessible = oldIsAccessible
}
}

fun MergeResources.resourceSetList2(): Iterable<ResourceSet> {
private fun MergeResources.resourceSetList2(): Iterable<*> {
val getConfiguredResourceSets = MergeResources::class.java.declaredMethods
.find { it.name == "getConfiguredResourceSets" && it.parameterCount == 1 }!!

Expand All @@ -44,7 +44,7 @@ fun MergeResources.resourceSetList2(): Iterable<ResourceSet> {
try {
getConfiguredResourceSets.isAccessible = true
getPreprocessor.isAccessible = true
return getConfiguredResourceSets.invoke(this, getPreprocessor.invoke(this)) as Iterable<ResourceSet>
return getConfiguredResourceSets.invoke(this, getPreprocessor.invoke(this)) as Iterable<*>
} finally {
getConfiguredResourceSets.isAccessible = getConfiguredResourceSetsAccess
getPreprocessor.isAccessible = getPreprocessorAccess
Expand Down
1 change: 1 addition & 0 deletions HookProguard/ProguardConfigurationResolver/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
7 changes: 7 additions & 0 deletions HookProguard/ProguardConfigurationResolver/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apply plugin: 'java'
group "$upload_group"
version "$upload_version"
apply from: rootProject.file('gradle/publish.gradle')
dependencies {
compileOnly gradleApi()
}
2 changes: 2 additions & 0 deletions HookProguard/ProguardConfigurationResolver/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ARTIFACT_GROUP=com.bytedance.android.byteX
ARTIFACT_NAME=ProguardConfigurationResolver
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.ss.android.ugc.bytex.proguardconfigurationresolver;

import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.file.FileCollection;

import javax.annotation.Nullable;

/**
* Created by yangzhiqian on 2020/7/6<br/>
*/
public abstract class ProguardConfigurationResolver {
protected Project project;
protected String variantName;

public ProguardConfigurationResolver(Project project, String variantName) {
this.project = project;
this.variantName = variantName;
}

@Nullable
public abstract Task getTask();

@Nullable
public abstract FileCollection getAllConfigurationFiles();
}

1 change: 1 addition & 0 deletions HookProguard/ProguardConfigurationResolver/task/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
11 changes: 11 additions & 0 deletions HookProguard/ProguardConfigurationResolver/task/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apply plugin: 'java'
group "$upload_group"
version "$upload_version"

dependencies {
compileOnly gradleApi()
compileOnly "com.android.tools.build:gradle:3.6.2"
implementation project(":ProguardConfigurationResolver")
}

apply from: rootProject.file('gradle/publish.gradle')
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ARTIFACT_GROUP=com.bytedance.android.byteX
ARTIFACT_NAME=ProguardConfigurationResolver-task
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.ss.android.ugc.bytex.proguardconfigurationresolver.task;

import com.android.build.gradle.internal.tasks.ProguardConfigurableTask;
import com.android.build.gradle.internal.tasks.ProguardTask;
import com.android.build.gradle.internal.tasks.R8Task;
import com.ss.android.ugc.bytex.proguardconfigurationresolver.ProguardConfigurationResolver;

import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.file.FileCollection;

/**
* Created by yangzhiqian on 2020/7/6<br/>
*/
public class ProguardConfigurableTaskResolver extends ProguardConfigurationResolver {

public ProguardConfigurableTaskResolver(Project project, String variantName) {
super(project, variantName);
}

@Override
public Task getTask() {
for (Task task : project.getTasks()) {
if ((task instanceof ProguardTask || task instanceof R8Task) &&
((ProguardConfigurableTask) task).getVariantName().equals(variantName)) {
return task;
}
}
return null;
}

@Override
public FileCollection getAllConfigurationFiles() {
Task task = getTask();
if (task == null) {
return null;
}
return ((ProguardConfigurableTask) getTask()).getConfigurationFiles();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
11 changes: 11 additions & 0 deletions HookProguard/ProguardConfigurationResolver/transform/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apply plugin: 'java'
group "$upload_group"
version "$upload_version"

dependencies {
compileOnly gradleApi()
compileOnly "com.android.tools.build:gradle:3.5.3"
implementation project(":ProguardConfigurationResolver")
}

apply from: rootProject.file('gradle/publish.gradle')
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ARTIFACT_GROUP=com.bytedance.android.byteX
ARTIFACT_NAME=ProguardConfigurationResolver-transform
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.ss.android.ugc.bytex.proguardconfigurationresolver.transform;

import com.android.build.gradle.internal.pipeline.TransformTask;
import com.android.build.gradle.internal.transforms.ProguardConfigurable;
import com.ss.android.ugc.bytex.proguardconfigurationresolver.ProguardConfigurationResolver;

import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.file.FileCollection;

import java.lang.reflect.Method;

/**
* Created by yangzhiqian on 2020/7/6<br/>
*/
public class ProguardConfigurableTransformResolver extends ProguardConfigurationResolver {

public ProguardConfigurableTransformResolver(Project project, String variantName) {
super(project, variantName);
}

@Override
public Task getTask() {
for (Task task : project.getTasks()) {
if (task instanceof TransformTask) {
TransformTask transformTask = (TransformTask) task;
String name = transformTask.getTransform().getClass().getName();
if (transformTask.getVariantName().equals(variantName) &&
("com.android.build.gradle.internal.transforms.ProGuardTransform".equals(name) ||
"com.android.build.gradle.internal.transforms.R8Transform".equals(name))) {
return task;
}
}
}
return null;
}

@Override
public FileCollection getAllConfigurationFiles() {
Task task = getTask();
if (task == null) {
return null;
}
ProguardConfigurable proguardConfigurable = (ProguardConfigurable) ((TransformTask) task).getTransform();
try {
Method method = ProguardConfigurable.class.getDeclaredMethod("getAllConfigurationFiles");
method.setAccessible(true);
return (FileCollection) method.invoke(proguardConfigurable);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
7 changes: 6 additions & 1 deletion HookProguard/build.gradle
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
apply from: rootProject.file('gradle/plugin.gradle')
apply from: rootProject.file('gradle/plugin.gradle')
dependencies {
implementation project(':ProguardConfigurationResolver')
implementation project(':ProguardConfigurationResolver-task')
implementation project(':ProguardConfigurationResolver-transform')
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@
import com.android.build.gradle.LibraryExtension;
import com.android.build.gradle.LibraryPlugin;
import com.android.build.gradle.api.BaseVariant;
import com.android.build.gradle.internal.pipeline.TransformTask;
import com.android.build.gradle.internal.transforms.ProGuardTransform;
import com.android.build.gradle.internal.transforms.ProguardConfigurable;
import com.android.build.gradle.internal.transforms.R8Transform;
import com.ss.android.ugc.bytex.common.configuration.BooleanProperty;
import com.ss.android.ugc.bytex.common.graph.Graph;
import com.ss.android.ugc.bytex.common.graph.Node;
import com.ss.android.ugc.bytex.proguardconfigurationresolver.ProguardConfigurationResolver;

import org.gradle.api.Plugin;
import org.gradle.api.Project;
Expand All @@ -24,9 +21,9 @@
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
Expand Down Expand Up @@ -293,38 +290,26 @@ private static class ProguardConfigurationWithVariantAnalyzer {
// specification with the class hierarchy
private final List<KeepClassSpecificationHolder> classHierarchySpecifications = new ArrayList<>();
private final Map<String, Long> configurationFiles = new HashMap<>();
private ProguardConfigurable proGuardTransform;
private final ProguardConfigurationResolver configurationResolver;
private boolean valid = false;

ProguardConfigurationWithVariantAnalyzer(Project project, String variantName, Set<String> dependenciesRunBeforeTasks, boolean verify) {
super();
Task hookTask = null;
for (Task task : project.getTasks()) {
if (task instanceof TransformTask) {
TransformTask transformTask = (TransformTask) task;
if (transformTask.getVariantName().equals(variantName) && (transformTask.getTransform() instanceof ProGuardTransform || transformTask.getTransform() instanceof R8Transform)) {
hookTask = task;
proGuardTransform = (ProguardConfigurable) transformTask.getTransform();
FileCollection fileCollection = getAllConfigurationFileCollection();
String capitalVariantName = variantName.substring(0, 1).toUpperCase() + variantName.substring(1);
dependenciesRunBeforeTasks.stream()
.map(s -> task.getProject().getTasks().findByName(s + capitalVariantName))
.filter(Objects::nonNull)
.forEach(t -> t.dependsOn(fileCollection));
if (verify) {
task.doFirst(task1 -> verifyProguardConfiguration());
task.doLast(task12 -> verifyProguardConfiguration());
}
}
}
}
final Task hTask = hookTask;
configurationResolver = ProguardConfigurationResolverFactory.createProguardConfigurationResolver(project, variantName);
Task hookTask = configurationResolver.getTask();
if (hookTask != null) {
String capitalVariantName = variantName.substring(0, 1).toUpperCase() + variantName.substring(1);
dependenciesRunBeforeTasks.stream()
.map(s -> hookTask.getProject().getTasks().findByName(s + capitalVariantName))
.filter(Objects::nonNull)
.forEach(t -> t.dependsOn(configurationResolver.getAllConfigurationFiles()));
if (verify) {
hookTask.doFirst(task1 -> verifyProguardConfiguration());
hookTask.doLast(task12 -> verifyProguardConfiguration());
}
project.getGradle()
.getTaskGraph()
.whenReady(taskExecutionGraph -> valid = taskExecutionGraph.hasTask(hTask));
} else {
project.getLogger().warn("Can not find proguard task for " + variantName);
.whenReady(taskExecutionGraph -> valid = taskExecutionGraph.hasTask(hookTask));
}
}

Expand Down Expand Up @@ -387,26 +372,17 @@ synchronized void prepare() {
}

private List<File> getAllConfigurationFiles() {
FileCollection allConfigurationFiles = configurationResolver.getAllConfigurationFiles();
if (allConfigurationFiles == null) {
return Collections.emptyList();
}
List<File> allFiles = new LinkedList<>();
for (File file : getAllConfigurationFileCollection()) {
for (File file : allConfigurationFiles) {
allFiles.add(file);
}
return allFiles;
}

private FileCollection getAllConfigurationFileCollection() {
if (proGuardTransform == null) {
throw new RuntimeException("This plugin can only be applied in release build or when proguard is enabled.");
}
try {
Method method = ProguardConfigurable.class.getDeclaredMethod("getAllConfigurationFiles");
method.setAccessible(true);
return (FileCollection) method.invoke(proGuardTransform);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

private void filterAndSplit(Configuration configuration) {
if (configuration.keep == null) return;
List<KeepClassSpecification> newKeepList = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.ss.android.ugc.bytex.hookproguard;

import com.android.builder.model.Version;
import com.android.repository.Revision;
import com.ss.android.ugc.bytex.proguardconfigurationresolver.ProguardConfigurationResolver;
import com.ss.android.ugc.bytex.proguardconfigurationresolver.task.ProguardConfigurableTaskResolver;
import com.ss.android.ugc.bytex.proguardconfigurationresolver.transform.ProguardConfigurableTransformResolver;

import org.gradle.api.Project;

/**
* Created by yangzhiqian on 2020/7/27<br/>
*/
class ProguardConfigurationResolverFactory {
public static ProguardConfigurationResolver createProguardConfigurationResolver(Project project, String variantName) {
Revision revision = Revision.parseRevision(Version.ANDROID_GRADLE_PLUGIN_VERSION);
if (revision.getMajor() >= 3 && revision.getMinor() >= 6) {
return new ProguardConfigurableTaskResolver(project, variantName);
} else {
return new ProguardConfigurableTransformResolver(project, variantName);
}
}
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Add those configuration code to your build.gradle, and apply your plugins on dem

```groovy
buildscript {
ext.plugin_version="0.1.7"
ext.plugin_version="0.1.8"
repositories {
google()
jcenter()
Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ ByteX是一个基于gradle transform api和ASM的字节码插件平台(或许

```groovy
buildscript {
ext.plugin_version="0.1.7"
ext.plugin_version="0.1.8"
repositories {
google()
jcenter()
Expand Down
Loading

0 comments on commit 4fa5445

Please sign in to comment.