add pins code

This commit is contained in:
yummylau
2019-10-22 20:14:31 +08:00
parent 23f4c67f7a
commit 9a8d5ba920
14 changed files with 1720 additions and 1 deletions

View File

@@ -0,0 +1,180 @@
package com.plugin.pins;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Stack;
/**
* An example class for directed graphs. The vertex type can be specified.
* There are no edge costs/weights.
*
* Written for CS211, Nov 2006.
*
* @author Paul Chew
*/
public class Digraph<V> {
/**
* The implementation here is basically an adjacency list, but instead
* of an array of lists, a Map is used to map each vertex to its list of
* adjacent vertices.
*/
private Map<V,List<V>> neighbors = new HashMap<V,List<V>>();
/**
* String representation of dependencyGraph.
*/
public String toString () {
StringBuffer s = new StringBuffer();
for (V v: neighbors.keySet()) s.append("\n " + v + " -> " + neighbors.get(v));
return s.toString();
}
/**
* Add a vertex to the dependencyGraph. Nothing happens if vertex is already in dependencyGraph.
*/
public void add (V vertex) {
if (neighbors.containsKey(vertex)) return;
neighbors.put(vertex, new ArrayList<V>());
}
/**
* True iff dependencyGraph contains vertex.
*/
public boolean contains (V vertex) {
return neighbors.containsKey(vertex);
}
/**
* Add an edge to the dependencyGraph; if either vertex does not exist, it's added.
* This implementation allows the creation of multi-edges and self-loops.
*/
public void add (V from, V to) {
this.add(from); this.add(to);
neighbors.get(from).add(to);
}
/**
* Remove an edge from the dependencyGraph. Nothing happens if no such edge.
* @throws IllegalArgumentException if either vertex doesn't exist.
*/
public void remove (V from, V to) {
if (!(this.contains(from) && this.contains(to)))
throw new IllegalArgumentException("Nonexistent vertex");
neighbors.get(from).remove(to);
}
/**
* Report (as a Map) the out-degree of each vertex.
*/
public Map<V,Integer> outDegree () {
Map<V,Integer> result = new HashMap<V,Integer>();
for (V v: neighbors.keySet()) result.put(v, neighbors.get(v).size());
return result;
}
/**
* Report (as a Map) the in-degree of each vertex.
*/
public Map<V,Integer> inDegree () {
Map<V,Integer> result = new HashMap<V,Integer>();
for (V v: neighbors.keySet()) result.put(v, 0); // All in-degrees are 0
for (V from: neighbors.keySet()) {
for (V to: neighbors.get(from)) {
result.put(to, result.get(to) + 1); // Increment in-degree
}
}
return result;
}
/**
* Report (as a List) the topological sort of the vertices; null for no such sort.
*/
public List<V> topSort () {
Map<V, Integer> degree = inDegree();
// Determine all vertices with zero in-degree
Stack<V> zeroVerts = new Stack<V>(); // Stack as good as any here
for (V v: degree.keySet()) {
if (degree.get(v) == 0) zeroVerts.push(v);
}
// Determine the topological order
List<V> result = new ArrayList<V>();
while (!zeroVerts.isEmpty()) {
V v = zeroVerts.pop(); // Choose a vertex with zero in-degree
result.add(v); // Vertex v is next in topol order
// "Remove" vertex v by updating its neighbors
for (V neighbor: neighbors.get(v)) {
degree.put(neighbor, degree.get(neighbor) - 1);
// Remember any vertices that now have zero in-degree
if (degree.get(neighbor) == 0) zeroVerts.push(neighbor);
}
}
// Check that we have used the entire dependencyGraph (if not, there was a cycle)
if (result.size() != neighbors.size()) return null;
return result;
}
/**
* True iff dependencyGraph is a dag (directed acyclic dependencyGraph).
*/
public boolean isDag () {
return topSort() != null;
}
/**
* Report (as a Map) the bfs distance to each vertex from the start vertex.
* The distance is an Integer; the value null is used to represent infinity
* (implying that the corresponding node cannot be reached).
*/
public Map bfsDistance (V start) {
Map<V,Integer> distance = new HashMap<V,Integer>();
// Initially, all distance are infinity, except start node
for (V v: neighbors.keySet()) distance.put(v, null);
distance.put(start, 0);
// Process nodes in queue order
Queue<V> queue = new LinkedList<V>();
queue.offer(start); // Place start node in queue
while (!queue.isEmpty()) {
V v = queue.remove();
int vDist = distance.get(v);
// Update neighbors
for (V neighbor: neighbors.get(v)) {
if (distance.get(neighbor) != null) continue; // Ignore if already done
distance.put(neighbor, vDist + 1);
queue.offer(neighbor);
}
}
return distance;
}
// /**
// * Main program (for testing).
// */
// public static void main (String[] args) {
// // Create a Graph with Integer nodes
// Digraph<Integer> graph = new Digraph<Integer>();
// graph.add(0, 1); graph.add(0, 2); graph.add(0, 3);
// graph.add(1, 2); graph.add(1, 3); graph.add(2, 3);
// graph.add(2, 4); graph.add(4, 5); graph.add(5, 6); // Tetrahedron with tail
// System.out.println("The current dependencyGraph: " + graph);
// System.out.println("In-degrees: " + graph.inDegree());
// System.out.println("Out-degrees: " + graph.outDegree());
// System.out.println("A topological sort of the vertices: " + graph.topSort());
// System.out.println("The dependencyGraph " + (graph.isDag()?"is":"is not") + " a dag");
// System.out.println("BFS distances starting from " + 0 + ": " + graph.bfsDistance(0));
// System.out.println("BFS distances starting from " + 1 + ": " + graph.bfsDistance(1));
// System.out.println("BFS distances starting from " + 2 + ": " + graph.bfsDistance(2));
// graph.add(4, 1); // Create a cycle
// System.out.println("Cycle created");
// System.out.println("The current dependencyGraph: " + graph);
// System.out.println("In-degrees: " + graph.inDegree());
// System.out.println("Out-degrees: " + graph.outDegree());
// System.out.println("A topological sort of the vertices: " + graph.topSort());
// System.out.println("The dependencyGraph " + (graph.isDag()?"is":"is not") + " a dag");
// System.out.println("BFS distances starting from " + 2 + ": " + graph.bfsDistance(2));
// }
}

View File

@@ -0,0 +1,11 @@
package com.plugin.pins
class MicroModule {
String name
File microModuleDir
boolean appliedScript
}

View File

