aboutsummaryrefslogtreecommitdiffstats
path: root/apkbuilder
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:29:09 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:29:09 -0800
commit55a2c71f27d3e0b8344597c7f281e687cb7aeb1b (patch)
treeecd18b995aea8eeeb8b3823266280d41245bf0f7 /apkbuilder
parent82ea7a177797b844b252effea5c7c7c5d63ea4ac (diff)
downloadsdk-55a2c71f27d3e0b8344597c7f281e687cb7aeb1b.zip
sdk-55a2c71f27d3e0b8344597c7f281e687cb7aeb1b.tar.gz
sdk-55a2c71f27d3e0b8344597c7f281e687cb7aeb1b.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'apkbuilder')
-rw-r--r--apkbuilder/.classpath8
-rw-r--r--apkbuilder/.project17
-rw-r--r--apkbuilder/Android.mk18
-rw-r--r--apkbuilder/etc/Android.mk22
-rwxr-xr-xapkbuilder/etc/apkbuilder81
-rwxr-xr-xapkbuilder/etc/apkbuilder.bat43
-rw-r--r--apkbuilder/etc/manifest.txt1
-rw-r--r--apkbuilder/src/Android.mk29
-rw-r--r--apkbuilder/src/com/android/apkbuilder/ApkBuilder.java468
9 files changed, 687 insertions, 0 deletions
diff --git a/apkbuilder/.classpath b/apkbuilder/.classpath
new file mode 100644
index 0000000..f1768fc
--- /dev/null
+++ b/apkbuilder/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/JarUtils"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/apkbuilder/.project b/apkbuilder/.project
new file mode 100644
index 0000000..cc97afc
--- /dev/null
+++ b/apkbuilder/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>ApkBuilder</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/apkbuilder/Android.mk b/apkbuilder/Android.mk
new file mode 100644
index 0000000..bdfe5c8
--- /dev/null
+++ b/apkbuilder/Android.mk
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+APKBUILDER_LOCAL_DIR := $(call my-dir)
+include $(APKBUILDER_LOCAL_DIR)/etc/Android.mk
+include $(APKBUILDER_LOCAL_DIR)/src/Android.mk
diff --git a/apkbuilder/etc/Android.mk b/apkbuilder/etc/Android.mk
new file mode 100644
index 0000000..d74db17
--- /dev/null
+++ b/apkbuilder/etc/Android.mk
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_PREBUILT_EXECUTABLES := apkbuilder
+include $(BUILD_HOST_PREBUILT)
+
diff --git a/apkbuilder/etc/apkbuilder b/apkbuilder/etc/apkbuilder
new file mode 100755
index 0000000..3e7e822
--- /dev/null
+++ b/apkbuilder/etc/apkbuilder
@@ -0,0 +1,81 @@
+#!/bin/sh
+# Copyright 2005-2007, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+jarfile=apkbuilder.jar
+frameworkdir="$progdir"
+libdir="$progdir"
+if [ ! -r "$frameworkdir/$jarfile" ]
+then
+ frameworkdir=`dirname "$progdir"`/tools/lib
+ libdir=`dirname "$progdir"`/tools/lib
+fi
+if [ ! -r "$frameworkdir/$jarfile" ]
+then
+ frameworkdir=`dirname "$progdir"`/framework
+ libdir=`dirname "$progdir"`/lib
+fi
+if [ ! -r "$frameworkdir/$jarfile" ]
+then
+ echo `basename "$prog"`": can't find $jarfile"
+ exit 1
+fi
+
+
+# Check args.
+if [ debug = "$1" ]; then
+ # add this in for debugging
+ java_debug=-agentlib:jdwp=transport=dt_socket,server=y,address=8050,suspend=y
+ shift 1
+else
+ java_debug=
+fi
+
+# Mac OS X needs an additional arg, or you get an "illegal thread" complaint.
+if [ `uname` = "Darwin" ]; then
+ os_opts="-XstartOnFirstThread"
+else
+ os_opts=
+fi
+
+if [ "$OSTYPE" = "cygwin" ] ; then
+ jarpath=`cygpath -w "$frameworkdir/$jarfile"`
+ progdir=`cygpath -w "$progdir"`
+else
+ jarpath="$frameworkdir/$jarfile"
+fi
+
+# need to use "java.ext.dirs" because "-jar" causes classpath to be ignored
+# might need more memory, e.g. -Xmx128M
+exec java -Xmx128M $os_opts $java_debug -Djava.ext.dirs="$frameworkdir" -Djava.library.path="$libdir" -jar "$jarpath" "$@"
diff --git a/apkbuilder/etc/apkbuilder.bat b/apkbuilder/etc/apkbuilder.bat
new file mode 100755
index 0000000..c4689c6
--- /dev/null
+++ b/apkbuilder/etc/apkbuilder.bat
@@ -0,0 +1,43 @@
+@echo off
+rem Copyright (C) 2007 The Android Open Source Project
+rem
+rem Licensed under the Apache License, Version 2.0 (the "License");
+rem you may not use this file except in compliance with the License.
+rem You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+
+rem don't modify the caller's environment
+setlocal
+
+rem Set up prog to be the path of this script, including following symlinks,
+rem and set up progdir to be the fully-qualified pathname of its directory.
+set prog=%~f0
+
+rem Change current directory and drive to where the script is, to avoid
+rem issues with directories containing whitespaces.
+cd /d %~dp0
+
+set jarfile=apkbuilder.jar
+set frameworkdir=
+set libdir=
+
+if exist %frameworkdir%%jarfile% goto JarFileOk
+ set frameworkdir=lib\
+ set libdir=lib\
+
+if exist %frameworkdir%%jarfile% goto JarFileOk
+ set frameworkdir=..\framework\
+ set libdir=..\lib\
+
+:JarFileOk
+
+set jarpath=%frameworkdir%%jarfile%
+
+call java -Djava.ext.dirs=%frameworkdir% -Djava.library.path=%libdir% -jar %jarpath% %*
diff --git a/apkbuilder/etc/manifest.txt b/apkbuilder/etc/manifest.txt
new file mode 100644
index 0000000..6aafb16
--- /dev/null
+++ b/apkbuilder/etc/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.apkbuilder.ApkBuilder
diff --git a/apkbuilder/src/Android.mk b/apkbuilder/src/Android.mk
new file mode 100644
index 0000000..e403ca7
--- /dev/null
+++ b/apkbuilder/src/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAR_MANIFEST := ../etc/manifest.txt
+LOCAL_JAVA_LIBRARIES := \
+ androidprefs \
+ jarutils
+
+LOCAL_MODULE := apkbuilder
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/apkbuilder/src/com/android/apkbuilder/ApkBuilder.java b/apkbuilder/src/com/android/apkbuilder/ApkBuilder.java
new file mode 100644
index 0000000..40abff1
--- /dev/null
+++ b/apkbuilder/src/com/android/apkbuilder/ApkBuilder.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.apkbuilder;
+
+import com.android.jarutils.DebugKeyProvider;
+import com.android.jarutils.JavaResourceFilter;
+import com.android.jarutils.SignedJarBuilder;
+import com.android.jarutils.DebugKeyProvider.KeytoolException;
+import com.android.prefs.AndroidLocation.AndroidLocationException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.regex.Pattern;
+
+/**
+ * Command line APK builder with signing support.
+ */
+public final class ApkBuilder {
+
+ private final static Pattern PATTERN_JAR_EXT = Pattern.compile("^.+\\.jar$",
+ Pattern.CASE_INSENSITIVE);
+ private final static Pattern PATTERN_NATIVELIB_EXT = Pattern.compile("^.+\\.so$",
+ Pattern.CASE_INSENSITIVE);
+
+ private final static String NATIVE_LIB_ROOT = "lib/";
+
+ /**
+ * A File to be added to the APK archive.
+ * <p/>This includes the {@link File} representing the file and its path in the archive.
+ */
+ public final static class ApkFile {
+ String archivePath;
+ File file;
+
+ ApkFile(File file, String path) {
+ this.file = file;
+ this.archivePath = path;
+ }
+ }
+
+ private JavaResourceFilter mResourceFilter = new JavaResourceFilter();
+ private boolean mVerbose = false;
+ private boolean mSignedPackage = true;
+ /** the optional type of the debug keystore. If <code>null</code>, the default */
+ private String mStoreType = null;
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) {
+ new ApkBuilder().run(args);
+ }
+
+ public void setVerbose(boolean verbose) {
+ mVerbose = verbose;
+ }
+
+ public void setSignedPackage(boolean signedPackage) {
+ mSignedPackage = signedPackage;
+ }
+
+ private void run(String[] args) {
+ if (args.length < 1) {
+ printUsageAndQuit();
+ }
+
+ try {
+ // read the first args that should be a file path
+ File outFile = getOutFile(args[0]);
+
+ ArrayList<FileInputStream> zipArchives = new ArrayList<FileInputStream>();
+ ArrayList<File> archiveFiles = new ArrayList<File>();
+ ArrayList<ApkFile> javaResources = new ArrayList<ApkFile>();
+ ArrayList<FileInputStream> resourcesJars = new ArrayList<FileInputStream>();
+ ArrayList<ApkFile> nativeLibraries = new ArrayList<ApkFile>();
+
+ int index = 1;
+ do {
+ String argument = args[index++];
+
+ if ("-v".equals(argument)) {
+ mVerbose = true;
+ } else if ("-u".equals(argument)) {
+ mSignedPackage = false;
+ } else if ("-z".equals(argument)) {
+ // quick check on the next argument.
+ if (index == args.length) printUsageAndQuit();
+
+ try {
+ FileInputStream input = new FileInputStream(args[index++]);
+ zipArchives.add(input);
+ } catch (FileNotFoundException e) {
+ printAndExit(e.getMessage());
+ }
+ } else if ("-f". equals(argument)) {
+ // quick check on the next argument.
+ if (index == args.length) printUsageAndQuit();
+
+ archiveFiles.add(getInputFile(args[index++]));
+ } else if ("-rf". equals(argument)) {
+ // quick check on the next argument.
+ if (index == args.length) printUsageAndQuit();
+
+ processSourceFolderForResource(args[index++], javaResources);
+ } else if ("-rj". equals(argument)) {
+ // quick check on the next argument.
+ if (index == args.length) printUsageAndQuit();
+
+ processJarFolder(args[index++], resourcesJars);
+ } else if ("-nf".equals(argument)) {
+ // quick check on the next argument.
+ if (index == args.length) printUsageAndQuit();
+
+ String parameter = args[index++];
+ File f = new File(parameter);
+
+ // compute the offset to get the relative path
+ int offset = parameter.length();
+ if (parameter.endsWith(File.separator) == false) {
+ offset++;
+ }
+
+ processNativeFolder(offset, f, nativeLibraries);
+ } else if ("-storetype".equals(argument)) {
+ // quick check on the next argument.
+ if (index == args.length) printUsageAndQuit();
+
+ mStoreType = args[index++];
+ } else {
+ printAndExit("Unknown argument: " + argument);
+ }
+ } while (index < args.length);
+
+ createPackage(outFile, zipArchives, archiveFiles, javaResources, resourcesJars,
+ nativeLibraries);
+ } catch (IllegalArgumentException e) {
+ printAndExit(e.getMessage());
+ } catch (FileNotFoundException e) {
+ printAndExit(e.getMessage());
+ }
+ }
+
+
+ private File getOutFile(String filepath) {
+ File f = new File(filepath);
+
+ if (f.isDirectory()) {
+ printAndExit(filepath + " is a directory!");
+ }
+
+ if (f.exists()) { // will be a file in this case.
+ if (f.canWrite() == false) {
+ printAndExit("Cannot write " + filepath);
+ }
+ } else {
+ try {
+ if (f.createNewFile() == false) {
+ printAndExit("Failed to create " + filepath);
+ }
+ } catch (IOException e) {
+ printAndExit("Failed to create '" + filepath + "' : " + e.getMessage());
+ }
+ }
+
+ return f;
+ }
+
+ public static File getInputFile(String filepath) throws IllegalArgumentException {
+ File f = new File(filepath);
+
+ if (f.isDirectory()) {
+ throw new IllegalArgumentException(filepath + " is a directory!");
+ }
+
+ if (f.exists()) {
+ if (f.canRead() == false) {
+ throw new IllegalArgumentException("Cannot read " + filepath);
+ }
+ } else {
+ throw new IllegalArgumentException(filepath + " does not exists!");
+ }
+
+ return f;
+ }
+
+ /**
+ * Processes a source folder and adds its java resources to a given list of {@link ApkFile}.
+ * @param folderPath the path to the source folder.
+ * @param javaResources the list of {@link ApkFile} to fill.
+ */
+ public static void processSourceFolderForResource(String folderPath,
+ ArrayList<ApkFile> javaResources) {
+
+ File folder = new File(folderPath);
+
+ if (folder.isDirectory()) {
+ // file is a directory, process its content.
+ File[] files = folder.listFiles();
+ for (File file : files) {
+ processFileForResource(file, null, javaResources);
+ }
+ } else {
+ // not a directory? output error and quit.
+ if (folder.exists()) {
+ throw new IllegalArgumentException(folderPath + " is not a folder!");
+ } else {
+ throw new IllegalArgumentException(folderPath + " does not exist!");
+ }
+ }
+ }
+
+ public static void processJarFolder(String parameter, ArrayList<FileInputStream> resourcesJars)
+ throws FileNotFoundException {
+ File f = new File(parameter);
+ if (f.isDirectory()) {
+ String[] files = f.list(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return PATTERN_JAR_EXT.matcher(name).matches();
+ }
+ });
+
+ for (String file : files) {
+ String path = f.getAbsolutePath() + File.separator + file;
+ FileInputStream input = new FileInputStream(path);
+ resourcesJars.add(input);
+ }
+ } else {
+ FileInputStream input = new FileInputStream(parameter);
+ resourcesJars.add(input);
+ }
+ }
+
+
+ /**
+ * Processes a {@link File} that could be a {@link ApkFile}, or a folder containing
+ * java resources.
+ * @param file the {@link File} to process.
+ * @param path the relative path of this file to the source folder. Can be <code>null</code> to
+ * identify a root file.
+ * @param javaResources the list of {@link ApkFile} object to fill.
+ */
+ private static void processFileForResource(File file, String path,
+ ArrayList<ApkFile> javaResources) {
+ if (file.isDirectory()) {
+ // a directory? we check it
+ if (JavaResourceFilter.checkFolderForPackaging(file.getName())) {
+ // if it's valid, we append its name to the current path.
+ if (path == null) {
+ path = file.getName();
+ } else {
+ path = path + "/" + file.getName();
+ }
+
+ // and process its content.
+ File[] files = file.listFiles();
+ for (File contentFile : files) {
+ processFileForResource(contentFile, path, javaResources);
+ }
+ }
+ } else {
+ // a file? we check it
+ if (JavaResourceFilter.checkFileForPackaging(file.getName())) {
+ // we append its name to the current path
+ if (path == null) {
+ path = file.getName();
+ } else {
+ path = path + "/" + file.getName();
+ }
+
+ // and add it to the list.
+ javaResources.add(new ApkFile(file, path));
+ }
+ }
+ }
+
+ /**
+ * Process a {@link File} for native library inclusion.
+ * @param offset the length of the root folder (used to compute relative path)
+ * @param f the {@link File} to process
+ * @param nativeLibraries the array to add native libraries.
+ */
+ public static void processNativeFolder(int offset, File f, ArrayList<ApkFile> nativeLibraries) {
+ if (f.isDirectory()) {
+ File[] children = f.listFiles();
+
+ if (children != null) {
+ for (File child : children) {
+ processNativeFolder(offset, child, nativeLibraries);
+ }
+ }
+ } else if (f.isFile()) {
+ if (PATTERN_NATIVELIB_EXT.matcher(f.getName()).matches()) {
+ String path = NATIVE_LIB_ROOT +
+ f.getAbsolutePath().substring(offset).replace('\\', '/');
+
+ nativeLibraries.add(new ApkFile(f, path));
+ }
+ }
+ }
+
+ /**
+ * Creates the application package
+ * @param outFile
+ * @param zipArchives
+ * @param resourcesJars
+ * @param files
+ * @param javaResources
+ * keystore type of the Java VM is used.
+ */
+ public void createPackage(File outFile, ArrayList<FileInputStream> zipArchives,
+ ArrayList<File> files, ArrayList<ApkFile> javaResources,
+ ArrayList<FileInputStream> resourcesJars, ArrayList<ApkFile> nativeLibraries) {
+
+ // get the debug key
+ try {
+ SignedJarBuilder builder;
+
+ if (mSignedPackage) {
+ System.err.println(String.format("Using keystore: %s",
+ DebugKeyProvider.getDefaultKeyStoreOsPath()));
+
+
+ DebugKeyProvider keyProvider = new DebugKeyProvider(
+ null /* osKeyPath: use default */,
+ mStoreType, null /* IKeyGenOutput */);
+ PrivateKey key = keyProvider.getDebugKey();
+ X509Certificate certificate = (X509Certificate)keyProvider.getCertificate();
+
+ if (key == null) {
+ throw new IllegalArgumentException("Unable to get debug signature key");
+ }
+
+ // compare the certificate expiration date
+ if (certificate != null && certificate.getNotAfter().compareTo(new Date()) < 0) {
+ // TODO, regenerate a new one.
+ throw new IllegalArgumentException("Debug Certificate expired on " +
+ DateFormat.getInstance().format(certificate.getNotAfter()));
+ }
+
+ builder = new SignedJarBuilder(
+ new FileOutputStream(outFile.getAbsolutePath(), false /* append */), key,
+ certificate);
+ } else {
+ builder = new SignedJarBuilder(
+ new FileOutputStream(outFile.getAbsolutePath(), false /* append */),
+ null /* key */, null /* certificate */);
+ }
+
+ // add the archives
+ for (FileInputStream input : zipArchives) {
+ builder.writeZip(input, null /* filter */);
+ }
+
+ // add the single files
+ for (File input : files) {
+ // always put the file at the root of the archive in this case
+ builder.writeFile(input, input.getName());
+ if (mVerbose) {
+ System.err.println(String.format("%1$s => %2$s", input.getAbsolutePath(),
+ input.getName()));
+ }
+ }
+
+ // add the java resource from the source folders.
+ for (ApkFile resource : javaResources) {
+ builder.writeFile(resource.file, resource.archivePath);
+ if (mVerbose) {
+ System.err.println(String.format("%1$s => %2$s",
+ resource.file.getAbsolutePath(), resource.archivePath));
+ }
+ }
+
+ // add the java resource from jar files.
+ for (FileInputStream input : resourcesJars) {
+ builder.writeZip(input, mResourceFilter);
+ }
+
+ // add the native files
+ for (ApkFile file : nativeLibraries) {
+ builder.writeFile(file.file, file.archivePath);
+ if (mVerbose) {
+ System.err.println(String.format("%1$s => %2$s", file.file.getAbsolutePath(),
+ file.archivePath));
+ }
+ }
+
+ // close and sign the application package.
+ builder.close();
+ } catch (KeytoolException e) {
+ if (e.getJavaHome() == null) {
+ throw new IllegalArgumentException(e.getMessage() +
+ "\nJAVA_HOME seems undefined, setting it will help locating keytool automatically\n" +
+ "You can also manually execute the following command\n:" +
+ e.getCommandLine());
+ } else {
+ throw new IllegalArgumentException(e.getMessage() +
+ "\nJAVA_HOME is set to: " + e.getJavaHome() +
+ "\nUpdate it if necessary, or manually execute the following command:\n" +
+ e.getCommandLine());
+ }
+ } catch (AndroidLocationException e) {
+ throw new IllegalArgumentException(e);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ private void printUsageAndQuit() {
+ // 80 cols marker: 01234567890123456789012345678901234567890123456789012345678901234567890123456789
+ System.err.println("A command line tool to package an Android application from various sources.");
+ System.err.println("Usage: apkbuilder <out archive> [-v][-u][-storetype STORE_TYPE] [-z inputzip]");
+ System.err.println(" [-f inputfile] [-rf input-folder] [-rj -input-path]");
+ System.err.println("");
+ System.err.println(" -v Verbose.");
+ System.err.println(" -u Creates an unsigned package.");
+ System.err.println(" -storetype Forces the KeyStore type. If ommited the default is used.");
+ System.err.println("");
+ System.err.println(" -z Followed by the path to a zip archive.");
+ System.err.println(" Adds the content of the application package.");
+ System.err.println("");
+ System.err.println(" -f Followed by the path to a file.");
+ System.err.println(" Adds the file to the application package.");
+ System.err.println("");
+ System.err.println(" -rf Followed by the path to a source folder.");
+ System.err.println(" Adds the java resources found in that folder to the application");
+ System.err.println(" package, while keeping their path relative to the source folder.");
+ System.err.println("");
+ System.err.println(" -rj Followed by the path to a jar file or a folder containing");
+ System.err.println(" jar files.");
+ System.err.println(" Adds the java resources found in the jar file(s) to the application");
+ System.err.println(" package.");
+ System.err.println("");
+ System.err.println(" -nf Followed by the root folder containing native libraries to");
+ System.err.println(" include in the application package.");
+
+ System.exit(1);
+ }
+
+ private void printAndExit(String... messages) {
+ for (String message : messages) {
+ System.err.println(message);
+ }
+ System.exit(1);
+ }
+}