Search in sources :

Example 1 with ResourceUsageAnalyzer

use of com.android.build.gradle.tasks.ResourceUsageAnalyzer 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 ResourceUsageAnalyzer

use of com.android.build.gradle.tasks.ResourceUsageAnalyzer in project bazel by bazelbuild.

the class ResourceShrinkerAction method main.

public static void main(String[] args) throws Exception {
    final Stopwatch timer = Stopwatch.createStarted();
    // Parse arguments.
    OptionsParser optionsParser = OptionsParser.newOptionsParser(Options.class, AaptConfigOptions.class);
    optionsParser.parseAndExitUponError(args);
    aaptConfigOptions = optionsParser.getOptions(AaptConfigOptions.class);
    options = optionsParser.getOptions(Options.class);
    AndroidResourceProcessor resourceProcessor = new AndroidResourceProcessor(stdLogger);
    // Setup temporary working directories.
    try (ScopedTemporaryDirectory scopedTmp = new ScopedTemporaryDirectory("resource_shrinker_tmp")) {
        Path working = scopedTmp.getPath();
        final Path resourceFiles = working.resolve("resource_files");
        final Path shrunkResources = working.resolve("shrunk_resources");
        // Gather package list from manifests.
        Set<String> resourcePackages = getManifestPackages(options.primaryManifest, options.dependencyManifests);
        resourcePackages.addAll(options.resourcePackages);
        // Expand resource files zip into working directory.
        try (ZipInputStream zin = new ZipInputStream(new FileInputStream(options.resourcesZip.toFile()))) {
            ZipEntry entry;
            while ((entry = zin.getNextEntry()) != null) {
                if (!entry.isDirectory()) {
                    Path output = resourceFiles.resolve(entry.getName());
                    Files.createDirectories(output.getParent());
                    try (FileOutputStream fos = new FileOutputStream(output.toFile())) {
                        ByteStreams.copy(zin, fos);
                    }
                }
            }
        }
        // Shrink resources.
        ResourceUsageAnalyzer resourceShrinker = new ResourceUsageAnalyzer(resourcePackages, options.rTxt, options.shrunkJar, options.primaryManifest, options.proguardMapping, resourceFiles.resolve("res"), options.log);
        resourceShrinker.shrink(shrunkResources);
        logger.fine(String.format("Shrinking resources finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
        Path generatedSources = null;
        if (options.rTxtOutput != null) {
            generatedSources = working.resolve("generated_resources");
        }
        // Build ap_ with shrunk resources.
        resourceProcessor.processResources(aaptConfigOptions.aapt, aaptConfigOptions.androidJar, aaptConfigOptions.buildToolsVersion, VariantType.DEFAULT, aaptConfigOptions.debug, null, /* packageForR */
        new FlagAaptOptions(aaptConfigOptions), aaptConfigOptions.resourceConfigs, aaptConfigOptions.splits, new MergedAndroidData(shrunkResources, resourceFiles.resolve("assets"), options.primaryManifest), ImmutableList.<DependencyAndroidData>of(), /* libraries */
        generatedSources, options.shrunkApk, null, /* proguardOutput */
        null, /* mainDexProguardOutput */
        null, /* publicResourcesOut */
        null);
        if (options.shrunkResources != null) {
            AndroidResourceOutputs.createResourcesZip(shrunkResources, resourceFiles.resolve("assets"), options.shrunkResources, false);
        }
        if (options.rTxtOutput != null) {
            AndroidResourceOutputs.copyRToOutput(generatedSources, options.rTxtOutput, options.packageType == VariantType.LIBRARY);
        }
        logger.fine(String.format("Packing resources finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
    } catch (Exception e) {
        logger.log(Level.SEVERE, "Error shrinking resources", e);
        throw e;
    } finally {
        resourceProcessor.shutdown();
    }
}
Also used : Path(java.nio.file.Path) AaptConfigOptions(com.google.devtools.build.android.AndroidResourceProcessor.AaptConfigOptions) FlagAaptOptions(com.google.devtools.build.android.AndroidResourceProcessor.FlagAaptOptions) FlagAaptOptions(com.google.devtools.build.android.AndroidResourceProcessor.FlagAaptOptions) ZipEntry(java.util.zip.ZipEntry) Stopwatch(com.google.common.base.Stopwatch) OptionsParser(com.google.devtools.common.options.OptionsParser) FileInputStream(java.io.FileInputStream) IOException(java.io.IOException) ParserConfigurationException(javax.xml.parsers.ParserConfigurationException) SAXException(org.xml.sax.SAXException) StreamException(com.android.io.StreamException) ZipInputStream(java.util.zip.ZipInputStream) FileOutputStream(java.io.FileOutputStream) ResourceUsageAnalyzer(com.android.build.gradle.tasks.ResourceUsageAnalyzer) AaptConfigOptions(com.google.devtools.build.android.AndroidResourceProcessor.AaptConfigOptions)

Aggregations

ResourceUsageAnalyzer (com.android.build.gradle.tasks.ResourceUsageAnalyzer)2 IOException (java.io.IOException)2 Nullable (com.android.annotations.Nullable)1 AppVariantOutputContext (com.android.build.gradle.internal.api.AppVariantOutputContext)1 AwbTransform (com.android.build.gradle.internal.api.AwbTransform)1 WaitableExecutor (com.android.ide.common.internal.WaitableExecutor)1 StreamException (com.android.io.StreamException)1 Stopwatch (com.google.common.base.Stopwatch)1 AaptConfigOptions (com.google.devtools.build.android.AndroidResourceProcessor.AaptConfigOptions)1 FlagAaptOptions (com.google.devtools.build.android.AndroidResourceProcessor.FlagAaptOptions)1 OptionsParser (com.google.devtools.common.options.OptionsParser)1 AwbBundle (com.taobao.android.builder.dependency.model.AwbBundle)1 File (java.io.File)1 FileInputStream (java.io.FileInputStream)1 FileOutputStream (java.io.FileOutputStream)1 Path (java.nio.file.Path)1 ExecutionException (java.util.concurrent.ExecutionException)1 ZipEntry (java.util.zip.ZipEntry)1 ZipInputStream (java.util.zip.ZipInputStream)1 ParserConfigurationException (javax.xml.parsers.ParserConfigurationException)1