@@ -0,0 +1,77 @@
package com.plugin.pins
import org.gradle.api.GradleException
import org.gradle.api.Project
class MicroModuleInfo {
Project project
MicroModule mainMicroModule
Map<String, MicroModule> includeMicroModules
Map<String, String> exportMicroModules
Digraph<String> dependencyGraph
MicroModuleInfo(Project project) {
this.project = project
this.includeMicroModules = new HashMap<>()
this.exportMicroModules = new HashMap<>()
dependencyGraph = new Digraph<String>()
MicroModule microModule = Utils.buildMicroModule(project, ':main')
if (microModule != null) {
setMainMicroModule(microModule)
}
}
void setMainMicroModule(MicroModule microModule) {
if (microModule == null) {
throw new GradleException("main MicroModule cannot be null.")
}
this.mainMicroModule = microModule
addIncludeMicroModule(microModule)
}
void addIncludeMicroModule(MicroModule microModule) {
includeMicroModules.put(microModule.name, microModule)
}
void addExportMicroModule(String name) {
MicroModule microModule = Utils.buildMicroModule(project, name)
if (microModule == null) {
throw new GradleException("MicroModule with path '${name}' could not be found in ${project.getDisplayName()}.")
}
exportMicroModules.put(name, null)
}
MicroModule getMicroModule(String name) {
return includeMicroModules.get(name)
}
void setMicroModuleDependency(String target, String dependency) {
MicroModule dependencyMicroModule = getMicroModule(dependency)
if(dependencyMicroModule == null) {
if(Utils.buildMicroModule(project, dependency) != null) {
throw new GradleException("MicroModule '${target}' dependency MicroModle '${dependency}', but its not included.")
} else {
throw new GradleException("MicroModule with path '${path}' could not be found in ${project.getDisplayName()}.")
}
}
dependencyGraph.add(target, dependency)
if(!dependencyGraph.isDag()) {
throw new GradleException("Circular dependency between MicroModule '${target}' and '${dependency}'.")
}
}
boolean hasDependency(String target, String dependency) {
Map<String, Integer> bfsDistance = dependencyGraph.bfsDistance(target)
for(String key: bfsDistance.keySet()) {
if(key == dependency) {
return bfsDistance.get(key) != null
}
}
return false
}
}

View File

