aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2010-06-01 17:00:00 -0700
committerXavier Ducrohet <xav@android.com>2010-06-01 19:43:02 -0700
commit8de0ba71344622c7bbe6699a6adb0804e0cd7094 (patch)
tree4365d6b28ab5be4898dd938dd3913074b82a118c
parentfd6c6d2a823a98d885d24626c520a242d40fb9a2 (diff)
downloadsdk-8de0ba71344622c7bbe6699a6adb0804e0cd7094.zip
sdk-8de0ba71344622c7bbe6699a6adb0804e0cd7094.tar.gz
sdk-8de0ba71344622c7bbe6699a6adb0804e0cd7094.tar.bz2
Add support for soft properties in multi-apk export.
First property is split by density. Change-Id: If68bc520ba4014bbd4b144e71d54bc1161a56e2d
-rw-r--r--anttasks/src/com/android/ant/MultiApkExportTask.java256
-rw-r--r--changes.txt5
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/ApkData.java106
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java30
4 files changed, 261 insertions, 136 deletions
diff --git a/anttasks/src/com/android/ant/MultiApkExportTask.java b/anttasks/src/com/android/ant/MultiApkExportTask.java
index 8c9ee3e..fd33b92 100644
--- a/anttasks/src/com/android/ant/MultiApkExportTask.java
+++ b/anttasks/src/com/android/ant/MultiApkExportTask.java
@@ -36,6 +36,9 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
@@ -49,6 +52,7 @@ import javax.xml.xpath.XPathFactory;
public class MultiApkExportTask extends Task {
private Target mTarget;
+ private XPathFactory mXPathFactory;
public void setTarget(String target) {
mTarget = Target.getTarget(target);
@@ -105,9 +109,10 @@ public class MultiApkExportTask extends Task {
// some temp var used by the project loop
HashSet<String> compiledProject = new HashSet<String>();
- XPathFactory xPathFactory = XPathFactory.newInstance();
+ mXPathFactory = XPathFactory.newInstance();
- File exportProjectOutput = new File(getValidatedProperty(antProject, "out.absolute.dir"));
+ File exportProjectOutput = new File(getValidatedProperty(antProject,
+ "out.absolute.dir"));
// if there's no error, and we can sign, prompt for the passwords.
String keyStorePassword = null;
@@ -135,105 +140,21 @@ public class MultiApkExportTask extends Task {
}
for (ApkData apk : apks) {
- // this output is prepended by "[android-export] " (17 chars), so we put 61 stars
- System.out.println("\n*************************************************************");
- System.out.println("Exporting project: " + apk.getRelativePath());
-
- SubAnt subAnt = new SubAnt();
- subAnt.setTarget(mTarget.getTarget());
- subAnt.setProject(antProject);
-
- File subProjectFolder = new File(antProject.getBaseDir(), apk.getRelativePath());
-
- FileSet fileSet = new FileSet();
- fileSet.setProject(antProject);
- fileSet.setDir(subProjectFolder);
- fileSet.setIncludes("build.xml");
- subAnt.addFileset(fileSet);
-
- // subAnt.setVerbose(true);
-
- if (mTarget == Target.RELEASE) {
- // only do the compilation part if it's the first time we export
- // this project.
- // (projects can be export multiple time if some properties are set up to
- // generate more than one APK (for instance ABI split).
- if (compiledProject.contains(apk.getRelativePath()) == false) {
- compiledProject.add(apk.getRelativePath());
- } else {
- addProp(subAnt, "do.not.compile", "true");
- }
-
- // set the version code, and filtering
- int compositeVersionCode = apk.getCompositeVersionCode(versionCode);
- addProp(subAnt, "version.code", Integer.toString(compositeVersionCode));
- System.out.println("Composite versionCode: " + compositeVersionCode);
- String abi = apk.getAbi();
- if (abi != null) {
- addProp(subAnt, "filter.abi", abi);
- System.out.println("ABI Filter: " + abi);
- }
-
- // end of the output by this task. Everything that follows will be output
- // by the subant.
- System.out.println("Calling to project's Ant file...");
- System.out.println("----------\n");
-
- // set the output file names/paths. Keep all the temporary files in the project
- // folder, and only put the final file (which is different depending on whether
- // the file can be signed) locally.
-
- // read the base name from the build.xml file.
- String name = null;
- try {
- File buildFile = new File(subProjectFolder, "build.xml");
- XPath xPath = xPathFactory.newXPath();
- name = xPath.evaluate("/project/@name",
- new InputSource(new FileInputStream(buildFile)));
- } catch (XPathExpressionException e) {
- throw new BuildException("Failed to read build.xml", e);
- } catch (FileNotFoundException e) {
- throw new BuildException("build.xml is missing.", e);
- }
-
- // override the resource pack file.
- addProp(subAnt, "resource.package.file.name",
- name + "-" + apk.getBuildInfo() + ".ap_");
-
- if (canSign) {
- // set the properties for the password.
- addProp(subAnt, "key.store", keyStore);
- addProp(subAnt, "key.alias", keyAlias);
- addProp(subAnt, "key.store.password", keyStorePassword);
- addProp(subAnt, "key.alias.password", keyAliasPassword);
-
- // temporary file only get a filename change (still stored in the project
- // bin folder).
- addProp(subAnt, "out.unsigned.file.name",
- name + "-" + apk.getBuildInfo() + "-unsigned.apk");
- addProp(subAnt, "out.unaligned.file",
- name + "-" + apk.getBuildInfo() + "-unaligned.apk");
-
- // final file is stored locally.
- apk.setOutputName(name + "-" + compositeVersionCode + "-release.apk");
- addProp(subAnt, "out.release.file", new File(exportProjectOutput,
- apk.getOutputName()).getAbsolutePath());
-
- } else {
- // put some empty prop. This is to override possible ones defined in the
- // project. The reason is that if there's more than one project, we don't
- // want some to signed and some not to be (and we don't want each project
- // to prompt for password.)
- addProp(subAnt, "key.store", "");
- addProp(subAnt, "key.alias", "");
- // final file is the unsigned version. It gets stored locally.
- apk.setOutputName(name + "-" + compositeVersionCode + "-unsigned.apk");
- addProp(subAnt, "out.unsigned.file", new File(exportProjectOutput,
- apk.getOutputName()).getAbsolutePath());
- }
+
+ Map<String, String> variantMap = apk.getSoftVariantMap();
+
+ // first, do the full export.
+ makeSubAnt(antProject, appPackage, versionCode, apk, null,
+ exportProjectOutput, canSign, keyStore, keyAlias,
+ keyStorePassword, keyAliasPassword, compiledProject);
+
+ // then do the soft variants.
+ for (Entry<String, String> entry : variantMap.entrySet()) {
+ makeSubAnt(antProject, appPackage, versionCode, apk, entry,
+ exportProjectOutput, canSign, keyStore, keyAlias,
+ keyStorePassword, keyAliasPassword, compiledProject);
}
- subAnt.execute();
}
if (mTarget == Target.RELEASE) {
@@ -248,6 +169,143 @@ public class MultiApkExportTask extends Task {
}
/**
+ * Creates and executes a sub ant task.
+ * @param antProject the current Ant project
+ * @param appPackage the application package string.
+ * @param versionCode the current version of the application
+ * @param apk the {@link ApkData} being exported.
+ * @param softVariant the soft variant being exported, or null, if this is a full export.
+ * @param exportProjectOutput the folder in which the files must be exported.
+ * @param canSign whether the application package can be signed. This is dependent on the
+ * availability of some required values.
+ * @param keyStore the path to the keystore for signing
+ * @param keyAlias the alias of the key to be used for signing
+ * @param keyStorePassword the password of the keystore for signing
+ * @param keyAliasPassword the password of the key alias for signing
+ * @param compiledProject a list of projects that have already been compiled.
+ */
+ private void makeSubAnt(Project antProject, String appPackage, int versionCode,
+ ApkData apk, Entry<String, String> softVariant, File exportProjectOutput,
+ boolean canSign, String keyStore, String keyAlias,
+ String keyStorePassword, String keyAliasPassword, Set<String> compiledProject) {
+
+ // this output is prepended by "[android-export] " (17 chars), so we put 61 stars
+ System.out.println("\n*************************************************************");
+ System.out.println("Exporting project: " + apk.getRelativePath());
+
+ SubAnt subAnt = new SubAnt();
+ subAnt.setTarget(mTarget.getTarget());
+ subAnt.setProject(antProject);
+
+ File subProjectFolder = new File(antProject.getBaseDir(), apk.getRelativePath());
+
+ FileSet fileSet = new FileSet();
+ fileSet.setProject(antProject);
+ fileSet.setDir(subProjectFolder);
+ fileSet.setIncludes("build.xml");
+ subAnt.addFileset(fileSet);
+
+// subAnt.setVerbose(true);
+
+ if (mTarget == Target.RELEASE) {
+ // only do the compilation part if it's the first time we export
+ // this project.
+ // (projects can be export multiple time if some properties are set up to
+ // generate more than one APK (for instance ABI split).
+ if (compiledProject.contains(apk.getRelativePath()) == false) {
+ compiledProject.add(apk.getRelativePath());
+ } else {
+ addProp(subAnt, "do.not.compile", "true");
+ }
+
+ // set the version code, and filtering
+ int compositeVersionCode = apk.getCompositeVersionCode(versionCode);
+ addProp(subAnt, "version.code", Integer.toString(compositeVersionCode));
+ System.out.println("Composite versionCode: " + compositeVersionCode);
+ String abi = apk.getAbi();
+ if (abi != null) {
+ addProp(subAnt, "filter.abi", abi);
+ System.out.println("ABI Filter: " + abi);
+ }
+
+ // set the output file names/paths. Keep all the temporary files in the project
+ // folder, and only put the final file (which is different depending on whether
+ // the file can be signed) locally.
+
+ // read the base name from the build.xml file.
+ String name = null;
+ try {
+ File buildFile = new File(subProjectFolder, "build.xml");
+ XPath xPath = mXPathFactory.newXPath();
+ name = xPath.evaluate("/project/@name",
+ new InputSource(new FileInputStream(buildFile)));
+ } catch (XPathExpressionException e) {
+ throw new BuildException("Failed to read build.xml", e);
+ } catch (FileNotFoundException e) {
+ throw new BuildException("build.xml is missing.", e);
+ }
+
+ // override the resource pack file as well as the final name
+ String pkgName = name + "-" + apk.getBuildInfo();
+ String finalNameRoot = appPackage + "-" + compositeVersionCode;
+ if (softVariant != null) {
+ String tmp = "-" + softVariant.getKey();
+ pkgName += tmp;
+ finalNameRoot += tmp;
+
+ // set the resource filter.
+ addProp(subAnt, "aapt.resource.filter", softVariant.getValue());
+ System.out.println("res Filter: " + softVariant.getValue());
+ }
+
+ // set the resource pack file name.
+ addProp(subAnt, "resource.package.file.name", pkgName + ".ap_");
+
+
+ if (canSign) {
+ // set the properties for the password.
+ addProp(subAnt, "key.store", keyStore);
+ addProp(subAnt, "key.alias", keyAlias);
+ addProp(subAnt, "key.store.password", keyStorePassword);
+ addProp(subAnt, "key.alias.password", keyAliasPassword);
+
+ // temporary file only get a filename change (still stored in the project
+ // bin folder).
+ addProp(subAnt, "out.unsigned.file.name",
+ name + "-" + apk.getBuildInfo() + "-unsigned.apk");
+ addProp(subAnt, "out.unaligned.file",
+ name + "-" + apk.getBuildInfo() + "-unaligned.apk");
+
+ // final file is stored locally with a name based on the package
+ String outputName = finalNameRoot + "-release.apk";
+ apk.setOutputName(softVariant != null ? softVariant.getKey() : null, outputName);
+ addProp(subAnt, "out.release.file",
+ new File(exportProjectOutput, outputName).getAbsolutePath());
+
+ } else {
+ // put some empty prop. This is to override possible ones defined in the
+ // project. The reason is that if there's more than one project, we don't
+ // want some to signed and some not to be (and we don't want each project
+ // to prompt for password.)
+ addProp(subAnt, "key.store", "");
+ addProp(subAnt, "key.alias", "");
+ // final file is the unsigned version. It gets stored locally.
+ String outputName = finalNameRoot + "-unsigned.apk";
+ apk.setOutputName(softVariant != null ? softVariant.getKey() : null, outputName);
+ addProp(subAnt, "out.unsigned.file",
+ new File(exportProjectOutput, outputName).getAbsolutePath());
+ }
+ }
+
+ // end of the output by this task. Everything that follows will be output
+ // by the subant.
+ System.out.println("Calling to project's Ant file...");
+ System.out.println("----------\n");
+
+ subAnt.execute();
+ }
+
+ /**
* Gets, validates and returns a project property.
* The property must exist and be non empty.
* @param antProject the project
diff --git a/changes.txt b/changes.txt
index 9bf3a57..282a98b 100644
--- a/changes.txt
+++ b/changes.txt
@@ -4,8 +4,11 @@ Revision 7:
- Support for Ant rules provided by the Tools components (override the one in
the platform component)
- Headless SDK update. See 'android -h update sdk' for more info.
+- Support for extension targets in Ant build to perform tasks between the
+ normal tasks: -pre-build, -pre-compile, -post-compile.
-Revision 6:
+
+Revision 6 (05/2010)
- Support for library project to share code/resources among projects
- Updated Ant rules and custom tasks
- New "android create lib-project", "android update lib-project" actions.
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/ApkData.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/ApkData.java
index f2cd97f..c32a360 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/ApkData.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/ApkData.java
@@ -39,8 +39,9 @@ public class ApkData implements Comparable<ApkData> {
private static final String PROP_PROJECT = "project";
private static final String PROP_MINOR = "minor";
private static final String PROP_BUILDINFO = "buildinfo";
+ private static final String PROP_DENSITY = "splitDensity";
- private String mOutputName;
+ private final HashMap<String, String> mOutputNames = new HashMap<String, String>();
private String mRelativePath;
private File mProject;
private int mBuildInfo;
@@ -52,6 +53,9 @@ public class ApkData implements Comparable<ApkData> {
private int mGlVersion = ManifestData.GL_ES_VERSION_NOT_SET;
private SupportsScreens mSupportsScreens;
+ // additional apk generation that doesn't impact the build info.
+ private boolean mSplitDensity;
+
ApkData() {
// do nothing.
}
@@ -73,12 +77,12 @@ public class ApkData implements Comparable<ApkData> {
mSupportsScreens = data.mSupportsScreens;
}
- public String getOutputName() {
- return mOutputName;
+ public String getOutputName(String key) {
+ return mOutputNames.get(key);
}
- public void setOutputName(String outputName) {
- mOutputName = outputName;
+ public void setOutputName(String key, String outputName) {
+ mOutputNames.put(key, outputName);
}
public String getRelativePath() {
@@ -146,28 +150,59 @@ public class ApkData implements Comparable<ApkData> {
return trueVersionCode;
}
+ public void setSplitDensity(boolean splitDensity) {
+ mSplitDensity = splitDensity;
+ }
+
+ public boolean isSplitDensity() {
+ return mSplitDensity;
+ }
+
+ /**
+ * Returns a map of pair values (apk name suffix, aapt res filter) to be used to generate
+ * multiple soft apk variants.
+ */
+ public Map<String, String> getSoftVariantMap() {
+ HashMap<String, String> map = new HashMap<String, String>();
+ if (mSplitDensity) {
+ map.put("hdpi", "hdpi,nodpi");
+ map.put("mdpi", "mdpi,nodpi");
+ map.put("ldpi", "ldpi,nodpi");
+ }
+
+ return map;
+ }
+
@Override
public String toString() {
- return getLogLine();
+ return getLogLine(null);
}
- public String getLogLine() {
+ public String getLogLine(String key) {
StringBuilder sb = new StringBuilder();
- sb.append(mOutputName).append(':');
- write(sb, PROP_BUILDINFO, mBuildInfo);
- write(sb, PROP_MINOR, mMinor);
- write(sb, PROP_PROJECT, mRelativePath);
- write(sb, PROP_API, mMinSdkVersion);
+ sb.append(getOutputName(key)).append(':');
+ if (key == null) {
+ write(sb, PROP_BUILDINFO, mBuildInfo);
+ write(sb, PROP_MINOR, mMinor);
+ write(sb, PROP_PROJECT, mRelativePath);
+ write(sb, PROP_API, mMinSdkVersion);
+
+ if (mGlVersion != ManifestData.GL_ES_VERSION_NOT_SET) {
+ write(sb, PROP_GL, "0x" + Integer.toHexString(mGlVersion));
+ }
- if (mGlVersion != ManifestData.GL_ES_VERSION_NOT_SET) {
- write(sb, PROP_GL, "0x" + Integer.toHexString(mGlVersion));
- }
+ if (mAbi != null) {
+ write(sb, PROP_ABI, mAbi);
+ }
- if (mAbi != null) {
- write(sb, PROP_ABI, mAbi);
- }
+ if (mSplitDensity) {
+ write(sb, PROP_DENSITY, Boolean.toString(true));
+ }
- write(sb, PROP_SCREENS, mSupportsScreens.getEncodedValues());
+ write(sb, PROP_SCREENS, mSupportsScreens.getEncodedValues());
+ } else {
+ write(sb, "resources", getSoftVariantMap().get(key));
+ }
return sb.toString();
}
@@ -210,7 +245,8 @@ public class ApkData implements Comparable<ApkData> {
public boolean hasSameApkProperties(ApkData apk) {
if (mMinSdkVersion != apk.mMinSdkVersion ||
mSupportsScreens.equals(apk.mSupportsScreens) == false ||
- mGlVersion != apk.mGlVersion) {
+ mGlVersion != apk.mGlVersion ||
+ mSplitDensity != apk.mSplitDensity) {
return false;
}
@@ -233,7 +269,7 @@ public class ApkData implements Comparable<ApkData> {
*/
public void initFromLogLine(String line) {
int colon = line.indexOf(':');
- mOutputName = line.substring(0, colon);
+ mOutputNames.put(null, line.substring(0, colon));
String[] properties = line.substring(colon+1).split(";");
HashMap<String, String> map = new HashMap<String, String>();
for (String prop : properties) {
@@ -244,19 +280,25 @@ public class ApkData implements Comparable<ApkData> {
}
private void setValues(Map<String, String> values) {
- mBuildInfo = Integer.parseInt(values.get(PROP_BUILDINFO));
- mMinor = Integer.parseInt(values.get(PROP_MINOR));
- mRelativePath = values.get(PROP_PROJECT);
- mMinSdkVersion = Integer.parseInt(values.get(PROP_API));
+ String tmp;
+ try {
+ mBuildInfo = Integer.parseInt(values.get(PROP_BUILDINFO));
+ mMinor = Integer.parseInt(values.get(PROP_MINOR));
+ mRelativePath = values.get(PROP_PROJECT);
+ mMinSdkVersion = Integer.parseInt(values.get(PROP_API));
+
+ tmp = values.get(PROP_GL);
+ if (tmp != null) {
+ mGlVersion = Integer.decode(tmp);
+ }
+ } catch (NumberFormatException e) {
+ // pass. This is probably due to a manual edit, and it'll most likely
+ // generate an error when matching the log to the current setup.
+ }
- String tmp = values.get(PROP_GL);
+ tmp = values.get(PROP_DENSITY);
if (tmp != null) {
- try {
- mGlVersion = Integer.decode(tmp);
- } catch (NumberFormatException e) {
- // pass. This is probably due to a manual edit, and it'll most likely
- // generate an error when matching the log to the current setup.
- }
+ mSplitDensity = Boolean.valueOf(tmp);
}
tmp = values.get(PROP_ABI);
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java
index ae70e6f..9340c47 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java
@@ -40,6 +40,7 @@ import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
@@ -175,8 +176,20 @@ public class MultiApkExportHelper {
"# Properties are written as <name>=<value>\n");
for (ApkData apk : apks) {
- writer.append(apk.getLogLine());
+ writer.append(apk.getLogLine(null));
writer.append('\n');
+
+ // display the soft variant as comments to the log file.
+ Map<String, String> softVariants = apk.getSoftVariantMap();
+ if (softVariants.size() > 0) {
+ writer.append("# Soft Variants -- DO NOT UNCOMMENT:\n");
+ }
+
+ for (String softVariant : softVariants.keySet()) {
+ writer.append("# ");
+ writer.append(apk.getLogLine(softVariant));
+ writer.append('\n');
+ }
}
writer.flush();
@@ -241,7 +254,7 @@ public class MultiApkExportHelper {
SdkConstants.FN_ANDROID_MANIFEST_XML));
}
- ArrayList<ApkData> datalist2 = checkManifest(androidManifest, manifests);
+ ArrayList<ApkData> datalist2 = checkProject(androidManifest, manifests);
// if the method returns without throwing, this is a good project to
// export.
@@ -268,7 +281,9 @@ public class MultiApkExportHelper {
}
/**
- * Checks a manifest of the project for inclusion in the list of exported APK.
+ * Checks a project inclusion in the list of exported APK.
+ * <p/>This performs a check on the manifest, as well as gathers more information about
+ * mutli-apk from the project's default.properties file.
* If the manifest is correct, a list of apk to export is created and returned.
*
* @param androidManifest the manifest to check
@@ -277,7 +292,7 @@ public class MultiApkExportHelper {
* @return A new non-null {@link ArrayList} of {@link ApkData}.
* @throws ExportException in case of error.
*/
- private ArrayList<ApkData> checkManifest(IAbstractFile androidManifest,
+ private ArrayList<ApkData> checkProject(IAbstractFile androidManifest,
ArrayList<Manifest> manifests) throws ExportException {
try {
ManifestData manifestData = AndroidManifestParser.parse(androidManifest);
@@ -398,6 +413,13 @@ public class MultiApkExportHelper {
current = null;
}
}
+
+ if (apkSettings.isSplitByDensity()) {
+ // set this value for all the apk that were create.
+ for (ApkData apk : dataList) {
+ apk.setSplitDensity(true);
+ }
+ }
}
return dataList;