diff options
author | Xavier Ducrohet <xav@android.com> | 2012-12-05 13:13:56 -0800 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2012-12-05 13:13:57 -0800 |
commit | 6ecf7a13a0dc2dbdfce71c83d86328ec6c80b6e3 (patch) | |
tree | cdc2831e9f4ed14f08aae925fdef0444908a8016 | |
parent | cb7a3df75e1fbef2f4de805f65008e0bbd085217 (diff) | |
parent | 131459f779b1a98bf354663298637589a2860fff (diff) | |
download | sdk-6ecf7a13a0dc2dbdfce71c83d86328ec6c80b6e3.zip sdk-6ecf7a13a0dc2dbdfce71c83d86328ec6c80b6e3.tar.gz sdk-6ecf7a13a0dc2dbdfce71c83d86328ec6c80b6e3.tar.bz2 |
Merge "Support package conflicts between app and libs."
3 files changed, 210 insertions, 32 deletions
diff --git a/anttasks/src/com/android/ant/AaptExecTask.java b/anttasks/src/com/android/ant/AaptExecTask.java index 2511500..f07cfad 100644 --- a/anttasks/src/com/android/ant/AaptExecTask.java +++ b/anttasks/src/com/android/ant/AaptExecTask.java @@ -19,19 +19,30 @@ package com.android.ant; import com.android.SdkConstants; import com.android.sdklib.internal.build.SymbolLoader; import com.android.sdklib.internal.build.SymbolWriter; +import com.android.xml.AndroidXPathFactory; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.ExecTask; import org.apache.tools.ant.types.Path; +import org.xml.sax.InputSource; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpressionException; + /** * Task to execute aapt. * @@ -88,8 +99,9 @@ public final class AaptExecTask extends SingleDependencyTask { private boolean mUseCrunchCache = false; private int mVersionCode = 0; private String mVersionName; - private String mManifest; + private String mManifestFile; private String mManifestPackage; + private String mOriginalManifestPackage; private ArrayList<Path> mResources; private String mAssets; private String mAndroidJar; @@ -223,7 +235,7 @@ public final class AaptExecTask extends SingleDependencyTask { * @param manifest the value. */ public void setManifest(Path manifest) { - mManifest = TaskHelper.checkSinglePath("manifest", manifest); + mManifestFile = TaskHelper.checkSinglePath("manifest", manifest); } /** @@ -241,6 +253,18 @@ public final class AaptExecTask extends SingleDependencyTask { } /** + * Sets the original package name found in the manifest. This is the package name where + * the R class is created. + * + * This is merely a shortcut in case the package is known when calling the aapt task. If not + * provided (and needed) this task will recompute it. + * @param packageName the package name declared in the manifest. + */ + public void setOriginalManifestPackage(String packageName) { + mOriginalManifestPackage = packageName; + } + + /** * Sets the value of the "resources" attribute. * @param resources the value. * @@ -446,8 +470,8 @@ public final class AaptExecTask extends SingleDependencyTask { sPathFactory); // let's not forget the manifest as an input path (with no extension restrictions). - if (mManifest != null) { - inputPaths.add(new InputPath(new File(mManifest))); + if (mManifestFile != null) { + inputPaths.add(new InputPath(new File(mManifestFile))); } // Check to see if our dependencies have changed. If not, then skip @@ -465,8 +489,8 @@ public final class AaptExecTask extends SingleDependencyTask { sPathFactory); // let's not forget the manifest as an input path. - if (mManifest != null) { - inputPaths.add(new InputPath(new File(mManifest))); + if (mManifestFile != null) { + inputPaths.add(new InputPath(new File(mManifestFile))); } // If we're here to generate a .ap_ file we need to use assets as an input path as well. @@ -586,9 +610,9 @@ public final class AaptExecTask extends SingleDependencyTask { } // manifest location - if (mManifest != null && mManifest.length() > 0) { + if (mManifestFile != null && mManifestFile.length() > 0) { task.createArg().setValue("-M"); - task.createArg().setValue(mManifest); + task.createArg().setValue(mManifestFile); } // Rename manifest package @@ -681,8 +705,9 @@ public final class AaptExecTask extends SingleDependencyTask { if (!mNonConstantId && libPkgProp != null && !libPkgProp.isEmpty()) { File rFile = new File(mBinFolder, SdkConstants.FN_RESOURCE_TEXT); if (rFile.isFile()) { - SymbolLoader symbolValues = new SymbolLoader(rFile); - symbolValues.load(); + // Load the full symbols from the full R.txt file. + SymbolLoader fullSymbols = new SymbolLoader(rFile); + fullSymbols.load(); // we have two props which contains list of items. Both items represent // 2 data of a single property. @@ -700,22 +725,105 @@ public final class AaptExecTask extends SingleDependencyTask { mLibraryPackagesRefid, mLibraryRFileRefid)); } - for (int i = 0 ; i < packages.length ; i++) { - File libRFile = new File(rFiles[i]); - if (libRFile.isFile()) { - SymbolLoader symbols = new SymbolLoader(libRFile); - symbols.load(); + if (mOriginalManifestPackage == null) { + mOriginalManifestPackage = getPackageName(mManifestFile); + } + + // simpler case of a single library + if (packages.length == 1) { + createRClass(fullSymbols, rFiles[0], packages[0]); + } else { + + Map<String, String> libPackages = Maps.newHashMapWithExpectedSize( + packages.length); + Set<String> duplicatePackages = Sets.newHashSet(); + + // preprocessing to figure out if there are dups in the package names of + // the libraries + for (int i = 0 ; i < packages.length ; i++) { + String libPackage = packages[i]; + if (mOriginalManifestPackage.equals(libPackage)) { + // skip libraries that have the same package name as the application. + continue; + } + + String existingPkg = libPackages.get(libPackage); + if (existingPkg != null) { + // record the dup package and keep going, in case there are all the same + duplicatePackages.add(libPackage); + continue; + } + + libPackages.put(libPackage, rFiles[i]); + } - SymbolWriter writer = new SymbolWriter(mRFolder, packages[i], - symbols, symbolValues); - writer.write(); + // check if we have duplicate but all files are the same. + if (duplicatePackages.size() > 0) { + // possible conflict! + // detect case of all libraries == same package. + if (duplicatePackages.size() == 1 && libPackages.size() == 1 && + duplicatePackages.iterator().next().equals(libPackages.keySet().iterator().next())) { + // this is ok, all libraries have the same package. + // Make a copy of the full R class. + SymbolWriter writer = new SymbolWriter(mRFolder, + duplicatePackages.iterator().next(), + fullSymbols, fullSymbols); + writer.write(); + } else { + StringBuilder sb = new StringBuilder(); + sb.append("The following packages have been found to be used by two or more libraries:"); + for (String pkg : duplicatePackages) { + sb.append("\n\t").append(pkg); + } + sb.append("\nNo libraries must share the same package, unless all libraries share the same packages."); + throw new BuildException(sb.toString()); + } + } else { + // no dups, all libraries have different packages. + // Conflicts with the main package have been removed already. + // Just process all the libraries from the list where we removed + // libs that had the same package as the app. + for (Entry<String, String> lib : libPackages.entrySet()) { + createRClass(fullSymbols, lib.getValue(), lib.getKey()); + } } } } } - } catch (IOException e) { - throw new BuildException(e); + } catch (Exception e) { + // HACK alert. + // in order for this step to happen again when this part fails, we delete + // the dependency file. + File f = new File(mRFolder, "R.java.d"); + f.delete(); + + throw (e instanceof BuildException) ? (BuildException)e : new BuildException(e); } + } + + private void createRClass(SymbolLoader fullSymbols, String libRTxtFile, String libPackage) + throws IOException { + File libSymbolFile = new File(libRTxtFile); + if (libSymbolFile.isFile()) { + SymbolLoader libSymbols = new SymbolLoader(libSymbolFile); + libSymbols.load(); + + SymbolWriter writer = new SymbolWriter(mRFolder, libPackage, libSymbols, fullSymbols); + writer.write(); + } + } + private String getPackageName(String manifest) { + XPath xpath = AndroidXPathFactory.newXPath(); + + try { + String s = xpath.evaluate("/manifest/@package", + new InputSource(new FileInputStream(manifest))); + return s; + } catch (XPathExpressionException e) { + throw new BuildException(e); + } catch (FileNotFoundException e) { + throw new BuildException(e); + } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java index 25b16e4..ba23c95 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java @@ -56,6 +56,8 @@ import com.android.utils.ILogger; import com.android.utils.Pair; import com.android.xml.AndroidManifest; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; @@ -78,6 +80,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Set; import javax.xml.parsers.ParserConfigurationException; @@ -1156,18 +1159,71 @@ public class PreCompilerBuilder extends BaseBuilder { File rFile = new File(outputFolder, SdkConstants.FN_RESOURCE_TEXT); // if the project has no resources, the file could not exist. if (rFile.isFile()) { - SymbolLoader symbolValues = new SymbolLoader(rFile); - symbolValues.load(); - - for (Pair<File, String> libData : libRFiles) { - File libRFile = libData.getFirst(); - if (libRFile.isFile()) { - SymbolLoader symbols = new SymbolLoader(libRFile); - symbols.load(); - - SymbolWriter writer = new SymbolWriter(osOutputPath, - libData.getSecond(), symbols, symbolValues); - writer.write(); + // Load the full symbols from the full R.txt file. + SymbolLoader fullSymbols = new SymbolLoader(rFile); + fullSymbols.load(); + + // simpler case of a single library + if (libRFiles.size() == 1) { + Pair<File, String> lib = libRFiles.get(0); + createRClass(fullSymbols, lib.getFirst(), lib.getSecond(), osOutputPath); + + } else { + Map<String, File> libPackages = Maps.newHashMapWithExpectedSize( + libRFiles.size()); + Set<String> duplicatePackages = Sets.newHashSet(); + + // preprocessing to figure out if there are dups in the package names of + // the libraries + for (Pair<File, String> lib : libRFiles) { + String libPackage = lib.getSecond(); + File existingPkg = libPackages.get(libPackage); + if (existingPkg != null) { + // record the dup package and keep going, in case there are all + // the same + duplicatePackages.add(libPackage); + continue; + } + + libPackages.put(libPackage, lib.getFirst()); + } + + // check if we have duplicate but all files are the same. + if (duplicatePackages.size() > 0) { + // possible conflict! + // detect case of all libraries == same package. + if (duplicatePackages.size() == 1 && libPackages.size() == 1 && + duplicatePackages.iterator().next().equals(libPackages.keySet().iterator().next())) { + // this is ok, all libraries have the same package. + // Make a copy of the full R class. + SymbolWriter writer = new SymbolWriter(osOutputPath, + duplicatePackages.iterator().next(), + fullSymbols, fullSymbols); + writer.write(); + } else { + StringBuilder sb = new StringBuilder(); + sb.append("The following packages have been found to be used by two or more libraries:"); + for (String pkg : duplicatePackages) { + sb.append("\n\t").append(pkg); + } + sb.append("\nNo libraries must share the same package, unless all libraries share the same packages."); + + String msg = sb.toString(); + markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); + + AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, + msg); + + throw new AbortBuildException(); + } + } else { + // no dups, all libraries have different packages. + // Conflicts with the main package have been removed already. + // Just process all the libraries. + for (Pair<File, String> lib : libRFiles) { + createRClass(fullSymbols, lib.getFirst(), lib.getSecond(), + osOutputPath); + } } } } @@ -1227,6 +1283,19 @@ public class PreCompilerBuilder extends BaseBuilder { } } + private void createRClass(SymbolLoader fullSymbols, File libRTxtFile, String libPackage, + String osOutputPath) throws IOException { + if (libRTxtFile.isFile()) { + SymbolLoader libSymbols = new SymbolLoader(libRTxtFile); + libSymbols.load(); + + SymbolWriter writer = new SymbolWriter(osOutputPath, libPackage, libSymbols, + fullSymbols); + writer.write(); + } + } + + /** * Creates a relative {@link IPath} from a java package. * @param javaPackageName the java package. diff --git a/files/ant/build.xml b/files/ant/build.xml index acba923..e3ae78a 100644 --- a/files/ant/build.xml +++ b/files/ant/build.xml @@ -674,6 +674,7 @@ command="package" verbose="${verbose}" manifest="${out.manifest.abs.file}" + originalManifestPackage="${project.app.package}" androidjar="${project.target.android.jar}" rfolder="${gen.absolute.dir}" nonConstantId="${android.library}" |