Search in sources :

Example 1 with JpsAndroidModuleExtension

use of org.jetbrains.jps.android.model.JpsAndroidModuleExtension in project android by JetBrains.

the class AndroidArtifactBuildTaskProvider method createArtifactBuildTasks.

@NotNull
@Override
public List<? extends BuildTask> createArtifactBuildTasks(@NotNull JpsArtifact artifact, @NotNull ArtifactBuildPhase buildPhase) {
    if (buildPhase != ArtifactBuildPhase.FINISHING_BUILD) {
        return Collections.emptyList();
    }
    if (!(artifact.getArtifactType() instanceof AndroidApplicationArtifactType)) {
        return Collections.emptyList();
    }
    final JpsElement props = artifact.getProperties();
    if (!(props instanceof JpsAndroidApplicationArtifactProperties)) {
        return Collections.emptyList();
    }
    final JpsAndroidApplicationArtifactProperties androidProps = (JpsAndroidApplicationArtifactProperties) props;
    if (!(artifact.getArtifactType() instanceof AndroidApplicationArtifactType)) {
        return Collections.emptyList();
    }
    final AndroidArtifactSigningMode signingMode = androidProps.getSigningMode();
    if (signingMode != AndroidArtifactSigningMode.RELEASE_SIGNED && signingMode != AndroidArtifactSigningMode.DEBUG_WITH_CUSTOM_CERTIFICATE) {
        return Collections.emptyList();
    }
    final JpsAndroidModuleExtension extension = AndroidJpsUtil.getPackagedFacet(artifact);
    return extension != null ? Collections.singletonList(new MyTask(artifact, extension, androidProps)) : Collections.<BuildTask>emptyList();
}
Also used : JpsElement(org.jetbrains.jps.model.JpsElement) JpsAndroidModuleExtension(org.jetbrains.jps.android.model.JpsAndroidModuleExtension) JpsAndroidApplicationArtifactProperties(org.jetbrains.jps.android.model.JpsAndroidApplicationArtifactProperties) AndroidApplicationArtifactType(org.jetbrains.jps.android.model.AndroidApplicationArtifactType) AndroidArtifactSigningMode(org.jetbrains.android.compiler.artifact.AndroidArtifactSigningMode) NotNull(org.jetbrains.annotations.NotNull)

Example 2 with JpsAndroidModuleExtension

use of org.jetbrains.jps.android.model.JpsAndroidModuleExtension in project android by JetBrains.

the class AndroidBuildDataCache method collectAndroidDependencies.

private static void collectAndroidDependencies(@NotNull JpsModule module, @NotNull MyAndroidDeps result, @NotNull Set<String> visitedSet, boolean fillLibs, boolean recursively) {
    final List<JpsDependencyElement> dependencies = new ArrayList<JpsDependencyElement>(JpsJavaExtensionService.getInstance().getDependencies(module, JpsJavaClasspathKind.PRODUCTION_RUNTIME, false));
    for (int i = dependencies.size() - 1; i >= 0; i--) {
        final JpsDependencyElement item = dependencies.get(i);
        if (item instanceof JpsModuleDependency) {
            final JpsModule depModule = ((JpsModuleDependency) item).getModule();
            if (depModule != null) {
                final JpsAndroidModuleExtension depExtension = AndroidJpsUtil.getExtension(depModule);
                if (depExtension != null && visitedSet.add(depModule.getName())) {
                    if (recursively) {
                        final boolean newRecursively = AndroidJpsUtil.shouldProcessDependenciesRecursively(depModule);
                        collectAndroidDependencies(depModule, result, visitedSet, fillLibs && depExtension.isLibrary(), newRecursively);
                    }
                    result.myAndroidDeps.add(depExtension);
                    if (fillLibs && depExtension.isLibrary()) {
                        result.myLibAndroidDeps.add(depExtension);
                    }
                }
            }
        }
    }
}
Also used : JpsModule(org.jetbrains.jps.model.module.JpsModule) JpsModuleDependency(org.jetbrains.jps.model.module.JpsModuleDependency) JpsDependencyElement(org.jetbrains.jps.model.module.JpsDependencyElement) JpsAndroidModuleExtension(org.jetbrains.jps.android.model.JpsAndroidModuleExtension)

Example 3 with JpsAndroidModuleExtension

