IDEA-83336 support BuildConfig.java generating [rev=nnmatveev]

This commit is contained in:
Eugene Kudelevsky
2012-04-23 17:09:46 +04:00
parent 95be7938ef
commit fdb4570e8d
11 changed files with 372 additions and 52 deletions
@@ -53,8 +53,9 @@ public class AndroidCommonUtils {
private static final Pattern ERROR_PATTERN = Pattern.compile(".*error.*");
private static final Pattern EXCEPTION_PATTERN = Pattern.compile(".*exception.*");
private static Pattern R_PATTERN = Pattern.compile("R(\\$.*)?\\.class");
private static Pattern MANIFEST_PATTERN = Pattern.compile("Manifest(\\$.*)?\\.class");
private static final Pattern R_PATTERN = Pattern.compile("R(\\$.*)?\\.class");
private static final Pattern MANIFEST_PATTERN = Pattern.compile("Manifest(\\$.*)?\\.class");
private static final String BUILD_CONFIG_CLASS_NAME = "BuildConfig.class";
public static final Pattern COMPILER_MESSAGE_PATTERN = Pattern.compile("(.+):(\\d+):.+");
@@ -226,7 +227,8 @@ public class AndroidCommonUtils {
if (!packRAndManifestClasses &&
(R_PATTERN.matcher(file.getName()).matches() ||
MANIFEST_PATTERN.matcher(file.getName()).matches())) {
MANIFEST_PATTERN.matcher(file.getName()).matches() ||
BUILD_CONFIG_CLASS_NAME.equals(file.getName()))) {
return;
}
+1
View File
@@ -134,6 +134,7 @@
<compiler implementation="org.jetbrains.android.compiler.AndroidIncludingCompiler"/>
<compiler implementation="org.jetbrains.android.compiler.AndroidRenderscriptCompiler"/>
<compiler implementation="org.jetbrains.android.compiler.AndroidBuildConfigGeneratingCompiler"/>
<compiler implementation="org.jetbrains.android.compiler.AndroidAptCompiler"/>
<compiler implementation="org.jetbrains.android.compiler.AndroidIdlCompiler"/>
<compiler implementation="org.jetbrains.android.compiler.AndroidLibraryPackagingCompiler"/>
@@ -26,8 +26,6 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.HashSet;
import org.jetbrains.android.compiler.tools.AndroidApt;
import org.jetbrains.android.dom.manifest.Manifest;
import org.jetbrains.android.facet.AndroidFacet;
@@ -260,7 +258,7 @@ public class AndroidAptCompiler implements SourceGeneratingCompiler {
-1, -1);
continue;
}
final String[] libPackages = getLibPackages(module, packageName);
final String[] libPackages = AndroidCompileUtil.getLibPackages(module, packageName);
final Module circularDepLibWithSamePackage = AndroidCompileUtil.findCircularDependencyOnLibraryWithSamePackage(facet);
if (circularDepLibWithSamePackage != null && !facet.getConfiguration().LIBRARY_PROJECT) {
@@ -304,22 +302,6 @@ public class AndroidAptCompiler implements SourceGeneratingCompiler {
}
return false;
}
@NotNull
private static String[] getLibPackages(@NotNull Module module, @NotNull String packageName) {
final Set<String> packageSet = new HashSet<String>();
packageSet.add(packageName);
final List<String> result = new ArrayList<String>();
for (String libPackage : AndroidUtils.getDepLibsPackages(module)) {
if (packageSet.add(libPackage)) {
result.add(libPackage);
}
}
return ArrayUtil.toStringArray(result);
}
}
private static class MyValidityState extends ResourceNamesValidityState {
@@ -2,6 +2,7 @@ package org.jetbrains.android.compiler;
import com.android.AndroidConstants;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.internal.build.BuildConfigGenerator;
import com.intellij.compiler.impl.CompilerUtil;
import com.intellij.compiler.impl.ModuleCompileScope;
import com.intellij.openapi.application.ApplicationManager;
@@ -58,6 +59,7 @@ public class AndroidAutogenerator {
case AIDL:
return facet.getConfiguration().REGENERATE_JAVA_BY_AIDL;
case RENDERSCRIPT:
case BUILDCONFIG:
return true;
default:
LOG.error("Unknown autogenerator mode " + mode);
@@ -81,6 +83,9 @@ public class AndroidAutogenerator {
case RENDERSCRIPT:
runRenderscript(facet, context);
break;
case BUILDCONFIG:
runBuildConfigGenerator(facet, context);
break;
default:
LOG.error("Unknown mode" + mode);
}
@@ -96,6 +101,80 @@ public class AndroidAutogenerator {
}
}
private static void runBuildConfigGenerator(@NotNull final AndroidFacet facet, @NotNull final CompileContext context) {
final Module module = facet.getModule();
final BuildconfigAutogenerationItem item = ApplicationManager.getApplication().runReadAction(
new Computable<BuildconfigAutogenerationItem>() {
@Nullable
@Override
public BuildconfigAutogenerationItem compute() {
if (module.isDisposed() || module.getProject().isDisposed()) {
return null;
}
final String sourceRootPath = AndroidRootUtil.getBuildconfigGenSourceRootPath(facet);
if (sourceRootPath == null) {
return null;
}
final VirtualFile manifestFile = AndroidRootUtil.getManifestFileForCompiler(facet);
if (manifestFile == null) {
context.addMessage(CompilerMessageCategory.ERROR, AndroidBundle.message("android.compilation.error.manifest.not.found"),
null, -1, -1);
return null;
}
final Manifest manifest = AndroidUtils.loadDomElement(module, manifestFile, Manifest.class);
if (manifest == null) {
context.addMessage(CompilerMessageCategory.ERROR, "Cannot parse file", manifestFile.getUrl(), -1, -1);
return null;
}
String packageName = manifest.getPackage().getValue();
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);
return null;
}
return new BuildconfigAutogenerationItem(packageName, FileUtil.toSystemDependentName(sourceRootPath));
}
});
// doesn't matter, because autogenerated class is used for resolve/completion only
final boolean debug = true;
final BuildConfigGenerator generator = new BuildConfigGenerator(item.mySourceRootOsPath, item.myPackage, debug);
try {
generator.generate();
final VirtualFile genSourceRoot = LocalFileSystem.getInstance().findFileByPath(item.mySourceRootOsPath);
if (genSourceRoot != null) {
genSourceRoot.refresh(false, true);
}
facet.clearAutogeneratedFiles(AndroidAutogeneratorMode.BUILDCONFIG);
final VirtualFile genFile = LocalFileSystem.getInstance().findFileByPath(
item.mySourceRootOsPath + '/' + item.myPackage.replace('.', '/') + '/' + BuildConfigGenerator.BUILD_CONFIG_NAME);
if (genFile != null && genFile.exists()) {
facet.markFileAutogenerated(AndroidAutogeneratorMode.BUILDCONFIG, genFile);
}
}
catch (final IOException e) {
LOG.info(e);
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
if (module.getProject().isDisposed()) return;
context.addMessage(CompilerMessageCategory.ERROR, "I/O error: " + e.getMessage(), null, -1, -1);
}
});
}
}
private static void runAapt(@NotNull final AndroidFacet facet, @NotNull final CompileContext context) {
final Module module = facet.getModule();
@@ -644,4 +723,14 @@ public class AndroidAutogenerator {
myRawDirPath = rawDirPath;
}
}
private static class BuildconfigAutogenerationItem {
final String myPackage;
final String mySourceRootOsPath;
private BuildconfigAutogenerationItem(@NotNull String aPackage, @NotNull String sourceRootOsPath) {
myPackage = aPackage;
mySourceRootOsPath = sourceRootOsPath;
}
}
}
@@ -4,5 +4,5 @@ package org.jetbrains.android.compiler;
* @author Eugene.Kudelevsky
*/
public enum AndroidAutogeneratorMode {
AAPT, AIDL, RENDERSCRIPT
AAPT, AIDL, RENDERSCRIPT, BUILDCONFIG
}
@@ -0,0 +1,193 @@
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.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.android.dom.manifest.Manifest;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidRootUtil;
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.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");
@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) {
continue;
}
final VirtualFile manifestFile = AndroidRootUtil.getManifestFileForCompiler(facet);
if (manifestFile == null) {
context.addMessage(CompilerMessageCategory.ERROR, AndroidBundle.message("android.compilation.error.manifest.not.found"),
null, -1, -1);
continue;
}
final Manifest manifest = AndroidUtils.loadDomElement(module, manifestFile, Manifest.class);
if (manifest == null) {
context.addMessage(CompilerMessageCategory.ERROR, "Cannot parse file", manifestFile.getUrl(), -1, -1);
continue;
}
String packageName = manifest.getPackage().getValue();
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) {
outputRootDirectory.refresh(false, true);
}
return result.toArray(new GenerationItem[result.size()]);
}
@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);
}
}
}
@@ -654,6 +654,8 @@ public class AndroidCompileUtil {
final Module module = facet.getModule();
final GlobalSearchScope moduleScope = facet.getModule().getModuleScope();
initializeGenSourceRoot(module, AndroidRootUtil.getBuildconfigGenSourceRootPath(facet), true, true);
initializeGenSourceRoot(module, AndroidRootUtil.getRenderscriptGenSourceRootPath(facet),
FileTypeIndex.getFiles(AndroidRenderscriptFileType.INSTANCE, moduleScope).size() > 0, true);
@@ -900,4 +902,20 @@ public class AndroidCompileUtil {
}
return null;
}
@NotNull
public static String[] getLibPackages(@NotNull Module module, @NotNull String packageName) {
final Set<String> packageSet = new HashSet<String>();
packageSet.add(packageName);
final List<String> result = new ArrayList<String>();
for (String libPackage : AndroidUtils.getDepLibsPackages(module)) {
if (packageSet.add(libPackage)) {
result.add(libPackage);
}
}
return ArrayUtil.toStringArray(result);
}
}
@@ -190,6 +190,11 @@ public class AndroidPrecompileTask implements CompileTask {
sourceRootSet.remove(rsGenRoot);
}
final VirtualFile buildconfigGenDir = AndroidRootUtil.getBuildconfigGenDir(facet);
if (buildconfigGenDir != null) {
sourceRootSet.remove(buildconfigGenDir);
}
final ExcludeEntryDescription[] descriptions = configuration.getExcludeEntryDescriptions();
configuration.removeAllExcludeEntryDescriptions();
@@ -459,6 +459,7 @@ public class AndroidFacet extends Facet<AndroidFacetConfiguration> {
AndroidCompileUtil.generate(module, AndroidAutogeneratorMode.AIDL);
}
AndroidCompileUtil.generate(module, AndroidAutogeneratorMode.RENDERSCRIPT);
AndroidCompileUtil.generate(module, AndroidAutogeneratorMode.BUILDCONFIG);
activateSourceAutogenerating();
}
@@ -39,6 +39,9 @@ import org.jetbrains.android.util.ResourceEntry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static org.jetbrains.android.util.AndroidUtils.findSourceRoot;
@@ -144,59 +147,61 @@ class AndroidResourceFilesListener extends VirtualFileAdapter {
return;
}
final AndroidAutogeneratorMode autogenerationMode =
ApplicationManager.getApplication().runReadAction(new Computable<AndroidAutogeneratorMode>() {
final List<AndroidAutogeneratorMode> autogenerationModes =
ApplicationManager.getApplication().runReadAction(new Computable<List<AndroidAutogeneratorMode>>() {
@Nullable
public AndroidAutogeneratorMode compute() {
return computeCompilerToRunAndInvalidateLocalAttributesMap();
public List<AndroidAutogeneratorMode> compute() {
return computeCompilersToRunAndInvalidateLocalAttributesMap();
}
});
if (autogenerationMode == null) {
if (autogenerationModes.isEmpty()) {
return;
}
if (autogenerationMode == AndroidAutogeneratorMode.AAPT &&
AndroidRootUtil.getManifestFile(myFacet) != myEvent.getFile()) {
for (AndroidAutogeneratorMode autogenerationMode : autogenerationModes) {
if (autogenerationMode == AndroidAutogeneratorMode.AAPT &&
AndroidRootUtil.getManifestFile(myFacet) != myEvent.getFile()) {
final HashSet<ResourceEntry> resourceSet = new HashSet<ResourceEntry>();
final HashSet<ResourceEntry> resourceSet = new HashSet<ResourceEntry>();
DumbService.getInstance(myFacet.getModule().getProject()).waitForSmartMode();
DumbService.getInstance(myFacet.getModule().getProject()).waitForSmartMode();
AndroidCompileUtil.collectAllResources(myFacet, resourceSet);
AndroidCompileUtil.collectAllResources(myFacet, resourceSet);
synchronized (RESOURCES_SET_LOCK) {
if (resourceSet.equals(myResourceSet) &&
!myFacet.areSourcesGeneratedWithErrors(AndroidAutogeneratorMode.AAPT)) {
return;
synchronized (RESOURCES_SET_LOCK) {
if (resourceSet.equals(myResourceSet) &&
!myFacet.areSourcesGeneratedWithErrors(AndroidAutogeneratorMode.AAPT)) {
return;
}
myResourceSet = resourceSet;
}
myResourceSet = resourceSet;
}
AndroidCompileUtil.generate(myFacet.getModule(), autogenerationMode, true);
}
AndroidCompileUtil.generate(myFacet.getModule(), autogenerationMode, true);
}
@Nullable
private AndroidAutogeneratorMode computeCompilerToRunAndInvalidateLocalAttributesMap() {
@NotNull
private List<AndroidAutogeneratorMode> computeCompilersToRunAndInvalidateLocalAttributesMap() {
if (myFacet.isDisposed()) {
return null;
return Collections.emptyList();
}
final Module myModule = myFacet.getModule();
final Project project = myModule.getProject();
if (project.isDisposed()) {
return null;
return Collections.emptyList();
}
final VirtualFile file = myEvent.getFile();
final Module module = ModuleUtil.findModuleForFile(file, project);
if (module != myModule) {
return null;
return Collections.emptyList();
}
VirtualFile parent = myEvent.getParent();
if (parent == null) {
return null;
return Collections.emptyList();
}
final VirtualFile gp = parent.getParent();
@@ -207,10 +212,13 @@ class AndroidResourceFilesListener extends VirtualFileAdapter {
ResourceFolderType.VALUES.getName().equals(AndroidCommonUtils.getResourceTypeByDirName(parent.getName()))) {
myFacet.getLocalResourceManager().invalidateAttributeDefinitions();
}
final VirtualFile manifestFile = AndroidRootUtil.getManifestFile(myFacet);
final List<AndroidAutogeneratorMode> modes = new ArrayList<AndroidAutogeneratorMode>();
if (AndroidAptCompiler.isToCompileModule(module, myFacet.getConfiguration()) &&
(myFacet.getConfiguration().REGENERATE_R_JAVA && (gp == resourceDir ||
AndroidRootUtil.getManifestFile(myFacet) == file))) {
manifestFile == file))) {
final Manifest manifest = myFacet.getManifest();
final String aPackage = manifest != null ? manifest.getPackage().getValue() : null;
@@ -219,23 +227,27 @@ class AndroidResourceFilesListener extends VirtualFileAdapter {
AndroidCompileUtil.removeDuplicatingClasses(myModule, myCachedPackage, AndroidUtils.R_CLASS_NAME, null, aptGenDirPath);
}
myCachedPackage = aPackage;
return AndroidAutogeneratorMode.AAPT;
modes.add(AndroidAutogeneratorMode.AAPT);
}
if (myFacet.getConfiguration().REGENERATE_JAVA_BY_AIDL && file.getFileType() == AndroidIdlFileType.ourFileType) {
VirtualFile sourceRoot = findSourceRoot(myModule, file);
if (sourceRoot != null && AndroidRootUtil.getAidlGenDir(myFacet) != sourceRoot) {
return AndroidAutogeneratorMode.AIDL;
modes.add(AndroidAutogeneratorMode.AIDL);
}
}
if (file.getFileType() == AndroidRenderscriptFileType.INSTANCE) {
final VirtualFile sourceRoot = findSourceRoot(myModule, file);
if (sourceRoot != null && AndroidRootUtil.getRenderscriptGenDir(myFacet) != sourceRoot) {
return AndroidAutogeneratorMode.RENDERSCRIPT;
modes.add(AndroidAutogeneratorMode.RENDERSCRIPT);
}
}
return null;
if (manifestFile == file) {
modes.add(AndroidAutogeneratorMode.BUILDCONFIG);
}
return modes;
}
@Override
@@ -178,6 +178,12 @@ public class AndroidRootUtil {
return path != null ? LocalFileSystem.getInstance().findFileByPath(path) : null;
}
@Nullable
public static VirtualFile getBuildconfigGenDir(@NotNull AndroidFacet facet) {
final String path = getBuildconfigGenSourceRootPath(facet);
return path != null ? LocalFileSystem.getInstance().findFileByPath(path) : null;
}
// works even if there is no Android facet in a module
@Nullable
@@ -355,14 +361,25 @@ public class AndroidRootUtil {
@Nullable
public static String getRenderscriptGenSourceRootPath(@NotNull AndroidFacet facet) {
// todo: return correct path for mavenized module
// todo: return correct path for mavenized module when it'll be supported
return getDefaultGenSourceRoot(facet);
}
@Nullable
public static String getBuildconfigGenSourceRootPath(@NotNull AndroidFacet facet) {
// todo: return correct path for mavenized module when it'll be supported
return getDefaultGenSourceRoot(facet);
}
@Nullable
private static String getDefaultGenSourceRoot(AndroidFacet facet) {
final VirtualFile mainContentRoot = getMainContentRoot(facet);
final String moduleDirPath = mainContentRoot != null ? mainContentRoot.getPath() : null;
return moduleDirPath != null
? moduleDirPath + '/' + SdkConstants.FD_GEN_SOURCES
: null;
}
@Nullable
public static VirtualFile getMainContentRoot(@NotNull AndroidFacet facet) {
final Module module = facet.getModule();