Chinese Annotation for android fat-aar.gradle

Keywords: Android Java xml Gradle

/**
 * This is free and unencumbered software released into the public domain.
 Anyone is free to copy, modify, publish, use, compile, sell, or
 distribute this software, either in source code form or as a compiled
 binary, for any purpose, commercial or non-commercial, and by any
 means.
 In jurisdictions that recognize copyright laws, the author or authors
 of this software dedicate any and all copyright interest in the
 software to the public domain. We make this dedication for the benefit
 of the public at large and to the detriment of our heirs and
 successors. We intend this dedication to be an overt act of
 relinquishment in perpetuity of all present and future rights to this
 software under copyright law.
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.
 For more information, please refer to <http://unlicense.org/>
 */
Translation:
/** 
    This is freely available software that is published to the public domain.
    Anyone can copy, modify, publish, use, compile, sell or distribute this software in any manner, either as source code or as compiled binary files, for any purpose, commercial or non-commercial.
    In jurisdictions that recognize copyright law, the authors of this software will make any copyright interest in the software publicly available.This dedication has been made for the benefit of the general public at the expense of our successors.We intend to use this dedication as a statement that this software will permanently waive existing and future copyrights under copyright law.
    The software is provided "as is" without warranties of any kind, expressed or implied, including, but not limited to, marketability, and is applicable to specific uses and non-infringement.Under no circumstances shall the author be liable, including any claim, damage or other, whether for contract, tort or other reasons, in use or in other trading software after use.
    For more information, see <http://unlicense.org/>
 */

Code content:

import com.android.annotations.NonNull
import com.android.manifmerger.ManifestMerger2
import com.android.manifmerger.ManifestMerger2.Invoker
import com.android.manifmerger.ManifestMerger2.MergeType
import com.android.manifmerger.MergingReport
import com.android.manifmerger.PlaceholderEncoder
import com.android.manifmerger.XmlDocument
import com.android.utils.ILogger
import com.google.common.base.Charsets
import com.google.common.io.Files
/**
 * Fat AAR Lib generator v 0.2.1
 * Target Gradle Version :: 2.2.0
 *
 * Latest version available at https://github.com/adwiv/android-fat-aar
 * Please report issues at https://github.com/adwiv/android-fat-aar/issues
 *
 * This code is in public domain.
 *
 * Use at your own risk and only if you understand what it does. You have been warned ! :-)
 */
Translation:
/**
    fat-aar library v0.2.1
    Target gradle version: 2.2.0
    The latest version can be downloaded at https://github.com/adwiv/android-fat-aar
    Report problems at https://github.com/adwiv/android-fat-aar/issues
    This code is public.
    Finally, let me remind you that the risk is yours.
 */
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:manifest-merger:25.3.2'
    }
}

configurations {
    embedded
}

dependencies {
    compile configurations.embedded
}
// Paths to embedded jar files
//Path to the jar file that needs to be packaged
ext.embeddedJars = new ArrayList()
// Paths to embedded aar projects
//Project path that needs to package aar
ext.embeddedAarDirs = new ArrayList()
// Embedded aar files dependencies
//File dependencies for AARS that need to be packaged
ext.embeddedAarFiles = new ArrayList<ResolvedArtifact>()
// List of embedded R classes
//List of R classes
ext.embeddedRClasses = new ArrayList()
// Change backslash to forward slash on windows
//Change backslash if you want to run on windows
ext.build_dir = buildDir.path.replace(File.separator, '/');
//Extended build directory = current build directory
ext.root_dir = project.rootDir.absolutePath.replace(File.separator, '/');
//Extended Root Directory = Absolute path to the root directory of the current project
ext.exploded_aar_dir = "$build_dir/intermediates/exploded-aar";
//Extended AAR in directory = build directory/intermediates/exploded-aar
ext.classs_release_dir = "$build_dir/intermediates/classes/release";
//Extended class in directory = build directory/intermediates/classes/release
ext.bundle_release_dir = "$build_dir/intermediates/bundles/release";
//Extended bundles in directory = build directory/intermediates/bundles/release
ext.manifest_aaapt_dir = "$build_dir/intermediates/manifests/aapt/release";
//Extended manifest in directory = build directory/intermediates/manifests/aapt/release
ext.generated_rsrc_dir = "$build_dir/generated/source/r/release";
//Generated Resource File Directory=build Directory/generated/source/r/release
ext.base_r2x_dir = "$build_dir/fat-aar/release/";
//Basic r2x directory = build directory / fat-aar/release

