aboutsummaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/AndroidManifest.tests.template21
-rw-r--r--scripts/README_add-ons.txt2
-rw-r--r--scripts/alias_rules.xml52
-rw-r--r--scripts/android_rules.xml280
-rw-r--r--scripts/build.alias.template49
-rw-r--r--scripts/build.template269
-rwxr-xr-xscripts/combine_sdks.sh62
-rw-r--r--scripts/default.properties.template13
-rwxr-xr-xscripts/divide_and_compress.py352
-rw-r--r--scripts/divide_and_compress_constants.py60
-rw-r--r--scripts/java_tests_file.template21
-rw-r--r--scripts/plugin.prop2
-rwxr-xr-xscripts/test_divide_and_compress.py490
13 files changed, 1360 insertions, 313 deletions
diff --git a/scripts/AndroidManifest.tests.template b/scripts/AndroidManifest.tests.template
new file mode 100644
index 0000000..1f7d827
--- /dev/null
+++ b/scripts/AndroidManifest.tests.template
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="PACKAGE.tests"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <!-- We add an application tag here just so that we can indicate that
+ this package needs to link against the android.test library,
+ which is needed when building test cases. -->
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <!--
+ This declares that this application uses the instrumentation test runner targeting
+ the package of PACKAGE. To run the tests use the command:
+ "adb shell am instrument -w PACKAGE.tests/android.test.InstrumentationTestRunner"
+ -->
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="PACKAGE"
+ android:label="Tests for ACTIVITY_NAME"/>
+</manifest>
diff --git a/scripts/README_add-ons.txt b/scripts/README_add-ons.txt
new file mode 100644
index 0000000..b8eb1d6
--- /dev/null
+++ b/scripts/README_add-ons.txt
@@ -0,0 +1,2 @@
+Add-on folder.
+Drop vendor supplied SDK add-on in this folder. \ No newline at end of file
diff --git a/scripts/alias_rules.xml b/scripts/alias_rules.xml
new file mode 100644
index 0000000..0443193
--- /dev/null
+++ b/scripts/alias_rules.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" ?>
+<project name="alias_rules" default="package">
+
+ <!-- No user servicable parts below. -->
+
+ <!-- Input directories -->
+ <property name="resource-dir" value="res" />
+
+ <!-- The final package file to generate -->
+ <property name="out-package" value="${ant.project.name}.apk" />
+
+ <!-- Tools -->
+ <condition property="aapt" value="${android-tools}/aapt.exe" else="${android-tools}/aapt" >
+ <os family="windows"/>
+ </condition>
+ <condition property="adb" value="${android-tools}/adb.exe" else="${android-tools}/adb" >
+ <os family="windows"/>
+ </condition>
+ <property name="android-jar" value="${sdk-folder}/android.jar" />
+
+ <!-- Rules -->
+
+ <!-- Packages the manifest and the resource files -->
+ <target name="package-res">
+ <echo>Packaging resources...</echo>
+ <exec executable="${aapt}" failonerror="true">
+ <arg value="package" />
+ <arg value="-f" />
+ <arg value="-M" />
+ <arg value="AndroidManifest.xml" />
+ <arg value="-S" />
+ <arg value="${resource-dir}" />
+ <arg value="-I" />
+ <arg value="${android-jar}" />
+ <arg value="-F" />
+ <arg value="${out-package}" />
+ </exec>
+ </target>
+
+ <!-- Create the package file for this project from the sources. -->
+ <target name="package" depends="package-res" />
+
+ <!-- Create the package and install package on the default emulator -->
+ <target name="install" depends="package">
+ <echo>Sending package to default emulator...</echo>
+ <exec executable="${adb}" failonerror="true">
+ <arg value="install" />
+ <arg value="${out-package}" />
+ </exec>
+ </target>
+
+</project>
diff --git a/scripts/android_rules.xml b/scripts/android_rules.xml
new file mode 100644
index 0000000..bed5f24
--- /dev/null
+++ b/scripts/android_rules.xml
@@ -0,0 +1,280 @@
+<?xml version="1.0" ?>
+<project name="android_rules" default="debug">
+
+ <!-- No user servicable parts below. -->
+
+ <property name="outdir-main" value="../${outdir}" />
+
+ <property name="android-tools" value="${sdk-folder}/tools" />
+ <property name="android-platform" value="${sdk-folder}/platforms/${target-folder}" />
+ <property name="android-framework" value="${android-platform}/framework.aidl" />
+ <property name="android-jar" value="${android-platform}/android.jar" />
+
+ <!-- Input directories -->
+ <property name="resource-dir" value="res" />
+ <property name="asset-dir" value="assets" />
+ <property name="srcdir" value="src" />
+ <condition property="srcdir-ospath"
+ value="${basedir}\${srcdir}"
+ else="${basedir}/${srcdir}" >
+ <os family="windows"/>
+ </condition>
+
+ <!-- folder for the 3rd party java libraries -->
+ <property name="external-libs" value="libs" />
+ <condition property="external-libs-ospath"
+ value="${basedir}\${external-libs}"
+ else="${basedir}/${external-libs}" >
+ <os family="windows"/>
+ </condition>
+
+ <!-- folder for the native libraries -->
+ <property name="native-libs" value="libs" />
+ <condition property="native-libs-ospath"
+ value="${basedir}\${native-libs}"
+ else="${basedir}/${native-libs}" >
+ <os family="windows"/>
+ </condition>
+
+ <!-- Output directories -->
+ <property name="outdir-classes" value="${outdir}/classes" />
+ <condition property="outdir-classes-ospath"
+ value="${basedir}\${outdir-classes}"
+ else="${basedir}/${outdir-classes}" >
+ <os family="windows"/>
+ </condition>
+ <condition property="outdir-main-classes"
+ value="${outdir-main}/classes">
+ <available file="${outdir-main}/classes"
+ type="dir"/>
+ </condition>
+
+ <!-- Create R.java in the source directory -->
+ <property name="outdir-r" value="src" />
+
+ <!-- Intermediate files -->
+ <property name="dex-file" value="classes.dex" />
+ <property name="intermediate-dex" value="${outdir}/${dex-file}" />
+ <condition property="intermediate-dex-ospath"
+ value="${basedir}\${intermediate-dex}"
+ else="${basedir}/${intermediate-dex}" >
+ <os family="windows"/>
+ </condition>
+
+ <!-- The final package file to generate -->
+ <property name="resources-package" value="${outdir}/${ant.project.name}.ap_" />
+ <condition property="resources-package-ospath"
+ value="${basedir}\${resources-package}"
+ else="${basedir}/${resources-package}" >
+ <os family="windows"/>
+ </condition>
+
+ <property name="out-debug-package" value="${outdir}/${ant.project.name}-debug.apk" />
+ <condition property="out-debug-package-ospath"
+ value="${basedir}\${out-debug-package}"
+ else="${basedir}/${out-debug-package}" >
+ <os family="windows"/>
+ </condition>
+
+ <property name="out-unsigned-package" value="${outdir}/${ant.project.name}-unsigned.apk" />
+ <condition property="out-unsigned-package-ospath"
+ value="${basedir}\${out-unsigned-package}"
+ else="${basedir}/${out-unsigned-package}" >
+ <os family="windows"/>
+ </condition>
+
+ <!-- Tools -->
+ <condition property="aapt" value="${android-tools}/aapt.exe" else="${android-tools}/aapt" >
+ <os family="windows"/>
+ </condition>
+ <condition property="aidl" value="${android-tools}/aidl.exe" else="${android-tools}/aidl" >
+ <os family="windows"/>
+ </condition>
+ <condition property="adb" value="${android-tools}/adb.exe" else="${android-tools}/adb" >
+ <os family="windows"/>
+ </condition>
+ <condition property="dx" value="${android-tools}/dx.bat" else="${android-tools}/dx" >
+ <os family="windows"/>
+ </condition>
+ <condition property="apk-builder" value="${android-tools}/apkbuilder.bat" else="${android-tools}/apkbuilder" >
+ <os family="windows"/>
+ </condition>
+
+ <!-- Rules -->
+
+ <!-- Create the output directories if they don't exist yet. -->
+ <target name="dirs">
+ <echo>Creating output directories if needed...</echo>
+ <mkdir dir="${outdir}" />
+ <mkdir dir="${outdir-classes}" />
+ </target>
+
+ <!-- Generate the R.java file for this project's resources. -->
+ <target name="resource-src" depends="dirs">
+ <echo>Generating R.java / Manifest.java from the resources...</echo>
+ <exec executable="${aapt}" failonerror="true">
+ <arg value="package" />
+ <arg value="-m" />
+ <arg value="-J" />
+ <arg value="${outdir-r}" />
+ <arg value="-M" />
+ <arg value="AndroidManifest.xml" />
+ <arg value="-S" />
+ <arg value="${resource-dir}" />
+ <arg value="-I" />
+ <arg value="${android-jar}" />
+ </exec>
+ </target>
+
+ <!-- Generate java classes from .aidl files. -->
+ <target name="aidl" depends="dirs">
+ <echo>Compiling aidl files into Java classes...</echo>
+ <apply executable="${aidl}" failonerror="true">
+ <arg value="-p${android-framework}" />
+ <arg value="-I${srcdir}" />
+ <fileset dir="${srcdir}">
+ <include name="**/*.aidl"/>
+ </fileset>
+ </apply>
+ </target>
+
+ <!-- Compile this project's .java files into .class files. -->
+ <target name="compile" depends="dirs, resource-src, aidl">
+ <javac encoding="ascii" target="1.5" debug="true" extdirs=""
+ srcdir="${srcdir}"
+ destdir="${outdir-classes}"
+ bootclasspath="${android-jar}">
+ <classpath>
+ <fileset dir="${external-libs}" includes="*.jar"/>
+ <pathelement path="${outdir-main-classes}"/>
+ </classpath>
+ </javac>
+ </target>
+
+ <!-- Convert this project's .class files into .dex files. -->
+ <target name="dex" depends="compile">
+ <echo>Converting compiled files and external libraries into ${outdir}/${dex-file}...</echo>
+ <apply executable="${dx}" failonerror="true" parallel="true">
+ <arg value="--dex" />
+ <arg value="--output=${intermediate-dex-ospath}" />
+ <arg path="${outdir-classes-ospath}" />
+ <fileset dir="${external-libs}" includes="*.jar"/>
+ </apply>
+ </target>
+
+ <!-- Put the project's resources into the output package file. -->
+ <target name="package-res-and-assets">
+ <echo>Packaging resources and assets...</echo>
+ <exec executable="${aapt}" failonerror="true">
+ <arg value="package" />
+ <arg value="-f" />
+ <arg value="-M" />
+ <arg value="AndroidManifest.xml" />
+ <arg value="-S" />
+ <arg value="${resource-dir}" />
+ <arg value="-A" />
+ <arg value="${asset-dir}" />
+ <arg value="-I" />
+ <arg value="${android-jar}" />
+ <arg value="-F" />
+ <arg value="${resources-package}" />
+ </exec>
+ </target>
+
+ <!-- Same as package-res-and-assets, but without "-A ${asset-dir}" -->
+ <target name="package-res-no-assets">
+ <echo>Packaging resources...</echo>
+ <exec executable="${aapt}" failonerror="true">
+ <arg value="package" />
+ <arg value="-f" />
+ <arg value="-M" />
+ <arg value="AndroidManifest.xml" />
+ <arg value="-S" />
+ <arg value="${resource-dir}" />
+ <!-- No assets directory -->
+ <arg value="-I" />
+ <arg value="${android-jar}" />
+ <arg value="-F" />
+ <arg value="${resources-package}" />
+ </exec>
+ </target>
+
+ <!-- Invoke the proper target depending on whether or not
+ an assets directory is present. -->
+ <!-- TODO: find a nicer way to include the "-A ${asset-dir}" argument
+ only when the assets dir exists. -->
+ <target name="package-res">
+ <available file="${asset-dir}" type="dir"
+ property="res-target" value="and-assets" />
+ <property name="res-target" value="no-assets" />
+ <antcall target="package-res-${res-target}" />
+ </target>
+
+ <!-- Package the application and sign it with a debug key.
+ This is the default target when building. It is used for debug. -->
+ <target name="debug" depends="dex, package-res">
+ <echo>Packaging ${out-debug-package}, and signing it with a debug key...</echo>
+ <exec executable="${apk-builder}" failonerror="true">
+ <arg value="${out-debug-package-ospath}" />
+ <arg value="-z" />
+ <arg value="${resources-package-ospath}" />
+ <arg value="-f" />
+ <arg value="${intermediate-dex-ospath}" />
+ <arg value="-rf" />
+ <arg value="${srcdir-ospath}" />
+ <arg value="-rj" />
+ <arg value="${external-libs-ospath}" />
+ <arg value="-nf" />
+ <arg value="${native-libs-ospath}" />
+ </exec>
+ </target>
+
+ <!-- Package the application without signing it.
+ This allows for the application to be signed later with an official publishing key. -->
+ <target name="release" depends="dex, package-res">
+ <echo>Packaging ${out-unsigned-package} for release...</echo>
+ <exec executable="${apk-builder}" failonerror="true">
+ <arg value="${out-unsigned-package-ospath}" />
+ <arg value="-u" />
+ <arg value="-z" />
+ <arg value="${resources-package-ospath}" />
+ <arg value="-f" />
+ <arg value="${intermediate-dex-ospath}" />
+ <arg value="-rf" />
+ <arg value="${srcdir-ospath}" />
+ <arg value="-rj" />
+ <arg value="${external-libs-ospath}" />
+ <arg value="-nf" />
+ <arg value="${native-libs-ospath}" />
+ </exec>
+ <echo>It will need to be signed with jarsigner before being published.</echo>
+ </target>
+
+ <!-- Install the package on the default emulator -->
+ <target name="install" depends="debug">
+ <echo>Installing ${out-debug-package} onto default emulator...</echo>
+ <exec executable="${adb}" failonerror="true">
+ <arg value="install" />
+ <arg value="${out-debug-package}" />
+ </exec>
+ </target>
+
+ <target name="reinstall" depends="debug">
+ <echo>Installing ${out-debug-package} onto default emulator...</echo>
+ <exec executable="${adb}" failonerror="true">
+ <arg value="install" />
+ <arg value="-r" />
+ <arg value="${out-debug-package}" />
+ </exec>
+ </target>
+
+ <!-- Uinstall the package from the default emulator -->
+ <target name="uninstall">
+ <echo>Uninstalling ${application-package} from the default emulator...</echo>
+ <exec executable="${adb}" failonerror="true">
+ <arg value="uninstall" />
+ <arg value="${application-package}" />
+ </exec>
+ </target>
+
+</project>
diff --git a/scripts/build.alias.template b/scripts/build.alias.template
index 22252aa..b85887e 100644
--- a/scripts/build.alias.template
+++ b/scripts/build.alias.template
@@ -11,52 +11,9 @@
<property file="default.properties"/>
<!-- ************************************************************************************* -->
- <!-- No user servicable parts below. -->
+ <!-- Import the default Android build rules.
+ This requires ant 1.6.0 or above. -->
- <!-- Input directories -->
- <property name="resource-dir" value="res" />
-
- <!-- The final package file to generate -->
- <property name="out-package" value="${ant.project.name}.apk" />
-
- <!-- Tools -->
- <condition property="aapt" value="${android-tools}/aapt.exe" else="${android-tools}/aapt" >
- <os family="windows"/>
- </condition>
- <condition property="adb" value="${android-tools}/adb.exe" else="${android-tools}/adb" >
- <os family="windows"/>
- </condition>
- <property name="android-jar" value="${sdk-folder}/android.jar" />
-
- <!-- Rules -->
-
- <!-- Packages the manifest and the resource files -->
- <target name="package-res">
- <echo>Packaging resources...</echo>
- <exec executable="${aapt}" failonerror="true">
- <arg value="package" />
- <arg value="-f" />
- <arg value="-M" />
- <arg value="AndroidManifest.xml" />
- <arg value="-S" />
- <arg value="${resource-dir}" />
- <arg value="-I" />
- <arg value="${android-jar}" />
- <arg value="-F" />
- <arg value="${out-package}" />
- </exec>
- </target>
-
- <!-- Create the package file for this project from the sources. -->
- <target name="package" depends="package-res" />
-
- <!-- Create the package and install package on the default emulator -->
- <target name="install" depends="package">
- <echo>Sending package to default emulator...</echo>
- <exec executable="${adb}" failonerror="true">
- <arg value="install" />
- <arg value="${out-package}" />
- </exec>
- </target>
+ <import file="${sdk-folder}/tools/lib/alias_rules.xml" />
</project>
diff --git a/scripts/build.template b/scripts/build.template
index 0081c33..f04f1d8 100644
--- a/scripts/build.template
+++ b/scripts/build.template
@@ -22,272 +22,9 @@
<property name="outdir" value="bin" />
<!-- ************************************************************************************* -->
- <!-- No user servicable parts below. -->
+ <!-- Import the default Android build rules.
+ This requires ant 1.6.0 or above. -->
- <property name="android-tools" value="${sdk-folder}/tools" />
- <property name="android-framework" value="${android-tools}/lib/framework.aidl" />
-
- <!-- Input directories -->
- <property name="resource-dir" value="res" />
- <property name="asset-dir" value="assets" />
- <property name="srcdir" value="src" />
- <condition property="srcdir-ospath"
- value="${basedir}\${srcdir}"
- else="${basedir}/${srcdir}" >
- <os family="windows"/>
- </condition>
-
- <!-- folder for the 3rd party java libraries -->
- <property name="external-libs" value="libs" />
- <condition property="external-libs-ospath"
- value="${basedir}\${external-libs}"
- else="${basedir}/${external-libs}" >
- <os family="windows"/>
- </condition>
-
- <!-- folder for the native libraries -->
- <property name="native-libs" value="libs" />
- <condition property="native-libs-ospath"
- value="${basedir}\${native-libs}"
- else="${basedir}/${native-libs}" >
- <os family="windows"/>
- </condition>
-
- <!-- Output directories -->
- <property name="outdir-classes" value="${outdir}/classes" />
- <condition property="outdir-classes-ospath"
- value="${basedir}\${outdir-classes}"
- else="${basedir}/${outdir-classes}" >
- <os family="windows"/>
- </condition>
-
- <!-- Create R.java in the source directory -->
- <property name="outdir-r" value="src" />
-
- <!-- Intermediate files -->
- <property name="dex-file" value="classes.dex" />
- <property name="intermediate-dex" value="${outdir}/${dex-file}" />
- <condition property="intermediate-dex-ospath"
- value="${basedir}\${intermediate-dex}"
- else="${basedir}/${intermediate-dex}" >
- <os family="windows"/>
- </condition>
-
- <!-- The final package file to generate -->
- <property name="resources-package" value="${outdir}/${ant.project.name}.ap_" />
- <condition property="resources-package-ospath"
- value="${basedir}\${resources-package}"
- else="${basedir}/${resources-package}" >
- <os family="windows"/>
- </condition>
-
- <property name="out-debug-package" value="${outdir}/${ant.project.name}-debug.apk" />
- <condition property="out-debug-package-ospath"
- value="${basedir}\${out-debug-package}"
- else="${basedir}/${out-debug-package}" >
- <os family="windows"/>
- </condition>
-
- <property name="out-unsigned-package" value="${outdir}/${ant.project.name}-unsigned.apk" />
- <condition property="out-unsigned-package-ospath"
- value="${basedir}\${out-unsigned-package}"
- else="${basedir}/${out-unsigned-package}" >
- <os family="windows"/>
- </condition>
-
- <!-- Tools -->
- <condition property="aapt" value="${android-tools}/aapt.exe" else="${android-tools}/aapt" >
- <os family="windows"/>
- </condition>
- <condition property="aidl" value="${android-tools}/aidl.exe" else="${android-tools}/aidl" >
- <os family="windows"/>
- </condition>
- <condition property="adb" value="${android-tools}/adb.exe" else="${android-tools}/adb" >
- <os family="windows"/>
- </condition>
- <condition property="dx" value="${android-tools}/dx.bat" else="${android-tools}/dx" >
- <os family="windows"/>
- </condition>
- <condition property="apk-builder" value="${android-tools}/apkbuilder.bat" else="${android-tools}/apkbuilder" >
- <os family="windows"/>
- </condition>
-
- <property name="android-jar" value="${sdk-folder}/android.jar" />
-
- <!-- Rules -->
-
- <!-- Create the output directories if they don't exist yet. -->
- <target name="dirs">
- <echo>Creating output directories if needed...</echo>
- <mkdir dir="${outdir}" />
- <mkdir dir="${outdir-classes}" />
- </target>
-
- <!-- Generate the R.java file for this project's resources. -->
- <target name="resource-src" depends="dirs">
- <echo>Generating R.java / Manifest.java from the resources...</echo>
- <exec executable="${aapt}" failonerror="true">
- <arg value="package" />
- <arg value="-m" />
- <arg value="-J" />
- <arg value="${outdir-r}" />
- <arg value="-M" />
- <arg value="AndroidManifest.xml" />
- <arg value="-S" />
- <arg value="${resource-dir}" />
- <arg value="-I" />
- <arg value="${android-jar}" />
- </exec>
- </target>
-
- <!-- Generate java classes from .aidl files. -->
- <target name="aidl" depends="dirs">
- <echo>Compiling aidl files into Java classes...</echo>
- <apply executable="${aidl}" failonerror="true">
- <arg value="-p${android-framework}" />
- <arg value="-I${srcdir}" />
- <fileset dir="${srcdir}">
- <include name="**/*.aidl"/>
- </fileset>
- </apply>
- </target>
-
- <!-- Compile this project's .java files into .class files. -->
- <target name="compile" depends="dirs, resource-src, aidl">
- <javac encoding="ascii" target="1.5" debug="true" extdirs=""
- srcdir="."
- destdir="${outdir-classes}"
- bootclasspath="${android-jar}">
- <classpath>
- <fileset dir="${external-libs}" includes="*.jar"/>
- </classpath>
- </javac>
- </target>
-
- <!-- Convert this project's .class files into .dex files. -->
- <target name="dex" depends="compile">
- <echo>Converting compiled files and external libraries into ${outdir}/${dex-file}...</echo>
- <apply executable="${dx}" failonerror="true" parallel="true">
- <arg value="--dex" />
- <arg value="--output=${intermediate-dex-ospath}" />
- <arg path="${outdir-classes-ospath}" />
- <fileset dir="${external-libs}" includes="*.jar"/>
- </apply>
- </target>
-
- <!-- Put the project's resources into the output package file. -->
- <target name="package-res-and-assets">
- <echo>Packaging resources and assets...</echo>
- <exec executable="${aapt}" failonerror="true">
- <arg value="package" />
- <arg value="-f" />
- <arg value="-M" />
- <arg value="AndroidManifest.xml" />
- <arg value="-S" />
- <arg value="${resource-dir}" />
- <arg value="-A" />
- <arg value="${asset-dir}" />
- <arg value="-I" />
- <arg value="${android-jar}" />
- <arg value="-F" />
- <arg value="${resources-package}" />
- </exec>
- </target>
-
- <!-- Same as package-res-and-assets, but without "-A ${asset-dir}" -->
- <target name="package-res-no-assets">
- <echo>Packaging resources...</echo>
- <exec executable="${aapt}" failonerror="true">
- <arg value="package" />
- <arg value="-f" />
- <arg value="-M" />
- <arg value="AndroidManifest.xml" />
- <arg value="-S" />
- <arg value="${resource-dir}" />
- <!-- No assets directory -->
- <arg value="-I" />
- <arg value="${android-jar}" />
- <arg value="-F" />
- <arg value="${resources-package}" />
- </exec>
- </target>
-
- <!-- Invoke the proper target depending on whether or not
- an assets directory is present. -->
- <!-- TODO: find a nicer way to include the "-A ${asset-dir}" argument
- only when the assets dir exists. -->
- <target name="package-res">
- <available file="${asset-dir}" type="dir"
- property="res-target" value="and-assets" />
- <property name="res-target" value="no-assets" />
- <antcall target="package-res-${res-target}" />
- </target>
-
- <!-- Package the application and sign it with a debug key.
- This is the default target when building. It is used for debug. -->
- <target name="debug" depends="dex, package-res">
- <echo>Packaging ${out-debug-package}, and signing it with a debug key...</echo>
- <exec executable="${apk-builder}" failonerror="true">
- <arg value="${out-debug-package-ospath}" />
- <arg value="-z" />
- <arg value="${resources-package-ospath}" />
- <arg value="-f" />
- <arg value="${intermediate-dex-ospath}" />
- <arg value="-rf" />
- <arg value="${srcdir-ospath}" />
- <arg value="-rj" />
- <arg value="${external-libs-ospath}" />
- <arg value="-nf" />
- <arg value="${native-libs-ospath}" />
- </exec>
- </target>
-
- <!-- Package the application without signing it.
- This allows for the application to be signed later with an official publishing key. -->
- <target name="release" depends="dex, package-res">
- <echo>Packaging ${out-unsigned-package} for release...</echo>
- <exec executable="${apk-builder}" failonerror="true">
- <arg value="${out-unsigned-package-ospath}" />
- <arg value="-u" />
- <arg value="-z" />
- <arg value="${resources-package-ospath}" />
- <arg value="-f" />
- <arg value="${intermediate-dex-ospath}" />
- <arg value="-rf" />
- <arg value="${srcdir-ospath}" />
- <arg value="-rj" />
- <arg value="${external-libs-ospath}" />
- <arg value="-nf" />
- <arg value="${native-libs-ospath}" />
- </exec>
- <echo>It will need to be signed with jarsigner before being published.</echo>
- </target>
-
- <!-- Install the package on the default emulator -->
- <target name="install" depends="debug">
- <echo>Installing ${out-debug-package} onto default emulator...</echo>
- <exec executable="${adb}" failonerror="true">
- <arg value="install" />
- <arg value="${out-debug-package}" />
- </exec>
- </target>
-
- <target name="reinstall" depends="debug">
- <echo>Installing ${out-debug-package} onto default emulator...</echo>
- <exec executable="${adb}" failonerror="true">
- <arg value="install" />
- <arg value="-r" />
- <arg value="${out-debug-package}" />
- </exec>
- </target>
-
- <!-- Uinstall the package from the default emulator -->
- <target name="uninstall">
- <echo>Uninstalling ${application-package} from the default emulator...</echo>
- <exec executable="${adb}" failonerror="true">
- <arg value="uninstall" />
- <arg value="${application-package}" />
- </exec>
- </target>
+ <import file="${sdk-folder}/tools/lib/android_rules.xml" />
</project>
diff --git a/scripts/combine_sdks.sh b/scripts/combine_sdks.sh
new file mode 100755
index 0000000..89a1141
--- /dev/null
+++ b/scripts/combine_sdks.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+function replace()
+{
+ echo replacing $1
+ rm -rf $UNZIPPED_BASE_DIR/$1
+ cp -rf $UNZIPPED_IMAGE_DIR/$1 $UNZIPPED_BASE_DIR/$1
+}
+
+BASE=$1
+IMAGES=$2
+OUTPUT=$3
+
+if [[ -z $BASE || -z $IMAGES || -z $OUTPUT ]] ; then
+ echo "usage: combine_sdks.sh BASE IMAGES OUTPUT"
+ echo
+ echo " BASE and IMAGES should be sdk zip files. The system image files,"
+ echo " emulator and other runtime files will be copied from IMAGES and"
+ echo " everything else will be copied from BASE. All of this will be"
+ echo " bundled into OUTPUT and zipped up again."
+ echo
+ exit 1
+fi
+
+TMP=$(mktemp -d)
+
+TMP_ZIP=tmp.zip
+
+BASE_DIR=$TMP/base
+IMAGES_DIR=$TMP/images
+OUTPUT_TMP_ZIP=$BASE_DIR/$TMP_ZIP
+
+unzip -q $BASE -d $BASE_DIR
+unzip -q $IMAGES -d $IMAGES_DIR
+
+UNZIPPED_BASE_DIR=$(echo $BASE_DIR/*)
+UNZIPPED_IMAGE_DIR=$(echo $IMAGES_DIR/*)
+
+#
+# The commands to copy over the files that we want
+#
+
+# replace tools/emulator # at this time we do not want the exe from SDK1.x
+replace tools/lib/images
+replace docs
+replace android.jar
+
+#
+# end
+#
+
+pushd $BASE_DIR &> /dev/null
+ # rename the directory to the leaf minus the .zip of OUTPUT
+ LEAF=$(echo $OUTPUT | sed -e "s:.*\.zip/::" | sed -e "s:.zip$::")
+ mv * $LEAF
+ # zip it
+ zip -qr $TMP_ZIP $LEAF
+popd &> /dev/null
+
+cp $OUTPUT_TMP_ZIP $OUTPUT
+
+rm -rf $TMP
diff --git a/scripts/default.properties.template b/scripts/default.properties.template
index 5d708f2..63df494 100644
--- a/scripts/default.properties.template
+++ b/scripts/default.properties.template
@@ -2,4 +2,17 @@
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
# Instead customize values in a "build.properties" file.
+# location of the SDK
sdk-folder=ANDROID_SDK_FOLDER
+
+# target mode. Value can be "platform" or "add-on"
+target-mode=TARGET_MODE
+
+# target API level.
+target-api=TARGET_API
+
+# target name, if target-mode=add-on
+target-name=TARGET_NAME
+
+# target platform. This is either the target itself or the platform the add-on is based on.
+target-folder=TARGET_FOLDER
diff --git a/scripts/divide_and_compress.py b/scripts/divide_and_compress.py
new file mode 100755
index 0000000..d369be4
--- /dev/null
+++ b/scripts/divide_and_compress.py
@@ -0,0 +1,352 @@
+#!/usr/bin/python2.4
+#
+# Copyright (C) 2008 Google Inc.
+#
+# 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.
+#
+
+"""Module to compress directories in to series of zip files.
+
+This module will take a directory and compress all its contents, including
+child directories into a series of zip files named N.zip where 'N' ranges from
+0 to infinity. The zip files will all be below a certain specified maximum
+threshold.
+
+The directory is compressed with a depth first traversal, each directory's
+file contents being compressed as it is visisted, before the compression of any
+child directory's contents. In this way the files within an archive are ordered
+and the archives themselves are ordered.
+
+The class also constructs a 'main.py' file intended for use with Google App
+Engine with a custom App Engine program not currently distributed with this
+code base. The custom App Engine runtime can leverage the index files written
+out by this class to more quickly locate which zip file to serve a given URL
+from.
+"""
+
+__author__ = 'jmatt@google.com (Justin Mattson)'
+
+from optparse import OptionParser
+import os
+import stat
+import sys
+import zipfile
+from zipfile import ZipFile
+import divide_and_compress_constants
+
+
+def Main(argv):
+ parser = CreateOptionsParser()
+ (options, args) = parser.parse_args()
+ VerifyArguments(options, parser)
+ zipper = DirectoryZipper(options.destination,
+ options.sourcefiles,
+ ParseSize(options.filesize),
+ options.compress)
+ zipper.StartCompress()
+
+
+def CreateOptionsParser():
+ rtn = OptionParser()
+ rtn.add_option('-s', '--sourcefiles', dest='sourcefiles', default=None,
+ help='The directory containing the files to compress')
+ rtn.add_option('-d', '--destination', dest='destination', default=None,
+ help=('Where to put the archive files, this should not be'
+ ' a child of where the source files exist.'))
+ rtn.add_option('-f', '--filesize', dest='filesize', default='1M',
+ help=('Maximum size of archive files. A number followed by'
+ 'a magnitude indicator, eg. 1000000B == one million '
+ 'BYTES, 500K == five hundred KILOBYTES, 1.2M == one '
+ 'point two MEGABYTES. 1M == 1048576 BYTES'))
+ rtn.add_option('-n', '--nocompress', action='store_false', dest='compress',
+ default=True,
+ help=('Whether the archive files should be compressed, or '
+ 'just a concatenation of the source files'))
+ return rtn
+
+
+def VerifyArguments(options, parser):
+ try:
+ if options.sourcefiles is None or options.destination is None:
+ parser.print_help()
+ sys.exit(-1)
+ except (AttributeError), err:
+ parser.print_help()
+ sys.exit(-1)
+
+
+def ParseSize(size_str):
+ if len(size_str) < 2:
+ raise ValueError(('filesize argument not understood, please include'
+ ' a numeric value and magnitude indicator'))
+ magnitude = size_str[len(size_str)-1:]
+ if not magnitude in ('K', 'B', 'M'):
+ raise ValueError(('filesize magnitude indicator not valid, must be \'K\','
+ '\'B\', or \'M\''))
+ numeral = float(size_str[0:len(size_str)-1])
+ if magnitude == 'K':
+ numeral *= 1024
+ elif magnitude == 'M':
+ numeral *= 1048576
+ return int(numeral)
+
+
+class DirectoryZipper(object):
+ """Class to compress a directory and all its sub-directories."""
+ current_archive = None
+ output_dir = None
+ base_path = None
+ max_size = None
+ compress = None
+ index_fp = None
+
+ def __init__(self, output_path, base_dir, archive_size, enable_compression):
+ """DirectoryZipper constructor.
+
+ Args:
+ output_path: the path to write the archives and index file to
+ base_dir: the directory to compress
+ archive_size: the maximum size, in bytes, of a single archive file
+ enable_compression: whether or not compression should be enabled, if
+ disabled, the files will be written into an uncompresed zip
+ """
+ self.output_dir = output_path
+ self.current_archive = '0.zip'
+ self.base_path = base_dir
+ self.max_size = archive_size
+ self.compress = enable_compression
+
+ def StartCompress(self):
+ """Start compress of the directory.
+
+ This will start the compression process and write the archives to the
+ specified output directory. It will also produce an 'index.txt' file in the
+ output directory that maps from file to archive.
+ """
+ self.index_fp = open(''.join([self.output_dir, 'main.py']), 'w')
+ self.index_fp.write(divide_and_compress_constants.file_preamble)
+ os.path.walk(self.base_path, self.CompressDirectory, 1)
+ self.index_fp.write(divide_and_compress_constants.file_endpiece)
+ self.index_fp.close()
+
+ def RemoveLastFile(self, archive_path=None):
+ """Removes the last item in the archive.
+
+ This removes the last item in the archive by reading the items out of the
+ archive, adding them to a new archive, deleting the old archive, and
+ moving the new archive to the location of the old archive.
+
+ Args:
+ archive_path: Path to the archive to modify. This archive should not be
+ open elsewhere, since it will need to be deleted.
+ Return:
+ A new ZipFile object that points to the modified archive file
+ """
+ if archive_path is None:
+ archive_path = ''.join([self.output_dir, self.current_archive])
+
+ # Move the old file and create a new one at its old location
+ ext_offset = archive_path.rfind('.')
+ old_archive = ''.join([archive_path[0:ext_offset], '-old',
+ archive_path[ext_offset:]])
+ os.rename(archive_path, old_archive)
+ old_fp = self.OpenZipFileAtPath(old_archive, mode='r')
+
+ if self.compress:
+ new_fp = self.OpenZipFileAtPath(archive_path,
+ mode='w',
+ compress=zipfile.ZIP_DEFLATED)
+ else:
+ new_fp = self.OpenZipFileAtPath(archive_path,
+ mode='w',
+ compress=zipfile.ZIP_STORED)
+
+ # Read the old archive in a new archive, except the last one
+ zip_members = enumerate(old_fp.infolist())
+ num_members = len(old_fp.infolist())
+ while num_members > 1:
+ this_member = zip_members.next()[1]
+ new_fp.writestr(this_member.filename, old_fp.read(this_member.filename))
+ num_members -= 1
+
+ # Close files and delete the old one
+ old_fp.close()
+ new_fp.close()
+ os.unlink(old_archive)
+
+ def OpenZipFileAtPath(self, path, mode=None, compress=zipfile.ZIP_DEFLATED):
+ """This method is mainly for testing purposes, eg dependency injection."""
+ if mode is None:
+ if os.path.exists(path):
+ mode = 'a'
+ else:
+ mode = 'w'
+
+ if mode == 'r':
+ return ZipFile(path, mode)
+ else:
+ return ZipFile(path, mode, compress)
+
+ def CompressDirectory(self, irrelevant, dir_path, dir_contents):
+ """Method to compress the given directory.
+
+ This method compresses the directory 'dir_path'. It will add to an existing
+ zip file that still has space and create new ones as necessary to keep zip
+ file sizes under the maximum specified size. This also writes out the
+ mapping of files to archives to the self.index_fp file descriptor
+
+ Args:
+ irrelevant: a numeric identifier passed by the os.path.walk method, this
+ is not used by this method
+ dir_path: the path to the directory to compress
+ dir_contents: a list of directory contents to be compressed
+ """
+
+ # construct the queue of files to be added that this method will use
+ # it seems that dir_contents is given in reverse alphabetical order,
+ # so put them in alphabetical order by inserting to front of the list
+ dir_contents.sort()
+ zip_queue = []
+ if dir_path[len(dir_path) - 1:] == os.sep:
+ for filename in dir_contents:
+ zip_queue.append(''.join([dir_path, filename]))
+ else:
+ for filename in dir_contents:
+ zip_queue.append(''.join([dir_path, os.sep, filename]))
+ compress_bit = zipfile.ZIP_DEFLATED
+ if not self.compress:
+ compress_bit = zipfile.ZIP_STORED
+
+ # zip all files in this directory, adding to existing archives and creating
+ # as necessary
+ while len(zip_queue) > 0:
+ target_file = zip_queue[0]
+ if os.path.isfile(target_file):
+ self.AddFileToArchive(target_file, compress_bit)
+
+ # see if adding the new file made our archive too large
+ if not self.ArchiveIsValid():
+
+ # IF fixing fails, the last added file was to large, skip it
+ # ELSE the current archive filled normally, make a new one and try
+ # adding the file again
+ if not self.FixArchive('SIZE'):
+ zip_queue.pop(0)
+ else:
+ self.current_archive = '%i.zip' % (
+ int(self.current_archive[
+ 0:self.current_archive.rfind('.zip')]) + 1)
+ else:
+
+ # if this the first file in the archive, write an index record
+ self.WriteIndexRecord()
+ zip_queue.pop(0)
+ else:
+ zip_queue.pop(0)
+
+ def WriteIndexRecord(self):
+ """Write an index record to the index file.
+
+ Only write an index record if this is the first file to go into archive
+
+ Returns:
+ True if an archive record is written, False if it isn't
+ """
+ archive = self.OpenZipFileAtPath(
+ ''.join([self.output_dir, self.current_archive]), 'r')
+ archive_index = archive.infolist()
+ if len(archive_index) == 1:
+ self.index_fp.write(
+ '[\'%s\', \'%s\'],\n' % (self.current_archive,
+ archive_index[0].filename))
+ archive.close()
+ return True
+ else:
+ archive.close()
+ return False
+
+ def FixArchive(self, problem):
+ """Make the archive compliant.
+
+ Args:
+ problem: the reason the archive is invalid
+
+ Returns:
+ Whether the file(s) removed to fix the archive could conceivably be
+ in an archive, but for some reason can't be added to this one.
+ """
+ archive_path = ''.join([self.output_dir, self.current_archive])
+ rtn_value = None
+
+ if problem == 'SIZE':
+ archive_obj = self.OpenZipFileAtPath(archive_path, mode='r')
+ num_archive_files = len(archive_obj.infolist())
+
+ # IF there is a single file, that means its too large to compress,
+ # delete the created archive
+ # ELSE do normal finalization
+ if num_archive_files == 1:
+ print ('WARNING: %s%s is too large to store.' % (
+ self.base_path, archive_obj.infolist()[0].filename))
+ archive_obj.close()
+ os.unlink(archive_path)
+ rtn_value = False
+ else:
+ self.RemoveLastFile(''.join([self.output_dir, self.current_archive]))
+ archive_obj.close()
+ print 'Final archive size for %s is %i' % (
+ self.current_archive, os.stat(archive_path)[stat.ST_SIZE])
+ rtn_value = True
+ return rtn_value
+
+ def AddFileToArchive(self, filepath, compress_bit):
+ """Add the file at filepath to the current archive.
+
+ Args:
+ filepath: the path of the file to add
+ compress_bit: whether or not this fiel should be compressed when added
+
+ Returns:
+ True if the file could be added (typically because this is a file) or
+ False if it couldn't be added (typically because its a directory)
+ """
+ curr_archive_path = ''.join([self.output_dir, self.current_archive])
+ if os.path.isfile(filepath):
+ if os.stat(filepath)[stat.ST_SIZE] > 1048576:
+ print 'Warning: %s is potentially too large to serve on GAE' % filepath
+ archive = self.OpenZipFileAtPath(curr_archive_path,
+ compress=compress_bit)
+ # add the file to the archive
+ archive.write(filepath, filepath[len(self.base_path):])
+ archive.close()
+ return True
+ else:
+ return False
+
+ def ArchiveIsValid(self):
+ """Check whether the archive is valid.
+
+ Currently this only checks whether the archive is under the required size.
+ The thought is that eventually this will do additional validation
+
+ Returns:
+ True if the archive is valid, False if its not
+ """
+ archive_path = ''.join([self.output_dir, self.current_archive])
+ if os.stat(archive_path)[stat.ST_SIZE] > self.max_size:
+ return False
+ else:
+ return True
+
+if __name__ == '__main__':
+ Main(sys.argv)
diff --git a/scripts/divide_and_compress_constants.py b/scripts/divide_and_compress_constants.py
new file mode 100644
index 0000000..4e11b6f
--- /dev/null
+++ b/scripts/divide_and_compress_constants.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python2.4
+#
+# Copyright (C) 2008 Google Inc.
+#
+# 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.
+#
+
+"""Constants for the divide_and_compress script and DirectoryZipper class."""
+
+__author__ = 'jmatt@google.com (Justin Mattson)'
+
+file_preamble = ('#!/usr/bin/env python\n'
+ '#\n'
+ '# Copyright 2008 Google Inc.\n'
+ '#\n'
+ '# Licensed under the Apache License, Version 2.0 (the'
+ '\"License");\n'
+ '# you may not use this file except in compliance with the '
+ 'License.\n'
+ '# You may obtain a copy of the License at\n'
+ '#\n'
+ '# http://www.apache.org/licenses/LICENSE-2.0\n'
+ '#\n'
+ '# Unless required by applicable law or agreed to in writing,'
+ ' software\n'
+ '# distributed under the License is distributed on an \"AS'
+ 'IS\" BASIS,\n'
+ '# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either '
+ 'express or implied.\n'
+ '# See the License for the specific language governing'
+ ' permissions and\n'
+ '# limitations under the License.\n'
+ '#\n\n'
+ 'import wsgiref.handlers\n'
+ 'from google.appengine.ext import zipserve\n'
+ 'from google.appengine.ext import webapp\n'
+ 'import memcache_zipserve\n\n\n'
+ 'class MainHandler(webapp.RequestHandler):\n\n'
+ ' def get(self):\n'
+ ' self.response.out.write(\'Hello world!\')\n\n'
+ 'def main():\n'
+ ' application = webapp.WSGIApplication([(\'/(.*)\','
+ ' memcache_zipserve.create_handler([')
+
+file_endpiece = ('])),\n'
+ '],\n'
+ 'debug=False)\n'
+ ' wsgiref.handlers.CGIHandler().run(application)\n\n'
+ 'if __name__ == \'__main__\':\n'
+ ' main()')
diff --git a/scripts/java_tests_file.template b/scripts/java_tests_file.template
new file mode 100644
index 0000000..7781a33
--- /dev/null
+++ b/scripts/java_tests_file.template
@@ -0,0 +1,21 @@
+package PACKAGE;
+
+import android.test.ActivityInstrumentationTestCase;
+
+/**
+ * This is a simple framework for a test of an Application. See
+ * {@link android.test.ApplicationTestCase ApplicationTestCase} for more information on
+ * how to write and extend Application tests.
+ * <p/>
+ * To run this test, you can type:
+ * adb shell am instrument -w \
+ * -e class PACKAGE.ACTIVITY_NAMETest \
+ * PACKAGE.tests/android.test.InstrumentationTestRunner
+ */
+public class ACTIVITY_NAMETest extends ActivityInstrumentationTestCase<ACTIVITY_NAME> {
+
+ public ACTIVITY_NAMETest() {
+ super("PACKAGE", ACTIVITY_NAME.class);
+ }
+
+} \ No newline at end of file
diff --git a/scripts/plugin.prop b/scripts/plugin.prop
index 15593ef..99dba4a 100644
--- a/scripts/plugin.prop
+++ b/scripts/plugin.prop
@@ -1,4 +1,4 @@
# begin plugin.prop
-plugin.version=0.8.0
+plugin.version=0.9.0
plugin.platform=android
# end plugin.prop \ No newline at end of file
diff --git a/scripts/test_divide_and_compress.py b/scripts/test_divide_and_compress.py
new file mode 100755
index 0000000..d0d27b3
--- /dev/null
+++ b/scripts/test_divide_and_compress.py
@@ -0,0 +1,490 @@
+#!/usr/bin/python2.4
+#
+# Copyright (C) 2008 Google Inc.
+#
+# 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.
+#
+
+"""Tests for divide_and_compress.py.
+
+TODO: Add tests for module methods.
+"""
+
+__author__ = 'jmatt@google.com (Justin Mattson)'
+
+import os
+import stat
+import unittest
+import zipfile
+from zipfile import ZipFile
+
+import divide_and_compress
+from mox import mox
+
+
+class BagOfParts(object):
+ """Just a generic class that I can use to assign random attributes to."""
+
+ def NoOp(self):
+ x = 1
+
+
+class ValidAndRemoveTests(unittest.TestCase):
+ """Test the ArchiveIsValid and RemoveLastFile methods."""
+
+ def setUp(self):
+ """Prepare the test.
+
+ Construct some mock objects for use with the tests.
+ """
+ self.my_mox = mox.Mox()
+ file1 = BagOfParts()
+ file1.filename = 'file1.txt'
+ file1.contents = 'This is a test file'
+ file2 = BagOfParts()
+ file2.filename = 'file2.txt'
+ file2.contents = ('akdjfk;djsf;kljdslkfjslkdfjlsfjkdvn;kn;2389rtu4i'
+ 'tn;ghf8:89H*hp748FJw80fu9WJFpwf39pujens;fihkhjfk'
+ 'sdjfljkgsc n;iself')
+ self.files = {'file1': file1, 'file2': file2}
+
+ def testArchiveIsValid(self):
+ """Test the DirectoryZipper.ArchiveIsValid method.
+
+ Run two tests, one that we expect to pass and one that we expect to fail
+ """
+ test_file_size = 1056730
+ self.my_mox.StubOutWithMock(os, 'stat')
+ os.stat('/foo/0.zip').AndReturn([test_file_size])
+ self.my_mox.StubOutWithMock(stat, 'ST_SIZE')
+ stat.ST_SIZE = 0
+ os.stat('/baz/0.zip').AndReturn([test_file_size])
+ mox.Replay(os.stat)
+ test_target = divide_and_compress.DirectoryZipper('/foo/', 'bar',
+ test_file_size - 1, True)
+
+ self.assertEqual(False, test_target.ArchiveIsValid(),
+ msg=('ERROR: Test failed, ArchiveIsValid should have '
+ 'returned false, but returned true'))
+
+ test_target = divide_and_compress.DirectoryZipper('/baz/', 'bar',
+ test_file_size + 1, True)
+ self.assertEqual(True, test_target.ArchiveIsValid(),
+ msg=('ERROR: Test failed, ArchiveIsValid should have'
+ ' returned true, but returned false'))
+
+ def testRemoveLastFile(self):
+ """Test DirectoryZipper.RemoveLastFile method.
+
+ Construct a ZipInfo mock object with two records, verify that write is
+ only called once on the new ZipFile object.
+ """
+ source = self.CreateZipSource()
+ dest = self.CreateZipDestination()
+ source_path = ''.join([os.getcwd(), '/0-old.zip'])
+ dest_path = ''.join([os.getcwd(), '/0.zip'])
+ test_target = divide_and_compress.DirectoryZipper(
+ ''.join([os.getcwd(), '/']), 'dummy', 1024*1024, True)
+ self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath')
+ test_target.OpenZipFileAtPath(source_path, mode='r').AndReturn(source)
+ test_target.OpenZipFileAtPath(dest_path,
+ compress=zipfile.ZIP_DEFLATED,
+ mode='w').AndReturn(dest)
+ self.my_mox.StubOutWithMock(os, 'rename')
+ os.rename(dest_path, source_path)
+ self.my_mox.StubOutWithMock(os, 'unlink')
+ os.unlink(source_path)
+
+ self.my_mox.ReplayAll()
+ test_target.RemoveLastFile()
+ self.my_mox.VerifyAll()
+
+ def CreateZipSource(self):
+ """Create a mock zip sourec object.
+
+ Read should only be called once, because the second file is the one
+ being removed.
+
+ Returns:
+ A configured mocked
+ """
+
+ source_zip = self.my_mox.CreateMock(ZipFile)
+ source_zip.infolist().AndReturn([self.files['file1'], self.files['file1']])
+ source_zip.infolist().AndReturn([self.files['file1'], self.files['file1']])
+ source_zip.read(self.files['file1'].filename).AndReturn(
+ self.files['file1'].contents)
+ source_zip.close()
+ return source_zip
+
+ def CreateZipDestination(self):
+ """Create mock destination zip.
+
+ Write should only be called once, because there are two files in the
+ source zip and we expect the second to be removed.
+
+ Returns:
+ A configured mocked
+ """
+
+ dest_zip = mox.MockObject(ZipFile)
+ dest_zip.writestr(self.files['file1'].filename,
+ self.files['file1'].contents)
+ dest_zip.close()
+ return dest_zip
+
+ def tearDown(self):
+ """Remove any stubs we've created."""
+ self.my_mox.UnsetStubs()
+
+
+class FixArchiveTests(unittest.TestCase):
+ """Tests for the DirectoryZipper.FixArchive method."""
+
+ def setUp(self):
+ """Create a mock file object."""
+ self.my_mox = mox.Mox()
+ self.file1 = BagOfParts()
+ self.file1.filename = 'file1.txt'
+ self.file1.contents = 'This is a test file'
+
+ def _InitMultiFileData(self):
+ """Create an array of mock file objects.
+
+ Create three mock file objects that we can use for testing.
+ """
+ self.multi_file_dir = []
+
+ file1 = BagOfParts()
+ file1.filename = 'file1.txt'
+ file1.contents = 'kjaskl;jkdjfkja;kjsnbvjnvnbuewklriujalvjsd'
+ self.multi_file_dir.append(file1)
+
+ file2 = BagOfParts()
+ file2.filename = 'file2.txt'
+ file2.contents = ('He entered the room and there in the center, it was.'
+ ' Looking upon the thing, suddenly he could not remember'
+ ' whether he had actually seen it before or whether'
+ ' his memory of it was merely the effect of something'
+ ' so often being imagined that it had long since become '
+ ' manifest in his mind.')
+ self.multi_file_dir.append(file2)
+
+ file3 = BagOfParts()
+ file3.filename = 'file3.txt'
+ file3.contents = 'Whoa, what is \'file2.txt\' all about?'
+ self.multi_file_dir.append(file3)
+
+ def testSingleFileArchive(self):
+ """Test behavior of FixArchive when the archive has a single member.
+
+ We expect that when this method is called with an archive that has a
+ single member that it will return False and unlink the archive.
+ """
+ test_target = divide_and_compress.DirectoryZipper(
+ ''.join([os.getcwd(), '/']), 'dummy', 1024*1024, True)
+ self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath')
+ test_target.OpenZipFileAtPath(
+ ''.join([os.getcwd(), '/0.zip']), mode='r').AndReturn(
+ self.CreateSingleFileMock())
+ self.my_mox.StubOutWithMock(os, 'unlink')
+ os.unlink(''.join([os.getcwd(), '/0.zip']))
+ self.my_mox.ReplayAll()
+ self.assertEqual(False, test_target.FixArchive('SIZE'))
+ self.my_mox.VerifyAll()
+
+ def CreateSingleFileMock(self):
+ """Create a mock ZipFile object for testSingleFileArchive.
+
+ We just need it to return a single member infolist twice
+
+ Returns:
+ A configured mock object
+ """
+ mock_zip = self.my_mox.CreateMock(ZipFile)
+ mock_zip.infolist().AndReturn([self.file1])
+ mock_zip.infolist().AndReturn([self.file1])
+ mock_zip.close()
+ return mock_zip
+
+ def testMultiFileArchive(self):
+ """Test behavior of DirectoryZipper.FixArchive with a multi-file archive.
+
+ We expect that FixArchive will rename the old archive, adding '-old' before
+ '.zip', read all the members except the last one of '-old' into a new
+ archive with the same name as the original, and then unlink the '-old' copy
+ """
+ test_target = divide_and_compress.DirectoryZipper(
+ ''.join([os.getcwd(), '/']), 'dummy', 1024*1024, True)
+ self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath')
+ test_target.OpenZipFileAtPath(
+ ''.join([os.getcwd(), '/0.zip']), mode='r').AndReturn(
+ self.CreateMultiFileMock())
+ self.my_mox.StubOutWithMock(test_target, 'RemoveLastFile')
+ test_target.RemoveLastFile(''.join([os.getcwd(), '/0.zip']))
+ self.my_mox.StubOutWithMock(os, 'stat')
+ os.stat(''.join([os.getcwd(), '/0.zip'])).AndReturn([49302])
+ self.my_mox.StubOutWithMock(stat, 'ST_SIZE')
+ stat.ST_SIZE = 0
+ self.my_mox.ReplayAll()
+ self.assertEqual(True, test_target.FixArchive('SIZE'))
+ self.my_mox.VerifyAll()
+
+ def CreateMultiFileMock(self):
+ """Create mock ZipFile object for use with testMultiFileArchive.
+
+ The mock just needs to return the infolist mock that is prepared in
+ InitMultiFileData()
+
+ Returns:
+ A configured mock object
+ """
+ self._InitMultiFileData()
+ mock_zip = self.my_mox.CreateMock(ZipFile)
+ mock_zip.infolist().AndReturn(self.multi_file_dir)
+ mock_zip.close()
+ return mock_zip
+
+ def tearDown(self):
+ """Unset any mocks that we've created."""
+ self.my_mox.UnsetStubs()
+
+
+class AddFileToArchiveTest(unittest.TestCase):
+ """Test behavior of method to add a file to an archive."""
+
+ def setUp(self):
+ """Setup the arguments for the DirectoryZipper object."""
+ self.my_mox = mox.Mox()
+ self.output_dir = '%s/' % os.getcwd()
+ self.file_to_add = 'file.txt'
+ self.input_dir = '/foo/bar/baz/'
+
+ def testAddFileToArchive(self):
+ """Test the DirectoryZipper.AddFileToArchive method.
+
+ We are testing a pretty trivial method, we just expect it to look at the
+ file its adding, so that it possible can through out a warning.
+ """
+ test_target = divide_and_compress.DirectoryZipper(self.output_dir,
+ self.input_dir,
+ 1024*1024, True)
+ self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath')
+ archive_mock = self.CreateArchiveMock()
+ test_target.OpenZipFileAtPath(
+ ''.join([self.output_dir, '0.zip']),
+ compress=zipfile.ZIP_DEFLATED).AndReturn(archive_mock)
+ self.StubOutOsModule()
+ self.my_mox.ReplayAll()
+ test_target.AddFileToArchive(''.join([self.input_dir, self.file_to_add]),
+ zipfile.ZIP_DEFLATED)
+ self.my_mox.VerifyAll()
+
+ def StubOutOsModule(self):
+ """Create a mock for the os.path and os.stat objects.
+
+ Create a stub that will return the type (file or directory) and size of the
+ object that is to be added.
+ """
+ self.my_mox.StubOutWithMock(os.path, 'isfile')
+ os.path.isfile(''.join([self.input_dir, self.file_to_add])).AndReturn(True)
+ self.my_mox.StubOutWithMock(os, 'stat')
+ os.stat(''.join([self.input_dir, self.file_to_add])).AndReturn([39480])
+ self.my_mox.StubOutWithMock(stat, 'ST_SIZE')
+ stat.ST_SIZE = 0
+
+ def CreateArchiveMock(self):
+ """Create a mock ZipFile for use with testAddFileToArchive.
+
+ Just verify that write is called with the file we expect and that the
+ archive is closed after the file addition
+
+ Returns:
+ A configured mock object
+ """
+ archive_mock = self.my_mox.CreateMock(ZipFile)
+ archive_mock.write(''.join([self.input_dir, self.file_to_add]),
+ self.file_to_add)
+ archive_mock.close()
+ return archive_mock
+
+ def tearDown(self):
+ self.my_mox.UnsetStubs()
+
+
+class CompressDirectoryTest(unittest.TestCase):
+ """Test the master method of the class.
+
+ Testing with the following directory structure.
+ /dir1/
+ /dir1/file1.txt
+ /dir1/file2.txt
+ /dir1/dir2/
+ /dir1/dir2/dir3/
+ /dir1/dir2/dir4/
+ /dir1/dir2/dir4/file3.txt
+ /dir1/dir5/
+ /dir1/dir5/file4.txt
+ /dir1/dir5/file5.txt
+ /dir1/dir5/file6.txt
+ /dir1/dir5/file7.txt
+ /dir1/dir6/
+ /dir1/dir6/file8.txt
+
+ file1.txt., file2.txt, file3.txt should be in 0.zip
+ file4.txt should be in 1.zip
+ file5.txt, file6.txt should be in 2.zip
+ file7.txt will not be stored since it will be too large compressed
+ file8.txt should b in 3.zip
+ """
+
+ def setUp(self):
+ """Setup all the mocks for this test."""
+ self.my_mox = mox.Mox()
+
+ self.base_dir = '/dir1'
+ self.output_path = '/out_dir/'
+ self.test_target = divide_and_compress.DirectoryZipper(
+ self.output_path, self.base_dir, 1024*1024, True)
+
+ self.InitArgLists()
+ self.InitOsDotPath()
+ self.InitArchiveIsValid()
+ self.InitWriteIndexRecord()
+ self.InitAddFileToArchive()
+
+ def tearDown(self):
+ self.my_mox.UnsetStubs()
+
+ def testCompressDirectory(self):
+ """Test the DirectoryZipper.CompressDirectory method."""
+ self.my_mox.ReplayAll()
+ for arguments in self.argument_lists:
+ self.test_target.CompressDirectory(None, arguments[0], arguments[1])
+ self.my_mox.VerifyAll()
+
+ def InitAddFileToArchive(self):
+ """Setup mock for DirectoryZipper.AddFileToArchive.
+
+ Make sure that the files are added in the order we expect.
+ """
+ self.my_mox.StubOutWithMock(self.test_target, 'AddFileToArchive')
+ self.test_target.AddFileToArchive('/dir1/file1.txt', zipfile.ZIP_DEFLATED)
+ self.test_target.AddFileToArchive('/dir1/file2.txt', zipfile.ZIP_DEFLATED)
+ self.test_target.AddFileToArchive('/dir1/dir2/dir4/file3.txt',
+ zipfile.ZIP_DEFLATED)
+ self.test_target.AddFileToArchive('/dir1/dir5/file4.txt',
+ zipfile.ZIP_DEFLATED)
+ self.test_target.AddFileToArchive('/dir1/dir5/file4.txt',
+ zipfile.ZIP_DEFLATED)
+ self.test_target.AddFileToArchive('/dir1/dir5/file5.txt',
+ zipfile.ZIP_DEFLATED)
+ self.test_target.AddFileToArchive('/dir1/dir5/file5.txt',
+ zipfile.ZIP_DEFLATED)
+ self.test_target.AddFileToArchive('/dir1/dir5/file6.txt',
+ zipfile.ZIP_DEFLATED)
+ self.test_target.AddFileToArchive('/dir1/dir5/file7.txt',
+ zipfile.ZIP_DEFLATED)
+ self.test_target.AddFileToArchive('/dir1/dir5/file7.txt',
+ zipfile.ZIP_DEFLATED)
+ self.test_target.AddFileToArchive('/dir1/dir6/file8.txt',
+ zipfile.ZIP_DEFLATED)
+
+ def InitWriteIndexRecord(self):
+ """Setup mock for DirectoryZipper.WriteIndexRecord."""
+ self.my_mox.StubOutWithMock(self.test_target, 'WriteIndexRecord')
+
+ # we are trying to compress 8 files, but we should only attempt to
+ # write an index record 7 times, because one file is too large to be stored
+ self.test_target.WriteIndexRecord().AndReturn(True)
+ self.test_target.WriteIndexRecord().AndReturn(False)
+ self.test_target.WriteIndexRecord().AndReturn(False)
+ self.test_target.WriteIndexRecord().AndReturn(True)
+ self.test_target.WriteIndexRecord().AndReturn(True)
+ self.test_target.WriteIndexRecord().AndReturn(False)
+ self.test_target.WriteIndexRecord().AndReturn(True)
+
+ def InitArchiveIsValid(self):
+ """Mock out DirectoryZipper.ArchiveIsValid and DirectoryZipper.FixArchive.
+
+ Mock these methods out such that file1, file2, and file3 go into one
+ archive. file4 then goes into the next archive, file5 and file6 in the
+ next, file 7 should appear too large to compress into an archive, and
+ file8 goes into the final archive
+ """
+ self.my_mox.StubOutWithMock(self.test_target, 'ArchiveIsValid')
+ self.my_mox.StubOutWithMock(self.test_target, 'FixArchive')
+ self.test_target.ArchiveIsValid().AndReturn(True)
+ self.test_target.ArchiveIsValid().AndReturn(True)
+ self.test_target.ArchiveIsValid().AndReturn(True)
+
+ # should be file4.txt
+ self.test_target.ArchiveIsValid().AndReturn(False)
+ self.test_target.FixArchive('SIZE').AndReturn(True)
+ self.test_target.ArchiveIsValid().AndReturn(True)
+
+ # should be file5.txt
+ self.test_target.ArchiveIsValid().AndReturn(False)
+ self.test_target.FixArchive('SIZE').AndReturn(True)
+ self.test_target.ArchiveIsValid().AndReturn(True)
+ self.test_target.ArchiveIsValid().AndReturn(True)
+
+ # should be file7.txt
+ self.test_target.ArchiveIsValid().AndReturn(False)
+ self.test_target.FixArchive('SIZE').AndReturn(True)
+ self.test_target.ArchiveIsValid().AndReturn(False)
+ self.test_target.FixArchive('SIZE').AndReturn(False)
+ self.test_target.ArchiveIsValid().AndReturn(True)
+
+ def InitOsDotPath(self):
+ """Mock out os.path.isfile.
+
+ Mock this out so the things we want to appear as files appear as files and
+ the things we want to appear as directories appear as directories. Also
+ make sure that the order of file visits is as we expect (which is why
+ InAnyOrder isn't used here).
+ """
+ self.my_mox.StubOutWithMock(os.path, 'isfile')
+ os.path.isfile('/dir1/dir2').AndReturn(False)
+ os.path.isfile('/dir1/dir5').AndReturn(False)
+ os.path.isfile('/dir1/dir6').AndReturn(False)
+ os.path.isfile('/dir1/file1.txt').AndReturn(True)
+ os.path.isfile('/dir1/file2.txt').AndReturn(True)
+ os.path.isfile('/dir1/dir2/dir3').AndReturn(False)
+ os.path.isfile('/dir1/dir2/dir4').AndReturn(False)
+ os.path.isfile('/dir1/dir2/dir4/file3.txt').AndReturn(True)
+ os.path.isfile('/dir1/dir5/file4.txt').AndReturn(True)
+ os.path.isfile('/dir1/dir5/file4.txt').AndReturn(True)
+ os.path.isfile('/dir1/dir5/file5.txt').AndReturn(True)
+ os.path.isfile('/dir1/dir5/file5.txt').AndReturn(True)
+ os.path.isfile('/dir1/dir5/file6.txt').AndReturn(True)
+ os.path.isfile('/dir1/dir5/file7.txt').AndReturn(True)
+ os.path.isfile('/dir1/dir5/file7.txt').AndReturn(True)
+ os.path.isfile('/dir1/dir6/file8.txt').AndReturn(True)
+
+ def InitArgLists(self):
+ """Create the directory path => directory contents mappings."""
+ self.argument_lists = []
+ self.argument_lists.append(['/dir1',
+ ['file1.txt', 'file2.txt', 'dir2', 'dir5',
+ 'dir6']])
+ self.argument_lists.append(['/dir1/dir2', ['dir3', 'dir4']])
+ self.argument_lists.append(['/dir1/dir2/dir3', []])
+ self.argument_lists.append(['/dir1/dir2/dir4', ['file3.txt']])
+ self.argument_lists.append(['/dir1/dir5',
+ ['file4.txt', 'file5.txt', 'file6.txt',
+ 'file7.txt']])
+ self.argument_lists.append(['/dir1/dir6', ['file8.txt']])
+
+if __name__ == '__main__':
+ unittest.main()