@@ -0,0 +1,608 @@
package com.plugin.pins
import com.android.build.gradle.*
import com.android.build.gradle.api.BaseVariant
import com.android.builder.model.ProductFlavor
import com.android.manifmerger.ManifestMerger2
import com.android.manifmerger.MergingReport
import com.android.manifmerger.XmlDocument
import com.android.utils.ILogger
import com.plugin.pins.check.CodeChecker
import com.plugin.pins.extension.DefaultMicroModuleExtension
import com.plugin.pins.extension.OnMicroModuleListener
import org.gradle.BuildListener
import org.gradle.BuildResult
import org.gradle.api.*
import org.gradle.api.artifacts.Configuration
import org.gradle.api.initialization.Settings
import org.gradle.api.invocation.Gradle
class MicroModulePlugin implements Plugin<Project> {
private final static String NORMAL = 'normal'
private final static String ASSEMBLE_OR_GENERATE = 'assemble_or_generate'
private final static String APPLY_NORMAL_MICRO_MODULE_SCRIPT = 'apply_normal_micro_module_script'
private final static String APPLY_INCLUDE_MICRO_MODULE_SCRIPT = 'apply_include_micro_module_script'
private final static String APPLY_EXPORT_MICRO_MODULE_SCRIPT = 'apply_export_micro_module_script'
private final static BuildListener buildListener = new BuildListener() {
@Override
void buildStarted(Gradle gradle) {
}
@Override
void settingsEvaluated(Settings settings) {
}
@Override
void projectsLoaded(Gradle gradle) {
}
@Override
void projectsEvaluated(Gradle gradle) {
}
@Override
void buildFinished(BuildResult buildResult) {
// generate microModules.xml for MicroModule IDEA plugin.
def ideaFile = new File(buildResult.gradle.rootProject.rootDir, '.idea')
if (!ideaFile.exists()) return
def microModuleInfo = '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<modules>\n'
buildResult.gradle.rootProject.allprojects.each {
MicroModulePlugin microModulePlugin = it.plugins.findPlugin('micro-module')
if (microModulePlugin == null) return
def displayName = it.displayName
microModuleInfo += ' <module name=\"' + displayName.substring(displayName.indexOf("'") + 1, displayName.lastIndexOf("'")) + '\" path=\"' + it.projectDir.getCanonicalPath() + '\">\n'
microModulePlugin.microModuleInfo.includeMicroModules.each {
MicroModule microModule = it.value
microModuleInfo += ' <microModule name=\"' + microModule.name + '\" path=\"' + microModule.microModuleDir.getCanonicalPath() + '\" />\n'
}
microModuleInfo += ' </module>\n'
}
microModuleInfo += '</modules>'
def microModules = new File(ideaFile, 'microModules.xml')
microModules.write(microModuleInfo, 'utf-8')
}
}
Project project
String startTaskState = NORMAL
MicroModuleInfo microModuleInfo
ProductFlavorInfo productFlavorInfo
MicroModule currentMicroModule
String applyScriptState
boolean appliedLibraryPlugin
boolean clearedOriginSourceSets
void apply(Project project) {
this.project = project
this.microModuleInfo = new MicroModuleInfo(project)
project.gradle.removeListener(buildListener)
project.gradle.addBuildListener(buildListener)
if (project.gradle.getStartParameter().taskNames.size() == 0) {
startTaskState = NORMAL
} else {
startTaskState = ASSEMBLE_OR_GENERATE
}
if (startTaskState != NORMAL) {
project.getConfigurations().whenObjectAdded {
Configuration configuration = it
configuration.dependencies.whenObjectAdded {
if (applyScriptState == APPLY_INCLUDE_MICRO_MODULE_SCRIPT) {
configuration.dependencies.remove(it)
return
} else if (applyScriptState == APPLY_NORMAL_MICRO_MODULE_SCRIPT
|| applyScriptState == APPLY_EXPORT_MICRO_MODULE_SCRIPT) {
return
} else if (currentMicroModule == null && startTaskState == ASSEMBLE_OR_GENERATE) {
return
} else if (it.group != null && it.group.startsWith('com.android.tools')) {
return
}
configuration.dependencies.remove(it)
}
}
}
DefaultMicroModuleExtension microModuleExtension = project.extensions.create(MicroModuleExtension, 'microModule', DefaultMicroModuleExtension, project)
microModuleExtension.onMicroModuleListener = new OnMicroModuleListener() {
@Override
void addIncludeMicroModule(MicroModule microModule, boolean mainMicroModule) {
if (mainMicroModule) {
microModuleInfo.setMainMicroModule(microModule)
} else {
microModuleInfo.addIncludeMicroModule(microModule)
}
if(!clearedOriginSourceSets) {
productFlavorInfo = new ProductFlavorInfo(project)
clearedOriginSourceSets = true
clearOriginSourceSet()
if(microModuleInfo.mainMicroModule != null) {
addMicroModuleSourceSet(microModuleInfo.mainMicroModule)
}
}
addMicroModuleSourceSet(microModule)
}
@Override
void addExportMicroModule(String... microModulePaths) {
microModulePaths.each {
microModuleInfo.addExportMicroModule(it)
}
}
}
project.dependencies.metaClass.microModule { String path ->
if (currentMicroModule == null || applyScriptState == APPLY_NORMAL_MICRO_MODULE_SCRIPT) {
return []
}
if (applyScriptState == APPLY_INCLUDE_MICRO_MODULE_SCRIPT) {
microModuleInfo.setMicroModuleDependency(currentMicroModule.name, path)
return []
}
MicroModule microModule = microModuleInfo.getMicroModule(path)
def result = []
if (startTaskState == ASSEMBLE_OR_GENERATE) {
addMicroModuleSourceSet(microModule)
applyMicroModuleScript(microModule)
microModule.appliedScript = true
}
return result
}
project.plugins.all {
Class extensionClass
if (it instanceof AppPlugin) {
extensionClass = AppExtension
} else if (it instanceof LibraryPlugin) {
extensionClass = LibraryExtension
} else {
return
}
project.extensions.configure(extensionClass, new Action<? extends TestedExtension>() {
@Override
void execute(TestedExtension testedExtension) {
boolean isLibrary
DomainObjectSet<BaseVariant> baseVariants
if (testedExtension instanceof AppExtension) {
AppExtension appExtension = (AppExtension) testedExtension
baseVariants = appExtension.applicationVariants
} else {
LibraryExtension libraryExtension = (LibraryExtension) testedExtension
baseVariants = libraryExtension.libraryVariants
isLibrary = true
}
baseVariants.all { BaseVariant variant ->
if (microModuleExtension.codeCheckEnabled) {
def taskNamePrefix = isLibrary ? 'package' : 'merge'
List<String> sourceFolders = new ArrayList<>()
sourceFolders.add('main')
sourceFolders.add(variant.buildType.name)
if (variant.productFlavors.size() > 0) {
sourceFolders.add(variant.name)
sourceFolders.add(variant.flavorName)
for (ProductFlavor productFlavor : variant.productFlavors) {
sourceFolders.add(productFlavor.name)
}
checkMicroModuleBoundary(taskNamePrefix, variant.buildType.name, variant.flavorName, sourceFolders)
} else {
checkMicroModuleBoundary(taskNamePrefix, variant.buildType.name, null, sourceFolders)
}
}
}
}
})
}
project.afterEvaluate {
microModuleExtension.onMicroModuleListener = null
if (microModuleInfo.mainMicroModule == null) {
throw new GradleException("the main MicroModule could not be found in ${project.getDisplayName()}.")
}
appliedLibraryPlugin = project.pluginManager.hasPlugin('com.android.library')
productFlavorInfo = new ProductFlavorInfo(project)
applyScriptState = APPLY_INCLUDE_MICRO_MODULE_SCRIPT
microModuleInfo.includeMicroModules.each {
MicroModule microModule = it.value
microModuleInfo.dependencyGraph.add(microModule.name)
applyMicroModuleScript(microModule)
}
clearOriginSourceSet()
if (startTaskState == ASSEMBLE_OR_GENERATE) {
applyScriptState = APPLY_EXPORT_MICRO_MODULE_SCRIPT
boolean hasExportMainMicroModule = false
boolean isEmpty = microModuleInfo.exportMicroModules.isEmpty()
List<String> dependencySort = microModuleInfo.dependencyGraph.topSort()
dependencySort.each {
if (isEmpty || microModuleInfo.exportMicroModules.containsKey(it)) {
MicroModule microModule = microModuleInfo.getMicroModule(it)
if (microModule == null) {
throw new GradleException("MicroModule with path '${it}' could not be found in ${project.getDisplayName()}.")
}
if (microModule == microModuleInfo.mainMicroModule) {
hasExportMainMicroModule = true
}
if (microModule.appliedScript) return
addMicroModuleSourceSet(microModule)
applyMicroModuleScript(microModule)
microModule.appliedScript = true
}
}
if (!hasExportMainMicroModule) {
throw new GradleException("the main MicroModule '${microModuleInfo.mainMicroModule.name}' is not in the export list.")
}
} else {
applyScriptState = APPLY_NORMAL_MICRO_MODULE_SCRIPT
microModuleInfo.includeMicroModules.each {
MicroModule microModule = it.value
addMicroModuleSourceSet(microModule)
applyMicroModuleScript(microModule)
}
}
currentMicroModule = null
generateAndroidManifest()
project.tasks.preBuild.doFirst {
clearOriginSourceSet()
if (startTaskState == ASSEMBLE_OR_GENERATE) {
microModuleInfo.includeMicroModules.each {
MicroModule microModule = it.value
if (microModule.appliedScript) {
addMicroModuleSourceSet(microModule)
}
}
} else {
microModuleInfo.includeMicroModules.each {
addMicroModuleSourceSet(it.value)
}
}
generateAndroidManifest()
}
}
}
def generateAndroidManifest() {
if ((startTaskState == ASSEMBLE_OR_GENERATE || !microModuleInfo.exportMicroModules.isEmpty()) && isMainSourceSetEmpty()) {
setMainSourceSetManifest()
return
}
mergeAndroidManifest('main')
productFlavorInfo.buildTypes.each {
mergeAndroidManifest(it)
}
if (!productFlavorInfo.singleDimension) {
productFlavorInfo.productFlavors.each {
mergeAndroidManifest(it)
}
}
productFlavorInfo.combinedProductFlavors.each {
mergeAndroidManifest(it)
def productFlavor = it
productFlavorInfo.buildTypes.each {
mergeAndroidManifest(productFlavor + Utils.upperCase(it))
}
}
def androidTest = 'androidTest'
mergeAndroidManifest(androidTest)
mergeAndroidManifest(androidTest + 'Debug')
if (!productFlavorInfo.singleDimension) {
productFlavorInfo.productFlavors.each {
mergeAndroidManifest(androidTest + Utils.upperCase(it))
}
}
productFlavorInfo.combinedProductFlavors.each {
mergeAndroidManifest(androidTest + Utils.upperCase(it))
mergeAndroidManifest(androidTest + Utils.upperCase(it) + 'Debug')
}
}
def mergeAndroidManifest(String variantName) {
File mainManifestFile = new File(microModuleInfo.mainMicroModule.microModuleDir, "/src/${variantName}/AndroidManifest.xml")
if (!mainManifestFile.exists()) return
ManifestMerger2.MergeType mergeType = ManifestMerger2.MergeType.APPLICATION
XmlDocument.Type documentType = XmlDocument.Type.MAIN
def logger = new ILogger() {
@Override
void error(Throwable t, String msgFormat, Object... args) {
println(msgFormat)
}
@Override
void warning(String msgFormat, Object... args) {
}
@Override
void info(String msgFormat, Object... args) {
}
@Override
void verbose(String msgFormat, Object... args) {
}
}
ManifestMerger2.Invoker invoker = new ManifestMerger2.Invoker(mainManifestFile, logger, mergeType, documentType)
invoker.withFeatures(ManifestMerger2.Invoker.Feature.NO_PLACEHOLDER_REPLACEMENT)
microModuleInfo.includeMicroModules.each {
MicroModule microModule = it.value
if (startTaskState == ASSEMBLE_OR_GENERATE && !microModule.appliedScript) return
if (microModule.name == microModuleInfo.mainMicroModule.name) return
def microManifestFile = new File(microModule.microModuleDir, "/src/${variantName}/AndroidManifest.xml")
if (microManifestFile.exists()) {
invoker.addLibraryManifest(microManifestFile)
}
}
def mergingReport = invoker.merge()
if (!mergingReport.result.success) {
mergingReport.log(logger)
throw new GradleException(mergingReport.reportString)
}
def moduleAndroidManifest = mergingReport.getMergedDocument(MergingReport.MergedManifestKind.MERGED)
moduleAndroidManifest = new String(moduleAndroidManifest.getBytes('UTF-8'))
def saveDir = new File(project.projectDir, "build/microModule/merge-manifest/${variantName}")
saveDir.mkdirs()
def AndroidManifestFile = new File(saveDir, 'AndroidManifest.xml')
AndroidManifestFile.createNewFile()
AndroidManifestFile.write(moduleAndroidManifest)
def extensionContainer = project.getExtensions()
BaseExtension android = extensionContainer.getByName('android')
def obj = android.sourceSets.findByName(variantName)
if (obj == null) {
return
}
obj.manifest.srcFile project.projectDir.absolutePath + "/build/microModule/merge-manifest/${variantName}/AndroidManifest.xml"
}
def addMicroModuleSourceSet(MicroModule microModule) {
addVariantSourceSet(microModule, 'main')
productFlavorInfo.buildTypes.each {
addVariantSourceSet(microModule, it)
}
if (!productFlavorInfo.singleDimension) {
productFlavorInfo.productFlavors.each {
addVariantSourceSet(microModule, it)
}
}
productFlavorInfo.combinedProductFlavors.each {
addVariantSourceSet(microModule, it)
def flavorName = it
productFlavorInfo.buildTypes.each {
addVariantSourceSet(microModule, flavorName + Utils.upperCase(it))
}
}
def testTypes = ['androidTest', 'test']
testTypes.each {
def testType = it
addVariantSourceSet(microModule, testType)
if (testType == 'test') {
productFlavorInfo.buildTypes.each {
addVariantSourceSet(microModule, testType + Utils.upperCase(it))
}
} else {
addVariantSourceSet(microModule, testType + 'Debug')
}
if (!productFlavorInfo.singleDimension) {
productFlavorInfo.productFlavors.each {
addVariantSourceSet(microModule, testType + Utils.upperCase(it))
}
}
productFlavorInfo.combinedProductFlavors.each {
def productFlavorName = testType + Utils.upperCase(it)
addVariantSourceSet(microModule, productFlavorName)
if (testType == 'test') {
productFlavorInfo.buildTypes.each {
addVariantSourceSet(microModule, productFlavorName + Utils.upperCase(it))
}
} else {
addVariantSourceSet(microModule, productFlavorName + 'Debug')
}
}
}
}
def clearOriginSourceSet() {
clearModuleSourceSet('main')
// buildTypes
productFlavorInfo.buildTypes.each {
clearModuleSourceSet(it)
}
if (!productFlavorInfo.singleDimension) {
productFlavorInfo.productFlavors.each {
clearModuleSourceSet(it)
}
}
productFlavorInfo.combinedProductFlavors.each {
clearModuleSourceSet(it)
def flavorName = it
productFlavorInfo.buildTypes.each {
clearModuleSourceSet(flavorName + Utils.upperCase(it))
}
}
def testTypes = ['androidTest', 'test']
testTypes.each {
def testType = it
clearModuleSourceSet(testType)
if (testType == 'test') {
productFlavorInfo.buildTypes.each {
clearModuleSourceSet(testType + Utils.upperCase(it))
}
} else {
clearModuleSourceSet(testType + 'Debug')
}
if (!productFlavorInfo.singleDimension) {
productFlavorInfo.productFlavors.each {
clearModuleSourceSet(testType + Utils.upperCase(it))
}
}
productFlavorInfo.combinedProductFlavors.each {
def productFlavorName = testType + Utils.upperCase(it)
clearModuleSourceSet(productFlavorName)
if (testType == 'test') {
productFlavorInfo.buildTypes.each {
clearModuleSourceSet(productFlavorName + Utils.upperCase(it))
}
} else {
clearModuleSourceSet(productFlavorName + 'Debug')
}
}
}
}
def isMainSourceSetEmpty() {
BaseExtension android = project.extensions.getByName('android')
def obj = android.sourceSets.findByName('main')
if (obj == null) {
return true
}
return obj.java.srcDirs.size() == 0;
}
def setMainSourceSetManifest() {
BaseExtension android = project.extensions.getByName('android')
def obj = android.sourceSets.findByName('main')
if (obj == null) {
obj = android.sourceSets.create('main')
}
File mainManifestFile = new File(microModuleInfo.mainMicroModule.microModuleDir, '/src/main/AndroidManifest.xml')
obj.manifest.srcFile mainManifestFile
}
def addVariantSourceSet(MicroModule microModule, def type) {
def absolutePath = microModule.microModuleDir.absolutePath
BaseExtension android = project.extensions.getByName('android')
def obj = android.sourceSets.findByName(type)
if (obj == null) {
obj = android.sourceSets.create(type)
}
obj.java.srcDir(absolutePath + "/src/${type}/java")
obj.java.srcDir(absolutePath + "/src/${type}/kotlin")
obj.res.srcDir(absolutePath + "/src/${type}/res")
obj.jni.srcDir(absolutePath + "/src/${type}/jni")
obj.jniLibs.srcDir(absolutePath + "/src/${type}/jniLibs")
obj.aidl.srcDir(absolutePath + "/src/${type}/aidl")
obj.assets.srcDir(absolutePath + "/src/${type}/assets")
obj.shaders.srcDir(absolutePath + "/src/${type}/shaders")
obj.resources.srcDir(absolutePath + "/src/${type}/resources")
obj.renderscript.srcDir(absolutePath + "/src/${type}/rs")
}
def clearModuleSourceSet(def type) {
def srcDirs = []
BaseExtension android = project.extensions.getByName('android')
def obj = android.sourceSets.findByName(type)
if (obj == null) {
return
}
obj.java.srcDirs = srcDirs
obj.res.srcDirs = srcDirs
obj.jni.srcDirs = srcDirs
obj.jniLibs.srcDirs = srcDirs
obj.aidl.srcDirs = srcDirs
obj.assets.srcDirs = srcDirs
obj.shaders.srcDirs = srcDirs
obj.resources.srcDirs = srcDirs
obj.renderscript.srcDirs = srcDirs
}
void applyMicroModuleScript(MicroModule microModule) {
def microModuleBuild = new File(microModule.microModuleDir, 'build.gradle')
if (microModuleBuild.exists()) {
MicroModule tempMicroModule = currentMicroModule
currentMicroModule = microModule
project.apply from: microModuleBuild.absolutePath
currentMicroModule = tempMicroModule
}
}
def checkMicroModuleBoundary(String taskPrefix, String buildType, String flavorName, List<String> sourceFolders) {
CodeChecker codeChecker
def buildTypeFirstUp = Utils.upperCase(buildType)
def productFlavorFirstUp = flavorName != null ? Utils.upperCase(flavorName) : ""
def mergeResourcesTaskName = taskPrefix + productFlavorFirstUp + buildTypeFirstUp + 'Resources'
def packageResourcesTask = project.tasks.findByName(mergeResourcesTaskName)
if (packageResourcesTask != null) {
codeChecker = new CodeChecker(project, microModuleInfo, productFlavorInfo, buildType, flavorName)
packageResourcesTask.doLast {
codeChecker.checkResources(mergeResourcesTaskName, sourceFolders)
}
}
def compileJavaTaskName = "compile${productFlavorFirstUp}${buildTypeFirstUp}JavaWithJavac"
def compileJavaTask = project.tasks.findByName(compileJavaTaskName)
if (compileJavaTask != null) {
compileJavaTask.doLast {
if (codeChecker == null) {
codeChecker = new CodeChecker(project, microModuleInfo, productFlavorInfo, buildType, flavorName)
}
codeChecker.checkClasses(mergeResourcesTaskName, sourceFolders)
}
}
}
}