//Get gradle version information
def gradleVersionStr = GradleVersion.current().getVersion();
ext.gradleApiVersion = gradleVersionStr.substring(0, gradleVersionStr.lastIndexOf(".")).toFloat();
//Print Gradle version information
println "Gradle version: " + gradleVersionStr;

afterEvaluate {
    // the list of dependency must be reversed to use the right overlay order.
    //Dependency lists must be in reverse order to use the correct override order.
    def dependencies = new ArrayList(configurations.embedded.resolvedConfiguration.firstLevelModuleDependencies)
    //Begin reverse order
    dependencies.reverseEach {

        def aarPath;
        if (gradleApiVersion >= 2.3f)
            aarPath = "${root_dir}/${it.moduleName}/build/intermediates/bundles/default"
        else
            aarPath = "${exploded_aar_dir}/${it.moduleGroup}/${it.moduleName}/${it.moduleVersion}"
//If the compiled version is higher than 2.3 aar directory=root directory/module name/build/intermediates/bundles/default
//Otherwise aar directory = extended aar directory / module group / module name / module version above
        it.moduleArtifacts.each {
            artifact ->

                println "ARTIFACT 3 : "
                println artifact
//If the component type is an aar file, join directly if it is not in the list of aar files that need to be packaged above, if it is not in the aar folder, copy directly from the Zip package to the aarpath if it is a file, and finally add the aarpath to the folder path
                if (artifact.type == 'aar') {
                    if (!embeddedAarFiles.contains(artifact)) {
                        embeddedAarFiles.add(artifact)
                    }
                    if (!embeddedAarDirs.contains(aarPath)) {
                        if( artifact.file.isFile() ){
                            println artifact.file
                            println aarPath

                            copy {
                                from zipTree( artifact.file )
                                into aarPath
                            }
                        }
                        embeddedAarDirs.add(aarPath)
                    }
                } else if (artifact.type == 'jar') {
//If it's a jar type, if it's not in the jar file list, join it directly
/If so aar Nor will it jar Throw an exception "Cannot handle this file type"
                    def artifactPath = artifact.file
                    if (!embeddedJars.contains(artifactPath))
                        embeddedJars.add(artifactPath)
                } else {
                    throw new Exception("Unhandled Artifact of type ${artifact.type}")
                }
        }
    }
    if (dependencies.size() > 0) {
        // Merge Assets Merge Catalog
        generateReleaseAssets.dependsOn embedAssets
        embedAssets.dependsOn prepareReleaseDependencies

        // Embed Resources by overwriting the inputResourceSets overwrite the input resource collection to embed the resource file
        packageReleaseResources.dependsOn embedLibraryResources
        embedLibraryResources.dependsOn prepareReleaseDependencies

        // Embed JNI Libraries Embedded in jni Library
        bundleRelease.dependsOn embedJniLibs

        if(gradleApiVersion >= 2.3f) {

            embedJniLibs.dependsOn transformNativeLibsWithSyncJniLibsForRelease
            ext.bundle_release_dir = "$build_dir/intermediates/bundles/default"
        }else{
            embedJniLibs.dependsOn transformNative_libsWithSyncJniLibsForRelease
            ext.bundle_release_dir = "$build_dir/intermediates/bundles/release";
        }

        // Merge Embedded Manifests merge embedded manifest files
        bundleRelease.dependsOn embedManifests
        embedManifests.dependsOn processReleaseManifest

        // Merge proguard files merge confusing files
        embedLibraryResources.dependsOn embedProguard
        embedProguard.dependsOn prepareReleaseDependencies

        // Generate R.java files to generate R.java files
        compileReleaseJavaWithJavac.dependsOn generateRJava
        generateRJava.dependsOn processReleaseResources

        // Bundle the java classes inherits Java class files
        bundleRelease.dependsOn embedJavaJars
        embedJavaJars.dependsOn compileReleaseJavaWithJavac

        // If proguard is enabled, run the tasks that bundleRelease should depend on before proguard If confusion is required, then run the dependency before confusion
        if (tasks.findByPath('proguardRelease') != null) {
            proguardRelease.dependsOn embedJavaJars
        } else if (tasks.findByPath('transformClassesAndResourcesWithProguardForRelease') != null) {
            transformClassesAndResourcesWithProguardForRelease.dependsOn embedJavaJars
        }
    }
}