use of org.jetbrains.jps.android.model.JpsAndroidModuleExtension in project android by JetBrains.

the class AndroidDexBuilder method runDex.

public static boolean runDex(@NotNull AndroidPlatform platform, @NotNull String outFilePath, @NotNull String[] compileTargets, @NotNull CompileContext context, @NotNull JpsProject project, @NotNull BuildOutputConsumer outputConsumer, @NotNull String builderName, @NotNull String srcTargetName, @Nullable JpsModule module) throws IOException {
    BuildToolInfo buildToolInfo = platform.getTarget().getBuildToolInfo();
    if (buildToolInfo == null) {
        return false;
    }
    final String dxJarPath = FileUtil.toSystemDependentName(buildToolInfo.getPath(BuildToolInfo.PathId.DX_JAR));
    final AndroidBuildTestingManager testingManager = AndroidBuildTestingManager.getTestingManager();
    final File dxJar = new File(dxJarPath);
    if (testingManager == null && !dxJar.isFile()) {
        context.processMessage(new CompilerMessage(builderName, BuildMessage.Kind.ERROR, AndroidJpsBundle.message("android.jps.cannot.find.file", dxJarPath)));
        return false;
    }
    boolean multiDex = false;
    if (module != null) {
        JpsAndroidModuleExtension extension = AndroidJpsUtil.getExtension(module);
        if (extension != null && extension.isMultiDexEnabled()) {
            outFilePath = new File(outFilePath).getParent();
            multiDex = true;
        }
    }
    final List<String> programParamList = new ArrayList<String>();
    programParamList.add(dxJarPath);
    programParamList.add(outFilePath);
    final JpsAndroidDexCompilerConfiguration configuration = JpsAndroidExtensionService.getInstance().getDexCompilerConfiguration(project);
    final List<String> vmOptions;
    if (configuration != null) {
        vmOptions = new ArrayList<String>();
        vmOptions.addAll(ParametersListUtil.parse(configuration.getVmOptions()));
        if (!AndroidCommonUtils.hasXmxParam(vmOptions)) {
            vmOptions.add("-Xmx" + configuration.getMaxHeapSize() + "M");
        }
        programParamList.addAll(Arrays.asList("--optimize", Boolean.toString(configuration.isOptimize())));
        if (configuration.isForceJumbo()) {
            programParamList.addAll(Arrays.asList("--forceJumbo", Boolean.TRUE.toString()));
        }
        if (configuration.isCoreLibrary()) {
            programParamList.add("--coreLibrary");
        }
    } else {
        vmOptions = Collections.singletonList("-Xmx1024M");
    }
    if (multiDex) {
        JpsAndroidModuleExtension extension = AndroidJpsUtil.getExtension(module);
        if (extension != null) {
            programParamList.add("--multi-dex");
            if (!StringUtil.isEmpty(extension.getMainDexList())) {
                programParamList.add("--main-dex-list");
                programParamList.add(extension.getMainDexList());
            }
            if (extension.isMinimalMainDex()) {
                programParamList.add("--minimal-main-dex");
            }
        }
    }
    programParamList.addAll(Arrays.asList(compileTargets));
    programParamList.add("--exclude");
    final List<String> classPath = new ArrayList<String>();
    classPath.add(ClasspathBootstrap.getResourcePath(AndroidDxRunner.class));
    classPath.add(ClasspathBootstrap.getResourcePath(FileUtilRt.class));
    final File outFile = new File(outFilePath);
    if (outFile.exists() && !outFile.isDirectory() && !outFile.delete()) {
        context.processMessage(new CompilerMessage(builderName, BuildMessage.Kind.WARNING, AndroidJpsBundle.message("android.jps.cannot.delete.file", outFilePath)));
    }
    final String javaExecutable = getJavaExecutable(platform, context, builderName);
    if (javaExecutable == null) {
        return false;
    }
    final List<String> commandLine = ExternalProcessUtil.buildJavaCommandLine(javaExecutable, AndroidDxRunner.class.getName(), Collections.<String>emptyList(), classPath, vmOptions, programParamList);
    LOG.info(AndroidCommonUtils.command2string(commandLine));
    final String[] commands = ArrayUtil.toStringArray(commandLine);
    final Process process;
    if (testingManager != null) {
        process = testingManager.getCommandExecutor().createProcess(commands, Collections.<String, String>emptyMap());
    } else {
        process = Runtime.getRuntime().exec(commands);
    }
    final HashMap<AndroidCompilerMessageKind, List<String>> messages = new HashMap<AndroidCompilerMessageKind, List<String>>(3);
    messages.put(AndroidCompilerMessageKind.ERROR, new ArrayList<String>());
    messages.put(AndroidCompilerMessageKind.WARNING, new ArrayList<String>());
    messages.put(AndroidCompilerMessageKind.INFORMATION, new ArrayList<String>());
    AndroidCommonUtils.handleDexCompilationResult(process, StringUtil.join(commandLine, " "), outFilePath, messages, multiDex);
    AndroidJpsUtil.addMessages(context, messages, builderName, srcTargetName);
    final boolean success = messages.get(AndroidCompilerMessageKind.ERROR).size() == 0;
    if (success) {
        final List<String> srcFiles = new ArrayList<String>();
        for (String compileTargetPath : compileTargets) {
            final File compileTarget = new File(compileTargetPath);
            if (compileTarget.isFile()) {
                srcFiles.add(compileTargetPath);
            } else if (compileTarget.isDirectory()) {
                AndroidJpsUtil.processClassFilesAndJarsRecursively(compileTarget, new Processor<File>() {

                    @Override
                    public boolean process(File file) {
                        if (file.isFile()) {
                            srcFiles.add(file.getPath());
                        }
                        return true;
                    }
                });
            }
        }
        outputConsumer.registerOutputFile(outFile, srcFiles);
    }
    return success;
}
Also used : AndroidBuildTestingManager(org.jetbrains.android.util.AndroidBuildTestingManager) CompilerMessage(org.jetbrains.jps.incremental.messages.CompilerMessage) Processor(com.intellij.util.Processor) BuildToolInfo(com.android.sdklib.BuildToolInfo) HashMap(com.intellij.util.containers.HashMap) AndroidCompilerMessageKind(org.jetbrains.android.util.AndroidCompilerMessageKind) AndroidDxRunner(org.jetbrains.android.compiler.tools.AndroidDxRunner) JpsAndroidDexCompilerConfiguration(org.jetbrains.jps.android.model.JpsAndroidDexCompilerConfiguration) JpsAndroidModuleExtension(org.jetbrains.jps.android.model.JpsAndroidModuleExtension) FileUtilRt(com.intellij.openapi.util.io.FileUtilRt) File(java.io.File)

