blob: b9c63ef6ad01c0c208ae689c50b06d9630711b25 [file] [log] [blame]
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.android.compiler;
import com.android.sdklib.IAndroidTarget;
import com.android.SdkConstants;
import com.intellij.facet.FacetManager;
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.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.android.compiler.tools.AndroidIdl;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidRootUtil;
import org.jetbrains.android.fileTypes.AndroidIdlFileType;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.DataInput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Android IDL compiler.
*
* @author Alexey Efimov
*/
public class AndroidIdlCompiler implements SourceGeneratingCompiler {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.compiler.AndroidIdlCompiler");
private static final GenerationItem[] EMPTY_GENERATION_ITEM_ARRAY = {};
private final Project myProject;
public AndroidIdlCompiler(Project project) {
myProject = project;
}
@Nullable
@Override
public VirtualFile getPresentableFile(CompileContext context, Module module, VirtualFile outputRoot, VirtualFile generatedFile) {
return null;
}
public GenerationItem[] getGenerationItems(CompileContext context) {
return ApplicationManager.getApplication().runReadAction(new PrepareAction(context));
}
public GenerationItem[] generate(CompileContext context, GenerationItem[] items, VirtualFile outputRootDirectory) {
if (items != null && items.length > 0) {
context.getProgressIndicator().setText("Generating AIDL files...");
return doGenerate(context, items, outputRootDirectory);
}
return EMPTY_GENERATION_ITEM_ARRAY;
}
@NotNull
public String getDescription() {
return FileUtil.getNameWithoutExtension(SdkConstants.FN_AIDL);
}
public boolean validateConfiguration(CompileScope scope) {
return true;
}
@Nullable
public ValidityState createValidityState(DataInput is) throws IOException {
return TimestampValidityState.load(is);
}
private final static class IdlGenerationItem implements GenerationItem {
final Module myModule;
final VirtualFile myFile;
final boolean myTestSource;
final IAndroidTarget myAndroidTarget;
final String myPackageName;
private final TimestampValidityState myValidityState;
public IdlGenerationItem(@NotNull Module module,
@NotNull VirtualFile file,
boolean testSource,
@NotNull IAndroidTarget androidTarget,
@NotNull String packageName) {
myModule = module;
myFile = file;
myTestSource = testSource;
myAndroidTarget = androidTarget;
myPackageName = packageName;
myValidityState = new TimestampValidityState(myFile.getTimeStamp());
}
@Nullable
public String getPath() {
return myPackageName.replace('.', '/') + '/' + myFile.getNameWithoutExtension() + ".java";
}
@Nullable
public ValidityState getValidityState() {
return myValidityState;
}
public Module getModule() {
return myModule;
}
public boolean isTestSource() {
return myTestSource;
}
}
private final class PrepareAction implements Computable<GenerationItem[]> {
private final CompileContext myContext;
public PrepareAction(CompileContext context) {
myContext = context;
}
public GenerationItem[] compute() {
if (myContext.getProject().isDisposed()) {
return EMPTY_GENERATION_ITEM_ARRAY;
}
VirtualFile[] files = myContext.getProjectCompileScope().getFiles(AndroidIdlFileType.ourFileType, true);
List<GenerationItem> items = new ArrayList<GenerationItem>(files.length);
for (VirtualFile file : files) {
Module module = myContext.getModuleByFile(file);
AndroidFacet facet = FacetManager.getInstance(module).getFacetByType(AndroidFacet.ID);
if (facet != null) {
IAndroidTarget target = facet.getConfiguration().getAndroidTarget();
if (target == null) {
myContext.addMessage(CompilerMessageCategory.ERROR,
AndroidBundle.message("android.compilation.error.specify.platform", module.getName()), null, -1, -1);
continue;
}
String packageName = AndroidUtils.computePackageName(module, file);
if (packageName == null) {
myContext.addMessage(CompilerMessageCategory.ERROR, "Cannot compute package for file", file.getUrl(), -1, -1);
continue;
}
addItem(file, facet, target, packageName, items);
}
}
return items.toArray(new GenerationItem[items.size()]);
}
private void addItem(VirtualFile file,
AndroidFacet facet,
IAndroidTarget target,
String packageName,
List<GenerationItem> items) {
Module module = facet.getModule();
String sourceRootPath = AndroidRootUtil.getAidlGenSourceRootPath(facet);
if (sourceRootPath == null) {
myContext.addMessage(CompilerMessageCategory.ERROR,
AndroidBundle.message("android.compilation.error.apt.gen.not.specified", module.getName()), null, -1, -1);
return;
}
ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex();
items.add(new IdlGenerationItem(module, file, fileIndex.isInTestSourceContent(file), target, packageName));
}
}
private static GenerationItem[] doGenerate(final CompileContext context, GenerationItem[] items, VirtualFile outputRootDirectory) {
if (context.getProject().isDisposed()) {
return EMPTY_GENERATION_ITEM_ARRAY;
}
List<GenerationItem> results = new ArrayList<GenerationItem>(items.length);
boolean toRefresh = false;
for (GenerationItem item : items) {
if (item instanceof IdlGenerationItem) {
final IdlGenerationItem idlItem = (IdlGenerationItem)item;
if (!AndroidCompileUtil.isModuleAffected(context, idlItem.myModule)) {
continue;
}
try {
VirtualFile[] sourceRoots = AndroidPackagingCompiler.getSourceRootsForModuleAndDependencies(idlItem.myModule, false);
final String[] sourceRootPaths = AndroidCompileUtil.toOsPaths(sourceRoots);
final String outFilePath = FileUtil.toSystemDependentName(
outputRootDirectory.getPath() + '/' + idlItem.myPackageName.replace('.', '/') + '/' +
idlItem.myFile.getNameWithoutExtension() + ".java");
final Map<CompilerMessageCategory, List<String>> messages = AndroidCompileUtil.toCompilerMessageCategoryKeys(
AndroidIdl.execute(idlItem.myAndroidTarget, idlItem.myFile.getPath(), outFilePath, sourceRootPaths));
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
if (context.getProject().isDisposed()) return;
addMessages(context, messages, idlItem.myFile.getUrl());
}
});
if (messages.get(CompilerMessageCategory.ERROR).isEmpty()) {
toRefresh = true;
results.add(idlItem);
}
}
catch (final IOException e) {
LOG.info(e);
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
if (context.getProject().isDisposed()) return;
context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), idlItem.myFile.getUrl(), -1, -1);
}
});
}
}
}
if (toRefresh) {
AndroidCompileUtil.markDirtyAndRefresh(outputRootDirectory, true);
}
return results.toArray(new GenerationItem[results.size()]);
}
private static void addMessages(CompileContext context, Map<CompilerMessageCategory, List<String>> messages, String url) {
for (CompilerMessageCategory category : messages.keySet()) {
List<String> messageList = messages.get(category);
for (String message : messageList) {
context.addMessage(category, message, url, -1, -1);
}
}
}
}