blob: 36142482cfb7113aaa666b98fdb00087f44cc780 [file] [log] [blame]
package org.jetbrains.android.compiler;
import com.android.sdklib.internal.build.BuildConfigGenerator;
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.module.ModuleManager;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.xml.NanoXmlUtil;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidRootUtil;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidBuildConfigGeneratingCompiler implements SourceGeneratingCompiler {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.compiler.AndroidBuildConfigGeneratingCompiler");
private static final String MANIFEST_LOCATION = NanoXmlUtil.createLocation("manifest");
@Nullable
@Override
public VirtualFile getPresentableFile(CompileContext context, Module module, VirtualFile outputRoot, VirtualFile generatedFile) {
return null;
}
@Override
public GenerationItem[] getGenerationItems(final CompileContext context) {
return ApplicationManager.getApplication().runReadAction(new Computable<GenerationItem[]>() {
@Override
public GenerationItem[] compute() {
final List<GenerationItem> result = new ArrayList<GenerationItem>();
for (Module module : ModuleManager.getInstance(context.getProject()).getModules()) {
final AndroidFacet facet = AndroidFacet.getInstance(module);
if (facet == null || AndroidCompileUtil.isLibraryWithBadCircularDependency(facet)) {
continue;
}
final VirtualFile manifestFile = AndroidRootUtil.getManifestFileForCompiler(facet);
if (manifestFile == null) {
context.addMessage(CompilerMessageCategory.ERROR,
AndroidBundle.message("android.compilation.error.manifest.not.found", module.getName()), null, -1, -1);
continue;
}
String packageName;
try {
// we cannot use DOM here because custom manifest file can be excluded (ex. it can be located in /target/ folder)
packageName = parsePackageName(manifestFile);
}
catch (IOException e) {
context.addMessage(CompilerMessageCategory.ERROR, "I/O error: " + e.getMessage(), null, -1, -1);
continue;
}
if (packageName != null) {
packageName = packageName.trim();
}
if (packageName == null || packageName.length() <= 0) {
context.addMessage(CompilerMessageCategory.ERROR, AndroidBundle.message("package.not.found.error"), manifestFile.getUrl(),
-1, -1);
continue;
}
final boolean debug = !AndroidCompileUtil.isReleaseBuild(context);
result.add(new MyGenerationItem(module, packageName, debug));
for (String libPackage : AndroidCompileUtil.getLibPackages(module, packageName)) {
result.add(new MyGenerationItem(module, libPackage, debug));
}
}
return result.toArray(new GenerationItem[result.size()]);
}
});
}
@Override
public GenerationItem[] generate(CompileContext context,
GenerationItem[] items,
VirtualFile outputRootDirectory) {
if (items == null || items.length == 0) {
return new GenerationItem[0];
}
context.getProgressIndicator().setText("Generating BuildConfig.java...");
final String genFolderOsPath = FileUtil.toSystemDependentName(outputRootDirectory.getPath());
final List<GenerationItem> result = new ArrayList<GenerationItem>();
for (GenerationItem item : items) {
final MyGenerationItem genItem = (MyGenerationItem)item;
final BuildConfigGenerator generator = new BuildConfigGenerator(genFolderOsPath, genItem.myPackage, genItem.myDebug);
try {
generator.generate();
result.add(genItem);
}
catch (IOException e) {
context.addMessage(CompilerMessageCategory.ERROR, "I/O error: " + e.getMessage(), null, -1, -1);
LOG.info(e);
}
}
if (result.size() > 0) {
AndroidCompileUtil.markDirtyAndRefresh(outputRootDirectory, true);
}
return result.toArray(new GenerationItem[result.size()]);
}
@Nullable
private static String parsePackageName(@NotNull VirtualFile manifestFile) throws IOException {
final Ref<String> packageNameRef = Ref.create(null);
NanoXmlUtil.parse(manifestFile.getInputStream(), new NanoXmlUtil.BaseXmlBuilder() {
@Override
public void addAttribute(String key, String nsPrefix, String nsURI, String value, String type)
throws Exception {
super.addAttribute(key, nsPrefix, nsURI, value, type);
if (AndroidCommonUtils.PACKAGE_MANIFEST_ATTRIBUTE.equals(key) &&
MANIFEST_LOCATION.equals(getLocation())) {
packageNameRef.set(value);
stop();
}
}
@Override
public void elementAttributesProcessed(String name, String nsPrefix, String nsURI) throws Exception {
super.elementAttributesProcessed(name, nsPrefix, nsURI);
stop();
}
});
return packageNameRef.get();
}
@NotNull
@Override
public String getDescription() {
return "Android BuildConfig Generator";
}
@Override
public boolean validateConfiguration(CompileScope scope) {
return true;
}
@Override
public ValidityState createValidityState(DataInput in) throws IOException {
return new MyValidityState(in);
}
private static class MyGenerationItem implements GenerationItem {
final Module myModule;
final String myPackage;
final boolean myDebug;
private MyGenerationItem(@NotNull Module module, @NotNull String aPackage, boolean debug) {
myModule = module;
myPackage = aPackage;
myDebug = debug;
}
@Override
public String getPath() {
return myPackage.replace('.', '/') + '/' + BuildConfigGenerator.BUILD_CONFIG_NAME;
}
@Override
public ValidityState getValidityState() {
return new MyValidityState(myPackage, myDebug);
}
@Override
public Module getModule() {
return myModule;
}
@Override
public boolean isTestSource() {
return false;
}
}
private static class MyValidityState implements ValidityState {
private final String myPackage;
private boolean myDebug;
private MyValidityState(DataInput in) throws IOException {
myPackage = in.readUTF();
myDebug = in.readBoolean();
}
private MyValidityState(@NotNull String aPackage, boolean debug) {
myPackage = aPackage;
myDebug = debug;
}
@Override
public boolean equalsTo(ValidityState otherState) {
if (!(otherState instanceof MyValidityState)) {
return false;
}
final MyValidityState otherState1 = (MyValidityState)otherState;
return otherState1.myPackage.equals(myPackage) && otherState1.myDebug == myDebug;
}
@Override
public void save(DataOutput out) throws IOException {
out.writeUTF(myPackage);
out.writeBoolean(myDebug);
}
}
}