View File

@@ -0,0 +1,102 @@
package com.plugin.pins
import com.android.build.gradle.BaseExtension
import org.gradle.api.Project
class ProductFlavorInfo {
List<String> flavorDimensions
List<String> productFlavors
List<String> buildTypes
List<String> combinedProductFlavors
Map<String, List<String>> combinedProductFlavorsMap
boolean singleDimension
private List<List<String>> flavorGroups
ProductFlavorInfo(Project project) {
BaseExtension extension = (BaseExtension) project.extensions.getByName("android")
buildTypes = new ArrayList<>()
if(extension.buildTypes != null) {
extension.buildTypes.each {
buildTypes.add(it.name)
}
}
flavorDimensions = extension.flavorDimensionList
if (flavorDimensions == null) {
flavorDimensions = new ArrayList<>()
}
productFlavors = new ArrayList<>()
flavorGroups = new ArrayList<>()
for (int i = 0; i < flavorDimensions.size(); i++) {
flavorGroups.add(new ArrayList<>())
}
extension.productFlavors.each {
productFlavors.add(it.name)
def position = flavorDimensions.indexOf(it.dimension)
flavorGroups.get(position).add(it.name)
}
List<List<String>> flavorGroupTemp = new ArrayList<>()
flavorGroups.each {
if (it.size() != 0) {
flavorGroupTemp.add(it)
}
}
flavorGroups = flavorGroupTemp
calculateFlavorCombination()
if (combinedProductFlavors.size() == extension.productFlavors.size()) {
singleDimension = true
}
}
private void calculateFlavorCombination() {
combinedProductFlavors = new ArrayList<>()
combinedProductFlavorsMap = new HashMap<>()
if (flavorGroups.size() == 0) {
return
}
List<Integer> combination = new ArrayList<Integer>()
int n = flavorGroups.size();
for (int i = 0; i < n; i++) {
combination.add(0);
}
int i = 0;
boolean isContinue = true;
while (isContinue) {
List<String> items = new ArrayList<>()
String item = flavorGroups.get(0).get(combination.get(0))
items.add(item)
String combined = item
for (int j = 1; j < n; j++) {
item = flavorGroups.get(j).get(combination.get(j))
combined += Utils.upperCase(item)
items.add(item)
}
combinedProductFlavors.add(combined)
combinedProductFlavorsMap.put(combined, items)
i++;
combination.set(n - 1, i);
for (int j = n - 1; j >= 0; j--) {
if (combination.get(j) >= flavorGroups.get(j).size()) {
combination.set(j, 0);
i = 0;
if (j - 1 >= 0) {
combination.set(j - 1, combination.get(j - 1) + 1);
}
}
}
isContinue = false;
for (Integer integer : combination) {
if (integer != 0) {
isContinue = true;
}
}
}
}
}