Example 4 with JpsAndroidModuleExtension

use of org.jetbrains.jps.android.model.JpsAndroidModuleExtension in project android by JetBrains.

the class AndroidSourceGeneratingBuilder method runAaptCompiler.

private static MyExitStatus runAaptCompiler(@NotNull final CompileContext context, @NotNull Map<JpsModule, MyModuleData> moduleDataMap) throws IOException {
    boolean success = true;
    boolean didSomething = false;
    for (Map.Entry<JpsModule, MyModuleData> entry : moduleDataMap.entrySet()) {
        final JpsModule module = entry.getKey();
        final ModuleBuildTarget moduleTarget = new ModuleBuildTarget(module, JavaModuleBuildTargetType.PRODUCTION);
        final AndroidAptStateStorage storage = context.getProjectDescriptor().dataManager.getStorage(moduleTarget, AndroidAptStateStorage.PROVIDER);
        final MyModuleData moduleData = entry.getValue();
        final JpsAndroidModuleExtension extension = moduleData.getAndroidExtension();
        final File generatedSourcesDir = AndroidJpsUtil.getGeneratedSourcesStorage(module, context.getProjectDescriptor().dataManager);
        final File aptOutputDirectory = new File(generatedSourcesDir, AndroidJpsUtil.AAPT_GENERATED_SOURCE_ROOT_NAME);
        final IAndroidTarget target = moduleData.getPlatform().getTarget();
        try {
            final String[] resPaths = AndroidJpsUtil.collectResourceDirsForCompilation(extension, false, context, true);
            if (resPaths.length == 0) {
                // there is no resources in the module
                if (!clearDirectoryIfNotEmpty(aptOutputDirectory, context, ANDROID_APT_COMPILER)) {
                    success = false;
                }
                continue;
            }
            final String packageName = moduleData.getPackage();
            final File manifestFile;
            if (extension.isLibrary() || !extension.isManifestMergingEnabled()) {
                manifestFile = moduleData.getManifestFileForCompiler();
            } else {
                manifestFile = new File(AndroidJpsUtil.getPreprocessedManifestDirectory(module, context.getProjectDescriptor().dataManager.getDataPaths()), SdkConstants.FN_ANDROID_MANIFEST_XML);
            }
            if (isLibraryWithBadCircularDependency(extension)) {
                if (!clearDirectoryIfNotEmpty(aptOutputDirectory, context, ANDROID_APT_COMPILER)) {
                    success = false;
                }
                continue;
            }
            final Map<JpsModule, String> packageMap = getDepLibPackages(module);
            packageMap.put(module, packageName);
            final JpsModule circularDepLibWithSamePackage = findCircularDependencyOnLibraryWithSamePackage(extension, packageMap);
            if (circularDepLibWithSamePackage != null && !extension.isLibrary()) {
                final String message = "Generated fields in " + packageName + ".R class in module '" + module.getName() + "' won't be final, because of circular dependency on module '" + circularDepLibWithSamePackage.getName() + "'";
                context.processMessage(new CompilerMessage(ANDROID_APT_COMPILER, BuildMessage.Kind.WARNING, message));
            }
            final boolean generateNonFinalFields = extension.isLibrary() || circularDepLibWithSamePackage != null;
            AndroidAptValidityState oldState;
            try {
                oldState = storage.getState(module.getName());
            } catch (IOException e) {
                LOG.info(e);
                oldState = null;
            }
            final Map<String, ResourceFileData> resources = new HashMap<String, ResourceFileData>();
            final TObjectLongHashMap<String> valueResFilesTimestamps = new TObjectLongHashMap<String>();
            collectResources(resPaths, resources, valueResFilesTimestamps, oldState);
            final List<ResourceEntry> manifestElements = collectManifestElements(manifestFile);
            final List<Pair<String, String>> libRTextFilesAndPackages = new ArrayList<Pair<String, String>>(packageMap.size());
            for (Map.Entry<JpsModule, String> entry1 : packageMap.entrySet()) {
                final String libPackage = entry1.getValue();
                if (!packageName.equals(libPackage)) {
                    final String libRTxtFilePath = new File(new File(AndroidJpsUtil.getDirectoryForIntermediateArtifacts(context, entry1.getKey()), R_TXT_OUTPUT_DIR_NAME), SdkConstants.FN_RESOURCE_TEXT).getPath();
                    libRTextFilesAndPackages.add(Pair.create(libRTxtFilePath, libPackage));
                }
            }
            AndroidJpsUtil.collectRTextFilesFromAarDeps(module, libRTextFilesAndPackages);
            final File outputDirForArtifacts = AndroidJpsUtil.getDirectoryForIntermediateArtifacts(context, module);
            final String proguardOutputCfgFilePath;
            if (AndroidJpsUtil.getProGuardConfigIfShouldRun(context, extension) != null) {
                if (AndroidJpsUtil.createDirIfNotExist(outputDirForArtifacts, context, BUILDER_NAME) == null) {
                    success = false;
                    continue;
                }
                proguardOutputCfgFilePath = new File(outputDirForArtifacts, AndroidCommonUtils.PROGUARD_CFG_OUTPUT_FILE_NAME).getPath();
            } else {
                proguardOutputCfgFilePath = null;
            }
            String rTxtOutDirOsPath = null;
            if (extension.isLibrary() || libRTextFilesAndPackages.size() > 0) {
                final File rTxtOutDir = new File(outputDirForArtifacts, R_TXT_OUTPUT_DIR_NAME);
                if (AndroidJpsUtil.createDirIfNotExist(rTxtOutDir, context, BUILDER_NAME) == null) {
                    success = false;
                    continue;
                }
                rTxtOutDirOsPath = rTxtOutDir.getPath();
            }
            final AndroidAptValidityState newState = new AndroidAptValidityState(resources, valueResFilesTimestamps, manifestElements, libRTextFilesAndPackages, packageName, proguardOutputCfgFilePath, rTxtOutDirOsPath, extension.isLibrary());
            if (newState.equalsTo(oldState)) {
                // we need to update state, because it also contains myValueResFilesTimestamps not taking into account by equalsTo()
                storage.update(module.getName(), newState);
                continue;
            }
            didSomething = true;
            context.processMessage(new ProgressMessage(AndroidJpsBundle.message("android.jps.progress.aapt", module.getName())));
            File tmpOutputDir = null;
            try {
                tmpOutputDir = FileUtil.createTempDirectory("android_apt_output", "tmp");
                final Map<AndroidCompilerMessageKind, List<String>> messages = AndroidApt.compile(target, -1, manifestFile.getPath(), packageName, tmpOutputDir.getPath(), resPaths, libRTextFilesAndPackages, generateNonFinalFields, proguardOutputCfgFilePath, rTxtOutDirOsPath, !extension.isLibrary());
                AndroidJpsUtil.addMessages(context, messages, ANDROID_APT_COMPILER, module.getName());
                if (messages.get(AndroidCompilerMessageKind.ERROR).size() > 0) {
                    success = false;
                    storage.update(module.getName(), null);
                } else {
                    if (!AndroidCommonUtils.directoriesContainSameContent(tmpOutputDir, aptOutputDirectory, JAVA_FILE_FILTER)) {
                        if (!deleteAndMarkRecursively(aptOutputDirectory, context, ANDROID_APT_COMPILER)) {
                            success = false;
                            continue;
                        }
                        final File parent = aptOutputDirectory.getParentFile();
                        if (parent != null && !parent.exists() && !parent.mkdirs()) {
                            context.processMessage(new CompilerMessage(ANDROID_APT_COMPILER, BuildMessage.Kind.ERROR, AndroidJpsBundle.message("android.jps.cannot.create.directory", parent.getPath())));
                            success = false;
                            continue;
                        }
                        // we use copyDir instead of moveDirWithContent here, because tmp directory may be located on other disk and
                        // moveDirWithContent doesn't work for such case
                        FileUtil.copyDir(tmpOutputDir, aptOutputDirectory);
                        markDirtyRecursively(aptOutputDirectory, context, ANDROID_APT_COMPILER, true);
                    }
                    storage.update(module.getName(), newState);
                }
            } finally {
                if (tmpOutputDir != null) {
                    FileUtil.delete(tmpOutputDir);
                }
            }
        } catch (IOException e) {
            AndroidJpsUtil.reportExceptionError(context, null, e, ANDROID_APT_COMPILER);
            success = false;
        }
    }
    if (!success) {
        return MyExitStatus.FAIL;
    } else if (didSomething) {
        return MyExitStatus.OK;
    }
    return MyExitStatus.NOTHING_CHANGED;
}
Also used : ProgressMessage(org.jetbrains.jps.incremental.messages.ProgressMessage) CompilerMessage(org.jetbrains.jps.incremental.messages.CompilerMessage) HashMap(com.intellij.util.containers.HashMap) TObjectLongHashMap(gnu.trove.TObjectLongHashMap) Pair(com.intellij.openapi.util.Pair) IAndroidTarget(com.android.sdklib.IAndroidTarget) TObjectLongHashMap(gnu.trove.TObjectLongHashMap) JpsModule(org.jetbrains.jps.model.module.JpsModule) JpsAndroidModuleExtension(org.jetbrains.jps.android.model.JpsAndroidModuleExtension) HashMap(com.intellij.util.containers.HashMap) TObjectLongHashMap(gnu.trove.TObjectLongHashMap)

