diff options
6 files changed, 186 insertions, 303 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java index e915544..ad4b787 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java @@ -21,8 +21,6 @@ import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AndroidPrintStream; import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity; -import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; -import com.android.ide.eclipse.adt.internal.project.ProjectHelper; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.sdklib.IAndroidTarget; @@ -33,11 +31,9 @@ import com.android.sdklib.build.ApkBuilder.JarStatus; import com.android.sdklib.build.ApkBuilder.SigningInfo; import com.android.sdklib.build.ApkCreationException; import com.android.sdklib.build.DuplicateFileException; -import com.android.sdklib.build.IArchiveBuilder; import com.android.sdklib.build.SealedApkException; import com.android.sdklib.internal.build.DebugKeyProvider; import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException; -import com.android.sdklib.internal.build.SignedJarBuilder; import com.android.sdklib.util.GrabProcessOutput; import com.android.sdklib.util.GrabProcessOutput.IProcessOutput; import com.android.sdklib.util.GrabProcessOutput.Wait; @@ -46,9 +42,6 @@ import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IResourceProxy; -import org.eclipse.core.resources.IResourceProxyVisitor; -import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; @@ -69,8 +62,11 @@ import java.io.PrintStream; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.TreeMap; /** @@ -108,6 +104,8 @@ public class BuildHelper { private final boolean mVerbose; private final boolean mDebugMode; + private final Set<String> mCompiledCodePaths = new HashSet<String>(); + public static final boolean BENCHMARK_FLAG = false; public static long sStartOverallTime = 0; public static long sStartJavaCTime = 0; @@ -128,14 +126,18 @@ public class BuildHelper { * @param errStream * @param debugMode whether this is a debug build * @param verbose + * @throws CoreException */ public BuildHelper(IProject project, AndroidPrintStream outStream, - AndroidPrintStream errStream, boolean debugMode, boolean verbose) { + AndroidPrintStream errStream, boolean debugMode, boolean verbose, + ResourceMarker resMarker) throws CoreException { mProject = project; mOutStream = outStream; mErrStream = errStream; mDebugMode = debugMode; mVerbose = verbose; + + gatherPaths(resMarker); } public void updateCrunchCache() throws AaptExecException, AaptResultException { @@ -169,11 +171,6 @@ public class BuildHelper { } } - public static void writeResources(IArchiveBuilder builder, IJavaProject javaProject) - throws DuplicateFileException, ApkCreationException, SealedApkException, CoreException { - writeStandardResources(builder, javaProject, null); - } - /** * Packages the resources of the projet into a .ap_ file. * @param manifestFile the manifest of the project. @@ -365,19 +362,16 @@ public class BuildHelper { mVerbose ? mOutStream: null); apkBuilder.setDebugMode(mDebugMode); - // Now we write the standard resources from the project and the referenced projects. - writeStandardResources(apkBuilder, javaProject, referencedJavaProjects); - - // Now we write the standard resources from the external jars - for (String libraryOsPath : getExternalDependencies(resMarker)) { - File libFile = new File(libraryOsPath); - if (libFile.isFile()) { - JarStatus jarStatus = apkBuilder.addResourcesFromJar(new File(libraryOsPath)); + // Now we write the standard resources from all the output paths. + for (String path : mCompiledCodePaths) { + File file = new File(path); + if (file.isFile()) { + JarStatus jarStatus = apkBuilder.addResourcesFromJar(file); // check if we found native libraries in the external library. This // constitutes an error or warning depending on if they are in lib/ if (jarStatus.getNativeLibs().size() > 0) { - String libName = new File(libraryOsPath).getName(); + String libName = file.getName(); String msg = String.format( "Native libraries detected in '%1$s'. See console for more information.", @@ -418,11 +412,11 @@ public class BuildHelper { } } } - } else if (libFile.isDirectory()) { + } else if (file.isDirectory()) { // this is technically not a source folder (class folder instead) but since we // only care about Java resources (ie non class/java files) this will do the // same - apkBuilder.addSourceFolder(libFile); + apkBuilder.addSourceFolder(file); } } @@ -453,66 +447,11 @@ public class BuildHelper { } } - /** - * Return a list of the project output for compiled Java code. - * @return - * @throws CoreException - */ - public String[] getProjectJavaOutputs() throws CoreException { - IFolder outputFolder = BaseProjectHelper.getJavaOutputFolder(mProject); - - // get the list of referenced projects output to add - List<IProject> javaProjects = ProjectHelper.getReferencedProjects(mProject); - List<IJavaProject> referencedJavaProjects = BuildHelper.getJavaProjects(javaProjects); - - // get the project output, and since it's a new list object, just add the outputFolder - // of the project directly to it. - List<String> projectOutputs = getProjectJavaOutputs(referencedJavaProjects); - - projectOutputs.add(0, outputFolder.getLocation().toOSString()); - - return projectOutputs.toArray(new String[projectOutputs.size()]); - } - - /** - * Returns an array for all the compiled code for the project. This can include the - * code compiled by Eclipse for the main project and dependencies (Java only projects), as well - * as external jars used by the project or its library. - * - * This array of paths is compatible with the input for dx and can be passed as is to - * {@link #executeDx(IJavaProject, String[], String)}. - * - * @param resMarker - * @return a array (never empty) containing paths to compiled code. - * @throws CoreException - */ - public String[] getCompiledCodePaths(boolean includeProjectOutputs, ResourceMarker resMarker) - throws CoreException { - - // get the list of libraries to include with the source code - String[] libraries = getExternalDependencies(resMarker); - - int startIndex = 0; - - String[] compiledPaths; - - if (includeProjectOutputs) { - String[] projectOutputs = getProjectJavaOutputs(); - - compiledPaths = new String[libraries.length + projectOutputs.length]; - - System.arraycopy(projectOutputs, 0, compiledPaths, 0, projectOutputs.length); - startIndex = projectOutputs.length; - } else { - compiledPaths = new String[libraries.length]; - } - - System.arraycopy(libraries, 0, compiledPaths, startIndex, libraries.length); - - return compiledPaths; + public Collection<String> getCompiledCodePaths() { + return mCompiledCodePaths; } - public void runProguard(List<File> proguardConfigs, File inputJar, String[] jarFiles, + public void runProguard(List<File> proguardConfigs, File inputJar, Collection<String> jarFiles, File obfuscatedJar, File logOutput) throws ProguardResultException, ProguardExecException, IOException { IAndroidTarget target = Sdk.getCurrent().getTarget(mProject); @@ -723,13 +662,14 @@ public class BuildHelper { /** * Execute the Dx tool for dalvik code conversion. * @param javaProject The java project - * @param inputPath the path to the main input of dex + * @param inputPaths the input paths for DX * @param osOutFilePath the path of the dex file to create. * * @throws CoreException * @throws DexException */ - public void executeDx(IJavaProject javaProject, String[] inputPaths, String osOutFilePath) + public void executeDx(IJavaProject javaProject, Collection<String> inputPaths, + String osOutFilePath) throws CoreException, DexException { // get the dex wrapper @@ -917,153 +857,94 @@ public class BuildHelper { } /** - * Writes the standard resources of a project and its referenced projects - * into a {@link SignedJarBuilder}. - * Standard resources are non java/aidl files placed in the java package folders. - * @param builder the archive builder. - * @param javaProject the javaProject object. - * @param referencedJavaProjects the java projects that this project references. - * @throws ApkCreationException if an error occurred - * @throws SealedApkException if the APK is already sealed. - * @throws DuplicateFileException if a file conflicts with another already added to the APK - * at the same location inside the APK archive. - * @throws CoreException - */ - private static void writeStandardResources(IArchiveBuilder builder, IJavaProject javaProject, - List<IJavaProject> referencedJavaProjects) - throws DuplicateFileException, ApkCreationException, SealedApkException, - CoreException { - IWorkspace ws = ResourcesPlugin.getWorkspace(); - IWorkspaceRoot wsRoot = ws.getRoot(); - - writeStandardProjectResources(builder, javaProject, wsRoot); - - if (referencedJavaProjects != null) { - for (IJavaProject referencedJavaProject : referencedJavaProjects) { - // only include output from non android referenced project - // (This is to handle the case of reference Android projects in the context of - // instrumentation projects that need to reference the projects to be tested). - if (referencedJavaProject.getProject().hasNature( - AdtConstants.NATURE_DEFAULT) == false) { - writeStandardProjectResources(builder, referencedJavaProject, wsRoot); - } - } - } - } - - /** - * Writes the standard resources of a {@link IJavaProject} into a {@link SignedJarBuilder}. - * Standard resources are non java/aidl files placed in the java package folders. - * @param jarBuilder the {@link ApkBuilder}. - * @param javaProject the javaProject object. - * @param wsRoot the {@link IWorkspaceRoot}. - * @throws ApkCreationException if an error occurred - * @throws SealedApkException if the APK is already sealed. - * @throws DuplicateFileException if a file conflicts with another already added to the APK - * at the same location inside the APK archive. + * Computes all the project output and dependencies that must go into building the apk. + * + * @param resMarker * @throws CoreException */ - private static void writeStandardProjectResources(IArchiveBuilder builder, - IJavaProject javaProject, IWorkspaceRoot wsRoot) - throws DuplicateFileException, ApkCreationException, SealedApkException, CoreException { - // get the source pathes - List<IPath> sourceFolders = BaseProjectHelper.getSourceClasspaths(javaProject); - - // loop on them and then recursively go through the content looking for matching files. - for (IPath sourcePath : sourceFolders) { - IResource sourceResource = wsRoot.findMember(sourcePath); - if (sourceResource != null && sourceResource.getType() == IResource.FOLDER) { - writeFolderResources(builder, javaProject, (IFolder) sourceResource); - } - } - } - - private static void writeFolderResources(IArchiveBuilder builder, - final IJavaProject javaProject, IFolder root) throws CoreException, - ApkCreationException, SealedApkException, DuplicateFileException { - final List<IPath> pathsToPackage = new ArrayList<IPath>(); - root.accept(new IResourceProxyVisitor() { - @Override - public boolean visit(IResourceProxy proxy) throws CoreException { - if (proxy.getType() == IResource.FOLDER) { - // If this folder isn't wanted, don't traverse into it. - return ApkBuilder.checkFolderForPackaging(proxy.getName()); - } - // If it's not a folder, it must be a file. We won't see any other resource type. - if (!ApkBuilder.checkFileForPackaging(proxy.getName())) { - return true; - } - IResource res = proxy.requestResource(); - if (!javaProject.isOnClasspath(res)) { - return true; - } - // Just record that we need to package this. Packaging here throws - // inappropriate checked exceptions. - IPath location = res.getLocation(); - pathsToPackage.add(location); - return true; - } - }, 0); - IPath rootLocation = root.getLocation(); - for (IPath path : pathsToPackage) { - IPath archivePath = path.makeRelativeTo(rootLocation); - builder.addFile(path.toFile(), archivePath.toString()); - } - } + private void gatherPaths(ResourceMarker resMarker) + throws CoreException { + IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot(); - /** - * Returns an array of external dependencies used the project. This can be paths to jar files - * or to source folders. - * - * @param resMarker if non null, used to put Resource marker on problem files. - * @return an array of OS-specific absolute file paths - */ - private final String[] getExternalDependencies(ResourceMarker resMarker) { - // get a java project from it + // get a java project for the project. IJavaProject javaProject = JavaCore.create(mProject); - IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot(); - ArrayList<String> oslibraryList = new ArrayList<String>(); + // get the output of the main project + IPath path = javaProject.getOutputLocation(); + IResource outputResource = wsRoot.findMember(path); + if (outputResource != null && outputResource.getType() == IResource.FOLDER) { + mCompiledCodePaths.add(outputResource.getLocation().toOSString()); + } // we could use IJavaProject.getResolvedClasspath directly, but we actually // want to see the containers themselves. IClasspathEntry[] classpaths = javaProject.readRawClasspath(); if (classpaths != null) { for (IClasspathEntry e : classpaths) { - // if this is a classpath variable reference, we resolve it. - if (e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) { - e = JavaCore.getResolvedClasspathEntry(e); + // ignore non exported entries, unless it's the LIBRARIES container, + // in which case we always want it (there may be some older projects that + // have it as non exported). + if (e.isExported() || + (e.getEntryKind() == IClasspathEntry.CPE_CONTAINER && + e.getPath().toString().equals(AdtConstants.CONTAINER_LIBRARIES))) { + handleCPE(e, javaProject, wsRoot, resMarker); } + } + } + } - if (e.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { - handleClasspathEntry(e, wsRoot, oslibraryList, resMarker); - } else if (e.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { - // get the container - try { - IClasspathContainer container = JavaCore.getClasspathContainer( - e.getPath(), javaProject); - // ignore the system and default_system types as they represent - // libraries that are part of the runtime. - if (container.getKind() == IClasspathContainer.K_APPLICATION) { - IClasspathEntry[] entries = container.getClasspathEntries(); - for (IClasspathEntry entry : entries) { - handleClasspathEntry(entry, wsRoot, oslibraryList, resMarker); - } - } - } catch (JavaModelException jme) { - // can't resolve the container? ignore it. - AdtPlugin.log(jme, "Failed to resolve ClasspathContainer: %s", e.getPath()); + private void handleCPE(IClasspathEntry entry, IJavaProject javaProject, + IWorkspaceRoot wsRoot, ResourceMarker resMarker) { + + // if this is a classpath variable reference, we resolve it. + if (entry.getEntryKind() == IClasspathEntry.CPE_VARIABLE) { + entry = JavaCore.getResolvedClasspathEntry(entry); + } + + if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) { + IProject refProject = wsRoot.getProject(entry.getPath().lastSegment()); + try { + // ignore if it's an Android project, or if it's not a Java Project + if (refProject.hasNature(JavaCore.NATURE_ID) && + refProject.hasNature(AdtConstants.NATURE_DEFAULT) == false) { + IJavaProject refJavaProject = JavaCore.create(refProject); + + // get the output folder + IPath path = refJavaProject.getOutputLocation(); + IResource outputResource = wsRoot.findMember(path); + if (outputResource != null && outputResource.getType() == IResource.FOLDER) { + mCompiledCodePaths.add(outputResource.getLocation().toOSString()); } } + } catch (CoreException exception) { + // can't query the project nature? ignore } - } - return oslibraryList.toArray(new String[oslibraryList.size()]); + } else if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { + handleClasspathLibrary(entry, wsRoot, resMarker); + } else if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { + // get the container + try { + IClasspathContainer container = JavaCore.getClasspathContainer( + entry.getPath(), javaProject); + // ignore the system and default_system types as they represent + // libraries that are part of the runtime. + if (container.getKind() == IClasspathContainer.K_APPLICATION) { + IClasspathEntry[] entries = container.getClasspathEntries(); + for (IClasspathEntry cpe : entries) { + handleCPE(cpe, javaProject, wsRoot, resMarker); + } + } + } catch (JavaModelException jme) { + // can't resolve the container? ignore it. + AdtPlugin.log(jme, "Failed to resolve ClasspathContainer: %s", entry.getPath()); + } + } } - private void handleClasspathEntry(IClasspathEntry e, IWorkspaceRoot wsRoot, - ArrayList<String> oslibraryList, ResourceMarker resMarker) { + private void handleClasspathLibrary(IClasspathEntry e, IWorkspaceRoot wsRoot, + ResourceMarker resMarker) { // get the IPath IPath path = e.getPath(); @@ -1077,7 +958,7 @@ public class BuildHelper { // case of a jar file (which could be relative to the workspace or a full path) if (resource != null && resource.exists() && resource.getType() == IResource.FILE) { - oslibraryList.add(resource.getLocation().toOSString()); + mCompiledCodePaths.add(resource.getLocation().toOSString()); } else { // if the jar path doesn't match a workspace resource, // then we get an OSString and check if this links to a valid file. @@ -1085,7 +966,7 @@ public class BuildHelper { File f = new File(osFullPath); if (f.isFile()) { - oslibraryList.add(osFullPath); + mCompiledCodePaths.add(osFullPath); } else { String message = String.format( Messages.Couldnt_Locate_s_Error, path); @@ -1102,7 +983,7 @@ public class BuildHelper { // this can be the case for a class folder. if (resource != null && resource.exists() && resource.getType() == IResource.FOLDER) { - oslibraryList.add(resource.getLocation().toOSString()); + mCompiledCodePaths.add(resource.getLocation().toOSString()); } else { // if the path doesn't match a workspace resource, // then we get an OSString and check if this links to a valid folder. @@ -1110,50 +991,10 @@ public class BuildHelper { File f = new File(osFullPath); if (f.isDirectory()) { - oslibraryList.add(osFullPath); - } - } - } - } - - /** - * Returns the list of the output folders for the specified {@link IJavaProject} objects, if - * they are Android projects. - * - * @param referencedJavaProjects the java projects. - * @return a new list object containing the output folder paths. - * @throws CoreException - */ - private List<String> getProjectJavaOutputs(List<IJavaProject> referencedJavaProjects) - throws CoreException { - ArrayList<String> list = new ArrayList<String>(); - - IWorkspace ws = ResourcesPlugin.getWorkspace(); - IWorkspaceRoot wsRoot = ws.getRoot(); - - for (IJavaProject javaProject : referencedJavaProjects) { - // only include output from non android referenced project - // (This is to handle the case of reference Android projects in the context of - // instrumentation projects that need to reference the projects to be tested). - if (javaProject.getProject().hasNature(AdtConstants.NATURE_DEFAULT) == false) { - // get the output folder - IPath path = null; - try { - path = javaProject.getOutputLocation(); - } catch (JavaModelException e) { - continue; - } - - IResource outputResource = wsRoot.findMember(path); - if (outputResource != null && outputResource.getType() == IResource.FOLDER) { - String outputOsPath = outputResource.getLocation().toOSString(); - - list.add(outputOsPath); + mCompiledCodePaths.add(osFullPath); } } } - - return list; } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/DexWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/DexWrapper.java index e03a150..015d230 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/DexWrapper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/DexWrapper.java @@ -30,6 +30,7 @@ import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.util.Collection; /** * Wrapper to access dx.jar through reflection. @@ -144,7 +145,7 @@ public final class DexWrapper { * @return the integer return code of com.android.dx.command.dexer.Main.run() * @throws CoreException */ - public synchronized int run(String osOutFilePath, String[] osFilenames, + public synchronized int run(String osOutFilePath, Collection<String> osFilenames, boolean verbose, PrintStream outStream, PrintStream errStream) throws CoreException { assert mRunMethod != null; @@ -171,7 +172,7 @@ public final class DexWrapper { // create the Arguments object. Object args = mArgConstructor.newInstance(); mArgOutName.set(args, osOutFilePath); - mArgFileNames.set(args, osFilenames); + mArgFileNames.set(args, osFilenames.toArray(new String[osFilenames.size()])); mArgJarOutput.set(args, false); mArgVerbose.set(args, verbose); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java index 6c4eca4..1c4f3f3 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java @@ -38,6 +38,7 @@ import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.ide.eclipse.adt.io.IFileWrapper; import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.sdklib.SdkConstants; +import com.android.sdklib.build.ApkBuilder; import com.android.sdklib.build.ApkCreationException; import com.android.sdklib.build.DuplicateFileException; import com.android.sdklib.build.IArchiveBuilder; @@ -68,6 +69,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.jar.Attributes; @@ -427,7 +429,8 @@ public class PostCompilerBuilder extends BaseBuilder { BuildHelper helper = new BuildHelper(project, mOutStream, mErrStream, true /*debugMode*/, - AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE); + AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE, + mResourceMarker); updateCrunchCache(project, helper); // refresh recursively bin/res folder @@ -542,7 +545,8 @@ public class PostCompilerBuilder extends BaseBuilder { BuildHelper helper = new BuildHelper(project, mOutStream, mErrStream, true /*debugMode*/, - AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE); + AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE, + mResourceMarker); // resource to the AndroidManifest.xml file IFile manifestFile = project.getFile(SdkConstants.FN_ANDROID_MANIFEST_XML); @@ -637,8 +641,7 @@ public class PostCompilerBuilder extends BaseBuilder { System.out.println("\trunning dex!"); } try { - String[] dxInputPaths = helper.getCompiledCodePaths( - true /*includeProjectOutputs*/, mResourceMarker); + Collection<String> dxInputPaths = helper.getCompiledCodePaths(); helper.executeDx(javaProject, dxInputPaths, classesDexPath); } catch (DexException e) { @@ -920,8 +923,8 @@ public class PostCompilerBuilder extends BaseBuilder { // write the class files writeClassFilesIntoJar(jarBuilder, javaOutputFolder, javaOutputFolder); - // now write the standard Java resources - BuildHelper.writeResources(jarBuilder, JavaCore.create(project)); + // now write the standard Java resources from the output folder + ApkBuilder.addSourceFolder(jarBuilder, javaOutputFolder.getLocation().toFile()); saveProjectBooleanProperty(PROPERTY_CONVERT_TO_DEX, mConvertToDex); } catch (Exception e) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java index 93fe43d..c95569b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java @@ -62,6 +62,8 @@ import java.io.OutputStream; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; @@ -122,7 +124,8 @@ public final class ExportHelper { BuildHelper helper = new BuildHelper(project, fakeStream, fakeStream, - debugMode, false /*verbose*/); + debugMode, false /*verbose*/, + null /*resourceMarker*/); // get the list of library projects ProjectState projectState = Sdk.getProjectState(project); @@ -200,44 +203,46 @@ public final class ExportHelper { } } - String[] dxInput; + Collection<String> dxInput; if (runProguard) { - // the output of the main project (and any java-only project dependency) - String[] projectOutputs = helper.getProjectJavaOutputs(); + // get all the compiled code paths. This will contain both project output + // folder and jar files. + Collection<String> paths = helper.getCompiledCodePaths(); - // create a jar from the output of these projects + // create a jar file containing all the project output (as proguard cannot + // process folders of .class files). File inputJar = File.createTempFile(TEMP_PREFIX, AdtConstants.DOT_JAR); inputJar.deleteOnExit(); - JarOutputStream jos = new JarOutputStream(new FileOutputStream(inputJar)); - for (String po : projectOutputs) { - File root = new File(po); - if (root.exists()) { + + // a list of the other paths (jar files.) + List<String> jars = new ArrayList<String>(); + + for (String path : paths) { + File root = new File(path); + if (root.isDirectory()) { addFileToJar(jos, root, root); + } else if (root.isFile()) { + jars.add(path); } } jos.close(); - // get the other jar files - String[] jarFiles = helper.getCompiledCodePaths(false /*includeProjectOutputs*/, - null /*resourceMarker*/); - // destination file for proguard File obfuscatedJar = File.createTempFile(TEMP_PREFIX, AdtConstants.DOT_JAR); obfuscatedJar.deleteOnExit(); // run proguard - helper.runProguard(proguardConfigFiles, inputJar, jarFiles, obfuscatedJar, + helper.runProguard(proguardConfigFiles, inputJar, jars, obfuscatedJar, new File(project.getLocation().toFile(), SdkConstants.FD_PROGUARD)); // dx input is proguard's output - dxInput = new String[] { obfuscatedJar.getAbsolutePath() }; + dxInput = Collections.singletonList(obfuscatedJar.getAbsolutePath()); } else { // no proguard, simply get all the compiled code path: project output(s) + // jar file(s) - dxInput = helper.getCompiledCodePaths(true /*includeProjectOutputs*/, - null /*resourceMarker*/); + dxInput = helper.getCompiledCodePaths(); } IJavaProject javaProject = JavaCore.create(project); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java index f17f0c5..c2f2510 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java @@ -214,17 +214,16 @@ public class LibraryClasspathContainerInitializer extends BaseClasspathContainer entries.add(entry); - // now, gather the content of this library project's libs folder. + // process all of the library project's dependencies + getDependencyListFromClasspath(libProject, refProjects, jarFiles, true); + // and the content of its libs folder. getJarListFromLibsFolder(libProject, jarFiles); } - - // get project dependencies - getDependencyListFromClasspath(libProject, refProjects, jarFiles, false); } - // now process this projects' referenced projects - getDependencyListFromClasspath(iProject, refProjects, jarFiles, false); - // and its own jar files from libs + // now process this projects' referenced projects only. + processReferencedProjects(iProject, refProjects, jarFiles); + // and the content of its libs folder getJarListFromLibsFolder(iProject, jarFiles); // annotations support for older version of android @@ -311,6 +310,32 @@ public class LibraryClasspathContainerInitializer extends BaseClasspathContainer } /** + * Process reference projects from the main projects to add indirect dependencies coming + * from Java project. + * @param project the main project + * @param projects the project list to add to + * @param jarFiles the jar list to add to. + */ + private static void processReferencedProjects(IProject project, + Set<IProject> projects, Set<File> jarFiles) { + try { + IProject[] refs = project.getReferencedProjects(); + for (IProject p : refs) { + // ignore if it's an Android project, or if it's not a Java + // Project + if (p.hasNature(JavaCore.NATURE_ID) + && p.hasNature(AdtConstants.NATURE_DEFAULT) == false) { + + // process this project's dependencies + getDependencyListFromClasspath(p, projects, jarFiles, true /*includeJarFiles*/); + } + } + } catch (CoreException e) { + // can't get the referenced projects? ignore + } + } + + /** * Finds all the dependencies of a given project and add them to a project list and * a jar list. * Only classpath entries that are exported are added, and only Java project (not Android @@ -332,7 +357,10 @@ public class LibraryClasspathContainerInitializer extends BaseClasspathContainer IClasspathEntry[] classpaths = javaProject.readRawClasspath(); if (classpaths != null) { for (IClasspathEntry e : classpaths) { - processCPE(e, javaProject, wsRoot, projects, jarFiles, includeJarFiles); + // ignore entries that are not exported + if (e.isExported()) { + processCPE(e, javaProject, wsRoot, projects, jarFiles, includeJarFiles); + } } } } @@ -351,21 +379,11 @@ public class LibraryClasspathContainerInitializer extends BaseClasspathContainer IWorkspaceRoot wsRoot, Set<IProject> projects, Set<File> jarFiles, boolean includeJarFiles) { - // ignore entries that are not exported - if (entry.isExported() == false) { - return; - } - // if this is a classpath variable reference, we resolve it. if (entry.getEntryKind() == IClasspathEntry.CPE_VARIABLE) { entry = JavaCore.getResolvedClasspathEntry(entry); } - // not sure if an exported var could resolve to an exported one? - if (entry.isExported() == false) { - return; - } - if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) { IProject refProject = wsRoot.getProject(entry.getPath().lastSegment()); try { diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/build/ApkBuilder.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/build/ApkBuilder.java index 5c95b81..3c749c4 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/build/ApkBuilder.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/build/ApkBuilder.java @@ -586,12 +586,25 @@ public final class ApkBuilder implements IArchiveBuilder { throw new SealedApkException("APK is already sealed"); } + addSourceFolder(this, sourceFolder); + } + + /** + * Adds the resources from a source folder to a given {@link IArchiveBuilder} + * @param sourceFolder the source folder. + * @throws ApkCreationException if an error occurred + * @throws SealedApkException if the APK is already sealed. + * @throws DuplicateFileException if a file conflicts with another already added to the APK + * at the same location inside the APK archive. + */ + public static void addSourceFolder(IArchiveBuilder builder, File sourceFolder) + throws ApkCreationException, DuplicateFileException { if (sourceFolder.isDirectory()) { try { // file is a directory, process its content. File[] files = sourceFolder.listFiles(); for (File file : files) { - processFileForResource(file, null); + processFileForResource(builder, file, null); } } catch (DuplicateFileException e) { throw e; @@ -799,9 +812,11 @@ public final class ApkBuilder implements IArchiveBuilder { * @throws IOException * @throws DuplicateFileException if a file conflicts with another already added * to the APK at the same location inside the APK archive. + * @throws SealedApkException if the APK is already sealed. + * @throws ApkCreationException if an error occurred */ - private void processFileForResource(File file, String path) - throws IOException, DuplicateFileException { + private static void processFileForResource(IArchiveBuilder builder, File file, String path) + throws IOException, DuplicateFileException, ApkCreationException, SealedApkException { if (file.isDirectory()) { // a directory? we check it if (checkFolderForPackaging(file.getName())) { @@ -815,7 +830,7 @@ public final class ApkBuilder implements IArchiveBuilder { // and process its content. File[] files = file.listFiles(); for (File contentFile : files) { - processFileForResource(contentFile, path); + processFileForResource(builder, contentFile, path); } } } else { @@ -829,7 +844,7 @@ public final class ApkBuilder implements IArchiveBuilder { } // and add it to the apk - doAddFile(file, path); + builder.addFile(file, path); } } } |