View File

@@ -0,0 +1,53 @@
package com.plugin.pins
import org.gradle.api.Project
import org.w3c.dom.Element
import javax.xml.parsers.DocumentBuilderFactory
class Utils {
static String upperCase(String str) {
char[] ch = str.toCharArray()
if (ch[0] >= 'a' && ch[0] <= 'z') {
ch[0] -= 32
}
return String.valueOf(ch)
}
static String getAndroidManifestPackageName(File androidManifest) {
def builderFactory = DocumentBuilderFactory.newInstance()
builderFactory.setNamespaceAware(true)
Element manifestXml = builderFactory.newDocumentBuilder().parse(androidManifest).documentElement
return manifestXml.getAttribute("package")
}
static MicroModule buildMicroModule(Project project, String microModulePath) {
String[] pathElements = removeTrailingColon(microModulePath).split(":")
int pathElementsLen = pathElements.size()
File parentMicroModuleDir = project.projectDir
for (int j = 0; j < pathElementsLen; j++) {
parentMicroModuleDir = new File(parentMicroModuleDir, pathElements[j])
}
File microModuleDir = parentMicroModuleDir.canonicalFile
String microModuleName = microModuleDir.absolutePath.replace(project.projectDir.absolutePath, "")
if (File.separator == "\\") {
microModuleName = microModuleName.replaceAll("\\\\", ":")
} else {
microModuleName = microModuleName.replaceAll("/", ":")
}
if (!microModuleDir.exists()) {
return null
}
MicroModule microModule = new MicroModule()
microModule.name = microModuleName
microModule.microModuleDir = microModuleDir
return microModule
}
private static String removeTrailingColon(String microModulePath) {
return microModulePath.startsWith(":") ? microModulePath.substring(1) : microModulePath
}
}

View File

