package org.jetbrains.jps.android; import com.android.sdklib.IAndroidTarget; import com.android.tools.idea.jps.AndroidTargetBuilder; import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.Processor; import com.intellij.util.containers.HashMap; import org.jetbrains.android.compiler.tools.AndroidApt; import org.jetbrains.android.util.AndroidCompilerMessageKind; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.android.builder.AndroidResourceCachingBuildTarget; import org.jetbrains.jps.android.model.JpsAndroidModuleExtension; import org.jetbrains.jps.builders.BuildOutputConsumer; import org.jetbrains.jps.builders.BuildRootDescriptor; import org.jetbrains.jps.builders.DirtyFilesHolder; import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.ProjectBuildException; import org.jetbrains.jps.incremental.StopBuildException; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; import org.jetbrains.jps.model.module.JpsModule; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; /** * @author Eugene.Kudelevsky */ public class AndroidResourceCachingBuilder extends AndroidTargetBuilder<BuildRootDescriptor, AndroidResourceCachingBuildTarget> { @NonNls private static final String BUILDER_NAME = "Android Resource Caching"; protected AndroidResourceCachingBuilder() { super(Collections.singletonList(AndroidResourceCachingBuildTarget.MyTargetType.INSTANCE)); } @Override protected void buildTarget(@NotNull AndroidResourceCachingBuildTarget target, @NotNull DirtyFilesHolder<BuildRootDescriptor, AndroidResourceCachingBuildTarget> holder, @NotNull BuildOutputConsumer outputConsumer, @NotNull CompileContext context) throws ProjectBuildException, IOException { if (AndroidJpsUtil.isLightBuild(context) || (!holder.hasDirtyFiles() && !holder.hasRemovedFiles())) { return; } if (!runPngCaching(target, context, outputConsumer)) { throw new StopBuildException(); } } private static boolean runPngCaching(AndroidResourceCachingBuildTarget target, CompileContext context, BuildOutputConsumer outputConsumer) throws IOException { final JpsModule module = target.getModule(); final JpsAndroidModuleExtension extension = AndroidJpsUtil.getExtension(module); assert extension != null; context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.INFO, AndroidJpsBundle.message("android.jps.progress.res.caching", module.getName()))); final AndroidPlatform platform = AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME); if (platform == null) { return false; } final File resCacheDir = target.getOutputDir(context); // todo: probably it may be done automatically if (context.getScope().isBuildForced(target) && resCacheDir.exists()) { if (!FileUtil.delete(resCacheDir)) { context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, AndroidJpsBundle.message("android.jps.cannot.create.directory", resCacheDir.getPath()))); return false; } } if (!resCacheDir.exists()) { if (!resCacheDir.mkdirs()) { context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, AndroidJpsBundle.message("android.jps.cannot.create.directory", resCacheDir.getPath()))); return false; } } final IAndroidTarget androidTarget = platform.getTarget(); final List<BuildRootDescriptor> roots = context.getProjectDescriptor(). getBuildRootIndex().getTargetRoots(target, context); if (roots.size() == 0) { return true; } final List<String> inputDirs = new ArrayList<String>(); for (BuildRootDescriptor root : roots) { final File f = root.getRootFile(); if (f.exists()) { inputDirs.add(f.getPath()); } } final Map<AndroidCompilerMessageKind, List<String>> messages = AndroidApt.crunch(androidTarget, inputDirs,resCacheDir.getPath()); AndroidJpsUtil.addMessages(context, messages, BUILDER_NAME, module.getName()); final boolean success = messages.get(AndroidCompilerMessageKind.ERROR).isEmpty(); if (success) { final Map<String, File> outputFiles = new HashMap<String, File>(); FileUtil.processFilesRecursively(resCacheDir, new Processor<File>() { @Override public boolean process(File file) { if (file.isFile()) { final String relativePath = FileUtil.getRelativePath(resCacheDir, file); if (relativePath != null) { outputFiles.put(relativePath, file); } } return true; } }); for (Map.Entry<String, File> entry : outputFiles.entrySet()) { final String relativePath = entry.getKey(); final File outputFile = entry.getValue(); for (String inputDir : inputDirs) { final File srcFile = new File(inputDir, relativePath); outputConsumer.registerOutputFile(outputFile, Collections.singletonList(srcFile.getPath())); } } } return success; } @NotNull @Override public String getPresentableName() { return BUILDER_NAME; } }