package org.jetbrains.jps.android; import com.android.tools.idea.jps.AndroidTargetBuilder; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtilRt; import org.jetbrains.android.util.AndroidBuildTestingManager; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.android.builder.AndroidPreDexBuildTarget; import org.jetbrains.jps.builders.BuildOutputConsumer; import org.jetbrains.jps.builders.DirtyFilesHolder; import org.jetbrains.jps.builders.FileProcessor; 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.incremental.messages.ProgressMessage; import org.jetbrains.jps.model.JpsProject; 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; /** * @author Eugene.Kudelevsky */ public class AndroidPreDexBuilder extends AndroidTargetBuilder<AndroidPreDexBuildTarget.MyRootDescriptor, AndroidPreDexBuildTarget> { @NonNls private static final String BUILDER_NAME = "Android Pre Dex"; protected AndroidPreDexBuilder() { super(Collections.singletonList(AndroidPreDexBuildTarget.MyTargetType.INSTANCE)); } @Nullable public static String getOutputFileNameForExternalJar(@NotNull File srcFile) { final String canonicalPath = FileUtil.toCanonicalPath(srcFile.getAbsolutePath()); if (canonicalPath == null) { return null; } if (AndroidBuildTestingManager.getTestingManager() != null) { return srcFile.getName(); } final int hashCode = canonicalPath.hashCode(); return FileUtil.getNameWithoutExtension(srcFile) + "-" + Integer.toHexString(hashCode) + ".jar"; } @Override protected void buildTarget(@NotNull AndroidPreDexBuildTarget target, @NotNull DirtyFilesHolder<AndroidPreDexBuildTarget.MyRootDescriptor, AndroidPreDexBuildTarget> holder, @NotNull BuildOutputConsumer outputConsumer, @NotNull final CompileContext context) throws ProjectBuildException, IOException { if (!doBuild(target, holder, outputConsumer, context)) { throw new StopBuildException(); } } private static boolean doBuild(@NotNull AndroidPreDexBuildTarget target, @NotNull DirtyFilesHolder<AndroidPreDexBuildTarget.MyRootDescriptor, AndroidPreDexBuildTarget> holder, @NotNull BuildOutputConsumer outputConsumer, @NotNull CompileContext context) throws IOException, ProjectBuildException { final List<Pair<File, String>> filesToPreDex = new ArrayList<Pair<File, String>>(); holder.processDirtyFiles(new FileProcessor<AndroidPreDexBuildTarget.MyRootDescriptor, AndroidPreDexBuildTarget>() { @Override public boolean apply(AndroidPreDexBuildTarget target, File file, AndroidPreDexBuildTarget.MyRootDescriptor root) throws IOException { if (canBePreDexed(file) && file.isFile()) { filesToPreDex.add(Pair.create(file, root.getModuleName())); } return true; } }); final JpsProject project = target.getProject(); AndroidPlatform platform = null; for (JpsModule module : project.getModules()) { if (AndroidJpsUtil.getExtension(module) != null) { platform = AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME); break; } } if (platform == null) { return false; } if (!filesToPreDex.isEmpty()) { final File outputDir = target.getOutputFile(context); for (Pair<File, String> pair : filesToPreDex) { context.checkCanceled(); final File srcFile = pair.getFirst(); final String moduleName = pair.getSecond(); final String srcFilePath = srcFile.getAbsolutePath(); final File outputFile; if (moduleName != null) { context.processMessage(new ProgressMessage("Pre-dex [" + moduleName + "]")); outputFile = new File(new File(outputDir, moduleName), srcFile.getName()); } else { context.processMessage(new ProgressMessage("Pre-dex: " + srcFile.getName())); final String outputFileName = getOutputFileNameForExternalJar(srcFile); if (outputFileName == null) { context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Cannot pre-dex file " + srcFilePath + ": incorrect path", srcFilePath)); return false; } outputFile = new File(outputDir, outputFileName); } if (AndroidJpsUtil.createDirIfNotExist(outputFile.getParentFile(), context, BUILDER_NAME) == null) { return false; } if (!AndroidDexBuilder.runDex(platform, outputFile.getPath(), new String[]{srcFilePath}, context, project, outputConsumer, BUILDER_NAME, srcFile.getName())) { return false; } } } return true; } public static boolean canBePreDexed(@NotNull File file) { return "jar".equals(FileUtilRt.getExtension(file.getName())); } @NotNull @Override public String getPresentableName() { return BUILDER_NAME; } }