Search in sources :

Example 1 with WaitableExecutor

use of com.android.ide.common.internal.WaitableExecutor in project atlas by alibaba.

the class ResourcesShrinker method splitAction.

@Nullable
public File splitAction(@NonNull ApkData apkData, @Nullable File uncompressedResourceFile, TransformInvocation invocation, SplitList splitList) {
    if (uncompressedResourceFile == null) {
        return null;
    }
    List<File> classes = new ArrayList<>();
    classes.addAll(AtlasBuildContext.atlasMainDexHelperMap.get(variantContext.getVariantName()).getInputDirs());
    classes.addAll(AtlasBuildContext.atlasMainDexHelperMap.get(variantContext.getVariantName()).getAllMainDexJars());
    AppVariantOutputContext appVariantOutputContext = variantContext.getAppVariantOutputContext(apkData);
    for (AwbTransform awbTransform : appVariantOutputContext.getAwbTransformMap().values()) {
        classes.addAll(awbTransform.getInputLibraries());
        if (awbTransform.getInputDirs() != null && awbTransform.getInputDirs().size() > 0) {
            classes.addAll(awbTransform.getInputDirs());
        }
    }
    WaitableExecutor executor = WaitableExecutor.useGlobalSharedThreadPool();
    Collection<BuildOutput> mergedManifests = BuildOutputs.load(ResourcesShrinker.this.mergedManifests);
    BuildOutput mergedManifest = OutputScope.getOutput(mergedManifests, TaskOutputHolder.TaskOutputType.MERGED_MANIFESTS, apkData);
    File mappingFile = mappingFileSrc != null ? mappingFileSrc.getSingleFile() : null;
    ForkJoinTask<File> task = executor.execute(() -> {
        File reportFile = null;
        if (mappingFile != null) {
            File logDir = mappingFile.getParentFile();
            if (logDir != null) {
                reportFile = new File(logDir, "resources.txt");
            }
        }
        File compressedResourceFile = new File(compressedResources, "resources-" + apkData.getBaseName() + "-stripped.ap_");
        FileUtils.mkdirs(compressedResourceFile.getParentFile());
        if (mergedManifest == null) {
            try {
                FileUtils.copyFile(uncompressedResourceFile, compressedResourceFile);
            } catch (IOException e) {
                logger.error("Failed to copy uncompressed resource file :", e);
                throw new RuntimeException("Failed to copy uncompressed resource file", e);
            }
            return compressedResourceFile;
        }
        // Analyze resources and usages and strip out unused
        ResourceUsageAnalyzer analyzer = new ResourceUsageAnalyzer(sourceDir, classes, mergedManifest.getOutputFile(), mappingFile, resourceDir.getSingleFile(), reportFile);
        try {
            analyzer.setVerbose(logger.isEnabled(LogLevel.INFO));
            analyzer.setDebug(logger.isEnabled(LogLevel.DEBUG));
            analyzer.analyze();
            // Just rewrite the .ap_ file to strip out the res/ files for unused resources
            analyzer.rewriteResourceZip(uncompressedResourceFile, compressedResourceFile);
            // Dump some stats
            int unused = analyzer.getUnusedResourceCount();
            if (unused > 0) {
                StringBuilder sb = new StringBuilder(200);
                sb.append("Removed unused resources");
                // This is a bit misleading until we can strip out all resource types:
                // int total = analyzer.getTotalResourceCount()
                // sb.append("(" + unused + "/" + total + ")")
                long before = uncompressedResourceFile.length();
                long after = compressedResourceFile.length();
                long percent = (int) ((before - after) * 100 / before);
                sb.append(": Binary resource data reduced from ").append(toKbString(before)).append("KB to ").append(toKbString(after)).append("KB: Removed ").append(percent).append("%");
                if (!ourWarned) {
                    ourWarned = true;
                    String name = variantData.getVariantConfiguration().getBuildType().getName();
                    sb.append("\n").append("Note: If necessary, you can disable resource shrinking by adding\n").append("android {\n").append("    buildTypes {\n").append("        ").append(name).append(" {\n").append("            shrinkResources false\n").append("        }\n").append("    }\n").append("}");
                }
                System.out.println(sb.toString());
            }
        } catch (Exception e) {
            logger.quiet("Failed to shrink resources: ignoring", e);
        } finally {
            analyzer.dispose();
        }
        return compressedResourceFile;
    });
    for (AwbTransform awbTransform : appVariantOutputContext.getAwbTransformMap().values()) {
        AwbBundle awbBundle = awbTransform.getAwbBundle();
        File compressedBundleResourceFile = appVariantOutputContext.getAwbCompressResourcePackageOutputFile(awbBundle);
        File unCompressedBundleResourceFile = appVariantOutputContext.getAwbProcessResourcePackageOutputFile(awbBundle);
        File awbResDir = appVariantOutputContext.getAwbMergedResourceDir(variantContext.getVariantConfiguration(), awbBundle);
        File reportFile = new File(uncompressedResourceFile.getParentFile(), "resources.txt");
        File bundleSourceDir = appVariantOutputContext.getAwbRClassSourceOutputDir(variantContext.getVariantConfiguration(), awbBundle);
        executor.execute(() -> {
            ResourceUsageAnalyzer analyzer = new ResourceUsageAnalyzer(bundleSourceDir, classes, mergedManifest.getOutputFile(), mappingFile, awbResDir, reportFile);
            try {
                analyzer.setVerbose(logger.isEnabled(LogLevel.INFO));
                analyzer.setDebug(logger.isEnabled(LogLevel.DEBUG));
                analyzer.analyze();
                // Just rewrite the .ap_ file to strip out the res/ files for unused resources
                analyzer.rewriteResourceZip(unCompressedBundleResourceFile, compressedBundleResourceFile);
                // Dump some stats
                int unused = analyzer.getUnusedResourceCount();
                if (unused > 0) {
                    StringBuilder sb = new StringBuilder(200);
                    sb.append("Removed awb bundle" + awbBundle.getName() + " unused resources");
                    // This is a bit misleading until we can strip out all resource types:
                    // int total = analyzer.getTotalResourceCount()
                    // sb.append("(" + unused + "/" + total + ")")
                    long before = unCompressedBundleResourceFile.length();
                    long after = compressedBundleResourceFile.length();
                    long percent = (int) ((before - after) * 100 / before);
                    sb.append(": Binary resource data reduced from ").append(toKbString(before)).append("KB to ").append(toKbString(after)).append("KB: Removed ").append(percent).append("%");
                    if (!ourWarned) {
                        ourWarned = true;
                        String name = variantData.getVariantConfiguration().getBuildType().getName();
                        sb.append("\n").append("Note: If necessary, you can disable resource shrinking by adding\n").append("android {\n").append("    buildTypes {\n").append("        ").append(name).append(" {\n").append("            shrinkResources false\n").append("        }\n").append("    }\n").append("}");
                    }
                    System.out.println(sb.toString());
                }
            } catch (Exception e) {
                logger.quiet("Failed to shrink resources: ignoring", e);
            } finally {
                analyzer.dispose();
            }
            return compressedBundleResourceFile;
        });
    }
    try {
        List<WaitableExecutor.TaskResult<File>> taskResults = executor.waitForAllTasks();
        taskResults.forEach(taskResult -> {
            if (taskResult.getException() != null) {
                throw new BuildException(taskResult.getException().getMessage(), taskResult.getException());
            }
        });
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new RuntimeException(e);
    }
    return task.join();
}
Also used : WaitableExecutor(com.android.ide.common.internal.WaitableExecutor) AppVariantOutputContext(com.android.build.gradle.internal.api.AppVariantOutputContext) IOException(java.io.IOException) IOException(java.io.IOException) ExecutionException(java.util.concurrent.ExecutionException) BuildException(org.gradle.tooling.BuildException) AwbTransform(com.android.build.gradle.internal.api.AwbTransform) ResourceUsageAnalyzer(com.android.build.gradle.tasks.ResourceUsageAnalyzer) BuildException(org.gradle.tooling.BuildException) AwbBundle(com.taobao.android.builder.dependency.model.AwbBundle) File(java.io.File) Nullable(com.android.annotations.Nullable)