Example 5 with JpsAndroidModuleExtension

use of org.jetbrains.jps.android.model.JpsAndroidModuleExtension in project android by JetBrains.

the class AndroidSourceGeneratingBuilder method runBuildConfigGeneration.

private static MyExitStatus runBuildConfigGeneration(@NotNull CompileContext context, @NotNull Map<JpsModule, MyModuleData> moduleDataMap) throws IOException {
    boolean success = true;
    boolean didSomething = false;
    for (Map.Entry<JpsModule, MyModuleData> entry : moduleDataMap.entrySet()) {
        final JpsModule module = entry.getKey();
        final ModuleBuildTarget moduleTarget = new ModuleBuildTarget(module, JavaModuleBuildTargetType.PRODUCTION);
        final AndroidBuildConfigStateStorage storage = context.getProjectDescriptor().dataManager.getStorage(moduleTarget, AndroidBuildConfigStateStorage.PROVIDER);
        final MyModuleData moduleData = entry.getValue();
        final JpsAndroidModuleExtension extension = AndroidJpsUtil.getExtension(module);
        final File generatedSourcesDir = AndroidJpsUtil.getGeneratedSourcesStorage(module, context.getProjectDescriptor().dataManager);
        final File outputDirectory = new File(generatedSourcesDir, AndroidJpsUtil.BUILD_CONFIG_GENERATED_SOURCE_ROOT_NAME);
        try {
            if (extension == null || isLibraryWithBadCircularDependency(extension)) {
                if (!clearDirectoryIfNotEmpty(outputDirectory, context, ANDROID_BUILD_CONFIG_GENERATOR)) {
                    success = false;
                }
                continue;
            }
            final String packageName = moduleData.getPackage();
            final boolean debug = !AndroidJpsUtil.isReleaseBuild(context);
            final Set<String> libPackages = new HashSet<String>(getDepLibPackages(module).values());
            libPackages.remove(packageName);
            final AndroidBuildConfigState newState = new AndroidBuildConfigState(packageName, libPackages, debug);
            final AndroidBuildConfigState oldState = storage.getState(module.getName());
            if (newState.equalsTo(oldState)) {
                continue;
            }
            didSomething = true;
            context.processMessage(new ProgressMessage(AndroidJpsBundle.message("android.jps.progress.build.config", module.getName())));
            // clear directory, because it may contain obsolete files (ex. if package name was changed)
            if (!clearDirectory(outputDirectory, context, ANDROID_BUILD_CONFIG_GENERATOR)) {
                success = false;
                continue;
            }
            if (doBuildConfigGeneration(packageName, libPackages, debug, outputDirectory, context)) {
                storage.update(module.getName(), newState);
                markDirtyRecursively(outputDirectory, context, ANDROID_BUILD_CONFIG_GENERATOR, true);
            } else {
                storage.update(module.getName(), null);
                success = false;
            }
        } catch (IOException e) {
            AndroidJpsUtil.reportExceptionError(context, null, e, ANDROID_BUILD_CONFIG_GENERATOR);
            success = false;
        }
    }
    if (!success) {
        return MyExitStatus.FAIL;
    } else if (didSomething) {
        return MyExitStatus.OK;
    }
    return MyExitStatus.NOTHING_CHANGED;
}
Also used : ProgressMessage(org.jetbrains.jps.incremental.messages.ProgressMessage) JpsModule(org.jetbrains.jps.model.module.JpsModule) JpsAndroidModuleExtension(org.jetbrains.jps.android.model.JpsAndroidModuleExtension) HashMap(com.intellij.util.containers.HashMap) TObjectLongHashMap(gnu.trove.TObjectLongHashMap) HashSet(com.intellij.util.containers.HashSet) THashSet(gnu.trove.THashSet)