@@ -0,0 +1,132 @@
package com.plugin.pins.check
import org.w3c.dom.Document
import org.w3c.dom.Element
import org.w3c.dom.NodeList
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.OutputKeys
import javax.xml.transform.Transformer
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
class CheckManifest {
Document document
Element rootElement
String packageName
Map<String, MicroModuleFile> lastModifiedResourcesMap
Map<String, MicroModuleFile> lastModifiedClassesMap
void load(File sourceFile) {
if (!sourceFile.exists()) return
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance()
document = builderFactory.newDocumentBuilder().parse(sourceFile)
rootElement = document.documentElement
packageName = rootElement.getAttribute("package")
}
void setResourcesLastModified(long lastModified) {
resourcesLastModified = lastModified
}
Map<String, MicroModuleFile> getResourcesMap() {
if (lastModifiedResourcesMap != null) return lastModifiedResourcesMap
lastModifiedResourcesMap = new HashMap<>()
if (rootElement == null) return lastModifiedResourcesMap
NodeList resourcesNodeList = rootElement.getElementsByTagName("resources")
if (resourcesNodeList.length == 0) {
return lastModifiedResourcesMap
}
Element resourcesElement = (Element) resourcesNodeList.item(0)
NodeList fileNodeList = resourcesElement.getElementsByTagName("file")
for (int i = 0; i < fileNodeList.getLength(); i++) {
Element fileElement = (Element) fileNodeList.item(i)
MicroModuleFile microModuleFile = new MicroModuleFile()
microModuleFile.name = fileElement.getAttribute("name")
microModuleFile.microModuleName = fileElement.getAttribute("microModuleName")
microModuleFile.path = fileElement.getAttribute("path")
microModuleFile.lastModified = fileElement.getAttribute("lastModified").toLong()
lastModifiedResourcesMap.put(microModuleFile.path, microModuleFile)
}
return lastModifiedResourcesMap
}
Map<String, MicroModuleFile> getClassesMap() {
if (lastModifiedClassesMap != null) return lastModifiedClassesMap
lastModifiedClassesMap = new HashMap<>()
if (rootElement == null) return lastModifiedClassesMap
NodeList classesNodeList = rootElement.getElementsByTagName("classes")
if (classesNodeList.length == 0) {
return lastModifiedClassesMap
}
Element classesElement = (Element) classesNodeList.item(0)
NodeList fileNodeList = classesElement.getElementsByTagName("file")
for (int i = 0; i < fileNodeList.getLength(); i++) {
Element fileElement = (Element) fileNodeList.item(i)
MicroModuleFile microModuleFile = new MicroModuleFile()
microModuleFile.name = fileElement.getAttribute("name")
microModuleFile.microModuleName = fileElement.getAttribute("microModuleName")
microModuleFile.path = fileElement.getAttribute("path")
microModuleFile.lastModified = fileElement.getAttribute("lastModified").toLong()
lastModifiedClassesMap.put(microModuleFile.path, microModuleFile)
}
return lastModifiedClassesMap
}
void save(File destFile) {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance()
Document documentTemp = builderFactory.newDocumentBuilder().newDocument()
Element microModuleXmlTemp = documentTemp.createElement("micro-module")
microModuleXmlTemp.setAttribute("package", packageName)
// resources
Element resourcesElement = documentTemp.createElement("resources")
microModuleXmlTemp.appendChild(resourcesElement)
if (lastModifiedResourcesMap != null) {
lastModifiedResourcesMap.each {
MicroModuleFile resourceFile = it.value
Element fileElement = documentTemp.createElement("file")
fileElement.setAttribute("name", resourceFile.name)
fileElement.setAttribute("path", resourceFile.path)
fileElement.setAttribute("lastModified", resourceFile.lastModified.toString())
fileElement.setAttribute("microModuleName", resourceFile.microModuleName)
resourcesElement.appendChild(fileElement)
}
}
// classes
if (lastModifiedClassesMap != null) {
Element classesElement = documentTemp.createElement("classes")
microModuleXmlTemp.appendChild(classesElement)
lastModifiedClassesMap.each {
MicroModuleFile resourceFile = it.value
Element fileElement = documentTemp.createElement("file")
fileElement.setAttribute("name", resourceFile.name)
fileElement.setAttribute("path", resourceFile.path)
fileElement.setAttribute("lastModified", resourceFile.lastModified.toString())
fileElement.setAttribute("microModuleName", resourceFile.microModuleName)
classesElement.appendChild(fileElement)
}
microModuleXmlTemp.appendChild(classesElement)
} else if (rootElement != null) {
NodeList classesNodeList = rootElement.getElementsByTagName("classes")
if (classesNodeList.length == 1) {
Element classesElement = (Element) classesNodeList.item(0)
microModuleXmlTemp.appendChild(documentTemp.importNode(classesElement, true))
}
}
// save
Transformer transformer = TransformerFactory.newInstance().newTransformer()
transformer.setOutputProperty(OutputKeys.INDENT, "yes")
transformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, "yes")
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2")
transformer.transform(new DOMSource(microModuleXmlTemp), new StreamResult(destFile))
}
}

View File

