diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java | 374 |
1 files changed, 111 insertions, 263 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..e8fbadb 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,12 @@ import java.io.PrintStream; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.TreeMap; /** @@ -108,11 +105,14 @@ 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; private final static int MILLION = 1000000; + private String mProguardFile; /** * An object able to put a marker on a resource. @@ -128,14 +128,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 +173,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. @@ -287,9 +286,7 @@ public class BuildHelper { * @param intermediateApk The path to the temporary resource file. * @param dex The path to the dex file. * @param output The path to the final package file to create. - * @param javaProject the java project being compiled * @param libProjects an optional list of library projects (can be null) - * @param referencedJavaProjects referenced projects. * @return true if success, false otherwise. * @throws ApkCreationException * @throws AndroidLocationException @@ -299,8 +296,7 @@ public class BuildHelper { * @throws DuplicateFileException */ public void finalDebugPackage(String intermediateApk, String dex, String output, - final IJavaProject javaProject, List<IProject> libProjects, - List<IJavaProject> referencedJavaProjects, ResourceMarker resMarker) + List<IProject> libProjects, ResourceMarker resMarker) throws ApkCreationException, KeytoolException, AndroidLocationException, NativeLibInJarException, DuplicateFileException, CoreException { @@ -324,8 +320,7 @@ public class BuildHelper { // from the keystore, get the signing info SigningInfo info = ApkBuilder.getDebugKey(keystoreOsPath, mVerbose ? mOutStream : null); - finalPackage(intermediateApk, dex, output, javaProject, libProjects, - referencedJavaProjects, + finalPackage(intermediateApk, dex, output, libProjects, info != null ? info.key : null, info != null ? info.certificate : null, resMarker); } @@ -341,9 +336,7 @@ public class BuildHelper { * @param dex The path to the dex file. * @param output The path to the final package file to create. * @param debugSign whether the apk must be signed with the debug key. - * @param javaProject the java project being compiled * @param libProjects an optional list of library projects (can be null) - * @param referencedJavaProjects referenced projects. * @param abiFilter an optional filter. If not null, then only the matching ABI is included in * the final archive * @return true if success, false otherwise. @@ -353,9 +346,8 @@ public class BuildHelper { * @throws DuplicateFileException */ public void finalPackage(String intermediateApk, String dex, String output, - final IJavaProject javaProject, List<IProject> libProjects, - List<IJavaProject> referencedJavaProjects, PrivateKey key, - X509Certificate certificate, ResourceMarker resMarker) + List<IProject> libProjects, + PrivateKey key, X509Certificate certificate, ResourceMarker resMarker) throws NativeLibInJarException, ApkCreationException, DuplicateFileException, CoreException { @@ -365,19 +357,24 @@ 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); + // either use the full compiled code paths or just the proguard file + // if present + Collection<String> pathsCollection = mCompiledCodePaths; + if (mProguardFile != null) { + pathsCollection = Collections.singletonList(mProguardFile); + mProguardFile = null; + } - // 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 : pathsCollection) { + 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 +415,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 +450,15 @@ 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()]); + public void setProguardOutput(String proguardFile) { + mProguardFile = proguardFile; } - /** - * 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 +669,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 +864,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 +965,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 +973,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 +990,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,53 +998,13 @@ public class BuildHelper { File f = new File(osFullPath); if (f.isDirectory()) { - oslibraryList.add(osFullPath); + mCompiledCodePaths.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); - } - } - } - - return list; - } - - /** * Checks a {@link IFile} to make sure it should be packaged as standard resources. * @param file the IFile representing the file. * @return true if the file should be packaged as standard java resources. |