美文网首页
gradle脚本合并libs ,manifest, jnilib

gradle脚本合并libs ,manifest, jnilib

作者: 钢镚koala | 来源:发表于2022-04-20 18:51 被阅读0次

    最近在合并modules或者aar , jar等工作上遇到了一些问题。查找各种文章,最后找到一些解决思路和办法。
    希望能帮助到大家。下面这个是早期作者的处理libs, 清单文件等合并的办法。

    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

    /**

    buildscript {
    repositories {
    jcenter()
    }
    dependencies {
    classpath 'com.android.tools.build:manifest-merger:25.2.0'
    }
    }

    configurations {
    embedded
    }

    dependencies {
    compile configurations.embedded
    }

    // Paths to embedded jar files
    ext.embeddedJars = new ArrayList<String>()
    // Paths to embedded aar projects
    ext.embeddedAarDirs = new ArrayList<String>()
    // List of embedded R classes
    ext.embeddedRClasses = new ArrayList<String>()

    // Change backslash to forward slash on windows
    ext.build_dir = buildDir.path.replace(File.separator, '/');

    ext.exploded_aar_dir = "build_dir/intermediates/exploded-aar"; ext.classs_release_dir = "build_dir/intermediates/classes/release";
    ext.bundle_release_dir = "build_dir/intermediates/bundles/release"; ext.manifest_aaapt_dir = "build_dir/intermediates/manifests/aapt/release";
    ext.generated_rsrc_dir = "$build_dir/generated/source/r/release";

    ext.base_r2x_dir = "$build_dir/fat-aar/release/";

    afterEvaluate {
    // the list of dependency must be reversed to use the right overlay order.
    def dependencies = new ArrayList(configurations.embedded.resolvedConfiguration.firstLevelModuleDependencies)
    dependencies.reverseEach {
    def aarPath = "{exploded_aar_dir}/{it.moduleGroup}/{it.moduleName}/{it.moduleVersion}"
    it.moduleArtifacts.each {
    artifact ->
    if (artifact.type == 'aar') {
    if (!embeddedAarDirs.contains(aarPath)) {
    embeddedAarDirs.add(aarPath)
    }
    } else if (artifact.type == 'jar') {
    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
        generateReleaseAssets.dependsOn embedAssets
        embedAssets.dependsOn prepareReleaseDependencies
    
        // Embed Resources by overwriting the inputResourceSets
        packageReleaseResources.dependsOn embedLibraryResources
        embedLibraryResources.dependsOn prepareReleaseDependencies
    
        // Embed JNI Libraries
        bundleRelease.dependsOn embedJniLibs
        embedJniLibs.dependsOn transformNative_libsWithSyncJniLibsForRelease
    
        // Merge Embedded Manifests
        bundleRelease.dependsOn embedManifests
        embedManifests.dependsOn processReleaseManifest
    
        // Merge proguard files
        embedLibraryResources.dependsOn embedProguard
        embedProguard.dependsOn prepareReleaseDependencies
    
        // Generate R.java files
        compileReleaseJavaWithJavac.dependsOn generateRJava
        generateRJava.dependsOn processReleaseResources
    
        // Bundle the java classes
        bundleRelease.dependsOn embedJavaJars
        embedJavaJars.dependsOn compileReleaseJavaWithJavac
    
        // If proguard is enabled, run the tasks that bundleRelease should depend on before proguard
        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
    def ResourceSetClass = inputResourceSet.get(0).class

    List newInputResourceSet = new ArrayList(inputResourceSet)
    
    embeddedAarDirs.each { aarPath ->
        try {
            def 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
            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.
      */
      task embedAssets << {
      println "Running FAT-AAR Task :embedAssets"
      embeddedAarDirs.each { aarPath ->
      // Merge Assets
      android.sourceSets.main.assets.srcDirs += file("$aarPath/assets")
      }
      }

    /**

    • Merge proguard.txt 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(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
    def libPackageName = new XmlParser().parse(android.sourceSets.main.manifest.srcFile).@package
    
    embeddedAarDirs.each { aarPath ->
    
        def aarManifest = new XmlParser().parse(file("$aarPath/AndroidManifest.xml"));
        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
        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 << {
    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) {
    destinationDir file("$bundle_release_dir/libs/")
    from 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
      */
      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
      copy {
      from zipTree("$aarPath/jars/classes.jar")
      into classs_release_dir
      }

       // Copy all additional jar files to bundle lib
       FileTree jars = fileTree(dir: "$aarPath/jars", include: '*.jar', exclude: 'classes.jar')
       jars += fileTree(dir: "$aarPath/jars/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 {
           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.
      */
      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<File> libraryManifests = new ArrayList<>()
    
    embeddedAarDirs.each { aarPath ->
        if (!libraryManifests.contains(aarPath)) {
            libraryManifests.add(file("$aarPath/AndroidManifest.xml"))
        }
    }
    
    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")
    
    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))
    }
    

    }

    当然综合各处,现在也有很多人在用此版本的方案处理:https://github.com/kezong/fat-aar-android
    里面有中文文档。

    欢迎大家提供更多的解决办法。

    相关文章

      网友评论

          本文标题:gradle脚本合并libs ,manifest, jnilib

          本文链接:https://www.haomeiwen.com/subject/fdvstrtx.html