aboutsummaryrefslogtreecommitdiffstats
path: root/anttasks
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2010-05-27 12:17:50 -0700
committerXavier Ducrohet <xav@android.com>2010-05-27 15:27:53 -0700
commitac8b6fa3ca5dd744a49a312c07835328f0805eab (patch)
tree015bc2e452bde3ab75be4fb481bddd499e7e93f4 /anttasks
parentd905872a881210bf8fd01f06d7771bd02aa4001c (diff)
downloadsdk-ac8b6fa3ca5dd744a49a312c07835328f0805eab.zip
sdk-ac8b6fa3ca5dd744a49a312c07835328f0805eab.tar.gz
sdk-ac8b6fa3ca5dd744a49a312c07835328f0805eab.tar.bz2
Refactored most of the multi-apk export out of the ant task into sdklib.
This common code will be used by ADT when processing multi-apk export. I also had to improve the API for IAbstractFile/Folder. Change-Id: Ied76f0a9f068223ceaba36124e841f804febfaf8
Diffstat (limited to 'anttasks')
-rw-r--r--anttasks/src/com/android/ant/MultiApkExportTask.java625
1 files changed, 145 insertions, 480 deletions
diff --git a/anttasks/src/com/android/ant/MultiApkExportTask.java b/anttasks/src/com/android/ant/MultiApkExportTask.java
index f515076..dbc1a1d 100644
--- a/anttasks/src/com/android/ant/MultiApkExportTask.java
+++ b/anttasks/src/com/android/ant/MultiApkExportTask.java
@@ -16,14 +16,12 @@
package com.android.ant;
-import com.android.sdklib.SdkConstants;
import com.android.sdklib.internal.export.ApkData;
-import com.android.sdklib.internal.project.ApkSettings;
-import com.android.sdklib.internal.project.ProjectProperties;
-import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
+import com.android.sdklib.internal.export.MultiApkExportHelper;
+import com.android.sdklib.internal.export.MultiApkExportHelper.ExportException;
+import com.android.sdklib.internal.export.MultiApkExportHelper.Target;
import com.android.sdklib.io.FileWrapper;
-import com.android.sdklib.io.StreamException;
-import com.android.sdklib.xml.AndroidManifest;
+import com.android.sdklib.io.IAbstractFile;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
@@ -34,18 +32,10 @@ import org.apache.tools.ant.taskdefs.SubAnt;
import org.apache.tools.ant.types.FileSet;
import org.xml.sax.InputSource;
-import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashSet;
-import java.util.List;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
@@ -58,37 +48,6 @@ import javax.xml.xpath.XPathFactory;
*/
public class MultiApkExportTask extends Task {
- private final static int MAX_MINOR = 100;
- private final static int MAX_BUILDINFO = 100;
- private final static int OFFSET_BUILD_INFO = MAX_MINOR;
- private final static int OFFSET_VERSION_CODE = OFFSET_BUILD_INFO * MAX_BUILDINFO;
-
-
- private static enum Target {
- RELEASE("release"), CLEAN("clean");
-
- private final String mName;
-
- Target(String name) {
- mName = name;
- }
-
- String getTarget() {
- return mName;
- }
-
- static Target getTarget(String value) {
- for (Target t : values()) {
- if (t.mName.equals(value)) {
- return t;
- }
-
- }
-
- return null;
- }
- }
-
private Target mTarget;
public void setTarget(String target) {
@@ -134,170 +93,154 @@ public class MultiApkExportTask extends Task {
canSign = keyStore != null && keyAlias != null;
}
- // get the list of apk to export and their configuration.
- ApkData[] apks = getProjects(antProject, appPackage);
+ // get the list of projects
+ String projects = getValidatedProperty(antProject, "projects");
// look to see if there's an export log from a previous export
- File log = getBuildLog(appPackage, versionCode);
- if (mTarget == Target.RELEASE && log.isFile()) {
- // load the log and compare to current export list.
- // Any difference will force a new versionCode.
- ApkData[] previousApks = getProjects(log);
-
- if (previousApks.length != apks.length) {
- throw new BuildException(String.format(
- "Project export is setup differently from previous export at versionCode %d.\n" +
- "Any change in the multi-apk configuration require a increment of the versionCode.",
- versionCode));
- }
+ IAbstractFile log = getBuildLog(appPackage, versionCode);
- for (int i = 0 ; i < previousApks.length ; i++) {
- // update the minor value from what is in the log file.
- apks[i].setMinor(previousApks[i].getMinor());
- if (apks[i].compareTo(previousApks[i]) != 0) {
- throw new BuildException(String.format(
- "Project export is setup differently from previous export at versionCode %d.\n" +
- "Any change in the multi-apk configuration require a increment of the versionCode.",
- versionCode));
- }
+ MultiApkExportHelper helper = new MultiApkExportHelper(appPackage, versionCode, mTarget);
+ try {
+ ApkData[] apks = helper.getProjects(projects, log);
+
+ // some temp var used by the project loop
+ HashSet<String> compiledProject = new HashSet<String>();
+ XPathFactory xPathFactory = XPathFactory.newInstance();
+
+ 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;
+ String keyAliasPassword = null;
+ if (canSign) {
+ System.out.println("Found signing keystore and key alias. Need passwords.");
+
+ Input input = new Input();
+ input.setProject(antProject);
+ input.setAddproperty("key.store.password");
+ input.setMessage(String.format("Please enter keystore password (store: %1$s):",
+ keyStore));
+ input.execute();
+
+ input = new Input();
+ input.setProject(antProject);
+ input.setAddproperty("key.alias.password");
+ input.setMessage(String.format("Please enter password for alias '%1$s':",
+ keyAlias));
+ input.execute();
+
+ // and now read the property so that they can be set into the sub ant task.
+ keyStorePassword = getValidatedProperty(antProject, "key.store.password");
+ keyAliasPassword = getValidatedProperty(antProject, "key.alias.password");
}
- }
-
- // some temp var used by the project loop
- HashSet<String> compiledProject = new HashSet<String>();
- XPathFactory xPathFactory = XPathFactory.newInstance();
-
- 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;
- String keyAliasPassword = null;
- if (canSign) {
- System.out.println("Found signing keystore and key alias. Need passwords.");
-
- Input input = new Input();
- input.setProject(antProject);
- input.setAddproperty("key.store.password");
- input.setMessage(String.format("Please enter keystore password (store: %1$s):",
- keyStore));
- input.execute();
-
- input = new Input();
- input.setProject(antProject);
- input.setAddproperty("key.alias.password");
- input.setMessage(String.format("Please enter password for alias '%1$s':",
- keyAlias));
- input.execute();
-
- // and now read the property so that they can be set into the sub ant task.
- keyStorePassword = getValidatedProperty(antProject, "key.store.password");
- keyAliasPassword = getValidatedProperty(antProject, "key.alias.password");
- }
-
- 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);
+ 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");
+ }
- 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
+ String compositeVersionCode = getVersionCodeString(versionCode, apk);
+ addProp(subAnt, "version.code", 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 version code, and filtering
- String compositeVersionCode = getVersionCodeString(versionCode, apk);
- addProp(subAnt, "version.code", 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);
+ }
- // 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());
+ }
}
- // 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());
- }
+ subAnt.execute();
}
- subAnt.execute();
- }
-
- if (mTarget == Target.RELEASE) {
- makeBuildLog(appPackage, versionCode, apks);
+ if (mTarget == Target.RELEASE) {
+ helper.makeBuildLog(log, apks);
+ }
+ } catch (ExportException e) {
+ throw new BuildException(e);
}
}
@@ -318,174 +261,6 @@ public class MultiApkExportTask extends Task {
return value;
}
- /**
- * gets the projects to export from the property, checks they exist, validates them,
- * loads their export info and return it.
- * If a project does not exist or is not valid, this will throw a {@link BuildException}.
- * @param antProject the Ant project.
- * @param appPackage the application package. Projects' manifest must match this.
- */
- private ApkData[] getProjects(Project antProject, String appPackage) {
- String projects = antProject.getProperty("projects");
- String[] paths = projects.split("\\:");
-
- ArrayList<ApkData> datalist = new ArrayList<ApkData>();
-
- for (String path : paths) {
- File projectFolder = new File(path);
-
- // resolve the path (to remove potential ..)
- try {
- projectFolder = projectFolder.getCanonicalFile();
-
- // project folder must exist and be a directory
- if (projectFolder.isDirectory() == false) {
- throw new BuildException(String.format(
- "Project folder '%1$s' is not a valid directory.",
- projectFolder.getAbsolutePath()));
- }
-
- // Check AndroidManifest.xml is present
- FileWrapper androidManifest = new FileWrapper(projectFolder,
- SdkConstants.FN_ANDROID_MANIFEST_XML);
-
- if (androidManifest.isFile() == false) {
- throw new BuildException(String.format(
- "%1$s is not a valid project (%2$s not found).",
- projectFolder.getAbsolutePath(),
- SdkConstants.FN_ANDROID_MANIFEST_XML));
- }
-
- ArrayList<ApkData> datalist2 = checkManifest(androidManifest, appPackage);
-
- // if the method returns without throwing, this is a good project to
- // export.
- for (ApkData data : datalist2) {
- data.setRelativePath(path);
- data.setProject(projectFolder);
- }
-
- datalist.addAll(datalist2);
-
- } catch (IOException e) {
- throw new BuildException(String.format("Failed to resolve path %1$s", path), e);
- }
- }
-
- // sort the projects and assign buildInfo
- Collections.sort(datalist);
- int buildInfo = 0;
- for (ApkData data : datalist) {
- data.setBuildInfo(buildInfo++);
- }
-
- return datalist.toArray(new ApkData[datalist.size()]);
- }
-
- /**
- * Checks a manifest of the project for inclusion in the list of exported APK.
- * If the manifest is correct, a list of apk to export is created and returned.
- *
- * @param androidManifest the manifest to check
- * @param appPackage the package name of the application being exported, as read from
- * export.properties.
- * @return A new non-null {@link ArrayList} of {@link ApkData}.
- *
- * @throws BuildException in case of error.
- */
- private ArrayList<ApkData> checkManifest(FileWrapper androidManifest, String appPackage) {
- try {
- String manifestPackage = AndroidManifest.getPackage(androidManifest);
- if (appPackage.equals(manifestPackage) == false) {
- throw new BuildException(String.format(
- "%1$s package value is not valid. Found '%2$s', expected '%3$s'.",
- androidManifest.getPath(), manifestPackage, appPackage));
- }
-
- if (AndroidManifest.hasVersionCode(androidManifest)) {
- throw new BuildException(String.format(
- "%1$s is not valid: versionCode must not be set for multi-apk export.",
- androidManifest.getPath()));
- }
-
- int minSdkVersion = AndroidManifest.getMinSdkVersion(androidManifest);
- if (minSdkVersion == -1) {
- throw new BuildException(
- "Codename in minSdkVersion is not supported by multi-apk export.");
- }
-
- ArrayList<ApkData> dataList = new ArrayList<ApkData>();
- ApkData data = new ApkData();
- dataList.add(data);
- data.setMinSdkVersion(minSdkVersion);
-
- // only look for more exports if the target is not clean.
- if (mTarget != Target.CLEAN) {
- // load the project properties
- String projectPath = androidManifest.getParent();
- ProjectProperties projectProp = ProjectProperties.load(projectPath,
- PropertyType.DEFAULT);
- if (projectProp == null) {
- throw new BuildException(String.format(
- "%1$s is missing.", PropertyType.DEFAULT.getFilename()));
- }
-
- ApkSettings apkSettings = new ApkSettings(projectProp);
- if (apkSettings.isSplitByAbi()) {
- // need to find the available ABIs.
- List<String> abis = findAbis(projectPath);
- ApkData current = data;
- for (String abi : abis) {
- if (current == null) {
- current = new ApkData(data);
- dataList.add(current);
- }
-
- current.setAbi(abi);
- current = null;
- }
- }
- }
-
- return dataList;
- } catch (XPathExpressionException e) {
- throw new BuildException(
- String.format("Failed to validate %1$s", androidManifest.getPath()), e);
- } catch (StreamException e) {
- throw new BuildException(
- String.format("Failed to validate %1$s", androidManifest.getPath()), e);
- }
- }
-
- /**
- * Finds ABIs in a project folder. This is based on the presence of libs/<abi>/ folder.
- *
- * @param projectPath The OS path of the project.
- * @return A new non-null, possibly empty, list of ABI strings.
- */
- private List<String> findAbis(String projectPath) {
- ArrayList<String> abiList = new ArrayList<String>();
- File libs = new File(projectPath, SdkConstants.FD_NATIVE_LIBS);
- if (libs.isDirectory()) {
- File[] abis = libs.listFiles();
- for (File abi : abis) {
- if (abi.isDirectory()) {
- // only add the abi folder if there are .so files in it.
- String[] content = abi.list(new FilenameFilter() {
- public boolean accept(File dir, String name) {
- return name.toLowerCase().endsWith(".so");
- }
- });
-
- if (content.length > 0) {
- abiList.add(abi.getName());
- }
- }
- }
- }
-
- return abiList;
- }
/**
* Adds a property to a {@link SubAnt} task.
@@ -507,8 +282,8 @@ public class MultiApkExportTask extends Task {
* @return the composite versionCode to be used in the manifest.
*/
private String getVersionCodeString(int versionCode, ApkData apkData) {
- int trueVersionCode = versionCode * OFFSET_VERSION_CODE;
- trueVersionCode += apkData.getBuildInfo() * OFFSET_BUILD_INFO;
+ int trueVersionCode = versionCode * MultiApkExportHelper.OFFSET_VERSION_CODE;
+ trueVersionCode += apkData.getBuildInfo() * MultiApkExportHelper.OFFSET_BUILD_INFO;
trueVersionCode += apkData.getMinor();
return Integer.toString(trueVersionCode);
@@ -518,119 +293,9 @@ public class MultiApkExportTask extends Task {
* Returns the {@link File} for the build log.
* @param appPackage
* @param versionCode
- * @return A new non-null {@link File} mapping to the build log.
+ * @return A new non-null {@link IAbstractFile} mapping to the build log.
*/
- private File getBuildLog(String appPackage, int versionCode) {
- return new File(appPackage + "." + versionCode + ".log");
+ private IAbstractFile getBuildLog(String appPackage, int versionCode) {
+ return new FileWrapper(appPackage + "." + versionCode + ".log");
}
-
- /**
- * Loads and returns a list of {@link ApkData} from a build log.
- * @param log
- * @return A new non-null, possibly empty, array of {@link ApkData}.
- * @throws BuildException in case of error.
- */
- private ApkData[] getProjects(File log) {
- ArrayList<ApkData> datalist = new ArrayList<ApkData>();
-
- FileReader reader = null;
- BufferedReader bufferedReader = null;
- try {
- reader = new FileReader(log);
- bufferedReader = new BufferedReader(reader);
- String line;
- int lineIndex = 0;
- int apkIndex = 0;
- while ((line = bufferedReader.readLine()) != null) {
- line = line.trim();
- if (line.length() == 0 || line.startsWith("#")) {
- continue;
- }
-
- switch (lineIndex) {
- case 0:
- // read package value
- lineIndex++;
- break;
- case 1:
- // read versionCode value
- lineIndex++;
- break;
- default:
- // read apk description
- ApkData data = new ApkData();
- data.setBuildInfo(apkIndex++);
- datalist.add(data);
- data.read(line);
- if (data.getMinor() >= MAX_MINOR) {
- throw new BuildException(
- "Valid minor version code values are 0-" + (MAX_MINOR-1));
- }
- break;
- }
- }
- } catch (IOException e) {
- throw new BuildException("Failed to read existing build log", e);
- } finally {
- try {
- if (reader != null) {
- reader.close();
- }
- } catch (IOException e) {
- throw new BuildException("Failed to read existing build log", e);
- }
- }
-
- return datalist.toArray(new ApkData[datalist.size()]);
- }
-
- /**
- * Writes the build log for a given list of {@link ApkData}.
- * @param appPackage
- * @param versionCode
- * @param apks
- */
- private void makeBuildLog(String appPackage, int versionCode, ApkData[] apks) {
- File log = getBuildLog(appPackage, versionCode);
- FileWriter writer = null;
- try {
- writer = new FileWriter(log);
-
- writer.append("# Multi-APK BUILD log.\n");
- writer.append("# Only edit manually to change minor versions.\n");
-
- writeValue(writer, "package", appPackage);
- writeValue(writer, "versionCode", versionCode);
-
- writer.append("# what follows is one line per generated apk with its description.\n");
- writer.append("# the format is CSV in the following order:\n");
- writer.append("# apkname,project,minor, minsdkversion, abi filter,\n");
-
- for (ApkData apk : apks) {
- apk.write(writer);
- writer.append('\n');
- }
-
- writer.flush();
- } catch (IOException e) {
- throw new BuildException("Failed to write build log", e);
- } finally {
- try {
- if (writer != null) {
- writer.close();
- }
- } catch (IOException e) {
- throw new BuildException("Failed to write build log", e);
- }
- }
- }
-
- private void writeValue(FileWriter writer, String name, String value) throws IOException {
- writer.append(name).append('=').append(value).append('\n');
- }
-
- private void writeValue(FileWriter writer, String name, int value) throws IOException {
- writeValue(writer, name, Integer.toString(value));
- }
-
}