task embedLibraryResources << {
    println "Running FAT-AAR Task :embedLibraryResources"

    def oldInputResourceSet = packageReleaseResources.inputResourceSets
    packageReleaseResources.conventionMapping.map("inputResourceSets") {
        getMergedInputResourceSets(oldInputResourceSet)
    }
}

private List getMergedInputResourceSets(List inputResourceSet) {
    //We need to do this trickery here since the class declared here and that used by the runtime
    //are different and results in class cast error Because classes are declared and run using different classes, which may result in a cast exception, we need to do a class spoofing here
    def ResourceSetClass = inputResourceSet.get(0).class

    List newInputResourceSet = new ArrayList(inputResourceSet)

    println "getMergedInputResourceSets"

    println embeddedAarDirs
    embeddedAarDirs.each { aarPath ->
        try {
            println aarPath
            def resname
            if (gradleApiVersion >= 2.3f) {
                def parentProject = project.rootProject.name.toString()
                println "parent: "
                println parentProject

                def startIndex = aarPath.indexOf('/' + parentProject)
                def endIndex = aarPath.indexOf('/build/')

                println "start"
                println startIndex
                println "end"
                println endIndex
                if (startIndex < 1 || endIndex < 1)
                    return;
                resname = aarPath.substring(startIndex, endIndex).replace('/', ':')
            }
            else
                resname = (aarPath.split(exploded_aar_dir)[1]).replace('/', ':');
            def rs = ResourceSetClass.newInstance([resname, true] as Object[])
            rs.addSource(file("$aarPath/res"))
            println "ResourceSet is " + rs
            println resname
            newInputResourceSet += rs
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    return newInputResourceSet
}

/**
 * Assets are simple files, so just adding them to source set seems to work.
 * Directories are simple files, so just add them to the resource file work
 */
task embedAssets << {
    println "Running FAT-AAR Task :embedAssets"
    embeddedAarDirs.each { aarPath ->
        // Merge Assets Merge Catalog
        android.sourceSets.main.assets.srcDirs += file("$aarPath/assets")
    }
}

/**
 * Merge proguard.txt files from all library modules
 * Merge confusing files from all library modules
 * @author Marian Klühspies
 */
task embedProguard << {
    println "Running FAT-AAR Task :embedProguard"

    def proguardRelease = file("$bundle_release_dir/proguard.txt")
    embeddedAarDirs.each { aarPath ->
        try {
            def proguardLibFile = file("$aarPath/proguard.txt")
            if (proguardLibFile.exists())
                proguardRelease.append("\n" + proguardLibFile.text)
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }
}


task generateRJava << {
    println "Running FAT-AAR Task :generateRJava"

    // Now generate the R.java file for each embedded dependency to generate R.java files for each embedded dependency
    def mainManifestFile = android.sourceSets.main.manifest.srcFile;
    def libPackageName = "";

    if(mainManifestFile.exists()) {

        libPackageName = new XmlParser().parse(mainManifestFile).@package
    }

    embeddedAarDirs.each { aarPath ->

        def manifestFile = file("$aarPath/AndroidManifest.xml");
        if(!manifestFile.exists()) {
            manifestFile = file("./src/main/AndroidManifest.xml");
        }

        if(manifestFile.exists()) {
            def aarManifest = new XmlParser().parse(manifestFile);
            def aarPackageName = aarManifest.@package

            String packagePath = aarPackageName.replace('.', '/')

            // Generate the R.java file and map to current project's R.java
            // This will recreate the class file to generate an R.java file and map it to the current project's R.java file, which will regenerate the class file
            def rTxt = file("$aarPath/R.txt")
            def rMap = new ConfigObject()

            if (rTxt.exists()) {
                rTxt.eachLine {
                    line ->
                        //noinspection GroovyUnusedAssignment
                        def (type, subclass, name, value) = line.tokenize(' ')
                        rMap[subclass].putAt(name, type)
                }
            }

            def sb = "package $aarPackageName;" << '\n' << '\n'
            sb << 'public final class R {' << '\n'

            rMap.each {
                subclass, values ->
                    sb << "  public static final class $subclass {" << '\n'
                    values.each {
                        name, type ->
                            sb << "    public static $type $name = ${libPackageName}.R.${subclass}.${name};" << '\n'
                    }
                    sb << "    }" << '\n'
            }

            sb << '}' << '\n'

            mkdir("$generated_rsrc_dir/$packagePath")
            file("$generated_rsrc_dir/$packagePath/R.java").write(sb.toString())

            embeddedRClasses += "$packagePath/R.class"
            embeddedRClasses += "$packagePath/R\$*.class"
        }

    }
}

task collectRClass << {
    println "COLLECTRCLASS"
    delete base_r2x_dir
    mkdir base_r2x_dir

    copy {
        from classs_release_dir
        include embeddedRClasses
        into base_r2x_dir
    }
}

task embedRClass(type: org.gradle.jvm.tasks.Jar, dependsOn: collectRClass) {
    println "EMBED R CLASS"

    destinationDir file("$bundle_release_dir/libs/")
    println destinationDir
    from base_r2x_dir
    println base_r2x_dir
}

/**
 * To embed the class files, we need to change the R.class to X.class, so we explode it in another
 * location, proguard it to modify R to X, and then finally copy it to build location
 * In order to embed the class file, we need to change R.class to X.class, so we uncompress it in another place or two, confuse it, modify R to X, and finally copy it to the build location
 */
task embedJavaJars(dependsOn: embedRClass) << {
    println "Running FAT-AAR Task :embedJavaJars"

    embeddedAarDirs.each { aarPath ->

        // Explode all classes.jar files to classes so that they can be proguarded Unzip all class.jar files, which can be confusing
        def jar_dir
        if (gradleApiVersion >= 2.3f)
            jar_dir = "$aarPath"
        else
            jar_dir = "$aarPath/jars"

        if(embeddedAarFiles.size() > 0){

            embeddedAarFiles.each {
                artifact ->
                    FileTree aarFileTree = zipTree(artifact.file.getAbsolutePath());

                    def aarFile = aarFileTree.files.find { it.name.contains("classes.jar") }

                    copy {
                        from zipTree(aarFile)
                        into classs_release_dir
                    }
            }

        }else{

            println jar_dir
            println classs_release_dir
            println bundle_release_dir
            println embeddedJars

            copy {
                from zipTree(jar_dir + "/classes.jar")
                into classs_release_dir
            }
        }

        // Copy all additional jar files to bundle lib Copy all additional jar files to bundle lib directory
        FileTree jars = fileTree(dir: jar_dir, include: '*.jar', exclude: 'classes.jar')
        jars += fileTree(dir: jar_dir + "/libs", include: '*.jar')
        jars += fileTree(dir: "$aarPath/libs", include: '*.jar')

        copy {
            from jars
            into file("$bundle_release_dir/libs")
        }

        // Copy all embedded jar files to bundle lib Copy all embedded jar files to bundle Lib
        copy {
            from embeddedJars
            into file("$bundle_release_dir/libs")
        }
    }
}

/**
 * For some reason, adding to the jniLibs source set does not work. So we simply copy all files.
 * For some reason, resources added to jni lib do not work, so we can only simply copy them in
 */
task embedJniLibs << {
    println "Running FAT-AAR Task :embedJniLibs"

    embeddedAarDirs.each { aarPath ->
        println "======= Copying JNI from $aarPath/jni"
        // Copy JNI Folders
        copy {
            from fileTree(dir: "$aarPath/jni")
            into file("$bundle_release_dir/jni")
        }
    }
}

task embedManifests << {
    println "Running FAT-AAR Task :embedManifests"

    ILogger mLogger = new MiLogger()
    List libraryManifests = new ArrayList<>()

    embeddedAarDirs.each { aarPath ->
        File dependencyManifest = file("$aarPath/AndroidManifest.xml")

        if (!libraryManifests.contains(aarPath) && dependencyManifest.exists()) {
            libraryManifests.add(dependencyManifest)
        }
    }

    File reportFile = file("${build_dir}/embedManifestReport.txt")

    File origManifest = file("$bundle_release_dir/AndroidManifest.xml")
    File copyManifest = file("$bundle_release_dir/AndroidManifest.orig.xml")
    File aaptManifest = file("$manifest_aaapt_dir/AndroidManifest.xml")

    if(!origManifest.exists()) {
        origManifest = file("./src/main/AndroidManifest.xml")
    }

    if(!origManifest.exists()) {
        return;
    }

    copy {
        from origManifest.parentFile
        into copyManifest.parentFile
        include origManifest.name
        rename(origManifest.name, copyManifest.name)
    }

    try {
        Invoker manifestMergerInvoker = ManifestMerger2.newMerger(copyManifest, mLogger, MergeType.APPLICATION)

        manifestMergerInvoker.addLibraryManifests(libraryManifests.toArray(new File[libraryManifests.size()]))

        // manifestMergerInvoker.setPlaceHolderValues(placeHolders)
        manifestMergerInvoker.setMergeReportFile(reportFile);

        MergingReport mergingReport = manifestMergerInvoker.merge();

        mLogger.info("Merging result:" + mergingReport.getResult());
        MergingReport.Result result = mergingReport.getResult();
        switch (result) {
            case MergingReport.Result.WARNING:
                mergingReport.log(mLogger);
        // fall through since these are just warnings.
            case MergingReport.Result.SUCCESS:
                XmlDocument xmlDocument = mergingReport.getMergedXmlDocument(MergingReport.MergedManifestKind.MERGED);
                try {
                    String annotatedDocument = mergingReport.getActions().blame(xmlDocument);
                    mLogger.verbose(annotatedDocument);
                } catch (Exception e) {
                    mLogger.error(e, "cannot print resulting xml");
                }
                save(xmlDocument, origManifest);
                mLogger.info("Merged manifest saved to " + origManifest);
                if (aaptManifest.exists()) {
                    new PlaceholderEncoder().visit(xmlDocument);
                    save(xmlDocument, aaptManifest);
                    mLogger.info("Merged aapt safe manifest saved to " + aaptManifest);
                }
                break;
            case MergingReport.Result.ERROR:
                mergingReport.log(mLogger);
                throw new RuntimeException(mergingReport.getReportString());
            default:
                throw new RuntimeException("Unhandled result type : " + mergingReport.getResult());
        }
    } catch (RuntimeException e) {
        // Unacceptable error
        e.printStackTrace()
        throw new RuntimeException(e);
    }
}

private void save(XmlDocument xmlDocument, File out) {
    try {
        Files.write(xmlDocument.prettyPrint(), out, Charsets.UTF_8);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

class MiLogger implements ILogger {

    @Override
    void error(
            @com.android.annotations.Nullable Throwable t,
            @com.android.annotations.Nullable String msgFormat, Object... args) {
        System.err.println(String.format("========== ERROR : " + msgFormat, args))
        if (t) t.printStackTrace(System.err)
    }

    @Override
    void warning(@NonNull String msgFormat, Object... args) {
        System.err.println(String.format("========== WARNING : " + msgFormat, args))
    }

    @Override
    void info(@NonNull String msgFormat, Object... args) {
        System.out.println(String.format("========== INFO : " + msgFormat, args))
    }

    @Override
    void verbose(@NonNull String msgFormat, Object... args) {
        // System.out.println(String.format("========== DEBUG : " + msgFormat, args))
    }
}

Posted by bullchina on Sun, 19 May 2019 19:51:24 -0700