@@ -0,0 +1,366 @@
package com.plugin.pins.check
import com.plugin.pins.MicroModule
import com.plugin.pins.MicroModuleInfo
import com.plugin.pins.ProductFlavorInfo
import org.gradle.api.GradleScriptException
import org.gradle.api.Project
import org.w3c.dom.Element
import org.w3c.dom.NodeList
class CodeChecker {
Project project
MicroModuleInfo microModuleInfo
ProductFlavorInfo productFlavorInfo
String buildType
String productFlavor
CheckManifest checkManifest
ResourceMerged resourceMerged
String errorMessage = ""
String lineSeparator = System.getProperty("line.separator")
Map<String, List<String>> microModulePackageNameMap
CodeChecker(Project project, MicroModuleInfo microModuleInfo, ProductFlavorInfo productFlavorInfo, String buildType, String productFlavor) {
this.project = project
this.microModuleInfo = microModuleInfo
this.productFlavorInfo = productFlavorInfo
this.buildType = buildType
this.productFlavor = productFlavor
this.checkManifest = getModuleCheckManifest()
}
void checkResources(String mergeResourcesTaskName, List<String> sourceFolders) {
resourceMerged = new ResourceMerged()
if (!resourceMerged.load(project.projectDir, mergeResourcesTaskName)) {
return
}
List<NodeList> resourceNodeLists = resourceMerged.getResourcesNodeList(sourceFolders)
List<File> modifiedResourcesList = getModifiedResourcesList(resourceNodeLists)
if (modifiedResourcesList.size() == 0) {
return
}
handleModifiedResources(modifiedResourcesList)
if (errorMessage != "") {
throw new GradleScriptException(errorMessage, null)
}
def manifest = new File(microModuleInfo.mainMicroModule.microModuleDir, "src/main/AndroidManifest.xml")
String packageName = Utils.getAndroidManifestPackageName(manifest)
checkManifest.packageName = packageName
saveModuleCheckManifest()
}
List<File> getModifiedResourcesList(List<NodeList> resourcesNodeList) {
Map<String, MicroModuleFile> lastModifiedResourcesMap = checkManifest.getResourcesMap()
List<File> modifiedResourcesList = new ArrayList<>()
if (resourcesNodeList == null || resourcesNodeList.length == 0) return modifiedResourcesList
resourcesNodeList.each {
for (int i = 0; i < it.getLength(); i++) {
Element resourcesElement = (Element) it.item(i)
NodeList fileNodeList = resourcesElement.getElementsByTagName("file")
for (int j = 0; j < fileNodeList.getLength(); j++) {
Element fileElement = (Element) fileNodeList.item(j)
String filePath = fileElement.getAttribute("path")
if (filePath != null && filePath.endsWith(".xml")) {
File file = project.file(filePath)
MicroModuleFile resourceFile = lastModifiedResourcesMap.get(filePath)
def currentModified = file.lastModified()
if (resourceFile == null || resourceFile.lastModified.longValue() < currentModified) {
modifiedResourcesList.add(file)
if (resourceFile == null) {
resourceFile = new MicroModuleFile()
resourceFile.name = file.name
resourceFile.path = filePath
resourceFile.microModuleName = getMicroModuleName(filePath)
lastModifiedResourcesMap.put(filePath, resourceFile)
}
resourceFile.lastModified = currentModified
}
}
}
}
}
return modifiedResourcesList
}
void handleModifiedResources(List<File> modifiedResourcesList) {
Map<String, String> resourcesMap = resourceMerged.getResourcesMap()
def resourcesPattern = /@(dimen|drawable|color|string|style|id|mipmap|layout)\/[A-Za-z0-9_]+/
modifiedResourcesList.each {
String text = it.text
List<String> textLines = text.readLines()
def matcher = (text =~ resourcesPattern)
def absolutePath = it.absolutePath
def microModuleName = getMicroModuleName(absolutePath)
while (matcher.find()) {
def find = matcher.group()
def name = find.substring(find.indexOf("/") + 1)
def from = resourcesMap.get(name)
if (from != null && microModuleName != from && !microModuleInfo.hasDependency(microModuleName, from)) {
List<Number> lines = textLines.findIndexValues { it.contains(find) }
lines.each {
def lineIndex = it.intValue()
def lineContext = textLines.get(lineIndex).trim()
if (lineContext.startsWith("<!--")) {
return
}
def message = absolutePath + ':' + (lineIndex + 1)
if (!errorMessage.contains(message)) {
message += lineSeparator
message += "- cannot use [" + find + "] which from MicroModule '${from}'."
message += lineSeparator
errorMessage += message
}
}
}
}
}
}
void checkClasses(String mergeResourcesTaskName, List<String> sourceFolders) {
if(resourceMerged == null) {
resourceMerged = new ResourceMerged()
if (!resourceMerged.load(project.projectDir, mergeResourcesTaskName)) {
return
}
resourceMerged.getResourcesNodeList(sourceFolders)
}
List<File> modifiedClassesList = getModifiedClassesList(sourceFolders)
if (modifiedClassesList.size() == 0) {
return
}
if (resourceMerged == null) {
resourceMerged = new ResourceMerged()
if (!resourceMerged.load(project.projectDir, mergeResourcesTaskName)) {
return
}
}
handleModifiedClasses(modifiedClassesList)
if (errorMessage != "") {
throw new GradleScriptException(errorMessage, null)
}
saveModuleCheckManifest()
}
List<File> getModifiedClassesList(List<String> sourceFolders) {
Map<String, MicroModuleFile> lastModifiedClassesMap = checkManifest.getClassesMap()
List<File> modifiedClassesList = new ArrayList<>()
microModuleInfo.includeMicroModules.each {
MicroModule microModule = it.value
sourceFolders.each {
File javaDir = new File(microModule.microModuleDir, "/src/${it}/java")
if (javaDir.exists()) {
getModifiedJavaFile(javaDir, modifiedClassesList, lastModifiedClassesMap)
}
File kotlinDir = new File(microModule.microModuleDir, "/src/${it}/kotlin")
if (kotlinDir.exists()) {
getModifiedJavaFile(kotlinDir, modifiedClassesList, lastModifiedClassesMap)
}
}
}
return modifiedClassesList
}
void getModifiedJavaFile(File directory, List<File> modifiedClassesList, Map<String, MicroModuleFile> lastModifiedClassesMap) {
directory.listFiles().each {
if (it.isDirectory()) {
getModifiedJavaFile(it, modifiedClassesList, lastModifiedClassesMap)
} else {
def currentModified = it.lastModified()
MicroModuleFile resourceFile = lastModifiedClassesMap.get(it.absolutePath)
if (resourceFile == null || resourceFile.lastModified.longValue() < currentModified) {
modifiedClassesList.add(it)
if (resourceFile == null) {
resourceFile = new MicroModuleFile()
resourceFile.name = it.name
resourceFile.path = it.absolutePath
resourceFile.microModuleName = getMicroModuleName(it.absolutePath)
lastModifiedClassesMap.put(it.absolutePath, resourceFile)
}
resourceFile.lastModified = it.lastModified()
}
}
}
}
void handleModifiedClasses(List<File> modifiedClassesList) {
Map<String, String> resourcesMap = resourceMerged.getResourcesMap()
Map<String, String> classesMap = new HashMap<>()
checkManifest.getClassesMap().each {
MicroModuleFile resourceFile = it.value
def name = getClassFullName(resourceFile.path)
classesMap.put(name, resourceFile.microModuleName)
}
initMicroModulePackageName()
def resourcesPattern = /R.(dimen|drawable|color|string|style|id|mipmap|layout).[A-Za-z0-9_]+|import\s[A-Za-z0-9_.]+/
modifiedClassesList.each {
String text = it.text
List<String> textLines = text.readLines()
def matcher = (text =~ resourcesPattern)
def absolutePath = it.absolutePath
def microModuleName = getMicroModuleName(absolutePath)
while (matcher.find()) {
matcher
def find = matcher.group()
def from, name
if (find.startsWith("R")) {
name = find.substring(find.lastIndexOf(".") + 1)
from = resourcesMap.get(name)
} else if (find.startsWith("import")) {
name = find.substring(find.lastIndexOf(" ") + 1, find.length())
if(name.endsWith('.R')) {
continue
}
from = classesMap.get(name)
}
if (from != null && microModuleName != from && !microModuleInfo.hasDependency(microModuleName, from)) {
List<Number> lines = textLines.findIndexValues { it.contains(find) }
lines.each {
def lineIndex = it.intValue()
def lineContext = textLines.get(lineIndex).trim()
if (lineContext.startsWith("//") || lineContext.startsWith("/*")) {
return
}
def message = absolutePath + ':' + (lineIndex + 1)
if (!errorMessage.contains(message)) {
message += lineSeparator
message += "- cannot use [" + find + "] which from MicroModule '${from}'."
message += lineSeparator
errorMessage += message
}
}
}
}
}
}
String getClassFullName(String path) {
String microModulePath = path.replace(project.projectDir.absolutePath, "")
if (microModulePath.startsWith(File.separator)) {
microModulePath = microModulePath.substring(microModulePath.indexOf(File.separator) + 1)
}
String scrPath = microModulePath.substring(microModulePath.indexOf(File.separator) + 1)
String variantPath = scrPath.substring(scrPath.indexOf(File.separator) + 1)
String javaPath = variantPath.substring(variantPath.indexOf(File.separator) + 1)
String classPath = javaPath.substring(javaPath.indexOf(File.separator) + 1)
return classPath.substring(0, classPath.lastIndexOf(".")).replace(File.separator, ".")
}
String getMicroModuleName(String absolutePath) {
String moduleName = absolutePath.replace(project.projectDir.absolutePath, "")
moduleName = moduleName.substring(0, moduleName.indexOf(ResourceMerged.SRC))
if (File.separator == "\\") {
moduleName = moduleName.replaceAll("\\\\", ":")
} else {
moduleName = moduleName.replaceAll("/", ":")
}
return moduleName
}
private CheckManifest getModuleCheckManifest() {
CheckManifest checkManifest = new CheckManifest()
StringBuilder stringBuilder = new StringBuilder('build/microModule/code-check/')
if (productFlavor != null) {
stringBuilder.append(productFlavor)
stringBuilder.append('/')
}
stringBuilder.append(buildType)
new File(project.projectDir, stringBuilder.toString()).mkdirs()
stringBuilder.append('/check-manifest.xml')
File manifest = new File(project.projectDir, stringBuilder.toString())
checkManifest.load(manifest)
return checkManifest
}
private CheckManifest saveModuleCheckManifest() {
if (checkManifest == null) {
checkManifest = new CheckManifest()
}
StringBuilder stringBuilder = new StringBuilder('build/microModule/code-check/')
if (productFlavor != null) {
stringBuilder.append(productFlavor)
stringBuilder.append('/')
}
stringBuilder.append(buildType)
new File(project.projectDir, stringBuilder.toString()).mkdirs()
stringBuilder.append('/check-manifest.xml')
File manifest = new File(project.projectDir, stringBuilder.toString())
return checkManifest.save(manifest)
}
private String initMicroModulePackageName() {
microModulePackageNameMap = new HashMap<>()
microModuleInfo.includeMicroModules.each {
MicroModule microModule = it.value
boolean find = false
List<String> flavorList = productFlavorInfo.combinedProductFlavorsMap.get(productFlavor)
if (flavorList != null && !flavorList.isEmpty()) {
for (String flavor : flavorList) {
File manifest = new File(microModule.microModuleDir, "/src/${flavor}/AndroidManifest.xml")
if (manifest.exists()) {
String packageName = Utils.getAndroidManifestPackageName(manifest)
if (packageName != null && !packageName.isEmpty()) {
List<String> microModuleList = microModulePackageNameMap.get(packageName)
if (microModuleList == null) {
microModuleList = new ArrayList<>()
microModulePackageNameMap.put(packageName, microModuleList)
}
microModuleList.add(microModule.name)
find = true
break
}
}
}
}
if (!find) {
File manifest = new File(microModule.microModuleDir, "/src/${buildType}/AndroidManifest.xml")
if (manifest.exists()) {
String packageName = Utils.getAndroidManifestPackageName(manifest)
if (packageName != null && !packageName.isEmpty()) {
List<String> microModuleList = microModulePackageNameMap.get(packageName)
if (microModuleList == null) {
microModuleList = new ArrayList<>()
microModulePackageNameMap.put(packageName, microModuleList)
}
microModuleList.add(microModule.name)
find = true
}
}
}
if (!find) {
File manifest = new File(microModule.microModuleDir, "/src/main/AndroidManifest.xml")
if (manifest.exists()) {
String packageName = Utils.getAndroidManifestPackageName(manifest)
if (packageName != null && !packageName.isEmpty()) {
List<String> microModuleList = microModulePackageNameMap.get(packageName)
if (microModuleList == null) {
microModuleList = new ArrayList<>()
microModulePackageNameMap.put(packageName, microModuleList)
}
microModuleList.add(microModule.name)
}
}
}
}
}
}