Example 2 with WaitableExecutor

use of com.android.ide.common.internal.WaitableExecutor in project atlas by alibaba.

the class AwbDataBindingRenameTask method createAwbPackages.

/**
 * Directory of so
 */
@TaskAction
void createAwbPackages() throws ExecutionException, InterruptedException {
    WaitableExecutor workerExecutor = WaitableExecutor.useGlobalSharedThreadPool();
    AtlasDependencyTree atlasDependencyTree = AtlasBuildContext.androidDependencyTrees.get(getVariantName());
    if (null == atlasDependencyTree) {
        return;
    }
    ExecutorServicesHelper executorServicesHelper = new ExecutorServicesHelper(taskName, getLogger(), 0);
    for (final AwbBundle awbBundle : atlasDependencyTree.getAwbBundles()) {
        if (!appVariantContext.getAtlasExtension().getTBuildConfig().getDataBindingBundles().contains(awbBundle.getPackageName())) {
            continue;
        }
        if (!awbBundle.isDataBindEnabled() || awbBundle.isMBundle) {
            continue;
        }
        workerExecutor.execute(new Callable() {

            @Override
            public Object call() {
                try {
                    File dataBindingClazzFolder = appVariantOutputContext.getVariantContext().getJAwbavaOutputDir(awbBundle);
                    String packageName = awbBundle.getPackageName();
                    String appName = awbBundle.getPackageName() + "._bundleapp_";
                    // Remove classes that already exist
                    File dataMapperClazz = new File(dataBindingClazzFolder, "android/databinding/DataBinderMapper.class");
                    if (!dataMapperClazz.exists()) {
                        throw new GradleException("missing datamapper class");
                    }
                    File dataBindComponentClazz = new File(dataBindingClazzFolder, "android/databinding/DataBindingComponent.class");
                    if (!dataBindComponentClazz.exists()) {
                        throw new GradleException("missing dataBindComponent.class");
                    }
                    File dataBindDynamicUtilsClazz = new File(dataBindingClazzFolder, "android/databinding/DynamicUtil.class");
                    if (!dataBindDynamicUtilsClazz.exists()) {
                        throw new GradleException("missing dataBindDynamicUtils.class");
                    }
                    ClassNameRenamer.rewriteDataBinderMapper(dataBindingClazzFolder, "android/databinding/DataBinderMapper", packageName.replace(".", "/") + "/DataBinderMapper", dataMapperClazz);
                    ClassNameRenamer.rewriteDataBinderMapper(dataBindingClazzFolder, "android/databinding/DataBindingComponent", packageName.replace(".", "/") + "/DataBindingComponent", dataBindComponentClazz);
                    ClassNameRenamer.rewriteDataBinderMapper(dataBindingClazzFolder, "android/databinding/dataBindDynamicUtils", packageName.replace(".", "/") + "/dataBindDynamicUtils", dataBindDynamicUtilsClazz);
                    FileUtils.deleteDirectory(new File(dataBindingClazzFolder, "android/databinding"));
                    // FileUtils.deleteDirectory(new File(dataBindingClazzFolder, packageName.replace(".", "/") +
                    // "/_bundleapp_" ));
                    File appDir = new File(dataBindingClazzFolder, appName.replace(".", "/"));
                    if (appDir.exists()) {
                        File[] files = appDir.listFiles(new FileFilter() {

                            @Override
                            public boolean accept(File pathname) {
                                return pathname.isFile() && !pathname.isDirectory();
                            }
                        });
                        for (File tmp : files) {
                            FileUtils.forceDelete(tmp);
                        }
                    }
                    // rename DataBindUtils
                    AwbTransform awbTransform = appVariantOutputContext.getAwbTransformMap().get(awbBundle.getName());
                    List<File> files = awbTransform.getInputLibraries();
                    Map<String, String> replaceMap = new HashMap<>();
                    replaceMap.put("android/databinding/DataBindingUtil", "android/databinding/AtlasDataBindingUtil");
                    List<File> newLibrarys = new ArrayList<>();
                    for (File inputJar : files) {
                        File outputJar = new File(appVariantContext.getAwbLibraryDirForDataBinding(awbBundle), FileNameUtils.getUniqueJarName(inputJar) + ".jar");
                        outputJar.delete();
                        outputJar.getParentFile().mkdirs();
                        outputJar.createNewFile();
                        new ClazzReplacer(inputJar, outputJar, replaceMap).execute();
                        newLibrarys.add(outputJar);
                        awbTransform.getFileTransform().put(inputJar, outputJar);
                    }
                    awbTransform.setInputLibraries(newLibrarys);
                } catch (Throwable e) {
                    e.printStackTrace();
                    throw new GradleException("databinding awb failed", e);
                }
                return null;
            }
        });
    }
    workerExecutor.waitForTasksWithQuickFail(true);
}
Also used : ClazzReplacer(com.taobao.android.builder.tools.asm.ClazzReplacer) WaitableExecutor(com.android.ide.common.internal.WaitableExecutor) AtlasDependencyTree(com.taobao.android.builder.dependency.AtlasDependencyTree) Callable(java.util.concurrent.Callable) ExecutorServicesHelper(com.taobao.android.builder.tools.concurrent.ExecutorServicesHelper) GradleException(org.gradle.api.GradleException) AwbTransform(com.android.build.gradle.internal.api.AwbTransform) ArrayList(java.util.ArrayList) List(java.util.List) AwbBundle(com.taobao.android.builder.dependency.model.AwbBundle) FileFilter(java.io.FileFilter) File(java.io.File) HashMap(java.util.HashMap) Map(java.util.Map) MtlBaseTaskAction(com.taobao.android.builder.tasks.manager.MtlBaseTaskAction) TaskAction(org.gradle.api.tasks.TaskAction)

