mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
android-jps: fix some bugs, support aapt and dex compilation
This commit is contained in:
@@ -14,7 +14,7 @@ import java.util.Properties;
|
||||
* @author Eugene Zhuravlev
|
||||
* Date: 10/12/11
|
||||
*/
|
||||
class FormsParsing {
|
||||
public class FormsParsing {
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.java.FormsParsing");
|
||||
private static final String FORM_TAG = "form";
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<orderEntry type="module" module-name="android-rt" />
|
||||
<orderEntry type="module" module-name="jps-builders" />
|
||||
<orderEntry type="module" module-name="util" />
|
||||
<orderEntry type="library" name="NanoXML" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ public class AndroidBuilderService extends BuilderService {
|
||||
@NotNull
|
||||
@Override
|
||||
public List<? extends ModuleLevelBuilder> createModuleLevelBuilders(ExecutorService executorService) {
|
||||
return Arrays.asList(new AndroidSourceGeneratingBuilder());
|
||||
return Arrays.asList(new AndroidSourceGeneratingBuilder(),
|
||||
new AndroidDexBuilder());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
package org.jetbrains.jps.android;
|
||||
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.jetbrains.android.compiler.tools.AndroidDxRunner;
|
||||
import org.jetbrains.android.util.AndroidCommonUtils;
|
||||
import org.jetbrains.android.util.AndroidCompilerMessageKind;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.Module;
|
||||
import org.jetbrains.jps.ModuleChunk;
|
||||
import org.jetbrains.jps.ProjectPaths;
|
||||
import org.jetbrains.jps.Sdk;
|
||||
import org.jetbrains.jps.incremental.*;
|
||||
import org.jetbrains.jps.incremental.messages.BuildMessage;
|
||||
import org.jetbrains.jps.incremental.messages.CompilerMessage;
|
||||
import org.jetbrains.jps.incremental.messages.ProgressMessage;
|
||||
import org.jetbrains.jps.server.ClasspathBootstrap;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
|
||||
// todo: change output folders
|
||||
// todo: save validity state
|
||||
|
||||
public class AndroidDexBuilder extends ModuleLevelBuilder {
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.android.AndroidDexBuilder");
|
||||
|
||||
@NonNls private static final String BUILDER_NAME = "android-dex";
|
||||
|
||||
protected AndroidDexBuilder() {
|
||||
super(BuilderCategory.CLASS_POST_PROCESSOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExitCode build(CompileContext context, ModuleChunk chunk) throws ProjectBuildException {
|
||||
if (context.isCompilingTests() || !AndroidJpsUtil.containsAndroidFacet(chunk)) {
|
||||
return ModuleLevelBuilder.ExitCode.OK;
|
||||
}
|
||||
context.processMessage(new ProgressMessage("Executing DEX"));
|
||||
|
||||
try {
|
||||
return doBuild(context, chunk);
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
|
||||
if (message == null) {
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
e.printStackTrace(new PrintStream(out));
|
||||
message = "Internal error: \n" + out.toString();
|
||||
}
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, message));
|
||||
throw new ProjectBuildException(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static ExitCode doBuild(CompileContext context, ModuleChunk chunk) {
|
||||
boolean success = true;
|
||||
|
||||
for (Module module : chunk.getModules()) {
|
||||
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
|
||||
if (facet == null || facet.getLibrary()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Sdk sdk = module.getSdk();
|
||||
if (!(sdk instanceof AndroidSdk)) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
|
||||
"Android SDK is not specified for module " + module.getName()));
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
final AndroidSdk androidSdk = (AndroidSdk)sdk;
|
||||
|
||||
final IAndroidTarget target = AndroidJpsUtil.parseAndroidTarget(androidSdk, context, BUILDER_NAME);
|
||||
if (target == null) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
|
||||
"Android SDK is invalid or not specified for module " + module.getName()));
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProjectPaths projectPaths = context.getProjectPaths();
|
||||
final File dexOutputDir = AndroidJpsUtil.getOutputDirectoryForPackagedFiles(projectPaths, module);
|
||||
|
||||
if (dexOutputDir == null) {
|
||||
context.processMessage(
|
||||
new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Output directory is not specified for module " + module.getName()));
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// todo: support proguard
|
||||
|
||||
final File classesDir = projectPaths.getModuleOutputDir(module, false);
|
||||
|
||||
if (classesDir == null || !classesDir.isDirectory()) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.INFO, "Dex won't be launched for module " +
|
||||
module.getName() +
|
||||
" because it doesn't contain compiled files"));
|
||||
continue;
|
||||
}
|
||||
|
||||
final Set<String> fileSet = new HashSet<String>();
|
||||
AndroidJpsUtil.addSubdirectories(classesDir, fileSet);
|
||||
fileSet.addAll(AndroidJpsUtil.getExternalLibraries(projectPaths, module));
|
||||
|
||||
for (String filePath : AndroidJpsUtil.getClassdirsOfDependentModules(projectPaths, module)) {
|
||||
if (!classesDir.getPath().equals(filePath)) {
|
||||
fileSet.add(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (facet.isLibrary()) {
|
||||
final File testsClassDir = projectPaths.getModuleOutputDir(module, true);
|
||||
|
||||
if (testsClassDir != null && testsClassDir.isDirectory()) {
|
||||
AndroidJpsUtil.addSubdirectories(testsClassDir, fileSet);
|
||||
}
|
||||
}
|
||||
|
||||
final String[] files = new String[fileSet.size()];
|
||||
int i = 0;
|
||||
for (String filePath : fileSet) {
|
||||
files[i++] = FileUtil.toSystemDependentName(filePath);
|
||||
}
|
||||
|
||||
if (!runDex(androidSdk, target, dexOutputDir.getPath(), files, context)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
return success ? ExitCode.OK : ExitCode.ABORT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return BUILDER_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Android Dex Builder";
|
||||
}
|
||||
|
||||
public static boolean runDex(@NotNull AndroidSdk sdk,
|
||||
@NotNull IAndroidTarget target,
|
||||
@NotNull String outputDir,
|
||||
@NotNull String[] compileTargets,
|
||||
@NotNull CompileContext context) {
|
||||
@SuppressWarnings("deprecation")
|
||||
final String dxJarPath = FileUtil.toSystemDependentName(target.getPath(IAndroidTarget.DX_JAR));
|
||||
|
||||
final File dxJar = new File(dxJarPath);
|
||||
if (!dxJar.isFile()) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Cannot find file " + dxJarPath));
|
||||
return false;
|
||||
}
|
||||
|
||||
final String outFilePath = outputDir + File.separatorChar + AndroidCommonUtils.CLASSES_FILE_NAME;
|
||||
|
||||
final List<String> programParamList = new ArrayList<String>();
|
||||
programParamList.add(dxJarPath);
|
||||
programParamList.add(outFilePath);
|
||||
programParamList.addAll(Arrays.asList(compileTargets));
|
||||
programParamList.add("--exclude");
|
||||
|
||||
final List<String> classPath = new ArrayList<String>();
|
||||
classPath.add(ClasspathBootstrap.getResourcePath(AndroidDxRunner.class).getPath());
|
||||
classPath.add(ClasspathBootstrap.getResourcePath(FileUtil.class).getPath());
|
||||
|
||||
if (!new File(outFilePath).delete()) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.WARNING, "Cannot delete file " + outFilePath));
|
||||
}
|
||||
|
||||
// todo: pass additional vm params and max heap size from settings
|
||||
|
||||
final List<String> commandLine = ExternalProcessUtil
|
||||
.buildJavaCommandLine(sdk.getJavaExecutable(), AndroidDxRunner.class.getName(), Collections.<String>emptyList(), classPath,
|
||||
Arrays.asList("-Xmx1024M"), programParamList);
|
||||
|
||||
LOG.info(AndroidCommonUtils.command2string(commandLine));
|
||||
|
||||
try {
|
||||
final Process process = Runtime.getRuntime().exec(ArrayUtil.toStringArray(commandLine));
|
||||
|
||||
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, outFilePath, messages);
|
||||
|
||||
AndroidJpsUtil.addMessages(context, messages, null, BUILDER_NAME);
|
||||
|
||||
return messages.get(AndroidCompilerMessageKind.ERROR).size() == 0;
|
||||
}
|
||||
catch (IOException e) {
|
||||
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,20 +5,46 @@ import org.jetbrains.jps.Module
|
||||
import org.jetbrains.jps.idea.Facet
|
||||
|
||||
/**
|
||||
* @author nik
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
class AndroidFacet extends Facet {
|
||||
final Module module
|
||||
final String resFolderRelativePath
|
||||
|
||||
AndroidFacet(Module module, String name, String resFolderRelativePath) {
|
||||
boolean library;
|
||||
|
||||
String resFolderRelativePath
|
||||
String resFolderForCompilationRelativePath
|
||||
boolean useCustomResFolderForCompilation;
|
||||
|
||||
String manifestRelativePath;
|
||||
String manifestForCompilationRelativePath;
|
||||
boolean useCustomManifestForCompilation;
|
||||
boolean packTestCode;
|
||||
|
||||
AndroidFacet(Module module, String name) {
|
||||
this.module = module
|
||||
this.name = name;
|
||||
this.resFolderRelativePath = resFolderRelativePath
|
||||
}
|
||||
|
||||
File getResourceDir() {
|
||||
return findFileByRelativeModulePath(resFolderRelativePath, true)
|
||||
File getResourceDir() throws IOException {
|
||||
def resDir = findFileByRelativeModulePath(resFolderRelativePath, true)
|
||||
return resDir != null ? resDir.getCanonicalFile() : null;
|
||||
}
|
||||
|
||||
File getResourceDirForCompilation() throws IOException {
|
||||
def resDir = findFileByRelativeModulePath(resFolderForCompilationRelativePath, false)
|
||||
return resDir != null ? resDir.getCanonicalFile() : null;
|
||||
}
|
||||
|
||||
File getManifestFile() throws IOException {
|
||||
def manifestFile = findFileByRelativeModulePath(manifestRelativePath, true);
|
||||
return manifestFile != null ? manifestFile.getCanonicalFile() : null;
|
||||
}
|
||||
|
||||
File getManifestFileForCompilation() throws IOException {
|
||||
def manifestFile = findFileByRelativeModulePath(manifestForCompilationRelativePath, false);
|
||||
return manifestFile != null ? manifestFile.getCanonicalFile() : null;
|
||||
}
|
||||
|
||||
private File findFileByRelativeModulePath(String relativePath, boolean lookInContentRoot) {
|
||||
@@ -32,7 +58,7 @@ class AndroidFacet extends Facet {
|
||||
}
|
||||
|
||||
if (lookInContentRoot) {
|
||||
module.contentRoots.each {
|
||||
module.contentRoots.each {String contentRoot ->
|
||||
def absPath = FileUtil.toSystemIndependentName(contentRoot + relativePath)
|
||||
def f = new File(absPath)
|
||||
|
||||
|
||||
@@ -17,6 +17,38 @@ class AndroidFacetType extends FacetTypeService {
|
||||
|
||||
@Override
|
||||
Facet createFacet(Module module, String name, Node facetConfiguration, MacroExpander macroExpander) {
|
||||
return new AndroidFacet(module, name, "");
|
||||
def facet = new AndroidFacet(module, name);
|
||||
|
||||
facetConfiguration.each {Node child ->
|
||||
String value = child."@value"
|
||||
|
||||
switch (child."@name") {
|
||||
case "RES_FOLDER_RELATIVE_PATH":
|
||||
facet.resFolderRelativePath = value
|
||||
break
|
||||
case "USE_CUSTOM_APK_RESOURCE_FOLDER":
|
||||
facet.useCustomResFolderForCompilation = Boolean.parseBoolean(value)
|
||||
break
|
||||
case "CUSTOM_APK_RESOURCE_FOLDER":
|
||||
facet.resFolderForCompilationRelativePath = value
|
||||
break
|
||||
case "LIBRARY_PROJECT":
|
||||
facet.library = Boolean.parseBoolean(value)
|
||||
break
|
||||
case "MANIFEST_FILE_RELATIVE_PATH":
|
||||
facet.manifestRelativePath = value
|
||||
break
|
||||
case "USE_CUSTOM_COMPILER_MANIFEST":
|
||||
facet.useCustomManifestForCompilation = Boolean.parseBoolean(value)
|
||||
break
|
||||
case "CUSTOM_COMPILER_MANIFEST":
|
||||
facet.manifestForCompilationRelativePath = value
|
||||
break
|
||||
case "PACK_TEST_CODE":
|
||||
facet.packTestCode = Boolean.parseBoolean(value)
|
||||
break
|
||||
}
|
||||
}
|
||||
return facet;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
package org.jetbrains.jps.android;
|
||||
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
import com.android.sdklib.SdkManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.jetbrains.android.sdk.MessageBuildingSdkLog;
|
||||
import org.jetbrains.android.util.AndroidCommonUtils;
|
||||
import org.jetbrains.android.util.AndroidCompilerMessageKind;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.*;
|
||||
import org.jetbrains.jps.idea.Facet;
|
||||
import org.jetbrains.jps.incremental.CompileContext;
|
||||
import org.jetbrains.jps.incremental.messages.BuildMessage;
|
||||
import org.jetbrains.jps.incremental.messages.CompilerMessage;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
class AndroidJpsUtil {
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.android.AndroidJpsUtil");
|
||||
|
||||
private AndroidJpsUtil() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static AndroidFacet getFacet(@NotNull Module module) {
|
||||
AndroidFacet androidFacet = null;
|
||||
|
||||
for (Facet facet : module.getFacets().values()) {
|
||||
if (facet instanceof AndroidFacet) {
|
||||
androidFacet = (AndroidFacet)facet;
|
||||
}
|
||||
}
|
||||
return androidFacet;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String[] toPaths(@NotNull File[] files) {
|
||||
final String[] result = new String[files.length];
|
||||
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = files[i].getPath();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getOutputDirectoryForPackagedFiles(@NotNull ProjectPaths paths, @NotNull Module module) {
|
||||
// todo: return build directory for mavenized modules to place .dex and .apk files into target dir (not target/classes)
|
||||
return paths.getModuleOutputDir(module, false);
|
||||
}
|
||||
|
||||
public static void addSubdirectories(@NotNull File baseDir, @NotNull Collection<String> result) {
|
||||
// only include files inside packages
|
||||
final File[] children = baseDir.listFiles();
|
||||
|
||||
if (children != null) {
|
||||
for (File child : children) {
|
||||
if (child.isDirectory()) {
|
||||
result.add(child.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Set<String> getExternalLibraries(@NotNull ProjectPaths paths, @NotNull Module module) {
|
||||
final Set<String> result = new HashSet<String>();
|
||||
fillClasspath(paths, module, null, result, new HashSet<String>(), false);
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Set<String> getClassdirsOfDependentModules(@NotNull ProjectPaths paths, @NotNull Module module) {
|
||||
final Set<String> result = new HashSet<String>();
|
||||
fillClasspath(paths, module, result, null, new HashSet<String>(), false);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void fillClasspath(@NotNull ProjectPaths paths,
|
||||
@NotNull final Module module,
|
||||
@Nullable final Set<String> outputDirs,
|
||||
@Nullable final Set<String> libraries,
|
||||
@NotNull final Set<String> visitedModules,
|
||||
final boolean exportedLibrariesOnly) {
|
||||
if (!visitedModules.add(module.getName())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (libraries != null) {
|
||||
for (ClasspathItem item : module.getClasspath(ClasspathKind.PRODUCTION_COMPILE, exportedLibrariesOnly)) {
|
||||
if (item instanceof Library && !(item instanceof Sdk)) {
|
||||
for (String filePath : item.getClasspathRoots(ClasspathKind.PRODUCTION_COMPILE)) {
|
||||
final File file = new File(filePath);
|
||||
|
||||
if (file.exists()) {
|
||||
collectClassFilesAndJars(filePath, libraries);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ClasspathItem item : module.getClasspath(ClasspathKind.PRODUCTION_COMPILE, false)) {
|
||||
if (item instanceof Module) {
|
||||
final Module depModule = (Module)item;
|
||||
final AndroidFacet depFacet = getFacet(depModule);
|
||||
final boolean depLibrary = depFacet != null && depFacet.isLibrary();
|
||||
final File depClassDir = paths.getModuleOutputDir(depModule, false);
|
||||
|
||||
if (outputDirs != null && depClassDir != null) {
|
||||
if (depLibrary) {
|
||||
final File packagedClassesJar = new File(depClassDir, AndroidCommonUtils.CLASSES_JAR_FILE_NAME);
|
||||
|
||||
if (packagedClassesJar.isDirectory()) {
|
||||
outputDirs.add(packagedClassesJar.getPath());
|
||||
}
|
||||
}
|
||||
else if (depFacet == null && depClassDir.isDirectory()) {
|
||||
// do not support android-app->android-app compile dependencies
|
||||
outputDirs.add(depClassDir.getPath());
|
||||
}
|
||||
}
|
||||
fillClasspath(paths, depModule, outputDirs, libraries, visitedModules, !depLibrary || exportedLibrariesOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void collectClassFilesAndJars(@NotNull String root,
|
||||
@NotNull final Set<String> result) {
|
||||
FileUtil.processFilesRecursively(new File(root), new Processor<File>() {
|
||||
@Override
|
||||
public boolean process(File file) {
|
||||
if (file.isFile()) {
|
||||
final String ext = FileUtil.getExtension(file.getName());
|
||||
|
||||
if ("jar".equals(ext) || "class".equals(ext)) {
|
||||
result.add(file.getPath());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static IAndroidTarget parseAndroidTarget(@NotNull AndroidSdk sdk, @NotNull CompileContext context, @NotNull String builderName) {
|
||||
final String targetHashString = sdk.getBuildTargetHashString();
|
||||
if (targetHashString == null) {
|
||||
context.processMessage(new CompilerMessage(builderName, BuildMessage.Kind.ERROR,
|
||||
"Cannot parse SDK " + sdk.getName() + ": build target is not specified"));
|
||||
return null;
|
||||
}
|
||||
|
||||
final MessageBuildingSdkLog log = new MessageBuildingSdkLog();
|
||||
final SdkManager manager = AndroidCommonUtils.createSdkManager(sdk.getSdkPath(), log);
|
||||
|
||||
if (manager == null) {
|
||||
final String message = log.getErrorMessage();
|
||||
context.processMessage(new CompilerMessage(builderName, BuildMessage.Kind.ERROR,
|
||||
"Android SDK is parsed incorrectly." +
|
||||
(message.length() > 0 ? " Parsing log:\n" + message : "")));
|
||||
return null;
|
||||
}
|
||||
|
||||
final IAndroidTarget target = manager.getTargetFromHashString(targetHashString);
|
||||
if (target == null) {
|
||||
context.processMessage(new CompilerMessage(builderName, BuildMessage.Kind.ERROR,
|
||||
"Cannot parse SDK '" + sdk.getName() + "': unknown target " + targetHashString));
|
||||
return null;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
public static void addMessages(@NotNull CompileContext context,
|
||||
@NotNull Map<AndroidCompilerMessageKind, List<String>> messages,
|
||||
@Nullable String sourcePath,
|
||||
@NotNull String builderName) {
|
||||
for (Map.Entry<AndroidCompilerMessageKind, List<String>> entry : messages.entrySet()) {
|
||||
final AndroidCompilerMessageKind kind = entry.getKey();
|
||||
final BuildMessage.Kind buildMessageKind = toBuildMessageKind(kind);
|
||||
|
||||
if (buildMessageKind == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (String message : entry.getValue()) {
|
||||
context.processMessage(new CompilerMessage(builderName, buildMessageKind, message, sourcePath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static BuildMessage.Kind toBuildMessageKind(@NotNull AndroidCompilerMessageKind kind) {
|
||||
switch (kind) {
|
||||
case ERROR:
|
||||
return BuildMessage.Kind.ERROR;
|
||||
case INFORMATION:
|
||||
return BuildMessage.Kind.INFO;
|
||||
case WARNING:
|
||||
return BuildMessage.Kind.WARNING;
|
||||
default:
|
||||
LOG.error("unknown AndroidCompilerMessageKind object " + kind);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void reportExceptionError(@NotNull CompileContext context,
|
||||
@Nullable String filePath,
|
||||
@NotNull Exception exception,
|
||||
@NotNull String builderName) {
|
||||
final String message = exception.getMessage();
|
||||
|
||||
if (message != null) {
|
||||
context.processMessage(new CompilerMessage(builderName, BuildMessage.Kind.ERROR, message, filePath));
|
||||
LOG.debug(exception);
|
||||
}
|
||||
else {
|
||||
context.processMessage(new CompilerMessage(builderName, exception));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean containsAndroidFacet(@NotNull ModuleChunk chunk) {
|
||||
for (Module module : chunk.getModules()) {
|
||||
if (getFacet(module) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package org.jetbrains.jps.android;
|
||||
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.incremental.Paths;
|
||||
import org.jetbrains.jps.incremental.storage.AbstractStateStorage;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidResourceNamesStateStorage extends AbstractStateStorage<String, AndroidResourceNamesValidityState> {
|
||||
|
||||
@NonNls private static final String ANDROID_STORAGE_DIR = "android_resource_names";
|
||||
@NonNls private static final String RESOURCE_NAMES_STORAGE = "resource_names";
|
||||
|
||||
public AndroidResourceNamesStateStorage(@NotNull String projectName) throws IOException {
|
||||
super(getStorageFile(projectName), new EnumeratorStringDescriptor(), new MyDataExternalizer());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static File getStorageFile(String projectName) {
|
||||
return new File(new File(Paths.getDataStorageRoot(projectName), ANDROID_STORAGE_DIR), RESOURCE_NAMES_STORAGE);
|
||||
}
|
||||
|
||||
private static class MyDataExternalizer implements DataExternalizer<AndroidResourceNamesValidityState> {
|
||||
|
||||
@Override
|
||||
public void save(DataOutput out, AndroidResourceNamesValidityState value) throws IOException {
|
||||
value.save(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidResourceNamesValidityState read(DataInput in) throws IOException {
|
||||
return new AndroidResourceNamesValidityState(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package org.jetbrains.jps.android;
|
||||
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.jetbrains.android.util.ResourceEntry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.incremental.storage.ValidityState;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidResourceNamesValidityState implements ValidityState {
|
||||
private final Set<ResourceEntry> myResourcesSet;
|
||||
private final String myPackageName;
|
||||
|
||||
public AndroidResourceNamesValidityState(@NotNull Collection<ResourceEntry> resourcesSet, @NotNull String packageName) {
|
||||
myResourcesSet = new HashSet<ResourceEntry>(resourcesSet);
|
||||
myPackageName = packageName;
|
||||
}
|
||||
|
||||
public AndroidResourceNamesValidityState(@NotNull DataInput in) throws IOException {
|
||||
myPackageName = in.readUTF();
|
||||
myResourcesSet = new HashSet<ResourceEntry>();
|
||||
|
||||
final int resourceCount = in.readInt();
|
||||
|
||||
for (int i = 0; i < resourceCount; i++) {
|
||||
final String resType = in.readUTF();
|
||||
final String resName = in.readUTF();
|
||||
myResourcesSet.add(new ResourceEntry(resType, resName));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equalsTo(ValidityState otherState) {
|
||||
if (!(otherState instanceof AndroidResourceNamesValidityState)) {
|
||||
return false;
|
||||
}
|
||||
final AndroidResourceNamesValidityState otherAndroidState = (AndroidResourceNamesValidityState)otherState;
|
||||
return otherAndroidState.myPackageName.equals(myPackageName) &&
|
||||
otherAndroidState.myResourcesSet.equals(myResourcesSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(DataOutput out) throws IOException {
|
||||
out.writeUTF(myPackageName);
|
||||
out.writeInt(myResourcesSet.size());
|
||||
|
||||
for (ResourceEntry resourceEntry : myResourcesSet) {
|
||||
out.writeUTF(resourceEntry.getType());
|
||||
out.writeUTF(resourceEntry.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
+246
-145
@@ -1,42 +1,46 @@
|
||||
package org.jetbrains.jps.android;
|
||||
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
import com.android.sdklib.SdkManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.jetbrains.android.compiler.tools.AndroidApt;
|
||||
import org.jetbrains.android.compiler.tools.AndroidIdl;
|
||||
import org.jetbrains.android.compiler.tools.AndroidRenderscript;
|
||||
import org.jetbrains.android.sdk.MessageBuildingSdkLog;
|
||||
import org.jetbrains.android.util.AndroidCommonUtils;
|
||||
import org.jetbrains.android.util.AndroidCompilerMessageKind;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.*;
|
||||
import org.jetbrains.jps.idea.Facet;
|
||||
import org.jetbrains.jps.incremental.*;
|
||||
import org.jetbrains.jps.incremental.java.FormsParsing;
|
||||
import org.jetbrains.jps.incremental.java.JavaBuilder;
|
||||
import org.jetbrains.jps.incremental.messages.BuildMessage;
|
||||
import org.jetbrains.jps.incremental.messages.CompilerMessage;
|
||||
import org.jetbrains.jps.incremental.messages.ProgressMessage;
|
||||
import org.jetbrains.jps.incremental.storage.SourceToOutputMapping;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
|
||||
// todo: change output folders
|
||||
|
||||
public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.android.AndroidSourceGeneratingBuilder");
|
||||
|
||||
@NonNls private static final String BUILDER_NAME = "android-source-generator";
|
||||
@NonNls private static final String AIDL_EXTENSION = "aidl";
|
||||
@NonNls private static final String RENDERSCRIPT_EXTENSION = "rs";
|
||||
@NonNls private static final String MANIFEST_TAG = "manifest";
|
||||
@NonNls private static final String PACKAGE_MANIFEST_ATTRIBUTE = "package";
|
||||
|
||||
public AndroidSourceGeneratingBuilder() {
|
||||
super(BuilderCategory.SOURCE_GENERATOR);
|
||||
@@ -49,16 +53,13 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
|
||||
@Override
|
||||
public ModuleLevelBuilder.ExitCode build(CompileContext context, ModuleChunk chunk) throws ProjectBuildException {
|
||||
if (context.isCompilingTests()) {
|
||||
if (context.isCompilingTests() || !AndroidJpsUtil.containsAndroidFacet(chunk)) {
|
||||
return ModuleLevelBuilder.ExitCode.OK;
|
||||
}
|
||||
|
||||
try {
|
||||
return doBuild(context, chunk);
|
||||
}
|
||||
catch (ProjectBuildException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
|
||||
@@ -73,15 +74,14 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
private static ModuleLevelBuilder.ExitCode doBuild(CompileContext context, ModuleChunk chunk) throws Exception {
|
||||
final Map<File, AndroidFacet> idlFilesToCompile = new HashMap<File, AndroidFacet>();
|
||||
final Map<File, AndroidFacet> rsFilesToCompile = new HashMap<File, AndroidFacet>();
|
||||
final Set<Module> modules = new HashSet<Module>();
|
||||
private static ModuleLevelBuilder.ExitCode doBuild(CompileContext context, ModuleChunk chunk) throws IOException {
|
||||
final Map<File, Module> idlFilesToCompile = new HashMap<File, Module>();
|
||||
final Map<File, Module> rsFilesToCompile = new HashMap<File, Module>();
|
||||
|
||||
context.processFilesToRecompile(chunk, new FileProcessor() {
|
||||
@Override
|
||||
public boolean apply(Module module, File file, String sourceRoot) throws IOException {
|
||||
final AndroidFacet facet = getFacet(module);
|
||||
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
|
||||
|
||||
if (facet == null) {
|
||||
return true;
|
||||
@@ -89,45 +89,48 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
final String ext = FileUtil.getExtension(file.getName());
|
||||
|
||||
if (AIDL_EXTENSION.equals(ext)) {
|
||||
idlFilesToCompile.put(file, facet);
|
||||
modules.add(facet.getModule());
|
||||
idlFilesToCompile.put(file, facet.getModule());
|
||||
}
|
||||
else if (RENDERSCRIPT_EXTENSION.equals(ext)) {
|
||||
rsFilesToCompile.put(file, facet);
|
||||
modules.add(facet.getModule());
|
||||
rsFilesToCompile.put(file, facet.getModule());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final Map<Module, MyModuleData> moduleDataMap = computeModuleDatas(modules, context);
|
||||
final Map<Module, MyModuleData> moduleDataMap = computeModuleDatas(chunk.getModules(), context);
|
||||
|
||||
if (moduleDataMap == null) {
|
||||
return ExitCode.OK;
|
||||
if (moduleDataMap == null || moduleDataMap.size() == 0) {
|
||||
return ExitCode.ABORT;
|
||||
}
|
||||
|
||||
boolean success = true;
|
||||
|
||||
if (!runAidlCompiler(context, idlFilesToCompile, moduleDataMap)) {
|
||||
return ExitCode.OK;
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (!runRenderscriptCompiler(context, rsFilesToCompile, moduleDataMap)) {
|
||||
return ExitCode.OK;
|
||||
success = false;
|
||||
}
|
||||
|
||||
return ExitCode.OK;
|
||||
if (!runAaptCompiler(context, moduleDataMap)) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success ? ExitCode.OK : ExitCode.ABORT;
|
||||
}
|
||||
|
||||
private static boolean runAidlCompiler(@NotNull final CompileContext context,
|
||||
@NotNull Map<File, AndroidFacet> files,
|
||||
@NotNull Map<File, Module> files,
|
||||
@NotNull Map<Module, MyModuleData> moduleDataMap) {
|
||||
context.processMessage(new ProgressMessage("Processing AIDL files..."));
|
||||
|
||||
|
||||
boolean success = true;
|
||||
|
||||
for (Map.Entry<File, AndroidFacet> entry : files.entrySet()) {
|
||||
for (Map.Entry<File, Module> entry : files.entrySet()) {
|
||||
final File file = entry.getKey();
|
||||
final Module module = entry.getValue().getModule();
|
||||
final Module module = entry.getValue();
|
||||
final String filePath = file.getPath();
|
||||
|
||||
final MyModuleData moduleData = moduleDataMap.get(module);
|
||||
@@ -143,7 +146,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
|
||||
try {
|
||||
final File[] sourceRoots = getSourceRootsForModuleAndDependencies(module);
|
||||
final String[] sourceRootPaths = toPaths(sourceRoots);
|
||||
final String[] sourceRootPaths = AndroidJpsUtil.toPaths(sourceRoots);
|
||||
final String packageName = computePackageForFile(context, file);
|
||||
|
||||
if (packageName == null) {
|
||||
@@ -159,19 +162,21 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
final Map<AndroidCompilerMessageKind, List<String>> messages =
|
||||
AndroidIdl.execute(target, filePath, outputFilePath, sourceRootPaths);
|
||||
|
||||
addMessages(context, messages, filePath);
|
||||
AndroidJpsUtil.addMessages(context, messages, filePath, BUILDER_NAME);
|
||||
|
||||
if (messages.get(AndroidCompilerMessageKind.ERROR).size() > 0) {
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
final String moduleName = getCannonicalModuleName(module);
|
||||
final SourceToOutputMapping sourceToOutputMap = context.getDataManager().getSourceToOutputMap(moduleName, false);
|
||||
sourceToOutputMap.update(filePath, outputFilePath);
|
||||
|
||||
final String moduleName = getCannonicalModuleName(module);
|
||||
final SourceToOutputMapping sourceToOutputMap = context.getDataManager().getSourceToOutputMap(moduleName, false);
|
||||
sourceToOutputMap.update(filePath, outputFilePath);
|
||||
JavaBuilder.addTempSourcePathRoot(context, aidlOutputDirectory);
|
||||
}
|
||||
}
|
||||
catch (final IOException e) {
|
||||
reportExceptionError(context, filePath, e);
|
||||
AndroidJpsUtil.reportExceptionError(context, filePath, e, BUILDER_NAME);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
@@ -179,15 +184,15 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
}
|
||||
|
||||
private static boolean runRenderscriptCompiler(@NotNull final CompileContext context,
|
||||
@NotNull Map<File, AndroidFacet> files,
|
||||
@NotNull Map<File, Module> files,
|
||||
@NotNull Map<Module, MyModuleData> moduleDataMap) {
|
||||
context.processMessage(new ProgressMessage("Processing Renderscript files..."));
|
||||
|
||||
boolean success = true;
|
||||
|
||||
for (Map.Entry<File, AndroidFacet> entry : files.entrySet()) {
|
||||
for (Map.Entry<File, Module> entry : files.entrySet()) {
|
||||
final File file = entry.getKey();
|
||||
final Module module = entry.getValue().getModule();
|
||||
final Module module = entry.getValue();
|
||||
|
||||
final MyModuleData moduleData = moduleDataMap.get(module);
|
||||
if (!LOG.assertTrue(moduleData != null)) {
|
||||
@@ -214,7 +219,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
final Map<AndroidCompilerMessageKind, List<String>> messages =
|
||||
AndroidRenderscript.execute(sdkLocation, target, filePath, tmpOutputDirectory.getPath(), depFolderPath, rawDir.getPath());
|
||||
|
||||
addMessages(context, messages, filePath);
|
||||
AndroidJpsUtil.addMessages(context, messages, filePath, BUILDER_NAME);
|
||||
|
||||
if (messages.get(AndroidCompilerMessageKind.ERROR).size() > 0) {
|
||||
success = false;
|
||||
@@ -227,15 +232,17 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
if (bcFile.exists()) {
|
||||
newFiles.add(bcFile);
|
||||
}
|
||||
final List<String> newFilePaths = Arrays.asList(toPaths(newFiles.toArray(new File[newFiles.size()])));
|
||||
final List<String> newFilePaths = Arrays.asList(AndroidJpsUtil.toPaths(newFiles.toArray(new File[newFiles.size()])));
|
||||
|
||||
final String moduleName = getCannonicalModuleName(module);
|
||||
final SourceToOutputMapping sourceToOutputMap = context.getDataManager().getSourceToOutputMap(moduleName, false);
|
||||
sourceToOutputMap.update(filePath, newFilePaths);
|
||||
|
||||
JavaBuilder.addTempSourcePathRoot(context, rsOutputDirectory);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
reportExceptionError(context, filePath, e);
|
||||
AndroidJpsUtil.reportExceptionError(context, filePath, e, BUILDER_NAME);
|
||||
success = false;
|
||||
}
|
||||
finally {
|
||||
@@ -247,15 +254,182 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
return success;
|
||||
}
|
||||
|
||||
private static void reportExceptionError(@NotNull CompileContext context, @NotNull String filePath, @NotNull Exception exception) {
|
||||
final String message = exception.getMessage();
|
||||
// todo: save validity state
|
||||
private static boolean runAaptCompiler(@NotNull final CompileContext context,
|
||||
@NotNull Map<Module, MyModuleData> moduleDataMap) {
|
||||
context.processMessage(new ProgressMessage("Generating R.java and Manifest.java files"));
|
||||
|
||||
if (message != null) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, message, filePath));
|
||||
LOG.debug(exception);
|
||||
boolean success = true;
|
||||
|
||||
for (Map.Entry<Module, MyModuleData> entry : moduleDataMap.entrySet()) {
|
||||
final Module module = entry.getKey();
|
||||
final MyModuleData moduleData = entry.getValue();
|
||||
final AndroidFacet facet = moduleData.getFacet();
|
||||
|
||||
// todo: check if we need special strategy for maven apksources
|
||||
|
||||
final IAndroidTarget target = moduleData.getAndroidTarget();
|
||||
|
||||
try {
|
||||
final String[] resPaths = collectResourceDirs(facet);
|
||||
if (resPaths.length == 0) {
|
||||
// there is no resources in the module
|
||||
continue;
|
||||
}
|
||||
|
||||
final File manifestFile = getManifestFileForCompilationPath(facet);
|
||||
if (manifestFile == null || !manifestFile.exists()) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
|
||||
"AndroidManifest.xml file not found in the module " + module.getName()));
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
final String packageName = parsePackageNameFromManifestFile(manifestFile);
|
||||
if (packageName == null || packageName.length() == 0) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
|
||||
"Package is not specified in AndroidManifest.xml for module " + module.getName()));
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
final Set<String> depLibPackagesSet = getDepLibPackages(module);
|
||||
depLibPackagesSet.remove(packageName);
|
||||
|
||||
final File outputDirectory = moduleData.getOutputDirectory();
|
||||
final File aptOutputDirectory = new File(outputDirectory, "generated-aapt");
|
||||
|
||||
if (aptOutputDirectory.exists()) {
|
||||
// clear directory, because it may contain obsolete files (ex. if package name was changed)
|
||||
FileUtil.delete(aptOutputDirectory);
|
||||
}
|
||||
|
||||
final Map<AndroidCompilerMessageKind, List<String>> messages =
|
||||
AndroidApt.compile(target, -1, manifestFile.getPath(), packageName, aptOutputDirectory.getPath(), resPaths,
|
||||
ArrayUtil.toStringArray(depLibPackagesSet), facet.getLibrary());
|
||||
|
||||
AndroidJpsUtil.addMessages(context, messages, null, BUILDER_NAME);
|
||||
|
||||
if (messages.get(AndroidCompilerMessageKind.ERROR).size() > 0) {
|
||||
success = false;
|
||||
}
|
||||
else {
|
||||
JavaBuilder.addTempSourcePathRoot(context, aptOutputDirectory);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, exception));
|
||||
return success;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Set<String> getDepLibPackages(@NotNull Module module) throws IOException {
|
||||
final Set<String> result = new HashSet<String>();
|
||||
|
||||
for (AndroidFacet depFacet : getAllDependentAndroidLibraries(module)) {
|
||||
final File depManifestFile = getManifestFileForCompilationPath(depFacet);
|
||||
|
||||
if (depManifestFile != null) {
|
||||
final String packageName = parsePackageNameFromManifestFile(depManifestFile);
|
||||
|
||||
if (packageName != null) {
|
||||
result.add(packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String[] collectResourceDirs(@NotNull AndroidFacet facet) throws IOException {
|
||||
final List<String> result = new ArrayList<String>();
|
||||
|
||||
final File resDir = getResourceDirForCompilationPath(facet);
|
||||
if (resDir != null) {
|
||||
result.add(resDir.getPath());
|
||||
}
|
||||
|
||||
for (AndroidFacet depFacet : getAllDependentAndroidLibraries(facet.getModule())) {
|
||||
final File depResDir = getResourceDirForCompilationPath(depFacet);
|
||||
if (depResDir != null) {
|
||||
result.add(depResDir.getPath());
|
||||
}
|
||||
}
|
||||
return ArrayUtil.toStringArray(result);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static File getResourceDirForCompilationPath(@NotNull AndroidFacet facet) throws IOException {
|
||||
return facet.getUseCustomResFolderForCompilation()
|
||||
? facet.getResourceDirForCompilation()
|
||||
: facet.getResourceDir();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static File getManifestFileForCompilationPath(@NotNull AndroidFacet facet) throws IOException {
|
||||
return facet.getUseCustomManifestForCompilation()
|
||||
? facet.getManifestFileForCompilation()
|
||||
: facet.getManifestFile();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<AndroidFacet> getAllDependentAndroidLibraries(@NotNull Module module) {
|
||||
final List<AndroidFacet> result = new ArrayList<AndroidFacet>();
|
||||
collectDependentAndroidLibraries(module, result, new HashSet<String>());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String parsePackageNameFromManifestFile(@NotNull File manifestFile) throws IOException {
|
||||
final InputStream inputStream = new BufferedInputStream(new FileInputStream(manifestFile));
|
||||
try {
|
||||
final Ref<String> packageName = new Ref<String>(null);
|
||||
FormsParsing.parse(inputStream, new FormsParsing.IXMLBuilderAdapter() {
|
||||
boolean processingManifestTagAttrs = false;
|
||||
|
||||
@Override
|
||||
public void startElement(String name, String nsPrefix, String nsURI, String systemID, int lineNr)
|
||||
throws Exception {
|
||||
if (MANIFEST_TAG.equals(name)) {
|
||||
processingManifestTagAttrs = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAttribute(String key, String nsPrefix, String nsURI, String value, String type)
|
||||
throws Exception {
|
||||
if (value != null && PACKAGE_MANIFEST_ATTRIBUTE.equals(key)) {
|
||||
packageName.set(value.trim());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void elementAttributesProcessed(String name, String nsPrefix, String nsURI) throws Exception {
|
||||
stop();
|
||||
}
|
||||
});
|
||||
|
||||
return packageName.get();
|
||||
}
|
||||
finally {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void collectDependentAndroidLibraries(@NotNull Module module,
|
||||
@NotNull List<AndroidFacet> result,
|
||||
@NotNull Set<String> visitedSet) {
|
||||
for (ClasspathItem item : module.getClasspath(ClasspathKind.PRODUCTION_COMPILE, false)) {
|
||||
if (item instanceof Module) {
|
||||
final Module depModule = (Module)item;
|
||||
final AndroidFacet depFacet = AndroidJpsUtil.getFacet(depModule);
|
||||
|
||||
if (depFacet != null && depFacet.getLibrary() && visitedSet.add(module.getName())) {
|
||||
collectDependentAndroidLibraries(depModule, result, visitedSet);
|
||||
result.add(0, depFacet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,13 +460,16 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Map<Module, MyModuleData> computeModuleDatas(@NotNull Collection<Module> modules, @NotNull CompileContext context)
|
||||
throws Exception {
|
||||
private static Map<Module, MyModuleData> computeModuleDatas(@NotNull Collection<Module> modules, @NotNull CompileContext context) {
|
||||
final Map<Module, MyModuleData> moduleDataMap = new HashMap<Module, MyModuleData>();
|
||||
|
||||
boolean success = true;
|
||||
|
||||
for (Module module : modules) {
|
||||
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
|
||||
if (facet == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Sdk sdk = module.getSdk();
|
||||
if (!(sdk instanceof AndroidSdk)) {
|
||||
@@ -303,7 +480,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
}
|
||||
final AndroidSdk androidSdk = (AndroidSdk)sdk;
|
||||
|
||||
final IAndroidTarget target = parseAndroidTarget(androidSdk, context);
|
||||
final IAndroidTarget target = AndroidJpsUtil.parseAndroidTarget(androidSdk, context, BUILDER_NAME);
|
||||
if (target == null) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
|
||||
"Android SDK is invalid or not specified for module " + module.getName()));
|
||||
@@ -319,44 +496,12 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
continue;
|
||||
}
|
||||
|
||||
moduleDataMap.put(module, new MyModuleData(outputDir, androidSdk.getSdkPath(), target));
|
||||
moduleDataMap.put(module, new MyModuleData(outputDir, androidSdk.getSdkPath(), target, facet));
|
||||
}
|
||||
|
||||
return success ? moduleDataMap : null;
|
||||
}
|
||||
|
||||
private static void addMessages(@NotNull CompileContext context,
|
||||
@NotNull Map<AndroidCompilerMessageKind, List<String>> messages,
|
||||
@Nullable String sourcePath) {
|
||||
for (Map.Entry<AndroidCompilerMessageKind, List<String>> entry : messages.entrySet()) {
|
||||
final AndroidCompilerMessageKind kind = entry.getKey();
|
||||
final BuildMessage.Kind buildMessageKind = toBuildMessageKind(kind);
|
||||
|
||||
if (buildMessageKind == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (String message : entry.getValue()) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, buildMessageKind, message, sourcePath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static BuildMessage.Kind toBuildMessageKind(@NotNull AndroidCompilerMessageKind kind) {
|
||||
switch (kind) {
|
||||
case ERROR:
|
||||
return BuildMessage.Kind.ERROR;
|
||||
case INFORMATION:
|
||||
return BuildMessage.Kind.INFO;
|
||||
case WARNING:
|
||||
return BuildMessage.Kind.WARNING;
|
||||
default:
|
||||
LOG.error("unknown AndroidCompilerMessageKind object " + kind);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String computePackageForFile(@NotNull CompileContext context, @NotNull File file) throws IOException {
|
||||
final RootDescriptor descriptor = context.getRootsIndex().getModuleAndRoot(file);
|
||||
@@ -372,52 +517,22 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
return FileUtil.toSystemIndependentName(relPath).replace('/', '.');
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static IAndroidTarget parseAndroidTarget(@NotNull AndroidSdk sdk, @NotNull CompileContext context) {
|
||||
final String targetHashString = sdk.getBuildTargetHashString();
|
||||
if (targetHashString == null) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
|
||||
"Cannot parse SDK " + sdk.getName() + ": build target is not specified"));
|
||||
return null;
|
||||
}
|
||||
|
||||
final MessageBuildingSdkLog log = new MessageBuildingSdkLog();
|
||||
final SdkManager manager = AndroidCommonUtils.createSdkManager(sdk.getSdkPath(), log);
|
||||
|
||||
if (manager == null) {
|
||||
final String message = log.getErrorMessage();
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
|
||||
"Android SDK is parsed incorrectly." +
|
||||
(message.length() > 0 ? " Parsing log:\n" + message : "")));
|
||||
return null;
|
||||
}
|
||||
|
||||
final IAndroidTarget target = manager.getTargetFromHashString(targetHashString);
|
||||
if (target == null) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
|
||||
"Cannot parse SDK '" + sdk.getName() + "': unknown target " + targetHashString));
|
||||
return null;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
private static void fillSourceRoots(@NotNull Module module, @NotNull Set<Module> visited, @NotNull Set<File> result)
|
||||
throws IOException {
|
||||
visited.add(module);
|
||||
final AndroidFacet facet = getFacet(module);
|
||||
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
|
||||
File resDir = null;
|
||||
File resDirForCompilation = null;
|
||||
|
||||
if (facet != null) {
|
||||
resDir = facet.getResourceDir();
|
||||
if (resDir != null) {
|
||||
resDir = resDir.getCanonicalFile();
|
||||
}
|
||||
resDirForCompilation = facet.getResourceDirForCompilation();
|
||||
}
|
||||
|
||||
for (String sourceRootPath : module.getSourceRoots()) {
|
||||
final File sourceRoot = new File(sourceRootPath).getCanonicalFile();
|
||||
|
||||
if (!sourceRoot.equals(resDir)) {
|
||||
if (!sourceRoot.equals(resDir) && !sourceRoot.equals(resDirForCompilation)) {
|
||||
result.add(sourceRoot);
|
||||
}
|
||||
}
|
||||
@@ -442,42 +557,23 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Android Builder";
|
||||
return "Android Source Generating Builder";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static AndroidFacet getFacet(@NotNull Module module) {
|
||||
AndroidFacet androidFacet = null;
|
||||
|
||||
for (Facet facet : module.getFacets().values()) {
|
||||
if (facet instanceof AndroidFacet) {
|
||||
androidFacet = (AndroidFacet)facet;
|
||||
}
|
||||
}
|
||||
return androidFacet;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String[] toPaths(@NotNull File[] files) {
|
||||
final String[] result = new String[files.length];
|
||||
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = files[i].getPath();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static class MyModuleData {
|
||||
private final File myOutputDirectory;
|
||||
private final String mySdkLocation;
|
||||
private final IAndroidTarget myAndroidTarget;
|
||||
private final AndroidFacet myFacet;
|
||||
|
||||
private MyModuleData(@NotNull File outputDirectory,
|
||||
@NotNull String sdkLocation,
|
||||
@NotNull IAndroidTarget androidTarget) {
|
||||
@NotNull IAndroidTarget androidTarget,
|
||||
@NotNull AndroidFacet facet) {
|
||||
myOutputDirectory = outputDirectory;
|
||||
mySdkLocation = sdkLocation;
|
||||
myAndroidTarget = androidTarget;
|
||||
myFacet = facet;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -494,5 +590,10 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
public String getSdkLocation() {
|
||||
return mySdkLocation;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public AndroidFacet getFacet() {
|
||||
return myFacet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@ package org.jetbrains.android.util;
|
||||
import com.android.sdklib.ISdkLog;
|
||||
import com.android.sdklib.SdkConstants;
|
||||
import com.android.sdklib.SdkManager;
|
||||
import com.intellij.execution.process.BaseOSProcessHandler;
|
||||
import com.intellij.execution.process.ProcessAdapter;
|
||||
import com.intellij.execution.process.ProcessEvent;
|
||||
import com.intellij.execution.process.ProcessOutputTypes;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -12,13 +18,23 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidCommonUtils {
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.util.AndroidCommonUtils");
|
||||
|
||||
@NonNls public static final Object MANIFEST_JAVA_FILE_NAME = "Manifest.java";
|
||||
@NonNls public static final String R_JAVA_FILENAME = "R.java";
|
||||
@NonNls public static final String CLASSES_JAR_FILE_NAME = "classes.jar";
|
||||
@NonNls public static final String CLASSES_FILE_NAME = "classes.dex";
|
||||
private static final Pattern WARNING_PATTERN = Pattern.compile(".*warning.*");
|
||||
private static final Pattern ERROR_PATTERN = Pattern.compile(".*error.*");
|
||||
private static final Pattern EXCEPTION_PATTERN = Pattern.compile(".*exception.*");
|
||||
|
||||
private AndroidCommonUtils() {
|
||||
}
|
||||
@@ -69,4 +85,52 @@ public class AndroidCommonUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleDexCompilationResult(@NotNull Process process,
|
||||
@NotNull String outputFilePath,
|
||||
@NotNull final Map<AndroidCompilerMessageKind, List<String>> messages) {
|
||||
final BaseOSProcessHandler handler = new BaseOSProcessHandler(process, null, null);
|
||||
handler.addProcessListener(new ProcessAdapter() {
|
||||
private AndroidCompilerMessageKind myCategory = null;
|
||||
|
||||
@Override
|
||||
public void onTextAvailable(ProcessEvent event, Key outputType) {
|
||||
String[] msgs = event.getText().split("\\n");
|
||||
for (String msg : msgs) {
|
||||
msg = msg.trim();
|
||||
String msglc = msg.toLowerCase();
|
||||
if (outputType == ProcessOutputTypes.STDERR) {
|
||||
if (WARNING_PATTERN.matcher(msglc).matches()) {
|
||||
myCategory = AndroidCompilerMessageKind.WARNING;
|
||||
}
|
||||
if (ERROR_PATTERN.matcher(msglc).matches() || EXCEPTION_PATTERN.matcher(msglc).matches() || myCategory == null) {
|
||||
myCategory = AndroidCompilerMessageKind.ERROR;
|
||||
}
|
||||
messages.get(myCategory).add(msg);
|
||||
}
|
||||
else if (outputType == ProcessOutputTypes.STDOUT) {
|
||||
if (!msglc.startsWith("processing")) {
|
||||
messages.get(AndroidCompilerMessageKind.INFORMATION).add(msg);
|
||||
}
|
||||
}
|
||||
|
||||
LOG.info(msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
handler.startNotify();
|
||||
handler.waitFor();
|
||||
|
||||
final List<String> errors = messages.get(AndroidCompilerMessageKind.ERROR);
|
||||
|
||||
if (new File(outputFilePath).isFile()) {
|
||||
// if compilation finished correctly, show all errors as warnings
|
||||
messages.get(AndroidCompilerMessageKind.WARNING).addAll(errors);
|
||||
errors.clear();
|
||||
}
|
||||
else if (errors.size() == 0) {
|
||||
errors.add("Cannot create classes.dex file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,12 +91,10 @@ public class AndroidCompileUtil {
|
||||
@NonNls private static final String GEN_MODULE_PREFIX = "~generated_";
|
||||
|
||||
@NonNls private static final String PROGUARD_CFG_FILE_NAME = "proguard.cfg";
|
||||
@NonNls public static final String CLASSES_JAR_FILE_NAME = "classes.jar";
|
||||
|
||||
@NonNls
|
||||
private static final String[] SCALA_TEST_CONFIGURATIONS =
|
||||
{"ScalaTestRunConfiguration", "SpecsRunConfiguration", "Specs2RunConfiguration"};
|
||||
public static final String CLASSES_FILE_NAME = "classes.dex";
|
||||
|
||||
private AndroidCompileUtil() {
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.jetbrains.android.maven.AndroidMavenProvider;
|
||||
import org.jetbrains.android.maven.AndroidMavenUtil;
|
||||
import org.jetbrains.android.sdk.AndroidPlatform;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.android.util.AndroidCommonUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -61,7 +62,7 @@ public class AndroidDexCompiler implements ClassPostProcessingCompiler {
|
||||
}
|
||||
|
||||
if (items != null && items.length > 0) {
|
||||
context.getProgressIndicator().setText("Generating " + AndroidCompileUtil.CLASSES_FILE_NAME + "...");
|
||||
context.getProgressIndicator().setText("Generating " + AndroidCommonUtils.CLASSES_FILE_NAME + "...");
|
||||
return new ProcessAction(context, items).compute();
|
||||
}
|
||||
return ProcessingItem.EMPTY_ARRAY;
|
||||
@@ -214,8 +215,8 @@ public class AndroidDexCompiler implements ClassPostProcessingCompiler {
|
||||
files[i++] = FileUtil.toSystemDependentName(file.getPath());
|
||||
}
|
||||
|
||||
Map<CompilerMessageCategory, List<String>> messages = AndroidDxWrapper
|
||||
.execute(dexItem.myModule, dexItem.myAndroidTarget, outputDirPath, files);
|
||||
Map<CompilerMessageCategory, List<String>> messages = AndroidCompileUtil.toCompilerMessageCategoryKeys(
|
||||
AndroidDxWrapper.execute(dexItem.myModule, dexItem.myAndroidTarget, outputDirPath, files));
|
||||
|
||||
addMessages(messages);
|
||||
if (messages.get(CompilerMessageCategory.ERROR).isEmpty()) {
|
||||
|
||||
+2
-1
@@ -13,6 +13,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.android.util.AndroidCommonUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.DataInput;
|
||||
@@ -87,7 +88,7 @@ public class AndroidLibraryPackagingCompiler implements ClassPostProcessingCompi
|
||||
|
||||
final String[] classesDirOsPaths = AndroidCompileUtil.toOsPaths(processingItem.getClassDirectories());
|
||||
final String outputJarOsPath = FileUtil.toSystemDependentName(processingItem.getOutputDirectory().getPath() + '/' +
|
||||
AndroidCompileUtil.CLASSES_JAR_FILE_NAME);
|
||||
AndroidCommonUtils.CLASSES_JAR_FILE_NAME);
|
||||
|
||||
final File outputJarFile = new File(outputJarOsPath);
|
||||
try {
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.jetbrains.android.facet.AndroidRootUtil;
|
||||
import org.jetbrains.android.sdk.AndroidPlatform;
|
||||
import org.jetbrains.android.sdk.AndroidSdkUtils;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.android.util.AndroidCommonUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -116,7 +117,7 @@ public class AndroidPackagingCompiler implements PackagingCompiler {
|
||||
File resPackage = AndroidResourcesPackagingCompiler.getOutputFile(module, outputDir);
|
||||
String resPackagePath = FileUtil.toSystemDependentName(resPackage.getPath());
|
||||
|
||||
File classesDexFile = new File(outputDir.getPath(), AndroidCompileUtil.CLASSES_FILE_NAME);
|
||||
File classesDexFile = new File(outputDir.getPath(), AndroidCommonUtils.CLASSES_FILE_NAME);
|
||||
String classesDexPath = FileUtil.toSystemDependentName(classesDexFile.getPath());
|
||||
|
||||
AndroidPlatform platform = configuration.getAndroidPlatform();
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.jetbrains.android.facet.AndroidFacet;
|
||||
import org.jetbrains.android.sdk.AndroidPlatform;
|
||||
import org.jetbrains.android.sdk.AndroidSdkUtils;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.android.util.AndroidCommonUtils;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -229,7 +230,7 @@ public class AndroidApkBuilder {
|
||||
fis.close();
|
||||
}
|
||||
|
||||
builder.writeFile(dexEntryFile, AndroidCompileUtil.CLASSES_FILE_NAME);
|
||||
builder.writeFile(dexEntryFile, AndroidCommonUtils.CLASSES_FILE_NAME);
|
||||
|
||||
final HashSet<String> added = new HashSet<String>();
|
||||
for (VirtualFile sourceRoot : sourceRoots) {
|
||||
|
||||
@@ -21,30 +21,24 @@ import com.intellij.execution.configurations.CommandLineBuilder;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.configurations.JavaParameters;
|
||||
import com.intellij.execution.configurations.ParametersList;
|
||||
import com.intellij.execution.process.OSProcessHandler;
|
||||
import com.intellij.execution.process.ProcessAdapter;
|
||||
import com.intellij.execution.process.ProcessEvent;
|
||||
import com.intellij.execution.process.ProcessOutputTypes;
|
||||
import com.intellij.openapi.compiler.CompilerMessageCategory;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.util.PathUtil;
|
||||
import com.intellij.util.PathsList;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import org.jetbrains.android.compiler.AndroidCompileUtil;
|
||||
import org.jetbrains.android.compiler.AndroidDexCompilerConfiguration;
|
||||
import org.jetbrains.android.util.AndroidBundle;
|
||||
import org.jetbrains.android.util.AndroidCommonUtils;
|
||||
import org.jetbrains.android.util.AndroidCompilerMessageKind;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
@@ -52,31 +46,27 @@ import java.util.regex.Pattern;
|
||||
public class AndroidDxWrapper {
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.compiler.tools.AndroidDx");
|
||||
|
||||
private static final Pattern WARNING_PATTERN = Pattern.compile(".*warning.*");
|
||||
private static final Pattern ERROR_PATTERN = Pattern.compile(".*error.*");
|
||||
private static final Pattern EXCEPTION_PATTERN = Pattern.compile(".*exception.*");
|
||||
|
||||
private AndroidDxWrapper() {
|
||||
}
|
||||
|
||||
@SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"})
|
||||
public static Map<CompilerMessageCategory, List<String>> execute(@NotNull Module module,
|
||||
public static Map<AndroidCompilerMessageKind, List<String>> execute(@NotNull Module module,
|
||||
@NotNull IAndroidTarget target,
|
||||
@NotNull String outputDir,
|
||||
@NotNull String[] compileTargets) {
|
||||
String outFile = outputDir + File.separatorChar + AndroidCompileUtil.CLASSES_FILE_NAME;
|
||||
String outFile = outputDir + File.separatorChar + AndroidCommonUtils.CLASSES_FILE_NAME;
|
||||
|
||||
final Map<CompilerMessageCategory, List<String>> messages = new HashMap<CompilerMessageCategory, List<String>>(2);
|
||||
messages.put(CompilerMessageCategory.ERROR, new ArrayList<String>());
|
||||
messages.put(CompilerMessageCategory.INFORMATION, new ArrayList<String>());
|
||||
messages.put(CompilerMessageCategory.WARNING, new ArrayList<String>());
|
||||
final Map<AndroidCompilerMessageKind, List<String>> messages = new HashMap<AndroidCompilerMessageKind, List<String>>(2);
|
||||
messages.put(AndroidCompilerMessageKind.ERROR, new ArrayList<String>());
|
||||
messages.put(AndroidCompilerMessageKind.INFORMATION, new ArrayList<String>());
|
||||
messages.put(AndroidCompilerMessageKind.WARNING, new ArrayList<String>());
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
String dxJarPath = target.getPath(IAndroidTarget.DX_JAR);
|
||||
|
||||
File dxJar = new File(dxJarPath);
|
||||
if (!dxJar.isFile()) {
|
||||
messages.get(CompilerMessageCategory.ERROR).add(AndroidBundle.message("android.file.not.exist.error", dxJarPath));
|
||||
messages.get(AndroidCompilerMessageKind.ERROR).add(AndroidBundle.message("android.file.not.exist.error", dxJarPath));
|
||||
return messages;
|
||||
}
|
||||
|
||||
@@ -121,53 +111,12 @@ public class AndroidDxWrapper {
|
||||
process = commandLine.createProcess();
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
messages.get(CompilerMessageCategory.ERROR).add("ExecutionException: " + e.getMessage());
|
||||
messages.get(AndroidCompilerMessageKind.ERROR).add("ExecutionException: " + e.getMessage());
|
||||
LOG.info(e);
|
||||
return messages;
|
||||
}
|
||||
|
||||
final OSProcessHandler handler = new OSProcessHandler(process, "");
|
||||
handler.addProcessListener(new ProcessAdapter() {
|
||||
private CompilerMessageCategory myCategory = null;
|
||||
|
||||
@Override
|
||||
public void onTextAvailable(ProcessEvent event, Key outputType) {
|
||||
String[] msgs = event.getText().split("\\n");
|
||||
for (String msg : msgs) {
|
||||
msg = msg.trim();
|
||||
String msglc = msg.toLowerCase();
|
||||
if (outputType == ProcessOutputTypes.STDERR) {
|
||||
if (WARNING_PATTERN.matcher(msglc).matches()) {
|
||||
myCategory = CompilerMessageCategory.WARNING;
|
||||
}
|
||||
if (ERROR_PATTERN.matcher(msglc).matches() || EXCEPTION_PATTERN.matcher(msglc).matches() || myCategory == null) {
|
||||
myCategory = CompilerMessageCategory.ERROR;
|
||||
}
|
||||
messages.get(myCategory).add(msg);
|
||||
}
|
||||
else if (outputType == ProcessOutputTypes.STDOUT) {
|
||||
if (!msglc.startsWith("processing")) {
|
||||
messages.get(CompilerMessageCategory.INFORMATION).add(msg);
|
||||
}
|
||||
}
|
||||
LOG.info(msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
handler.startNotify();
|
||||
handler.waitFor();
|
||||
|
||||
final List<String> errors = messages.get(CompilerMessageCategory.ERROR);
|
||||
|
||||
if (new File(outFile).isFile()) {
|
||||
// if compilation finished correctly, show all errors as warnings
|
||||
messages.get(CompilerMessageCategory.WARNING).addAll(errors);
|
||||
errors.clear();
|
||||
}
|
||||
else if (errors.size() == 0) {
|
||||
errors.add("Cannot create classes.dex file");
|
||||
}
|
||||
AndroidCommonUtils.handleDexCompilationResult(process, outFile, messages);
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.util.containers.OrderedSet;
|
||||
import org.jetbrains.android.compiler.AndroidCompileUtil;
|
||||
import org.jetbrains.android.util.AndroidCommonUtils;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -245,7 +246,7 @@ public class AndroidRootUtil {
|
||||
|
||||
if (libraryProject) {
|
||||
if (classDir != null) {
|
||||
final VirtualFile packedClassesJar = classDir.findChild(AndroidCompileUtil.CLASSES_JAR_FILE_NAME);
|
||||
final VirtualFile packedClassesJar = classDir.findChild(AndroidCommonUtils.CLASSES_JAR_FILE_NAME);
|
||||
if (packedClassesJar != null) {
|
||||
outputDirs.add(packedClassesJar);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user