blob: 290ab987ea6ee9ee45a64c11b0ecc6c9e8ed21fb [file] [log] [blame]
package org.jetbrains.android.compiler;
import com.android.sdklib.IAndroidTarget;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.android.compiler.tools.AndroidApt;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.sdk.AndroidPlatform;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.*;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidPngFilesCachingCompiler implements SourceGeneratingCompiler {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.compiler.AndroidPngFilesCachingCompiler");
@NotNull
@Override
public GenerationItem[] getGenerationItems(final CompileContext context) {
return ApplicationManager.getApplication().runReadAction(new Computable<GenerationItem[]>() {
@Override
public GenerationItem[] compute() {
final List<GenerationItem> items = new ArrayList<GenerationItem>();
final Module[] modules = ModuleManager.getInstance(context.getProject()).getModules();
for (Module module : modules) {
final AndroidFacet facet = AndroidFacet.getInstance(module);
if (facet == null) {
continue;
}
final VirtualFile resourcesDir = AndroidAptCompiler.getResourceDirForApkCompiler(facet);
if (resourcesDir == null) {
continue;
}
final AndroidPlatform platform = facet.getConfiguration().getAndroidPlatform();
if (platform == null) {
context.addMessage(CompilerMessageCategory.ERROR,
AndroidBundle.message("android.compilation.error.specify.platform", module.getName()), null, -1, -1);
continue;
}
final int platformToolsRevision = platform.getSdkData().getPlatformToolsRevision();
if (platformToolsRevision > 0 && platformToolsRevision <= 7) {
// png files cache is supported since platform-tools-r8
continue;
}
items.add(new MyItem(module, platform.getTarget(), resourcesDir));
}
return items.toArray(new GenerationItem[items.size()]);
}
});
}
@Override
public GenerationItem[] generate(CompileContext context,
GenerationItem[] items,
VirtualFile outputRootDirectory) {
if (items == null || items.length == 0 || !AndroidCompileUtil.isFullBuild(context)) {
return new GenerationItem[0];
}
context.getProgressIndicator().setText("Processing PNG files...");
final List<GenerationItem> processedItems = new ArrayList<GenerationItem>();
for (GenerationItem GenerationItem : items) {
final MyItem item = (MyItem)GenerationItem;
final Module module = item.getModule();
if (!AndroidCompileUtil.isModuleAffected(context, module)) {
continue;
}
final String resDirOsPath = FileUtil.toSystemDependentName(item.getResourceDir().getPath());
try {
final String resCacheDirOsPath = AndroidCompileUtil.findResourcesCacheDirectory(module, true, context);
if (resCacheDirOsPath == null) {
continue;
}
final Map<CompilerMessageCategory, List<String>> messages = AndroidCompileUtil.toCompilerMessageCategoryKeys(
AndroidApt.crunch(item.getTarget(), Collections.singletonList(resDirOsPath), resCacheDirOsPath));
AndroidCompileUtil.addMessages(context, messages, module);
if (context.getMessages(CompilerMessageCategory.ERROR).length == 0) {
processedItems.add(item);
}
}
catch (IOException e) {
final String message = e.getMessage();
if (message != null) {
LOG.info(e);
context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
}
else {
LOG.error(e);
}
}
}
return processedItems.toArray(new GenerationItem[processedItems.size()]);
}
@Nullable
@Override
public VirtualFile getPresentableFile(CompileContext context, Module module, VirtualFile outputRoot, VirtualFile generatedFile) {
return null;
}
@NotNull
@Override
public String getDescription() {
return "Android PNG files caching compiler";
}
@Override
public boolean validateConfiguration(CompileScope scope) {
return true;
}
@Override
public ValidityState createValidityState(DataInput in) throws IOException {
return new MyValidityState(in);
}
private static class MyItem implements GenerationItem {
private final Module myModule;
private final IAndroidTarget myTarget;
private final VirtualFile myResourceDir;
private final MyValidityState myValidityState;
private MyItem(@NotNull Module module,
@NotNull IAndroidTarget target,
@NotNull VirtualFile resourceDir) {
myModule = module;
myTarget = target;
myResourceDir = resourceDir;
myValidityState = new MyValidityState(myTarget, myResourceDir);
}
@NotNull
public IAndroidTarget getTarget() {
return myTarget;
}
@NotNull
public VirtualFile getResourceDir() {
return myResourceDir;
}
@NotNull
public Module getModule() {
return myModule;
}
@Override
public boolean isTestSource() {
return false;
}
@Nullable
@Override
public String getPath() {
return null;
}
@Override
public ValidityState getValidityState() {
return myValidityState;
}
}
private static class MyValidityState implements ValidityState {
private final String myTargetHashString;
private final Map<String, Long> myTimestamps = new HashMap<String, Long>();
private MyValidityState(@NotNull IAndroidTarget target, @NotNull VirtualFile resourceDir) {
myTargetHashString = target.hashString();
collectPngFiles(resourceDir, new HashSet<VirtualFile>());
}
public MyValidityState(@NotNull DataInput in) throws IOException {
myTargetHashString = in.readUTF();
final int pairsCount = in.readInt();
for (int i = 0; i < pairsCount; i++) {
final String path = in.readUTF();
final long timestamp = in.readLong();
myTimestamps.put(path, timestamp);
}
}
private void collectPngFiles(@NotNull VirtualFile file, @NotNull Set<VirtualFile> visited) {
if (!visited.add(file)) {
return;
}
if (file.isDirectory()) {
for (VirtualFile child : file.getChildren()) {
collectPngFiles(child, visited);
}
}
else if (AndroidCommonUtils.PNG_EXTENSION.equals(file.getExtension())) {
myTimestamps.put(file.getPath(), file.getTimeStamp());
}
}
@Override
public boolean equalsTo(ValidityState otherState) {
if (!(otherState instanceof MyValidityState)) {
return false;
}
final MyValidityState other = (MyValidityState)otherState;
if (!Comparing.equal(myTargetHashString, other.myTargetHashString)) {
return false;
}
return myTimestamps.equals(other.myTimestamps);
}
@Override
public void save(DataOutput out) throws IOException {
out.writeUTF(myTargetHashString);
out.writeInt(myTimestamps.size());
for (Map.Entry<String, Long> e : myTimestamps.entrySet()) {
out.writeUTF(e.getKey());
out.writeLong(e.getValue());
}
}
}
}