package org.jetbrains.jps.android.builder; import com.intellij.openapi.util.io.FileUtil; import gnu.trove.THashMap; import gnu.trove.THashSet; import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.android.*; import org.jetbrains.jps.android.model.JpsAndroidDexCompilerConfiguration; import org.jetbrains.jps.android.model.JpsAndroidExtensionService; import org.jetbrains.jps.android.model.JpsAndroidModuleExtension; import org.jetbrains.jps.builders.BuildRootDescriptor; import org.jetbrains.jps.builders.BuildTarget; import org.jetbrains.jps.builders.BuildTargetRegistry; import org.jetbrains.jps.builders.TargetOutputIndex; import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType; import org.jetbrains.jps.builders.storage.BuildDataPaths; import org.jetbrains.jps.cmdline.ProjectDescriptor; import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.ModuleBuildTarget; import org.jetbrains.jps.indices.IgnoredFileIndex; import org.jetbrains.jps.indices.ModuleExcludeIndex; import org.jetbrains.jps.model.JpsModel; import org.jetbrains.jps.model.module.JpsModule; import java.io.File; import java.io.PrintWriter; import java.util.*; /** * @author Eugene.Kudelevsky */ public class AndroidDexBuildTarget extends AndroidBuildTarget { public AndroidDexBuildTarget(@NotNull JpsModule module) { super(MyTargetType.INSTANCE, module); } @Override public void writeConfiguration(ProjectDescriptor pd, PrintWriter out) { super.writeConfiguration(pd, out); final JpsAndroidDexCompilerConfiguration c = JpsAndroidExtensionService. getInstance().getDexCompilerConfiguration(getModule().getProject()); if (c != null) { out.println(c.getVmOptions()); out.println(c.getMaxHeapSize()); out.println(c.isOptimize()); out.println(c.isForceJumbo()); out.println(c.isCoreLibrary()); out.println(c.getProguardVmOptions()); } } @NotNull @Override protected List<BuildRootDescriptor> doComputeRootDescriptors(JpsModel model, ModuleExcludeIndex index, IgnoredFileIndex ignoredFileIndex, BuildDataPaths dataPaths) { final JpsAndroidModuleExtension extension = AndroidJpsUtil.getExtension(myModule); assert extension != null; if (extension.isLibrary()) { return Collections.emptyList(); } final Map<String, String> libPackage2ModuleName = new THashMap<String, String>(FileUtil.PATH_HASHING_STRATEGY); final Set<String> appClassesDirs = new THashSet<String>(FileUtil.PATH_HASHING_STRATEGY); final Set<String> javaClassesDirs = new THashSet<String>(FileUtil.PATH_HASHING_STRATEGY); final Set<String> libClassesDirs = new THashSet<String>(FileUtil.PATH_HASHING_STRATEGY); final File moduleClassesDir = new ModuleBuildTarget( myModule, JavaModuleBuildTargetType.PRODUCTION).getOutputDir(); if (moduleClassesDir != null) { appClassesDirs.add(moduleClassesDir.getPath()); } AndroidJpsUtil.processClasspath(dataPaths, myModule, new AndroidDependencyProcessor() { @Override public void processAndroidLibraryPackage(@NotNull File file, @NotNull JpsModule depModule) { libPackage2ModuleName.put(file.getPath(), depModule.getName()); } @Override public void processAndroidLibraryOutputDirectory(@NotNull File dir) { libClassesDirs.add(dir.getPath()); } @Override public void processJavaModuleOutputDirectory(@NotNull File dir) { javaClassesDirs.add(dir.getPath()); } @Override public boolean isToProcess(@NotNull AndroidDependencyType type) { return type == AndroidDependencyType.ANDROID_LIBRARY_PACKAGE || type == AndroidDependencyType.ANDROID_LIBRARY_OUTPUT_DIRECTORY || type == AndroidDependencyType.JAVA_MODULE_OUTPUT_DIR; } }, false, false); if (extension.isPackTestCode()) { final File testModuleClassesDir = new ModuleBuildTarget( myModule, JavaModuleBuildTargetType.TEST).getOutputDir(); if (testModuleClassesDir != null) { appClassesDirs.add(testModuleClassesDir.getPath()); } } final List<BuildRootDescriptor> result = new ArrayList<BuildRootDescriptor>(); for (String classesDir : appClassesDirs) { result.add(new MyClassesDirBuildRootDescriptor(this, new File(classesDir), ClassesDirType.ANDROID_APP)); } for (String classesDir : libClassesDirs) { result.add(new MyClassesDirBuildRootDescriptor(this, new File(classesDir), ClassesDirType.ANDROID_LIB)); } for (String classesDir : javaClassesDirs) { result.add(new MyClassesDirBuildRootDescriptor(this, new File(classesDir), ClassesDirType.JAVA)); } final File preDexOutputDir = AndroidPreDexBuildTarget.getOutputDir(dataPaths); for (Map.Entry<String, String> entry : libPackage2ModuleName.entrySet()) { final String libPackage = entry.getKey(); final String moduleName = entry.getValue(); final File libPackageJarFile = new File(libPackage); assert AndroidPreDexBuilder.canBePreDexed(libPackageJarFile); result.add(new MyJarBuildRootDescriptor(this, libPackageJarFile, true, false)); result.add(new MyJarBuildRootDescriptor(this, new File( new File(preDexOutputDir, moduleName), libPackageJarFile.getName()), true, true)); } final AndroidPlatform platform = AndroidJpsUtil.getAndroidPlatform(myModule, null, null); if (platform != null) { for (String jarOrLibDir : AndroidJpsUtil.getExternalLibraries(dataPaths, myModule, platform, false, false, true)) { File file = new File(jarOrLibDir); File preDexedFile = file; if (AndroidPreDexBuilder.canBePreDexed(file)) { final String preDexedFileName = AndroidPreDexBuilder.getOutputFileNameForExternalJar(file); if (preDexedFileName != null) { preDexedFile = new File(preDexOutputDir, preDexedFileName); } } result.add(new MyJarBuildRootDescriptor(this, file, false, false)); result.add(new MyJarBuildRootDescriptor(this, preDexedFile, false, true)); } } for (String path : AndroidJpsUtil.getProvidedLibraries(dataPaths, myModule)) { result.add(new MyProvidedJarBuildRootDescriptor(this, new File(path))); } return result; } @NotNull @Override public Collection<File> getOutputRoots(CompileContext context) { return Collections.singletonList(getOutputFile(context)); } @NotNull public File getOutputFile(CompileContext context) { return getOutputFile(context.getProjectDescriptor().dataManager.getDataPaths(), myModule); } @NotNull public static File getOutputFile(@NotNull BuildDataPaths dataPaths, @NotNull JpsModule module) { final File dir = AndroidJpsUtil.getDirectoryForIntermediateArtifacts(dataPaths, module); return new File(dir, AndroidCommonUtils.CLASSES_FILE_NAME); } @Override public Collection<BuildTarget<?>> computeDependencies(BuildTargetRegistry registry, TargetOutputIndex outputIndex) { final List<BuildTarget<?>> result = new ArrayList<BuildTarget<?>>( super.computeDependencies(registry, outputIndex)); result.add(new AndroidAarDepsBuildTarget(myModule)); result.add(new AndroidPreDexBuildTarget(myModule.getProject())); return result; } public static class MyTargetType extends AndroidBuildTargetType<AndroidDexBuildTarget> { public static final MyTargetType INSTANCE = new MyTargetType(); private MyTargetType() { super(AndroidCommonUtils.DEX_BUILD_TARGET_TYPE_ID, "DEX"); } @Override public AndroidDexBuildTarget createBuildTarget(@NotNull JpsAndroidModuleExtension extension) { return !extension.isLibrary() ? new AndroidDexBuildTarget(extension.getModule()) : null; } } public enum ClassesDirType { ANDROID_APP, ANDROID_LIB, JAVA } public static class MyClassesDirBuildRootDescriptor extends AndroidClassesDirBuildRootDescriptor { private final ClassesDirType myClassesDirType; public MyClassesDirBuildRootDescriptor(@NotNull BuildTarget target, @NotNull File root, @NotNull ClassesDirType classesDirType) { super(target, root); myClassesDirType = classesDirType; } @NotNull public ClassesDirType getClassesDirType() { return myClassesDirType; } } public static class MyProvidedJarBuildRootDescriptor extends AndroidFileBasedBuildRootDescriptor { public MyProvidedJarBuildRootDescriptor(@NotNull BuildTarget target, @NotNull File file) { super(target, file); } } public static class MyJarBuildRootDescriptor extends AndroidFileBasedBuildRootDescriptor { private final boolean myLibPackage; private final boolean myPreDexed; public MyJarBuildRootDescriptor(@NotNull BuildTarget target, @NotNull File file, boolean libPackage, boolean preDexed) { super(target, file); myLibPackage = libPackage; myPreDexed = preDexed; } public boolean isLibPackage() { return myLibPackage; } public boolean isPreDexed() { return myPreDexed; } } }