mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
android-jps: refactor, support library packaging
This commit is contained in:
@@ -16,6 +16,7 @@ public class AndroidBuilderService extends BuilderService {
|
||||
@Override
|
||||
public List<? extends ModuleLevelBuilder> createModuleLevelBuilders(ExecutorService executorService) {
|
||||
return Arrays.asList(new AndroidSourceGeneratingBuilder(),
|
||||
new AndroidLibraryPackagingBuilder(),
|
||||
new AndroidDexBuilder());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,16 +21,15 @@ 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: save validity state
|
||||
// todo: support light builds (for tests)
|
||||
|
||||
public class AndroidDexBuilder extends ModuleLevelBuilder {
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.android.AndroidDexBuilder");
|
||||
@@ -52,16 +51,7 @@ public class AndroidDexBuilder extends ModuleLevelBuilder {
|
||||
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);
|
||||
return AndroidJpsUtil.handleException(context, e, BUILDER_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,15 @@ 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.ModuleLevelBuilder;
|
||||
import org.jetbrains.jps.incremental.ProjectBuildException;
|
||||
import org.jetbrains.jps.incremental.messages.BuildMessage;
|
||||
import org.jetbrains.jps.incremental.messages.CompilerMessage;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -238,4 +243,25 @@ class AndroidJpsUtil {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ModuleLevelBuilder.ExitCode handleException(@NotNull CompileContext context, @NotNull Exception e, @NotNull String builderName)
|
||||
throws ProjectBuildException {
|
||||
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(builderName, BuildMessage.Kind.ERROR, message));
|
||||
throw new ProjectBuildException(message, e);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getManifestFileForCompilationPath(@NotNull AndroidFacet facet) throws IOException {
|
||||
return facet.getUseCustomManifestForCompilation()
|
||||
? facet.getManifestFileForCompilation()
|
||||
: facet.getManifestFile();
|
||||
}
|
||||
}
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package org.jetbrains.jps.android;
|
||||
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.jetbrains.android.util.AndroidCommonUtils;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.jps.Module;
|
||||
import org.jetbrains.jps.ModuleChunk;
|
||||
import org.jetbrains.jps.ProjectPaths;
|
||||
import org.jetbrains.jps.incremental.BuilderCategory;
|
||||
import org.jetbrains.jps.incremental.CompileContext;
|
||||
import org.jetbrains.jps.incremental.ModuleLevelBuilder;
|
||||
import org.jetbrains.jps.incremental.ProjectBuildException;
|
||||
import org.jetbrains.jps.incremental.messages.BuildMessage;
|
||||
import org.jetbrains.jps.incremental.messages.CompilerMessage;
|
||||
import org.jetbrains.jps.incremental.messages.ProgressMessage;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
|
||||
// todo: save validity states
|
||||
// todo: support light builds
|
||||
|
||||
public class AndroidLibraryPackagingBuilder extends ModuleLevelBuilder {
|
||||
@NonNls private static final String BUILDER_NAME = "android-library-packager";
|
||||
|
||||
protected AndroidLibraryPackagingBuilder() {
|
||||
super(BuilderCategory.CLASS_POST_PROCESSOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleLevelBuilder.ExitCode build(CompileContext context, ModuleChunk chunk) throws ProjectBuildException {
|
||||
if (context.isCompilingTests() || !AndroidJpsUtil.containsAndroidFacet(chunk)) {
|
||||
return ModuleLevelBuilder.ExitCode.OK;
|
||||
}
|
||||
context.processMessage(new ProgressMessage("Packaging Android libraries"));
|
||||
|
||||
try {
|
||||
return doBuild(context, chunk);
|
||||
}
|
||||
catch (Exception e) {
|
||||
return AndroidJpsUtil.handleException(context, e, BUILDER_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
private static ModuleLevelBuilder.ExitCode doBuild(CompileContext context, ModuleChunk chunk) {
|
||||
boolean success = true;
|
||||
|
||||
for (Module module : chunk.getModules()) {
|
||||
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
|
||||
if (facet == null || !facet.isLibrary()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProjectPaths projectPaths = context.getProjectPaths();
|
||||
final File outputDirectoryForPackagedFiles = AndroidJpsUtil.getOutputDirectoryForPackagedFiles(projectPaths, module);
|
||||
|
||||
if (outputDirectoryForPackagedFiles == null) {
|
||||
context.processMessage(
|
||||
new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Output directory is not specified for module " + module.getName()));
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
final File classesDir = projectPaths.getModuleOutputDir(module, false);
|
||||
if (classesDir == null || !classesDir.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Set<String> subdirs = new HashSet<String>();
|
||||
AndroidJpsUtil.addSubdirectories(classesDir, subdirs);
|
||||
|
||||
if (subdirs.size() > 0) {
|
||||
final File outputJarFile = new File(outputDirectoryForPackagedFiles, AndroidCommonUtils.CLASSES_JAR_FILE_NAME);
|
||||
|
||||
try {
|
||||
AndroidCommonUtils.packClassFilesIntoJar(ArrayUtil.EMPTY_STRING_ARRAY, ArrayUtil.toStringArray(subdirs), outputJarFile);
|
||||
}
|
||||
catch (IOException e) {
|
||||
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return success ? ModuleLevelBuilder.ExitCode.OK : ModuleLevelBuilder.ExitCode.ABORT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return BUILDER_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Android Library Packaging Builder";
|
||||
}
|
||||
}
|
||||
+3
-19
@@ -61,16 +61,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
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);
|
||||
return AndroidJpsUtil.handleException(context, e, BUILDER_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +268,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
continue;
|
||||
}
|
||||
|
||||
final File manifestFile = getManifestFileForCompilationPath(facet);
|
||||
final File manifestFile = AndroidJpsUtil.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()));
|
||||
@@ -329,7 +320,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
final Set<String> result = new HashSet<String>();
|
||||
|
||||
for (AndroidFacet depFacet : getAllDependentAndroidLibraries(module)) {
|
||||
final File depManifestFile = getManifestFileForCompilationPath(depFacet);
|
||||
final File depManifestFile = AndroidJpsUtil.getManifestFileForCompilationPath(depFacet);
|
||||
|
||||
if (depManifestFile != null) {
|
||||
final String packageName = parsePackageNameFromManifestFile(depManifestFile);
|
||||
@@ -366,13 +357,6 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder {
|
||||
: 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>();
|
||||
|
||||
@@ -9,17 +9,16 @@ 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.Pair;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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.io.*;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
@@ -35,6 +34,7 @@ public class AndroidCommonUtils {
|
||||
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 static Pattern R_PATTERN = Pattern.compile("R(\\$.*)?\\.class");
|
||||
|
||||
private AndroidCommonUtils() {
|
||||
}
|
||||
@@ -133,4 +133,95 @@ public class AndroidCommonUtils {
|
||||
errors.add("Cannot create classes.dex file");
|
||||
}
|
||||
}
|
||||
|
||||
public static void packClassFilesIntoJar(@NotNull String[] firstPackageDirPaths,
|
||||
@NotNull String[] libFirstPackageDirPaths,
|
||||
@NotNull File jarFile) throws IOException {
|
||||
final List<Pair<File, String>> files = new ArrayList<Pair<File, String>>();
|
||||
for (String path : firstPackageDirPaths) {
|
||||
final File firstPackageDir = new File(path);
|
||||
if (firstPackageDir.exists()) {
|
||||
addFileToJar(firstPackageDir, firstPackageDir.getParentFile(), true, files);
|
||||
}
|
||||
}
|
||||
|
||||
for (String path : libFirstPackageDirPaths) {
|
||||
final File firstPackageDir = new File(path);
|
||||
if (firstPackageDir.exists()) {
|
||||
addFileToJar(firstPackageDir, firstPackageDir.getParentFile(), false, files);
|
||||
}
|
||||
}
|
||||
|
||||
if (files.size() > 0) {
|
||||
final JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFile));
|
||||
try {
|
||||
for (Pair<File, String> pair : files) {
|
||||
packIntoJar(jos, pair.getFirst(), pair.getSecond());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
jos.close();
|
||||
}
|
||||
}
|
||||
else if (jarFile.isFile()) {
|
||||
if (!jarFile.delete()) {
|
||||
throw new IOException("Cannot delete file " + FileUtil.toSystemDependentName(jarFile.getPath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addFileToJar(@NotNull File file,
|
||||
@NotNull File rootDirectory,
|
||||
boolean packRClasses,
|
||||
@NotNull List<Pair<File, String>> files)
|
||||
throws IOException {
|
||||
|
||||
if (file.isDirectory()) {
|
||||
final File[] children = file.listFiles();
|
||||
|
||||
if (children != null) {
|
||||
for (File child : children) {
|
||||
addFileToJar(child, rootDirectory, packRClasses, files);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (file.isFile()) {
|
||||
if (!FileUtil.getExtension(file.getName()).equals("class")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!packRClasses && R_PATTERN.matcher(file.getName()).matches()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String rootPath = rootDirectory.getAbsolutePath();
|
||||
|
||||
String path = file.getAbsolutePath();
|
||||
path = FileUtil.toSystemIndependentName(path.substring(rootPath.length()));
|
||||
if (path.charAt(0) == '/') {
|
||||
path = path.substring(1);
|
||||
}
|
||||
|
||||
files.add(new Pair<File, String>(file, path));
|
||||
}
|
||||
}
|
||||
|
||||
private static void packIntoJar(@NotNull JarOutputStream jar, @NotNull File file, @NotNull String path) throws IOException {
|
||||
final JarEntry entry = new JarEntry(path);
|
||||
entry.setTime(file.lastModified());
|
||||
jar.putNextEntry(entry);
|
||||
|
||||
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
|
||||
try {
|
||||
final byte[] buffer = new byte[1024];
|
||||
int count;
|
||||
while ((count = bis.read(buffer)) != -1) {
|
||||
jar.write(buffer, 0, count);
|
||||
}
|
||||
jar.closeEntry();
|
||||
}
|
||||
finally {
|
||||
bis.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.*;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.*;
|
||||
import com.intellij.psi.JavaPsiFacade;
|
||||
@@ -70,10 +69,9 @@ import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -82,7 +80,6 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class AndroidCompileUtil {
|
||||
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.compiler.AndroidCompileUtil");
|
||||
private static Pattern R_PATTERN = Pattern.compile("R(\\$.*)?\\.class");
|
||||
|
||||
private static final Pattern ourMessagePattern = Pattern.compile("(.+):(\\d+):.+");
|
||||
|
||||
@@ -750,97 +747,6 @@ public class AndroidCompileUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static void packClassFilesIntoJar(@NotNull String[] firstPackageDirPaths,
|
||||
@NotNull String[] libFirstPackageDirPaths,
|
||||
@NotNull File jarFile) throws IOException {
|
||||
final List<Pair<File, String>> files = new ArrayList<Pair<File, String>>();
|
||||
for (String path : firstPackageDirPaths) {
|
||||
final File firstPackageDir = new File(path);
|
||||
if (firstPackageDir.exists()) {
|
||||
addFileToJar(firstPackageDir, firstPackageDir.getParentFile(), true, files);
|
||||
}
|
||||
}
|
||||
|
||||
for (String path : libFirstPackageDirPaths) {
|
||||
final File firstPackageDir = new File(path);
|
||||
if (firstPackageDir.exists()) {
|
||||
addFileToJar(firstPackageDir, firstPackageDir.getParentFile(), false, files);
|
||||
}
|
||||
}
|
||||
|
||||
if (files.size() > 0) {
|
||||
final JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFile));
|
||||
try {
|
||||
for (Pair<File, String> pair : files) {
|
||||
packIntoJar(jos, pair.getFirst(), pair.getSecond());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
jos.close();
|
||||
}
|
||||
}
|
||||
else if (jarFile.isFile()) {
|
||||
if (!jarFile.delete()) {
|
||||
throw new IOException("Cannot delete file " + FileUtil.toSystemDependentName(jarFile.getPath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addFileToJar(@NotNull File file,
|
||||
@NotNull File rootDirectory,
|
||||
boolean packRClasses,
|
||||
@NotNull List<Pair<File, String>> files)
|
||||
throws IOException {
|
||||
|
||||
if (file.isDirectory()) {
|
||||
final File[] children = file.listFiles();
|
||||
|
||||
if (children != null) {
|
||||
for (File child : children) {
|
||||
addFileToJar(child, rootDirectory, packRClasses, files);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (file.isFile()) {
|
||||
if (!FileUtil.getExtension(file.getName()).equals("class")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!packRClasses && R_PATTERN.matcher(file.getName()).matches()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String rootPath = rootDirectory.getAbsolutePath();
|
||||
|
||||
String path = file.getAbsolutePath();
|
||||
path = FileUtil.toSystemIndependentName(path.substring(rootPath.length()));
|
||||
if (path.charAt(0) == '/') {
|
||||
path = path.substring(1);
|
||||
}
|
||||
|
||||
files.add(new Pair<File, String>(file, path));
|
||||
}
|
||||
}
|
||||
|
||||
private static void packIntoJar(@NotNull JarOutputStream jar, @NotNull File file, @NotNull String path) throws IOException {
|
||||
final JarEntry entry = new JarEntry(path);
|
||||
entry.setTime(file.lastModified());
|
||||
jar.putNextEntry(entry);
|
||||
|
||||
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
|
||||
try {
|
||||
final byte[] buffer = new byte[1024];
|
||||
int count;
|
||||
while ((count = bis.read(buffer)) != -1) {
|
||||
jar.write(buffer, 0, count);
|
||||
}
|
||||
jar.closeEntry();
|
||||
}
|
||||
finally {
|
||||
bis.close();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String[] toOsPaths(@NotNull VirtualFile[] classFilesDirs) {
|
||||
final String[] classFilesDirOsPaths = new String[classFilesDirs.length];
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ public class AndroidLibraryPackagingCompiler implements ClassPostProcessingCompi
|
||||
|
||||
final File outputJarFile = new File(outputJarOsPath);
|
||||
try {
|
||||
AndroidCompileUtil.packClassFilesIntoJar(ArrayUtil.EMPTY_STRING_ARRAY, classesDirOsPaths, outputJarFile);
|
||||
AndroidCommonUtils.packClassFilesIntoJar(ArrayUtil.EMPTY_STRING_ARRAY, classesDirOsPaths, outputJarFile);
|
||||
CompilerUtil.refreshIOFile(outputJarFile);
|
||||
result.add(processingItem);
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ public class AndroidProguardCompiler implements ClassPostProcessingCompiler {
|
||||
throws IOException {
|
||||
final File inputJar = FileUtil.createTempFile("proguard_input", ".jar");
|
||||
|
||||
AndroidCompileUtil.packClassFilesIntoJar(classFilesDirOsPaths, libClassFilesDirOsPaths, inputJar);
|
||||
AndroidCommonUtils.packClassFilesIntoJar(classFilesDirOsPaths, libClassFilesDirOsPaths, inputJar);
|
||||
|
||||
return FileUtil.toSystemDependentName(inputJar.getPath());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user