View File

@@ -0,0 +1,8 @@
package com.plugin.pins.check
class MicroModuleFile {
String path
String name
Long lastModified
String microModuleName
}

View File

@@ -0,0 +1,102 @@
package com.plugin.pins.check
import org.w3c.dom.Document
import org.w3c.dom.Element
import org.w3c.dom.NodeList
import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory
class ResourceMerged {
static String SRC = File.separator + "src" + File.separator
String projectPath
File resourcesMergerFile
List<NodeList> resourceNodeLists
Map<String, String> resourcesMap
boolean load(File projectDir, String mergeResourcesTaskName) {
projectPath = projectDir.absolutePath
String mergedPath = "build/intermediates/incremental/${mergeResourcesTaskName}/merger.xml"
resourcesMergerFile = new File(projectDir, mergedPath)
return resourcesMergerFile.exists()
}
List<NodeList> getResourcesNodeList(List<String> sourceFolders) {
if (resourceNodeLists == null) {
resourceNodeLists = new ArrayList<>()
} else {
return resourceNodeLists
}
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance()
DocumentBuilder builder = factory.newDocumentBuilder()
FileInputStream inputStream = new FileInputStream(resourcesMergerFile)
Document doc = builder.parse(inputStream)
Element rootElement = doc.getDocumentElement()
NodeList dataSetNodeList = rootElement.getElementsByTagName("dataSet")
for (int i = 0; i < dataSetNodeList.getLength(); i++) {
Element dataSetElement = (Element) dataSetNodeList.item(i)
def config = dataSetElement.getAttribute("config")
if (sourceFolders.contains(config)) {
resourceNodeLists.add(dataSetElement.getElementsByTagName("source"))
}
}
return resourceNodeLists
}
Map<String, String> getResourcesMap() {
if (resourcesMap != null) return resourcesMap
Map<String, String> resourcesMap = new HashMap<String, String>()
if (resourceNodeLists == null || resourceNodeLists.length == 0) {
return resourcesMap
}
resourceNodeLists.each {
int length = it.length
for (int i = 0; i < length; i++) {
Element resourcesElement = (Element) it.item(i)
String path = resourcesElement.getAttribute("path")
String moduleName = path.replace(projectPath, "")
if (moduleName.startsWith(File.separator + "build")) continue
moduleName = moduleName.substring(0, moduleName.indexOf(SRC))
if (File.separator == "\\") {
moduleName = moduleName.replaceAll("\\\\", ":")
} else {
moduleName = moduleName.replaceAll("/", ":")
}
NodeList fileNodeList = resourcesElement.getElementsByTagName("file")
def fileNodeLength = fileNodeList.getLength()
if (fileNodeLength <= 0) {
continue
}
for (int j = 0; j < fileNodeLength; j++) {
Element fileElement = (Element) fileNodeList.item(j)
String name = fileElement.getAttribute("name")
if (name == "") {
NodeList nodeList = fileElement.getChildNodes()
def nodeLength = nodeList.getLength()
if (nodeLength <= 0) {
continue
}
for (int k = 0; k < nodeLength; k++) {
Element childElement = (Element) nodeList.item(k)
name = childElement.getAttribute("name")
resourcesMap.put(name, moduleName)
}
} else {
resourcesMap.put(name, moduleName)
}
}
}
}
return resourcesMap
}
}

View File

@@ -0,0 +1,55 @@
package com.plugin.pins.extension
import com.plugin.pins.MicroModule
import org.gradle.api.GradleException
import org.gradle.api.Project
class DefaultMicroModuleExtension implements MicroModuleExtension {
Project project
OnMicroModuleListener onMicroModuleListener
boolean codeCheckEnabled = true
DefaultMicroModuleExtension(Project project) {
this.project = project
}
@Override
void codeCheckEnabled(boolean enabled) {
this.codeCheckEnabled = enabled
}
@Override
void export(String... microModulePaths) {
if(onMicroModuleListener == null) return
onMicroModuleListener.addExportMicroModule(microModulePaths)
}
@Override
void include(String... microModulePaths) {
if(onMicroModuleListener == null) return
int size = microModulePaths.size()
for (int i = 0; i < size; i++) {
MicroModule microModule = Utils.buildMicroModule(project, microModulePaths[i])
if (microModule == null) {
throw new GradleException("MicroModule with path '${microModulePaths[i]}' could not be found in ${project.getDisplayName()}.")
}
onMicroModuleListener.addIncludeMicroModule(microModule, false)
}
}
@Override
void includeMain(String microModulePath) {
if(onMicroModuleListener == null) return
MicroModule microModule = Utils.buildMicroModule(project, microModulePath)
if (microModule == null) {
throw new GradleException("MicroModule with path '${microModulePath}' could not be found in ${project.getDisplayName()}.")
}
onMicroModuleListener.addIncludeMicroModule(microModule, true)
}
}

View File

@@ -0,0 +1,13 @@
package com.plugin.pins.extension
interface MicroModuleExtension {
void codeCheckEnabled(boolean disable)
void export(String... microModulePaths)
void includeMain(String microModulePath)
void include(String... microModulePaths)
}

View File

@@ -0,0 +1,12 @@
package com.plugin.pins.extension
import com.plugin.pins.MicroModule
interface OnMicroModuleListener {
void addIncludeMicroModule(MicroModule microModule, boolean mainMicroModule)
void addExportMicroModule(String... microModulePaths)
}

View File

@@ -5,7 +5,7 @@
//include ':component-plugin'
//调试插件
//includeBuild './component-plugin'
includeBuild './component-plugin'
//
include ':app'
include ':debugModule'