blob: 23fdade51367dbe18e22d1079e6c23250a16d955 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.build.gradle.internal.tasks.multidex
import com.android.build.gradle.internal.PostCompilationData
import com.android.build.gradle.internal.scope.ConventionMappingHelper
import com.android.build.gradle.internal.scope.TaskConfigAction
import com.android.build.gradle.internal.scope.VariantScope
import com.android.build.gradle.internal.tasks.DefaultAndroidTask
import com.google.common.base.Charsets
import com.google.common.base.Joiner
import com.google.common.collect.Maps
import com.google.common.collect.Sets
import com.google.common.io.Files
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
/**
* Take a list of classes for the main dex (that was computed before obfuscation),
* a proguard-generated mapping file and create a new list of classes with the new
* obfuscated names.
*/
class RetraceMainDexList extends DefaultAndroidTask {
@InputFile
File mainDexListFile
@OutputFile
File outputFile
/**
* Gradle doesn't really handle optional inputs as being a file that
* doesn't exist. Optional means the task field is null. So we do some
* custom logic to return null if the file doesn't exist since we cannot
* know ahead of time without parsing the proguard config rule files.
*/
@InputFile @Optional
File getMappingFileInput() {
File file = getMappingFile()
if (file != null && file.isFile()) {
return file
}
return null
}
File mappingFile
@TaskAction
void retrace() {
File mapping = getMappingFile()
// if there is no mapping file or if it doesn't exist, then we just copy from the main
// dex list ot the output.
if (mapping == null || !mapping.isFile()) {
Files.copy(getMainDexListFile(), getOutputFile())
return
}
// load the main class names
List<String> classes = Files.readLines(getMainDexListFile(), Charsets.UTF_8)
// load the mapping file and create a dictionary
List<String> mappingLines = Files.readLines(mapping, Charsets.UTF_8)
Map<String, String> map = createDict(mappingLines)
// create the deobfuscated class list. This is a set to detect dups
// You can have the same class coming from the non-obfuscated list and from
// the shrinked jar that have different names until we remap them.
Set<String> deobfuscatedClasses = Sets.newHashSetWithExpectedSize(classes.size())
for (String clazz : classes) {
String fullName = map.get(clazz)
deobfuscatedClasses.add(fullName != null ? fullName : clazz)
}
String fileContent =
Joiner.on(System.getProperty("line.separator")).join(deobfuscatedClasses)
Files.write(fileContent, getOutputFile(), Charsets.UTF_8)
}
static Map<String, String> createDict(List<String> lines) {
Map<String, String> map = Maps.newHashMap()
for (String line : lines) {
if (line.startsWith(" ")) {
continue
}
int pos = line.indexOf(" -> ")
if (pos == -1) {
throw new RuntimeException("unable to read mapping file.")
}
String fullName = line.substring(0, pos)
String obfuscatedName = line.substring(pos + 4, line.size() - 1)
map.put(obfuscatedName.replace(".", "/") + ".class",
fullName.replace(".", "/") + ".class")
}
return map
}
public static class ConfigAction implements TaskConfigAction<RetraceMainDexList> {
VariantScope scope;
PostCompilationData pcData;
ConfigAction(VariantScope scope, PostCompilationData pcData) {
this.scope = scope
this.pcData = pcData
}
@Override
String getName() {
return scope.getTaskName("retrace", "MainDexClassList");
}
@Override
Class<RetraceMainDexList> getType() {
return RetraceMainDexList.class
}
@Override
void execute(RetraceMainDexList retraceTask) {
retraceTask.setVariantName(scope.getVariantConfiguration().getFullName())
ConventionMappingHelper.map(retraceTask, "mainDexListFile") { scope.getMainDexListFile() }
ConventionMappingHelper.map(retraceTask, "mappingFile") {
scope.variantData.getMappingFile()
}
retraceTask.outputFile = new File(
"${scope.globalScope.buildDir}/${FD_INTERMEDIATES}/multi-dex/" +
"${scope.variantConfiguration.dirName}/maindexlist_deobfuscated.txt")
}
}
}