Example 3 with WaitableExecutor

use of com.android.ide.common.internal.WaitableExecutor in project atlas by alibaba.

the class TaobaoExtractJarsTransform method transform.

@Override
public void transform(TransformInvocation transformInvocation) throws IOException, TransformException, InterruptedException {
    TransformOutputProvider outputProvider = transformInvocation.getOutputProvider();
    boolean isIncremental = transformInvocation.isIncremental();
    checkNotNull(outputProvider, "Missing output object for transform " + getName());
    // as_input transform and no referenced scopes, all the inputs will in InputOutputStreams.
    final boolean extractCode = contentTypes.contains(QualifiedContent.DefaultContentType.CLASSES);
    if (!isIncremental) {
        outputProvider.deleteAll();
    }
    try {
        WaitableExecutor executor = WaitableExecutor.useGlobalSharedThreadPool();
        AtlasBuildContext.atlasMainDexHelperMap.get(variantContext.getVariantName()).getInputDirs().clear();
        for (File jarFile : AtlasBuildContext.atlasMainDexHelperMap.get(variantContext.getVariantName()).getAllMainDexJars()) {
            // final File jarFile = jarInput.getFile();
            JarInput jarInput = makeJarInput(jarFile);
            LOGGER.warn("input JarFile:" + jarFile.getAbsolutePath());
            // create an output folder for this jar, keeping its type and scopes.
            final File outJarFolder = outputProvider.getContentLocation(jarInput.getName(), jarInput.getContentTypes(), jarInput.getScopes(), Format.DIRECTORY);
            FileUtils.mkdirs(outJarFolder);
            LOGGER.warn("outJar Folder:" + outJarFolder.getAbsolutePath());
            AtlasBuildContext.atlasMainDexHelperMap.get(variantContext.getVariantName()).getInputDirs().add(outJarFolder);
            if (!isIncremental) {
                executor.execute(() -> {
                    extractJar(outJarFolder, jarFile, extractCode);
                    return null;
                });
            } else {
                switch(jarInput.getStatus()) {
                    case CHANGED:
                        executor.execute(() -> {
                            FileUtils.cleanOutputDir(outJarFolder);
                            extractJar(outJarFolder, jarFile, extractCode);
                            return null;
                        });
                        break;
                    case ADDED:
                        executor.execute(() -> {
                            extractJar(outJarFolder, jarFile, extractCode);
                            return null;
                        });
                        break;
                    case REMOVED:
                        executor.execute(() -> {
                            FileUtils.cleanOutputDir(outJarFolder);
                            return null;
                        });
                        break;
                    case NOTCHANGED:
                        break;
                }
            }
        }
        AtlasBuildContext.atlasMainDexHelperMap.get(variantContext.getVariantName()).addMainDexJars(Sets.newHashSet());
        for (AwbTransform awbTransform : variantOutputContext.getAwbTransformMap().values()) {
            File outJarFolder = variantOutputContext.getAwbExtractJarsFolder(awbTransform.getAwbBundle());
            awbTransform.getInputFiles().forEach(file -> executor.execute(() -> {
                LOGGER.warn("ExtractAwbJar[" + awbTransform.getAwbBundle().getPackageName() + "]---------------------" + file.getAbsolutePath());
                extractJar(outJarFolder, file, extractCode);
                return null;
            }));
            awbTransform.getInputLibraries().forEach(file -> executor.execute(() -> {
                LOGGER.warn("ExtractAwbJar[" + awbTransform.getAwbBundle().getPackageName() + "]---------------------" + file.getAbsolutePath());
                extractJar(outJarFolder, file, extractCode);
                return null;
            }));
            awbTransform.addDir(outJarFolder);
            awbTransform.getInputFiles().clear();
        }
        executor.waitForTasksWithQuickFail(true);
    } catch (InterruptedException e) {
        throw e;
    } catch (Exception e) {
        throw new TransformException(e);
    }
}
Also used : WaitableExecutor(com.android.ide.common.internal.WaitableExecutor) AwbTransform(com.android.build.gradle.internal.api.AwbTransform) JarFile(java.util.jar.JarFile)

Aggregations

AwbTransform (com.android.build.gradle.internal.api.AwbTransform)3 WaitableExecutor (com.android.ide.common.internal.WaitableExecutor)3 AwbBundle (com.taobao.android.builder.dependency.model.AwbBundle)2 File (java.io.File)2 Nullable (com.android.annotations.Nullable)1 AppVariantOutputContext (com.android.build.gradle.internal.api.AppVariantOutputContext)1 ResourceUsageAnalyzer (com.android.build.gradle.tasks.ResourceUsageAnalyzer)1 AtlasDependencyTree (com.taobao.android.builder.dependency.AtlasDependencyTree)1 MtlBaseTaskAction (com.taobao.android.builder.tasks.manager.MtlBaseTaskAction)1 ClazzReplacer (com.taobao.android.builder.tools.asm.ClazzReplacer)1 ExecutorServicesHelper (com.taobao.android.builder.tools.concurrent.ExecutorServicesHelper)1 FileFilter (java.io.FileFilter)1 IOException (java.io.IOException)1 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 List (java.util.List)1 Map (java.util.Map)1 Callable (java.util.concurrent.Callable)1 ExecutionException (java.util.concurrent.ExecutionException)1 JarFile (java.util.jar.JarFile)1