aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2012-12-04 18:34:43 -0800
committerXavier Ducrohet <xav@android.com>2012-12-05 12:52:20 -0800
commit131459f779b1a98bf354663298637589a2860fff (patch)
treecd6cda7e03b6872ba200a0412cbc71f5ca80fba9
parent0f9622decc4d8ff78dc7ea6ca703841db44d69b7 (diff)
downloadsdk-131459f779b1a98bf354663298637589a2860fff.zip
sdk-131459f779b1a98bf354663298637589a2860fff.tar.gz
sdk-131459f779b1a98bf354663298637589a2860fff.tar.bz2
Support package conflicts between app and libs.
If 2 libraries share the same package name, this is now a build breakage, unless all libraries share the same package. This is because the libraries R classes only contain the resources declared in the libraries themselves (plus dependencies). Since two libraries could share the same package name without depending on one another, it's possible to ensure that creating only one R class would work for both. (Merging both R class might be possible but is too risky for a quick fix like this). If all the libraries share the same package, then a single R class is created for that package that contains all the symbol of the app (simpler than merging all the symbols for now) If a library and the app share the same package name, the R class for the library is not created (since the R class for the app contains all resources). This already worked in ADT, so this changeset only fixes Ant. Change-Id: I95f0b734ba263051961268d960d59749f5b6e1a5
-rw-r--r--anttasks/src/com/android/ant/AaptExecTask.java148
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java93
-rw-r--r--files/ant/build.xml1
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}"