Aggregations

JpsAndroidModuleExtension (org.jetbrains.jps.android.model.JpsAndroidModuleExtension)30 File (java.io.File)16 JpsModule (org.jetbrains.jps.model.module.JpsModule)14 CompilerMessage (org.jetbrains.jps.incremental.messages.CompilerMessage)11 NotNull (org.jetbrains.annotations.NotNull)9 HashMap (com.intellij.util.containers.HashMap)8 ArrayList (java.util.ArrayList)8 BuildRootDescriptor (org.jetbrains.jps.builders.BuildRootDescriptor)7 ProgressMessage (org.jetbrains.jps.incremental.messages.ProgressMessage)7 HashSet (com.intellij.util.containers.HashSet)6 TObjectLongHashMap (gnu.trove.TObjectLongHashMap)6 IAndroidTarget (com.android.sdklib.IAndroidTarget)3 THashSet (gnu.trove.THashSet)3 IOException (java.io.IOException)3 AndroidBuildTestingManager (org.jetbrains.android.util.AndroidBuildTestingManager)3 AndroidCompilerMessageKind (org.jetbrains.android.util.AndroidCompilerMessageKind)3 BuildRootDescriptorImpl (org.jetbrains.jps.builders.impl.BuildRootDescriptorImpl)3 AndroidArtifactSigningMode (org.jetbrains.android.compiler.artifact.AndroidArtifactSigningMode)2 Nullable (org.jetbrains.annotations.Nullable)2 AndroidPlatform (org.jetbrains.jps.android.AndroidPlatform)2